@astryxdesign/cli 0.6.3-canary.db4e378 → 0.6.3-canary.db52d98

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 (397) hide show
  1. package/README.md +38 -15
  2. package/api/blog/blog.doc.mjs +1 -0
  3. package/api/build/_adapter.d.mts +50 -0
  4. package/api/build/_adapter.mjs +60 -0
  5. package/api/build/build.doc.mjs +14 -7
  6. package/api/build/build.test.mjs +184 -6
  7. package/api/build/build.type.d.mts +57 -2
  8. package/api/build/build.type.mjs +30 -7
  9. package/api/build/help/help.d.mts +4 -0
  10. package/api/build/help/help.mjs +24 -11
  11. package/api/build/kit/kit.d.mts +4 -1
  12. package/api/build/kit/kit.mjs +165 -49
  13. package/api/build/kit/rank.d.mts +44 -0
  14. package/api/build/kit/rank.mjs +432 -0
  15. package/api/build/kit/rank.test.mjs +196 -0
  16. package/api/component/_adapter.d.mts +6 -12
  17. package/api/component/_adapter.mjs +20 -10
  18. package/api/component/component.doc.mjs +1 -0
  19. package/api/component/component.mjs +60 -12
  20. package/api/component/list/list.mjs +3 -2
  21. package/api/discover/discover.doc.mjs +1 -0
  22. package/api/docs/_adapter.d.mts +252 -34
  23. package/api/docs/_adapter.mjs +923 -132
  24. package/api/docs/detail/detail.mjs +11 -3
  25. package/api/docs/detail/section/section.mjs +24 -13
  26. package/api/docs/detail/section/section.test.mjs +15 -6
  27. package/api/docs/docs.d.mts +5 -3
  28. package/api/docs/docs.doc.mjs +39 -17
  29. package/api/docs/docs.mjs +44 -8
  30. package/api/docs/docs.test.mjs +158 -4
  31. package/api/docs/docs.type.d.mts +181 -2
  32. package/api/docs/docs.type.mjs +117 -3
  33. package/api/docs/index/index.mjs +11 -3
  34. package/api/docs/index/index.test.mjs +1 -1
  35. package/api/docs/integration-tree.test.mjs +555 -0
  36. package/api/docs/integrationDocs.test.mjs +14 -14
  37. package/api/docs/list/list.mjs +28 -12
  38. package/api/docs/node/node.d.mts +43 -0
  39. package/api/docs/node/node.mjs +192 -0
  40. package/api/docs/reference-blocks.test.mjs +406 -0
  41. package/api/doctor/doctor.d.mts +54 -4
  42. package/api/doctor/doctor.doc.mjs +1 -0
  43. package/api/doctor/doctor.mjs +331 -16
  44. package/api/doctor/doctor.test.mjs +420 -7
  45. package/api/gap-report/gap-report.doc.mjs +1 -0
  46. package/api/hook/_adapter.mjs +19 -5
  47. package/api/hook/hook.doc.mjs +1 -0
  48. package/api/hook/list/list.d.mts +1 -1
  49. package/api/hook/list/list.mjs +69 -17
  50. package/api/index.d.mts +1 -1
  51. package/api/index.mjs +1 -0
  52. package/api/init/init.doc.mjs +2 -1
  53. package/api/integration/add-contribution.d.mts +2 -1
  54. package/api/integration/add-contribution.mjs +114 -12
  55. package/api/integration/add-contribution.test.mjs +174 -3
  56. package/api/integration/add-theme.mjs +34 -64
  57. package/api/integration/add-theme.test.mjs +105 -21
  58. package/api/integration/authoring-checks.mjs +138 -28
  59. package/api/integration/authoring-checks.test.mjs +179 -7
  60. package/api/integration/authoring-checks.type.mjs +6 -1
  61. package/api/integration/integration-authoring.type.d.mts +2 -0
  62. package/api/integration/integration-authoring.type.mjs +2 -0
  63. package/api/integration/integration-block-exports.test.mjs +10 -6
  64. package/api/integration/integrationAdd.doc.mjs +7 -0
  65. package/api/integration/integrationAddAgentDoc.doc.mjs +1 -0
  66. package/api/integration/integrationAddCodemod.doc.mjs +1 -0
  67. package/api/integration/integrationAddComponent.doc.mjs +1 -0
  68. package/api/integration/integrationAddDoc.doc.mjs +8 -1
  69. package/api/integration/integrationAddTemplate.doc.mjs +1 -0
  70. package/api/integration/integrationAddTheme.doc.mjs +6 -5
  71. package/api/integration/integrationComponentConflicts.doc.mjs +1 -0
  72. package/api/integration/integrationDocConflicts.doc.mjs +2 -1
  73. package/api/integration/integrationPackCheck.doc.mjs +2 -1
  74. package/api/integration/integrationTemplateConflicts.doc.d.mts +3 -0
  75. package/api/integration/integrationTemplateConflicts.doc.mjs +4 -0
  76. package/api/integration/pack-check.mjs +34 -0
  77. package/api/integration/pack-check.test.mjs +138 -47
  78. package/api/integration/pack-check.type.d.mts +26 -2
  79. package/api/integration/pack-check.type.mjs +14 -1
  80. package/api/integration/summarizeIssues.doc.mjs +1 -0
  81. package/api/integration/template-conflict-compatibility.test.mjs +73 -0
  82. package/api/integration/validate-integration-fixes.test.mjs +1389 -0
  83. package/api/integration/validate-integration.mjs +50 -102
  84. package/api/integration/validate-integration.test.mjs +88 -26
  85. package/api/integration/validate-unread-theme-folders.test.mjs +110 -0
  86. package/api/integration/validateIntegration.doc.mjs +2 -1
  87. package/api/json/assertResponse.doc.mjs +1 -0
  88. package/api/json/index.ts +2 -0
  89. package/api/json/isError.doc.mjs +1 -0
  90. package/api/json/parseResponse.doc.mjs +1 -0
  91. package/api/layout/_adapter.mjs +20 -5
  92. package/api/layout/layoutCheck.doc.mjs +1 -0
  93. package/api/layout/layoutExpand.doc.mjs +1 -0
  94. package/api/layout/layoutGrammar.doc.mjs +1 -0
  95. package/api/search/search.d.mts +59 -1
  96. package/api/search/search.doc.mjs +5 -3
  97. package/api/search/search.mjs +458 -77
  98. package/api/search/search.test.mjs +91 -2
  99. package/api/search/search.type.d.mts +13 -1
  100. package/api/search/search.type.mjs +4 -1
  101. package/api/swizzle/swizzle.doc.mjs +1 -0
  102. package/api/template/list/list.mjs +1 -0
  103. package/api/template/table-floating-bulk-actions.test.mjs +66 -0
  104. package/api/template/template-integration.test.mjs +1072 -3
  105. package/api/template/template-suffix.test.mjs +41 -21
  106. package/api/template/template.doc.mjs +28 -7
  107. package/api/template/template.mjs +45 -8
  108. package/api/template/template.type.d.mts +6 -8
  109. package/api/template/template.type.mjs +3 -2
  110. package/api/theme/_adapter.d.mts +2 -3
  111. package/api/theme/_adapter.mjs +4 -5
  112. package/api/theme/add/add.binary.test.mjs +10 -17
  113. package/api/theme/add/add.test.mjs +14 -1
  114. package/api/theme/generateTonalPalette.doc.mjs +1 -0
  115. package/api/theme/integration-themes.test.mjs +39 -28
  116. package/api/theme/list/list.test.mjs +19 -20
  117. package/api/theme/listThemes.doc.mjs +6 -5
  118. package/api/theme/template/template.test.mjs +5 -0
  119. package/api/theme/themeAdd.doc.mjs +4 -3
  120. package/api/theme/themeBuild.doc.mjs +1 -0
  121. package/api/theme/themeList.doc.mjs +6 -3
  122. package/api/theme/themeListAvailable.doc.mjs +5 -3
  123. package/api/theme/themePaletteGenerate.doc.mjs +1 -0
  124. package/api/theme/themeTargets.doc.mjs +1 -0
  125. package/api/theme/themeTemplate.doc.mjs +2 -1
  126. package/api/upgrade/_adapter.d.mts +32 -5
  127. package/api/upgrade/_adapter.mjs +97 -69
  128. package/api/upgrade/provider-agreement.test.mjs +152 -0
  129. package/api/upgrade/run/run.mjs +356 -59
  130. package/api/upgrade/upgrade.doc.mjs +6 -1
  131. package/api/upgrade/upgrade.type.d.mts +34 -0
  132. package/api/upgrade/upgrade.type.mjs +15 -0
  133. package/assets/codemods/__tests__/runner.test.mjs +330 -8
  134. package/assets/codemods/integration-discovery.mjs +8 -2
  135. package/assets/codemods/integration-discovery.test.mjs +15 -0
  136. package/assets/codemods/integration-runner.mjs +56 -4
  137. package/assets/codemods/integration-runner.protection.test.mjs +153 -0
  138. package/assets/codemods/run-codemod.mjs +177 -34
  139. package/assets/codemods/runner.mjs +350 -102
  140. package/assets/codemods/transforms/next/__tests__/migrate-native-picker-to-presentation.test.mjs +63 -0
  141. package/assets/codemods/transforms/next/__tests__/migrate-theme-catalog-to-descriptors.test.mjs +220 -0
  142. package/assets/codemods/transforms/next/index.mjs +19 -1
  143. package/assets/codemods/transforms/next/migrate-native-picker-to-presentation.mjs +148 -0
  144. package/assets/codemods/transforms/next/migrate-theme-catalog-to-descriptors.mjs +141 -0
  145. package/assets/docs/getting-started.doc.mjs +2 -2
  146. package/assets/docs/layout.doc.dense.mjs +2 -2
  147. package/assets/docs/layout.doc.mjs +1 -1
  148. package/assets/docs/principles.doc.mjs +6 -6
  149. package/assets/docs/styling-libraries.doc.mjs +3 -3
  150. package/assets/docs/styling.doc.mjs +4 -4
  151. package/assets/docs/theme.doc.mjs +5 -5
  152. package/assets/docs/tokens.doc.mjs +1 -1
  153. package/assets/docs/tree/api.doc.mjs +30 -0
  154. package/assets/docs/tree/cli.doc.mjs +23 -0
  155. package/assets/docs/tree/commands.doc.mjs +25 -0
  156. package/assets/docs/{cli-integrations.doc.mjs → tree/integrations.doc.mjs} +66 -38
  157. package/assets/docs/tree/integrations.test.mjs +62 -0
  158. package/assets/docs/tree/writing-docs.doc.mjs +286 -0
  159. package/assets/docs/working-with-ai.doc.mjs +3 -3
  160. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.doc.mjs +1 -1
  161. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.tsx +1 -3
  162. package/assets/templates/blocks/components/DateInput/DateInputDateRange.tsx +1 -1
  163. package/assets/templates/blocks/components/Item/ItemDocumentTabs.doc.mjs +14 -0
  164. package/assets/templates/blocks/components/Item/ItemDocumentTabs.tsx +100 -0
  165. package/assets/templates/blocks/components/Table/TableBulkActionsTable.doc.mjs +14 -0
  166. package/assets/templates/blocks/components/Table/TableBulkActionsTable.tsx +71 -0
  167. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.doc.mjs +19 -0
  168. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.tsx +139 -0
  169. package/assets/templates/blocks/components/TimeInput/TimeInputConstrained.tsx +1 -0
  170. package/assets/templates/themes/butter/butterTheme.doc.mjs +11 -0
  171. package/assets/templates/themes/chocolate/chocolateTheme.doc.mjs +11 -0
  172. package/assets/templates/themes/gothic/gothicTheme.doc.mjs +11 -0
  173. package/assets/templates/themes/matcha/matchaTheme.doc.mjs +11 -0
  174. package/assets/templates/themes/neutral/neutralTheme.doc.mjs +11 -0
  175. package/assets/templates/themes/stone/stoneTheme.doc.mjs +11 -0
  176. package/assets/templates/themes/y2k/y2kTheme.doc.mjs +11 -0
  177. package/authoring/codemod/codemod.doc.mjs +1 -1
  178. package/authoring/codemod/type.ts +12 -0
  179. package/authoring/config/config.doc.mjs +1 -1
  180. package/authoring/debug/debug.doc.d.mts +11 -0
  181. package/authoring/debug/debug.doc.mjs +182 -0
  182. package/authoring/debug/parse.d.mts +3 -3
  183. package/authoring/doctypes/_schema.d.mts +119 -117
  184. package/authoring/doctypes/_schema.mjs +54 -3
  185. package/authoring/doctypes/base/graph-fields.doc.mjs +8 -6
  186. package/authoring/doctypes/base/type.ts +6 -5
  187. package/authoring/doctypes/command/command.doc.mjs +1 -1
  188. package/authoring/doctypes/command/type.ts +2 -2
  189. package/authoring/doctypes/component/type.ts +2 -2
  190. package/authoring/doctypes/doctypes-new.test.mjs +48 -6
  191. package/authoring/doctypes/enum/enum.doc.mjs +1 -1
  192. package/authoring/doctypes/enum/type.ts +1 -1
  193. package/authoring/doctypes/function/function.doc.mjs +3 -2
  194. package/authoring/doctypes/function/type.ts +3 -2
  195. package/authoring/doctypes/hook/type.ts +2 -2
  196. package/authoring/doctypes/load-contract.test.mjs +28 -2
  197. package/authoring/doctypes/namespace/namespace.doc.mjs +6 -10
  198. package/authoring/doctypes/namespace/parse.test.mjs +23 -25
  199. package/authoring/doctypes/namespace/type.ts +5 -2
  200. package/authoring/doctypes/parse.d.mts +4 -2
  201. package/authoring/doctypes/parse.mjs +10 -5
  202. package/authoring/doctypes/reference/reference.doc.mjs +35 -6
  203. package/authoring/doctypes/reference/type.ts +30 -13
  204. package/authoring/doctypes/schema/schema.doc.mjs +1 -1
  205. package/authoring/doctypes/schema/type.ts +1 -2
  206. package/authoring/doctypes/template/parse.d.mts +2 -0
  207. package/authoring/doctypes/template/parse.mjs +4 -0
  208. package/authoring/doctypes/template/parse.test.mjs +18 -0
  209. package/authoring/doctypes/template/template.doc.mjs +9 -3
  210. package/authoring/doctypes/template/type.ts +8 -0
  211. package/authoring/doctypes/theme/parse.d.mts +35 -0
  212. package/authoring/doctypes/theme/parse.mjs +76 -0
  213. package/authoring/doctypes/theme/theme.doc.d.mts +9 -0
  214. package/authoring/doctypes/theme/theme.doc.mjs +79 -0
  215. package/authoring/doctypes/theme/type.ts +42 -0
  216. package/authoring/doctypes/types.ts +2 -1
  217. package/authoring/gap-report/gap-report.doc.d.mts +12 -0
  218. package/authoring/gap-report/gap-report.doc.mjs +183 -0
  219. package/authoring/identity/identity.doc.mjs +2 -2
  220. package/authoring/index.d.mts +1 -0
  221. package/authoring/index.d.ts +7 -4
  222. package/authoring/index.mjs +2 -1
  223. package/authoring/integration/integration.doc.mjs +2 -2
  224. package/authoring/integration/type.ts +4 -10
  225. package/clients/cli/__tests__/cliManifest.test.ts +27 -29
  226. package/clients/cli/commands/blog.doc.mjs +1 -1
  227. package/clients/cli/commands/build-theme.adaptations.test.mjs +100 -1
  228. package/clients/cli/commands/build-theme.mjs +11 -45
  229. package/clients/cli/commands/build.doc.mjs +12 -7
  230. package/clients/cli/commands/build.mjs +105 -67
  231. package/clients/cli/commands/build.text-fields.test.mjs +41 -0
  232. package/clients/cli/commands/component/index.mjs +1 -1
  233. package/clients/cli/commands/component-ownership.test.mjs +3 -3
  234. package/clients/cli/commands/component.doc.mjs +1 -1
  235. package/clients/cli/commands/discover.broken-integration.test.mjs +7 -8
  236. package/clients/cli/commands/discover.doc.mjs +1 -1
  237. package/clients/cli/commands/discover.mjs +1 -1
  238. package/clients/cli/commands/docs.doc.mjs +21 -9
  239. package/clients/cli/commands/docs.mjs +184 -70
  240. package/clients/cli/commands/docs.test.mjs +120 -16
  241. package/clients/cli/commands/doctor-integration-components.doc.mjs +1 -1
  242. package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -3
  243. package/clients/cli/commands/doctor-integration-templates.doc.mjs +15 -8
  244. package/clients/cli/commands/doctor-integration-validate.doc.mjs +1 -1
  245. package/clients/cli/commands/doctor-integration.doc.mjs +1 -1
  246. package/clients/cli/commands/doctor-integration.test.mjs +90 -8
  247. package/clients/cli/commands/doctor.doc.mjs +1 -1
  248. package/clients/cli/commands/doctor.mjs +53 -16
  249. package/clients/cli/commands/gap-report.doc.mjs +1 -1
  250. package/clients/cli/commands/hook.doc.mjs +1 -1
  251. package/clients/cli/commands/init.doc.mjs +1 -1
  252. package/clients/cli/commands/integration-add.controls.test.mjs +1 -1
  253. package/clients/cli/commands/integration-add.doc.mjs +11 -1
  254. package/clients/cli/commands/integration-pack.doc.mjs +1 -1
  255. package/clients/cli/commands/integration-real-world.test.mjs +3 -9
  256. package/clients/cli/commands/integration.doc.mjs +1 -1
  257. package/clients/cli/commands/integration.mjs +1 -0
  258. package/clients/cli/commands/layout-check.doc.mjs +1 -1
  259. package/clients/cli/commands/layout-expand.doc.mjs +1 -1
  260. package/clients/cli/commands/layout-grammar.doc.mjs +1 -1
  261. package/clients/cli/commands/layout.doc.mjs +1 -1
  262. package/clients/cli/commands/manifest.doc.mjs +1 -1
  263. package/clients/cli/commands/search.doc.mjs +1 -1
  264. package/clients/cli/commands/search.mjs +11 -2
  265. package/clients/cli/commands/setup-nudge.test.mjs +6 -0
  266. package/clients/cli/commands/swizzle.doc.mjs +1 -1
  267. package/clients/cli/commands/template.doc.mjs +24 -7
  268. package/clients/cli/commands/template.mjs +4 -91
  269. package/clients/cli/commands/text-json-parity.test.mjs +719 -0
  270. package/clients/cli/commands/theme-add.doc.mjs +2 -2
  271. package/clients/cli/commands/theme-build.doc.mjs +1 -1
  272. package/clients/cli/commands/theme-list.doc.mjs +2 -2
  273. package/clients/cli/commands/theme-palette-generate.doc.mjs +3 -3
  274. package/clients/cli/commands/theme-palette.doc.mjs +1 -1
  275. package/clients/cli/commands/theme-targets.behavior.test.mjs +4 -3
  276. package/clients/cli/commands/theme-targets.doc.mjs +1 -1
  277. package/clients/cli/commands/theme-template.doc.mjs +1 -1
  278. package/clients/cli/commands/theme.doc.mjs +1 -1
  279. package/clients/cli/commands/upgrade.doc.mjs +3 -2
  280. package/clients/cli/commands/upgrade.file-protection.test.mjs +228 -0
  281. package/clients/cli/commands/upgrade.mjs +29 -7
  282. package/clients/cli/formatters/index.mjs +2 -0
  283. package/clients/cli/formatters/index.test.mjs +6 -0
  284. package/clients/cli/index.mjs +8 -0
  285. package/clients/cli/lib/hook-format.mjs +14 -5
  286. package/clients/cli/lib/manifest.mjs +9 -2
  287. package/foundation/agent-docs/agent-docs.d.mts +3 -2
  288. package/foundation/agent-docs/agent-docs.mjs +13 -9
  289. package/foundation/agent-docs/agent-docs.test.mjs +19 -1
  290. package/foundation/config/project-themes.test.mjs +11 -19
  291. package/foundation/config/project.d.mts +20 -11
  292. package/foundation/config/project.mjs +123 -80
  293. package/foundation/config/project.test.mjs +144 -21
  294. package/foundation/discovery/authoring-self-docs.d.mts +18 -0
  295. package/foundation/discovery/authoring-self-docs.mjs +37 -15
  296. package/foundation/discovery/authoring-self-docs.test.mjs +25 -9
  297. package/foundation/discovery/authoring-surface.d.mts +74 -0
  298. package/foundation/discovery/authoring-surface.mjs +525 -0
  299. package/foundation/discovery/authoring-surface.test.mjs +392 -0
  300. package/foundation/discovery/cli-self-docs.d.mts +119 -0
  301. package/foundation/discovery/cli-self-docs.mjs +490 -0
  302. package/foundation/discovery/cli-self-docs.test.mjs +375 -0
  303. package/foundation/discovery/component-discovery.d.mts +38 -0
  304. package/foundation/discovery/component-discovery.mjs +48 -0
  305. package/foundation/discovery/component-loader.d.mts +35 -38
  306. package/foundation/discovery/component-loader.mjs +53 -222
  307. package/foundation/discovery/docs-discovery.d.mts +112 -11
  308. package/foundation/discovery/docs-discovery.mjs +231 -36
  309. package/foundation/discovery/docs-discovery.test.mjs +111 -32
  310. package/foundation/discovery/docs-output-budget.d.mts +2 -2
  311. package/foundation/discovery/docs-output-budget.mjs +1 -1
  312. package/foundation/discovery/docs-section-key.d.mts +28 -10
  313. package/foundation/discovery/docs-section-key.mjs +134 -33
  314. package/foundation/discovery/docs-section-key.test.mjs +41 -19
  315. package/foundation/discovery/template-adapter.d.mts +99 -6
  316. package/foundation/discovery/template-adapter.mjs +480 -57
  317. package/foundation/discovery/template-adapter.test.mjs +57 -0
  318. package/foundation/discovery/template-conflict-release.d.mts +13 -0
  319. package/foundation/discovery/template-conflict-release.mjs +40 -0
  320. package/foundation/discovery/template-conflict-release.test.mjs +40 -0
  321. package/foundation/discovery/theme-discovery.d.mts +67 -7
  322. package/foundation/discovery/theme-discovery.mjs +916 -186
  323. package/foundation/discovery/theme-discovery.test.mjs +613 -219
  324. package/foundation/doc-compiler/bundle.d.mts +47 -0
  325. package/foundation/doc-compiler/bundle.mjs +278 -0
  326. package/foundation/doc-compiler/bundle.test.mjs +266 -0
  327. package/foundation/doc-compiler/compile.d.mts +220 -39
  328. package/foundation/doc-compiler/compile.mjs +311 -15
  329. package/foundation/doc-compiler/diagnostics.d.mts +126 -0
  330. package/foundation/doc-compiler/diagnostics.mjs +305 -0
  331. package/foundation/doc-compiler/doc-compiler.test.mjs +28 -1
  332. package/foundation/doc-compiler/doc-loads.test.mjs +1642 -0
  333. package/foundation/doc-compiler/import.d.mts +24 -0
  334. package/foundation/doc-compiler/import.mjs +59 -0
  335. package/foundation/doc-compiler/inputs.d.mts +102 -0
  336. package/foundation/doc-compiler/inputs.mjs +291 -0
  337. package/foundation/doc-compiler/inputs.test.mjs +299 -0
  338. package/foundation/doc-compiler/ir.d.mts +13 -0
  339. package/foundation/doc-compiler/ir.mjs +196 -12
  340. package/foundation/doc-compiler/lenses.d.mts +8 -5
  341. package/foundation/doc-compiler/lenses.mjs +50 -4
  342. package/foundation/doc-compiler/links.d.mts +162 -0
  343. package/foundation/doc-compiler/links.mjs +294 -0
  344. package/foundation/doc-compiler/links.test.mjs +192 -0
  345. package/foundation/doc-compiler/lower-doc.test.mjs +495 -0
  346. package/foundation/doc-compiler/overlays.d.mts +37 -0
  347. package/foundation/doc-compiler/overlays.mjs +206 -0
  348. package/foundation/doc-compiler/parse-readable.d.mts +9 -0
  349. package/foundation/doc-compiler/parse-readable.mjs +29 -0
  350. package/foundation/doc-compiler/read.d.mts +127 -0
  351. package/foundation/doc-compiler/read.mjs +325 -0
  352. package/foundation/doc-compiler/read.test.mjs +313 -0
  353. package/foundation/doc-compiler/source.d.mts +33 -0
  354. package/foundation/doc-compiler/source.mjs +128 -0
  355. package/foundation/doc-compiler/tree.d.mts +288 -0
  356. package/foundation/doc-compiler/tree.mjs +876 -0
  357. package/foundation/doc-compiler/tree.test.mjs +598 -0
  358. package/foundation/fs/file-protection.d.mts +33 -0
  359. package/foundation/fs/file-protection.mjs +825 -0
  360. package/foundation/fs/file-protection.test.mjs +250 -0
  361. package/foundation/integrations/autolink.d.mts +58 -1
  362. package/foundation/integrations/autolink.mjs +143 -57
  363. package/foundation/integrations/autolink.test.mjs +1 -1
  364. package/foundation/integrations/cli-requirement.d.mts +45 -0
  365. package/foundation/integrations/cli-requirement.mjs +154 -0
  366. package/foundation/integrations/cli-requirement.test.mjs +84 -0
  367. package/foundation/integrations/contribution-fixes.d.mts +145 -0
  368. package/foundation/integrations/contribution-fixes.mjs +1284 -0
  369. package/foundation/integrations/contribution-inventory.d.mts +3 -2
  370. package/foundation/integrations/contribution-inventory.mjs +27 -24
  371. package/foundation/integrations/contribution-inventory.test.mjs +86 -27
  372. package/foundation/integrations/integration-warnings.d.mts +9 -2
  373. package/foundation/integrations/integration-warnings.mjs +51 -26
  374. package/foundation/integrations/integration-warnings.test.mjs +74 -1
  375. package/foundation/integrations/integrations.d.mts +3 -0
  376. package/foundation/integrations/integrations.mjs +11 -97
  377. package/foundation/integrations/provider-ledger.test.mjs +275 -0
  378. package/foundation/integrations/provider-resolution.d.mts +152 -0
  379. package/foundation/integrations/provider-resolution.mjs +576 -0
  380. package/foundation/integrations/provider-resolution.test.mjs +369 -0
  381. package/foundation/integrations/theme-descriptor.d.mts +8 -0
  382. package/foundation/integrations/theme-descriptor.mjs +44 -0
  383. package/foundation/integrations/validate-contributions.mjs +121 -29
  384. package/foundation/response/error-codes.d.mts +3 -1
  385. package/foundation/response/error-codes.d.ts +2 -0
  386. package/foundation/response/error-codes.doc.mjs +12 -2
  387. package/foundation/response/error-codes.mjs +7 -1
  388. package/foundation/response/error-codes.test.mjs +55 -11
  389. package/foundation/response/response-types.doc.d.mts +5 -1
  390. package/foundation/response/response-types.doc.mjs +20 -11
  391. package/foundation/response/response.doc.mjs +1 -1
  392. package/foundation/text/string-utils.d.mts +8 -0
  393. package/foundation/text/string-utils.mjs +40 -10
  394. package/foundation/xle/expand.mjs +1 -1
  395. package/foundation/xle/xle.test.mjs +13 -0
  396. package/package.json +10 -9
  397. package/assets/templates/themes/manifest.json +0 -95
@@ -13,7 +13,7 @@
13
13
 
14
14
  import {linkReferenceTopic} from '../../../foundation/doc-compiler/compile.mjs';
15
15
  import {detailView} from '../../../foundation/doc-compiler/lenses.mjs';
16
- import {referenceTargets, resolveTopicDocs} from '../_adapter.mjs';
16
+ import {referenceTargets, resolveTopicDocs, topicLinks} from '../_adapter.mjs';
17
17
 
18
18
  /**
19
19
  * @param {string} topic
@@ -25,10 +25,18 @@ import {referenceTargets, resolveTopicDocs} from '../_adapter.mjs';
25
25
  * @returns {Promise<import('../docs.type.mjs').DocsDetailResponse>}
26
26
  */
27
27
  export async function detail(topic, options = {}) {
28
- const {catalog, node, lang} = await resolveTopicDocs(topic, options);
28
+ const {catalog, node, lang, entry} = await resolveTopicDocs(topic, options);
29
29
  const linked = await linkReferenceTopic(
30
30
  node,
31
31
  referenceTargets(catalog, lang),
32
32
  );
33
- return {type: 'docs.detail', data: detailView(linked)};
33
+ return {
34
+ type: 'docs.detail',
35
+ data: {
36
+ ...detailView(linked),
37
+ // A guide the docs tree places is read by its route, not its doc name.
38
+ ...(entry.tree ? {name: entry.name} : {}),
39
+ links: await topicLinks(catalog, entry),
40
+ },
41
+ };
34
42
  }
@@ -4,9 +4,9 @@
4
4
  * @file docs.detail.section leaf — load a single section of a topic.
5
5
  *
6
6
  * @input A topic name, a section query, and optional {lang, zh, dense}. Resolves
7
- * the topic via the shared adapter, finds the section in its lowered compiled
8
- * node by its stable key, then by exact title, then by a title that contains
9
- * the query, and links only that section.
7
+ * the topic via the shared adapter, keeps the 0.6.x first title-substring
8
+ * match, then falls back to a stable key or normalized title key, and links
9
+ * only that section.
10
10
  * @output { type: 'docs.detail.section', data: ReferenceSection } with any
11
11
  * token-ref blocks inlined — matching `astryx --json docs <topic> <section>`.
12
12
  * Throws ERR_UNKNOWN_SECTION when nothing matches, or when the query matches
@@ -50,18 +50,15 @@ export async function section(topic, sectionName, options = {}) {
50
50
  );
51
51
  }
52
52
 
53
- const {catalog, node, lang} = await resolveTopicDocs(topic, options);
53
+ const {catalog, node, lang, entry} = await resolveTopicDocs(topic, options);
54
54
  const sections = readerSections(node);
55
- const {section: match, candidates} = findDocSection(sections, sectionName);
55
+ const {section: match} = findDocSection(sections, sectionName);
56
56
  if (!match) {
57
- const ambiguous = candidates.length > 1;
58
57
  throw new AstryxError(
59
- ambiguous
60
- ? `Section "${sectionName}" matches ${candidates.length} sections in "${topic}". Read one by its key.`
61
- : `Section "${sectionName}" not found in "${topic}"`,
62
- (ambiguous ? candidates : sections).map(s => ({
63
- name: sectionKey(s),
64
- reason: s.title,
58
+ `Section "${sectionName}" not found in "${topic}"`,
59
+ sections.map(s => ({
60
+ name: s.title,
61
+ reason: 'available section',
65
62
  })),
66
63
  ERROR_CODES.ERR_UNKNOWN_SECTION,
67
64
  );
@@ -74,5 +71,19 @@ export async function section(topic, sectionName, options = {}) {
74
71
  match,
75
72
  referenceTargets(catalog, lang),
76
73
  );
77
- return {type: 'docs.detail.section', data: sectionView(node, linked)};
74
+ // The moves from one section (spec:AST-047): up to its topic's index, and
75
+ // across to the sections before and after it.
76
+ const at = sections.indexOf(match);
77
+ /** @type {import('../../docs.type.mjs').DocsLinks} */
78
+ const links = {up: `astryx docs ${entry.name} --index`};
79
+ if (at > 0) {
80
+ links.previous = `astryx docs ${entry.name} ${sectionKey(sections[at - 1])}`;
81
+ }
82
+ if (at !== -1 && at < sections.length - 1) {
83
+ links.next = `astryx docs ${entry.name} ${sectionKey(sections[at + 1])}`;
84
+ }
85
+ return {
86
+ type: 'docs.detail.section',
87
+ data: {...sectionView(node, linked), links},
88
+ };
78
89
  }
@@ -30,6 +30,17 @@ describe('docs.detail.section leaf', () => {
30
30
  }
31
31
  expect(err).toBeInstanceOf(AstryxError);
32
32
  expect(err.code).toBe('ERR_UNKNOWN_SECTION');
33
+ expect(err.suggestions).toEqual(
34
+ expect.arrayContaining([
35
+ expect.objectContaining({
36
+ name: expect.any(String),
37
+ reason: 'available section',
38
+ }),
39
+ ]),
40
+ );
41
+ expect(err.suggestions.some(suggestion => /\s/u.test(suggestion.name))).toBe(
42
+ true,
43
+ );
33
44
  }, SLOW);
34
45
 
35
46
  it('does not return the first section for an empty section name', async () => {
@@ -50,12 +61,10 @@ describe('docs.detail.section leaf', () => {
50
61
  expect(res.data.id).toBe(target.id);
51
62
  }, SLOW);
52
63
 
53
- it('refuses a query that matches more than one section', async () => {
54
- const err = await section('theme', 'e').catch(e => e);
55
- expect(err).toBeInstanceOf(AstryxError);
56
- expect(err.code).toBe('ERR_UNKNOWN_SECTION');
57
- expect(err.message).toMatch(/matches \d+ sections/);
58
- expect(err.suggestions.length).toBeGreaterThan(1);
64
+ it('keeps a previously accepted ambiguous query on its first match', async () => {
65
+ const res = await section('theme', 'e');
66
+ expect(res.type).toBe('docs.detail.section');
67
+ expect(res.data).toBeDefined();
59
68
  }, SLOW);
60
69
 
61
70
  it.each([null, 'zh', 'dense'])(
@@ -15,7 +15,8 @@
15
15
  * import('./docs.type.mjs').DocsListResponse |
16
16
  * import('./docs.type.mjs').DocsIndexResponse |
17
17
  * import('./docs.type.mjs').DocsDetailResponse |
18
- * import('./docs.type.mjs').DocsDetailSectionResponse
18
+ * import('./docs.type.mjs').DocsDetailSectionResponse |
19
+ * import('./docs.type.mjs').DocsNodeResponse
19
20
  * >}
20
21
  */
21
22
  export function docs(topic?: string, section?: string, options?: {
@@ -24,9 +25,10 @@ export function docs(topic?: string, section?: string, options?: {
24
25
  dense?: boolean | undefined;
25
26
  index?: boolean | undefined;
26
27
  cwd?: string | undefined;
27
- }): Promise<import("./docs.type.mjs").DocsListResponse | import("./docs.type.mjs").DocsIndexResponse | import("./docs.type.mjs").DocsDetailResponse | import("./docs.type.mjs").DocsDetailSectionResponse>;
28
+ }): Promise<import("./docs.type.mjs").DocsListResponse | import("./docs.type.mjs").DocsIndexResponse | import("./docs.type.mjs").DocsDetailResponse | import("./docs.type.mjs").DocsDetailSectionResponse | import("./docs.type.mjs").DocsNodeResponse>;
28
29
  import { list } from './list/list.mjs';
29
30
  import { index } from './index/index.mjs';
30
31
  import { detail } from './detail/detail.mjs';
31
32
  import { section as sectionLeaf } from './detail/section/section.mjs';
32
- export { list, index, detail, sectionLeaf as section };
33
+ import { node as nodeLeaf } from './node/node.mjs';
34
+ export { list, index, detail, sectionLeaf as section, nodeLeaf as node };
@@ -11,22 +11,32 @@ export const doc = {
11
11
  type: 'function',
12
12
  kind: 'api',
13
13
  name: 'docs',
14
+ namespace: 'cli/api',
14
15
  displayName: 'docs()',
15
16
  summary:
16
17
  'Read the reference docs: list every topic, one topic\'s sections, one section, or a whole topic.',
17
18
  description:
18
- 'No topic lists every reference-doc topic; a topic returns that full ' +
19
- 'ReferenceDoc; `index: true` returns the topic\'s section index instead ' +
20
- '(each section\'s key, title, and summary); a topic plus a section returns ' +
21
- 'that one section, found by its key, then its exact title, then a unique ' +
22
- 'part of its title (an ambiguous query is refused). Token-ref blocks are ' +
23
- 'inlined in every read. The topic set is the CLI\'s own docs plus the ' +
19
+ 'No topic lists every reference-doc topic; a topic returns its whole ' +
20
+ 'ReferenceDoc, and `index: true` returns its section index (each ' +
21
+ 'section\'s key, title, and summary); a topic plus a section returns ' +
22
+ 'that one section. Token-ref blocks are ' +
23
+ 'inlined in every read, and so is a section\'s reference block: the doc it ' +
24
+ 'includes, then the command that opens that doc. The topic set is the CLI\'s own docs plus the ' +
24
25
  'ones the project\'s configured integrations contribute, including any ' +
25
26
  'topic an integration replaces or extends, so it depends on the cwd. ' +
27
+ 'A route opens a node of the docs tree instead: a namespace such as ' +
28
+ "`cli/api` returns its children one level down, a typed doc such as " +
29
+ "`cli/api/functions/search` returns its content, and a guide the tree " +
30
+ 'places (`cli/integrations`) reads like any topic. ' +
31
+ 'Every read but the list carries `links`, the commands that move from it: ' +
32
+ '`up` to the level it sits in, `previous` and `next` to its neighbors, and, ' +
33
+ 'for a typed doc, `related` to the docs it names (its command or function, ' +
34
+ 'and its related docs); `previous` and `next` ' +
35
+ 'to the section or node before and after it. ' +
26
36
  'Overlay options select localized or dense variants.',
27
37
  importPath: '@astryxdesign/cli/api',
28
38
  signature:
29
- 'docs(topic?: string, section?: string, options?: DocsOptions): Promise<DocsListResponse | DocsIndexResponse | DocsDetailResponse | DocsDetailSectionResponse>',
39
+ 'docs(topic?: string, section?: string, options?: DocsOptions): Promise<DocsListResponse | DocsIndexResponse | DocsDetailResponse | DocsDetailSectionResponse | DocsNodeResponse>',
30
40
  keywords: [
31
41
  'docs',
32
42
  'documentation',
@@ -42,7 +52,7 @@ export const doc = {
42
52
  name: 'topic',
43
53
  type: 'string',
44
54
  description:
45
- "Doc topic to load (e.g. 'principles'). Omit to list all topics.",
55
+ "Doc topic to load (e.g. 'principles'), or a docs-tree route (e.g. 'cli/api/functions/search'). Omit to list all topics.",
46
56
  },
47
57
  {
48
58
  name: 'section',
@@ -63,13 +73,14 @@ export const doc = {
63
73
  {
64
74
  name: 'options.dense',
65
75
  type: 'boolean',
66
- description: 'Return the token-efficient dense doc variant.',
76
+ description:
77
+ 'Return the token-efficient dense doc variant. It is written to be read whole, so it returns the whole doc.',
67
78
  },
68
79
  {
69
80
  name: 'options.index',
70
81
  type: 'boolean',
71
82
  description:
72
- "Return the topic's section index (each section's key, title, and summary) instead of the whole doc.",
83
+ "Return the topic's section index (each section's key, title, and summary), even for a topic with one section.",
73
84
  },
74
85
  {
75
86
  name: 'options.cwd',
@@ -82,41 +93,52 @@ export const doc = {
82
93
  {
83
94
  type: 'docs.list',
84
95
  description:
85
- 'All available reference-doc topics as DocsListEntry[] ({topic, description, package, replaces?}), in read order.',
96
+ "Every reference-doc topic in read order, as DocsListEntry[] ({topic, description, package, replaces?}), each readable with docs(topic). meta.namespaces lists the docs tree's top-level namespaces ({topic, description, package}), and meta.notLoaded each package whose docs did not load ({package, message}).",
86
97
  },
87
98
  {
88
99
  type: 'docs.detail',
89
100
  description:
90
- "One topic's full ReferenceDoc, with token-ref blocks inlined.",
101
+ "One topic's full ReferenceDoc, with token-ref and reference blocks inlined, plus links.",
91
102
  },
92
103
  {
93
104
  type: 'docs.index',
94
105
  description:
95
- "One topic's section index (index: true): {name, title, description, sections: [{id, title, summary}]}.",
106
+ "One topic's section index (index: true): {name, title, description, sections: [{id, title, summary}], links}.",
96
107
  },
97
108
  {
98
109
  type: 'docs.detail.section',
99
110
  description:
100
- 'One ReferenceSection of the topic, found by key or title, with token-ref blocks inlined.',
111
+ 'One ReferenceSection of the topic, found by key or title, with token-ref and reference blocks inlined.',
112
+ },
113
+ {
114
+ type: 'docs.node',
115
+ description:
116
+ "A namespace or typed doc in the docs tree, read by its route: {id, route, kind, package, title, summary, breadcrumb, slots, content}. A namespace lists each slot's children one level down; a typed doc carries its content.",
101
117
  },
102
118
  ],
103
119
  throws: [
104
120
  {
105
121
  code: 'ERR_UNKNOWN_TOPIC',
106
- when: 'the topic is not a string or matches no known doc topic',
122
+ when: 'the topic is not a string, or matches no topic and no docs-tree route',
107
123
  },
108
124
  {
109
125
  code: 'ERR_UNKNOWN_SECTION',
110
- when: 'a section is requested but is empty, matches no section, or matches more than one',
126
+ when: 'a section is requested but is empty, matches no section, matches more than one, or is asked of a docs-tree namespace or typed doc, which have no sections',
111
127
  },
112
128
  ],
113
129
  examples: [
114
130
  {label: 'List topics', code: 'const r = await docs();'},
115
- {label: 'Load a topic', code: "await docs('principles');"},
131
+ {label: 'A whole topic', code: "await docs('principles');"},
116
132
  {
117
133
  label: "A topic's sections",
118
134
  code: "await docs('principles', undefined, {index: true});",
119
135
  },
136
+ {label: 'A docs-tree namespace', code: "await docs('cli/api');"},
137
+ {label: 'One API function', code: "await docs('cli/api/functions/search');"},
138
+ {
139
+ label: 'A whole guide from the docs tree',
140
+ code: "await docs('cli/integrations');",
141
+ },
120
142
  {label: 'One section by key', code: "await docs('tokens', 'spacing');"},
121
143
  ],
122
144
  command: 'docs',
package/api/docs/docs.mjs CHANGED
@@ -3,27 +3,35 @@
3
3
  /**
4
4
  * @file Programmatic API for the docs command.
5
5
  *
6
- * Dispatcher + barrel. `docs()` routes by argument shape into one of four
6
+ * Dispatcher + barrel. `docs()` routes by argument shape into one of five
7
7
  * leaves, each projecting into a single { type, data } envelope:
8
8
  *
9
9
  * docs() -> list -> docs.list
10
10
  * docs(topic) -> detail -> docs.detail
11
11
  * docs(topic, undefined, {index: true}) -> index -> docs.index
12
12
  * docs(topic, section) -> section -> docs.detail.section
13
+ * docs(route) -> node -> docs.node
13
14
  *
14
- * A topic read returns the whole doc, as it always has. The index is how a
15
- * reader works progressively instead: list the topic's sections, then read one
16
- * by its key. The leaves live in list/, index/, detail/, and detail/section/;
17
- * the discovery, overlay loading, and topic resolution they share sit in
18
- * _adapter.mjs.
15
+ * A topic read returns the whole doc, as it always has; `index` returns its
16
+ * sections, so a reader can open one by its key (spec:AST-047). The CLI's text
17
+ * view lists the sections by default. A route names a node
18
+ * of the docs tree (spec:AST-046): a namespace lists its children, a guide the
19
+ * tree places reads like any topic, and a typed doc prints its content. The
20
+ * leaves
21
+ * live in list/, index/, detail/, detail/section/, and node/; the discovery,
22
+ * overlay loading, and resolution they share sit in _adapter.mjs.
19
23
  */
20
24
 
21
25
  import {list} from './list/list.mjs';
22
26
  import {index} from './index/index.mjs';
23
27
  import {detail} from './detail/detail.mjs';
24
28
  import {section as sectionLeaf} from './detail/section/section.mjs';
29
+ import {node as nodeLeaf, nodeView} from './node/node.mjs';
30
+ import {resolveDocsArgument} from './_adapter.mjs';
31
+ import {AstryxError} from '../error.mjs';
32
+ import {ERROR_CODES} from '../../foundation/response/error-codes.mjs';
25
33
 
26
- export {list, index, detail, sectionLeaf as section};
34
+ export {list, index, detail, sectionLeaf as section, nodeLeaf as node};
27
35
 
28
36
  /**
29
37
  * @param {string} [topic]
@@ -39,11 +47,39 @@ export {list, index, detail, sectionLeaf as section};
39
47
  * import('./docs.type.mjs').DocsListResponse |
40
48
  * import('./docs.type.mjs').DocsIndexResponse |
41
49
  * import('./docs.type.mjs').DocsDetailResponse |
42
- * import('./docs.type.mjs').DocsDetailSectionResponse
50
+ * import('./docs.type.mjs').DocsDetailSectionResponse |
51
+ * import('./docs.type.mjs').DocsNodeResponse
43
52
  * >}
44
53
  */
45
54
  export async function docs(topic, section, options = {}) {
46
55
  if (!topic) return list(options);
56
+ const found = await resolveDocsArgument(topic, options);
57
+ if (found.kind === 'node') {
58
+ // A namespace or a typed doc has no sections: it is one read. `--index`
59
+ // asks for what the node read already is.
60
+ if (section) {
61
+ throw new AstryxError(
62
+ `"${found.node.route}" has no sections. ${
63
+ found.node.kind === 'namespace'
64
+ ? 'Open one of its children instead.'
65
+ : `Read it whole: astryx docs ${found.node.route}.`
66
+ }`,
67
+ found.node.kind === 'namespace'
68
+ ? found.node.slots.flatMap(slot =>
69
+ slot.children.map(route => ({
70
+ name: route,
71
+ reason: found.tree.get(route)?.summary ?? '',
72
+ })),
73
+ )
74
+ : [{name: found.node.route, reason: found.node.summary}],
75
+ ERROR_CODES.ERR_UNKNOWN_SECTION,
76
+ );
77
+ }
78
+ return {
79
+ type: 'docs.node',
80
+ data: await nodeView(found.catalog, found.tree, found.node),
81
+ };
82
+ }
47
83
  if (section) return sectionLeaf(topic, section, options);
48
84
  if (options.index) return index(topic, options);
49
85
  return detail(topic, options);
@@ -26,11 +26,9 @@ describe('docs() dispatcher routing', () => {
26
26
  expect((await docs('')).type).toBe('docs.list');
27
27
  }, SLOW);
28
28
 
29
- it('topic only -> docs.detail', async () => {
29
+ it('topic only -> docs.detail, the whole doc', async () => {
30
30
  const {data} = await docs();
31
- const topic = data[0].topic;
32
- const r = await docs(topic);
33
- expect(r.type).toBe('docs.detail');
31
+ expect((await docs(data[0].topic)).type).toBe('docs.detail');
34
32
  }, SLOW);
35
33
 
36
34
  it('topic + index -> docs.index', async () => {
@@ -69,6 +67,162 @@ describe('docs() dispatcher routing', () => {
69
67
  });
70
68
  }, SLOW);
71
69
 
70
+ it("lists the docs tree's namespaces in meta, so every data entry reads as a topic", async () => {
71
+ const res = await docs();
72
+ expect(res.meta.namespaces.map(entry => entry.topic)).toEqual(['cli', 'unorganized']);
73
+ for (const entry of res.data) {
74
+ expect((await docs(entry.topic)).type).toBe('docs.detail');
75
+ }
76
+ }, SLOW);
77
+
78
+ it('opens a route typed in another case', async () => {
79
+ const r = await docs('CLI/API');
80
+ expect(r.type).toBe('docs.node');
81
+ expect(r.data).toMatchObject({route: 'cli/api', links: {up: 'astryx docs cli'}});
82
+ }, SLOW);
83
+
84
+ it('a section of a typed doc -> ERR_UNKNOWN_SECTION, suggesting the doc itself', async () => {
85
+ const err = await docs('cli/api/functions/search', 'x').catch(e => e);
86
+ expect(err.code).toBe('ERR_UNKNOWN_SECTION');
87
+ expect(err.message).toContain('astryx docs cli/api/functions/search');
88
+ expect(err.suggestions.map(s => s.name)).toEqual(['cli/api/functions/search']);
89
+ }, SLOW);
90
+
91
+ it('a namespace route -> docs.node, one level of children', async () => {
92
+ const r = await docs('cli/api');
93
+ expect(r.type).toBe('docs.node');
94
+ expect(r.data).toMatchObject({
95
+ id: 'astryx:artifact:v1/%40astryxdesign%2Fcli/namespace/api',
96
+ route: 'cli/api',
97
+ kind: 'namespace',
98
+ package: '@astryxdesign/cli',
99
+ breadcrumb: [{route: 'cli', title: 'Astryx CLI'}],
100
+ content: [],
101
+ });
102
+ expect(r.data.slots.flatMap(slot => slot.children.map(c => c.route))).toEqual([
103
+ 'cli/api/functions',
104
+ 'cli/api/schemas',
105
+ 'cli/api/enums',
106
+ ]);
107
+ }, SLOW);
108
+
109
+ it('a typed doc route -> docs.node with its content', async () => {
110
+ const r = await docs('cli/api/functions/search');
111
+ expect(r.type).toBe('docs.node');
112
+ expect(r.data).toMatchObject({
113
+ id: 'astryx:artifact:v1/%40astryxdesign%2Fcli/function/search',
114
+ kind: 'function',
115
+ title: 'search()',
116
+ slots: [],
117
+ });
118
+ expect(r.data.breadcrumb.map(link => link.route)).toEqual([
119
+ 'cli',
120
+ 'cli/api',
121
+ 'cli/api/functions',
122
+ ]);
123
+ expect(JSON.stringify(r.data.content)).toContain(
124
+ '`astryx search` runs it. Read it with `astryx docs cli/commands/search`.',
125
+ );
126
+ }, SLOW);
127
+
128
+ it('a guide the tree places reads like a topic: the whole doc, its index, a section', async () => {
129
+ expect((await docs('cli/integrations')).type).toBe('docs.detail');
130
+ const index = await docs('cli/integrations', undefined, {index: true});
131
+ expect(index).toMatchObject({type: 'docs.index', data: {name: 'cli/integrations'}});
132
+ expect(index.data.sections.length).toBeGreaterThan(1);
133
+ expect((await docs('cli/integrations', 'components')).type).toBe(
134
+ 'docs.detail.section',
135
+ );
136
+ }, SLOW);
137
+
138
+ it('every read carries its moves (spec:AST-047)', async () => {
139
+ const root = (await docs('cli')).data;
140
+ expect(root.links).toEqual({up: 'astryx docs'});
141
+ const leaf = (await docs('cli/api/functions/search')).data;
142
+ expect(leaf.links.up).toBe('astryx docs cli/api/functions');
143
+ expect(leaf.links.previous).toMatch(/^astryx docs cli\/api\/functions\/[a-z-]+$/);
144
+ expect(leaf.links.next).toMatch(/^astryx docs cli\/api\/functions\/[a-z-]+$/);
145
+ const index = await docs('cli/integrations', undefined, {index: true});
146
+ // A guide moves across its namespace's slot, as any tree node does.
147
+ expect(index.data.links).toEqual({
148
+ up: 'astryx docs cli',
149
+ next: 'astryx docs cli/writing-docs',
150
+ });
151
+ const first = (await docs('cli/integrations', index.data.sections[0].id)).data;
152
+ expect(first.links).toEqual({
153
+ up: 'astryx docs cli/integrations --index',
154
+ next: `astryx docs cli/integrations ${index.data.sections[1].id}`,
155
+ });
156
+ const last = index.data.sections.at(-1).id;
157
+ const end = (await docs('cli/integrations', last)).data;
158
+ expect(end.links.next).toBeUndefined();
159
+ expect(end.links.previous).toBe(
160
+ `astryx docs cli/integrations ${index.data.sections.at(-2).id}`,
161
+ );
162
+ // A flat topic's home is the Unorganized level, and it moves across it.
163
+ const flat = (await docs('principles')).data.links;
164
+ expect(flat.up).toBe('astryx docs unorganized');
165
+ expect(flat.previous).toMatch(/^astryx docs [a-z-]+$/);
166
+ expect(flat.next).toMatch(/^astryx docs [a-z-]+$/);
167
+ const home = (await docs('unorganized')).data;
168
+ expect(home).toMatchObject({id: null, kind: 'namespace', links: {up: 'astryx docs'}});
169
+ expect(home.slots[0].children.map(child => child.route)).toEqual(
170
+ (await docs()).data.map(entry => entry.topic),
171
+ );
172
+ }, SLOW);
173
+
174
+ it('names a node by its doc identity; a generated level has id null', async () => {
175
+ const level = (await docs('cli/api/functions')).data;
176
+ expect(level).toHaveProperty('id', null);
177
+ expect(level.route).toBe('cli/api/functions');
178
+ }, SLOW);
179
+
180
+ it("offers a typed doc's related docs as moves", async () => {
181
+ const fn = (await docs('cli/api/functions/search')).data;
182
+ expect(fn.links.related).toContain('astryx docs cli/commands/search');
183
+ const cmd = (await docs('cli/commands/search')).data;
184
+ expect(cmd.links.related).toContain('astryx docs cli/api/functions/search');
185
+ }, SLOW);
186
+
187
+ it('a topic read returns the whole doc; index returns its sections', async () => {
188
+ expect((await docs('principles')).type).toBe('docs.detail');
189
+ const index = await docs('principles', undefined, {index: true});
190
+ expect(index.type).toBe('docs.index');
191
+ expect(index.data.sections.length).toBeGreaterThan(1);
192
+ expect((await docs('principles', undefined, {dense: true})).type).toBe(
193
+ 'docs.detail',
194
+ );
195
+ }, SLOW);
196
+
197
+ it("the guide's old flat name is gone, and its error names the new route", async () => {
198
+ await expect(docs('cli-integrations')).rejects.toMatchObject({
199
+ code: 'ERR_UNKNOWN_TOPIC',
200
+ suggestions: [expect.objectContaining({name: 'cli/integrations'})],
201
+ });
202
+ expect((await docs()).data.map(entry => entry.topic)).not.toContain(
203
+ 'cli-integrations',
204
+ );
205
+ }, SLOW);
206
+
207
+ it('a section of a namespace -> ERR_UNKNOWN_SECTION, naming its children', async () => {
208
+ const err = await docs('cli', 'commands').catch(e => e);
209
+ expect(err).toBeInstanceOf(AstryxError);
210
+ expect(err.code).toBe('ERR_UNKNOWN_SECTION');
211
+ expect(err.suggestions.map(s => s.name)).toEqual([
212
+ 'cli/integrations',
213
+ 'cli/writing-docs',
214
+ 'cli/commands',
215
+ 'cli/api',
216
+ ]);
217
+ }, SLOW);
218
+
219
+ it('an unknown route suggests the children of the deepest namespace it reaches', async () => {
220
+ const err = await docs('cli/api/functions/serch').catch(e => e);
221
+ expect(err.code).toBe('ERR_UNKNOWN_TOPIC');
222
+ expect(err.suggestions.map(s => s.name)).toContain('cli/api/functions/search');
223
+ expect(err.suggestions.every(s => s.name.startsWith('cli/api/functions/'))).toBe(true);
224
+ }, SLOW);
225
+
72
226
  it('non-string topic -> ERR_UNKNOWN_TOPIC (not a raw TypeError)', async () => {
73
227
  for (const bad of [123, {}, ['tokens'], true]) {
74
228
  const err = await docs(/** @type {any} */ (bad)).catch(e => e);