@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,305 @@
1
+ import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
2
+ import { tmpdir } from 'node:os';
3
+ import { resolve } from 'node:path';
4
+
5
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
6
+
7
+ import { AvailableLanguage } from '@wildo-ai/saas-models/public-runtime';
8
+
9
+ import { WildoMarketingWebsiteConfigSchema } from '../../config/wildo-website-config.schemas';
10
+ import { WebsiteLabelKeySchema } from '../../schemas/label-keys/website-label-key.schemas';
11
+ import { WebsitePageManifestSchema } from '../../schemas/manifests/website-page-manifest.shared.schemas';
12
+ import {
13
+ WebsiteRootConfigSchema,
14
+ type WebsiteRootConfig,
15
+ } from '../../schemas/manifests/website-root-config.shared.schemas';
16
+ import { WebsitePageRefSchema } from '../../schemas/refs/page-ref.schemas';
17
+ import { WebsiteSectionRefSchema } from '../../schemas/refs/section-ref.schemas';
18
+ import {
19
+ assertLabelPacksValid,
20
+ loadAllLabelPacks,
21
+ loadAndValidateAllLabelPacks,
22
+ loadLabelPack,
23
+ } from '../label-pack-loader';
24
+ import { composeWebsiteSiteContext, type WebsiteSiteContext } from '../website-site-context';
25
+
26
+ const NoopComponent = () => null;
27
+
28
+ const COLORS = {
29
+ background: '#fff',
30
+ foreground: '#111',
31
+ primary: '#0070f3',
32
+ primaryForeground: '#fff',
33
+ accent: '#ff5e5b',
34
+ accentForeground: '#fff',
35
+ surface: '#fafafa',
36
+ surfaceForeground: '#111',
37
+ border: '#e5e5e5',
38
+ muted: '#f5f5f5',
39
+ mutedForeground: '#737373',
40
+ };
41
+ const RADII = {
42
+ none: '0',
43
+ sm: '0.125rem',
44
+ md: '0.375rem',
45
+ lg: '0.75rem',
46
+ xl: '1.5rem',
47
+ full: '9999px',
48
+ };
49
+
50
+ const buildManifest = () =>
51
+ WebsitePageManifestSchema.parse({
52
+ ref: WebsitePageRefSchema.parse('landing'),
53
+ routePath: '/',
54
+ pageComponent: NoopComponent,
55
+ sectionRefs: [WebsiteSectionRefSchema.parse('hero')],
56
+ metaLabelKeys: {
57
+ titleKey: WebsiteLabelKeySchema.parse('website.landing.meta.title'),
58
+ descriptionKey: WebsiteLabelKeySchema.parse('website.landing.meta.description'),
59
+ },
60
+ });
61
+
62
+ const SKIP_KEY_STR = 'website.chrome.layout.skip-to-main-content';
63
+
64
+ const buildRootConfig = (): WebsiteRootConfig =>
65
+ WebsiteRootConfigSchema.parse({
66
+ pageManifests: [buildManifest()],
67
+ designTokens: { colors: COLORS, radii: RADII },
68
+ navigation: { headerComponent: NoopComponent, footerComponent: NoopComponent },
69
+ seo: { siteName: 'Example' },
70
+ skipToMainContentLabelKey: WebsiteLabelKeySchema.parse(SKIP_KEY_STR),
71
+ }) as WebsiteRootConfig;
72
+
73
+ const buildSiteContext = (): WebsiteSiteContext =>
74
+ composeWebsiteSiteContext({
75
+ rootConfig: buildRootConfig(),
76
+ websiteConfig: WildoMarketingWebsiteConfigSchema.parse({
77
+ publicOrigin: 'https://example.com',
78
+ appOrigin: 'https://app.example.com',
79
+ defaultLocale: AvailableLanguage.EN,
80
+ locales: [
81
+ { locale: AvailableLanguage.EN, path: 'en' },
82
+ { locale: AvailableLanguage.FR, path: 'fr' },
83
+ ],
84
+ }),
85
+ });
86
+
87
+ const buildExpectedKeys = () => {
88
+ const map = new Map();
89
+ map.set(WebsiteSectionRefSchema.parse('hero'), [
90
+ WebsiteLabelKeySchema.parse('website.landing.hero.headline'),
91
+ ]);
92
+ return map;
93
+ };
94
+
95
+ describe('label-pack-loader', () => {
96
+ let tmpDir: string;
97
+
98
+ beforeEach(() => {
99
+ tmpDir = mkdtempSync(resolve(tmpdir(), 'website-label-pack-test-'));
100
+ });
101
+
102
+ afterEach(() => {
103
+ rmSync(tmpDir, { recursive: true, force: true });
104
+ });
105
+
106
+ describe('loadLabelPack', () => {
107
+ it('loads and parses a well-formed pack', () => {
108
+ writeFileSync(
109
+ resolve(tmpDir, 'en.json'),
110
+ JSON.stringify({ 'website.landing.hero.headline': 'Welcome' }),
111
+ );
112
+ const pack = loadLabelPack(tmpDir, AvailableLanguage.EN);
113
+ expect(pack['website.landing.hero.headline']).toBe('Welcome');
114
+ });
115
+
116
+ it('throws an actionable error when the file is missing', () => {
117
+ expect(() => loadLabelPack(tmpDir, AvailableLanguage.FR)).toThrow(
118
+ /Could not read label pack.*fr\.json.*locale="fr"/s,
119
+ );
120
+ });
121
+
122
+ it('throws when JSON is malformed', () => {
123
+ writeFileSync(resolve(tmpDir, 'en.json'), '{ not json');
124
+ expect(() => loadLabelPack(tmpDir, AvailableLanguage.EN)).toThrow(
125
+ /Failed to parse JSON/,
126
+ );
127
+ });
128
+
129
+ it('throws when the top-level shape is not an object', () => {
130
+ writeFileSync(resolve(tmpDir, 'en.json'), JSON.stringify(['arr']));
131
+ expect(() => loadLabelPack(tmpDir, AvailableLanguage.EN)).toThrow(
132
+ /must contain a JSON object at the top level.*got array/s,
133
+ );
134
+ });
135
+
136
+ it('throws when a value is not a string', () => {
137
+ writeFileSync(
138
+ resolve(tmpDir, 'en.json'),
139
+ JSON.stringify({ 'website.landing.hero.headline': 42 }),
140
+ );
141
+ expect(() => loadLabelPack(tmpDir, AvailableLanguage.EN)).toThrow(
142
+ /key "website\.landing\.hero\.headline".*must map to a string.*got number/s,
143
+ );
144
+ });
145
+ });
146
+
147
+ describe('loadAllLabelPacks', () => {
148
+ it('loads every locale declared on the website config in declaration order', () => {
149
+ writeFileSync(
150
+ resolve(tmpDir, 'en.json'),
151
+ JSON.stringify({ 'website.landing.hero.headline': 'EN headline' }),
152
+ );
153
+ writeFileSync(
154
+ resolve(tmpDir, 'fr.json'),
155
+ JSON.stringify({ 'website.landing.hero.headline': 'FR headline' }),
156
+ );
157
+ const packs = loadAllLabelPacks(tmpDir, buildSiteContext());
158
+ expect([...packs.keys()]).toEqual([AvailableLanguage.EN, AvailableLanguage.FR]);
159
+ expect(packs.get(AvailableLanguage.FR)?.['website.landing.hero.headline']).toBe(
160
+ 'FR headline',
161
+ );
162
+ });
163
+ });
164
+
165
+ describe('loadAndValidateAllLabelPacks', () => {
166
+ it('returns per-locale validation results without throwing on missing keys', () => {
167
+ writeFileSync(
168
+ resolve(tmpDir, 'en.json'),
169
+ JSON.stringify({
170
+ 'website.landing.hero.headline': 'EN',
171
+ 'website.landing.meta.title': 'EN',
172
+ 'website.landing.meta.description': 'EN',
173
+ }),
174
+ );
175
+ writeFileSync(
176
+ resolve(tmpDir, 'fr.json'),
177
+ JSON.stringify({
178
+ 'website.landing.meta.title': 'FR',
179
+ }),
180
+ );
181
+ const validations = loadAndValidateAllLabelPacks(
182
+ tmpDir,
183
+ buildSiteContext(),
184
+ buildExpectedKeys(),
185
+ );
186
+ expect(validations).toHaveLength(2);
187
+ const fr = validations.find((v) => v.locale === AvailableLanguage.FR);
188
+ expect(fr?.result.missing).toEqual(
189
+ expect.arrayContaining([
190
+ 'website.landing.hero.headline',
191
+ 'website.landing.meta.description',
192
+ ]),
193
+ );
194
+ });
195
+ });
196
+
197
+ describe('assertLabelPacksValid', () => {
198
+ it('throws on missing keys with the locale tagged in the aggregated message', () => {
199
+ writeFileSync(
200
+ resolve(tmpDir, 'en.json'),
201
+ JSON.stringify({
202
+ 'website.landing.hero.headline': 'EN',
203
+ 'website.landing.meta.title': 'EN',
204
+ 'website.landing.meta.description': 'EN',
205
+ }),
206
+ );
207
+ writeFileSync(resolve(tmpDir, 'fr.json'), JSON.stringify({}));
208
+ expect(() =>
209
+ assertLabelPacksValid(tmpDir, buildSiteContext(), buildExpectedKeys()),
210
+ ).toThrow(/Locale "fr".*missing 4 required key\(s\)/s);
211
+ });
212
+
213
+ it('annotates each missing key with its declaring page / section origin', () => {
214
+ writeFileSync(resolve(tmpDir, 'en.json'), JSON.stringify({}));
215
+ writeFileSync(resolve(tmpDir, 'fr.json'), JSON.stringify({}));
216
+ let captured: string | undefined;
217
+ try {
218
+ assertLabelPacksValid(tmpDir, buildSiteContext(), buildExpectedKeys());
219
+ } catch (err) {
220
+ captured = (err as Error).message;
221
+ }
222
+ expect(captured).toBeDefined();
223
+ expect(captured).toMatch(
224
+ /"website\.landing\.meta\.title".*page "landing" metaLabelKeys\.titleKey/s,
225
+ );
226
+ expect(captured).toMatch(
227
+ /"website\.landing\.meta\.description".*page "landing" metaLabelKeys\.descriptionKey/s,
228
+ );
229
+ expect(captured).toMatch(
230
+ /"website\.landing\.hero\.headline".*section "hero" in page "landing"/s,
231
+ );
232
+ });
233
+
234
+ it('aggregates errors across multiple locales in a single throw (not first-fail)', () => {
235
+ writeFileSync(resolve(tmpDir, 'en.json'), JSON.stringify({}));
236
+ writeFileSync(resolve(tmpDir, 'fr.json'), JSON.stringify({}));
237
+ let captured: string | undefined;
238
+ try {
239
+ assertLabelPacksValid(tmpDir, buildSiteContext(), buildExpectedKeys());
240
+ } catch (err) {
241
+ captured = (err as Error).message;
242
+ }
243
+ expect(captured).toBeDefined();
244
+ expect(captured).toMatch(/validation failed for 2 locale\(s\)/);
245
+ expect(captured).toMatch(/Locale "en":/);
246
+ expect(captured).toMatch(/Locale "fr":/);
247
+ });
248
+
249
+ it('throws on unmappedSectionRefs (build-pipeline bug) inside the aggregated message', () => {
250
+ writeFileSync(resolve(tmpDir, 'en.json'), JSON.stringify({}));
251
+ writeFileSync(resolve(tmpDir, 'fr.json'), JSON.stringify({}));
252
+ const emptyExpected = new Map();
253
+ expect(() =>
254
+ assertLabelPacksValid(tmpDir, buildSiteContext(), emptyExpected),
255
+ ).toThrow(/unmappedSectionRefs.*\[hero\]/s);
256
+ });
257
+
258
+ it('passes with no thrown error when every key is present', () => {
259
+ const fullPack = JSON.stringify({
260
+ 'website.landing.hero.headline': 'X',
261
+ 'website.landing.meta.title': 'X',
262
+ 'website.landing.meta.description': 'X',
263
+ [SKIP_KEY_STR]: 'Skip to main content',
264
+ });
265
+ writeFileSync(resolve(tmpDir, 'en.json'), fullPack);
266
+ writeFileSync(resolve(tmpDir, 'fr.json'), fullPack);
267
+ expect(() =>
268
+ assertLabelPacksValid(tmpDir, buildSiteContext(), buildExpectedKeys()),
269
+ ).not.toThrow();
270
+ });
271
+
272
+ it('warns (not throws) on orphaned keys', () => {
273
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined);
274
+ const packWithOrphan = JSON.stringify({
275
+ 'website.landing.hero.headline': 'X',
276
+ 'website.landing.meta.title': 'X',
277
+ 'website.landing.meta.description': 'X',
278
+ 'website.landing.hero.unused-orphan': 'X',
279
+ [SKIP_KEY_STR]: 'Skip to main content',
280
+ });
281
+ writeFileSync(resolve(tmpDir, 'en.json'), packWithOrphan);
282
+ writeFileSync(resolve(tmpDir, 'fr.json'), packWithOrphan);
283
+ expect(() =>
284
+ assertLabelPacksValid(tmpDir, buildSiteContext(), buildExpectedKeys()),
285
+ ).not.toThrow();
286
+ expect(warnSpy).toHaveBeenCalledWith(
287
+ expect.stringMatching(/orphaned key\(s\).*website\.landing\.hero\.unused-orphan/s),
288
+ );
289
+ warnSpy.mockRestore();
290
+ });
291
+
292
+ it('treats WebsiteRootConfig.skipToMainContentLabelKey as required (Phase 6 third audit fix M-α)', () => {
293
+ const partialPack = JSON.stringify({
294
+ 'website.landing.hero.headline': 'X',
295
+ 'website.landing.meta.title': 'X',
296
+ 'website.landing.meta.description': 'X',
297
+ });
298
+ writeFileSync(resolve(tmpDir, 'en.json'), partialPack);
299
+ writeFileSync(resolve(tmpDir, 'fr.json'), partialPack);
300
+ expect(() =>
301
+ assertLabelPacksValid(tmpDir, buildSiteContext(), buildExpectedKeys()),
302
+ ).toThrow(new RegExp(`missing.*"${SKIP_KEY_STR.replace(/\./g, '\\.')}"`, 's'));
303
+ });
304
+ });
305
+ });
@@ -0,0 +1,127 @@
1
+ import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { afterEach, describe, expect, it } from 'vitest';
5
+
6
+ import { AvailableLanguage } from '@wildo-ai/saas-models/public-runtime';
7
+
8
+ import { WildoMarketingWebsiteConfigSchema } from '../../config/wildo-website-config.schemas';
9
+ import { WebsiteLabelKeySchema } from '../../schemas/label-keys/website-label-key.schemas';
10
+ import {
11
+ WebsitePageManifestSchema,
12
+ type WebsitePageManifest,
13
+ } from '../../schemas/manifests/website-page-manifest.shared.schemas';
14
+ import {
15
+ WebsiteRootConfigSchema,
16
+ type WebsiteRootConfig,
17
+ } from '../../schemas/manifests/website-root-config.shared.schemas';
18
+ import { WebsitePageRefSchema } from '../../schemas/refs/page-ref.schemas';
19
+ import { WebsiteSectionRefSchema } from '../../schemas/refs/section-ref.schemas';
20
+ import { readLlmsTxtBody } from '../llms-txt-server';
21
+ import {
22
+ composeWebsiteSiteContext,
23
+ type WebsiteSiteContext,
24
+ } from '../website-site-context';
25
+
26
+ const NoopComponent = () => null;
27
+ const tempDirs: string[] = [];
28
+
29
+ function buildManifest(): WebsitePageManifest {
30
+ return WebsitePageManifestSchema.parse({
31
+ ref: WebsitePageRefSchema.parse('landing'),
32
+ routePath: '/',
33
+ pageComponent: NoopComponent,
34
+ sectionRefs: [WebsiteSectionRefSchema.parse('hero')],
35
+ metaLabelKeys: {
36
+ titleKey: WebsiteLabelKeySchema.parse('website.landing.meta.title'),
37
+ descriptionKey: WebsiteLabelKeySchema.parse('website.landing.meta.description'),
38
+ },
39
+ }) as WebsitePageManifest;
40
+ }
41
+
42
+ function buildSiteContext(llmsTxtConfig?: { enabled?: boolean }): WebsiteSiteContext {
43
+ const rootConfig = WebsiteRootConfigSchema.parse({
44
+ pageManifests: [buildManifest()],
45
+ designTokens: {
46
+ colors: {
47
+ background: '#fff',
48
+ foreground: '#111',
49
+ primary: '#0070f3',
50
+ primaryForeground: '#fff',
51
+ accent: '#ff5e5b',
52
+ accentForeground: '#fff',
53
+ surface: '#fafafa',
54
+ surfaceForeground: '#111',
55
+ border: '#e5e5e5',
56
+ muted: '#f5f5f5',
57
+ mutedForeground: '#737373',
58
+ },
59
+ radii: {
60
+ none: '0',
61
+ sm: '0.125rem',
62
+ md: '0.375rem',
63
+ lg: '0.75rem',
64
+ xl: '1.5rem',
65
+ full: '9999px',
66
+ },
67
+ },
68
+ navigation: { headerComponent: NoopComponent, footerComponent: NoopComponent },
69
+ seo: {
70
+ siteName: 'Example Site',
71
+ ...(llmsTxtConfig !== undefined ? { llmsTxt: llmsTxtConfig } : {}),
72
+ },
73
+ skipToMainContentLabelKey: WebsiteLabelKeySchema.parse(
74
+ 'website.chrome.layout.skip-to-main-content',
75
+ ),
76
+ }) as WebsiteRootConfig;
77
+
78
+ const websiteConfig = WildoMarketingWebsiteConfigSchema.parse({
79
+ publicOrigin: 'https://example.com',
80
+ appOrigin: 'https://app.example.com',
81
+ defaultLocale: AvailableLanguage.EN,
82
+ locales: [{ locale: AvailableLanguage.EN, path: 'en' }],
83
+ });
84
+
85
+ return composeWebsiteSiteContext({ rootConfig, websiteConfig });
86
+ }
87
+
88
+ function makeContentDir(files: Record<string, string>): string {
89
+ const dir = mkdtempSync(join(tmpdir(), 'wildo-llms-'));
90
+ tempDirs.push(dir);
91
+ for (const [fileName, contents] of Object.entries(files)) {
92
+ writeFileSync(join(dir, fileName), contents, 'utf-8');
93
+ }
94
+ return dir;
95
+ }
96
+
97
+ afterEach(() => {
98
+ for (const dir of tempDirs.splice(0)) {
99
+ rmSync(dir, { recursive: true, force: true });
100
+ }
101
+ });
102
+
103
+ describe('readLlmsTxtBody', () => {
104
+ it('returns the file body when llms.txt exists and publication is auto-detected', () => {
105
+ const contentDir = makeContentDir({
106
+ 'llms.txt': '# Wonder Todos\n\n- /pricing\n',
107
+ });
108
+ expect(readLlmsTxtBody(buildSiteContext(), { contentDir })).toBe('# Wonder Todos\n\n- /pricing\n');
109
+ });
110
+
111
+ it('returns null when llms.txt is absent and publication is auto-detected', () => {
112
+ const contentDir = makeContentDir({});
113
+ expect(readLlmsTxtBody(buildSiteContext(), { contentDir })).toBeNull();
114
+ });
115
+
116
+ it('returns null when enabled=false even if the file exists', () => {
117
+ const contentDir = makeContentDir({
118
+ 'llms.txt': '# Wonder Todos\n',
119
+ });
120
+ expect(readLlmsTxtBody(buildSiteContext({ enabled: false }), { contentDir })).toBeNull();
121
+ });
122
+
123
+ it('returns null when enabled=true but the file is absent', () => {
124
+ const contentDir = makeContentDir({});
125
+ expect(readLlmsTxtBody(buildSiteContext({ enabled: true }), { contentDir })).toBeNull();
126
+ });
127
+ });
@@ -0,0 +1,60 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { WebsiteLabelKeySchema } from '../../schemas/label-keys/website-label-key.schemas';
4
+ import {
5
+ WebsitePageManifestSchema,
6
+ type WebsitePageManifest,
7
+ } from '../../schemas/manifests/website-page-manifest.shared.schemas';
8
+ import { WebsitePageRefSchema } from '../../schemas/refs/page-ref.schemas';
9
+ import { WebsiteSectionRefSchema } from '../../schemas/refs/section-ref.schemas';
10
+ import type { LabelPack } from '../../schemas/validators/label-pack.validator';
11
+ import { pickLabelsForPage } from '../pick-labels-for-page';
12
+
13
+ const NoopComponent = () => null;
14
+
15
+ const buildLandingManifest = (): WebsitePageManifest =>
16
+ WebsitePageManifestSchema.parse({
17
+ ref: WebsitePageRefSchema.parse('landing'),
18
+ routePath: '/',
19
+ pageComponent: NoopComponent,
20
+ sectionRefs: [WebsiteSectionRefSchema.parse('hero')],
21
+ metaLabelKeys: {
22
+ titleKey: WebsiteLabelKeySchema.parse('website.landing.meta.title'),
23
+ descriptionKey: WebsiteLabelKeySchema.parse('website.landing.meta.description'),
24
+ },
25
+ }) as WebsitePageManifest;
26
+
27
+ describe('pickLabelsForPage', () => {
28
+ const fullPack: LabelPack = {
29
+ 'website.landing.meta.title': 'Landing title',
30
+ 'website.landing.hero.headline': 'Landing headline',
31
+ 'website.features.meta.title': 'Features title',
32
+ 'website.features.hero.headline': 'Features headline',
33
+ 'website.pricing.tiers.tier1.name': 'Pricing starter',
34
+ 'website.contact.form.submit': 'Send',
35
+ 'website.chrome.header.brand': 'Acme',
36
+ 'website.chrome.footer.copyright': '© 2026',
37
+ };
38
+
39
+ it("keeps only the page's own slice plus chrome", () => {
40
+ const filtered = pickLabelsForPage(fullPack, buildLandingManifest());
41
+ expect(Object.keys(filtered).sort()).toEqual([
42
+ 'website.chrome.footer.copyright',
43
+ 'website.chrome.header.brand',
44
+ 'website.landing.hero.headline',
45
+ 'website.landing.meta.title',
46
+ ]);
47
+ });
48
+
49
+ it('returns an empty pack when neither the page slice nor any chrome key matches', () => {
50
+ const minimalPack: LabelPack = { 'website.features.meta.title': 'Features' };
51
+ const filtered = pickLabelsForPage(minimalPack, buildLandingManifest());
52
+ expect(filtered).toEqual({});
53
+ });
54
+
55
+ it('does not mutate the input pack', () => {
56
+ const snapshot = { ...fullPack };
57
+ pickLabelsForPage(fullPack, buildLandingManifest());
58
+ expect(fullPack).toEqual(snapshot);
59
+ });
60
+ });