@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,438 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file Core codemods ship inside the CLI, so a CLI older than the installed
5
+ * Core has none for the releases after its own version. Every result past the
6
+ * up-to-date exit says so in `cliBehindCore`, with the same upgrade pinned to
7
+ * the CLI release that matches the Core; a CLI that covers the range returns
8
+ * what it always did. The pinned command changes to the folder the run used
9
+ * and names the directory it scanned as an absolute path, both with symbolic
10
+ * links resolved, so a POSIX shell runs it there, with every argument
11
+ * unchanged, wherever it is pasted, even when upgrade() reached the project
12
+ * through a link.
13
+ */
14
+
15
+ import {describe, it, expect, beforeEach, afterEach, vi} from 'vitest';
16
+ import {execFileSync, spawnSync} from 'node:child_process';
17
+ import * as fs from 'node:fs';
18
+ import * as os from 'node:os';
19
+ import * as path from 'node:path';
20
+ import {upgrade} from '../upgrade.mjs';
21
+ import {logCliBehindCore} from '../status/status.mjs';
22
+ import {logger} from '../../logger.mjs';
23
+ import {CLI_ROOT} from '../../../foundation/fs/paths.mjs';
24
+ import {
25
+ commandInFolder,
26
+ getDlxPrefix,
27
+ quoteCommandArgument,
28
+ } from '../../../foundation/env/package-manager.mjs';
29
+
30
+ const SLOW = 30_000;
31
+
32
+ const CLI_VERSION = JSON.parse(
33
+ fs.readFileSync(path.join(CLI_ROOT, 'package.json'), 'utf8'),
34
+ ).version;
35
+ const [MAJOR, MINOR] = CLI_VERSION.split('.').map(Number);
36
+ /** A Core release after this CLI: it has no codemods for it. */
37
+ const NEWER_CORE = `${MAJOR}.${MINOR + 1}.0`;
38
+
39
+ /** A source file a 0.7.0 codemod rewrites (`emptySearchResultsText`). */
40
+ const TYPEAHEAD_SOURCE =
41
+ "import {Typeahead} from '@astryxdesign/core';\n" +
42
+ 'export const x = <Typeahead emptySearchResultsText="None" />;\n';
43
+
44
+ let dir;
45
+
46
+ /** @param {string} version @param {string} [packageName] */
47
+ function installCore(version, packageName = '@astryxdesign/core') {
48
+ const core = path.join(dir, 'node_modules', ...packageName.split('/'));
49
+ fs.mkdirSync(core, {recursive: true});
50
+ fs.writeFileSync(
51
+ path.join(core, 'package.json'),
52
+ JSON.stringify({name: packageName, version}),
53
+ );
54
+ }
55
+
56
+ /** @param {string} rel @param {string} body */
57
+ function write(rel, body) {
58
+ const file = path.join(dir, rel);
59
+ fs.mkdirSync(path.dirname(file), {recursive: true});
60
+ fs.writeFileSync(file, body);
61
+ }
62
+
63
+ /**
64
+ * A path in the project as a process running there sees it: every symbolic
65
+ * link resolved.
66
+ * @param {string} rel
67
+ */
68
+ function inProject(rel) {
69
+ return path.join(fs.realpathSync(dir), rel);
70
+ }
71
+
72
+ /**
73
+ * The pinned command for this project, as the CLI prints it.
74
+ * @param {...string} args the arguments after `upgrade`
75
+ */
76
+ function pinned(...args) {
77
+ return commandInFolder(
78
+ fs.realpathSync(dir),
79
+ [
80
+ getDlxPrefix(dir),
81
+ `@astryxdesign/cli@${NEWER_CORE}`,
82
+ 'upgrade',
83
+ ...args.map(arg => quoteCommandArgument(arg)),
84
+ ].join(' '),
85
+ );
86
+ }
87
+
88
+ /**
89
+ * Stand-ins for the package runners: each prints the folder it runs in and the
90
+ * arguments the shell passed.
91
+ */
92
+ const RUNNERS = ['npx', 'pnpm', 'yarn', 'bunx']
93
+ .map(
94
+ name =>
95
+ `${name}() { "$0" -e 'process.stdout.write(JSON.stringify({cwd: process.cwd(), argv: process.argv.slice(1)}))' -- "$@"; }`,
96
+ )
97
+ .join('; ');
98
+
99
+ /**
100
+ * What a POSIX shell runs for `command` pasted in `cwd`: the folder the
101
+ * package runner ran in, and its arguments from the pinned CLI package on.
102
+ * @param {string} command
103
+ * @param {string} cwd the folder the command is pasted in
104
+ * @returns {{cwd: string, args: string[]}}
105
+ */
106
+ function runInShell(command, cwd) {
107
+ const ran = JSON.parse(
108
+ execFileSync(
109
+ '/bin/sh',
110
+ ['-c', `${RUNNERS}; ${command}`, process.execPath],
111
+ {
112
+ cwd,
113
+ encoding: 'utf8',
114
+ },
115
+ ),
116
+ );
117
+ return {
118
+ cwd: ran.cwd,
119
+ args: ran.argv.slice(ran.argv.indexOf(`@astryxdesign/cli@${NEWER_CORE}`)),
120
+ };
121
+ }
122
+
123
+ beforeEach(() => {
124
+ dir = fs.mkdtempSync(path.join(os.tmpdir(), 'upg-cli-behind-'));
125
+ write('package.json', JSON.stringify({name: 'consumer', version: '1.0.0'}));
126
+ write('src/index.tsx', TYPEAHEAD_SOURCE);
127
+ });
128
+
129
+ afterEach(() => fs.rmSync(dir, {recursive: true, force: true}));
130
+
131
+ describe('a CLI older than the installed Core', {timeout: SLOW}, () => {
132
+ // The defect: the range past this CLI has no codemods here, so the run
133
+ // reported `no_codemods` (and "already up to date") and exited 0, while the
134
+ // project still needed the newer releases' migrations.
135
+ it('marks a range this CLI has no codemods for, and names the CLI that does', async () => {
136
+ installCore(NEWER_CORE);
137
+
138
+ const res = await upgrade({from: CLI_VERSION, apply: true}, {cwd: dir});
139
+
140
+ expect(res.type).toBe('upgrade.status');
141
+ expect(res.data.status).toBe('no_codemods');
142
+ expect(res.data.cliBehindCore).toEqual({
143
+ cliVersion: CLI_VERSION,
144
+ command: pinned(
145
+ '--from',
146
+ CLI_VERSION,
147
+ '--path',
148
+ inProject('src'),
149
+ '--apply',
150
+ ),
151
+ });
152
+ });
153
+
154
+ it('runs the codemods it has for a range it covers only in part, and marks the rest', async () => {
155
+ installCore(NEWER_CORE);
156
+
157
+ const res = await upgrade({from: '0.6.8', apply: true}, {cwd: dir});
158
+
159
+ expect(res.type).toBe('upgrade.run');
160
+ expect(res.data.codemods).toBeGreaterThan(0);
161
+ expect(fs.readFileSync(path.join(dir, 'src/index.tsx'), 'utf8')).toContain(
162
+ 'emptySearchText="None"',
163
+ );
164
+ expect(res.data.cliBehindCore).toEqual({
165
+ cliVersion: CLI_VERSION,
166
+ command: pinned('--from', '0.6.8', '--path', inProject('src'), '--apply'),
167
+ });
168
+ });
169
+
170
+ it('repeats the invocation in the pinned command', async () => {
171
+ installCore(NEWER_CORE);
172
+ write('my src/index.tsx', TYPEAHEAD_SOURCE);
173
+ // An installed integration the run names, so --integration is repeated.
174
+ write(
175
+ 'node_modules/@acme/widgets/package.json',
176
+ JSON.stringify({name: '@acme/widgets', version: '1.0.0'}),
177
+ );
178
+ write(
179
+ 'node_modules/@acme/widgets/astryx.integration.mjs',
180
+ 'export default {};\n',
181
+ );
182
+
183
+ const res = await upgrade(
184
+ {
185
+ from: CLI_VERSION,
186
+ path: 'my src',
187
+ codemod: 'only-this',
188
+ skipCodemod: ['first', 'second'],
189
+ integration: ['@acme/widgets'],
190
+ force: true,
191
+ installDeps: true,
192
+ },
193
+ {cwd: dir},
194
+ );
195
+
196
+ expect(res.data.cliBehindCore?.command).toBe(
197
+ pinned(
198
+ '--from',
199
+ CLI_VERSION,
200
+ '--path',
201
+ inProject('my src'),
202
+ '--codemod',
203
+ 'only-this',
204
+ '--skip-codemod',
205
+ 'first',
206
+ '--skip-codemod',
207
+ 'second',
208
+ '--integration',
209
+ '@acme/widgets',
210
+ '--force',
211
+ '--install-deps',
212
+ ),
213
+ );
214
+ });
215
+
216
+ // Where an upgrade runs decides what it finds: the installed Core is read
217
+ // from that folder, and --path must stay inside it. The command printed
218
+ // without a folder failed when pasted anywhere else: ERR_PATH_TRAVERSAL from
219
+ // a subfolder, ERR_VERSION_DETECT from a parent. A package runner can also
220
+ // move the run: Yarn 1 runs `yarn astryx` at the project root.
221
+ it('changes to the folder the run used and names the directory it scanned as an absolute path', async () => {
222
+ installCore(NEWER_CORE);
223
+ write('app/index.tsx', TYPEAHEAD_SOURCE);
224
+ const project = fs.realpathSync(dir);
225
+
226
+ for (const [given, scanned] of [
227
+ [undefined, inProject('src')],
228
+ ['.', project],
229
+ ['app', inProject('app')],
230
+ ['./app/', inProject('app')],
231
+ ]) {
232
+ const res = await upgrade(
233
+ {from: CLI_VERSION, ...(given ? {path: given} : {})},
234
+ {cwd: dir},
235
+ );
236
+ const command = /** @type {string} */ (res.data.cliBehindCore?.command);
237
+
238
+ expect(command).toBe(pinned('--from', CLI_VERSION, '--path', scanned));
239
+ if (process.platform !== 'win32') {
240
+ for (const pastedIn of [
241
+ path.join(dir, 'app'),
242
+ path.dirname(dir),
243
+ path.parse(dir).root,
244
+ ]) {
245
+ const ran = runInShell(command, pastedIn);
246
+ expect(ran.cwd).toBe(project);
247
+ expect(ran.args[ran.args.indexOf('--path') + 1]).toBe(scanned);
248
+ }
249
+ }
250
+ }
251
+ });
252
+
253
+ it.skipIf(process.platform === 'win32')(
254
+ 'quotes the command so a POSIX shell enters the folder and passes every argument unchanged',
255
+ async () => {
256
+ fs.rmSync(dir, {recursive: true, force: true});
257
+ dir = fs.mkdtempSync(
258
+ path.join(os.tmpdir(), 'upg cost $5 `date` \'one\' "two" !x -'),
259
+ );
260
+ write(
261
+ 'package.json',
262
+ JSON.stringify({name: 'consumer', version: '1.0.0'}),
263
+ );
264
+ write('src/index.tsx', TYPEAHEAD_SOURCE);
265
+ installCore(NEWER_CORE);
266
+
267
+ const res = await upgrade(
268
+ {
269
+ from: CLI_VERSION,
270
+ skipCodemod: ["it's", 'a$b c'],
271
+ apply: true,
272
+ },
273
+ {cwd: dir},
274
+ );
275
+
276
+ const ran = runInShell(
277
+ /** @type {string} */ (res.data.cliBehindCore?.command),
278
+ path.dirname(dir),
279
+ );
280
+
281
+ expect(ran.cwd).toBe(fs.realpathSync(dir));
282
+ expect(ran.args).toEqual([
283
+ `@astryxdesign/cli@${NEWER_CORE}`,
284
+ 'upgrade',
285
+ '--from',
286
+ CLI_VERSION,
287
+ '--path',
288
+ inProject('src'),
289
+ '--skip-codemod',
290
+ "it's",
291
+ '--skip-codemod',
292
+ 'a$b c',
293
+ '--apply',
294
+ ]);
295
+ },
296
+ );
297
+ });
298
+
299
+ /** The CLI's own bin, which the stand-in runners below run. */
300
+ const BIN = path.join(CLI_ROOT, 'clients', 'cli', 'bin', 'astryx.mjs');
301
+
302
+ /**
303
+ * Stand-ins for the package runners: each drops its own words and the pinned
304
+ * package, then runs this CLI with the rest of the command.
305
+ */
306
+ const RUNS_THIS_CLI = /** @type {Array<[string, number]>} */ ([
307
+ ['npx', 1],
308
+ ['bunx', 1],
309
+ ['pnpm', 2],
310
+ ['yarn', 2],
311
+ ])
312
+ .map(
313
+ ([name, words]) =>
314
+ `${name}() { shift ${words}; "$0" ${quoteCommandArgument(BIN)} "$@"; }`,
315
+ )
316
+ .join('; ');
317
+
318
+ // A shell's `cd` follows a symbolic link, but the CLI it starts reads its
319
+ // folder with every link resolved and checks --path against that. A command
320
+ // that kept the link upgrade() was given exited 1 with ERR_PATH_TRAVERSAL.
321
+ describe.skipIf(process.platform === 'win32')(
322
+ 'a project upgrade() reaches through a symbolic link',
323
+ {timeout: SLOW},
324
+ () => {
325
+ /** @type {string} */
326
+ let base;
327
+
328
+ beforeEach(() => {
329
+ fs.rmSync(dir, {recursive: true, force: true});
330
+ base = fs.mkdtempSync(path.join(os.tmpdir(), 'upg-link-'));
331
+ dir = path.join(base, 'real', 'my app');
332
+ write(
333
+ 'package.json',
334
+ JSON.stringify({name: 'consumer', version: '1.0.0'}),
335
+ );
336
+ write('src/index.tsx', TYPEAHEAD_SOURCE);
337
+ installCore(NEWER_CORE);
338
+ });
339
+
340
+ afterEach(() => fs.rmSync(base, {recursive: true, force: true}));
341
+
342
+ /**
343
+ * Run upgrade() from `cwd`, then the command it prints, in /bin/sh, from
344
+ * `cwd` and from the folder above the project.
345
+ * @param {string} cwd
346
+ */
347
+ async function expectPrintedCommandToRun(cwd) {
348
+ const res = await upgrade({from: CLI_VERSION}, {cwd});
349
+ const command = /** @type {string} */ (res.data.cliBehindCore?.command);
350
+
351
+ for (const pastedIn of [cwd, base]) {
352
+ const ran = spawnSync(
353
+ '/bin/sh',
354
+ ['-c', `${RUNS_THIS_CLI}; ${command}`, process.execPath],
355
+ {cwd: pastedIn, encoding: 'utf8'},
356
+ );
357
+
358
+ expect(ran.status, ran.stderr).toBe(0);
359
+ expect(ran.stdout).toContain(`Installed target: ${NEWER_CORE}`);
360
+ expect(ran.stdout).toContain(`Scanning ${inProject('src')} `);
361
+ }
362
+ expect(command).toBe(
363
+ pinned('--from', CLI_VERSION, '--path', inProject('src')),
364
+ );
365
+ }
366
+
367
+ it('prints a command that runs, from a folder reached through a link above the project', async () => {
368
+ fs.symlinkSync(path.join(base, 'real'), path.join(base, 'link'), 'dir');
369
+
370
+ await expectPrintedCommandToRun(path.join(base, 'link', 'my app'));
371
+ });
372
+
373
+ it('prints a command that runs, through a project root that is a link', async () => {
374
+ fs.symlinkSync(dir, path.join(base, 'app'), 'dir');
375
+
376
+ await expectPrintedCommandToRun(path.join(base, 'app'));
377
+ });
378
+ },
379
+ );
380
+
381
+ describe('a CLI that covers the range', {timeout: SLOW}, () => {
382
+ it('adds nothing when the installed Core is this CLI release', async () => {
383
+ installCore(CLI_VERSION);
384
+
385
+ const res = await upgrade({from: '0.6.8'}, {cwd: dir});
386
+
387
+ expect(res.type).toBe('upgrade.run');
388
+ expect('cliBehindCore' in res.data).toBe(false);
389
+ });
390
+
391
+ it('adds nothing when --from is already the installed Core', async () => {
392
+ installCore(NEWER_CORE);
393
+
394
+ const res = await upgrade({from: NEWER_CORE}, {cwd: dir});
395
+
396
+ expect(res.data.status).toBe('up_to_date');
397
+ expect('cliBehindCore' in res.data).toBe(false);
398
+
399
+ const forced = await upgrade({from: NEWER_CORE, force: true}, {cwd: dir});
400
+
401
+ expect(forced.data.status).toBe('no_codemods');
402
+ expect('cliBehindCore' in forced.data).toBe(false);
403
+ });
404
+
405
+ it('adds nothing for legacy @xds/core, which shares no version with a CLI release', async () => {
406
+ installCore(NEWER_CORE, '@xds/core');
407
+
408
+ const res = await upgrade({from: CLI_VERSION}, {cwd: dir});
409
+
410
+ expect(res.data.status).toBe('no_codemods');
411
+ expect('cliBehindCore' in res.data).toBe(false);
412
+ });
413
+ });
414
+
415
+ describe('logCliBehindCore: the warning names the shell its command is written for', () => {
416
+ it.each([
417
+ ['win32', 'Run it in PowerShell with the CLI that matches your Core:'],
418
+ ['linux', 'Run it with the CLI that matches your Core:'],
419
+ ['darwin', 'Run it with the CLI that matches your Core:'],
420
+ ])('on %s', (platform, ending) => {
421
+ const warn = vi.spyOn(logger, 'warn').mockImplementation(() => {});
422
+ try {
423
+ logCliBehindCore(
424
+ {cliVersion: '0.7.0', command: 'cd x; npx y'},
425
+ '0.8.0',
426
+ platform,
427
+ );
428
+ expect(warn.mock.calls.map(([line]) => String(line))).toEqual([
429
+ expect.stringMatching(
430
+ new RegExp(`${ending.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&')}$`, 'u'),
431
+ ),
432
+ ' cd x; npx y',
433
+ ]);
434
+ } finally {
435
+ warn.mockRestore();
436
+ }
437
+ });
438
+ });
@@ -15,12 +15,18 @@
15
15
  * ShadCN-copied compositions → post-codemod hooks → render + refresh agent docs
16
16
  * from final post-upgrade state. Integration DISCOVERY errors skip that
17
17
  * integration; execution errors abort before the agent-doc write.
18
+ *
19
+ * Core codemods ship inside the CLI, so a CLI older than the installed Core
20
+ * has none for the releases after its own version. Every result past the
21
+ * up-to-date exit then carries `cliBehindCore`, and no human line says the
22
+ * upgrade finished; the warning names the same upgrade on the matching CLI.
18
23
  */
19
24
 
20
25
  import * as fs from 'node:fs';
21
26
  import * as path from 'node:path';
22
27
  import {
23
28
  detectInstalledTargetVersion,
29
+ detectCliBehindCore,
24
30
  prepareAgentDocsRefresh,
25
31
  applyAgentDocsRefresh,
26
32
  inspectAgentDocs,
@@ -42,9 +48,11 @@ import {
42
48
  statusUpToDate,
43
49
  statusNoCodemods,
44
50
  statusConfigFixable,
51
+ logCliBehindCore,
45
52
  } from '../status/status.mjs';
46
53
  import {semverGte} from '../../../foundation/env/semver.mjs';
47
54
  import {getCliInvocation} from '../../../foundation/env/package-manager.mjs';
55
+ import {isOutsideProject} from '../../../foundation/env/project-scope.mjs';
48
56
  import {ERROR_CODES} from '../../../foundation/response/error-codes.mjs';
49
57
  import {AstryxError} from '../../error.mjs';
50
58
  import {logger} from '../../logger.mjs';
@@ -146,7 +154,10 @@ export async function run(options = {}, {cwd = process.cwd()} = {}) {
146
154
  const currentVersion = /** @type {string} */ (options.from);
147
155
  const installed = detectInstalledTargetVersion(cwd);
148
156
  if (!installed) {
149
- const msg = `Could not find installed @astryxdesign/core (or legacy @xds/core). Install the target version first, then rerun \`${getCliInvocation()} upgrade --from <old-version>\`.`;
157
+ // Outside a project there is no version to install (spec AST-081 FR3).
158
+ const msg = isOutsideProject(cwd)
159
+ ? 'Could not find installed @astryxdesign/core (or legacy @xds/core). This folder is outside a project; upgrade migrates the code of an app project that installs @astryxdesign/core.'
160
+ : `Could not find installed @astryxdesign/core (or legacy @xds/core). Install the target version first, then rerun \`${getCliInvocation()} upgrade --from <old-version>\`.`;
150
161
  logger.error(msg);
151
162
  logger.log('Aborted\n');
152
163
  throw new AstryxError(msg, undefined, ERROR_CODES.ERR_VERSION_DETECT);
@@ -195,6 +206,17 @@ export async function run(options = {}, {cwd = process.cwd()} = {}) {
195
206
  });
196
207
  }
197
208
 
209
+ // Past the up-to-date exit, codemods are needed through targetVersion. A
210
+ // CLI older than that has none for the releases after its own version.
211
+ const cliBehindCore = detectCliBehindCore({
212
+ from: currentVersion,
213
+ to: targetVersion,
214
+ packageName: installed.packageName,
215
+ options,
216
+ cwd,
217
+ sourcePath: path_,
218
+ });
219
+
198
220
  const versionManifests = await getCoreVersionManifests(
199
221
  currentVersion,
200
222
  targetVersion,
@@ -243,6 +265,7 @@ export async function run(options = {}, {cwd = process.cwd()} = {}) {
243
265
  skipCodemods,
244
266
  root: cwd,
245
267
  protection,
268
+ coversTarget: !cliBehindCore,
246
269
  });
247
270
  const coreResult =
248
271
  codemodResult && 'totalFilesChanged' in codemodResult
@@ -271,6 +294,7 @@ export async function run(options = {}, {cwd = process.cwd()} = {}) {
271
294
  for (const item of protectedFiles) {
272
295
  logger.error(` ${item.file} — ${item.declarations.join('; ')}`);
273
296
  }
297
+ if (cliBehindCore) logCliBehindCore(cliBehindCore, targetVersion);
274
298
  logger.log('Upgrade incomplete: protected changes remain\n');
275
299
  return {
276
300
  type: 'upgrade.run',
@@ -299,6 +323,7 @@ export async function run(options = {}, {cwd = process.cwd()} = {}) {
299
323
  complete: false,
300
324
  errorCode: 'ERR_CODEMOD_PROTECTED',
301
325
  errors: coreResult?.errors ?? [],
326
+ ...(cliBehindCore ? {cliBehindCore} : {}),
302
327
  },
303
328
  };
304
329
  }
@@ -316,6 +341,7 @@ export async function run(options = {}, {cwd = process.cwd()} = {}) {
316
341
  to: targetVersion,
317
342
  configError: configErr.message,
318
343
  configCodemods: coreConfigCodemodNames,
344
+ cliBehindCore,
319
345
  agentDocs:
320
346
  /** @type {import('../upgrade.type.mjs').AgentDocsSummary} */ ({
321
347
  status: inspection.status,
@@ -381,6 +407,7 @@ export async function run(options = {}, {cwd = process.cwd()} = {}) {
381
407
  to: targetVersion,
382
408
  agentDocs,
383
409
  ...(registryResult ? {registryCompositions: registryResult.summary} : {}),
410
+ cliBehindCore,
384
411
  });
385
412
  }
386
413
 
@@ -620,6 +647,7 @@ export async function run(options = {}, {cwd = process.cwd()} = {}) {
620
647
  }
621
648
  receipt.errors = finalErrors;
622
649
  if (registryResult) receipt.registryCompositions = registryResult.summary;
650
+ if (cliBehindCore) receipt.cliBehindCore = cliBehindCore;
623
651
 
624
652
  if (protectedFiles.length > 0) {
625
653
  logger.error(
@@ -655,7 +683,12 @@ export async function run(options = {}, {cwd = process.cwd()} = {}) {
655
683
  }
656
684
 
657
685
  const registryOk = receipt.registryCompositions?.ok ?? true;
658
- const done = apply ? 'Upgrade complete' : 'Dry run complete';
686
+ if (cliBehindCore) logCliBehindCore(cliBehindCore, targetVersion);
687
+ const done = cliBehindCore
688
+ ? 'Upgrade incomplete'
689
+ : apply
690
+ ? 'Upgrade complete'
691
+ : 'Dry run complete';
659
692
  logger.log(
660
693
  protectedFiles.length > 0
661
694
  ? 'Upgrade incomplete: protected changes remain\n'
@@ -13,27 +13,43 @@ export function statusUpToDate({ from, to, agentDocs, registryCompositions }: {
13
13
  registryCompositions?: import("../upgrade.type.mjs").RegistryCompositionSummary;
14
14
  }): import("../upgrade.type.mjs").UpgradeStatusResponse;
15
15
  /**
16
- * No core or integration codemods apply to the requested version range.
17
- * @param {{from: string, to: string, agentDocs: import('../upgrade.type.mjs').AgentDocsSummary, registryCompositions?: import('../upgrade.type.mjs').RegistryCompositionSummary}} data
16
+ * The one warning for a CLI older than the installed Core: it has no codemods
17
+ * for the releases after its own version, and the pinned command does. The
18
+ * command gets a line of its own, so it copies exactly, whatever quotes or
19
+ * backticks its arguments carry. On Windows it is written for PowerShell, and
20
+ * the warning says so.
21
+ * @param {import('../upgrade.type.mjs').CliBehindCore} cliBehindCore
22
+ * @param {string} to the installed Core version
23
+ * @param {string} [platform] the `process.platform` the command was printed
24
+ * for, as `quoteCommandArgument` and `commandInFolder` take it
25
+ */
26
+ export function logCliBehindCore({ cliVersion, command }: import("../upgrade.type.mjs").CliBehindCore, to: string, platform?: string): void;
27
+ /**
28
+ * No core or integration codemods apply to the requested version range. When
29
+ * the CLI is older than the installed Core, that only means this CLI has none:
30
+ * the result carries `cliBehindCore` and the human lines say so.
31
+ * @param {{from: string, to: string, agentDocs: import('../upgrade.type.mjs').AgentDocsSummary, registryCompositions?: import('../upgrade.type.mjs').RegistryCompositionSummary, cliBehindCore?: import('../upgrade.type.mjs').CliBehindCore | null}} data
18
32
  * @returns {import('../upgrade.type.mjs').UpgradeStatusResponse}
19
33
  */
20
- export function statusNoCodemods({ from, to, agentDocs, registryCompositions }: {
34
+ export function statusNoCodemods({ from, to, agentDocs, registryCompositions, cliBehindCore }: {
21
35
  from: string;
22
36
  to: string;
23
37
  agentDocs: import("../upgrade.type.mjs").AgentDocsSummary;
24
38
  registryCompositions?: import("../upgrade.type.mjs").RegistryCompositionSummary;
39
+ cliBehindCore?: import("../upgrade.type.mjs").CliBehindCore | null;
25
40
  }): import("../upgrade.type.mjs").UpgradeStatusResponse;
26
41
  /**
27
42
  * DRY-RUN only: the consumer's astryx.config fails strict validation, but a
28
43
  * pending core CONFIG codemod previewed a change that would repair it. Preview
29
44
  * the fix + report the exact `--apply` command; integrations are skipped here.
30
- * @param {{from: string, to: string, configError: string, configCodemods: string[], agentDocs: import('../upgrade.type.mjs').AgentDocsSummary}} data
45
+ * @param {{from: string, to: string, configError: string, configCodemods: string[], agentDocs: import('../upgrade.type.mjs').AgentDocsSummary, cliBehindCore?: import('../upgrade.type.mjs').CliBehindCore | null}} data
31
46
  * @returns {import('../upgrade.type.mjs').UpgradeStatusResponse}
32
47
  */
33
- export function statusConfigFixable({ from, to, configError, configCodemods, agentDocs }: {
48
+ export function statusConfigFixable({ from, to, configError, configCodemods, agentDocs, cliBehindCore }: {
34
49
  from: string;
35
50
  to: string;
36
51
  configError: string;
37
52
  configCodemods: string[];
38
53
  agentDocs: import("../upgrade.type.mjs").AgentDocsSummary;
54
+ cliBehindCore?: import("../upgrade.type.mjs").CliBehindCore | null;
39
55
  }): import("../upgrade.type.mjs").UpgradeStatusResponse;