@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,170 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo } from 'react';
3
+ import { BUILTIN_PROVIDER_CAPABILITY } from '@wildo-ai/saas-models/public-runtime';
4
+ import { WebsitePageContextProvider } from '../contexts/WebsitePageContext.js';
5
+ import { useWebsiteRuntime } from '../contexts/useWebsiteRuntime.js';
6
+ import { useWebsiteLabelByKey } from '../hooks/useWebsiteLabel.js';
7
+ import { WebsitePageRefSchema, } from '../../schemas/refs/page-ref.schemas.js';
8
+ /**
9
+ * Stable DOM id stamped on the `<main>` element so the framework's
10
+ * skip-link `<a href="#main-content">` can move keyboard focus past
11
+ * the chrome on every page (Phase 6 audit fix M-α). Co-located here
12
+ * (rather than exposed as a prop) because:
13
+ * - The skip-link target is a framework concern — exposing it as a
14
+ * consumer-tunable id would invite drift between the link's href
15
+ * and the main element's id.
16
+ * - Screen readers announce the heading inside `<main id="...">` on
17
+ * focus; a stable, framework-owned id keeps that affordance
18
+ * deterministic across every page on every site.
19
+ */
20
+ const MAIN_CONTENT_DOM_ID = 'main-content';
21
+ /**
22
+ * Inline style for the skip link's "visually hidden by default,
23
+ * visible on focus" pattern. Inlined (NOT shipped as a CSS class)
24
+ * because:
25
+ * - The framework intentionally avoids shipping any CSS file the
26
+ * consumer must opt in to — the only stylesheet emitted is the
27
+ * `:root { --website-*: … }` design-token block, and the rest of
28
+ * the styling surface lives in consumer code (see Phase 5
29
+ * `renderWebsiteDesignTokensCss`). Inlining keeps the skip link
30
+ * self-sufficient and side-effect-free.
31
+ * - The "skip-link" pattern is a small, well-known a11y idiom; the
32
+ * handful of style declarations here do not benefit from being
33
+ * reorganized into a class with a custom-property hook.
34
+ *
35
+ * The "focused" variant uses BOTH `:focus` AND `:focus-visible`
36
+ * pseudo-classes via a scoped `<style>` tag inside the layout output.
37
+ * We cannot apply pseudo-class styles via a `style` attribute, but
38
+ * emitting a tiny scoped block once at the layout root is safe (one
39
+ * block per page render, ~120 bytes) and avoids importing a CSS-in-JS
40
+ * runtime.
41
+ *
42
+ * **Why both selectors and not just `:focus-visible`** (Phase 6 fourth
43
+ * audit fix M-Η): `:focus-visible` is the modern UA-heuristic-driven
44
+ * selector that hides focus rings on mouse clicks, and it is the
45
+ * RIGHT default for buttons / links. For a skip link though we WANT
46
+ * the affordance to appear on EVERY focus event — the link is
47
+ * invisible by default and a keyboard-only user (the only intended
48
+ * audience) is the only one who can reach it. Older browsers that
49
+ * predate `:focus-visible` (and Safari < 15.4 with the feature
50
+ * disabled) would skip the rule entirely with a `:focus-visible`-only
51
+ * declaration, leaving the keyboard user looking at an apparently
52
+ * empty page when they pressed Tab. Pairing `:focus, :focus-visible`
53
+ * is the documented WCAG 2.1 fallback pattern.
54
+ */
55
+ const SKIP_LINK_HIDDEN_STYLE = {
56
+ position: 'absolute',
57
+ top: 0,
58
+ left: 0,
59
+ width: '1px',
60
+ height: '1px',
61
+ padding: 0,
62
+ margin: '-1px',
63
+ overflow: 'hidden',
64
+ clip: 'rect(0 0 0 0)',
65
+ whiteSpace: 'nowrap',
66
+ border: 0,
67
+ };
68
+ /**
69
+ * Single scoped style block for the skip link's `:focus-visible`
70
+ * affordance. Targets the `data-website-skip-link` attribute (NOT a
71
+ * class) so consumer Tailwind / utility classes cannot accidentally
72
+ * shadow it. Stays trivially small to keep payload under the
73
+ * "framework rendering" implicit budget.
74
+ */
75
+ const SKIP_LINK_FOCUS_CSS = `
76
+ [data-website-skip-link]:focus,
77
+ [data-website-skip-link]:focus-visible {
78
+ position: fixed !important;
79
+ top: 0.5rem !important;
80
+ left: 0.5rem !important;
81
+ width: auto !important;
82
+ height: auto !important;
83
+ padding: 0.75rem 1rem !important;
84
+ margin: 0 !important;
85
+ overflow: visible !important;
86
+ clip: auto !important;
87
+ white-space: normal !important;
88
+ background: var(--website-colors-background, #fff) !important;
89
+ color: var(--website-colors-foreground, #000) !important;
90
+ border: 2px solid var(--website-colors-primary, #000) !important;
91
+ border-radius: var(--website-radii-md, 0.375rem) !important;
92
+ z-index: 9999 !important;
93
+ text-decoration: none !important;
94
+ }
95
+ `.trim();
96
+ export function WebsitePageLayout({ pageRef, header, footer, className, mainClassName, children, }) {
97
+ const runtime = useWebsiteRuntime();
98
+ /**
99
+ * Defensive parse: callers should pass a `WebsitePageManifest.ref`
100
+ * (already branded), but accepting raw strings + parsing here means
101
+ * a hand-typed call site can't smuggle a malformed ref into the
102
+ * label-key derivation chain.
103
+ */
104
+ const safePageRef = useMemo(() => WebsitePageRefSchema.parse(pageRef), [pageRef]);
105
+ const pageContextValue = useMemo(() => ({ pageRef: safePageRef }), [safePageRef]);
106
+ /**
107
+ * Header resolution rules:
108
+ * - `header === null` => explicit suppression
109
+ * - `header !== undefined` => per-page override (any ReactNode)
110
+ * - `header === undefined` => fall back to the runtime navigation
111
+ * config's `headerComponent`
112
+ *
113
+ * The fallback is the common path: every page renders the same
114
+ * site-wide chrome. The override path is the escape hatch (campaign
115
+ * landing pages with custom header layouts).
116
+ */
117
+ const resolvedHeader = useMemo(() => {
118
+ if (header === null)
119
+ return null;
120
+ if (header !== undefined)
121
+ return header;
122
+ const HeaderComponent = runtime.navigation.headerComponent;
123
+ return _jsx(HeaderComponent, {});
124
+ }, [header, runtime.navigation.headerComponent]);
125
+ const resolvedFooter = useMemo(() => {
126
+ if (footer === null)
127
+ return null;
128
+ if (footer !== undefined)
129
+ return footer;
130
+ const FooterComponent = runtime.navigation.footerComponent;
131
+ return _jsx(FooterComponent, {});
132
+ }, [footer, runtime.navigation.footerComponent]);
133
+ const providerRootAttributes = useMemo(() => {
134
+ const attrs = {};
135
+ const productAnalyticsProviderRef = runtime.frontendProviderRegistry
136
+ ?.findProviderByProviderCapability(BUILTIN_PROVIDER_CAPABILITY.FRONTEND_PRODUCT_ANALYTICS)
137
+ ?.metadata.ref;
138
+ const errorMonitoringProviderRef = runtime.frontendProviderRegistry
139
+ ?.findProviderByProviderCapability(BUILTIN_PROVIDER_CAPABILITY.FRONTEND_ERROR_MONITORING)
140
+ ?.metadata.ref;
141
+ const captchaProviderRef = runtime.frontendProviderRegistry
142
+ ?.findProviderByProviderCapability(BUILTIN_PROVIDER_CAPABILITY.FRONTEND_CAPTCHA)
143
+ ?.metadata.ref;
144
+ if (productAnalyticsProviderRef) {
145
+ attrs['data-website-product-analytics-provider-ref'] = productAnalyticsProviderRef;
146
+ }
147
+ if (errorMonitoringProviderRef) {
148
+ attrs['data-website-error-monitoring-provider-ref'] = errorMonitoringProviderRef;
149
+ }
150
+ if (captchaProviderRef) {
151
+ attrs['data-website-captcha-provider-ref'] = captchaProviderRef;
152
+ }
153
+ return attrs;
154
+ }, [runtime.frontendProviderRegistry]);
155
+ /**
156
+ * Resolved skip-link label. Reads through the runtime label pack
157
+ * via the standard `useWebsiteLabelByKey` hook so a missing
158
+ * translation surfaces the same diagnostic shape as any other
159
+ * missing chrome label. The framework guarantees the key reaches
160
+ * the bridge (see `pickLabelsForPage`) and the consumer's
161
+ * label-pack validator (see `composeEffectiveChromeLabelKeys` in
162
+ * `label-pack-loader.ts`) ensures it is present in every locale
163
+ * pack — there is therefore no silent-fallback path that would
164
+ * ship an English skip link to a non-EN visitor.
165
+ */
166
+ const skipLinkLabel = useWebsiteLabelByKey(runtime.skipToMainContentLabelKey);
167
+ return (_jsx(WebsitePageContextProvider, { value: pageContextValue, children: _jsxs("div", { "data-website-page-ref": safePageRef, className: className, ...providerRootAttributes, children: [_jsx("style", { children: SKIP_LINK_FOCUS_CSS }), _jsx("a", { href: `#${MAIN_CONTENT_DOM_ID}`, "data-website-skip-link": "", style: SKIP_LINK_HIDDEN_STYLE, children: skipLinkLabel }), resolvedHeader !== null ? _jsx("header", { children: resolvedHeader }) : null, _jsx("main", { id: MAIN_CONTENT_DOM_ID, className: mainClassName, tabIndex: -1, children: children }), resolvedFooter !== null ? _jsx("footer", { children: resolvedFooter }) : null] }) }));
168
+ }
169
+ WebsitePageLayout.displayName = 'WebsitePageLayout';
170
+ //# sourceMappingURL=WebsitePageLayout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebsitePageLayout.js","sourceRoot":"","sources":["../../../../../src/core/layouts/WebsitePageLayout.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,OAAO,EAAkB,MAAM,OAAO,CAAC;AACvD,OAAO,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC;AAEnF,OAAO,EAAE,0BAA0B,EAAE,MAAM,gCAAgC,CAAC;AAE5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EACL,oBAAoB,GAErB,MAAM,qCAAqC,CAAC;AAE7C;;;;;;;;;;;GAWG;AACH,MAAM,mBAAmB,GAAG,cAAc,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,sBAAsB,GAAwB;IAClD,QAAQ,EAAE,UAAU;IACpB,GAAG,EAAE,CAAC;IACN,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,KAAK;IACZ,MAAM,EAAE,KAAK;IACb,OAAO,EAAE,CAAC;IACV,MAAM,EAAE,MAAM;IACd,QAAQ,EAAE,QAAQ;IAClB,IAAI,EAAE,eAAe;IACrB,UAAU,EAAE,QAAQ;IACpB,MAAM,EAAE,CAAC;CACV,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;CAoB3B,CAAC,IAAI,EAAE,CAAC;AAkFT,MAAM,UAAU,iBAAiB,CAAC,EAChC,OAAO,EACP,MAAM,EACN,MAAM,EACN,SAAS,EACT,aAAa,EACb,QAAQ,GACe;IACvB,MAAM,OAAO,GAAG,iBAAiB,EAAE,CAAC;IAEpC;;;;;OAKG;IACH,MAAM,WAAW,GAAG,OAAO,CACzB,GAAG,EAAE,CAAC,oBAAoB,CAAC,KAAK,CAAC,OAAO,CAAC,EACzC,CAAC,OAAO,CAAC,CACV,CAAC;IAEF,MAAM,gBAAgB,GAAG,OAAO,CAC9B,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,EAChC,CAAC,WAAW,CAAC,CACd,CAAC;IAEF;;;;;;;;;;OAUG;IACH,MAAM,cAAc,GAAG,OAAO,CAAY,GAAG,EAAE;QAC7C,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QACjC,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,MAAM,CAAC;QACxC,MAAM,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC;QAC3D,OAAO,KAAC,eAAe,KAAG,CAAC;IAC7B,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC;IAEjD,MAAM,cAAc,GAAG,OAAO,CAAY,GAAG,EAAE;QAC7C,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QACjC,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,MAAM,CAAC;QACxC,MAAM,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC;QAC3D,OAAO,KAAC,eAAe,KAAG,CAAC;IAC7B,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC;IAEjD,MAAM,sBAAsB,GAAG,OAAO,CAAyB,GAAG,EAAE;QAClE,MAAM,KAAK,GAA2B,EAAE,CAAC;QACzC,MAAM,2BAA2B,GAAG,OAAO,CAAC,wBAAwB;YAClE,EAAE,gCAAgC,CAChC,2BAA2B,CAAC,0BAA0B,CACvD;YACD,EAAE,QAAQ,CAAC,GAAG,CAAC;QACjB,MAAM,0BAA0B,GAAG,OAAO,CAAC,wBAAwB;YACjE,EAAE,gCAAgC,CAChC,2BAA2B,CAAC,yBAAyB,CACtD;YACD,EAAE,QAAQ,CAAC,GAAG,CAAC;QACjB,MAAM,kBAAkB,GAAG,OAAO,CAAC,wBAAwB;YACzD,EAAE,gCAAgC,CAChC,2BAA2B,CAAC,gBAAgB,CAC7C;YACD,EAAE,QAAQ,CAAC,GAAG,CAAC;QAEjB,IAAI,2BAA2B,EAAE,CAAC;YAChC,KAAK,CAAC,6CAA6C,CAAC,GAAG,2BAA2B,CAAC;QACrF,CAAC;QACD,IAAI,0BAA0B,EAAE,CAAC;YAC/B,KAAK,CAAC,4CAA4C,CAAC,GAAG,0BAA0B,CAAC;QACnF,CAAC;QACD,IAAI,kBAAkB,EAAE,CAAC;YACvB,KAAK,CAAC,mCAAmC,CAAC,GAAG,kBAAkB,CAAC;QAClE,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC,EAAE,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC,CAAC;IAEvC;;;;;;;;;;OAUG;IACH,MAAM,aAAa,GAAG,oBAAoB,CAAC,OAAO,CAAC,yBAAyB,CAAC,CAAC;IAE9E,OAAO,CACL,KAAC,0BAA0B,IAAC,KAAK,EAAE,gBAAgB,YACjD,wCACyB,WAAgC,EACvD,SAAS,EAAE,SAAS,KAChB,sBAAsB,aAS1B,0BAAQ,mBAAmB,GAAS,EACpC,YACE,IAAI,EAAE,IAAI,mBAAmB,EAAE,4BACR,EAAE,EACzB,KAAK,EAAE,sBAAsB,YAE5B,aAAa,GACZ,EACH,cAAc,KAAK,IAAI,CAAC,CAAC,CAAC,2BAAS,cAAc,GAAU,CAAC,CAAC,CAAC,IAAI,EACnE,eAAM,EAAE,EAAE,mBAAmB,EAAE,SAAS,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC,CAAC,YAClE,QAAQ,GACJ,EACN,cAAc,KAAK,IAAI,CAAC,CAAC,CAAC,2BAAS,cAAc,GAAU,CAAC,CAAC,CAAC,IAAI,IAC/D,GACqB,CAC9B,CAAC;AACJ,CAAC;AACD,iBAAiB,CAAC,WAAW,GAAG,mBAAmB,CAAC","sourcesContent":["import React, { useMemo, type ReactNode } from 'react';\nimport { BUILTIN_PROVIDER_CAPABILITY } from '@wildo-ai/saas-models/public-runtime';\n\nimport { WebsitePageContextProvider } from '../contexts/WebsitePageContext';\nimport type { WebsitePageContextValue } from '../contexts/WebsitePageContext';\nimport { useWebsiteRuntime } from '../contexts/useWebsiteRuntime';\nimport { useWebsiteLabelByKey } from '../hooks/useWebsiteLabel';\nimport {\n WebsitePageRefSchema,\n type WebsitePageRef,\n} from '../../schemas/refs/page-ref.schemas';\n\n/**\n * Stable DOM id stamped on the `<main>` element so the framework's\n * skip-link `<a href=\"#main-content\">` can move keyboard focus past\n * the chrome on every page (Phase 6 audit fix M-α). Co-located here\n * (rather than exposed as a prop) because:\n * - The skip-link target is a framework concern — exposing it as a\n * consumer-tunable id would invite drift between the link's href\n * and the main element's id.\n * - Screen readers announce the heading inside `<main id=\"...\">` on\n * focus; a stable, framework-owned id keeps that affordance\n * deterministic across every page on every site.\n */\nconst MAIN_CONTENT_DOM_ID = 'main-content';\n\n/**\n * Inline style for the skip link's \"visually hidden by default,\n * visible on focus\" pattern. Inlined (NOT shipped as a CSS class)\n * because:\n * - The framework intentionally avoids shipping any CSS file the\n * consumer must opt in to — the only stylesheet emitted is the\n * `:root { --website-*: … }` design-token block, and the rest of\n * the styling surface lives in consumer code (see Phase 5\n * `renderWebsiteDesignTokensCss`). Inlining keeps the skip link\n * self-sufficient and side-effect-free.\n * - The \"skip-link\" pattern is a small, well-known a11y idiom; the\n * handful of style declarations here do not benefit from being\n * reorganized into a class with a custom-property hook.\n *\n * The \"focused\" variant uses BOTH `:focus` AND `:focus-visible`\n * pseudo-classes via a scoped `<style>` tag inside the layout output.\n * We cannot apply pseudo-class styles via a `style` attribute, but\n * emitting a tiny scoped block once at the layout root is safe (one\n * block per page render, ~120 bytes) and avoids importing a CSS-in-JS\n * runtime.\n *\n * **Why both selectors and not just `:focus-visible`** (Phase 6 fourth\n * audit fix M-Η): `:focus-visible` is the modern UA-heuristic-driven\n * selector that hides focus rings on mouse clicks, and it is the\n * RIGHT default for buttons / links. For a skip link though we WANT\n * the affordance to appear on EVERY focus event — the link is\n * invisible by default and a keyboard-only user (the only intended\n * audience) is the only one who can reach it. Older browsers that\n * predate `:focus-visible` (and Safari < 15.4 with the feature\n * disabled) would skip the rule entirely with a `:focus-visible`-only\n * declaration, leaving the keyboard user looking at an apparently\n * empty page when they pressed Tab. Pairing `:focus, :focus-visible`\n * is the documented WCAG 2.1 fallback pattern.\n */\nconst SKIP_LINK_HIDDEN_STYLE: React.CSSProperties = {\n position: 'absolute',\n top: 0,\n left: 0,\n width: '1px',\n height: '1px',\n padding: 0,\n margin: '-1px',\n overflow: 'hidden',\n clip: 'rect(0 0 0 0)',\n whiteSpace: 'nowrap',\n border: 0,\n};\n\n/**\n * Single scoped style block for the skip link's `:focus-visible`\n * affordance. Targets the `data-website-skip-link` attribute (NOT a\n * class) so consumer Tailwind / utility classes cannot accidentally\n * shadow it. Stays trivially small to keep payload under the\n * \"framework rendering\" implicit budget.\n */\nconst SKIP_LINK_FOCUS_CSS = `\n[data-website-skip-link]:focus,\n[data-website-skip-link]:focus-visible {\n position: fixed !important;\n top: 0.5rem !important;\n left: 0.5rem !important;\n width: auto !important;\n height: auto !important;\n padding: 0.75rem 1rem !important;\n margin: 0 !important;\n overflow: visible !important;\n clip: auto !important;\n white-space: normal !important;\n background: var(--website-colors-background, #fff) !important;\n color: var(--website-colors-foreground, #000) !important;\n border: 2px solid var(--website-colors-primary, #000) !important;\n border-radius: var(--website-radii-md, 0.375rem) !important;\n z-index: 9999 !important;\n text-decoration: none !important;\n}\n`.trim();\n\n/**\n * @wildo_source:part:start saas.website.page-layout.component facet:layer:core facet:family:website\n *\n * `WebsitePageLayout` — the per-page structural primitive.\n *\n * **Three responsibilities** (each carrying its own semantic weight):\n *\n * 1. **Provides `WebsitePageContext`** — registers the active page\n * ref so child sections (`<WebsiteSection>`) and label hooks\n * (`useWebsiteLabel`) can derive label keys without re-typing\n * the page ref at every leaf.\n * 2. **Renders the site-wide chrome** — pulls\n * `headerComponent` and `footerComponent` from the runtime\n * navigation config and wraps the page's main content in the\n * site's standard header/main/footer scaffold. Per-page\n * overrides (`header={…}` / `footer={…}` props) take precedence\n * over the runtime defaults so individual pages can opt into a\n * custom chrome (eg. a campaign landing page running with\n * header-only minimal chrome).\n * 3. **Stamps DOM landmarks** — emits `data-website-page-ref` on\n * the layout root for analytics / SEO crawlers / overlay\n * tooling that needs to identify the active page from the DOM\n * without booting React. When the website facet hydrates\n * frontend-provider bootstraps, the same root also carries the\n * active public provider refs for analytics / browser-monitoring /\n * captcha so non-React tooling can detect the configured vendor\n * without reverse-engineering app code.\n *\n * **Why semantic `<header>` / `<main>` / `<footer>` elements**:\n * marketing-site SEO scoring penalizes pages that ship a single\n * unstructured `<div>` tree. Using semantic landmarks costs nothing\n * and improves crawler accessibility AND screen-reader\n * navigability — both are first-class concerns for a public\n * marketing surface.\n *\n * **Why this lives in `core/`** (not `components/`): the layout is\n * structural framework machinery (it owns the page-context\n * provisioning + the navigation slot resolution), not a styled\n * primitive. Designers override `header` / `footer` via runtime\n * config or per-page props, NOT by replacing this component.\n */\nexport interface WebsitePageLayoutProps {\n /**\n * The active page's ref. Accepts either an already-branded\n * `WebsitePageRef` or a raw string — the layout parses through\n * `WebsitePageRefSchema` so a malformed ref fails loud at render.\n *\n * Typically sourced from the page's `WebsitePageManifest.ref`\n * (eg. `<WebsitePageLayout pageRef={LANDING_PAGE_MANIFEST.ref}>`).\n */\n pageRef: WebsitePageRef | string;\n /**\n * Per-page header override. When supplied, takes precedence over\n * `WebsiteRuntimeContext.designTokens` navigation config's\n * `headerComponent`. Pass `null` explicitly to suppress the header\n * entirely (campaign landing pages without site-wide nav).\n */\n header?: ReactNode | null;\n /**\n * Per-page footer override. Same semantics as `header`.\n */\n footer?: ReactNode | null;\n /**\n * Optional className applied to the root `<div data-website-page-ref=\"…\">`\n * wrapper. Use for page-level layout tweaks (eg. `min-h-screen` on\n * landing pages).\n */\n className?: string;\n /**\n * Optional className applied to the inner `<main>` element. Use for\n * vertical-rhythm overrides on long-scroll landing pages.\n */\n mainClassName?: string;\n /**\n * The page's section composition. Typically a sequence of\n * `<WebsiteSection>` wrappers around concrete section components.\n */\n children: ReactNode;\n}\n\nexport function WebsitePageLayout({\n pageRef,\n header,\n footer,\n className,\n mainClassName,\n children,\n}: WebsitePageLayoutProps): ReactNode {\n const runtime = useWebsiteRuntime();\n\n /**\n * Defensive parse: callers should pass a `WebsitePageManifest.ref`\n * (already branded), but accepting raw strings + parsing here means\n * a hand-typed call site can't smuggle a malformed ref into the\n * label-key derivation chain.\n */\n const safePageRef = useMemo<WebsitePageRef>(\n () => WebsitePageRefSchema.parse(pageRef),\n [pageRef],\n );\n\n const pageContextValue = useMemo<WebsitePageContextValue>(\n () => ({ pageRef: safePageRef }),\n [safePageRef],\n );\n\n /**\n * Header resolution rules:\n * - `header === null` => explicit suppression\n * - `header !== undefined` => per-page override (any ReactNode)\n * - `header === undefined` => fall back to the runtime navigation\n * config's `headerComponent`\n *\n * The fallback is the common path: every page renders the same\n * site-wide chrome. The override path is the escape hatch (campaign\n * landing pages with custom header layouts).\n */\n const resolvedHeader = useMemo<ReactNode>(() => {\n if (header === null) return null;\n if (header !== undefined) return header;\n const HeaderComponent = runtime.navigation.headerComponent;\n return <HeaderComponent />;\n }, [header, runtime.navigation.headerComponent]);\n\n const resolvedFooter = useMemo<ReactNode>(() => {\n if (footer === null) return null;\n if (footer !== undefined) return footer;\n const FooterComponent = runtime.navigation.footerComponent;\n return <FooterComponent />;\n }, [footer, runtime.navigation.footerComponent]);\n\n const providerRootAttributes = useMemo<Record<string, string>>(() => {\n const attrs: Record<string, string> = {};\n const productAnalyticsProviderRef = runtime.frontendProviderRegistry\n ?.findProviderByProviderCapability(\n BUILTIN_PROVIDER_CAPABILITY.FRONTEND_PRODUCT_ANALYTICS,\n )\n ?.metadata.ref;\n const errorMonitoringProviderRef = runtime.frontendProviderRegistry\n ?.findProviderByProviderCapability(\n BUILTIN_PROVIDER_CAPABILITY.FRONTEND_ERROR_MONITORING,\n )\n ?.metadata.ref;\n const captchaProviderRef = runtime.frontendProviderRegistry\n ?.findProviderByProviderCapability(\n BUILTIN_PROVIDER_CAPABILITY.FRONTEND_CAPTCHA,\n )\n ?.metadata.ref;\n\n if (productAnalyticsProviderRef) {\n attrs['data-website-product-analytics-provider-ref'] = productAnalyticsProviderRef;\n }\n if (errorMonitoringProviderRef) {\n attrs['data-website-error-monitoring-provider-ref'] = errorMonitoringProviderRef;\n }\n if (captchaProviderRef) {\n attrs['data-website-captcha-provider-ref'] = captchaProviderRef;\n }\n\n return attrs;\n }, [runtime.frontendProviderRegistry]);\n\n /**\n * Resolved skip-link label. Reads through the runtime label pack\n * via the standard `useWebsiteLabelByKey` hook so a missing\n * translation surfaces the same diagnostic shape as any other\n * missing chrome label. The framework guarantees the key reaches\n * the bridge (see `pickLabelsForPage`) and the consumer's\n * label-pack validator (see `composeEffectiveChromeLabelKeys` in\n * `label-pack-loader.ts`) ensures it is present in every locale\n * pack — there is therefore no silent-fallback path that would\n * ship an English skip link to a non-EN visitor.\n */\n const skipLinkLabel = useWebsiteLabelByKey(runtime.skipToMainContentLabelKey);\n\n return (\n <WebsitePageContextProvider value={pageContextValue}>\n <div\n data-website-page-ref={safePageRef as unknown as string}\n className={className}\n {...providerRootAttributes}\n >\n {/*\n * Skip link rendered as the FIRST focusable element on the\n * page so a keyboard / AT user can bypass the chrome on every\n * navigation (WCAG 2.1 §2.4.1 \"Bypass Blocks\", Level A).\n * Visually hidden by default; revealed on focus via the\n * scoped `<style>` block below.\n */}\n <style>{SKIP_LINK_FOCUS_CSS}</style>\n <a\n href={`#${MAIN_CONTENT_DOM_ID}`}\n data-website-skip-link=\"\"\n style={SKIP_LINK_HIDDEN_STYLE}\n >\n {skipLinkLabel}\n </a>\n {resolvedHeader !== null ? <header>{resolvedHeader}</header> : null}\n <main id={MAIN_CONTENT_DOM_ID} className={mainClassName} tabIndex={-1}>\n {children}\n </main>\n {resolvedFooter !== null ? <footer>{resolvedFooter}</footer> : null}\n </div>\n </WebsitePageContextProvider>\n );\n}\nWebsitePageLayout.displayName = 'WebsitePageLayout';\n/** @wildo_source:part:end saas.website.page-layout.component */\n"]}
@@ -0,0 +1,91 @@
1
+ import { type ReactNode } from 'react';
2
+ import { WebsiteSectionCategory } from '../../schemas/sections/website-section-category.shared';
3
+ import { type WebsiteSectionRef } from '../../schemas/refs/section-ref.schemas';
4
+ /**
5
+ * @wildo_source:part:start saas.website.section.component facet:layer:core facet:family:website
6
+ *
7
+ * `WebsiteSection` — the per-section structural primitive.
8
+ *
9
+ * **Three responsibilities** (semantic-weight discipline):
10
+ *
11
+ * 1. **Provides `WebsiteSectionContext`** — registers the active
12
+ * sectionRef + category so leaf primitives (`useWebsiteLabel`,
13
+ * analytics overlays, structured-data emitters) can derive
14
+ * keys / metadata without re-typing the section's identity.
15
+ * 2. **Renders a semantic `<section>` landmark** — emits
16
+ * `data-section-ref` and `data-section-category` attributes so
17
+ * analytics tooling and SEO crawlers can identify and group
18
+ * sections without parsing className conventions. Optional
19
+ * `id` prop wires up anchor-link navigation
20
+ * (`<a href="#pricing">`).
21
+ * 3. **Provides a styling seam** — `className` is forwarded to the
22
+ * `<section>` element so designers can apply utility-class
23
+ * decorations (background, padding, max-width container) without
24
+ * having to compose multiple wrappers.
25
+ *
26
+ * **Why NOT render a `<div>` instead of `<section>`**: marketing
27
+ * pages benefit from semantic landmarks for SEO scoring and
28
+ * accessibility (screen-reader section navigation). A `<section>`
29
+ * with `data-section-category="hero"` is structurally meaningful;
30
+ * a `<div>` with the same data-attribute carries the same metadata
31
+ * but loses the landmark benefit.
32
+ *
33
+ * **Why expose `as` is NOT a prop**: switching the rendered tag
34
+ * (`<article>` for blog post bodies, `<aside>` for sidebar widgets)
35
+ * is legitimate but rare enough to be worth a separate primitive
36
+ * (Phase 4: `<WebsiteAside>`, `<WebsiteArticle>`). Keeping
37
+ * `WebsiteSection` rendered as a literal `<section>` removes a
38
+ * rarely-used prop from the section-author API and keeps the SEO
39
+ * landmark contract guaranteed at the type level.
40
+ */
41
+ export interface WebsiteSectionProps {
42
+ /**
43
+ * The section's ref. Accepts an already-branded `WebsiteSectionRef`
44
+ * or a raw string — the layout parses through
45
+ * `WebsiteSectionRefSchema` so a malformed ref fails loud at
46
+ * render. Typically sourced from the page manifest's
47
+ * `sectionRefs[]` (eg. `<WebsiteSection sectionRef="hero" …>`).
48
+ */
49
+ sectionRef: WebsiteSectionRef | string;
50
+ /**
51
+ * The section's structural intent (see `WebsiteSectionCategory`).
52
+ * Required at the wrapper level (rather than only on the section
53
+ * definition) because the wrapper emits `data-section-category`
54
+ * for analytics, and asking the wrapper to look up the category
55
+ * on a separate manifest registry would re-introduce the runtime
56
+ * coupling the per-section context exists to avoid.
57
+ */
58
+ category: WebsiteSectionCategory;
59
+ /**
60
+ * Optional DOM id. Wires anchor-link navigation
61
+ * (`<a href="#pricing">`). When omitted, the section has no DOM
62
+ * id — by design; not every section is an anchor target.
63
+ */
64
+ id?: string;
65
+ /**
66
+ * Optional className applied to the `<section>` element. Used by
67
+ * designers for layout / spacing utilities. Section components
68
+ * themselves should NOT pass className through to this wrapper —
69
+ * keep the styling responsibility at the page-composition level.
70
+ */
71
+ className?: string;
72
+ /**
73
+ * The section's body. Typically a single section component
74
+ * (`<HeroSection {…heroProps} />`). May be plain JSX for one-off
75
+ * sections that don't justify a `defineWebsiteSection` factory call.
76
+ */
77
+ children: ReactNode;
78
+ /**
79
+ * Pass-through `aria-*` / `role` attributes for sections that need
80
+ * to refine their landmark semantics (eg. `aria-label="Featured
81
+ * customer logos"` on a logo wall).
82
+ */
83
+ ariaLabel?: string;
84
+ ariaLabelledBy?: string;
85
+ }
86
+ export declare function WebsiteSection({ sectionRef, category, id, className, children, ariaLabel, ariaLabelledBy, }: WebsiteSectionProps): ReactNode;
87
+ export declare namespace WebsiteSection {
88
+ var displayName: string;
89
+ }
90
+ /** @wildo_source:part:end saas.website.section.component */
91
+ //# sourceMappingURL=WebsiteSection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebsiteSection.d.ts","sourceRoot":"","sources":["../../../../../src/core/layouts/WebsiteSection.tsx"],"names":[],"mappings":"AAAA,OAAc,EAAgC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAI5E,OAAO,EAAE,sBAAsB,EAAE,MAAM,wDAAwD,CAAC;AAChG,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,wCAAwC,CAAC;AAEhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;;OAMG;IACH,UAAU,EAAE,iBAAiB,GAAG,MAAM,CAAC;IACvC;;;;;;;OAOG;IACH,QAAQ,EAAE,sBAAsB,CAAC;IACjC;;;;OAIG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,QAAQ,EAAE,SAAS,CAAC;IACpB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,cAAc,CAAC,EAC7B,UAAU,EACV,QAAQ,EACR,EAAE,EACF,SAAS,EACT,QAAQ,EACR,SAAS,EACT,cAAc,GACf,EAAE,mBAAmB,GAAG,SAAS,CAmCjC;yBA3Ce,cAAc;;;AA6C9B,4DAA4D"}
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useMemo } from 'react';
3
+ import { WebsiteSectionContextProvider } from '../contexts/WebsiteSectionContext.js';
4
+ import { WebsiteSectionRefSchema, } from '../../schemas/refs/section-ref.schemas.js';
5
+ export function WebsiteSection({ sectionRef, category, id, className, children, ariaLabel, ariaLabelledBy, }) {
6
+ const safeSectionRef = useMemo(() => WebsiteSectionRefSchema.parse(sectionRef), [sectionRef]);
7
+ const sectionContextValue = useMemo(() => ({ sectionRef: safeSectionRef, category }), [safeSectionRef, category]);
8
+ /**
9
+ * Build the `<section>` attribute set imperatively to keep
10
+ * `aria-*` props off the element when omitted (rendering
11
+ * `aria-label={undefined}` still emits the attribute in some
12
+ * React versions — explicit absence is cleaner).
13
+ */
14
+ const sectionAttrs = {
15
+ className,
16
+ };
17
+ if (id !== undefined)
18
+ sectionAttrs.id = id;
19
+ if (ariaLabel !== undefined)
20
+ sectionAttrs['aria-label'] = ariaLabel;
21
+ if (ariaLabelledBy !== undefined)
22
+ sectionAttrs['aria-labelledby'] = ariaLabelledBy;
23
+ return (_jsx(WebsiteSectionContextProvider, { value: sectionContextValue, children: _jsx("section", { "data-section-ref": safeSectionRef, "data-section-category": category, ...sectionAttrs, children: children }) }));
24
+ }
25
+ WebsiteSection.displayName = 'WebsiteSection';
26
+ //# sourceMappingURL=WebsiteSection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebsiteSection.js","sourceRoot":"","sources":["../../../../../src/core/layouts/WebsiteSection.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,OAAO,EAAuC,MAAM,OAAO,CAAC;AAE5E,OAAO,EAAE,6BAA6B,EAAE,MAAM,mCAAmC,CAAC;AAGlF,OAAO,EACL,uBAAuB,GAExB,MAAM,wCAAwC,CAAC;AAqFhD,MAAM,UAAU,cAAc,CAAC,EAC7B,UAAU,EACV,QAAQ,EACR,EAAE,EACF,SAAS,EACT,QAAQ,EACR,SAAS,EACT,cAAc,GACM;IACpB,MAAM,cAAc,GAAG,OAAO,CAC5B,GAAG,EAAE,CAAC,uBAAuB,CAAC,KAAK,CAAC,UAAU,CAAC,EAC/C,CAAC,UAAU,CAAC,CACb,CAAC;IAEF,MAAM,mBAAmB,GAAG,OAAO,CACjC,GAAG,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC,EAChD,CAAC,cAAc,EAAE,QAAQ,CAAC,CAC3B,CAAC;IAEF;;;;;OAKG;IACH,MAAM,YAAY,GAAgC;QAChD,SAAS;KACV,CAAC;IACF,IAAI,EAAE,KAAK,SAAS;QAAE,YAAY,CAAC,EAAE,GAAG,EAAE,CAAC;IAC3C,IAAI,SAAS,KAAK,SAAS;QAAE,YAAY,CAAC,YAAY,CAAC,GAAG,SAAS,CAAC;IACpE,IAAI,cAAc,KAAK,SAAS;QAAE,YAAY,CAAC,iBAAiB,CAAC,GAAG,cAAc,CAAC;IAEnF,OAAO,CACL,KAAC,6BAA6B,IAAC,KAAK,EAAE,mBAAmB,YACvD,sCACoB,cAAmC,2BAC9B,QAAQ,KAC3B,YAAY,YAEf,QAAQ,GACD,GACoB,CACjC,CAAC;AACJ,CAAC;AACD,cAAc,CAAC,WAAW,GAAG,gBAAgB,CAAC","sourcesContent":["import React, { useMemo, type HTMLAttributes, type ReactNode } from 'react';\n\nimport { WebsiteSectionContextProvider } from '../contexts/WebsiteSectionContext';\nimport type { WebsiteSectionContextValue } from '../contexts/WebsiteSectionContext';\nimport { WebsiteSectionCategory } from '../../schemas/sections/website-section-category.shared';\nimport {\n WebsiteSectionRefSchema,\n type WebsiteSectionRef,\n} from '../../schemas/refs/section-ref.schemas';\n\n/**\n * @wildo_source:part:start saas.website.section.component facet:layer:core facet:family:website\n *\n * `WebsiteSection` — the per-section structural primitive.\n *\n * **Three responsibilities** (semantic-weight discipline):\n *\n * 1. **Provides `WebsiteSectionContext`** — registers the active\n * sectionRef + category so leaf primitives (`useWebsiteLabel`,\n * analytics overlays, structured-data emitters) can derive\n * keys / metadata without re-typing the section's identity.\n * 2. **Renders a semantic `<section>` landmark** — emits\n * `data-section-ref` and `data-section-category` attributes so\n * analytics tooling and SEO crawlers can identify and group\n * sections without parsing className conventions. Optional\n * `id` prop wires up anchor-link navigation\n * (`<a href=\"#pricing\">`).\n * 3. **Provides a styling seam** — `className` is forwarded to the\n * `<section>` element so designers can apply utility-class\n * decorations (background, padding, max-width container) without\n * having to compose multiple wrappers.\n *\n * **Why NOT render a `<div>` instead of `<section>`**: marketing\n * pages benefit from semantic landmarks for SEO scoring and\n * accessibility (screen-reader section navigation). A `<section>`\n * with `data-section-category=\"hero\"` is structurally meaningful;\n * a `<div>` with the same data-attribute carries the same metadata\n * but loses the landmark benefit.\n *\n * **Why expose `as` is NOT a prop**: switching the rendered tag\n * (`<article>` for blog post bodies, `<aside>` for sidebar widgets)\n * is legitimate but rare enough to be worth a separate primitive\n * (Phase 4: `<WebsiteAside>`, `<WebsiteArticle>`). Keeping\n * `WebsiteSection` rendered as a literal `<section>` removes a\n * rarely-used prop from the section-author API and keeps the SEO\n * landmark contract guaranteed at the type level.\n */\nexport interface WebsiteSectionProps {\n /**\n * The section's ref. Accepts an already-branded `WebsiteSectionRef`\n * or a raw string — the layout parses through\n * `WebsiteSectionRefSchema` so a malformed ref fails loud at\n * render. Typically sourced from the page manifest's\n * `sectionRefs[]` (eg. `<WebsiteSection sectionRef=\"hero\" …>`).\n */\n sectionRef: WebsiteSectionRef | string;\n /**\n * The section's structural intent (see `WebsiteSectionCategory`).\n * Required at the wrapper level (rather than only on the section\n * definition) because the wrapper emits `data-section-category`\n * for analytics, and asking the wrapper to look up the category\n * on a separate manifest registry would re-introduce the runtime\n * coupling the per-section context exists to avoid.\n */\n category: WebsiteSectionCategory;\n /**\n * Optional DOM id. Wires anchor-link navigation\n * (`<a href=\"#pricing\">`). When omitted, the section has no DOM\n * id — by design; not every section is an anchor target.\n */\n id?: string;\n /**\n * Optional className applied to the `<section>` element. Used by\n * designers for layout / spacing utilities. Section components\n * themselves should NOT pass className through to this wrapper —\n * keep the styling responsibility at the page-composition level.\n */\n className?: string;\n /**\n * The section's body. Typically a single section component\n * (`<HeroSection {…heroProps} />`). May be plain JSX for one-off\n * sections that don't justify a `defineWebsiteSection` factory call.\n */\n children: ReactNode;\n /**\n * Pass-through `aria-*` / `role` attributes for sections that need\n * to refine their landmark semantics (eg. `aria-label=\"Featured\n * customer logos\"` on a logo wall).\n */\n ariaLabel?: string;\n ariaLabelledBy?: string;\n}\n\nexport function WebsiteSection({\n sectionRef,\n category,\n id,\n className,\n children,\n ariaLabel,\n ariaLabelledBy,\n}: WebsiteSectionProps): ReactNode {\n const safeSectionRef = useMemo<WebsiteSectionRef>(\n () => WebsiteSectionRefSchema.parse(sectionRef),\n [sectionRef],\n );\n\n const sectionContextValue = useMemo<WebsiteSectionContextValue>(\n () => ({ sectionRef: safeSectionRef, category }),\n [safeSectionRef, category],\n );\n\n /**\n * Build the `<section>` attribute set imperatively to keep\n * `aria-*` props off the element when omitted (rendering\n * `aria-label={undefined}` still emits the attribute in some\n * React versions — explicit absence is cleaner).\n */\n const sectionAttrs: HTMLAttributes<HTMLElement> = {\n className,\n };\n if (id !== undefined) sectionAttrs.id = id;\n if (ariaLabel !== undefined) sectionAttrs['aria-label'] = ariaLabel;\n if (ariaLabelledBy !== undefined) sectionAttrs['aria-labelledby'] = ariaLabelledBy;\n\n return (\n <WebsiteSectionContextProvider value={sectionContextValue}>\n <section\n data-section-ref={safeSectionRef as unknown as string}\n data-section-category={category}\n {...sectionAttrs}\n >\n {children}\n </section>\n </WebsiteSectionContextProvider>\n );\n}\nWebsiteSection.displayName = 'WebsiteSection';\n/** @wildo_source:part:end saas.website.section.component */\n"]}
@@ -0,0 +1,65 @@
1
+ import type { AvailableLanguage } from '@wildo-ai/saas-models/public-runtime';
2
+ /**
3
+ * @wildo_source:part:start saas.website.routing.localized-path facet:layer:core facet:family:website
4
+ *
5
+ * `buildLocalizedRoutePath(opts)` — pure, isomorphic helper that
6
+ * returns the locale-prefixed pathname for a given route, honoring the
7
+ * marketing-site routing convention enforced by `buildAstroI18nConfig`
8
+ * (`prefixDefaultLocale: false`).
9
+ *
10
+ * **The architectural problem this solves**: the marketing site mounts the
11
+ * default locale at the
12
+ * un-prefixed path (`/`, `/pricing`, `/contact`) and every other locale
13
+ * under `/<locale>/…` (`/fr/`, `/fr/pricing`, `/fr/contact`). Internal
14
+ * links authored as raw `href="/pricing"` therefore work on
15
+ * default-locale pages and SILENTLY BREAK on non-default-locale pages
16
+ * (a French visitor on `/fr/index` clicking "Pricing" lands on the
17
+ * English `/pricing`, falling out of their locale).
18
+ *
19
+ * **Why a helper rather than asking authors to construct the path**:
20
+ * the rule is a framework-level routing convention — every consumer
21
+ * authors links the same way, every consumer must apply the same
22
+ * prefix logic, and getting it wrong produces a silent locale-leak
23
+ * that test suites do not catch (they navigate inside a single
24
+ * locale). A single helper makes the rule the framework's
25
+ * responsibility, not the section author's.
26
+ *
27
+ * **Routing rule** (matches `buildAstroI18nConfig` and the head
28
+ * renderer's `buildAbsoluteUrl` helper, which delegates here as the
29
+ * single source of truth):
30
+ * - When `locale === defaultLocale`: return `routePath` verbatim
31
+ * (the un-prefixed path).
32
+ * - Otherwise: return `/<locale>${routePath}`, with the special
33
+ * case that `routePath === '/'` collapses to `/<locale>` (no
34
+ * trailing slash) so the locale-prefixed root does not become
35
+ * the awkward `/<locale>/`.
36
+ *
37
+ * **Inputs are validated by the calling layer**, not here:
38
+ * - `locale` is `AvailableLanguage` (branded), so the type system
39
+ * prevents an unknown locale from reaching this function.
40
+ * - `routePath` is the same `routePath` field validated by the page
41
+ * manifest schema (`WebsitePageRoutePathSchema` enforces it
42
+ * starts with `/`). We do not re-validate here — duplicate
43
+ * validation costs CPU on every link render with no incremental
44
+ * safety, and the page-manifest layer fails loud at config-load
45
+ * time if a malformed path leaks in.
46
+ *
47
+ * **Boundary**: pure function, zero React / DOM / fs dependencies.
48
+ * Safe to import from both the React-island bundle (consumed by
49
+ * `WebsiteInternalLink`) and the SSR head renderer (consumed by
50
+ * `buildAbsoluteUrl`). The package's routing tests pin this behavior.
51
+ */
52
+ export interface BuildLocalizedRoutePathOptions {
53
+ /** The currently-active locale (typically from `useWebsiteRuntime().locale`). */
54
+ locale: AvailableLanguage;
55
+ /** The site's default locale (typically from `useWebsiteRuntime().defaultLocale`). */
56
+ defaultLocale: AvailableLanguage;
57
+ /**
58
+ * The page's route path, exactly as declared in
59
+ * `WebsitePageManifest.routePath` (always starts with `/`).
60
+ */
61
+ routePath: string;
62
+ }
63
+ export declare function buildLocalizedRoutePath(opts: BuildLocalizedRoutePathOptions): string;
64
+ /** @wildo_source:part:end saas.website.routing.localized-path */
65
+ //# sourceMappingURL=internal-routing.utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal-routing.utils.d.ts","sourceRoot":"","sources":["../../../../../src/core/routing/internal-routing.utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAE9E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,MAAM,WAAW,8BAA8B;IAC7C,iFAAiF;IACjF,MAAM,EAAE,iBAAiB,CAAC;IAC1B,sFAAsF;IACtF,aAAa,EAAE,iBAAiB,CAAC;IACjC;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,8BAA8B,GAAG,MAAM,CAWpF;AACD,iEAAiE"}
@@ -0,0 +1,14 @@
1
+ export function buildLocalizedRoutePath(opts) {
2
+ const { locale, defaultLocale, routePath } = opts;
3
+ const localeStr = String(locale);
4
+ const defaultLocaleStr = String(defaultLocale);
5
+ if (localeStr === defaultLocaleStr) {
6
+ return routePath;
7
+ }
8
+ if (routePath === '/') {
9
+ return `/${localeStr}`;
10
+ }
11
+ return `/${localeStr}${routePath}`;
12
+ }
13
+ /** @wildo_source:part:end saas.website.routing.localized-path */
14
+ //# sourceMappingURL=internal-routing.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal-routing.utils.js","sourceRoot":"","sources":["../../../../../src/core/routing/internal-routing.utils.ts"],"names":[],"mappings":"AAgEA,MAAM,UAAU,uBAAuB,CAAC,IAAoC;IAC1E,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;IAClD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IACjC,MAAM,gBAAgB,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC;IAC/C,IAAI,SAAS,KAAK,gBAAgB,EAAE,CAAC;QACnC,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,SAAS,KAAK,GAAG,EAAE,CAAC;QACtB,OAAO,IAAI,SAAS,EAAE,CAAC;IACzB,CAAC;IACD,OAAO,IAAI,SAAS,GAAG,SAAS,EAAE,CAAC;AACrC,CAAC;AACD,iEAAiE","sourcesContent":["import type { AvailableLanguage } from '@wildo-ai/saas-models/public-runtime';\n\n/**\n * @wildo_source:part:start saas.website.routing.localized-path facet:layer:core facet:family:website\n *\n * `buildLocalizedRoutePath(opts)` — pure, isomorphic helper that\n * returns the locale-prefixed pathname for a given route, honoring the\n * marketing-site routing convention enforced by `buildAstroI18nConfig`\n * (`prefixDefaultLocale: false`).\n *\n * **The architectural problem this solves**: the marketing site mounts the\n * default locale at the\n * un-prefixed path (`/`, `/pricing`, `/contact`) and every other locale\n * under `/<locale>/…` (`/fr/`, `/fr/pricing`, `/fr/contact`). Internal\n * links authored as raw `href=\"/pricing\"` therefore work on\n * default-locale pages and SILENTLY BREAK on non-default-locale pages\n * (a French visitor on `/fr/index` clicking \"Pricing\" lands on the\n * English `/pricing`, falling out of their locale).\n *\n * **Why a helper rather than asking authors to construct the path**:\n * the rule is a framework-level routing convention — every consumer\n * authors links the same way, every consumer must apply the same\n * prefix logic, and getting it wrong produces a silent locale-leak\n * that test suites do not catch (they navigate inside a single\n * locale). A single helper makes the rule the framework's\n * responsibility, not the section author's.\n *\n * **Routing rule** (matches `buildAstroI18nConfig` and the head\n * renderer's `buildAbsoluteUrl` helper, which delegates here as the\n * single source of truth):\n * - When `locale === defaultLocale`: return `routePath` verbatim\n * (the un-prefixed path).\n * - Otherwise: return `/<locale>${routePath}`, with the special\n * case that `routePath === '/'` collapses to `/<locale>` (no\n * trailing slash) so the locale-prefixed root does not become\n * the awkward `/<locale>/`.\n *\n * **Inputs are validated by the calling layer**, not here:\n * - `locale` is `AvailableLanguage` (branded), so the type system\n * prevents an unknown locale from reaching this function.\n * - `routePath` is the same `routePath` field validated by the page\n * manifest schema (`WebsitePageRoutePathSchema` enforces it\n * starts with `/`). We do not re-validate here — duplicate\n * validation costs CPU on every link render with no incremental\n * safety, and the page-manifest layer fails loud at config-load\n * time if a malformed path leaks in.\n *\n * **Boundary**: pure function, zero React / DOM / fs dependencies.\n * Safe to import from both the React-island bundle (consumed by\n * `WebsiteInternalLink`) and the SSR head renderer (consumed by\n * `buildAbsoluteUrl`). The package's routing tests pin this behavior.\n */\nexport interface BuildLocalizedRoutePathOptions {\n /** The currently-active locale (typically from `useWebsiteRuntime().locale`). */\n locale: AvailableLanguage;\n /** The site's default locale (typically from `useWebsiteRuntime().defaultLocale`). */\n defaultLocale: AvailableLanguage;\n /**\n * The page's route path, exactly as declared in\n * `WebsitePageManifest.routePath` (always starts with `/`).\n */\n routePath: string;\n}\n\nexport function buildLocalizedRoutePath(opts: BuildLocalizedRoutePathOptions): string {\n const { locale, defaultLocale, routePath } = opts;\n const localeStr = String(locale);\n const defaultLocaleStr = String(defaultLocale);\n if (localeStr === defaultLocaleStr) {\n return routePath;\n }\n if (routePath === '/') {\n return `/${localeStr}`;\n }\n return `/${localeStr}${routePath}`;\n}\n/** @wildo_source:part:end saas.website.routing.localized-path */\n"]}
@@ -0,0 +1,129 @@
1
+ /**
2
+ * @wildo-package @wildo-ai/saas-website
3
+ *
4
+ * SaaS marketing/landing website engine — schemas + helpers + layout
5
+ * placeholders + Astro/MDX adapters + SEO/AEO/GEO builder library.
6
+ *
7
+ * Per `saas-website-slice.md` D6/D15: this engine ships **NO concrete
8
+ * business sections** (no Hero, no CTA, no Pricing). It ships the
9
+ * structural and helper machinery; consumers author every business
10
+ * section themselves via the `defineWebsiteSection` factory and the
11
+ * accompanying SKILLs in `framework-knowledge`.
12
+ *
13
+ * @wildo-boundary
14
+ * - This barrel MAY re-export from any subdirectory of `src/`.
15
+ * - Subdirectories MUST observe their own boundary contracts (see
16
+ * each subdirectory's own `index.ts` `@wildo-boundary` block):
17
+ * - `schemas/` : NO React, NO Astro, NO `~wildo-frontend-lib/**`
18
+ * - `seo/` : NO React, NO Astro
19
+ * - `core/` : MAY use React; NO `~wildo-frontend-lib/**` (Q17=B)
20
+ * - `astro/` : MAY use Astro; NO React component runtime imports
21
+ * - `mdx/` : MAY use `@mdx-js/*`
22
+ * - `integration/` : MAY use React; MAY import the
23
+ * `@wildo-ai/saas-models/public-runtime` SUBPATH,
24
+ * the `@wildo-ai/presets-components-models/public-runtime`
25
+ * SUBPATH, `@wildo-ai/zod-decorators`,
26
+ * plus the SSR / Astro-middleware tier
27
+ * (`axios`, `jsonwebtoken`, `socket.io-client`,
28
+ * `reflect-metadata`) — D12 revised
29
+ * 2026-04-19 (second same-day loosening);
30
+ * the ROOT barrels of `@wildo-ai/saas-models`
31
+ * and `@wildo-ai/presets-components-models`
32
+ * are FORBIDDEN (they boot the decorator
33
+ * framework / pull the heavy registry);
34
+ * the saas-app DI / backend stack
35
+ * (`inversify`, `mongoose`, `express`,
36
+ * `pino`, `helmet`, `argon2`, `bcrypt`,
37
+ * the `@wildo-ai/saas-frontend-lib` /
38
+ * `@wildo-ai/saas-backend-lib` /
39
+ * `@wildo-ai/external-connectors-models`
40
+ * root + subpath surfaces) is FORBIDDEN;
41
+ * MUST NOT import `~wildo-frontend-lib/**`
42
+ * (Q17=B / D16)
43
+ *
44
+ * D12 (client-bundle import discipline, all `core/`/`schemas/`/`integration/`
45
+ * files): the entire saas-website source tree ships into a browser bundle
46
+ * (Astro SSG + React islands). Every shared model atom MUST come from a
47
+ * side-effect-free `/public-runtime` subpath:
48
+ * - `@wildo-ai/saas-models/public-runtime` is the primary door
49
+ * (saas-models-public-runtime.md K-6 — consumers see one path).
50
+ * - `@wildo-ai/presets-components-models/public-runtime` is the
51
+ * internal seam for symbols not (yet) re-exported by saas-models.
52
+ * - `@wildo-ai/zod-decorators` is allowed to import (first revision
53
+ * 2026-04-19). The package itself is side-effect-free; only an
54
+ * explicit `initZodDecorators()` call mutates `z.ZodType.prototype`,
55
+ * which neither saas-website nor the saas-models /public-runtime
56
+ * barrel does.
57
+ * - `axios`, `jsonwebtoken`, `socket.io-client`, `reflect-metadata` are
58
+ * allowed for SSR / Astro-middleware code paths (second same-day
59
+ * revision 2026-04-19). The website engine needs them to provide
60
+ * middleware utilities (HTTP proxying to the SaaS backend, JWT
61
+ * verification of cross-frontend handoff codes, real-time push,
62
+ * decorator-metadata reads). They are SSR-tier — discipline
63
+ * (and a future strict/loose entry split for React islands vs
64
+ * middleware) keeps them out of the React-island bundle.
65
+ *
66
+ * The ROOT barrel of `@wildo-ai/saas-models` is forbidden everywhere in
67
+ * this package — it calls `initZodDecorators()` at module load and pulls
68
+ * `@wildo-ai/external-connectors-models` + the full schema graph into
69
+ * the bundle. Same forbid applies to the root barrel of
70
+ * `@wildo-ai/presets-components-models`, the saas-app DI runtime
71
+ * (`inversify`, `@wildo-ai/saas-frontend-lib`, `@wildo-ai/saas-backend-lib`),
72
+ * and pure-backend libs (`mongoose`, `express`, `pino`, `helmet`,
73
+ * `argon2`, `bcrypt`). Enforced by `src/__tests__/bundle-isolation.test.ts`.
74
+ *
75
+ * These boundaries are enforced by review + the bundle-isolation test
76
+ * above until Phase 8 promotes them to a programmatic ESLint check
77
+ * (cf. `saas-website-slice.md`
78
+ * §"DEFERRED to Phase 8 — boundary lint mechanism design").
79
+ *
80
+ * Phase 1+2 status: schemas + runtime contexts/hooks + layout
81
+ * primitives + L1 placeholders + factories are all exported. Phase 5
82
+ * adds the Astro adapter; Phase 5.7 the `integration/` layer.
83
+ *
84
+ * D18 (L1 boundary, Phase 2): the L1 primitives (`WebsiteHeading`,
85
+ * `WebsiteText`, `WebsiteButton`, `WebsiteLink`, `WebsiteImage`) are
86
+ * self-contained placeholders — they do NOT consume
87
+ * `ComponentRegistryService` from `saas-frontend-lib` and do NOT
88
+ * re-export `saas-frontend-lib` primitives. Consumers override at the
89
+ * L1 boundary by barrel-re-exporting their preferred styled variant
90
+ * from their app's `app/website/primitives/<Primitive>.tsx`. Phase 6
91
+ * dogfood will revisit and decide whether to promote a registry-based
92
+ * override mechanism.
93
+ */
94
+ export * from './schemas/refs/page-ref.schemas';
95
+ export * from './schemas/refs/section-ref.schemas';
96
+ export * from './schemas/label-keys/website-label-key.schemas';
97
+ export * from './schemas/sections/website-section-category.shared';
98
+ export * from './schemas/sections/website-section-definition.shared.schemas';
99
+ export * from './schemas/structured-data/website-structured-data.shared.schemas';
100
+ export * from './schemas/structured-data/structured-data-reconciliation.shared';
101
+ export * from './schemas/design-tokens/website-design-tokens.shared.schemas';
102
+ export * from './schemas/manifests/website-page-manifest.shared.schemas';
103
+ export * from './schemas/manifests/website-root-config.shared.schemas';
104
+ export * from './schemas/blog/blog-post-frontmatter.shared.schemas';
105
+ export * from './schemas/validators/label-pack.validator';
106
+ export * from './core/contexts/WebsiteRuntimeContext';
107
+ export * from './core/contexts/useWebsiteRuntime';
108
+ export * from './core/contexts/WebsitePageContext';
109
+ export * from './core/contexts/useWebsitePage';
110
+ export * from './core/contexts/WebsiteSectionContext';
111
+ export * from './core/contexts/useWebsiteSection';
112
+ export * from './core/hooks/useWebsiteLabel';
113
+ export * from './core/hooks/useWebsiteDesignTokens';
114
+ export * from './core/factories/define-website-section';
115
+ export * from './core/factories/define-website-page-manifest';
116
+ export * from './core/layouts/WebsitePageLayout';
117
+ export * from './core/layouts/WebsiteSection';
118
+ export * from './core/anonymous-session/website-anonymous-session-client';
119
+ export * from './core/anonymous-session/inbound-contact-form.schema';
120
+ export * from './core/anonymous-session/InboundContactForm';
121
+ export * from './components/low-level/WebsiteHeading';
122
+ export * from './components/low-level/WebsiteText';
123
+ export * from './components/low-level/WebsiteLink';
124
+ export * from './components/low-level/WebsiteInternalLink';
125
+ export * from './core/routing/internal-routing.utils';
126
+ export * from './components/low-level/WebsiteButton';
127
+ export * from './components/low-level/WebsiteInternalButton';
128
+ export * from './components/low-level/WebsiteImage';
129
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4FG;AAEH,cAAc,iCAAiC,CAAC;AAChD,cAAc,oCAAoC,CAAC;AACnD,cAAc,gDAAgD,CAAC;AAC/D,cAAc,oDAAoD,CAAC;AACnE,cAAc,8DAA8D,CAAC;AAC7E,cAAc,kEAAkE,CAAC;AACjF,cAAc,iEAAiE,CAAC;AAChF,cAAc,8DAA8D,CAAC;AAC7E,cAAc,0DAA0D,CAAC;AACzE,cAAc,wDAAwD,CAAC;AACvE,cAAc,qDAAqD,CAAC;AACpE,cAAc,2CAA2C,CAAC;AAE1D,cAAc,uCAAuC,CAAC;AACtD,cAAc,mCAAmC,CAAC;AAClD,cAAc,oCAAoC,CAAC;AACnD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,uCAAuC,CAAC;AACtD,cAAc,mCAAmC,CAAC;AAClD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,qCAAqC,CAAC;AACpD,cAAc,yCAAyC,CAAC;AACxD,cAAc,+CAA+C,CAAC;AAC9D,cAAc,kCAAkC,CAAC;AACjD,cAAc,+BAA+B,CAAC;AAE9C,cAAc,2DAA2D,CAAC;AAC1E,cAAc,sDAAsD,CAAC;AACrE,cAAc,6CAA6C,CAAC;AAE5D,cAAc,uCAAuC,CAAC;AACtD,cAAc,oCAAoC,CAAC;AACnD,cAAc,oCAAoC,CAAC;AACnD,cAAc,4CAA4C,CAAC;AAC3D,cAAc,uCAAuC,CAAC;AACtD,cAAc,sCAAsC,CAAC;AACrD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,qCAAqC,CAAC"}