@astryxdesign/cli 0.7.2-canary.fe2c655 → 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 (509) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/README.md +175 -154
  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/help/help.mjs +1 -1
  7. package/api/build/kit/kit.mjs +3 -11
  8. package/api/component/_adapter.d.mts +72 -1
  9. package/api/component/_adapter.mjs +212 -18
  10. package/api/component/component.doc.mjs +6 -1
  11. package/api/component/component.mjs +82 -15
  12. package/api/component/component.test.mjs +80 -0
  13. package/api/component/doc-links.test.mjs +386 -0
  14. package/api/component/integration-export-name.test.mjs +644 -0
  15. package/api/component/list/list.mjs +42 -4
  16. package/api/discover/_adapter.d.mts +132 -12
  17. package/api/discover/_adapter.mjs +488 -27
  18. package/api/discover/_adapter.test.mjs +599 -17
  19. package/api/discover/_catalog-view.d.mts +16 -5
  20. package/api/discover/_catalog-view.mjs +32 -8
  21. package/api/discover/detail/doc/doc.mjs +9 -1
  22. package/api/discover/discover.d.mts +11 -0
  23. package/api/discover/discover.doc.mjs +9 -7
  24. package/api/discover/discover.mjs +121 -19
  25. package/api/discover/discover.type.d.mts +20 -7
  26. package/api/discover/discover.type.mjs +17 -7
  27. package/api/discover/list/list.d.mts +4 -3
  28. package/api/discover/list/list.mjs +17 -10
  29. package/api/docs/_adapter.d.mts +74 -0
  30. package/api/docs/_adapter.mjs +553 -16
  31. package/api/docs/docs.doc.mjs +2 -1
  32. package/api/docs/docs.mjs +2 -1
  33. package/api/docs/docs.test.mjs +5 -2
  34. package/api/docs/integration-tree.test.mjs +22 -0
  35. package/api/docs/integrationDocs.test.mjs +124 -0
  36. package/api/docs/list/list.mjs +12 -2
  37. package/api/docs/reference-blocks.test.mjs +1 -3
  38. package/api/doctor/_adapter.d.mts +108 -0
  39. package/api/doctor/_adapter.mjs +223 -4
  40. package/api/doctor/doctor.d.mts +199 -0
  41. package/api/doctor/doctor.doc.mjs +3 -2
  42. package/api/doctor/doctor.mjs +530 -34
  43. package/api/doctor/doctor.test.mjs +180 -14
  44. package/api/doctor/release-checks.test.mjs +156 -0
  45. package/api/doctor/theme-checks.mjs +17 -4
  46. package/api/doctor/theme-checks.test.mjs +223 -7
  47. package/api/error.d.mts +10 -0
  48. package/api/error.mjs +25 -0
  49. package/api/gap-report/gap-report.d.mts +3 -0
  50. package/api/gap-report/gap-report.doc.mjs +11 -3
  51. package/api/gap-report/gap-report.mjs +219 -52
  52. package/api/gap-report/gap-report.test.mjs +233 -0
  53. package/api/gap-report/gap-report.type.d.mts +41 -0
  54. package/api/gap-report/gap-report.type.mjs +18 -0
  55. package/api/hook/_adapter.mjs +3 -11
  56. package/api/hook/hook.doc.mjs +4 -0
  57. package/api/hook/list/list.d.mts +1 -1
  58. package/api/index.d.mts +2 -0
  59. package/api/index.mjs +2 -0
  60. package/api/init/_adapter.d.mts +22 -0
  61. package/api/init/_adapter.mjs +59 -0
  62. package/api/init/init.d.mts +7 -5
  63. package/api/init/init.doc.mjs +28 -7
  64. package/api/init/init.mjs +28 -4
  65. package/api/init/init.type.d.mts +48 -3
  66. package/api/init/init.type.mjs +37 -3
  67. package/api/init/run/run.d.mts +1 -1
  68. package/api/init/run/run.mjs +17 -6
  69. package/api/init/user/user.d.mts +14 -0
  70. package/api/init/user/user.mjs +87 -0
  71. package/api/init/user/user.test.mjs +232 -0
  72. package/api/integration/_adapter.d.mts +45 -0
  73. package/api/integration/_adapter.mjs +112 -0
  74. package/api/integration/add-contribution.component-names.test.mjs +66 -0
  75. package/api/integration/add-contribution.mjs +8 -4
  76. package/api/integration/add-contribution.test.mjs +4 -2
  77. package/api/integration/authoring-checks.mjs +110 -6
  78. package/api/integration/authoring-checks.test.mjs +808 -0
  79. package/api/integration/authoring-checks.type.d.mts +143 -0
  80. package/api/integration/authoring-checks.type.mjs +74 -2
  81. package/api/integration/check/check.d.mts +96 -0
  82. package/api/integration/check/check.mjs +357 -0
  83. package/api/integration/check/check.test.mjs +489 -0
  84. package/api/integration/integration-check.type.d.mts +118 -0
  85. package/api/integration/integration-check.type.mjs +84 -0
  86. package/api/integration/integrationCheck.doc.d.mts +10 -0
  87. package/api/integration/integrationCheck.doc.mjs +73 -0
  88. package/api/integration/integrationComponentConflicts.doc.mjs +45 -6
  89. package/api/integration/integrationDocConflicts.doc.mjs +22 -5
  90. package/api/integration/pack-check.mjs +364 -120
  91. package/api/integration/pack-check.test.mjs +730 -10
  92. package/api/integration/validate-integration-fixes.test.mjs +67 -0
  93. package/api/integration/validate-integration.d.mts +8 -0
  94. package/api/integration/validate-integration.mjs +63 -9
  95. package/api/integration/validate-integration.test.mjs +88 -8
  96. package/api/integration/validate-integration.type.d.mts +16 -0
  97. package/api/integration/validate-integration.type.mjs +14 -1
  98. package/api/integration/validate-unread-theme-folders.test.mjs +3 -2
  99. package/api/integration/validateIntegration.doc.mjs +19 -3
  100. package/api/search/_adapter.d.mts +11 -0
  101. package/api/search/_adapter.mjs +25 -0
  102. package/api/search/search.d.mts +82 -13
  103. package/api/search/search.doc.mjs +8 -3
  104. package/api/search/search.domain-priority.test.mjs +108 -18
  105. package/api/search/search.integration-check.test.mjs +24 -0
  106. package/api/search/search.mjs +645 -64
  107. package/api/search/search.test.mjs +179 -1
  108. package/api/search/search.type.d.mts +14 -1
  109. package/api/search/search.type.mjs +6 -1
  110. package/api/swizzle/_adapter.mjs +2 -2
  111. package/api/swizzle/copy/copy.mjs +29 -15
  112. package/api/template/template-integration.test.mjs +1 -1
  113. package/api/template/template.test.mjs +120 -3
  114. package/api/theme/_adapter.d.mts +16 -0
  115. package/api/theme/_adapter.mjs +32 -5
  116. package/api/theme/build/build.d.mts +2 -1
  117. package/api/theme/build/build.family.test.mjs +64 -0
  118. package/api/theme/build/build.mjs +250 -38
  119. package/api/theme/build/build.packed-old-core.test.mjs +20 -3
  120. package/api/theme/build/font-warning.mjs +5 -3
  121. package/api/theme/build/font-warning.test.mjs +2 -1
  122. package/api/theme/remove/remove.test.mjs +2 -2
  123. package/api/theme/targets/targets.mjs +3 -10
  124. package/api/theme/themeBuild.doc.mjs +4 -4
  125. package/api/theme/themeTargets.doc.mjs +4 -0
  126. package/api/theme/use/use.test.mjs +1 -1
  127. package/api/upgrade/_adapter.d.mts +40 -2
  128. package/api/upgrade/_adapter.mjs +118 -5
  129. package/api/upgrade/run/cli-behind-core.test.mjs +438 -0
  130. package/api/upgrade/run/run.mjs +35 -2
  131. package/api/upgrade/status/status.d.mts +21 -5
  132. package/api/upgrade/status/status.mjs +45 -8
  133. package/api/upgrade/upgrade.doc.mjs +6 -3
  134. package/api/upgrade/upgrade.mjs +5 -1
  135. package/api/upgrade/upgrade.type.d.mts +23 -0
  136. package/api/upgrade/upgrade.type.mjs +16 -1
  137. package/assets/codemods/__tests__/registry.test.mjs +1 -0
  138. package/assets/codemods/registry.mjs +1 -0
  139. package/assets/codemods/runner.mjs +7 -1
  140. package/assets/codemods/transforms/v0.7.2/__tests__/migrate-list-edge-compensation-to-edge.test.mjs +120 -0
  141. package/assets/codemods/transforms/v0.7.2/index.mjs +20 -0
  142. package/assets/codemods/transforms/v0.7.2/migrate-list-edge-compensation-to-edge.mjs +198 -0
  143. package/assets/discover/official-packages.json +367 -0
  144. package/assets/docs/README.md +10 -0
  145. package/assets/docs/author-a-theme.doc.mjs +3 -3
  146. package/assets/docs/browser-support.doc.mjs +1 -1
  147. package/assets/docs/getting-started.doc.mjs +26 -6
  148. package/assets/docs/styling-overview.doc.mjs +1 -1
  149. package/assets/docs/theme.doc.dense.mjs +1 -1
  150. package/assets/docs/theme.doc.mjs +2 -2
  151. package/assets/docs/theme.doc.zh.mjs +1 -1
  152. package/assets/docs/tree/add-a-component.doc.mjs +22 -2
  153. package/assets/docs/tree/add-a-theme.doc.mjs +13 -3
  154. package/assets/docs/tree/add-a-topic.doc.mjs +54 -9
  155. package/assets/docs/tree/agent-guidance.doc.mjs +19 -5
  156. package/assets/docs/tree/block-template.doc.mjs +8 -20
  157. package/assets/docs/tree/build-the-template.doc.mjs +1 -1
  158. package/assets/docs/tree/building-blocks.doc.mjs +4 -0
  159. package/assets/docs/tree/check-the-props.doc.mjs +201 -0
  160. package/assets/docs/tree/check-your-docs.doc.mjs +69 -11
  161. package/assets/docs/tree/checks.doc.mjs +32 -24
  162. package/assets/docs/tree/ci-and-containers.doc.mjs +88 -0
  163. package/assets/docs/tree/cli-command.doc.mjs +142 -0
  164. package/assets/docs/tree/cli.doc.mjs +6 -0
  165. package/assets/docs/tree/codemods.doc.mjs +17 -7
  166. package/assets/docs/tree/commands.doc.mjs +7 -1
  167. package/assets/docs/tree/component-family.doc.mjs +8 -6
  168. package/assets/docs/tree/component-imports.doc.mjs +21 -3
  169. package/assets/docs/tree/components.doc.mjs +7 -1
  170. package/assets/docs/tree/computer-agent-guide.doc.mjs +160 -0
  171. package/assets/docs/tree/computer-integrations.doc.mjs +93 -0
  172. package/assets/docs/tree/configuration.doc.mjs +7 -3
  173. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +41 -22
  174. package/assets/docs/tree/define-the-theme.doc.mjs +6 -5
  175. package/assets/docs/tree/describe-the-component.doc.mjs +3 -3
  176. package/assets/docs/tree/docs.doc.mjs +9 -3
  177. package/assets/docs/tree/document-the-template.doc.mjs +1 -1
  178. package/assets/docs/tree/document-the-theme.doc.mjs +3 -3
  179. package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
  180. package/assets/docs/tree/extend-or-replace.doc.mjs +19 -9
  181. package/assets/docs/tree/fonts-and-assets.doc.mjs +2 -2
  182. package/assets/docs/tree/generate-a-palette.doc.mjs +7 -5
  183. package/assets/docs/tree/global-integrations.doc.mjs +124 -0
  184. package/assets/docs/tree/global.doc.mjs +131 -0
  185. package/assets/docs/tree/grade-template-with-agent.doc.mjs +5 -5
  186. package/assets/docs/tree/install-once.doc.mjs +105 -0
  187. package/assets/docs/tree/integrations.doc.mjs +13 -1
  188. package/assets/docs/tree/layout-spacing.doc.mjs +2 -3
  189. package/assets/docs/tree/layout.doc.mjs +6 -6
  190. package/assets/docs/tree/links.doc.mjs +7 -6
  191. package/assets/docs/tree/migration-setup.doc.mjs +3 -2
  192. package/assets/docs/tree/migration-verification.doc.mjs +0 -1
  193. package/assets/docs/tree/migration.doc.mjs +0 -1
  194. package/assets/docs/tree/monorepos.doc.mjs +84 -0
  195. package/assets/docs/tree/package-and-test.doc.mjs +1 -1
  196. package/assets/docs/tree/page-template.doc.mjs +4 -8
  197. package/assets/docs/tree/problems.doc.mjs +205 -0
  198. package/assets/docs/tree/project-global-or-both.doc.mjs +131 -0
  199. package/assets/docs/tree/publishing.doc.mjs +6 -6
  200. package/assets/docs/tree/quick-start.doc.mjs +53 -27
  201. package/assets/docs/tree/reference.doc.mjs +192 -0
  202. package/assets/docs/tree/replace-a-core-component.doc.mjs +15 -7
  203. package/assets/docs/tree/replace-a-core-template.doc.mjs +10 -12
  204. package/assets/docs/tree/scaffold.doc.mjs +1 -1
  205. package/assets/docs/tree/sections-and-placement.doc.mjs +9 -11
  206. package/assets/docs/tree/see-it-in-an-app.doc.mjs +17 -5
  207. package/assets/docs/tree/short-and-findable.doc.mjs +9 -8
  208. package/assets/docs/tree/single-component.doc.mjs +15 -6
  209. package/assets/docs/tree/start-a-template.doc.mjs +18 -8
  210. package/assets/docs/tree/subcomponent.doc.mjs +6 -6
  211. package/assets/docs/tree/template-assets.doc.mjs +2 -2
  212. package/assets/docs/tree/template-doc-overview.doc.mjs +6 -17
  213. package/assets/docs/tree/template-fonts.doc.mjs +1 -1
  214. package/assets/docs/tree/template-icons.doc.mjs +1 -1
  215. package/assets/docs/tree/template-images-media.doc.mjs +1 -1
  216. package/assets/docs/tree/template-styles.doc.mjs +2 -2
  217. package/assets/docs/tree/templates.doc.mjs +7 -1
  218. package/assets/docs/tree/test-in-an-app.doc.mjs +7 -7
  219. package/assets/docs/tree/test-template-in-app.doc.mjs +5 -5
  220. package/assets/docs/tree/themes.doc.mjs +4 -0
  221. package/assets/docs/tree/tokens-and-setup.doc.mjs +19 -15
  222. package/assets/docs/tree/troubleshooting.doc.mjs +29 -13
  223. package/assets/docs/tree/type-scale.doc.mjs +3 -3
  224. package/assets/docs/tree/uninstall.doc.mjs +109 -0
  225. package/assets/docs/tree/updates.doc.mjs +92 -0
  226. package/assets/docs/tree/upgrading.doc.mjs +6 -6
  227. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +14 -2
  228. package/assets/docs/tree/verify-packed-template.doc.mjs +3 -3
  229. package/assets/docs/tree/versioning.doc.mjs +10 -9
  230. package/assets/docs/tree/who-answers.doc.mjs +119 -0
  231. package/assets/docs/tree/write-the-template-file.doc.mjs +7 -6
  232. package/assets/docs/use-a-theme.doc.mjs +2 -2
  233. package/assets/docs/working-with-ai.doc.mjs +66 -13
  234. package/assets/theme.template.ts +1 -1
  235. package/authoring/codemod/codemod.doc.mjs +7 -3
  236. package/authoring/config/config.doc.mjs +9 -6
  237. package/authoring/config/type.ts +4 -2
  238. package/authoring/debug/parse.d.mts +5 -5
  239. package/authoring/discover/discover.doc.mjs +7 -1
  240. package/authoring/doctypes/_schema.d.mts +8 -3
  241. package/authoring/doctypes/_schema.mjs +1 -0
  242. package/authoring/doctypes/base/graph-fields.doc.mjs +6 -4
  243. package/authoring/doctypes/command/command.doc.mjs +20 -12
  244. package/authoring/doctypes/command/type.ts +14 -0
  245. package/authoring/doctypes/component/component.doc.mjs +30 -27
  246. package/authoring/doctypes/component/type.ts +21 -8
  247. package/authoring/doctypes/enum/enum.doc.mjs +7 -4
  248. package/authoring/doctypes/function/function.doc.mjs +9 -8
  249. package/authoring/doctypes/hook/hook.doc.mjs +5 -4
  250. package/authoring/doctypes/hook/type.ts +4 -0
  251. package/authoring/doctypes/namespace/namespace.doc.mjs +7 -6
  252. package/authoring/doctypes/reference/reference.doc.mjs +12 -11
  253. package/authoring/doctypes/schema/schema.doc.mjs +9 -9
  254. package/authoring/doctypes/template/integration-template.doc.d.mts +11 -0
  255. package/authoring/doctypes/template/integration-template.doc.mjs +49 -0
  256. package/authoring/doctypes/template/parse.d.mts +6 -2
  257. package/authoring/doctypes/template/parse.mjs +3 -1
  258. package/authoring/doctypes/template/template.doc.mjs +3 -3
  259. package/authoring/doctypes/template/type.ts +22 -0
  260. package/authoring/doctypes/theme/theme.doc.mjs +2 -2
  261. package/authoring/gap-report/gap-report.doc.mjs +2 -2
  262. package/authoring/identity/identity.doc.mjs +3 -3
  263. package/authoring/index.d.ts +2 -0
  264. package/authoring/integration/integration.doc.mjs +30 -6
  265. package/authoring/integration/parse.test.mjs +21 -0
  266. package/authoring/integration/schema.d.mts +16 -3
  267. package/authoring/integration/schema.mjs +34 -2
  268. package/authoring/integration/type.ts +12 -0
  269. package/clients/cli/bin/astryx.mjs +20 -3
  270. package/clients/cli/cli-command.test.mjs +166 -0
  271. package/clients/cli/commands/blog.doc.mjs +1 -0
  272. package/clients/cli/commands/build-theme.batch-report.test.mjs +17 -3
  273. package/clients/cli/commands/build-theme.font-warning.test.mjs +3 -3
  274. package/clients/cli/commands/build-theme.mjs +26 -13
  275. package/clients/cli/commands/build-theme.path-safety.test.mjs +224 -6
  276. package/clients/cli/commands/build-theme.registry.test.mjs +5 -2
  277. package/clients/cli/commands/build.doc.mjs +1 -0
  278. package/clients/cli/commands/build.exit-codes-doc.test.mjs +4 -0
  279. package/clients/cli/commands/cli-postinstall.test.mjs +49 -1
  280. package/clients/cli/commands/component.doc.mjs +1 -0
  281. package/clients/cli/commands/discover.broken-integration.test.mjs +11 -7
  282. package/clients/cli/commands/discover.doc.mjs +9 -1
  283. package/clients/cli/commands/discover.mjs +144 -93
  284. package/clients/cli/commands/discover.no-source.test.mjs +448 -33
  285. package/clients/cli/commands/discover.sources.test.mjs +20 -1
  286. package/clients/cli/commands/docs.doc.mjs +1 -0
  287. package/clients/cli/commands/docs.mjs +49 -19
  288. package/clients/cli/commands/docs.test.mjs +2 -2
  289. package/clients/cli/commands/doctor-integration-components.doc.mjs +19 -4
  290. package/clients/cli/commands/doctor-integration-docs.doc.mjs +20 -5
  291. package/clients/cli/commands/doctor-integration-templates.doc.mjs +1 -0
  292. package/clients/cli/commands/doctor-integration-validate.doc.mjs +22 -2
  293. package/clients/cli/commands/doctor-integration.test.mjs +888 -3
  294. package/clients/cli/commands/doctor.doc.mjs +12 -2
  295. package/clients/cli/commands/doctor.mjs +292 -7
  296. package/clients/cli/commands/doctor.test.mjs +10 -4
  297. package/clients/cli/commands/gap-report.doc.mjs +15 -2
  298. package/clients/cli/commands/gap-report.mjs +24 -2
  299. package/clients/cli/commands/gap-report.test.mjs +79 -0
  300. package/clients/cli/commands/hook.doc.mjs +1 -0
  301. package/clients/cli/commands/init.behavior.test.mjs +5 -0
  302. package/clients/cli/commands/init.doc.mjs +29 -3
  303. package/clients/cli/commands/integration-add.doc.mjs +7 -0
  304. package/clients/cli/commands/integration-authoring.test.mjs +49 -3
  305. package/clients/cli/commands/integration-check.doc.mjs +93 -0
  306. package/clients/cli/commands/integration-check.test.mjs +441 -0
  307. package/clients/cli/commands/integration-pack.doc.mjs +8 -1
  308. package/clients/cli/commands/integration-verify.doc.mjs +8 -1
  309. package/clients/cli/commands/integration.doc.mjs +6 -2
  310. package/clients/cli/commands/integration.mjs +119 -7
  311. package/clients/cli/commands/json-contract.test.mjs +11 -2
  312. package/clients/cli/commands/manifest.doc.mjs +1 -0
  313. package/clients/cli/commands/outside-project.test.mjs +774 -0
  314. package/clients/cli/commands/search.doc.mjs +1 -0
  315. package/clients/cli/commands/search.mjs +16 -11
  316. package/clients/cli/commands/search.test.mjs +429 -296
  317. package/clients/cli/commands/setup-nudge.test.mjs +102 -2
  318. package/clients/cli/commands/swizzle.doc.mjs +7 -0
  319. package/clients/cli/commands/swizzle.mjs +5 -3
  320. package/clients/cli/commands/swizzle.routing.test.mjs +19 -2
  321. package/clients/cli/commands/template.doc.mjs +1 -0
  322. package/clients/cli/commands/text-json-parity.test.mjs +5 -0
  323. package/clients/cli/commands/theme-add-lifecycle.test.mjs +3 -3
  324. package/clients/cli/commands/theme-add.doc.mjs +8 -1
  325. package/clients/cli/commands/theme-build.doc.mjs +3 -2
  326. package/clients/cli/commands/theme-eject.doc.mjs +1 -0
  327. package/clients/cli/commands/theme-list.behavior.test.mjs +6 -0
  328. package/clients/cli/commands/theme-list.doc.mjs +1 -0
  329. package/clients/cli/commands/theme-palette-generate.doc.mjs +1 -0
  330. package/clients/cli/commands/theme-remove.doc.mjs +7 -0
  331. package/clients/cli/commands/theme-targets.doc.mjs +1 -0
  332. package/clients/cli/commands/theme-template.doc.mjs +1 -0
  333. package/clients/cli/commands/theme-use.doc.mjs +7 -0
  334. package/clients/cli/commands/upgrade.config-ordering.test.mjs +2 -1
  335. package/clients/cli/commands/upgrade.doc.mjs +22 -1
  336. package/clients/cli/commands/upgrade.file-protection.test.mjs +3 -2
  337. package/clients/cli/commands/upgrade.hook-output.test.mjs +2 -1
  338. package/clients/cli/commands/upgrade.older-cli.test.mjs +160 -0
  339. package/clients/cli/index.mjs +112 -16
  340. package/clients/cli/lib/cli-error.mjs +4 -1
  341. package/clients/cli/lib/component-format.test.mjs +1 -1
  342. package/clients/cli/lib/define-command.mjs +2 -2
  343. package/clients/cli/lib/json-shim.mjs +3 -1
  344. package/clients/cli/lib/manifest.d.ts +9 -0
  345. package/clients/cli/lib/manifest.mjs +17 -1
  346. package/clients/cli/lib/manifest.test.mjs +25 -0
  347. package/foundation/agent-docs/agent-doc-state.d.mts +35 -6
  348. package/foundation/agent-docs/agent-doc-state.mjs +66 -6
  349. package/foundation/agent-docs/agent-docs.d.mts +4 -3
  350. package/foundation/agent-docs/agent-docs.mjs +11 -9
  351. package/foundation/agent-docs/computer-agent-text.d.mts +133 -0
  352. package/foundation/agent-docs/computer-agent-text.mjs +623 -0
  353. package/foundation/agent-docs/computer-agent-text.test.mjs +602 -0
  354. package/foundation/config/project.d.mts +35 -3
  355. package/foundation/config/project.mjs +226 -7
  356. package/foundation/config/project.test.mjs +66 -0
  357. package/foundation/config/theme-state.mjs +5 -4
  358. package/foundation/discovery/authoring-self-docs.mjs +2 -1
  359. package/foundation/discovery/cli-self-docs.mjs +20 -3
  360. package/foundation/discovery/cli-self-docs.test.mjs +3 -2
  361. package/foundation/discovery/component-discovery.d.mts +190 -10
  362. package/foundation/discovery/component-discovery.export-names.test.mjs +760 -0
  363. package/foundation/discovery/component-discovery.mjs +864 -56
  364. package/foundation/discovery/component-discovery.test.mjs +96 -0
  365. package/foundation/discovery/component-replacement.mjs +20 -1
  366. package/foundation/discovery/component-replacement.test.mjs +40 -0
  367. package/foundation/discovery/copied-passages.d.mts +216 -0
  368. package/foundation/discovery/copied-passages.mjs +720 -0
  369. package/foundation/discovery/copied-passages.test.mjs +124 -0
  370. package/foundation/discovery/doc-consistency.d.mts +34 -0
  371. package/foundation/discovery/doc-consistency.mjs +513 -0
  372. package/foundation/discovery/doc-consistency.test.mjs +347 -0
  373. package/foundation/discovery/doc-fluff.d.mts +112 -0
  374. package/foundation/discovery/doc-fluff.mjs +328 -0
  375. package/foundation/discovery/doc-fluff.test.mjs +142 -0
  376. package/foundation/discovery/doc-types.d.mts +157 -0
  377. package/foundation/discovery/doc-types.mjs +1231 -0
  378. package/foundation/discovery/doc-types.test.mjs +744 -0
  379. package/foundation/discovery/docs-discovery.d.mts +88 -0
  380. package/foundation/discovery/docs-discovery.mjs +83 -2
  381. package/foundation/discovery/integration-doc-files.d.mts +22 -0
  382. package/foundation/discovery/integration-doc-files.mjs +231 -0
  383. package/foundation/discovery/integration-doc-files.test.mjs +246 -0
  384. package/foundation/discovery/module-exports.d.mts +27 -0
  385. package/foundation/discovery/module-exports.mjs +356 -31
  386. package/foundation/discovery/module-exports.test.mjs +172 -0
  387. package/foundation/discovery/package-tsconfig.d.mts +14 -0
  388. package/foundation/discovery/package-tsconfig.mjs +228 -0
  389. package/foundation/discovery/package-tsconfig.test.mjs +210 -0
  390. package/foundation/discovery/project-typescript.d.mts +32 -0
  391. package/foundation/discovery/project-typescript.mjs +89 -0
  392. package/foundation/discovery/prop-drift.d.mts +297 -0
  393. package/foundation/discovery/prop-drift.mjs +2428 -0
  394. package/foundation/discovery/prop-drift.test.mjs +2337 -0
  395. package/foundation/discovery/runner-allowlist.d.mts +40 -0
  396. package/foundation/discovery/runner-allowlist.mjs +53 -0
  397. package/foundation/discovery/stray-pointers.d.mts +52 -0
  398. package/foundation/discovery/stray-pointers.mjs +293 -0
  399. package/foundation/discovery/stray-pointers.test.mjs +86 -0
  400. package/foundation/discovery/template-adapter.mjs +74 -5
  401. package/foundation/discovery/template-needs.mjs +5 -2
  402. package/foundation/discovery/theme-imports.mjs +8 -4
  403. package/foundation/discovery/theme-imports.test.mjs +1 -1
  404. package/foundation/discovery/type-match.d.mts +53 -0
  405. package/foundation/discovery/type-match.mjs +660 -0
  406. package/foundation/discovery/type-match.test.mjs +169 -0
  407. package/foundation/discovery/type-text.d.mts +68 -0
  408. package/foundation/discovery/type-text.mjs +658 -0
  409. package/foundation/discovery/type-text.test.mjs +196 -0
  410. package/foundation/discovery/unresolved-references.d.mts +62 -0
  411. package/foundation/discovery/unresolved-references.mjs +790 -0
  412. package/foundation/discovery/unresolved-references.test.mjs +567 -0
  413. package/foundation/doc-compiler/doc-loads.test.mjs +51 -1
  414. package/foundation/doc-compiler/inputs.mjs +27 -1
  415. package/foundation/doc-compiler/inputs.test.mjs +0 -1
  416. package/foundation/doc-compiler/links.d.mts +41 -5
  417. package/foundation/doc-compiler/links.mjs +167 -28
  418. package/foundation/doc-compiler/links.test.mjs +97 -4
  419. package/foundation/doc-compiler/read.d.mts +1 -1
  420. package/foundation/doc-compiler/tree.mjs +30 -1
  421. package/foundation/doc-compiler/tree.test.mjs +8 -1
  422. package/foundation/env/cli-command.d.mts +83 -0
  423. package/foundation/env/cli-command.mjs +152 -0
  424. package/foundation/env/cli-command.test.mjs +129 -0
  425. package/foundation/env/node-version.test.mjs +15 -3
  426. package/foundation/env/package-manager.d.mts +93 -9
  427. package/foundation/env/package-manager.global.test.mjs +66 -0
  428. package/foundation/env/package-manager.mjs +184 -13
  429. package/foundation/env/package-manager.test.mjs +341 -15
  430. package/foundation/env/project-scope.d.mts +9 -0
  431. package/foundation/env/project-scope.mjs +33 -0
  432. package/foundation/env/search-path.d.mts +42 -0
  433. package/foundation/env/search-path.mjs +205 -0
  434. package/foundation/env/search-path.test.mjs +122 -0
  435. package/foundation/env/user-dirs.d.mts +39 -0
  436. package/foundation/env/user-dirs.mjs +93 -0
  437. package/foundation/env/user-dirs.test.mjs +72 -0
  438. package/foundation/env/user-settings.d.mts +46 -0
  439. package/foundation/env/user-settings.mjs +106 -0
  440. package/foundation/env/user-settings.test.mjs +64 -0
  441. package/foundation/fs/file-protection.mjs +15 -1
  442. package/foundation/fs/module-loader.d.mts +2 -1
  443. package/foundation/fs/module-loader.mjs +60 -4
  444. package/foundation/fs/module-loader.stdout.test.mjs +3 -2
  445. package/foundation/fs/module-loader.test.mjs +194 -0
  446. package/foundation/fs/path-compare.d.mts +33 -0
  447. package/foundation/fs/path-compare.mjs +73 -0
  448. package/foundation/fs/paths.d.mts +57 -1
  449. package/foundation/fs/paths.mjs +131 -3
  450. package/foundation/fs/paths.test.mjs +10 -1
  451. package/foundation/integrations/computer-integrations.d.mts +78 -0
  452. package/foundation/integrations/computer-integrations.mjs +316 -0
  453. package/foundation/integrations/computer-integrations.test.mjs +292 -0
  454. package/foundation/integrations/contribution-inventory.d.mts +11 -0
  455. package/foundation/integrations/contribution-inventory.mjs +56 -3
  456. package/foundation/integrations/integration-warnings.mjs +3 -2
  457. package/foundation/integrations/integration-warnings.test.mjs +8 -9
  458. package/foundation/integrations/integrations.d.mts +25 -1
  459. package/foundation/integrations/integrations.mjs +46 -3
  460. package/foundation/integrations/issue.ts +6 -0
  461. package/foundation/integrations/validate-contributions.d.mts +2 -0
  462. package/foundation/integrations/validate-contributions.mjs +41 -1
  463. package/foundation/release/agent-text-worker.d.mts +4 -0
  464. package/foundation/release/agent-text-worker.mjs +52 -0
  465. package/foundation/release/cache.d.mts +310 -0
  466. package/foundation/release/cache.mjs +1216 -0
  467. package/foundation/release/cache.test.mjs +920 -0
  468. package/foundation/release/choose.d.mts +207 -0
  469. package/foundation/release/choose.mjs +677 -0
  470. package/foundation/release/choose.test.mjs +427 -0
  471. package/foundation/release/core-worker.d.mts +4 -0
  472. package/foundation/release/core-worker.mjs +38 -0
  473. package/foundation/release/global-state.d.mts +259 -0
  474. package/foundation/release/global-state.mjs +472 -0
  475. package/foundation/release/global-state.test.mjs +381 -0
  476. package/foundation/release/handoff.d.mts +60 -0
  477. package/foundation/release/handoff.e2e.test.mjs +278 -0
  478. package/foundation/release/handoff.mjs +357 -0
  479. package/foundation/release/lock-lease.d.mts +23 -0
  480. package/foundation/release/lock-lease.mjs +80 -0
  481. package/foundation/release/notices.d.mts +253 -0
  482. package/foundation/release/notices.mjs +596 -0
  483. package/foundation/release/notices.test.mjs +565 -0
  484. package/foundation/release/npm-guard.d.mts +4 -0
  485. package/foundation/release/npm-guard.mjs +126 -0
  486. package/foundation/release/project.d.mts +141 -0
  487. package/foundation/release/project.mjs +466 -0
  488. package/foundation/release/project.test.mjs +288 -0
  489. package/foundation/release/refresh-worker.d.mts +4 -0
  490. package/foundation/release/refresh-worker.mjs +27 -0
  491. package/foundation/release/run-context.d.mts +47 -0
  492. package/foundation/release/run-context.mjs +149 -0
  493. package/foundation/release/update-notice-env.d.mts +28 -0
  494. package/foundation/release/update-notice-env.mjs +39 -0
  495. package/foundation/response/base.d.ts +26 -3
  496. package/foundation/response/error-codes.d.mts +3 -1
  497. package/foundation/response/error-codes.doc.mjs +12 -0
  498. package/foundation/response/error-codes.mjs +17 -0
  499. package/foundation/response/error-codes.test.mjs +2 -0
  500. package/foundation/response/json-contract.test.mjs +7 -2
  501. package/foundation/response/json.mjs +4 -1
  502. package/foundation/response/response-types.doc.mjs +22 -11
  503. package/foundation/response/response-types.doc.test.mjs +46 -0
  504. package/foundation/response/response.doc.mjs +3 -3
  505. package/foundation/xle/browser-imports.test.mjs +74 -0
  506. package/foundation/xle/registry.mjs +4 -0
  507. package/foundation/xle/registry.test.mjs +28 -0
  508. package/package.json +11 -11
  509. package/scripts/postinstall.mjs +24 -5
@@ -8,6 +8,7 @@
8
8
  * none of them touch the integration system, the filesystem, or a source. This
9
9
  * adapter does: it resolves the project's installed integrations and what each
10
10
  * one adds, calls every discover source and keeps a saved copy of each answer,
11
+ * reads the official list the CLI carries after them,
11
12
  * locates and loads a component's doc, and names the package manager's add
12
13
  * command. Keeping that I/O here (INV21) is what lets `list`, `detail`,
13
14
  * `detail/doc`, `detail/item`, and `search` stay pure projections.
@@ -18,9 +19,12 @@
18
19
 
19
20
  import {createHash} from 'node:crypto';
20
21
  import * as fs from 'node:fs';
21
- import * as os from 'node:os';
22
22
  import * as path from 'node:path';
23
- import {Project} from '../../foundation/config/project.mjs';
23
+ import {
24
+ Project,
25
+ availableComputerIntegrations,
26
+ } from '../../foundation/config/project.mjs';
27
+ import {isOutsideProject} from '../../foundation/env/project-scope.mjs';
24
28
  import {
25
29
  scanAllPackages,
26
30
  findComponentInPackages,
@@ -28,13 +32,30 @@ import {
28
32
  import {loadDocs} from '../../foundation/discovery/component-loader.mjs';
29
33
  import {collectIdentities} from '../../foundation/integrations/contribution-inventory.mjs';
30
34
  import {detectPackageManager} from '../../foundation/env/package-manager.mjs';
35
+ import {isValidSemver, semverCompare} from '../../foundation/env/semver.mjs';
31
36
  import {assertWithin} from '../../foundation/fs/path-safety.mjs';
37
+ import {userCacheDir} from '../../foundation/env/user-dirs.mjs';
38
+ import {CLI_ROOT, findInstalledPackage} from '../../foundation/fs/paths.mjs';
32
39
  import {parseDiscoverCatalog} from '../../authoring/discover/parse.mjs';
33
40
  import {AstryxError} from '../error.mjs';
34
41
  import {ERROR_CODES} from '../../foundation/response/error-codes.mjs';
35
42
 
36
43
  const SOURCE_TIMEOUT_MS = 30_000;
37
44
 
45
+ /** The name the official list goes by, as a source and on each of its entries. */
46
+ export const OFFICIAL_SOURCE_NAME = 'Astryx';
47
+
48
+ /** Where the official list comes from: the CLI package that carries it. */
49
+ const OFFICIAL_SOURCE_FROM = '@astryxdesign/cli';
50
+
51
+ /** The official list, generated from the repository when the CLI is built. */
52
+ export const OFFICIAL_LIST_FILE = path.join(
53
+ CLI_ROOT,
54
+ 'assets',
55
+ 'discover',
56
+ 'official-packages.json',
57
+ );
58
+
38
59
  const DEPENDENCY_FIELDS = [
39
60
  'dependencies',
40
61
  'devDependencies',
@@ -48,6 +69,29 @@ const DEPENDENCY_FIELDS = [
48
69
  * @typedef {import('../../authoring/discover/type').DiscoverSource} DiscoverSource
49
70
  */
50
71
 
72
+ /**
73
+ * An official package as this CLI lists it (spec:AST-051 FR11), with what its
74
+ * add command pins (FR2): the project's Core version, the Core range the
75
+ * project declares, or the latest release; with none, it pins nothing.
76
+ * @typedef {DiscoverPackage & {pin?: string}} OfficialPackage
77
+ */
78
+
79
+ /**
80
+ * The official packages the project's package.json declares: the real package
81
+ * name of each, mapped to the name it is installed under (an npm alias such as
82
+ * `"my-theme": "npm:@astryxdesign/theme-gothic@0.7.0"` installs under
83
+ * `my-theme`). `dependency` holds those declared in dependencies,
84
+ * devDependencies or optionalDependencies, which make a package the project's
85
+ * own; `any` adds peerDependencies, which only keep it from being offered.
86
+ * @typedef {{dependency: Map<string, string>, any: Set<string>}} OfficialDeclarations
87
+ */
88
+
89
+ /**
90
+ * The official list as this CLI reads it. `error` says why the file could not
91
+ * be read; the list is then empty and discover reports it like a failed source.
92
+ * @typedef {{packages: OfficialPackage[], error?: string}} OfficialList
93
+ */
94
+
51
95
  /**
52
96
  * An installed integration with what it adds, per kind. `components` is the
53
97
  * scanned component list the discover leaves have always used.
@@ -64,7 +108,8 @@ const DEPENDENCY_FIELDS = [
64
108
  * @typedef {object} SourceState
65
109
  * @property {string} name the source's own name, or where it came from when it
66
110
  * gave none
67
- * @property {string} from `astryx.config`, or the integration that exports it
111
+ * @property {string} from `astryx.config`, the integration that exports it, or
112
+ * `@astryxdesign/cli` for the official list
68
113
  * @property {'fresh' | 'saved' | 'failed'} status
69
114
  * @property {string} [generatedAt]
70
115
  * @property {boolean} [complete]
@@ -73,9 +118,10 @@ const DEPENDENCY_FIELDS = [
73
118
  */
74
119
 
75
120
  /**
76
- * Everything every source returned, merged. When two sources list the same
77
- * package, the earlier source's entry is kept.
78
- * @typedef {{sources: SourceState[], packages: Array<DiscoverPackage & {source: string}>}} CatalogResult
121
+ * Everything every source returned, merged, the official list last. When two
122
+ * sources list the same package, the earlier source's entry is kept. `official`
123
+ * marks an entry the official list supplied.
124
+ * @typedef {{sources: SourceState[], packages: Array<DiscoverPackage & {source: string, official?: true}>}} CatalogResult
79
125
  */
80
126
 
81
127
  /**
@@ -203,6 +249,38 @@ export function hasSources(project) {
203
249
  );
204
250
  }
205
251
 
252
+ /** The source name of a package installed for the computer. */
253
+ const COMPUTER_SOURCE = 'installed for this computer';
254
+
255
+ /**
256
+ * The integrations installed for the computer that a project could add, as
257
+ * available entries with the project's install command (spec AST-081 FR12).
258
+ * Empty outside a project, where they load instead.
259
+ * @param {Project | undefined} project
260
+ * @param {Array<{name: string}>} installed
261
+ * @param {Set<string>} declared
262
+ * @param {(name: string, version?: string) => string} add
263
+ * @returns {Promise<import('./discover.type.mjs').DiscoverAvailableEntry[]>}
264
+ */
265
+ export async function computerAvailableEntries(
266
+ project,
267
+ installed,
268
+ declared,
269
+ add,
270
+ ) {
271
+ const cwd = project?.cwd ?? process.cwd();
272
+ if (isOutsideProject(cwd)) return [];
273
+ const have = new Set([...installed.map(pkg => pkg.name), ...declared]);
274
+ return (await availableComputerIntegrations(cwd, have)).map(c => ({
275
+ name: c.name,
276
+ components: [],
277
+ ...(c.version ? {version: c.version} : {}),
278
+ ...(c.description ? {description: c.description} : {}),
279
+ source: COMPUTER_SOURCE,
280
+ install: add(c.name),
281
+ }));
282
+ }
283
+
206
284
  /**
207
285
  * Package names the project's package.json declares in any dependency field.
208
286
  * @param {Project | undefined} project
@@ -225,13 +303,151 @@ export function declaredDependencies(project) {
225
303
  return names;
226
304
  }
227
305
 
306
+ /**
307
+ * The nearest package.json at or above the project's folder, read. Discover
308
+ * runs from any folder of a project; this is the project's own manifest.
309
+ * @param {Project | undefined} project
310
+ * @returns {{dir: string, pkg: Record<string, any>} | null}
311
+ */
312
+ function nearestManifest(project) {
313
+ let dir = project?.cwd ?? process.cwd();
314
+ for (;;) {
315
+ const file = path.join(dir, 'package.json');
316
+ if (fs.existsSync(file)) {
317
+ try {
318
+ return {dir, pkg: JSON.parse(fs.readFileSync(file, 'utf8'))};
319
+ } catch {
320
+ return null;
321
+ }
322
+ }
323
+ const parent = path.dirname(dir);
324
+ if (parent === dir) return null;
325
+ dir = parent;
326
+ }
327
+ }
328
+
329
+ /** The fields that make a declared package the project's own. */
330
+ const OWN_DEPENDENCY_FIELDS = [
331
+ 'dependencies',
332
+ 'devDependencies',
333
+ 'optionalDependencies',
334
+ ];
335
+
336
+ /**
337
+ * The package a dependency entry installs: its own name, or the target of an
338
+ * npm alias (`npm:<name>@<range>`).
339
+ * @param {string} name
340
+ * @param {unknown} spec
341
+ * @returns {string}
342
+ */
343
+ function declaredPackage(name, spec) {
344
+ if (typeof spec !== 'string' || !spec.startsWith('npm:')) return name;
345
+ const target = spec.slice('npm:'.length);
346
+ const at = target.indexOf('@', 1);
347
+ return at > 0 ? target.slice(0, at) : target;
348
+ }
349
+
350
+ /**
351
+ * What the project's own package.json, the nearest one above the folder
352
+ * discover runs in, declares among the official packages.
353
+ * @param {Project | undefined} project
354
+ * @param {OfficialPackage[]} official
355
+ * @returns {OfficialDeclarations}
356
+ */
357
+ export function officialDeclarations(project, official) {
358
+ const names = new Set(official.map(pkg => pkg.package));
359
+ /** @type {OfficialDeclarations} */
360
+ const declared = {dependency: new Map(), any: new Set()};
361
+ const manifest = nearestManifest(project);
362
+ if (manifest == null) return declared;
363
+ for (const field of DEPENDENCY_FIELDS) {
364
+ for (const [local, spec] of Object.entries(manifest.pkg?.[field] ?? {})) {
365
+ const real = declaredPackage(local, spec);
366
+ if (!names.has(real)) continue;
367
+ declared.any.add(real);
368
+ if (
369
+ OWN_DEPENDENCY_FIELDS.includes(field) &&
370
+ !declared.dependency.has(real)
371
+ )
372
+ declared.dependency.set(real, local);
373
+ }
374
+ }
375
+ return declared;
376
+ }
377
+
378
+ /**
379
+ * One part of a semver range: an optional comparator and a version that may
380
+ * use `x` or `*` wildcards.
381
+ */
382
+ const RANGE_PART =
383
+ /^(?:[<>]=?|=|\^|~)?v?(?:\d+|[xX*])(?:\.(?:\d+|[xX*])){0,2}(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/u;
384
+
385
+ /**
386
+ * Whether a dependency spec is a semver range every supported package manager
387
+ * resolves: comparators, wildcards, `||` and hyphen ranges. A dist-tag, path,
388
+ * URL, alias or workspace protocol is not.
389
+ * @param {string} spec
390
+ * @returns {boolean}
391
+ */
392
+ function isSemverRange(spec) {
393
+ const parts = spec
394
+ .trim()
395
+ .split(/\s*\|\|\s*|\s+/u)
396
+ .filter(part => part !== '-');
397
+ return parts.length > 0 && parts.every(part => RANGE_PART.test(part));
398
+ }
399
+
400
+ /**
401
+ * The project's Core: the installed copy's version, else what its own
402
+ * package.json declares (a fresh clone before install): an exact `version`, or
403
+ * a semver `range`. Neither, for a declaration no package manager can pin
404
+ * against, such as a path or a dist-tag. Null when the project has no Core.
405
+ * @param {Project | undefined} project
406
+ * @returns {{version?: string, range?: string} | null}
407
+ */
408
+ function projectCore(project) {
409
+ const installed = installedVersion(project, '@astryxdesign/core');
410
+ if (installed) return {version: installed};
411
+ const manifest = nearestManifest(project);
412
+ for (const field of OWN_DEPENDENCY_FIELDS) {
413
+ const spec = manifest?.pkg?.[field]?.['@astryxdesign/core'];
414
+ if (typeof spec !== 'string') continue;
415
+ // A space after an operator (`~ 0.6.8`, `>= 0.6.8 < 0.7.0`) is allowed by
416
+ // node-semver; join each operator to its version before reading the spec.
417
+ const range = spec.trim().replace(/([<>]=?|=|\^|~)\s+/gu, '$1');
418
+ const exact = range.replace(/^=/u, '');
419
+ if (isValidSemver(exact)) return {version: exact};
420
+ return isSemverRange(range) ? {range} : {};
421
+ }
422
+ return null;
423
+ }
424
+
425
+ /**
426
+ * Newest first: by release number, then a release before its prereleases.
427
+ * @param {string} a
428
+ * @param {string} b
429
+ * @returns {number}
430
+ */
431
+ function newerFirst(a, b) {
432
+ const byNumber = semverCompare(b, a);
433
+ if (byNumber !== 0) return byNumber;
434
+ const aRelease = !a.includes('-');
435
+ if (aRelease !== !b.includes('-')) return aRelease ? -1 : 1;
436
+ return b.localeCompare(a, 'en', {numeric: true});
437
+ }
438
+
228
439
  /**
229
440
  * The command that adds a package with the project's package manager. Discover
230
- * only prints it; it never runs it.
441
+ * only prints it; it never runs it. Asked for one version, it adds that
442
+ * version. Given the official list, it otherwise pins an official package to
443
+ * what its entry names (spec:AST-051 FR2): the project's Core version or
444
+ * declared Core range, else the latest release. A range is quoted, because
445
+ * `^`, `<`, `>`, `|` and spaces mean something to a shell.
231
446
  * @param {Project | undefined} project
447
+ * @param {{official?: OfficialPackage[]}} [options]
232
448
  * @returns {(name: string, version?: string) => string}
233
449
  */
234
- export function addCommand(project) {
450
+ export function addCommand(project, {official = []} = {}) {
235
451
  const pm = detectPackageManager(project?.cwd ?? process.cwd());
236
452
  const verb =
237
453
  pm === 'pnpm'
@@ -241,7 +457,15 @@ export function addCommand(project) {
241
457
  : pm === 'bun'
242
458
  ? 'bun add'
243
459
  : 'npm install';
244
- return (name, version) => `${verb} ${version ? `${name}@${version}` : name}`;
460
+ const byName = new Map(official.map(pkg => [pkg.package, pkg]));
461
+ return (name, version) => {
462
+ if (version) return `${verb} ${name}@${version}`;
463
+ const pin = byName.get(name)?.pin;
464
+ if (!pin) return `${verb} ${name}`;
465
+ return isValidSemver(pin)
466
+ ? `${verb} ${name}@${pin}`
467
+ : `${verb} "${name}@${pin}"`;
468
+ };
245
469
  }
246
470
 
247
471
  /**
@@ -250,21 +474,7 @@ export function addCommand(project) {
250
474
  * @returns {string}
251
475
  */
252
476
  export function defaultCacheDir() {
253
- const home = os.homedir();
254
- if (process.platform === 'win32') {
255
- return path.join(
256
- process.env.LOCALAPPDATA || path.join(home, 'AppData', 'Local'),
257
- 'astryx',
258
- 'Cache',
259
- );
260
- }
261
- if (process.platform === 'darwin') {
262
- return path.join(home, 'Library', 'Caches', 'astryx');
263
- }
264
- return path.join(
265
- process.env.XDG_CACHE_HOME || path.join(home, '.cache'),
266
- 'astryx',
267
- );
477
+ return userCacheDir();
268
478
  }
269
479
 
270
480
  /**
@@ -347,8 +557,8 @@ async function callSource(source, request, label, timeoutMs) {
347
557
  }
348
558
 
349
559
  /**
350
- * Call every discover source — the project's `discover` first, then each
351
- * integration's in load order — once per distinct function. A source that
560
+ * Call the project's own discover sources — the project's `discover` first,
561
+ * then each integration's in load order — once per distinct function. A source that
352
562
  * throws, times out, or returns an invalid catalog falls back to its saved copy;
353
563
  * with none, it is reported and adds nothing. The others always count.
354
564
  *
@@ -358,7 +568,7 @@ async function callSource(source, request, label, timeoutMs) {
358
568
  * @param {{cacheDir?: string, timeoutMs?: number}} [options]
359
569
  * @returns {Promise<CatalogResult>}
360
570
  */
361
- export async function callSources(project, request = {}, options = {}) {
571
+ async function callOwnSources(project, request = {}, options = {}) {
362
572
  const {cacheDir = defaultCacheDir(), timeoutMs = SOURCE_TIMEOUT_MS} = options;
363
573
  /** @type {CatalogResult} */
364
574
  const result = {sources: [], packages: []};
@@ -433,6 +643,257 @@ export async function callSources(project, request = {}, options = {}) {
433
643
  return result;
434
644
  }
435
645
 
646
+ /**
647
+ * Call every discover source (see {@link callOwnSources}), then read the
648
+ * official list (spec:AST-051 FR11). The list comes last, so a source keeps
649
+ * its entry for an official package, or for any package of the same
650
+ * integration (FR7); the list adds the official packages no source covers.
651
+ * The result stays in source order; discover puts official packages first
652
+ * only where it shows them ({@link officialFirst}).
653
+ *
654
+ * @param {Project | undefined} project
655
+ * @param {{package?: string, version?: string}} [request]
656
+ * @param {{cacheDir?: string, timeoutMs?: number, official?: OfficialList}} [options]
657
+ * `official`: the list {@link officialList} read; read here when absent
658
+ * @returns {Promise<CatalogResult>}
659
+ */
660
+ export async function callSources(project, request = {}, options = {}) {
661
+ const {official = officialList({project}), ...rest} = options;
662
+ const own = await callOwnSources(project, request, rest);
663
+ // A source's entry wins for its package and for the integration it
664
+ // publishes under any name (FR7), so a fork or mirror keeps its place.
665
+ const seen = new Set(
666
+ own.packages.flatMap(pkg => [pkg.package, pkg.integration, ...pkg.aliases]),
667
+ );
668
+ /** @type {CatalogResult['packages']} */
669
+ const added = [];
670
+ for (const {pin: _pin, ...pkg} of official.packages) {
671
+ if (seen.has(pkg.package)) continue;
672
+ if (request.package && pkg.package !== request.package) continue;
673
+ added.push({...pkg, source: OFFICIAL_SOURCE_NAME, official: true});
674
+ }
675
+ return {
676
+ sources: [
677
+ ...own.sources,
678
+ official.error
679
+ ? {
680
+ name: OFFICIAL_SOURCE_NAME,
681
+ from: OFFICIAL_SOURCE_FROM,
682
+ status: 'failed',
683
+ error: official.error,
684
+ }
685
+ : {
686
+ name: OFFICIAL_SOURCE_NAME,
687
+ from: OFFICIAL_SOURCE_FROM,
688
+ status: 'fresh',
689
+ complete: true,
690
+ },
691
+ ],
692
+ // Source order: the first entry per integration is the one discover
693
+ // keeps. Official packages are put first only where discover shows them.
694
+ packages: [...own.packages, ...added],
695
+ };
696
+ }
697
+
698
+ /**
699
+ * Official packages first, in the official list's order; the rest keep theirs.
700
+ * @template T
701
+ * @param {T[]} items
702
+ * @param {OfficialPackage[]} official
703
+ * @param {(item: T) => string} nameOf
704
+ * @returns {T[]}
705
+ */
706
+ export function officialFirst(items, official, nameOf) {
707
+ const order = new Map(official.map((pkg, index) => [pkg.package, index]));
708
+ const rank = (/** @type {T} */ item) => order.get(nameOf(item)) ?? order.size;
709
+ return [...items].sort((a, b) => rank(a) - rank(b));
710
+ }
711
+
712
+ /**
713
+ * The official list this CLI carries (spec:AST-051 FR11). The file holds no
714
+ * versions; each entry takes them from this CLI and the project:
715
+ *
716
+ * - Its latest release is the newest release this CLI knows: its own version
717
+ * on a release CLI, or the project's Core version when that is a newer
718
+ * release.
719
+ * - A package released with Core lists a release CLI's own version and the
720
+ * project's Core version (installed, else declared exactly), so each,
721
+ * the latest release included, can be looked up. Its add command pins the project's Core version, else the Core range
722
+ * the project declares, else the latest release; nothing on a canary CLI in
723
+ * a project with no Core, which may have been published without it.
724
+ * - A canary-only package is listed only by a canary CLI, as a prerelease at
725
+ * the project's Core version when that is a prerelease, else at the CLI's
726
+ * own version when the project has no Core. It is not listed beside a
727
+ * released or declared Core, which it cannot be installed with.
728
+ *
729
+ * @param {{file?: string, version?: string, project?: Project}} [options] the
730
+ * list file and the CLI version to read it for, both defaulting to this
731
+ * CLI's own, and the project whose Core decides the versions
732
+ * @returns {OfficialList}
733
+ */
734
+ export function officialList({
735
+ file = OFFICIAL_LIST_FILE,
736
+ version,
737
+ project,
738
+ } = {}) {
739
+ try {
740
+ const cli = version ?? packageVersion(CLI_ROOT);
741
+ if (cli == null) throw new Error('the CLI package names no version');
742
+ const core = project == null ? null : projectCore(project);
743
+ const list = JSON.parse(fs.readFileSync(file, 'utf8'));
744
+ const canaryCli = /-canary\./u.test(cli);
745
+ /** @type {(v: string | undefined) => v is string} */
746
+ const isRelease = v => v != null && !v.includes('-');
747
+ const latest =
748
+ [cli, core?.version].filter(isRelease).sort(newerFirst)[0] ?? null;
749
+ /** @type {OfficialPackage[]} */
750
+ const packages = [];
751
+ for (const pkg of list.packages) {
752
+ /** @type {string[]} */
753
+ let listed = [];
754
+ /** @type {string | null} */
755
+ let entryLatest = null;
756
+ /** @type {string | undefined} */
757
+ let pin;
758
+ if (pkg.canaryOnly === true) {
759
+ if (!canaryCli) continue;
760
+ if (core == null) pin = cli;
761
+ else if (core.version && !isRelease(core.version)) pin = core.version;
762
+ else continue;
763
+ listed = [pin];
764
+ } else if (pkg.releasedWithCore === true) {
765
+ entryLatest = latest;
766
+ listed = [
767
+ ...new Set(
768
+ [isRelease(cli) ? cli : undefined, core?.version].filter(
769
+ /** @returns {v is string} */ v => v != null,
770
+ ),
771
+ ),
772
+ ].sort(newerFirst);
773
+ // The project's Core version, else the plain range it declares, else
774
+ // the latest release (spec:AST-051 FR11): a Core declared as
775
+ // `workspace:*`, a `file:` path or a dist-tag, and not installed,
776
+ // still gets a pin.
777
+ pin =
778
+ core == null
779
+ ? canaryCli
780
+ ? undefined
781
+ : cli
782
+ : (core.version ?? core.range ?? entryLatest ?? undefined);
783
+ }
784
+ packages.push({
785
+ package: pkg.package,
786
+ integration: pkg.package,
787
+ aliases: [],
788
+ ...(pkg.description ? {description: pkg.description} : {}),
789
+ latest: entryLatest,
790
+ versions: listed.map(v => ({
791
+ version: v,
792
+ publishedAt: null,
793
+ prerelease: v.includes('-'),
794
+ status: 'ok',
795
+ })),
796
+ contributions: pkg.contributions,
797
+ ...(pin ? {pin} : {}),
798
+ });
799
+ }
800
+ return {packages};
801
+ } catch (err) {
802
+ return {
803
+ packages: [],
804
+ error: `the official list could not be read (${err instanceof Error ? err.message : String(err)})`,
805
+ };
806
+ }
807
+ }
808
+
809
+ /**
810
+ * What the project has: its installed integrations plus each official package
811
+ * its own package.json declares as a dependency whose entry the official list
812
+ * supplied, official packages first. A declared official package that a source
813
+ * lists stays as that source shows it (FR7). One that failed to load as an
814
+ * integration stays out. A declared one takes what it adds from the list and
815
+ * its version from the copy the project installed, under an npm alias too.
816
+ *
817
+ * @param {Project | undefined} project
818
+ * @param {InstalledPackage[]} installed from {@link describeInstalled}
819
+ * @param {CatalogResult} catalog
820
+ * @param {OfficialDeclarations} declared from {@link officialDeclarations}
821
+ * @param {OfficialPackage[]} official the official list, for the order
822
+ * @returns {Array<InstalledPackage & {declaredOfficial?: true}>}
823
+ */
824
+ export function withDeclaredOfficial(
825
+ project,
826
+ installed,
827
+ catalog,
828
+ declared,
829
+ official,
830
+ ) {
831
+ const loaded = new Set(project?.loadedIntegrations.map(i => i.name) ?? []);
832
+ /** @type {Array<InstalledPackage & {declaredOfficial?: true}>} */
833
+ const added = [];
834
+ for (const pkg of catalog.packages) {
835
+ const local = declared.dependency.get(pkg.package);
836
+ if (!pkg.official || local == null || loaded.has(pkg.package)) continue;
837
+ const version = installedVersion(project, local);
838
+ added.push(
839
+ /** @type {InstalledPackage & {declaredOfficial: true}} */ (
840
+ /** @type {unknown} */ ({
841
+ name: pkg.package,
842
+ category: pkg.package,
843
+ ...(version ? {version} : {}),
844
+ ...(pkg.description ? {description: pkg.description} : {}),
845
+ ...itemLists(pkg.contributions),
846
+ declaredOfficial: true,
847
+ })
848
+ ),
849
+ );
850
+ }
851
+ return officialFirst([...installed, ...added], official, pkg => pkg.name);
852
+ }
853
+
854
+ /**
855
+ * Group contributions into the entry fields, one list per kind that has any.
856
+ * `components` is always present, like on a scanned package.
857
+ * @param {DiscoverPackage['contributions']} contributions
858
+ * @returns {Record<string, string[]>}
859
+ */
860
+ function itemLists(contributions) {
861
+ /** @type {Record<string, string[]>} */
862
+ const lists = {components: []};
863
+ for (const [kind, field] of KIND_FIELDS) {
864
+ const names = contributions.filter(c => c.kind === kind).map(c => c.name);
865
+ if (names.length > 0) lists[field] = names;
866
+ }
867
+ return lists;
868
+ }
869
+
870
+ /**
871
+ * The version in a package directory's package.json, if it names one.
872
+ * @param {string} dir
873
+ * @returns {string | undefined}
874
+ */
875
+ function packageVersion(dir) {
876
+ try {
877
+ const {version} = JSON.parse(
878
+ fs.readFileSync(path.join(dir, 'package.json'), 'utf8'),
879
+ );
880
+ return typeof version === 'string' && version ? version : undefined;
881
+ } catch {
882
+ return undefined;
883
+ }
884
+ }
885
+
886
+ /**
887
+ * The version of `name` the project installed, if it installed it.
888
+ * @param {Project | undefined} project
889
+ * @param {string} name
890
+ * @returns {string | undefined}
891
+ */
892
+ function installedVersion(project, name) {
893
+ const dir = findInstalledPackage(project?.cwd ?? process.cwd(), name);
894
+ return dir == null ? undefined : packageVersion(dir);
895
+ }
896
+
436
897
  /** The list and detail entry fields for each item kind, in display order. */
437
898
  export const KIND_FIELDS = /** @type {const} */ ([
438
899
  ['component', 'components'],