@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
@@ -0,0 +1,278 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file The hand-off through the real bin: a run started in this copy, in a
5
+ * project whose release is another one, is answered by that release with the
6
+ * same arguments, folder, standard streams, and exit status, and one line on
7
+ * standard error in text mode (spec AST-081 FR2, FR9).
8
+ */
9
+
10
+ import {describe, it, expect, afterEach} from 'vitest';
11
+ import {spawnSync} from 'node:child_process';
12
+ import * as fs from 'node:fs';
13
+ import * as path from 'node:path';
14
+ import {fileURLToPath} from 'node:url';
15
+ import {
16
+ CLI_VERSION,
17
+ cleanupFixtures,
18
+ installCore,
19
+ linkThisCliAsProjectCli,
20
+ installStandInCli,
21
+ project,
22
+ seedRelease,
23
+ tempDir,
24
+ writeJson,
25
+ } from '../../test-utils/release-fixtures.mjs';
26
+
27
+ const BIN = fileURLToPath(
28
+ new URL('../../clients/cli/bin/astryx.mjs', import.meta.url),
29
+ );
30
+ const SELF_VERSION = JSON.parse(
31
+ fs.readFileSync(
32
+ fileURLToPath(new URL('../../package.json', import.meta.url)),
33
+ 'utf-8',
34
+ ),
35
+ ).version;
36
+
37
+ afterEach(cleanupFixtures);
38
+
39
+ /**
40
+ * Run this copy's bin in `cwd` with a private home and cache.
41
+ * @param {string} cwd
42
+ * @param {string[]} args
43
+ * @param {{input?: string, cache?: string, home?: string, env?: Record<string, string>}} [options]
44
+ */
45
+ function run(cwd, args, options = {}) {
46
+ const home = options.home ?? tempDir('home');
47
+ const cache = options.cache ?? tempDir('cache');
48
+ const result = spawnSync(process.execPath, [BIN, ...args], {
49
+ cwd,
50
+ encoding: 'utf-8',
51
+ input: options.input ?? '',
52
+ env: {
53
+ // Not the package manager running the tests: its npm_* settings would
54
+ // name it as the project's package manager. Nor the test runner's CI
55
+ // or NO_UPDATE_NOTIFIER, which turn update notices off.
56
+ ...Object.fromEntries(
57
+ Object.entries(process.env).filter(
58
+ ([key]) => !/^(?:npm_|CI$|NO_UPDATE_NOTIFIER$)/iu.test(key),
59
+ ),
60
+ ),
61
+ HOME: home,
62
+ USERPROFILE: home,
63
+ XDG_CACHE_HOME: cache,
64
+ LOCALAPPDATA: cache,
65
+ XDG_CONFIG_HOME: path.join(home, '.config'),
66
+ APPDATA: path.join(home, 'AppData', 'Roaming'),
67
+ NO_COLOR: '1',
68
+ ...options.env,
69
+ },
70
+ timeout: 60_000,
71
+ });
72
+ return {
73
+ status: result.status,
74
+ stdout: result.stdout,
75
+ stderr: result.stderr,
76
+ /** The stand-in's report, when a stand-in answered. */
77
+ report: () => JSON.parse(result.stdout.trim().split('\n').pop() ?? 'null'),
78
+ };
79
+ }
80
+
81
+ describe('hand-off to the project CLI', () => {
82
+ it('passes argv, folder, stdin and the exit status, with one stderr line', () => {
83
+ const root = project(tempDir(), {'@astryxdesign/core': '0.6.8'});
84
+ installCore(root, '0.6.8');
85
+ installStandInCli(root, '0.6.8');
86
+ const src = path.join(root, 'src');
87
+ fs.mkdirSync(src);
88
+ const result = run(src, ['component', 'Button', '--exit', '7'], {
89
+ input: 'piped',
90
+ });
91
+ expect(result.status).toBe(7);
92
+ expect(result.report()).toEqual({
93
+ version: '0.6.8',
94
+ args: ['component', 'Button', '--exit', '7'],
95
+ cwd: src,
96
+ stdin: 'piped',
97
+ });
98
+ const lines = result.stderr.trim().split('\n');
99
+ expect(lines).toEqual([
100
+ `astryx: answered by this project's CLI 0.6.8, handed over from CLI ${SELF_VERSION}`,
101
+ 'stand-in 0.6.8',
102
+ ]);
103
+ });
104
+
105
+ it('prints no line with --json', () => {
106
+ const root = project(tempDir(), {'@astryxdesign/core': '0.6.8'});
107
+ installCore(root, '0.6.8');
108
+ installStandInCli(root, '0.6.8');
109
+ const result = run(root, ['docs', '--json']);
110
+ expect(result.status).toBe(0);
111
+ expect(result.stderr.trim()).toBe('stand-in 0.6.8');
112
+ });
113
+
114
+ it("a project CLI that cannot start: the release matching the project's Core answers; offline, this copy, with the warning", () => {
115
+ const root = project(tempDir(), {'@astryxdesign/core': '0.6.8'});
116
+ installCore(root, '0.6.8');
117
+ installStandInCli(root, '0.6.8', {bin: false});
118
+ const offline = {npm_config_registry: 'http://127.0.0.1:9'};
119
+ const json = run(root, ['docs', '--json'], {env: offline});
120
+ expect(json.status).toBe(0);
121
+ expect(JSON.parse(json.stdout).meta.answeredBy.copy).toBe('fallback');
122
+ const text = run(root, ['--version'], {env: offline});
123
+ expect(text.status).toBe(0);
124
+ expect(text.stdout.trim()).toBe(SELF_VERSION);
125
+ expect(text.stderr).toMatch(
126
+ /astryx: could not get Astryx 0\.6\.8 to match this project's Core/u,
127
+ );
128
+ });
129
+ });
130
+
131
+ describe('hand-off to the matched release', () => {
132
+ it("runs the project's own CLI even when its version differs from the project's Core", () => {
133
+ const cache = tempDir('cache');
134
+ seedRelease(path.join(cache, 'astryx'), '0.7.0');
135
+ const root = project(tempDir(), {'@astryxdesign/core': '0.7.0'});
136
+ installCore(root, '0.7.0');
137
+ installStandInCli(root, '0.6.8');
138
+ const result = run(root, ['upgrade', '--from', '0.6.8'], {cache});
139
+ expect(result.status).toBe(0);
140
+ expect(result.report()).toMatchObject({
141
+ version: '0.6.8',
142
+ args: ['upgrade', '--from', '0.6.8'],
143
+ });
144
+ });
145
+
146
+ it("runs the cached release equal to the project's Core when the project's own CLI cannot start", () => {
147
+ const cache = tempDir('cache');
148
+ seedRelease(path.join(cache, 'astryx'), '0.7.0');
149
+ const root = project(tempDir(), {'@astryxdesign/core': '0.7.0'});
150
+ installCore(root, '0.7.0');
151
+ installStandInCli(root, '0.6.8', {bin: false});
152
+ const result = run(root, ['upgrade', '--from', '0.6.8'], {cache});
153
+ expect(result.status).toBe(0);
154
+ expect(result.report()).toMatchObject({
155
+ version: '0.7.0',
156
+ args: ['upgrade', '--from', '0.6.8'],
157
+ });
158
+ expect(result.stderr.trim().split('\n')).toEqual([
159
+ "astryx: answered by CLI 0.7.0 with Core 0.7.0, the release that matches this project's Core",
160
+ 'stand-in 0.7.0',
161
+ ]);
162
+ });
163
+
164
+ it('answers a Core-only project itself, as released, with no line and no install', () => {
165
+ const cache = tempDir('cache');
166
+ const root = project(tempDir(), {'@astryxdesign/core': '0.7.0'});
167
+ installCore(root, '0.7.0');
168
+ const result = run(root, ['docs', '--json'], {cache});
169
+ expect(result.status).toBe(0);
170
+ expect(JSON.parse(result.stdout).meta.answeredBy).toEqual({
171
+ cli: SELF_VERSION,
172
+ core: '0.7.0',
173
+ copy: 'starting',
174
+ });
175
+ expect(fs.existsSync(path.join(cache, 'astryx', 'releases', '0.7.0'))).toBe(
176
+ false,
177
+ );
178
+ });
179
+ });
180
+
181
+ describe('answering itself', () => {
182
+ it('outside a project: one line in text mode, answeredBy in JSON', () => {
183
+ const empty = tempDir('empty');
184
+ const text = run(empty, ['--version']);
185
+ expect(text.stdout.trim()).toBe(SELF_VERSION);
186
+ expect(text.stderr.trim()).toBe(
187
+ `astryx: answered by CLI ${SELF_VERSION} with Core ${SELF_VERSION}, outside a project`,
188
+ );
189
+ const json = run(empty, ['docs', '--json']);
190
+ expect(json.stderr).toBe('');
191
+ expect(JSON.parse(json.stdout).meta.answeredBy).toEqual({
192
+ cli: SELF_VERSION,
193
+ core: SELF_VERSION,
194
+ copy: 'carried',
195
+ });
196
+ });
197
+ });
198
+
199
+ describe('answeredBy on every successful JSON result', () => {
200
+ it('covers the manifest, the version, and help', () => {
201
+ const empty = tempDir('empty');
202
+ for (const args of [
203
+ ['manifest', '--json'],
204
+ ['--json'],
205
+ ['--version', '--json'],
206
+ ['component', '--help', '--json'],
207
+ ]) {
208
+ const result = run(empty, args);
209
+ expect(result.status, args.join(' ')).toBe(0);
210
+ expect(
211
+ JSON.parse(result.stdout).meta?.answeredBy,
212
+ args.join(' '),
213
+ ).toEqual({
214
+ cli: SELF_VERSION,
215
+ core: SELF_VERSION,
216
+ copy: 'carried',
217
+ });
218
+ }
219
+ });
220
+ });
221
+
222
+ describe('computer-level commands are never handed to a project', () => {
223
+ it('answers init --user itself inside a project whose CLI is another release', () => {
224
+ const root = project(tempDir(), {'@astryxdesign/core': '0.6.8'});
225
+ installCore(root, '0.6.8');
226
+ installStandInCli(root, '0.6.8');
227
+ for (const args of [
228
+ ['init', '--user'],
229
+ ['--json', 'init', '--user', '--remove-agents'],
230
+ ]) {
231
+ const result = run(root, args);
232
+ expect(result.status, result.stderr).toBe(0);
233
+ expect(result.stdout).not.toContain('"version":"0.6.8"');
234
+ expect(result.stderr).not.toContain('stand-in');
235
+ }
236
+ const text = run(root, ['init', '--user']);
237
+ expect(text.stderr.split('\n')[0]).toBe(
238
+ `astryx: answered by CLI ${SELF_VERSION} with Core ${SELF_VERSION}, for this computer`,
239
+ );
240
+ });
241
+ });
242
+
243
+ describe('update notices through the real bin (FR10)', () => {
244
+ it('prints the project notice after the command, once a day, never with --json, in CI, or with NO_UPDATE_NOTIFIER', () => {
245
+ const [major, minor, patch] = CLI_VERSION.split(/[.-]/u).map(Number);
246
+ const newest = `${major}.${minor}.${patch + 1}`;
247
+ const root = project(tempDir(), {
248
+ '@astryxdesign/core': CLI_VERSION,
249
+ '@astryxdesign/cli': CLI_VERSION,
250
+ });
251
+ installCore(root, CLI_VERSION);
252
+ linkThisCliAsProjectCli(root);
253
+ writeJson(path.join(root, 'package-lock.json'), {lockfileVersion: 3});
254
+ const cache = tempDir('cache');
255
+ const home = tempDir('home');
256
+ writeJson(path.join(home, '.config', 'astryx', 'state.json'), {
257
+ latest: newest,
258
+ checkOkAt: 1,
259
+ });
260
+ const notice = [
261
+ `A new release of Astryx is available: ${CLI_VERSION} → ${newest}`,
262
+ `To upgrade, run: npm install @astryxdesign/core@${newest} @astryxdesign/cli@${newest}, then npx astryx upgrade --from ${CLI_VERSION} --apply`,
263
+ ];
264
+ for (const env of [{CI: 'true'}, {NO_UPDATE_NOTIFIER: '1'}]) {
265
+ const off = run(root, ['--version'], {cache, home, env});
266
+ expect(off.stderr).not.toContain('A new release');
267
+ }
268
+ const json = run(root, ['--version', '--json'], {cache, home});
269
+ expect(json.status).toBe(0);
270
+ expect(json.stderr).not.toContain('A new release');
271
+ const first = run(root, ['--version'], {cache, home});
272
+ expect(first.status).toBe(0);
273
+ expect(first.stdout.trim()).toBe(CLI_VERSION);
274
+ expect(first.stderr.trim().split('\n').slice(-2)).toEqual(notice);
275
+ const second = run(root, ['--version'], {cache, home});
276
+ expect(second.stderr).not.toContain('A new release');
277
+ });
278
+ });
@@ -0,0 +1,357 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file The first thing every run does: choose the release that answers and,
5
+ * when it is not this copy, hand the run to it (spec AST-081 FR2, FR6, FR9).
6
+ *
7
+ * Runs in the bin right after the Node version gate, before the CLI loads
8
+ * the folder's `astryx.config`, its integrations, or its Core. A hand-off
9
+ * starts the chosen CLI with exactly this run's arguments, working folder, and
10
+ * standard streams, adds nothing to its environment, options, or
11
+ * configuration, and ends this process with that CLI's exit status. The
12
+ * chosen copy makes the same choice and finds itself, so a run is handed off
13
+ * at most once.
14
+ *
15
+ * In text mode a run that a copy other than the project's own CLI answers
16
+ * prints one line on standard error naming the CLI and Core that answered; a
17
+ * run with `--json` prints none and carries `meta.answeredBy` instead. After a
18
+ * successful command, the copy that started the run prints any update notice
19
+ * (notices.mjs); a global copy also starts the daily update check, which
20
+ * installs nothing (global-state.mjs).
21
+ *
22
+ * @input the running copy, argv, the working folder
23
+ * @output returns when this copy answers; otherwise never returns
24
+ * @position foundation/release — called once by clients/cli/bin/astryx.mjs.
25
+ */
26
+
27
+ import {spawn} from 'node:child_process';
28
+ import {writeSync} from 'node:fs';
29
+ import {
30
+ chooseRelease,
31
+ isComputerLevel,
32
+ isGlobalCopy,
33
+ namesWhoAnswered,
34
+ packageRootOf,
35
+ runningCopy,
36
+ } from './choose.mjs';
37
+ import {ensureRelease, userCacheDir} from './cache.mjs';
38
+ import {
39
+ maybeFetchCore,
40
+ maybeKeepAgentText,
41
+ maybeStartCheck,
42
+ } from './global-state.mjs';
43
+ import {recordRunChoice} from './run-context.mjs';
44
+ import {noticeLines} from './notices.mjs';
45
+ import {cliDoor, runWithCliDoor} from '../env/cli-command.mjs';
46
+
47
+ /** Where each release's notes are published. */
48
+
49
+ /**
50
+ * The who-answered line for a run a copy other than the project's own CLI
51
+ * answers.
52
+ * @param {import('./choose.mjs').ReleaseChoice} choice
53
+ * @param {boolean} [computer] the command acts on the computer, not the folder
54
+ * @returns {string}
55
+ */
56
+ export function whoAnsweredLine(choice, computer = false) {
57
+ const core = choice.coreVersion ?? choice.version;
58
+ if (computer) {
59
+ return `astryx: answered by CLI ${choice.version} with Core ${core}, for this computer`;
60
+ }
61
+ return choice.copy === 'matched'
62
+ ? `astryx: answered by CLI ${choice.version} with Core ${core}, the release that matches this project's Core`
63
+ : `astryx: answered by CLI ${choice.version} with Core ${core}, outside a project`;
64
+ }
65
+
66
+ /**
67
+ * The line for a run handed to the project's own CLI.
68
+ * @param {string} projectVersion
69
+ * @param {string} startedIn the version of the copy the run started in
70
+ * @returns {string}
71
+ */
72
+ export function handedToProjectLine(projectVersion, startedIn) {
73
+ return `astryx: answered by this project's CLI ${projectVersion}, handed over from CLI ${startedIn}`;
74
+ }
75
+
76
+ /**
77
+ * One line on standard error while a release installs, so a slow download
78
+ * never looks like a hang. Text mode only.
79
+ * @param {'install' | 'wait'} event
80
+ * @param {string} version
81
+ */
82
+ export function progressLine(event, version) {
83
+ console.error(progressText(event, version));
84
+ }
85
+
86
+ /**
87
+ * The text of {@link progressLine}.
88
+ * @param {'install' | 'wait'} event
89
+ * @param {string} version
90
+ * @returns {string}
91
+ */
92
+ export function progressText(event, version) {
93
+ return event === 'install'
94
+ ? `astryx: installing CLI release ${version} into the per-user cache (once)...`
95
+ : `astryx: waiting for another run to finish installing CLI release ${version}...`;
96
+ }
97
+
98
+ /**
99
+ * The warning a run prints, in text mode, when the release that matches the
100
+ * project's Core cannot be obtained and the copy that answered before global
101
+ * mode answers instead (spec AST-081 FR2).
102
+ * @param {string} wanted the matched release, or the range it was read from
103
+ * @param {string} reason why it cannot be obtained
104
+ * @param {string} answering the version that answers instead
105
+ * @returns {string}
106
+ */
107
+ export function fallbackLine(wanted, reason, answering) {
108
+ return `astryx: could not get Astryx ${wanted} to match this project's Core (${reason}); answering with CLI ${answering} instead`;
109
+ }
110
+
111
+ /**
112
+ * Fail the run before any command starts, in the shape every command fails
113
+ * in: the JSON error envelope on stdout with `--json`, else `Error: …` on
114
+ * standard error. Exit status 1.
115
+ * @param {string} message
116
+ * @param {boolean} json
117
+ * @returns {never}
118
+ */
119
+ function failUnavailable(message, json) {
120
+ if (json) {
121
+ process.stdout.write(
122
+ `${JSON.stringify(
123
+ {apiVersion: 1, error: message, code: 'ERR_VERSIONS_UNAVAILABLE'},
124
+ null,
125
+ 2,
126
+ )}\n`,
127
+ );
128
+ } else {
129
+ console.error(`Error: ${message}`);
130
+ }
131
+ process.exit(1);
132
+ }
133
+
134
+ /**
135
+ * Choose who answers and hand off when it is not this copy.
136
+ * @param {{cliRoot: string, argv: string[]}} input argv after `node astryx`
137
+ * @returns {Promise<void>} resolves when this copy answers
138
+ */
139
+ export async function startRun({cliRoot, argv}) {
140
+ const json = argv.includes('--json');
141
+ const self = runningCopy(cliRoot);
142
+ const cacheDir = userCacheDir();
143
+ const global = isGlobalCopy(cliRoot, cacheDir);
144
+ if (global) {
145
+ try {
146
+ maybeStartCheck();
147
+ } catch {
148
+ // A check that cannot start never fails a run.
149
+ }
150
+ try {
151
+ // The global copy keeps the computer-level agent text on its own
152
+ // release's text, so it follows each upgrade of the copy (FR8).
153
+ maybeKeepAgentText({release: self});
154
+ } catch {
155
+ // Nor does the agent text.
156
+ }
157
+ // And its own Core, so component reads work offline outside a project
158
+ // after one online run (FR3). Started as the run ends, so it never takes
159
+ // the install lock from this run's own Core read, nor holds anything the
160
+ // command's caller waits on while the command runs.
161
+ process.once('exit', () => {
162
+ try {
163
+ maybeFetchCore({release: self, cacheDir});
164
+ } catch {
165
+ // Nor does the Core download.
166
+ }
167
+ });
168
+ }
169
+
170
+ const computer = isComputerLevel(argv);
171
+ /** @type {import('./choose.mjs').ReleaseChoice} */
172
+ let choice;
173
+ try {
174
+ choice = chooseRelease({
175
+ cwd: process.cwd(),
176
+ self,
177
+ cacheDir,
178
+ computer,
179
+ });
180
+ } catch {
181
+ choice = {
182
+ copy: 'carried',
183
+ answer: 'self',
184
+ version: self.version,
185
+ bin: null,
186
+ coreVersion: null,
187
+ inProject: false,
188
+ unavailable: null,
189
+ };
190
+ }
191
+
192
+ if (choice.unavailable && choice.copy === 'project') {
193
+ failUnavailable(
194
+ `This project's CLI ${choice.version} could not be started: ${choice.unavailable}. Reinstall the project's dependencies.`,
195
+ json,
196
+ );
197
+ }
198
+
199
+ // A host's door holds only inside the call that set it; notices print as
200
+ // the process exits, outside it, so the door this run started under is
201
+ // kept and set again for them (FR5).
202
+ const door = cliDoor();
203
+ /** FR10: after a successful command, at most once a day per notice. */
204
+ const notify = () => {
205
+ const print = () => {
206
+ for (const line of noticeLines({
207
+ cwd: process.cwd(),
208
+ self,
209
+ choice: computer ? {...choice, inProject: false} : choice,
210
+ json,
211
+ cacheDir,
212
+ })) {
213
+ // Synchronous: the process is exiting.
214
+ writeSync(2, `${line}\n`);
215
+ }
216
+ };
217
+ try {
218
+ if (door) runWithCliDoor(door, print);
219
+ else print();
220
+ } catch {
221
+ // A notice never fails a run.
222
+ }
223
+ };
224
+ /**
225
+ * The matched release cannot be obtained: the copy that started the run
226
+ * answers instead, as before global mode, announced (FR2).
227
+ * @param {string} reason
228
+ * @returns {Promise<void>}
229
+ */
230
+ const fallBack = async reason => {
231
+ recordRunChoice({
232
+ ...choice,
233
+ copy: 'fallback',
234
+ answer: 'self',
235
+ version: self.version,
236
+ bin: null,
237
+ coreVersion: null,
238
+ unavailable: null,
239
+ });
240
+ if (!json) {
241
+ console.error(fallbackLine(choice.version, reason, self.version));
242
+ }
243
+ process.once('exit', code => {
244
+ if (code === 0) notify();
245
+ });
246
+ };
247
+
248
+ if (choice.unavailable) {
249
+ await fallBack(choice.unavailable);
250
+ return;
251
+ }
252
+
253
+ if (choice.answer === 'self') {
254
+ recordRunChoice(choice);
255
+ // A project with no CLI of its own is answered as released, with no
256
+ // line; a hint after the command, once a day, says how to match it.
257
+ if (!json && (choice.copy === 'matched' || choice.copy === 'carried'))
258
+ console.error(whoAnsweredLine(choice, computer));
259
+ process.once('exit', code => {
260
+ if (code === 0) notify();
261
+ });
262
+ return;
263
+ }
264
+
265
+ let bin = choice.bin;
266
+ if (!bin) {
267
+ const release = ensureRelease(choice.version, {
268
+ cacheDir,
269
+ onProgress: json ? undefined : progressLine,
270
+ });
271
+ if (!('bin' in release)) {
272
+ await fallBack(release.reason);
273
+ return;
274
+ }
275
+ bin = release.bin;
276
+ }
277
+
278
+ if (!json) {
279
+ if (choice.copy === 'project') {
280
+ console.error(handedToProjectLine(choice.version, self.version));
281
+ } else if (!namesWhoAnswered(packageRootOf(bin))) {
282
+ console.error(whoAnsweredLine(choice, computer));
283
+ }
284
+ }
285
+
286
+ await handOff(bin, argv, choice, json, notify);
287
+ }
288
+
289
+ /**
290
+ * How long a hand-off waits after an interrupt before passing it on to a CLI
291
+ * that is still running. An interrupt sent to the whole process group (a
292
+ * terminal's Ctrl-C, a process manager) already reached that CLI, and one that
293
+ * stops within this time is never interrupted twice; one sent to this process
294
+ * alone (an agent's `kill('SIGINT')`) stops the run once it is passed on.
295
+ */
296
+ export const INTERRUPT_GRACE_MS = 5000;
297
+
298
+ /**
299
+ * Run `bin` as this run and end with its exit status.
300
+ * @param {string} bin
301
+ * @param {string[]} argv
302
+ * @param {import('./choose.mjs').ReleaseChoice} choice
303
+ * @param {boolean} json
304
+ * @param {() => void} [notify] prints update notices after a successful run
305
+ * @returns {Promise<never>}
306
+ */
307
+ function handOff(bin, argv, choice, json, notify = () => {}) {
308
+ return new Promise(() => {
309
+ const child = spawn(process.execPath, [bin, ...argv], {
310
+ stdio: 'inherit',
311
+ windowsHide: false,
312
+ });
313
+ /** @param {NodeJS.Signals} signal */
314
+ const forward = signal => {
315
+ try {
316
+ child.kill(signal);
317
+ } catch {
318
+ // The child is already gone.
319
+ }
320
+ };
321
+ const running = () => child.exitCode === null && child.signalCode === null;
322
+ // An interrupt is passed on only to a CLI still running after the grace
323
+ // (see INTERRUPT_GRACE_MS): one sent to this process alone then stops the
324
+ // run, and one the whole process group got is not delivered twice to a CLI
325
+ // that stops in time. On Windows, Ctrl-C reaches every process in the
326
+ // console and a forwarded SIGINT would end the child abruptly, so this
327
+ // process only outlives it.
328
+ const onInterrupt = () => {
329
+ if (process.platform === 'win32' || !running()) return;
330
+ setTimeout(() => {
331
+ if (running()) forward('SIGINT');
332
+ }, INTERRUPT_GRACE_MS).unref();
333
+ };
334
+ process.on('SIGINT', onInterrupt);
335
+ process.on('SIGTERM', forward);
336
+ process.on('SIGHUP', forward);
337
+ child.on('error', error => {
338
+ failUnavailable(
339
+ choice.copy === 'project'
340
+ ? `This project's CLI ${choice.version} could not be started: ${error.message}. Reinstall the project's dependencies.`
341
+ : `CLI release ${choice.version} could not be started: ${error.message}.`,
342
+ json,
343
+ );
344
+ });
345
+ child.on('exit', (code, signal) => {
346
+ process.off('SIGINT', onInterrupt);
347
+ process.off('SIGTERM', forward);
348
+ process.off('SIGHUP', forward);
349
+ if (signal) {
350
+ process.kill(process.pid, signal);
351
+ return;
352
+ }
353
+ if (code === 0) notify();
354
+ process.exit(code ?? 1);
355
+ });
356
+ });
357
+ }
@@ -0,0 +1,23 @@
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
+ * Renew the holder's record: the same process and computer, with a counter
6
+ * that changes on every renewal. Never throws.
7
+ * @param {string} lockDir
8
+ */
9
+ export function renewLock(lockDir: string): void;
10
+ /**
11
+ * Whether a lock held from another computer has gone silent: its record has
12
+ * not changed for `LEASE_SILENCE_MS` of this process's own observation.
13
+ * @param {string} lockDir
14
+ * @param {number} [now]
15
+ * @returns {boolean}
16
+ */
17
+ export function foreignLockIsSilent(lockDir: string, now?: number): boolean;
18
+ /** Forget what this process observed (tests). */
19
+ export function forgetObservedLocks(): void;
20
+ /** How often the holder renews its record while the package manager works. */
21
+ export const LEASE_RENEW_MS: 10000;
22
+ /** How long a record from another computer may go unrenewed, as observed. */
23
+ export const LEASE_SILENCE_MS: 60000;