@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,298 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import { validateLabelPackAgainstManifest, } from '../schemas/validators/label-pack.validator.js';
4
+ /**
5
+ * @wildo_source:part:start saas.website.astro.label-loader facet:layer:astro facet:family:website
6
+ *
7
+ * `label-pack-loader.ts` — synchronous, fs-backed loader for
8
+ * marketing-site label packs. Consumed by:
9
+ * - the Astro adapter (`WebsitePageBridge.astro` reads the active
10
+ * locale's pack at request time during SSR / pre-render),
11
+ * - the `wildo website audit` CLI (Phase 7) to validate every
12
+ * locale pack against every manifest at CI time,
13
+ * - any build-time hook that wants to fail fast on a missing or
14
+ * malformed pack.
15
+ *
16
+ * **Convention** (matches the slice plan + the validator's contract):
17
+ *
18
+ * - One file per locale: `<packsDir>/<locale>.json`
19
+ * - JSON object: flat key-value map, `Record<string, string>`
20
+ * - Keys are fully-composed `WebsiteLabelKey`s (`website.<page>.<section>.<slot>`)
21
+ * - Values are the localized strings
22
+ *
23
+ * **Why synchronous I/O**:
24
+ * - The Astro frontmatter executes at build / SSR time in a Node
25
+ * process (no event loop pressure to relieve), and the bridge
26
+ * wants the pack resolved before it emits any HTML — async would
27
+ * force the consumer to `await` inside an Astro frontmatter,
28
+ * which is supported but adds a needless yield-point on a single
29
+ * small JSON file.
30
+ * - The validator is pure and synchronous; making the loader async
31
+ * would add a coloring boundary for no benefit.
32
+ *
33
+ * **Failure modes — all surfaced as thrown errors with actionable
34
+ * messages**:
35
+ * - File missing — wrapped in a `[label-pack-loader] Could not read
36
+ * pack at <path>` error so a typo in the dir / locale is obvious.
37
+ * - JSON parse failure — error includes the file path and the
38
+ * parser's own message.
39
+ * - Top-level shape is not an object, or a value is not a string —
40
+ * error names the offending key + observed type.
41
+ *
42
+ * **Why three exports** (loader, multi-loader, validator-runner):
43
+ * - `loadLabelPack(packsDir, locale)` is the atomic primitive
44
+ * (one file → one validated `LabelPack`). Used by the bridge at
45
+ * request time.
46
+ * - `loadAllLabelPacks(packsDir, siteContext)` is the build-time
47
+ * batch — every locale pack at once. Used by the CLI auditor
48
+ * and by the build-start hook.
49
+ * - `validateAllLabelPacks(packsDir, siteContext, expectedKeysBySectionRef)`
50
+ * is the highest-level entry point: load every pack, run the
51
+ * validator on each, and either return the diagnostic results
52
+ * or throw on the first hard error. The returned shape carries
53
+ * per-locale diagnostics so the caller can format a CLI report.
54
+ *
55
+ * **`siteContext` parameter** — the consumer's composed
56
+ * `WebsiteSiteContext` (see `website-site-context.ts`). The loader
57
+ * reads `siteContext.websiteConfig.locales` for "which locales does
58
+ * the site ship?" and `siteContext.rootConfig.{pageManifests,
59
+ * chromeLabelKeys, skipToMainContentLabelKey}` for the manifest
60
+ * registry / chrome-key set the validator runs against.
61
+ */
62
+ /**
63
+ * Load and validate a single locale's label pack. Synchronous —
64
+ * suitable for Astro frontmatter execution.
65
+ *
66
+ * @param packsDir Absolute or cwd-relative path to the directory
67
+ * containing per-locale `<locale>.json` files.
68
+ * @param locale The locale to load. Must be a member of
69
+ * `WildoMarketingWebsiteConfig.locales`.
70
+ * @returns A flat `LabelPack` (`Record<string, string>`) ready to
71
+ * feed into `WebsiteRuntimeContextValue.labelPack`.
72
+ * @throws When the file is missing, malformed JSON, not an object,
73
+ * or contains a non-string value.
74
+ */
75
+ export function loadLabelPack(packsDir, locale) {
76
+ const filePath = resolve(packsDir, `${locale}.json`);
77
+ let raw;
78
+ try {
79
+ raw = readFileSync(filePath, 'utf-8');
80
+ }
81
+ catch (cause) {
82
+ throw new Error(`[label-pack-loader] Could not read label pack at ${filePath} ` +
83
+ `(locale="${locale}"). ` +
84
+ `Make sure the directory exists and contains a "${locale}.json" file. ` +
85
+ `Underlying error: ${cause.message}`, { cause });
86
+ }
87
+ let parsed;
88
+ try {
89
+ parsed = JSON.parse(raw);
90
+ }
91
+ catch (cause) {
92
+ throw new Error(`[label-pack-loader] Failed to parse JSON in ${filePath} ` +
93
+ `(locale="${locale}"). Underlying parser error: ${cause.message}`, { cause });
94
+ }
95
+ return assertLabelPackShape(parsed, filePath, locale);
96
+ }
97
+ /**
98
+ * Batch loader — every locale declared on the site at once. The map
99
+ * iteration order matches the declaration order of
100
+ * `siteContext.websiteConfig.locales` so CLI reports come out in a
101
+ * deterministic, authored sequence.
102
+ */
103
+ export function loadAllLabelPacks(packsDir, siteContext) {
104
+ const packs = new Map();
105
+ for (const entry of siteContext.websiteConfig.locales) {
106
+ packs.set(entry.locale, loadLabelPack(packsDir, entry.locale));
107
+ }
108
+ return packs;
109
+ }
110
+ /**
111
+ * Highest-level entry point: load every locale's pack and run the
112
+ * label-pack validator against the manifest registry.
113
+ *
114
+ * **Return shape decision** — this function does NOT throw on
115
+ * `missing` keys; it returns the per-locale results so the caller
116
+ * (the build-start hook OR the CLI auditor OR a debugging
117
+ * subprocess) can decide how to surface the failure (process exit
118
+ * code, formatted CLI output, JSON report, …). A thin `throwIfAny…`
119
+ * helper sits next to it for the common "fail the build on first
120
+ * missing key" path.
121
+ */
122
+ export function loadAndValidateAllLabelPacks(packsDir, siteContext, expectedKeysBySectionRef) {
123
+ const { rootConfig } = siteContext;
124
+ const packs = loadAllLabelPacks(packsDir, siteContext);
125
+ const manifests = rootConfig.pageManifests;
126
+ const effectiveChromeKeys = composeEffectiveChromeLabelKeys(rootConfig);
127
+ const validations = [];
128
+ for (const [locale, pack] of packs) {
129
+ validations.push({
130
+ locale,
131
+ result: validateLabelPackAgainstManifest(manifests, expectedKeysBySectionRef, pack, effectiveChromeKeys),
132
+ });
133
+ }
134
+ return validations;
135
+ }
136
+ /**
137
+ * Compose the chrome-label-key set the validator should treat as
138
+ * "expected" — `WebsiteRootConfig.chromeLabelKeys` (consumer-authored)
139
+ * UNION the framework-required `skipToMainContentLabelKey`.
140
+ *
141
+ * Centralized here because:
142
+ * - Both `loadAndValidateAllLabelPacks` (used at build time) and
143
+ * `buildKeyOriginMap` (used to annotate missing-key errors) need
144
+ * the same composed list, and forking the union into two places
145
+ * would be a drift hazard.
146
+ * - The framework owns the skip-link key (it is rendered by
147
+ * `WebsitePageLayout`, not the consumer's chrome), so the
148
+ * consumer should NOT have to remember to also list it in
149
+ * `chromeLabelKeys`. Auto-injection here keeps the contract
150
+ * minimal at the consumer's call site (just declare the key in
151
+ * `WebsiteRootConfig.skipToMainContentLabelKey` once + ship the
152
+ * translation in every locale pack) while still surfacing the key
153
+ * through the validator's "missing"/"orphaned" diagnostics.
154
+ * - De-duplication is via `Set` semantics so a consumer who
155
+ * defensively lists the key in both places (no-op but harmless)
156
+ * does not produce a duplicate-key warning downstream.
157
+ */
158
+ function composeEffectiveChromeLabelKeys(rootConfig) {
159
+ const set = new Set(rootConfig.chromeLabelKeys);
160
+ set.add(rootConfig.skipToMainContentLabelKey);
161
+ return Array.from(set);
162
+ }
163
+ /**
164
+ * Convenience wrapper for the build-start hook: validates every
165
+ * declared locale's pack and throws a SINGLE aggregated error if any
166
+ * locale has a hard failure (`missing` keys or `unmappedSectionRefs`).
167
+ * Orphaned keys are logged via `console.warn` (soft warning per the
168
+ * validator's contract) but do NOT throw.
169
+ *
170
+ * **Why aggregate (not first-fail)**: a multi-locale marketing site
171
+ * with `en` and `fr` packs both missing keys would, under a
172
+ * first-fail policy, force the author to fix `en`, re-run the build,
173
+ * THEN see `fr`'s problem. Aggregating means one build run surfaces
174
+ * every locale's hard errors together — material UX win for the
175
+ * authoring loop. The aggregated message keeps each locale's failure
176
+ * in its own block so the diagnostic stays scannable.
177
+ *
178
+ * **Why throw vs. return**: the build-start hook lives in
179
+ * `astro.config.mjs` (or a `vite-plugin`-style integration) and
180
+ * benefits from the shortest possible code path — a thrown error
181
+ * naturally aborts `astro build` with a non-zero exit code and
182
+ * surfaces the message in the build log. The richer
183
+ * `loadAndValidateAllLabelPacks` is what `wildo website audit` calls
184
+ * when it wants programmatic access to the per-locale results.
185
+ *
186
+ * **Key-origin annotation**: every `missing` key is tagged with the
187
+ * page (and, if section-scoped, the section) that declared it, so the
188
+ * author can grep + open the right `defineWebsiteSection` /
189
+ * `defineWebsitePageManifest` call site without leaving the build log.
190
+ */
191
+ export function assertLabelPacksValid(packsDir, siteContext, expectedKeysBySectionRef) {
192
+ const { rootConfig } = siteContext;
193
+ const validations = loadAndValidateAllLabelPacks(packsDir, siteContext, expectedKeysBySectionRef);
194
+ /**
195
+ * Build a key → origin map ONCE per assert call (cost is bounded by
196
+ * the total number of declared keys across the whole site, which is
197
+ * typically small for a marketing surface). Enriches the missing-key
198
+ * error with the page/section that committed to each key — the
199
+ * author can grep the ref to find the site authoring call site.
200
+ */
201
+ const keyOrigins = buildKeyOriginMap(rootConfig.pageManifests, expectedKeysBySectionRef, composeEffectiveChromeLabelKeys(rootConfig));
202
+ const localeErrorBlocks = [];
203
+ for (const { locale, result } of validations) {
204
+ const blocks = [];
205
+ if (result.unmappedSectionRefs.length > 0) {
206
+ blocks.push(` unmappedSectionRefs (build-pipeline bug — sections referenced by a ` +
207
+ `manifest but missing from the expected-keys collector): ` +
208
+ `[${result.unmappedSectionRefs.join(', ')}]. ` +
209
+ `Add every referenced section to the array passed to ` +
210
+ `collectExpectedLabelKeysFromSections().`);
211
+ }
212
+ if (result.missing.length > 0) {
213
+ const annotated = result.missing.map((key) => ` "${key}" ← ${keyOrigins.get(key) ?? 'unknown origin'}`);
214
+ blocks.push(` missing ${result.missing.length} required key(s) in ${packsDir}/${locale}.json:\n` +
215
+ annotated.join('\n'));
216
+ }
217
+ if (blocks.length > 0) {
218
+ localeErrorBlocks.push(`Locale "${locale}":\n${blocks.join('\n')}`);
219
+ }
220
+ if (result.orphaned.length > 0) {
221
+ console.warn(`[label-pack-loader] Locale "${locale}": ${result.orphaned.length} ` +
222
+ `orphaned key(s) in ${locale}.json (declared in pack but not consumed ` +
223
+ `by any section or page manifest):\n ${result.orphaned.join('\n ')}`);
224
+ }
225
+ }
226
+ if (localeErrorBlocks.length > 0) {
227
+ throw new Error(`[label-pack-loader] Label pack validation failed for ` +
228
+ `${localeErrorBlocks.length} locale(s):\n\n${localeErrorBlocks.join('\n\n')}`);
229
+ }
230
+ }
231
+ /**
232
+ * Build a `keyString -> "page <pageRef> meta titleKey" | "section <sectionRef> in page <pageRef>"`
233
+ * lookup so the assert message can name the call site that committed
234
+ * to each missing key. Pure and small (cost is O(totalKeys) per build
235
+ * — bounded by the marketing surface).
236
+ */
237
+ function buildKeyOriginMap(manifests, expectedKeysBySectionRef, chromeLabelKeys) {
238
+ const origins = new Map();
239
+ for (const manifest of manifests) {
240
+ const pageRefStr = manifest.ref;
241
+ const titleKey = manifest.metaLabelKeys.titleKey;
242
+ const descriptionKey = manifest.metaLabelKeys.descriptionKey;
243
+ if (!origins.has(titleKey)) {
244
+ origins.set(titleKey, `page "${pageRefStr}" metaLabelKeys.titleKey`);
245
+ }
246
+ if (!origins.has(descriptionKey)) {
247
+ origins.set(descriptionKey, `page "${pageRefStr}" metaLabelKeys.descriptionKey`);
248
+ }
249
+ for (const sectionRef of manifest.sectionRefs) {
250
+ const sectionRefStr = sectionRef;
251
+ const sectionKeys = expectedKeysBySectionRef.get(sectionRef);
252
+ if (sectionKeys === undefined)
253
+ continue;
254
+ for (const sectionKey of sectionKeys) {
255
+ const keyStr = sectionKey;
256
+ if (!origins.has(keyStr)) {
257
+ origins.set(keyStr, `section "${sectionRefStr}" in page "${pageRefStr}"`);
258
+ }
259
+ }
260
+ }
261
+ }
262
+ for (const chromeKey of chromeLabelKeys) {
263
+ const keyStr = chromeKey;
264
+ if (!origins.has(keyStr)) {
265
+ origins.set(keyStr, `WebsiteRootConfig.chromeLabelKeys`);
266
+ }
267
+ }
268
+ return origins;
269
+ }
270
+ /**
271
+ * Defensive shape guard. Surface the offending key + observed type
272
+ * so the consumer can find the malformed entry quickly. Pure
273
+ * validation — no transformation; the returned value is the same
274
+ * object reference passed in (cast to `LabelPack`) so the caller
275
+ * does not pay an unnecessary copy.
276
+ */
277
+ function assertLabelPackShape(parsed, filePath, locale) {
278
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
279
+ throw new Error(`[label-pack-loader] ${filePath} (locale="${locale}") must contain a ` +
280
+ `JSON object at the top level (got ${describeJson(parsed)}).`);
281
+ }
282
+ for (const [key, value] of Object.entries(parsed)) {
283
+ if (typeof value !== 'string') {
284
+ throw new Error(`[label-pack-loader] ${filePath} (locale="${locale}"): key "${key}" ` +
285
+ `must map to a string (got ${describeJson(value)}).`);
286
+ }
287
+ }
288
+ return parsed;
289
+ }
290
+ function describeJson(value) {
291
+ if (value === null)
292
+ return 'null';
293
+ if (Array.isArray(value))
294
+ return 'array';
295
+ return typeof value;
296
+ }
297
+ /** @wildo_source:part:end saas.website.astro.label-loader */
298
+ //# sourceMappingURL=label-pack-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"label-pack-loader.js","sourceRoot":"","sources":["../../../../src/astro/label-pack-loader.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport type { AvailableLanguage } from '@wildo-ai/saas-models/public-runtime';\n\nimport type { WebsiteLabelKey } from '../schemas/label-keys/website-label-key.schemas';\nimport type { WebsitePageManifest } from '../schemas/manifests/website-page-manifest.shared.schemas';\nimport type { WebsiteRootConfig } from '../schemas/manifests/website-root-config.shared.schemas';\nimport {\n validateLabelPackAgainstManifest,\n type ExpectedLabelKeysBySectionRef,\n type LabelPack,\n type LabelPackValidationResult,\n} from '../schemas/validators/label-pack.validator';\n\nimport type { WebsiteSiteContext } from './website-site-context';\n\n/**\n * @wildo_source:part:start saas.website.astro.label-loader facet:layer:astro facet:family:website\n *\n * `label-pack-loader.ts` — synchronous, fs-backed loader for\n * marketing-site label packs. Consumed by:\n * - the Astro adapter (`WebsitePageBridge.astro` reads the active\n * locale's pack at request time during SSR / pre-render),\n * - the `wildo website audit` CLI (Phase 7) to validate every\n * locale pack against every manifest at CI time,\n * - any build-time hook that wants to fail fast on a missing or\n * malformed pack.\n *\n * **Convention** (matches the slice plan + the validator's contract):\n *\n * - One file per locale: `<packsDir>/<locale>.json`\n * - JSON object: flat key-value map, `Record<string, string>`\n * - Keys are fully-composed `WebsiteLabelKey`s (`website.<page>.<section>.<slot>`)\n * - Values are the localized strings\n *\n * **Why synchronous I/O**:\n * - The Astro frontmatter executes at build / SSR time in a Node\n * process (no event loop pressure to relieve), and the bridge\n * wants the pack resolved before it emits any HTML — async would\n * force the consumer to `await` inside an Astro frontmatter,\n * which is supported but adds a needless yield-point on a single\n * small JSON file.\n * - The validator is pure and synchronous; making the loader async\n * would add a coloring boundary for no benefit.\n *\n * **Failure modes — all surfaced as thrown errors with actionable\n * messages**:\n * - File missing — wrapped in a `[label-pack-loader] Could not read\n * pack at <path>` error so a typo in the dir / locale is obvious.\n * - JSON parse failure — error includes the file path and the\n * parser's own message.\n * - Top-level shape is not an object, or a value is not a string —\n * error names the offending key + observed type.\n *\n * **Why three exports** (loader, multi-loader, validator-runner):\n * - `loadLabelPack(packsDir, locale)` is the atomic primitive\n * (one file → one validated `LabelPack`). Used by the bridge at\n * request time.\n * - `loadAllLabelPacks(packsDir, siteContext)` is the build-time\n * batch — every locale pack at once. Used by the CLI auditor\n * and by the build-start hook.\n * - `validateAllLabelPacks(packsDir, siteContext, expectedKeysBySectionRef)`\n * is the highest-level entry point: load every pack, run the\n * validator on each, and either return the diagnostic results\n * or throw on the first hard error. The returned shape carries\n * per-locale diagnostics so the caller can format a CLI report.\n *\n * **`siteContext` parameter** — the consumer's composed\n * `WebsiteSiteContext` (see `website-site-context.ts`). The loader\n * reads `siteContext.websiteConfig.locales` for \"which locales does\n * the site ship?\" and `siteContext.rootConfig.{pageManifests,\n * chromeLabelKeys, skipToMainContentLabelKey}` for the manifest\n * registry / chrome-key set the validator runs against.\n */\n\n/**\n * Load and validate a single locale's label pack. Synchronous —\n * suitable for Astro frontmatter execution.\n *\n * @param packsDir Absolute or cwd-relative path to the directory\n * containing per-locale `<locale>.json` files.\n * @param locale The locale to load. Must be a member of\n * `WildoMarketingWebsiteConfig.locales`.\n * @returns A flat `LabelPack` (`Record<string, string>`) ready to\n * feed into `WebsiteRuntimeContextValue.labelPack`.\n * @throws When the file is missing, malformed JSON, not an object,\n * or contains a non-string value.\n */\nexport function loadLabelPack(packsDir: string, locale: AvailableLanguage): LabelPack {\n const filePath = resolve(packsDir, `${locale}.json`);\n\n let raw: string;\n try {\n raw = readFileSync(filePath, 'utf-8');\n } catch (cause) {\n throw new Error(\n `[label-pack-loader] Could not read label pack at ${filePath} ` +\n `(locale=\"${locale}\"). ` +\n `Make sure the directory exists and contains a \"${locale}.json\" file. ` +\n `Underlying error: ${(cause as Error).message}`,\n { cause },\n );\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (cause) {\n throw new Error(\n `[label-pack-loader] Failed to parse JSON in ${filePath} ` +\n `(locale=\"${locale}\"). Underlying parser error: ${(cause as Error).message}`,\n { cause },\n );\n }\n\n return assertLabelPackShape(parsed, filePath, locale);\n}\n\n/**\n * Batch loader — every locale declared on the site at once. The map\n * iteration order matches the declaration order of\n * `siteContext.websiteConfig.locales` so CLI reports come out in a\n * deterministic, authored sequence.\n */\nexport function loadAllLabelPacks(\n packsDir: string,\n siteContext: WebsiteSiteContext,\n): Map<AvailableLanguage, LabelPack> {\n const packs = new Map<AvailableLanguage, LabelPack>();\n for (const entry of siteContext.websiteConfig.locales) {\n packs.set(entry.locale, loadLabelPack(packsDir, entry.locale));\n }\n return packs;\n}\n\n/**\n * Per-locale validation result — the validator's flat output extended\n * with the locale tag so a multi-locale report can flatten + group.\n */\nexport interface LocaleLabelPackValidation {\n locale: AvailableLanguage;\n result: LabelPackValidationResult;\n}\n\n/**\n * Highest-level entry point: load every locale's pack and run the\n * label-pack validator against the manifest registry.\n *\n * **Return shape decision** — this function does NOT throw on\n * `missing` keys; it returns the per-locale results so the caller\n * (the build-start hook OR the CLI auditor OR a debugging\n * subprocess) can decide how to surface the failure (process exit\n * code, formatted CLI output, JSON report, …). A thin `throwIfAny…`\n * helper sits next to it for the common \"fail the build on first\n * missing key\" path.\n */\nexport function loadAndValidateAllLabelPacks(\n packsDir: string,\n siteContext: WebsiteSiteContext,\n expectedKeysBySectionRef: ExpectedLabelKeysBySectionRef,\n): readonly LocaleLabelPackValidation[] {\n const { rootConfig } = siteContext;\n const packs = loadAllLabelPacks(packsDir, siteContext);\n const manifests: readonly WebsitePageManifest[] = rootConfig.pageManifests;\n const effectiveChromeKeys = composeEffectiveChromeLabelKeys(rootConfig);\n\n const validations: LocaleLabelPackValidation[] = [];\n for (const [locale, pack] of packs) {\n validations.push({\n locale,\n result: validateLabelPackAgainstManifest(\n manifests,\n expectedKeysBySectionRef,\n pack,\n effectiveChromeKeys,\n ),\n });\n }\n return validations;\n}\n\n/**\n * Compose the chrome-label-key set the validator should treat as\n * \"expected\" — `WebsiteRootConfig.chromeLabelKeys` (consumer-authored)\n * UNION the framework-required `skipToMainContentLabelKey`.\n *\n * Centralized here because:\n * - Both `loadAndValidateAllLabelPacks` (used at build time) and\n * `buildKeyOriginMap` (used to annotate missing-key errors) need\n * the same composed list, and forking the union into two places\n * would be a drift hazard.\n * - The framework owns the skip-link key (it is rendered by\n * `WebsitePageLayout`, not the consumer's chrome), so the\n * consumer should NOT have to remember to also list it in\n * `chromeLabelKeys`. Auto-injection here keeps the contract\n * minimal at the consumer's call site (just declare the key in\n * `WebsiteRootConfig.skipToMainContentLabelKey` once + ship the\n * translation in every locale pack) while still surfacing the key\n * through the validator's \"missing\"/\"orphaned\" diagnostics.\n * - De-duplication is via `Set` semantics so a consumer who\n * defensively lists the key in both places (no-op but harmless)\n * does not produce a duplicate-key warning downstream.\n */\nfunction composeEffectiveChromeLabelKeys(\n rootConfig: WebsiteRootConfig,\n): readonly WebsiteLabelKey[] {\n const set = new Set<WebsiteLabelKey>(rootConfig.chromeLabelKeys);\n set.add(rootConfig.skipToMainContentLabelKey);\n return Array.from(set);\n}\n\n/**\n * Convenience wrapper for the build-start hook: validates every\n * declared locale's pack and throws a SINGLE aggregated error if any\n * locale has a hard failure (`missing` keys or `unmappedSectionRefs`).\n * Orphaned keys are logged via `console.warn` (soft warning per the\n * validator's contract) but do NOT throw.\n *\n * **Why aggregate (not first-fail)**: a multi-locale marketing site\n * with `en` and `fr` packs both missing keys would, under a\n * first-fail policy, force the author to fix `en`, re-run the build,\n * THEN see `fr`'s problem. Aggregating means one build run surfaces\n * every locale's hard errors together — material UX win for the\n * authoring loop. The aggregated message keeps each locale's failure\n * in its own block so the diagnostic stays scannable.\n *\n * **Why throw vs. return**: the build-start hook lives in\n * `astro.config.mjs` (or a `vite-plugin`-style integration) and\n * benefits from the shortest possible code path — a thrown error\n * naturally aborts `astro build` with a non-zero exit code and\n * surfaces the message in the build log. The richer\n * `loadAndValidateAllLabelPacks` is what `wildo website audit` calls\n * when it wants programmatic access to the per-locale results.\n *\n * **Key-origin annotation**: every `missing` key is tagged with the\n * page (and, if section-scoped, the section) that declared it, so the\n * author can grep + open the right `defineWebsiteSection` /\n * `defineWebsitePageManifest` call site without leaving the build log.\n */\nexport function assertLabelPacksValid(\n packsDir: string,\n siteContext: WebsiteSiteContext,\n expectedKeysBySectionRef: ExpectedLabelKeysBySectionRef,\n): void {\n const { rootConfig } = siteContext;\n const validations = loadAndValidateAllLabelPacks(\n packsDir,\n siteContext,\n expectedKeysBySectionRef,\n );\n\n /**\n * Build a key → origin map ONCE per assert call (cost is bounded by\n * the total number of declared keys across the whole site, which is\n * typically small for a marketing surface). Enriches the missing-key\n * error with the page/section that committed to each key — the\n * author can grep the ref to find the site authoring call site.\n */\n const keyOrigins = buildKeyOriginMap(\n rootConfig.pageManifests,\n expectedKeysBySectionRef,\n composeEffectiveChromeLabelKeys(rootConfig),\n );\n\n const localeErrorBlocks: string[] = [];\n\n for (const { locale, result } of validations) {\n const blocks: string[] = [];\n if (result.unmappedSectionRefs.length > 0) {\n blocks.push(\n ` unmappedSectionRefs (build-pipeline bug — sections referenced by a ` +\n `manifest but missing from the expected-keys collector): ` +\n `[${(result.unmappedSectionRefs as unknown as readonly string[]).join(', ')}]. ` +\n `Add every referenced section to the array passed to ` +\n `collectExpectedLabelKeysFromSections().`,\n );\n }\n if (result.missing.length > 0) {\n const annotated = (result.missing as unknown as readonly string[]).map(\n (key) => ` \"${key}\" ← ${keyOrigins.get(key) ?? 'unknown origin'}`,\n );\n blocks.push(\n ` missing ${result.missing.length} required key(s) in ${packsDir}/${locale}.json:\\n` +\n annotated.join('\\n'),\n );\n }\n if (blocks.length > 0) {\n localeErrorBlocks.push(`Locale \"${locale}\":\\n${blocks.join('\\n')}`);\n }\n if (result.orphaned.length > 0) {\n console.warn(\n `[label-pack-loader] Locale \"${locale}\": ${result.orphaned.length} ` +\n `orphaned key(s) in ${locale}.json (declared in pack but not consumed ` +\n `by any section or page manifest):\\n ${result.orphaned.join('\\n ')}`,\n );\n }\n }\n\n if (localeErrorBlocks.length > 0) {\n throw new Error(\n `[label-pack-loader] Label pack validation failed for ` +\n `${localeErrorBlocks.length} locale(s):\\n\\n${localeErrorBlocks.join('\\n\\n')}`,\n );\n }\n}\n\n/**\n * Build a `keyString -> \"page <pageRef> meta titleKey\" | \"section <sectionRef> in page <pageRef>\"`\n * lookup so the assert message can name the call site that committed\n * to each missing key. Pure and small (cost is O(totalKeys) per build\n * — bounded by the marketing surface).\n */\nfunction buildKeyOriginMap(\n manifests: readonly WebsitePageManifest[],\n expectedKeysBySectionRef: ExpectedLabelKeysBySectionRef,\n chromeLabelKeys: readonly WebsiteLabelKey[],\n): Map<string, string> {\n const origins = new Map<string, string>();\n for (const manifest of manifests) {\n const pageRefStr = manifest.ref as unknown as string;\n const titleKey = manifest.metaLabelKeys.titleKey as unknown as string;\n const descriptionKey = manifest.metaLabelKeys.descriptionKey as unknown as string;\n if (!origins.has(titleKey)) {\n origins.set(titleKey, `page \"${pageRefStr}\" metaLabelKeys.titleKey`);\n }\n if (!origins.has(descriptionKey)) {\n origins.set(descriptionKey, `page \"${pageRefStr}\" metaLabelKeys.descriptionKey`);\n }\n for (const sectionRef of manifest.sectionRefs) {\n const sectionRefStr = sectionRef as unknown as string;\n const sectionKeys = expectedKeysBySectionRef.get(sectionRef);\n if (sectionKeys === undefined) continue;\n for (const sectionKey of sectionKeys) {\n const keyStr = sectionKey as unknown as string;\n if (!origins.has(keyStr)) {\n origins.set(keyStr, `section \"${sectionRefStr}\" in page \"${pageRefStr}\"`);\n }\n }\n }\n }\n for (const chromeKey of chromeLabelKeys) {\n const keyStr = chromeKey as unknown as string;\n if (!origins.has(keyStr)) {\n origins.set(keyStr, `WebsiteRootConfig.chromeLabelKeys`);\n }\n }\n return origins;\n}\n\n/**\n * Defensive shape guard. Surface the offending key + observed type\n * so the consumer can find the malformed entry quickly. Pure\n * validation — no transformation; the returned value is the same\n * object reference passed in (cast to `LabelPack`) so the caller\n * does not pay an unnecessary copy.\n */\nfunction assertLabelPackShape(\n parsed: unknown,\n filePath: string,\n locale: AvailableLanguage,\n): LabelPack {\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error(\n `[label-pack-loader] ${filePath} (locale=\"${locale}\") must contain a ` +\n `JSON object at the top level (got ${describeJson(parsed)}).`,\n );\n }\n for (const [key, value] of Object.entries(parsed)) {\n if (typeof value !== 'string') {\n throw new Error(\n `[label-pack-loader] ${filePath} (locale=\"${locale}\"): key \"${key}\" ` +\n `must map to a string (got ${describeJson(value)}).`,\n );\n }\n }\n return parsed as LabelPack;\n}\n\nfunction describeJson(value: unknown): string {\n if (value === null) return 'null';\n if (Array.isArray(value)) return 'array';\n return typeof value;\n}\n/** @wildo_source:part:end saas.website.astro.label-loader */\n"]}
@@ -0,0 +1,28 @@
1
+ import type { WebsiteSiteContext } from './website-site-context';
2
+ export interface ReadLlmsTxtBodyOptions {
3
+ /**
4
+ * Directory containing the hand-authored `llms.txt` file. Defaults
5
+ * to the conventional `./src/content` path from the website package
6
+ * root.
7
+ */
8
+ contentDir?: string;
9
+ /**
10
+ * File name under `contentDir`. Exposed for tests; consumers should
11
+ * stick to the canonical `llms.txt`.
12
+ */
13
+ fileName?: string;
14
+ }
15
+ /**
16
+ * @wildo_source:part:start saas.website.astro.llms-txt-server facet:layer:astro facet:family:website
17
+ *
18
+ * Read the hand-authored `llms.txt` body for the marketing site.
19
+ *
20
+ * Publication policy:
21
+ * - `seo.llmsTxt?.enabled === false` → return `null`
22
+ * - `seo.llmsTxt?.enabled === true` → read the file if present, else `null`
23
+ * - `seo.llmsTxt === undefined` or `enabled === undefined` → auto-detect:
24
+ * publish when the file exists, otherwise return `null`
25
+ */
26
+ export declare function readLlmsTxtBody(siteContext: WebsiteSiteContext, options?: ReadLlmsTxtBodyOptions): string | null;
27
+ /** @wildo_source:part:end saas.website.astro.llms-txt-server */
28
+ //# sourceMappingURL=llms-txt-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llms-txt-server.d.ts","sourceRoot":"","sources":["../../../../src/astro/llms-txt-server.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEjE,MAAM,WAAW,sBAAsB;IACrC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,WAAW,EAAE,kBAAkB,EAC/B,OAAO,GAAE,sBAA2B,GACnC,MAAM,GAAG,IAAI,CAqBf;AACD,gEAAgE"}
@@ -0,0 +1,33 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ /**
4
+ * @wildo_source:part:start saas.website.astro.llms-txt-server facet:layer:astro facet:family:website
5
+ *
6
+ * Read the hand-authored `llms.txt` body for the marketing site.
7
+ *
8
+ * Publication policy:
9
+ * - `seo.llmsTxt?.enabled === false` → return `null`
10
+ * - `seo.llmsTxt?.enabled === true` → read the file if present, else `null`
11
+ * - `seo.llmsTxt === undefined` or `enabled === undefined` → auto-detect:
12
+ * publish when the file exists, otherwise return `null`
13
+ */
14
+ export function readLlmsTxtBody(siteContext, options = {}) {
15
+ const contentDir = options.contentDir ?? './src/content';
16
+ const fileName = options.fileName ?? 'llms.txt';
17
+ const filePath = resolve(contentDir, fileName);
18
+ if (siteContext.rootConfig.seo.llmsTxt?.enabled === false) {
19
+ return null;
20
+ }
21
+ if (!existsSync(filePath)) {
22
+ return null;
23
+ }
24
+ try {
25
+ return readFileSync(filePath, 'utf-8');
26
+ }
27
+ catch (cause) {
28
+ throw new Error(`[llms-txt-server] Could not read llms.txt at ${filePath}. ` +
29
+ `Underlying error: ${cause.message}`, { cause });
30
+ }
31
+ }
32
+ /** @wildo_source:part:end saas.website.astro.llms-txt-server */
33
+ //# sourceMappingURL=llms-txt-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llms-txt-server.js","sourceRoot":"","sources":["../../../../src/astro/llms-txt-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAkBpC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAC7B,WAA+B,EAC/B,UAAkC,EAAE;IAEpC,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,eAAe,CAAC;IACzD,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,UAAU,CAAC;IAChD,MAAM,QAAQ,GAAG,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAE/C,IAAI,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,KAAK,EAAE,CAAC;QAC1D,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACb,gDAAgD,QAAQ,IAAI;YAC1D,qBAAsB,KAAe,CAAC,OAAO,EAAE,EACjD,EAAE,KAAK,EAAE,CACV,CAAC;IACJ,CAAC;AACH,CAAC;AACD,gEAAgE","sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport type { WebsiteSiteContext } from './website-site-context';\n\nexport interface ReadLlmsTxtBodyOptions {\n /**\n * Directory containing the hand-authored `llms.txt` file. Defaults\n * to the conventional `./src/content` path from the website package\n * root.\n */\n contentDir?: string;\n /**\n * File name under `contentDir`. Exposed for tests; consumers should\n * stick to the canonical `llms.txt`.\n */\n fileName?: string;\n}\n\n/**\n * @wildo_source:part:start saas.website.astro.llms-txt-server facet:layer:astro facet:family:website\n *\n * Read the hand-authored `llms.txt` body for the marketing site.\n *\n * Publication policy:\n * - `seo.llmsTxt?.enabled === false` → return `null`\n * - `seo.llmsTxt?.enabled === true` → read the file if present, else `null`\n * - `seo.llmsTxt === undefined` or `enabled === undefined` → auto-detect:\n * publish when the file exists, otherwise return `null`\n */\nexport function readLlmsTxtBody(\n siteContext: WebsiteSiteContext,\n options: ReadLlmsTxtBodyOptions = {},\n): string | null {\n const contentDir = options.contentDir ?? './src/content';\n const fileName = options.fileName ?? 'llms.txt';\n const filePath = resolve(contentDir, fileName);\n\n if (siteContext.rootConfig.seo.llmsTxt?.enabled === false) {\n return null;\n }\n if (!existsSync(filePath)) {\n return null;\n }\n\n try {\n return readFileSync(filePath, 'utf-8');\n } catch (cause) {\n throw new Error(\n `[llms-txt-server] Could not read llms.txt at ${filePath}. ` +\n `Underlying error: ${(cause as Error).message}`,\n { cause },\n );\n }\n}\n/** @wildo_source:part:end saas.website.astro.llms-txt-server */\n"]}
@@ -0,0 +1,63 @@
1
+ import type { WebsiteLabelKey } from '../schemas/label-keys/website-label-key.schemas';
2
+ import type { WebsitePageManifest } from '../schemas/manifests/website-page-manifest.shared.schemas';
3
+ import type { LabelPack } from '../schemas/validators/label-pack.validator';
4
+ /**
5
+ * @wildo_source:part:start saas.website.astro.pick-labels-for-page facet:layer:astro facet:family:website
6
+ *
7
+ * `pickLabelsForPage(pack, manifest)` — return the subset of a loaded
8
+ * label pack that the given page actually consumes at render time.
9
+ *
10
+ * **Why this exists**: the per-page bridge island carries its
11
+ * `labelPack` prop across the SSR → client hydration boundary as
12
+ * serialized JSON inside the `<astro-island props=...>` attribute.
13
+ * Shipping the entire site's pack on every page (every locale × every
14
+ * page = every visitor pays the full pack) inflates the static HTML
15
+ * payload by ~70% on a typical marketing surface where each page reads
16
+ * only its own slice plus the shared chrome slice.
17
+ *
18
+ * **Filter rule** (matches the runtime contract enforced by
19
+ * `useWebsiteLabel` / `useWebsiteLabelByKey`):
20
+ * - Keep keys starting with `website.<manifest.ref>.` — every key a
21
+ * `useWebsiteLabel(slot)` call inside any of this page's sections
22
+ * can ever resolve to (the hook composes
23
+ * `website.<pageRef>.<sectionRef>.<slot>`).
24
+ * - Keep keys starting with `website.chrome.` — the synthetic
25
+ * namespace site-wide chrome (header / footer) reads via
26
+ * `useWebsiteLabelByKey(absoluteKey)`. The chrome contract is
27
+ * declared on `WebsiteRootConfig.chromeLabelKeys` and validated
28
+ * by the label-pack validator.
29
+ *
30
+ * **What gets dropped**: every `website.<otherPage>.…` key. A page
31
+ * cannot legitimately read another page's labels through the hook
32
+ * surface — the hook would either throw (`useWebsiteLabel`) or
33
+ * surface a missing-key diagnostic (`useWebsiteLabelByKey`) — so
34
+ * dropping those keys is observably safe.
35
+ *
36
+ * **Why prefix-based** (vs. walking `expectedKeysBySectionRef` for an
37
+ * exact key list): prefix matching is O(N) over `Object.entries(pack)`
38
+ * with no extra inputs threaded through `loadWebsitePageRuntime`. An
39
+ * exact-list filter would need to know the section-key map at request
40
+ * time, forcing every consumer's `.astro` page to import the
41
+ * collected map — extra config-surface for a tighter filter that
42
+ * saves an additional handful of bytes (the section keys for the
43
+ * page would already match the prefix).
44
+ *
45
+ * **Pure**: no I/O, no observable side-effects. Returns a fresh
46
+ * object — the input `pack` reference is not mutated.
47
+ *
48
+ * @param pack The full loaded label pack for the active locale.
49
+ * @param manifest The page manifest being rendered (drives the
50
+ * `website.<manifest.ref>.*` page-own prefix).
51
+ * @param explicitKeys OPTIONAL allow-list of additional fully-composed
52
+ * label keys to keep regardless of prefix. Used by
53
+ * the framework to guarantee that
54
+ * `WebsiteRootConfig.skipToMainContentLabelKey`
55
+ * reaches the bridge even if a consumer chose to
56
+ * namespace it outside `website.chrome.*` (the
57
+ * framework MUST be able to render the skip link
58
+ * on every page in every locale — Phase 6 audit
59
+ * fix M-α).
60
+ */
61
+ export declare function pickLabelsForPage(pack: LabelPack, manifest: WebsitePageManifest, explicitKeys?: readonly WebsiteLabelKey[]): LabelPack;
62
+ /** @wildo_source:part:end saas.website.astro.pick-labels-for-page */
63
+ //# sourceMappingURL=pick-labels-for-page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pick-labels-for-page.d.ts","sourceRoot":"","sources":["../../../../src/astro/pick-labels-for-page.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iDAAiD,CAAC;AACvF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2DAA2D,CAAC;AACrG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4CAA4C,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,SAAS,EACf,QAAQ,EAAE,mBAAmB,EAC7B,YAAY,GAAE,SAAS,eAAe,EAAO,GAC5C,SAAS,CAkBX;AACD,qEAAqE"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * @wildo_source:part:start saas.website.astro.pick-labels-for-page facet:layer:astro facet:family:website
3
+ *
4
+ * `pickLabelsForPage(pack, manifest)` — return the subset of a loaded
5
+ * label pack that the given page actually consumes at render time.
6
+ *
7
+ * **Why this exists**: the per-page bridge island carries its
8
+ * `labelPack` prop across the SSR → client hydration boundary as
9
+ * serialized JSON inside the `<astro-island props=...>` attribute.
10
+ * Shipping the entire site's pack on every page (every locale × every
11
+ * page = every visitor pays the full pack) inflates the static HTML
12
+ * payload by ~70% on a typical marketing surface where each page reads
13
+ * only its own slice plus the shared chrome slice.
14
+ *
15
+ * **Filter rule** (matches the runtime contract enforced by
16
+ * `useWebsiteLabel` / `useWebsiteLabelByKey`):
17
+ * - Keep keys starting with `website.<manifest.ref>.` — every key a
18
+ * `useWebsiteLabel(slot)` call inside any of this page's sections
19
+ * can ever resolve to (the hook composes
20
+ * `website.<pageRef>.<sectionRef>.<slot>`).
21
+ * - Keep keys starting with `website.chrome.` — the synthetic
22
+ * namespace site-wide chrome (header / footer) reads via
23
+ * `useWebsiteLabelByKey(absoluteKey)`. The chrome contract is
24
+ * declared on `WebsiteRootConfig.chromeLabelKeys` and validated
25
+ * by the label-pack validator.
26
+ *
27
+ * **What gets dropped**: every `website.<otherPage>.…` key. A page
28
+ * cannot legitimately read another page's labels through the hook
29
+ * surface — the hook would either throw (`useWebsiteLabel`) or
30
+ * surface a missing-key diagnostic (`useWebsiteLabelByKey`) — so
31
+ * dropping those keys is observably safe.
32
+ *
33
+ * **Why prefix-based** (vs. walking `expectedKeysBySectionRef` for an
34
+ * exact key list): prefix matching is O(N) over `Object.entries(pack)`
35
+ * with no extra inputs threaded through `loadWebsitePageRuntime`. An
36
+ * exact-list filter would need to know the section-key map at request
37
+ * time, forcing every consumer's `.astro` page to import the
38
+ * collected map — extra config-surface for a tighter filter that
39
+ * saves an additional handful of bytes (the section keys for the
40
+ * page would already match the prefix).
41
+ *
42
+ * **Pure**: no I/O, no observable side-effects. Returns a fresh
43
+ * object — the input `pack` reference is not mutated.
44
+ *
45
+ * @param pack The full loaded label pack for the active locale.
46
+ * @param manifest The page manifest being rendered (drives the
47
+ * `website.<manifest.ref>.*` page-own prefix).
48
+ * @param explicitKeys OPTIONAL allow-list of additional fully-composed
49
+ * label keys to keep regardless of prefix. Used by
50
+ * the framework to guarantee that
51
+ * `WebsiteRootConfig.skipToMainContentLabelKey`
52
+ * reaches the bridge even if a consumer chose to
53
+ * namespace it outside `website.chrome.*` (the
54
+ * framework MUST be able to render the skip link
55
+ * on every page in every locale — Phase 6 audit
56
+ * fix M-α).
57
+ */
58
+ export function pickLabelsForPage(pack, manifest, explicitKeys = []) {
59
+ const pagePrefix = `website.${manifest.ref}.`;
60
+ const chromePrefix = 'website.chrome.';
61
+ const explicitKeySet = new Set(explicitKeys.map((key) => key));
62
+ const filtered = {};
63
+ for (const [key, value] of Object.entries(pack)) {
64
+ if (key.startsWith(pagePrefix) ||
65
+ key.startsWith(chromePrefix) ||
66
+ explicitKeySet.has(key)) {
67
+ filtered[key] = value;
68
+ }
69
+ }
70
+ return filtered;
71
+ }
72
+ /** @wildo_source:part:end saas.website.astro.pick-labels-for-page */
73
+ //# sourceMappingURL=pick-labels-for-page.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pick-labels-for-page.js","sourceRoot":"","sources":["../../../../src/astro/pick-labels-for-page.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,MAAM,UAAU,iBAAiB,CAC/B,IAAe,EACf,QAA6B,EAC7B,eAA2C,EAAE;IAE7C,MAAM,UAAU,GAAG,WAAW,QAAQ,CAAC,GAAwB,GAAG,CAAC;IACnE,MAAM,YAAY,GAAG,iBAAiB,CAAC;IACvC,MAAM,cAAc,GAAG,IAAI,GAAG,CAC5B,YAAY,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAwB,CAAC,CACpD,CAAC;IAEF,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAChD,IACE,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC;YAC1B,GAAG,CAAC,UAAU,CAAC,YAAY,CAAC;YAC5B,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,EACvB,CAAC;YACD,QAAQ,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACxB,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AACD,qEAAqE","sourcesContent":["import type { WebsiteLabelKey } from '../schemas/label-keys/website-label-key.schemas';\nimport type { WebsitePageManifest } from '../schemas/manifests/website-page-manifest.shared.schemas';\nimport type { LabelPack } from '../schemas/validators/label-pack.validator';\n\n/**\n * @wildo_source:part:start saas.website.astro.pick-labels-for-page facet:layer:astro facet:family:website\n *\n * `pickLabelsForPage(pack, manifest)` — return the subset of a loaded\n * label pack that the given page actually consumes at render time.\n *\n * **Why this exists**: the per-page bridge island carries its\n * `labelPack` prop across the SSR → client hydration boundary as\n * serialized JSON inside the `<astro-island props=...>` attribute.\n * Shipping the entire site's pack on every page (every locale × every\n * page = every visitor pays the full pack) inflates the static HTML\n * payload by ~70% on a typical marketing surface where each page reads\n * only its own slice plus the shared chrome slice.\n *\n * **Filter rule** (matches the runtime contract enforced by\n * `useWebsiteLabel` / `useWebsiteLabelByKey`):\n * - Keep keys starting with `website.<manifest.ref>.` — every key a\n * `useWebsiteLabel(slot)` call inside any of this page's sections\n * can ever resolve to (the hook composes\n * `website.<pageRef>.<sectionRef>.<slot>`).\n * - Keep keys starting with `website.chrome.` — the synthetic\n * namespace site-wide chrome (header / footer) reads via\n * `useWebsiteLabelByKey(absoluteKey)`. The chrome contract is\n * declared on `WebsiteRootConfig.chromeLabelKeys` and validated\n * by the label-pack validator.\n *\n * **What gets dropped**: every `website.<otherPage>.…` key. A page\n * cannot legitimately read another page's labels through the hook\n * surface — the hook would either throw (`useWebsiteLabel`) or\n * surface a missing-key diagnostic (`useWebsiteLabelByKey`) — so\n * dropping those keys is observably safe.\n *\n * **Why prefix-based** (vs. walking `expectedKeysBySectionRef` for an\n * exact key list): prefix matching is O(N) over `Object.entries(pack)`\n * with no extra inputs threaded through `loadWebsitePageRuntime`. An\n * exact-list filter would need to know the section-key map at request\n * time, forcing every consumer's `.astro` page to import the\n * collected map — extra config-surface for a tighter filter that\n * saves an additional handful of bytes (the section keys for the\n * page would already match the prefix).\n *\n * **Pure**: no I/O, no observable side-effects. Returns a fresh\n * object — the input `pack` reference is not mutated.\n *\n * @param pack The full loaded label pack for the active locale.\n * @param manifest The page manifest being rendered (drives the\n * `website.<manifest.ref>.*` page-own prefix).\n * @param explicitKeys OPTIONAL allow-list of additional fully-composed\n * label keys to keep regardless of prefix. Used by\n * the framework to guarantee that\n * `WebsiteRootConfig.skipToMainContentLabelKey`\n * reaches the bridge even if a consumer chose to\n * namespace it outside `website.chrome.*` (the\n * framework MUST be able to render the skip link\n * on every page in every locale — Phase 6 audit\n * fix M-α).\n */\nexport function pickLabelsForPage(\n pack: LabelPack,\n manifest: WebsitePageManifest,\n explicitKeys: readonly WebsiteLabelKey[] = [],\n): LabelPack {\n const pagePrefix = `website.${manifest.ref as unknown as string}.`;\n const chromePrefix = 'website.chrome.';\n const explicitKeySet = new Set<string>(\n explicitKeys.map((key) => key as unknown as string),\n );\n\n const filtered: LabelPack = {};\n for (const [key, value] of Object.entries(pack)) {\n if (\n key.startsWith(pagePrefix) ||\n key.startsWith(chromePrefix) ||\n explicitKeySet.has(key)\n ) {\n filtered[key] = value;\n }\n }\n return filtered;\n}\n/** @wildo_source:part:end saas.website.astro.pick-labels-for-page */\n"]}