@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,404 @@
1
+ import type { ComponentType, FunctionComponent, ReactNode } from 'react';
2
+
3
+ import type { FrontendProvidersBlock } from '@wildo-ai/saas-models/public-runtime';
4
+ import type { AvailableLanguage } from '@wildo-ai/saas-models/public-runtime';
5
+
6
+ import {
7
+ WebsiteRuntimeContextProvider,
8
+ type WebsiteRuntimeContextValue,
9
+ } from '../core/contexts/WebsiteRuntimeContext';
10
+ import { createFrontendWebsiteProviderRegistry } from '../core/external-providers/frontend-provider-registry.website';
11
+ import { BlogPost, BLOG_POST_PAGE_REF } from '../mdx/BlogPost';
12
+ import type { WebsiteMdxComponentsMap } from '../mdx/website-mdx-provider';
13
+ import type { WebsiteDesignTokens } from '../schemas/design-tokens/website-design-tokens.shared.schemas';
14
+ import type { WebsiteLabelKey } from '../schemas/label-keys/website-label-key.schemas';
15
+ import type { WebsiteNavigationConfig } from '../schemas/manifests/website-root-config.shared.schemas';
16
+ import type { LabelPack } from '../schemas/validators/label-pack.validator';
17
+
18
+ /**
19
+ * @wildo_source:part:start saas.website.blog.bridge facet:layer:astro facet:family:website
20
+ *
21
+ * `createWebsiteBlogPostBridgeIsland(deps)` — factory that returns the
22
+ * React island a consumer's per-locale `*.bridge.tsx` module exports for
23
+ * mounting blog post pages. **Sibling** of `createWebsitePageBridgeIsland`
24
+ * but adapted to the dynamic-routing constraint of the marketing-blog
25
+ * adapter (Phase 7, D-6 = Option A).
26
+ *
27
+ * # Why blog posts cannot reuse the page bridge factory
28
+ *
29
+ * Three structural differences between marketing pages and blog posts
30
+ * forced this dedicated factory shape:
31
+ *
32
+ * 1. **Per-slug body resolution.** A marketing page (`landing`,
33
+ * `pricing`, etc.) has ONE `PageComponent` known at bridge-module
34
+ * load time — the page bridge closes over it. A blog post's body
35
+ * is the compiled MDX module, which varies per slug. Closing the
36
+ * MDX body inside the factory at module load time would force one
37
+ * bridge file per post (defeats Q16=B "one MDX file per post").
38
+ * Instead, the factory closes over a `slugToMdxBody: Record<string,
39
+ * ComponentType>` map — typically built from
40
+ * `import.meta.glob('./content/blog/<locale>/*.mdx', { eager: true })`
41
+ * on the consumer side — and the island accepts the active `slug`
42
+ * as a primitive (JSON-serializable) prop.
43
+ *
44
+ * 2. **Layout already wrapped by `<BlogPost>`.** The page bridge wraps
45
+ * its `PageComponent` in `<WebsitePageLayout pageRef={…}>`. The
46
+ * `<BlogPost>` renderer ALREADY wraps its tree in
47
+ * `<WebsitePageLayout pageRef={BLOG_POST_PAGE_REF}>` (the framework
48
+ * reserves the page ref for the entire blog surface — see
49
+ * `BLOG_POST_PAGE_REF` JSDoc in `BlogPost.tsx`). Reusing the page
50
+ * bridge would double-wrap the layout. This factory instead
51
+ * provides the runtime context and yields directly to `<BlogPost>`,
52
+ * which owns the layout boundary itself.
53
+ *
54
+ * 3. **Astro's React-children context-propagation gap.** Astro
55
+ * renders React component children INDEPENDENTLY of the parent
56
+ * component when the boundary is `.astro` ↔ `.tsx`, even on the
57
+ * server side. This means
58
+ * `<BlogPost client:load><Content /></BlogPost>` written in an
59
+ * `.astro` file would NOT propagate the `<WebsiteMdxProvider>`
60
+ * context to the React components embedded inside `<Content />`
61
+ * (the embedded `<CTASection />` would render WITHOUT the engine's
62
+ * MDX provider scope; the `mdxComponents` map would be ignored).
63
+ * Confirmed via the Astro docs, `@astrojs/react` integration
64
+ * readme, and the documented "Bridging two JSX runtimes" gotcha.
65
+ *
66
+ * The fix: the entire blog post tree (BlogPost + MDX body +
67
+ * embedded section components) lives inside ONE React island. The
68
+ * MDX module is imported as a React component via Astro's
69
+ * `import.meta.glob`, and the bridge renders it as JSX children
70
+ * of `<BlogPost>` from INSIDE the island — no `.astro` ↔ `.tsx`
71
+ * boundary is crossed during the body render.
72
+ *
73
+ * # Why hydration is required (vs. pure SSR)
74
+ *
75
+ * The `WebsiteRuntimeContextProvider` + `<BlogPost>` tree could in
76
+ * theory render on the server only (no `client:*` directive). But:
77
+ *
78
+ * - Embedded sections (`<CTASection />`) typically have their own
79
+ * interactivity (analytics handlers, A/B variant flippers, etc.)
80
+ * that need React event listeners to attach client-side.
81
+ * - The framework's `useWebsiteLabel(...)` hook chain assumes a live
82
+ * React render tree — a pure-SSR tree would freeze label resolution
83
+ * at build time, but any future client-driven label override (e.g.
84
+ * a debug-mode label inspector) would silently break.
85
+ *
86
+ * The factory therefore returns an island that the consumer's `.astro`
87
+ * mounts with `client:load` (or `client:idle` for posts known to be
88
+ * non-interactive). The hydration cost is bounded by the per-locale MDX
89
+ * glob — see "Bundle scaling cliff" below.
90
+ *
91
+ * # Bundle scaling cliff (deferred trade-off)
92
+ *
93
+ * `import.meta.glob({ eager: true })` materializes EVERY post's
94
+ * compiled JS into the per-locale React-island bundle (~5–20 KB per
95
+ * post depending on length and embedded section count). For the
96
+ * Phase 7 dogfood (≤10 posts) this is fine and matches the documented
97
+ * Astro+MDX pattern. Past ~50 posts this becomes a real budget concern.
98
+ * The factory's API is forward-compatible with `{ eager: false }`
99
+ * (lazy loaders): swap the consumer's glob to lazy mode and the
100
+ * `slugToMdxBody[slug]` lookup returns a `() => Promise<{ default:
101
+ * ComponentType }>`; wrap in `React.lazy()` + `<Suspense>` and the
102
+ * factory call site stays unchanged. The migration is a consumer-side
103
+ * change, not an engine API break.
104
+ *
105
+ * # Authoring pattern
106
+ *
107
+ * **Per-locale bridge file** (consumer side, ONE per locale):
108
+ *
109
+ * ```tsx
110
+ * // examples/<app>/website/src/pages-impl/blog-post-en.bridge.tsx
111
+ * import type { ComponentType } from 'react';
112
+ *
113
+ * import { createWebsiteBlogPostBridgeIsland }
114
+ * from '@wildo-ai/saas-website/mdx';
115
+ * import { BLOG_POST_MDX_COMPONENTS }
116
+ * from './blog-post.mdx-components';
117
+ * import { WEBSITE_ROOT_CONFIG } from '../website-root.config';
118
+ *
119
+ * // Static analyzable glob — Vite resolves at build time. The glob
120
+ * // pattern MUST be a single string literal (no concatenation, no
121
+ * // variable interpolation); Vite parses it AST-side. The `as` cast
122
+ * // narrows the unknown record to the per-MDX-file shape Astro's mdx
123
+ * // integration emits (default export = the compiled React component).
124
+ * const rawModules = import.meta.glob('./content/blog/en/*.mdx', {
125
+ * eager: true,
126
+ * }) as Record<string, { default: ComponentType }>;
127
+ *
128
+ * const slugToMdxBody: Record<string, ComponentType> = {};
129
+ * for (const [path, mod] of Object.entries(rawModules)) {
130
+ * // './content/blog/en/why-context.mdx' -> 'why-context'
131
+ * const slug = path.split('/').pop()!.replace(/\.mdx$/, '');
132
+ * slugToMdxBody[slug] = mod.default;
133
+ * }
134
+ *
135
+ * export const BlogPostEnBridgeIsland =
136
+ * createWebsiteBlogPostBridgeIsland({
137
+ * slugToMdxBody,
138
+ * navigation: WEBSITE_ROOT_CONFIG.navigation,
139
+ * mdxComponents: BLOG_POST_MDX_COMPONENTS,
140
+ * });
141
+ * ```
142
+ *
143
+ * **Per-locale `[slug].astro` shell** (consumer side):
144
+ *
145
+ * ```astro
146
+ * ---
147
+ * import { getCollection } from 'astro:content';
148
+ * import {
149
+ * loadWebsitePageRuntime,
150
+ * renderBlogPostHeadHtml,
151
+ * renderWebsiteDesignTokensCss,
152
+ * } from '@wildo-ai/saas-website/astro';
153
+ * import { parseBlogPostFrontmatter } from '@wildo-ai/saas-website/mdx';
154
+ * import { BlogPostEnBridgeIsland } from '@/pages-impl/blog-post-en.bridge';
155
+ * import { BLOG_POST_PAGE_MANIFEST } from '@/pages-impl/blog-post.page';
156
+ * import { WEBSITE_SITE_CONTEXT } from '@/website-site-context';
157
+ *
158
+ * export async function getStaticPaths() {
159
+ * const posts = await getCollection('blog');
160
+ * const defaultLocale = WEBSITE_SITE_CONTEXT.websiteConfig.defaultLocale;
161
+ * return posts
162
+ * .filter((p) => p.data.locale === defaultLocale)
163
+ * .map((p) => ({ params: { slug: p.data.slug }, props: { entry: p } }));
164
+ * }
165
+ * const { entry } = Astro.props;
166
+ *
167
+ * const runtime = loadWebsitePageRuntime({
168
+ * manifest: BLOG_POST_PAGE_MANIFEST,
169
+ * siteContext: WEBSITE_SITE_CONTEXT,
170
+ * labelPacksDir: './src/i18n',
171
+ * astroLocale: Astro.currentLocale,
172
+ * });
173
+ * const frontmatter = parseBlogPostFrontmatter(entry.data, { source: entry.id });
174
+ * const headHtml = renderBlogPostHeadHtml({
175
+ * siteContext: WEBSITE_SITE_CONTEXT,
176
+ * locale: runtime.locale,
177
+ * routePath: `/blog/${frontmatter.slug}`,
178
+ * frontmatter,
179
+ * });
180
+ * const designTokensCss = renderWebsiteDesignTokensCss(
181
+ * WEBSITE_SITE_CONTEXT.rootConfig.designTokens,
182
+ * );
183
+ * ---
184
+ * <!doctype html>
185
+ * <html lang={runtime.locale}>
186
+ * <head>
187
+ * <meta charset="UTF-8" />
188
+ * <meta name="viewport" content="width=device-width,initial-scale=1.0" />
189
+ * <Fragment set:html={headHtml} />
190
+ * <style set:html={designTokensCss}></style>
191
+ * </head>
192
+ * <body>
193
+ * <BlogPostEnBridgeIsland
194
+ * client:load
195
+ * slug={frontmatter.slug}
196
+ * rawFrontmatter={entry.data}
197
+ * source={entry.id}
198
+ * locale={runtime.locale}
199
+ * defaultLocale={runtime.defaultLocale}
200
+ * labelPack={runtime.labelPack}
201
+ * designTokens={WEBSITE_SITE_CONTEXT.rootConfig.designTokens}
202
+ * skipToMainContentLabelKey={
203
+ * WEBSITE_SITE_CONTEXT.rootConfig.skipToMainContentLabelKey
204
+ * }
205
+ * />
206
+ * </body>
207
+ * </html>
208
+ * ```
209
+ *
210
+ * # Boundary
211
+ *
212
+ * Strictly client-bundle-safe (re-exported from `mdx.ts` — NOT from
213
+ * `astro-island.ts`, because this module hard-imports the MDX adapter
214
+ * whose `@mdx-js/react` peer is optional; walked by the
215
+ * bundle-isolation test). MUST stay clear of `node:*` and SSR-tier
216
+ * specifiers.
217
+ */
218
+
219
+ /**
220
+ * Module-load-time dependencies — closed over inside the returned
221
+ * island. NEVER cross the hydration boundary as serialized props.
222
+ */
223
+ export interface CreateWebsiteBlogPostBridgeIslandDeps {
224
+ /**
225
+ * Per-slug map of compiled MDX bodies (each entry is a React
226
+ * component — the `.default` export of the MDX module). Typically
227
+ * built from `import.meta.glob('./content/blog/<locale>/*.mdx',
228
+ * { eager: true })` with the path-key normalized to the slug. Closed
229
+ * over at module-load time so the entire MDX body graph for the
230
+ * locale lands inside ONE React island bundle (D-6 Option A) — this
231
+ * is the structural fix for Astro's React-children context-propagation
232
+ * gap (see file preamble).
233
+ *
234
+ * The map is read by `slugToMdxBody[props.slug]` at island render
235
+ * time. A missing slug throws a clear framework-owned error (rather than
236
+ * rendering an empty body silently); the consumer's `getStaticPaths`
237
+ * is responsible for not emitting routes for slugs absent from the
238
+ * map.
239
+ *
240
+ * **Why `ComponentType<any>` and not a stricter generic**: matches
241
+ * the upstream `@mdx-js/react` convention (`MDXContent` from
242
+ * `mdx/types` is parameterized over an open `MDXProps` shape). MDX
243
+ * compilation hands the body a compiler-managed props object that
244
+ * the type system cannot narrow at the framework boundary; using
245
+ * `<any>` here keeps the consumer's
246
+ * `import.meta.glob(...) as Record<string, { default: ComponentType }>`
247
+ * cast assignable without an additional wrap/unwrap. Same posture
248
+ * as `WebsiteMdxComponentsMap` (intentional symmetry).
249
+ */
250
+ slugToMdxBody: Readonly<Record<string, ComponentType<any>>>;
251
+ /**
252
+ * Site-wide navigation slot bindings (header / footer components).
253
+ * Closed over here for the same reason as the page bridge: both
254
+ * fields are `ComponentType` refs that cannot survive Astro's
255
+ * serialization boundary.
256
+ */
257
+ navigation: WebsiteNavigationConfig;
258
+ /**
259
+ * MDX-tag → React-component map forwarded into `<BlogPost>` and
260
+ * scoped via `<WebsiteMdxProvider>`. Typically a small object
261
+ * literal exporting the consumer-authored business sections that
262
+ * blog posts can embed (`<CTASection />`, etc.). Closed over here
263
+ * (rather than passed as an island prop) because the values are
264
+ * `ComponentType` refs.
265
+ */
266
+ mdxComponents?: WebsiteMdxComponentsMap;
267
+ }
268
+
269
+ /**
270
+ * Hydration-boundary props — what the consumer's `.astro` page passes
271
+ * to `<BlogPostEnBridgeIsland client:load ...{primitiveProps}/>`.
272
+ * Every field is JSON-serializable.
273
+ */
274
+ export interface WebsiteBlogPostBridgeIslandProps {
275
+ /**
276
+ * Slug of the post being rendered. Used by the island to look up
277
+ * the matching MDX body in `deps.slugToMdxBody`. Carried as a plain
278
+ * string (not the branded `BlogPostSlug`) because Astro's
279
+ * serialization boundary unwraps brands.
280
+ */
281
+ slug: string;
282
+ /**
283
+ * Raw frontmatter object Astro hands the page from `entry.data`.
284
+ * Forwarded straight to `<BlogPost>` for strict Zod-4 validation.
285
+ * The bridge does NOT re-validate (single validation surface, owned
286
+ * by the renderer).
287
+ */
288
+ rawFrontmatter: unknown;
289
+ /**
290
+ * Diagnostic source identifier echoed into validation errors —
291
+ * typically `entry.id` from Astro's content collection. Optional
292
+ * but recommended for actionable error messages.
293
+ */
294
+ source?: string;
295
+ /** The locale Astro resolved (with default-locale fallback). */
296
+ locale: AvailableLanguage;
297
+ /**
298
+ * The site's default locale. Required for `WebsiteInternalLink`
299
+ * locale-prefix logic (mirrors the page bridge's `defaultLocale`
300
+ * island prop).
301
+ */
302
+ defaultLocale: AvailableLanguage;
303
+ /** Already-loaded + already-shape-validated label pack. */
304
+ labelPack: LabelPack;
305
+ /** Design-token bundle from `WebsiteRootConfig.designTokens`. */
306
+ designTokens: WebsiteDesignTokens;
307
+ /**
308
+ * Fully-composed label key the framework reads to render the
309
+ * "Skip to main content" affordance. Mirrors
310
+ * `WebsiteRootConfig.skipToMainContentLabelKey`. Carried as a
311
+ * primitive island prop for the same reason as the page bridge.
312
+ */
313
+ skipToMainContentLabelKey: WebsiteLabelKey;
314
+ /** Optional website-facing public providers materialized at build time. */
315
+ frontendProviders?: FrontendProvidersBlock;
316
+ }
317
+
318
+ const EMPTY_MDX_COMPONENTS: WebsiteMdxComponentsMap = Object.freeze({});
319
+
320
+ /**
321
+ * Returns a FUNCTION component, not the wider `ComponentType` union. The island IS a function
322
+ * component — Astro hydrates nothing else here — and the union is not callable, so declaring it
323
+ * cost callers the ability to invoke the island directly (which the bridge suites do, to observe
324
+ * the synchronous throw for an unregistered slug without routing it through React's reconciler
325
+ * error path). `FunctionComponent` is assignable to `ComponentType`, so no consumer narrows.
326
+ */
327
+ export function createWebsiteBlogPostBridgeIsland(
328
+ deps: CreateWebsiteBlogPostBridgeIslandDeps,
329
+ ): FunctionComponent<WebsiteBlogPostBridgeIslandProps> {
330
+ const {
331
+ slugToMdxBody,
332
+ navigation,
333
+ mdxComponents = EMPTY_MDX_COMPONENTS,
334
+ } = deps;
335
+
336
+ function WebsiteBlogPostBridgeIsland({
337
+ slug,
338
+ rawFrontmatter,
339
+ source,
340
+ locale,
341
+ defaultLocale,
342
+ labelPack,
343
+ designTokens,
344
+ skipToMainContentLabelKey,
345
+ frontendProviders,
346
+ }: WebsiteBlogPostBridgeIslandProps): ReactNode {
347
+ const MdxBody = slugToMdxBody[slug];
348
+ if (MdxBody === undefined) {
349
+ /**
350
+ * Loud failure — the consumer's `getStaticPaths` is supposed to
351
+ * only emit routes for slugs present in the MDX glob. A missing
352
+ * lookup means the glob and the collection are out of sync; the
353
+ * operator needs an actionable error string with the offending
354
+ * slug + the keys that ARE present (truncated to keep the build
355
+ * log readable).
356
+ */
357
+ const knownKeys = Object.keys(slugToMdxBody);
358
+ const known =
359
+ knownKeys.length <= 5
360
+ ? knownKeys.join(', ')
361
+ : `${knownKeys.slice(0, 5).join(', ')}, … (${knownKeys.length} total)`;
362
+ throw new Error(
363
+ `[blog-post-bridge] No MDX body registered for slug="${slug}". ` +
364
+ `The bridge factory's slugToMdxBody map only knows: ${known}. ` +
365
+ `Verify the consumer's import.meta.glob('...content/blog/<locale>/*.mdx') ` +
366
+ `pattern matches the source file naming.`,
367
+ );
368
+ }
369
+
370
+ const runtimeValue: WebsiteRuntimeContextValue = {
371
+ locale,
372
+ defaultLocale,
373
+ labelPack,
374
+ designTokens,
375
+ navigation,
376
+ skipToMainContentLabelKey,
377
+ frontendProviderRegistry: createFrontendWebsiteProviderRegistry(frontendProviders),
378
+ };
379
+ return (
380
+ <WebsiteRuntimeContextProvider value={runtimeValue}>
381
+ <BlogPost
382
+ rawFrontmatter={rawFrontmatter}
383
+ source={source}
384
+ mdxComponents={mdxComponents}
385
+ >
386
+ <MdxBody />
387
+ </BlogPost>
388
+ </WebsiteRuntimeContextProvider>
389
+ );
390
+ }
391
+ WebsiteBlogPostBridgeIsland.displayName = 'WebsiteBlogPostBridgeIsland';
392
+
393
+ return WebsiteBlogPostBridgeIsland;
394
+ }
395
+
396
+ /**
397
+ * Re-export of the framework-reserved blog-post page ref. Consumers
398
+ * authoring `BLOG_POST_PAGE_MANIFEST` need this constant to satisfy
399
+ * the manifest's `ref` field; re-exporting it from the bridge module
400
+ * means the consumer's per-locale bridge file can grab both symbols
401
+ * from the one `@wildo-ai/saas-website/mdx` import it already has.
402
+ */
403
+ export { BLOG_POST_PAGE_REF };
404
+ /** @wildo_source:part:end saas.website.blog.bridge */