@astryxdesign/cli 0.6.3 → 0.6.4-canary.10dd683

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 (643) hide show
  1. package/CHANGELOG.md +194 -0
  2. package/README.md +117 -78
  3. package/api/blog/blog.doc.mjs +1 -0
  4. package/api/build/_adapter.d.mts +50 -0
  5. package/api/build/_adapter.mjs +60 -0
  6. package/api/build/build.doc.mjs +16 -9
  7. package/api/build/build.test.mjs +197 -8
  8. package/api/build/build.type.d.mts +91 -2
  9. package/api/build/build.type.mjs +52 -8
  10. package/api/build/help/help.d.mts +12 -5
  11. package/api/build/help/help.mjs +69 -6
  12. package/api/build/kit/kit.d.mts +4 -1
  13. package/api/build/kit/kit.mjs +165 -49
  14. package/api/build/kit/rank.d.mts +44 -0
  15. package/api/build/kit/rank.mjs +432 -0
  16. package/api/build/kit/rank.test.mjs +196 -0
  17. package/api/component/_adapter.d.mts +6 -12
  18. package/api/component/_adapter.mjs +20 -10
  19. package/api/component/component.doc.mjs +13 -3
  20. package/api/component/component.mjs +91 -14
  21. package/api/component/component.test.mjs +38 -0
  22. package/api/component/component.type.d.mts +22 -11
  23. package/api/component/component.type.mjs +32 -24
  24. package/api/component/detail/blocks/blocks.d.mts +2 -1
  25. package/api/component/detail/blocks/blocks.mjs +4 -3
  26. package/api/component/list/list.d.mts +0 -5
  27. package/api/component/list/list.mjs +40 -11
  28. package/api/discover/_adapter.d.mts +114 -6
  29. package/api/discover/_adapter.mjs +372 -17
  30. package/api/discover/_adapter.test.mjs +215 -0
  31. package/api/discover/_catalog-view.d.mts +115 -0
  32. package/api/discover/_catalog-view.mjs +203 -0
  33. package/api/discover/_catalog-view.test.mjs +128 -0
  34. package/api/discover/detail/detail.d.mts +18 -6
  35. package/api/discover/detail/detail.mjs +67 -13
  36. package/api/discover/detail/detail.test.mjs +85 -0
  37. package/api/discover/detail/item/item.d.mts +26 -0
  38. package/api/discover/detail/item/item.mjs +78 -0
  39. package/api/discover/detail/item/item.test.mjs +73 -0
  40. package/api/discover/discover.d.mts +3 -9
  41. package/api/discover/discover.doc.mjs +62 -18
  42. package/api/discover/discover.mjs +220 -36
  43. package/api/discover/discover.test.mjs +11 -2
  44. package/api/discover/discover.type.d.mts +150 -11
  45. package/api/discover/discover.type.mjs +107 -17
  46. package/api/discover/list/list.d.mts +20 -6
  47. package/api/discover/list/list.mjs +45 -12
  48. package/api/discover/list/list.test.mjs +46 -0
  49. package/api/discover/search/search.d.mts +18 -16
  50. package/api/discover/search/search.mjs +102 -56
  51. package/api/discover/search/search.test.mjs +144 -10
  52. package/api/docs/_adapter.d.mts +272 -41
  53. package/api/docs/_adapter.mjs +985 -108
  54. package/api/docs/compiled-topics.test.mjs +78 -0
  55. package/api/docs/detail/detail.mjs +22 -63
  56. package/api/docs/detail/section/section.d.mts +1 -1
  57. package/api/docs/detail/section/section.mjs +54 -19
  58. package/api/docs/detail/section/section.test.mjs +50 -0
  59. package/api/docs/docs.d.mts +10 -3
  60. package/api/docs/docs.doc.mjs +55 -16
  61. package/api/docs/docs.mjs +53 -10
  62. package/api/docs/docs.test.mjs +164 -4
  63. package/api/docs/docs.type.d.mts +221 -5
  64. package/api/docs/docs.type.mjs +153 -11
  65. package/api/docs/index/index.d.mts +18 -0
  66. package/api/docs/index/index.mjs +40 -0
  67. package/api/docs/index/index.test.mjs +62 -0
  68. package/api/docs/integration-tree.test.mjs +555 -0
  69. package/api/docs/integrationDocs.test.mjs +114 -8
  70. package/api/docs/list/list.mjs +28 -12
  71. package/api/docs/node/node.d.mts +43 -0
  72. package/api/docs/node/node.mjs +192 -0
  73. package/api/docs/reference-blocks.test.mjs +406 -0
  74. package/api/doctor/doctor.d.mts +99 -1
  75. package/api/doctor/doctor.doc.mjs +1 -0
  76. package/api/doctor/doctor.mjs +548 -1
  77. package/api/doctor/doctor.test.mjs +610 -1
  78. package/api/gap-report/gap-report.doc.mjs +8 -4
  79. package/api/hook/_adapter.mjs +19 -5
  80. package/api/hook/hook.doc.mjs +1 -0
  81. package/api/hook/hook.type.d.mts +3 -3
  82. package/api/hook/hook.type.mjs +11 -11
  83. package/api/hook/list/list.d.mts +2 -2
  84. package/api/hook/list/list.mjs +69 -17
  85. package/api/index.d.mts +1 -1
  86. package/api/index.mjs +1 -0
  87. package/api/init/init.doc.mjs +6 -1
  88. package/api/init/init.test.mjs +41 -1
  89. package/api/init/remove/remove.mjs +1 -1
  90. package/api/init/run/run.mjs +20 -10
  91. package/api/integration/add-contribution.component-names.test.mjs +120 -0
  92. package/api/integration/add-contribution.d.mts +2 -1
  93. package/api/integration/add-contribution.mjs +130 -15
  94. package/api/integration/add-contribution.test.mjs +258 -7
  95. package/api/integration/add-helpers.d.mts +5 -2
  96. package/api/integration/add-helpers.mjs +36 -9
  97. package/api/integration/add-theme.mjs +34 -64
  98. package/api/integration/add-theme.test.mjs +105 -21
  99. package/api/integration/authoring-checks.mjs +138 -28
  100. package/api/integration/authoring-checks.test.mjs +179 -7
  101. package/api/integration/authoring-checks.type.mjs +6 -1
  102. package/api/integration/integration-authoring.type.d.mts +3 -1
  103. package/api/integration/integration-authoring.type.mjs +2 -0
  104. package/api/integration/integration-block-exports.test.mjs +10 -6
  105. package/api/integration/integrationAdd.doc.mjs +14 -4
  106. package/api/integration/integrationAddAgentDoc.doc.mjs +1 -0
  107. package/api/integration/integrationAddCodemod.doc.mjs +1 -0
  108. package/api/integration/integrationAddComponent.doc.mjs +2 -1
  109. package/api/integration/integrationAddDoc.doc.mjs +8 -1
  110. package/api/integration/integrationAddTemplate.doc.mjs +1 -0
  111. package/api/integration/integrationAddTheme.doc.mjs +6 -5
  112. package/api/integration/integrationComponentConflicts.doc.mjs +1 -0
  113. package/api/integration/integrationDocConflicts.doc.mjs +2 -1
  114. package/api/integration/integrationPackCheck.doc.mjs +2 -1
  115. package/api/integration/integrationTemplateConflicts.doc.d.mts +3 -0
  116. package/api/integration/integrationTemplateConflicts.doc.mjs +4 -0
  117. package/api/integration/pack-check.mjs +83 -7
  118. package/api/integration/pack-check.test.mjs +387 -47
  119. package/api/integration/pack-check.type.d.mts +26 -2
  120. package/api/integration/pack-check.type.mjs +14 -1
  121. package/api/integration/summarizeIssues.doc.mjs +1 -0
  122. package/api/integration/template-conflict-compatibility.test.mjs +73 -0
  123. package/api/integration/validate-integration-fixes.test.mjs +1389 -0
  124. package/api/integration/validate-integration.mjs +52 -102
  125. package/api/integration/validate-integration.test.mjs +179 -26
  126. package/api/integration/validate-unread-theme-folders.test.mjs +110 -0
  127. package/api/integration/validateIntegration.doc.mjs +3 -2
  128. package/api/json/assertResponse.doc.mjs +1 -0
  129. package/api/json/envelope-types.test.mjs +76 -0
  130. package/api/json/index.ts +2 -0
  131. package/api/json/isError.doc.mjs +1 -0
  132. package/api/json/parseResponse.doc.mjs +3 -2
  133. package/api/layout/_adapter.mjs +20 -5
  134. package/api/layout/expand/expand.mjs +7 -5
  135. package/api/layout/expand/expand.path-safety.test.mjs +53 -0
  136. package/api/layout/grammar/grammar.mjs +2 -1
  137. package/api/layout/layoutCheck.doc.mjs +1 -0
  138. package/api/layout/layoutExpand.doc.mjs +2 -1
  139. package/api/layout/layoutGrammar.doc.mjs +1 -0
  140. package/api/search/search-return-type.test.mjs +54 -0
  141. package/api/search/search.d.mts +62 -11
  142. package/api/search/search.doc.mjs +8 -2
  143. package/api/search/search.mjs +471 -83
  144. package/api/search/search.test.mjs +124 -1
  145. package/api/search/search.type.d.mts +15 -3
  146. package/api/search/search.type.mjs +5 -2
  147. package/api/swizzle/copy/copy.mjs +28 -11
  148. package/api/swizzle/swizzle.doc.mjs +2 -1
  149. package/api/swizzle/swizzle.type.d.mts +2 -2
  150. package/api/swizzle/swizzle.type.mjs +2 -2
  151. package/api/template/copy/copy.mjs +17 -23
  152. package/api/template/copy/copy.test.mjs +17 -0
  153. package/api/template/list/list.mjs +1 -0
  154. package/api/template/table-floating-bulk-actions.test.mjs +66 -0
  155. package/api/template/template-integration.test.mjs +1072 -3
  156. package/api/template/template-suffix.test.mjs +41 -21
  157. package/api/template/template.d.mts +1 -1
  158. package/api/template/template.doc.mjs +30 -8
  159. package/api/template/template.mjs +45 -8
  160. package/api/template/template.type.d.mts +12 -14
  161. package/api/template/template.type.mjs +15 -14
  162. package/api/theme/_adapter.d.mts +2 -3
  163. package/api/theme/_adapter.mjs +4 -5
  164. package/api/theme/add/add.binary.test.mjs +84 -0
  165. package/api/theme/add/add.mjs +31 -22
  166. package/api/theme/add/add.rollback.test.mjs +158 -0
  167. package/api/theme/add/add.staging.test.mjs +83 -0
  168. package/api/theme/add/add.test.mjs +14 -1
  169. package/api/theme/build/build.family.test.mjs +7 -12
  170. package/api/theme/build/build.mjs +140 -59
  171. package/api/theme/build/build.public-component-vars.test.mjs +1 -1
  172. package/api/theme/build/build.receipt-doc.test.mjs +111 -0
  173. package/api/theme/build/build.rollback.test.mjs +148 -0
  174. package/api/theme/build/build.test.mjs +127 -0
  175. package/api/theme/build/font-warning.mjs +3 -3
  176. package/api/theme/build/font-warning.test.mjs +5 -2
  177. package/api/theme/generateTonalPalette.doc.mjs +1 -0
  178. package/api/theme/integration-themes.test.mjs +39 -28
  179. package/api/theme/list/list.test.mjs +19 -20
  180. package/api/theme/listThemes.doc.mjs +6 -5
  181. package/api/theme/palette/generate/generate.mjs +8 -3
  182. package/api/theme/palette/generate/generate.test.mjs +96 -0
  183. package/api/theme/palette/generate/generator.d.mts +10 -13
  184. package/api/theme/palette/generate/generator.mjs +15 -4
  185. package/api/theme/palette/generate/generator.test.mjs +10 -0
  186. package/api/theme/template/template.mjs +11 -2
  187. package/api/theme/template/template.test.mjs +20 -0
  188. package/api/theme/theme.type.d.mts +170 -11
  189. package/api/theme/theme.type.mjs +94 -27
  190. package/api/theme/themeAdd.doc.mjs +4 -3
  191. package/api/theme/themeBuild.doc.mjs +8 -4
  192. package/api/theme/themeList.doc.mjs +6 -3
  193. package/api/theme/themeListAvailable.doc.mjs +5 -3
  194. package/api/theme/themePaletteGenerate.doc.mjs +1 -0
  195. package/api/theme/themeTargets.doc.mjs +1 -0
  196. package/api/theme/themeTemplate.doc.mjs +6 -2
  197. package/api/upgrade/_adapter.d.mts +32 -5
  198. package/api/upgrade/_adapter.mjs +139 -22
  199. package/api/upgrade/list/list.mjs +2 -1
  200. package/api/upgrade/list/list.test.mjs +73 -0
  201. package/api/upgrade/project-context.test.mjs +272 -0
  202. package/api/upgrade/provider-agreement.test.mjs +152 -0
  203. package/api/upgrade/run/run.mjs +356 -59
  204. package/api/upgrade/status/status.mjs +2 -2
  205. package/api/upgrade/upgrade.doc.mjs +12 -5
  206. package/api/upgrade/upgrade.type.d.mts +43 -5
  207. package/api/upgrade/upgrade.type.mjs +27 -11
  208. package/assets/codemods/__tests__/registry.test.mjs +1 -0
  209. package/assets/codemods/__tests__/runner.test.mjs +330 -8
  210. package/assets/codemods/integration-discovery.mjs +48 -4
  211. package/assets/codemods/integration-discovery.test.mjs +73 -0
  212. package/assets/codemods/integration-runner.mjs +56 -4
  213. package/assets/codemods/integration-runner.protection.test.mjs +153 -0
  214. package/assets/codemods/registry.mjs +1 -0
  215. package/assets/codemods/run-codemod.mjs +177 -34
  216. package/assets/codemods/runner.mjs +350 -102
  217. package/assets/codemods/term-log.mjs +32 -8
  218. package/assets/codemods/term-log.test.mjs +19 -1
  219. package/assets/codemods/transform-prop.mjs +109 -0
  220. package/assets/codemods/transform-prop.test.mjs +95 -0
  221. package/assets/codemods/transforms/v0.0.14/__tests__/rename-status-variants.test.mjs +86 -165
  222. package/assets/codemods/transforms/v0.0.14/rename-status-variants.mjs +72 -210
  223. package/assets/codemods/transforms/v0.1.8/__tests__/rename-avatar-size-scale.test.mjs +83 -115
  224. package/assets/codemods/transforms/v0.1.8/rename-avatar-size-scale.mjs +57 -186
  225. package/assets/codemods/transforms/v0.3.0/__tests__/unwrap-authoring-factories.test.mjs +27 -5
  226. package/assets/codemods/transforms/v0.3.0/unwrap-authoring-factories.mjs +20 -5
  227. package/assets/codemods/transforms/v0.6.0/__tests__/next-codemods.test.mjs +47 -0
  228. package/assets/codemods/transforms/v0.6.0/rename-resizable-pixel-bounds.mjs +57 -10
  229. package/assets/codemods/transforms/v0.6.4/__tests__/migrate-native-picker-to-presentation.test.mjs +63 -0
  230. package/assets/codemods/transforms/v0.6.4/__tests__/migrate-theme-catalog-to-descriptors.test.mjs +220 -0
  231. package/assets/codemods/transforms/v0.6.4/index.mjs +31 -0
  232. package/assets/codemods/transforms/v0.6.4/migrate-native-picker-to-presentation.mjs +148 -0
  233. package/assets/codemods/transforms/v0.6.4/migrate-theme-catalog-to-descriptors.mjs +141 -0
  234. package/assets/docs/README.md +9 -0
  235. package/assets/docs/authoring.doc.mjs +14 -0
  236. package/assets/docs/getting-started.doc.mjs +2 -2
  237. package/assets/docs/internationalization.doc.mjs +7 -5
  238. package/assets/docs/layout.doc.dense.mjs +2 -2
  239. package/assets/docs/layout.doc.mjs +1 -1
  240. package/assets/docs/principles.doc.mjs +6 -6
  241. package/assets/docs/styling-libraries.doc.mjs +4 -4
  242. package/assets/docs/styling.doc.mjs +4 -4
  243. package/assets/docs/theme.doc.mjs +5 -5
  244. package/assets/docs/tokens.doc.mjs +1 -1
  245. package/assets/docs/tree/api.doc.mjs +30 -0
  246. package/assets/docs/tree/cli.doc.mjs +23 -0
  247. package/assets/docs/tree/commands.doc.mjs +25 -0
  248. package/assets/docs/{cli-integrations.doc.mjs → tree/integrations.doc.mjs} +144 -26
  249. package/assets/docs/tree/integrations.test.mjs +62 -0
  250. package/assets/docs/tree/writing-docs.doc.mjs +286 -0
  251. package/assets/docs/working-with-ai.doc.mjs +4 -4
  252. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.doc.mjs +1 -1
  253. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.tsx +1 -3
  254. package/assets/templates/blocks/components/DateInput/DateInputDateRange.tsx +1 -1
  255. package/assets/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.tsx +1 -1
  256. package/assets/templates/blocks/components/Item/ItemDocumentTabs.doc.mjs +14 -0
  257. package/assets/templates/blocks/components/Item/ItemDocumentTabs.tsx +100 -0
  258. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.doc.mjs +14 -0
  259. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.tsx +61 -0
  260. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.doc.mjs +14 -0
  261. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.tsx +126 -0
  262. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.doc.mjs +15 -0
  263. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.tsx +86 -0
  264. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.doc.mjs +14 -0
  265. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.tsx +99 -0
  266. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.doc.mjs +14 -0
  267. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.tsx +122 -0
  268. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.doc.mjs +14 -0
  269. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.tsx +95 -0
  270. package/assets/templates/blocks/components/Table/TableBulkActionsTable.doc.mjs +14 -0
  271. package/assets/templates/blocks/components/Table/TableBulkActionsTable.tsx +71 -0
  272. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.doc.mjs +19 -0
  273. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.tsx +139 -0
  274. package/assets/templates/blocks/components/TimeInput/TimeInputConstrained.tsx +1 -0
  275. package/assets/templates/blocks/components/Timer/TimerFormats.doc.mjs +14 -0
  276. package/assets/templates/blocks/components/Timer/TimerFormats.tsx +34 -0
  277. package/assets/templates/blocks/components/Timer/TimerInline.doc.mjs +14 -0
  278. package/assets/templates/blocks/components/Timer/TimerInline.tsx +14 -0
  279. package/assets/templates/blocks/components/Timer/TimerShowcase.doc.mjs +13 -0
  280. package/assets/templates/blocks/components/Timer/TimerShowcase.tsx +47 -0
  281. package/assets/templates/blocks/components/Timer/TimerTypography.doc.mjs +14 -0
  282. package/assets/templates/blocks/components/Timer/TimerTypography.tsx +31 -0
  283. package/assets/templates/blocks/components/Toolbar/ToolbarTableFilter.doc.mjs +19 -3
  284. package/assets/templates/blocks/components/Toolbar/ToolbarTableFilter.tsx +383 -65
  285. package/assets/templates/pages/table-tree/page.tsx +1704 -0
  286. package/assets/templates/pages/table-tree/template.doc.mjs +12 -0
  287. package/assets/templates/themes/butter/butterTheme.doc.mjs +11 -0
  288. package/assets/templates/themes/chocolate/chocolateTheme.doc.mjs +11 -0
  289. package/assets/templates/themes/gothic/gothicTheme.doc.mjs +11 -0
  290. package/assets/templates/themes/matcha/matchaTheme.doc.mjs +11 -0
  291. package/assets/templates/themes/neutral/neutralTheme.doc.mjs +11 -0
  292. package/assets/templates/themes/stone/stoneTheme.doc.mjs +11 -0
  293. package/assets/templates/themes/y2k/y2kTheme.doc.mjs +11 -0
  294. package/authoring/_shared/contract.ts +22 -0
  295. package/authoring/codemod/codemod.doc.mjs +7 -2
  296. package/authoring/codemod/parse.d.mts +8 -8
  297. package/authoring/codemod/parse.mjs +8 -6
  298. package/authoring/codemod/type.ts +12 -0
  299. package/authoring/config/config.doc.mjs +10 -2
  300. package/authoring/config/debug-composition.test.mjs +92 -0
  301. package/authoring/config/parse.d.mts +15 -13
  302. package/authoring/config/parse.mjs +27 -8
  303. package/authoring/config/parse.test.mjs +8 -0
  304. package/authoring/config/type.ts +29 -6
  305. package/authoring/debug/debug.doc.d.mts +11 -0
  306. package/authoring/debug/debug.doc.mjs +182 -0
  307. package/authoring/debug/parse.d.mts +8 -8
  308. package/authoring/debug/parse.mjs +3 -3
  309. package/authoring/discover/discover.doc.d.mts +13 -0
  310. package/authoring/discover/discover.doc.mjs +138 -0
  311. package/authoring/discover/parse.d.mts +24 -0
  312. package/authoring/discover/parse.mjs +128 -0
  313. package/authoring/discover/parse.test.mjs +124 -0
  314. package/authoring/discover/type.ts +87 -0
  315. package/authoring/doctypes/_schema.d.mts +790 -23
  316. package/authoring/doctypes/_schema.mjs +543 -39
  317. package/authoring/doctypes/base/graph-fields.doc.d.mts +9 -0
  318. package/authoring/doctypes/base/graph-fields.doc.mjs +64 -0
  319. package/authoring/doctypes/base/type.ts +41 -0
  320. package/authoring/doctypes/command/command.doc.mjs +4 -3
  321. package/authoring/doctypes/command/parse.d.mts +2 -2
  322. package/authoring/doctypes/command/parse.mjs +1 -1
  323. package/authoring/doctypes/command/type.ts +5 -4
  324. package/authoring/doctypes/component/component.doc.mjs +6 -3
  325. package/authoring/doctypes/component/parse.d.mts +2 -2
  326. package/authoring/doctypes/component/parse.mjs +1 -1
  327. package/authoring/doctypes/component/type.ts +6 -5
  328. package/authoring/doctypes/doctypes-new.test.mjs +48 -6
  329. package/authoring/doctypes/enum/enum.doc.mjs +1 -1
  330. package/authoring/doctypes/enum/parse.d.mts +2 -2
  331. package/authoring/doctypes/enum/parse.mjs +1 -1
  332. package/authoring/doctypes/enum/type.ts +4 -2
  333. package/authoring/doctypes/function/function.doc.mjs +7 -2
  334. package/authoring/doctypes/function/parse.d.mts +2 -2
  335. package/authoring/doctypes/function/parse.mjs +1 -1
  336. package/authoring/doctypes/function/type.ts +9 -4
  337. package/authoring/doctypes/hook/hook.doc.mjs +4 -0
  338. package/authoring/doctypes/hook/parse.d.mts +2 -2
  339. package/authoring/doctypes/hook/parse.mjs +1 -1
  340. package/authoring/doctypes/hook/type.ts +5 -4
  341. package/authoring/doctypes/legacy.d.mts +8 -6
  342. package/authoring/doctypes/legacy.mjs +5 -4
  343. package/authoring/doctypes/load-contract.test.mjs +233 -0
  344. package/authoring/doctypes/namespace/namespace.doc.d.mts +9 -0
  345. package/authoring/doctypes/namespace/namespace.doc.mjs +128 -0
  346. package/authoring/doctypes/namespace/parse.d.mts +12 -0
  347. package/authoring/doctypes/namespace/parse.mjs +25 -0
  348. package/authoring/doctypes/namespace/parse.test.mjs +163 -0
  349. package/authoring/doctypes/namespace/type.ts +74 -0
  350. package/authoring/doctypes/parse.d.mts +22 -18
  351. package/authoring/doctypes/parse.mjs +22 -11
  352. package/authoring/doctypes/parse.test.mjs +77 -3
  353. package/authoring/doctypes/reference/parse.d.mts +2 -2
  354. package/authoring/doctypes/reference/parse.mjs +8 -5
  355. package/authoring/doctypes/reference/reference.doc.mjs +48 -6
  356. package/authoring/doctypes/reference/type.ts +70 -7
  357. package/authoring/doctypes/schema/parse.d.mts +2 -2
  358. package/authoring/doctypes/schema/parse.mjs +1 -1
  359. package/authoring/doctypes/schema/schema.doc.mjs +1 -1
  360. package/authoring/doctypes/schema/type.ts +4 -4
  361. package/authoring/doctypes/template/parse.d.mts +94 -1
  362. package/authoring/doctypes/template/parse.mjs +40 -2
  363. package/authoring/doctypes/template/parse.test.mjs +26 -2
  364. package/authoring/doctypes/template/template.doc.mjs +13 -3
  365. package/authoring/doctypes/template/type.ts +13 -2
  366. package/authoring/doctypes/theme/parse.d.mts +35 -0
  367. package/authoring/doctypes/theme/parse.mjs +76 -0
  368. package/authoring/doctypes/theme/theme.doc.d.mts +9 -0
  369. package/authoring/doctypes/theme/theme.doc.mjs +79 -0
  370. package/authoring/doctypes/theme/type.ts +42 -0
  371. package/authoring/doctypes/types.ts +12 -10
  372. package/authoring/gap-report/gap-report.doc.d.mts +12 -0
  373. package/authoring/gap-report/gap-report.doc.mjs +183 -0
  374. package/authoring/gap-report/parse.d.mts +10 -10
  375. package/authoring/gap-report/parse.mjs +6 -6
  376. package/authoring/gap-report/type.ts +1 -1
  377. package/authoring/identity/identity.doc.d.mts +9 -0
  378. package/authoring/identity/identity.doc.mjs +61 -0
  379. package/authoring/identity/type.ts +132 -0
  380. package/authoring/index.d.mts +3 -0
  381. package/authoring/index.d.ts +62 -17
  382. package/authoring/index.mjs +4 -1
  383. package/authoring/integration/integration.doc.mjs +15 -8
  384. package/authoring/integration/parse.d.mts +2 -2
  385. package/authoring/integration/parse.mjs +1 -1
  386. package/authoring/integration/parse.test.mjs +10 -1
  387. package/authoring/integration/schema.d.mts +6 -4
  388. package/authoring/integration/schema.mjs +9 -3
  389. package/authoring/integration/type.ts +19 -8
  390. package/authoring/shadcn/receipt.d.mts +6 -6
  391. package/clients/cli/__tests__/cliManifest.test.ts +27 -29
  392. package/clients/cli/command-load-failure.test.mjs +83 -0
  393. package/clients/cli/commands/blog.doc.mjs +1 -1
  394. package/clients/cli/commands/blog.mjs +23 -8
  395. package/clients/cli/commands/blog.test.mjs +42 -1
  396. package/clients/cli/commands/build-theme.adaptations.test.mjs +100 -1
  397. package/clients/cli/commands/build-theme.ascii-output.test.mjs +161 -0
  398. package/clients/cli/commands/build-theme.flag-docs.test.mjs +110 -0
  399. package/clients/cli/commands/build-theme.mjs +16 -50
  400. package/clients/cli/commands/build-theme.path-safety.test.mjs +86 -1
  401. package/clients/cli/commands/build-theme.variants.test.mjs +76 -0
  402. package/clients/cli/commands/build.doc.mjs +16 -8
  403. package/clients/cli/commands/build.exit-codes-doc.test.mjs +50 -0
  404. package/clients/cli/commands/build.mjs +137 -114
  405. package/clients/cli/commands/build.playbook.test.mjs +75 -0
  406. package/clients/cli/commands/build.text-fields.test.mjs +81 -0
  407. package/clients/cli/commands/component/index.mjs +3 -8
  408. package/clients/cli/commands/component-ownership.test.mjs +3 -3
  409. package/clients/cli/commands/component-package.test.mjs +46 -0
  410. package/clients/cli/commands/component-resolution.test.mjs +21 -0
  411. package/clients/cli/commands/component.doc.mjs +1 -1
  412. package/clients/cli/commands/component.test.mjs +19 -0
  413. package/clients/cli/commands/detail-levels.test.mjs +2 -2
  414. package/clients/cli/commands/discover.broken-integration.test.mjs +52 -5
  415. package/clients/cli/commands/discover.components-flag.test.mjs +97 -0
  416. package/clients/cli/commands/discover.doc.mjs +55 -9
  417. package/clients/cli/commands/discover.mjs +393 -118
  418. package/clients/cli/commands/discover.sources.test.mjs +267 -0
  419. package/clients/cli/commands/discover.text-projection.test.mjs +103 -0
  420. package/clients/cli/commands/docs.doc.mjs +28 -6
  421. package/clients/cli/commands/docs.mjs +240 -26
  422. package/clients/cli/commands/docs.test.mjs +193 -1
  423. package/clients/cli/commands/doctor-integration-components.doc.mjs +1 -1
  424. package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -3
  425. package/clients/cli/commands/doctor-integration-templates.doc.mjs +15 -8
  426. package/clients/cli/commands/doctor-integration-validate.doc.mjs +1 -1
  427. package/clients/cli/commands/doctor-integration.doc.mjs +1 -1
  428. package/clients/cli/commands/doctor-integration.package-json.test.mjs +53 -0
  429. package/clients/cli/commands/doctor-integration.test.mjs +90 -8
  430. package/clients/cli/commands/doctor.doc.mjs +1 -1
  431. package/clients/cli/commands/doctor.mjs +59 -32
  432. package/clients/cli/commands/doctor.test.mjs +42 -0
  433. package/clients/cli/commands/gap-report.doc.mjs +17 -6
  434. package/clients/cli/commands/gap-report.test.mjs +72 -0
  435. package/clients/cli/commands/hook/index.mjs +7 -17
  436. package/clients/cli/commands/hook.doc.mjs +1 -1
  437. package/clients/cli/commands/hook.text-projection.test.mjs +45 -0
  438. package/clients/cli/commands/init.doc.mjs +20 -9
  439. package/clients/cli/commands/init.flag-help.test.mjs +153 -0
  440. package/clients/cli/commands/integration-add.controls.test.mjs +132 -0
  441. package/clients/cli/commands/integration-add.doc.mjs +32 -6
  442. package/clients/cli/commands/integration-authoring.test.mjs +13 -9
  443. package/clients/cli/commands/integration-pack.doc.mjs +1 -1
  444. package/clients/cli/commands/integration-real-world.test.mjs +3 -9
  445. package/clients/cli/commands/integration.doc.mjs +1 -1
  446. package/clients/cli/commands/integration.mjs +1 -0
  447. package/clients/cli/commands/interactive-guard.test.mjs +101 -24
  448. package/clients/cli/commands/json-contract.test.mjs +33 -0
  449. package/clients/cli/commands/layout-check.doc.mjs +15 -4
  450. package/clients/cli/commands/layout-expand.doc.mjs +22 -5
  451. package/clients/cli/commands/layout-grammar.doc.mjs +1 -1
  452. package/clients/cli/commands/layout.doc.mjs +3 -3
  453. package/clients/cli/commands/layout.mjs +21 -9
  454. package/clients/cli/commands/layout.path-help.test.mjs +33 -0
  455. package/clients/cli/commands/layout.stdin-cap.test.mjs +47 -0
  456. package/clients/cli/commands/layout.text-fields.test.mjs +39 -0
  457. package/clients/cli/commands/manifest.doc.mjs +1 -1
  458. package/clients/cli/commands/no-prompt-wording.test.mjs +94 -0
  459. package/clients/cli/commands/search.doc.mjs +7 -4
  460. package/clients/cli/commands/search.mjs +28 -9
  461. package/clients/cli/commands/search.test.mjs +75 -0
  462. package/clients/cli/commands/setup-nudge.test.mjs +6 -0
  463. package/clients/cli/commands/swizzle.doc.mjs +3 -2
  464. package/clients/cli/commands/swizzle.path-safety.test.mjs +42 -0
  465. package/clients/cli/commands/template.doc.mjs +52 -13
  466. package/clients/cli/commands/template.flag-help.test.mjs +117 -0
  467. package/clients/cli/commands/template.mjs +4 -91
  468. package/clients/cli/commands/template.path-help.test.mjs +40 -0
  469. package/clients/cli/commands/text-json-parity.test.mjs +719 -0
  470. package/clients/cli/commands/theme-add.doc.mjs +4 -3
  471. package/clients/cli/commands/theme-build.doc.mjs +8 -7
  472. package/clients/cli/commands/theme-list.doc.mjs +2 -2
  473. package/clients/cli/commands/theme-palette-generate.doc.mjs +9 -5
  474. package/clients/cli/commands/theme-palette-generate.test.mjs +19 -0
  475. package/clients/cli/commands/theme-palette.doc.mjs +1 -1
  476. package/clients/cli/commands/theme-targets.behavior.test.mjs +4 -3
  477. package/clients/cli/commands/theme-targets.doc.mjs +1 -1
  478. package/clients/cli/commands/theme-template.behavior.test.mjs +12 -0
  479. package/clients/cli/commands/theme-template.doc.mjs +2 -2
  480. package/clients/cli/commands/theme.doc.mjs +1 -1
  481. package/clients/cli/commands/upgrade.ascii-output.test.mjs +87 -0
  482. package/clients/cli/commands/upgrade.doc.mjs +22 -10
  483. package/clients/cli/commands/upgrade.file-protection.test.mjs +228 -0
  484. package/clients/cli/commands/upgrade.flag-help.test.mjs +188 -0
  485. package/clients/cli/commands/upgrade.hook-output.test.mjs +88 -0
  486. package/clients/cli/commands/upgrade.mjs +29 -7
  487. package/clients/cli/formatters/index.mjs +164 -1
  488. package/clients/cli/formatters/index.test.mjs +97 -0
  489. package/clients/cli/index.mjs +21 -30
  490. package/clients/cli/latest-version-env.test.mjs +50 -0
  491. package/clients/cli/lib/cli-error.test.mjs +7 -0
  492. package/clients/cli/lib/component-format.mjs +9 -9
  493. package/clients/cli/lib/component-format.test.mjs +1 -1
  494. package/clients/cli/lib/define-command.mjs +32 -6
  495. package/clients/cli/lib/doc-text-ascii.test.mjs +82 -0
  496. package/clients/cli/lib/exit-codes.test.mjs +97 -0
  497. package/clients/cli/lib/hook-format.mjs +19 -10
  498. package/clients/cli/lib/json-shim.mjs +38 -2
  499. package/clients/cli/lib/json-shim.test.mjs +83 -0
  500. package/clients/cli/lib/manifest.d.ts +2 -0
  501. package/clients/cli/lib/manifest.mjs +37 -2
  502. package/clients/cli/lib/manifest.test.mjs +17 -0
  503. package/foundation/agent-docs/agent-docs.d.mts +7 -2
  504. package/foundation/agent-docs/agent-docs.mjs +82 -12
  505. package/foundation/agent-docs/agent-docs.path-safety.test.mjs +266 -4
  506. package/foundation/agent-docs/agent-docs.test.mjs +19 -1
  507. package/foundation/config/integration-debug.test.mjs +28 -3
  508. package/foundation/config/project-themes.test.mjs +11 -19
  509. package/foundation/config/project.d.mts +20 -11
  510. package/foundation/config/project.mjs +263 -91
  511. package/foundation/config/project.test.mjs +270 -21
  512. package/foundation/discovery/authoring-self-docs.d.mts +87 -0
  513. package/foundation/discovery/authoring-self-docs.mjs +237 -0
  514. package/foundation/discovery/authoring-self-docs.test.mjs +170 -0
  515. package/foundation/discovery/authoring-surface.d.mts +74 -0
  516. package/foundation/discovery/authoring-surface.mjs +525 -0
  517. package/foundation/discovery/authoring-surface.test.mjs +392 -0
  518. package/foundation/discovery/cli-self-docs.d.mts +119 -0
  519. package/foundation/discovery/cli-self-docs.mjs +490 -0
  520. package/foundation/discovery/cli-self-docs.test.mjs +375 -0
  521. package/foundation/discovery/component-discovery.d.mts +39 -1
  522. package/foundation/discovery/component-discovery.mjs +50 -1
  523. package/foundation/discovery/component-loader.d.mts +35 -38
  524. package/foundation/discovery/component-loader.mjs +53 -222
  525. package/foundation/discovery/docs-discovery.d.mts +119 -11
  526. package/foundation/discovery/docs-discovery.mjs +423 -108
  527. package/foundation/discovery/docs-discovery.test.mjs +365 -20
  528. package/foundation/discovery/docs-output-budget.d.mts +28 -0
  529. package/foundation/discovery/docs-output-budget.mjs +50 -0
  530. package/foundation/discovery/docs-section-key.d.mts +116 -0
  531. package/foundation/discovery/docs-section-key.mjs +322 -0
  532. package/foundation/discovery/docs-section-key.test.mjs +246 -0
  533. package/foundation/discovery/template-adapter.d.mts +113 -11
  534. package/foundation/discovery/template-adapter.fixture-refs.test.mjs +248 -0
  535. package/foundation/discovery/template-adapter.integration-isolation.test.mjs +94 -0
  536. package/foundation/discovery/template-adapter.mjs +774 -83
  537. package/foundation/discovery/template-adapter.test.mjs +57 -0
  538. package/foundation/discovery/template-conflict-release.d.mts +13 -0
  539. package/foundation/discovery/template-conflict-release.mjs +40 -0
  540. package/foundation/discovery/template-conflict-release.test.mjs +40 -0
  541. package/foundation/discovery/theme-discovery.d.mts +67 -7
  542. package/foundation/discovery/theme-discovery.mjs +916 -186
  543. package/foundation/discovery/theme-discovery.test.mjs +613 -219
  544. package/foundation/discovery/theming-targets.test.mjs +4 -0
  545. package/foundation/doc-compiler/bundle.d.mts +47 -0
  546. package/foundation/doc-compiler/bundle.mjs +278 -0
  547. package/foundation/doc-compiler/bundle.test.mjs +266 -0
  548. package/foundation/doc-compiler/compile.d.mts +343 -0
  549. package/foundation/doc-compiler/compile.mjs +558 -0
  550. package/foundation/doc-compiler/diagnostics.d.mts +126 -0
  551. package/foundation/doc-compiler/diagnostics.mjs +305 -0
  552. package/foundation/doc-compiler/doc-compiler.test.mjs +714 -0
  553. package/foundation/doc-compiler/doc-loads.test.mjs +1642 -0
  554. package/foundation/doc-compiler/import.d.mts +24 -0
  555. package/foundation/doc-compiler/import.mjs +59 -0
  556. package/foundation/doc-compiler/inputs.d.mts +102 -0
  557. package/foundation/doc-compiler/inputs.mjs +291 -0
  558. package/foundation/doc-compiler/inputs.test.mjs +299 -0
  559. package/foundation/doc-compiler/ir.d.mts +22 -0
  560. package/foundation/doc-compiler/ir.mjs +471 -0
  561. package/foundation/doc-compiler/lenses.d.mts +36 -0
  562. package/foundation/doc-compiler/lenses.mjs +173 -0
  563. package/foundation/doc-compiler/links.d.mts +162 -0
  564. package/foundation/doc-compiler/links.mjs +294 -0
  565. package/foundation/doc-compiler/links.test.mjs +192 -0
  566. package/foundation/doc-compiler/lower-doc.test.mjs +495 -0
  567. package/foundation/doc-compiler/overlays.d.mts +37 -0
  568. package/foundation/doc-compiler/overlays.mjs +206 -0
  569. package/foundation/doc-compiler/parse-readable.d.mts +9 -0
  570. package/foundation/doc-compiler/parse-readable.mjs +29 -0
  571. package/foundation/doc-compiler/read.d.mts +127 -0
  572. package/foundation/doc-compiler/read.mjs +325 -0
  573. package/foundation/doc-compiler/read.test.mjs +313 -0
  574. package/foundation/doc-compiler/source.d.mts +33 -0
  575. package/foundation/doc-compiler/source.mjs +128 -0
  576. package/foundation/doc-compiler/tree.d.mts +288 -0
  577. package/foundation/doc-compiler/tree.mjs +876 -0
  578. package/foundation/doc-compiler/tree.test.mjs +598 -0
  579. package/foundation/fs/file-protection.d.mts +33 -0
  580. package/foundation/fs/file-protection.mjs +825 -0
  581. package/foundation/fs/file-protection.test.mjs +250 -0
  582. package/foundation/fs/module-loader.d.mts +1 -0
  583. package/foundation/fs/module-loader.mjs +50 -1
  584. package/foundation/fs/module-loader.stdout.test.mjs +332 -0
  585. package/foundation/fs/path-safety.d.mts +3 -2
  586. package/foundation/fs/path-safety.mjs +49 -19
  587. package/foundation/fs/path-safety.test.mjs +50 -0
  588. package/foundation/fs/publish-file-hardlink-unavailable.test.mjs +129 -94
  589. package/foundation/identity/provider-identity.d.mts +90 -0
  590. package/foundation/identity/provider-identity.mjs +320 -0
  591. package/foundation/identity/provider-identity.test.mjs +254 -0
  592. package/foundation/identity/providers.d.mts +7 -0
  593. package/foundation/identity/providers.mjs +16 -0
  594. package/foundation/integrations/autolink.d.mts +58 -1
  595. package/foundation/integrations/autolink.mjs +143 -45
  596. package/foundation/integrations/autolink.test.mjs +1 -1
  597. package/foundation/integrations/cli-requirement.d.mts +45 -0
  598. package/foundation/integrations/cli-requirement.mjs +154 -0
  599. package/foundation/integrations/cli-requirement.test.mjs +84 -0
  600. package/foundation/integrations/contribution-fixes.d.mts +145 -0
  601. package/foundation/integrations/contribution-fixes.mjs +1284 -0
  602. package/foundation/integrations/contribution-inventory.d.mts +3 -2
  603. package/foundation/integrations/contribution-inventory.mjs +27 -24
  604. package/foundation/integrations/contribution-inventory.test.mjs +86 -27
  605. package/foundation/integrations/integration-warnings.d.mts +9 -2
  606. package/foundation/integrations/integration-warnings.mjs +52 -21
  607. package/foundation/integrations/integration-warnings.test.mjs +74 -1
  608. package/foundation/integrations/integrations.d.mts +63 -3
  609. package/foundation/integrations/integrations.mjs +122 -9
  610. package/foundation/integrations/integrations.test.mjs +415 -1
  611. package/foundation/integrations/provider-conflicts.test.mjs +125 -0
  612. package/foundation/integrations/provider-ledger.test.mjs +275 -0
  613. package/foundation/integrations/provider-resolution.d.mts +152 -0
  614. package/foundation/integrations/provider-resolution.mjs +576 -0
  615. package/foundation/integrations/provider-resolution.test.mjs +369 -0
  616. package/foundation/integrations/theme-descriptor.d.mts +8 -0
  617. package/foundation/integrations/theme-descriptor.mjs +44 -0
  618. package/foundation/integrations/validate-contributions.d.mts +2 -0
  619. package/foundation/integrations/validate-contributions.mjs +131 -29
  620. package/foundation/response/base.d.ts +8 -4
  621. package/foundation/response/error-codes.d.mts +3 -1
  622. package/foundation/response/error-codes.d.ts +2 -0
  623. package/foundation/response/error-codes.doc.mjs +13 -4
  624. package/foundation/response/error-codes.mjs +8 -2
  625. package/foundation/response/error-codes.test.mjs +137 -10
  626. package/foundation/response/json-contract.test.mjs +57 -17
  627. package/foundation/response/json.d.mts +4 -2
  628. package/foundation/response/json.mjs +8 -10
  629. package/foundation/response/response-types.doc.d.mts +5 -1
  630. package/foundation/response/response-types.doc.mjs +41 -21
  631. package/foundation/response/response-types.doc.test.mjs +158 -0
  632. package/foundation/response/response.doc.mjs +1 -1
  633. package/foundation/text/string-utils.d.mts +8 -0
  634. package/foundation/text/string-utils.mjs +40 -10
  635. package/foundation/xle/expand.d.mts +2 -0
  636. package/foundation/xle/expand.mjs +4 -3
  637. package/foundation/xle/expand.test.mjs +54 -0
  638. package/foundation/xle/xle.test.mjs +13 -0
  639. package/package.json +10 -11
  640. package/assets/templates/themes/manifest.json +0 -95
  641. package/clients/cli/lib/update-check.mjs +0 -83
  642. package/clients/cli/lib/update-check.test.mjs +0 -137
  643. package/clients/cli/update-hint-commands.test.mjs +0 -54
@@ -32,8 +32,17 @@
32
32
  import * as fs from 'node:fs';
33
33
  import * as path from 'node:path';
34
34
  import {CLI_ROOT} from '../fs/paths.mjs';
35
- import {importUserModule} from '../fs/module-loader.mjs';
36
- import {parseDoc} from '../../authoring/doctypes/parse.mjs';
35
+ import {importDocModule} from '../doc-compiler/import.mjs';
36
+ import {CLI_PROVIDER_ID} from '../identity/providers.mjs';
37
+ import {parseReadableDoc} from '../doc-compiler/parse-readable.mjs';
38
+ import {
39
+ sectionKey,
40
+ sectionKeyErrors,
41
+ sourceTitle,
42
+ withSourceTitle,
43
+ } from './docs-section-key.mjs';
44
+
45
+ export {withSourceTitle};
37
46
 
38
47
  /** Where the CLI's own topics live. */
39
48
  const BUILTIN_DOCS_DIR = path.join(CLI_ROOT, 'assets', 'docs');
@@ -43,7 +52,7 @@ const BUILTIN_DOCS_DIR = path.join(CLI_ROOT, 'assets', 'docs');
43
52
  * (assets/docs), not in @astryxdesign/core, so this is the CLI's own name —
44
53
  * unlike component discovery, whose built-ins belong to core.
45
54
  */
46
- export const BUILTIN_DOCS_PACKAGE = '@astryxdesign/cli';
55
+ export const BUILTIN_DOCS_PACKAGE = CLI_PROVIDER_ID;
47
56
 
48
57
  /**
49
58
  * A built-in topic file: `{topic}.doc.mjs`. Anchored at both ends so a
@@ -62,6 +71,8 @@ const TOPIC_NAME_RE = /^[\w-]+$/;
62
71
  * @typedef {object} DocsTopicRecord A doc file discovered under a docs root.
63
72
  * @property {string} name
64
73
  * @property {string} package owner package
74
+ * @property {string} [providerId] the owner's ProviderId, when it differs from
75
+ * the package name
65
76
  * @property {string} path absolute path to the doc file
66
77
  * @property {string} [title]
67
78
  * @property {string} [description]
@@ -74,13 +85,21 @@ const TOPIC_NAME_RE = /^[\w-]+$/;
74
85
  * @typedef {object} DocsTopicEntry A resolved topic in the catalog.
75
86
  * @property {string} name
76
87
  * @property {string} package owner package
88
+ * @property {string} [providerId] the owner's ProviderId; the package name when
89
+ * absent. Links in the topic resolve against it.
77
90
  * @property {string} path absolute path to the doc file
78
91
  * @property {string} [title]
79
92
  * @property {string} [description]
80
93
  * @property {string|null} [category]
81
94
  * @property {string} [replaces] the topic this one took the place of
82
- * @property {Array<{package: string, path: string}>} extensions overlays to
83
- * merge onto the base doc, in the order their integrations were configured
95
+ * @property {Array<{package: string, path: string, providerId?: string}>} extensions
96
+ * overlays to merge onto the base doc, in the order their integrations were
97
+ * configured; each section an extension adds resolves its links against the
98
+ * extension's provider id (its package name when absent)
99
+ * @property {string} [parent] the route of the namespace a tree guide sits in
100
+ * @property {string} [route] a tree guide's route
101
+ * @property {boolean} [tree] a guide that only the docs tree reads, by its
102
+ * route; never a flat topic
84
103
  */
85
104
 
86
105
  /**
@@ -108,7 +127,7 @@ export function discoverBuiltinTopics() {
108
127
  * @returns {Promise<unknown>} the authored doc value
109
128
  */
110
129
  export async function loadTopicModule(file) {
111
- const mod = await importUserModule(file);
130
+ const mod = await importDocModule(file);
112
131
  const doc = mod?.docs ?? mod?.default;
113
132
  if (doc == null) {
114
133
  throw new Error(
@@ -126,14 +145,32 @@ const BLOCK_FIELDS = {
126
145
  table: ['headers', 'rows'],
127
146
  list: ['style', 'items'],
128
147
  'token-ref': ['topic', 'section'],
148
+ // A read inlines it as the doc it names includes (spec:AST-047 FR9).
149
+ reference: ['target'],
129
150
  };
130
151
 
152
+ /**
153
+ * Blocks that are valid authoring but require the compiled graph renderer: a
154
+ * namespace doc's `blocks` hold them, a topic section does not.
155
+ */
156
+ export const GRAPH_BLOCK_TYPES = new Set(['workflow', 'collection']);
157
+
158
+ /**
159
+ * Doc fields only the docs tree reads. A flat topic that sets one fails to
160
+ * load; a guide the tree places may set `placement` (spec:AST-046).
161
+ */
162
+ export const GRAPH_ONLY_FIELDS = ['placement', 'aliases', 'audience'];
163
+
131
164
  /**
132
165
  * Fields a block kind may carry but does not need. Kept per kind rather than
133
166
  * globally: only a code block renders a `label`, so allowing it everywhere
134
167
  * would wave through the misspellings this check exists to catch.
135
168
  */
136
- const OPTIONAL_BLOCK_FIELDS = {code: ['label']};
169
+ /** @type {Record<string, string[]>} */
170
+ const OPTIONAL_BLOCK_FIELDS = {
171
+ code: ['label'],
172
+ reference: ['projection', 'presentation'],
173
+ };
137
174
 
138
175
  /**
139
176
  * Fields whose value has to be one of a set, because the renderer indexes on
@@ -144,10 +181,14 @@ const OPTIONAL_BLOCK_FIELDS = {code: ['label']};
144
181
  const BLOCK_FIELD_VALUES = {
145
182
  heading: {level: [3, 4, 5, 6]},
146
183
  list: {style: ['ordered', 'unordered', 'do', 'dont']},
184
+ reference: {presentation: ['summary', 'compact', 'full']},
147
185
  };
148
186
 
187
+ /** The parts of a doc a reference block's `projection` may select. */
188
+ const PROJECTION_FIELDS = ['fields', 'sections'];
189
+
149
190
  /** Keys a section may carry. */
150
- const SECTION_FIELDS = ['title', 'category', 'content', 'previewType'];
191
+ const SECTION_FIELDS = ['id', 'title', 'category', 'content', 'previewType'];
151
192
 
152
193
  /**
153
194
  * Check the fields the docs surfaces actually read. `parseDoc` is the outer
@@ -158,9 +199,18 @@ const SECTION_FIELDS = ['title', 'category', 'content', 'previewType'];
158
199
  * where the file that needs fixing can be named.
159
200
  *
160
201
  * @param {any} doc a parsed doc
202
+ * @param {{placement?: boolean}} [options] `placement`: the doc is a guide the
203
+ * docs tree places, so its `placement` field is read, not rejected
161
204
  * @returns {string[]} problems, each already pointed at a place in the doc
162
205
  */
163
- export function problemsInTopic(doc) {
206
+ export function problemsInTopic(doc, {placement = false} = {}) {
207
+ // A namespace doc is valid authoring that only the docs tree reads. Said
208
+ // plainly, instead of as the topic fields it does not have.
209
+ if (doc?.type === 'namespace') {
210
+ return [
211
+ `"${doc.name}" is a namespace doc, which the docs tree reads, not the topic list. The CLI keeps its own in assets/docs/tree; an integration ships its namespace docs in its docs directory.`,
212
+ ];
213
+ }
164
214
  /** @type {string[]} */
165
215
  const problems = [];
166
216
  for (const field of ['name', 'title', 'description']) {
@@ -173,83 +223,179 @@ export function problemsInTopic(doc) {
173
223
  `name: "${doc.name}" is not URL-safe. A topic name is its CLI argument and its docsite path, so it may hold only letters, digits, "_" and "-".`,
174
224
  );
175
225
  }
226
+ for (const field of GRAPH_ONLY_FIELDS) {
227
+ // A guide the docs tree places carries `placement`; the tree reads it.
228
+ if (field === 'placement' && placement) continue;
229
+ if (doc?.[field] != null) {
230
+ problems.push(
231
+ `${field}: requires the compiled graph reader and is not supported by legacy topic readers`,
232
+ );
233
+ }
234
+ }
176
235
  if (!Array.isArray(doc?.sections) || doc.sections.length === 0) {
177
236
  problems.push('sections: expected at least one section');
178
237
  return problems;
179
238
  }
180
239
 
181
- doc.sections.forEach((/** @type {any} */ section, /** @type {number} */ s) => {
182
- const at = `sections[${s}]`;
183
- if (typeof section?.title !== 'string' || section.title === '') {
184
- problems.push(`${at}.title: expected a non-empty string`);
185
- }
186
- for (const key of Object.keys(section ?? {})) {
187
- if (!SECTION_FIELDS.includes(key)) {
188
- problems.push(`${at}.${key}: not a field of a section`);
189
- }
190
- }
191
- if (!Array.isArray(section?.content)) {
192
- problems.push(`${at}.content: expected an array of blocks`);
193
- return;
194
- }
195
- section.content.forEach((/** @type {any} */ block, /** @type {number} */ b) => {
196
- const blockAt = `${at}.content[${b}]`;
197
- const fields = /** @type {Record<string, string[]>} */ (BLOCK_FIELDS)[block?.type];
198
- if (fields == null) {
199
- problems.push(
200
- `${blockAt}.type: ${JSON.stringify(block?.type)} is not one of ${Object.keys(BLOCK_FIELDS).join(', ')}`,
201
- );
202
- return;
240
+ doc.sections.forEach(
241
+ (/** @type {any} */ section, /** @type {number} */ s) => {
242
+ const at = `sections[${s}]`;
243
+ if (typeof section?.title !== 'string' || section.title === '') {
244
+ problems.push(`${at}.title: expected a non-empty string`);
203
245
  }
204
- for (const field of fields) {
205
- const value = block[field];
206
- // Empty counts as missing, the way it does for the doc's own title: a
207
- // block whose text is '' passes every other check and renders as a gap.
208
- if (value == null) {
209
- problems.push(`${blockAt}.${field}: required for a ${block.type} block`);
210
- } else if (typeof value === 'string' && value.trim() === '') {
211
- problems.push(`${blockAt}.${field}: expected a non-empty string`);
212
- } else if (Array.isArray(value) && value.length === 0) {
213
- problems.push(`${blockAt}.${field}: expected a non-empty array`);
246
+ for (const key of Object.keys(section ?? {})) {
247
+ if (!SECTION_FIELDS.includes(key)) {
248
+ problems.push(`${at}.${key}: not a field of a section`);
214
249
  }
215
250
  }
216
- const allowedValues =
217
- /** @type {Record<string, Record<string, unknown[]>>} */ (BLOCK_FIELD_VALUES)[block.type] ?? {};
218
- for (const [field, values] of Object.entries(allowedValues)) {
219
- const value = block[field];
220
- if (value != null && !values.includes(value)) {
221
- problems.push(
222
- `${blockAt}.${field}: ${JSON.stringify(value)} is not one of ${values.join(', ')}`,
223
- );
224
- }
251
+ if (!Array.isArray(section?.content)) {
252
+ problems.push(`${at}.content: expected an array of blocks`);
253
+ return;
225
254
  }
226
- // A table's cells are read by column index, so a short row renders blank
227
- // cells and a long one drops its tail — both silently.
228
- if (block.type === 'table' && Array.isArray(block.headers) && Array.isArray(block.rows)) {
229
- block.rows.forEach((/** @type {any} */ row, /** @type {number} */ r) => {
230
- if (!Array.isArray(row)) {
231
- problems.push(`${blockAt}.rows[${r}]: expected an array of cells`);
232
- } else if (row.length !== block.headers.length) {
233
- problems.push(
234
- `${blockAt}.rows[${r}]: has ${row.length} cells but the table has ${block.headers.length} headers`,
255
+ section.content.forEach(
256
+ (/** @type {any} */ block, /** @type {number} */ b) => {
257
+ const blockAt = `${at}.content[${b}]`;
258
+ const fields = /** @type {Record<string, string[]>} */ (BLOCK_FIELDS)[
259
+ block?.type
260
+ ];
261
+ if (fields == null) {
262
+ if (GRAPH_BLOCK_TYPES.has(block?.type)) {
263
+ problems.push(
264
+ `${blockAt}.type: ${JSON.stringify(block.type)} requires the compiled graph renderer and is not supported by legacy topic readers`,
265
+ );
266
+ } else {
267
+ problems.push(
268
+ `${blockAt}.type: ${JSON.stringify(block?.type)} is not one of ${Object.keys(BLOCK_FIELDS).join(', ')}`,
269
+ );
270
+ }
271
+ return;
272
+ }
273
+ for (const field of fields) {
274
+ const value = block[field];
275
+ // Empty counts as missing, the way it does for the doc's own title: a
276
+ // block whose text is '' passes every other check and renders as a gap.
277
+ if (value == null) {
278
+ problems.push(
279
+ `${blockAt}.${field}: required for a ${block.type} block`,
280
+ );
281
+ } else if (typeof value === 'string' && value.trim() === '') {
282
+ problems.push(`${blockAt}.${field}: expected a non-empty string`);
283
+ } else if (Array.isArray(value) && value.length === 0) {
284
+ problems.push(`${blockAt}.${field}: expected a non-empty array`);
285
+ }
286
+ }
287
+ const allowedValues =
288
+ /** @type {Record<string, Record<string, unknown[]>>} */ (
289
+ BLOCK_FIELD_VALUES
290
+ )[block.type] ?? {};
291
+ for (const [field, values] of Object.entries(allowedValues)) {
292
+ const value = block[field];
293
+ if (value != null && !values.includes(value)) {
294
+ problems.push(
295
+ `${blockAt}.${field}: ${JSON.stringify(value)} is not one of ${values.join(', ')}`,
296
+ );
297
+ }
298
+ }
299
+ // A table's cells are read by column index, so a short row renders blank
300
+ // cells and a long one drops its tail — both silently.
301
+ if (
302
+ block.type === 'table' &&
303
+ Array.isArray(block.headers) &&
304
+ Array.isArray(block.rows)
305
+ ) {
306
+ block.rows.forEach(
307
+ (/** @type {any} */ row, /** @type {number} */ r) => {
308
+ if (!Array.isArray(row)) {
309
+ problems.push(
310
+ `${blockAt}.rows[${r}]: expected an array of cells`,
311
+ );
312
+ } else if (row.length !== block.headers.length) {
313
+ problems.push(
314
+ `${blockAt}.rows[${r}]: has ${row.length} cells but the table has ${block.headers.length} headers`,
315
+ );
316
+ }
317
+ },
235
318
  );
236
319
  }
237
- });
238
- }
239
- // An unknown key is almost always a misspelled required one, and it
240
- // would otherwise reach a reader as a block that renders nothing.
241
- const allowed = [
242
- 'type',
243
- ...fields,
244
- ...(/** @type {Record<string, string[]>} */ (OPTIONAL_BLOCK_FIELDS)[block.type] ?? []),
245
- ];
246
- for (const key of Object.keys(block)) {
247
- if (!allowed.includes(key)) {
248
- problems.push(`${blockAt}.${key}: not a field of a ${block.type} block`);
249
- }
250
- }
251
- });
252
- });
320
+ // An unknown key is almost always a misspelled required one, and it
321
+ // would otherwise reach a reader as a block that renders nothing.
322
+ const allowed = [
323
+ 'type',
324
+ ...fields,
325
+ ...(OPTIONAL_BLOCK_FIELDS[block.type] ?? []),
326
+ ];
327
+ for (const key of Object.keys(block)) {
328
+ if (!allowed.includes(key)) {
329
+ problems.push(
330
+ `${blockAt}.${key}: not a field of a ${block.type} block`,
331
+ );
332
+ }
333
+ }
334
+ // A projection names the parts of the doc to include, so each part
335
+ // it names is a non-empty list of names.
336
+ if (block.type === 'reference' && block.projection != null) {
337
+ const projection = block.projection;
338
+ if (typeof projection !== 'object' || Array.isArray(projection)) {
339
+ problems.push(
340
+ `${blockAt}.projection: expected {fields?, sections?}, naming the parts of the doc to include`,
341
+ );
342
+ } else {
343
+ for (const [key, names] of Object.entries(projection)) {
344
+ if (!PROJECTION_FIELDS.includes(key)) {
345
+ problems.push(
346
+ `${blockAt}.projection.${key}: not a field of a projection`,
347
+ );
348
+ } else if (
349
+ !Array.isArray(names) ||
350
+ names.length === 0 ||
351
+ names.some(
352
+ name => typeof name !== 'string' || name.trim() === '',
353
+ )
354
+ ) {
355
+ problems.push(
356
+ `${blockAt}.projection.${key}: expected a non-empty array of names`,
357
+ );
358
+ }
359
+ }
360
+ }
361
+ }
362
+ },
363
+ );
364
+ },
365
+ );
366
+ // Explicit authored IDs are a new opt-in contract and remain strict. Topics
367
+ // that relied on 0.6.x title-only sections keep loading; the compiler assigns
368
+ // deterministic fallback/suffixed keys for the additive index API.
369
+ problems.push(...sectionKeyErrors(doc.sections));
370
+ return problems;
371
+ }
372
+
373
+ /**
374
+ * The fields the docs tree reads from a namespace doc an integration ships.
375
+ * @param {any} doc
376
+ * @returns {string[]}
377
+ */
378
+ export function problemsInNamespace(doc) {
379
+ /** @type {string[]} */
380
+ const problems = [];
381
+ for (const field of ['name', 'title', 'summary']) {
382
+ if (typeof doc?.[field] !== 'string' || doc[field] === '') {
383
+ problems.push(`${field}: expected a non-empty string`);
384
+ }
385
+ }
386
+ const slots = doc?.slots;
387
+ if (slots == null || typeof slots !== 'object' || Object.keys(slots).length === 0) {
388
+ problems.push('slots: expected at least one slot');
389
+ return problems;
390
+ }
391
+ for (const [name, slot] of Object.entries(slots)) {
392
+ if (typeof slot?.title !== 'string' || slot.title === '') {
393
+ problems.push(`slots.${name}.title: expected a non-empty string`);
394
+ }
395
+ if (!Array.isArray(slot?.accepts?.kinds) || slot.accepts.kinds.length === 0) {
396
+ problems.push(`slots.${name}.accepts.kinds: expected at least one kind`);
397
+ }
398
+ }
253
399
  return problems;
254
400
  }
255
401
 
@@ -260,11 +406,15 @@ export function problemsInTopic(doc) {
260
406
  * discovery this loads each doc, because a topic's name and its relationship
261
407
  * to an existing topic are fields inside the file.
262
408
  *
409
+ * A namespace doc and a guide with `placement` go to the docs tree instead of
410
+ * the topic list (spec:AST-046): they come back in `namespaces` and `guides`,
411
+ * named by the integration's provider id.
412
+ *
263
413
  * Errors are returned, not thrown: one unusable doc is reported as an issue
264
414
  * against its package while the rest of the CLI keeps working.
265
415
  *
266
- * @param {{name: string, docs?: string}} integration a loaded integration
267
- * @returns {Promise<{records: DocsTopicRecord[], errors: Error[]}>}
416
+ * @param {{name: string, docs?: string, providerId?: string}} integration a loaded integration
417
+ * @returns {Promise<{records: DocsTopicRecord[], errors: Error[], namespaces: import('../doc-compiler/tree.mjs').TreeNamespaceInput[], guides: import('../doc-compiler/tree.mjs').TreeDocInput[]}>}
268
418
  */
269
419
  export async function discoverIntegrationDocs(integration) {
270
420
  const docsDir = integration?.docs;
@@ -272,7 +422,14 @@ export async function discoverIntegrationDocs(integration) {
272
422
  const records = [];
273
423
  /** @type {Error[]} */
274
424
  const errors = [];
275
- if (!docsDir || !fs.existsSync(docsDir)) return {records, errors};
425
+ /** @type {import('../doc-compiler/tree.mjs').TreeNamespaceInput[]} */
426
+ const namespaces = [];
427
+ /** @type {import('../doc-compiler/tree.mjs').TreeDocInput[]} */
428
+ const guides = [];
429
+ if (!docsDir || !fs.existsSync(docsDir)) {
430
+ return {records, errors, namespaces, guides};
431
+ }
432
+ const providerId = integration.providerId ?? integration.name;
276
433
 
277
434
  /** @type {string[]} */
278
435
  const files = [];
@@ -283,7 +440,9 @@ export async function discoverIntegrationDocs(integration) {
283
440
  const full = path.join(dirPath, entry.name);
284
441
  if (entry.isDirectory()) {
285
442
  scanDir(full);
286
- } else if (INTEGRATION_DOC_SUFFIXES.some(suffix => entry.name.endsWith(suffix))) {
443
+ } else if (
444
+ INTEGRATION_DOC_SUFFIXES.some(suffix => entry.name.endsWith(suffix))
445
+ ) {
287
446
  files.push(full);
288
447
  }
289
448
  }
@@ -296,16 +455,43 @@ export async function discoverIntegrationDocs(integration) {
296
455
  for (const file of files) {
297
456
  let doc;
298
457
  try {
299
- doc = parseDoc(await loadTopicModule(file), path.basename(file));
458
+ doc = parseReadableDoc(await loadTopicModule(file), path.basename(file));
300
459
  } catch (err) {
301
- errors.push(new Error(`${path.relative(docsDir, file)}: ${/** @type {any} */ (err).message}`));
460
+ errors.push(
461
+ new Error(
462
+ `${path.relative(docsDir, file)}: ${/** @type {any} */ (err).message}`,
463
+ ),
464
+ );
465
+ continue;
466
+ }
467
+ const relative = path.relative(docsDir, file);
468
+ const source = `${integration.name}/${relative.split(path.sep).join('/')}`;
469
+ if (/** @type {any} */ (doc)?.type === 'namespace') {
470
+ const problems = problemsInNamespace(doc);
471
+ if (problems.length > 0) {
472
+ errors.push(
473
+ new Error(
474
+ `${relative} is not a usable namespace doc:\n${problems
475
+ .map(problem => ` ${problem}`)
476
+ .join('\n')}`,
477
+ ),
478
+ );
479
+ continue;
480
+ }
481
+ namespaces.push({
482
+ provider: integration.name,
483
+ providerId,
484
+ source,
485
+ doc: /** @type {any} */ (doc),
486
+ });
302
487
  continue;
303
488
  }
304
- const problems = problemsInTopic(doc);
489
+ const placed = /** @type {any} */ (doc)?.placement != null;
490
+ const problems = problemsInTopic(doc, {placement: placed});
305
491
  if (problems.length > 0) {
306
492
  errors.push(
307
493
  new Error(
308
- `${path.relative(docsDir, file)} is not a usable topic:\n${problems
494
+ `${relative} is not a usable topic:\n${problems
309
495
  .map(problem => ` ${problem}`)
310
496
  .join('\n')}`,
311
497
  ),
@@ -315,7 +501,8 @@ export async function discoverIntegrationDocs(integration) {
315
501
  const parsed = /** @type {any} */ (doc);
316
502
  // Two files claiming one name would collapse into a single entry, and the
317
503
  // one that lost would never be reachable. Named here, where both files are.
318
- const previous = seen.get(parsed.name);
504
+ const topicKey = parsed.name.toLowerCase();
505
+ const previous = seen.get(topicKey);
319
506
  if (previous) {
320
507
  errors.push(
321
508
  new Error(
@@ -324,7 +511,30 @@ export async function discoverIntegrationDocs(integration) {
324
511
  );
325
512
  continue;
326
513
  }
327
- seen.set(parsed.name, path.relative(docsDir, file));
514
+ seen.set(topicKey, path.relative(docsDir, file));
515
+ if (placed) {
516
+ if (parsed.replaces != null || parsed.extends != null) {
517
+ errors.push(
518
+ new Error(
519
+ `${relative} is placed in the docs tree and also declares \`${parsed.replaces != null ? 'replaces' : 'extends'}\`. A placed guide has its own route; only a flat topic takes over or extends another.`,
520
+ ),
521
+ );
522
+ continue;
523
+ }
524
+ guides.push({
525
+ provider: integration.name,
526
+ providerId,
527
+ source,
528
+ kind: 'generic',
529
+ name: parsed.name,
530
+ title: parsed.title,
531
+ summary: parsed.description,
532
+ group: null,
533
+ placement: parsed.placement,
534
+ ref: {topicFile: file},
535
+ });
536
+ continue;
537
+ }
328
538
  if (parsed.replaces != null && parsed.extends != null) {
329
539
  errors.push(
330
540
  new Error(
@@ -342,16 +552,19 @@ export async function discoverIntegrationDocs(integration) {
342
552
  category: parsed.category ?? null,
343
553
  replaces: parsed.replaces,
344
554
  extendsTopic: parsed.extends,
555
+ ...(providerId === integration.name ? {} : {providerId}),
345
556
  });
346
557
  }
347
558
 
348
- return {records, errors};
559
+ return {records, errors, namespaces, guides};
349
560
  }
350
561
 
351
562
  /**
352
- * Merge an extension onto a base topic: a section whose title matches one in
353
- * the base replaces it, a section the base does not have is appended, and the
354
- * title/description are taken from the extension when it states them.
563
+ * Merge an extension onto a base topic: a section with a stable `id` replaces
564
+ * the base section with the same `id`; legacy sections without IDs fall back to
565
+ * title matching. A section with no match is appended. The title and
566
+ * description stay the base topic's: an extension adds to a topic, it never
567
+ * renames it. A topic that `replaces` another is the one that renames.
355
568
  *
356
569
  * Keyed by section TITLE rather than by position, the way the localization
357
570
  * overlays are — position keying grafts an overlay onto whichever section
@@ -365,16 +578,55 @@ export async function discoverIntegrationDocs(integration) {
365
578
  export function mergeTopic(base, overlay) {
366
579
  const sections = [...(base.sections ?? [])];
367
580
  for (const section of overlay.sections ?? []) {
368
- const at = sections.findIndex((/** @type {any} */ s) => s.title === section.title);
369
- if (at === -1) sections.push(section);
370
- else sections[at] = section;
581
+ const at = findMergeTarget(sections, section);
582
+ if (at === -1) {
583
+ sections.push(section);
584
+ } else {
585
+ // A legacy extension that replaces a section which has since gained a
586
+ // stable ID keeps that ID, so readers addressing it keep working.
587
+ const replaced = sections[at];
588
+ sections[at] =
589
+ section.id == null && replaced.id != null
590
+ ? withSourceTitle({...section, id: replaced.id}, sourceTitle(section))
591
+ : section;
592
+ }
593
+ }
594
+ return {...base, sections};
595
+ }
596
+
597
+ /**
598
+ * The base section an extension section replaces. A stable ID matches first.
599
+ * Otherwise the exact title matches when at least one side has no ID: the
600
+ * migration window in which the base or the extension adopts stable IDs
601
+ * before the other does. Two different authored IDs stay distinct even under
602
+ * one title.
603
+ *
604
+ * @param {any[]} sections
605
+ * @param {any} section
606
+ * @returns {number}
607
+ */
608
+ function findMergeTarget(sections, section) {
609
+ const title = sourceTitle(section);
610
+ const key = sectionKey(section);
611
+ // A section is addressed by its key: an authored id, or the key its title
612
+ // derives, which is the key the topic's index shows. Matching on it means an
613
+ // extension never appends a second section under a key already in use.
614
+ const byKey = () =>
615
+ sections.findIndex(candidate => sectionKey(candidate) === key);
616
+ const legacyTitleMatch = () =>
617
+ sections.findIndex(
618
+ candidate => candidate.id == null && sourceTitle(candidate) === title,
619
+ );
620
+ if (section.id != null) {
621
+ const byId = byKey();
622
+ return byId === -1 ? legacyTitleMatch() : byId;
371
623
  }
372
- return {
373
- ...base,
374
- title: overlay.title || base.title,
375
- description: overlay.description || base.description,
376
- sections,
377
- };
624
+ const legacy = legacyTitleMatch();
625
+ if (legacy !== -1) return legacy;
626
+ const sameTitle = sections.findIndex(
627
+ candidate => sourceTitle(candidate) === title,
628
+ );
629
+ return sameTitle;
378
630
  }
379
631
 
380
632
  /**
@@ -390,6 +642,46 @@ export class DocsCatalog {
390
642
  #topics = new Map();
391
643
  /** @type {Map<string, string>} old topic name → the name that replaced it */
392
644
  #aliases = new Map();
645
+ /** @type {Array<{namespaces: import('../doc-compiler/tree.mjs').TreeNamespaceInput[], guides: import('../doc-compiler/tree.mjs').TreeDocInput[]}>} */
646
+ #treeInputs = [];
647
+ /** @type {Array<{package: string, message: string}>} */
648
+ #issues = [];
649
+
650
+ /**
651
+ * Record a doc file a package ships that did not load. Its package's docs
652
+ * are withdrawn; readers name the package so an author knows where to look.
653
+ * @param {{package: string, message: string}} issue
654
+ */
655
+ addIssue(issue) {
656
+ this.#issues.push(issue);
657
+ }
658
+
659
+ /**
660
+ * The doc files that did not load, by package.
661
+ * @returns {ReadonlyArray<{package: string, message: string}>}
662
+ */
663
+ get issues() {
664
+ return this.#issues;
665
+ }
666
+
667
+ /**
668
+ * Add the namespace docs and placed guides one integration ships to the
669
+ * docs tree (spec:AST-046).
670
+ * @param {{namespaces: import('../doc-compiler/tree.mjs').TreeNamespaceInput[], guides: import('../doc-compiler/tree.mjs').TreeDocInput[]}} inputs
671
+ */
672
+ addTreeInputs(inputs) {
673
+ if (inputs.namespaces.length > 0 || inputs.guides.length > 0) {
674
+ this.#treeInputs.push(inputs);
675
+ }
676
+ }
677
+
678
+ /**
679
+ * What the integrations add to the docs tree, in configured order.
680
+ * @returns {ReadonlyArray<{namespaces: import('../doc-compiler/tree.mjs').TreeNamespaceInput[], guides: import('../doc-compiler/tree.mjs').TreeDocInput[]}>}
681
+ */
682
+ get treeInputs() {
683
+ return this.#treeInputs;
684
+ }
393
685
 
394
686
  /**
395
687
  * Seed a catalog with the CLI's own topics.
@@ -399,7 +691,7 @@ export class DocsCatalog {
399
691
  static fromBuiltins(builtins = discoverBuiltinTopics()) {
400
692
  const catalog = new DocsCatalog();
401
693
  for (const [name, file] of Object.entries(builtins)) {
402
- catalog.#topics.set(name, {
694
+ catalog.#topics.set(name.toLowerCase(), {
403
695
  name,
404
696
  package: BUILTIN_DOCS_PACKAGE,
405
697
  path: file,
@@ -427,7 +719,11 @@ export class DocsCatalog {
427
719
  message: `"${record.name}" extends "${record.extendsTopic}", which is not a topic in this project.`,
428
720
  };
429
721
  }
430
- target.extensions.push({package: record.package, path: record.path});
722
+ target.extensions.push({
723
+ package: record.package,
724
+ path: record.path,
725
+ ...(record.providerId ? {providerId: record.providerId} : {}),
726
+ });
431
727
  return null;
432
728
  }
433
729
 
@@ -455,7 +751,9 @@ export class DocsCatalog {
455
751
  // The replacement takes the base topic's slot, so a reader that opens
456
752
  // the first topic (or the nth) sees the same one it did before.
457
753
  const replaced = target.name;
458
- this.#replaceAt(replaced, {
754
+ const replacedKey = replaced.toLowerCase();
755
+ const replacementKey = record.name.toLowerCase();
756
+ this.#replaceAt(replacedKey, {
459
757
  name: record.name,
460
758
  package: record.package,
461
759
  path: record.path,
@@ -463,20 +761,22 @@ export class DocsCatalog {
463
761
  description: record.description,
464
762
  category: record.category,
465
763
  replaces: replaced,
764
+ ...(record.providerId ? {providerId: record.providerId} : {}),
466
765
  // Extensions were authored against the content that just went away.
467
766
  extensions: [],
468
767
  });
469
- if (record.name !== replaced) {
470
- this.#aliases.set(replaced, record.name);
768
+ if (replacementKey !== replacedKey) {
769
+ this.#aliases.set(replacedKey, replacementKey);
471
770
  // A topic renamed twice keeps every name it has ever answered to.
472
771
  for (const [from, to] of this.#aliases) {
473
- if (to === replaced) this.#aliases.set(from, record.name);
772
+ if (to === replacedKey) this.#aliases.set(from, replacementKey);
474
773
  }
475
774
  }
476
775
  return warning;
477
776
  }
478
777
 
479
- const existing = this.#topics.get(record.name);
778
+ const topicKey = record.name.toLowerCase();
779
+ const existing = this.#topics.get(topicKey);
480
780
  if (existing) {
481
781
  return {
482
782
  code: 'invalid_doc',
@@ -484,18 +784,33 @@ export class DocsCatalog {
484
784
  message: `Topic "${record.name}" is already provided by ${existing.package}. Give it another name, or declare \`replaces: '${record.name}'\` to take its place.`,
485
785
  };
486
786
  }
487
- this.#topics.set(record.name, {
787
+ this.#topics.set(topicKey, {
488
788
  name: record.name,
489
789
  package: record.package,
490
790
  path: record.path,
491
791
  title: record.title,
492
792
  description: record.description,
493
793
  category: record.category,
794
+ ...(record.providerId ? {providerId: record.providerId} : {}),
494
795
  extensions: [],
495
796
  });
496
797
  return null;
497
798
  }
498
799
 
800
+ /**
801
+ * Every other name a topic answers to, lowercased: the names of the topics
802
+ * it replaced, directly or through a chain of replacements. `resolve` finds
803
+ * the topic by each of them.
804
+ * @param {DocsTopicEntry} entry
805
+ * @returns {string[]}
806
+ */
807
+ aliasesOf(entry) {
808
+ const key = entry.name.toLowerCase();
809
+ return [...this.#aliases]
810
+ .filter(([, to]) => to === key)
811
+ .map(([from]) => from);
812
+ }
813
+
499
814
  /**
500
815
  * Look a topic up by name, case-insensitively, following the alias a renamed
501
816
  * replacement left behind.
@@ -519,7 +834,7 @@ export class DocsCatalog {
519
834
 
520
835
  /** @returns {string[]} every topic name, in read order */
521
836
  names() {
522
- return [...this.#topics.keys()];
837
+ return [...this.#topics.values()].map(entry => entry.name);
523
838
  }
524
839
 
525
840
  /** @returns {DocsTopicEntry[]} every topic, in read order */
@@ -536,7 +851,7 @@ export class DocsCatalog {
536
851
  /** @type {Map<string, DocsTopicEntry>} */
537
852
  const next = new Map();
538
853
  for (const [key, value] of this.#topics) {
539
- if (key === name) next.set(entry.name, entry);
854
+ if (key === name.toLowerCase()) next.set(entry.name.toLowerCase(), entry);
540
855
  else next.set(key, value);
541
856
  }
542
857
  this.#topics = next;