@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
@@ -1,11 +1,67 @@
1
1
  // @generated by scripts/sync-api-types.mjs from the JSDoc in foundation/**/*.mjs.
2
2
  // DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
3
3
 
4
+ /**
5
+ * The Core a command answers from: the project's (see
6
+ * {@link findProjectCoreDir}), else, when a copy other than the project's own
7
+ * CLI answers, the Core that copy carries (spec AST-081 FR3): the
8
+ * `@astryxdesign/core` of its own release, beside it or in the per-user
9
+ * release cache. Never fetches; {@link requireCoreDir} does.
10
+ *
11
+ * Commands that judge or change a project (`doctor`, `swizzle`, `upgrade`)
12
+ * read {@link findProjectCoreDir} instead, so a carried Core never passes a
13
+ * project check or lands in a folder that installed none.
14
+ *
15
+ * @param {string} [startDir]
16
+ * @returns {string | null}
17
+ */
18
+ export function findCoreDir(startDir?: string): string | null;
19
+ /**
20
+ * Why a command that reads Core cannot run, when the answering release's own
21
+ * Core is needed and cannot be installed: outside a project, or in a project
22
+ * with nothing installed (spec AST-081 FR3).
23
+ * @param {{unavailable: string | null, version?: string, reason?: string}} found
24
+ * @param {boolean} inProject
25
+ * @returns {string}
26
+ */
27
+ export function unavailableCoreMessage(found: {
28
+ unavailable: string | null;
29
+ version?: string;
30
+ reason?: string;
31
+ }, inProject: boolean): string;
32
+ /**
33
+ * Like {@link findCoreDir}, but a copy that carries no Core installs its
34
+ * release's Core into the per-user cache first. Throws ERR_CORE_NOT_FOUND
35
+ * when there is no Core to answer from, and ERR_VERSIONS_UNAVAILABLE when the
36
+ * carried Core is needed and cannot be installed.
37
+ *
38
+ * @param {string} [startDir]
39
+ * @returns {string}
40
+ */
41
+ export function requireCoreDir(startDir?: string): string;
42
+ /**
43
+ * The carried Core alone, for a caller that already looked for the project's
44
+ * Core its own way: the folder, or null when the copy answering in `startDir`
45
+ * is the project's own CLI or carries none.
46
+ * @param {string} startDir
47
+ * @param {{fetch?: boolean}} [options]
48
+ * @returns {string | null}
49
+ */
50
+ export function findCarriedCoreDir(startDir: string, { fetch }?: {
51
+ fetch?: boolean;
52
+ }): string | null;
4
53
  /**
5
54
  * Find packages/core directory by walking up from startDir.
6
55
  * Also checks node_modules/@astryxdesign/core for installed usage.
56
+ *
57
+ * The project's Core only: in the folder or four above it, the reach every
58
+ * released version used, else from the folder of the package.json that makes
59
+ * the folder part of a project, however deep the folder is.
60
+ *
61
+ * @param {string} [startDir]
62
+ * @returns {string | null}
7
63
  */
8
- export function findCoreDir(startDir?: string): string | null;
64
+ export function findProjectCoreDir(startDir?: string): string | null;
9
65
  /**
10
66
  * Locate a package the project at `startDir` actually installed.
11
67
  *
@@ -11,17 +11,144 @@ import {createRequire} from 'node:module';
11
11
  import * as path from 'node:path';
12
12
  import {fileURLToPath} from 'node:url';
13
13
  import {listDir, pathExists} from './listing-cache.mjs';
14
+ import {ERROR_CODES} from '../response/error-codes.mjs';
15
+ import {carriedCore, runChoice} from '../release/run-context.mjs';
16
+ import {findDeclaringPackage} from '../release/project.mjs';
14
17
 
15
18
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
16
19
 
17
20
  /** Root of the @astryxdesign/cli package */
18
21
  export const CLI_ROOT = path.resolve(__dirname, '..', '..');
19
22
 
23
+ /**
24
+ * The Core a command answers from: the project's (see
25
+ * {@link findProjectCoreDir}), else, when a copy other than the project's own
26
+ * CLI answers, the Core that copy carries (spec AST-081 FR3): the
27
+ * `@astryxdesign/core` of its own release, beside it or in the per-user
28
+ * release cache. Never fetches; {@link requireCoreDir} does.
29
+ *
30
+ * Commands that judge or change a project (`doctor`, `swizzle`, `upgrade`)
31
+ * read {@link findProjectCoreDir} instead, so a carried Core never passes a
32
+ * project check or lands in a folder that installed none.
33
+ *
34
+ * @param {string} [startDir]
35
+ * @returns {string | null}
36
+ */
37
+ export function findCoreDir(startDir = process.cwd()) {
38
+ return findProjectCoreDir(startDir) ?? carriedCoreFor(startDir).dir;
39
+ }
40
+
41
+ /**
42
+ * Why a command that reads Core cannot run, when the answering release's own
43
+ * Core is needed and cannot be installed: outside a project, or in a project
44
+ * with nothing installed (spec AST-081 FR3).
45
+ * @param {{unavailable: string | null, version?: string, reason?: string}} found
46
+ * @param {boolean} inProject
47
+ * @returns {string}
48
+ */
49
+ export function unavailableCoreMessage(found, inProject) {
50
+ const unwritable = /cannot be written/.test(found.unavailable ?? '');
51
+ if (inProject) {
52
+ const core = found.version ? `Core ${found.version}` : 'its Core';
53
+ const why = found.reason ? ` (${found.reason})` : '';
54
+ return `This project has no Core installed, and ${core} could not be downloaded${why}. Install the project's dependencies${unwritable ? ', or make that folder writable' : ''}, then run again.`;
55
+ }
56
+ return `${found.unavailable}. Outside a project, commands that read Core use the Core of the release that answers; ${unwritable ? 'make that folder writable and run again' : 'run again when the package registry can be reached'}.`;
57
+ }
58
+
59
+ /**
60
+ * Like {@link findCoreDir}, but a copy that carries no Core installs its
61
+ * release's Core into the per-user cache first. Throws ERR_CORE_NOT_FOUND
62
+ * when there is no Core to answer from, and ERR_VERSIONS_UNAVAILABLE when the
63
+ * carried Core is needed and cannot be installed.
64
+ *
65
+ * @param {string} [startDir]
66
+ * @returns {string}
67
+ */
68
+ export function requireCoreDir(startDir = process.cwd()) {
69
+ const found = findProjectCoreDir(startDir) ?? carriedCoreFor(startDir, true);
70
+ if (typeof found === 'string') return found;
71
+ if (found.unavailable) {
72
+ throw codedError(
73
+ unavailableCoreMessage(found, runChoice(startDir).inProject),
74
+ ERROR_CODES.ERR_VERSIONS_UNAVAILABLE,
75
+ );
76
+ }
77
+ if (found.dir) return found.dir;
78
+ throw codedError(
79
+ 'Could not find @astryxdesign/core package',
80
+ ERROR_CODES.ERR_CORE_NOT_FOUND,
81
+ );
82
+ }
83
+
84
+ /**
85
+ * An Error carrying a stable error code, which the JSON envelope and every
86
+ * command's error output read the way they read an API error's.
87
+ * @param {string} message
88
+ * @param {string} code
89
+ * @returns {Error & {code: string}}
90
+ */
91
+ function codedError(message, code) {
92
+ return Object.assign(new Error(message), {code});
93
+ }
94
+
95
+ /**
96
+ * The carried Core alone, for a caller that already looked for the project's
97
+ * Core its own way: the folder, or null when the copy answering in `startDir`
98
+ * is the project's own CLI or carries none.
99
+ * @param {string} startDir
100
+ * @param {{fetch?: boolean}} [options]
101
+ * @returns {string | null}
102
+ */
103
+ export function findCarriedCoreDir(startDir, {fetch = false} = {}) {
104
+ return carriedCoreFor(startDir, fetch).dir;
105
+ }
106
+
107
+ /**
108
+ * The carried Core for a run in `startDir`, when the copy answering there is
109
+ * not the project's own CLI.
110
+ * @param {string} startDir
111
+ * @param {boolean} [fetch]
112
+ * @returns {{dir: string | null, unavailable: string | null, version?: string, reason?: string}}
113
+ */
114
+ function carriedCoreFor(startDir, fetch = false) {
115
+ const carried = carriedCore({fetch: false});
116
+ if (!carried.dir && !fetch) return carried;
117
+ if (runChoice(startDir).copy === 'project') {
118
+ return {dir: null, unavailable: null};
119
+ }
120
+ return carried.dir ? carried : carriedCore({fetch: true});
121
+ }
122
+
20
123
  /**
21
124
  * Find packages/core directory by walking up from startDir.
22
125
  * Also checks node_modules/@astryxdesign/core for installed usage.
126
+ *
127
+ * The project's Core only: in the folder or four above it, the reach every
128
+ * released version used, else from the folder of the package.json that makes
129
+ * the folder part of a project, however deep the folder is.
130
+ *
131
+ * @param {string} [startDir]
132
+ * @returns {string | null}
23
133
  */
24
- export function findCoreDir(startDir = process.cwd()) {
134
+ export function findProjectCoreDir(startDir = process.cwd()) {
135
+ const near = findNearbyCoreDir(startDir);
136
+ if (near) return near;
137
+ // A folder deeper than that reach still belongs to the project the walk to
138
+ // its package.json finds, so its Core is looked up from there too (spec
139
+ // AST-081, a folder's project): never the carried Core in its place.
140
+ const declaring = findDeclaringPackage(startDir);
141
+ return declaring && declaring.dir !== path.resolve(startDir)
142
+ ? findNearbyCoreDir(declaring.dir)
143
+ : null;
144
+ }
145
+
146
+ /**
147
+ * The Core installed in `startDir` or one of the four folders above it.
148
+ * @param {string} startDir
149
+ * @returns {string | null}
150
+ */
151
+ function findNearbyCoreDir(startDir) {
25
152
  let dir = startDir;
26
153
 
27
154
  for (let i = 0; i < 5; i++) {
@@ -153,7 +280,7 @@ export function discoverExternalPackages(startDir = process.cwd()) {
153
280
  if (!nodeModulesDir) return externals;
154
281
 
155
282
  /** @param {string} searchDir */
156
- const scanDir = (searchDir) => {
283
+ const scanDir = searchDir => {
157
284
  if (!fs.existsSync(searchDir)) return;
158
285
  const entries = fs.readdirSync(searchDir, {withFileTypes: true});
159
286
 
@@ -236,7 +363,8 @@ export function existsCaseExact(targetPath, rootDir) {
236
363
 
237
364
  const rel = path.relative(rootDir, targetPath);
238
365
  if (rel === '') return true;
239
- if (path.isAbsolute(rel) || rel === '..' || rel.startsWith(`..${path.sep}`)) return false;
366
+ if (path.isAbsolute(rel) || rel === '..' || rel.startsWith(`..${path.sep}`))
367
+ return false;
240
368
 
241
369
  let dir = rootDir;
242
370
  for (const segment of rel.split(path.sep)) {
@@ -4,7 +4,9 @@ import {describe, it, expect, beforeEach, afterEach} from 'vitest';
4
4
  import * as fs from 'node:fs';
5
5
  import * as path from 'node:path';
6
6
  import * as os from 'node:os';
7
- import {findCoreDir, findProjectRoot, listComponents, discoverExternalPackages, existsCaseExact, findInstalledPackage} from './paths.mjs';
7
+ import {findCoreDir, findProjectCoreDir, findProjectRoot, listComponents, discoverExternalPackages, existsCaseExact, findInstalledPackage} from './paths.mjs';
8
+ import {CLI_ROOT} from './paths.mjs';
9
+ import {linkThisCliAsProjectCli} from '../../test-utils/release-fixtures.mjs';
8
10
 
9
11
  let tmpDir;
10
12
 
@@ -39,6 +41,13 @@ describe('findCoreDir', () => {
39
41
  });
40
42
 
41
43
  it('returns null when nothing found', () => {
44
+ expect(findProjectCoreDir(tmpDir)).toBeNull();
45
+ });
46
+
47
+ it('falls back to the carried Core outside a project, and only there', () => {
48
+ // This copy's carried Core is the workspace Core beside it.
49
+ expect(findCoreDir(tmpDir)).toBe(path.resolve(CLI_ROOT, '..', 'core'));
50
+ linkThisCliAsProjectCli(tmpDir);
42
51
  expect(findCoreDir(tmpDir)).toBeNull();
43
52
  });
44
53
  });
@@ -0,0 +1,78 @@
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
+ * The install root of the CLI package at `cliRoot`, or null when it has none
6
+ * that counts: it is not installed in a `node_modules` folder, or it lives in
7
+ * a package runner's temporary install or the per-user release cache.
8
+ * @param {string} cliRoot
9
+ * @param {{cacheDir?: string}} [options]
10
+ * @returns {string | null}
11
+ */
12
+ export function computerInstallRoot(cliRoot: string, options?: {
13
+ cacheDir?: string;
14
+ }): string | null;
15
+ /**
16
+ * Every install root that holds packages installed with the CLI at
17
+ * `cliRoot`. Usually one: its install root. pnpm 11 and later install each
18
+ * global package in a folder of its own, `<pnpm home>/global/v<N>/<id>`, with
19
+ * its files in `<pnpm home>/store/v<N>/links` or inside that folder, so every
20
+ * such global folder counts, provided one of them installs this CLI.
21
+ * @param {string} cliRoot
22
+ * @param {{cacheDir?: string}} [options]
23
+ * @returns {string[]}
24
+ */
25
+ export function computerInstallRoots(cliRoot: string, options?: {
26
+ cacheDir?: string;
27
+ }): string[];
28
+ /**
29
+ * Why `pkg`'s peer ranges exclude `release`, or null when they admit it.
30
+ * @param {Record<string, any>} pkg
31
+ * @param {string} release the answering release's version
32
+ * @returns {string | null}
33
+ */
34
+ export function peerProblem(pkg: Record<string, any>, release: string): string | null;
35
+ /**
36
+ * The computer-level integrations installed beside the CLI at `cliRoot`, in
37
+ * name order (declaration order when the root declares them). Reads
38
+ * package.json files only.
39
+ * @param {{cliRoot: string, release: string, cacheDir?: string}} options
40
+ * @returns {ComputerIntegrationCandidate[]}
41
+ */
42
+ export function scanComputerIntegrations({ cliRoot, release, cacheDir }: {
43
+ cliRoot: string;
44
+ release: string;
45
+ cacheDir?: string;
46
+ }): ComputerIntegrationCandidate[];
47
+ /**
48
+ * Load the compatible computer-level integrations as provider candidates,
49
+ * each in isolation, under the autolinked policy (a broken one is recorded
50
+ * and contributes nothing; the same package reached twice loads once). Each
51
+ * loaded record is marked `__computer`. An incompatible one is not loaded.
52
+ * @param {ComputerIntegrationCandidate[]} candidates
53
+ * @param {{fresh?: boolean, exclude?: Iterable<string>}} [options] `exclude`
54
+ * names packages the folder already loads, which keep their own copy
55
+ * @returns {Promise<import('./provider-resolution.mjs').ProviderCandidate[]>}
56
+ */
57
+ export function loadComputerIntegrations(candidates: ComputerIntegrationCandidate[], options?: {
58
+ fresh?: boolean;
59
+ exclude?: Iterable<string>;
60
+ }): Promise<import("./provider-resolution.mjs").ProviderCandidate[]>;
61
+ export type ComputerIntegrationCandidate = {
62
+ /**
63
+ * the package name
64
+ */
65
+ name: string;
66
+ version: string | undefined;
67
+ description: string | undefined;
68
+ packageDir: string;
69
+ /**
70
+ * the folder whose node_modules holds it
71
+ */
72
+ hostDir: string;
73
+ /**
74
+ * why its peer ranges exclude the
75
+ * answering release, or null
76
+ */
77
+ incompatible: string | null;
78
+ };
@@ -0,0 +1,316 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file Computer-level integrations (spec AST-081 FR12): the integration
5
+ * packages installed in the same install root as the running copy of the CLI,
6
+ * such as a package manager's global folder beside a global copy, or the
7
+ * package set a door installs together.
8
+ *
9
+ * - The install root is the folder whose `node_modules` holds the running
10
+ * CLI package (above pnpm's `.pnpm` store). pnpm 11 and later give each
11
+ * global package a folder of its own, and every one of them counts. A
12
+ * package runner's temporary install and the per-user release cache are not
13
+ * install roots: they hold what one run asked for.
14
+ * - When the root has a package.json, its declared dependencies are read, as
15
+ * autolink reads a project's; otherwise (npm's global folder) every package
16
+ * directly in its `node_modules`, scoped ones included. Either way, only a
17
+ * package with exactly one root `astryx.integration.*` manifest counts, and
18
+ * the CLI and Core packages never do.
19
+ * - Outside a project they load as a project's integrations do (Project.load).
20
+ * One whose declared `@astryxdesign/core` or `@astryxdesign/cli` peer range
21
+ * excludes the answering release is skipped, and doctor says why.
22
+ * - Inside a project none load; discover lists the ones the project does not
23
+ * install as available, and doctor names them.
24
+ *
25
+ * Listing reads package.json files only; nothing is imported until a load.
26
+ *
27
+ * @input the running CLI package's folder
28
+ * @output the computer-level integration candidates, and their loaded records
29
+ * @position foundation/integrations — read by config/project, discover, and
30
+ * doctor.
31
+ */
32
+
33
+ import * as fs from 'node:fs';
34
+ import * as path from 'node:path';
35
+ import {findManifestPaths, loadIntegrations} from './integrations.mjs';
36
+ import {
37
+ readDeclaredDependencies,
38
+ resolveInstalledPackageDir,
39
+ } from './autolink.mjs';
40
+ import {installPlaceOf} from '../release/choose.mjs';
41
+ import {isInReleaseCache} from '../release/cache.mjs';
42
+ import {satisfiesRange} from '../env/semver.mjs';
43
+
44
+ /** Packages that are the CLI itself, never an integration of it. */
45
+ const NOT_INTEGRATIONS = new Set(['@astryxdesign/cli', '@astryxdesign/core']);
46
+
47
+ /** Peers whose ranges must admit the answering release. */
48
+ const RELEASE_PEERS = ['@astryxdesign/core', '@astryxdesign/cli'];
49
+
50
+ /**
51
+ * @typedef {object} ComputerIntegrationCandidate
52
+ * @property {string} name the package name
53
+ * @property {string | undefined} version
54
+ * @property {string | undefined} description
55
+ * @property {string} packageDir
56
+ * @property {string} hostDir the folder whose node_modules holds it
57
+ * @property {string | null} incompatible why its peer ranges exclude the
58
+ * answering release, or null
59
+ */
60
+
61
+ /**
62
+ * @param {string} p
63
+ * @returns {string}
64
+ */
65
+ function real(p) {
66
+ try {
67
+ return fs.realpathSync(p);
68
+ } catch {
69
+ return path.resolve(p);
70
+ }
71
+ }
72
+
73
+ /**
74
+ * @param {string} dir
75
+ * @returns {Record<string, any> | null}
76
+ */
77
+ function readPackage(dir) {
78
+ try {
79
+ const pkg = JSON.parse(
80
+ fs.readFileSync(path.join(dir, 'package.json'), 'utf8'),
81
+ );
82
+ return pkg && typeof pkg === 'object' ? pkg : null;
83
+ } catch {
84
+ return null;
85
+ }
86
+ }
87
+
88
+ /**
89
+ * The install root of the CLI package at `cliRoot`, or null when it has none
90
+ * that counts: it is not installed in a `node_modules` folder, or it lives in
91
+ * a package runner's temporary install or the per-user release cache.
92
+ * @param {string} cliRoot
93
+ * @param {{cacheDir?: string}} [options]
94
+ * @returns {string | null}
95
+ */
96
+ export function computerInstallRoot(cliRoot, options = {}) {
97
+ const root = real(cliRoot);
98
+ const place = installPlaceOf(root);
99
+ if (place.runner || !place.owner) return null;
100
+ if (isInReleaseCache(root, options.cacheDir)) return null;
101
+ return place.owner;
102
+ }
103
+
104
+ /**
105
+ * Every install root that holds packages installed with the CLI at
106
+ * `cliRoot`. Usually one: its install root. pnpm 11 and later install each
107
+ * global package in a folder of its own, `<pnpm home>/global/v<N>/<id>`, with
108
+ * its files in `<pnpm home>/store/v<N>/links` or inside that folder, so every
109
+ * such global folder counts, provided one of them installs this CLI.
110
+ * @param {string} cliRoot
111
+ * @param {{cacheDir?: string}} [options]
112
+ * @returns {string[]}
113
+ */
114
+ export function computerInstallRoots(cliRoot, options = {}) {
115
+ const fromPnpm = pnpmGlobalFolders(real(cliRoot));
116
+ if (fromPnpm.length > 0) return fromPnpm;
117
+ const root = computerInstallRoot(cliRoot, options);
118
+ return root ? [root] : [];
119
+ }
120
+
121
+ /**
122
+ * pnpm's global folders for a CLI installed globally by pnpm 11 or later, or
123
+ * none. Its files are either in pnpm's store (`<home>/store/v<N>/links/...`,
124
+ * with the global virtual store) or inside its own global folder
125
+ * (`<home>/global/v<N>/<id>/node_modules/...`, without it).
126
+ * @param {string} cli the CLI package's real path
127
+ * @returns {string[]}
128
+ */
129
+ function pnpmGlobalFolders(cli) {
130
+ const parts = cli.split(/[\\/]/);
131
+ /** @type {string | null} */
132
+ let globalDir = null;
133
+ const storeAt = parts.lastIndexOf('store');
134
+ if (
135
+ storeAt > 0 &&
136
+ /^v\d+$/.test(parts[storeAt + 1] ?? '') &&
137
+ parts[storeAt + 2] === 'links'
138
+ ) {
139
+ const home = parts.slice(0, storeAt).join(path.sep) || path.sep;
140
+ globalDir = path.join(home, 'global', parts[storeAt + 1]);
141
+ } else {
142
+ const at = parts.findIndex(
143
+ (part, k) =>
144
+ part === 'global' &&
145
+ /^v\d+$/.test(parts[k + 1] ?? '') &&
146
+ parts[k + 3] === 'node_modules',
147
+ );
148
+ if (at > 0) globalDir = parts.slice(0, at + 2).join(path.sep) || path.sep;
149
+ }
150
+ if (globalDir == null) return [];
151
+ /** @type {string[]} */
152
+ let names;
153
+ try {
154
+ names = fs.readdirSync(globalDir).sort();
155
+ } catch {
156
+ return [];
157
+ }
158
+ const folders = names
159
+ .map(name => path.join(globalDir, name))
160
+ .filter(dir => fs.existsSync(path.join(dir, 'package.json')));
161
+ const installsThisCli = folders.some(
162
+ dir => real(path.join(dir, 'node_modules', '@astryxdesign', 'cli')) === cli,
163
+ );
164
+ return installsThisCli ? folders : [];
165
+ }
166
+
167
+ /**
168
+ * Every package name directly in `<root>/node_modules`, scoped ones included.
169
+ * @param {string} root
170
+ * @returns {string[]}
171
+ */
172
+ function installedNames(root) {
173
+ const modules = path.join(root, 'node_modules');
174
+ /** @type {string[]} */
175
+ const names = [];
176
+ /** @type {fs.Dirent[]} */
177
+ let entries;
178
+ try {
179
+ entries = fs.readdirSync(modules, {withFileTypes: true});
180
+ } catch {
181
+ return names;
182
+ }
183
+ for (const entry of entries) {
184
+ if (entry.name.startsWith('.')) continue;
185
+ if (entry.name.startsWith('@')) {
186
+ try {
187
+ for (const scoped of fs.readdirSync(path.join(modules, entry.name))) {
188
+ if (!scoped.startsWith('.')) names.push(`${entry.name}/${scoped}`);
189
+ }
190
+ } catch {
191
+ // An unreadable scope folder holds nothing to read.
192
+ }
193
+ } else {
194
+ names.push(entry.name);
195
+ }
196
+ }
197
+ return names.sort();
198
+ }
199
+
200
+ /**
201
+ * Why `pkg`'s peer ranges exclude `release`, or null when they admit it.
202
+ * @param {Record<string, any>} pkg
203
+ * @param {string} release the answering release's version
204
+ * @returns {string | null}
205
+ */
206
+ export function peerProblem(pkg, release) {
207
+ const peers = pkg.peerDependencies;
208
+ if (!peers || typeof peers !== 'object') return null;
209
+ for (const peer of RELEASE_PEERS) {
210
+ const range = peers[peer];
211
+ if (typeof range !== 'string') continue;
212
+ let admits;
213
+ try {
214
+ admits = satisfiesRange(release, range);
215
+ } catch {
216
+ // A range this CLI cannot read never keeps the integration out.
217
+ admits = true;
218
+ }
219
+ if (!admits) {
220
+ return `it needs ${peer} ${range}, and this computer's CLI is ${release}`;
221
+ }
222
+ }
223
+ return null;
224
+ }
225
+
226
+ /**
227
+ * The computer-level integrations installed beside the CLI at `cliRoot`, in
228
+ * name order (declaration order when the root declares them). Reads
229
+ * package.json files only.
230
+ * @param {{cliRoot: string, release: string, cacheDir?: string}} options
231
+ * @returns {ComputerIntegrationCandidate[]}
232
+ */
233
+ export function scanComputerIntegrations({cliRoot, release, cacheDir}) {
234
+ const self = real(cliRoot);
235
+ /** @type {ComputerIntegrationCandidate[]} */
236
+ const found = [];
237
+ const seen = new Set();
238
+ for (const root of computerInstallRoots(cliRoot, {cacheDir})) {
239
+ scanRoot(root, {self, release, found, seen});
240
+ }
241
+ return found;
242
+ }
243
+
244
+ /**
245
+ * Add the integrations installed in one install root.
246
+ * @param {string} root
247
+ * @param {{self: string, release: string, found: ComputerIntegrationCandidate[], seen: Set<string>}} into
248
+ */
249
+ function scanRoot(root, {self, release, found, seen}) {
250
+ const declared = readPackage(root)
251
+ ? readDeclaredDependencies(root).map(d => d.name)
252
+ : installedNames(root);
253
+ for (const name of declared) {
254
+ if (NOT_INTEGRATIONS.has(name)) continue;
255
+ const resolved = resolveInstalledPackageDir(name, root);
256
+ if (!resolved) continue;
257
+ const identity = real(resolved.packageDir);
258
+ if (identity === self || seen.has(identity)) continue;
259
+ if (findManifestPaths(resolved.packageDir).length !== 1) continue;
260
+ const pkg = readPackage(resolved.packageDir);
261
+ if (!pkg || typeof pkg.name !== 'string') continue;
262
+ if (NOT_INTEGRATIONS.has(pkg.name)) continue;
263
+ seen.add(identity);
264
+ found.push({
265
+ name: pkg.name,
266
+ version: typeof pkg.version === 'string' ? pkg.version : undefined,
267
+ description:
268
+ typeof pkg.description === 'string' ? pkg.description : undefined,
269
+ packageDir: resolved.packageDir,
270
+ hostDir: resolved.hostDir,
271
+ incompatible: peerProblem(pkg, release),
272
+ });
273
+ }
274
+ }
275
+
276
+ /**
277
+ * Load the compatible computer-level integrations as provider candidates,
278
+ * each in isolation, under the autolinked policy (a broken one is recorded
279
+ * and contributes nothing; the same package reached twice loads once). Each
280
+ * loaded record is marked `__computer`. An incompatible one is not loaded.
281
+ * @param {ComputerIntegrationCandidate[]} candidates
282
+ * @param {{fresh?: boolean, exclude?: Iterable<string>}} [options] `exclude`
283
+ * names packages the folder already loads, which keep their own copy
284
+ * @returns {Promise<import('./provider-resolution.mjs').ProviderCandidate[]>}
285
+ */
286
+ export async function loadComputerIntegrations(candidates, options = {}) {
287
+ const {fresh = false, exclude = []} = options;
288
+ const taken = new Set(exclude);
289
+ /** @type {import('./provider-resolution.mjs').ProviderCandidate[]} */
290
+ const loaded = [];
291
+ for (const candidate of candidates) {
292
+ if (candidate.incompatible || taken.has(candidate.name)) continue;
293
+ try {
294
+ const [integration] = await loadIntegrations([candidate.name], {
295
+ cwd: candidate.hostDir,
296
+ fresh,
297
+ });
298
+ loaded.push({
299
+ source: 'autolinked',
300
+ spec: candidate.name,
301
+ packageDir: candidate.packageDir,
302
+ ...(integration == null
303
+ ? {}
304
+ : {integration: {...integration, __computer: true}}),
305
+ });
306
+ } catch (error) {
307
+ loaded.push({
308
+ source: 'autolinked',
309
+ spec: candidate.name,
310
+ packageDir: candidate.packageDir,
311
+ error: error instanceof Error ? error.message : String(error),
312
+ });
313
+ }
314
+ }
315
+ return loaded;
316
+ }