@wildo-ai/saas-website 1.1.4 → 1.1.6

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 (265) hide show
  1. package/dist/esm/astro/blog-post-bridge.d.ts +23 -20
  2. package/dist/esm/astro/blog-post-bridge.d.ts.map +1 -1
  3. package/dist/esm/astro/blog-post-bridge.js +13 -6
  4. package/dist/esm/astro/blog-post-bridge.js.map +1 -1
  5. package/dist/esm/astro/blog-post-head.renderer.d.ts.map +1 -1
  6. package/dist/esm/astro/bridge-runtime.d.ts +55 -26
  7. package/dist/esm/astro/bridge-runtime.d.ts.map +1 -1
  8. package/dist/esm/astro/bridge-runtime.js +21 -7
  9. package/dist/esm/astro/bridge-runtime.js.map +1 -1
  10. package/dist/esm/astro/collect-expected-label-keys.d.ts.map +1 -1
  11. package/dist/esm/astro/headers-renderer.d.ts.map +1 -1
  12. package/dist/esm/astro/i18n-routing.helper.d.ts.map +1 -1
  13. package/dist/esm/astro/label-pack-loader.d.ts +4 -4
  14. package/dist/esm/astro/label-pack-loader.d.ts.map +1 -1
  15. package/dist/esm/astro/label-pack-loader.js +14 -4
  16. package/dist/esm/astro/label-pack-loader.js.map +1 -1
  17. package/dist/esm/astro/llms-txt-server.d.ts.map +1 -1
  18. package/dist/esm/astro/pick-labels-for-page.d.ts.map +1 -1
  19. package/dist/esm/astro/robots-renderer.d.ts +9 -8
  20. package/dist/esm/astro/robots-renderer.d.ts.map +1 -1
  21. package/dist/esm/astro/robots-renderer.js +15 -15
  22. package/dist/esm/astro/robots-renderer.js.map +1 -1
  23. package/dist/esm/astro/sitemap-coverage.d.ts.map +1 -1
  24. package/dist/esm/astro/structured-data.renderer.d.ts.map +1 -1
  25. package/dist/esm/astro/website-page-head.renderer.d.ts +1 -1
  26. package/dist/esm/astro/website-page-head.renderer.d.ts.map +1 -1
  27. package/dist/esm/astro/website-page-head.renderer.js +2 -1
  28. package/dist/esm/astro/website-page-head.renderer.js.map +1 -1
  29. package/dist/esm/astro/website-page-runtime.helper.d.ts.map +1 -1
  30. package/dist/esm/astro/website-site-context.d.ts +5 -2
  31. package/dist/esm/astro/website-site-context.d.ts.map +1 -1
  32. package/dist/esm/astro/website-site-context.js.map +1 -1
  33. package/dist/esm/astro-island.d.ts.map +1 -1
  34. package/dist/esm/astro.d.ts.map +1 -1
  35. package/dist/esm/companion-exports.d.ts +8 -15
  36. package/dist/esm/companion-exports.d.ts.map +1 -1
  37. package/dist/esm/companion-exports.js +8 -15
  38. package/dist/esm/companion-exports.js.map +1 -1
  39. package/dist/esm/components/low-level/WebsiteInternalButton.d.ts.map +1 -1
  40. package/dist/esm/components/low-level/WebsiteInternalLink.d.ts.map +1 -1
  41. package/dist/esm/config/define-website-config.d.ts +3 -2
  42. package/dist/esm/config/define-website-config.d.ts.map +1 -1
  43. package/dist/esm/config/define-website-config.js +10 -10
  44. package/dist/esm/config/define-website-config.js.map +1 -1
  45. package/dist/esm/config/index.d.ts.map +1 -1
  46. package/dist/esm/config/load-website-config.d.ts +6 -0
  47. package/dist/esm/config/load-website-config.d.ts.map +1 -1
  48. package/dist/esm/config/load-website-config.js +20 -10
  49. package/dist/esm/config/load-website-config.js.map +1 -1
  50. package/dist/esm/config/load-website-pricing-catalog.d.ts +13 -0
  51. package/dist/esm/config/load-website-pricing-catalog.d.ts.map +1 -0
  52. package/dist/esm/config/load-website-pricing-catalog.js +34 -0
  53. package/dist/esm/config/load-website-pricing-catalog.js.map +1 -0
  54. package/dist/esm/config/load-website-site-context.d.ts +53 -0
  55. package/dist/esm/config/load-website-site-context.d.ts.map +1 -0
  56. package/dist/esm/config/load-website-site-context.js +98 -0
  57. package/dist/esm/config/load-website-site-context.js.map +1 -0
  58. package/dist/esm/config/wildo-website-config.schemas.d.ts +10 -5
  59. package/dist/esm/config/wildo-website-config.schemas.d.ts.map +1 -1
  60. package/dist/esm/config/wildo-website-config.schemas.js +10 -5
  61. package/dist/esm/config/wildo-website-config.schemas.js.map +1 -1
  62. package/dist/esm/config-loader.d.ts +2 -0
  63. package/dist/esm/config-loader.d.ts.map +1 -1
  64. package/dist/esm/config-loader.js +2 -0
  65. package/dist/esm/config-loader.js.map +1 -1
  66. package/dist/esm/core/anonymous-session/InboundContactForm.d.ts +26 -6
  67. package/dist/esm/core/anonymous-session/InboundContactForm.d.ts.map +1 -1
  68. package/dist/esm/core/anonymous-session/InboundContactForm.js +51 -33
  69. package/dist/esm/core/anonymous-session/InboundContactForm.js.map +1 -1
  70. package/dist/esm/core/anonymous-session/inbound-contact-form.schema.d.ts +14 -3
  71. package/dist/esm/core/anonymous-session/inbound-contact-form.schema.d.ts.map +1 -1
  72. package/dist/esm/core/anonymous-session/inbound-contact-form.schema.js +28 -13
  73. package/dist/esm/core/anonymous-session/inbound-contact-form.schema.js.map +1 -1
  74. package/dist/esm/core/anonymous-session/website-anonymous-session-client.d.ts +24 -1
  75. package/dist/esm/core/anonymous-session/website-anonymous-session-client.d.ts.map +1 -1
  76. package/dist/esm/core/anonymous-session/website-anonymous-session-client.js +46 -3
  77. package/dist/esm/core/anonymous-session/website-anonymous-session-client.js.map +1 -1
  78. package/dist/esm/core/anonymous-session/website-submission-challenge.d.ts +36 -0
  79. package/dist/esm/core/anonymous-session/website-submission-challenge.d.ts.map +1 -0
  80. package/dist/esm/core/anonymous-session/website-submission-challenge.js +69 -0
  81. package/dist/esm/core/anonymous-session/website-submission-challenge.js.map +1 -0
  82. package/dist/esm/core/consent/WebsiteConsentBanner.d.ts +6 -0
  83. package/dist/esm/core/consent/WebsiteConsentBanner.d.ts.map +1 -0
  84. package/dist/esm/core/consent/WebsiteConsentBanner.js +129 -0
  85. package/dist/esm/core/consent/WebsiteConsentBanner.js.map +1 -0
  86. package/dist/esm/core/consent/WebsiteConsentContext.d.ts +85 -0
  87. package/dist/esm/core/consent/WebsiteConsentContext.d.ts.map +1 -0
  88. package/dist/esm/core/consent/WebsiteConsentContext.js +89 -0
  89. package/dist/esm/core/consent/WebsiteConsentContext.js.map +1 -0
  90. package/dist/esm/core/consent/WebsiteConsentGate.d.ts +27 -0
  91. package/dist/esm/core/consent/WebsiteConsentGate.d.ts.map +1 -0
  92. package/dist/esm/core/consent/WebsiteConsentGate.js +9 -0
  93. package/dist/esm/core/consent/WebsiteConsentGate.js.map +1 -0
  94. package/dist/esm/core/consent/useWebsiteConsent.d.ts +10 -0
  95. package/dist/esm/core/consent/useWebsiteConsent.d.ts.map +1 -0
  96. package/dist/esm/core/consent/useWebsiteConsent.js +17 -0
  97. package/dist/esm/core/consent/useWebsiteConsent.js.map +1 -0
  98. package/dist/esm/core/consent/website-consent-storage.d.ts +22 -0
  99. package/dist/esm/core/consent/website-consent-storage.d.ts.map +1 -0
  100. package/dist/esm/core/consent/website-consent-storage.js +102 -0
  101. package/dist/esm/core/consent/website-consent-storage.js.map +1 -0
  102. package/dist/esm/core/contexts/WebsitePageContext.d.ts.map +1 -1
  103. package/dist/esm/core/contexts/WebsitePageContext.js.map +1 -1
  104. package/dist/esm/core/contexts/WebsiteRuntimeContext.d.ts +33 -6
  105. package/dist/esm/core/contexts/WebsiteRuntimeContext.d.ts.map +1 -1
  106. package/dist/esm/core/contexts/WebsiteRuntimeContext.js.map +1 -1
  107. package/dist/esm/core/contexts/WebsiteSectionContext.d.ts.map +1 -1
  108. package/dist/esm/core/contexts/WebsiteSectionContext.js.map +1 -1
  109. package/dist/esm/core/contexts/useWebsitePage.d.ts.map +1 -1
  110. package/dist/esm/core/contexts/useWebsiteRuntime.d.ts +18 -0
  111. package/dist/esm/core/contexts/useWebsiteRuntime.d.ts.map +1 -1
  112. package/dist/esm/core/contexts/useWebsiteRuntime.js +28 -2
  113. package/dist/esm/core/contexts/useWebsiteRuntime.js.map +1 -1
  114. package/dist/esm/core/contexts/useWebsiteSection.d.ts.map +1 -1
  115. package/dist/esm/core/external-providers/WebsiteProviderComponent.d.ts +57 -0
  116. package/dist/esm/core/external-providers/WebsiteProviderComponent.d.ts.map +1 -0
  117. package/dist/esm/core/external-providers/WebsiteProviderComponent.js +56 -0
  118. package/dist/esm/core/external-providers/WebsiteProviderComponent.js.map +1 -0
  119. package/dist/esm/core/external-providers/frontend-provider-registry.website.d.ts +1 -2
  120. package/dist/esm/core/external-providers/frontend-provider-registry.website.d.ts.map +1 -1
  121. package/dist/esm/core/external-providers/frontend-provider-registry.website.js.map +1 -1
  122. package/dist/esm/core/external-providers/provider-component-registry.website.d.ts +63 -0
  123. package/dist/esm/core/external-providers/provider-component-registry.website.d.ts.map +1 -0
  124. package/dist/esm/core/external-providers/provider-component-registry.website.js +68 -0
  125. package/dist/esm/core/external-providers/provider-component-registry.website.js.map +1 -0
  126. package/dist/esm/core/external-providers/useWebsiteProviderComponent.d.ts +36 -0
  127. package/dist/esm/core/external-providers/useWebsiteProviderComponent.d.ts.map +1 -0
  128. package/dist/esm/core/external-providers/useWebsiteProviderComponent.js +59 -0
  129. package/dist/esm/core/external-providers/useWebsiteProviderComponent.js.map +1 -0
  130. package/dist/esm/core/external-providers/useWebsiteProviderScripts.d.ts +43 -10
  131. package/dist/esm/core/external-providers/useWebsiteProviderScripts.d.ts.map +1 -1
  132. package/dist/esm/core/external-providers/useWebsiteProviderScripts.js +92 -29
  133. package/dist/esm/core/external-providers/useWebsiteProviderScripts.js.map +1 -1
  134. package/dist/esm/core/external-providers/website-provider-sdk-activations.d.ts +11 -0
  135. package/dist/esm/core/external-providers/website-provider-sdk-activations.d.ts.map +1 -0
  136. package/dist/esm/core/external-providers/website-provider-sdk-activations.js +14 -0
  137. package/dist/esm/core/external-providers/website-provider-sdk-activations.js.map +1 -0
  138. package/dist/esm/core/factories/define-website-page-manifest.d.ts.map +1 -1
  139. package/dist/esm/core/factories/define-website-section.d.ts +3 -3
  140. package/dist/esm/core/factories/define-website-section.d.ts.map +1 -1
  141. package/dist/esm/core/factories/define-website-section.js +3 -3
  142. package/dist/esm/core/factories/define-website-section.js.map +1 -1
  143. package/dist/esm/core/hooks/useWebsiteDesignTokens.d.ts.map +1 -1
  144. package/dist/esm/core/hooks/useWebsiteLabel.d.ts.map +1 -1
  145. package/dist/esm/core/layouts/WebsitePageLayout.d.ts +21 -1
  146. package/dist/esm/core/layouts/WebsitePageLayout.d.ts.map +1 -1
  147. package/dist/esm/core/layouts/WebsitePageLayout.js +39 -8
  148. package/dist/esm/core/layouts/WebsitePageLayout.js.map +1 -1
  149. package/dist/esm/core/layouts/WebsiteSection.d.ts.map +1 -1
  150. package/dist/esm/core/routing/internal-routing.utils.d.ts.map +1 -1
  151. package/dist/esm/index.d.ts +36 -65
  152. package/dist/esm/index.d.ts.map +1 -1
  153. package/dist/esm/index.js +36 -65
  154. package/dist/esm/index.js.map +1 -1
  155. package/dist/esm/mdx/BlogPost.d.ts.map +1 -1
  156. package/dist/esm/mdx/blog-post-collection.config.d.ts.map +1 -1
  157. package/dist/esm/mdx/blog-post-frontmatter-source.d.ts.map +1 -1
  158. package/dist/esm/mdx/index.d.ts.map +1 -1
  159. package/dist/esm/mdx.d.ts.map +1 -1
  160. package/dist/esm/schemas/blog/blog-post-frontmatter.shared.schemas.d.ts.map +1 -1
  161. package/dist/esm/schemas/design-tokens/website-design-tokens.shared.schemas.d.ts +35 -6
  162. package/dist/esm/schemas/design-tokens/website-design-tokens.shared.schemas.d.ts.map +1 -1
  163. package/dist/esm/schemas/design-tokens/website-design-tokens.shared.schemas.js +39 -5
  164. package/dist/esm/schemas/design-tokens/website-design-tokens.shared.schemas.js.map +1 -1
  165. package/dist/esm/schemas/label-keys/website-consent-label-keys.schemas.d.ts +61 -0
  166. package/dist/esm/schemas/label-keys/website-consent-label-keys.schemas.d.ts.map +1 -0
  167. package/dist/esm/schemas/label-keys/website-consent-label-keys.schemas.js +73 -0
  168. package/dist/esm/schemas/label-keys/website-consent-label-keys.schemas.js.map +1 -0
  169. package/dist/esm/schemas/label-keys/website-label-key.schemas.d.ts.map +1 -1
  170. package/dist/esm/schemas/label-keys/website-provider-component-label-keys.schemas.d.ts +32 -0
  171. package/dist/esm/schemas/label-keys/website-provider-component-label-keys.schemas.d.ts.map +1 -0
  172. package/dist/esm/schemas/label-keys/website-provider-component-label-keys.schemas.js +36 -0
  173. package/dist/esm/schemas/label-keys/website-provider-component-label-keys.schemas.js.map +1 -0
  174. package/dist/esm/schemas/manifests/website-page-manifest.shared.schemas.d.ts.map +1 -1
  175. package/dist/esm/schemas/manifests/website-root-config.shared.schemas.d.ts +6 -5
  176. package/dist/esm/schemas/manifests/website-root-config.shared.schemas.d.ts.map +1 -1
  177. package/dist/esm/schemas/manifests/website-root-config.shared.schemas.js +8 -7
  178. package/dist/esm/schemas/manifests/website-root-config.shared.schemas.js.map +1 -1
  179. package/dist/esm/schemas/refs/page-ref.schemas.d.ts.map +1 -1
  180. package/dist/esm/schemas/refs/section-ref.schemas.d.ts.map +1 -1
  181. package/dist/esm/schemas/sections/website-section-category.shared.d.ts.map +1 -1
  182. package/dist/esm/schemas/sections/website-section-category.shared.js.map +1 -1
  183. package/dist/esm/schemas/sections/website-section-definition.shared.schemas.d.ts +14 -7
  184. package/dist/esm/schemas/sections/website-section-definition.shared.schemas.d.ts.map +1 -1
  185. package/dist/esm/schemas/sections/website-section-definition.shared.schemas.js +14 -7
  186. package/dist/esm/schemas/sections/website-section-definition.shared.schemas.js.map +1 -1
  187. package/dist/esm/schemas/structured-data/pricing-offer-structured-data.shared.d.ts +57 -0
  188. package/dist/esm/schemas/structured-data/pricing-offer-structured-data.shared.d.ts.map +1 -0
  189. package/dist/esm/schemas/structured-data/pricing-offer-structured-data.shared.js +76 -0
  190. package/dist/esm/schemas/structured-data/pricing-offer-structured-data.shared.js.map +1 -0
  191. package/dist/esm/schemas/structured-data/structured-data-reconciliation.shared.d.ts +23 -0
  192. package/dist/esm/schemas/structured-data/structured-data-reconciliation.shared.d.ts.map +1 -1
  193. package/dist/esm/schemas/structured-data/structured-data-reconciliation.shared.js +20 -2
  194. package/dist/esm/schemas/structured-data/structured-data-reconciliation.shared.js.map +1 -1
  195. package/dist/esm/schemas/structured-data/website-structured-data.shared.schemas.d.ts +7 -5
  196. package/dist/esm/schemas/structured-data/website-structured-data.shared.schemas.d.ts.map +1 -1
  197. package/dist/esm/schemas/structured-data/website-structured-data.shared.schemas.js.map +1 -1
  198. package/dist/esm/schemas/validators/label-pack.validator.d.ts.map +1 -1
  199. package/dist/tsconfig.build.tsbuildinfo +1 -1
  200. package/package.json +6 -29
  201. package/src/__tests__/bundle-isolation.test.ts +19 -22
  202. package/src/astro/__tests__/blog-post-bridge.test.tsx +58 -1
  203. package/src/astro/__tests__/bridge-runtime.test.tsx +44 -1
  204. package/src/astro/__tests__/label-pack-loader.test.ts +29 -1
  205. package/src/astro/blog-post-bridge.tsx +36 -26
  206. package/src/astro/bridge-runtime.tsx +79 -33
  207. package/src/astro/label-pack-loader.ts +12 -4
  208. package/src/astro/robots-renderer.ts +15 -15
  209. package/src/astro/website-page-head.renderer.ts +2 -2
  210. package/src/astro/website-site-context.ts +5 -2
  211. package/src/companion-exports.ts +8 -15
  212. package/src/config/__tests__/website-build-origins.test.ts +52 -0
  213. package/src/config/define-website-config.ts +10 -10
  214. package/src/config/load-website-config.ts +27 -15
  215. package/src/config/load-website-pricing-catalog.ts +52 -0
  216. package/src/config/load-website-site-context.ts +145 -0
  217. package/src/config/wildo-website-config.schemas.ts +10 -5
  218. package/src/config-loader.ts +2 -0
  219. package/src/core/__tests__/WebsitePageLayout.test.tsx +1 -1
  220. package/src/core/__tests__/contexts.test.tsx +20 -1
  221. package/src/core/__tests__/website-consent.test.tsx +312 -0
  222. package/src/core/anonymous-session/InboundContactForm.tsx +100 -45
  223. package/src/core/anonymous-session/__tests__/InboundContactForm.labels.test.tsx +108 -0
  224. package/src/core/anonymous-session/__tests__/InboundContactForm.submission-challenge.test.tsx +139 -0
  225. package/src/core/anonymous-session/__tests__/inbound-contact-form.schema.test.ts +11 -4
  226. package/src/core/anonymous-session/inbound-contact-form.schema.ts +30 -13
  227. package/src/core/anonymous-session/website-anonymous-session-client.ts +52 -3
  228. package/src/core/anonymous-session/website-submission-challenge.ts +99 -0
  229. package/src/core/consent/WebsiteConsentBanner.tsx +223 -0
  230. package/src/core/consent/WebsiteConsentContext.tsx +164 -0
  231. package/src/core/consent/WebsiteConsentGate.tsx +34 -0
  232. package/src/core/consent/useWebsiteConsent.ts +18 -0
  233. package/src/core/consent/website-consent-storage.ts +116 -0
  234. package/src/core/contexts/WebsiteRuntimeContext.tsx +33 -6
  235. package/src/core/contexts/useWebsiteRuntime.ts +33 -2
  236. package/src/core/external-providers/WebsiteProviderComponent.tsx +153 -0
  237. package/src/core/external-providers/__tests__/frontend-provider-registry.website.test.ts +4 -0
  238. package/src/core/external-providers/__tests__/website-provider-component.test.tsx +238 -0
  239. package/src/core/external-providers/__tests__/website-provider-sdks.test.tsx +72 -0
  240. package/src/core/external-providers/frontend-provider-registry.website.ts +1 -2
  241. package/src/core/external-providers/provider-component-registry.website.ts +115 -0
  242. package/src/core/external-providers/useWebsiteProviderComponent.ts +76 -0
  243. package/src/core/external-providers/useWebsiteProviderScripts.ts +114 -40
  244. package/src/core/external-providers/website-provider-sdk-activations.ts +23 -0
  245. package/src/core/factories/define-website-section.ts +3 -3
  246. package/src/core/layouts/WebsitePageLayout.tsx +75 -7
  247. package/src/index.ts +38 -65
  248. package/src/schemas/__tests__/design-token-css-property.test.ts +72 -0
  249. package/src/schemas/__tests__/pricing-offer-structured-data.test.ts +122 -0
  250. package/src/schemas/__tests__/structured-data-reconciliation.test.ts +35 -0
  251. package/src/schemas/design-tokens/website-design-tokens.shared.schemas.ts +45 -5
  252. package/src/schemas/label-keys/__tests__/scaffolded-consent-label-pack.parity.test.ts +83 -0
  253. package/src/schemas/label-keys/website-consent-label-keys.schemas.ts +78 -0
  254. package/src/schemas/label-keys/website-provider-component-label-keys.schemas.ts +40 -0
  255. package/src/schemas/manifests/website-root-config.shared.schemas.ts +10 -9
  256. package/src/schemas/sections/website-section-definition.shared.schemas.ts +14 -7
  257. package/src/schemas/structured-data/pricing-offer-structured-data.shared.ts +144 -0
  258. package/src/schemas/structured-data/structured-data-reconciliation.shared.ts +46 -1
  259. package/src/schemas/structured-data/website-structured-data.shared.schemas.ts +7 -5
  260. package/dist/esm/core/external-providers/provider-scripts.website.d.ts +0 -68
  261. package/dist/esm/core/external-providers/provider-scripts.website.d.ts.map +0 -1
  262. package/dist/esm/core/external-providers/provider-scripts.website.js +0 -99
  263. package/dist/esm/core/external-providers/provider-scripts.website.js.map +0 -1
  264. package/src/core/external-providers/__tests__/provider-scripts.website.test.ts +0 -146
  265. package/src/core/external-providers/provider-scripts.website.ts +0 -126
@@ -0,0 +1,89 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useCallback, useEffect, useMemo, useState } from 'react';
3
+ import { ANONYMOUS_CONSENT_RECORD_METHOD, ANONYMOUS_CONSENT_RECORD_PATH } from '@wildo-ai/saas-models/public-runtime';
4
+ import { readWebsiteVisitorConsent, subscribeWebsiteVisitorConsent, writeWebsiteVisitorConsent, } from './website-consent-storage.js';
5
+ /**
6
+ * @wildo_source:part:start saas.website.consent.context facet:layer:core facet:family:website
7
+ *
8
+ * `WebsiteConsentContext` — a website visitor's consent state (#534), provided once per page by
9
+ * `WebsitePageLayout`.
10
+ *
11
+ * Consent is a SET of `ConsentPurpose`s, each granted or not on its own. Nothing optional runs until its
12
+ * purpose is granted: the layout's provider SDKs and scripts read `grantedPurposes`, and so does
13
+ * `<WebsiteConsentGate purpose={…}>`.
14
+ *
15
+ * `purposesInUse` is DERIVED from the live providers (`consentPurposesInUseByProviders`), so the panel asks
16
+ * about exactly the purposes this site processes and nothing else. A site running no consent-based
17
+ * provider shows no panel at all.
18
+ */
19
+ export var WebsiteConsentStatus;
20
+ (function (WebsiteConsentStatus) {
21
+ /** The visitor has not decided — or their decision cannot be read. Nothing optional runs. */
22
+ WebsiteConsentStatus["UNDECIDED"] = "undecided";
23
+ /** The visitor decided; `grantedPurposes` is their answer, possibly empty. */
24
+ WebsiteConsentStatus["DECIDED"] = "decided";
25
+ })(WebsiteConsentStatus || (WebsiteConsentStatus = {}));
26
+ export const WebsiteConsentContext = createContext(null);
27
+ export function WebsiteConsentProvider({ purposesInUse, recorder, children }) {
28
+ const [record, setRecord] = useState(null);
29
+ const [isResolved, setIsResolved] = useState(false);
30
+ const [isSettingsOpen, setIsSettingsOpen] = useState(false);
31
+ // Read after mount, never during render: the static HTML was produced with no storage at all.
32
+ useEffect(() => {
33
+ setRecord(readWebsiteVisitorConsent());
34
+ setIsResolved(true);
35
+ return subscribeWebsiteVisitorConsent(() => setRecord(readWebsiteVisitorConsent()));
36
+ }, []);
37
+ const decide = useCallback((grantedPurposes) => {
38
+ /*
39
+ * Local storage FIRST, and it is what gates this page (D6). The site is statically built, so a
40
+ * refusal that only held while an API answered would not hold at all — and the visitor's panel
41
+ * must close on their click, not on a round trip.
42
+ */
43
+ setRecord(writeWebsiteVisitorConsent(grantedPurposes));
44
+ setIsSettingsOpen(false);
45
+ /*
46
+ * Then the DURABLE copy, best-effort. This is the one that survives the browser and is carried
47
+ * onto the account at signup; its failure changes nothing the visitor can see, so it is never
48
+ * awaited and never surfaced.
49
+ *
50
+ * `purposesInUse` is sent as the OFFERED set — what this panel actually put in front of them.
51
+ * Without it the application could not tell a declined purpose from one this site never asked
52
+ * about, and would record a decision the visitor never made
53
+ * (`AnonymousUserPrivacySchema.offeredPurposes`).
54
+ */
55
+ if (recorder !== undefined && purposesInUse.length > 0) {
56
+ void recorder
57
+ .requestAnonymous(ANONYMOUS_CONSENT_RECORD_METHOD, ANONYMOUS_CONSENT_RECORD_PATH, {
58
+ grantedPurposes: [...grantedPurposes],
59
+ offeredPurposes: [...purposesInUse],
60
+ })
61
+ .catch(() => {
62
+ /*
63
+ * Deliberately silent. The decision HELD — it is in this browser and gating this page. All
64
+ * that is lost is the carry, and telling a visitor their privacy choice failed when it did
65
+ * not would be worse than losing it.
66
+ */
67
+ });
68
+ }
69
+ }, [recorder, purposesInUse]);
70
+ const openSettings = useCallback(() => setIsSettingsOpen(true), []);
71
+ const closeSettings = useCallback(() => setIsSettingsOpen(false), []);
72
+ const value = useMemo(() => {
73
+ const grantedPurposes = record?.grantedPurposes ?? [];
74
+ return {
75
+ status: record === null ? WebsiteConsentStatus.UNDECIDED : WebsiteConsentStatus.DECIDED,
76
+ isResolved,
77
+ grantedPurposes,
78
+ ...(record !== null ? { decidedAt: record.decidedAt } : {}),
79
+ purposesInUse,
80
+ isSettingsOpen,
81
+ hasGranted: (purpose) => grantedPurposes.includes(purpose),
82
+ decide,
83
+ openSettings,
84
+ closeSettings,
85
+ };
86
+ }, [record, isResolved, purposesInUse, isSettingsOpen, decide, openSettings, closeSettings]);
87
+ return _jsx(WebsiteConsentContext.Provider, { value: value, children: children });
88
+ }
89
+ //# sourceMappingURL=WebsiteConsentContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebsiteConsentContext.js","sourceRoot":"","sources":["../../../../../src/core/consent/WebsiteConsentContext.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { createContext, useCallback, useEffect, useMemo, useState, type ReactNode } from 'react';\nimport { ANONYMOUS_CONSENT_RECORD_METHOD, ANONYMOUS_CONSENT_RECORD_PATH } from '@wildo-ai/saas-models/public-runtime';\nimport type { ConsentPurpose } from '@wildo-ai/saas-models/public-runtime';\n\nimport {\n readWebsiteVisitorConsent,\n subscribeWebsiteVisitorConsent,\n writeWebsiteVisitorConsent,\n type WebsiteVisitorConsentRecord,\n} from './website-consent-storage';\n\n/**\n * @wildo_source:part:start saas.website.consent.context facet:layer:core facet:family:website\n *\n * `WebsiteConsentContext` — a website visitor's consent state (#534), provided once per page by\n * `WebsitePageLayout`.\n *\n * Consent is a SET of `ConsentPurpose`s, each granted or not on its own. Nothing optional runs until its\n * purpose is granted: the layout's provider SDKs and scripts read `grantedPurposes`, and so does\n * `<WebsiteConsentGate purpose={…}>`.\n *\n * `purposesInUse` is DERIVED from the live providers (`consentPurposesInUseByProviders`), so the panel asks\n * about exactly the purposes this site processes and nothing else. A site running no consent-based\n * provider shows no panel at all.\n */\nexport enum WebsiteConsentStatus {\n /** The visitor has not decided — or their decision cannot be read. Nothing optional runs. */\n UNDECIDED = 'undecided',\n /** The visitor decided; `grantedPurposes` is their answer, possibly empty. */\n DECIDED = 'decided',\n}\n\nexport interface WebsiteConsentContextValue {\n status: WebsiteConsentStatus;\n /**\n * `false` during the server render and the first client render, before storage has been read. A\n * consent panel must not render while this is false: it would flash for a visitor who already decided,\n * and differ from the static HTML. Optional behaviour stays off either way.\n */\n isResolved: boolean;\n /** What the visitor granted. Empty while UNDECIDED. */\n grantedPurposes: readonly ConsentPurpose[];\n /** When the visitor decided, ISO-8601; absent while UNDECIDED. */\n decidedAt?: string;\n /** The purposes this site's live providers rest on — what a consent panel offers. */\n purposesInUse: readonly ConsentPurpose[];\n /** Whether the visitor re-opened the panel to change a decision already made. */\n isSettingsOpen: boolean;\n /** Whether `purpose` is granted. */\n hasGranted: (purpose: ConsentPurpose) => boolean;\n /** Record the WHOLE set the visitor grants — a purpose left out is not granted. Closes the panel. */\n decide: (grantedPurposes: readonly ConsentPurpose[]) => void;\n /** Re-open the panel so the visitor can change their decision. */\n openSettings: () => void;\n /** Close a re-opened panel without changing anything. */\n closeSettings: () => void;\n}\n\nexport const WebsiteConsentContext: React.Context<WebsiteConsentContextValue | null> = createContext<WebsiteConsentContextValue | null>(null);\n\n/**\n * The narrow slice of `WebsiteAnonymousSessionClient` this provider needs, declared structurally\n * rather than imported (#1258).\n *\n * Structural because the provider does not want the client's construction concerns — an `apiOrigin`\n * and the application's own `frontendServiceName`, which only the application knows — and because it\n * makes the durable write trivially substitutable in a test.\n */\nexport interface WebsiteConsentRecorder {\n /**\n * NOT generic, deliberately, though `WebsiteAnonymousSessionClient.requestAnonymous` is: this\n * provider ignores the response entirely, so a type parameter here would be a promise to the caller\n * that nothing keeps. The client's generic method satisfies this shape structurally.\n *\n * It takes the METHOD rather than being named after one. The consent door is a PUT — its operation\n * borrows `UPDATE_MANY` — and this interface previously named `postAnonymous`, so every decision a\n * visitor made 404'd, silently, by this provider's own (correct) design of never telling a visitor\n * their privacy choice failed. A door's method is part of its address, so it is passed like one.\n */\n requestAnonymous(method: string, path: string, body: unknown): Promise<unknown>;\n}\n\nexport interface WebsiteConsentProviderProps {\n purposesInUse: readonly ConsentPurpose[];\n /**\n * OPTIONAL. When present, a decision is ALSO recorded server-side against the visitor's anonymous\n * session, which is what lets it follow them into the account they go on to create (#1258).\n *\n * Absent is a first-class answer and is exactly today's behaviour: the decision lives in this\n * browser and nowhere else. A site with no anonymous-session wiring keeps working, gates the same\n * way, and simply carries nothing forward.\n */\n recorder?: WebsiteConsentRecorder;\n children: ReactNode;\n}\n\nexport function WebsiteConsentProvider({ purposesInUse, recorder, children }: WebsiteConsentProviderProps): ReactNode {\n const [record, setRecord] = useState<WebsiteVisitorConsentRecord | null>(null);\n const [isResolved, setIsResolved] = useState(false);\n const [isSettingsOpen, setIsSettingsOpen] = useState(false);\n\n // Read after mount, never during render: the static HTML was produced with no storage at all.\n useEffect(() => {\n setRecord(readWebsiteVisitorConsent());\n setIsResolved(true);\n return subscribeWebsiteVisitorConsent(() => setRecord(readWebsiteVisitorConsent()));\n }, []);\n\n const decide = useCallback((grantedPurposes: readonly ConsentPurpose[]): void => {\n /*\n * Local storage FIRST, and it is what gates this page (D6). The site is statically built, so a\n * refusal that only held while an API answered would not hold at all — and the visitor's panel\n * must close on their click, not on a round trip.\n */\n setRecord(writeWebsiteVisitorConsent(grantedPurposes));\n setIsSettingsOpen(false);\n\n /*\n * Then the DURABLE copy, best-effort. This is the one that survives the browser and is carried\n * onto the account at signup; its failure changes nothing the visitor can see, so it is never\n * awaited and never surfaced.\n *\n * `purposesInUse` is sent as the OFFERED set — what this panel actually put in front of them.\n * Without it the application could not tell a declined purpose from one this site never asked\n * about, and would record a decision the visitor never made\n * (`AnonymousUserPrivacySchema.offeredPurposes`).\n */\n if (recorder !== undefined && purposesInUse.length > 0) {\n void recorder\n .requestAnonymous(ANONYMOUS_CONSENT_RECORD_METHOD, ANONYMOUS_CONSENT_RECORD_PATH, {\n grantedPurposes: [...grantedPurposes],\n offeredPurposes: [...purposesInUse],\n })\n .catch(() => {\n /*\n * Deliberately silent. The decision HELD — it is in this browser and gating this page. All\n * that is lost is the carry, and telling a visitor their privacy choice failed when it did\n * not would be worse than losing it.\n */\n });\n }\n }, [recorder, purposesInUse]);\n const openSettings = useCallback((): void => setIsSettingsOpen(true), []);\n const closeSettings = useCallback((): void => setIsSettingsOpen(false), []);\n\n const value = useMemo<WebsiteConsentContextValue>(() => {\n const grantedPurposes = record?.grantedPurposes ?? [];\n return {\n status: record === null ? WebsiteConsentStatus.UNDECIDED : WebsiteConsentStatus.DECIDED,\n isResolved,\n grantedPurposes,\n ...(record !== null ? { decidedAt: record.decidedAt } : {}),\n purposesInUse,\n isSettingsOpen,\n hasGranted: (purpose) => grantedPurposes.includes(purpose),\n decide,\n openSettings,\n closeSettings,\n };\n }, [record, isResolved, purposesInUse, isSettingsOpen, decide, openSettings, closeSettings]);\n\n return <WebsiteConsentContext.Provider value={value}>{children}</WebsiteConsentContext.Provider>;\n}\n/** @wildo_source:part:end saas.website.consent.context */\n"]}
@@ -0,0 +1,27 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { ConsentPurpose } from '@wildo-ai/saas-models/public-runtime';
3
+ /**
4
+ * @wildo_source:part:start saas.website.consent.gate facet:layer:core facet:family:website
5
+ *
6
+ * `<WebsiteConsentGate>` — renders its children only when the visitor's consent allows it (#534).
7
+ *
8
+ * - **No `purpose`** — the content is ESSENTIAL and always renders. This is the right wrapper for a
9
+ * contact form: submitting a form the visitor chose to fill in never depends on optional consent, and the
10
+ * form's own opt-ins (marketing) are recorded with the submission, not here.
11
+ * - **`purpose`** — the content rests on that purpose (an embedded video that tracks, a chat widget, a
12
+ * marketing pixel). It renders only once the visitor granted it; until then `fallback` renders — a
13
+ * placeholder that says what is withheld and offers `useWebsiteConsent().openSettings()`.
14
+ *
15
+ * It renders `fallback` during the server render and before the stored decision is read, so a static page
16
+ * never ships consent-based content in its HTML.
17
+ */
18
+ export interface WebsiteConsentGateProps {
19
+ /** The purpose the children rest on. Omit for essential content. */
20
+ purpose?: ConsentPurpose;
21
+ /** Rendered in place of consent-based children until the purpose is granted. Defaults to nothing. */
22
+ fallback?: ReactNode;
23
+ children: ReactNode;
24
+ }
25
+ export declare function WebsiteConsentGate({ purpose, fallback, children }: WebsiteConsentGateProps): ReactNode;
26
+ /** @wildo_source:part:end saas.website.consent.gate */
27
+ //# sourceMappingURL=WebsiteConsentGate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebsiteConsentGate.d.ts","sourceRoot":"","sources":["../../../../../src/core/consent/WebsiteConsentGate.tsx"],"names":[],"mappings":"AAAA,OAAc,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AAI3E;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,uBAAuB;IACtC,oEAAoE;IACpE,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,qGAAqG;IACrG,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,OAAO,EAAE,QAAe,EAAE,QAAQ,EAAE,EAAE,uBAAuB,GAAG,SAAS,CAI7G;AACD,uDAAuD"}
@@ -0,0 +1,9 @@
1
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
+ import { useWebsiteConsent } from './useWebsiteConsent.js';
3
+ export function WebsiteConsentGate({ purpose, fallback = null, children }) {
4
+ const consent = useWebsiteConsent();
5
+ if (purpose === undefined)
6
+ return _jsx(_Fragment, { children: children });
7
+ return _jsx(_Fragment, { children: consent.isResolved && consent.hasGranted(purpose) ? children : fallback });
8
+ }
9
+ //# sourceMappingURL=WebsiteConsentGate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebsiteConsentGate.js","sourceRoot":"","sources":["../../../../../src/core/consent/WebsiteConsentGate.tsx"],"names":[],"mappings":";AAGA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAyBxD,MAAM,UAAU,kBAAkB,CAAC,EAAE,OAAO,EAAE,QAAQ,GAAG,IAAI,EAAE,QAAQ,EAA2B;IAChG,MAAM,OAAO,GAAG,iBAAiB,EAAE,CAAC;IACpC,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,4BAAG,QAAQ,GAAI,CAAC;IAClD,OAAO,4BAAG,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,GAAI,CAAC;AACxF,CAAC","sourcesContent":["import React, { type ReactNode } from 'react';\nimport type { ConsentPurpose } from '@wildo-ai/saas-models/public-runtime';\n\nimport { useWebsiteConsent } from './useWebsiteConsent';\n\n/**\n * @wildo_source:part:start saas.website.consent.gate facet:layer:core facet:family:website\n *\n * `<WebsiteConsentGate>` — renders its children only when the visitor's consent allows it (#534).\n *\n * - **No `purpose`** — the content is ESSENTIAL and always renders. This is the right wrapper for a\n * contact form: submitting a form the visitor chose to fill in never depends on optional consent, and the\n * form's own opt-ins (marketing) are recorded with the submission, not here.\n * - **`purpose`** — the content rests on that purpose (an embedded video that tracks, a chat widget, a\n * marketing pixel). It renders only once the visitor granted it; until then `fallback` renders — a\n * placeholder that says what is withheld and offers `useWebsiteConsent().openSettings()`.\n *\n * It renders `fallback` during the server render and before the stored decision is read, so a static page\n * never ships consent-based content in its HTML.\n */\nexport interface WebsiteConsentGateProps {\n /** The purpose the children rest on. Omit for essential content. */\n purpose?: ConsentPurpose;\n /** Rendered in place of consent-based children until the purpose is granted. Defaults to nothing. */\n fallback?: ReactNode;\n children: ReactNode;\n}\n\nexport function WebsiteConsentGate({ purpose, fallback = null, children }: WebsiteConsentGateProps): ReactNode {\n const consent = useWebsiteConsent();\n if (purpose === undefined) return <>{children}</>;\n return <>{consent.isResolved && consent.hasGranted(purpose) ? children : fallback}</>;\n}\n/** @wildo_source:part:end saas.website.consent.gate */\n"]}
@@ -0,0 +1,10 @@
1
+ import { type WebsiteConsentContextValue } from './WebsiteConsentContext';
2
+ /**
3
+ * The visitor's consent state (#534). Read this — never storage, never a cookie — to decide whether
4
+ * optional behaviour may run.
5
+ *
6
+ * Throws outside `WebsitePageLayout`, which provides it: a consumer that silently read "nothing granted"
7
+ * would look correct and hide a page mounted outside the layout.
8
+ */
9
+ export declare function useWebsiteConsent(): WebsiteConsentContextValue;
10
+ //# sourceMappingURL=useWebsiteConsent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useWebsiteConsent.d.ts","sourceRoot":"","sources":["../../../../../src/core/consent/useWebsiteConsent.ts"],"names":[],"mappings":"AAEA,OAAO,EAAyB,KAAK,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AAEjG;;;;;;GAMG;AACH,wBAAgB,iBAAiB,IAAI,0BAA0B,CAM9D"}
@@ -0,0 +1,17 @@
1
+ import { useContext } from 'react';
2
+ import { WebsiteConsentContext } from './WebsiteConsentContext.js';
3
+ /**
4
+ * The visitor's consent state (#534). Read this — never storage, never a cookie — to decide whether
5
+ * optional behaviour may run.
6
+ *
7
+ * Throws outside `WebsitePageLayout`, which provides it: a consumer that silently read "nothing granted"
8
+ * would look correct and hide a page mounted outside the layout.
9
+ */
10
+ export function useWebsiteConsent() {
11
+ const value = useContext(WebsiteConsentContext);
12
+ if (value === null) {
13
+ throw new Error('useWebsiteConsent must be used inside <WebsitePageLayout>, which provides the visitor consent state.');
14
+ }
15
+ return value;
16
+ }
17
+ //# sourceMappingURL=useWebsiteConsent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useWebsiteConsent.js","sourceRoot":"","sources":["../../../../../src/core/consent/useWebsiteConsent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAEnC,OAAO,EAAE,qBAAqB,EAAmC,MAAM,yBAAyB,CAAC;AAEjG;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB;IAC/B,MAAM,KAAK,GAAG,UAAU,CAAC,qBAAqB,CAAC,CAAC;IAChD,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,sGAAsG,CAAC,CAAC;IAC1H,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["import { useContext } from 'react';\n\nimport { WebsiteConsentContext, type WebsiteConsentContextValue } from './WebsiteConsentContext';\n\n/**\n * The visitor's consent state (#534). Read this — never storage, never a cookie — to decide whether\n * optional behaviour may run.\n *\n * Throws outside `WebsitePageLayout`, which provides it: a consumer that silently read \"nothing granted\"\n * would look correct and hide a page mounted outside the layout.\n */\nexport function useWebsiteConsent(): WebsiteConsentContextValue {\n const value = useContext(WebsiteConsentContext);\n if (value === null) {\n throw new Error('useWebsiteConsent must be used inside <WebsitePageLayout>, which provides the visitor consent state.');\n }\n return value;\n}\n"]}
@@ -0,0 +1,22 @@
1
+ import { ConsentPurpose } from '@wildo-ai/saas-models/public-runtime';
2
+ /** A visitor's decision, as the website runtime uses it. */
3
+ export interface WebsiteVisitorConsentRecord {
4
+ readonly grantedPurposes: readonly ConsentPurpose[];
5
+ /** When the visitor decided, ISO-8601. Its presence is what separates "declined everything" from "never asked". */
6
+ readonly decidedAt: string;
7
+ }
8
+ /**
9
+ * Dispatched on `window` after this document writes a decision, so every island on the page updates. The
10
+ * browser's own `storage` event covers OTHER documents only.
11
+ */
12
+ export declare const WEBSITE_CONSENT_CHANGED_EVENT = "wildo:website-consent-changed";
13
+ /** The stored decision, or `null` when there is none or it cannot be read. */
14
+ export declare function readWebsiteVisitorConsent(storage?: Storage | null): WebsiteVisitorConsentRecord | null;
15
+ /**
16
+ * Store a decision and tell the page. Returns whether it was stored: when storage is unavailable the
17
+ * decision still applies for this page, and the visitor is asked again on the next one.
18
+ */
19
+ export declare function writeWebsiteVisitorConsent(grantedPurposes: readonly ConsentPurpose[], now?: Date, storage?: Storage | null): WebsiteVisitorConsentRecord;
20
+ /** Calls `listener` whenever a decision changes in this document or in another tab. Returns the unsubscribe. */
21
+ export declare function subscribeWebsiteVisitorConsent(listener: () => void): () => void;
22
+ //# sourceMappingURL=website-consent-storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"website-consent-storage.d.ts","sourceRoot":"","sources":["../../../../../src/core/consent/website-consent-storage.ts"],"names":[],"mappings":"AACA,OAAO,EACL,cAAc,EAGf,MAAM,sCAAsC,CAAC;AA+B9C,4DAA4D;AAC5D,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,eAAe,EAAE,SAAS,cAAc,EAAE,CAAC;IACpD,mHAAmH;IACnH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED;;;GAGG;AACH,eAAO,MAAM,6BAA6B,kCAAkC,CAAC;AAW7E,8EAA8E;AAC9E,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,OAAO,GAAG,IAAuB,GAAG,2BAA2B,GAAG,IAAI,CAkBxH;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,eAAe,EAAE,SAAS,cAAc,EAAE,EAC1C,GAAG,GAAE,IAAiB,EACtB,OAAO,GAAE,OAAO,GAAG,IAAuB,GACzC,2BAA2B,CAc7B;AAED,gHAAgH;AAChH,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAW/E"}
@@ -0,0 +1,102 @@
1
+ import { z } from 'zod';
2
+ import { normalizeConsentPurposes, WEBSITE_VISITOR_CONSENT_STORAGE_KEY, } from '@wildo-ai/saas-models/public-runtime';
3
+ /**
4
+ * The ONE place a Wildo website reads or writes a visitor's consent decision (#534).
5
+ *
6
+ * Page and section components never touch storage for consent: they read `useWebsiteConsent()`. A second
7
+ * reader would be a second interpretation of the record — the defect the old `CONSENT_ORDER` was, one
8
+ * layer down.
9
+ *
10
+ * ## Why web storage and not a cookie
11
+ *
12
+ * Nothing on the server reads a visitor's consent — the marketing site is static — so a cookie would be
13
+ * sent on every request for no reader. Web storage stays in the browser. The key is declared in the
14
+ * engine's cookie table (`ENGINE_CLIENT_STORAGE_ENTRIES`) as strictly necessary: a refusal that is not
15
+ * remembered does not hold.
16
+ *
17
+ * ## Fail closed, never loud
18
+ *
19
+ * Storage can be absent (a server render), disabled (a privacy mode) or corrupt (a hand edit). Every one
20
+ * of those reads as "no decision": the visitor is asked, and nothing optional starts. A corrupt record is
21
+ * not repaired silently — it is ignored, and the next decision overwrites it.
22
+ */
23
+ /** Stored shape. `schemaVersion` so a future shape change can refuse an old record instead of misreading it. */
24
+ const StoredWebsiteVisitorConsentSchema = z.object({
25
+ schemaVersion: z.literal(1),
26
+ /** Strings, not the enum: a purpose later removed from the vocabulary must not invalidate the rest. */
27
+ grantedPurposes: z.array(z.string()),
28
+ decidedAt: z.iso.datetime(),
29
+ });
30
+ /**
31
+ * Dispatched on `window` after this document writes a decision, so every island on the page updates. The
32
+ * browser's own `storage` event covers OTHER documents only.
33
+ */
34
+ export const WEBSITE_CONSENT_CHANGED_EVENT = 'wildo:website-consent-changed';
35
+ function browserStorage() {
36
+ try {
37
+ return typeof window === 'undefined' ? null : window.localStorage;
38
+ }
39
+ catch {
40
+ // Accessing `localStorage` throws when the browser blocks site data.
41
+ return null;
42
+ }
43
+ }
44
+ /** The stored decision, or `null` when there is none or it cannot be read. */
45
+ export function readWebsiteVisitorConsent(storage = browserStorage()) {
46
+ if (storage === null)
47
+ return null;
48
+ let raw;
49
+ try {
50
+ raw = storage.getItem(WEBSITE_VISITOR_CONSENT_STORAGE_KEY);
51
+ }
52
+ catch {
53
+ return null;
54
+ }
55
+ if (raw === null)
56
+ return null;
57
+ let json;
58
+ try {
59
+ json = JSON.parse(raw);
60
+ }
61
+ catch {
62
+ return null;
63
+ }
64
+ const parsed = StoredWebsiteVisitorConsentSchema.safeParse(json);
65
+ if (!parsed.success)
66
+ return null;
67
+ return { grantedPurposes: normalizeConsentPurposes(parsed.data.grantedPurposes), decidedAt: parsed.data.decidedAt };
68
+ }
69
+ /**
70
+ * Store a decision and tell the page. Returns whether it was stored: when storage is unavailable the
71
+ * decision still applies for this page, and the visitor is asked again on the next one.
72
+ */
73
+ export function writeWebsiteVisitorConsent(grantedPurposes, now = new Date(), storage = browserStorage()) {
74
+ const record = { grantedPurposes: normalizeConsentPurposes(grantedPurposes), decidedAt: now.toISOString() };
75
+ if (storage !== null) {
76
+ try {
77
+ storage.setItem(WEBSITE_VISITOR_CONSENT_STORAGE_KEY, JSON.stringify({ schemaVersion: 1, grantedPurposes: record.grantedPurposes, decidedAt: record.decidedAt }));
78
+ }
79
+ catch {
80
+ // Quota or blocked storage: the decision holds for this page only.
81
+ }
82
+ }
83
+ if (typeof window !== 'undefined')
84
+ window.dispatchEvent(new CustomEvent(WEBSITE_CONSENT_CHANGED_EVENT));
85
+ return record;
86
+ }
87
+ /** Calls `listener` whenever a decision changes in this document or in another tab. Returns the unsubscribe. */
88
+ export function subscribeWebsiteVisitorConsent(listener) {
89
+ if (typeof window === 'undefined')
90
+ return () => { };
91
+ const onStorage = (event) => {
92
+ if (event.key === null || event.key === WEBSITE_VISITOR_CONSENT_STORAGE_KEY)
93
+ listener();
94
+ };
95
+ window.addEventListener(WEBSITE_CONSENT_CHANGED_EVENT, listener);
96
+ window.addEventListener('storage', onStorage);
97
+ return () => {
98
+ window.removeEventListener(WEBSITE_CONSENT_CHANGED_EVENT, listener);
99
+ window.removeEventListener('storage', onStorage);
100
+ };
101
+ }
102
+ //# sourceMappingURL=website-consent-storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"website-consent-storage.js","sourceRoot":"","sources":["../../../../../src/core/consent/website-consent-storage.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\nimport {\n ConsentPurpose,\n normalizeConsentPurposes,\n WEBSITE_VISITOR_CONSENT_STORAGE_KEY,\n} from '@wildo-ai/saas-models/public-runtime';\n\n/**\n * The ONE place a Wildo website reads or writes a visitor's consent decision (#534).\n *\n * Page and section components never touch storage for consent: they read `useWebsiteConsent()`. A second\n * reader would be a second interpretation of the record — the defect the old `CONSENT_ORDER` was, one\n * layer down.\n *\n * ## Why web storage and not a cookie\n *\n * Nothing on the server reads a visitor's consent — the marketing site is static — so a cookie would be\n * sent on every request for no reader. Web storage stays in the browser. The key is declared in the\n * engine's cookie table (`ENGINE_CLIENT_STORAGE_ENTRIES`) as strictly necessary: a refusal that is not\n * remembered does not hold.\n *\n * ## Fail closed, never loud\n *\n * Storage can be absent (a server render), disabled (a privacy mode) or corrupt (a hand edit). Every one\n * of those reads as \"no decision\": the visitor is asked, and nothing optional starts. A corrupt record is\n * not repaired silently — it is ignored, and the next decision overwrites it.\n */\n\n/** Stored shape. `schemaVersion` so a future shape change can refuse an old record instead of misreading it. */\nconst StoredWebsiteVisitorConsentSchema = z.object({\n schemaVersion: z.literal(1),\n /** Strings, not the enum: a purpose later removed from the vocabulary must not invalidate the rest. */\n grantedPurposes: z.array(z.string()),\n decidedAt: z.iso.datetime(),\n});\n\n/** A visitor's decision, as the website runtime uses it. */\nexport interface WebsiteVisitorConsentRecord {\n readonly grantedPurposes: readonly ConsentPurpose[];\n /** When the visitor decided, ISO-8601. Its presence is what separates \"declined everything\" from \"never asked\". */\n readonly decidedAt: string;\n}\n\n/**\n * Dispatched on `window` after this document writes a decision, so every island on the page updates. The\n * browser's own `storage` event covers OTHER documents only.\n */\nexport const WEBSITE_CONSENT_CHANGED_EVENT = 'wildo:website-consent-changed';\n\nfunction browserStorage(): Storage | null {\n try {\n return typeof window === 'undefined' ? null : window.localStorage;\n } catch {\n // Accessing `localStorage` throws when the browser blocks site data.\n return null;\n }\n}\n\n/** The stored decision, or `null` when there is none or it cannot be read. */\nexport function readWebsiteVisitorConsent(storage: Storage | null = browserStorage()): WebsiteVisitorConsentRecord | null {\n if (storage === null) return null;\n let raw: string | null;\n try {\n raw = storage.getItem(WEBSITE_VISITOR_CONSENT_STORAGE_KEY);\n } catch {\n return null;\n }\n if (raw === null) return null;\n let json: unknown;\n try {\n json = JSON.parse(raw);\n } catch {\n return null;\n }\n const parsed = StoredWebsiteVisitorConsentSchema.safeParse(json);\n if (!parsed.success) return null;\n return { grantedPurposes: normalizeConsentPurposes(parsed.data.grantedPurposes), decidedAt: parsed.data.decidedAt };\n}\n\n/**\n * Store a decision and tell the page. Returns whether it was stored: when storage is unavailable the\n * decision still applies for this page, and the visitor is asked again on the next one.\n */\nexport function writeWebsiteVisitorConsent(\n grantedPurposes: readonly ConsentPurpose[],\n now: Date = new Date(),\n storage: Storage | null = browserStorage(),\n): WebsiteVisitorConsentRecord {\n const record: WebsiteVisitorConsentRecord = { grantedPurposes: normalizeConsentPurposes(grantedPurposes), decidedAt: now.toISOString() };\n if (storage !== null) {\n try {\n storage.setItem(\n WEBSITE_VISITOR_CONSENT_STORAGE_KEY,\n JSON.stringify({ schemaVersion: 1, grantedPurposes: record.grantedPurposes, decidedAt: record.decidedAt }),\n );\n } catch {\n // Quota or blocked storage: the decision holds for this page only.\n }\n }\n if (typeof window !== 'undefined') window.dispatchEvent(new CustomEvent(WEBSITE_CONSENT_CHANGED_EVENT));\n return record;\n}\n\n/** Calls `listener` whenever a decision changes in this document or in another tab. Returns the unsubscribe. */\nexport function subscribeWebsiteVisitorConsent(listener: () => void): () => void {\n if (typeof window === 'undefined') return () => {};\n const onStorage = (event: StorageEvent): void => {\n if (event.key === null || event.key === WEBSITE_VISITOR_CONSENT_STORAGE_KEY) listener();\n };\n window.addEventListener(WEBSITE_CONSENT_CHANGED_EVENT, listener);\n window.addEventListener('storage', onStorage);\n return () => {\n window.removeEventListener(WEBSITE_CONSENT_CHANGED_EVENT, listener);\n window.removeEventListener('storage', onStorage);\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"WebsitePageContext.d.ts","sourceRoot":"","sources":["../../../../src/core/contexts/WebsitePageContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAiB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AAE1E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,OAAO,EAAE,cAAc,CAAC;CACzB;AAED,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,OAAO,CAAC,uBAAuB,GAAG,IAAI,CACxB,CAAC;AAEtD,MAAM,WAAW,+BAA+B;IAC9C,KAAK,EAAE,uBAAuB,CAAC;IAC/B,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,0BAA0B,CAAC,EACzC,KAAK,EACL,QAAQ,GACT,EAAE,+BAA+B,GAAG,SAAS,CAM7C;yBATe,0BAA0B;;;AAW1C,uDAAuD"}
1
+ {"version":3,"file":"WebsitePageContext.d.ts","sourceRoot":"","sources":["../../../../../src/core/contexts/WebsitePageContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAiB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AAE1E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,OAAO,EAAE,cAAc,CAAC;CACzB;AAED,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,OAAO,CAAC,uBAAuB,GAAG,IAAI,CACxB,CAAC;AAEtD,MAAM,WAAW,+BAA+B;IAC9C,KAAK,EAAE,uBAAuB,CAAC;IAC/B,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,0BAA0B,CAAC,EACzC,KAAK,EACL,QAAQ,GACT,EAAE,+BAA+B,GAAG,SAAS,CAM7C;yBATe,0BAA0B;;;AAW1C,uDAAuD"}
@@ -1 +1 @@
1
- {"version":3,"file":"WebsitePageContext.js","sourceRoot":"","sources":["../../../../src/core/contexts/WebsitePageContext.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,aAAa,EAAkB,MAAM,OAAO,CAAC;AAiC7D,MAAM,CAAC,MAAM,kBAAkB,GAC7B,aAAa,CAAiC,IAAI,CAAC,CAAC;AAOtD,MAAM,UAAU,0BAA0B,CAAC,EACzC,KAAK,EACL,QAAQ,GACwB;IAChC,OAAO,CACL,KAAC,kBAAkB,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YACtC,QAAQ,GACmB,CAC/B,CAAC;AACJ,CAAC;AACD,0BAA0B,CAAC,WAAW,GAAG,4BAA4B,CAAC","sourcesContent":["import React, { createContext, type ReactNode } from 'react';\n\nimport type { WebsitePageRef } from '../../schemas/refs/page-ref.schemas';\n\n/**\n * @wildo_source:part:start saas.website.page.context facet:layer:core facet:family:website\n *\n * `WebsitePageContext` — identity of the page currently rendering.\n * Provided by `<WebsitePageLayout pageRef=\"…\">` exactly once per\n * page render and consumed by `useWebsiteLabel(leaf)` to compose the\n * full label key (`website.{pageRef}.{sectionRef}.{leaf}`).\n *\n * **Why an isolated context (not merged into runtime)**: the runtime\n * context is set at the host-adapter boundary and is constant per\n * build; the page context is set per page route. Splitting them\n * lets a single host adapter render multiple pages (preview tooling,\n * MDX-driven blog index aggregating post excerpts, etc.) without\n * lying about which page each subtree belongs to.\n *\n * **Why the hook lives in a separate file**: same Fast-Refresh\n * rationale as `WebsiteRuntimeContext` — the context and its provider\n * component stay together here (`useContext` matches on object\n * identity), and only the hook is split out, into `useWebsitePage.ts`.\n */\nexport interface WebsitePageContextValue {\n /**\n * The validated `WebsitePageRef` of the page currently rendering.\n * MUST match a `WebsitePageManifest.pageRef` registered in the\n * site's `WebsiteRootConfig`.\n */\n pageRef: WebsitePageRef;\n}\n\nexport const WebsitePageContext: React.Context<WebsitePageContextValue | null> =\n createContext<WebsitePageContextValue | null>(null);\n\nexport interface WebsitePageContextProviderProps {\n value: WebsitePageContextValue;\n children: ReactNode;\n}\n\nexport function WebsitePageContextProvider({\n value,\n children,\n}: WebsitePageContextProviderProps): ReactNode {\n return (\n <WebsitePageContext.Provider value={value}>\n {children}\n </WebsitePageContext.Provider>\n );\n}\nWebsitePageContextProvider.displayName = 'WebsitePageContextProvider';\n/** @wildo_source:part:end saas.website.page.context */\n"]}
1
+ {"version":3,"file":"WebsitePageContext.js","sourceRoot":"","sources":["../../../../../src/core/contexts/WebsitePageContext.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,aAAa,EAAkB,MAAM,OAAO,CAAC;AAiC7D,MAAM,CAAC,MAAM,kBAAkB,GAC7B,aAAa,CAAiC,IAAI,CAAC,CAAC;AAOtD,MAAM,UAAU,0BAA0B,CAAC,EACzC,KAAK,EACL,QAAQ,GACwB;IAChC,OAAO,CACL,KAAC,kBAAkB,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YACtC,QAAQ,GACmB,CAC/B,CAAC;AACJ,CAAC;AACD,0BAA0B,CAAC,WAAW,GAAG,4BAA4B,CAAC","sourcesContent":["import React, { createContext, type ReactNode } from 'react';\n\nimport type { WebsitePageRef } from '../../schemas/refs/page-ref.schemas';\n\n/**\n * @wildo_source:part:start saas.website.page.context facet:layer:core facet:family:website\n *\n * `WebsitePageContext` — identity of the page currently rendering.\n * Provided by `<WebsitePageLayout pageRef=\"…\">` exactly once per\n * page render and consumed by `useWebsiteLabel(leaf)` to compose the\n * full label key (`website.{pageRef}.{sectionRef}.{leaf}`).\n *\n * **Why an isolated context (not merged into runtime)**: the runtime\n * context is set at the host-adapter boundary and is constant per\n * build; the page context is set per page route. Splitting them\n * lets a single host adapter render multiple pages (preview tooling,\n * MDX-driven blog index aggregating post excerpts, etc.) without\n * lying about which page each subtree belongs to.\n *\n * **Why the hook lives in a separate file**: same Fast-Refresh\n * rationale as `WebsiteRuntimeContext` — the context and its provider\n * component stay together here (`useContext` matches on object\n * identity), and only the hook is split out, into `useWebsitePage.ts`.\n */\nexport interface WebsitePageContextValue {\n /**\n * The validated `WebsitePageRef` of the page currently rendering.\n * MUST match a `WebsitePageManifest.pageRef` registered in the\n * site's `WebsiteRootConfig`.\n */\n pageRef: WebsitePageRef;\n}\n\nexport const WebsitePageContext: React.Context<WebsitePageContextValue | null> =\n createContext<WebsitePageContextValue | null>(null);\n\nexport interface WebsitePageContextProviderProps {\n value: WebsitePageContextValue;\n children: ReactNode;\n}\n\nexport function WebsitePageContextProvider({\n value,\n children,\n}: WebsitePageContextProviderProps): ReactNode {\n return (\n <WebsitePageContext.Provider value={value}>\n {children}\n </WebsitePageContext.Provider>\n );\n}\nWebsitePageContextProvider.displayName = 'WebsitePageContextProvider';\n/** @wildo_source:part:end saas.website.page.context */\n"]}
@@ -1,7 +1,8 @@
1
- import React, { type ReactNode } from 'react';
2
- import type { AvailableLanguage } from '@wildo-ai/saas-models/public-runtime';
1
+ import React, { type ComponentType, type ReactNode } from 'react';
2
+ import type { AvailableLanguage, PublicPricingCatalog } from '@wildo-ai/saas-models/public-runtime';
3
3
  import type { WebsiteDesignTokens } from '../../schemas/design-tokens/website-design-tokens.shared.schemas';
4
4
  import type { FrontendWebsiteProviderRegistry } from '../external-providers/frontend-provider-registry.website';
5
+ import type { WebsiteProviderComponentRegistry } from '../external-providers/provider-component-registry.website';
5
6
  import type { WebsiteLabelKey } from '../../schemas/label-keys/website-label-key.schemas';
6
7
  import type { WebsiteNavigationConfig } from '../../schemas/manifests/website-root-config.shared.schemas';
7
8
  import type { LabelPack } from '../../schemas/validators/label-pack.validator';
@@ -9,10 +10,10 @@ import type { LabelPack } from '../../schemas/validators/label-pack.validator';
9
10
  * @wildo_source:part:start saas.website.runtime.context facet:layer:core facet:family:website
10
11
  *
11
12
  * `WebsiteRuntimeContext` — site-wide runtime state for the marketing
12
- * surface. Created **once per page render** by the host adapter
13
- * (Phase 5 `WebsitePageBridge.astro` reads `WebsiteRootConfig` and the
14
- * resolved label pack from disk and calls
15
- * `<WebsiteRuntimeContextProvider value={…}>` around the page tree).
13
+ * surface. Created **once per page render** by the host adapter: the
14
+ * page's `.astro` frontmatter loads the site context and the resolved label
15
+ * pack, and its bridge island (`createWebsitePageBridgeIsland`) calls
16
+ * `<WebsiteRuntimeContextProvider value={…}>` around the page tree.
16
17
  *
17
18
  * **Why "runtime" and not "config"**: the values here are the
18
19
  * locale-resolved subset of `WebsiteRootConfig` — only the active
@@ -149,6 +150,21 @@ export interface WebsiteRuntimeContextValue {
149
150
  * catalog for analytics / captcha / browser-monitoring slices.
150
151
  */
151
152
  frontendProviderRegistry?: FrontendWebsiteProviderRegistry;
153
+ /**
154
+ * Components the live providers contribute, keyed `capability:providerRef` (#1703) — the same
155
+ * registry the application builds, from the same `components` declaration. Built by the page bridge
156
+ * once per island from `frontendProviderRegistry`; read through `useWebsiteProviderComponent` and
157
+ * rendered by `WebsiteProviderComponent`. Optional for the same reason as the registry above: a
158
+ * provider-free site, or a test runtime, has none, and every slot then renders nothing.
159
+ */
160
+ providerComponentRegistry?: WebsiteProviderComponentRegistry;
161
+ /**
162
+ * Replaces the framework's visitor consent panel (#534). Reads `useWebsiteConsent()`; see
163
+ * `WebsiteConsentBanner` for the three properties a replacement must keep. Absent uses the default.
164
+ * Closed over by the bridge rather than passed as an island prop, like `navigation`, because a
165
+ * component cannot cross Astro's serialization boundary.
166
+ */
167
+ consentBannerComponent?: ComponentType;
152
168
  /**
153
169
  * Backend API origin (WITHOUT a trailing slash) that anonymous-capture
154
170
  * sections POST to when constructing a `WebsiteAnonymousSessionClient`
@@ -163,6 +179,17 @@ export interface WebsiteRuntimeContextValue {
163
179
  * silently failing to submit.
164
180
  */
165
181
  apiOrigin?: string;
182
+ /**
183
+ * The application's public pricing catalogue (#1835), derived at build time from the billing
184
+ * catalogue the backend sells from — so a pricing section states the prices the application
185
+ * actually charges instead of a hand copy of them. Node-loaded
186
+ * (`loadWebsitePricingCatalog` on `/config-loader`) and carried as an island prop for the
187
+ * reason `appOrigin` above is. Read it with `useWebsitePricingCatalog()`.
188
+ *
189
+ * Optional because only pricing pages need it; a section that reads it on a page that did not
190
+ * pass it fails loudly rather than rendering no plans.
191
+ */
192
+ pricingCatalog?: PublicPricingCatalog;
166
193
  }
167
194
  /**
168
195
  * Bare React context. Default `null` — `useWebsiteRuntime()` throws
@@ -1 +1 @@
1
- {"version":3,"file":"WebsiteRuntimeContext.d.ts","sourceRoot":"","sources":["../../../../src/core/contexts/WebsiteRuntimeContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAiB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAE9E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kEAAkE,CAAC;AAC5G,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,0DAA0D,CAAC;AAChH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oDAAoD,CAAC;AAC1F,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,4DAA4D,CAAC;AAC1G,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+CAA+C,CAAC;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AACH,MAAM,WAAW,0BAA0B;IACzC;;;;OAIG;IACH,MAAM,EAAE,iBAAiB,CAAC;IAC1B;;;;;;;;;OASG;IACH,aAAa,EAAE,iBAAiB,CAAC;IACjC;;;;;;;;;;;;;;;;;;OAkBG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,SAAS,EAAE,SAAS,CAAC;IACrB;;;;;OAKG;IACH,YAAY,EAAE,mBAAmB,CAAC;IAClC;;;;;;OAMG;IACH,UAAU,EAAE,uBAAuB,CAAC;IACpC;;;;;;;;;;;;;;;OAeG;IACH,yBAAyB,EAAE,eAAe,CAAC;IAC3C;;;;;OAKG;IACH,wBAAwB,CAAC,EAAE,+BAA+B,CAAC;IAC3D;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,OAAO,CAAC,0BAA0B,GAAG,IAAI,CAC3B,CAAC;AAEzD,MAAM,WAAW,kCAAkC;IACjD,KAAK,EAAE,0BAA0B,CAAC;IAClC,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,EAC5C,KAAK,EACL,QAAQ,GACT,EAAE,kCAAkC,GAAG,SAAS,CAMhD;yBATe,6BAA6B;;;AAW7C,0DAA0D"}
1
+ {"version":3,"file":"WebsiteRuntimeContext.d.ts","sourceRoot":"","sources":["../../../../../src/core/contexts/WebsiteRuntimeContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAiB,KAAK,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEjF,OAAO,KAAK,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAEpG,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kEAAkE,CAAC;AAC5G,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,0DAA0D,CAAC;AAChH,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,2DAA2D,CAAC;AAClH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oDAAoD,CAAC;AAC1F,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,4DAA4D,CAAC;AAC1G,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+CAA+C,CAAC;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AACH,MAAM,WAAW,0BAA0B;IACzC;;;;OAIG;IACH,MAAM,EAAE,iBAAiB,CAAC;IAC1B;;;;;;;;;OASG;IACH,aAAa,EAAE,iBAAiB,CAAC;IACjC;;;;;;;;;;;;;;;;;;OAkBG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,SAAS,EAAE,SAAS,CAAC;IACrB;;;;;OAKG;IACH,YAAY,EAAE,mBAAmB,CAAC;IAClC;;;;;;OAMG;IACH,UAAU,EAAE,uBAAuB,CAAC;IACpC;;;;;;;;;;;;;;;OAeG;IACH,yBAAyB,EAAE,eAAe,CAAC;IAC3C;;;;;OAKG;IACH,wBAAwB,CAAC,EAAE,+BAA+B,CAAC;IAC3D;;;;;;OAMG;IACH,yBAAyB,CAAC,EAAE,gCAAgC,CAAC;IAC7D;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,aAAa,CAAC;IACvC;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;;;OASG;IACH,cAAc,CAAC,EAAE,oBAAoB,CAAC;CACvC;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,OAAO,CAAC,0BAA0B,GAAG,IAAI,CAC3B,CAAC;AAEzD,MAAM,WAAW,kCAAkC;IACjD,KAAK,EAAE,0BAA0B,CAAC;IAClC,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,EAC5C,KAAK,EACL,QAAQ,GACT,EAAE,kCAAkC,GAAG,SAAS,CAMhD;yBATe,6BAA6B;;;AAW7C,0DAA0D"}
@@ -1 +1 @@
1
- {"version":3,"file":"WebsiteRuntimeContext.js","sourceRoot":"","sources":["../../../../src/core/contexts/WebsiteRuntimeContext.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,aAAa,EAAkB,MAAM,OAAO,CAAC;AA0K7D;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAChC,aAAa,CAAoC,IAAI,CAAC,CAAC;AAOzD;;;;;GAKG;AACH,MAAM,UAAU,6BAA6B,CAAC,EAC5C,KAAK,EACL,QAAQ,GAC2B;IACnC,OAAO,CACL,KAAC,qBAAqB,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YACzC,QAAQ,GACsB,CAClC,CAAC;AACJ,CAAC;AACD,6BAA6B,CAAC,WAAW,GAAG,+BAA+B,CAAC","sourcesContent":["import React, { createContext, type ReactNode } from 'react';\n\nimport type { AvailableLanguage } from '@wildo-ai/saas-models/public-runtime';\n\nimport type { WebsiteDesignTokens } from '../../schemas/design-tokens/website-design-tokens.shared.schemas';\nimport type { FrontendWebsiteProviderRegistry } from '../external-providers/frontend-provider-registry.website';\nimport type { WebsiteLabelKey } from '../../schemas/label-keys/website-label-key.schemas';\nimport type { WebsiteNavigationConfig } from '../../schemas/manifests/website-root-config.shared.schemas';\nimport type { LabelPack } from '../../schemas/validators/label-pack.validator';\n\n/**\n * @wildo_source:part:start saas.website.runtime.context facet:layer:core facet:family:website\n *\n * `WebsiteRuntimeContext` — site-wide runtime state for the marketing\n * surface. Created **once per page render** by the host adapter\n * (Phase 5 `WebsitePageBridge.astro` reads `WebsiteRootConfig` and the\n * resolved label pack from disk and calls\n * `<WebsiteRuntimeContextProvider value={…}>` around the page tree).\n *\n * **Why \"runtime\" and not \"config\"**: the values here are the\n * locale-resolved subset of `WebsiteRootConfig` — only the active\n * locale's label pack travels with the React tree (the other locales\n * live in their own per-locale static builds). A `*Config` name would\n * suggest the full multi-locale surface is in scope.\n *\n * **Three pieces of state**:\n *\n * - `locale` — the currently-rendering locale. Sections that need\n * locale-aware formatting (number/date in marketing copy is rare\n * but legitimate) read it via `useWebsiteRuntime()`.\n * - `labelPack` — the locale-resolved flat key→string map, validated\n * against the manifest by `validateLabelPackAgainstManifest` at\n * build start. Consumed by `useWebsiteLabel` and\n * `useWebsiteLabelByKey`.\n * - `designTokens` — the `WebsiteDesignTokens` from\n * `WebsiteRootConfig.designTokens`. Consumed by\n * `useWebsiteDesignTokens` and by the Phase 5 CSS emitter that\n * materializes them as `--website-*` custom properties on the\n * `<WebsitePageLayout>` root.\n * - `navigation` — the `WebsiteNavigationConfig` from\n * `WebsiteRootConfig.navigation`. Carried in the runtime context\n * (rather than as a separate `WebsiteNavigationContext`) because\n * `<WebsitePageLayout>` already reads runtime state and\n * header/footer slots are inseparable from \"the site is rendering\n * right now\". A second context for two component refs would split\n * the host-adapter's wiring surface without buying isolation.\n * - `frontendProviderRegistry` — optional website-facing public provider\n * registry hydrated from the materialized provider artifact for the\n * concrete `STATIC_WEBSITE` service. This keeps analytics / captcha /\n * browser-monitoring integrations on the same frontend-provider\n * contract the SaaS app now uses, instead of inventing a website-only\n * metadata seam.\n *\n * **Why the hook lives in a separate file**: React Fast Refresh only\n * preserves state for a module whose every export is a component, so\n * a module mixing components with plain functions loses its HMR\n * boundary. This file holds the two things that must be identical\n * objects at runtime — the context and the provider component that\n * writes it — while `useWebsiteRuntime` sits alone in\n * `useWebsiteRuntime.ts`. A section author editing a component that\n * merely READS the runtime therefore never remounts the provider.\n *\n * Until 2026-09-09 this paragraph described a different arrangement:\n * that only the bare context was kept here and the provider lived in\n * a file of its own. No such file has ever existed — the provider is\n * exported below — and splitting it out would be the wrong shape\n * anyway, since `useContext` matches on object identity.\n * prose-referent-waiver: the retired file name is quoted only to retire it.\n * The claim named `WebsiteRuntimeContextProvider.tsx`.\n */\nexport interface WebsiteRuntimeContextValue {\n /**\n * The currently-rendering locale. Marketing-site routing emits one\n * static build per locale (D8), so this value is constant per build\n * — never re-renders mid-session.\n */\n locale: AvailableLanguage;\n /**\n * The site's default locale (mirrors `WebsiteRootConfig.defaultLocale`).\n * Carried in the runtime context so client-tier components — namely\n * `WebsiteInternalLink` (Phase 6 fix H-β) — can compute the correct\n * locale-prefixed pathname (`/pricing` on the EN site,\n * `/fr/pricing` on the FR site) without re-importing the root\n * config. Plumbing it through the runtime keeps the routing rule\n * a framework-internal concern: section authors NEVER hand-prefix\n * paths.\n */\n defaultLocale: AvailableLanguage;\n /**\n * Origin of the application this marketing site sends visitors INTO — the\n * marketing → product handoff target, authored once as\n * `WildoMarketingWebsiteConfig.appOrigin` (#493).\n *\n * Carried here for exactly the reason `defaultLocale` above is: a\n * `wildo.website.config.ts` is Node-loaded and therefore invisible to a\n * client island, so before this a site resolved the app origin a SECOND\n * time from a public build-time environment variable it could read in the\n * browser — one host authored twice, in two files, with no mechanical link\n * between the copies. That is how a marketing site comes to point at the\n * wrong environment: a defect nobody notices until a customer follows the\n * link.\n *\n * Threading it through the runtime keeps the handoff a framework-internal\n * concern, the same way threading `defaultLocale` kept locale-prefixing one:\n * section authors never hand-assemble the product URL, and there is one\n * place to change it.\n */\n appOrigin?: string;\n /**\n * Flat `Record<string, string>` label pack for the active locale.\n * Lookup keys MUST be fully-composed `website.<page>.<section>.<slot>`\n * strings; consumers that hold leaf paths use `useWebsiteLabel(leaf)`\n * which composes the full key against the page+section context.\n */\n labelPack: LabelPack;\n /**\n * Design-token bundle for the active site. Same object referenced\n * by `WebsiteRootConfig.designTokens`; passed by reference (not\n * cloned) so token-aware components can rely on referential\n * stability across renders.\n */\n designTokens: WebsiteDesignTokens;\n /**\n * Site-wide navigation slot bindings (header / footer components).\n * Same object referenced by `WebsiteRootConfig.navigation`. Read\n * by `<WebsitePageLayout>` to render the chrome around each page's\n * `<main>` content; per-page `header` / `footer` props on\n * `<WebsitePageLayout>` override these defaults.\n */\n navigation: WebsiteNavigationConfig;\n /**\n * Fully-composed label key the framework reads to render the\n * \"Skip to main content\" affordance as the FIRST focusable element\n * on every page (Phase 6 third audit fix M-α). Mirrors\n * `WebsiteRootConfig.skipToMainContentLabelKey`. Carried in the\n * runtime context (NOT re-derived from the root config inside\n * `WebsitePageLayout`) for two reasons:\n * - The layout is a client-tier React component; it cannot import\n * the consumer's `WEBSITE_ROOT_CONFIG` symbol without dragging\n * server-only code (manifest registry, page components) into\n * the client bundle.\n * - Threading the key through the runtime keeps the layout's\n * contract symmetric with how every other localized text on the\n * page is sourced (label pack + key), so the skip link is\n * translated automatically without any per-locale branching.\n */\n skipToMainContentLabelKey: WebsiteLabelKey;\n /**\n * Website-facing public providers for the active static website\n * service. Optional so older tests and provider-free sites can omit\n * it, but when present it is the canonical frontend-safe provider\n * catalog for analytics / captcha / browser-monitoring slices.\n */\n frontendProviderRegistry?: FrontendWebsiteProviderRegistry;\n /**\n * Backend API origin (WITHOUT a trailing slash) that anonymous-capture\n * sections POST to when constructing a `WebsiteAnonymousSessionClient`\n * (lead / draft-note forms). Sourced from\n * `WildoMarketingWebsiteConfig.apiOrigin` and passed as a\n * JSON-serializable island prop (the `.astro` page resolves the\n * per-environment value and forwards it).\n *\n * Optional because only pages that host an anonymous-capture section\n * need it — brochure pages omit it. A capture section that reads\n * `undefined` here surfaces a loud misconfiguration rather than\n * silently failing to submit.\n */\n apiOrigin?: string;\n}\n\n/**\n * Bare React context. Default `null` — `useWebsiteRuntime()` throws\n * with a clear error message when invoked outside of\n * `<WebsiteRuntimeContextProvider>`. The defensive throw is preferred\n * over a silent default value so a missing provider is detected at\n * the first hook call rather than at render time when a missing\n * label silently renders as the key string.\n */\nexport const WebsiteRuntimeContext: React.Context<WebsiteRuntimeContextValue | null> =\n createContext<WebsiteRuntimeContextValue | null>(null);\n\nexport interface WebsiteRuntimeContextProviderProps {\n value: WebsiteRuntimeContextValue;\n children: ReactNode;\n}\n\n/**\n * Provider component. Authored as a thin wrapper so consumers do NOT\n * have to import `WebsiteRuntimeContext.Provider` directly — the\n * provider component carries the documented authoring API and is the\n * Fast-Refresh-stable surface section authors compose against.\n */\nexport function WebsiteRuntimeContextProvider({\n value,\n children,\n}: WebsiteRuntimeContextProviderProps): ReactNode {\n return (\n <WebsiteRuntimeContext.Provider value={value}>\n {children}\n </WebsiteRuntimeContext.Provider>\n );\n}\nWebsiteRuntimeContextProvider.displayName = 'WebsiteRuntimeContextProvider';\n/** @wildo_source:part:end saas.website.runtime.context */\n"]}
1
+ {"version":3,"file":"WebsiteRuntimeContext.js","sourceRoot":"","sources":["../../../../../src/core/contexts/WebsiteRuntimeContext.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,aAAa,EAAsC,MAAM,OAAO,CAAC;AAqMjF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAChC,aAAa,CAAoC,IAAI,CAAC,CAAC;AAOzD;;;;;GAKG;AACH,MAAM,UAAU,6BAA6B,CAAC,EAC5C,KAAK,EACL,QAAQ,GAC2B;IACnC,OAAO,CACL,KAAC,qBAAqB,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YACzC,QAAQ,GACsB,CAClC,CAAC;AACJ,CAAC;AACD,6BAA6B,CAAC,WAAW,GAAG,+BAA+B,CAAC","sourcesContent":["import React, { createContext, type ComponentType, type ReactNode } from 'react';\n\nimport type { AvailableLanguage, PublicPricingCatalog } from '@wildo-ai/saas-models/public-runtime';\n\nimport type { WebsiteDesignTokens } from '../../schemas/design-tokens/website-design-tokens.shared.schemas';\nimport type { FrontendWebsiteProviderRegistry } from '../external-providers/frontend-provider-registry.website';\nimport type { WebsiteProviderComponentRegistry } from '../external-providers/provider-component-registry.website';\nimport type { WebsiteLabelKey } from '../../schemas/label-keys/website-label-key.schemas';\nimport type { WebsiteNavigationConfig } from '../../schemas/manifests/website-root-config.shared.schemas';\nimport type { LabelPack } from '../../schemas/validators/label-pack.validator';\n\n/**\n * @wildo_source:part:start saas.website.runtime.context facet:layer:core facet:family:website\n *\n * `WebsiteRuntimeContext` — site-wide runtime state for the marketing\n * surface. Created **once per page render** by the host adapter: the\n * page's `.astro` frontmatter loads the site context and the resolved label\n * pack, and its bridge island (`createWebsitePageBridgeIsland`) calls\n * `<WebsiteRuntimeContextProvider value={…}>` around the page tree.\n *\n * **Why \"runtime\" and not \"config\"**: the values here are the\n * locale-resolved subset of `WebsiteRootConfig` — only the active\n * locale's label pack travels with the React tree (the other locales\n * live in their own per-locale static builds). A `*Config` name would\n * suggest the full multi-locale surface is in scope.\n *\n * **Three pieces of state**:\n *\n * - `locale` — the currently-rendering locale. Sections that need\n * locale-aware formatting (number/date in marketing copy is rare\n * but legitimate) read it via `useWebsiteRuntime()`.\n * - `labelPack` — the locale-resolved flat key→string map, validated\n * against the manifest by `validateLabelPackAgainstManifest` at\n * build start. Consumed by `useWebsiteLabel` and\n * `useWebsiteLabelByKey`.\n * - `designTokens` — the `WebsiteDesignTokens` from\n * `WebsiteRootConfig.designTokens`. Consumed by\n * `useWebsiteDesignTokens` and by the Phase 5 CSS emitter that\n * materializes them as `--website-*` custom properties on the\n * `<WebsitePageLayout>` root.\n * - `navigation` — the `WebsiteNavigationConfig` from\n * `WebsiteRootConfig.navigation`. Carried in the runtime context\n * (rather than as a separate `WebsiteNavigationContext`) because\n * `<WebsitePageLayout>` already reads runtime state and\n * header/footer slots are inseparable from \"the site is rendering\n * right now\". A second context for two component refs would split\n * the host-adapter's wiring surface without buying isolation.\n * - `frontendProviderRegistry` — optional website-facing public provider\n * registry hydrated from the materialized provider artifact for the\n * concrete `STATIC_WEBSITE` service. This keeps analytics / captcha /\n * browser-monitoring integrations on the same frontend-provider\n * contract the SaaS app now uses, instead of inventing a website-only\n * metadata seam.\n *\n * **Why the hook lives in a separate file**: React Fast Refresh only\n * preserves state for a module whose every export is a component, so\n * a module mixing components with plain functions loses its HMR\n * boundary. This file holds the two things that must be identical\n * objects at runtime — the context and the provider component that\n * writes it — while `useWebsiteRuntime` sits alone in\n * `useWebsiteRuntime.ts`. A section author editing a component that\n * merely READS the runtime therefore never remounts the provider.\n *\n * Until 2026-09-09 this paragraph described a different arrangement:\n * that only the bare context was kept here and the provider lived in\n * a file of its own. No such file has ever existed — the provider is\n * exported below — and splitting it out would be the wrong shape\n * anyway, since `useContext` matches on object identity.\n * prose-referent-waiver: the retired file name is quoted only to retire it.\n * The claim named `WebsiteRuntimeContextProvider.tsx`.\n */\nexport interface WebsiteRuntimeContextValue {\n /**\n * The currently-rendering locale. Marketing-site routing emits one\n * static build per locale (D8), so this value is constant per build\n * — never re-renders mid-session.\n */\n locale: AvailableLanguage;\n /**\n * The site's default locale (mirrors `WebsiteRootConfig.defaultLocale`).\n * Carried in the runtime context so client-tier components — namely\n * `WebsiteInternalLink` (Phase 6 fix H-β) — can compute the correct\n * locale-prefixed pathname (`/pricing` on the EN site,\n * `/fr/pricing` on the FR site) without re-importing the root\n * config. Plumbing it through the runtime keeps the routing rule\n * a framework-internal concern: section authors NEVER hand-prefix\n * paths.\n */\n defaultLocale: AvailableLanguage;\n /**\n * Origin of the application this marketing site sends visitors INTO — the\n * marketing → product handoff target, authored once as\n * `WildoMarketingWebsiteConfig.appOrigin` (#493).\n *\n * Carried here for exactly the reason `defaultLocale` above is: a\n * `wildo.website.config.ts` is Node-loaded and therefore invisible to a\n * client island, so before this a site resolved the app origin a SECOND\n * time from a public build-time environment variable it could read in the\n * browser — one host authored twice, in two files, with no mechanical link\n * between the copies. That is how a marketing site comes to point at the\n * wrong environment: a defect nobody notices until a customer follows the\n * link.\n *\n * Threading it through the runtime keeps the handoff a framework-internal\n * concern, the same way threading `defaultLocale` kept locale-prefixing one:\n * section authors never hand-assemble the product URL, and there is one\n * place to change it.\n */\n appOrigin?: string;\n /**\n * Flat `Record<string, string>` label pack for the active locale.\n * Lookup keys MUST be fully-composed `website.<page>.<section>.<slot>`\n * strings; consumers that hold leaf paths use `useWebsiteLabel(leaf)`\n * which composes the full key against the page+section context.\n */\n labelPack: LabelPack;\n /**\n * Design-token bundle for the active site. Same object referenced\n * by `WebsiteRootConfig.designTokens`; passed by reference (not\n * cloned) so token-aware components can rely on referential\n * stability across renders.\n */\n designTokens: WebsiteDesignTokens;\n /**\n * Site-wide navigation slot bindings (header / footer components).\n * Same object referenced by `WebsiteRootConfig.navigation`. Read\n * by `<WebsitePageLayout>` to render the chrome around each page's\n * `<main>` content; per-page `header` / `footer` props on\n * `<WebsitePageLayout>` override these defaults.\n */\n navigation: WebsiteNavigationConfig;\n /**\n * Fully-composed label key the framework reads to render the\n * \"Skip to main content\" affordance as the FIRST focusable element\n * on every page (Phase 6 third audit fix M-α). Mirrors\n * `WebsiteRootConfig.skipToMainContentLabelKey`. Carried in the\n * runtime context (NOT re-derived from the root config inside\n * `WebsitePageLayout`) for two reasons:\n * - The layout is a client-tier React component; it cannot import\n * the consumer's `WEBSITE_ROOT_CONFIG` symbol without dragging\n * server-only code (manifest registry, page components) into\n * the client bundle.\n * - Threading the key through the runtime keeps the layout's\n * contract symmetric with how every other localized text on the\n * page is sourced (label pack + key), so the skip link is\n * translated automatically without any per-locale branching.\n */\n skipToMainContentLabelKey: WebsiteLabelKey;\n /**\n * Website-facing public providers for the active static website\n * service. Optional so older tests and provider-free sites can omit\n * it, but when present it is the canonical frontend-safe provider\n * catalog for analytics / captcha / browser-monitoring slices.\n */\n frontendProviderRegistry?: FrontendWebsiteProviderRegistry;\n /**\n * Components the live providers contribute, keyed `capability:providerRef` (#1703) — the same\n * registry the application builds, from the same `components` declaration. Built by the page bridge\n * once per island from `frontendProviderRegistry`; read through `useWebsiteProviderComponent` and\n * rendered by `WebsiteProviderComponent`. Optional for the same reason as the registry above: a\n * provider-free site, or a test runtime, has none, and every slot then renders nothing.\n */\n providerComponentRegistry?: WebsiteProviderComponentRegistry;\n /**\n * Replaces the framework's visitor consent panel (#534). Reads `useWebsiteConsent()`; see\n * `WebsiteConsentBanner` for the three properties a replacement must keep. Absent uses the default.\n * Closed over by the bridge rather than passed as an island prop, like `navigation`, because a\n * component cannot cross Astro's serialization boundary.\n */\n consentBannerComponent?: ComponentType;\n /**\n * Backend API origin (WITHOUT a trailing slash) that anonymous-capture\n * sections POST to when constructing a `WebsiteAnonymousSessionClient`\n * (lead / draft-note forms). Sourced from\n * `WildoMarketingWebsiteConfig.apiOrigin` and passed as a\n * JSON-serializable island prop (the `.astro` page resolves the\n * per-environment value and forwards it).\n *\n * Optional because only pages that host an anonymous-capture section\n * need it — brochure pages omit it. A capture section that reads\n * `undefined` here surfaces a loud misconfiguration rather than\n * silently failing to submit.\n */\n apiOrigin?: string;\n /**\n * The application's public pricing catalogue (#1835), derived at build time from the billing\n * catalogue the backend sells from — so a pricing section states the prices the application\n * actually charges instead of a hand copy of them. Node-loaded\n * (`loadWebsitePricingCatalog` on `/config-loader`) and carried as an island prop for the\n * reason `appOrigin` above is. Read it with `useWebsitePricingCatalog()`.\n *\n * Optional because only pricing pages need it; a section that reads it on a page that did not\n * pass it fails loudly rather than rendering no plans.\n */\n pricingCatalog?: PublicPricingCatalog;\n}\n\n/**\n * Bare React context. Default `null` — `useWebsiteRuntime()` throws\n * with a clear error message when invoked outside of\n * `<WebsiteRuntimeContextProvider>`. The defensive throw is preferred\n * over a silent default value so a missing provider is detected at\n * the first hook call rather than at render time when a missing\n * label silently renders as the key string.\n */\nexport const WebsiteRuntimeContext: React.Context<WebsiteRuntimeContextValue | null> =\n createContext<WebsiteRuntimeContextValue | null>(null);\n\nexport interface WebsiteRuntimeContextProviderProps {\n value: WebsiteRuntimeContextValue;\n children: ReactNode;\n}\n\n/**\n * Provider component. Authored as a thin wrapper so consumers do NOT\n * have to import `WebsiteRuntimeContext.Provider` directly — the\n * provider component carries the documented authoring API and is the\n * Fast-Refresh-stable surface section authors compose against.\n */\nexport function WebsiteRuntimeContextProvider({\n value,\n children,\n}: WebsiteRuntimeContextProviderProps): ReactNode {\n return (\n <WebsiteRuntimeContext.Provider value={value}>\n {children}\n </WebsiteRuntimeContext.Provider>\n );\n}\nWebsiteRuntimeContextProvider.displayName = 'WebsiteRuntimeContextProvider';\n/** @wildo_source:part:end saas.website.runtime.context */\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"WebsiteSectionContext.d.ts","sourceRoot":"","sources":["../../../../src/core/contexts/WebsiteSectionContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAiB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE7D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wDAAwD,CAAC;AACrG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAEhF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,0BAA0B;IACzC;;;OAGG;IACH,UAAU,EAAE,iBAAiB,CAAC;IAC9B;;;;;OAKG;IACH,QAAQ,EAAE,sBAAsB,CAAC;CAClC;AAED,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,OAAO,CAAC,0BAA0B,GAAG,IAAI,CAC3B,CAAC;AAEzD,MAAM,WAAW,kCAAkC;IACjD,KAAK,EAAE,0BAA0B,CAAC;IAClC,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,6BAA6B,CAAC,EAC5C,KAAK,EACL,QAAQ,GACT,EAAE,kCAAkC,GAAG,SAAS,CAMhD;yBATe,6BAA6B;;;AAW7C,0DAA0D"}
1
+ {"version":3,"file":"WebsiteSectionContext.d.ts","sourceRoot":"","sources":["../../../../../src/core/contexts/WebsiteSectionContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAiB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE7D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wDAAwD,CAAC;AACrG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAEhF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,0BAA0B;IACzC;;;OAGG;IACH,UAAU,EAAE,iBAAiB,CAAC;IAC9B;;;;;OAKG;IACH,QAAQ,EAAE,sBAAsB,CAAC;CAClC;AAED,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,OAAO,CAAC,0BAA0B,GAAG,IAAI,CAC3B,CAAC;AAEzD,MAAM,WAAW,kCAAkC;IACjD,KAAK,EAAE,0BAA0B,CAAC;IAClC,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,6BAA6B,CAAC,EAC5C,KAAK,EACL,QAAQ,GACT,EAAE,kCAAkC,GAAG,SAAS,CAMhD;yBATe,6BAA6B;;;AAW7C,0DAA0D"}
@@ -1 +1 @@
1
- {"version":3,"file":"WebsiteSectionContext.js","sourceRoot":"","sources":["../../../../src/core/contexts/WebsiteSectionContext.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,aAAa,EAAkB,MAAM,OAAO,CAAC;AAwC7D,MAAM,CAAC,MAAM,qBAAqB,GAChC,aAAa,CAAoC,IAAI,CAAC,CAAC;AAOzD,MAAM,UAAU,6BAA6B,CAAC,EAC5C,KAAK,EACL,QAAQ,GAC2B;IACnC,OAAO,CACL,KAAC,qBAAqB,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YACzC,QAAQ,GACsB,CAClC,CAAC;AACJ,CAAC;AACD,6BAA6B,CAAC,WAAW,GAAG,+BAA+B,CAAC","sourcesContent":["import React, { createContext, type ReactNode } from 'react';\n\nimport type { WebsiteSectionCategory } from '../../schemas/sections/website-section-category.shared';\nimport type { WebsiteSectionRef } from '../../schemas/refs/section-ref.schemas';\n\n/**\n * @wildo_source:part:start saas.website.section.context facet:layer:core facet:family:website\n *\n * `WebsiteSectionContext` — identity + metadata of the section\n * currently rendering. Provided by `<WebsiteSection>` and consumed\n * by `useWebsiteLabel(leaf)` to compose the full label key, by\n * analytics overlays that need the category, and by SEO emitters\n * that decorate sections with `data-*` attributes.\n *\n * **Why expose `category` here (not just `sectionRef`)**: the\n * category is the section's semantic role (`hero`, `cta`, `social`,\n * etc.) and is needed by overlay tooling and analytics consumers\n * that don't know the per-site `sectionRef` vocabulary. Bundling\n * both saves consumers from having to thread the manifest just to\n * map a ref back to its category.\n *\n * **Why the hook lives in a separate file**: same Fast-Refresh\n * rationale as the runtime + page contexts — context and provider\n * together here, the hook alone in `useWebsiteSection.ts`.\n */\nexport interface WebsiteSectionContextValue {\n /**\n * The validated `WebsiteSectionRef` of the section currently\n * rendering. Composed by `useWebsiteLabel(leaf)` into the full key.\n */\n sectionRef: WebsiteSectionRef;\n /**\n * The semantic category of the section (`hero` / `cta` / etc.).\n * Sourced from the matching `WebsiteSectionDefinition.category`\n * by the host adapter and surfaced here so analytics + SEO\n * tooling does not need to re-resolve it.\n */\n category: WebsiteSectionCategory;\n}\n\nexport const WebsiteSectionContext: React.Context<WebsiteSectionContextValue | null> =\n createContext<WebsiteSectionContextValue | null>(null);\n\nexport interface WebsiteSectionContextProviderProps {\n value: WebsiteSectionContextValue;\n children: ReactNode;\n}\n\nexport function WebsiteSectionContextProvider({\n value,\n children,\n}: WebsiteSectionContextProviderProps): ReactNode {\n return (\n <WebsiteSectionContext.Provider value={value}>\n {children}\n </WebsiteSectionContext.Provider>\n );\n}\nWebsiteSectionContextProvider.displayName = 'WebsiteSectionContextProvider';\n/** @wildo_source:part:end saas.website.section.context */\n"]}
1
+ {"version":3,"file":"WebsiteSectionContext.js","sourceRoot":"","sources":["../../../../../src/core/contexts/WebsiteSectionContext.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,aAAa,EAAkB,MAAM,OAAO,CAAC;AAwC7D,MAAM,CAAC,MAAM,qBAAqB,GAChC,aAAa,CAAoC,IAAI,CAAC,CAAC;AAOzD,MAAM,UAAU,6BAA6B,CAAC,EAC5C,KAAK,EACL,QAAQ,GAC2B;IACnC,OAAO,CACL,KAAC,qBAAqB,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YACzC,QAAQ,GACsB,CAClC,CAAC;AACJ,CAAC;AACD,6BAA6B,CAAC,WAAW,GAAG,+BAA+B,CAAC","sourcesContent":["import React, { createContext, type ReactNode } from 'react';\n\nimport type { WebsiteSectionCategory } from '../../schemas/sections/website-section-category.shared';\nimport type { WebsiteSectionRef } from '../../schemas/refs/section-ref.schemas';\n\n/**\n * @wildo_source:part:start saas.website.section.context facet:layer:core facet:family:website\n *\n * `WebsiteSectionContext` — identity + metadata of the section\n * currently rendering. Provided by `<WebsiteSection>` and consumed\n * by `useWebsiteLabel(leaf)` to compose the full label key, by\n * analytics overlays that need the category, and by SEO emitters\n * that decorate sections with `data-*` attributes.\n *\n * **Why expose `category` here (not just `sectionRef`)**: the\n * category is the section's semantic role (`hero`, `cta`, `social`,\n * etc.) and is needed by overlay tooling and analytics consumers\n * that don't know the per-site `sectionRef` vocabulary. Bundling\n * both saves consumers from having to thread the manifest just to\n * map a ref back to its category.\n *\n * **Why the hook lives in a separate file**: same Fast-Refresh\n * rationale as the runtime + page contexts — context and provider\n * together here, the hook alone in `useWebsiteSection.ts`.\n */\nexport interface WebsiteSectionContextValue {\n /**\n * The validated `WebsiteSectionRef` of the section currently\n * rendering. Composed by `useWebsiteLabel(leaf)` into the full key.\n */\n sectionRef: WebsiteSectionRef;\n /**\n * The semantic category of the section (`hero` / `cta` / etc.).\n * Sourced from the matching `WebsiteSectionDefinition.category`\n * by the host adapter and surfaced here so analytics + SEO\n * tooling does not need to re-resolve it.\n */\n category: WebsiteSectionCategory;\n}\n\nexport const WebsiteSectionContext: React.Context<WebsiteSectionContextValue | null> =\n createContext<WebsiteSectionContextValue | null>(null);\n\nexport interface WebsiteSectionContextProviderProps {\n value: WebsiteSectionContextValue;\n children: ReactNode;\n}\n\nexport function WebsiteSectionContextProvider({\n value,\n children,\n}: WebsiteSectionContextProviderProps): ReactNode {\n return (\n <WebsiteSectionContext.Provider value={value}>\n {children}\n </WebsiteSectionContext.Provider>\n );\n}\nWebsiteSectionContextProvider.displayName = 'WebsiteSectionContextProvider';\n/** @wildo_source:part:end saas.website.section.context */\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"useWebsitePage.d.ts","sourceRoot":"","sources":["../../../../src/core/contexts/useWebsitePage.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,sBAAsB,CAAC;AAE9B;;;;;;;;GAQG;AACH,wBAAgB,cAAc,IAAI,uBAAuB,CAUxD;AACD,wDAAwD"}
1
+ {"version":3,"file":"useWebsitePage.d.ts","sourceRoot":"","sources":["../../../../../src/core/contexts/useWebsitePage.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,sBAAsB,CAAC;AAE9B;;;;;;;;GAQG;AACH,wBAAgB,cAAc,IAAI,uBAAuB,CAUxD;AACD,wDAAwD"}
@@ -1,3 +1,4 @@
1
+ import type { PublicPricingCatalog } from '@wildo-ai/saas-models/public-runtime';
1
2
  import { type WebsiteRuntimeContextValue } from './WebsiteRuntimeContext';
2
3
  /**
3
4
  * @wildo_source:part:start saas.website.use-runtime.hook facet:layer:core facet:family:website
@@ -16,4 +17,21 @@ import { type WebsiteRuntimeContextValue } from './WebsiteRuntimeContext';
16
17
  */
17
18
  export declare function useWebsiteRuntime(): WebsiteRuntimeContextValue;
18
19
  /** @wildo_source:part:end saas.website.use-runtime.hook */
20
+ /**
21
+ * @wildo_source:part:start saas.website.use-pricing-catalog.hook facet:layer:core facet:family:website facet:audience:app-developer
22
+ *
23
+ * `useWebsitePricingCatalog()` — the application's public pricing catalogue, for a pricing section.
24
+ *
25
+ * The plans, their prices, which one is recommended, how much of each price may be stated, and which
26
+ * are announced but not yet on sale — all derived at build time from the billing catalogue the
27
+ * backend sells from. A pricing section renders from THIS and never keeps its own copy of a price:
28
+ * a copy is a price that will disagree with the one the application charges.
29
+ *
30
+ * Throws when the page did not pass the catalogue to its bridge island: a pricing section with no
31
+ * plans would silently state that the application sells nothing. The page's `.astro` frontmatter
32
+ * loads it with `loadWebsitePricingCatalog()` from `@wildo-ai/saas-website/config-loader` and passes
33
+ * it as the island's `pricingCatalog` prop.
34
+ */
35
+ export declare function useWebsitePricingCatalog(): PublicPricingCatalog;
36
+ /** @wildo_source:part:end saas.website.use-pricing-catalog.hook */
19
37
  //# sourceMappingURL=useWebsiteRuntime.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useWebsiteRuntime.d.ts","sourceRoot":"","sources":["../../../../src/core/contexts/useWebsiteRuntime.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,0BAA0B,EAChC,MAAM,yBAAyB,CAAC;AAEjC;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,IAAI,0BAA0B,CAU9D;AACD,2DAA2D"}
1
+ {"version":3,"file":"useWebsiteRuntime.d.ts","sourceRoot":"","sources":["../../../../../src/core/contexts/useWebsiteRuntime.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAEjF,OAAO,EAEL,KAAK,0BAA0B,EAChC,MAAM,yBAAyB,CAAC;AAEjC;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,IAAI,0BAA0B,CAW9D;AACD,2DAA2D;AAE3D;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,wBAAwB,IAAI,oBAAoB,CAU/D;AACD,mEAAmE"}