@wildo-ai/saas-website 1.1.7 → 1.1.9

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 (146) hide show
  1. package/dist/esm/astro/assert-website-pages-have-sections.d.ts +28 -0
  2. package/dist/esm/astro/assert-website-pages-have-sections.d.ts.map +1 -0
  3. package/dist/esm/astro/assert-website-pages-have-sections.js +38 -0
  4. package/dist/esm/astro/assert-website-pages-have-sections.js.map +1 -0
  5. package/dist/esm/astro/blog-post-bridge.d.ts.map +1 -1
  6. package/dist/esm/astro/blog-post-bridge.js.map +1 -1
  7. package/dist/esm/astro/blog-post-head.renderer.d.ts.map +1 -1
  8. package/dist/esm/astro/bridge-runtime.d.ts.map +1 -1
  9. package/dist/esm/astro/bridge-runtime.js.map +1 -1
  10. package/dist/esm/astro/collect-expected-label-keys.d.ts.map +1 -1
  11. package/dist/esm/astro/headers-renderer.d.ts.map +1 -1
  12. package/dist/esm/astro/i18n-routing.helper.d.ts.map +1 -1
  13. package/dist/esm/astro/label-pack-loader.d.ts +6 -0
  14. package/dist/esm/astro/label-pack-loader.d.ts.map +1 -1
  15. package/dist/esm/astro/label-pack-loader.js +10 -0
  16. package/dist/esm/astro/label-pack-loader.js.map +1 -1
  17. package/dist/esm/astro/llms-txt-server.d.ts.map +1 -1
  18. package/dist/esm/astro/pick-labels-for-page.d.ts.map +1 -1
  19. package/dist/esm/astro/robots-renderer.d.ts.map +1 -1
  20. package/dist/esm/astro/robots-renderer.js.map +1 -1
  21. package/dist/esm/astro/sitemap-coverage.d.ts.map +1 -1
  22. package/dist/esm/astro/structured-data.renderer.d.ts.map +1 -1
  23. package/dist/esm/astro/website-page-head.renderer.d.ts.map +1 -1
  24. package/dist/esm/astro/website-page-head.renderer.js.map +1 -1
  25. package/dist/esm/astro/website-page-runtime.helper.d.ts.map +1 -1
  26. package/dist/esm/astro/website-site-context.d.ts.map +1 -1
  27. package/dist/esm/astro/website-site-context.js.map +1 -1
  28. package/dist/esm/astro-island.d.ts.map +1 -1
  29. package/dist/esm/astro.d.ts +1 -0
  30. package/dist/esm/astro.d.ts.map +1 -1
  31. package/dist/esm/astro.js +1 -0
  32. package/dist/esm/astro.js.map +1 -1
  33. package/dist/esm/companion-exports.d.ts.map +1 -1
  34. package/dist/esm/companion-exports.js.map +1 -1
  35. package/dist/esm/components/low-level/WebsiteInternalButton.d.ts.map +1 -1
  36. package/dist/esm/components/low-level/WebsiteInternalLink.d.ts.map +1 -1
  37. package/dist/esm/config/define-website-config.d.ts.map +1 -1
  38. package/dist/esm/config/define-website-config.js.map +1 -1
  39. package/dist/esm/config/index.d.ts.map +1 -1
  40. package/dist/esm/config/load-website-config.d.ts.map +1 -1
  41. package/dist/esm/config/load-website-config.js.map +1 -1
  42. package/dist/esm/config/load-website-pricing-catalog.d.ts.map +1 -1
  43. package/dist/esm/config/load-website-pricing-catalog.js.map +1 -1
  44. package/dist/esm/config/load-website-site-context.d.ts.map +1 -1
  45. package/dist/esm/config/load-website-site-context.js.map +1 -1
  46. package/dist/esm/config/wildo-website-config.schemas.d.ts.map +1 -1
  47. package/dist/esm/config-loader.d.ts.map +1 -1
  48. package/dist/esm/config-loader.js.map +1 -1
  49. package/dist/esm/core/anonymous-session/InboundContactForm.d.ts.map +1 -1
  50. package/dist/esm/core/anonymous-session/InboundContactForm.js.map +1 -1
  51. package/dist/esm/core/anonymous-session/inbound-contact-form.schema.d.ts.map +1 -1
  52. package/dist/esm/core/anonymous-session/inbound-contact-form.schema.js.map +1 -1
  53. package/dist/esm/core/anonymous-session/website-anonymous-session-client.d.ts.map +1 -1
  54. package/dist/esm/core/anonymous-session/website-anonymous-session-client.js.map +1 -1
  55. package/dist/esm/core/anonymous-session/website-submission-challenge.d.ts.map +1 -1
  56. package/dist/esm/core/anonymous-session/website-submission-challenge.js.map +1 -1
  57. package/dist/esm/core/consent/WebsiteConsentBanner.d.ts.map +1 -1
  58. package/dist/esm/core/consent/WebsiteConsentBanner.js.map +1 -1
  59. package/dist/esm/core/consent/WebsiteConsentContext.d.ts.map +1 -1
  60. package/dist/esm/core/consent/WebsiteConsentGate.d.ts.map +1 -1
  61. package/dist/esm/core/consent/useWebsiteConsent.d.ts.map +1 -1
  62. package/dist/esm/core/consent/website-consent-storage.d.ts.map +1 -1
  63. package/dist/esm/core/contexts/WebsitePageContext.d.ts.map +1 -1
  64. package/dist/esm/core/contexts/WebsiteRuntimeContext.d.ts.map +1 -1
  65. package/dist/esm/core/contexts/WebsiteRuntimeContext.js.map +1 -1
  66. package/dist/esm/core/contexts/WebsiteSectionContext.d.ts.map +1 -1
  67. package/dist/esm/core/contexts/useWebsitePage.d.ts.map +1 -1
  68. package/dist/esm/core/contexts/useWebsiteRuntime.d.ts.map +1 -1
  69. package/dist/esm/core/contexts/useWebsiteRuntime.js.map +1 -1
  70. package/dist/esm/core/contexts/useWebsiteSection.d.ts.map +1 -1
  71. package/dist/esm/core/external-providers/WebsiteProviderComponent.d.ts.map +1 -1
  72. package/dist/esm/core/external-providers/WebsiteProviderComponent.js.map +1 -1
  73. package/dist/esm/core/external-providers/frontend-provider-registry.website.d.ts.map +1 -1
  74. package/dist/esm/core/external-providers/provider-component-registry.website.d.ts.map +1 -1
  75. package/dist/esm/core/external-providers/provider-component-registry.website.js.map +1 -1
  76. package/dist/esm/core/external-providers/useWebsiteProviderComponent.d.ts.map +1 -1
  77. package/dist/esm/core/external-providers/useWebsiteProviderComponent.js.map +1 -1
  78. package/dist/esm/core/external-providers/useWebsiteProviderScripts.d.ts.map +1 -1
  79. package/dist/esm/core/external-providers/useWebsiteProviderScripts.js.map +1 -1
  80. package/dist/esm/core/external-providers/website-provider-sdk-activations.d.ts.map +1 -1
  81. package/dist/esm/core/external-providers/website-provider-sdk-activations.js.map +1 -1
  82. package/dist/esm/core/factories/define-website-page-manifest.d.ts.map +1 -1
  83. package/dist/esm/core/factories/define-website-section.d.ts +2 -0
  84. package/dist/esm/core/factories/define-website-section.d.ts.map +1 -1
  85. package/dist/esm/core/factories/define-website-section.js +2 -0
  86. package/dist/esm/core/factories/define-website-section.js.map +1 -1
  87. package/dist/esm/core/hooks/useWebsiteDesignTokens.d.ts.map +1 -1
  88. package/dist/esm/core/hooks/useWebsiteLabel.d.ts.map +1 -1
  89. package/dist/esm/core/layouts/WebsitePageLayout.d.ts.map +1 -1
  90. package/dist/esm/core/layouts/WebsitePageLayout.js.map +1 -1
  91. package/dist/esm/core/layouts/WebsitePageSections.d.ts +43 -0
  92. package/dist/esm/core/layouts/WebsitePageSections.d.ts.map +1 -0
  93. package/dist/esm/core/layouts/WebsitePageSections.js +13 -0
  94. package/dist/esm/core/layouts/WebsitePageSections.js.map +1 -0
  95. package/dist/esm/core/layouts/WebsiteSection.d.ts.map +1 -1
  96. package/dist/esm/core/routing/internal-routing.utils.d.ts.map +1 -1
  97. package/dist/esm/index.d.ts +1 -0
  98. package/dist/esm/index.d.ts.map +1 -1
  99. package/dist/esm/index.js +1 -0
  100. package/dist/esm/index.js.map +1 -1
  101. package/dist/esm/mdx/BlogPost.d.ts.map +1 -1
  102. package/dist/esm/mdx/blog-post-collection.config.d.ts.map +1 -1
  103. package/dist/esm/mdx/blog-post-frontmatter-source.d.ts.map +1 -1
  104. package/dist/esm/mdx/index.d.ts.map +1 -1
  105. package/dist/esm/mdx.d.ts.map +1 -1
  106. package/dist/esm/schemas/blog/blog-post-frontmatter.shared.schemas.d.ts.map +1 -1
  107. package/dist/esm/schemas/design-tokens/website-design-tokens.shared.schemas.d.ts.map +1 -1
  108. package/dist/esm/schemas/design-tokens/website-design-tokens.shared.schemas.js.map +1 -1
  109. package/dist/esm/schemas/label-keys/website-consent-label-keys.schemas.d.ts.map +1 -1
  110. package/dist/esm/schemas/label-keys/website-label-key.schemas.d.ts.map +1 -1
  111. package/dist/esm/schemas/label-keys/website-provider-component-label-keys.schemas.d.ts.map +1 -1
  112. package/dist/esm/schemas/label-keys/website-provider-component-label-keys.schemas.js.map +1 -1
  113. package/dist/esm/schemas/manifests/website-page-manifest.shared.schemas.d.ts +2 -0
  114. package/dist/esm/schemas/manifests/website-page-manifest.shared.schemas.d.ts.map +1 -1
  115. package/dist/esm/schemas/manifests/website-page-manifest.shared.schemas.js +12 -3
  116. package/dist/esm/schemas/manifests/website-page-manifest.shared.schemas.js.map +1 -1
  117. package/dist/esm/schemas/manifests/website-root-config.shared.schemas.d.ts +3 -3
  118. package/dist/esm/schemas/manifests/website-root-config.shared.schemas.d.ts.map +1 -1
  119. package/dist/esm/schemas/manifests/website-root-config.shared.schemas.js.map +1 -1
  120. package/dist/esm/schemas/refs/page-ref.schemas.d.ts.map +1 -1
  121. package/dist/esm/schemas/refs/section-ref.schemas.d.ts.map +1 -1
  122. package/dist/esm/schemas/sections/website-section-category.shared.d.ts.map +1 -1
  123. package/dist/esm/schemas/sections/website-section-definition.shared.schemas.d.ts +9 -0
  124. package/dist/esm/schemas/sections/website-section-definition.shared.schemas.d.ts.map +1 -1
  125. package/dist/esm/schemas/sections/website-section-definition.shared.schemas.js +11 -0
  126. package/dist/esm/schemas/sections/website-section-definition.shared.schemas.js.map +1 -1
  127. package/dist/esm/schemas/structured-data/pricing-offer-structured-data.shared.d.ts.map +1 -1
  128. package/dist/esm/schemas/structured-data/pricing-offer-structured-data.shared.js.map +1 -1
  129. package/dist/esm/schemas/structured-data/structured-data-reconciliation.shared.d.ts.map +1 -1
  130. package/dist/esm/schemas/structured-data/structured-data-reconciliation.shared.js.map +1 -1
  131. package/dist/esm/schemas/structured-data/website-structured-data.shared.schemas.d.ts.map +1 -1
  132. package/dist/esm/schemas/structured-data/website-structured-data.shared.schemas.js.map +1 -1
  133. package/dist/esm/schemas/validators/label-pack.validator.d.ts.map +1 -1
  134. package/dist/tsconfig.build.tsbuildinfo +1 -1
  135. package/package.json +5 -5
  136. package/src/astro/__tests__/assert-website-pages-have-sections.test.ts +75 -0
  137. package/src/astro/assert-website-pages-have-sections.ts +41 -0
  138. package/src/astro/label-pack-loader.ts +10 -0
  139. package/src/astro.ts +1 -0
  140. package/src/core/__tests__/WebsitePageSections.test.tsx +57 -0
  141. package/src/core/factories/define-website-section.ts +4 -0
  142. package/src/core/layouts/WebsitePageSections.tsx +68 -0
  143. package/src/index.ts +1 -0
  144. package/src/schemas/__tests__/page-manifest.test.ts +4 -7
  145. package/src/schemas/manifests/website-page-manifest.shared.schemas.ts +12 -3
  146. package/src/schemas/sections/website-section-definition.shared.schemas.ts +11 -0
@@ -1 +1 @@
1
- {"version":3,"file":"blog-post-frontmatter-source.d.ts","sourceRoot":"","sources":["../../../src/mdx/blog-post-frontmatter-source.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,+BAA+B,EACrC,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sDAAsD,CAAC;AAEhG;;;;;;GAMG;AACH,MAAM,MAAM,4CAA4C,GAAG,+BAA+B,CAAC;AAE3F,wBAAgB,qCAAqC,CACnD,MAAM,EAAE,MAAM,EACd,GAAG,GAAE,4CAAiD,GACrD,mBAAmB,CAUrB"}
1
+ {"version":3,"file":"blog-post-frontmatter-source.d.ts","sourceRoot":"","sources":["../../../../src/mdx/blog-post-frontmatter-source.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,+BAA+B,EACrC,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sDAAsD,CAAC;AAEhG;;;;;;GAMG;AACH,MAAM,MAAM,4CAA4C,GAAG,+BAA+B,CAAC;AAE3F,wBAAgB,qCAAqC,CACnD,MAAM,EAAE,MAAM,EACd,GAAG,GAAE,4CAAiD,GACrD,mBAAmB,CAUrB"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/mdx/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,QAAQ,EACR,kBAAkB,EAClB,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,GAC1B,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,6BAA6B,EAC7B,wBAAwB,EACxB,KAAK,oBAAoB,EACzB,KAAK,oCAAoC,EACzC,KAAK,6BAA6B,EAClC,KAAK,+BAA+B,GACrC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,kBAAkB,EAClB,KAAK,uBAAuB,EAC5B,KAAK,uBAAuB,GAC7B,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/mdx/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,QAAQ,EACR,kBAAkB,EAClB,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,GAC1B,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,6BAA6B,EAC7B,wBAAwB,EACxB,KAAK,oBAAoB,EACzB,KAAK,oCAAoC,EACzC,KAAK,6BAA6B,EAClC,KAAK,+BAA+B,GACrC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,kBAAkB,EAClB,KAAK,uBAAuB,EAC5B,KAAK,uBAAuB,GAC7B,MAAM,wBAAwB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"mdx.d.ts","sourceRoot":"","sources":["../../src/mdx.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,cAAc,aAAa,CAAC;AAC5B;;;;;;;;;;;GAWG;AACH,cAAc,0BAA0B,CAAC"}
1
+ {"version":3,"file":"mdx.d.ts","sourceRoot":"","sources":["../../../src/mdx.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,cAAc,aAAa,CAAC;AAC5B;;;;;;;;;;;GAWG;AACH,cAAc,0BAA0B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"blog-post-frontmatter.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../src/schemas/blog/blog-post-frontmatter.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAEzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,QAAA,MAAM,mBAAmB,wDAQC,CAAC;AAE3B,eAAO,MAAM,kBAAkB,EAAE,OAAO,mBAAyC,CAAC;AAElF,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAE/D,QAAA,MAAM,oBAAoB,yDAQC,CAAC;AAE5B,eAAO,MAAM,mBAAmB,EAAE,OAAO,oBAA2C,CAAC;AAErF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAEjE,QAAA,MAAM,kBAAkB,uDAQC,CAAC;AAE1B,eAAO,MAAM,iBAAiB,EAAE,OAAO,kBAAuC,CAAC;AAE/E,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE7D,QAAA,MAAM,0BAA0B;;;;;;;;;iBAS9B,CAAC;AAEH,eAAO,MAAM,yBAAyB,EAAE,OAAO,0BACnB,CAAC;AAE7B,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;;AAC7E,2DAA2D"}
1
+ {"version":3,"file":"blog-post-frontmatter.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../../src/schemas/blog/blog-post-frontmatter.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAEzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,QAAA,MAAM,mBAAmB,wDAQC,CAAC;AAE3B,eAAO,MAAM,kBAAkB,EAAE,OAAO,mBAAyC,CAAC;AAElF,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAE/D,QAAA,MAAM,oBAAoB,yDAQC,CAAC;AAE5B,eAAO,MAAM,mBAAmB,EAAE,OAAO,oBAA2C,CAAC;AAErF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAEjE,QAAA,MAAM,kBAAkB,uDAQC,CAAC;AAE1B,eAAO,MAAM,iBAAiB,EAAE,OAAO,kBAAuC,CAAC;AAE/E,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE7D,QAAA,MAAM,0BAA0B;;;;;;;;;iBAS9B,CAAC;AAEH,eAAO,MAAM,yBAAyB,EAAE,OAAO,0BACnB,CAAC;AAE7B,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;;AAC7E,2DAA2D"}
@@ -1 +1 @@
1
- {"version":3,"file":"website-design-tokens.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../src/schemas/design-tokens/website-design-tokens.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA4DxB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,QAAA,MAAM,yBAAyB;;;;;;;;;;;;iBAY7B,CAAC;AAEH,eAAO,MAAM,wBAAwB,EAAE,OAAO,yBAAqD,CAAC;AAEpG,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAC3E,+DAA+D;AAE/D;;;;;;;;;;;;;;;GAeG;AACH,QAAA,MAAM,0BAA0B;;;;;;;iBAO9B,CAAC;AAEH,eAAO,MAAM,yBAAyB,EAAE,OAAO,0BAAuD,CAAC;AAEvG,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAC7E,+DAA+D;AAE/D;;;;;;;;;;GAUG;AACH,QAAA,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;iBAG9B,CAAC;AAEH,eAAO,MAAM,yBAAyB,EAAE,OAAO,0BAAuD,CAAC;AAEvG,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAC7E,+DAA+D;AAE/D;;;;;;;;;;;;GAYG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAElF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,SAAS,MAAM,mBAAmB,EAC1E,KAAK,EAAE,CAAC,EACR,KAAK,EAAE,MAAM,mBAAmB,CAAC,CAAC,CAAC,GAAG,MAAM,EAC5C,QAAQ,CAAC,EAAE,MAAM,GAChB,MAAM,CAGR"}
1
+ {"version":3,"file":"website-design-tokens.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../../src/schemas/design-tokens/website-design-tokens.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA4DxB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,QAAA,MAAM,yBAAyB;;;;;;;;;;;;iBAY7B,CAAC;AAEH,eAAO,MAAM,wBAAwB,EAAE,OAAO,yBAAqD,CAAC;AAEpG,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAC3E,+DAA+D;AAE/D;;;;;;;;;;;;;;;GAeG;AACH,QAAA,MAAM,0BAA0B;;;;;;;iBAO9B,CAAC;AAEH,eAAO,MAAM,yBAAyB,EAAE,OAAO,0BAAuD,CAAC;AAEvG,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAC7E,+DAA+D;AAE/D;;;;;;;;;;GAUG;AACH,QAAA,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;iBAG9B,CAAC;AAEH,eAAO,MAAM,yBAAyB,EAAE,OAAO,0BAAuD,CAAC;AAEvG,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAC7E,+DAA+D;AAE/D;;;;;;;;;;;;GAYG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAElF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,SAAS,MAAM,mBAAmB,EAC1E,KAAK,EAAE,CAAC,EACR,KAAK,EAAE,MAAM,mBAAmB,CAAC,CAAC,CAAC,GAAG,MAAM,EAC5C,QAAQ,CAAC,EAAE,MAAM,GAChB,MAAM,CAGR"}
@@ -1 +1 @@
1
- {"version":3,"file":"website-design-tokens.shared.schemas.js","sourceRoot":"","sources":["../../../../src/schemas/design-tokens/website-design-tokens.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,MAAM,+BAA+B,GAAG,uBAAuB,CAAC;AAChE,MAAM,YAAY,GAAG,CAAC,SAAiB,EAAe,EAAE,CACtD,CAAC;KACE,MAAM,EAAE;KACR,GAAG,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,GAAG,SAAS,oBAAoB,EAAE,CAAC;KACnD,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,+BAA+B,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;IAC/D,KAAK,EACH,GAAG,SAAS,oDAAoD;QAChE,8DAA8D;QAC9D,wEAAwE;CAC3E,CAA2B,CAAC;AAEjC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,UAAU,EAAE,YAAY,CAAC,kBAAkB,CAAC;IAC5C,UAAU,EAAE,YAAY,CAAC,kBAAkB,CAAC;IAC5C,OAAO,EAAE,YAAY,CAAC,eAAe,CAAC;IACtC,iBAAiB,EAAE,YAAY,CAAC,yBAAyB,CAAC;IAC1D,MAAM,EAAE,YAAY,CAAC,cAAc,CAAC;IACpC,gBAAgB,EAAE,YAAY,CAAC,wBAAwB,CAAC;IACxD,OAAO,EAAE,YAAY,CAAC,eAAe,CAAC;IACtC,iBAAiB,EAAE,YAAY,CAAC,yBAAyB,CAAC;IAC1D,MAAM,EAAE,YAAY,CAAC,cAAc,CAAC;IACpC,KAAK,EAAE,YAAY,CAAC,aAAa,CAAC;IAClC,eAAe,EAAE,YAAY,CAAC,uBAAuB,CAAC;CACvD,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,wBAAwB,GAAqC,yBAAyB,CAAC;AAGpG,+DAA+D;AAE/D;;;;;;;;;;;;;;;GAeG;AACH,MAAM,0BAA0B,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,IAAI,EAAE,YAAY,CAAC,aAAa,CAAC;IACjC,EAAE,EAAE,YAAY,CAAC,WAAW,CAAC;IAC7B,EAAE,EAAE,YAAY,CAAC,WAAW,CAAC;IAC7B,EAAE,EAAE,YAAY,CAAC,WAAW,CAAC;IAC7B,EAAE,EAAE,YAAY,CAAC,WAAW,CAAC;IAC7B,IAAI,EAAE,YAAY,CAAC,aAAa,CAAC;CAClC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,yBAAyB,GAAsC,0BAA0B,CAAC;AAGvG,+DAA+D;AAE/D;;;;;;;;;;GAUG;AACH,MAAM,0BAA0B,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,MAAM,EAAE,yBAAyB;IACjC,KAAK,EAAE,0BAA0B;CAClC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,yBAAyB,GAAsC,0BAA0B,CAAC;AAGvG,+DAA+D;AAE/D;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,6BAA6B,CAAC,KAAa,EAAE,KAAa;IACxE,OAAO,aAAa,KAAK,IAAI,KAAK,EAAE,CAAC;AACvC,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,wBAAwB,CACtC,KAAQ,EACR,KAA4C,EAC5C,QAAiB;IAEjB,MAAM,QAAQ,GAAG,6BAA6B,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC7D,OAAO,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,QAAQ,GAAG,CAAC,CAAC,CAAC,OAAO,QAAQ,KAAK,QAAQ,GAAG,CAAC;AACvF,CAAC","sourcesContent":["import { z } from 'zod';\n\n/**\n * CSS-injection-safe value validator shared by every design-token field.\n *\n * **Threat model** — TWO escape surfaces, BOTH covered:\n *\n * 1. CSS *declaration* break-out (Phase 6 third audit fix H-α):\n * the renderer `renderWebsiteDesignTokensCss` interpolates token\n * values **raw** into a `:root { --website-<group>-<token>: <value>; }`\n * block. A value containing any of `;`, `}`, `{`, `/*`, `*\\/`, or a\n * newline can break out of the property declaration and inject\n * arbitrary CSS — at minimum a config typo silently corrupts the\n * page; at worst a malicious or untrusted config author injects a\n * `body { display: none; }` rule that blanks the entire site.\n *\n * 2. HTML *style-element* break-out (Phase 6 fourth audit fix H-Χ):\n * the rendered `:root { ... }` block is itself wrapped in an inline\n * `<style>…</style>` element by the head renderer. Per the HTML spec\n * the `<style>` element is a *raw-text* element — its only\n * terminator is the literal sequence `</style`. A token value\n * containing `<` (or, for symmetry, `>`) can therefore close the\n * `<style>` element early and follow up with `<svg onload=alert(1)>`\n * or any other HTML / JS payload, which the H-α deny-list (which\n * did NOT forbid `<` / `>`) failed to catch. CSS values legitimately\n * need NEITHER `<` NOR `>` (no CSS grammar uses them — no color,\n * length, variable reference, calc expression, or functional\n * notation does), so denying both is conservative and correct.\n *\n * **Why validate at the schema layer (not inside the renderer)**:\n * 1. Fail at config load (build-time / boot-time), not at every\n * `<head>` render — a malformed token surfaces in the schema-parse\n * error message instead of silently emitting broken CSS / HTML.\n * 2. Single source of truth — every field that flows into the\n * `<style>`-wrapped CSS block gets the same guard (impossible to\n * add a future token field that bypasses the renderer's escape\n * pass).\n * 3. The renderer stays a simple interpolator, which makes its own\n * audit surface trivially small.\n *\n * **Why a `.refine` over a strict color-string regex**: colors accept\n * `currentColor`, named colors, `var(--foo)` references, oklch\n * functional notation, etc. — a strict positive grammar is ~80 lines\n * and high-maintenance. A negative deny-list of the eight characters\n * that are LITERALLY the only ones that can break out of either the CSS\n * property declaration OR the wrapping `<style>` element is small,\n * complete, and obviously correct.\n */\nconst CSS_INJECTION_FORBIDDEN_PATTERN = /[;{}<>\\n\\r]|\\/\\*|\\*\\//;\nconst cssSafeValue = (fieldName: string): z.ZodString =>\n z\n .string()\n .min(1, { error: `${fieldName} must not be empty` })\n .refine((value) => !CSS_INJECTION_FORBIDDEN_PATTERN.test(value), {\n error:\n `${fieldName} must not contain CSS-injection or HTML break-out ` +\n `metacharacters (; { } < > /* */ newline) — token values are ` +\n `interpolated raw into <style>:root { --website-...: <value>; }</style>`,\n }) as unknown as z.ZodString;\n\n/**\n * @wildo_source:part:start saas.website.design-tokens.colors facet:layer:shared facet:family:website\n *\n * `WebsiteColorTokens` — minimal palette the marketing-site exposes as\n * CSS custom properties (`--website-colors-<token>`, the key verbatim, so\n * `surfaceForeground` → `--website-colors-surfaceForeground`) on `:root`.\n * Reference one from code through `websiteDesignTokenCssVar('colors', …)`\n * rather than spelling the name.\n *\n * **Why a dedicated marketing palette** (vs. reusing\n * `presets-components-models/design-tokens-config`):\n *\n * - The SaaS app's design tokens carry many semantic states (`muted`,\n * `destructive`, `ring`, `sidebar-*`, …) that have no marketing-site\n * analogue. Forcing the website to consume the full palette would\n * leak app-internal semantics into a public surface.\n * - Marketing pages need *display-grade* colors (heavily branded\n * accents, gradient stops, hero-overlay tints) that the SaaS app's\n * functional palette deliberately avoids.\n * - Designers overriding website tokens (Phase 6 dogfood proves this\n * path) should not be able to break the SaaS app by editing one\n * tailwind.config — the two surfaces stay decoupled.\n *\n * **Token format**: every value is a CSS color string. Shape validation\n * is intentionally permissive (accepts hex, rgb/rgba, hsl/hsla, oklch,\n * `currentColor`, named colors, CSS variable references) — a strict\n * color-string regex would reject legitimate values (oklch grammar\n * alone is ~80 lines) and a malformed-but-syntactically-valid color\n * fails loudly at render time.\n *\n * **What IS strictly enforced**: every value passes `cssSafeValue`,\n * which rejects the six characters that can break out of a CSS\n * property declaration (`;`, `{`, `}`, `/*`, `*\\/`, newline). See the\n * `cssSafeValue` JSDoc above for the full threat model.\n *\n * **Adding a token**: append the field here using `cssSafeValue(...)`,\n * document its semantic intent in the JSDoc. `renderWebsiteDesignTokensCss`\n * (from `@wildo-ai/saas-website/astro`) emits every key generically, so no\n * emitter needs editing.\n */\nconst _WebsiteColorTokensSchema = z.object({\n background: cssSafeValue('background color'),\n foreground: cssSafeValue('foreground color'),\n primary: cssSafeValue('primary color'),\n primaryForeground: cssSafeValue('primaryForeground color'),\n accent: cssSafeValue('accent color'),\n accentForeground: cssSafeValue('accentForeground color'),\n surface: cssSafeValue('surface color'),\n surfaceForeground: cssSafeValue('surfaceForeground color'),\n border: cssSafeValue('border color'),\n muted: cssSafeValue('muted color'),\n mutedForeground: cssSafeValue('mutedForeground color'),\n});\n\nexport const WebsiteColorTokensSchema: typeof _WebsiteColorTokensSchema = _WebsiteColorTokensSchema;\n\nexport type WebsiteColorTokens = z.infer<typeof _WebsiteColorTokensSchema>;\n/** @wildo_source:part:end saas.website.design-tokens.colors */\n\n/**\n * @wildo_source:part:start saas.website.design-tokens.radius facet:layer:shared facet:family:website\n *\n * `WebsiteRadiusTokens` — border-radius scale exposed as\n * `--website-radii-<token>` CSS variables.\n *\n * Marketing radii are intentionally a separate token set from the\n * SaaS app's radii: the website typically wants larger pill / hero\n * radii than the app's compact-UI radii. Keeping them separate lets\n * either surface evolve without coupling.\n *\n * Values are CSS length strings (`'0.25rem'`, `'12px'`,\n * `'9999px'`). Same permissive-shape + strict-CSS-injection\n * rationale as `WebsiteColorTokens` (every value runs through\n * `cssSafeValue`).\n */\nconst _WebsiteRadiusTokensSchema = z.object({\n none: cssSafeValue('none radius'),\n sm: cssSafeValue('sm radius'),\n md: cssSafeValue('md radius'),\n lg: cssSafeValue('lg radius'),\n xl: cssSafeValue('xl radius'),\n full: cssSafeValue('full radius'),\n});\n\nexport const WebsiteRadiusTokensSchema: typeof _WebsiteRadiusTokensSchema = _WebsiteRadiusTokensSchema;\n\nexport type WebsiteRadiusTokens = z.infer<typeof _WebsiteRadiusTokensSchema>;\n/** @wildo_source:part:end saas.website.design-tokens.radius */\n\n/**\n * @wildo_source:part:start saas.website.design-tokens.bundle facet:layer:shared facet:family:website\n *\n * `WebsiteDesignTokens` — the complete design-token bundle attached to\n * `WebsiteRootConfig.designTokens`. Currently colors + radii only\n * (per plan Phase 1 scope: \"WebsiteColorTokensSchema + WebsiteRadiusTokensSchema (only)\").\n *\n * Future expansions (typography scale, motion tokens, shadow elevation)\n * will land as additional fields here without breaking the\n * `WebsiteRootConfig` shape.\n */\nconst _WebsiteDesignTokensSchema = z.object({\n colors: _WebsiteColorTokensSchema,\n radii: _WebsiteRadiusTokensSchema,\n});\n\nexport const WebsiteDesignTokensSchema: typeof _WebsiteDesignTokensSchema = _WebsiteDesignTokensSchema;\n\nexport type WebsiteDesignTokens = z.infer<typeof _WebsiteDesignTokensSchema>;\n/** @wildo_source:part:end saas.website.design-tokens.bundle */\n\n/**\n * The CSS custom-property NAME one design token is emitted under —\n * `--website-<group>-<token>`, with the token key verbatim (no case\n * conversion), so `('colors', 'surfaceForeground')` is\n * `--website-colors-surfaceForeground`.\n *\n * This is the one place the naming rule lives. `renderWebsiteDesignTokensCss`\n * emits through it, and every engine component that reads a token reads\n * through `websiteDesignTokenCssVar`. It exists because the rule used to be\n * spelled by hand at each reader, and one reader spelled it\n * `surface-foreground`: the consent banner's text colour silently fell back to\n * `#000` on every site, whatever palette the application authored.\n */\nexport function websiteDesignTokenCssProperty(group: string, token: string): string {\n return `--website-${group}-${token}`;\n}\n\n/**\n * A `var(...)` reference to one design token, typed against the token bundle\n * so a misspelt group or key does not compile.\n *\n * @example\n * ```ts\n * import { websiteDesignTokenCssVar } from '@wildo-ai/saas-website';\n * const style = { color: websiteDesignTokenCssVar('colors', 'surfaceForeground', '#000') };\n * // → 'var(--website-colors-surfaceForeground, #000)'\n * ```\n */\nexport function websiteDesignTokenCssVar<G extends keyof WebsiteDesignTokens>(\n group: G,\n token: keyof WebsiteDesignTokens[G] & string,\n fallback?: string,\n): string {\n const property = websiteDesignTokenCssProperty(group, token);\n return fallback === undefined ? `var(${property})` : `var(${property}, ${fallback})`;\n}\n"]}
1
+ {"version":3,"file":"website-design-tokens.shared.schemas.js","sourceRoot":"","sources":["../../../../../src/schemas/design-tokens/website-design-tokens.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,MAAM,+BAA+B,GAAG,uBAAuB,CAAC;AAChE,MAAM,YAAY,GAAG,CAAC,SAAiB,EAAe,EAAE,CACtD,CAAC;KACE,MAAM,EAAE;KACR,GAAG,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,GAAG,SAAS,oBAAoB,EAAE,CAAC;KACnD,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,+BAA+B,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;IAC/D,KAAK,EACH,GAAG,SAAS,oDAAoD;QAChE,8DAA8D;QAC9D,wEAAwE;CAC3E,CAA2B,CAAC;AAEjC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,UAAU,EAAE,YAAY,CAAC,kBAAkB,CAAC;IAC5C,UAAU,EAAE,YAAY,CAAC,kBAAkB,CAAC;IAC5C,OAAO,EAAE,YAAY,CAAC,eAAe,CAAC;IACtC,iBAAiB,EAAE,YAAY,CAAC,yBAAyB,CAAC;IAC1D,MAAM,EAAE,YAAY,CAAC,cAAc,CAAC;IACpC,gBAAgB,EAAE,YAAY,CAAC,wBAAwB,CAAC;IACxD,OAAO,EAAE,YAAY,CAAC,eAAe,CAAC;IACtC,iBAAiB,EAAE,YAAY,CAAC,yBAAyB,CAAC;IAC1D,MAAM,EAAE,YAAY,CAAC,cAAc,CAAC;IACpC,KAAK,EAAE,YAAY,CAAC,aAAa,CAAC;IAClC,eAAe,EAAE,YAAY,CAAC,uBAAuB,CAAC;CACvD,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,wBAAwB,GAAqC,yBAAyB,CAAC;AAGpG,+DAA+D;AAE/D;;;;;;;;;;;;;;;GAeG;AACH,MAAM,0BAA0B,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,IAAI,EAAE,YAAY,CAAC,aAAa,CAAC;IACjC,EAAE,EAAE,YAAY,CAAC,WAAW,CAAC;IAC7B,EAAE,EAAE,YAAY,CAAC,WAAW,CAAC;IAC7B,EAAE,EAAE,YAAY,CAAC,WAAW,CAAC;IAC7B,EAAE,EAAE,YAAY,CAAC,WAAW,CAAC;IAC7B,IAAI,EAAE,YAAY,CAAC,aAAa,CAAC;CAClC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,yBAAyB,GAAsC,0BAA0B,CAAC;AAGvG,+DAA+D;AAE/D;;;;;;;;;;GAUG;AACH,MAAM,0BAA0B,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,MAAM,EAAE,yBAAyB;IACjC,KAAK,EAAE,0BAA0B;CAClC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,yBAAyB,GAAsC,0BAA0B,CAAC;AAGvG,+DAA+D;AAE/D;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,6BAA6B,CAAC,KAAa,EAAE,KAAa;IACxE,OAAO,aAAa,KAAK,IAAI,KAAK,EAAE,CAAC;AACvC,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,wBAAwB,CACtC,KAAQ,EACR,KAA4C,EAC5C,QAAiB;IAEjB,MAAM,QAAQ,GAAG,6BAA6B,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC7D,OAAO,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,QAAQ,GAAG,CAAC,CAAC,CAAC,OAAO,QAAQ,KAAK,QAAQ,GAAG,CAAC;AACvF,CAAC","sourcesContent":["import { z } from 'zod';\n\n/**\n * CSS-injection-safe value validator shared by every design-token field.\n *\n * **Threat model** — TWO escape surfaces, BOTH covered:\n *\n * 1. CSS *declaration* break-out (Phase 6 third audit fix H-α):\n * the renderer `renderWebsiteDesignTokensCss` interpolates token\n * values **raw** into a `:root { --website-<group>-<token>: <value>; }`\n * block. A value containing any of `;`, `}`, `{`, `/*`, `*\\/`, or a\n * newline can break out of the property declaration and inject\n * arbitrary CSS — at minimum a config typo silently corrupts the\n * page; at worst a malicious or untrusted config author injects a\n * `body { display: none; }` rule that blanks the entire site.\n *\n * 2. HTML *style-element* break-out (Phase 6 fourth audit fix H-Χ):\n * the rendered `:root { ... }` block is itself wrapped in an inline\n * `<style>…</style>` element by the head renderer. Per the HTML spec\n * the `<style>` element is a *raw-text* element — its only\n * terminator is the literal sequence `</style`. A token value\n * containing `<` (or, for symmetry, `>`) can therefore close the\n * `<style>` element early and follow up with `<svg onload=alert(1)>`\n * or any other HTML / JS payload, which the H-α deny-list (which\n * did NOT forbid `<` / `>`) failed to catch. CSS values legitimately\n * need NEITHER `<` NOR `>` (no CSS grammar uses them — no color,\n * length, variable reference, calc expression, or functional\n * notation does), so denying both is conservative and correct.\n *\n * **Why validate at the schema layer (not inside the renderer)**:\n * 1. Fail at config load (build-time / boot-time), not at every\n * `<head>` render — a malformed token surfaces in the schema-parse\n * error message instead of silently emitting broken CSS / HTML.\n * 2. Single source of truth — every field that flows into the\n * `<style>`-wrapped CSS block gets the same guard (impossible to\n * add a future token field that bypasses the renderer's escape\n * pass).\n * 3. The renderer stays a simple interpolator, which makes its own\n * audit surface trivially small.\n *\n * **Why a `.refine` over a strict color-string regex**: colors accept\n * `currentColor`, named colors, `var(--foo)` references, oklch\n * functional notation, etc. — a strict positive grammar is ~80 lines\n * and high-maintenance. A negative deny-list of the eight characters\n * that are LITERALLY the only ones that can break out of either the CSS\n * property declaration OR the wrapping `<style>` element is small,\n * complete, and obviously correct.\n */\nconst CSS_INJECTION_FORBIDDEN_PATTERN = /[;{}<>\\n\\r]|\\/\\*|\\*\\//;\nconst cssSafeValue = (fieldName: string): z.ZodString =>\n z\n .string()\n .min(1, { error: `${fieldName} must not be empty` })\n .refine((value) => !CSS_INJECTION_FORBIDDEN_PATTERN.test(value), {\n error:\n `${fieldName} must not contain CSS-injection or HTML break-out ` +\n `metacharacters (; { } < > /* */ newline) — token values are ` +\n `interpolated raw into <style>:root { --website-...: <value>; }</style>`,\n }) as unknown as z.ZodString;\n\n/**\n * @wildo_source:part:start saas.website.design-tokens.colors facet:layer:shared facet:family:website\n *\n * `WebsiteColorTokens` — minimal palette the marketing-site exposes as\n * CSS custom properties (`--website-colors-<token>`, the key verbatim, so\n * `surfaceForeground` → `--website-colors-surfaceForeground`) on `:root`.\n * Reference one from code through `websiteDesignTokenCssVar('colors', …)`\n * rather than spelling the name.\n *\n * **Why a dedicated marketing palette** (vs. reusing\n * `presets-components-models/design-tokens-config`):\n *\n * - The SaaS app's design tokens carry many semantic states (`muted`,\n * `destructive`, `ring`, `sidebar-*`, …) that have no marketing-site\n * analogue. Forcing the website to consume the full palette would\n * leak app-internal semantics into a public surface.\n * - Marketing pages need *display-grade* colors (heavily branded\n * accents, gradient stops, hero-overlay tints) that the SaaS app's\n * functional palette deliberately avoids.\n * - Designers overriding website tokens (Phase 6 dogfood proves this\n * path) should not be able to break the SaaS app by editing one\n * tailwind.config — the two surfaces stay decoupled.\n *\n * **Token format**: every value is a CSS color string. Shape validation\n * is intentionally permissive (accepts hex, rgb/rgba, hsl/hsla, oklch,\n * `currentColor`, named colors, CSS variable references) — a strict\n * color-string regex would reject legitimate values (oklch grammar\n * alone is ~80 lines) and a malformed-but-syntactically-valid color\n * fails loudly at render time.\n *\n * **What IS strictly enforced**: every value passes `cssSafeValue`,\n * which rejects the six characters that can break out of a CSS\n * property declaration (`;`, `{`, `}`, `/*`, `*\\/`, newline). See the\n * `cssSafeValue` JSDoc above for the full threat model.\n *\n * **Adding a token**: append the field here using `cssSafeValue(...)`,\n * document its semantic intent in the JSDoc. `renderWebsiteDesignTokensCss`\n * (from `@wildo-ai/saas-website/astro`) emits every key generically, so no\n * emitter needs editing.\n */\nconst _WebsiteColorTokensSchema = z.object({\n background: cssSafeValue('background color'),\n foreground: cssSafeValue('foreground color'),\n primary: cssSafeValue('primary color'),\n primaryForeground: cssSafeValue('primaryForeground color'),\n accent: cssSafeValue('accent color'),\n accentForeground: cssSafeValue('accentForeground color'),\n surface: cssSafeValue('surface color'),\n surfaceForeground: cssSafeValue('surfaceForeground color'),\n border: cssSafeValue('border color'),\n muted: cssSafeValue('muted color'),\n mutedForeground: cssSafeValue('mutedForeground color'),\n});\n\nexport const WebsiteColorTokensSchema: typeof _WebsiteColorTokensSchema = _WebsiteColorTokensSchema;\n\nexport type WebsiteColorTokens = z.infer<typeof _WebsiteColorTokensSchema>;\n/** @wildo_source:part:end saas.website.design-tokens.colors */\n\n/**\n * @wildo_source:part:start saas.website.design-tokens.radius facet:layer:shared facet:family:website\n *\n * `WebsiteRadiusTokens` — border-radius scale exposed as\n * `--website-radii-<token>` CSS variables.\n *\n * Marketing radii are intentionally a separate token set from the\n * SaaS app's radii: the website typically wants larger pill / hero\n * radii than the app's compact-UI radii. Keeping them separate lets\n * either surface evolve without coupling.\n *\n * Values are CSS length strings (`'0.25rem'`, `'12px'`,\n * `'9999px'`). Same permissive-shape + strict-CSS-injection\n * rationale as `WebsiteColorTokens` (every value runs through\n * `cssSafeValue`).\n */\nconst _WebsiteRadiusTokensSchema = z.object({\n none: cssSafeValue('none radius'),\n sm: cssSafeValue('sm radius'),\n md: cssSafeValue('md radius'),\n lg: cssSafeValue('lg radius'),\n xl: cssSafeValue('xl radius'),\n full: cssSafeValue('full radius'),\n});\n\nexport const WebsiteRadiusTokensSchema: typeof _WebsiteRadiusTokensSchema = _WebsiteRadiusTokensSchema;\n\nexport type WebsiteRadiusTokens = z.infer<typeof _WebsiteRadiusTokensSchema>;\n/** @wildo_source:part:end saas.website.design-tokens.radius */\n\n/**\n * @wildo_source:part:start saas.website.design-tokens.bundle facet:layer:shared facet:family:website\n *\n * `WebsiteDesignTokens` — the complete design-token bundle attached to\n * `WebsiteRootConfig.designTokens`. Currently colors + radii only\n * (per plan Phase 1 scope: \"WebsiteColorTokensSchema + WebsiteRadiusTokensSchema (only)\").\n *\n * Future expansions (typography scale, motion tokens, shadow elevation)\n * will land as additional fields here without breaking the\n * `WebsiteRootConfig` shape.\n */\nconst _WebsiteDesignTokensSchema = z.object({\n colors: _WebsiteColorTokensSchema,\n radii: _WebsiteRadiusTokensSchema,\n});\n\nexport const WebsiteDesignTokensSchema: typeof _WebsiteDesignTokensSchema = _WebsiteDesignTokensSchema;\n\nexport type WebsiteDesignTokens = z.infer<typeof _WebsiteDesignTokensSchema>;\n/** @wildo_source:part:end saas.website.design-tokens.bundle */\n\n/**\n * The CSS custom-property NAME one design token is emitted under —\n * `--website-<group>-<token>`, with the token key verbatim (no case\n * conversion), so `('colors', 'surfaceForeground')` is\n * `--website-colors-surfaceForeground`.\n *\n * This is the one place the naming rule lives. `renderWebsiteDesignTokensCss`\n * emits through it, and every engine component that reads a token reads\n * through `websiteDesignTokenCssVar`. It exists because the rule used to be\n * spelled by hand at each reader, and one reader spelled it\n * `surface-foreground`: the consent banner's text colour silently fell back to\n * `#000` on every site, whatever palette the application authored.\n */\nexport function websiteDesignTokenCssProperty(group: string, token: string): string {\n return `--website-${group}-${token}`;\n}\n\n/**\n * A `var(...)` reference to one design token, typed against the token bundle\n * so a misspelt group or key does not compile.\n *\n * @example\n * ```ts\n * import { websiteDesignTokenCssVar } from '@wildo-ai/saas-website';\n * const style = { color: websiteDesignTokenCssVar('colors', 'surfaceForeground', '#000') };\n * // → 'var(--website-colors-surfaceForeground, #000)'\n * ```\n */\nexport function websiteDesignTokenCssVar<G extends keyof WebsiteDesignTokens>(\n group: G,\n token: keyof WebsiteDesignTokens[G] & string,\n fallback?: string,\n): string {\n const property = websiteDesignTokenCssProperty(group, token);\n return fallback === undefined ? `var(${property})` : `var(${property}, ${fallback})`;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"website-consent-label-keys.schemas.d.ts","sourceRoot":"","sources":["../../../../src/schemas/label-keys/website-consent-label-keys.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAoB,MAAM,sCAAsC,CAAC;AAExF,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAE1F;;;;;;;;;;;;;;GAcG;AACH,oBAAY,uBAAuB;IACjC,oCAAoC;IACpC,KAAK,UAAU;IACf,+GAA+G;IAC/G,WAAW,gBAAgB;IAC3B,qCAAqC;IACrC,UAAU,eAAe;IACzB,kFAAkF;IAClF,WAAW,gBAAgB;IAC3B,wCAAwC;IACxC,IAAI,SAAS;IACb,yDAAyD;IACzD,MAAM,WAAW;IACjB;;;;;;;OAOG;IACH,eAAe,oBAAoB;IACnC,+FAA+F;IAC/F,qBAAqB,0BAA0B;CAChD;AAID,iDAAiD;AACjD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,uBAAuB,GAAG,eAAe,CAErF;AAED,yDAAyD;AACzD,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,cAAc,GAAG,eAAe,CAEtF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wCAAwC,CAAC,OAAO,EAAE,cAAc,GAAG,eAAe,CAEjG;AAED,uGAAuG;AACvG,eAAO,MAAM,0BAA0B,EAAE,SAAS,eAAe,EAI/D,CAAC"}
1
+ {"version":3,"file":"website-consent-label-keys.schemas.d.ts","sourceRoot":"","sources":["../../../../../src/schemas/label-keys/website-consent-label-keys.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAoB,MAAM,sCAAsC,CAAC;AAExF,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAE1F;;;;;;;;;;;;;;GAcG;AACH,oBAAY,uBAAuB;IACjC,oCAAoC;IACpC,KAAK,UAAU;IACf,+GAA+G;IAC/G,WAAW,gBAAgB;IAC3B,qCAAqC;IACrC,UAAU,eAAe;IACzB,kFAAkF;IAClF,WAAW,gBAAgB;IAC3B,wCAAwC;IACxC,IAAI,SAAS;IACb,yDAAyD;IACzD,MAAM,WAAW;IACjB;;;;;;;OAOG;IACH,eAAe,oBAAoB;IACnC,+FAA+F;IAC/F,qBAAqB,0BAA0B;CAChD;AAID,iDAAiD;AACjD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,uBAAuB,GAAG,eAAe,CAErF;AAED,yDAAyD;AACzD,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,cAAc,GAAG,eAAe,CAEtF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wCAAwC,CAAC,OAAO,EAAE,cAAc,GAAG,eAAe,CAEjG;AAED,uGAAuG;AACvG,eAAO,MAAM,0BAA0B,EAAE,SAAS,eAAe,EAI/D,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"website-label-key.schemas.d.ts","sourceRoot":"","sources":["../../../../src/schemas/label-keys/website-label-key.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,QAAA,MAAM,sBAAsB,2DAWC,CAAC;AAE9B,eAAO,MAAM,qBAAqB,EAAE,OAAO,sBAA+C,CAAC;AAE3F,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACrE,4DAA4D;AAE5D;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,eAAe,CAElB;;AACD,oEAAoE"}
1
+ {"version":3,"file":"website-label-key.schemas.d.ts","sourceRoot":"","sources":["../../../../../src/schemas/label-keys/website-label-key.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,QAAA,MAAM,sBAAsB,2DAWC,CAAC;AAE9B,eAAO,MAAM,qBAAqB,EAAE,OAAO,sBAA+C,CAAC;AAE3F,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACrE,4DAA4D;AAE5D;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,eAAe,CAElB;;AACD,oEAAoE"}
@@ -1 +1 @@
1
- {"version":3,"file":"website-provider-component-label-keys.schemas.d.ts","sourceRoot":"","sources":["../../../../src/schemas/label-keys/website-provider-component-label-keys.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAE1F;;;;;;;;;;;;;GAaG;AACH,oBAAY,iCAAiC;IAC3C;;;OAGG;IACH,gBAAgB,qBAAqB;IACrC;;;OAGG;IACH,WAAW,gBAAgB;CAC5B;AAID,iDAAiD;AACjD,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,iCAAiC,GAAG,eAAe,CAEzG;AAED,+GAA+G;AAC/G,eAAO,MAAM,qCAAqC,EAAE,SAAS,eAAe,EAE3E,CAAC"}
1
+ {"version":3,"file":"website-provider-component-label-keys.schemas.d.ts","sourceRoot":"","sources":["../../../../../src/schemas/label-keys/website-provider-component-label-keys.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAE1F;;;;;;;;;;;;;GAaG;AACH,oBAAY,iCAAiC;IAC3C;;;OAGG;IACH,gBAAgB,qBAAqB;IACrC;;;OAGG;IACH,WAAW,gBAAgB;CAC5B;AAID,iDAAiD;AACjD,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,iCAAiC,GAAG,eAAe,CAEzG;AAED,+GAA+G;AAC/G,eAAO,MAAM,qCAAqC,EAAE,SAAS,eAAe,EAE3E,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"website-provider-component-label-keys.schemas.js","sourceRoot":"","sources":["../../../../src/schemas/label-keys/website-provider-component-label-keys.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAwB,MAAM,6BAA6B,CAAC;AAE1F;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAN,IAAY,iCAWX;AAXD,WAAY,iCAAiC;IAC3C;;;OAGG;IACH,0EAAqC,CAAA;IACrC;;;OAGG;IACH,gEAA2B,CAAA;AAC7B,CAAC,EAXW,iCAAiC,KAAjC,iCAAiC,QAW5C;AAED,MAAM,mCAAmC,GAAG,mCAAmC,CAAC;AAEhF,iDAAiD;AACjD,MAAM,UAAU,gCAAgC,CAAC,IAAuC;IACtF,OAAO,qBAAqB,CAAC,KAAK,CAAC,GAAG,mCAAmC,IAAI,IAAI,EAAE,CAAC,CAAC;AACvF,CAAC;AAED,+GAA+G;AAC/G,MAAM,CAAC,MAAM,qCAAqC,GAA+B,MAAM,CAAC,MAAM,CAC5F,MAAM,CAAC,MAAM,CAAC,iCAAiC,CAAC,CAAC,GAAG,CAAC,gCAAgC,CAAC,CACvF,CAAC","sourcesContent":["import { WebsiteLabelKeySchema, type WebsiteLabelKey } from './website-label-key.schemas';\n\n/**\n * The label keys the framework's provider-component slot reads (#1703).\n *\n * FRAMEWORK-OWNED, like the consent panel's keys: `WebsiteProviderComponent` renders these states,\n * not the site's chrome, so the site does not list them in `chromeLabelKeys`.\n * `composeEffectiveChromeLabelKeys` unions them into the label-pack validator's expected set, so a\n * locale pack missing one fails the build rather than showing a visitor a raw key.\n *\n * Required of every site, including one that places no provider component today, for the reason the\n * consent keys are: the words are cheap, and a site that adds an address lookup tomorrow must not\n * ship it with an unlabelled refusal. The consent-required state also names each missing purpose and\n * offers the panel, and it reuses the consent keys for both (`purpose-<purpose>`, `manage`) rather\n * than a second wording of the same thing.\n */\nexport enum WebsiteProviderComponentLabelSlot {\n /**\n * Shown where a provider component would be when the visitor has not agreed to a purpose it rests\n * on. Says the feature is off until they agree; the purposes and the way to agree follow it.\n */\n CONSENT_REQUIRED = 'consent-required',\n /**\n * Shown when the component could not be loaded (a network failure, a blocked script). Says the\n * feature is unavailable right now, without blaming the visitor.\n */\n UNAVAILABLE = 'unavailable',\n}\n\nconst PROVIDER_COMPONENT_LABEL_KEY_PREFIX = 'website.chrome.provider-component';\n\n/** The fully-composed key for one slot state. */\nexport function websiteProviderComponentLabelKey(slot: WebsiteProviderComponentLabelSlot): WebsiteLabelKey {\n return WebsiteLabelKeySchema.parse(`${PROVIDER_COMPONENT_LABEL_KEY_PREFIX}.${slot}`);\n}\n\n/** Every framework-owned provider-component key — what a locale pack must carry. Total over the vocabulary. */\nexport const WEBSITE_PROVIDER_COMPONENT_LABEL_KEYS: readonly WebsiteLabelKey[] = Object.freeze(\n Object.values(WebsiteProviderComponentLabelSlot).map(websiteProviderComponentLabelKey),\n);\n"]}
1
+ {"version":3,"file":"website-provider-component-label-keys.schemas.js","sourceRoot":"","sources":["../../../../../src/schemas/label-keys/website-provider-component-label-keys.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAwB,MAAM,6BAA6B,CAAC;AAE1F;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAN,IAAY,iCAWX;AAXD,WAAY,iCAAiC;IAC3C;;;OAGG;IACH,0EAAqC,CAAA;IACrC;;;OAGG;IACH,gEAA2B,CAAA;AAC7B,CAAC,EAXW,iCAAiC,KAAjC,iCAAiC,QAW5C;AAED,MAAM,mCAAmC,GAAG,mCAAmC,CAAC;AAEhF,iDAAiD;AACjD,MAAM,UAAU,gCAAgC,CAAC,IAAuC;IACtF,OAAO,qBAAqB,CAAC,KAAK,CAAC,GAAG,mCAAmC,IAAI,IAAI,EAAE,CAAC,CAAC;AACvF,CAAC;AAED,+GAA+G;AAC/G,MAAM,CAAC,MAAM,qCAAqC,GAA+B,MAAM,CAAC,MAAM,CAC5F,MAAM,CAAC,MAAM,CAAC,iCAAiC,CAAC,CAAC,GAAG,CAAC,gCAAgC,CAAC,CACvF,CAAC","sourcesContent":["import { WebsiteLabelKeySchema, type WebsiteLabelKey } from './website-label-key.schemas';\n\n/**\n * The label keys the framework's provider-component slot reads (#1703).\n *\n * FRAMEWORK-OWNED, like the consent panel's keys: `WebsiteProviderComponent` renders these states,\n * not the site's chrome, so the site does not list them in `chromeLabelKeys`.\n * `composeEffectiveChromeLabelKeys` unions them into the label-pack validator's expected set, so a\n * locale pack missing one fails the build rather than showing a visitor a raw key.\n *\n * Required of every site, including one that places no provider component today, for the reason the\n * consent keys are: the words are cheap, and a site that adds an address lookup tomorrow must not\n * ship it with an unlabelled refusal. The consent-required state also names each missing purpose and\n * offers the panel, and it reuses the consent keys for both (`purpose-<purpose>`, `manage`) rather\n * than a second wording of the same thing.\n */\nexport enum WebsiteProviderComponentLabelSlot {\n /**\n * Shown where a provider component would be when the visitor has not agreed to a purpose it rests\n * on. Says the feature is off until they agree; the purposes and the way to agree follow it.\n */\n CONSENT_REQUIRED = 'consent-required',\n /**\n * Shown when the component could not be loaded (a network failure, a blocked script). Says the\n * feature is unavailable right now, without blaming the visitor.\n */\n UNAVAILABLE = 'unavailable',\n}\n\nconst PROVIDER_COMPONENT_LABEL_KEY_PREFIX = 'website.chrome.provider-component';\n\n/** The fully-composed key for one slot state. */\nexport function websiteProviderComponentLabelKey(slot: WebsiteProviderComponentLabelSlot): WebsiteLabelKey {\n return WebsiteLabelKeySchema.parse(`${PROVIDER_COMPONENT_LABEL_KEY_PREFIX}.${slot}`);\n}\n\n/** Every framework-owned provider-component key — what a locale pack must carry. Total over the vocabulary. */\nexport const WEBSITE_PROVIDER_COMPONENT_LABEL_KEYS: readonly WebsiteLabelKey[] = Object.freeze(\n Object.values(WebsiteProviderComponentLabelSlot).map(websiteProviderComponentLabelKey),\n);\n"]}
@@ -117,6 +117,8 @@ export type WebsitePageRoutePath = z.infer<typeof _WebsitePageRoutePathSchema>;
117
117
  * not match the directive in its `.astro` file.
118
118
  *
119
119
  * **Refinements**:
120
+ * - `sectionRefs` MAY be empty — a page whose sections are still being authored. It may be
121
+ * served by a dev server and may not be built: see `assertWebsitePagesHaveSections`.
120
122
  * - `sectionRefs` MUST contain unique values. Duplicates indicate
121
123
  * either a copy-paste bug (two sections pointing at the same
122
124
  * label-key root) or an intent to share a label-key root that
@@ -1 +1 @@
1
- {"version":3,"file":"website-page-manifest.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../src/schemas/manifests/website-page-manifest.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAC3C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,QAAA,MAAM,+BAA+B;;;;iBAInC,CAAC;AAEH,eAAO,MAAM,8BAA8B,EAAE,OAAO,+BACnB,CAAC;AAElC,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AACvF,wEAAwE;AAExE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,QAAA,MAAM,2BAA2B,gEAWC,CAAC;AAEnC,eAAO,MAAM,0BAA0B,EAAE,OAAO,2BACnB,CAAC;AAE9B,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAC/E,mEAAmE;AAEnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AACH,oBAAY,4BAA4B;IACtC,+DAA+D;IAC/D,IAAI,SAAS;IACb,gEAAgE;IAChE,KAAK,UAAU;CAChB;AAED,eAAO,MAAM,kCAAkC,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,4BAA4B,CACxD,CAAC;AAEvC;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,QAAA,MAAM,2BAA2B;;iBAE/B,CAAC;AAEH,eAAO,MAAM,0BAA0B,EAAE,OAAO,2BACnB,CAAC;AAE9B,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAC/E,4DAA4D;AAE5D,QAAA,MAAM,0BAA0B;;;;;;;;;;;;;;iBAuC5B,CAAC;AAEL,eAAO,MAAM,yBAAyB,EAAE,OAAO,0BACnB,CAAC;AAE7B;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,mBAAoB,SAAQ,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC;IACrF,aAAa,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC;CACnC;;AACD,+DAA+D"}
1
+ {"version":3,"file":"website-page-manifest.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../../src/schemas/manifests/website-page-manifest.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAC3C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,QAAA,MAAM,+BAA+B;;;;iBAInC,CAAC;AAEH,eAAO,MAAM,8BAA8B,EAAE,OAAO,+BACnB,CAAC;AAElC,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AACvF,wEAAwE;AAExE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,QAAA,MAAM,2BAA2B,gEAWC,CAAC;AAEnC,eAAO,MAAM,0BAA0B,EAAE,OAAO,2BACnB,CAAC;AAE9B,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAC/E,mEAAmE;AAEnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;AACH,oBAAY,4BAA4B;IACtC,+DAA+D;IAC/D,IAAI,SAAS;IACb,gEAAgE;IAChE,KAAK,UAAU;CAChB;AAED,eAAO,MAAM,kCAAkC,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,4BAA4B,CACxD,CAAC;AAEvC;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,QAAA,MAAM,2BAA2B;;iBAE/B,CAAC;AAEH,eAAO,MAAM,0BAA0B,EAAE,OAAO,2BACnB,CAAC;AAE9B,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAC/E,4DAA4D;AAE5D,QAAA,MAAM,0BAA0B;;;;;;;;;;;;;;iBA8C5B,CAAC;AAEL,eAAO,MAAM,yBAAyB,EAAE,OAAO,0BACnB,CAAC;AAE7B;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,mBAAoB,SAAQ,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC;IACrF,aAAa,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC;CACnC;;AACD,+DAA+D"}
@@ -124,6 +124,8 @@ export const WebsitePageRoutePathSchema = _WebsitePageRoutePathSchema;
124
124
  * not match the directive in its `.astro` file.
125
125
  *
126
126
  * **Refinements**:
127
+ * - `sectionRefs` MAY be empty — a page whose sections are still being authored. It may be
128
+ * served by a dev server and may not be built: see `assertWebsitePagesHaveSections`.
127
129
  * - `sectionRefs` MUST contain unique values. Duplicates indicate
128
130
  * either a copy-paste bug (two sections pointing at the same
129
131
  * label-key root) or an intent to share a label-key root that
@@ -179,9 +181,16 @@ const _WebsitePageManifestSchema = z
179
181
  ref: WebsitePageRefSchema,
180
182
  routePath: _WebsitePageRoutePathSchema,
181
183
  pageComponent: z.unknown(),
182
- sectionRefs: z.array(WebsiteSectionRefSchema).min(1, {
183
- error: 'A page manifest must declare at least one sectionRef',
184
- }),
184
+ /*
185
+ * EMPTY is a valid manifest (#2205): a page whose sections are not authored YET. The build lane
186
+ * creates that state on purpose (the page task writes the empty list, then each section task
187
+ * appends itself), and refusing it here made `astro dev` crash at module load until every page
188
+ * of the site had a section. An empty page is unfinished, not false, so it is representable and
189
+ * a dev server renders it (`WebsitePageSections` shows the pending state). What is refused is
190
+ * BUILDING one: `assertWebsitePagesHaveSections`, run from the build-start hook, because a
191
+ * published page with nothing on it is a statement to a real reader.
192
+ */
193
+ sectionRefs: z.array(WebsiteSectionRefSchema),
185
194
  metaLabelKeys: _WebsitePageMetaLabelKeysSchema,
186
195
  hydration: WebsitePageHydrationStrategySchema.optional(),
187
196
  seo: _WebsitePageSeoConfigSchema.optional(),
@@ -1 +1 @@
1
- {"version":3,"file":"website-page-manifest.shared.schemas.js","sourceRoot":"","sources":["../../../../../src/schemas/manifests/website-page-manifest.shared.schemas.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ComponentType } from 'react';\nimport { z } from 'zod';\n\nimport { WebsiteLabelKeySchema } from '../label-keys/website-label-key.schemas';\nimport { WebsitePageRefSchema } from '../refs/page-ref.schemas';\nimport { WebsiteSectionRefSchema } from '../refs/section-ref.schemas';\n\n/**\n * @wildo_source:part:start saas.website.page-manifest.meta-label-keys facet:layer:shared facet:family:website\n *\n * `WebsitePageMetaLabelKeys` — the i18n keys the Astro adapter (Phase 5)\n * resolves at static-build time to populate `<title>` / `<meta>` /\n * Open Graph tags for the page.\n *\n * - `titleKey` — REQUIRED. Renders into `<title>` AND `og:title`. The\n * manifest commits to declaring this key in every locale's label\n * pack; the validator surfaces missing entries at build start.\n * - `descriptionKey` — REQUIRED. Renders into `<meta name=\"description\">`,\n * `og:description`, and `twitter:description`. Same per-locale\n * commitment.\n * - `ogImageUrl` — OPTIONAL. Static URL (typically a per-locale CDN\n * path) for `og:image` / `twitter:image`. Not a label key because\n * marketing teams overwhelmingly ship one localized asset URL per\n * page rather than authoring image URLs in label packs. When\n * per-locale images are needed, the consumer supplies a function\n * in their site-root config that resolves locale → URL (Phase 5).\n *\n * **Why these three only**: the JSON-LD / canonical / `hreflang` /\n * robots tags are NOT label-key-driven — they are derived\n * mechanically from the manifest (`routePath`), the root config\n * (`locales`, `defaultLocale`), and the section-level\n * `getStructuredData()` hooks. Mixing them into the meta-label-keys\n * shape would conflate human-authored copy with structural metadata.\n */\nconst _WebsitePageMetaLabelKeysSchema = z.object({\n titleKey: WebsiteLabelKeySchema,\n descriptionKey: WebsiteLabelKeySchema,\n ogImageUrl: z.url({ error: 'ogImageUrl must be a valid URL' }).optional(),\n});\n\nexport const WebsitePageMetaLabelKeysSchema: typeof _WebsitePageMetaLabelKeysSchema =\n _WebsitePageMetaLabelKeysSchema;\n\nexport type WebsitePageMetaLabelKeys = z.infer<typeof _WebsitePageMetaLabelKeysSchema>;\n/** @wildo_source:part:end saas.website.page-manifest.meta-label-keys */\n\n/**\n * @wildo_source:part:start saas.website.page-manifest.route-path facet:layer:shared facet:family:website\n *\n * `WebsitePageRoutePath` — the URL path the page mounts at (locale\n * prefix stripped).\n *\n * **Format**:\n * - MUST start with `/`\n * - MAY be the root (`'/'`) for the landing page\n * - segments are lowercase kebab-case\n * - parameter segments use Astro's `[param]` convention\n * (e.g. `'/blog/[slug]'`, `'/case-studies/[slug]'`)\n *\n * The Astro adapter (Phase 5) prepends the locale prefix\n * (`/en`, `/fr`, …) when generating routes. The manifest stores the\n * locale-neutral path because the same `pageComponent` renders for\n * every locale.\n *\n * **Brand**: prevents accidental cross-talk with absolute URLs\n * (`https://…`), with locale-prefixed paths (`/en/pricing`), and with\n * arbitrary string props in the manifest factory.\n */\nconst _WebsitePageRoutePathSchema = z\n .string()\n .min(1, { error: 'routePath must not be empty' })\n .max(256, { error: 'routePath must be at most 256 characters' })\n .regex(\n /^\\/(?:[a-z0-9][a-z0-9-]*(?:\\/[a-z0-9-]+|\\/\\[[a-z][a-zA-Z0-9]*\\])*)?\\/?$/,\n {\n error:\n 'routePath must start with `/`, use lowercase kebab-case segments, and may include Astro `[param]` segments (e.g. \"/\", \"/pricing\", \"/blog/[slug]\")',\n },\n )\n .brand<'WebsitePageRoutePath'>();\n\nexport const WebsitePageRoutePathSchema: typeof _WebsitePageRoutePathSchema =\n _WebsitePageRoutePathSchema;\n\nexport type WebsitePageRoutePath = z.infer<typeof _WebsitePageRoutePathSchema>;\n/** @wildo_source:part:end saas.website.page-manifest.route-path */\n\n/**\n * @wildo_source:part:start saas.website.page-manifest.schema facet:layer:shared facet:family:website\n *\n * `WebsitePageManifest` — the registration-time descriptor for one\n * marketing-site page.\n *\n * **Authoring path**: each page-impl module exports both a\n * `<PageName>Page` React component AND a `<PAGE_NAME>_PAGE_MANIFEST`\n * literal. The manifest is collected into `WebsiteRootConfig.pageManifests[]`;\n * the Astro page file (`src/pages/<page>.astro`) imports the manifest\n * and hands it to `<WebsitePageBridge manifest={…}>` (Phase 5).\n *\n * **Field-by-field rationale** (semantic-weight discipline):\n *\n * - `ref` — the page's structural identity. Used for label-key\n * derivation (`website.<ref>.<sectionRef>.<slot>`), for\n * cross-page navigation lookup, and as the manifest registry key.\n * - `routePath` — the locale-neutral URL path. Decoupled from `ref`\n * because the same page (e.g. `ref: 'pricing'`) may mount at a\n * localized path (`/tarifs` in French) — the locale-aware path\n * resolution is a Phase 5 concern; this field is the canonical\n * English-language path.\n * - `pageComponent` — the React component the bridge renders inside\n * `<WebsitePageContext value={{ pageRef: ref }}>`. Type relaxed to\n * `unknown` at the schema layer for the same reasons as\n * `WebsiteSectionDefinition.Component`; the manifest factory\n * (`defineWebsitePageManifest`, Phase 2) preserves precise typing\n * at the consumer call site.\n * - `sectionRefs` — the contractual list of section refs the page\n * declares. Used by the label-pack validator to cross-reference\n * `expectedLabelKeys` collected from each section against the\n * page's commitment. **Order matters** for the validator's\n * diagnostic output (sections are reported in declaration order)\n * but does NOT control render order — render order is whatever the\n * `pageComponent` JSX lays out.\n * - `metaLabelKeys` — see `WebsitePageMetaLabelKeysSchema`.\n * - `hydration` — opt-in flag. `'lazy'` (default) corresponds to\n * `client:idle` in the Astro adapter (deferred hydration); `'eager'`\n * corresponds to `client:load` (immediate hydration). The page\n * author flips this to `'eager'` only when the page hosts\n * above-the-fold interactive sections (e.g. a contact form).\n *\n * **Important — informational, NOT auto-wired**: Astro's\n * `client:*` directives MUST resolve to a literal token at compile\n * time (the Astro compiler reads the directive from the JSX source\n * to decide which hydration helper to bundle); a runtime-computed\n * value will not work. The framework therefore CANNOT translate\n * this field into the directive on the consumer's behalf — the\n * manifest field is the single source of truth a consumer's\n * per-page `.astro` template should USE to pick the matching\n * `client:idle` / `client:load` directive by hand. Keeping the two\n * in lock-step is a discipline obligation; the audit CLI (Phase 7)\n * will surface a drift warning when a page's hydration field does\n * not match the directive in its `.astro` file.\n *\n * **Refinements**:\n * - `sectionRefs` MUST contain unique values. Duplicates indicate\n * either a copy-paste bug (two sections pointing at the same\n * label-key root) or an intent to share a label-key root that\n * the framework rejects to keep the per-page validation\n * unambiguous (consumers who need a shared label-key root\n * across two distinct sections should pull the shared content\n * to the page level via `WebsiteRootConfig`-level globals,\n * Phase 2.5).\n */\nexport enum WebsitePageHydrationStrategy {\n /** `client:idle` in the Astro adapter (deferred hydration). */\n LAZY = 'lazy',\n /** `client:load` in the Astro adapter (immediate hydration). */\n EAGER = 'eager',\n}\n\nexport const WebsitePageHydrationStrategySchema: z.ZodEnum<typeof WebsitePageHydrationStrategy> =\n z.enum(WebsitePageHydrationStrategy);\n\n/**\n * @wildo_source:part:start saas.website.page-manifest.seo facet:layer:shared facet:family:website\n *\n * `WebsitePageSeoConfig` — per-page SEO/AEO/GEO knobs.\n *\n * The marketing-site default is to be FULLY discoverable by every\n * crawler (search engines AND AI training crawlers). This block holds\n * the per-page opt-out levers that flip a page out of that default.\n *\n * **Field rationale**:\n *\n * - `noaiOptOut?` — when `true`, the page emits\n * `<meta name=\"robots\" content=\"noai, noimageai\">` AND a sibling\n * `_headers` file entry signalling `X-Robots-Tag: noai, noimageai`\n * for crawlers that don't honor in-document meta. ONE knob drives\n * BOTH channels (`headers-renderer.ts` reads the same flag) — the\n * framework owns the dual emission so consumers can't accidentally\n * set one and forget the other.\n *\n * **Why default-IN to AI training (D-AG3)**: the marketing-site\n * default is `false` (consent to AI training). Marketing pages\n * benefit from AI-engine citation; opting out is a per-page\n * editorial decision (typically pricing pages — to avoid stale\n * price training; or legal/internal pages — to avoid free-text\n * reproduction of contractual language). Per-blog-post opt-out\n * would mirror this knob on a sibling blog-post manifest schema\n * if that need materializes.\n */\nconst _WebsitePageSeoConfigSchema = z.object({\n noaiOptOut: z.boolean().optional(),\n});\n\nexport const WebsitePageSeoConfigSchema: typeof _WebsitePageSeoConfigSchema =\n _WebsitePageSeoConfigSchema;\n\nexport type WebsitePageSeoConfig = z.infer<typeof _WebsitePageSeoConfigSchema>;\n/** @wildo_source:part:end saas.website.page-manifest.seo */\n\nconst _WebsitePageManifestSchema = z\n .object({\n ref: WebsitePageRefSchema,\n routePath: _WebsitePageRoutePathSchema,\n pageComponent: z.unknown(),\n sectionRefs: z.array(WebsiteSectionRefSchema).min(1, {\n error: 'A page manifest must declare at least one sectionRef',\n }),\n metaLabelKeys: _WebsitePageMetaLabelKeysSchema,\n hydration: WebsitePageHydrationStrategySchema.optional(),\n seo: _WebsitePageSeoConfigSchema.optional(),\n })\n .superRefine((manifest, ctx) => {\n const seen = new Set<string>();\n for (const [index, sectionRef] of manifest.sectionRefs.entries()) {\n const key = sectionRef as unknown as string;\n if (seen.has(key)) {\n ctx.addIssue({\n code: 'custom',\n path: ['sectionRefs', index],\n message: `Duplicate sectionRef \"${key}\" in page manifest \"${manifest.ref as unknown as string}\"`,\n });\n }\n seen.add(key);\n }\n\n const expectedPagePrefix = `website.${manifest.ref as unknown as string}.`;\n for (const [field, key] of [\n ['titleKey', manifest.metaLabelKeys.titleKey] as const,\n ['descriptionKey', manifest.metaLabelKeys.descriptionKey] as const,\n ]) {\n if (!(key as unknown as string).startsWith(expectedPagePrefix)) {\n ctx.addIssue({\n code: 'custom',\n path: ['metaLabelKeys', field],\n message: `metaLabelKeys.${field} (\"${key as unknown as string}\") must be rooted under the page ref \"${manifest.ref as unknown as string}\" (expected prefix \"${expectedPagePrefix}\")`,\n });\n }\n }\n });\n\nexport const WebsitePageManifestSchema: typeof _WebsitePageManifestSchema =\n _WebsitePageManifestSchema;\n\n/**\n * Runtime shape of `WebsitePageManifest`.\n *\n * **Why `ComponentType<any>` and NOT `ComponentType<unknown>`** —\n * same variance reasoning as `WebsiteSectionDefinition.Component`:\n * `ComponentType<TProps>` is CONTRAVARIANT in `TProps`, so a\n * narrower `TypedWebsitePageManifest<TProps>` (returned by\n * `defineWebsitePageManifest<TProps>`) was not assignable to a\n * `WebsitePageManifest` typed with `ComponentType<unknown>`. That\n * blocked every consumer call-site that passed a typed manifest to\n * helpers / registries / validators expecting the bare type\n * (root config's `pageManifests[]`, the Astro adapter's\n * `loadWebsitePageRuntime`, and the head-renderer). Using `any`\n * matches the React-canonical pattern for heterogeneous component\n * containers and reflects the runtime contract: the framework only\n * RENDERS these components — it never constructs ad-hoc props for\n * them; the consumer's JSX call site supplies the precise props.\n */\nexport interface WebsitePageManifest extends z.infer<typeof _WebsitePageManifestSchema> {\n pageComponent: ComponentType<any>;\n}\n/** @wildo_source:part:end saas.website.page-manifest.schema */\n"]}
1
+ {"version":3,"file":"website-page-manifest.shared.schemas.js","sourceRoot":"","sources":["../../../../../src/schemas/manifests/website-page-manifest.shared.schemas.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ComponentType } from 'react';\nimport { z } from 'zod';\n\nimport { WebsiteLabelKeySchema } from '../label-keys/website-label-key.schemas';\nimport { WebsitePageRefSchema } from '../refs/page-ref.schemas';\nimport { WebsiteSectionRefSchema } from '../refs/section-ref.schemas';\n\n/**\n * @wildo_source:part:start saas.website.page-manifest.meta-label-keys facet:layer:shared facet:family:website\n *\n * `WebsitePageMetaLabelKeys` — the i18n keys the Astro adapter (Phase 5)\n * resolves at static-build time to populate `<title>` / `<meta>` /\n * Open Graph tags for the page.\n *\n * - `titleKey` — REQUIRED. Renders into `<title>` AND `og:title`. The\n * manifest commits to declaring this key in every locale's label\n * pack; the validator surfaces missing entries at build start.\n * - `descriptionKey` — REQUIRED. Renders into `<meta name=\"description\">`,\n * `og:description`, and `twitter:description`. Same per-locale\n * commitment.\n * - `ogImageUrl` — OPTIONAL. Static URL (typically a per-locale CDN\n * path) for `og:image` / `twitter:image`. Not a label key because\n * marketing teams overwhelmingly ship one localized asset URL per\n * page rather than authoring image URLs in label packs. When\n * per-locale images are needed, the consumer supplies a function\n * in their site-root config that resolves locale → URL (Phase 5).\n *\n * **Why these three only**: the JSON-LD / canonical / `hreflang` /\n * robots tags are NOT label-key-driven — they are derived\n * mechanically from the manifest (`routePath`), the root config\n * (`locales`, `defaultLocale`), and the section-level\n * `getStructuredData()` hooks. Mixing them into the meta-label-keys\n * shape would conflate human-authored copy with structural metadata.\n */\nconst _WebsitePageMetaLabelKeysSchema = z.object({\n titleKey: WebsiteLabelKeySchema,\n descriptionKey: WebsiteLabelKeySchema,\n ogImageUrl: z.url({ error: 'ogImageUrl must be a valid URL' }).optional(),\n});\n\nexport const WebsitePageMetaLabelKeysSchema: typeof _WebsitePageMetaLabelKeysSchema =\n _WebsitePageMetaLabelKeysSchema;\n\nexport type WebsitePageMetaLabelKeys = z.infer<typeof _WebsitePageMetaLabelKeysSchema>;\n/** @wildo_source:part:end saas.website.page-manifest.meta-label-keys */\n\n/**\n * @wildo_source:part:start saas.website.page-manifest.route-path facet:layer:shared facet:family:website\n *\n * `WebsitePageRoutePath` — the URL path the page mounts at (locale\n * prefix stripped).\n *\n * **Format**:\n * - MUST start with `/`\n * - MAY be the root (`'/'`) for the landing page\n * - segments are lowercase kebab-case\n * - parameter segments use Astro's `[param]` convention\n * (e.g. `'/blog/[slug]'`, `'/case-studies/[slug]'`)\n *\n * The Astro adapter (Phase 5) prepends the locale prefix\n * (`/en`, `/fr`, …) when generating routes. The manifest stores the\n * locale-neutral path because the same `pageComponent` renders for\n * every locale.\n *\n * **Brand**: prevents accidental cross-talk with absolute URLs\n * (`https://…`), with locale-prefixed paths (`/en/pricing`), and with\n * arbitrary string props in the manifest factory.\n */\nconst _WebsitePageRoutePathSchema = z\n .string()\n .min(1, { error: 'routePath must not be empty' })\n .max(256, { error: 'routePath must be at most 256 characters' })\n .regex(\n /^\\/(?:[a-z0-9][a-z0-9-]*(?:\\/[a-z0-9-]+|\\/\\[[a-z][a-zA-Z0-9]*\\])*)?\\/?$/,\n {\n error:\n 'routePath must start with `/`, use lowercase kebab-case segments, and may include Astro `[param]` segments (e.g. \"/\", \"/pricing\", \"/blog/[slug]\")',\n },\n )\n .brand<'WebsitePageRoutePath'>();\n\nexport const WebsitePageRoutePathSchema: typeof _WebsitePageRoutePathSchema =\n _WebsitePageRoutePathSchema;\n\nexport type WebsitePageRoutePath = z.infer<typeof _WebsitePageRoutePathSchema>;\n/** @wildo_source:part:end saas.website.page-manifest.route-path */\n\n/**\n * @wildo_source:part:start saas.website.page-manifest.schema facet:layer:shared facet:family:website\n *\n * `WebsitePageManifest` — the registration-time descriptor for one\n * marketing-site page.\n *\n * **Authoring path**: each page-impl module exports both a\n * `<PageName>Page` React component AND a `<PAGE_NAME>_PAGE_MANIFEST`\n * literal. The manifest is collected into `WebsiteRootConfig.pageManifests[]`;\n * the Astro page file (`src/pages/<page>.astro`) imports the manifest\n * and hands it to `<WebsitePageBridge manifest={…}>` (Phase 5).\n *\n * **Field-by-field rationale** (semantic-weight discipline):\n *\n * - `ref` — the page's structural identity. Used for label-key\n * derivation (`website.<ref>.<sectionRef>.<slot>`), for\n * cross-page navigation lookup, and as the manifest registry key.\n * - `routePath` — the locale-neutral URL path. Decoupled from `ref`\n * because the same page (e.g. `ref: 'pricing'`) may mount at a\n * localized path (`/tarifs` in French) — the locale-aware path\n * resolution is a Phase 5 concern; this field is the canonical\n * English-language path.\n * - `pageComponent` — the React component the bridge renders inside\n * `<WebsitePageContext value={{ pageRef: ref }}>`. Type relaxed to\n * `unknown` at the schema layer for the same reasons as\n * `WebsiteSectionDefinition.Component`; the manifest factory\n * (`defineWebsitePageManifest`, Phase 2) preserves precise typing\n * at the consumer call site.\n * - `sectionRefs` — the contractual list of section refs the page\n * declares. Used by the label-pack validator to cross-reference\n * `expectedLabelKeys` collected from each section against the\n * page's commitment. **Order matters** for the validator's\n * diagnostic output (sections are reported in declaration order)\n * but does NOT control render order — render order is whatever the\n * `pageComponent` JSX lays out.\n * - `metaLabelKeys` — see `WebsitePageMetaLabelKeysSchema`.\n * - `hydration` — opt-in flag. `'lazy'` (default) corresponds to\n * `client:idle` in the Astro adapter (deferred hydration); `'eager'`\n * corresponds to `client:load` (immediate hydration). The page\n * author flips this to `'eager'` only when the page hosts\n * above-the-fold interactive sections (e.g. a contact form).\n *\n * **Important — informational, NOT auto-wired**: Astro's\n * `client:*` directives MUST resolve to a literal token at compile\n * time (the Astro compiler reads the directive from the JSX source\n * to decide which hydration helper to bundle); a runtime-computed\n * value will not work. The framework therefore CANNOT translate\n * this field into the directive on the consumer's behalf — the\n * manifest field is the single source of truth a consumer's\n * per-page `.astro` template should USE to pick the matching\n * `client:idle` / `client:load` directive by hand. Keeping the two\n * in lock-step is a discipline obligation; the audit CLI (Phase 7)\n * will surface a drift warning when a page's hydration field does\n * not match the directive in its `.astro` file.\n *\n * **Refinements**:\n * - `sectionRefs` MAY be empty — a page whose sections are still being authored. It may be\n * served by a dev server and may not be built: see `assertWebsitePagesHaveSections`.\n * - `sectionRefs` MUST contain unique values. Duplicates indicate\n * either a copy-paste bug (two sections pointing at the same\n * label-key root) or an intent to share a label-key root that\n * the framework rejects to keep the per-page validation\n * unambiguous (consumers who need a shared label-key root\n * across two distinct sections should pull the shared content\n * to the page level via `WebsiteRootConfig`-level globals,\n * Phase 2.5).\n */\nexport enum WebsitePageHydrationStrategy {\n /** `client:idle` in the Astro adapter (deferred hydration). */\n LAZY = 'lazy',\n /** `client:load` in the Astro adapter (immediate hydration). */\n EAGER = 'eager',\n}\n\nexport const WebsitePageHydrationStrategySchema: z.ZodEnum<typeof WebsitePageHydrationStrategy> =\n z.enum(WebsitePageHydrationStrategy);\n\n/**\n * @wildo_source:part:start saas.website.page-manifest.seo facet:layer:shared facet:family:website\n *\n * `WebsitePageSeoConfig` — per-page SEO/AEO/GEO knobs.\n *\n * The marketing-site default is to be FULLY discoverable by every\n * crawler (search engines AND AI training crawlers). This block holds\n * the per-page opt-out levers that flip a page out of that default.\n *\n * **Field rationale**:\n *\n * - `noaiOptOut?` — when `true`, the page emits\n * `<meta name=\"robots\" content=\"noai, noimageai\">` AND a sibling\n * `_headers` file entry signalling `X-Robots-Tag: noai, noimageai`\n * for crawlers that don't honor in-document meta. ONE knob drives\n * BOTH channels (`headers-renderer.ts` reads the same flag) — the\n * framework owns the dual emission so consumers can't accidentally\n * set one and forget the other.\n *\n * **Why default-IN to AI training (D-AG3)**: the marketing-site\n * default is `false` (consent to AI training). Marketing pages\n * benefit from AI-engine citation; opting out is a per-page\n * editorial decision (typically pricing pages — to avoid stale\n * price training; or legal/internal pages — to avoid free-text\n * reproduction of contractual language). Per-blog-post opt-out\n * would mirror this knob on a sibling blog-post manifest schema\n * if that need materializes.\n */\nconst _WebsitePageSeoConfigSchema = z.object({\n noaiOptOut: z.boolean().optional(),\n});\n\nexport const WebsitePageSeoConfigSchema: typeof _WebsitePageSeoConfigSchema =\n _WebsitePageSeoConfigSchema;\n\nexport type WebsitePageSeoConfig = z.infer<typeof _WebsitePageSeoConfigSchema>;\n/** @wildo_source:part:end saas.website.page-manifest.seo */\n\nconst _WebsitePageManifestSchema = z\n .object({\n ref: WebsitePageRefSchema,\n routePath: _WebsitePageRoutePathSchema,\n pageComponent: z.unknown(),\n /*\n * EMPTY is a valid manifest (#2205): a page whose sections are not authored YET. The build lane\n * creates that state on purpose (the page task writes the empty list, then each section task\n * appends itself), and refusing it here made `astro dev` crash at module load until every page\n * of the site had a section. An empty page is unfinished, not false, so it is representable and\n * a dev server renders it (`WebsitePageSections` shows the pending state). What is refused is\n * BUILDING one: `assertWebsitePagesHaveSections`, run from the build-start hook, because a\n * published page with nothing on it is a statement to a real reader.\n */\n sectionRefs: z.array(WebsiteSectionRefSchema),\n metaLabelKeys: _WebsitePageMetaLabelKeysSchema,\n hydration: WebsitePageHydrationStrategySchema.optional(),\n seo: _WebsitePageSeoConfigSchema.optional(),\n })\n .superRefine((manifest, ctx) => {\n const seen = new Set<string>();\n for (const [index, sectionRef] of manifest.sectionRefs.entries()) {\n const key = sectionRef as unknown as string;\n if (seen.has(key)) {\n ctx.addIssue({\n code: 'custom',\n path: ['sectionRefs', index],\n message: `Duplicate sectionRef \"${key}\" in page manifest \"${manifest.ref as unknown as string}\"`,\n });\n }\n seen.add(key);\n }\n\n const expectedPagePrefix = `website.${manifest.ref as unknown as string}.`;\n for (const [field, key] of [\n ['titleKey', manifest.metaLabelKeys.titleKey] as const,\n ['descriptionKey', manifest.metaLabelKeys.descriptionKey] as const,\n ]) {\n if (!(key as unknown as string).startsWith(expectedPagePrefix)) {\n ctx.addIssue({\n code: 'custom',\n path: ['metaLabelKeys', field],\n message: `metaLabelKeys.${field} (\"${key as unknown as string}\") must be rooted under the page ref \"${manifest.ref as unknown as string}\" (expected prefix \"${expectedPagePrefix}\")`,\n });\n }\n }\n });\n\nexport const WebsitePageManifestSchema: typeof _WebsitePageManifestSchema =\n _WebsitePageManifestSchema;\n\n/**\n * Runtime shape of `WebsitePageManifest`.\n *\n * **Why `ComponentType<any>` and NOT `ComponentType<unknown>`** —\n * same variance reasoning as `WebsiteSectionDefinition.Component`:\n * `ComponentType<TProps>` is CONTRAVARIANT in `TProps`, so a\n * narrower `TypedWebsitePageManifest<TProps>` (returned by\n * `defineWebsitePageManifest<TProps>`) was not assignable to a\n * `WebsitePageManifest` typed with `ComponentType<unknown>`. That\n * blocked every consumer call-site that passed a typed manifest to\n * helpers / registries / validators expecting the bare type\n * (root config's `pageManifests[]`, the Astro adapter's\n * `loadWebsitePageRuntime`, and the head-renderer). Using `any`\n * matches the React-canonical pattern for heterogeneous component\n * containers and reflects the runtime contract: the framework only\n * RENDERS these components — it never constructs ad-hoc props for\n * them; the consumer's JSX call site supplies the precise props.\n */\nexport interface WebsitePageManifest extends z.infer<typeof _WebsitePageManifestSchema> {\n pageComponent: ComponentType<any>;\n}\n/** @wildo_source:part:end saas.website.page-manifest.schema */\n"]}
@@ -51,13 +51,13 @@ import { type WebsitePageManifest } from './website-page-manifest.shared.schemas
51
51
  declare const _WebsiteSitemapConfigSchema: z.ZodObject<{
52
52
  enabled: z.ZodDefault<z.ZodBoolean>;
53
53
  changefreq: z.ZodOptional<z.ZodEnum<{
54
+ never: "never";
54
55
  always: "always";
55
56
  hourly: "hourly";
56
57
  daily: "daily";
57
58
  weekly: "weekly";
58
59
  monthly: "monthly";
59
60
  yearly: "yearly";
60
- never: "never";
61
61
  }>>;
62
62
  priority: z.ZodOptional<z.ZodNumber>;
63
63
  }, z.core.$strip>;
@@ -217,13 +217,13 @@ declare const _WebsiteSeoConfigSchema: z.ZodObject<{
217
217
  sitemap: z.ZodOptional<z.ZodObject<{
218
218
  enabled: z.ZodDefault<z.ZodBoolean>;
219
219
  changefreq: z.ZodOptional<z.ZodEnum<{
220
+ never: "never";
220
221
  always: "always";
221
222
  hourly: "hourly";
222
223
  daily: "daily";
223
224
  weekly: "weekly";
224
225
  monthly: "monthly";
225
226
  yearly: "yearly";
226
- never: "never";
227
227
  }>>;
228
228
  priority: z.ZodOptional<z.ZodNumber>;
229
229
  }, z.core.$strip>>;
@@ -378,13 +378,13 @@ declare const _WebsiteRootConfigSchema: z.ZodObject<{
378
378
  sitemap: z.ZodOptional<z.ZodObject<{
379
379
  enabled: z.ZodDefault<z.ZodBoolean>;
380
380
  changefreq: z.ZodOptional<z.ZodEnum<{
381
+ never: "never";
381
382
  always: "always";
382
383
  hourly: "hourly";
383
384
  daily: "daily";
384
385
  weekly: "weekly";
385
386
  monthly: "monthly";
386
387
  yearly: "yearly";
387
- never: "never";
388
388
  }>>;
389
389
  priority: z.ZodOptional<z.ZodNumber>;
390
390
  }, z.core.$strip>>;
@@ -1 +1 @@
1
- {"version":3,"file":"website-root-config.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../src/schemas/manifests/website-root-config.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAC3C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,wCAAwC,CAAC;AAEhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,QAAA,MAAM,2BAA2B;;;;;;;;;;;;iBAU/B,CAAC;AAEH,eAAO,MAAM,0BAA0B,EAAE,OAAO,2BACnB,CAAC;AAE9B,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAC/E,6DAA6D;AAE7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,QAAA,MAAM,0BAA0B;;;;;;;;iBAsC5B,CAAC;AAEL,eAAO,MAAM,yBAAyB,EAAE,OAAO,0BACnB,CAAC;AAE7B,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAC7E,4DAA4D;AAE5D;;;;;;;;;;;;;;;;;;GAkBG;AACH,QAAA,MAAM,2BAA2B;;iBAE/B,CAAC;AAEH,eAAO,MAAM,0BAA0B,EAAE,OAAO,2BACnB,CAAC;AAE9B,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAC/E,8DAA8D;AAE9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,QAAA,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA0BzB,CAAC;AAEL,eAAO,MAAM,sBAAsB,EAAE,OAAO,uBAAiD,CAAC;AAE9F,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AACvE,0DAA0D;AAE1D;;;;;;;;;;;;;GAaG;AACH,QAAA,MAAM,8BAA8B;;;iBAGlC,CAAC;AAEH,eAAO,MAAM,6BAA6B,EAAE,OAAO,8BACnB,CAAC;AAEjC,MAAM,WAAW,uBACf,SAAQ,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC;IACtD,eAAe,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IACxC,eAAe,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;CACzC;AACD,iEAAiE;AAEjE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AACH,QAAA,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA2F1B,CAAC;AAEL,eAAO,MAAM,uBAAuB,EAAE,OAAO,wBAAmD,CAAC;AAEjG;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAClC,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,EACxC,eAAe,GAAG,YAAY,CAC/B,GAAG;IACF,aAAa,EAAE,mBAAmB,EAAE,CAAC;IACrC,UAAU,EAAE,uBAAuB,CAAC;CACrC,CAAC;;AACF,6DAA6D"}
1
+ {"version":3,"file":"website-root-config.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../../src/schemas/manifests/website-root-config.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAC3C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,wCAAwC,CAAC;AAEhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,QAAA,MAAM,2BAA2B;;;;;;;;;;;;iBAU/B,CAAC;AAEH,eAAO,MAAM,0BAA0B,EAAE,OAAO,2BACnB,CAAC;AAE9B,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAC/E,6DAA6D;AAE7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,QAAA,MAAM,0BAA0B;;;;;;;;iBAsC5B,CAAC;AAEL,eAAO,MAAM,yBAAyB,EAAE,OAAO,0BACnB,CAAC;AAE7B,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAC7E,4DAA4D;AAE5D;;;;;;;;;;;;;;;;;;GAkBG;AACH,QAAA,MAAM,2BAA2B;;iBAE/B,CAAC;AAEH,eAAO,MAAM,0BAA0B,EAAE,OAAO,2BACnB,CAAC;AAE9B,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAC/E,8DAA8D;AAE9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,QAAA,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA0BzB,CAAC;AAEL,eAAO,MAAM,sBAAsB,EAAE,OAAO,uBAAiD,CAAC;AAE9F,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AACvE,0DAA0D;AAE1D;;;;;;;;;;;;;GAaG;AACH,QAAA,MAAM,8BAA8B;;;iBAGlC,CAAC;AAEH,eAAO,MAAM,6BAA6B,EAAE,OAAO,8BACnB,CAAC;AAEjC,MAAM,WAAW,uBACf,SAAQ,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC;IACtD,eAAe,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IACxC,eAAe,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;CACzC;AACD,iEAAiE;AAEjE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AACH,QAAA,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA2F1B,CAAC;AAEL,eAAO,MAAM,uBAAuB,EAAE,OAAO,wBAAmD,CAAC;AAEjG;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAClC,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,EACxC,eAAe,GAAG,YAAY,CAC/B,GAAG;IACF,aAAa,EAAE,mBAAmB,EAAE,CAAC;IACrC,UAAU,EAAE,uBAAuB,CAAC;CACrC,CAAC;;AACF,6DAA6D"}
@@ -1 +1 @@
1
- {"version":3,"file":"website-root-config.shared.schemas.js","sourceRoot":"","sources":["../../../../src/schemas/manifests/website-root-config.shared.schemas.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ComponentType } from 'react';\nimport { z } from 'zod';\n\nimport { WebsiteDesignTokensSchema } from '../design-tokens/website-design-tokens.shared.schemas';\nimport { WebsiteLabelKeySchema } from '../label-keys/website-label-key.schemas';\nimport {\n WebsitePageManifestSchema,\n type WebsitePageManifest,\n} from './website-page-manifest.shared.schemas';\n\n/**\n * @wildo_source:part:start saas.website.seo.sitemap-config facet:layer:shared facet:family:website\n *\n * `WebsiteSitemapConfig` — site-wide sitemap policy block.\n *\n * Materialized at build time by `@astrojs/sitemap` (the official\n * Astro plugin) when the consumer wires it into `astro.config.ts`\n * (see `website-seo-aeo-geo` SKILL). The framework owns the\n * **schema** (so config + enforcement live in lock-step with the\n * page registry) but the framework does NOT own the runtime\n * generator — `@astrojs/sitemap` already covers per-page\n * `lastmod` / `changefreq` / `priority` + i18n `xhtml:link\n * hreflang` annotations natively, and re-implementing that surface\n * inside the engine would duplicate well-trodden Astro tooling for\n * zero added value.\n *\n * # Field rationale\n *\n * **Why `enabled` defaults to `true`**:\n * - Marketing sites that want to ship public pages overwhelmingly\n * also want them in `sitemap.xml` (search engines treat its\n * absence as a discoverability signal). The opt-OUT shape avoids\n * a silent regression where a consumer ships pages that crawlers\n * never index because they forgot to flip a flag.\n * - The escape hatch (`enabled: false`) covers staging deployments\n * and the rare per-app decision to suppress sitemap emission\n * entirely (e.g. a private beta surface routed under a\n * `noindex` policy).\n *\n * **Why `changefreq` / `priority` are OPTIONAL** (no defaults):\n * - Both fields are non-binding hints — Google explicitly\n * ignores `priority` and treats `changefreq` as a soft signal.\n * Forcing the consumer to pick a value would either invite\n * superstitious authoring (`priority: 1.0` on every page) or\n * bury the field's actual ergonomic shape (per-page overrides\n * via `WebsitePageManifest.seo` — deferred until a real\n * consumer requests it).\n * - Unset values are simply omitted from the emitted `<url>`\n * entries; `@astrojs/sitemap` handles that automatically.\n *\n * **Validation**:\n * - `priority` is `z.number().min(0).max(1)` — outside the spec\n * range would silently break crawler ingestion.\n * - `changefreq` is the union of the seven values the sitemap\n * spec defines; arbitrary strings would be silently dropped by\n * compliant crawlers.\n */\nconst _WebsiteSitemapConfigSchema = z.object({\n enabled: z.boolean().default(true),\n changefreq: z\n .enum(['always', 'hourly', 'daily', 'weekly', 'monthly', 'yearly', 'never'])\n .optional(),\n priority: z\n .number()\n .min(0, { error: 'WebsiteSitemapConfig.priority must be between 0 and 1' })\n .max(1, { error: 'WebsiteSitemapConfig.priority must be between 0 and 1' })\n .optional(),\n});\n\nexport const WebsiteSitemapConfigSchema: typeof _WebsiteSitemapConfigSchema =\n _WebsiteSitemapConfigSchema;\n\nexport type WebsiteSitemapConfig = z.infer<typeof _WebsiteSitemapConfigSchema>;\n/** @wildo_source:part:end saas.website.seo.sitemap-config */\n\n/**\n * @wildo_source:part:start saas.website.seo.robots-config facet:layer:shared facet:family:website\n *\n * `WebsiteRobotsConfig` — site-wide `robots.txt` policy block.\n *\n * Materialized at request time by `renderRobotsTxt(siteContext)`\n * from `@wildo-ai/saas-website/astro` (consumer wires a thin\n * `src/pages/robots.txt.ts` Astro endpoint that returns the helper's\n * output). Scoped to the SITE-WIDE policy surface — per-page\n * `noindex` is a separate concern that lands on\n * `WebsitePageManifest.seo` once a real consumer requests it (today\n * the site-wide `disallow[]` covers staging-environment exclusion of\n * specific path prefixes, the dominant real-world use case).\n *\n * # Field rationale\n *\n * **Why `policy` defaults to `'index'`**:\n * - The default for a public marketing site is \"let crawlers in\";\n * `'noindex'` is the deliberate exception (staging environments,\n * beta surfaces, sites still under construction). The opt-OUT\n * shape mirrors `WebsiteSitemapConfig.enabled` for consistency.\n * - `'noindex'` is expressed by a `<meta name=\"robots\" content=\"noindex\">`\n * on every page, NOT by `Disallow: /` in `robots.txt` (#494): a crawler\n * barred from fetching a page never reads its noindex, so the URL can\n * still be indexed bare. `robots.txt` stays allow-everything for it.\n *\n * **Why `disallow[]` is a path-prefix list (not a regex)**:\n * - The `robots.txt` spec only supports literal path prefixes\n * with `*` / `$` wildcards; Disallow is NOT a regex surface.\n * Typing it as `string[]` (validated to start with `/`) keeps\n * the contract honest and lets consumers paste from their CDN\n * allowlists without translation.\n *\n * **Why `allowedCrawlers` and `disallowedCrawlers` are mutually\n * exclusive**:\n * - The two fields are two halves of the SAME AI-crawler policy\n * decision. `allowedCrawlers` is the deny-by-default shape\n * (only the named bots may crawl); `disallowedCrawlers` is the\n * allow-by-default shape (every bot except the named ones may\n * crawl). Mixing both would emit a contradictory `robots.txt`\n * (a bot in both lists would receive conflicting directives).\n * A `superRefine` enforces the mutual-exclusion invariant at\n * parse time so the build fails fast.\n *\n * **Validation**:\n * - `disallow[]` entries must start with `/` (sitemap path-prefix\n * spec) — anything else would silently match nothing.\n * - `allowedCrawlers` / `disallowedCrawlers` entries must be\n * non-empty strings (a blank User-agent token would emit\n * `User-agent: ` which compliant crawlers ignore — silent\n * misconfig).\n * - `superRefine` rejects the combined-list anti-pattern.\n */\nconst _WebsiteRobotsConfigSchema = z\n .object({\n policy: z.enum(['index', 'noindex']).default('index'),\n disallow: z\n .array(\n z\n .string()\n .min(1)\n .refine((path) => path.startsWith('/'), {\n error: 'WebsiteRobotsConfig.disallow[] entries must start with \"/\"',\n }),\n )\n .optional(),\n /**\n * Deny-by-default AI-crawler allowlist. When set, the emitted\n * `robots.txt` lists each named bot under its own\n * `User-agent: <bot>\\nAllow: /` block, then closes with a\n * catch-all `User-agent: *\\nDisallow: /`. Mutually exclusive\n * with `disallowedCrawlers` (see `superRefine` below).\n */\n allowedCrawlers: z.array(z.string().min(1)).optional(),\n /**\n * Allow-by-default AI-crawler denylist. When set, the emitted\n * `robots.txt` lists each named bot under its own\n * `User-agent: <bot>\\nDisallow: /` block. Mutually exclusive\n * with `allowedCrawlers` (see `superRefine` below).\n */\n disallowedCrawlers: z.array(z.string().min(1)).optional(),\n })\n .superRefine((value, ctx) => {\n if (value.allowedCrawlers !== undefined && value.disallowedCrawlers !== undefined) {\n ctx.addIssue({\n code: 'custom',\n path: ['allowedCrawlers'],\n message:\n 'WebsiteRobotsConfig.allowedCrawlers and disallowedCrawlers are mutually exclusive — pick one policy direction (deny-by-default vs allow-by-default).',\n });\n }\n });\n\nexport const WebsiteRobotsConfigSchema: typeof _WebsiteRobotsConfigSchema =\n _WebsiteRobotsConfigSchema;\n\nexport type WebsiteRobotsConfig = z.infer<typeof _WebsiteRobotsConfigSchema>;\n/** @wildo_source:part:end saas.website.seo.robots-config */\n\n/**\n * @wildo_source:part:start saas.website.seo.llms-txt-config facet:layer:shared facet:family:website\n *\n * `WebsiteLlmsTxtConfig` — site-wide `llms.txt` publication policy.\n *\n * The framework owns only the SERVING surface; the CONTENT remains\n * hand-authored in `src/content/llms.txt`. This block therefore needs\n * exactly one knob:\n *\n * - `enabled?: boolean`\n * - `true` → publish when the file exists\n * - `false` → suppress publication even if the file exists\n * - `undefined` → auto-detect (`readLlmsTxtBody` publishes when the\n * file exists, returns `null` when absent)\n *\n * Keeping the \"auto-detect when undefined\" convention in the HELPER\n * (not as a schema default) is deliberate: schema defaults are static,\n * while file presence is runtime/build-time state.\n */\nconst _WebsiteLlmsTxtConfigSchema = z.object({\n enabled: z.boolean().optional(),\n});\n\nexport const WebsiteLlmsTxtConfigSchema: typeof _WebsiteLlmsTxtConfigSchema =\n _WebsiteLlmsTxtConfigSchema;\n\nexport type WebsiteLlmsTxtConfig = z.infer<typeof _WebsiteLlmsTxtConfigSchema>;\n/** @wildo_source:part:end saas.website.seo.llms-txt-config */\n\n/**\n * @wildo_source:part:start saas.website.root-config.seo facet:layer:shared facet:family:website\n *\n * `WebsiteSeoConfig` — site-wide SEO/social-graph configuration\n * AUTHORED ON THE ROOT CONFIG.\n *\n * # Scope\n *\n * The public marketing-site origin belongs only in the per-service\n * `wildo.website.config.ts` declaration. Keep it out of this site-level SEO\n * block so the origin and localized routing have one source of truth.\n *\n * What stays on this block: site-level branding + hero-image\n * fallbacks that are co-authored with chrome labels and page\n * manifests respectively, NOT with the URL/i18n routing surface.\n *\n * # Field rationale\n *\n * **Why `siteName` is REQUIRED**:\n * - `og:site_name` is the brand label social platforms render\n * above the page title in shared previews. Without it, every\n * LinkedIn / Facebook / Slack preview shows the bare title with\n * no brand context.\n * - It is also the recommended `application-name` meta tag value\n * and a useful default for JSON-LD `Organization.name` schema\n * when the application ships structured data.\n *\n * **Why `defaultOgImageUrl` is OPTIONAL**:\n * - Each page's `WebsitePageManifest.metaLabelKeys.ogImageUrl`\n * takes precedence; the site-level default is a fallback for\n * pages that did not commit to a hero image.\n * - Marketing surfaces shipping no OG images at all (acceptable\n * for a placeholder build) should not be forced to author one.\n *\n * **Why `sitemap?` and `robots?` are OPTIONAL** (with sensible\n * defaults inside each sub-schema):\n * - The marketing-site default ships a public surface — every\n * page indexable, no AI-crawler restrictions, no per-path\n * disallow. Forcing every consumer to author both blocks just\n * to land in that default state would pollute the call site.\n * - Omitting either block at the call site is equivalent to\n * authoring `{}` — `WebsiteSitemapConfig.enabled` defaults to\n * `true`, `WebsiteRobotsConfig.policy` defaults to `'index'`.\n * The per-block field rationale lives on each sub-schema.\n * - `llmsTxt?` is similarly optional, but with a different default\n * mechanic: omitting the block means \"auto-detect from file\n * presence\" rather than \"use a static schema default\". The helper\n * `readLlmsTxtBody(...)` publishes when `src/content/llms.txt`\n * exists and returns `null` otherwise.\n *\n * **Validation**:\n * - `siteName` is `z.string().min(1)` — empty strings would emit\n * `<meta property=\"og:site_name\" content=\"\" />` which social\n * platforms render as a blank line.\n * - `defaultOgImageUrl` (when present) is `z.url()`.\n * - `sitemap` / `robots` validation lives on the sub-schemas.\n */\nconst _WebsiteSeoConfigSchema = z\n .object({\n siteName: z\n .string()\n .min(1, { error: 'WebsiteSeoConfig.siteName must be a non-empty string' }),\n defaultOgImageUrl: z\n .url({ error: 'WebsiteSeoConfig.defaultOgImageUrl must be a fully-qualified URL' })\n .optional(),\n /**\n * Sitemap policy block. Optional — omitting it is equivalent to\n * `{ enabled: true }` (the marketing-site default). See\n * `WebsiteSitemapConfigSchema` for field-by-field rationale.\n */\n sitemap: _WebsiteSitemapConfigSchema.optional(),\n /**\n * Robots.txt policy block. Optional — omitting it is equivalent\n * to `{ policy: 'index' }` (the marketing-site default). See\n * `WebsiteRobotsConfigSchema` for field-by-field rationale.\n */\n robots: _WebsiteRobotsConfigSchema.optional(),\n /**\n * `llms.txt` publication policy. Optional — omitting it means\n * \"auto-detect from file presence\". See\n * `WebsiteLlmsTxtConfigSchema` for the serving contract.\n */\n llmsTxt: _WebsiteLlmsTxtConfigSchema.optional(),\n });\n\nexport const WebsiteSeoConfigSchema: typeof _WebsiteSeoConfigSchema = _WebsiteSeoConfigSchema;\n\nexport type WebsiteSeoConfig = z.infer<typeof _WebsiteSeoConfigSchema>;\n/** @wildo_source:part:end saas.website.root-config.seo */\n\n/**\n * @wildo_source:part:start saas.website.root-config.navigation facet:layer:shared facet:family:website\n *\n * `WebsiteNavigationConfig` — site-wide header/footer slot bindings.\n *\n * The framework intentionally ships no concrete header or footer. The\n * application authors both as React components, registers them once here, and\n * the page layout renders the site-wide chrome around every page.\n *\n * Both fields are typed as `unknown` at the schema layer with the\n * `WebsiteNavigationConfig` interface narrowing them to\n * `ComponentType<unknown>` — same rationale as\n * `WebsiteSectionDefinition.Component` and `WebsitePageManifest.pageComponent`.\n */\nconst _WebsiteNavigationConfigSchema = z.object({\n headerComponent: z.unknown(),\n footerComponent: z.unknown(),\n});\n\nexport const WebsiteNavigationConfigSchema: typeof _WebsiteNavigationConfigSchema =\n _WebsiteNavigationConfigSchema;\n\nexport interface WebsiteNavigationConfig\n extends z.infer<typeof _WebsiteNavigationConfigSchema> {\n headerComponent: ComponentType<unknown>;\n footerComponent: ComponentType<unknown>;\n}\n/** @wildo_source:part:end saas.website.root-config.navigation */\n\n/**\n * @wildo_source:part:start saas.website.root-config.schema facet:layer:shared facet:family:website\n *\n * `WebsiteRootConfig` — the site-level configuration consumed by:\n * - the Astro adapter to drive i18n routing, page mounting,\n * and design-token CSS emission,\n * - the website audit command to validate label-pack\n * coverage and SEO structured-data conformance,\n * - the runtime `<WebsitePageLayout>` to render navigation\n * and apply design tokens.\n *\n * **Authoring convention**: each marketing-site service exports a single\n * `WEBSITE_ROOT_CONFIG: WebsiteRootConfig` from `src/website-root.config.ts`.\n * The Astro adapter and the audit command\n * both consume this canonical entry point; there is exactly one site\n * config per marketing site (no per-page or per-section overrides at\n * this level — overrides flow through the L0/L1/L1.5 component\n * registration hierarchy described in D6).\n *\n * # Scope\n *\n * This block carries site-level chrome and registry data only. Per-locale\n * routing (`locales` / `defaultLocale`) and the public origin belong in the\n * per-service `wildo.website.config.ts` declaration, avoiding duplicated\n * configuration that could diverge during site generation.\n *\n * **Field-by-field rationale**:\n *\n * - `pageManifests` — the registry of every page on the site. Indexed\n * downstream by `manifest.ref`. Refs MUST be unique (refinement\n * below).\n * - `designTokens` — see `WebsiteDesignTokensSchema`.\n * - `navigation` — see `WebsiteNavigationConfigSchema`.\n * - `chromeLabelKeys` — OPTIONAL. The set of fully-composed label keys\n * the site-wide chrome (header / footer / any global slot the\n * consumer renders outside a section context) reads via\n * `useWebsiteLabelByKey(...)`. Declared here, NOT in section\n * `expectedLabelKeys`, because chrome is rendered outside the\n * page+section context — `useWebsiteLabel(slot)` is structurally\n * unavailable to it. Threading these through the label-pack\n * validator (a) silences the always-on \"orphaned chrome key\" build\n * warnings and (b) lets the build fail fast when a chrome key is\n * missing from a locale pack, the same way section keys do.\n * Defaults to an empty array; sites with no chrome label reads\n * may omit the field entirely.\n * - `seo` — site-level branding + hero-image fallback (see\n * `WebsiteSeoConfigSchema`). The marketing-site origin USED to live\n * here as `seo.siteOrigin`; it now lives only on\n * `WildoMarketingWebsiteConfig.publicOrigin`.\n * - `skipToMainContentLabelKey` — see field JSDoc below.\n *\n * **Refinements**:\n * - `pageManifests[].ref` uniqueness — the registry is keyed by ref;\n * duplicates would silently overwrite each other in the lookup\n * table the bridge uses for cross-page links.\n * - `pageManifests[].routePath` uniqueness — two pages cannot mount\n * at the same path (Astro would error at build time, but we\n * surface it earlier here with a clearer diagnostic).\n * - `chromeLabelKeys` uniqueness — duplicates indicate copy-paste\n * bugs at the consumer's call site; surface them here.\n */\nconst _WebsiteRootConfigSchema = z\n .object({\n pageManifests: z.array(WebsitePageManifestSchema).min(1, {\n error: 'WebsiteRootConfig must declare at least one page manifest',\n }),\n designTokens: WebsiteDesignTokensSchema,\n navigation: _WebsiteNavigationConfigSchema,\n chromeLabelKeys: z.array(WebsiteLabelKeySchema).default([]),\n /**\n * Site-level branding + hero-image fallback used by the Astro\n * head renderer to compose `<meta property=\"og:site_name\">` and\n * the per-page OG image fallback. The marketing-site origin\n * (`siteOrigin`) USED to live here; it was moved to\n * `WildoMarketingWebsiteConfig.publicOrigin` in the seam-collapse\n * (single source of truth for \"what's the origin this site is\n * reachable at\").\n */\n seo: _WebsiteSeoConfigSchema,\n /**\n * Label key the framework reads to render the \"Skip to main\n * content\" link as the first focusable element on every page\n * Required because:\n * - Skip-to-main is a baseline keyboard-accessibility\n * affordance — WCAG 2.1 §2.4.1 \"Bypass Blocks\" (Level A)\n * mandates it. A marketing-site framework that omits it\n * fails AA for the entire surface; making the contract\n * opt-in invites that regression on every new consumer.\n * - The text is announced verbatim by assistive tech, so it\n * MUST be localized — a hardcoded English fallback would\n * speak \"Skip to main content\" to French users. Forcing the\n * key through the label-pack validation surface means the\n * dev server fails fast when the consumer forgets to add\n * the entry to a locale pack.\n * - The framework (NOT the consumer's chrome) owns rendering\n * the skip link because the chrome is itself the obstacle\n * the link bypasses — a chrome-side implementation would\n * either be inside the chrome (impossible to focus before\n * the chrome) or above it (creating a structural surface\n * the framework needs anyway).\n *\n * It is validated as a chrome key without being listed in\n * `chromeLabelKeys`: the label-pack loader adds it to the effective\n * chrome key set (`composeEffectiveChromeLabelKeys` in\n * `astro/label-pack-loader.ts`), so consumers cannot forget it.\n */\n skipToMainContentLabelKey: WebsiteLabelKeySchema,\n })\n .superRefine((config, ctx) => {\n const seenRefs = new Set<string>();\n const seenRoutePaths = new Set<string>();\n for (const [index, manifest] of config.pageManifests.entries()) {\n const refKey = manifest.ref as unknown as string;\n if (seenRefs.has(refKey)) {\n ctx.addIssue({\n code: 'custom',\n path: ['pageManifests', index, 'ref'],\n message: `Duplicate page ref \"${refKey}\" in WebsiteRootConfig.pageManifests`,\n });\n }\n seenRefs.add(refKey);\n\n const routeKey = manifest.routePath as unknown as string;\n if (seenRoutePaths.has(routeKey)) {\n ctx.addIssue({\n code: 'custom',\n path: ['pageManifests', index, 'routePath'],\n message: `Duplicate routePath \"${routeKey}\" in WebsiteRootConfig.pageManifests`,\n });\n }\n seenRoutePaths.add(routeKey);\n }\n\n /**\n * `chromeLabelKeys` uniqueness — a duplicate is harmless at\n * runtime (the validator de-dupes via a Set) but always indicates\n * a copy-paste bug at the consumer's call site. Surfacing it here\n * keeps the chrome contract tidy and matches the discipline applied\n * to `pageManifests[].ref` and `locales`.\n */\n const seenChromeKeys = new Set<string>();\n for (const [index, key] of config.chromeLabelKeys.entries()) {\n const keyStr = key as unknown as string;\n if (seenChromeKeys.has(keyStr)) {\n ctx.addIssue({\n code: 'custom',\n path: ['chromeLabelKeys', index],\n message: `Duplicate chrome label key \"${keyStr}\" in WebsiteRootConfig.chromeLabelKeys`,\n });\n }\n seenChromeKeys.add(keyStr);\n }\n });\n\nexport const WebsiteRootConfigSchema: typeof _WebsiteRootConfigSchema = _WebsiteRootConfigSchema;\n\n/**\n * Runtime shape of `WebsiteRootConfig`.\n *\n * Hand-written (NOT a bare `z.infer<typeof _WebsiteRootConfigSchema>`)\n * because `z.infer` walks the schema tree and would type\n * `pageManifests[number].pageComponent`, `navigation.headerComponent`\n * and `navigation.footerComponent` as `unknown` (their schema field\n * type), losing the `ComponentType<unknown>` narrowing carried by the\n * hand-written `WebsitePageManifest` and `WebsiteNavigationConfig`\n * interfaces. The layout primitives and the Astro helpers consume\n * `WebsiteRootConfig` and must see those\n * fields pre-narrowed; otherwise every consumer would need a local\n * `as ComponentType` cast at the JSX call site, defeating the type-safe\n * component contract.\n *\n * The `Omit` + intersection pattern keeps the rest of the inferred\n * shape (`designTokens`, `seo`, `chromeLabelKeys`,\n * `skipToMainContentLabelKey`) sourced directly from the schema.\n */\nexport type WebsiteRootConfig = Omit<\n z.infer<typeof _WebsiteRootConfigSchema>,\n 'pageManifests' | 'navigation'\n> & {\n pageManifests: WebsitePageManifest[];\n navigation: WebsiteNavigationConfig;\n};\n/** @wildo_source:part:end saas.website.root-config.schema */\n"]}
1
+ {"version":3,"file":"website-root-config.shared.schemas.js","sourceRoot":"","sources":["../../../../../src/schemas/manifests/website-root-config.shared.schemas.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ComponentType } from 'react';\nimport { z } from 'zod';\n\nimport { WebsiteDesignTokensSchema } from '../design-tokens/website-design-tokens.shared.schemas';\nimport { WebsiteLabelKeySchema } from '../label-keys/website-label-key.schemas';\nimport {\n WebsitePageManifestSchema,\n type WebsitePageManifest,\n} from './website-page-manifest.shared.schemas';\n\n/**\n * @wildo_source:part:start saas.website.seo.sitemap-config facet:layer:shared facet:family:website\n *\n * `WebsiteSitemapConfig` — site-wide sitemap policy block.\n *\n * Materialized at build time by `@astrojs/sitemap` (the official\n * Astro plugin) when the consumer wires it into `astro.config.ts`\n * (see `website-seo-aeo-geo` SKILL). The framework owns the\n * **schema** (so config + enforcement live in lock-step with the\n * page registry) but the framework does NOT own the runtime\n * generator — `@astrojs/sitemap` already covers per-page\n * `lastmod` / `changefreq` / `priority` + i18n `xhtml:link\n * hreflang` annotations natively, and re-implementing that surface\n * inside the engine would duplicate well-trodden Astro tooling for\n * zero added value.\n *\n * # Field rationale\n *\n * **Why `enabled` defaults to `true`**:\n * - Marketing sites that want to ship public pages overwhelmingly\n * also want them in `sitemap.xml` (search engines treat its\n * absence as a discoverability signal). The opt-OUT shape avoids\n * a silent regression where a consumer ships pages that crawlers\n * never index because they forgot to flip a flag.\n * - The escape hatch (`enabled: false`) covers staging deployments\n * and the rare per-app decision to suppress sitemap emission\n * entirely (e.g. a private beta surface routed under a\n * `noindex` policy).\n *\n * **Why `changefreq` / `priority` are OPTIONAL** (no defaults):\n * - Both fields are non-binding hints — Google explicitly\n * ignores `priority` and treats `changefreq` as a soft signal.\n * Forcing the consumer to pick a value would either invite\n * superstitious authoring (`priority: 1.0` on every page) or\n * bury the field's actual ergonomic shape (per-page overrides\n * via `WebsitePageManifest.seo` — deferred until a real\n * consumer requests it).\n * - Unset values are simply omitted from the emitted `<url>`\n * entries; `@astrojs/sitemap` handles that automatically.\n *\n * **Validation**:\n * - `priority` is `z.number().min(0).max(1)` — outside the spec\n * range would silently break crawler ingestion.\n * - `changefreq` is the union of the seven values the sitemap\n * spec defines; arbitrary strings would be silently dropped by\n * compliant crawlers.\n */\nconst _WebsiteSitemapConfigSchema = z.object({\n enabled: z.boolean().default(true),\n changefreq: z\n .enum(['always', 'hourly', 'daily', 'weekly', 'monthly', 'yearly', 'never'])\n .optional(),\n priority: z\n .number()\n .min(0, { error: 'WebsiteSitemapConfig.priority must be between 0 and 1' })\n .max(1, { error: 'WebsiteSitemapConfig.priority must be between 0 and 1' })\n .optional(),\n});\n\nexport const WebsiteSitemapConfigSchema: typeof _WebsiteSitemapConfigSchema =\n _WebsiteSitemapConfigSchema;\n\nexport type WebsiteSitemapConfig = z.infer<typeof _WebsiteSitemapConfigSchema>;\n/** @wildo_source:part:end saas.website.seo.sitemap-config */\n\n/**\n * @wildo_source:part:start saas.website.seo.robots-config facet:layer:shared facet:family:website\n *\n * `WebsiteRobotsConfig` — site-wide `robots.txt` policy block.\n *\n * Materialized at request time by `renderRobotsTxt(siteContext)`\n * from `@wildo-ai/saas-website/astro` (consumer wires a thin\n * `src/pages/robots.txt.ts` Astro endpoint that returns the helper's\n * output). Scoped to the SITE-WIDE policy surface — per-page\n * `noindex` is a separate concern that lands on\n * `WebsitePageManifest.seo` once a real consumer requests it (today\n * the site-wide `disallow[]` covers staging-environment exclusion of\n * specific path prefixes, the dominant real-world use case).\n *\n * # Field rationale\n *\n * **Why `policy` defaults to `'index'`**:\n * - The default for a public marketing site is \"let crawlers in\";\n * `'noindex'` is the deliberate exception (staging environments,\n * beta surfaces, sites still under construction). The opt-OUT\n * shape mirrors `WebsiteSitemapConfig.enabled` for consistency.\n * - `'noindex'` is expressed by a `<meta name=\"robots\" content=\"noindex\">`\n * on every page, NOT by `Disallow: /` in `robots.txt` (#494): a crawler\n * barred from fetching a page never reads its noindex, so the URL can\n * still be indexed bare. `robots.txt` stays allow-everything for it.\n *\n * **Why `disallow[]` is a path-prefix list (not a regex)**:\n * - The `robots.txt` spec only supports literal path prefixes\n * with `*` / `$` wildcards; Disallow is NOT a regex surface.\n * Typing it as `string[]` (validated to start with `/`) keeps\n * the contract honest and lets consumers paste from their CDN\n * allowlists without translation.\n *\n * **Why `allowedCrawlers` and `disallowedCrawlers` are mutually\n * exclusive**:\n * - The two fields are two halves of the SAME AI-crawler policy\n * decision. `allowedCrawlers` is the deny-by-default shape\n * (only the named bots may crawl); `disallowedCrawlers` is the\n * allow-by-default shape (every bot except the named ones may\n * crawl). Mixing both would emit a contradictory `robots.txt`\n * (a bot in both lists would receive conflicting directives).\n * A `superRefine` enforces the mutual-exclusion invariant at\n * parse time so the build fails fast.\n *\n * **Validation**:\n * - `disallow[]` entries must start with `/` (sitemap path-prefix\n * spec) — anything else would silently match nothing.\n * - `allowedCrawlers` / `disallowedCrawlers` entries must be\n * non-empty strings (a blank User-agent token would emit\n * `User-agent: ` which compliant crawlers ignore — silent\n * misconfig).\n * - `superRefine` rejects the combined-list anti-pattern.\n */\nconst _WebsiteRobotsConfigSchema = z\n .object({\n policy: z.enum(['index', 'noindex']).default('index'),\n disallow: z\n .array(\n z\n .string()\n .min(1)\n .refine((path) => path.startsWith('/'), {\n error: 'WebsiteRobotsConfig.disallow[] entries must start with \"/\"',\n }),\n )\n .optional(),\n /**\n * Deny-by-default AI-crawler allowlist. When set, the emitted\n * `robots.txt` lists each named bot under its own\n * `User-agent: <bot>\\nAllow: /` block, then closes with a\n * catch-all `User-agent: *\\nDisallow: /`. Mutually exclusive\n * with `disallowedCrawlers` (see `superRefine` below).\n */\n allowedCrawlers: z.array(z.string().min(1)).optional(),\n /**\n * Allow-by-default AI-crawler denylist. When set, the emitted\n * `robots.txt` lists each named bot under its own\n * `User-agent: <bot>\\nDisallow: /` block. Mutually exclusive\n * with `allowedCrawlers` (see `superRefine` below).\n */\n disallowedCrawlers: z.array(z.string().min(1)).optional(),\n })\n .superRefine((value, ctx) => {\n if (value.allowedCrawlers !== undefined && value.disallowedCrawlers !== undefined) {\n ctx.addIssue({\n code: 'custom',\n path: ['allowedCrawlers'],\n message:\n 'WebsiteRobotsConfig.allowedCrawlers and disallowedCrawlers are mutually exclusive — pick one policy direction (deny-by-default vs allow-by-default).',\n });\n }\n });\n\nexport const WebsiteRobotsConfigSchema: typeof _WebsiteRobotsConfigSchema =\n _WebsiteRobotsConfigSchema;\n\nexport type WebsiteRobotsConfig = z.infer<typeof _WebsiteRobotsConfigSchema>;\n/** @wildo_source:part:end saas.website.seo.robots-config */\n\n/**\n * @wildo_source:part:start saas.website.seo.llms-txt-config facet:layer:shared facet:family:website\n *\n * `WebsiteLlmsTxtConfig` — site-wide `llms.txt` publication policy.\n *\n * The framework owns only the SERVING surface; the CONTENT remains\n * hand-authored in `src/content/llms.txt`. This block therefore needs\n * exactly one knob:\n *\n * - `enabled?: boolean`\n * - `true` → publish when the file exists\n * - `false` → suppress publication even if the file exists\n * - `undefined` → auto-detect (`readLlmsTxtBody` publishes when the\n * file exists, returns `null` when absent)\n *\n * Keeping the \"auto-detect when undefined\" convention in the HELPER\n * (not as a schema default) is deliberate: schema defaults are static,\n * while file presence is runtime/build-time state.\n */\nconst _WebsiteLlmsTxtConfigSchema = z.object({\n enabled: z.boolean().optional(),\n});\n\nexport const WebsiteLlmsTxtConfigSchema: typeof _WebsiteLlmsTxtConfigSchema =\n _WebsiteLlmsTxtConfigSchema;\n\nexport type WebsiteLlmsTxtConfig = z.infer<typeof _WebsiteLlmsTxtConfigSchema>;\n/** @wildo_source:part:end saas.website.seo.llms-txt-config */\n\n/**\n * @wildo_source:part:start saas.website.root-config.seo facet:layer:shared facet:family:website\n *\n * `WebsiteSeoConfig` — site-wide SEO/social-graph configuration\n * AUTHORED ON THE ROOT CONFIG.\n *\n * # Scope\n *\n * The public marketing-site origin belongs only in the per-service\n * `wildo.website.config.ts` declaration. Keep it out of this site-level SEO\n * block so the origin and localized routing have one source of truth.\n *\n * What stays on this block: site-level branding + hero-image\n * fallbacks that are co-authored with chrome labels and page\n * manifests respectively, NOT with the URL/i18n routing surface.\n *\n * # Field rationale\n *\n * **Why `siteName` is REQUIRED**:\n * - `og:site_name` is the brand label social platforms render\n * above the page title in shared previews. Without it, every\n * LinkedIn / Facebook / Slack preview shows the bare title with\n * no brand context.\n * - It is also the recommended `application-name` meta tag value\n * and a useful default for JSON-LD `Organization.name` schema\n * when the application ships structured data.\n *\n * **Why `defaultOgImageUrl` is OPTIONAL**:\n * - Each page's `WebsitePageManifest.metaLabelKeys.ogImageUrl`\n * takes precedence; the site-level default is a fallback for\n * pages that did not commit to a hero image.\n * - Marketing surfaces shipping no OG images at all (acceptable\n * for a placeholder build) should not be forced to author one.\n *\n * **Why `sitemap?` and `robots?` are OPTIONAL** (with sensible\n * defaults inside each sub-schema):\n * - The marketing-site default ships a public surface — every\n * page indexable, no AI-crawler restrictions, no per-path\n * disallow. Forcing every consumer to author both blocks just\n * to land in that default state would pollute the call site.\n * - Omitting either block at the call site is equivalent to\n * authoring `{}` — `WebsiteSitemapConfig.enabled` defaults to\n * `true`, `WebsiteRobotsConfig.policy` defaults to `'index'`.\n * The per-block field rationale lives on each sub-schema.\n * - `llmsTxt?` is similarly optional, but with a different default\n * mechanic: omitting the block means \"auto-detect from file\n * presence\" rather than \"use a static schema default\". The helper\n * `readLlmsTxtBody(...)` publishes when `src/content/llms.txt`\n * exists and returns `null` otherwise.\n *\n * **Validation**:\n * - `siteName` is `z.string().min(1)` — empty strings would emit\n * `<meta property=\"og:site_name\" content=\"\" />` which social\n * platforms render as a blank line.\n * - `defaultOgImageUrl` (when present) is `z.url()`.\n * - `sitemap` / `robots` validation lives on the sub-schemas.\n */\nconst _WebsiteSeoConfigSchema = z\n .object({\n siteName: z\n .string()\n .min(1, { error: 'WebsiteSeoConfig.siteName must be a non-empty string' }),\n defaultOgImageUrl: z\n .url({ error: 'WebsiteSeoConfig.defaultOgImageUrl must be a fully-qualified URL' })\n .optional(),\n /**\n * Sitemap policy block. Optional — omitting it is equivalent to\n * `{ enabled: true }` (the marketing-site default). See\n * `WebsiteSitemapConfigSchema` for field-by-field rationale.\n */\n sitemap: _WebsiteSitemapConfigSchema.optional(),\n /**\n * Robots.txt policy block. Optional — omitting it is equivalent\n * to `{ policy: 'index' }` (the marketing-site default). See\n * `WebsiteRobotsConfigSchema` for field-by-field rationale.\n */\n robots: _WebsiteRobotsConfigSchema.optional(),\n /**\n * `llms.txt` publication policy. Optional — omitting it means\n * \"auto-detect from file presence\". See\n * `WebsiteLlmsTxtConfigSchema` for the serving contract.\n */\n llmsTxt: _WebsiteLlmsTxtConfigSchema.optional(),\n });\n\nexport const WebsiteSeoConfigSchema: typeof _WebsiteSeoConfigSchema = _WebsiteSeoConfigSchema;\n\nexport type WebsiteSeoConfig = z.infer<typeof _WebsiteSeoConfigSchema>;\n/** @wildo_source:part:end saas.website.root-config.seo */\n\n/**\n * @wildo_source:part:start saas.website.root-config.navigation facet:layer:shared facet:family:website\n *\n * `WebsiteNavigationConfig` — site-wide header/footer slot bindings.\n *\n * The framework intentionally ships no concrete header or footer. The\n * application authors both as React components, registers them once here, and\n * the page layout renders the site-wide chrome around every page.\n *\n * Both fields are typed as `unknown` at the schema layer with the\n * `WebsiteNavigationConfig` interface narrowing them to\n * `ComponentType<unknown>` — same rationale as\n * `WebsiteSectionDefinition.Component` and `WebsitePageManifest.pageComponent`.\n */\nconst _WebsiteNavigationConfigSchema = z.object({\n headerComponent: z.unknown(),\n footerComponent: z.unknown(),\n});\n\nexport const WebsiteNavigationConfigSchema: typeof _WebsiteNavigationConfigSchema =\n _WebsiteNavigationConfigSchema;\n\nexport interface WebsiteNavigationConfig\n extends z.infer<typeof _WebsiteNavigationConfigSchema> {\n headerComponent: ComponentType<unknown>;\n footerComponent: ComponentType<unknown>;\n}\n/** @wildo_source:part:end saas.website.root-config.navigation */\n\n/**\n * @wildo_source:part:start saas.website.root-config.schema facet:layer:shared facet:family:website\n *\n * `WebsiteRootConfig` — the site-level configuration consumed by:\n * - the Astro adapter to drive i18n routing, page mounting,\n * and design-token CSS emission,\n * - the website audit command to validate label-pack\n * coverage and SEO structured-data conformance,\n * - the runtime `<WebsitePageLayout>` to render navigation\n * and apply design tokens.\n *\n * **Authoring convention**: each marketing-site service exports a single\n * `WEBSITE_ROOT_CONFIG: WebsiteRootConfig` from `src/website-root.config.ts`.\n * The Astro adapter and the audit command\n * both consume this canonical entry point; there is exactly one site\n * config per marketing site (no per-page or per-section overrides at\n * this level — overrides flow through the L0/L1/L1.5 component\n * registration hierarchy described in D6).\n *\n * # Scope\n *\n * This block carries site-level chrome and registry data only. Per-locale\n * routing (`locales` / `defaultLocale`) and the public origin belong in the\n * per-service `wildo.website.config.ts` declaration, avoiding duplicated\n * configuration that could diverge during site generation.\n *\n * **Field-by-field rationale**:\n *\n * - `pageManifests` — the registry of every page on the site. Indexed\n * downstream by `manifest.ref`. Refs MUST be unique (refinement\n * below).\n * - `designTokens` — see `WebsiteDesignTokensSchema`.\n * - `navigation` — see `WebsiteNavigationConfigSchema`.\n * - `chromeLabelKeys` — OPTIONAL. The set of fully-composed label keys\n * the site-wide chrome (header / footer / any global slot the\n * consumer renders outside a section context) reads via\n * `useWebsiteLabelByKey(...)`. Declared here, NOT in section\n * `expectedLabelKeys`, because chrome is rendered outside the\n * page+section context — `useWebsiteLabel(slot)` is structurally\n * unavailable to it. Threading these through the label-pack\n * validator (a) silences the always-on \"orphaned chrome key\" build\n * warnings and (b) lets the build fail fast when a chrome key is\n * missing from a locale pack, the same way section keys do.\n * Defaults to an empty array; sites with no chrome label reads\n * may omit the field entirely.\n * - `seo` — site-level branding + hero-image fallback (see\n * `WebsiteSeoConfigSchema`). The marketing-site origin USED to live\n * here as `seo.siteOrigin`; it now lives only on\n * `WildoMarketingWebsiteConfig.publicOrigin`.\n * - `skipToMainContentLabelKey` — see field JSDoc below.\n *\n * **Refinements**:\n * - `pageManifests[].ref` uniqueness — the registry is keyed by ref;\n * duplicates would silently overwrite each other in the lookup\n * table the bridge uses for cross-page links.\n * - `pageManifests[].routePath` uniqueness — two pages cannot mount\n * at the same path (Astro would error at build time, but we\n * surface it earlier here with a clearer diagnostic).\n * - `chromeLabelKeys` uniqueness — duplicates indicate copy-paste\n * bugs at the consumer's call site; surface them here.\n */\nconst _WebsiteRootConfigSchema = z\n .object({\n pageManifests: z.array(WebsitePageManifestSchema).min(1, {\n error: 'WebsiteRootConfig must declare at least one page manifest',\n }),\n designTokens: WebsiteDesignTokensSchema,\n navigation: _WebsiteNavigationConfigSchema,\n chromeLabelKeys: z.array(WebsiteLabelKeySchema).default([]),\n /**\n * Site-level branding + hero-image fallback used by the Astro\n * head renderer to compose `<meta property=\"og:site_name\">` and\n * the per-page OG image fallback. The marketing-site origin\n * (`siteOrigin`) USED to live here; it was moved to\n * `WildoMarketingWebsiteConfig.publicOrigin` in the seam-collapse\n * (single source of truth for \"what's the origin this site is\n * reachable at\").\n */\n seo: _WebsiteSeoConfigSchema,\n /**\n * Label key the framework reads to render the \"Skip to main\n * content\" link as the first focusable element on every page\n * Required because:\n * - Skip-to-main is a baseline keyboard-accessibility\n * affordance — WCAG 2.1 §2.4.1 \"Bypass Blocks\" (Level A)\n * mandates it. A marketing-site framework that omits it\n * fails AA for the entire surface; making the contract\n * opt-in invites that regression on every new consumer.\n * - The text is announced verbatim by assistive tech, so it\n * MUST be localized — a hardcoded English fallback would\n * speak \"Skip to main content\" to French users. Forcing the\n * key through the label-pack validation surface means the\n * dev server fails fast when the consumer forgets to add\n * the entry to a locale pack.\n * - The framework (NOT the consumer's chrome) owns rendering\n * the skip link because the chrome is itself the obstacle\n * the link bypasses — a chrome-side implementation would\n * either be inside the chrome (impossible to focus before\n * the chrome) or above it (creating a structural surface\n * the framework needs anyway).\n *\n * It is validated as a chrome key without being listed in\n * `chromeLabelKeys`: the label-pack loader adds it to the effective\n * chrome key set (`composeEffectiveChromeLabelKeys` in\n * `astro/label-pack-loader.ts`), so consumers cannot forget it.\n */\n skipToMainContentLabelKey: WebsiteLabelKeySchema,\n })\n .superRefine((config, ctx) => {\n const seenRefs = new Set<string>();\n const seenRoutePaths = new Set<string>();\n for (const [index, manifest] of config.pageManifests.entries()) {\n const refKey = manifest.ref as unknown as string;\n if (seenRefs.has(refKey)) {\n ctx.addIssue({\n code: 'custom',\n path: ['pageManifests', index, 'ref'],\n message: `Duplicate page ref \"${refKey}\" in WebsiteRootConfig.pageManifests`,\n });\n }\n seenRefs.add(refKey);\n\n const routeKey = manifest.routePath as unknown as string;\n if (seenRoutePaths.has(routeKey)) {\n ctx.addIssue({\n code: 'custom',\n path: ['pageManifests', index, 'routePath'],\n message: `Duplicate routePath \"${routeKey}\" in WebsiteRootConfig.pageManifests`,\n });\n }\n seenRoutePaths.add(routeKey);\n }\n\n /**\n * `chromeLabelKeys` uniqueness — a duplicate is harmless at\n * runtime (the validator de-dupes via a Set) but always indicates\n * a copy-paste bug at the consumer's call site. Surfacing it here\n * keeps the chrome contract tidy and matches the discipline applied\n * to `pageManifests[].ref` and `locales`.\n */\n const seenChromeKeys = new Set<string>();\n for (const [index, key] of config.chromeLabelKeys.entries()) {\n const keyStr = key as unknown as string;\n if (seenChromeKeys.has(keyStr)) {\n ctx.addIssue({\n code: 'custom',\n path: ['chromeLabelKeys', index],\n message: `Duplicate chrome label key \"${keyStr}\" in WebsiteRootConfig.chromeLabelKeys`,\n });\n }\n seenChromeKeys.add(keyStr);\n }\n });\n\nexport const WebsiteRootConfigSchema: typeof _WebsiteRootConfigSchema = _WebsiteRootConfigSchema;\n\n/**\n * Runtime shape of `WebsiteRootConfig`.\n *\n * Hand-written (NOT a bare `z.infer<typeof _WebsiteRootConfigSchema>`)\n * because `z.infer` walks the schema tree and would type\n * `pageManifests[number].pageComponent`, `navigation.headerComponent`\n * and `navigation.footerComponent` as `unknown` (their schema field\n * type), losing the `ComponentType<unknown>` narrowing carried by the\n * hand-written `WebsitePageManifest` and `WebsiteNavigationConfig`\n * interfaces. The layout primitives and the Astro helpers consume\n * `WebsiteRootConfig` and must see those\n * fields pre-narrowed; otherwise every consumer would need a local\n * `as ComponentType` cast at the JSX call site, defeating the type-safe\n * component contract.\n *\n * The `Omit` + intersection pattern keeps the rest of the inferred\n * shape (`designTokens`, `seo`, `chromeLabelKeys`,\n * `skipToMainContentLabelKey`) sourced directly from the schema.\n */\nexport type WebsiteRootConfig = Omit<\n z.infer<typeof _WebsiteRootConfigSchema>,\n 'pageManifests' | 'navigation'\n> & {\n pageManifests: WebsitePageManifest[];\n navigation: WebsiteNavigationConfig;\n};\n/** @wildo_source:part:end saas.website.root-config.schema */\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"page-ref.schemas.d.ts","sourceRoot":"","sources":["../../../../src/schemas/refs/page-ref.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,QAAA,MAAM,qBAAqB,0DAQC,CAAC;AAE7B,eAAO,MAAM,oBAAoB,EAAE,OAAO,qBAA6C,CAAC;AAExF,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;;AACnE,oDAAoD"}
1
+ {"version":3,"file":"page-ref.schemas.d.ts","sourceRoot":"","sources":["../../../../../src/schemas/refs/page-ref.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,QAAA,MAAM,qBAAqB,0DAQC,CAAC;AAE7B,eAAO,MAAM,oBAAoB,EAAE,OAAO,qBAA6C,CAAC;AAExF,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;;AACnE,oDAAoD"}
@@ -1 +1 @@
1
- {"version":3,"file":"section-ref.schemas.d.ts","sourceRoot":"","sources":["../../../../src/schemas/refs/section-ref.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,QAAA,MAAM,wBAAwB,6DAQC,CAAC;AAEhC,eAAO,MAAM,uBAAuB,EAAE,OAAO,wBAAmD,CAAC;AAEjG,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;;AACzE,uDAAuD"}
1
+ {"version":3,"file":"section-ref.schemas.d.ts","sourceRoot":"","sources":["../../../../../src/schemas/refs/section-ref.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,QAAA,MAAM,wBAAwB,6DAQC,CAAC;AAEhC,eAAO,MAAM,uBAAuB,EAAE,OAAO,wBAAmD,CAAC;AAEjG,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;;AACzE,uDAAuD"}
@@ -1 +1 @@
1
- {"version":3,"file":"website-section-category.shared.d.ts","sourceRoot":"","sources":["../../../../src/schemas/sections/website-section-category.shared.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,oBAAY,sBAAsB;IAChC,IAAI,SAAS;IACb,GAAG,QAAQ;IACX,uBAAuB,4BAA4B;IACnD,cAAc,mBAAmB;IACjC,OAAO,YAAY;IACnB,OAAO,YAAY;IACnB,YAAY,iBAAiB;IAC7B,GAAG,QAAQ;IACX,UAAU,eAAe;IACzB;;;;;;;;OAQG;IACH,GAAG,QAAQ;CACZ;AAED,eAAO,MAAM,4BAA4B,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,sBAAsB,CAClD,CAAC;AACjC,iEAAiE"}
1
+ {"version":3,"file":"website-section-category.shared.d.ts","sourceRoot":"","sources":["../../../../../src/schemas/sections/website-section-category.shared.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,oBAAY,sBAAsB;IAChC,IAAI,SAAS;IACb,GAAG,QAAQ;IACX,uBAAuB,4BAA4B;IACnD,cAAc,mBAAmB;IACjC,OAAO,YAAY;IACnB,OAAO,YAAY;IACnB,YAAY,iBAAiB;IAC7B,GAAG,QAAQ;IACX,UAAU,eAAe;IACzB;;;;;;;;OAQG;IACH,GAAG,QAAQ;CACZ;AAED,eAAO,MAAM,4BAA4B,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,sBAAsB,CAClD,CAAC;AACjC,iEAAiE"}
@@ -41,6 +41,14 @@ import type { WebsiteStructuredDataReturn } from '../structured-data/website-str
41
41
  * and is caught by nothing until a reviewer reads the page. The
42
42
  * build and `wildo website audit` check the pack against the
43
43
  * DECLARED keys, so declare every slot the component reads.
44
+ * - `anchorId?` — the section's DOM id, so a link (`<a href="#tiers">`)
45
+ * can target it. Belongs to the DEFINITION because a page renders its
46
+ * sections list through `WebsitePageSections`, which places every
47
+ * section itself: an anchor a page author would otherwise write on a
48
+ * hand-placed `<WebsiteSection id=…>` has nowhere else to live. A
49
+ * valid HTML id that is also URL-fragment-safe: a letter, then
50
+ * letters, digits, `-` or `_`. Uniqueness on a page is the author's
51
+ * to keep, like the id attribute it becomes.
44
52
  * - `Component` — the React component that renders the section. Typed
45
53
  * as `ComponentType<unknown>` here because schema-layer cannot lock
46
54
  * the component's prop shape (props vary per section). The factory
@@ -67,6 +75,7 @@ declare const _WebsiteSectionDefinitionSchema: z.ZodObject<{
67
75
  sectionRef: z.core.$ZodBranded<z.ZodString, "WebsiteSectionRef", "out">;
68
76
  category: z.ZodEnum<typeof import("./website-section-category.shared").WebsiteSectionCategory>;
69
77
  expectedLabelKeys: z.ZodReadonly<z.ZodArray<z.core.$ZodBranded<z.ZodString, "WebsiteLabelKey", "out">>>;
78
+ anchorId: z.ZodOptional<z.ZodString>;
70
79
  Component: z.ZodUnknown;
71
80
  getStructuredData: z.ZodOptional<z.ZodUnknown>;
72
81
  }, z.core.$strip>;
@@ -1 +1 @@
1
- {"version":3,"file":"website-section-definition.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../src/schemas/sections/website-section-definition.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAC3C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,2DAA2D,CAAC;AAG7G;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AACH,QAAA,MAAM,+BAA+B;;;;;;iBAMnC,CAAC;AAEH,eAAO,MAAM,8BAA8B,EAAE,OAAO,+BACnB,CAAC;AAElC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,WAAW,wBAAyB,SAAQ,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC;IAC/F,SAAS,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC;IAC9B;;;;;;;;;;;;;;;;;OAiBG;IACH,iBAAiB,CAAC,EAAE,MAAM,2BAA2B,CAAC;CACvD;;AACD,qEAAqE"}
1
+ {"version":3,"file":"website-section-definition.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../../src/schemas/sections/website-section-definition.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAC3C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,2DAA2D,CAAC;AAG7G;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqEG;AACH,QAAA,MAAM,+BAA+B;;;;;;;iBASnC,CAAC;AAEH,eAAO,MAAM,8BAA8B,EAAE,OAAO,+BACnB,CAAC;AAElC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,WAAW,wBAAyB,SAAQ,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC;IAC/F,SAAS,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC;IAC9B;;;;;;;;;;;;;;;;;OAiBG;IACH,iBAAiB,CAAC,EAAE,MAAM,2BAA2B,CAAC;CACvD;;AACD,qEAAqE"}
@@ -42,6 +42,14 @@ import { WebsiteSectionCategorySchema } from './website-section-category.shared.
42
42
  * and is caught by nothing until a reviewer reads the page. The
43
43
  * build and `wildo website audit` check the pack against the
44
44
  * DECLARED keys, so declare every slot the component reads.
45
+ * - `anchorId?` — the section's DOM id, so a link (`<a href="#tiers">`)
46
+ * can target it. Belongs to the DEFINITION because a page renders its
47
+ * sections list through `WebsitePageSections`, which places every
48
+ * section itself: an anchor a page author would otherwise write on a
49
+ * hand-placed `<WebsiteSection id=…>` has nowhere else to live. A
50
+ * valid HTML id that is also URL-fragment-safe: a letter, then
51
+ * letters, digits, `-` or `_`. Uniqueness on a page is the author's
52
+ * to keep, like the id attribute it becomes.
45
53
  * - `Component` — the React component that renders the section. Typed
46
54
  * as `ComponentType<unknown>` here because schema-layer cannot lock
47
55
  * the component's prop shape (props vary per section). The factory
@@ -68,6 +76,9 @@ const _WebsiteSectionDefinitionSchema = z.object({
68
76
  sectionRef: WebsiteSectionRefSchema,
69
77
  category: WebsiteSectionCategorySchema,
70
78
  expectedLabelKeys: z.array(WebsiteLabelKeySchema).readonly(),
79
+ anchorId: z.string().regex(/^[A-Za-z][A-Za-z0-9_-]*$/, {
80
+ error: 'A section anchorId must start with a letter and contain only letters, digits, "-" or "_"',
81
+ }).optional(),
71
82
  Component: z.unknown(),
72
83
  getStructuredData: z.unknown().optional(),
73
84
  });
@@ -1 +1 @@
1
- {"version":3,"file":"website-section-definition.shared.schemas.js","sourceRoot":"","sources":["../../../../src/schemas/sections/website-section-definition.shared.schemas.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;AAChF,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAEtE,OAAO,EAAE,4BAA4B,EAAE,MAAM,mCAAmC,CAAC;AAEjF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AACH,MAAM,+BAA+B,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/C,UAAU,EAAE,uBAAuB;IACnC,QAAQ,EAAE,4BAA4B;IACtC,iBAAiB,EAAE,CAAC,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC,QAAQ,EAAE;IAC5D,SAAS,EAAE,CAAC,CAAC,OAAO,EAAE;IACtB,iBAAiB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CAC1C,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,8BAA8B,GACzC,+BAA+B,CAAC;AA+ClC,qEAAqE","sourcesContent":["import type { ComponentType } from 'react';\nimport { z } from 'zod';\n\nimport { WebsiteLabelKeySchema } from '../label-keys/website-label-key.schemas';\nimport { WebsiteSectionRefSchema } from '../refs/section-ref.schemas';\nimport type { WebsiteStructuredDataReturn } from '../structured-data/website-structured-data.shared.schemas';\nimport { WebsiteSectionCategorySchema } from './website-section-category.shared';\n\n/**\n * @wildo_source:part:start saas.website.sections.definition-schema facet:layer:shared facet:family:website\n *\n * `WebsiteSectionDefinition` — the runtime contract returned by the\n * `defineWebsiteSection({ … })` factory (authored as a separate\n * `core/define-website-section.tsx` module in Phase 2 because it carries\n * React types in its inputs). The shape is declared in `schemas/`\n * because it is the **observable contract** the page-manifest\n * registration, the label-pack validator, `renderPageStructuredData` and the\n * structured-data audit depend on.\n *\n * **Shape rationale** (each field carries semantic weight — see\n * `design-philosophy.mdc`):\n *\n * - `sectionRef` — the section's identity inside its hosting page.\n * Uniqueness is enforced **per page** at manifest registration time\n * (a section can be reused with the same `sectionRef` across pages\n * when its label-key root is intentionally shared).\n * - `category` — see `WebsiteSectionCategory` JSDoc; tags the section\n * for skill-mapping and analytics labelling. It does NOT select the\n * section's structured data.\n * - `expectedLabelKeys` — the **fully-composed** label keys the section\n * commits to consuming. The plan originally proposed leaf-relative\n * keys (`'headline'`) resolved at runtime against the\n * page+section context, but storing the fully-composed key on the\n * definition has three concrete benefits:\n * 1. The label-pack validator (`validateLabelPackAgainstManifest`)\n * runs as a pure function with no need to walk the JSX tree at\n * build time to discover the leaf-to-key resolution.\n * 2. The contract is auditable from outside the React render\n * graph — `wildo website audit` can list a page's required keys\n * without booting React.\n * 3. The leaf-relative ergonomics (`useWebsiteLabel('headline')`)\n * survive — the hook composes the leaf with the page+section\n * context and looks the result up in the active pack. It does\n * NOT check the key against `expectedLabelKeys`: a leaf the\n * section never declared renders its key (with a console warning)\n * and is caught by nothing until a reviewer reads the page. The\n * build and `wildo website audit` check the pack against the\n * DECLARED keys, so declare every slot the component reads.\n * - `Component` — the React component that renders the section. Typed\n * as `ComponentType<unknown>` here because schema-layer cannot lock\n * the component's prop shape (props vary per section). The factory\n * function (`defineWebsiteSection<TProps>(…)`) preserves the precise\n * prop type at the consumer call site via generics; this schema only\n * guards the runtime shape of \"is callable as a React component\".\n * - `getStructuredData?` — optional pure function the SEO/AEO/GEO\n * builder library invokes at static-build time to materialize JSON-LD\n * for the section. The return type is intentionally `unknown` here;\n * nothing narrows it by `category` — the section returns whatever\n * vocabulary it emits, and `wildo website audit` reconciles that\n * against the application's Schema.org catalog.\n *\n * **Why not `z.custom<ComponentType>(…)`**: a `z.custom` predicate\n * cannot meaningfully validate \"is a React component\" beyond\n * `typeof === 'function'`, and the false-positive cost (any function\n * passes) is too high for the value (none — the consumer either\n * imports a real component or fails at JSX render time with a clearer\n * error). Using `z.unknown()` + a TS-side type cast keeps the runtime\n * cost zero and the type-safety contract entirely on the TS side\n * (where it actually has teeth via the factory's generic).\n */\nconst _WebsiteSectionDefinitionSchema = z.object({\n sectionRef: WebsiteSectionRefSchema,\n category: WebsiteSectionCategorySchema,\n expectedLabelKeys: z.array(WebsiteLabelKeySchema).readonly(),\n Component: z.unknown(),\n getStructuredData: z.unknown().optional(),\n});\n\nexport const WebsiteSectionDefinitionSchema: typeof _WebsiteSectionDefinitionSchema =\n _WebsiteSectionDefinitionSchema;\n\n/**\n * Runtime shape of `WebsiteSectionDefinition`. Note: `Component` and\n * `getStructuredData` are typed precisely on the factory return type\n * (`defineWebsiteSection<TProps>` in Phase 2); the schema-inferred\n * type is the lowest common denominator used at the persistence /\n * validation boundary only.\n *\n * **Why `ComponentType<any>` and NOT `ComponentType<unknown>`**:\n * React component props are CONTRAVARIANT in `ComponentType<T>` —\n * a narrower `ComponentType<{ foo: string }>` is NOT assignable to\n * `ComponentType<unknown>` (because a caller could pass an `unknown`\n * the narrower component cannot accept). This made every consumer\n * who built sections via `defineWebsiteSection<TProps>(…)` unable\n * to feed the resulting `TypedWebsiteSectionDefinition<TProps>` to\n * helpers typed against the bare `WebsiteSectionDefinition` —\n * collectors, validators, and registries. Using `any` here is the\n * exact escape hatch the React typings themselves use for\n * heterogeneous component containers (`React.forwardRef`,\n * `React.lazy`'s internal component slot, etc.) and matches the\n * runtime contract: the framework NEVER instantiates these\n * components directly with arbitrary props — the consumer's JSX\n * supplies the right props at the call site.\n */\nexport interface WebsiteSectionDefinition extends z.infer<typeof _WebsiteSectionDefinitionSchema> {\n Component: ComponentType<any>;\n /**\n * Optional pure function the SEO/AEO/GEO renderer invokes at\n * static-build time to materialize JSON-LD for the section.\n *\n * **Why typed as `WebsiteStructuredDataReturn` (not `unknown`)** —\n * a typed return shape gives section authors autocomplete on\n * `@type` / `@id` / `@context` (the framework-owned fields) while\n * keeping all other fields free-form (Schema.org has hundreds of\n * optional fields per entity; locking them in TS would be both\n * impractical and brittle as Schema.org evolves). The companion\n * catalog reconciliation surface catches missing-required and\n * deprecated-field-used at the SEMANTIC layer (warning-only per\n * D-AG4), so the runtime type stays loose.\n *\n * Returns `null`, `undefined`, or `[]` when the section has no\n * structured-data contribution for the current state — the\n * renderer short-circuits all three.\n */\n getStructuredData?: () => WebsiteStructuredDataReturn;\n}\n/** @wildo_source:part:end saas.website.sections.definition-schema */\n"]}
1
+ {"version":3,"file":"website-section-definition.shared.schemas.js","sourceRoot":"","sources":["../../../../../src/schemas/sections/website-section-definition.shared.schemas.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;AAChF,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAEtE,OAAO,EAAE,4BAA4B,EAAE,MAAM,mCAAmC,CAAC;AAEjF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqEG;AACH,MAAM,+BAA+B,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/C,UAAU,EAAE,uBAAuB;IACnC,QAAQ,EAAE,4BAA4B;IACtC,iBAAiB,EAAE,CAAC,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC,QAAQ,EAAE;IAC5D,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,0BAA0B,EAAE;QACrD,KAAK,EAAE,0FAA0F;KAClG,CAAC,CAAC,QAAQ,EAAE;IACb,SAAS,EAAE,CAAC,CAAC,OAAO,EAAE;IACtB,iBAAiB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CAC1C,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,8BAA8B,GACzC,+BAA+B,CAAC;AA+ClC,qEAAqE","sourcesContent":["import type { ComponentType } from 'react';\nimport { z } from 'zod';\n\nimport { WebsiteLabelKeySchema } from '../label-keys/website-label-key.schemas';\nimport { WebsiteSectionRefSchema } from '../refs/section-ref.schemas';\nimport type { WebsiteStructuredDataReturn } from '../structured-data/website-structured-data.shared.schemas';\nimport { WebsiteSectionCategorySchema } from './website-section-category.shared';\n\n/**\n * @wildo_source:part:start saas.website.sections.definition-schema facet:layer:shared facet:family:website\n *\n * `WebsiteSectionDefinition` — the runtime contract returned by the\n * `defineWebsiteSection({ … })` factory (authored as a separate\n * `core/define-website-section.tsx` module in Phase 2 because it carries\n * React types in its inputs). The shape is declared in `schemas/`\n * because it is the **observable contract** the page-manifest\n * registration, the label-pack validator, `renderPageStructuredData` and the\n * structured-data audit depend on.\n *\n * **Shape rationale** (each field carries semantic weight — see\n * `design-philosophy.mdc`):\n *\n * - `sectionRef` — the section's identity inside its hosting page.\n * Uniqueness is enforced **per page** at manifest registration time\n * (a section can be reused with the same `sectionRef` across pages\n * when its label-key root is intentionally shared).\n * - `category` — see `WebsiteSectionCategory` JSDoc; tags the section\n * for skill-mapping and analytics labelling. It does NOT select the\n * section's structured data.\n * - `expectedLabelKeys` — the **fully-composed** label keys the section\n * commits to consuming. The plan originally proposed leaf-relative\n * keys (`'headline'`) resolved at runtime against the\n * page+section context, but storing the fully-composed key on the\n * definition has three concrete benefits:\n * 1. The label-pack validator (`validateLabelPackAgainstManifest`)\n * runs as a pure function with no need to walk the JSX tree at\n * build time to discover the leaf-to-key resolution.\n * 2. The contract is auditable from outside the React render\n * graph — `wildo website audit` can list a page's required keys\n * without booting React.\n * 3. The leaf-relative ergonomics (`useWebsiteLabel('headline')`)\n * survive — the hook composes the leaf with the page+section\n * context and looks the result up in the active pack. It does\n * NOT check the key against `expectedLabelKeys`: a leaf the\n * section never declared renders its key (with a console warning)\n * and is caught by nothing until a reviewer reads the page. The\n * build and `wildo website audit` check the pack against the\n * DECLARED keys, so declare every slot the component reads.\n * - `anchorId?` — the section's DOM id, so a link (`<a href=\"#tiers\">`)\n * can target it. Belongs to the DEFINITION because a page renders its\n * sections list through `WebsitePageSections`, which places every\n * section itself: an anchor a page author would otherwise write on a\n * hand-placed `<WebsiteSection id=…>` has nowhere else to live. A\n * valid HTML id that is also URL-fragment-safe: a letter, then\n * letters, digits, `-` or `_`. Uniqueness on a page is the author's\n * to keep, like the id attribute it becomes.\n * - `Component` — the React component that renders the section. Typed\n * as `ComponentType<unknown>` here because schema-layer cannot lock\n * the component's prop shape (props vary per section). The factory\n * function (`defineWebsiteSection<TProps>(…)`) preserves the precise\n * prop type at the consumer call site via generics; this schema only\n * guards the runtime shape of \"is callable as a React component\".\n * - `getStructuredData?` — optional pure function the SEO/AEO/GEO\n * builder library invokes at static-build time to materialize JSON-LD\n * for the section. The return type is intentionally `unknown` here;\n * nothing narrows it by `category` — the section returns whatever\n * vocabulary it emits, and `wildo website audit` reconciles that\n * against the application's Schema.org catalog.\n *\n * **Why not `z.custom<ComponentType>(…)`**: a `z.custom` predicate\n * cannot meaningfully validate \"is a React component\" beyond\n * `typeof === 'function'`, and the false-positive cost (any function\n * passes) is too high for the value (none — the consumer either\n * imports a real component or fails at JSX render time with a clearer\n * error). Using `z.unknown()` + a TS-side type cast keeps the runtime\n * cost zero and the type-safety contract entirely on the TS side\n * (where it actually has teeth via the factory's generic).\n */\nconst _WebsiteSectionDefinitionSchema = z.object({\n sectionRef: WebsiteSectionRefSchema,\n category: WebsiteSectionCategorySchema,\n expectedLabelKeys: z.array(WebsiteLabelKeySchema).readonly(),\n anchorId: z.string().regex(/^[A-Za-z][A-Za-z0-9_-]*$/, {\n error: 'A section anchorId must start with a letter and contain only letters, digits, \"-\" or \"_\"',\n }).optional(),\n Component: z.unknown(),\n getStructuredData: z.unknown().optional(),\n});\n\nexport const WebsiteSectionDefinitionSchema: typeof _WebsiteSectionDefinitionSchema =\n _WebsiteSectionDefinitionSchema;\n\n/**\n * Runtime shape of `WebsiteSectionDefinition`. Note: `Component` and\n * `getStructuredData` are typed precisely on the factory return type\n * (`defineWebsiteSection<TProps>` in Phase 2); the schema-inferred\n * type is the lowest common denominator used at the persistence /\n * validation boundary only.\n *\n * **Why `ComponentType<any>` and NOT `ComponentType<unknown>`**:\n * React component props are CONTRAVARIANT in `ComponentType<T>` —\n * a narrower `ComponentType<{ foo: string }>` is NOT assignable to\n * `ComponentType<unknown>` (because a caller could pass an `unknown`\n * the narrower component cannot accept). This made every consumer\n * who built sections via `defineWebsiteSection<TProps>(…)` unable\n * to feed the resulting `TypedWebsiteSectionDefinition<TProps>` to\n * helpers typed against the bare `WebsiteSectionDefinition` —\n * collectors, validators, and registries. Using `any` here is the\n * exact escape hatch the React typings themselves use for\n * heterogeneous component containers (`React.forwardRef`,\n * `React.lazy`'s internal component slot, etc.) and matches the\n * runtime contract: the framework NEVER instantiates these\n * components directly with arbitrary props — the consumer's JSX\n * supplies the right props at the call site.\n */\nexport interface WebsiteSectionDefinition extends z.infer<typeof _WebsiteSectionDefinitionSchema> {\n Component: ComponentType<any>;\n /**\n * Optional pure function the SEO/AEO/GEO renderer invokes at\n * static-build time to materialize JSON-LD for the section.\n *\n * **Why typed as `WebsiteStructuredDataReturn` (not `unknown`)** —\n * a typed return shape gives section authors autocomplete on\n * `@type` / `@id` / `@context` (the framework-owned fields) while\n * keeping all other fields free-form (Schema.org has hundreds of\n * optional fields per entity; locking them in TS would be both\n * impractical and brittle as Schema.org evolves). The companion\n * catalog reconciliation surface catches missing-required and\n * deprecated-field-used at the SEMANTIC layer (warning-only per\n * D-AG4), so the runtime type stays loose.\n *\n * Returns `null`, `undefined`, or `[]` when the section has no\n * structured-data contribution for the current state — the\n * renderer short-circuits all three.\n */\n getStructuredData?: () => WebsiteStructuredDataReturn;\n}\n/** @wildo_source:part:end saas.website.sections.definition-schema */\n"]}