@wildo-ai/saas-website 1.1.1

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 (383) hide show
  1. package/LICENSE +34 -0
  2. package/dist/esm/.builder.pid +9 -0
  3. package/dist/esm/astro/blog-post-bridge.d.ts +324 -0
  4. package/dist/esm/astro/blog-post-bridge.d.ts.map +1 -0
  5. package/dist/esm/astro/blog-post-bridge.js +57 -0
  6. package/dist/esm/astro/blog-post-bridge.js.map +1 -0
  7. package/dist/esm/astro/blog-post-head.renderer.d.ts +129 -0
  8. package/dist/esm/astro/blog-post-head.renderer.d.ts.map +1 -0
  9. package/dist/esm/astro/blog-post-head.renderer.js +139 -0
  10. package/dist/esm/astro/blog-post-head.renderer.js.map +1 -0
  11. package/dist/esm/astro/bridge-runtime.d.ts +224 -0
  12. package/dist/esm/astro/bridge-runtime.d.ts.map +1 -0
  13. package/dist/esm/astro/bridge-runtime.js +30 -0
  14. package/dist/esm/astro/bridge-runtime.js.map +1 -0
  15. package/dist/esm/astro/collect-expected-label-keys.d.ts +65 -0
  16. package/dist/esm/astro/collect-expected-label-keys.d.ts.map +1 -0
  17. package/dist/esm/astro/collect-expected-label-keys.js +101 -0
  18. package/dist/esm/astro/collect-expected-label-keys.js.map +1 -0
  19. package/dist/esm/astro/headers-renderer.d.ts +32 -0
  20. package/dist/esm/astro/headers-renderer.d.ts.map +1 -0
  21. package/dist/esm/astro/headers-renderer.js +64 -0
  22. package/dist/esm/astro/headers-renderer.js.map +1 -0
  23. package/dist/esm/astro/i18n-routing.helper.d.ts +91 -0
  24. package/dist/esm/astro/i18n-routing.helper.d.ts.map +1 -0
  25. package/dist/esm/astro/i18n-routing.helper.js +23 -0
  26. package/dist/esm/astro/i18n-routing.helper.js.map +1 -0
  27. package/dist/esm/astro/internal/html-escape.d.ts +31 -0
  28. package/dist/esm/astro/internal/html-escape.d.ts.map +1 -0
  29. package/dist/esm/astro/internal/html-escape.js +40 -0
  30. package/dist/esm/astro/internal/html-escape.js.map +1 -0
  31. package/dist/esm/astro/label-pack-loader.d.ts +134 -0
  32. package/dist/esm/astro/label-pack-loader.d.ts.map +1 -0
  33. package/dist/esm/astro/label-pack-loader.js +298 -0
  34. package/dist/esm/astro/label-pack-loader.js.map +1 -0
  35. package/dist/esm/astro/llms-txt-server.d.ts +28 -0
  36. package/dist/esm/astro/llms-txt-server.d.ts.map +1 -0
  37. package/dist/esm/astro/llms-txt-server.js +33 -0
  38. package/dist/esm/astro/llms-txt-server.js.map +1 -0
  39. package/dist/esm/astro/pick-labels-for-page.d.ts +63 -0
  40. package/dist/esm/astro/pick-labels-for-page.d.ts.map +1 -0
  41. package/dist/esm/astro/pick-labels-for-page.js +73 -0
  42. package/dist/esm/astro/pick-labels-for-page.js.map +1 -0
  43. package/dist/esm/astro/robots-renderer.d.ts +85 -0
  44. package/dist/esm/astro/robots-renderer.d.ts.map +1 -0
  45. package/dist/esm/astro/robots-renderer.js +157 -0
  46. package/dist/esm/astro/robots-renderer.js.map +1 -0
  47. package/dist/esm/astro/sitemap-coverage.d.ts +101 -0
  48. package/dist/esm/astro/sitemap-coverage.d.ts.map +1 -0
  49. package/dist/esm/astro/sitemap-coverage.js +67 -0
  50. package/dist/esm/astro/sitemap-coverage.js.map +1 -0
  51. package/dist/esm/astro/structured-data.renderer.d.ts +21 -0
  52. package/dist/esm/astro/structured-data.renderer.d.ts.map +1 -0
  53. package/dist/esm/astro/structured-data.renderer.js +142 -0
  54. package/dist/esm/astro/structured-data.renderer.js.map +1 -0
  55. package/dist/esm/astro/website-page-head.renderer.d.ts +93 -0
  56. package/dist/esm/astro/website-page-head.renderer.d.ts.map +1 -0
  57. package/dist/esm/astro/website-page-head.renderer.js +184 -0
  58. package/dist/esm/astro/website-page-head.renderer.js.map +1 -0
  59. package/dist/esm/astro/website-page-runtime.helper.d.ts +112 -0
  60. package/dist/esm/astro/website-page-runtime.helper.d.ts.map +1 -0
  61. package/dist/esm/astro/website-page-runtime.helper.js +60 -0
  62. package/dist/esm/astro/website-page-runtime.helper.js.map +1 -0
  63. package/dist/esm/astro/website-site-context.d.ts +126 -0
  64. package/dist/esm/astro/website-site-context.d.ts.map +1 -0
  65. package/dist/esm/astro/website-site-context.js +37 -0
  66. package/dist/esm/astro/website-site-context.js.map +1 -0
  67. package/dist/esm/astro-island.d.ts +56 -0
  68. package/dist/esm/astro-island.d.ts.map +1 -0
  69. package/dist/esm/astro-island.js +61 -0
  70. package/dist/esm/astro-island.js.map +1 -0
  71. package/dist/esm/astro.d.ts +151 -0
  72. package/dist/esm/astro.d.ts.map +1 -0
  73. package/dist/esm/astro.js +151 -0
  74. package/dist/esm/astro.js.map +1 -0
  75. package/dist/esm/companion-exports.d.ts +38 -0
  76. package/dist/esm/companion-exports.d.ts.map +1 -0
  77. package/dist/esm/companion-exports.js +38 -0
  78. package/dist/esm/companion-exports.js.map +1 -0
  79. package/dist/esm/components/low-level/WebsiteButton.d.ts +73 -0
  80. package/dist/esm/components/low-level/WebsiteButton.d.ts.map +1 -0
  81. package/dist/esm/components/low-level/WebsiteButton.js +68 -0
  82. package/dist/esm/components/low-level/WebsiteButton.js.map +1 -0
  83. package/dist/esm/components/low-level/WebsiteHeading.d.ts +78 -0
  84. package/dist/esm/components/low-level/WebsiteHeading.d.ts.map +1 -0
  85. package/dist/esm/components/low-level/WebsiteHeading.js +41 -0
  86. package/dist/esm/components/low-level/WebsiteHeading.js.map +1 -0
  87. package/dist/esm/components/low-level/WebsiteImage.d.ts +73 -0
  88. package/dist/esm/components/low-level/WebsiteImage.d.ts.map +1 -0
  89. package/dist/esm/components/low-level/WebsiteImage.js +21 -0
  90. package/dist/esm/components/low-level/WebsiteImage.js.map +1 -0
  91. package/dist/esm/components/low-level/WebsiteInternalButton.d.ts +90 -0
  92. package/dist/esm/components/low-level/WebsiteInternalButton.d.ts.map +1 -0
  93. package/dist/esm/components/low-level/WebsiteInternalButton.js +47 -0
  94. package/dist/esm/components/low-level/WebsiteInternalButton.js.map +1 -0
  95. package/dist/esm/components/low-level/WebsiteInternalLink.d.ts +99 -0
  96. package/dist/esm/components/low-level/WebsiteInternalLink.d.ts.map +1 -0
  97. package/dist/esm/components/low-level/WebsiteInternalLink.js +68 -0
  98. package/dist/esm/components/low-level/WebsiteInternalLink.js.map +1 -0
  99. package/dist/esm/components/low-level/WebsiteLink.d.ts +50 -0
  100. package/dist/esm/components/low-level/WebsiteLink.d.ts.map +1 -0
  101. package/dist/esm/components/low-level/WebsiteLink.js +22 -0
  102. package/dist/esm/components/low-level/WebsiteLink.js.map +1 -0
  103. package/dist/esm/components/low-level/WebsiteText.d.ts +57 -0
  104. package/dist/esm/components/low-level/WebsiteText.d.ts.map +1 -0
  105. package/dist/esm/components/low-level/WebsiteText.js +57 -0
  106. package/dist/esm/components/low-level/WebsiteText.js.map +1 -0
  107. package/dist/esm/config/define-website-config.d.ts +83 -0
  108. package/dist/esm/config/define-website-config.d.ts.map +1 -0
  109. package/dist/esm/config/define-website-config.js +126 -0
  110. package/dist/esm/config/define-website-config.js.map +1 -0
  111. package/dist/esm/config/index.d.ts +38 -0
  112. package/dist/esm/config/index.d.ts.map +1 -0
  113. package/dist/esm/config/index.js +38 -0
  114. package/dist/esm/config/index.js.map +1 -0
  115. package/dist/esm/config/load-website-config.d.ts +136 -0
  116. package/dist/esm/config/load-website-config.d.ts.map +1 -0
  117. package/dist/esm/config/load-website-config.js +177 -0
  118. package/dist/esm/config/load-website-config.js.map +1 -0
  119. package/dist/esm/config/wildo-website-config.schemas.d.ts +251 -0
  120. package/dist/esm/config/wildo-website-config.schemas.d.ts.map +1 -0
  121. package/dist/esm/config/wildo-website-config.schemas.js +302 -0
  122. package/dist/esm/config/wildo-website-config.schemas.js.map +1 -0
  123. package/dist/esm/config-loader.d.ts +35 -0
  124. package/dist/esm/config-loader.d.ts.map +1 -0
  125. package/dist/esm/config-loader.js +35 -0
  126. package/dist/esm/config-loader.js.map +1 -0
  127. package/dist/esm/core/anonymous-session/InboundContactForm.d.ts +38 -0
  128. package/dist/esm/core/anonymous-session/InboundContactForm.d.ts.map +1 -0
  129. package/dist/esm/core/anonymous-session/InboundContactForm.js +77 -0
  130. package/dist/esm/core/anonymous-session/InboundContactForm.js.map +1 -0
  131. package/dist/esm/core/anonymous-session/inbound-contact-form.schema.d.ts +41 -0
  132. package/dist/esm/core/anonymous-session/inbound-contact-form.schema.d.ts.map +1 -0
  133. package/dist/esm/core/anonymous-session/inbound-contact-form.schema.js +58 -0
  134. package/dist/esm/core/anonymous-session/inbound-contact-form.schema.js.map +1 -0
  135. package/dist/esm/core/anonymous-session/website-anonymous-session-client.d.ts +81 -0
  136. package/dist/esm/core/anonymous-session/website-anonymous-session-client.d.ts.map +1 -0
  137. package/dist/esm/core/anonymous-session/website-anonymous-session-client.js +177 -0
  138. package/dist/esm/core/anonymous-session/website-anonymous-session-client.js.map +1 -0
  139. package/dist/esm/core/contexts/WebsitePageContext.d.ts +40 -0
  140. package/dist/esm/core/contexts/WebsitePageContext.d.ts.map +1 -0
  141. package/dist/esm/core/contexts/WebsitePageContext.js +8 -0
  142. package/dist/esm/core/contexts/WebsitePageContext.js.map +1 -0
  143. package/dist/esm/core/contexts/WebsiteRuntimeContext.d.ts +162 -0
  144. package/dist/esm/core/contexts/WebsiteRuntimeContext.d.ts.map +1 -0
  145. package/dist/esm/core/contexts/WebsiteRuntimeContext.js +22 -0
  146. package/dist/esm/core/contexts/WebsiteRuntimeContext.js.map +1 -0
  147. package/dist/esm/core/contexts/WebsiteSectionContext.d.ts +47 -0
  148. package/dist/esm/core/contexts/WebsiteSectionContext.d.ts.map +1 -0
  149. package/dist/esm/core/contexts/WebsiteSectionContext.js +8 -0
  150. package/dist/esm/core/contexts/WebsiteSectionContext.js.map +1 -0
  151. package/dist/esm/core/contexts/useWebsitePage.d.ts +13 -0
  152. package/dist/esm/core/contexts/useWebsitePage.d.ts.map +1 -0
  153. package/dist/esm/core/contexts/useWebsitePage.js +22 -0
  154. package/dist/esm/core/contexts/useWebsitePage.js.map +1 -0
  155. package/dist/esm/core/contexts/useWebsiteRuntime.d.ts +19 -0
  156. package/dist/esm/core/contexts/useWebsiteRuntime.d.ts.map +1 -0
  157. package/dist/esm/core/contexts/useWebsiteRuntime.js +28 -0
  158. package/dist/esm/core/contexts/useWebsiteRuntime.js.map +1 -0
  159. package/dist/esm/core/contexts/useWebsiteSection.d.ts +12 -0
  160. package/dist/esm/core/contexts/useWebsiteSection.d.ts.map +1 -0
  161. package/dist/esm/core/contexts/useWebsiteSection.js +22 -0
  162. package/dist/esm/core/contexts/useWebsiteSection.js.map +1 -0
  163. package/dist/esm/core/external-providers/frontend-provider-registry.website.d.ts +17 -0
  164. package/dist/esm/core/external-providers/frontend-provider-registry.website.d.ts.map +1 -0
  165. package/dist/esm/core/external-providers/frontend-provider-registry.website.js +23 -0
  166. package/dist/esm/core/external-providers/frontend-provider-registry.website.js.map +1 -0
  167. package/dist/esm/core/factories/define-website-page-manifest.d.ts +70 -0
  168. package/dist/esm/core/factories/define-website-page-manifest.d.ts.map +1 -0
  169. package/dist/esm/core/factories/define-website-page-manifest.js +51 -0
  170. package/dist/esm/core/factories/define-website-page-manifest.js.map +1 -0
  171. package/dist/esm/core/factories/define-website-section.d.ts +101 -0
  172. package/dist/esm/core/factories/define-website-section.d.ts.map +1 -0
  173. package/dist/esm/core/factories/define-website-section.js +71 -0
  174. package/dist/esm/core/factories/define-website-section.js.map +1 -0
  175. package/dist/esm/core/hooks/useWebsiteDesignTokens.d.ts +23 -0
  176. package/dist/esm/core/hooks/useWebsiteDesignTokens.d.ts.map +1 -0
  177. package/dist/esm/core/hooks/useWebsiteDesignTokens.js +25 -0
  178. package/dist/esm/core/hooks/useWebsiteDesignTokens.js.map +1 -0
  179. package/dist/esm/core/hooks/useWebsiteLabel.d.ts +68 -0
  180. package/dist/esm/core/hooks/useWebsiteLabel.d.ts.map +1 -0
  181. package/dist/esm/core/hooks/useWebsiteLabel.js +103 -0
  182. package/dist/esm/core/hooks/useWebsiteLabel.js.map +1 -0
  183. package/dist/esm/core/layouts/WebsitePageLayout.d.ts +87 -0
  184. package/dist/esm/core/layouts/WebsitePageLayout.d.ts.map +1 -0
  185. package/dist/esm/core/layouts/WebsitePageLayout.js +170 -0
  186. package/dist/esm/core/layouts/WebsitePageLayout.js.map +1 -0
  187. package/dist/esm/core/layouts/WebsiteSection.d.ts +91 -0
  188. package/dist/esm/core/layouts/WebsiteSection.d.ts.map +1 -0
  189. package/dist/esm/core/layouts/WebsiteSection.js +26 -0
  190. package/dist/esm/core/layouts/WebsiteSection.js.map +1 -0
  191. package/dist/esm/core/routing/internal-routing.utils.d.ts +65 -0
  192. package/dist/esm/core/routing/internal-routing.utils.d.ts.map +1 -0
  193. package/dist/esm/core/routing/internal-routing.utils.js +14 -0
  194. package/dist/esm/core/routing/internal-routing.utils.js.map +1 -0
  195. package/dist/esm/index.d.ts +129 -0
  196. package/dist/esm/index.d.ts.map +1 -0
  197. package/dist/esm/index.js +129 -0
  198. package/dist/esm/index.js.map +1 -0
  199. package/dist/esm/mdx/BlogPost.d.ts +193 -0
  200. package/dist/esm/mdx/BlogPost.d.ts.map +1 -0
  201. package/dist/esm/mdx/BlogPost.js +161 -0
  202. package/dist/esm/mdx/BlogPost.js.map +1 -0
  203. package/dist/esm/mdx/blog-post-collection.config.d.ts +139 -0
  204. package/dist/esm/mdx/blog-post-collection.config.d.ts.map +1 -0
  205. package/dist/esm/mdx/blog-post-collection.config.js +27 -0
  206. package/dist/esm/mdx/blog-post-collection.config.js.map +1 -0
  207. package/dist/esm/mdx/blog-post-frontmatter-source.d.ts +12 -0
  208. package/dist/esm/mdx/blog-post-frontmatter-source.d.ts.map +1 -0
  209. package/dist/esm/mdx/blog-post-frontmatter-source.js +13 -0
  210. package/dist/esm/mdx/blog-post-frontmatter-source.js.map +1 -0
  211. package/dist/esm/mdx/index.d.ts +22 -0
  212. package/dist/esm/mdx/index.d.ts.map +1 -0
  213. package/dist/esm/mdx/index.js +22 -0
  214. package/dist/esm/mdx/index.js.map +1 -0
  215. package/dist/esm/mdx/website-mdx-provider.d.ts +90 -0
  216. package/dist/esm/mdx/website-mdx-provider.d.ts.map +1 -0
  217. package/dist/esm/mdx/website-mdx-provider.js +8 -0
  218. package/dist/esm/mdx/website-mdx-provider.js.map +1 -0
  219. package/dist/esm/mdx.d.ts +38 -0
  220. package/dist/esm/mdx.d.ts.map +1 -0
  221. package/dist/esm/mdx.js +38 -0
  222. package/dist/esm/mdx.js.map +1 -0
  223. package/dist/esm/schemas/blog/blog-post-frontmatter.shared.schemas.d.ts +75 -0
  224. package/dist/esm/schemas/blog/blog-post-frontmatter.shared.schemas.d.ts.map +1 -0
  225. package/dist/esm/schemas/blog/blog-post-frontmatter.shared.schemas.js +85 -0
  226. package/dist/esm/schemas/blog/blog-post-frontmatter.shared.schemas.js.map +1 -0
  227. package/dist/esm/schemas/design-tokens/website-design-tokens.shared.schemas.d.ts +120 -0
  228. package/dist/esm/schemas/design-tokens/website-design-tokens.shared.schemas.d.ts.map +1 -0
  229. package/dist/esm/schemas/design-tokens/website-design-tokens.shared.schemas.js +152 -0
  230. package/dist/esm/schemas/design-tokens/website-design-tokens.shared.schemas.js.map +1 -0
  231. package/dist/esm/schemas/label-keys/website-label-key.schemas.d.ts +73 -0
  232. package/dist/esm/schemas/label-keys/website-label-key.schemas.d.ts.map +1 -0
  233. package/dist/esm/schemas/label-keys/website-label-key.schemas.js +76 -0
  234. package/dist/esm/schemas/label-keys/website-label-key.schemas.js.map +1 -0
  235. package/dist/esm/schemas/manifests/website-page-manifest.shared.schemas.d.ts +209 -0
  236. package/dist/esm/schemas/manifests/website-page-manifest.shared.schemas.d.ts.map +1 -0
  237. package/dist/esm/schemas/manifests/website-page-manifest.shared.schemas.js +218 -0
  238. package/dist/esm/schemas/manifests/website-page-manifest.shared.schemas.js.map +1 -0
  239. package/dist/esm/schemas/manifests/website-root-config.shared.schemas.d.ts +431 -0
  240. package/dist/esm/schemas/manifests/website-root-config.shared.schemas.d.ts.map +1 -0
  241. package/dist/esm/schemas/manifests/website-root-config.shared.schemas.js +438 -0
  242. package/dist/esm/schemas/manifests/website-root-config.shared.schemas.js.map +1 -0
  243. package/dist/esm/schemas/refs/page-ref.schemas.d.ts +40 -0
  244. package/dist/esm/schemas/refs/page-ref.schemas.d.ts.map +1 -0
  245. package/dist/esm/schemas/refs/page-ref.schemas.js +43 -0
  246. package/dist/esm/schemas/refs/page-ref.schemas.js.map +1 -0
  247. package/dist/esm/schemas/refs/section-ref.schemas.d.ts +41 -0
  248. package/dist/esm/schemas/refs/section-ref.schemas.d.ts.map +1 -0
  249. package/dist/esm/schemas/refs/section-ref.schemas.js +44 -0
  250. package/dist/esm/schemas/refs/section-ref.schemas.js.map +1 -0
  251. package/dist/esm/schemas/sections/website-section-category.shared.d.ts +63 -0
  252. package/dist/esm/schemas/sections/website-section-category.shared.d.ts.map +1 -0
  253. package/dist/esm/schemas/sections/website-section-category.shared.js +64 -0
  254. package/dist/esm/schemas/sections/website-section-category.shared.js.map +1 -0
  255. package/dist/esm/schemas/sections/website-section-definition.shared.schemas.d.ts +114 -0
  256. package/dist/esm/schemas/sections/website-section-definition.shared.schemas.d.ts.map +1 -0
  257. package/dist/esm/schemas/sections/website-section-definition.shared.schemas.js +69 -0
  258. package/dist/esm/schemas/sections/website-section-definition.shared.schemas.js.map +1 -0
  259. package/dist/esm/schemas/structured-data/structured-data-reconciliation.shared.d.ts +100 -0
  260. package/dist/esm/schemas/structured-data/structured-data-reconciliation.shared.d.ts.map +1 -0
  261. package/dist/esm/schemas/structured-data/structured-data-reconciliation.shared.js +355 -0
  262. package/dist/esm/schemas/structured-data/structured-data-reconciliation.shared.js.map +1 -0
  263. package/dist/esm/schemas/structured-data/website-structured-data.shared.schemas.d.ts +58 -0
  264. package/dist/esm/schemas/structured-data/website-structured-data.shared.schemas.d.ts.map +1 -0
  265. package/dist/esm/schemas/structured-data/website-structured-data.shared.schemas.js +2 -0
  266. package/dist/esm/schemas/structured-data/website-structured-data.shared.schemas.js.map +1 -0
  267. package/dist/esm/schemas/validators/label-pack.validator.d.ts +144 -0
  268. package/dist/esm/schemas/validators/label-pack.validator.d.ts.map +1 -0
  269. package/dist/esm/schemas/validators/label-pack.validator.js +100 -0
  270. package/dist/esm/schemas/validators/label-pack.validator.js.map +1 -0
  271. package/dist/tsconfig.build.tsbuildinfo +1 -0
  272. package/package.json +143 -0
  273. package/src/__tests__/bundle-isolation.test.ts +607 -0
  274. package/src/astro/__tests__/blog-post-bridge.test.tsx +288 -0
  275. package/src/astro/__tests__/blog-post-head.renderer.test.ts +249 -0
  276. package/src/astro/__tests__/bridge-runtime.test.tsx +198 -0
  277. package/src/astro/__tests__/collect-expected-label-keys.test.ts +69 -0
  278. package/src/astro/__tests__/headers-renderer.test.ts +143 -0
  279. package/src/astro/__tests__/i18n-routing.helper.test.ts +116 -0
  280. package/src/astro/__tests__/label-pack-loader.test.ts +305 -0
  281. package/src/astro/__tests__/llms-txt-server.test.ts +127 -0
  282. package/src/astro/__tests__/pick-labels-for-page.test.ts +60 -0
  283. package/src/astro/__tests__/robots-renderer.test.ts +228 -0
  284. package/src/astro/__tests__/sitemap-coverage.test.ts +230 -0
  285. package/src/astro/__tests__/structured-data.renderer.test.ts +172 -0
  286. package/src/astro/__tests__/website-page-head.renderer.test.ts +424 -0
  287. package/src/astro/__tests__/website-page-runtime.helper.test.ts +157 -0
  288. package/src/astro/__tests__/website-site-context.test.ts +121 -0
  289. package/src/astro/blog-post-bridge.tsx +404 -0
  290. package/src/astro/blog-post-head.renderer.ts +293 -0
  291. package/src/astro/bridge-runtime.tsx +270 -0
  292. package/src/astro/collect-expected-label-keys.ts +113 -0
  293. package/src/astro/headers-renderer.ts +82 -0
  294. package/src/astro/i18n-routing.helper.ts +116 -0
  295. package/src/astro/internal/html-escape.ts +41 -0
  296. package/src/astro/label-pack-loader.ts +385 -0
  297. package/src/astro/llms-txt-server.ts +56 -0
  298. package/src/astro/pick-labels-for-page.ts +85 -0
  299. package/src/astro/robots-renderer.ts +171 -0
  300. package/src/astro/sitemap-coverage.ts +186 -0
  301. package/src/astro/structured-data.renderer.ts +171 -0
  302. package/src/astro/website-page-head.renderer.ts +279 -0
  303. package/src/astro/website-page-runtime.helper.ts +188 -0
  304. package/src/astro/website-site-context.ts +136 -0
  305. package/src/astro-island.ts +61 -0
  306. package/src/astro.ts +151 -0
  307. package/src/companion-exports.ts +38 -0
  308. package/src/components/low-level/WebsiteButton.tsx +118 -0
  309. package/src/components/low-level/WebsiteHeading.tsx +96 -0
  310. package/src/components/low-level/WebsiteImage.tsx +96 -0
  311. package/src/components/low-level/WebsiteInternalButton.tsx +172 -0
  312. package/src/components/low-level/WebsiteInternalLink.tsx +193 -0
  313. package/src/components/low-level/WebsiteLink.tsx +71 -0
  314. package/src/components/low-level/WebsiteText.tsx +75 -0
  315. package/src/components/low-level/__tests__/WebsiteInternalButton.test.tsx +169 -0
  316. package/src/components/low-level/__tests__/WebsiteInternalLink.test.tsx +202 -0
  317. package/src/components/low-level/__tests__/primitives.test.tsx +141 -0
  318. package/src/config/__tests__/define-website-config.test.ts +231 -0
  319. package/src/config/__tests__/load-website-config.test.ts +215 -0
  320. package/src/config/define-website-config.ts +134 -0
  321. package/src/config/index.ts +38 -0
  322. package/src/config/load-website-config.ts +229 -0
  323. package/src/config/wildo-website-config.schemas.ts +322 -0
  324. package/src/config-loader.ts +35 -0
  325. package/src/core/__tests__/WebsitePageLayout.test.tsx +184 -0
  326. package/src/core/__tests__/WebsiteSection.test.tsx +85 -0
  327. package/src/core/__tests__/contexts.test.tsx +133 -0
  328. package/src/core/__tests__/core-boundary.test.ts +85 -0
  329. package/src/core/__tests__/define-website-page-manifest.test.tsx +111 -0
  330. package/src/core/__tests__/define-website-section.test.tsx +89 -0
  331. package/src/core/__tests__/useWebsiteDesignTokens.test.tsx +41 -0
  332. package/src/core/__tests__/useWebsiteLabel.test.tsx +117 -0
  333. package/src/core/anonymous-session/InboundContactForm.tsx +157 -0
  334. package/src/core/anonymous-session/__tests__/inbound-contact-form.schema.test.ts +44 -0
  335. package/src/core/anonymous-session/inbound-contact-form.schema.ts +62 -0
  336. package/src/core/anonymous-session/website-anonymous-session-client.ts +194 -0
  337. package/src/core/contexts/WebsitePageContext.tsx +52 -0
  338. package/src/core/contexts/WebsiteRuntimeContext.tsx +175 -0
  339. package/src/core/contexts/WebsiteSectionContext.tsx +59 -0
  340. package/src/core/contexts/useWebsitePage.ts +28 -0
  341. package/src/core/contexts/useWebsiteRuntime.ts +34 -0
  342. package/src/core/contexts/useWebsiteSection.ts +28 -0
  343. package/src/core/external-providers/frontend-provider-registry.website.ts +62 -0
  344. package/src/core/factories/define-website-page-manifest.ts +102 -0
  345. package/src/core/factories/define-website-section.ts +128 -0
  346. package/src/core/hooks/useWebsiteDesignTokens.ts +26 -0
  347. package/src/core/hooks/useWebsiteLabel.ts +121 -0
  348. package/src/core/layouts/WebsitePageLayout.tsx +311 -0
  349. package/src/core/layouts/WebsiteSection.tsx +139 -0
  350. package/src/core/routing/__tests__/internal-routing.utils.test.ts +134 -0
  351. package/src/core/routing/internal-routing.utils.ts +77 -0
  352. package/src/index.ts +132 -0
  353. package/src/mdx/BlogPost.tsx +296 -0
  354. package/src/mdx/__tests__/BlogPost.test.tsx +218 -0
  355. package/src/mdx/__tests__/blog-post-collection.config.test.ts +127 -0
  356. package/src/mdx/__tests__/website-mdx-provider.test.tsx +82 -0
  357. package/src/mdx/blog-post-collection.config.ts +177 -0
  358. package/src/mdx/blog-post-frontmatter-source.ts +31 -0
  359. package/src/mdx/index.ts +39 -0
  360. package/src/mdx/website-mdx-provider.tsx +95 -0
  361. package/src/mdx.ts +38 -0
  362. package/src/schemas/__tests__/blog-frontmatter.test.ts +50 -0
  363. package/src/schemas/__tests__/companion-exports-react-free.test.ts +51 -0
  364. package/src/schemas/__tests__/design-tokens.test.ts +163 -0
  365. package/src/schemas/__tests__/label-key.test.ts +46 -0
  366. package/src/schemas/__tests__/label-pack-validator.test.ts +146 -0
  367. package/src/schemas/__tests__/page-manifest.test.ts +66 -0
  368. package/src/schemas/__tests__/refs.test.ts +37 -0
  369. package/src/schemas/__tests__/root-config.test.ts +292 -0
  370. package/src/schemas/__tests__/section-definition.test.ts +47 -0
  371. package/src/schemas/__tests__/structured-data-reconciliation.test.ts +543 -0
  372. package/src/schemas/blog/blog-post-frontmatter.shared.schemas.ts +111 -0
  373. package/src/schemas/design-tokens/website-design-tokens.shared.schemas.ts +166 -0
  374. package/src/schemas/label-keys/website-label-key.schemas.ts +88 -0
  375. package/src/schemas/manifests/website-page-manifest.shared.schemas.ts +266 -0
  376. package/src/schemas/manifests/website-root-config.shared.schemas.ts +504 -0
  377. package/src/schemas/refs/page-ref.schemas.ts +49 -0
  378. package/src/schemas/refs/section-ref.schemas.ts +50 -0
  379. package/src/schemas/sections/website-section-category.shared.ts +65 -0
  380. package/src/schemas/sections/website-section-definition.shared.schemas.ts +120 -0
  381. package/src/schemas/structured-data/structured-data-reconciliation.shared.ts +579 -0
  382. package/src/schemas/structured-data/website-structured-data.shared.schemas.ts +62 -0
  383. package/src/schemas/validators/label-pack.validator.ts +201 -0
@@ -0,0 +1,251 @@
1
+ /**
2
+ * @wildo_source:part:start engine.saas-website.config facet:layer:engine facet:family:marketing-website
3
+ *
4
+ * Per-service marketing-website configuration block, authored in
5
+ * the website service folder as `wildo.website.config.ts`.
6
+ *
7
+ * # What this file's loaded value is used for TODAY
8
+ *
9
+ * This schema is the single source of truth for the marketing-site routing
10
+ * and origin surface. Every consumer reads here:
11
+ *
12
+ * - `defaultLocale` + `locales` — read by `buildAstroI18nConfig(...)`,
13
+ * `assertLabelPacksValid(...)`, `loadWebsitePageRuntime(...)` and
14
+ * `renderWebsitePageHeadHtml(...)` via the shared `WebsiteSiteContext`.
15
+ * - `publicOrigin` — read by the head renderer to compose absolute
16
+ * `<link rel="canonical">`, `og:url`, hreflang `href` URLs and
17
+ * the structured-data `mainEntityOfPage`. The previous
18
+ * `WebsiteRootConfig.seo.siteOrigin` was deleted.
19
+ * - `appOrigin` — has NO runtime consumer today. The
20
+ * marketing → app handoff link surface that will read it does
21
+ * not exist yet. Authored eagerly so the link-surface PR is a
22
+ * single framework-owned move; until then the field is exercised
23
+ * only by the schema parser.
24
+ * - `csp?` — read by the website-local `sync-nginx-csp` build step,
25
+ * which merges the app-static posture with `infra.csp?.<serviceKey>`,
26
+ * emits the generated artifact consumed by Astro pages, and rewrites
27
+ * the checked-in nginx marker file used by the website Docker image.
28
+ *
29
+ * Future companion subprocesses that introspect the marketing
30
+ * surface (locale-coverage audits, label-pack validation against
31
+ * authored locales, SEO-link sanity checks) will also read here —
32
+ * none exist today.
33
+ *
34
+ * # Why this is per-service website configuration
35
+ *
36
+ * Co-locating the schema with the engine package that owns its semantics
37
+ * keeps three concerns separate from application-root service declarations:
38
+ *
39
+ * 1. **Right reuse axis.** Marketing-site content (locales, SEO origins,
40
+ * future label-pack overrides, marketing-only CTAs) is conceptually
41
+ * different from saas-app-level service declarations (`path`,
42
+ * `serviceName`, `frontendType`, `sessionStorage`). The marketing
43
+ * config travels with the website service folder, NOT with the
44
+ * SaaS app root. Two unrelated lifecycles, two different review
45
+ * audiences.
46
+ * 2. **Engine boundary.** `platform-config-lib` no longer needs to
47
+ * depend on `@wildo-ai/presets-components-models` (the home of
48
+ * `AvailableLanguage`) just to type the marketing-site locale
49
+ * enum. Adding a new website primitive (a new SEO field, a new
50
+ * locale-pack id) belongs with the website package and its public
51
+ * configuration contract.
52
+ * 3. **Disambiguator hazard.** The previous root-level
53
+ * `marketingWebsite?` block silently assumed "one STATIC_WEBSITE
54
+ * service per SaaS app" — but nothing in the schema enforced it.
55
+ * Two STATIC_WEBSITE services would have silently shared one
56
+ * config block with no obvious "which one wins?" answer. The
57
+ * application configuration rejects more than one STATIC_WEBSITE (and
58
+ * TECH_DOC) service, so the per-service config-file lookup is
59
+ * deterministic by construction.
60
+ *
61
+ * # Schema decisions encoded here
62
+ *
63
+ * - `z.strictObject` so authoring an unknown key surfaces as a parse
64
+ * error at `defineWebsiteConfig` time instead of being silently
65
+ * dropped (same posture as `WildoSaasConfigSchema` and
66
+ * `WildoTechnicalDocConfigSchema`).
67
+ *
68
+ * - **No `enabled` field.** The gate is "is there a `wildo.website.config.ts`
69
+ * in a `frontendType: STATIC_WEBSITE` service folder?" — file
70
+ * presence under a STATIC_WEBSITE service IS the enable signal.
71
+ * The Astro build / dev companion / future audit CLI requires the
72
+ * file when it walks a STATIC_WEBSITE service folder, so a
73
+ * half-authored config simply cannot exist (the loader fails
74
+ * closed). Removing the boolean closes a foot-gun: an authored
75
+ * `enabled: false` block was previously a silent way to keep the
76
+ * config "warm" without shipping anything, and consumers had to
77
+ * remember to read the flag in addition to the file presence — a
78
+ * future read-the-config / forget-the-flag bug waiting to happen.
79
+ * A separate `services.<key>.enabled` pattern (orthogonal to
80
+ * marketing-website) is the right place for any "temporarily
81
+ * disable this service" story (deferred — see DASHBOARD).
82
+ *
83
+ * - URL fields are `z.url()` (Zod 4 native validator) so we get
84
+ * protocol + host parsing for free, matching the rest of the
85
+ * codebase.
86
+ *
87
+ * - `csp?` is typed against `WildoCspConfigSchema` from
88
+ * `@wildo-ai/platform-config-lib`, mirroring the docs service's
89
+ * `wildo.tech-doc.config.ts` contract. The parsed value is consumed by
90
+ * the shared `resolveCspPolicy()` / `emitCspArtifacts()` generator flow.
91
+ *
92
+ * - `defaultLocale ∈ locales` cross-check via `.superRefine` —
93
+ * keeping `defaultLocale` as a top-level field is product-explicit
94
+ * ("which locale URL is the canonical one") and decouples it from
95
+ * list ordering. Locale-path uniqueness AND locale-value
96
+ * uniqueness are also enforced — duplicate paths would silently
97
+ * overwrite each other in the Astro build and corrupt the sitemap;
98
+ * duplicate locales would produce duplicate hreflang entries.
99
+ *
100
+ * - Locale primitive is `AvailableLanguage`. The schema imports it
101
+ * from `@wildo-ai/presets-components-models/public-runtime` (the
102
+ * side-effect-free public-runtime seam), but **consumers authoring
103
+ * `wildo.website.config.ts` must import it from
104
+ * `@wildo-ai/saas-models/public-runtime`**. Both paths produce identical
105
+ * values; saas-models simply re-exports the presets atom. Picking
106
+ * one path consistently across consumer files prevents drift in
107
+ * docs / examples / search results. Values authored in
108
+ * `wildo.website.config.ts` are the lowercase BCP-47 strings
109
+ * (`'en'`, `'fr'`), NOT the UPPERCASE TS enum member names
110
+ * (`EN`, `FR`). `z.enum(AvailableLanguage)` parses against the
111
+ * enum VALUES, which is what we want.
112
+ */
113
+ import { z } from 'zod';
114
+ import { AvailableLanguage } from '@wildo-ai/presets-components-models/public-runtime';
115
+ /**
116
+ * Per-locale configuration. Each authored locale gets one entry.
117
+ *
118
+ * `path` is the locale URL prefix segment (e.g. `'en'`, `'fr'`) — the
119
+ * Astro builder maps `/en/...` and `/fr/...` paths via this value. We
120
+ * do NOT auto-derive `path` from the locale string because some
121
+ * deployments may want a flat root for the default locale (`/...`
122
+ * instead of `/en/...`); keeping it explicit per locale leaves that
123
+ * door open without a config-shape change.
124
+ */
125
+ export declare const WebsiteLocaleConfigSchema: z.ZodObject<{
126
+ locale: z.ZodEnum<typeof AvailableLanguage>;
127
+ path: z.ZodString;
128
+ }, z.core.$strict>;
129
+ export type WebsiteLocaleConfig = z.infer<typeof WebsiteLocaleConfigSchema>;
130
+ /**
131
+ * Per-service marketing-website configuration block.
132
+ *
133
+ * Authored as `export default defineWebsiteConfig({...})` in
134
+ * `<website-service-root>/wildo.website.config.ts`. The Astro
135
+ * build loads this file once at bootstrap via jiti and composes
136
+ * a `WebsiteSiteContext`; helpers consume that composed context rather than
137
+ * this raw value.
138
+ *
139
+ * @example
140
+ * ```ts
141
+ * import { AvailableLanguage } from '@wildo-ai/saas-models/public-runtime';
142
+ * import { defineWebsiteConfig } from '@wildo-ai/saas-website';
143
+ *
144
+ * export default defineWebsiteConfig({
145
+ * publicOrigin: 'https://example.com',
146
+ * appOrigin: 'https://app.example.com',
147
+ * defaultLocale: AvailableLanguage.EN,
148
+ * locales: [
149
+ * { locale: AvailableLanguage.EN, path: 'en' },
150
+ * { locale: AvailableLanguage.FR, path: 'fr' },
151
+ * ],
152
+ * csp: { enabled: true, reportOnly: true },
153
+ * });
154
+ * ```
155
+ */
156
+ export declare const WildoMarketingWebsiteConfigSchema: z.ZodObject<{
157
+ /**
158
+ * Public origin of the marketing site (apex / www) — canonical
159
+ * input the Astro head renderer emits as absolute URLs
160
+ * (`<link rel="canonical">`, `og:url`, hreflang `href`,
161
+ * sitemap base, structured-data `mainEntityOfPage`).
162
+ *
163
+ * MUST NOT end with `/` — the head renderer composes
164
+ * `${publicOrigin}${routePath}` and a trailing slash would emit
165
+ * `https://example.com//`. Surface the constraint loudly here
166
+ * rather than silently corrupting URLs at build time.
167
+ */
168
+ publicOrigin: z.ZodURL;
169
+ /**
170
+ * Origin of the SaaS application the marketing site links into
171
+ * (signup / login / pricing → checkout).
172
+ *
173
+ * **Today**: NO runtime consumer. The cross-origin handoff
174
+ * surface that will read it does not exist yet. Authored
175
+ * eagerly so the link-surface PR is a single framework-owned move
176
+ * — until then the field is exercised only by the schema
177
+ * parser.
178
+ *
179
+ * Distinct from the general `WildoSaasConfig.services.<name>`
180
+ * URL surface because the marketing site is a separate Astro
181
+ * project that does not load the full SaaS app graph at build
182
+ * time.
183
+ */
184
+ appOrigin: z.ZodURL;
185
+ /**
186
+ * Backend API origin the marketing site POSTs to for anonymous
187
+ * captures (lead / draft-note forms) — e.g.
188
+ * `https://api.example.com`. Threaded through
189
+ * `WebsiteRuntimeContext.apiOrigin` into the React island that
190
+ * constructs the `WebsiteAnonymousSessionClient`.
191
+ *
192
+ * OPTIONAL because a pure brochure site with no capture forms never
193
+ * talks to an API — only sites that host an anonymous-capture
194
+ * section need it. When such a section renders without a resolved
195
+ * `apiOrigin`, the section surfaces a loud misconfiguration rather
196
+ * than silently POSTing nowhere.
197
+ *
198
+ * MUST NOT end with `/` — the anon client composes
199
+ * `${apiOrigin}${MAIN_API_BASE_PATH}${path}` and a trailing slash
200
+ * would emit a doubled `//` in the request URL. (The client also
201
+ * defensively strips trailing slashes, but the refine surfaces the
202
+ * mistake at authoring time.)
203
+ *
204
+ * Distinct from `appOrigin` (the human-facing app the marketing CTAs
205
+ * link into) and `publicOrigin` (the marketing site's own canonical
206
+ * origin): this is the machine-to-machine API host.
207
+ */
208
+ apiOrigin: z.ZodOptional<z.ZodURL>;
209
+ /**
210
+ * Optional Content-Security-Policy override for the marketing site.
211
+ * Consumed by the website-local `sync-nginx-csp` step, which emits
212
+ * the generated artifact for Astro page injection and rewrites the
213
+ * website nginx marker file from the same resolved output.
214
+ */
215
+ csp: z.ZodOptional<z.ZodObject<{
216
+ enabled: z.ZodDefault<z.ZodBoolean>;
217
+ reportOnly: z.ZodDefault<z.ZodBoolean>;
218
+ mergeStrategy: z.ZodDefault<z.ZodEnum<typeof import("@wildo-ai/platform-config-lib").CspMergeStrategy>>;
219
+ directives: z.ZodDefault<z.ZodObject<Record<"default-src" | "script-src" | "style-src" | "img-src" | "connect-src" | "font-src" | "frame-src" | "frame-ancestors" | "object-src" | "form-action" | "base-uri" | "manifest-src" | "media-src" | "worker-src", z.ZodOptional<z.ZodArray<z.ZodString>>>, z.core.$strict>>;
220
+ reportUri: z.ZodOptional<z.ZodString>;
221
+ }, z.core.$strict>>;
222
+ /**
223
+ * Default locale the marketing site serves for unmatched URLs
224
+ * and as the canonical hreflang signal. Must appear in
225
+ * `locales` below (cross-check enforced by the schema-level
226
+ * `.superRefine`).
227
+ *
228
+ * Read by `buildAstroI18nConfig(...)` (drives Astro's own
229
+ * `i18n.defaultLocale`) and by the head renderer (drives
230
+ * `<html lang="...">`, `og:locale`, hreflang `x-default`).
231
+ */
232
+ defaultLocale: z.ZodEnum<typeof AvailableLanguage>;
233
+ /**
234
+ * Ordered list of locales the marketing site builds. Order is
235
+ * preserved (used by the language switcher rendering and by
236
+ * the sitemap emitter — when the sitemap surface lands).
237
+ *
238
+ * Read by `buildAstroI18nConfig(...)` (Astro `i18n.locales`),
239
+ * by `assertLabelPacksValid(...)` (one label pack per locale),
240
+ * by `loadWebsitePageRuntime(...)` (per-page locale resolution),
241
+ * and by the head renderer (hreflang alternates).
242
+ */
243
+ locales: z.ZodArray<z.ZodObject<{
244
+ locale: z.ZodEnum<typeof AvailableLanguage>;
245
+ path: z.ZodString;
246
+ }, z.core.$strict>>;
247
+ }, z.core.$strict>;
248
+ export type WildoMarketingWebsiteConfig = z.infer<typeof WildoMarketingWebsiteConfigSchema>;
249
+ export type WildoMarketingWebsiteConfigInput = z.input<typeof WildoMarketingWebsiteConfigSchema>;
250
+ /** @wildo_source:part:end engine.saas-website.config */
251
+ //# sourceMappingURL=wildo-website-config.schemas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wildo-website-config.schemas.d.ts","sourceRoot":"","sources":["../../../../src/config/wildo-website-config.schemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+GG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAE,MAAM,oDAAoD,CAAC;AAEvF;;;;;;;;;GASG;AACH,eAAO,MAAM,yBAAyB;;;kBASpC,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,iCAAiC;IAE1C;;;;;;;;;;OAUG;;IASH;;;;;;;;;;;;;;OAcG;;IAGH;;;;;;;;;;;;;;;;;;;;;;OAsBG;;IAWH;;;;;OAKG;;;;;;;;IAGH;;;;;;;;;OASG;;IAGH;;;;;;;;;OASG;;;;;kBAqDH,CAAC;AACL,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC;AAC5F,MAAM,MAAM,gCAAgC,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC;AACjG,wDAAwD"}
@@ -0,0 +1,302 @@
1
+ /**
2
+ * @wildo_source:part:start engine.saas-website.config facet:layer:engine facet:family:marketing-website
3
+ *
4
+ * Per-service marketing-website configuration block, authored in
5
+ * the website service folder as `wildo.website.config.ts`.
6
+ *
7
+ * # What this file's loaded value is used for TODAY
8
+ *
9
+ * This schema is the single source of truth for the marketing-site routing
10
+ * and origin surface. Every consumer reads here:
11
+ *
12
+ * - `defaultLocale` + `locales` — read by `buildAstroI18nConfig(...)`,
13
+ * `assertLabelPacksValid(...)`, `loadWebsitePageRuntime(...)` and
14
+ * `renderWebsitePageHeadHtml(...)` via the shared `WebsiteSiteContext`.
15
+ * - `publicOrigin` — read by the head renderer to compose absolute
16
+ * `<link rel="canonical">`, `og:url`, hreflang `href` URLs and
17
+ * the structured-data `mainEntityOfPage`. The previous
18
+ * `WebsiteRootConfig.seo.siteOrigin` was deleted.
19
+ * - `appOrigin` — has NO runtime consumer today. The
20
+ * marketing → app handoff link surface that will read it does
21
+ * not exist yet. Authored eagerly so the link-surface PR is a
22
+ * single framework-owned move; until then the field is exercised
23
+ * only by the schema parser.
24
+ * - `csp?` — read by the website-local `sync-nginx-csp` build step,
25
+ * which merges the app-static posture with `infra.csp?.<serviceKey>`,
26
+ * emits the generated artifact consumed by Astro pages, and rewrites
27
+ * the checked-in nginx marker file used by the website Docker image.
28
+ *
29
+ * Future companion subprocesses that introspect the marketing
30
+ * surface (locale-coverage audits, label-pack validation against
31
+ * authored locales, SEO-link sanity checks) will also read here —
32
+ * none exist today.
33
+ *
34
+ * # Why this is per-service website configuration
35
+ *
36
+ * Co-locating the schema with the engine package that owns its semantics
37
+ * keeps three concerns separate from application-root service declarations:
38
+ *
39
+ * 1. **Right reuse axis.** Marketing-site content (locales, SEO origins,
40
+ * future label-pack overrides, marketing-only CTAs) is conceptually
41
+ * different from saas-app-level service declarations (`path`,
42
+ * `serviceName`, `frontendType`, `sessionStorage`). The marketing
43
+ * config travels with the website service folder, NOT with the
44
+ * SaaS app root. Two unrelated lifecycles, two different review
45
+ * audiences.
46
+ * 2. **Engine boundary.** `platform-config-lib` no longer needs to
47
+ * depend on `@wildo-ai/presets-components-models` (the home of
48
+ * `AvailableLanguage`) just to type the marketing-site locale
49
+ * enum. Adding a new website primitive (a new SEO field, a new
50
+ * locale-pack id) belongs with the website package and its public
51
+ * configuration contract.
52
+ * 3. **Disambiguator hazard.** The previous root-level
53
+ * `marketingWebsite?` block silently assumed "one STATIC_WEBSITE
54
+ * service per SaaS app" — but nothing in the schema enforced it.
55
+ * Two STATIC_WEBSITE services would have silently shared one
56
+ * config block with no obvious "which one wins?" answer. The
57
+ * application configuration rejects more than one STATIC_WEBSITE (and
58
+ * TECH_DOC) service, so the per-service config-file lookup is
59
+ * deterministic by construction.
60
+ *
61
+ * # Schema decisions encoded here
62
+ *
63
+ * - `z.strictObject` so authoring an unknown key surfaces as a parse
64
+ * error at `defineWebsiteConfig` time instead of being silently
65
+ * dropped (same posture as `WildoSaasConfigSchema` and
66
+ * `WildoTechnicalDocConfigSchema`).
67
+ *
68
+ * - **No `enabled` field.** The gate is "is there a `wildo.website.config.ts`
69
+ * in a `frontendType: STATIC_WEBSITE` service folder?" — file
70
+ * presence under a STATIC_WEBSITE service IS the enable signal.
71
+ * The Astro build / dev companion / future audit CLI requires the
72
+ * file when it walks a STATIC_WEBSITE service folder, so a
73
+ * half-authored config simply cannot exist (the loader fails
74
+ * closed). Removing the boolean closes a foot-gun: an authored
75
+ * `enabled: false` block was previously a silent way to keep the
76
+ * config "warm" without shipping anything, and consumers had to
77
+ * remember to read the flag in addition to the file presence — a
78
+ * future read-the-config / forget-the-flag bug waiting to happen.
79
+ * A separate `services.<key>.enabled` pattern (orthogonal to
80
+ * marketing-website) is the right place for any "temporarily
81
+ * disable this service" story (deferred — see DASHBOARD).
82
+ *
83
+ * - URL fields are `z.url()` (Zod 4 native validator) so we get
84
+ * protocol + host parsing for free, matching the rest of the
85
+ * codebase.
86
+ *
87
+ * - `csp?` is typed against `WildoCspConfigSchema` from
88
+ * `@wildo-ai/platform-config-lib`, mirroring the docs service's
89
+ * `wildo.tech-doc.config.ts` contract. The parsed value is consumed by
90
+ * the shared `resolveCspPolicy()` / `emitCspArtifacts()` generator flow.
91
+ *
92
+ * - `defaultLocale ∈ locales` cross-check via `.superRefine` —
93
+ * keeping `defaultLocale` as a top-level field is product-explicit
94
+ * ("which locale URL is the canonical one") and decouples it from
95
+ * list ordering. Locale-path uniqueness AND locale-value
96
+ * uniqueness are also enforced — duplicate paths would silently
97
+ * overwrite each other in the Astro build and corrupt the sitemap;
98
+ * duplicate locales would produce duplicate hreflang entries.
99
+ *
100
+ * - Locale primitive is `AvailableLanguage`. The schema imports it
101
+ * from `@wildo-ai/presets-components-models/public-runtime` (the
102
+ * side-effect-free public-runtime seam), but **consumers authoring
103
+ * `wildo.website.config.ts` must import it from
104
+ * `@wildo-ai/saas-models/public-runtime`**. Both paths produce identical
105
+ * values; saas-models simply re-exports the presets atom. Picking
106
+ * one path consistently across consumer files prevents drift in
107
+ * docs / examples / search results. Values authored in
108
+ * `wildo.website.config.ts` are the lowercase BCP-47 strings
109
+ * (`'en'`, `'fr'`), NOT the UPPERCASE TS enum member names
110
+ * (`EN`, `FR`). `z.enum(AvailableLanguage)` parses against the
111
+ * enum VALUES, which is what we want.
112
+ */
113
+ import { WildoCspConfigSchema } from '@wildo-ai/platform-config-lib';
114
+ import { z } from 'zod';
115
+ import { AvailableLanguage } from '@wildo-ai/presets-components-models/public-runtime';
116
+ /**
117
+ * Per-locale configuration. Each authored locale gets one entry.
118
+ *
119
+ * `path` is the locale URL prefix segment (e.g. `'en'`, `'fr'`) — the
120
+ * Astro builder maps `/en/...` and `/fr/...` paths via this value. We
121
+ * do NOT auto-derive `path` from the locale string because some
122
+ * deployments may want a flat root for the default locale (`/...`
123
+ * instead of `/en/...`); keeping it explicit per locale leaves that
124
+ * door open without a config-shape change.
125
+ */
126
+ export const WebsiteLocaleConfigSchema = z.strictObject({
127
+ locale: z.enum(AvailableLanguage),
128
+ path: z
129
+ .string()
130
+ .min(1)
131
+ .regex(/^[a-z0-9-]+$/, 'locale path must be lowercase alphanumeric with hyphens (becomes a URL segment)'),
132
+ });
133
+ /**
134
+ * Per-service marketing-website configuration block.
135
+ *
136
+ * Authored as `export default defineWebsiteConfig({...})` in
137
+ * `<website-service-root>/wildo.website.config.ts`. The Astro
138
+ * build loads this file once at bootstrap via jiti and composes
139
+ * a `WebsiteSiteContext`; helpers consume that composed context rather than
140
+ * this raw value.
141
+ *
142
+ * @example
143
+ * ```ts
144
+ * import { AvailableLanguage } from '@wildo-ai/saas-models/public-runtime';
145
+ * import { defineWebsiteConfig } from '@wildo-ai/saas-website';
146
+ *
147
+ * export default defineWebsiteConfig({
148
+ * publicOrigin: 'https://example.com',
149
+ * appOrigin: 'https://app.example.com',
150
+ * defaultLocale: AvailableLanguage.EN,
151
+ * locales: [
152
+ * { locale: AvailableLanguage.EN, path: 'en' },
153
+ * { locale: AvailableLanguage.FR, path: 'fr' },
154
+ * ],
155
+ * csp: { enabled: true, reportOnly: true },
156
+ * });
157
+ * ```
158
+ */
159
+ export const WildoMarketingWebsiteConfigSchema = z
160
+ .strictObject({
161
+ /**
162
+ * Public origin of the marketing site (apex / www) — canonical
163
+ * input the Astro head renderer emits as absolute URLs
164
+ * (`<link rel="canonical">`, `og:url`, hreflang `href`,
165
+ * sitemap base, structured-data `mainEntityOfPage`).
166
+ *
167
+ * MUST NOT end with `/` — the head renderer composes
168
+ * `${publicOrigin}${routePath}` and a trailing slash would emit
169
+ * `https://example.com//`. Surface the constraint loudly here
170
+ * rather than silently corrupting URLs at build time.
171
+ */
172
+ publicOrigin: z
173
+ .url()
174
+ .refine((value) => !value.endsWith('/'), {
175
+ error: 'WildoMarketingWebsiteConfig.publicOrigin must NOT end with "/" (the head ' +
176
+ 'renderer concatenates publicOrigin + routePath; trailing slash would emit "//")',
177
+ }),
178
+ /**
179
+ * Origin of the SaaS application the marketing site links into
180
+ * (signup / login / pricing → checkout).
181
+ *
182
+ * **Today**: NO runtime consumer. The cross-origin handoff
183
+ * surface that will read it does not exist yet. Authored
184
+ * eagerly so the link-surface PR is a single framework-owned move
185
+ * — until then the field is exercised only by the schema
186
+ * parser.
187
+ *
188
+ * Distinct from the general `WildoSaasConfig.services.<name>`
189
+ * URL surface because the marketing site is a separate Astro
190
+ * project that does not load the full SaaS app graph at build
191
+ * time.
192
+ */
193
+ appOrigin: z.url(),
194
+ /**
195
+ * Backend API origin the marketing site POSTs to for anonymous
196
+ * captures (lead / draft-note forms) — e.g.
197
+ * `https://api.example.com`. Threaded through
198
+ * `WebsiteRuntimeContext.apiOrigin` into the React island that
199
+ * constructs the `WebsiteAnonymousSessionClient`.
200
+ *
201
+ * OPTIONAL because a pure brochure site with no capture forms never
202
+ * talks to an API — only sites that host an anonymous-capture
203
+ * section need it. When such a section renders without a resolved
204
+ * `apiOrigin`, the section surfaces a loud misconfiguration rather
205
+ * than silently POSTing nowhere.
206
+ *
207
+ * MUST NOT end with `/` — the anon client composes
208
+ * `${apiOrigin}${MAIN_API_BASE_PATH}${path}` and a trailing slash
209
+ * would emit a doubled `//` in the request URL. (The client also
210
+ * defensively strips trailing slashes, but the refine surfaces the
211
+ * mistake at authoring time.)
212
+ *
213
+ * Distinct from `appOrigin` (the human-facing app the marketing CTAs
214
+ * link into) and `publicOrigin` (the marketing site's own canonical
215
+ * origin): this is the machine-to-machine API host.
216
+ */
217
+ apiOrigin: z
218
+ .url()
219
+ .refine((value) => !value.endsWith('/'), {
220
+ error: 'WildoMarketingWebsiteConfig.apiOrigin must NOT end with "/" (the anonymous ' +
221
+ 'client concatenates apiOrigin + MAIN_API_BASE_PATH + path; a trailing slash ' +
222
+ 'would emit "//")',
223
+ })
224
+ .optional(),
225
+ /**
226
+ * Optional Content-Security-Policy override for the marketing site.
227
+ * Consumed by the website-local `sync-nginx-csp` step, which emits
228
+ * the generated artifact for Astro page injection and rewrites the
229
+ * website nginx marker file from the same resolved output.
230
+ */
231
+ csp: WildoCspConfigSchema.optional(),
232
+ /**
233
+ * Default locale the marketing site serves for unmatched URLs
234
+ * and as the canonical hreflang signal. Must appear in
235
+ * `locales` below (cross-check enforced by the schema-level
236
+ * `.superRefine`).
237
+ *
238
+ * Read by `buildAstroI18nConfig(...)` (drives Astro's own
239
+ * `i18n.defaultLocale`) and by the head renderer (drives
240
+ * `<html lang="...">`, `og:locale`, hreflang `x-default`).
241
+ */
242
+ defaultLocale: z.enum(AvailableLanguage),
243
+ /**
244
+ * Ordered list of locales the marketing site builds. Order is
245
+ * preserved (used by the language switcher rendering and by
246
+ * the sitemap emitter — when the sitemap surface lands).
247
+ *
248
+ * Read by `buildAstroI18nConfig(...)` (Astro `i18n.locales`),
249
+ * by `assertLabelPacksValid(...)` (one label pack per locale),
250
+ * by `loadWebsitePageRuntime(...)` (per-page locale resolution),
251
+ * and by the head renderer (hreflang alternates).
252
+ */
253
+ locales: z.array(WebsiteLocaleConfigSchema).min(1),
254
+ })
255
+ .superRefine((config, ctx) => {
256
+ const declaredLocales = new Set(config.locales.map((entry) => entry.locale));
257
+ if (!declaredLocales.has(config.defaultLocale)) {
258
+ ctx.addIssue({
259
+ code: 'custom',
260
+ path: ['defaultLocale'],
261
+ message: `defaultLocale '${config.defaultLocale}' is not declared in 'locales' ` +
262
+ `(declared: [${[...declaredLocales].join(', ')}]). The default locale ` +
263
+ `must be one of the locales the site actually builds.`,
264
+ });
265
+ }
266
+ // Locale-path uniqueness: two entries collapsing to the same URL
267
+ // segment would silently overwrite each other in the Astro build
268
+ // and corrupt the sitemap. Fail closed at parse time.
269
+ const pathOwners = new Map();
270
+ for (const entry of config.locales) {
271
+ const previous = pathOwners.get(entry.path);
272
+ if (previous !== undefined) {
273
+ ctx.addIssue({
274
+ code: 'custom',
275
+ path: ['locales'],
276
+ message: `Locale path '${entry.path}' is declared by both '${previous}' and ` +
277
+ `'${entry.locale}'. Each locale must have a unique URL path segment.`,
278
+ });
279
+ continue;
280
+ }
281
+ pathOwners.set(entry.path, entry.locale);
282
+ }
283
+ // Locale-value uniqueness: same locale appearing twice would
284
+ // produce duplicate hreflang entries and ambiguous default-locale
285
+ // resolution.
286
+ const localeOwners = new Map();
287
+ for (const entry of config.locales) {
288
+ const previousPath = localeOwners.get(entry.locale);
289
+ if (previousPath !== undefined) {
290
+ ctx.addIssue({
291
+ code: 'custom',
292
+ path: ['locales'],
293
+ message: `Locale '${entry.locale}' is declared twice (paths '${previousPath}' ` +
294
+ `and '${entry.path}'). Each locale must appear at most once.`,
295
+ });
296
+ continue;
297
+ }
298
+ localeOwners.set(entry.locale, entry.path);
299
+ }
300
+ });
301
+ /** @wildo_source:part:end engine.saas-website.config */
302
+ //# sourceMappingURL=wildo-website-config.schemas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wildo-website-config.schemas.js","sourceRoot":"","sources":["../../../../src/config/wildo-website-config.schemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+GG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAE,MAAM,oDAAoD,CAAC;AAEvF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,YAAY,CAAC;IACtD,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC;IACjC,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,KAAK,CACJ,cAAc,EACd,iFAAiF,CAClF;CACJ,CAAC,CAAC;AAGH;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAG,CAAC;KAC/C,YAAY,CAAC;IACZ;;;;;;;;;;OAUG;IACH,YAAY,EAAE,CAAC;SACZ,GAAG,EAAE;SACL,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE;QACvC,KAAK,EACH,2EAA2E;YAC3E,iFAAiF;KACpF,CAAC;IAEJ;;;;;;;;;;;;;;OAcG;IACH,SAAS,EAAE,CAAC,CAAC,GAAG,EAAE;IAElB;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,SAAS,EAAE,CAAC;SACT,GAAG,EAAE;SACL,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE;QACvC,KAAK,EACH,6EAA6E;YAC7E,8EAA8E;YAC9E,kBAAkB;KACrB,CAAC;SACD,QAAQ,EAAE;IAEb;;;;;OAKG;IACH,GAAG,EAAE,oBAAoB,CAAC,QAAQ,EAAE;IAEpC;;;;;;;;;OASG;IACH,aAAa,EAAE,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC;IAExC;;;;;;;;;OASG;IACH,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;CACnD,CAAC;KACD,WAAW,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE;IAC3B,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAC7E,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC;QAC/C,GAAG,CAAC,QAAQ,CAAC;YACX,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,CAAC,eAAe,CAAC;YACvB,OAAO,EACL,kBAAkB,MAAM,CAAC,aAAa,iCAAiC;gBACvE,eAAe,CAAC,GAAG,eAAe,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,yBAAyB;gBACvE,sDAAsD;SACzD,CAAC,CAAC;IACL,CAAC;IAED,iEAAiE;IACjE,iEAAiE;IACjE,sDAAsD;IACtD,MAAM,UAAU,GAAG,IAAI,GAAG,EAA6B,CAAC;IACxD,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnC,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,GAAG,CAAC,QAAQ,CAAC;gBACX,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,CAAC,SAAS,CAAC;gBACjB,OAAO,EACL,gBAAgB,KAAK,CAAC,IAAI,0BAA0B,QAAQ,QAAQ;oBACpE,IAAI,KAAK,CAAC,MAAM,qDAAqD;aACxE,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QACD,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC3C,CAAC;IAED,6DAA6D;IAC7D,kEAAkE;IAClE,cAAc;IACd,MAAM,YAAY,GAAG,IAAI,GAAG,EAA6B,CAAC;IAC1D,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnC,MAAM,YAAY,GAAG,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACpD,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;YAC/B,GAAG,CAAC,QAAQ,CAAC;gBACX,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,CAAC,SAAS,CAAC;gBACjB,OAAO,EACL,WAAW,KAAK,CAAC,MAAM,+BAA+B,YAAY,IAAI;oBACtE,QAAQ,KAAK,CAAC,IAAI,2CAA2C;aAChE,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QACD,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7C,CAAC;AACH,CAAC,CAAC,CAAC;AAGL,wDAAwD","sourcesContent":["/**\n * @wildo_source:part:start engine.saas-website.config facet:layer:engine facet:family:marketing-website\n *\n * Per-service marketing-website configuration block, authored in\n * the website service folder as `wildo.website.config.ts`.\n *\n * # What this file's loaded value is used for TODAY\n *\n * This schema is the single source of truth for the marketing-site routing\n * and origin surface. Every consumer reads here:\n *\n * - `defaultLocale` + `locales` — read by `buildAstroI18nConfig(...)`,\n * `assertLabelPacksValid(...)`, `loadWebsitePageRuntime(...)` and\n * `renderWebsitePageHeadHtml(...)` via the shared `WebsiteSiteContext`.\n * - `publicOrigin` — read by the head renderer to compose absolute\n * `<link rel=\"canonical\">`, `og:url`, hreflang `href` URLs and\n * the structured-data `mainEntityOfPage`. The previous\n * `WebsiteRootConfig.seo.siteOrigin` was deleted.\n * - `appOrigin` — has NO runtime consumer today. The\n * marketing → app handoff link surface that will read it does\n * not exist yet. Authored eagerly so the link-surface PR is a\n * single framework-owned move; until then the field is exercised\n * only by the schema parser.\n * - `csp?` — read by the website-local `sync-nginx-csp` build step,\n * which merges the app-static posture with `infra.csp?.<serviceKey>`,\n * emits the generated artifact consumed by Astro pages, and rewrites\n * the checked-in nginx marker file used by the website Docker image.\n *\n * Future companion subprocesses that introspect the marketing\n * surface (locale-coverage audits, label-pack validation against\n * authored locales, SEO-link sanity checks) will also read here —\n * none exist today.\n *\n * # Why this is per-service website configuration\n *\n * Co-locating the schema with the engine package that owns its semantics\n * keeps three concerns separate from application-root service declarations:\n *\n * 1. **Right reuse axis.** Marketing-site content (locales, SEO origins,\n * future label-pack overrides, marketing-only CTAs) is conceptually\n * different from saas-app-level service declarations (`path`,\n * `serviceName`, `frontendType`, `sessionStorage`). The marketing\n * config travels with the website service folder, NOT with the\n * SaaS app root. Two unrelated lifecycles, two different review\n * audiences.\n * 2. **Engine boundary.** `platform-config-lib` no longer needs to\n * depend on `@wildo-ai/presets-components-models` (the home of\n * `AvailableLanguage`) just to type the marketing-site locale\n * enum. Adding a new website primitive (a new SEO field, a new\n * locale-pack id) belongs with the website package and its public\n * configuration contract.\n * 3. **Disambiguator hazard.** The previous root-level\n * `marketingWebsite?` block silently assumed \"one STATIC_WEBSITE\n * service per SaaS app\" — but nothing in the schema enforced it.\n * Two STATIC_WEBSITE services would have silently shared one\n * config block with no obvious \"which one wins?\" answer. The\n * application configuration rejects more than one STATIC_WEBSITE (and\n * TECH_DOC) service, so the per-service config-file lookup is\n * deterministic by construction.\n *\n * # Schema decisions encoded here\n *\n * - `z.strictObject` so authoring an unknown key surfaces as a parse\n * error at `defineWebsiteConfig` time instead of being silently\n * dropped (same posture as `WildoSaasConfigSchema` and\n * `WildoTechnicalDocConfigSchema`).\n *\n * - **No `enabled` field.** The gate is \"is there a `wildo.website.config.ts`\n * in a `frontendType: STATIC_WEBSITE` service folder?\" — file\n * presence under a STATIC_WEBSITE service IS the enable signal.\n * The Astro build / dev companion / future audit CLI requires the\n * file when it walks a STATIC_WEBSITE service folder, so a\n * half-authored config simply cannot exist (the loader fails\n * closed). Removing the boolean closes a foot-gun: an authored\n * `enabled: false` block was previously a silent way to keep the\n * config \"warm\" without shipping anything, and consumers had to\n * remember to read the flag in addition to the file presence — a\n * future read-the-config / forget-the-flag bug waiting to happen.\n * A separate `services.<key>.enabled` pattern (orthogonal to\n * marketing-website) is the right place for any \"temporarily\n * disable this service\" story (deferred — see DASHBOARD).\n *\n * - URL fields are `z.url()` (Zod 4 native validator) so we get\n * protocol + host parsing for free, matching the rest of the\n * codebase.\n *\n * - `csp?` is typed against `WildoCspConfigSchema` from\n * `@wildo-ai/platform-config-lib`, mirroring the docs service's\n * `wildo.tech-doc.config.ts` contract. The parsed value is consumed by\n * the shared `resolveCspPolicy()` / `emitCspArtifacts()` generator flow.\n *\n * - `defaultLocale ∈ locales` cross-check via `.superRefine` —\n * keeping `defaultLocale` as a top-level field is product-explicit\n * (\"which locale URL is the canonical one\") and decouples it from\n * list ordering. Locale-path uniqueness AND locale-value\n * uniqueness are also enforced — duplicate paths would silently\n * overwrite each other in the Astro build and corrupt the sitemap;\n * duplicate locales would produce duplicate hreflang entries.\n *\n * - Locale primitive is `AvailableLanguage`. The schema imports it\n * from `@wildo-ai/presets-components-models/public-runtime` (the\n * side-effect-free public-runtime seam), but **consumers authoring\n * `wildo.website.config.ts` must import it from\n * `@wildo-ai/saas-models/public-runtime`**. Both paths produce identical\n * values; saas-models simply re-exports the presets atom. Picking\n * one path consistently across consumer files prevents drift in\n * docs / examples / search results. Values authored in\n * `wildo.website.config.ts` are the lowercase BCP-47 strings\n * (`'en'`, `'fr'`), NOT the UPPERCASE TS enum member names\n * (`EN`, `FR`). `z.enum(AvailableLanguage)` parses against the\n * enum VALUES, which is what we want.\n */\n\nimport { WildoCspConfigSchema } from '@wildo-ai/platform-config-lib';\nimport { z } from 'zod';\nimport { AvailableLanguage } from '@wildo-ai/presets-components-models/public-runtime';\n\n/**\n * Per-locale configuration. Each authored locale gets one entry.\n *\n * `path` is the locale URL prefix segment (e.g. `'en'`, `'fr'`) — the\n * Astro builder maps `/en/...` and `/fr/...` paths via this value. We\n * do NOT auto-derive `path` from the locale string because some\n * deployments may want a flat root for the default locale (`/...`\n * instead of `/en/...`); keeping it explicit per locale leaves that\n * door open without a config-shape change.\n */\nexport const WebsiteLocaleConfigSchema = z.strictObject({\n locale: z.enum(AvailableLanguage),\n path: z\n .string()\n .min(1)\n .regex(\n /^[a-z0-9-]+$/,\n 'locale path must be lowercase alphanumeric with hyphens (becomes a URL segment)',\n ),\n});\nexport type WebsiteLocaleConfig = z.infer<typeof WebsiteLocaleConfigSchema>;\n\n/**\n * Per-service marketing-website configuration block.\n *\n * Authored as `export default defineWebsiteConfig({...})` in\n * `<website-service-root>/wildo.website.config.ts`. The Astro\n * build loads this file once at bootstrap via jiti and composes\n * a `WebsiteSiteContext`; helpers consume that composed context rather than\n * this raw value.\n *\n * @example\n * ```ts\n * import { AvailableLanguage } from '@wildo-ai/saas-models/public-runtime';\n * import { defineWebsiteConfig } from '@wildo-ai/saas-website';\n *\n * export default defineWebsiteConfig({\n * publicOrigin: 'https://example.com',\n * appOrigin: 'https://app.example.com',\n * defaultLocale: AvailableLanguage.EN,\n * locales: [\n * { locale: AvailableLanguage.EN, path: 'en' },\n * { locale: AvailableLanguage.FR, path: 'fr' },\n * ],\n * csp: { enabled: true, reportOnly: true },\n * });\n * ```\n */\nexport const WildoMarketingWebsiteConfigSchema = z\n .strictObject({\n /**\n * Public origin of the marketing site (apex / www) — canonical\n * input the Astro head renderer emits as absolute URLs\n * (`<link rel=\"canonical\">`, `og:url`, hreflang `href`,\n * sitemap base, structured-data `mainEntityOfPage`).\n *\n * MUST NOT end with `/` — the head renderer composes\n * `${publicOrigin}${routePath}` and a trailing slash would emit\n * `https://example.com//`. Surface the constraint loudly here\n * rather than silently corrupting URLs at build time.\n */\n publicOrigin: z\n .url()\n .refine((value) => !value.endsWith('/'), {\n error:\n 'WildoMarketingWebsiteConfig.publicOrigin must NOT end with \"/\" (the head ' +\n 'renderer concatenates publicOrigin + routePath; trailing slash would emit \"//\")',\n }),\n\n /**\n * Origin of the SaaS application the marketing site links into\n * (signup / login / pricing → checkout).\n *\n * **Today**: NO runtime consumer. The cross-origin handoff\n * surface that will read it does not exist yet. Authored\n * eagerly so the link-surface PR is a single framework-owned move\n * — until then the field is exercised only by the schema\n * parser.\n *\n * Distinct from the general `WildoSaasConfig.services.<name>`\n * URL surface because the marketing site is a separate Astro\n * project that does not load the full SaaS app graph at build\n * time.\n */\n appOrigin: z.url(),\n\n /**\n * Backend API origin the marketing site POSTs to for anonymous\n * captures (lead / draft-note forms) — e.g.\n * `https://api.example.com`. Threaded through\n * `WebsiteRuntimeContext.apiOrigin` into the React island that\n * constructs the `WebsiteAnonymousSessionClient`.\n *\n * OPTIONAL because a pure brochure site with no capture forms never\n * talks to an API — only sites that host an anonymous-capture\n * section need it. When such a section renders without a resolved\n * `apiOrigin`, the section surfaces a loud misconfiguration rather\n * than silently POSTing nowhere.\n *\n * MUST NOT end with `/` — the anon client composes\n * `${apiOrigin}${MAIN_API_BASE_PATH}${path}` and a trailing slash\n * would emit a doubled `//` in the request URL. (The client also\n * defensively strips trailing slashes, but the refine surfaces the\n * mistake at authoring time.)\n *\n * Distinct from `appOrigin` (the human-facing app the marketing CTAs\n * link into) and `publicOrigin` (the marketing site's own canonical\n * origin): this is the machine-to-machine API host.\n */\n apiOrigin: z\n .url()\n .refine((value) => !value.endsWith('/'), {\n error:\n 'WildoMarketingWebsiteConfig.apiOrigin must NOT end with \"/\" (the anonymous ' +\n 'client concatenates apiOrigin + MAIN_API_BASE_PATH + path; a trailing slash ' +\n 'would emit \"//\")',\n })\n .optional(),\n\n /**\n * Optional Content-Security-Policy override for the marketing site.\n * Consumed by the website-local `sync-nginx-csp` step, which emits\n * the generated artifact for Astro page injection and rewrites the\n * website nginx marker file from the same resolved output.\n */\n csp: WildoCspConfigSchema.optional(),\n\n /**\n * Default locale the marketing site serves for unmatched URLs\n * and as the canonical hreflang signal. Must appear in\n * `locales` below (cross-check enforced by the schema-level\n * `.superRefine`).\n *\n * Read by `buildAstroI18nConfig(...)` (drives Astro's own\n * `i18n.defaultLocale`) and by the head renderer (drives\n * `<html lang=\"...\">`, `og:locale`, hreflang `x-default`).\n */\n defaultLocale: z.enum(AvailableLanguage),\n\n /**\n * Ordered list of locales the marketing site builds. Order is\n * preserved (used by the language switcher rendering and by\n * the sitemap emitter — when the sitemap surface lands).\n *\n * Read by `buildAstroI18nConfig(...)` (Astro `i18n.locales`),\n * by `assertLabelPacksValid(...)` (one label pack per locale),\n * by `loadWebsitePageRuntime(...)` (per-page locale resolution),\n * and by the head renderer (hreflang alternates).\n */\n locales: z.array(WebsiteLocaleConfigSchema).min(1),\n })\n .superRefine((config, ctx) => {\n const declaredLocales = new Set(config.locales.map((entry) => entry.locale));\n if (!declaredLocales.has(config.defaultLocale)) {\n ctx.addIssue({\n code: 'custom',\n path: ['defaultLocale'],\n message:\n `defaultLocale '${config.defaultLocale}' is not declared in 'locales' ` +\n `(declared: [${[...declaredLocales].join(', ')}]). The default locale ` +\n `must be one of the locales the site actually builds.`,\n });\n }\n\n // Locale-path uniqueness: two entries collapsing to the same URL\n // segment would silently overwrite each other in the Astro build\n // and corrupt the sitemap. Fail closed at parse time.\n const pathOwners = new Map<string, AvailableLanguage>();\n for (const entry of config.locales) {\n const previous = pathOwners.get(entry.path);\n if (previous !== undefined) {\n ctx.addIssue({\n code: 'custom',\n path: ['locales'],\n message:\n `Locale path '${entry.path}' is declared by both '${previous}' and ` +\n `'${entry.locale}'. Each locale must have a unique URL path segment.`,\n });\n continue;\n }\n pathOwners.set(entry.path, entry.locale);\n }\n\n // Locale-value uniqueness: same locale appearing twice would\n // produce duplicate hreflang entries and ambiguous default-locale\n // resolution.\n const localeOwners = new Map<AvailableLanguage, string>();\n for (const entry of config.locales) {\n const previousPath = localeOwners.get(entry.locale);\n if (previousPath !== undefined) {\n ctx.addIssue({\n code: 'custom',\n path: ['locales'],\n message:\n `Locale '${entry.locale}' is declared twice (paths '${previousPath}' ` +\n `and '${entry.path}'). Each locale must appear at most once.`,\n });\n continue;\n }\n localeOwners.set(entry.locale, entry.path);\n }\n });\nexport type WildoMarketingWebsiteConfig = z.infer<typeof WildoMarketingWebsiteConfigSchema>;\nexport type WildoMarketingWebsiteConfigInput = z.input<typeof WildoMarketingWebsiteConfigSchema>;\n/** @wildo_source:part:end engine.saas-website.config */\n"]}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * @wildo-package @wildo-ai/saas-website (Node-only config-loader entry)
3
+ *
4
+ * Dedicated subpath for the per-service `wildo.website.config.ts`
5
+ * loader (`saas-website-slice.md` Step 7.5 — mirrors the
6
+ * `saas-technical-doc.md` Step 7.5 ship). Consumers:
7
+ *
8
+ * - `examples/wonder-todos/website/astro.config.ts` — wires the
9
+ * loader into the Astro build's `i18n` config.
10
+ * - Future companion subprocesses that introspect the marketing
11
+ * surface (locale coverage audits, SEO-link sanity checks).
12
+ *
13
+ * @wildo-boundary
14
+ * This entry is on the **Node-only** side of the bundle boundary.
15
+ * It re-exports `loadWebsiteConfig` (which transitively pulls
16
+ * `loadConfig` from `@wildo-ai/platform-config-lib` — jiti-backed,
17
+ * uses `node:fs` / `node:path` / `node:crypto`) and is therefore
18
+ * carved into its own package subpath so it cannot leak into the
19
+ * browser-side React-island bundle.
20
+ *
21
+ * Enforced by `engine/saas-website/src/__tests__/bundle-isolation.test.ts`:
22
+ * - This file IS in `ENTRY_FILES` (so the closure walker covers it).
23
+ * - `@wildo-ai/platform-config-lib` is in `ALLOWED_BARE_SPECIFIERS`
24
+ * AND `SSR_ONLY_BARE_SPECIFIERS`, which together prove the
25
+ * specifier reaches the closure ONLY through this entry — never
26
+ * through `index.ts`, `companion-exports.ts`, or `astro-island.ts`.
27
+ *
28
+ * Re-exporting `loadWebsiteConfig` from `index.ts` would (a) drag
29
+ * `@wildo-ai/platform-config-lib`'s heavy graph into the marketing-
30
+ * site browser bundle and (b) trip the SSR-tier negative test
31
+ * above — both fail-loud signals that this subpath separation
32
+ * exists for a reason.
33
+ */
34
+ export * from './config/load-website-config';
35
+ //# sourceMappingURL=config-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../../src/config-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,cAAc,8BAA8B,CAAC"}