@astryxdesign/cli 0.7.2-canary.fdd384f → 0.7.2

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 (450) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/README.md +169 -149
  3. package/api/build/_adapter.d.mts +8 -0
  4. package/api/build/_adapter.mjs +12 -1
  5. package/api/build/build.doc.mjs +4 -0
  6. package/api/build/kit/kit.mjs +3 -11
  7. package/api/component/_adapter.d.mts +31 -0
  8. package/api/component/_adapter.mjs +119 -11
  9. package/api/component/component.doc.mjs +6 -1
  10. package/api/component/component.mjs +60 -10
  11. package/api/component/doc-links.test.mjs +386 -0
  12. package/api/discover/_adapter.d.mts +132 -12
  13. package/api/discover/_adapter.mjs +488 -27
  14. package/api/discover/_adapter.test.mjs +599 -17
  15. package/api/discover/_catalog-view.d.mts +16 -5
  16. package/api/discover/_catalog-view.mjs +32 -8
  17. package/api/discover/detail/doc/doc.mjs +9 -1
  18. package/api/discover/discover.d.mts +11 -0
  19. package/api/discover/discover.doc.mjs +9 -7
  20. package/api/discover/discover.mjs +121 -19
  21. package/api/discover/discover.type.d.mts +20 -7
  22. package/api/discover/discover.type.mjs +17 -7
  23. package/api/discover/list/list.d.mts +4 -3
  24. package/api/discover/list/list.mjs +17 -10
  25. package/api/docs/_adapter.d.mts +63 -0
  26. package/api/docs/_adapter.mjs +312 -18
  27. package/api/docs/docs.doc.mjs +2 -1
  28. package/api/docs/docs.mjs +2 -1
  29. package/api/docs/docs.test.mjs +5 -2
  30. package/api/docs/integration-tree.test.mjs +22 -0
  31. package/api/docs/list/list.mjs +12 -2
  32. package/api/docs/reference-blocks.test.mjs +1 -3
  33. package/api/doctor/_adapter.d.mts +108 -0
  34. package/api/doctor/_adapter.mjs +223 -4
  35. package/api/doctor/doctor.d.mts +199 -0
  36. package/api/doctor/doctor.doc.mjs +3 -2
  37. package/api/doctor/doctor.mjs +530 -34
  38. package/api/doctor/doctor.test.mjs +180 -14
  39. package/api/doctor/release-checks.test.mjs +156 -0
  40. package/api/doctor/theme-checks.mjs +16 -3
  41. package/api/doctor/theme-checks.test.mjs +223 -7
  42. package/api/error.d.mts +10 -0
  43. package/api/error.mjs +25 -0
  44. package/api/gap-report/gap-report.d.mts +3 -0
  45. package/api/gap-report/gap-report.doc.mjs +11 -3
  46. package/api/gap-report/gap-report.mjs +152 -48
  47. package/api/gap-report/gap-report.test.mjs +233 -0
  48. package/api/gap-report/gap-report.type.d.mts +41 -0
  49. package/api/gap-report/gap-report.type.mjs +18 -0
  50. package/api/hook/_adapter.mjs +3 -11
  51. package/api/hook/hook.doc.mjs +4 -0
  52. package/api/hook/list/list.d.mts +1 -1
  53. package/api/init/_adapter.d.mts +22 -0
  54. package/api/init/_adapter.mjs +59 -0
  55. package/api/init/init.d.mts +7 -5
  56. package/api/init/init.doc.mjs +28 -7
  57. package/api/init/init.mjs +28 -4
  58. package/api/init/init.type.d.mts +48 -3
  59. package/api/init/init.type.mjs +37 -3
  60. package/api/init/run/run.d.mts +1 -1
  61. package/api/init/run/run.mjs +15 -4
  62. package/api/init/user/user.d.mts +14 -0
  63. package/api/init/user/user.mjs +87 -0
  64. package/api/init/user/user.test.mjs +232 -0
  65. package/api/integration/_adapter.d.mts +33 -0
  66. package/api/integration/_adapter.mjs +58 -0
  67. package/api/integration/authoring-checks.mjs +100 -5
  68. package/api/integration/authoring-checks.test.mjs +769 -0
  69. package/api/integration/authoring-checks.type.d.mts +134 -0
  70. package/api/integration/authoring-checks.type.mjs +60 -1
  71. package/api/integration/integrationCheck.doc.mjs +1 -1
  72. package/api/integration/integrationComponentConflicts.doc.mjs +11 -3
  73. package/api/integration/integrationDocConflicts.doc.mjs +22 -5
  74. package/api/integration/pack-check.mjs +303 -13
  75. package/api/integration/pack-check.test.mjs +371 -10
  76. package/api/integration/validate-integration-fixes.test.mjs +67 -0
  77. package/api/integration/validate-integration.d.mts +8 -0
  78. package/api/integration/validate-integration.mjs +55 -9
  79. package/api/integration/validate-integration.test.mjs +77 -8
  80. package/api/integration/validate-integration.type.d.mts +8 -0
  81. package/api/integration/validate-integration.type.mjs +3 -1
  82. package/api/integration/validate-unread-theme-folders.test.mjs +3 -2
  83. package/api/integration/validateIntegration.doc.mjs +4 -2
  84. package/api/search/_adapter.d.mts +11 -0
  85. package/api/search/_adapter.mjs +25 -0
  86. package/api/search/search.d.mts +73 -13
  87. package/api/search/search.doc.mjs +8 -3
  88. package/api/search/search.domain-priority.test.mjs +108 -18
  89. package/api/search/search.mjs +541 -52
  90. package/api/search/search.test.mjs +143 -1
  91. package/api/search/search.type.d.mts +14 -1
  92. package/api/search/search.type.mjs +6 -1
  93. package/api/swizzle/_adapter.mjs +2 -2
  94. package/api/swizzle/copy/copy.mjs +1 -1
  95. package/api/template/template-integration.test.mjs +1 -1
  96. package/api/theme/_adapter.d.mts +16 -0
  97. package/api/theme/_adapter.mjs +32 -5
  98. package/api/theme/build/build.d.mts +2 -1
  99. package/api/theme/build/build.mjs +70 -14
  100. package/api/theme/build/build.packed-old-core.test.mjs +20 -3
  101. package/api/theme/build/font-warning.mjs +5 -3
  102. package/api/theme/build/font-warning.test.mjs +2 -1
  103. package/api/theme/remove/remove.test.mjs +2 -2
  104. package/api/theme/targets/targets.mjs +3 -10
  105. package/api/theme/themeTargets.doc.mjs +4 -0
  106. package/api/theme/use/use.test.mjs +1 -1
  107. package/api/upgrade/_adapter.d.mts +40 -2
  108. package/api/upgrade/_adapter.mjs +118 -5
  109. package/api/upgrade/run/cli-behind-core.test.mjs +438 -0
  110. package/api/upgrade/run/run.mjs +35 -2
  111. package/api/upgrade/status/status.d.mts +21 -5
  112. package/api/upgrade/status/status.mjs +45 -8
  113. package/api/upgrade/upgrade.doc.mjs +6 -3
  114. package/api/upgrade/upgrade.mjs +5 -1
  115. package/api/upgrade/upgrade.type.d.mts +23 -0
  116. package/api/upgrade/upgrade.type.mjs +16 -1
  117. package/assets/codemods/__tests__/registry.test.mjs +1 -0
  118. package/assets/codemods/registry.mjs +1 -0
  119. package/assets/codemods/runner.mjs +7 -1
  120. package/assets/codemods/transforms/next/index.mjs +1 -11
  121. package/assets/codemods/transforms/v0.7.2/index.mjs +20 -0
  122. package/assets/discover/official-packages.json +367 -0
  123. package/assets/docs/author-a-theme.doc.mjs +2 -2
  124. package/assets/docs/browser-support.doc.mjs +1 -1
  125. package/assets/docs/getting-started.doc.mjs +23 -3
  126. package/assets/docs/styling-overview.doc.mjs +1 -1
  127. package/assets/docs/theme.doc.mjs +1 -1
  128. package/assets/docs/tree/add-a-component.doc.mjs +12 -2
  129. package/assets/docs/tree/add-a-theme.doc.mjs +13 -3
  130. package/assets/docs/tree/add-a-topic.doc.mjs +54 -9
  131. package/assets/docs/tree/agent-guidance.doc.mjs +19 -5
  132. package/assets/docs/tree/block-template.doc.mjs +6 -18
  133. package/assets/docs/tree/build-the-template.doc.mjs +1 -1
  134. package/assets/docs/tree/building-blocks.doc.mjs +4 -0
  135. package/assets/docs/tree/check-the-props.doc.mjs +8 -3
  136. package/assets/docs/tree/check-your-docs.doc.mjs +30 -13
  137. package/assets/docs/tree/checks.doc.mjs +17 -17
  138. package/assets/docs/tree/ci-and-containers.doc.mjs +88 -0
  139. package/assets/docs/tree/cli-command.doc.mjs +142 -0
  140. package/assets/docs/tree/cli.doc.mjs +6 -0
  141. package/assets/docs/tree/codemods.doc.mjs +17 -7
  142. package/assets/docs/tree/commands.doc.mjs +7 -1
  143. package/assets/docs/tree/component-family.doc.mjs +3 -5
  144. package/assets/docs/tree/component-imports.doc.mjs +13 -3
  145. package/assets/docs/tree/components.doc.mjs +7 -1
  146. package/assets/docs/tree/computer-agent-guide.doc.mjs +160 -0
  147. package/assets/docs/tree/computer-integrations.doc.mjs +93 -0
  148. package/assets/docs/tree/configuration.doc.mjs +7 -3
  149. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +41 -22
  150. package/assets/docs/tree/define-the-theme.doc.mjs +6 -5
  151. package/assets/docs/tree/describe-the-component.doc.mjs +2 -2
  152. package/assets/docs/tree/docs.doc.mjs +9 -3
  153. package/assets/docs/tree/document-the-template.doc.mjs +1 -1
  154. package/assets/docs/tree/document-the-theme.doc.mjs +3 -3
  155. package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
  156. package/assets/docs/tree/extend-or-replace.doc.mjs +19 -9
  157. package/assets/docs/tree/fonts-and-assets.doc.mjs +2 -2
  158. package/assets/docs/tree/generate-a-palette.doc.mjs +7 -5
  159. package/assets/docs/tree/global-integrations.doc.mjs +124 -0
  160. package/assets/docs/tree/global.doc.mjs +131 -0
  161. package/assets/docs/tree/grade-template-with-agent.doc.mjs +5 -5
  162. package/assets/docs/tree/install-once.doc.mjs +105 -0
  163. package/assets/docs/tree/integrations.doc.mjs +13 -1
  164. package/assets/docs/tree/layout-spacing.doc.mjs +2 -3
  165. package/assets/docs/tree/layout.doc.mjs +6 -6
  166. package/assets/docs/tree/links.doc.mjs +6 -5
  167. package/assets/docs/tree/migration-setup.doc.mjs +3 -2
  168. package/assets/docs/tree/migration-verification.doc.mjs +0 -1
  169. package/assets/docs/tree/migration.doc.mjs +0 -1
  170. package/assets/docs/tree/monorepos.doc.mjs +84 -0
  171. package/assets/docs/tree/package-and-test.doc.mjs +1 -1
  172. package/assets/docs/tree/page-template.doc.mjs +3 -7
  173. package/assets/docs/tree/problems.doc.mjs +205 -0
  174. package/assets/docs/tree/project-global-or-both.doc.mjs +131 -0
  175. package/assets/docs/tree/publishing.doc.mjs +6 -6
  176. package/assets/docs/tree/quick-start.doc.mjs +52 -26
  177. package/assets/docs/tree/reference.doc.mjs +192 -0
  178. package/assets/docs/tree/replace-a-core-component.doc.mjs +15 -7
  179. package/assets/docs/tree/replace-a-core-template.doc.mjs +9 -11
  180. package/assets/docs/tree/scaffold.doc.mjs +1 -1
  181. package/assets/docs/tree/sections-and-placement.doc.mjs +9 -11
  182. package/assets/docs/tree/see-it-in-an-app.doc.mjs +17 -5
  183. package/assets/docs/tree/short-and-findable.doc.mjs +9 -8
  184. package/assets/docs/tree/single-component.doc.mjs +14 -6
  185. package/assets/docs/tree/start-a-template.doc.mjs +18 -8
  186. package/assets/docs/tree/subcomponent.doc.mjs +6 -6
  187. package/assets/docs/tree/template-assets.doc.mjs +2 -2
  188. package/assets/docs/tree/template-doc-overview.doc.mjs +6 -17
  189. package/assets/docs/tree/template-fonts.doc.mjs +1 -1
  190. package/assets/docs/tree/template-icons.doc.mjs +1 -1
  191. package/assets/docs/tree/template-images-media.doc.mjs +1 -1
  192. package/assets/docs/tree/template-styles.doc.mjs +2 -2
  193. package/assets/docs/tree/templates.doc.mjs +7 -1
  194. package/assets/docs/tree/test-in-an-app.doc.mjs +7 -7
  195. package/assets/docs/tree/test-template-in-app.doc.mjs +5 -5
  196. package/assets/docs/tree/themes.doc.mjs +4 -0
  197. package/assets/docs/tree/tokens-and-setup.doc.mjs +19 -15
  198. package/assets/docs/tree/troubleshooting.doc.mjs +22 -14
  199. package/assets/docs/tree/uninstall.doc.mjs +109 -0
  200. package/assets/docs/tree/updates.doc.mjs +92 -0
  201. package/assets/docs/tree/upgrading.doc.mjs +6 -6
  202. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +14 -2
  203. package/assets/docs/tree/verify-packed-template.doc.mjs +3 -3
  204. package/assets/docs/tree/versioning.doc.mjs +9 -9
  205. package/assets/docs/tree/who-answers.doc.mjs +119 -0
  206. package/assets/docs/tree/write-the-template-file.doc.mjs +5 -5
  207. package/assets/docs/working-with-ai.doc.mjs +23 -9
  208. package/authoring/codemod/codemod.doc.mjs +7 -3
  209. package/authoring/config/config.doc.mjs +9 -6
  210. package/authoring/config/type.ts +4 -2
  211. package/authoring/debug/parse.d.mts +5 -5
  212. package/authoring/discover/discover.doc.mjs +7 -1
  213. package/authoring/doctypes/_schema.d.mts +8 -3
  214. package/authoring/doctypes/_schema.mjs +1 -0
  215. package/authoring/doctypes/base/graph-fields.doc.mjs +6 -4
  216. package/authoring/doctypes/command/command.doc.mjs +20 -12
  217. package/authoring/doctypes/command/type.ts +14 -0
  218. package/authoring/doctypes/component/component.doc.mjs +29 -26
  219. package/authoring/doctypes/component/type.ts +15 -5
  220. package/authoring/doctypes/enum/enum.doc.mjs +7 -4
  221. package/authoring/doctypes/function/function.doc.mjs +9 -8
  222. package/authoring/doctypes/hook/hook.doc.mjs +5 -4
  223. package/authoring/doctypes/hook/type.ts +4 -0
  224. package/authoring/doctypes/namespace/namespace.doc.mjs +7 -6
  225. package/authoring/doctypes/reference/reference.doc.mjs +12 -11
  226. package/authoring/doctypes/schema/schema.doc.mjs +9 -9
  227. package/authoring/doctypes/template/template.doc.mjs +2 -2
  228. package/authoring/doctypes/theme/theme.doc.mjs +2 -2
  229. package/authoring/gap-report/gap-report.doc.mjs +2 -2
  230. package/authoring/identity/identity.doc.mjs +3 -3
  231. package/authoring/index.d.ts +1 -0
  232. package/authoring/integration/integration.doc.mjs +22 -5
  233. package/authoring/integration/parse.test.mjs +11 -0
  234. package/authoring/integration/schema.d.mts +14 -3
  235. package/authoring/integration/schema.mjs +33 -2
  236. package/authoring/integration/type.ts +8 -0
  237. package/clients/cli/bin/astryx.mjs +20 -3
  238. package/clients/cli/cli-command.test.mjs +166 -0
  239. package/clients/cli/commands/blog.doc.mjs +1 -0
  240. package/clients/cli/commands/build-theme.batch-report.test.mjs +14 -2
  241. package/clients/cli/commands/build-theme.font-warning.test.mjs +3 -3
  242. package/clients/cli/commands/build-theme.mjs +25 -12
  243. package/clients/cli/commands/build.doc.mjs +1 -0
  244. package/clients/cli/commands/build.exit-codes-doc.test.mjs +4 -0
  245. package/clients/cli/commands/cli-postinstall.test.mjs +49 -1
  246. package/clients/cli/commands/component.doc.mjs +1 -0
  247. package/clients/cli/commands/discover.broken-integration.test.mjs +11 -7
  248. package/clients/cli/commands/discover.doc.mjs +9 -1
  249. package/clients/cli/commands/discover.mjs +144 -93
  250. package/clients/cli/commands/discover.no-source.test.mjs +448 -33
  251. package/clients/cli/commands/discover.sources.test.mjs +20 -1
  252. package/clients/cli/commands/docs.doc.mjs +1 -0
  253. package/clients/cli/commands/docs.mjs +49 -19
  254. package/clients/cli/commands/docs.test.mjs +2 -2
  255. package/clients/cli/commands/doctor-integration-components.doc.mjs +7 -3
  256. package/clients/cli/commands/doctor-integration-docs.doc.mjs +20 -5
  257. package/clients/cli/commands/doctor-integration-templates.doc.mjs +1 -0
  258. package/clients/cli/commands/doctor-integration-validate.doc.mjs +13 -2
  259. package/clients/cli/commands/doctor-integration.test.mjs +52 -4
  260. package/clients/cli/commands/doctor.doc.mjs +12 -2
  261. package/clients/cli/commands/doctor.mjs +102 -7
  262. package/clients/cli/commands/doctor.test.mjs +10 -4
  263. package/clients/cli/commands/gap-report.doc.mjs +15 -2
  264. package/clients/cli/commands/gap-report.mjs +24 -2
  265. package/clients/cli/commands/gap-report.test.mjs +79 -0
  266. package/clients/cli/commands/hook.doc.mjs +1 -0
  267. package/clients/cli/commands/init.behavior.test.mjs +5 -0
  268. package/clients/cli/commands/init.doc.mjs +29 -3
  269. package/clients/cli/commands/integration-add.doc.mjs +7 -0
  270. package/clients/cli/commands/integration-authoring.test.mjs +49 -3
  271. package/clients/cli/commands/integration-check.doc.mjs +10 -2
  272. package/clients/cli/commands/integration-check.test.mjs +175 -1
  273. package/clients/cli/commands/integration-pack.doc.mjs +8 -1
  274. package/clients/cli/commands/integration-verify.doc.mjs +8 -1
  275. package/clients/cli/commands/integration.mjs +15 -9
  276. package/clients/cli/commands/json-contract.test.mjs +11 -2
  277. package/clients/cli/commands/manifest.doc.mjs +1 -0
  278. package/clients/cli/commands/outside-project.test.mjs +774 -0
  279. package/clients/cli/commands/search.doc.mjs +1 -0
  280. package/clients/cli/commands/search.mjs +16 -11
  281. package/clients/cli/commands/search.test.mjs +429 -296
  282. package/clients/cli/commands/setup-nudge.test.mjs +102 -2
  283. package/clients/cli/commands/swizzle.doc.mjs +7 -0
  284. package/clients/cli/commands/swizzle.mjs +5 -3
  285. package/clients/cli/commands/swizzle.routing.test.mjs +19 -2
  286. package/clients/cli/commands/template.doc.mjs +1 -0
  287. package/clients/cli/commands/theme-add-lifecycle.test.mjs +3 -3
  288. package/clients/cli/commands/theme-add.doc.mjs +8 -1
  289. package/clients/cli/commands/theme-build.doc.mjs +1 -0
  290. package/clients/cli/commands/theme-eject.doc.mjs +1 -0
  291. package/clients/cli/commands/theme-list.behavior.test.mjs +6 -0
  292. package/clients/cli/commands/theme-list.doc.mjs +1 -0
  293. package/clients/cli/commands/theme-palette-generate.doc.mjs +1 -0
  294. package/clients/cli/commands/theme-remove.doc.mjs +7 -0
  295. package/clients/cli/commands/theme-targets.doc.mjs +1 -0
  296. package/clients/cli/commands/theme-template.doc.mjs +1 -0
  297. package/clients/cli/commands/theme-use.doc.mjs +7 -0
  298. package/clients/cli/commands/upgrade.config-ordering.test.mjs +2 -1
  299. package/clients/cli/commands/upgrade.doc.mjs +22 -1
  300. package/clients/cli/commands/upgrade.file-protection.test.mjs +3 -2
  301. package/clients/cli/commands/upgrade.hook-output.test.mjs +2 -1
  302. package/clients/cli/commands/upgrade.older-cli.test.mjs +160 -0
  303. package/clients/cli/index.mjs +111 -16
  304. package/clients/cli/lib/cli-error.mjs +4 -1
  305. package/clients/cli/lib/component-format.test.mjs +1 -1
  306. package/clients/cli/lib/define-command.mjs +2 -2
  307. package/clients/cli/lib/json-shim.mjs +3 -1
  308. package/clients/cli/lib/manifest.d.ts +9 -0
  309. package/clients/cli/lib/manifest.mjs +17 -1
  310. package/clients/cli/lib/manifest.test.mjs +25 -0
  311. package/foundation/agent-docs/agent-doc-state.d.mts +35 -6
  312. package/foundation/agent-docs/agent-doc-state.mjs +66 -6
  313. package/foundation/agent-docs/agent-docs.d.mts +4 -3
  314. package/foundation/agent-docs/agent-docs.mjs +11 -9
  315. package/foundation/agent-docs/computer-agent-text.d.mts +133 -0
  316. package/foundation/agent-docs/computer-agent-text.mjs +623 -0
  317. package/foundation/agent-docs/computer-agent-text.test.mjs +602 -0
  318. package/foundation/config/project.d.mts +35 -3
  319. package/foundation/config/project.mjs +145 -8
  320. package/foundation/config/project.test.mjs +66 -0
  321. package/foundation/config/theme-state.mjs +5 -4
  322. package/foundation/discovery/authoring-self-docs.mjs +1 -1
  323. package/foundation/discovery/cli-self-docs.mjs +20 -3
  324. package/foundation/discovery/cli-self-docs.test.mjs +3 -2
  325. package/foundation/discovery/component-replacement.mjs +2 -1
  326. package/foundation/discovery/copied-passages.d.mts +216 -0
  327. package/foundation/discovery/copied-passages.mjs +720 -0
  328. package/foundation/discovery/copied-passages.test.mjs +124 -0
  329. package/foundation/discovery/doc-consistency.d.mts +34 -0
  330. package/foundation/discovery/doc-consistency.mjs +513 -0
  331. package/foundation/discovery/doc-consistency.test.mjs +347 -0
  332. package/foundation/discovery/doc-fluff.d.mts +112 -0
  333. package/foundation/discovery/doc-fluff.mjs +328 -0
  334. package/foundation/discovery/doc-fluff.test.mjs +142 -0
  335. package/foundation/discovery/doc-types.d.mts +1 -1
  336. package/foundation/discovery/doc-types.mjs +15 -8
  337. package/foundation/discovery/doc-types.test.mjs +94 -2
  338. package/foundation/discovery/docs-discovery.d.mts +7 -0
  339. package/foundation/discovery/docs-discovery.mjs +26 -2
  340. package/foundation/discovery/project-typescript.d.mts +11 -2
  341. package/foundation/discovery/project-typescript.mjs +32 -4
  342. package/foundation/discovery/prop-drift.d.mts +26 -7
  343. package/foundation/discovery/prop-drift.mjs +173 -8
  344. package/foundation/discovery/prop-drift.test.mjs +120 -2
  345. package/foundation/discovery/runner-allowlist.d.mts +40 -0
  346. package/foundation/discovery/runner-allowlist.mjs +53 -0
  347. package/foundation/discovery/stray-pointers.d.mts +52 -0
  348. package/foundation/discovery/stray-pointers.mjs +293 -0
  349. package/foundation/discovery/stray-pointers.test.mjs +86 -0
  350. package/foundation/discovery/template-needs.mjs +5 -2
  351. package/foundation/discovery/theme-imports.mjs +8 -4
  352. package/foundation/discovery/theme-imports.test.mjs +1 -1
  353. package/foundation/discovery/type-text.mjs +1 -1
  354. package/foundation/discovery/type-text.test.mjs +1 -1
  355. package/foundation/doc-compiler/doc-loads.test.mjs +42 -2
  356. package/foundation/doc-compiler/inputs.mjs +4 -1
  357. package/foundation/doc-compiler/links.d.mts +32 -0
  358. package/foundation/doc-compiler/links.mjs +163 -26
  359. package/foundation/doc-compiler/links.test.mjs +97 -4
  360. package/foundation/doc-compiler/read.d.mts +1 -1
  361. package/foundation/doc-compiler/tree.mjs +30 -1
  362. package/foundation/doc-compiler/tree.test.mjs +8 -1
  363. package/foundation/env/cli-command.d.mts +83 -0
  364. package/foundation/env/cli-command.mjs +152 -0
  365. package/foundation/env/cli-command.test.mjs +129 -0
  366. package/foundation/env/node-version.test.mjs +15 -3
  367. package/foundation/env/package-manager.d.mts +93 -9
  368. package/foundation/env/package-manager.global.test.mjs +66 -0
  369. package/foundation/env/package-manager.mjs +184 -13
  370. package/foundation/env/package-manager.test.mjs +341 -15
  371. package/foundation/env/project-scope.d.mts +9 -0
  372. package/foundation/env/project-scope.mjs +33 -0
  373. package/foundation/env/search-path.d.mts +42 -0
  374. package/foundation/env/search-path.mjs +205 -0
  375. package/foundation/env/search-path.test.mjs +122 -0
  376. package/foundation/env/user-dirs.d.mts +39 -0
  377. package/foundation/env/user-dirs.mjs +93 -0
  378. package/foundation/env/user-dirs.test.mjs +72 -0
  379. package/foundation/env/user-settings.d.mts +46 -0
  380. package/foundation/env/user-settings.mjs +106 -0
  381. package/foundation/env/user-settings.test.mjs +64 -0
  382. package/foundation/fs/file-protection.mjs +15 -1
  383. package/foundation/fs/module-loader.d.mts +2 -1
  384. package/foundation/fs/module-loader.mjs +60 -4
  385. package/foundation/fs/module-loader.stdout.test.mjs +3 -2
  386. package/foundation/fs/module-loader.test.mjs +194 -0
  387. package/foundation/fs/path-compare.d.mts +33 -0
  388. package/foundation/fs/path-compare.mjs +73 -0
  389. package/foundation/fs/paths.d.mts +57 -1
  390. package/foundation/fs/paths.mjs +131 -3
  391. package/foundation/fs/paths.test.mjs +10 -1
  392. package/foundation/integrations/computer-integrations.d.mts +78 -0
  393. package/foundation/integrations/computer-integrations.mjs +316 -0
  394. package/foundation/integrations/computer-integrations.test.mjs +292 -0
  395. package/foundation/integrations/contribution-inventory.d.mts +11 -0
  396. package/foundation/integrations/contribution-inventory.mjs +50 -2
  397. package/foundation/integrations/integration-warnings.mjs +3 -2
  398. package/foundation/integrations/integration-warnings.test.mjs +8 -9
  399. package/foundation/integrations/integrations.d.mts +25 -1
  400. package/foundation/integrations/integrations.mjs +46 -3
  401. package/foundation/integrations/issue.ts +6 -0
  402. package/foundation/integrations/validate-contributions.d.mts +2 -0
  403. package/foundation/integrations/validate-contributions.mjs +41 -1
  404. package/foundation/release/agent-text-worker.d.mts +4 -0
  405. package/foundation/release/agent-text-worker.mjs +52 -0
  406. package/foundation/release/cache.d.mts +310 -0
  407. package/foundation/release/cache.mjs +1216 -0
  408. package/foundation/release/cache.test.mjs +920 -0
  409. package/foundation/release/choose.d.mts +207 -0
  410. package/foundation/release/choose.mjs +677 -0
  411. package/foundation/release/choose.test.mjs +427 -0
  412. package/foundation/release/core-worker.d.mts +4 -0
  413. package/foundation/release/core-worker.mjs +38 -0
  414. package/foundation/release/global-state.d.mts +259 -0
  415. package/foundation/release/global-state.mjs +472 -0
  416. package/foundation/release/global-state.test.mjs +381 -0
  417. package/foundation/release/handoff.d.mts +60 -0
  418. package/foundation/release/handoff.e2e.test.mjs +278 -0
  419. package/foundation/release/handoff.mjs +357 -0
  420. package/foundation/release/lock-lease.d.mts +23 -0
  421. package/foundation/release/lock-lease.mjs +80 -0
  422. package/foundation/release/notices.d.mts +253 -0
  423. package/foundation/release/notices.mjs +596 -0
  424. package/foundation/release/notices.test.mjs +565 -0
  425. package/foundation/release/npm-guard.d.mts +4 -0
  426. package/foundation/release/npm-guard.mjs +126 -0
  427. package/foundation/release/project.d.mts +141 -0
  428. package/foundation/release/project.mjs +466 -0
  429. package/foundation/release/project.test.mjs +288 -0
  430. package/foundation/release/refresh-worker.d.mts +4 -0
  431. package/foundation/release/refresh-worker.mjs +27 -0
  432. package/foundation/release/run-context.d.mts +47 -0
  433. package/foundation/release/run-context.mjs +149 -0
  434. package/foundation/release/update-notice-env.d.mts +28 -0
  435. package/foundation/release/update-notice-env.mjs +39 -0
  436. package/foundation/response/base.d.ts +26 -3
  437. package/foundation/response/error-codes.d.mts +3 -1
  438. package/foundation/response/error-codes.doc.mjs +12 -0
  439. package/foundation/response/error-codes.mjs +17 -0
  440. package/foundation/response/error-codes.test.mjs +2 -0
  441. package/foundation/response/json-contract.test.mjs +7 -2
  442. package/foundation/response/json.mjs +4 -1
  443. package/foundation/response/response-types.doc.mjs +17 -11
  444. package/foundation/response/response-types.doc.test.mjs +46 -0
  445. package/foundation/response/response.doc.mjs +3 -3
  446. package/foundation/xle/browser-imports.test.mjs +74 -0
  447. package/package.json +11 -11
  448. package/scripts/postinstall.mjs +24 -5
  449. /package/assets/codemods/transforms/{next → v0.7.2}/__tests__/migrate-list-edge-compensation-to-edge.test.mjs +0 -0
  450. /package/assets/codemods/transforms/{next → v0.7.2}/migrate-list-edge-compensation-to-edge.mjs +0 -0
@@ -29,6 +29,8 @@
29
29
  * @property {string} target the target as written
30
30
  * @property {string} message
31
31
  * @property {string} [section] the key of the section the link sits in
32
+ * @property {string} [field] the field the link sits in, outside any section:
33
+ * `description`, or a path such as `props[2].description`
32
34
  * @property {true} [include] the problem is in a `reference` block, which
33
35
  * includes content rather than linking to it: a reader loses that content,
34
36
  * so the authoring check fails on it
@@ -55,6 +57,31 @@ export function parseLinkTarget(target: unknown): LinkTarget | {
55
57
  * @returns {string}
56
58
  */
57
59
  export function unlinkText(text: string): string;
60
+ /**
61
+ * Every text field of a doc object, in authored order, by the same rules
62
+ * {@link linkFields} resolves links by: the strings a reader reads, never a
63
+ * name, type, default, or code.
64
+ * @param {unknown} doc
65
+ * @returns {{field: string, text: string}[]}
66
+ */
67
+ export function textFields(doc: unknown): {
68
+ field: string;
69
+ text: string;
70
+ }[];
71
+ /**
72
+ * Resolve every inline link in the text fields of a doc object, such as a
73
+ * component doc or a topic's summary fields (spec:AST-047 FR9): its
74
+ * description, its usage and best practices, each prop's and example's
75
+ * description, at any depth. Code and code ticks show link syntax as written.
76
+ * @template T
77
+ * @param {T} doc
78
+ * @param {LinkResolver} resolve
79
+ * @returns {Promise<{doc: T, problems: LinkProblem[]}>}
80
+ */
81
+ export function linkFields<T>(doc: T, resolve: LinkResolver): Promise<{
82
+ doc: T;
83
+ problems: LinkProblem[];
84
+ }>;
58
85
  /**
59
86
  * Resolve every link in a list of content blocks, in order.
60
87
  * @param {any[]} blocks
@@ -148,6 +175,11 @@ export type LinkProblem = {
148
175
  * the key of the section the link sits in
149
176
  */
150
177
  section?: string | undefined;
178
+ /**
179
+ * the field the link sits in, outside any section:
180
+ * `description`, or a path such as `props[2].description`
181
+ */
182
+ field?: string | undefined;
151
183
  /**
152
184
  * the problem is in a `reference` block, which
153
185
  * includes content rather than linking to it: a reader loses that content,
@@ -65,6 +65,8 @@ const CODE_SPAN = /(`[^`]*`)/;
65
65
  * @property {string} target the target as written
66
66
  * @property {string} message
67
67
  * @property {string} [section] the key of the section the link sits in
68
+ * @property {string} [field] the field the link sits in, outside any section:
69
+ * `description`, or a path such as `props[2].description`
68
70
  * @property {true} [include] the problem is in a `reference` block, which
69
71
  * includes content rather than linking to it: a reader loses that content,
70
72
  * so the authoring check fails on it
@@ -91,7 +93,11 @@ export function parseLinkTarget(target) {
91
93
  };
92
94
  }
93
95
  const parts = target.trim().split(':');
94
- if (parts.length < 2 || parts.length > 3 || parts.some(p => p.trim() === '')) {
96
+ if (
97
+ parts.length < 2 ||
98
+ parts.length > 3 ||
99
+ parts.some(p => p.trim() === '')
100
+ ) {
95
101
  return {error: `"${target}" is not \`[<provider>:]<kind>:<name>\``};
96
102
  }
97
103
  const provider = parts.length === 3 ? parts[0].trim() : null;
@@ -127,6 +133,159 @@ export function unlinkText(text) {
127
133
  .join('');
128
134
  }
129
135
 
136
+ /**
137
+ * Text with each inline link replaced by the command that opens its doc. A
138
+ * link written inside code ticks is shown as written: it documents the syntax,
139
+ * it does not link. A link that names no doc prints as written: text that was
140
+ * never meant as a link reads the way it did, and doctor names the problem.
141
+ * @param {unknown} text
142
+ * @param {(target: string) => Promise<DocLink | null>} find
143
+ * @returns {Promise<any>}
144
+ */
145
+ async function resolveInline(text, find) {
146
+ if (typeof text !== 'string' || !text.includes('{@link')) return text;
147
+ let out = '';
148
+ for (const part of text.split(CODE_SPAN)) {
149
+ if (part.startsWith('`')) {
150
+ out += part;
151
+ continue;
152
+ }
153
+ let last = 0;
154
+ for (const match of part.matchAll(INLINE_LINK)) {
155
+ const link = await find(match[1]);
156
+ const at = /** @type {number} */ (match.index);
157
+ out += part.slice(last, at);
158
+ out += link ? `\`${link.command}\`` : match[0];
159
+ last = at + match[0].length;
160
+ }
161
+ out += part.slice(last);
162
+ }
163
+ return out;
164
+ }
165
+
166
+ /**
167
+ * The fields of a doc that hold text a reader reads, where a link resolves:
168
+ * a description or summary, an example's label, and each value of a
169
+ * `propDescriptions` overlay. Every other string is a name, a type, a
170
+ * default, or code, and is shown as written.
171
+ */
172
+ const TEXT_FIELDS = new Set(['description', 'summary', 'label']);
173
+
174
+ /** Fields that hold code or data, never text: nothing below them links. */
175
+ const CODE_FIELDS = new Set([
176
+ 'code',
177
+ 'playground',
178
+ 'slotElements',
179
+ 'registry',
180
+ 'keywords',
181
+ ]);
182
+
183
+ /**
184
+ * Every text field of a doc object, in authored order, by the same rules
185
+ * {@link linkFields} resolves links by: the strings a reader reads, never a
186
+ * name, type, default, or code.
187
+ * @param {unknown} doc
188
+ * @returns {{field: string, text: string}[]}
189
+ */
190
+ export function textFields(doc) {
191
+ /** @type {{field: string, text: string}[]} */
192
+ const out = [];
193
+ /**
194
+ * @param {unknown} value
195
+ * @param {string} at
196
+ * @param {boolean} text
197
+ */
198
+ const walk = (value, at, text) => {
199
+ if (typeof value === 'string') {
200
+ if (text) out.push({field: at, text: value});
201
+ return;
202
+ }
203
+ if (Array.isArray(value)) {
204
+ value.forEach((item, i) => walk(item, `${at}[${i}]`, false));
205
+ return;
206
+ }
207
+ if (value == null || typeof value !== 'object') return;
208
+ for (const [key, field] of Object.entries(value)) {
209
+ const path = at === '' ? key : `${at}.${key}`;
210
+ if (CODE_FIELDS.has(key)) continue;
211
+ if (key === 'propDescriptions' && field && typeof field === 'object') {
212
+ for (const [prop, description] of Object.entries(field)) {
213
+ walk(description, `${path}.${prop}`, true);
214
+ }
215
+ } else {
216
+ walk(field, path, TEXT_FIELDS.has(key));
217
+ }
218
+ }
219
+ };
220
+ walk(doc, '', false);
221
+ return out;
222
+ }
223
+
224
+ /**
225
+ * Resolve every inline link in the text fields of a doc object, such as a
226
+ * component doc or a topic's summary fields (spec:AST-047 FR9): its
227
+ * description, its usage and best practices, each prop's and example's
228
+ * description, at any depth. Code and code ticks show link syntax as written.
229
+ * @template T
230
+ * @param {T} doc
231
+ * @param {LinkResolver} resolve
232
+ * @returns {Promise<{doc: T, problems: LinkProblem[]}>}
233
+ */
234
+ export async function linkFields(doc, resolve) {
235
+ /** @type {LinkProblem[]} */
236
+ const problems = [];
237
+ /**
238
+ * @param {unknown} value
239
+ * @param {string} at the field path, e.g. `usage.bestPractices[0].description`
240
+ * @param {boolean} text whether a string here is text
241
+ * @returns {Promise<any>}
242
+ */
243
+ const walk = async (value, at, text) => {
244
+ if (typeof value === 'string') {
245
+ if (!text) return value;
246
+ return resolveInline(value, async target => {
247
+ const found = await resolve(target);
248
+ if ('problem' in found) {
249
+ problems.push({target, message: found.problem, field: at});
250
+ return null;
251
+ }
252
+ return found;
253
+ });
254
+ }
255
+ if (Array.isArray(value)) {
256
+ const out = [];
257
+ for (let i = 0; i < value.length; i++) {
258
+ out.push(await walk(value[i], `${at}[${i}]`, false));
259
+ }
260
+ return out;
261
+ }
262
+ if (value == null || typeof value !== 'object') return value;
263
+ /** @type {Record<string, unknown>} */
264
+ const out = {};
265
+ for (const [key, field] of Object.entries(value)) {
266
+ const path = at === '' ? key : `${at}.${key}`;
267
+ if (CODE_FIELDS.has(key)) {
268
+ out[key] = field;
269
+ } else if (
270
+ key === 'propDescriptions' &&
271
+ field &&
272
+ typeof field === 'object'
273
+ ) {
274
+ /** @type {Record<string, unknown>} */
275
+ const described = {};
276
+ for (const [prop, description] of Object.entries(field)) {
277
+ described[prop] = await walk(description, `${path}.${prop}`, true);
278
+ }
279
+ out[key] = described;
280
+ } else {
281
+ out[key] = await walk(field, path, TEXT_FIELDS.has(key));
282
+ }
283
+ }
284
+ return out;
285
+ };
286
+ return {doc: await walk(doc, '', false), problems};
287
+ }
288
+
130
289
  /**
131
290
  * Resolve every link in a list of content blocks, in order.
132
291
  * @param {any[]} blocks
@@ -156,30 +315,7 @@ export async function linkBlocks(blocks, resolve, at = {}, include) {
156
315
  * @param {unknown} text
157
316
  * @returns {Promise<any>}
158
317
  */
159
- const linkText = async text => {
160
- if (typeof text !== 'string' || !text.includes('{@link')) return text;
161
- let out = '';
162
- // A link written inside code ticks is shown as written: it documents the
163
- // syntax, it does not link.
164
- for (const part of text.split(CODE_SPAN)) {
165
- if (part.startsWith('`')) {
166
- out += part;
167
- continue;
168
- }
169
- let last = 0;
170
- for (const match of part.matchAll(INLINE_LINK)) {
171
- const link = await find(match[1]);
172
- const at = /** @type {number} */ (match.index);
173
- out += part.slice(last, at);
174
- // A link that names no doc prints as written: text that was never
175
- // meant as a link reads the way it did, and doctor names the problem.
176
- out += link ? `\`${link.command}\`` : match[0];
177
- last = at + match[0].length;
178
- }
179
- out += part.slice(last);
180
- }
181
- return out;
182
- };
318
+ const linkText = text => resolveInline(text, find);
183
319
  /**
184
320
  * @param {unknown[]} values
185
321
  * @returns {Promise<any[]>}
@@ -251,7 +387,8 @@ export async function linkBlocks(blocks, resolve, at = {}, include) {
251
387
  }
252
388
  if (Array.isArray(step.references)) {
253
389
  const links = [];
254
- for (const target of step.references) links.push(await find(target));
390
+ for (const target of step.references)
391
+ links.push(await find(target));
255
392
  linked.links = links;
256
393
  }
257
394
  steps.push(linked);
@@ -8,8 +8,10 @@
8
8
  import {describe, expect, it} from 'vitest';
9
9
  import {
10
10
  linkBlocks,
11
+ linkFields,
11
12
  linkSections,
12
13
  parseLinkTarget,
14
+ textFields,
13
15
  unlinkText,
14
16
  } from './links.mjs';
15
17
 
@@ -58,14 +60,20 @@ describe('linkBlocks', () => {
58
60
  const {content, problems} = await linkBlocks(
59
61
  [
60
62
  {type: 'prose', text: 'Check it with {@link command:doctor}.'},
61
- {type: 'list', style: 'unordered', items: ['See {@link @acme/kit:generic:setup}']},
63
+ {
64
+ type: 'list',
65
+ style: 'unordered',
66
+ items: ['See {@link @acme/kit:generic:setup}'],
67
+ },
62
68
  {type: 'table', headers: ['Doc'], rows: [['{@link command:doctor}']]},
63
69
  {type: 'code', lang: 'js', code: '{@link command:doctor}'},
64
70
  ],
65
71
  resolve,
66
72
  );
67
73
  expect(problems).toEqual([]);
68
- expect(content[0].text).toBe('Check it with `astryx docs cli/commands/doctor`.');
74
+ expect(content[0].text).toBe(
75
+ 'Check it with `astryx docs cli/commands/doctor`.',
76
+ );
69
77
  expect(content[1].items).toEqual(['See `astryx docs acme/setup`']);
70
78
  expect(content[2].rows).toEqual([['`astryx docs cli/commands/doctor`']]);
71
79
  // Code is an example: it is shown as written.
@@ -74,7 +82,12 @@ describe('linkBlocks', () => {
74
82
 
75
83
  it('shows a link written in code ticks as written', async () => {
76
84
  const {content, problems} = await linkBlocks(
77
- [{type: 'prose', text: 'Write `{@link <target>}` to link; {@link command:doctor} checks it.'}],
85
+ [
86
+ {
87
+ type: 'prose',
88
+ text: 'Write `{@link <target>}` to link; {@link command:doctor} checks it.',
89
+ },
90
+ ],
78
91
  resolve,
79
92
  );
80
93
  expect(problems).toEqual([]);
@@ -172,7 +185,10 @@ describe('linkBlocks', () => {
172
185
  },
173
186
  ],
174
187
  resolve,
175
- ).then(({sections, problems}) => ({content: sections[0].content, problems}));
188
+ ).then(({sections, problems}) => ({
189
+ content: sections[0].content,
190
+ problems,
191
+ }));
176
192
  expect(content[0].text).toBe('See {@link command:nope}.');
177
193
  expect(content[1].link).toBeNull();
178
194
  expect(problems.map(p => [p.section, p.target])).toEqual([
@@ -190,3 +206,80 @@ describe('unlinkText', () => {
190
206
  );
191
207
  });
192
208
  });
209
+
210
+ describe('linkFields', () => {
211
+ /** A component doc with a link in every kind of text field, and in code. */
212
+ const DOC = {
213
+ name: 'AcmeButton',
214
+ description: 'Run {@link command:doctor} first.',
215
+ usage: {
216
+ description: 'Set it up: {@link @acme/kit:generic:setup}.',
217
+ bestPractices: [
218
+ {guidance: true, description: 'Check with {@link command:doctor}.'},
219
+ ],
220
+ },
221
+ props: [
222
+ {
223
+ name: 'tone',
224
+ type: '{@link command:doctor}',
225
+ default: "'{@link command:doctor}'",
226
+ description: 'Like `{@link command:doctor}`, or {@link generic:nope}.',
227
+ },
228
+ ],
229
+ propDescriptions: {tone: 'See {@link command:doctor}.'},
230
+ examples: [
231
+ {label: 'From {@link command:doctor}', code: '{@link command:doctor}'},
232
+ ],
233
+ playground: {defaults: {label: '{@link command:doctor}'}},
234
+ };
235
+
236
+ it('resolves each text field and leaves names, types, defaults, and code as written', async () => {
237
+ const {doc, problems} = await linkFields(DOC, resolve);
238
+ expect(doc).toEqual({
239
+ ...DOC,
240
+ description: 'Run `astryx docs cli/commands/doctor` first.',
241
+ usage: {
242
+ description: 'Set it up: `astryx docs acme/setup`.',
243
+ bestPractices: [
244
+ {
245
+ guidance: true,
246
+ description: 'Check with `astryx docs cli/commands/doctor`.',
247
+ },
248
+ ],
249
+ },
250
+ props: [
251
+ {
252
+ ...DOC.props[0],
253
+ description:
254
+ 'Like `{@link command:doctor}`, or {@link generic:nope}.',
255
+ },
256
+ ],
257
+ propDescriptions: {tone: 'See `astryx docs cli/commands/doctor`.'},
258
+ examples: [
259
+ {
260
+ label: 'From `astryx docs cli/commands/doctor`',
261
+ code: '{@link command:doctor}',
262
+ },
263
+ ],
264
+ });
265
+ expect(problems).toEqual([
266
+ {
267
+ target: 'generic:nope',
268
+ message: expect.any(String),
269
+ field: 'props[0].description',
270
+ },
271
+ ]);
272
+ });
273
+
274
+ it('names the same text fields textFields reads', async () => {
275
+ const {problems} = await linkFields(DOC, async target => ({
276
+ problem: `no ${target}`,
277
+ }));
278
+ const fields = textFields(DOC)
279
+ .filter(({text}) => text.replace(/`[^`]*`/g, '').includes('{@link'))
280
+ .map(({field}) => field);
281
+ expect([...new Set(problems.map(problem => problem.field))]).toEqual(
282
+ fields,
283
+ );
284
+ });
285
+ });
@@ -102,7 +102,7 @@ export type ReadOptions = {
102
102
  * how to import the module (default
103
103
  * `user`)
104
104
  */
105
- loader?: "template" | "native" | "user" | undefined;
105
+ loader?: "template" | "user" | "native" | undefined;
106
106
  /**
107
107
  * which exports name the doc, in
108
108
  * precedence order, for a reader that has always read a narrower set than its
@@ -242,7 +242,8 @@ export function buildDocsTree({namespaces, docs, topics = []}) {
242
242
  // without case, as topic names do, so `CLI` claims `cli`.
243
243
  const cliRoutes = new Set(topics.length > 0 ? [UNORGANIZED] : []);
244
244
  for (const topic of topics) {
245
- if (topic.provider === CLI_PROVIDER) cliRoutes.add(topic.name.toLowerCase());
245
+ if (topic.provider === CLI_PROVIDER)
246
+ cliRoutes.add(topic.name.toLowerCase());
246
247
  }
247
248
  // Every other name a topic answers to (the topics it replaced, directly or
248
249
  // through a chain) is that topic's route too: `astryx docs <name>` opens the
@@ -375,6 +376,34 @@ export function buildDocsTree({namespaces, docs, topics = []}) {
375
376
  return false;
376
377
  }
377
378
  const taken = byFoldedRoute.get(folded);
379
+ if (
380
+ taken &&
381
+ taken.provider === CLI_PROVIDER &&
382
+ node.provider !== CLI_PROVIDER &&
383
+ !node.generated
384
+ ) {
385
+ // Astryx's own doc keeps its route: name both docs as an author knows
386
+ // them, never by their ids, and say what to change.
387
+ const what = (/** @type {TreeNode} */ n) =>
388
+ n.kind === 'namespace'
389
+ ? 'namespace'
390
+ : n.kind === 'generic'
391
+ ? 'topic'
392
+ : `${n.kind} doc`;
393
+ const scope = String(node.provider).split('/').pop() ?? '';
394
+ const suggestion = `${
395
+ scope
396
+ .replace(/[^a-z0-9]+/gi, '-')
397
+ .replace(/^-|-$/g, '')
398
+ .toLowerCase() || 'my'
399
+ }-${node.name.toLowerCase()}`;
400
+ report(
401
+ 'duplicate_route',
402
+ at,
403
+ `The ${what(node)} "${node.name}" from ${node.provider} and Astryx's own ${what(taken)} "${taken.name}" both have the route "${node.route}", so \`astryx docs ${node.route}\` cannot open both. Fix: rename "${node.name}" (its \`name\` and its file), such as \`name: '${suggestion}'\`${node.route.includes('/') ? ', or place it elsewhere' : ''}.`,
404
+ );
405
+ return false;
406
+ }
378
407
  if (taken) {
379
408
  report(
380
409
  'duplicate_route',
@@ -171,6 +171,13 @@ describe('buildDocsTree', () => {
171
171
  ['duplicate_route', expect.stringContaining('takes the route "UNORGANIZED", which the CLI\'s own docs keep')],
172
172
  ]),
173
173
  );
174
+ // It names both docs as their authors know them, never by artifact id,
175
+ // and says what to change.
176
+ const taken = problems(tree).find(([, message]) => message.includes('route "CLI"'))?.[1];
177
+ expect(taken).toBe(
178
+ `The topic "CLI" from ${P} and Astryx's own namespace "cli" both have the route "CLI", so \`astryx docs CLI\` cannot open both. Fix: rename "CLI" (its \`name\` and its file), such as \`name: 'kit-cli'\`.`,
179
+ );
180
+ expect(taken).not.toContain('astryx:artifact');
174
181
  expect(tree.getFolded('THEME')?.provider).toBe(CLI);
175
182
  // A CLI topic whose own name has capitals keeps its route against a
176
183
  // lowercase namespace, although namespaces go into the tree first.
@@ -495,7 +502,7 @@ describe("the CLI's own docs tree", () => {
495
502
  async () => {
496
503
  const tree = await loadDocsTree({fresh: true});
497
504
  expect(tree.diagnostics).toEqual([]);
498
- expect(tree.roots().map(node => node.route)).toEqual(['cli', 'internationalization', 'layout', 'migration', 'styling', 'styling-libraries', 'tokens', 'typography']);
505
+ expect(tree.roots().map(node => node.route)).toEqual(['cli', 'global', 'internationalization', 'layout', 'migration', 'styling', 'styling-libraries', 'tokens', 'typography']);
499
506
  // Every slot child is a node one level below the namespace that places
500
507
  // it, and every node but the root is placed. Read from the tree, so a
501
508
  // restructure of the guides does not need this test edited.
@@ -0,0 +1,83 @@
1
+ // @generated by scripts/sync-api-types.mjs from the JSDoc in foundation/**/*.mjs.
2
+ // DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
3
+
4
+ /**
5
+ * Run `fn` with `command` as the door printed commands start with. The slot a
6
+ * host fills for one call. An integration's `cliCommand` still comes first,
7
+ * and conflicting ones still fall back to the guess.
8
+ * @template T
9
+ * @param {string} command e.g. `astryx` for a global launcher
10
+ * @param {() => T} fn
11
+ * @returns {T}
12
+ */
13
+ export function runWithCliDoor<T>(command: string, fn: () => T): T;
14
+ /**
15
+ * The door of the current call, if a host set one.
16
+ * @returns {string | undefined}
17
+ */
18
+ export function cliDoor(): string | undefined;
19
+ /**
20
+ * The commands a set of loaded integrations declares, in load order. An
21
+ * integration that failed to load, or was set aside for a provider conflict,
22
+ * contributes nothing, so it declares nothing either.
23
+ * @param {ReadonlyArray<{name?: string, __spec?: string, cliCommand?: string, __loadError?: string, __providerConflict?: unknown}>} integrations
24
+ * @returns {DeclaredCliCommand[]}
25
+ */
26
+ export function declaredCliCommandsOf(integrations: ReadonlyArray<{
27
+ name?: string;
28
+ __spec?: string;
29
+ cliCommand?: string;
30
+ __loadError?: string;
31
+ __providerConflict?: unknown;
32
+ }>): DeclaredCliCommand[];
33
+ /**
34
+ * Choose among declarations: one command, declared by one or more
35
+ * integrations, is used; different commands are a conflict.
36
+ * @param {DeclaredCliCommand[]} declared
37
+ * @returns {{command?: string, conflict: boolean, declared: DeclaredCliCommand[]}}
38
+ */
39
+ export function chooseDeclaredCliCommand(declared: DeclaredCliCommand[]): {
40
+ command?: string;
41
+ conflict: boolean;
42
+ declared: DeclaredCliCommand[];
43
+ };
44
+ /**
45
+ * Record the commands a project's loaded integrations declare. Called by
46
+ * `Project.load` each time it resolves the integration set; the latest load
47
+ * wins.
48
+ * @param {string} projectDir
49
+ * @param {Parameters<typeof declaredCliCommandsOf>[0]} integrations
50
+ * @returns {void}
51
+ */
52
+ export function recordDeclaredCliCommands(projectDir: string, integrations: Parameters<typeof declaredCliCommandsOf>[0]): void;
53
+ /**
54
+ * Whether this process has loaded the project at `targetDir`.
55
+ * @param {string} [targetDir=process.cwd()]
56
+ * @returns {boolean}
57
+ */
58
+ export function hasRecordedProject(targetDir?: string): boolean;
59
+ /**
60
+ * What the integrations of the project at `targetDir` declare, as recorded by
61
+ * its last `Project.load` in this process; see
62
+ * {@link chooseDeclaredCliCommand}.
63
+ * @param {string} [targetDir=process.cwd()]
64
+ * @returns {{command?: string, conflict: boolean, declared: DeclaredCliCommand[]}}
65
+ */
66
+ export function declaredCliCommand(targetDir?: string): {
67
+ command?: string;
68
+ conflict: boolean;
69
+ declared: DeclaredCliCommand[];
70
+ };
71
+ /**
72
+ * One integration's declared command.
73
+ */
74
+ export type DeclaredCliCommand = {
75
+ /**
76
+ * the integration package that declared it
77
+ */
78
+ package: string;
79
+ /**
80
+ * its `cliCommand` value
81
+ */
82
+ command: string;
83
+ };