@cosmicdrift/kumiko-bundled-features 0.374.0 → 0.377.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.
Files changed (205) hide show
  1. package/dist/account-security/constants.d.ts +1 -0
  2. package/dist/account-security/constants.js +1 -0
  3. package/dist/account-security/index.d.ts +1 -1
  4. package/dist/account-security/index.js +1 -1
  5. package/dist/admin-shell/web/client-plugin.d.ts +1 -1
  6. package/dist/audit/web/client-plugin.d.ts +1 -1
  7. package/dist/auth-email-password/mail-notifications.js +1 -1
  8. package/dist/auth-email-password/web/auth-client.js +1 -1
  9. package/dist/auth-email-password/web/auth-form-primitives.js +1 -1
  10. package/dist/auth-email-password/web/default-topbar-actions.js +1 -1
  11. package/dist/auth-email-password/web/index.js +1 -1
  12. package/dist/auth-email-password/web/session.js +1 -1
  13. package/dist/auth-email-password/web/tenant-menu-items.js +1 -1
  14. package/dist/auth-email-password/web/tenant-switcher.js +1 -1
  15. package/dist/auth-email-password/web/user-menu.js +1 -1
  16. package/dist/auth-foundation/env.d.ts +9 -0
  17. package/dist/auth-foundation/env.js +21 -0
  18. package/dist/auth-foundation/index.d.ts +1 -0
  19. package/dist/auth-foundation/index.js +1 -0
  20. package/dist/auth-mfa/schema/user-mfa.d.ts +6 -6
  21. package/dist/billing-foundation/consumer-protection/confirmation-mail.js +1 -1
  22. package/dist/billing-foundation/consumer-protection/termination-pages.js +1 -1
  23. package/dist/billing-foundation/contact-card.d.ts +2 -0
  24. package/dist/billing-foundation/contact-card.js +16 -0
  25. package/dist/billing-foundation/entities.d.ts +8 -8
  26. package/dist/billing-foundation/handlers/billing-plans.query.js +1 -1
  27. package/dist/billing-foundation/index.d.ts +1 -1
  28. package/dist/billing-foundation/plan-catalog.js +1 -0
  29. package/dist/billing-foundation/projection.d.ts +8 -8
  30. package/dist/billing-foundation/types.d.ts +11 -0
  31. package/dist/billing-foundation/validate-options.js +13 -0
  32. package/dist/billing-foundation/web/billing-contact-plan.d.ts +5 -0
  33. package/dist/billing-foundation/web/billing-contact-plan.js +17 -0
  34. package/dist/billing-foundation/web/billing-plans-panel.js +5 -13
  35. package/dist/billing-foundation/web/cancel-contract-dialog.js +2 -2
  36. package/dist/billing-foundation/web/checkout-consent-dialog.d.ts +1 -1
  37. package/dist/billing-foundation/web/checkout-consent-dialog.js +1 -0
  38. package/dist/billing-foundation/web/client-plugin.d.ts +1 -1
  39. package/dist/billing-foundation/web/localized-params.d.ts +1 -0
  40. package/dist/billing-foundation/web/localized-params.js +11 -0
  41. package/dist/cap-counter/entity.d.ts +1 -1
  42. package/dist/cap-overview/web/cap-cards-panel.js +3 -3
  43. package/dist/cap-overview/web/client-plugin.d.ts +1 -1
  44. package/dist/channel-email/email-channel.d.ts +2 -1
  45. package/dist/compliance-profiles/web/client-plugin.d.ts +1 -1
  46. package/dist/config/feature.js +2 -2
  47. package/dist/config/table.d.ts +6 -6
  48. package/dist/custom-fields/entity.d.ts +4 -4
  49. package/dist/custom-fields/web/client-plugin.d.ts +1 -1
  50. package/dist/data-retention/schema/tenant-retention-override.d.ts +4 -4
  51. package/dist/delivery/tables.d.ts +6 -6
  52. package/dist/delivery/unsubscribe-page.js +1 -1
  53. package/dist/delivery/web/client-plugin.d.ts +1 -1
  54. package/dist/delivery/web/delivery-status-cell.js +1 -1
  55. package/dist/document-ingest-foundation/entity.d.ts +4 -4
  56. package/dist/feature-toggles/web/client-plugin.d.ts +1 -1
  57. package/dist/file-provider-s3/env-named-feature.d.ts +17 -0
  58. package/dist/file-provider-s3/env-named-feature.js +23 -0
  59. package/dist/file-provider-s3/env-schema.d.ts +7 -0
  60. package/dist/file-provider-s3/env-schema.js +15 -10
  61. package/dist/file-provider-s3/index.d.ts +1 -0
  62. package/dist/file-provider-s3/index.js +1 -0
  63. package/dist/folders/entity.d.ts +8 -8
  64. package/dist/folders/web/client-plugin.d.ts +1 -1
  65. package/dist/form-draft/entity.d.ts +2 -2
  66. package/dist/host-routing/constants.d.ts +2 -0
  67. package/dist/host-routing/constants.js +3 -0
  68. package/dist/host-routing/dispatch.d.ts +1 -1
  69. package/dist/host-routing/dispatch.js +35 -25
  70. package/dist/host-routing/feature.js +100 -27
  71. package/dist/host-routing/host.d.ts +9 -0
  72. package/dist/host-routing/host.js +24 -2
  73. package/dist/host-routing/index.d.ts +2 -1
  74. package/dist/host-routing/index.js +1 -0
  75. package/dist/host-routing/options.d.ts +14 -0
  76. package/dist/host-routing/redirects.d.ts +6 -0
  77. package/dist/host-routing/redirects.js +27 -0
  78. package/dist/host-routing/tenant-lookup.d.ts +7 -0
  79. package/dist/host-routing/tenant-lookup.js +39 -0
  80. package/dist/host-routing/validate-options.js +34 -1
  81. package/dist/inbound-mail-foundation/entities.d.ts +25 -25
  82. package/dist/inbound-mail-foundation/feature.js +7 -0
  83. package/dist/inbound-mail-foundation/i18n.d.ts +3 -0
  84. package/dist/inbound-mail-foundation/i18n.js +35 -0
  85. package/dist/inbound-mail-foundation/index.d.ts +1 -0
  86. package/dist/inbound-mail-foundation/index.js +1 -0
  87. package/dist/inbound-mail-foundation/projection.d.ts +20 -20
  88. package/dist/inbound-mail-foundation/screens.d.ts +7 -0
  89. package/dist/inbound-mail-foundation/screens.js +86 -0
  90. package/dist/inbound-mail-foundation/types.d.ts +2 -2
  91. package/dist/jobs/job-run-table.d.ts +6 -6
  92. package/dist/jobs/web/client-plugin.d.ts +1 -1
  93. package/dist/ledger/entity.d.ts +13 -13
  94. package/dist/legal-pages/feature.js +11 -1
  95. package/dist/legal-pages/layout.d.ts +2 -2
  96. package/dist/legal-pages/layout.js +4 -4
  97. package/dist/legal-pages/web/client-plugin.d.ts +1 -1
  98. package/dist/mail-foundation/feature.d.ts +20 -21
  99. package/dist/mail-foundation/feature.js +1 -1
  100. package/dist/managed-pages/table.d.ts +12 -12
  101. package/dist/managed-pages/web/client-plugin.d.ts +1 -1
  102. package/dist/notes-history/entity.d.ts +12 -12
  103. package/dist/notes-history/web/client-plugin.d.ts +1 -1
  104. package/dist/page-render/branding.js +1 -1
  105. package/dist/page-render/layout.d.ts +1 -1
  106. package/dist/page-render/layout.js +2 -2
  107. package/dist/page-render/markdown.js +1 -1
  108. package/dist/personal-access-tokens/i18n.js +1 -1
  109. package/dist/personal-access-tokens/schema/api-token.d.ts +6 -6
  110. package/dist/personal-access-tokens/screens.js +1 -1
  111. package/dist/secrets/feature.d.ts +1 -1
  112. package/dist/secrets/feature.js +3 -3
  113. package/dist/secrets/table.d.ts +1 -1
  114. package/dist/seo/sitemap.js +1 -1
  115. package/dist/sessions/schema/user-session.d.ts +4 -4
  116. package/dist/shared/mail-template-body.js +1 -1
  117. package/dist/shared/purge-files-reaching-subject.js +98 -3
  118. package/dist/tags/entity.d.ts +9 -9
  119. package/dist/tags/web/client-plugin.d.ts +1 -1
  120. package/dist/template-resolver/document-blocks.d.ts +5 -0
  121. package/dist/template-resolver/document-blocks.js +21 -0
  122. package/dist/template-resolver/document-ops.d.ts +167 -0
  123. package/dist/template-resolver/document-ops.js +390 -0
  124. package/dist/template-resolver/document-sanitizer.d.ts +3 -0
  125. package/dist/template-resolver/document-sanitizer.js +6 -0
  126. package/dist/template-resolver/feature.d.ts +2 -0
  127. package/dist/template-resolver/feature.js +4 -0
  128. package/dist/template-resolver/handlers/bundle-import.js +1 -0
  129. package/dist/template-resolver/handlers/document-base.d.ts +23 -0
  130. package/dist/template-resolver/handlers/document-base.js +55 -0
  131. package/dist/template-resolver/handlers/document-blocks.query.d.ts +22 -0
  132. package/dist/template-resolver/handlers/document-blocks.query.js +46 -0
  133. package/dist/template-resolver/handlers/export-bundle.query.d.ts +1 -0
  134. package/dist/template-resolver/handlers/export-bundle.query.js +9 -2
  135. package/dist/template-resolver/handlers/import-bundle-system.write.d.ts +1 -0
  136. package/dist/template-resolver/handlers/import-bundle.write.d.ts +1 -0
  137. package/dist/template-resolver/handlers/patch-draft.write.d.ts +66 -0
  138. package/dist/template-resolver/handlers/patch-draft.write.js +105 -0
  139. package/dist/template-resolver/handlers/preview.query.d.ts +1 -0
  140. package/dist/template-resolver/handlers/preview.query.js +2 -1
  141. package/dist/template-resolver/handlers/send-test.write.d.ts +1 -0
  142. package/dist/template-resolver/handlers/send-test.write.js +2 -1
  143. package/dist/template-resolver/handlers/template-write.d.ts +2 -0
  144. package/dist/template-resolver/handlers/template-write.js +14 -2
  145. package/dist/template-resolver/handlers/validate.query.d.ts +10 -4
  146. package/dist/template-resolver/handlers/validate.query.js +12 -7
  147. package/dist/template-resolver/{web/placeholder-text.js → placeholder-text.js} +1 -1
  148. package/dist/template-resolver/qualified-names.d.ts +2 -0
  149. package/dist/template-resolver/qualified-names.js +2 -0
  150. package/dist/template-resolver/seeding.d.ts +1 -0
  151. package/dist/template-resolver/seeding.js +2 -1
  152. package/dist/template-resolver/table.d.ts +10 -10
  153. package/dist/template-resolver/template-bundle.d.ts +1 -0
  154. package/dist/template-resolver/template-bundle.js +6 -1
  155. package/dist/template-resolver/template-preview.d.ts +4 -1
  156. package/dist/template-resolver/template-preview.js +16 -3
  157. package/dist/template-resolver/template-validation.d.ts +2 -1
  158. package/dist/template-resolver/template-validation.js +1 -1
  159. package/dist/template-resolver/user-content-table.d.ts +10 -10
  160. package/dist/template-resolver/web/client-plugin.d.ts +1 -1
  161. package/dist/template-resolver/web/client-plugin.js +3 -3
  162. package/dist/template-resolver/web/document-tiptap-editor.js +1 -1
  163. package/dist/template-resolver/web/document-toolbar-blocks.d.ts +2 -5
  164. package/dist/template-resolver/web/document-toolbar-blocks.js +1 -21
  165. package/dist/template-resolver/web/i18n.js +14 -0
  166. package/dist/template-resolver/web/placeholder-line-editor.js +2 -2
  167. package/dist/tenant/invitation-table.d.ts +6 -6
  168. package/dist/tenant/membership-table.d.ts +4 -4
  169. package/dist/tenant/schema/tenant.d.ts +6 -6
  170. package/dist/tenant/web/client-plugin.d.ts +1 -1
  171. package/dist/tenant/web/member-status-cell.js +1 -1
  172. package/dist/tier-engine/entity.d.ts +1 -1
  173. package/dist/tier-engine/tier-resolver.d.ts +1 -1
  174. package/dist/tier-engine/web/client-plugin.d.ts +1 -1
  175. package/dist/user/schema/user.d.ts +12 -12
  176. package/dist/user-data-rights/env.d.ts +5 -0
  177. package/dist/user-data-rights/env.js +11 -0
  178. package/dist/user-data-rights/feature.js +4 -2
  179. package/dist/user-data-rights/index.d.ts +1 -0
  180. package/dist/user-data-rights/index.js +1 -0
  181. package/dist/user-data-rights/lib/mail-transport-resolver.js +1 -1
  182. package/dist/user-data-rights/lib/storage-provider-resolver.js +1 -1
  183. package/dist/user-data-rights/mail-notifications.js +1 -1
  184. package/dist/user-data-rights/schema/download-attempt.d.ts +14 -14
  185. package/dist/user-data-rights/schema/download-token.d.ts +8 -8
  186. package/dist/user-data-rights/schema/export-job.d.ts +6 -6
  187. package/dist/user-data-rights/web/client-plugin.d.ts +1 -1
  188. package/dist/user-data-rights/web/privacy-center-screen.js +2 -2
  189. package/dist/waitlist/entity.d.ts +14 -14
  190. package/dist/waitlist/legacy-migrate.d.ts +2 -0
  191. package/dist/waitlist/legacy-migrate.js +38 -3
  192. package/package.json +16 -11
  193. package/src/account-security/changes.json +6 -0
  194. package/src/auth-foundation/changes.json +6 -0
  195. package/src/billing-foundation/changes.json +6 -0
  196. package/src/channel-email/changes.json +6 -0
  197. package/src/crypto-shredding/changes.json +6 -0
  198. package/src/file-provider-s3/changes.json +6 -0
  199. package/src/host-routing/changes.json +6 -0
  200. package/src/inbound-mail-foundation/changes.json +7 -0
  201. package/src/legal-pages/changes.json +6 -0
  202. package/src/template-resolver/changes.json +18 -0
  203. package/src/user-data-rights/changes.json +6 -0
  204. package/src/waitlist/changes.json +6 -0
  205. /package/dist/template-resolver/{web/placeholder-text.d.ts → placeholder-text.d.ts} +0 -0
@@ -1,4 +1,5 @@
1
1
  export declare const ACCOUNT_SECURITY_FEATURE = "account-security";
2
2
  export declare const ACCOUNT_SECURITY_SCREEN_ID = "account-security";
3
3
  export declare const ACCOUNT_SECURITY_SCREEN_QN = "account-security:screen:account-security";
4
+ export declare const ACCOUNT_SECURITY_SCREEN_TITLE_KEY = "screen:account-security.title";
4
5
  export declare const ACCOUNT_SECURITY_NAV_LABEL = "account-security:nav.accountSecurity";
@@ -2,4 +2,5 @@
2
2
  export const ACCOUNT_SECURITY_FEATURE = "account-security";
3
3
  export const ACCOUNT_SECURITY_SCREEN_ID = "account-security";
4
4
  export const ACCOUNT_SECURITY_SCREEN_QN = `${ACCOUNT_SECURITY_FEATURE}:screen:${ACCOUNT_SECURITY_SCREEN_ID}`;
5
+ export const ACCOUNT_SECURITY_SCREEN_TITLE_KEY = `screen:${ACCOUNT_SECURITY_SCREEN_ID}.title`;
5
6
  export const ACCOUNT_SECURITY_NAV_LABEL = `${ACCOUNT_SECURITY_FEATURE}:nav.accountSecurity`;
@@ -1,3 +1,3 @@
1
- export { ACCOUNT_SECURITY_FEATURE, ACCOUNT_SECURITY_NAV_LABEL, ACCOUNT_SECURITY_SCREEN_ID, ACCOUNT_SECURITY_SCREEN_QN, } from "./constants.ts";
1
+ export { ACCOUNT_SECURITY_FEATURE, ACCOUNT_SECURITY_NAV_LABEL, ACCOUNT_SECURITY_SCREEN_ID, ACCOUNT_SECURITY_SCREEN_QN, ACCOUNT_SECURITY_SCREEN_TITLE_KEY, } from "./constants.ts";
2
2
  export { type AccountSecurityFeatureOptions, type AccountSecurityNavOptions, accountSecurityMfaStatusVisibility, createAccountSecurityFeature, } from "./feature.ts";
3
3
  export { ACCOUNT_SECURITY_I18N } from "./i18n.ts";
@@ -1,3 +1,3 @@
1
- export { ACCOUNT_SECURITY_FEATURE, ACCOUNT_SECURITY_NAV_LABEL, ACCOUNT_SECURITY_SCREEN_ID, ACCOUNT_SECURITY_SCREEN_QN, } from "./constants.js";
1
+ export { ACCOUNT_SECURITY_FEATURE, ACCOUNT_SECURITY_NAV_LABEL, ACCOUNT_SECURITY_SCREEN_ID, ACCOUNT_SECURITY_SCREEN_QN, ACCOUNT_SECURITY_SCREEN_TITLE_KEY, } from "./constants.js";
2
2
  export { accountSecurityMfaStatusVisibility, createAccountSecurityFeature, } from "./feature.js";
3
3
  export { ACCOUNT_SECURITY_I18N } from "./i18n.js";
@@ -1,5 +1,5 @@
1
1
  import { type TranslationsByLocale } from "@cosmicdrift/kumiko-renderer";
2
- import type { ClientFeatureDefinition } from "@cosmicdrift/kumiko-renderer-web";
2
+ import type { ClientFeatureDefinition } from "@cosmicdrift/kumiko-renderer/web";
3
3
  export type AdminShellClientOptions = {
4
4
  readonly translations?: TranslationsByLocale;
5
5
  };
@@ -1,5 +1,5 @@
1
1
  import { type TranslationsByLocale } from "@cosmicdrift/kumiko-renderer";
2
- import type { ClientFeatureDefinition } from "@cosmicdrift/kumiko-renderer-web";
2
+ import type { ClientFeatureDefinition } from "@cosmicdrift/kumiko-renderer/web";
3
3
  export type AuditClientOptions = {
4
4
  readonly translations?: TranslationsByLocale;
5
5
  };
@@ -1,6 +1,6 @@
1
1
  import { QnTypes, qn, templateVariable, } from "@cosmicdrift/kumiko-framework/engine";
2
2
  import { hasMailTranslationKey, listMailLocales, mailT, resolveMailLocale, } from "@cosmicdrift/kumiko-framework/i18n";
3
- import { escapeHtml, escapeHtmlAttr } from "@cosmicdrift/kumiko-headless";
3
+ import { escapeHtml, escapeHtmlAttr } from "@cosmicdrift/kumiko-renderer/headless";
4
4
  import { AUTH_EMAIL_PASSWORD_FEATURE } from "./constants.js";
5
5
  export const AUTH_MAIL_KINDS = {
6
6
  passwordReset: { name: "password-reset", translationKey: "reset" },
@@ -8,8 +8,8 @@
8
8
  // Die dispatcher-live nutzt denselben readCsrfToken-Helper; wir
9
9
  // reuse'n ihn hier, damit die Konstanten (Cookie-Name, Header-Name)
10
10
  // nicht divergieren.
11
- import { CSRF_HEADER_NAME, readCsrfToken } from "@cosmicdrift/kumiko-dispatcher-live";
12
11
  import { parseRoles } from "@cosmicdrift/kumiko-framework/utils";
12
+ import { CSRF_HEADER_NAME, readCsrfToken } from "@cosmicdrift/kumiko-renderer/live";
13
13
  export function csrfHeader() {
14
14
  const token = readCsrfToken();
15
15
  return token !== undefined ? { [CSRF_HEADER_NAME]: token } : {};
@@ -15,7 +15,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
15
15
  // Link-Styles laufen über das Link-Primitive (variant="button"/"muted") —
16
16
  // die früheren authButtonClass/authMutedLinkClass sind dorthin gewandert.
17
17
  import { usePrimitives } from "@cosmicdrift/kumiko-renderer";
18
- import { BareFormProvider, cn } from "@cosmicdrift/kumiko-renderer-web";
18
+ import { BareFormProvider, cn } from "@cosmicdrift/kumiko-renderer/web";
19
19
  import { createContext, useContext, useEffect, useState } from "react";
20
20
  const defaultAuthShell = (card) => (_jsx("div", { className: "min-h-screen flex items-center justify-center bg-background px-4", children: card }));
21
21
  const AuthShellContext = createContext(null);
@@ -6,7 +6,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
6
6
  // ThemeToggle → UserMenu (rechts). Apps mit eigener Anordnung
7
7
  // importieren weiter die Einzelkomponenten direkt — DefaultTopbarActions
8
8
  // ist Convenience, kein Muss.
9
- import { ThemeToggle } from "@cosmicdrift/kumiko-renderer-web";
9
+ import { ThemeToggle } from "@cosmicdrift/kumiko-renderer/web";
10
10
  import { TenantSwitcher } from "./tenant-switcher.js";
11
11
  import { UserMenu } from "./user-menu.js";
12
12
  export function DefaultTopbarActions({ tenantName, extras, lightIcon, darkIcon, } = {}) {
@@ -3,7 +3,7 @@
3
3
  // Wird über den Sub-Path-Export `@cosmicdrift/kumiko-bundled-features/auth-email-
4
4
  // password/web` konsumiert — die Server-Seite (defineFeature) lebt in
5
5
  // `@cosmicdrift/kumiko-bundled-features/auth-email-password` und hat keine
6
- // React-/DOM-Deps. Trennung bleibt sauber so wie renderer vs renderer-web.
6
+ // React-/DOM-Deps. Trennung bleibt sauber so wie renderer vs renderer/web.
7
7
  export { defaultTranslations, mergeTranslations } from "../i18n.js";
8
8
  export { AuthRequestError, confirmAccountUnlock, confirmSignup, requestAccountUnlock, requestEmailVerification, requestPasswordReset, requestSignup, resetPassword, toLoginResponse, verifyEmail, } from "./auth-client.js";
9
9
  export { AuthCard, AuthShellProvider, useAuthShell } from "./auth-form-primitives.js";
@@ -9,8 +9,8 @@ import { jsx as _jsx } from "react/jsx-runtime";
9
9
  // eine Dependency weniger im Browser-Bundle haben. Die Consumer leben
10
10
  // unter `<SessionProvider>`; der `useSession()`-Hook liefert den State
11
11
  // und die Transitions.
12
- import { readCsrfToken } from "@cosmicdrift/kumiko-dispatcher-live";
13
12
  import { CurrentUserProvider, useSessionEndedSignal, } from "@cosmicdrift/kumiko-renderer";
13
+ import { readCsrfToken } from "@cosmicdrift/kumiko-renderer/live";
14
14
  import { createContext, useCallback, useContext, useEffect, useMemo, useState, } from "react";
15
15
  import { AuthRequestError, fetchCurrentUser, fetchTenants, login as loginApi, logout as logoutApi, switchTenant as switchTenantApi, } from "./auth-client.js";
16
16
  // Exported so screens that render on BOTH authenticated and anonymous public
@@ -3,7 +3,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
3
3
  // Tenant choices as dropdown entries — the TenantSwitcher's list for a user
4
4
  // menu that carries the shell controls. Renders nothing for a single tenant.
5
5
  import { useTranslation } from "@cosmicdrift/kumiko-renderer";
6
- import { DropdownMenuCheckboxItem, DropdownMenuLabel, DropdownMenuSeparator, } from "@cosmicdrift/kumiko-renderer-web";
6
+ import { DropdownMenuCheckboxItem, DropdownMenuLabel, DropdownMenuSeparator, } from "@cosmicdrift/kumiko-renderer/web";
7
7
  import { useSession } from "./session.js";
8
8
  export function TenantMenuItems({ tenantName }) {
9
9
  const t = useTranslation();
@@ -15,7 +15,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
15
15
  // der Host den Resolver rein (kommt z.B. aus einer useTenantNames()-
16
16
  // Hook die einmalig am App-Boot geladen wird).
17
17
  import { useTranslation } from "@cosmicdrift/kumiko-renderer";
18
- import { cn, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuLabel, DropdownMenuTrigger, } from "@cosmicdrift/kumiko-renderer-web";
18
+ import { cn, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuLabel, DropdownMenuTrigger, } from "@cosmicdrift/kumiko-renderer/web";
19
19
  import { Building2, ChevronDown } from "lucide-react";
20
20
  import { useCallback, useState } from "react";
21
21
  import { useSession } from "./session.js";
@@ -10,7 +10,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
10
10
  // Component außerhalb des AuthGate einhängen ohne dass ein harter
11
11
  // Fehler entsteht.
12
12
  import { useTranslation } from "@cosmicdrift/kumiko-renderer";
13
- import { cn, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, SidebarMenu, SidebarMenuButton, SidebarMenuItem, } from "@cosmicdrift/kumiko-renderer-web";
13
+ import { cn, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, SidebarMenu, SidebarMenuButton, SidebarMenuItem, } from "@cosmicdrift/kumiko-renderer/web";
14
14
  import { ChevronDown, ChevronsUpDown, LogOut } from "lucide-react";
15
15
  import { useSession } from "./session.js";
16
16
  function initials(value) {
@@ -0,0 +1,9 @@
1
+ import * as z from "zod";
2
+ /** Env block behind `auth.admin` and `auth.mail`: the first admin login and the public app URL
3
+ * the mail links and the Origin guard derive from. Unset keys leave the matching option to
4
+ * explicit values; an app spreads `.shape` into its env schema and passes the parsed values on. */
5
+ export declare const authEnvSchema: z.ZodObject<{
6
+ APP_BASE_URL: z.ZodOptional<z.ZodURL>;
7
+ AUTH_ADMIN_EMAIL: z.ZodOptional<z.ZodEmail>;
8
+ AUTH_ADMIN_PASSWORD: z.ZodOptional<z.ZodString>;
9
+ }, z.core.$strip>;
@@ -0,0 +1,21 @@
1
+ import * as z from "zod";
2
+ const ADMIN_PASSWORD_MIN_LENGTH = 16;
3
+ /** Env block behind `auth.admin` and `auth.mail`: the first admin login and the public app URL
4
+ * the mail links and the Origin guard derive from. Unset keys leave the matching option to
5
+ * explicit values; an app spreads `.shape` into its env schema and passes the parsed values on. */
6
+ export const authEnvSchema = z.object({
7
+ APP_BASE_URL: z
8
+ .url()
9
+ .optional()
10
+ .describe("Public https URL of the app; auth mail links, the Origin guard and unsubscribe links use it."),
11
+ AUTH_ADMIN_EMAIL: z
12
+ .email()
13
+ .optional()
14
+ .describe("Login of the admin seeded through `auth.admin`."),
15
+ AUTH_ADMIN_PASSWORD: z
16
+ .string()
17
+ .min(ADMIN_PASSWORD_MIN_LENGTH)
18
+ .optional()
19
+ .describe("Password of the admin seeded through `auth.admin`. Generate with: openssl rand -hex 24")
20
+ .meta({ kumiko: { pulumi: { generator: "openssl rand -hex 24", secret: true } } }),
21
+ });
@@ -1,3 +1,4 @@
1
+ export { authEnvSchema } from "./env.ts";
1
2
  export { createAuthFoundationFeature, type ResolvedTenantResolver, resolveSessionStore, resolveTenantExistence, resolveTenantResolver, resolveTokenVerifier, } from "./feature.ts";
2
3
  export { resolveAnonymousAccessFromRegistry } from "./resolve-anonymous-access.ts";
3
4
  export { type AuthProviderBuildDeps, type AuthProviderPlugin, type AuthVerifier, EXT_SESSION_STORE, EXT_TENANT_EXISTENCE, EXT_TENANT_RESOLVER, EXT_TOKEN_VERIFIER, isAuthProviderPlugin, isSessionStoreProvider, isTenantExistenceProvider, isTenantResolverProvider, isTenantResolverTrust, isValidTokenShape, type SessionMassRevoker, type SessionStore, type SessionStoreProvider, type TenantExistenceProvider, type TenantExistsFn, type TenantResolverFn, type TenantResolverProvider, type TenantResolverTrust, type TokenShape, tokenShapeKey, tokenShapeMatches, } from "./types.ts";
@@ -1,4 +1,5 @@
1
1
  // Public API of the auth-foundation bundled-feature.
2
+ export { authEnvSchema } from "./env.js";
2
3
  export { createAuthFoundationFeature, resolveSessionStore, resolveTenantExistence, resolveTenantResolver, resolveTokenVerifier, } from "./feature.js";
3
4
  export { resolveAnonymousAccessFromRegistry } from "./resolve-anonymous-access.js";
4
5
  export { EXT_SESSION_STORE, EXT_TENANT_EXISTENCE, EXT_TENANT_RESOLVER, EXT_TOKEN_VERIFIER, isAuthProviderPlugin, isSessionStoreProvider, isTenantExistenceProvider, isTenantResolverProvider, isTenantResolverTrust, isValidTokenShape, tokenShapeKey, tokenShapeMatches, } from "./types.js";
@@ -13,7 +13,7 @@ export declare const userMfaEntity: import("@cosmicdrift/kumiko-types/fields").E
13
13
  readonly sensitive?: boolean;
14
14
  readonly writeOnly?: boolean;
15
15
  readonly readAsInstruction?: boolean;
16
- readonly format?: "email" | "url" | "phone" | "password";
16
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
17
17
  readonly default?: string;
18
18
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
19
19
  readonly multiline?: boolean | {
@@ -36,7 +36,7 @@ export declare const userMfaEntity: import("@cosmicdrift/kumiko-types/fields").E
36
36
  readonly sensitive?: boolean;
37
37
  readonly writeOnly?: boolean;
38
38
  readonly readAsInstruction?: boolean;
39
- readonly format?: "email" | "url" | "phone" | "password";
39
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
40
40
  readonly default?: string;
41
41
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
42
42
  readonly multiline?: boolean | {
@@ -59,7 +59,7 @@ export declare const userMfaEntity: import("@cosmicdrift/kumiko-types/fields").E
59
59
  readonly sensitive?: boolean;
60
60
  readonly writeOnly?: boolean;
61
61
  readonly readAsInstruction?: boolean;
62
- readonly format?: "email" | "url" | "phone" | "password";
62
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
63
63
  readonly default?: string;
64
64
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
65
65
  readonly multiline?: boolean | {
@@ -114,7 +114,7 @@ export declare const userMfaTable: import("@cosmicdrift/kumiko-framework/db").En
114
114
  readonly sensitive?: boolean;
115
115
  readonly writeOnly?: boolean;
116
116
  readonly readAsInstruction?: boolean;
117
- readonly format?: "email" | "url" | "phone" | "password";
117
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
118
118
  readonly default?: string;
119
119
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
120
120
  readonly multiline?: boolean | {
@@ -137,7 +137,7 @@ export declare const userMfaTable: import("@cosmicdrift/kumiko-framework/db").En
137
137
  readonly sensitive?: boolean;
138
138
  readonly writeOnly?: boolean;
139
139
  readonly readAsInstruction?: boolean;
140
- readonly format?: "email" | "url" | "phone" | "password";
140
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
141
141
  readonly default?: string;
142
142
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
143
143
  readonly multiline?: boolean | {
@@ -160,7 +160,7 @@ export declare const userMfaTable: import("@cosmicdrift/kumiko-framework/db").En
160
160
  readonly sensitive?: boolean;
161
161
  readonly writeOnly?: boolean;
162
162
  readonly readAsInstruction?: boolean;
163
- readonly format?: "email" | "url" | "phone" | "password";
163
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
164
164
  readonly default?: string;
165
165
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
166
166
  readonly multiline?: boolean | {
@@ -1,4 +1,4 @@
1
- import { escapeHtml, escapeHtmlAttr } from "@cosmicdrift/kumiko-headless";
1
+ import { escapeHtml, escapeHtmlAttr } from "@cosmicdrift/kumiko-renderer/headless";
2
2
  import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
3
3
  import { Marked } from "marked";
4
4
  import { isAllowedDocumentLinkHref, isDocumentPlaceholderHref, } from "../../template-resolver/index.js";
@@ -1,7 +1,7 @@
1
1
  // Public § 312k pages (/legal/kuendigen, /legal/cancel): form → review → confirm,
2
2
  // no JavaScript. Every echoed value goes through escapeHtml/escapeHtmlAttr.
3
3
  import { getSessionCookies } from "@cosmicdrift/kumiko-framework/api";
4
- import { escapeHtml, escapeHtmlAttr } from "@cosmicdrift/kumiko-headless";
4
+ import { escapeHtml, escapeHtmlAttr } from "@cosmicdrift/kumiko-renderer/headless";
5
5
  import { isOneOf } from "@cosmicdrift/kumiko-types/type-guards";
6
6
  import { getCookie } from "hono/cookie";
7
7
  import { redirectToCanonicalPath, securePageHeaders, slashVariantOf, wrapInLayout, } from "../../page-render/index.js";
@@ -0,0 +1,2 @@
1
+ /** Shared by boot validation and the panel (defense in depth); keep free of server imports. */
2
+ export declare function isContactCardHref(href: string): boolean;
@@ -0,0 +1,16 @@
1
+ // @runtime client
2
+ /** Shared by boot validation and the panel (defense in depth); keep free of server imports. */
3
+ export function isContactCardHref(href) {
4
+ if (href !== href.trim())
5
+ return false;
6
+ let url;
7
+ try {
8
+ url = new URL(href);
9
+ }
10
+ catch {
11
+ return false;
12
+ }
13
+ if (url.protocol === "https:")
14
+ return true;
15
+ return url.protocol === "mailto:" && url.pathname !== "";
16
+ }
@@ -13,7 +13,7 @@ export declare const subscriptionEntity: import("@cosmicdrift/kumiko-types/field
13
13
  readonly sensitive?: boolean;
14
14
  readonly writeOnly?: boolean;
15
15
  readonly readAsInstruction?: boolean;
16
- readonly format?: "email" | "url" | "phone" | "password";
16
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
17
17
  readonly default?: string;
18
18
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
19
19
  readonly multiline?: boolean | {
@@ -36,7 +36,7 @@ export declare const subscriptionEntity: import("@cosmicdrift/kumiko-types/field
36
36
  readonly sensitive?: boolean;
37
37
  readonly writeOnly?: boolean;
38
38
  readonly readAsInstruction?: boolean;
39
- readonly format?: "email" | "url" | "phone" | "password";
39
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
40
40
  readonly default?: string;
41
41
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
42
42
  readonly multiline?: boolean | {
@@ -59,7 +59,7 @@ export declare const subscriptionEntity: import("@cosmicdrift/kumiko-types/field
59
59
  readonly sensitive?: boolean;
60
60
  readonly writeOnly?: boolean;
61
61
  readonly readAsInstruction?: boolean;
62
- readonly format?: "email" | "url" | "phone" | "password";
62
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
63
63
  readonly default?: string;
64
64
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
65
65
  readonly multiline?: boolean | {
@@ -82,7 +82,7 @@ export declare const subscriptionEntity: import("@cosmicdrift/kumiko-types/field
82
82
  readonly sensitive?: boolean;
83
83
  readonly writeOnly?: boolean;
84
84
  readonly readAsInstruction?: boolean;
85
- readonly format?: "email" | "url" | "phone" | "password";
85
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
86
86
  readonly default?: string;
87
87
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
88
88
  readonly multiline?: boolean | {
@@ -105,7 +105,7 @@ export declare const subscriptionEntity: import("@cosmicdrift/kumiko-types/field
105
105
  readonly sensitive?: boolean;
106
106
  readonly writeOnly?: boolean;
107
107
  readonly readAsInstruction?: boolean;
108
- readonly format?: "email" | "url" | "phone" | "password";
108
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
109
109
  readonly default?: string;
110
110
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
111
111
  readonly multiline?: boolean | {
@@ -161,7 +161,7 @@ export declare const paymentEntity: import("@cosmicdrift/kumiko-types/fields").E
161
161
  readonly sensitive?: boolean;
162
162
  readonly writeOnly?: boolean;
163
163
  readonly readAsInstruction?: boolean;
164
- readonly format?: "email" | "url" | "phone" | "password";
164
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
165
165
  readonly default?: string;
166
166
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
167
167
  readonly multiline?: boolean | {
@@ -184,7 +184,7 @@ export declare const paymentEntity: import("@cosmicdrift/kumiko-types/fields").E
184
184
  readonly sensitive?: boolean;
185
185
  readonly writeOnly?: boolean;
186
186
  readonly readAsInstruction?: boolean;
187
- readonly format?: "email" | "url" | "phone" | "password";
187
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
188
188
  readonly default?: string;
189
189
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
190
190
  readonly multiline?: boolean | {
@@ -207,7 +207,7 @@ export declare const paymentEntity: import("@cosmicdrift/kumiko-types/fields").E
207
207
  readonly sensitive?: boolean;
208
208
  readonly writeOnly?: boolean;
209
209
  readonly readAsInstruction?: boolean;
210
- readonly format?: "email" | "url" | "phone" | "password";
210
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
211
211
  readonly default?: string;
212
212
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
213
213
  readonly multiline?: boolean | {
@@ -10,7 +10,7 @@ const billingPlansSchema = z.object({}).strict();
10
10
  export function createBillingPlansQuery(options, catalog) {
11
11
  return {
12
12
  name: "billing-plans",
13
- description: "Lists the purchasable plans with live price, benefits, the caller's current tier and which action (checkout | switch | current | unavailable | paymentPending) each plan offers.",
13
+ description: "Lists the purchasable plans with live price, benefits, the caller's current tier and which action (checkout | switch | current | unavailable | paymentPending) each plan offers, plus the optional price-less contact card.",
14
14
  schema: billingPlansSchema,
15
15
  access: { roles: catalog.viewRoles },
16
16
  handler: async (_query, ctx) => {
@@ -12,5 +12,5 @@ export { getSubscriptionForTenant, type SubscriptionView } from "./get-subscript
12
12
  export { paymentsProjectionTable, subscriptionsProjectionTable } from "./projection.ts";
13
13
  export { billingPlansPanel, createBillingPlansScreen } from "./screens.ts";
14
14
  export { createSubscriptionTierSync, effectiveTierFromSubscription, SUBSCRIPTION_WEBHOOK_PATH, type SubscriptionTierSyncDeps, type TierSyncResult, } from "./subscription-tier-sync.ts";
15
- export { type BillingFoundationOptions, type BillingPlanBenefit, type BillingPlanCatalog, type BillingPlanPrice, type BillingPlansResult, type BillingPlanView, type ConsumerProtectionOptions, KNOWN_RECURRING_INTERVALS, type LegalLinkSet, type PaymentEvent, type ProviderPrice, type ProviderSubscriptionSnapshot, type RecurringInterval, type SubscriptionEvent, type SubscriptionProviderPlugin, } from "./types.ts";
15
+ export { type BillingFoundationOptions, type BillingPlanBenefit, type BillingPlanCatalog, type BillingPlanContactCard, type BillingPlanPrice, type BillingPlansResult, type BillingPlanView, type ConsumerProtectionOptions, KNOWN_RECURRING_INTERVALS, type LegalLinkSet, type PaymentEvent, type ProviderPrice, type ProviderSubscriptionSnapshot, type RecurringInterval, type SubscriptionEvent, type SubscriptionProviderPlugin, } from "./types.ts";
16
16
  export { createSubscriptionWebhookRoute, type SubscriptionWebhookRouteOptions, } from "./webhook-handler.ts";
@@ -159,5 +159,6 @@ export async function buildBillingPlans(ctx, plugin, catalog, now) {
159
159
  subscription,
160
160
  canPurchase,
161
161
  plans,
162
+ ...(catalog.contact !== undefined && { contact: catalog.contact }),
162
163
  };
163
164
  }
@@ -13,7 +13,7 @@ export declare const subscriptionsProjectionTable: import("@cosmicdrift/kumiko-f
13
13
  readonly sensitive?: boolean;
14
14
  readonly writeOnly?: boolean;
15
15
  readonly readAsInstruction?: boolean;
16
- readonly format?: "email" | "url" | "phone" | "password";
16
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
17
17
  readonly default?: string;
18
18
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
19
19
  readonly multiline?: boolean | {
@@ -36,7 +36,7 @@ export declare const subscriptionsProjectionTable: import("@cosmicdrift/kumiko-f
36
36
  readonly sensitive?: boolean;
37
37
  readonly writeOnly?: boolean;
38
38
  readonly readAsInstruction?: boolean;
39
- readonly format?: "email" | "url" | "phone" | "password";
39
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
40
40
  readonly default?: string;
41
41
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
42
42
  readonly multiline?: boolean | {
@@ -59,7 +59,7 @@ export declare const subscriptionsProjectionTable: import("@cosmicdrift/kumiko-f
59
59
  readonly sensitive?: boolean;
60
60
  readonly writeOnly?: boolean;
61
61
  readonly readAsInstruction?: boolean;
62
- readonly format?: "email" | "url" | "phone" | "password";
62
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
63
63
  readonly default?: string;
64
64
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
65
65
  readonly multiline?: boolean | {
@@ -82,7 +82,7 @@ export declare const subscriptionsProjectionTable: import("@cosmicdrift/kumiko-f
82
82
  readonly sensitive?: boolean;
83
83
  readonly writeOnly?: boolean;
84
84
  readonly readAsInstruction?: boolean;
85
- readonly format?: "email" | "url" | "phone" | "password";
85
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
86
86
  readonly default?: string;
87
87
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
88
88
  readonly multiline?: boolean | {
@@ -105,7 +105,7 @@ export declare const subscriptionsProjectionTable: import("@cosmicdrift/kumiko-f
105
105
  readonly sensitive?: boolean;
106
106
  readonly writeOnly?: boolean;
107
107
  readonly readAsInstruction?: boolean;
108
- readonly format?: "email" | "url" | "phone" | "password";
108
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
109
109
  readonly default?: string;
110
110
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
111
111
  readonly multiline?: boolean | {
@@ -160,7 +160,7 @@ export declare const paymentsProjectionTable: import("@cosmicdrift/kumiko-framew
160
160
  readonly sensitive?: boolean;
161
161
  readonly writeOnly?: boolean;
162
162
  readonly readAsInstruction?: boolean;
163
- readonly format?: "email" | "url" | "phone" | "password";
163
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
164
164
  readonly default?: string;
165
165
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
166
166
  readonly multiline?: boolean | {
@@ -183,7 +183,7 @@ export declare const paymentsProjectionTable: import("@cosmicdrift/kumiko-framew
183
183
  readonly sensitive?: boolean;
184
184
  readonly writeOnly?: boolean;
185
185
  readonly readAsInstruction?: boolean;
186
- readonly format?: "email" | "url" | "phone" | "password";
186
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
187
187
  readonly default?: string;
188
188
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
189
189
  readonly multiline?: boolean | {
@@ -206,7 +206,7 @@ export declare const paymentsProjectionTable: import("@cosmicdrift/kumiko-framew
206
206
  readonly sensitive?: boolean;
207
207
  readonly writeOnly?: boolean;
208
208
  readonly readAsInstruction?: boolean;
209
- readonly format?: "email" | "url" | "phone" | "password";
209
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
210
210
  readonly default?: string;
211
211
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
212
212
  readonly multiline?: boolean | {
@@ -259,6 +259,13 @@ export type BillingPlanBenefit = {
259
259
  readonly labelKey: string;
260
260
  readonly params?: Readonly<Record<string, string | number>>;
261
261
  };
262
+ /** Price-less card (e.g. "Enterprise · Contact us"); `href` must be an absolute https: or mailto: URL. */
263
+ export type BillingPlanContactCard = {
264
+ readonly labelKey: string;
265
+ readonly benefits: readonly BillingPlanBenefit[];
266
+ readonly actionLabelKey: string;
267
+ readonly href: string;
268
+ };
262
269
  export type BillingPlanCatalog<TTier extends string = string> = {
263
270
  /** Purchasable tiers, in display order. */
264
271
  readonly plans: readonly TTier[];
@@ -280,6 +287,8 @@ export type BillingPlanCatalog<TTier extends string = string> = {
280
287
  readonly returnPath?: string;
281
288
  /** Defaults to the only registered provider exposing `priceToTier`. */
282
289
  readonly providerName?: string;
290
+ /** Rendered as the last card, no price, visible to `viewRoles` regardless of `purchaseRoles`. */
291
+ readonly contact?: BillingPlanContactCard;
283
292
  };
284
293
  export type LegalLinkSet = {
285
294
  readonly terms: string;
@@ -368,6 +377,8 @@ export type BillingPlansResult = {
368
377
  } | null;
369
378
  readonly canPurchase: boolean;
370
379
  readonly plans: readonly BillingPlanView[];
380
+ /** Present only when `catalog.contact` is configured. */
381
+ readonly contact?: BillingPlanContactCard;
371
382
  /** Present only when `consumerProtection` is configured. */
372
383
  readonly consumerProtection?: {
373
384
  readonly consentTexts: Readonly<Record<"de" | "en", {
@@ -5,6 +5,7 @@
5
5
  import * as z from "zod";
6
6
  import { isTerminationScope, TERMINATION_SCOPES } from "./constants.js";
7
7
  import { CONSENT_LOCALES } from "./consumer-protection/consent-locale.js";
8
+ import { isContactCardHref } from "./contact-card.js";
8
9
  import { isLegalLinkSet, } from "./types.js";
9
10
  function isRootRelativePath(path) {
10
11
  return path.startsWith("/") && !path.startsWith("//");
@@ -60,6 +61,16 @@ function validateLegalLinkSet(set, path) {
60
61
  }
61
62
  }
62
63
  }
64
+ function validateContactCard(contact) {
65
+ for (const key of ["labelKey", "actionLabelKey"]) {
66
+ if (contact[key].trim() === "") {
67
+ throw new Error(`createBillingFoundationFeature: catalog.contact.${key} must not be empty.`);
68
+ }
69
+ }
70
+ if (!isContactCardHref(contact.href)) {
71
+ throw new Error(`createBillingFoundationFeature: catalog.contact.href "${contact.href}" must be an absolute https: or mailto: URL.`);
72
+ }
73
+ }
63
74
  function validateBaseUrl(baseUrl) {
64
75
  let parsed;
65
76
  try {
@@ -114,4 +125,6 @@ export function validateOptions(options) {
114
125
  if (catalog.viewRoles.length === 0) {
115
126
  throw new Error("createBillingFoundationFeature: catalog.viewRoles must not be empty.");
116
127
  }
128
+ if (catalog.contact !== undefined)
129
+ validateContactCard(catalog.contact);
117
130
  }
@@ -0,0 +1,5 @@
1
+ import type { ReactNode } from "react";
2
+ import type { BillingPlanContactCard } from "../types.ts";
3
+ export declare function BillingContactPlan({ contact, }: {
4
+ readonly contact: BillingPlanContactCard;
5
+ }): ReactNode;
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // @runtime client
3
+ import { useLocale, useTranslation } from "@cosmicdrift/kumiko-renderer";
4
+ import { PlanCard } from "@cosmicdrift/kumiko-renderer/web";
5
+ import { isContactCardHref } from "../contact-card.js";
6
+ import { localizedParams } from "./localized-params.js";
7
+ export function BillingContactPlan({ contact, }) {
8
+ const t = useTranslation();
9
+ const locale = useLocale().locale();
10
+ if (!isContactCardHref(contact.href))
11
+ return null;
12
+ return (_jsx(PlanCard, { title: t(contact.labelKey), features: contact.benefits.map((benefit) => t(benefit.labelKey, localizedParams(benefit.params, locale))), ctaLink: {
13
+ label: t(contact.actionLabelKey),
14
+ href: contact.href,
15
+ target: new URL(contact.href).protocol === "https:" ? "_blank" : undefined,
16
+ }, testId: "billing-plan-card-contact" }));
17
+ }
@@ -1,12 +1,14 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // @runtime client
3
- import { toInstant } from "@cosmicdrift/kumiko-headless";
4
3
  import { useLocale, useMutation, usePrimitives, useQuery, useTranslation, } from "@cosmicdrift/kumiko-renderer";
5
- import { formatMoney, PlanCard, PlanGrid, } from "@cosmicdrift/kumiko-renderer-web";
4
+ import { toInstant } from "@cosmicdrift/kumiko-renderer/headless";
5
+ import { formatMoney, PlanCard, PlanGrid, } from "@cosmicdrift/kumiko-renderer/web";
6
6
  import { useState } from "react";
7
7
  import { BillingPlanActions, SubscriptionFoundationHandlers, SubscriptionFoundationQueries, SubscriptionStatuses, } from "../constants.js";
8
+ import { BillingContactPlan } from "./billing-contact-plan.js";
8
9
  import { CancelContractDialog } from "./cancel-contract-dialog.js";
9
10
  import { CheckoutConsentDialog } from "./checkout-consent-dialog.js";
11
+ import { localizedParams } from "./localized-params.js";
10
12
  function intervalKey(price) {
11
13
  if (price === null || price.interval === null)
12
14
  return undefined;
@@ -21,16 +23,6 @@ function renewalKey(price) {
21
23
  ? `billing-foundation.consent.renewsEvery.${price.interval}`
22
24
  : `billing-foundation.consent.renews.${price.interval}`;
23
25
  }
24
- function localizedParams(params, locale) {
25
- if (params === undefined)
26
- return undefined;
27
- const localized = {};
28
- for (const [key, value] of Object.entries(params)) {
29
- localized[key] =
30
- typeof value === "number" ? new Intl.NumberFormat(locale).format(value) : value;
31
- }
32
- return localized;
33
- }
34
26
  /** Which action the plan "would" offer if it weren't blocked — mirrors
35
27
  * plan-catalog.ts's own decision tree, since `unavailable` collapses both
36
28
  * "would checkout" and "would switch" into one action and the disabled CTA
@@ -189,7 +181,7 @@ export function BillingPlansPanel(_props) {
189
181
  date: toInstant(result.subscription.cancelAt).toLocaleString(locale, {
190
182
  dateStyle: "medium",
191
183
  }),
192
- }), " ", t("billing-foundation.plans.switchRequiresReactivation")] })), consumerProtection !== undefined && canManage && result.subscription?.cancelAt == null && (_jsx("div", { children: _jsx(Button, { variant: "secondary", onClick: () => setCancelOpen(true), testId: "billing-plans-cancel-contract", children: t("billing-foundation.cancel.open") }) })), _jsxs(PlanGrid, { testId: "billing-plans-grid", children: [!currentTierInPlans && (_jsx(PlanCard, { title: t(result.currentTier.labelKey), current: true, testId: "billing-plan-card-current", features: result.currentTier.benefits.map((benefit) => t(benefit.labelKey, localizedParams(benefit.params, locale))), secondaryAction: manageOrReactivateAction(canManage, result.subscription, t, handleManage, redirecting) })), visiblePlans.map((plan) => (_jsx(PlanCard, { testId: `billing-plan-card-${plan.tier}`, ...toCardProps(plan) }, plan.tier)))] }), consumerProtection !== undefined &&
184
+ }), " ", t("billing-foundation.plans.switchRequiresReactivation")] })), consumerProtection !== undefined && canManage && result.subscription?.cancelAt == null && (_jsx("div", { children: _jsx(Button, { variant: "secondary", onClick: () => setCancelOpen(true), testId: "billing-plans-cancel-contract", children: t("billing-foundation.cancel.open") }) })), _jsxs(PlanGrid, { testId: "billing-plans-grid", children: [!currentTierInPlans && (_jsx(PlanCard, { title: t(result.currentTier.labelKey), current: true, testId: "billing-plan-card-current", features: result.currentTier.benefits.map((benefit) => t(benefit.labelKey, localizedParams(benefit.params, locale))), secondaryAction: manageOrReactivateAction(canManage, result.subscription, t, handleManage, redirecting) })), visiblePlans.map((plan) => (_jsx(PlanCard, { testId: `billing-plan-card-${plan.tier}`, ...toCardProps(plan) }, plan.tier))), result.contact !== undefined && _jsx(BillingContactPlan, { contact: result.contact })] }), consumerProtection !== undefined &&
193
185
  consentPlan !== undefined &&
194
186
  consentPlan.price !== null && (_jsx(CheckoutConsentDialog, { open: true, onOpenChange: (open) => {
195
187
  if (!open)
@@ -1,8 +1,8 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // @runtime client
3
- import { toInstant } from "@cosmicdrift/kumiko-headless";
4
3
  import { useLocale, useMutation, usePrimitives, useTranslation, } from "@cosmicdrift/kumiko-renderer";
5
- import { ModeSwitch } from "@cosmicdrift/kumiko-renderer-web";
4
+ import { toInstant } from "@cosmicdrift/kumiko-renderer/headless";
5
+ import { ModeSwitch } from "@cosmicdrift/kumiko-renderer/web";
6
6
  import { useState } from "react";
7
7
  import { SubscriptionFoundationHandlers } from "../constants.js";
8
8
  const DECLARATION_TYPES = [
@@ -1,4 +1,4 @@
1
- import type { WriteResult } from "@cosmicdrift/kumiko-headless";
1
+ import type { WriteResult } from "@cosmicdrift/kumiko-renderer/headless";
2
2
  import { type ReactNode } from "react";
3
3
  import type { BillingPlansResult } from "../types.ts";
4
4
  export declare const CONSENT_TEXT_OUTDATED_CODE = "consent_text_outdated";
@@ -1,4 +1,5 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // @runtime client
2
3
  import { useLocale, usePrimitives, useTranslation } from "@cosmicdrift/kumiko-renderer";
3
4
  import { useState } from "react";
4
5
  import { resolveConsentLocale } from "../consumer-protection/consent-locale.js";