@shoalrun/cli 1.0.0 → 1.1.0

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 (406) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/CODE-REFERENCES.md +19 -0
  3. package/README.md +13 -4
  4. package/adapters/typescript/README.md +27 -0
  5. package/adapters/typescript/shoalrun-reference.mjs +260 -0
  6. package/agent-docs/.well-known/shoalrun/navigation/project-updates-50f26fe22670.txt +3 -1
  7. package/agent-docs/SNAPSHOT.json +5 -5
  8. package/agent-docs/api-docs/code-reference-settings.md +2 -2
  9. package/agent-docs/api-docs/code-reference-settings.sections/catalog-settings.md +2 -2
  10. package/agent-docs/api-docs/code-reference-settings.sections/complete-reference-page.md +2 -2
  11. package/agent-docs/api-docs/code-reference-settings.sections/converter-snapshot.md +2 -2
  12. package/agent-docs/api-docs/code-reference-settings.sections/descriptions-type-links-and-source-links.md +2 -2
  13. package/agent-docs/api-docs/code-reference-settings.sections/html-markdown-and-agents.md +2 -2
  14. package/agent-docs/api-docs/code-reference-settings.sections/metadata-and-selection.md +2 -2
  15. package/agent-docs/api-docs/code-reference-settings.sections/publication-sources.md +2 -2
  16. package/agent-docs/api-docs/code-reference-settings.sections/python-adapter-limits.md +2 -2
  17. package/agent-docs/api-docs/code-reference-settings.sections/related-reading.md +2 -2
  18. package/agent-docs/api-docs/code-reference-settings.sections/symbol-fields.md +2 -2
  19. package/agent-docs/api-docs/code-references.md +9 -1
  20. package/agent-docs/api-docs/code-references.sections/a-real-typescript-example.md +38 -0
  21. package/agent-docs/api-docs/code-references.sections/add-python-source.md +56 -0
  22. package/agent-docs/api-docs/code-references.sections/before-you-start.md +40 -0
  23. package/agent-docs/api-docs/code-references.sections/build-and-verify.md +47 -0
  24. package/agent-docs/api-docs/code-references.sections/choose-the-reading-layout.md +46 -0
  25. package/agent-docs/api-docs/code-references.sections/extract-and-validate-a-snapshot.md +52 -0
  26. package/agent-docs/api-docs/code-references.sections/html-markdown-and-agents.md +42 -0
  27. package/agent-docs/api-docs/code-references.sections/register-the-catalog-and-navigation.md +58 -0
  28. package/agent-docs/api-docs/code-references.sections/related-reading.md +40 -0
  29. package/agent-docs/api-docs/code-references.sections/versions-profiles-and-delivery.md +40 -0
  30. package/agent-docs/api-docs/typescript-references.md +93 -0
  31. package/agent-docs/author/components/for-facet.md +127 -0
  32. package/agent-docs/author/components/only.md +3 -1
  33. package/agent-docs/author/components.md +2 -1
  34. package/agent-docs/author/reader-views.md +2 -2
  35. package/agent-docs/author/reader-views.sections/configure-the-choices.md +2 -2
  36. package/agent-docs/author/reader-views.sections/connect-your-own-control.md +2 -2
  37. package/agent-docs/author/reader-views.sections/how-it-works-for-agents.md +2 -2
  38. package/agent-docs/author/reader-views.sections/mark-pages.md +2 -2
  39. package/agent-docs/author/reader-views.sections/mark-sections-and-add-an-entry-choice.md +2 -2
  40. package/agent-docs/author/reader-views.sections/place-filter-controls.md +2 -2
  41. package/agent-docs/author/reader-views.sections/try-the-controls-here.md +2 -2
  42. package/agent-docs/changelog/facet-typescript.md +54 -0
  43. package/agent-docs/changelog.md +8 -0
  44. package/agent-docs/config/examples.md +2 -2
  45. package/agent-docs/config/examples.sections/feature-showcase.md +2 -2
  46. package/agent-docs/config/examples.sections/minimal-site.md +1 -1
  47. package/agent-docs/config/examples.sections/multilingual-help-center.md +1 -1
  48. package/agent-docs/config/examples.sections/parcel-api-documentation.md +1 -1
  49. package/agent-docs/config/examples.sections/related-reading.md +1 -1
  50. package/agent-docs/config/examples.sections/shared-company-preset.md +1 -1
  51. package/agent-docs/config/outputs.md +12 -12
  52. package/agent-docs/config/outputs.sections/exports.md +12 -12
  53. package/agent-docs/config/outputs.sections/metadata.md +1 -1
  54. package/agent-docs/config/outputs.sections/pdf-output.md +1 -1
  55. package/agent-docs/config/outputs.sections/related-reading.md +1 -1
  56. package/agent-docs/config/site.md +17 -17
  57. package/agent-docs/config/site.sections/related-reading.md +1 -1
  58. package/agent-docs/config/site.sections/theme.md +1 -1
  59. package/agent-docs/configuration.md +1 -1
  60. package/agent-docs/configuration.sections/start-with-one-working-file.md +1 -1
  61. package/agent-docs/content-delivery/api.md +10 -2
  62. package/agent-docs/content-delivery/api.sections/errors.md +52 -0
  63. package/agent-docs/content-delivery/api.sections/generated-typescript-reference.md +36 -0
  64. package/agent-docs/content-delivery/api.sections/html-markdown-and-agents.md +36 -0
  65. package/agent-docs/content-delivery/api.sections/mount-html.md +42 -0
  66. package/agent-docs/content-delivery/api.sections/open-a-rest-source.md +55 -0
  67. package/agent-docs/content-delivery/api.sections/open-a-source.md +51 -0
  68. package/agent-docs/content-delivery/api.sections/read-a-snapshot.md +55 -0
  69. package/agent-docs/content-delivery/api.sections/related-reading.md +42 -0
  70. package/agent-docs/content-delivery/api.sections/validate-a-manifest.md +40 -0
  71. package/agent-docs/content-delivery/client.md +9 -1
  72. package/agent-docs/content-delivery/sdk/all.md +1772 -0
  73. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentanchor-exportedid.md +34 -0
  74. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentanchor-id.md +34 -0
  75. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentanchor-title.md +34 -0
  76. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentanchor.md +44 -0
  77. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentasset-mediatype.md +34 -0
  78. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentasset.md +40 -0
  79. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentdeliveryerror-code.md +34 -0
  80. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentdeliveryerror.md +46 -0
  81. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentdeliverylimits-manifestbytes.md +36 -0
  82. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentdeliverylimits-resourcebytes.md +36 -0
  83. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentdeliverylimits-resources.md +36 -0
  84. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentdeliverylimits.md +44 -0
  85. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contenterrorcode.md +32 -0
  86. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentfile-bytes.md +34 -0
  87. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentfile-digest.md +34 -0
  88. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentfile-path.md +34 -0
  89. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentfile.md +44 -0
  90. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentformat.md +32 -0
  91. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentlink-asset.md +34 -0
  92. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentlink-fragment.md +34 -0
  93. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentlink-href.md +34 -0
  94. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentlink-key.md +34 -0
  95. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentlink-kind.md +34 -0
  96. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentlink-query.md +34 -0
  97. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentlink-ref.md +34 -0
  98. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentlink.md +52 -0
  99. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentmanifest-assets.md +34 -0
  100. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentmanifest-diagnostics.md +34 -0
  101. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentmanifest-digest.md +34 -0
  102. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentmanifest-files.md +34 -0
  103. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentmanifest-kind.md +34 -0
  104. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentmanifest-publication.md +34 -0
  105. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentmanifest-publicbase.md +34 -0
  106. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentmanifest-resources.md +34 -0
  107. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentmanifest-review.md +34 -0
  108. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentmanifest-schemaversion.md +34 -0
  109. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentmanifest.md +68 -0
  110. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentmount-anchors.md +36 -0
  111. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentmount-links.md +36 -0
  112. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentmount-unmount.md +36 -0
  113. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentmount.md +49 -0
  114. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentpublication-channel.md +34 -0
  115. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentpublication-language.md +34 -0
  116. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentpublication-product.md +34 -0
  117. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentpublication-version.md +34 -0
  118. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentpublication.md +46 -0
  119. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentref.md +43 -0
  120. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentrefkey.md +42 -0
  121. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentrepresentation-anchors.md +34 -0
  122. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentrepresentation-links.md +34 -0
  123. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentrepresentation.md +42 -0
  124. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentresource-assets.md +34 -0
  125. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentresource-canonicalurl.md +34 -0
  126. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentresource-externaldependencies.md +34 -0
  127. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentresource-publication.md +36 -0
  128. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentresource-ref.md +36 -0
  129. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentresource-representations.md +36 -0
  130. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentresource-source.md +34 -0
  131. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentresource-summary.md +36 -0
  132. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentresource-title.md +36 -0
  133. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentresource-type.md +36 -0
  134. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentresource.md +65 -0
  135. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentresourcekey.md +42 -0
  136. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentrestentry-key.md +34 -0
  137. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentrestentry-resource.md +34 -0
  138. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentrestentry.md +42 -0
  139. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentresterror-error.md +34 -0
  140. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentresterror.md +43 -0
  141. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentresterrorcode.md +32 -0
  142. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentrestlist-publication.md +34 -0
  143. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentrestlist-resources.md +34 -0
  144. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentrestlist-revision.md +34 -0
  145. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentrestlist-schemaversion.md +34 -0
  146. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentrestlist.md +46 -0
  147. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentrestresource-key.md +34 -0
  148. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentrestresource-publication.md +34 -0
  149. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentrestresource-resource.md +34 -0
  150. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentrestresource-revision.md +34 -0
  151. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentrestresource-schemaversion.md +34 -0
  152. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentrestresource.md +48 -0
  153. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentresult-content.md +36 -0
  154. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentresult-format.md +36 -0
  155. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentresult-representation.md +36 -0
  156. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentresult-resource.md +36 -0
  157. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentresult-revision.md +36 -0
  158. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentresult.md +48 -0
  159. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentsnapshot-dispose.md +36 -0
  160. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentsnapshot-get.md +45 -0
  161. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentsnapshot-list.md +38 -0
  162. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentsnapshot-publication.md +36 -0
  163. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentsnapshot-revision.md +36 -0
  164. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-contentsnapshot.md +47 -0
  165. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-manifestcontentsourceoptions-fetch.md +36 -0
  166. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-manifestcontentsourceoptions-limits.md +36 -0
  167. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-manifestcontentsourceoptions-manifesturl.md +36 -0
  168. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-manifestcontentsourceoptions-signal.md +36 -0
  169. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-manifestcontentsourceoptions.md +46 -0
  170. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-mountcontent.md +48 -0
  171. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-openmanifestcontentsource.md +54 -0
  172. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-openrestcontentsource.md +44 -0
  173. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-restcontentsourceoptions-baseurl.md +36 -0
  174. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-restcontentsourceoptions-fetch.md +36 -0
  175. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-restcontentsourceoptions-limits.md +36 -0
  176. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-restcontentsourceoptions-publication.md +36 -0
  177. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-restcontentsourceoptions-revision.md +36 -0
  178. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-restcontentsourceoptions-signal.md +36 -0
  179. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-restcontentsourceoptions.md +50 -0
  180. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content-validatecontentmanifest.md +45 -0
  181. package/agent-docs/content-delivery/sdk/all.sections/shoalrun-content.md +65 -0
  182. package/agent-docs/content-delivery/sdk/content-anchor/exported-id.md +25 -0
  183. package/agent-docs/content-delivery/sdk/content-anchor/id.md +25 -0
  184. package/agent-docs/content-delivery/sdk/content-anchor/title.md +25 -0
  185. package/agent-docs/content-delivery/sdk/content-anchor.md +71 -0
  186. package/agent-docs/content-delivery/sdk/content-asset/media-type.md +25 -0
  187. package/agent-docs/content-delivery/sdk/content-asset.md +43 -0
  188. package/agent-docs/content-delivery/sdk/content-delivery-error/code.md +25 -0
  189. package/agent-docs/content-delivery/sdk/content-delivery-error.md +53 -0
  190. package/agent-docs/content-delivery/sdk/content-delivery-limits/manifest-bytes.md +27 -0
  191. package/agent-docs/content-delivery/sdk/content-delivery-limits/resource-bytes.md +27 -0
  192. package/agent-docs/content-delivery/sdk/content-delivery-limits/resources.md +27 -0
  193. package/agent-docs/content-delivery/sdk/content-delivery-limits.md +77 -0
  194. package/agent-docs/content-delivery/sdk/content-error-code.md +23 -0
  195. package/agent-docs/content-delivery/sdk/content-file/bytes.md +25 -0
  196. package/agent-docs/content-delivery/sdk/content-file/digest.md +25 -0
  197. package/agent-docs/content-delivery/sdk/content-file/path.md +25 -0
  198. package/agent-docs/content-delivery/sdk/content-file.md +71 -0
  199. package/agent-docs/content-delivery/sdk/content-format.md +23 -0
  200. package/agent-docs/content-delivery/sdk/content-link/asset.md +25 -0
  201. package/agent-docs/content-delivery/sdk/content-link/fragment.md +25 -0
  202. package/agent-docs/content-delivery/sdk/content-link/href.md +25 -0
  203. package/agent-docs/content-delivery/sdk/content-link/key.md +25 -0
  204. package/agent-docs/content-delivery/sdk/content-link/kind.md +25 -0
  205. package/agent-docs/content-delivery/sdk/content-link/query.md +25 -0
  206. package/agent-docs/content-delivery/sdk/content-link/ref.md +25 -0
  207. package/agent-docs/content-delivery/sdk/content-link.md +127 -0
  208. package/agent-docs/content-delivery/sdk/content-manifest/assets.md +25 -0
  209. package/agent-docs/content-delivery/sdk/content-manifest/diagnostics.md +25 -0
  210. package/agent-docs/content-delivery/sdk/content-manifest/digest.md +25 -0
  211. package/agent-docs/content-delivery/sdk/content-manifest/files.md +25 -0
  212. package/agent-docs/content-delivery/sdk/content-manifest/kind.md +25 -0
  213. package/agent-docs/content-delivery/sdk/content-manifest/public-base.md +25 -0
  214. package/agent-docs/content-delivery/sdk/content-manifest/publication.md +25 -0
  215. package/agent-docs/content-delivery/sdk/content-manifest/resources.md +25 -0
  216. package/agent-docs/content-delivery/sdk/content-manifest/review.md +25 -0
  217. package/agent-docs/content-delivery/sdk/content-manifest/schema-version.md +25 -0
  218. package/agent-docs/content-delivery/sdk/content-manifest.md +179 -0
  219. package/agent-docs/content-delivery/sdk/content-mount/anchors.md +27 -0
  220. package/agent-docs/content-delivery/sdk/content-mount/links.md +27 -0
  221. package/agent-docs/content-delivery/sdk/content-mount/unmount.md +27 -0
  222. package/agent-docs/content-delivery/sdk/content-mount.md +82 -0
  223. package/agent-docs/content-delivery/sdk/content-publication/channel.md +25 -0
  224. package/agent-docs/content-delivery/sdk/content-publication/language.md +25 -0
  225. package/agent-docs/content-delivery/sdk/content-publication/product.md +25 -0
  226. package/agent-docs/content-delivery/sdk/content-publication/version.md +25 -0
  227. package/agent-docs/content-delivery/sdk/content-publication.md +85 -0
  228. package/agent-docs/content-delivery/sdk/content-ref-key.md +34 -0
  229. package/agent-docs/content-delivery/sdk/content-ref.md +40 -0
  230. package/agent-docs/content-delivery/sdk/content-representation/anchors.md +25 -0
  231. package/agent-docs/content-delivery/sdk/content-representation/links.md +25 -0
  232. package/agent-docs/content-delivery/sdk/content-representation.md +57 -0
  233. package/agent-docs/content-delivery/sdk/content-resource/assets.md +25 -0
  234. package/agent-docs/content-delivery/sdk/content-resource/canonical-url.md +25 -0
  235. package/agent-docs/content-delivery/sdk/content-resource/external-dependencies.md +25 -0
  236. package/agent-docs/content-delivery/sdk/content-resource/publication.md +27 -0
  237. package/agent-docs/content-delivery/sdk/content-resource/ref.md +27 -0
  238. package/agent-docs/content-delivery/sdk/content-resource/representations.md +27 -0
  239. package/agent-docs/content-delivery/sdk/content-resource/source.md +25 -0
  240. package/agent-docs/content-delivery/sdk/content-resource/summary.md +27 -0
  241. package/agent-docs/content-delivery/sdk/content-resource/title.md +27 -0
  242. package/agent-docs/content-delivery/sdk/content-resource/type.md +27 -0
  243. package/agent-docs/content-delivery/sdk/content-resource-key.md +34 -0
  244. package/agent-docs/content-delivery/sdk/content-resource.md +192 -0
  245. package/agent-docs/content-delivery/sdk/content-rest-entry/key.md +25 -0
  246. package/agent-docs/content-delivery/sdk/content-rest-entry/resource.md +25 -0
  247. package/agent-docs/content-delivery/sdk/content-rest-entry.md +57 -0
  248. package/agent-docs/content-delivery/sdk/content-rest-error/error.md +25 -0
  249. package/agent-docs/content-delivery/sdk/content-rest-error-code.md +23 -0
  250. package/agent-docs/content-delivery/sdk/content-rest-error.md +46 -0
  251. package/agent-docs/content-delivery/sdk/content-rest-list/publication.md +25 -0
  252. package/agent-docs/content-delivery/sdk/content-rest-list/resources.md +25 -0
  253. package/agent-docs/content-delivery/sdk/content-rest-list/revision.md +25 -0
  254. package/agent-docs/content-delivery/sdk/content-rest-list/schema-version.md +25 -0
  255. package/agent-docs/content-delivery/sdk/content-rest-list.md +85 -0
  256. package/agent-docs/content-delivery/sdk/content-rest-resource/key.md +25 -0
  257. package/agent-docs/content-delivery/sdk/content-rest-resource/publication.md +25 -0
  258. package/agent-docs/content-delivery/sdk/content-rest-resource/resource.md +25 -0
  259. package/agent-docs/content-delivery/sdk/content-rest-resource/revision.md +25 -0
  260. package/agent-docs/content-delivery/sdk/content-rest-resource/schema-version.md +25 -0
  261. package/agent-docs/content-delivery/sdk/content-rest-resource.md +99 -0
  262. package/agent-docs/content-delivery/sdk/content-result/content.md +27 -0
  263. package/agent-docs/content-delivery/sdk/content-result/format.md +27 -0
  264. package/agent-docs/content-delivery/sdk/content-result/representation.md +27 -0
  265. package/agent-docs/content-delivery/sdk/content-result/resource.md +27 -0
  266. package/agent-docs/content-delivery/sdk/content-result/revision.md +27 -0
  267. package/agent-docs/content-delivery/sdk/content-result.md +114 -0
  268. package/agent-docs/content-delivery/sdk/content-snapshot/dispose.md +27 -0
  269. package/agent-docs/content-delivery/sdk/content-snapshot/get.md +36 -0
  270. package/agent-docs/content-delivery/sdk/content-snapshot/list.md +29 -0
  271. package/agent-docs/content-delivery/sdk/content-snapshot/publication.md +27 -0
  272. package/agent-docs/content-delivery/sdk/content-snapshot/revision.md +27 -0
  273. package/agent-docs/content-delivery/sdk/content-snapshot.md +123 -0
  274. package/agent-docs/content-delivery/sdk/exports.md +742 -0
  275. package/agent-docs/content-delivery/sdk/exports.sections/functions.md +30 -0
  276. package/agent-docs/content-delivery/sdk/exports.sections/members.md +47 -0
  277. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-contentanchor.md +41 -0
  278. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-contentasset.md +37 -0
  279. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-contentdeliveryerror.md +43 -0
  280. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-contentdeliverylimits.md +41 -0
  281. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-contenterrorcode.md +29 -0
  282. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-contentfile.md +41 -0
  283. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-contentformat.md +29 -0
  284. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-contentlink.md +49 -0
  285. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-contentmanifest.md +65 -0
  286. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-contentmount.md +46 -0
  287. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-contentpublication.md +43 -0
  288. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-contentref.md +40 -0
  289. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-contentrefkey.md +39 -0
  290. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-contentrepresentation.md +39 -0
  291. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-contentresource.md +62 -0
  292. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-contentresourcekey.md +39 -0
  293. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-contentrestentry.md +39 -0
  294. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-contentresterror.md +40 -0
  295. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-contentresterrorcode.md +29 -0
  296. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-contentrestlist.md +43 -0
  297. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-contentrestresource.md +45 -0
  298. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-contentresult.md +45 -0
  299. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-contentsnapshot.md +44 -0
  300. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-manifestcontentsourceoptions.md +43 -0
  301. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-mountcontent.md +45 -0
  302. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-openmanifestcontentsource.md +51 -0
  303. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-openrestcontentsource.md +41 -0
  304. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-restcontentsourceoptions.md +47 -0
  305. package/agent-docs/content-delivery/sdk/exports.sections/shoalrun-content-validatecontentmanifest.md +42 -0
  306. package/agent-docs/content-delivery/sdk/manifest-content-source-options/fetch.md +27 -0
  307. package/agent-docs/content-delivery/sdk/manifest-content-source-options/limits.md +27 -0
  308. package/agent-docs/content-delivery/sdk/manifest-content-source-options/manifest-url.md +27 -0
  309. package/agent-docs/content-delivery/sdk/manifest-content-source-options/signal.md +27 -0
  310. package/agent-docs/content-delivery/sdk/manifest-content-source-options.md +93 -0
  311. package/agent-docs/content-delivery/sdk/mount-content.md +46 -0
  312. package/agent-docs/content-delivery/sdk/open-manifest-content-source.md +51 -0
  313. package/agent-docs/content-delivery/sdk/open-rest-content-source.md +39 -0
  314. package/agent-docs/content-delivery/sdk/rest-content-source-options/base-url.md +28 -0
  315. package/agent-docs/content-delivery/sdk/rest-content-source-options/fetch.md +27 -0
  316. package/agent-docs/content-delivery/sdk/rest-content-source-options/limits.md +27 -0
  317. package/agent-docs/content-delivery/sdk/rest-content-source-options/publication.md +27 -0
  318. package/agent-docs/content-delivery/sdk/rest-content-source-options/revision.md +27 -0
  319. package/agent-docs/content-delivery/sdk/rest-content-source-options/signal.md +27 -0
  320. package/agent-docs/content-delivery/sdk/rest-content-source-options.md +125 -0
  321. package/agent-docs/content-delivery/sdk/validate-content-manifest.md +38 -0
  322. package/agent-docs/contribute/agent-skills.md +33 -6
  323. package/agent-docs/documents.json +7094 -308
  324. package/agent-docs/features.md +18 -17
  325. package/agent-docs/provenance.json +6 -6
  326. package/agent-docs/publish/content-export.md +3 -1
  327. package/agent-docs/publish/content-export.sections/assets-downloads-and-notices.md +1 -1
  328. package/agent-docs/publish/content-export.sections/check-the-result.md +1 -1
  329. package/agent-docs/publish/content-export.sections/inspect-the-manifest.md +1 -1
  330. package/agent-docs/publish/content-export.sections/prerequisites.md +1 -1
  331. package/agent-docs/publish/content-export.sections/related-reading.md +1 -1
  332. package/agent-docs/publish/content-export.sections/review-exported-api-material.md +1 -1
  333. package/agent-docs/publish/content-export.sections/try-the-help-drawer-example.md +1 -1
  334. package/agent-docs/publish/content-export.sections/what-the-representations-retain.md +1 -1
  335. package/agent-docs/publish/pdf.md +2 -2
  336. package/agent-docs/publish/pdf.sections/apply-print-styling.md +1 -1
  337. package/agent-docs/publish/pdf.sections/check-the-result.md +1 -1
  338. package/agent-docs/publish/pdf.sections/export-a-page-or-publication.md +2 -2
  339. package/agent-docs/publish/pdf.sections/html-markdown-and-agents.md +1 -1
  340. package/agent-docs/publish/pdf.sections/offer-browser-page-printing.md +1 -1
  341. package/agent-docs/publish/pdf.sections/offer-direct-downloads-through-a-node-server.md +1 -1
  342. package/agent-docs/publish/pdf.sections/prerequisites.md +1 -1
  343. package/agent-docs/publish/pdf.sections/publish-an-optional-manual-download.md +1 -1
  344. package/agent-docs/publish/single-source-sites.md +26 -2
  345. package/agent-docs/publish/single-source-sites.sections/build-and-check-each-output.md +49 -0
  346. package/agent-docs/publish/single-source-sites.sections/html-markdown-and-agents.md +33 -0
  347. package/agent-docs/publish/single-source-sites.sections/include-additional-sections-in-a-build.md +47 -0
  348. package/agent-docs/publish/single-source-sites.sections/related-reading.md +43 -0
  349. package/agent-docs/publish/single-source-sites.sections/select-the-pages-for-each-site.md +74 -0
  350. package/agent-docs/publish/single-source-sites.sections/share-the-base-configuration.md +93 -0
  351. package/agent-docs/reference/agent-skills.md +2 -2
  352. package/agent-docs/search-index.json +1 -1
  353. package/agent-docs/start.md +1 -1
  354. package/agent-docs/start.sections/start-your-own-project.md +1 -1
  355. package/assets/search-lexical.js +15 -4
  356. package/dist/code-reference-model.d.ts +4 -4
  357. package/dist/compiler.d.ts +8 -0
  358. package/dist/compiler.js +10 -4
  359. package/dist/component-names.js +1 -1
  360. package/dist/component-transform.js +14 -22
  361. package/dist/conditional-tree.d.ts +2 -0
  362. package/dist/conditional-tree.js +14 -0
  363. package/dist/content-delivery-contract.d.ts +39 -0
  364. package/dist/content-delivery-contract.js +2 -0
  365. package/dist/content-delivery-validation.d.ts +6 -0
  366. package/dist/content-delivery-validation.js +6 -0
  367. package/dist/content-delivery.d.ts +19 -1
  368. package/dist/content-delivery.js +10 -1
  369. package/dist/content-export-command.d.ts +42 -3
  370. package/dist/content-export.d.ts +42 -3
  371. package/dist/content-export.js +15 -6
  372. package/dist/content-filter.d.ts +2 -0
  373. package/dist/content-filter.js +4 -0
  374. package/dist/content-mount.d.ts +12 -1
  375. package/dist/content-mount.js +8 -1
  376. package/dist/content-rest-client.d.ts +11 -1
  377. package/dist/content-rest-client.js +4 -1
  378. package/dist/content-rest-contract.d.ts +5 -0
  379. package/dist/content-source.d.ts +1 -0
  380. package/dist/content-workspace.d.ts +4 -1
  381. package/dist/content-workspace.js +5 -2
  382. package/dist/deployment-profile.d.ts +3 -1
  383. package/dist/deployment-profile.js +18 -19
  384. package/dist/palettes.d.ts +1 -1
  385. package/dist/publication-manifest.js +1 -1
  386. package/dist/representation-tree.js +8 -2
  387. package/dist/search-query.js +13 -6
  388. package/dist/section-selection.d.ts +29 -0
  389. package/dist/section-selection.js +92 -0
  390. package/dist/snippets.d.ts +1 -0
  391. package/example-configs/facet-sections/README.md +37 -0
  392. package/example-configs/facet-sections/base.yaml +29 -0
  393. package/example-configs/facet-sections/content/billing.mdx +25 -0
  394. package/example-configs/facet-sections/customer.config.yaml +4 -0
  395. package/example-configs/facet-sections/docs.config.yaml +3 -0
  396. package/example-configs/faceted-sites/README.md +4 -2
  397. package/package.json +2 -1
  398. package/skills/shoalrun-author/SKILL.md +13 -0
  399. package/skills/shoalrun-author/references/code-api-references.md +5 -1
  400. package/skills/shoalrun-author/references/facet-selection.md +48 -0
  401. package/skills/shoalrun-brand/SKILL.md +9 -0
  402. package/skills/shoalrun-component-author/SKILL.md +9 -0
  403. package/skills/shoalrun-pdf/SKILL.md +9 -0
  404. package/skills/shoalrun-recipes/SKILL.md +9 -0
  405. package/skills/shoalrun-setup/SKILL.md +9 -0
  406. package/skills/shoalrun-walkthrough/SKILL.md +9 -0
@@ -1,5 +1,6 @@
1
+ import { createSectionSelection, pruneSelectedTree, sectionSelectionReport, writeSectionSelectionReport } from './section-selection.js';
1
2
  import { documentKey, publicationKey, samePublication } from './publication-identity.js';
2
- import { pruneFeatureTree, profileSelection } from './deployment-profile.js';
3
+ import { profileSelection } from './deployment-profile.js';
3
4
  import { readFile, realpath } from 'node:fs/promises';
4
5
  import path from 'node:path';
5
6
  import { fileURLToPath } from 'node:url';
@@ -121,7 +122,8 @@ export async function exportContent(configFile, destination, options = {}) {
121
122
  const providerSetting = configField(config.metadata, 'provider');
122
123
  const metadataProvider = await loadMetadataProvider(providerSetting, providerSetting ? explainSegments(loaded, ['metadata', 'provider', 'module']).source.file : configFile);
123
124
  const metadataDependencies = new Set();
124
- const workspace = await prepareContentWorkspace({ base, basePath, config, loaded, variants: [publication], language, readerFilters, metadataProvider, metadataDependencies, allowRouteCollisions: true });
125
+ const sectionSelection = createSectionSelection(readerFilters, selection, publications);
126
+ const workspace = await prepareContentWorkspace({ base, basePath, config, loaded, variants: [publication], language, readerFilters, metadataProvider, metadataDependencies, sectionSelection, allowRouteCollisions: true });
125
127
  const { docs: eligible, apiPlan, snippets, sourceForOperation } = workspace;
126
128
  const ids = new Set(target.resources ? target.resources.filter(item => item.kind !== 'snippet').map(item => item.documentId) : target.documents);
127
129
  for (const id of ids) {
@@ -180,7 +182,7 @@ export async function exportContent(configFile, destination, options = {}) {
180
182
  throw new Error(`Unknown export snippet ${selection.snippetId}`);
181
183
  // This is an explicit isolated context. It has no containing page, operations,
182
184
  // relationships, page variables, or inherited publication/access metadata.
183
- const snippetDocument = { featureContext: loaded.deployment?.context, file: definition.file, body: '', tree: root([]), variant: publication,
185
+ const snippetDocument = { sectionSelection, featureContext: loaded.deployment?.context, file: definition.file, body: '', tree: root([]), variant: publication,
184
186
  route: `/__content_snippets/${encodeURIComponent(selection.id)}`, markdownRoute: '',
185
187
  frontmatter: { id: selection.id, title: selection.title, summary: selection.summary, type: 'snippet', slug: '/', access: { policy: 'public' } } };
186
188
  const checkSnippet = (id, seen = new Set()) => {
@@ -190,7 +192,7 @@ export async function exportContent(configFile, destination, options = {}) {
190
192
  const source = snippets.get(id);
191
193
  if (!source)
192
194
  throw new Error(`Unknown snippet ${id}`);
193
- visitTree(pruneFeatureTree(source.tree, loaded.deployment?.context, source.file), node => {
195
+ visitTree(pruneSelectedTree(source.tree, snippetDocument, source.file, false), node => {
194
196
  if (['RelatedReading', 'DocCard', 'DocCards', 'DocCollection', 'CookbookChooser', 'ApiExample', 'ApiSchema', 'FrontmatterReference', 'Anchor'].includes(node.name))
195
197
  throw new Error(`Snippet instance ${selection.id}: <${node.name}> needs page context`);
196
198
  if (node.name === 'Snippet') {
@@ -316,6 +318,11 @@ export async function exportContent(configFile, destination, options = {}) {
316
318
  const key = `${sourceKeyFor(destination)}:${format}`;
317
319
  if (canonicalAnchors.has(key))
318
320
  return canonicalAnchors.get(key);
321
+ // An export-selected tree cannot prove anchors in a differently selected website.
322
+ // Refuse the fragment rather than silently publish a broken canonical link.
323
+ const normalizeSelection = (value) => JSON.stringify(Object.entries(value ?? {}).sort(([a], [b]) => compare(a, b)).map(([facet, choices]) => [facet, [...choices].sort(compare)]));
324
+ if (sectionSelection.hasConditions && normalizeSelection(selection) !== normalizeSelection(siteSelection))
325
+ throw new Error(`Cannot verify canonical anchor in ${destination.frontmatter.id} across different site and export contentFilter selections; include the target resource or use its page URL without a fragment`);
319
326
  const source = sourceInventories.get(key);
320
327
  if (!source || destination.endpoint)
321
328
  throw new Error(`Cannot validate canonical fragments in ${destination.frontmatter.id}; select the target resource explicitly`);
@@ -553,7 +560,7 @@ export async function exportContent(configFile, destination, options = {}) {
553
560
  };
554
561
  const input = { documents: printDocuments, title: selected.length === 1 ? selected[0].title : site.title, publication, assets: assets.artifacts, publicBase: target.publicBase, config: pdfConfig, configFile, assetRoots: [...roots, ...pdfInputs.map(file => path.dirname(file))] };
555
562
  const preparedPdf = { input, manifest, inputs };
556
- const common = { status: unreviewed.length ? 'needs-review' : 'ok', output: path.resolve(destination || base), publication, formats: target.formats, profileSelection: profileSelection(loaded.deployment), resources: records.length, documents: records.length, documentIds: selected.map(item => item.doc.frontmatter.id), comparison: { status: 'no-baseline', added: [], removed: [], diagnostics: [] }, assets: assets.records.size, apiReview: manifest.review.apis, diagnostics };
563
+ const common = { status: unreviewed.length ? 'needs-review' : 'ok', output: path.resolve(destination || base), publication, formats: target.formats, profileSelection: profileSelection(loaded.deployment), contentSelection: sectionSelectionReport(sectionSelection), resources: records.length, documents: records.length, documentIds: selected.map(item => item.doc.frontmatter.id), comparison: { status: 'no-baseline', added: [], removed: [], diagnostics: [] }, assets: assets.records.size, apiReview: manifest.review.apis, diagnostics };
557
564
  if (options.preparePdfOnly)
558
565
  return { ...common, digest: exportDigest(JSON.stringify(manifest)), preparedPdf };
559
566
  checkOutputDirectory(destination, base, workspace.contentDir, inputs);
@@ -573,6 +580,7 @@ export async function exportContent(configFile, destination, options = {}) {
573
580
  const digest = exportDigest(JSON.stringify(payload));
574
581
  pdfArtifacts.set('manifest.json', JSON.stringify({ ...payload, digest }, null, 2) + '\n');
575
582
  await commitArtifacts(path.resolve(destination), pdfArtifacts, undefined, 'pdf');
583
+ await writeSectionSelectionReport(base, sectionSelection, { target: { kind: 'pdf', name: options.target ?? null, output: path.resolve(destination) }, buildId: digest, configDigest: loaded.digest, filterOrigin: target.contentFilter ? 'export.contentFilter' : 'site.contentFilter' });
576
584
  return { ...common, digest, pdf: filename };
577
585
  }
578
586
  const manifest = { schemaVersion: publication.product === undefined ? 1 : 2, kind: 'shoalrun-content-export', publicBase: target.publicBase, publication,
@@ -599,7 +607,8 @@ export async function exportContent(configFile, destination, options = {}) {
599
607
  throw new Error('Review candidate cannot overwrite an approved export; choose a separate review destination');
600
608
  }
601
609
  await commitArtifacts(path.resolve(destination), artifacts, undefined, 'export');
610
+ await writeSectionSelectionReport(base, sectionSelection, { target: { kind: 'export', name: options.target ?? null, output: path.resolve(destination) }, buildId: bundleDigest, configDigest: loaded.digest, filterOrigin: target.contentFilter ? 'export.contentFilter' : 'unfiltered' });
602
611
  return { status: unreviewed.length ? 'needs-review' : 'ok', output: path.resolve(destination), publication, formats: target.formats,
603
- profileSelection: profileSelection(loaded.deployment), resources: records.length, documents: records.filter(item => item.ref.kind === 'page').length, documentIds: records.flatMap(item => item.ref.kind === 'page' ? [item.ref.documentId] : []), comparison, assets: assets.records.size, digest: bundleDigest,
612
+ profileSelection: profileSelection(loaded.deployment), contentSelection: sectionSelectionReport(sectionSelection), resources: records.length, documents: records.filter(item => item.ref.kind === 'page').length, documentIds: records.flatMap(item => item.ref.kind === 'page' ? [item.ref.documentId] : []), comparison, assets: assets.records.size, digest: bundleDigest,
604
613
  apiReview: reviews.map(row => ({ apiId: row.apiId, digest: row.digest })), diagnostics };
605
614
  }
@@ -2,3 +2,5 @@ import { type ReaderFiltersConfig, type ReaderSelection } from './reader-filters
2
2
  /** Build-time page selection; every selected facet must match at least one choice. */
3
3
  export declare function validateContentFilter(value: unknown, facets?: ReaderFiltersConfig): ReaderSelection | undefined;
4
4
  export declare function matchesContentFilter(marks: ReaderSelection | undefined, selection: ReaderSelection | undefined): boolean;
5
+ /** Section requirements narrow a selected page; unselected facets pass. */
6
+ export declare function matchesSectionContentFilter(requirements: ReaderSelection, selection: ReaderSelection | undefined): boolean;
@@ -14,3 +14,7 @@ export function validateContentFilter(value, facets) {
14
14
  export function matchesContentFilter(marks, selection) {
15
15
  return !selection || Object.entries(selection).every(([facet, choices]) => choices.some(choice => marks?.[facet]?.includes(choice)));
16
16
  }
17
+ /** Section requirements narrow a selected page; unselected facets pass. */
18
+ export function matchesSectionContentFilter(requirements, selection) {
19
+ return Object.entries(requirements).every(([facet, choices]) => !selection?.[facet] || choices.some(choice => selection[facet].includes(choice)));
20
+ }
@@ -1,10 +1,21 @@
1
1
  import type { ContentAnchor, ContentLink, ContentResult } from './content-delivery-contract.js';
2
+ /** Handles and rewritten routing metadata for one mounted HTML instance. */
2
3
  export type ContentMount = {
4
+ /** Anchors after instance-specific ID rewriting. */
3
5
  anchors: readonly ContentAnchor[];
6
+ /** Typed links, including rewritten local fragments for this instance. */
4
7
  links: readonly ContentLink[];
8
+ /** Remove nodes that still belong to this mount. */
5
9
  unmount(): void;
6
10
  };
7
- /** Mount trusted compiler output, not arbitrary HTML. This is not a sanitizer. */
11
+ /** Mount trusted compiler HTML with instance-specific anchor and accessibility IDs.
12
+ * This is not a sanitizer. The result must come from trusted compiler output.
13
+ * The mount replaces the container's children and rewrites local routing metadata.
14
+ * @param container DOM element receiving the content.
15
+ * @param result A validated HTML representation; Markdown results aren't accepted.
16
+ * @param options Optional unique instance ID and cancellation signal.
17
+ * @returns Rewritten anchors, typed links, and an unmount handle.
18
+ */
8
19
  export declare function mountContent(container: Element, result: ContentResult, options?: {
9
20
  instanceId?: string;
10
21
  signal?: AbortSignal;
@@ -1,5 +1,12 @@
1
1
  import { ContentDeliveryError, contentRefKey } from './content-delivery-contract.js';
2
- /** Mount trusted compiler output, not arbitrary HTML. This is not a sanitizer. */
2
+ /** Mount trusted compiler HTML with instance-specific anchor and accessibility IDs.
3
+ * This is not a sanitizer. The result must come from trusted compiler output.
4
+ * The mount replaces the container's children and rewrites local routing metadata.
5
+ * @param container DOM element receiving the content.
6
+ * @param result A validated HTML representation; Markdown results aren't accepted.
7
+ * @param options Optional unique instance ID and cancellation signal.
8
+ * @returns Rewritten anchors, typed links, and an unmount handle.
9
+ */
3
10
  export function mountContent(container, result, options = {}) {
4
11
  options.signal?.throwIfAborted();
5
12
  if (result.format !== 'html')
@@ -1,12 +1,22 @@
1
1
  import type { ContentSnapshot, ContentPublication } from './content-delivery-contract.js';
2
2
  import type { ContentDeliveryLimits } from './content-delivery.js';
3
+ /** Options for a REST inventory and its revision-pinned resource reads. */
3
4
  export type RestContentSourceOptions = {
5
+ /** REST mount URL, such as /content/v1. Browser requests must use the application's origin. */
4
6
  baseUrl: string | URL;
7
+ /** Exact publication to request; include product for a product-scoped source. */
5
8
  publication: ContentPublication;
9
+ /** Expected inventory revision. Omit it to open the current revision. */
6
10
  revision?: string;
11
+ /** Fetch implementation. Defaults to globalThis.fetch. */
7
12
  fetch?: typeof globalThis.fetch;
13
+ /** Cancel the inventory-opening request. */
8
14
  signal?: AbortSignal;
15
+ /** Inventory and representation bounds. */
9
16
  limits?: ContentDeliveryLimits;
10
17
  };
11
- /** Preloads a REST inventory and pins all subsequent reads to that revision. */
18
+ /** Open a REST inventory and pin subsequent resource reads to its revision.
19
+ * @param options REST mount, exact publication, optional revision, and request settings.
20
+ * @returns A snapshot with the same list/get/dispose interface as the manifest source.
21
+ */
12
22
  export declare function openRestContentSource(options: RestContentSourceOptions): Promise<ContentSnapshot>;
@@ -7,7 +7,10 @@ const digestPattern = /^[a-f0-9]{64}$/;
7
7
  const record = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
8
8
  const exactKeys = (value, keys) => record(value) && Object.keys(value).length === keys.length && Object.keys(value).every(key => keys.includes(key));
9
9
  const invalid = (message) => { throw new ContentDeliveryError('invalid_manifest', message); };
10
- /** Preloads a REST inventory and pins all subsequent reads to that revision. */
10
+ /** Open a REST inventory and pin subsequent resource reads to its revision.
11
+ * @param options REST mount, exact publication, optional revision, and request settings.
12
+ * @returns A snapshot with the same list/get/dispose interface as the manifest source.
13
+ */
11
14
  export async function openRestContentSource(options) {
12
15
  let baseUrl;
13
16
  try {
@@ -1,16 +1,19 @@
1
1
  import type { ContentPublication, ContentRef, ContentResource } from './content-delivery-contract.js';
2
2
  /** Public identity only: independent of publication, revision, and export filenames. */
3
3
  export declare function contentResourceKey(ref: ContentRef): string;
4
+ /** One resource inventory entry with its REST lookup key. */
4
5
  export type ContentRestEntry = {
5
6
  key: string;
6
7
  resource: ContentResource;
7
8
  };
9
+ /** Revision-scoped inventory envelope returned by the content REST service. */
8
10
  export type ContentRestList = {
9
11
  schemaVersion: 1 | 2;
10
12
  revision: string;
11
13
  publication: ContentPublication;
12
14
  resources: ContentRestEntry[];
13
15
  };
16
+ /** Revision-scoped metadata envelope for a single content resource. */
14
17
  export type ContentRestResource = {
15
18
  schemaVersion: 1 | 2;
16
19
  revision: string;
@@ -18,7 +21,9 @@ export type ContentRestResource = {
18
21
  key: string;
19
22
  resource: ContentResource;
20
23
  };
24
+ /** Error categories used by the content REST protocol. */
21
25
  export type ContentRestErrorCode = 'invalid_request' | 'not_found' | 'unsupported_format' | 'stale_revision' | 'method_not_allowed' | 'internal_error';
26
+ /** JSON error envelope returned by the content REST service. */
22
27
  export type ContentRestError = {
23
28
  error: {
24
29
  code: ContentRestErrorCode;
@@ -57,6 +57,7 @@ export type Frontmatter = {
57
57
  [key: string]: unknown;
58
58
  };
59
59
  export type Document = {
60
+ sectionSelection?: import('./section-selection.js').SectionSelection;
60
61
  codeReference?: import('./code-reference-model.js').CodeReference;
61
62
  featureContext?: import('./deployment-profile.js').FeatureContext;
62
63
  change?: ResolvedChange;
@@ -1,3 +1,4 @@
1
+ import { type SectionSelection } from './section-selection.js';
1
2
  import { type Document, type Variant } from './content.js';
2
3
  import { type AuthoredPublicationConfig } from './publication-plan.js';
3
4
  import { type loadConfig } from './config.js';
@@ -5,7 +6,7 @@ import { type SnippetDefinition } from './snippets.js';
5
6
  import type { ReaderFiltersConfig, ReaderSelection } from './reader-filters.js';
6
7
  import type { loadMetadataProvider } from './metadata-provider.js';
7
8
  /** Shared source resolution. No website navigation, styles, hosting or provider setup. */
8
- export declare function prepareContentWorkspace({ base, basePath, config, loaded, variants, language, readerFilters, contentFilter, metadataProvider, metadataDependencies, sourceOverrides, authoringFiles, allowRouteCollisions }: {
9
+ export declare function prepareContentWorkspace({ base, basePath, config, loaded, variants, language, readerFilters, contentFilter, metadataProvider, metadataDependencies, sourceOverrides, authoringFiles, allowRouteCollisions, sectionSelection: suppliedSectionSelection }: {
9
10
  base: string;
10
11
  basePath: string;
11
12
  config: AuthoredPublicationConfig & {
@@ -19,12 +20,14 @@ export declare function prepareContentWorkspace({ base, basePath, config, loaded
19
20
  language: string;
20
21
  readerFilters?: ReaderFiltersConfig;
21
22
  contentFilter?: ReaderSelection;
23
+ sectionSelection?: SectionSelection;
22
24
  metadataProvider?: Awaited<ReturnType<typeof loadMetadataProvider>>;
23
25
  metadataDependencies: Set<string>;
24
26
  sourceOverrides?: ReadonlyMap<string, string>;
25
27
  authoringFiles?: ReadonlySet<string>;
26
28
  allowRouteCollisions?: boolean;
27
29
  }): Promise<{
30
+ sectionSelection: SectionSelection;
28
31
  docs: Map<string, Document>;
29
32
  knownDocumentIds: Set<string>;
30
33
  knownDocumentTypes: Map<string, string>;
@@ -1,3 +1,4 @@
1
+ import { createSectionSelection } from './section-selection.js';
1
2
  import { documentKey } from './publication-identity.js';
2
3
  import { matchesFeatures } from './deployment-profile.js';
3
4
  import path from 'node:path';
@@ -10,7 +11,8 @@ import { explainSegments } from './config.js';
10
11
  import { validateSnippetConfig } from './snippets.js';
11
12
  const keyFor = documentKey;
12
13
  /** Shared source resolution. No website navigation, styles, hosting or provider setup. */
13
- export async function prepareContentWorkspace({ base, basePath, config, loaded, variants, language, readerFilters, contentFilter, metadataProvider, metadataDependencies, sourceOverrides, authoringFiles, allowRouteCollisions }) {
14
+ export async function prepareContentWorkspace({ base, basePath, config, loaded, variants, language, readerFilters, contentFilter, metadataProvider, metadataDependencies, sourceOverrides, authoringFiles, allowRouteCollisions, sectionSelection: suppliedSectionSelection }) {
15
+ const sectionSelection = suppliedSectionSelection ?? createSectionSelection(readerFilters, contentFilter, config.publications);
14
16
  const { docs, knownDocumentIds, knownDocumentTypes, authoredOperationIds, routeOwners, contentDir } = await planAuthoredDocuments({
15
17
  base, basePath, config, variants, language, readerFilters, contentFilter, featureContext: loaded.deployment?.context, metadataProvider, metadataDependencies, sourceOverrides, authoringFiles, allowRouteCollisions
16
18
  });
@@ -34,6 +36,7 @@ export async function prepareContentWorkspace({ base, basePath, config, loaded,
34
36
  });
35
37
  const { api, sourceForVariant, knownOperations } = apiPlan;
36
38
  for (const [key, doc] of docs) {
39
+ doc.sectionSelection = sectionSelection;
37
40
  doc.featureContext = loaded.deployment?.context;
38
41
  for (const operationId of doc.frontmatter.operations ?? []) {
39
42
  const known = knownOperations.get(operationId);
@@ -84,6 +87,6 @@ export async function prepareContentWorkspace({ base, basePath, config, loaded,
84
87
  throw new Error(`no OpenAPI source for ${variant.channel}/${variant.version} (${operationId})`);
85
88
  return { ...binding, sourceOperationId: operation.sourceOperationId };
86
89
  };
87
- return { docs, knownDocumentIds, knownDocumentTypes, authoredOperationIds, routeOwners, contentDir, snippets,
90
+ return { sectionSelection, docs, knownDocumentIds, knownDocumentTypes, authoredOperationIds, routeOwners, contentDir, snippets,
88
91
  apiPlan, api, sourceForVariant, knownOperations, relatedApiTargets, guidesByApi, sourceForOperation };
89
92
  }
@@ -34,7 +34,9 @@ export type ProfileResolution = {
34
34
  export declare function validateFeatureClassification(source: Record<string, unknown>, context: FeatureContext | undefined, label: string): void;
35
35
  export declare function featureRequirements(value: unknown, context: FeatureContext | undefined, label: string): FeatureRequirements | undefined;
36
36
  export declare function matchesFeatures(value: unknown, context: FeatureContext | undefined, label: string, kind?: 'page' | 'section'): boolean;
37
- /** Validate every condition, but never resolve a dependency in an excluded branch. */
37
+ /** Evaluate a feature guard without expanding its children. */
38
+ export declare function evaluateFeatureGuard(node: any, context: FeatureContext | undefined, file: string, ancestorIncluded?: boolean, record?: boolean): boolean | undefined;
39
+ /** Compatibility entry point for consumers that only select feature grants. */
38
40
  export declare function pruneFeatureTree(tree: any, context: FeatureContext | undefined, file: string): any;
39
41
  /** Returned to the caller or written under .shoalrun, never a served artifact. */
40
42
  export declare function profileSelection(resolution: ProfileResolution | undefined): {
@@ -1,3 +1,4 @@
1
+ import { pruneConditionalTree } from './conditional-tree.js';
1
2
  import { resolvePublication } from './compiler-config.js';
2
3
  import { publicationKey } from './publication-identity.js';
3
4
  import { readFile, lstat, realpath } from 'node:fs/promises';
@@ -69,26 +70,24 @@ export function matchesFeatures(value, context, label, kind = 'page') {
69
70
  context?.decisions.push({ kind, id: label, included, ...(requirement ? { requires: requirement } : {}) });
70
71
  return included;
71
72
  }
72
- /** Validate every condition, but never resolve a dependency in an excluded branch. */
73
+ /** Evaluate a feature guard without expanding its children. */
74
+ export function evaluateFeatureGuard(node, context, file, ancestorIncluded = true, record = true) {
75
+ if (node.name !== 'ForFeature')
76
+ return;
77
+ const label = `${file}:${node.position?.start?.line ?? 1}: <ForFeature>`;
78
+ const attributes = node.attributes ?? [];
79
+ if (node.type !== 'mdxJsxFlowElement' || !node.children?.length || !attributes.length || attributes.some((item) => item.type !== 'mdxJsxAttribute' || !['allOf', 'anyOf'].includes(item.name) || typeof item.value !== 'string') || new Set(attributes.map((item) => item.name)).size !== attributes.length)
80
+ throw new Error(`${label} needs block content and distinct literal allOf/anyOf attributes`);
81
+ const requirements = Object.fromEntries(attributes.map((item) => [item.name, item.value.trim().split(/\s+/).filter(Boolean)]));
82
+ const target = !record && context ? { ...context, decisions: [] } : context;
83
+ const included = matchesFeatures(requirements, target, label, 'section');
84
+ if (!ancestorIncluded && target?.decisions.length)
85
+ target.decisions[target.decisions.length - 1].included = false;
86
+ return included;
87
+ }
88
+ /** Compatibility entry point for consumers that only select feature grants. */
73
89
  export function pruneFeatureTree(tree, context, file) {
74
- const walk = (node, parentIncluded = true) => {
75
- if (!node || typeof node !== 'object')
76
- return [node];
77
- if (node.name === 'ForFeature') {
78
- const label = `${file}:${node.position?.start?.line ?? 1}: <ForFeature>`;
79
- const attributes = node.attributes ?? [];
80
- if (node.type !== 'mdxJsxFlowElement' || !node.children?.length || !attributes.length || attributes.some((item) => item.type !== 'mdxJsxAttribute' || !['allOf', 'anyOf'].includes(item.name) || typeof item.value !== 'string') || new Set(attributes.map((item) => item.name)).size !== attributes.length)
81
- throw new Error(`${label} needs block content and distinct literal allOf/anyOf attributes`);
82
- const requirements = Object.fromEntries(attributes.map((item) => [item.name, item.value.trim().split(/\s+/).filter(Boolean)]));
83
- const included = matchesFeatures(requirements, context, label, 'section');
84
- if (!parentIncluded && context?.decisions.length)
85
- context.decisions[context.decisions.length - 1].included = false;
86
- const children = node.children.flatMap((child) => walk(child, parentIncluded && included));
87
- return included && parentIncluded ? children : [];
88
- }
89
- return [{ ...node, ...(node.children ? { children: node.children.flatMap((child) => walk(child, parentIncluded)) } : {}) }];
90
- };
91
- return walk(tree)[0];
90
+ return pruneConditionalTree(tree, (node, ancestorIncluded) => evaluateFeatureGuard(node, context, file, ancestorIncluded));
92
91
  }
93
92
  /** Returned to the caller or written under .shoalrun, never a served artifact. */
94
93
  export function profileSelection(resolution) {
@@ -1,4 +1,4 @@
1
- export declare const paletteTokens: readonly ["text", "page", "surface", "link", "link-hover", "focus", "ink", "accent", "accent-contrast", "header-muted", "header-border", "border", "border-soft", "border-nested", "field-surface", "field-border", "field-accent", "presence-bg", "presence-text", "optional-bg", "optional-text", "muted", "nav-heading", "section-heading", "nav-text", "nav-hover", "nav-active", "nav-active-text", "deprecated-bg", "deprecated-text", "beta-bg", "beta-text", "development-bg", "development-text", "experimental-bg", "experimental-text", "emphasis", "summary", "code-bg", "code-text", "code-muted", "code-border", "code-block", "callout-line", "callout-bg", ...("callout-note-line" | "callout-tip-line" | "callout-important-line" | "callout-warning-line" | "callout-caution-line" | "callout-note-bg" | "callout-tip-bg" | "callout-important-bg" | "callout-warning-bg" | "callout-caution-bg" | "callout-note-text" | "callout-tip-text" | "callout-important-text" | "callout-warning-text" | "callout-caution-text")[], "strong-surface", "strong-text"];
1
+ export declare const paletteTokens: readonly ["text", "page", "surface", "link", "link-hover", "focus", "ink", "accent", "accent-contrast", "header-muted", "header-border", "border", "border-soft", "border-nested", "field-surface", "field-border", "field-accent", "presence-bg", "presence-text", "optional-bg", "optional-text", "muted", "nav-heading", "section-heading", "nav-text", "nav-hover", "nav-active", "nav-active-text", "deprecated-bg", "deprecated-text", "beta-bg", "beta-text", "development-bg", "development-text", "experimental-bg", "experimental-text", "emphasis", "summary", "code-bg", "code-text", "code-muted", "code-border", "code-block", "callout-line", "callout-bg", ...("callout-warning-line" | "callout-note-line" | "callout-tip-line" | "callout-important-line" | "callout-caution-line" | "callout-warning-bg" | "callout-note-bg" | "callout-tip-bg" | "callout-important-bg" | "callout-caution-bg" | "callout-warning-text" | "callout-note-text" | "callout-tip-text" | "callout-important-text" | "callout-caution-text")[], "strong-surface", "strong-text"];
2
2
  export type Token = typeof paletteTokens[number];
3
3
  export type Mode = 'light' | 'dark';
4
4
  export type Palette = {
@@ -34,7 +34,7 @@ export function buildPublicationManifest({ configPublications, fictionalDocument
34
34
  ...(taxonomy ? { taxonomy } : {}),
35
35
  dependencies,
36
36
  ...(tabs.length ? { navigationTabs: tabs } : {}),
37
- documents: rendered.map(({ doc, markdown, sections }) => ({ id: doc.frontmatter.id, demo: fictionalDocuments.has(doc), type: doc.frontmatter.type, title: doc.frontmatter.title, summary: doc.frontmatter.summary, subjectLifecycle: doc.frontmatter.subjectLifecycle ?? null, ...(doc.change ? { change: doc.change } : {}), ...(doc.subjectDeprecation ? { subjectDeprecation: doc.subjectDeprecation } : {}), variant: doc.variant, url: doc.route, markdownUrl: doc.markdownRoute, ...tabFor(doc), indexable: indexable(doc), ...(updatedFor(doc) ? { updated: updatedFor(doc) } : {}), ...(doc.frontmatter.availability ? { availability: doc.frontmatter.availability } : {}), ...(doc.frontmatter.verified ? { verified: doc.frontmatter.verified } : {}), contentDigest: doc.contentDigest, breadcrumbs: navigationPath(resolvedNavigation.get(publicationKey(doc.variant)) ?? [], doc.route), ...(doc.frontmatter.product !== undefined ? { product: doc.frontmatter.product } : {}), ...(doc.frontmatter.area !== undefined ? { area: doc.frontmatter.area } : {}), ...(doc.frontmatter.subarea !== undefined ? { subarea: doc.frontmatter.subarea } : {}), ...(readerFilters ? { readerFilters: doc.readerFilters ?? {} } : {}), cardTargets: cardUses.get(doc)?.targets ?? [], cardQueries: cardUses.get(doc)?.queries ?? [], snippets: [...new Set(snippetUses.filter(use => use.documentId === doc.frontmatter.id).map(use => use.id))], apiEmbeds: apiEmbedUses.filter(use => use.documentKey === keyFor(doc.frontmatter.id, doc.variant)).map(({ sourceFile, documentKey, ...request }) => request), focusedContractUrl: doc.focusedContractUrl ?? null, focusedContractBytes: doc.focusedContractUrl ? focusedContracts.get(doc.focusedContractUrl)?.bytes : null, focusedContractDigest: doc.focusedContractUrl ? focusedContracts.get(doc.focusedContractUrl)?.digest : null, sections: (sections ?? []).map(({ content, ...record }) => record), ...markdownSize(markdown), digest: digest(markdown) })),
37
+ documents: rendered.map(({ doc, markdown, sections }) => ({ id: doc.frontmatter.id, demo: fictionalDocuments.has(doc), type: doc.frontmatter.type, title: doc.frontmatter.title, summary: doc.frontmatter.summary, subjectLifecycle: doc.frontmatter.subjectLifecycle ?? null, ...(doc.change ? { change: doc.change } : {}), ...(doc.subjectDeprecation ? { subjectDeprecation: doc.subjectDeprecation } : {}), variant: doc.variant, url: doc.route, markdownUrl: doc.markdownRoute, ...tabFor(doc), indexable: indexable(doc), ...(updatedFor(doc) ? { updated: updatedFor(doc) } : {}), ...(doc.frontmatter.availability ? { availability: doc.frontmatter.availability } : {}), ...(doc.frontmatter.verified ? { verified: doc.frontmatter.verified } : {}), contentDigest: doc.contentDigest, breadcrumbs: navigationPath(resolvedNavigation.get(publicationKey(doc.variant)) ?? [], doc.route), ...(doc.frontmatter.product !== undefined ? { product: doc.frontmatter.product } : {}), ...(doc.frontmatter.area !== undefined ? { area: doc.frontmatter.area } : {}), ...(doc.frontmatter.subarea !== undefined ? { subarea: doc.frontmatter.subarea } : {}), ...(readerFilters ? { readerFilters: doc.readerFilters ?? {} } : {}), cardTargets: cardUses.get(doc)?.targets ?? [], cardQueries: cardUses.get(doc)?.queries ?? [], snippets: [...new Set(snippetUses.filter(use => (use.documentKey ? use.documentKey === keyFor(doc.frontmatter.id, doc.variant) : use.documentId === doc.frontmatter.id)).map(use => use.id))], apiEmbeds: apiEmbedUses.filter(use => use.documentKey === keyFor(doc.frontmatter.id, doc.variant)).map(({ sourceFile, documentKey, ...request }) => request), focusedContractUrl: doc.focusedContractUrl ?? null, focusedContractBytes: doc.focusedContractUrl ? focusedContracts.get(doc.focusedContractUrl)?.bytes : null, focusedContractDigest: doc.focusedContractUrl ? focusedContracts.get(doc.focusedContractUrl)?.digest : null, sections: (sections ?? []).map(({ content, ...record }) => record), ...markdownSize(markdown), digest: digest(markdown) })),
38
38
  operations,
39
39
  relationships: publishedRelationships,
40
40
  redirects,
@@ -20,8 +20,14 @@ export function publicTree(tree, representation, documentRoutes, mode = 'light',
20
20
  element('div', { 'data-component-content': '' }, children)
21
21
  ])];
22
22
  }
23
- if (node.type === 'docRepresentation')
24
- return node.representation === representation ? node.children.flatMap(walk) : [];
23
+ if (node.type === 'docRepresentation') {
24
+ if (node.representation !== representation)
25
+ return [];
26
+ const children = node.children.flatMap(walk);
27
+ // Mark HTML-only content so agent-parity checks can distinguish intentional
28
+ // format-specific prose from accidental content loss in the Markdown twin.
29
+ return representation === 'html' ? [element('div', { 'data-markdown-ignore': '' }, children)] : children;
30
+ }
25
31
  if (node.type === 'docCookbookChooser') {
26
32
  const prompt = node.mode === 'single' ? 'Choose one recipe.' : 'Choose one or more recipes.';
27
33
  const details = (option) => [option.when ? `Use when: ${option.when}` : option.summary, option.needs ? `You need: ${option.needs}` : '', option.outcome ? `You will have: ${option.outcome}` : ''].filter(Boolean);
@@ -64,7 +64,7 @@ export async function queryBuiltSite(directory, query, options = {}) {
64
64
  return [];
65
65
  const termScore = (term) => weight(title, term, title.startsWith(term.term) ? 12 : 8, true) || weight(summary, term, 4) || (headings.some(heading => match(heading, term)) ? 3 : 0) || weight(body, term, 1);
66
66
  let score = terms.reduce((total, term) => total + termScore(term), 0);
67
- const contentScore = terms.filter(term => !fallbackStopWords.has(term.term)).reduce((total, term) => total + termScore(term), 0);
67
+ let contentScore = terms.filter(term => !fallbackStopWords.has(term.term)).reduce((total, term) => total + termScore(term), 0);
68
68
  const prominentMatches = terms.filter(term => !fallbackStopWords.has(term.term) && (matchTitle(title, term) || match(summary, term) || headings.some(heading => match(heading, term)))).length;
69
69
  const section = terms.every(term => matchTitle(title, term)) ? undefined : doc.headings.find((heading, at) => terms.every(term => match(headings[at], term)));
70
70
  // Use only query terms with meaning, and require a unique matching section.
@@ -77,8 +77,10 @@ export async function queryBuiltSite(directory, query, options = {}) {
77
77
  // older indexes and unreviewed sections conservatively retain the full page.
78
78
  const selected = candidates.length === 1 && candidates[0].standalone === true ? candidates[0] : undefined;
79
79
  // A complete multiword section-title match outranks incidental page-title words.
80
- if (meaningful.length >= 2 && candidates.some(candidate => meaningful.every(term => match(candidate.title.toLocaleLowerCase(), term))))
80
+ if (meaningful.length >= 2 && candidates.some(candidate => meaningful.every(term => match(candidate.title.toLocaleLowerCase(), term)))) {
81
81
  score += 20;
82
+ contentScore += 20;
83
+ }
82
84
  const { product, channel, version, language } = parsePublicationKey(doc.variant);
83
85
  const tokens = selected?.markdownTokens ?? doc.markdownTokens;
84
86
  const read = { tool: selected ? 'get_section' : 'get_page', arguments: {
@@ -87,15 +89,20 @@ export async function queryBuiltSite(directory, query, options = {}) {
87
89
  } };
88
90
  return [{ score, contentScore, prominentMatches, matched: matched.length, partialMatched: matched.filter(term => !fallbackStopWords.has(term.term)).length, id: doc.id, type: doc.type, title: doc.title, summary: doc.summary, url: section ? `${doc.url}#${section.id}` : doc.url, markdownUrl: selected?.markdownUrl ?? doc.markdownUrl, pageMarkdownUrl: doc.markdownUrl, read, ...(tokens !== undefined ? { markdownTokens: tokens, markdownBytes: selected?.markdownBytes ?? doc.markdownBytes, markdownTokenEncoding: doc.markdownTokenEncoding } : {}), ...(selected || section ? { section: selected?.title ?? section?.title } : {}), snippet: excerpt(doc.text, terms.flatMap(unit => [unit.term, ...unit.alternatives])) }];
89
91
  });
90
- const complete = hits.filter(hit => hit.matched === terms.length);
92
+ const contentTerms = terms.filter(term => !fallbackStopWords.has(term.term)).length;
93
+ // Filler words shape the sentence, not the subject. A page that covers every
94
+ // content term is complete even when it omits "how", "do", or "the"; otherwise
95
+ // a page that happens to repeat filler words can hide the focused result.
96
+ const complete = contentTerms === 0
97
+ ? hits.filter(hit => hit.matched === terms.length)
98
+ : hits.filter(hit => hit.partialMatched === contentTerms);
91
99
  // Relax the all-words gate only when complete matches are entirely incidental
92
100
  // body matches. A near-complete result must cover all but one content term and
93
101
  // have a stronger non-filler field score. Title/summary/heading matches on any
94
102
  // complete result preserve the original preference; filler cannot earn promotion.
95
- const contentTerms = terms.filter(term => !fallbackStopWords.has(term.term)).length;
96
103
  const bestCompleteContent = Math.max(...complete.map(hit => hit.contentScore));
97
104
  const stronger = complete.length && contentTerms >= 3 && complete.every(hit => hit.prominentMatches === 0)
98
- ? hits.filter(hit => hit.matched < terms.length && hit.partialMatched >= contentTerms - 1
105
+ ? hits.filter(hit => hit.partialMatched < contentTerms && hit.partialMatched >= contentTerms - 1
99
106
  && hit.prominentMatches > 0 && hit.contentScore > bestCompleteContent) : [];
100
107
  const ranked = complete.length ? [...complete, ...stronger] : hits.filter(hit => hit.partialMatched > 0);
101
108
  // In partial fallback, one extra incidental word must not outweigh a focused
@@ -114,7 +121,7 @@ export async function queryBuiltSite(directory, query, options = {}) {
114
121
  if (aNear && a.contentScore !== b.contentScore)
115
122
  return b.contentScore - a.contentScore;
116
123
  }
117
- return (complete.length ? b.matched - a.matched : b.partialMatched - a.partialMatched) || b.score - a.score || a.title.localeCompare(b.title) || a.id.localeCompare(b.id);
124
+ return (complete.length ? b.contentScore - a.contentScore : b.partialMatched - a.partialMatched) || b.score - a.score || a.title.localeCompare(b.title) || a.id.localeCompare(b.id);
118
125
  });
119
126
  const publication = parsePublicationKey(variants[0]);
120
127
  return { status: 'ok', query: query.trim(), publication, total: ranked.length, matchMode: stronger.length ? 'mixed' : complete.length ? 'all' : 'partial', results: ranked.slice(0, limit).map(({ matched, partialMatched, contentScore, prominentMatches, ...result }) => result) };
@@ -0,0 +1,29 @@
1
+ import { type ReaderFiltersConfig, type ReaderSelection } from './reader-filters.js';
2
+ import type { CompilerPublicationsConfig } from './compiler-config.js';
3
+ import type { Document } from './content-source.js';
4
+ export type SectionSelection = {
5
+ facets?: ReaderFiltersConfig;
6
+ selection?: ReaderSelection;
7
+ decisions: Map<string, Record<string, unknown>>;
8
+ warnings: Map<string, {
9
+ code: string;
10
+ message: string;
11
+ }>;
12
+ hasConditions: boolean;
13
+ publications?: CompilerPublicationsConfig;
14
+ };
15
+ export declare function createSectionSelection(facets?: ReaderFiltersConfig, selection?: ReaderSelection, publications?: CompilerPublicationsConfig): SectionSelection;
16
+ /** Share publication validation with pruning so receipts reflect every build-time ancestor. */
17
+ export declare function evaluatePublicationGuard(node: any, doc: Document, file: string, publications?: CompilerPublicationsConfig): boolean | undefined;
18
+ export declare function pruneSelectedTree(tree: any, doc: Document, file?: string, record?: boolean): any;
19
+ export declare function warnReaderSection(doc: Document, file: string, line: number, requirements: ReaderSelection): void;
20
+ export declare function sectionSelectionReport(context: SectionSelection): {
21
+ selection: ReaderSelection | null;
22
+ decisions: Record<string, unknown>[];
23
+ warnings: {
24
+ code: string;
25
+ message: string;
26
+ }[];
27
+ };
28
+ /** Keep receipts private, atomic, and keyed by artifact so nested exports can't replace a site's record. */
29
+ export declare function writeSectionSelectionReport(base: string, context: SectionSelection, artifact: Record<string, unknown>): Promise<void>;
@@ -0,0 +1,92 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { mkdir, writeFile, rename, rm } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { pruneConditionalTree } from './conditional-tree.js';
5
+ import { evaluateFeatureGuard } from './deployment-profile.js';
6
+ import { matchesSectionContentFilter } from './content-filter.js';
7
+ import { validateReaderSelection } from './reader-filters.js';
8
+ import { documentKey } from './publication-identity.js';
9
+ export function createSectionSelection(facets, selection, publications) {
10
+ return { facets, selection, publications, decisions: new Map(), warnings: new Map(), hasConditions: false };
11
+ }
12
+ /** Share publication validation with pruning so receipts reflect every build-time ancestor. */
13
+ export function evaluatePublicationGuard(node, doc, file, publications) {
14
+ if (node.name !== 'ForPublication')
15
+ return;
16
+ const attributes = node.attributes ?? [];
17
+ const allowed = ['product', 'channel', 'version', 'language'];
18
+ if (node.type !== 'mdxJsxFlowElement' || !node.children?.length || !attributes.length || attributes.some((item) => item.type !== 'mdxJsxAttribute' || typeof item.value !== 'string' || !allowed.includes(item.name) || !item.value.trim()) || new Set(attributes.map((item) => item.name)).size !== attributes.length)
19
+ throw new Error(`${file}: <ForPublication> needs block content and distinct literal product, channel, version, or language attributes`);
20
+ const product = attributes.find((item) => item.name === 'product')?.value ?? doc.variant.product;
21
+ for (const item of attributes) {
22
+ const catalog = item.name === 'product' ? publications?.products : item.name === 'channel' ? publications?.channels : item.name === 'version' ? (product ? publications?.products?.[product]?.versions : publications?.versions) : undefined;
23
+ if (publications && item.name !== 'language' && (!catalog || !Object.hasOwn(catalog, item.value)))
24
+ throw new Error(`${file}: <ForPublication> names unknown ${item.name} ${item.value}`);
25
+ }
26
+ return !attributes.some((item) => doc.variant[item.name] !== item.value);
27
+ }
28
+ export function pruneSelectedTree(tree, doc, file = doc.file, record = true) {
29
+ const context = doc.sectionSelection;
30
+ return pruneConditionalTree(tree, (node, ancestorIncluded) => {
31
+ if (node.name === 'ForPublication')
32
+ return evaluatePublicationGuard(node, doc, file, context?.publications);
33
+ if (node.name === 'ForFeature')
34
+ return evaluateFeatureGuard(node, doc.featureContext, file, ancestorIncluded, record);
35
+ if (node.name !== 'ForFacet')
36
+ return;
37
+ const label = `${file}:${node.position?.start?.line ?? 1}: <ForFacet>`;
38
+ const attributes = node.attributes ?? [];
39
+ if (node.type !== 'mdxJsxFlowElement' || !node.children?.length || !attributes.length || attributes.some((item) => item.type !== 'mdxJsxAttribute' || typeof item.value !== 'string' || !item.value.trim()) || new Set(attributes.map((item) => item.name)).size !== attributes.length)
40
+ throw new Error(`${label} needs block content and distinct nonempty literal facet attributes`);
41
+ if (!context?.facets)
42
+ throw new Error(`${label} needs configured readerFilters.facets`);
43
+ const requirements = validateReaderSelection(Object.fromEntries(attributes.map((item) => [item.name, item.value.trim().split(/\s+/)])), context.facets, label);
44
+ context.hasConditions = true;
45
+ const match = matchesSectionContentFilter(requirements, context.selection);
46
+ if (record) {
47
+ const line = node.position?.start?.line ?? 1;
48
+ const key = JSON.stringify([documentKey(doc.frontmatter.id, doc.variant), file, line, requirements, ancestorIncluded]);
49
+ context.decisions.set(key, { documentId: doc.frontmatter.id, publication: doc.variant, source: file, line, requirements, localMatch: match, included: ancestorIncluded && match, reason: !ancestorIncluded ? 'ancestor-excluded' : match ? 'matched' : 'facet-mismatch' });
50
+ }
51
+ return match;
52
+ });
53
+ }
54
+ export function warnReaderSection(doc, file, line, requirements) {
55
+ const context = doc.sectionSelection;
56
+ if (!context?.selection)
57
+ return;
58
+ const overlap = Object.keys(requirements).filter(facet => context.selection?.[facet]);
59
+ if (!overlap.length)
60
+ return;
61
+ const key = JSON.stringify([file, line, overlap]);
62
+ if (context.warnings.has(key))
63
+ return;
64
+ const message = `${file}:${line}: <ReaderSection> uses build-selected facet(s) ${overlap.join(', ')} but only changes reader visibility; its content remains in the output. Use <ForFacet> to omit unmatched content during this build.`;
65
+ context.warnings.set(key, { code: 'reader-section-build-facet', message });
66
+ process.stderr.write(`warning [reader-section-build-facet] ${message}\n`);
67
+ }
68
+ export function sectionSelectionReport(context) {
69
+ return { selection: context.selection ?? null, decisions: [...context.decisions.values()], warnings: [...context.warnings.values()] };
70
+ }
71
+ /** Keep receipts private, atomic, and keyed by artifact so nested exports can't replace a site's record. */
72
+ export async function writeSectionSelectionReport(base, context, artifact) {
73
+ const folder = path.join(base, '.shoalrun');
74
+ await mkdir(folder, { recursive: true });
75
+ const file = path.join(folder, 'content-selection.json');
76
+ const key = createHash('sha256').update(JSON.stringify(artifact.target)).digest('hex');
77
+ const receipt = { schemaVersion: 1, ...artifact, ...sectionSelectionReport(context) };
78
+ const receipts = path.join(folder, 'content-selection');
79
+ await mkdir(receipts, { recursive: true });
80
+ const atomic = async (destination, value) => {
81
+ const temporary = path.join(folder, `.content-selection-${randomUUID()}.json`);
82
+ try {
83
+ await writeFile(temporary, JSON.stringify(value, null, 2) + '\n');
84
+ await rename(temporary, destination);
85
+ }
86
+ finally {
87
+ await rm(temporary, { force: true });
88
+ }
89
+ };
90
+ await atomic(path.join(receipts, key + '.json'), receipt);
91
+ await atomic(file, { ...receipt, receipt: `content-selection/${key}.json` });
92
+ }
@@ -13,6 +13,7 @@ export type SnippetUse = {
13
13
  id: string;
14
14
  file: string;
15
15
  documentId: string;
16
+ documentKey?: string;
16
17
  };
17
18
  export declare function validateSnippetConfig(id: string, definition: any): Record<string, SnippetVariable>;
18
19
  export declare function snippetAttribute(attribute: any, label: string): unknown;
@@ -0,0 +1,37 @@
1
+ # Additional information in a selected build
2
+
3
+ This example uses one shared page, with an internal operations section. It needs
4
+ no customer profile or feature manifest. From this directory, with Shoalrun installed:
5
+
6
+ ```sh
7
+ shoalrun build docs.config.yaml dist/complete
8
+ shoalrun build customer.config.yaml dist/customer
9
+ shoalrun serve dist/complete --port 8771
10
+ ```
11
+
12
+ Open <http://127.0.0.1:8771/>. In another terminal, serve the customer output:
13
+
14
+ ```sh
15
+ shoalrun serve dist/customer --port 8772
16
+ ```
17
+
18
+ Open <http://127.0.0.1:8772/>. Both sites show **Review a bill** and **Get help**.
19
+ Only the complete build has **Check the retry queue**. Check `index.md` and
20
+ `search-index.json` in each output: the customer files must contain neither that
21
+ heading nor its instructions. The unfiltered build includes all valid blocks.
22
+
23
+ **Unmarked sections stay included. Unmarked pages are excluded when a selected
24
+ facet has no matching page mark.** This page explicitly belongs to both audiences.
25
+ `<ForFacet audience="internal partner">` would accept either choice if both were
26
+ configured; multiple facet attributes must all match. A facet the build doesn't
27
+ filter passes automatically. Selection removes sections rather than swapping
28
+ variants. Use positive literal matches only.
29
+
30
+ `<ReaderSection>` is different: it hides content in browser views while publishing
31
+ it in output files. Never use it to omit internal instructions from a customer
32
+ build. Build-time omission doesn't authenticate the complete site's deployment.
33
+
34
+ After each successful build, `.shoalrun/content-selection.json` records its target
35
+ and section decisions. Follow its `receipt` path for the artifact-specific copy;
36
+ these reports belong outside served output. Portable exports need their own
37
+ `contentFilter`; PDF exports inherit the site's filter unless overridden.