@astryxdesign/cli 0.6.3-canary.ea2f048 → 0.6.3-canary.ee13359

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 (571) hide show
  1. package/README.md +114 -76
  2. package/api/blog/blog.doc.mjs +1 -0
  3. package/api/build/build.doc.mjs +4 -3
  4. package/api/build/build.test.mjs +14 -3
  5. package/api/build/build.type.d.mts +36 -2
  6. package/api/build/build.type.mjs +25 -4
  7. package/api/build/help/help.d.mts +8 -5
  8. package/api/build/help/help.mjs +56 -6
  9. package/api/component/_adapter.d.mts +6 -12
  10. package/api/component/_adapter.mjs +20 -10
  11. package/api/component/component.doc.mjs +13 -3
  12. package/api/component/component.mjs +91 -14
  13. package/api/component/component.test.mjs +38 -0
  14. package/api/component/component.type.d.mts +22 -11
  15. package/api/component/component.type.mjs +32 -24
  16. package/api/component/detail/blocks/blocks.d.mts +2 -1
  17. package/api/component/detail/blocks/blocks.mjs +4 -3
  18. package/api/component/list/list.d.mts +0 -5
  19. package/api/component/list/list.mjs +40 -11
  20. package/api/discover/discover.doc.mjs +2 -1
  21. package/api/discover/discover.type.d.mts +4 -4
  22. package/api/discover/discover.type.mjs +10 -10
  23. package/api/docs/_adapter.d.mts +230 -41
  24. package/api/docs/_adapter.mjs +734 -108
  25. package/api/docs/compiled-topics.test.mjs +78 -0
  26. package/api/docs/detail/detail.mjs +22 -63
  27. package/api/docs/detail/section/section.d.mts +1 -1
  28. package/api/docs/detail/section/section.mjs +54 -19
  29. package/api/docs/detail/section/section.test.mjs +50 -0
  30. package/api/docs/docs.d.mts +10 -3
  31. package/api/docs/docs.doc.mjs +54 -16
  32. package/api/docs/docs.mjs +53 -10
  33. package/api/docs/docs.test.mjs +164 -4
  34. package/api/docs/docs.type.d.mts +205 -5
  35. package/api/docs/docs.type.mjs +137 -11
  36. package/api/docs/index/index.d.mts +18 -0
  37. package/api/docs/index/index.mjs +40 -0
  38. package/api/docs/index/index.test.mjs +62 -0
  39. package/api/docs/integration-tree.test.mjs +547 -0
  40. package/api/docs/integrationDocs.test.mjs +114 -8
  41. package/api/docs/list/list.mjs +28 -12
  42. package/api/docs/node/node.d.mts +43 -0
  43. package/api/docs/node/node.mjs +192 -0
  44. package/api/doctor/doctor.d.mts +99 -1
  45. package/api/doctor/doctor.doc.mjs +1 -0
  46. package/api/doctor/doctor.mjs +544 -1
  47. package/api/doctor/doctor.test.mjs +610 -1
  48. package/api/gap-report/gap-report.doc.mjs +8 -4
  49. package/api/hook/_adapter.mjs +19 -5
  50. package/api/hook/hook.doc.mjs +1 -0
  51. package/api/hook/hook.type.d.mts +3 -3
  52. package/api/hook/hook.type.mjs +11 -11
  53. package/api/hook/list/list.d.mts +2 -2
  54. package/api/hook/list/list.mjs +69 -17
  55. package/api/index.d.mts +1 -1
  56. package/api/index.mjs +1 -0
  57. package/api/init/init.doc.mjs +6 -1
  58. package/api/init/init.test.mjs +41 -1
  59. package/api/init/remove/remove.mjs +1 -1
  60. package/api/init/run/run.mjs +20 -10
  61. package/api/integration/add-contribution.component-names.test.mjs +120 -0
  62. package/api/integration/add-contribution.d.mts +2 -1
  63. package/api/integration/add-contribution.mjs +130 -15
  64. package/api/integration/add-contribution.test.mjs +258 -7
  65. package/api/integration/add-theme.mjs +34 -64
  66. package/api/integration/add-theme.test.mjs +105 -21
  67. package/api/integration/authoring-checks.mjs +108 -31
  68. package/api/integration/authoring-checks.test.mjs +171 -6
  69. package/api/integration/authoring-checks.type.d.mts +3 -1
  70. package/api/integration/authoring-checks.type.mjs +3 -1
  71. package/api/integration/integration-authoring.type.d.mts +3 -1
  72. package/api/integration/integration-authoring.type.mjs +2 -0
  73. package/api/integration/integration-block-exports.test.mjs +10 -6
  74. package/api/integration/integrationAdd.doc.mjs +14 -4
  75. package/api/integration/integrationAddAgentDoc.doc.mjs +1 -0
  76. package/api/integration/integrationAddCodemod.doc.mjs +1 -0
  77. package/api/integration/integrationAddComponent.doc.mjs +2 -1
  78. package/api/integration/integrationAddDoc.doc.mjs +8 -1
  79. package/api/integration/integrationAddTemplate.doc.mjs +1 -0
  80. package/api/integration/integrationAddTheme.doc.mjs +6 -5
  81. package/api/integration/integrationComponentConflicts.doc.mjs +1 -0
  82. package/api/integration/integrationDocConflicts.doc.mjs +2 -1
  83. package/api/integration/integrationPackCheck.doc.mjs +2 -1
  84. package/api/integration/integrationTemplateConflicts.doc.mjs +7 -5
  85. package/api/integration/pack-check.mjs +83 -7
  86. package/api/integration/pack-check.test.mjs +387 -47
  87. package/api/integration/pack-check.type.d.mts +26 -2
  88. package/api/integration/pack-check.type.mjs +14 -1
  89. package/api/integration/summarizeIssues.doc.mjs +1 -0
  90. package/api/integration/validate-integration-fixes.test.mjs +1389 -0
  91. package/api/integration/validate-integration.mjs +52 -102
  92. package/api/integration/validate-integration.test.mjs +179 -26
  93. package/api/integration/validate-unread-theme-folders.test.mjs +110 -0
  94. package/api/integration/validateIntegration.doc.mjs +3 -2
  95. package/api/json/assertResponse.doc.mjs +1 -0
  96. package/api/json/envelope-types.test.mjs +76 -0
  97. package/api/json/index.ts +2 -0
  98. package/api/json/isError.doc.mjs +1 -0
  99. package/api/json/parseResponse.doc.mjs +3 -2
  100. package/api/layout/_adapter.mjs +20 -5
  101. package/api/layout/expand/expand.mjs +7 -5
  102. package/api/layout/expand/expand.path-safety.test.mjs +53 -0
  103. package/api/layout/grammar/grammar.mjs +2 -1
  104. package/api/layout/layoutCheck.doc.mjs +1 -0
  105. package/api/layout/layoutExpand.doc.mjs +2 -1
  106. package/api/layout/layoutGrammar.doc.mjs +1 -0
  107. package/api/search/search-return-type.test.mjs +54 -0
  108. package/api/search/search.d.mts +29 -11
  109. package/api/search/search.doc.mjs +8 -2
  110. package/api/search/search.mjs +337 -41
  111. package/api/search/search.test.mjs +124 -1
  112. package/api/search/search.type.d.mts +15 -3
  113. package/api/search/search.type.mjs +5 -2
  114. package/api/swizzle/copy/copy.mjs +28 -11
  115. package/api/swizzle/swizzle.doc.mjs +2 -1
  116. package/api/swizzle/swizzle.type.d.mts +2 -2
  117. package/api/swizzle/swizzle.type.mjs +2 -2
  118. package/api/template/copy/copy.mjs +17 -23
  119. package/api/template/copy/copy.test.mjs +17 -0
  120. package/api/template/list/list.mjs +1 -0
  121. package/api/template/template-integration.test.mjs +1022 -3
  122. package/api/template/template-suffix.test.mjs +41 -21
  123. package/api/template/template.d.mts +1 -1
  124. package/api/template/template.doc.mjs +30 -8
  125. package/api/template/template.mjs +45 -8
  126. package/api/template/template.type.d.mts +12 -14
  127. package/api/template/template.type.mjs +15 -14
  128. package/api/theme/_adapter.d.mts +2 -3
  129. package/api/theme/_adapter.mjs +4 -5
  130. package/api/theme/add/add.binary.test.mjs +84 -0
  131. package/api/theme/add/add.mjs +20 -3
  132. package/api/theme/add/add.staging.test.mjs +66 -0
  133. package/api/theme/add/add.test.mjs +14 -1
  134. package/api/theme/build/build.mjs +133 -42
  135. package/api/theme/build/build.public-component-vars.test.mjs +1 -1
  136. package/api/theme/build/build.receipt-doc.test.mjs +111 -0
  137. package/api/theme/build/build.test.mjs +127 -0
  138. package/api/theme/build/font-warning.mjs +3 -3
  139. package/api/theme/build/font-warning.test.mjs +5 -2
  140. package/api/theme/generateTonalPalette.doc.mjs +1 -0
  141. package/api/theme/integration-themes.test.mjs +39 -28
  142. package/api/theme/list/list.test.mjs +19 -20
  143. package/api/theme/listThemes.doc.mjs +6 -5
  144. package/api/theme/palette/generate/generate.mjs +8 -3
  145. package/api/theme/palette/generate/generate.test.mjs +96 -0
  146. package/api/theme/palette/generate/generator.d.mts +10 -13
  147. package/api/theme/palette/generate/generator.mjs +15 -4
  148. package/api/theme/palette/generate/generator.test.mjs +10 -0
  149. package/api/theme/template/template.mjs +11 -2
  150. package/api/theme/template/template.test.mjs +20 -0
  151. package/api/theme/theme.type.d.mts +170 -11
  152. package/api/theme/theme.type.mjs +94 -27
  153. package/api/theme/themeAdd.doc.mjs +4 -3
  154. package/api/theme/themeBuild.doc.mjs +8 -4
  155. package/api/theme/themeList.doc.mjs +6 -3
  156. package/api/theme/themeListAvailable.doc.mjs +5 -3
  157. package/api/theme/themePaletteGenerate.doc.mjs +1 -0
  158. package/api/theme/themeTargets.doc.mjs +1 -0
  159. package/api/theme/themeTemplate.doc.mjs +6 -2
  160. package/api/upgrade/_adapter.d.mts +32 -5
  161. package/api/upgrade/_adapter.mjs +139 -22
  162. package/api/upgrade/list/list.mjs +2 -1
  163. package/api/upgrade/list/list.test.mjs +73 -0
  164. package/api/upgrade/project-context.test.mjs +272 -0
  165. package/api/upgrade/provider-agreement.test.mjs +152 -0
  166. package/api/upgrade/run/run.mjs +356 -59
  167. package/api/upgrade/status/status.mjs +2 -2
  168. package/api/upgrade/upgrade.doc.mjs +12 -5
  169. package/api/upgrade/upgrade.type.d.mts +43 -5
  170. package/api/upgrade/upgrade.type.mjs +27 -11
  171. package/assets/codemods/__tests__/runner.test.mjs +330 -8
  172. package/assets/codemods/integration-discovery.mjs +48 -4
  173. package/assets/codemods/integration-discovery.test.mjs +73 -0
  174. package/assets/codemods/integration-runner.mjs +56 -4
  175. package/assets/codemods/integration-runner.protection.test.mjs +153 -0
  176. package/assets/codemods/run-codemod.mjs +177 -34
  177. package/assets/codemods/runner.mjs +350 -102
  178. package/assets/codemods/term-log.mjs +32 -8
  179. package/assets/codemods/term-log.test.mjs +19 -1
  180. package/assets/codemods/transform-prop.mjs +109 -0
  181. package/assets/codemods/transform-prop.test.mjs +95 -0
  182. package/assets/codemods/transforms/next/__tests__/migrate-native-picker-to-presentation.test.mjs +63 -0
  183. package/assets/codemods/transforms/next/__tests__/migrate-theme-catalog-to-descriptors.test.mjs +220 -0
  184. package/assets/codemods/transforms/next/index.mjs +19 -1
  185. package/assets/codemods/transforms/next/migrate-native-picker-to-presentation.mjs +148 -0
  186. package/assets/codemods/transforms/next/migrate-theme-catalog-to-descriptors.mjs +141 -0
  187. package/assets/codemods/transforms/v0.0.14/__tests__/rename-status-variants.test.mjs +86 -165
  188. package/assets/codemods/transforms/v0.0.14/rename-status-variants.mjs +72 -210
  189. package/assets/codemods/transforms/v0.1.8/__tests__/rename-avatar-size-scale.test.mjs +83 -115
  190. package/assets/codemods/transforms/v0.1.8/rename-avatar-size-scale.mjs +57 -186
  191. package/assets/codemods/transforms/v0.3.0/__tests__/unwrap-authoring-factories.test.mjs +27 -5
  192. package/assets/codemods/transforms/v0.3.0/unwrap-authoring-factories.mjs +20 -5
  193. package/assets/codemods/transforms/v0.6.0/__tests__/next-codemods.test.mjs +47 -0
  194. package/assets/codemods/transforms/v0.6.0/rename-resizable-pixel-bounds.mjs +57 -10
  195. package/assets/docs/README.md +9 -0
  196. package/assets/docs/authoring.doc.mjs +14 -0
  197. package/assets/docs/getting-started.doc.mjs +2 -2
  198. package/assets/docs/principles.doc.mjs +6 -6
  199. package/assets/docs/styling-libraries.doc.mjs +3 -3
  200. package/assets/docs/styling.doc.mjs +4 -4
  201. package/assets/docs/theme.doc.mjs +4 -4
  202. package/assets/docs/tokens.doc.mjs +1 -1
  203. package/assets/docs/tree/api.doc.mjs +30 -0
  204. package/assets/docs/tree/cli.doc.mjs +23 -0
  205. package/assets/docs/tree/commands.doc.mjs +25 -0
  206. package/assets/docs/{cli-integrations.doc.mjs → tree/integrations.doc.mjs} +124 -25
  207. package/assets/docs/tree/integrations.test.mjs +62 -0
  208. package/assets/docs/tree/writing-docs.doc.mjs +286 -0
  209. package/assets/docs/working-with-ai.doc.mjs +1 -1
  210. package/assets/templates/blocks/components/DateInput/DateInputDateRange.tsx +1 -1
  211. package/assets/templates/blocks/components/Item/ItemDocumentTabs.doc.mjs +14 -0
  212. package/assets/templates/blocks/components/Item/ItemDocumentTabs.tsx +100 -0
  213. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.doc.mjs +14 -0
  214. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.tsx +61 -0
  215. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.doc.mjs +14 -0
  216. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.tsx +126 -0
  217. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.doc.mjs +15 -0
  218. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.tsx +86 -0
  219. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.doc.mjs +14 -0
  220. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.tsx +99 -0
  221. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.doc.mjs +14 -0
  222. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.tsx +122 -0
  223. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.doc.mjs +14 -0
  224. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.tsx +95 -0
  225. package/assets/templates/blocks/components/TimeInput/TimeInputConstrained.tsx +1 -0
  226. package/assets/templates/pages/table-tree/page.tsx +1704 -0
  227. package/assets/templates/pages/table-tree/template.doc.mjs +12 -0
  228. package/assets/templates/themes/butter/butterTheme.doc.mjs +11 -0
  229. package/assets/templates/themes/chocolate/chocolateTheme.doc.mjs +11 -0
  230. package/assets/templates/themes/gothic/gothicTheme.doc.mjs +11 -0
  231. package/assets/templates/themes/matcha/matchaTheme.doc.mjs +11 -0
  232. package/assets/templates/themes/neutral/neutralTheme.doc.mjs +11 -0
  233. package/assets/templates/themes/stone/stoneTheme.doc.mjs +11 -0
  234. package/assets/templates/themes/y2k/y2kTheme.doc.mjs +11 -0
  235. package/authoring/_shared/contract.ts +22 -0
  236. package/authoring/codemod/codemod.doc.mjs +7 -2
  237. package/authoring/codemod/parse.d.mts +8 -8
  238. package/authoring/codemod/parse.mjs +8 -6
  239. package/authoring/codemod/type.ts +12 -0
  240. package/authoring/config/config.doc.mjs +2 -2
  241. package/authoring/config/debug-composition.test.mjs +92 -0
  242. package/authoring/config/parse.d.mts +13 -13
  243. package/authoring/config/parse.mjs +8 -8
  244. package/authoring/config/type.ts +18 -6
  245. package/authoring/debug/debug.doc.d.mts +11 -0
  246. package/authoring/debug/debug.doc.mjs +182 -0
  247. package/authoring/debug/parse.d.mts +8 -8
  248. package/authoring/debug/parse.mjs +3 -3
  249. package/authoring/doctypes/_schema.d.mts +718 -23
  250. package/authoring/doctypes/_schema.mjs +519 -39
  251. package/authoring/doctypes/base/graph-fields.doc.d.mts +9 -0
  252. package/authoring/doctypes/base/graph-fields.doc.mjs +64 -0
  253. package/authoring/doctypes/base/type.ts +41 -0
  254. package/authoring/doctypes/command/command.doc.mjs +4 -3
  255. package/authoring/doctypes/command/parse.d.mts +2 -2
  256. package/authoring/doctypes/command/parse.mjs +1 -1
  257. package/authoring/doctypes/command/type.ts +5 -4
  258. package/authoring/doctypes/component/component.doc.mjs +6 -3
  259. package/authoring/doctypes/component/parse.d.mts +2 -2
  260. package/authoring/doctypes/component/parse.mjs +1 -1
  261. package/authoring/doctypes/component/type.ts +6 -5
  262. package/authoring/doctypes/doctypes-new.test.mjs +48 -6
  263. package/authoring/doctypes/enum/enum.doc.mjs +1 -1
  264. package/authoring/doctypes/enum/parse.d.mts +2 -2
  265. package/authoring/doctypes/enum/parse.mjs +1 -1
  266. package/authoring/doctypes/enum/type.ts +4 -2
  267. package/authoring/doctypes/function/function.doc.mjs +7 -2
  268. package/authoring/doctypes/function/parse.d.mts +2 -2
  269. package/authoring/doctypes/function/parse.mjs +1 -1
  270. package/authoring/doctypes/function/type.ts +9 -4
  271. package/authoring/doctypes/hook/hook.doc.mjs +4 -0
  272. package/authoring/doctypes/hook/parse.d.mts +2 -2
  273. package/authoring/doctypes/hook/parse.mjs +1 -1
  274. package/authoring/doctypes/hook/type.ts +5 -4
  275. package/authoring/doctypes/legacy.d.mts +8 -6
  276. package/authoring/doctypes/legacy.mjs +5 -4
  277. package/authoring/doctypes/load-contract.test.mjs +208 -0
  278. package/authoring/doctypes/namespace/namespace.doc.d.mts +9 -0
  279. package/authoring/doctypes/namespace/namespace.doc.mjs +128 -0
  280. package/authoring/doctypes/namespace/parse.d.mts +12 -0
  281. package/authoring/doctypes/namespace/parse.mjs +25 -0
  282. package/authoring/doctypes/namespace/parse.test.mjs +163 -0
  283. package/authoring/doctypes/namespace/type.ts +74 -0
  284. package/authoring/doctypes/parse.d.mts +22 -18
  285. package/authoring/doctypes/parse.mjs +22 -11
  286. package/authoring/doctypes/parse.test.mjs +77 -3
  287. package/authoring/doctypes/reference/parse.d.mts +2 -2
  288. package/authoring/doctypes/reference/parse.mjs +8 -5
  289. package/authoring/doctypes/reference/reference.doc.mjs +20 -5
  290. package/authoring/doctypes/reference/type.ts +53 -5
  291. package/authoring/doctypes/schema/parse.d.mts +2 -2
  292. package/authoring/doctypes/schema/parse.mjs +1 -1
  293. package/authoring/doctypes/schema/schema.doc.mjs +1 -1
  294. package/authoring/doctypes/schema/type.ts +4 -4
  295. package/authoring/doctypes/template/parse.d.mts +94 -1
  296. package/authoring/doctypes/template/parse.mjs +40 -2
  297. package/authoring/doctypes/template/parse.test.mjs +26 -2
  298. package/authoring/doctypes/template/template.doc.mjs +13 -3
  299. package/authoring/doctypes/template/type.ts +13 -2
  300. package/authoring/doctypes/theme/parse.d.mts +35 -0
  301. package/authoring/doctypes/theme/parse.mjs +76 -0
  302. package/authoring/doctypes/theme/theme.doc.d.mts +9 -0
  303. package/authoring/doctypes/theme/theme.doc.mjs +79 -0
  304. package/authoring/doctypes/theme/type.ts +42 -0
  305. package/authoring/doctypes/types.ts +12 -10
  306. package/authoring/gap-report/gap-report.doc.d.mts +12 -0
  307. package/authoring/gap-report/gap-report.doc.mjs +183 -0
  308. package/authoring/gap-report/parse.d.mts +10 -10
  309. package/authoring/gap-report/parse.mjs +6 -6
  310. package/authoring/gap-report/type.ts +1 -1
  311. package/authoring/identity/identity.doc.d.mts +9 -0
  312. package/authoring/identity/identity.doc.mjs +61 -0
  313. package/authoring/identity/type.ts +132 -0
  314. package/authoring/index.d.mts +2 -0
  315. package/authoring/index.d.ts +52 -17
  316. package/authoring/index.mjs +3 -1
  317. package/authoring/integration/integration.doc.mjs +15 -8
  318. package/authoring/integration/parse.d.mts +2 -2
  319. package/authoring/integration/parse.mjs +1 -1
  320. package/authoring/integration/parse.test.mjs +10 -1
  321. package/authoring/integration/schema.d.mts +6 -4
  322. package/authoring/integration/schema.mjs +9 -3
  323. package/authoring/integration/type.ts +19 -8
  324. package/authoring/shadcn/receipt.d.mts +6 -6
  325. package/clients/cli/__tests__/cliManifest.test.ts +27 -29
  326. package/clients/cli/command-load-failure.test.mjs +83 -0
  327. package/clients/cli/commands/blog.doc.mjs +1 -1
  328. package/clients/cli/commands/blog.mjs +23 -8
  329. package/clients/cli/commands/blog.test.mjs +42 -1
  330. package/clients/cli/commands/build-theme.adaptations.test.mjs +100 -1
  331. package/clients/cli/commands/build-theme.ascii-output.test.mjs +161 -0
  332. package/clients/cli/commands/build-theme.flag-docs.test.mjs +110 -0
  333. package/clients/cli/commands/build-theme.mjs +16 -50
  334. package/clients/cli/commands/build-theme.path-safety.test.mjs +86 -1
  335. package/clients/cli/commands/build-theme.variants.test.mjs +76 -0
  336. package/clients/cli/commands/build.doc.mjs +5 -2
  337. package/clients/cli/commands/build.exit-codes-doc.test.mjs +50 -0
  338. package/clients/cli/commands/build.mjs +37 -49
  339. package/clients/cli/commands/build.playbook.test.mjs +75 -0
  340. package/clients/cli/commands/build.text-fields.test.mjs +40 -0
  341. package/clients/cli/commands/component/index.mjs +3 -8
  342. package/clients/cli/commands/component-ownership.test.mjs +3 -3
  343. package/clients/cli/commands/component-package.test.mjs +46 -0
  344. package/clients/cli/commands/component-resolution.test.mjs +21 -0
  345. package/clients/cli/commands/component.doc.mjs +1 -1
  346. package/clients/cli/commands/component.test.mjs +19 -0
  347. package/clients/cli/commands/detail-levels.test.mjs +2 -2
  348. package/clients/cli/commands/discover.broken-integration.test.mjs +52 -5
  349. package/clients/cli/commands/discover.components-flag.test.mjs +97 -0
  350. package/clients/cli/commands/discover.doc.mjs +5 -3
  351. package/clients/cli/commands/discover.mjs +4 -4
  352. package/clients/cli/commands/discover.text-projection.test.mjs +103 -0
  353. package/clients/cli/commands/docs.doc.mjs +28 -6
  354. package/clients/cli/commands/docs.mjs +238 -24
  355. package/clients/cli/commands/docs.test.mjs +193 -1
  356. package/clients/cli/commands/doctor-integration-components.doc.mjs +1 -1
  357. package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -3
  358. package/clients/cli/commands/doctor-integration-templates.doc.mjs +15 -8
  359. package/clients/cli/commands/doctor-integration-validate.doc.mjs +1 -1
  360. package/clients/cli/commands/doctor-integration.doc.mjs +1 -1
  361. package/clients/cli/commands/doctor-integration.package-json.test.mjs +53 -0
  362. package/clients/cli/commands/doctor-integration.test.mjs +83 -8
  363. package/clients/cli/commands/doctor.doc.mjs +1 -1
  364. package/clients/cli/commands/doctor.mjs +30 -25
  365. package/clients/cli/commands/doctor.test.mjs +42 -0
  366. package/clients/cli/commands/gap-report.doc.mjs +17 -6
  367. package/clients/cli/commands/gap-report.test.mjs +72 -0
  368. package/clients/cli/commands/hook/index.mjs +7 -17
  369. package/clients/cli/commands/hook.doc.mjs +1 -1
  370. package/clients/cli/commands/hook.text-projection.test.mjs +45 -0
  371. package/clients/cli/commands/init.doc.mjs +20 -9
  372. package/clients/cli/commands/init.flag-help.test.mjs +153 -0
  373. package/clients/cli/commands/integration-add.controls.test.mjs +132 -0
  374. package/clients/cli/commands/integration-add.doc.mjs +32 -6
  375. package/clients/cli/commands/integration-authoring.test.mjs +13 -9
  376. package/clients/cli/commands/integration-pack.doc.mjs +1 -1
  377. package/clients/cli/commands/integration-real-world.test.mjs +3 -9
  378. package/clients/cli/commands/integration.doc.mjs +1 -1
  379. package/clients/cli/commands/integration.mjs +1 -0
  380. package/clients/cli/commands/interactive-guard.test.mjs +101 -24
  381. package/clients/cli/commands/json-contract.test.mjs +33 -0
  382. package/clients/cli/commands/layout-check.doc.mjs +15 -4
  383. package/clients/cli/commands/layout-expand.doc.mjs +22 -5
  384. package/clients/cli/commands/layout-grammar.doc.mjs +1 -1
  385. package/clients/cli/commands/layout.doc.mjs +3 -3
  386. package/clients/cli/commands/layout.mjs +21 -9
  387. package/clients/cli/commands/layout.path-help.test.mjs +33 -0
  388. package/clients/cli/commands/layout.stdin-cap.test.mjs +47 -0
  389. package/clients/cli/commands/layout.text-fields.test.mjs +39 -0
  390. package/clients/cli/commands/manifest.doc.mjs +1 -1
  391. package/clients/cli/commands/no-prompt-wording.test.mjs +94 -0
  392. package/clients/cli/commands/search.doc.mjs +7 -4
  393. package/clients/cli/commands/search.mjs +28 -9
  394. package/clients/cli/commands/search.test.mjs +75 -0
  395. package/clients/cli/commands/setup-nudge.test.mjs +6 -0
  396. package/clients/cli/commands/swizzle.doc.mjs +3 -2
  397. package/clients/cli/commands/swizzle.path-safety.test.mjs +42 -0
  398. package/clients/cli/commands/template.doc.mjs +52 -13
  399. package/clients/cli/commands/template.flag-help.test.mjs +117 -0
  400. package/clients/cli/commands/template.mjs +4 -91
  401. package/clients/cli/commands/template.path-help.test.mjs +40 -0
  402. package/clients/cli/commands/text-json-parity.test.mjs +719 -0
  403. package/clients/cli/commands/theme-add.doc.mjs +4 -3
  404. package/clients/cli/commands/theme-build.doc.mjs +8 -7
  405. package/clients/cli/commands/theme-list.doc.mjs +2 -2
  406. package/clients/cli/commands/theme-palette-generate.doc.mjs +9 -5
  407. package/clients/cli/commands/theme-palette-generate.test.mjs +19 -0
  408. package/clients/cli/commands/theme-palette.doc.mjs +1 -1
  409. package/clients/cli/commands/theme-targets.behavior.test.mjs +4 -3
  410. package/clients/cli/commands/theme-targets.doc.mjs +1 -1
  411. package/clients/cli/commands/theme-template.behavior.test.mjs +12 -0
  412. package/clients/cli/commands/theme-template.doc.mjs +2 -2
  413. package/clients/cli/commands/theme.doc.mjs +1 -1
  414. package/clients/cli/commands/upgrade.ascii-output.test.mjs +87 -0
  415. package/clients/cli/commands/upgrade.doc.mjs +22 -10
  416. package/clients/cli/commands/upgrade.file-protection.test.mjs +228 -0
  417. package/clients/cli/commands/upgrade.flag-help.test.mjs +188 -0
  418. package/clients/cli/commands/upgrade.hook-output.test.mjs +88 -0
  419. package/clients/cli/commands/upgrade.mjs +29 -7
  420. package/clients/cli/formatters/index.mjs +164 -1
  421. package/clients/cli/formatters/index.test.mjs +97 -0
  422. package/clients/cli/index.mjs +21 -30
  423. package/clients/cli/latest-version-env.test.mjs +50 -0
  424. package/clients/cli/lib/cli-error.test.mjs +7 -0
  425. package/clients/cli/lib/component-format.mjs +9 -9
  426. package/clients/cli/lib/component-format.test.mjs +1 -1
  427. package/clients/cli/lib/define-command.mjs +32 -6
  428. package/clients/cli/lib/doc-text-ascii.test.mjs +82 -0
  429. package/clients/cli/lib/exit-codes.test.mjs +97 -0
  430. package/clients/cli/lib/hook-format.mjs +19 -10
  431. package/clients/cli/lib/json-shim.mjs +38 -2
  432. package/clients/cli/lib/json-shim.test.mjs +83 -0
  433. package/clients/cli/lib/manifest.d.ts +2 -0
  434. package/clients/cli/lib/manifest.mjs +36 -2
  435. package/clients/cli/lib/manifest.test.mjs +17 -0
  436. package/foundation/agent-docs/agent-docs.d.mts +4 -0
  437. package/foundation/agent-docs/agent-docs.mjs +70 -3
  438. package/foundation/agent-docs/agent-docs.path-safety.test.mjs +266 -4
  439. package/foundation/agent-docs/agent-docs.test.mjs +10 -1
  440. package/foundation/config/integration-debug.test.mjs +28 -3
  441. package/foundation/config/project-themes.test.mjs +11 -19
  442. package/foundation/config/project.d.mts +20 -11
  443. package/foundation/config/project.mjs +263 -91
  444. package/foundation/config/project.test.mjs +270 -21
  445. package/foundation/discovery/authoring-self-docs.d.mts +75 -0
  446. package/foundation/discovery/authoring-self-docs.mjs +224 -0
  447. package/foundation/discovery/authoring-self-docs.test.mjs +165 -0
  448. package/foundation/discovery/authoring-surface.d.mts +74 -0
  449. package/foundation/discovery/authoring-surface.mjs +525 -0
  450. package/foundation/discovery/authoring-surface.test.mjs +392 -0
  451. package/foundation/discovery/cli-self-docs.d.mts +119 -0
  452. package/foundation/discovery/cli-self-docs.mjs +490 -0
  453. package/foundation/discovery/cli-self-docs.test.mjs +375 -0
  454. package/foundation/discovery/component-discovery.d.mts +39 -1
  455. package/foundation/discovery/component-discovery.mjs +50 -1
  456. package/foundation/discovery/component-loader.d.mts +35 -38
  457. package/foundation/discovery/component-loader.mjs +53 -222
  458. package/foundation/discovery/docs-discovery.d.mts +116 -11
  459. package/foundation/discovery/docs-discovery.mjs +389 -108
  460. package/foundation/discovery/docs-discovery.test.mjs +325 -20
  461. package/foundation/discovery/docs-output-budget.d.mts +28 -0
  462. package/foundation/discovery/docs-output-budget.mjs +50 -0
  463. package/foundation/discovery/docs-section-key.d.mts +116 -0
  464. package/foundation/discovery/docs-section-key.mjs +322 -0
  465. package/foundation/discovery/docs-section-key.test.mjs +246 -0
  466. package/foundation/discovery/template-adapter.d.mts +113 -11
  467. package/foundation/discovery/template-adapter.fixture-refs.test.mjs +248 -0
  468. package/foundation/discovery/template-adapter.integration-isolation.test.mjs +94 -0
  469. package/foundation/discovery/template-adapter.mjs +774 -83
  470. package/foundation/discovery/template-adapter.test.mjs +57 -0
  471. package/foundation/discovery/theme-discovery.d.mts +67 -7
  472. package/foundation/discovery/theme-discovery.mjs +916 -186
  473. package/foundation/discovery/theme-discovery.test.mjs +613 -219
  474. package/foundation/discovery/theming-targets.test.mjs +4 -0
  475. package/foundation/doc-compiler/bundle.d.mts +47 -0
  476. package/foundation/doc-compiler/bundle.mjs +278 -0
  477. package/foundation/doc-compiler/bundle.test.mjs +266 -0
  478. package/foundation/doc-compiler/compile.d.mts +343 -0
  479. package/foundation/doc-compiler/compile.mjs +558 -0
  480. package/foundation/doc-compiler/diagnostics.d.mts +126 -0
  481. package/foundation/doc-compiler/diagnostics.mjs +305 -0
  482. package/foundation/doc-compiler/doc-compiler.test.mjs +714 -0
  483. package/foundation/doc-compiler/doc-loads.test.mjs +1642 -0
  484. package/foundation/doc-compiler/import.d.mts +24 -0
  485. package/foundation/doc-compiler/import.mjs +59 -0
  486. package/foundation/doc-compiler/inputs.d.mts +102 -0
  487. package/foundation/doc-compiler/inputs.mjs +291 -0
  488. package/foundation/doc-compiler/inputs.test.mjs +299 -0
  489. package/foundation/doc-compiler/ir.d.mts +22 -0
  490. package/foundation/doc-compiler/ir.mjs +471 -0
  491. package/foundation/doc-compiler/lenses.d.mts +34 -0
  492. package/foundation/doc-compiler/lenses.mjs +128 -0
  493. package/foundation/doc-compiler/links.d.mts +135 -0
  494. package/foundation/doc-compiler/links.mjs +253 -0
  495. package/foundation/doc-compiler/links.test.mjs +139 -0
  496. package/foundation/doc-compiler/lower-doc.test.mjs +495 -0
  497. package/foundation/doc-compiler/overlays.d.mts +37 -0
  498. package/foundation/doc-compiler/overlays.mjs +206 -0
  499. package/foundation/doc-compiler/parse-readable.d.mts +9 -0
  500. package/foundation/doc-compiler/parse-readable.mjs +29 -0
  501. package/foundation/doc-compiler/read.d.mts +127 -0
  502. package/foundation/doc-compiler/read.mjs +325 -0
  503. package/foundation/doc-compiler/read.test.mjs +313 -0
  504. package/foundation/doc-compiler/source.d.mts +33 -0
  505. package/foundation/doc-compiler/source.mjs +128 -0
  506. package/foundation/doc-compiler/tree.d.mts +288 -0
  507. package/foundation/doc-compiler/tree.mjs +876 -0
  508. package/foundation/doc-compiler/tree.test.mjs +598 -0
  509. package/foundation/fs/file-protection.d.mts +33 -0
  510. package/foundation/fs/file-protection.mjs +825 -0
  511. package/foundation/fs/file-protection.test.mjs +250 -0
  512. package/foundation/fs/module-loader.d.mts +1 -0
  513. package/foundation/fs/module-loader.mjs +50 -1
  514. package/foundation/fs/module-loader.stdout.test.mjs +332 -0
  515. package/foundation/fs/path-safety.d.mts +3 -2
  516. package/foundation/fs/path-safety.mjs +49 -19
  517. package/foundation/fs/path-safety.test.mjs +50 -0
  518. package/foundation/fs/publish-file-hardlink-unavailable.test.mjs +129 -94
  519. package/foundation/identity/provider-identity.d.mts +90 -0
  520. package/foundation/identity/provider-identity.mjs +320 -0
  521. package/foundation/identity/provider-identity.test.mjs +254 -0
  522. package/foundation/identity/providers.d.mts +7 -0
  523. package/foundation/identity/providers.mjs +16 -0
  524. package/foundation/integrations/autolink.d.mts +58 -1
  525. package/foundation/integrations/autolink.mjs +143 -45
  526. package/foundation/integrations/autolink.test.mjs +1 -1
  527. package/foundation/integrations/cli-requirement.d.mts +45 -0
  528. package/foundation/integrations/cli-requirement.mjs +154 -0
  529. package/foundation/integrations/cli-requirement.test.mjs +109 -0
  530. package/foundation/integrations/contribution-fixes.d.mts +145 -0
  531. package/foundation/integrations/contribution-fixes.mjs +1284 -0
  532. package/foundation/integrations/contribution-inventory.d.mts +3 -2
  533. package/foundation/integrations/contribution-inventory.mjs +27 -24
  534. package/foundation/integrations/contribution-inventory.test.mjs +86 -27
  535. package/foundation/integrations/integration-warnings.d.mts +9 -2
  536. package/foundation/integrations/integration-warnings.mjs +52 -21
  537. package/foundation/integrations/integration-warnings.test.mjs +74 -1
  538. package/foundation/integrations/integrations.d.mts +49 -2
  539. package/foundation/integrations/integrations.mjs +81 -8
  540. package/foundation/integrations/integrations.test.mjs +384 -1
  541. package/foundation/integrations/provider-conflicts.test.mjs +125 -0
  542. package/foundation/integrations/provider-ledger.test.mjs +275 -0
  543. package/foundation/integrations/provider-resolution.d.mts +152 -0
  544. package/foundation/integrations/provider-resolution.mjs +576 -0
  545. package/foundation/integrations/provider-resolution.test.mjs +369 -0
  546. package/foundation/integrations/theme-descriptor.d.mts +8 -0
  547. package/foundation/integrations/theme-descriptor.mjs +44 -0
  548. package/foundation/integrations/validate-contributions.d.mts +2 -0
  549. package/foundation/integrations/validate-contributions.mjs +131 -29
  550. package/foundation/response/base.d.ts +8 -4
  551. package/foundation/response/error-codes.d.mts +3 -1
  552. package/foundation/response/error-codes.d.ts +2 -0
  553. package/foundation/response/error-codes.doc.mjs +13 -4
  554. package/foundation/response/error-codes.mjs +8 -2
  555. package/foundation/response/error-codes.test.mjs +137 -10
  556. package/foundation/response/json-contract.test.mjs +57 -17
  557. package/foundation/response/json.d.mts +4 -2
  558. package/foundation/response/json.mjs +8 -10
  559. package/foundation/response/response-types.doc.mjs +26 -16
  560. package/foundation/response/response-types.doc.test.mjs +158 -0
  561. package/foundation/response/response.doc.mjs +1 -1
  562. package/foundation/text/string-utils.mjs +22 -10
  563. package/foundation/xle/expand.d.mts +2 -0
  564. package/foundation/xle/expand.mjs +4 -3
  565. package/foundation/xle/expand.test.mjs +54 -0
  566. package/foundation/xle/xle.test.mjs +13 -0
  567. package/package.json +10 -11
  568. package/assets/templates/themes/manifest.json +0 -95
  569. package/clients/cli/lib/update-check.mjs +0 -83
  570. package/clients/cli/lib/update-check.test.mjs +0 -137
  571. package/clients/cli/update-hint-commands.test.mjs +0 -54
@@ -120,7 +120,7 @@ describe('integration authoring CLI', () => {
120
120
  );
121
121
  });
122
122
 
123
- it('packs the generated contribution and proves the consumer inventory', async () => {
123
+ it('pack-check rejects a generated component without an exports map (no-map false green)', async () => {
124
124
  const added = await runCli(
125
125
  ['integration', 'add', 'component', 'AcmeWidget', '--json'],
126
126
  tmpDir,
@@ -131,19 +131,23 @@ describe('integration authoring CLI', () => {
131
131
  ['integration', 'pack', '--check', '--json'],
132
132
  tmpDir,
133
133
  );
134
- expect(checked.status).toBe(0);
135
- expect(parseEnvelope(checked.stdout)).toMatchObject({
134
+ // Without an exports map, the extensionless import cannot resolve —
135
+ // pack-check must fail, not false-green.
136
+ expect(checked.status).not.toBe(0);
137
+ const envelope = parseEnvelope(checked.stdout);
138
+ expect(envelope).toMatchObject({
136
139
  type: 'integration.pack-check',
137
140
  data: {
138
141
  name: '@acme/widgets',
139
- version: '1.0.0',
140
- packable: true,
141
- contributions: {
142
- local: {components: ['AcmeWidget']},
143
- packed: {components: ['AcmeWidget']},
144
- },
142
+ packable: false,
145
143
  },
146
144
  });
145
+ expect(envelope.data.issues).toContainEqual(
146
+ expect.objectContaining({
147
+ severity: 'error',
148
+ message: expect.stringContaining('AcmeWidget'),
149
+ }),
150
+ );
147
151
  });
148
152
 
149
153
  it('requires the explicit --check gate on pack', async () => {
@@ -5,7 +5,7 @@ export const doc = {
5
5
  type: 'command',
6
6
  name: 'integration pack',
7
7
  displayName: 'astryx integration pack',
8
- namespace: 'cli',
8
+ namespace: 'cli/commands',
9
9
  summary: 'Prove the packed integration is what consumers receive',
10
10
  description:
11
11
  'Runs the package lifecycle, packs with npm, checks every required contribution file against the real tarball, extracts it into a scratch consumer, and compares the local and packed contribution inventories through one shared contract.',
@@ -201,13 +201,6 @@ export const oceanTheme = defineTheme({
201
201
  });
202
202
  `,
203
203
  );
204
- const catalogFile = path.join(providerDir, 'themes', 'manifest.json');
205
- const catalog = JSON.parse(fs.readFileSync(catalogFile, 'utf-8'));
206
- catalog.themes[0].files.push(
207
- 'tokens/ocean.palette.ts',
208
- 'tokens/ocean.palette.receipt.json',
209
- );
210
- writeJson(catalogFile, catalog);
211
204
 
212
205
  const providerPackage = JSON.parse(
213
206
  fs.readFileSync(path.join(providerDir, 'package.json'), 'utf-8'),
@@ -464,8 +457,9 @@ export const oceanTheme = defineTheme({
464
457
  outputDir: target,
465
458
  files: [
466
459
  'oceanTheme.ts',
467
- 'tokens/ocean.palette.ts',
460
+ 'oceanTheme.doc.mjs',
468
461
  'tokens/ocean.palette.receipt.json',
462
+ 'tokens/ocean.palette.ts',
469
463
  ],
470
464
  },
471
465
  });
@@ -612,7 +606,7 @@ export const oceanTheme = defineTheme({
612
606
  expect(corruptedInstall.status).not.toBe(0);
613
607
  expect(parseEnvelope(corruptedInstall.stdout)).toMatchObject({
614
608
  code: 'ERR_THEME_INVALID',
615
- error: expect.stringContaining('ocean.palette.ts'),
609
+ error: expect.stringContaining('./tokens/ocean.palette'),
616
610
  });
617
611
  }, 120_000);
618
612
  });
@@ -5,7 +5,7 @@ export const doc = {
5
5
  type: 'command',
6
6
  name: 'integration',
7
7
  displayName: 'astryx integration',
8
- namespace: 'cli',
8
+ namespace: 'cli/commands',
9
9
  summary: 'Author and verify an Astryx integration package',
10
10
  description:
11
11
  'Add contributions that are valid on arrival, then prove the packed package exposes the same contributions a consumer will receive.',
@@ -58,6 +58,7 @@ export function registerIntegration(program) {
58
58
  templateType: options.type,
59
59
  replaces: options.replaces,
60
60
  extends: options.extends,
61
+ parent: options.parent,
61
62
  to: options.to,
62
63
  });
63
64
  } catch (error) {
@@ -1,17 +1,19 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
 
3
3
  /**
4
- * @file Subprocess no-hang tests for `astryx init`.
4
+ * @file Subprocess no-hang tests: the CLI never waits on stdin (INV1).
5
5
  *
6
- * TTY was removed from the CLI: `init` is non-interactive by default and must
7
- * run cleanly (exit 0, writing the agent cheat sheet) instead of hanging on a
8
- * prompt. These tests spawn the CLI with stdin/stdout NOT a TTY (the CI / piped
9
- * / agent condition). A hang would show as signal SIGTERM + status null; we
10
- * assert `signal === null && status === 0` to prove it exits cleanly and works.
6
+ * Every run gets a stdin pipe that stays open and silent, the way an agent's
7
+ * harness holds it. A prompt or a stdin read for control flow then blocks until
8
+ * the deadline, and the run ends with signal SIGTERM and status null. With stdin
9
+ * ignored or closed, the same read would see EOF at once and pass.
10
+ *
11
+ * Every command and group in the manifest runs once, with a placeholder for
12
+ * each required argument so its action is reached, plus the bare `astryx`.
11
13
  */
12
14
 
13
15
  import {describe, it, expect, beforeEach, afterEach} from 'vitest';
14
- import {spawnSync} from 'node:child_process';
16
+ import {spawn, spawnSync} from 'node:child_process';
15
17
  import * as fs from 'node:fs';
16
18
  import * as path from 'node:path';
17
19
  import * as os from 'node:os';
@@ -19,37 +21,112 @@ import {fileURLToPath} from 'node:url';
19
21
 
20
22
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
21
23
  const CLI = path.resolve(__dirname, '..', 'bin', 'astryx.mjs');
24
+ const DEADLINE_MS = 20_000;
25
+ // Keeps every run offline: `blog` fetches its feed when run bare.
26
+ const OFFLINE = `data:text/javascript,${encodeURIComponent(
27
+ "globalThis.fetch = () => Promise.reject(new TypeError('fetch failed'));",
28
+ )}`;
29
+
30
+ /**
31
+ * Run `node <argv>` with a stdin pipe that is never written or ended.
32
+ * @param {string[]} argv
33
+ * @param {string} cwd
34
+ * @param {number} [deadline]
35
+ * @returns {Promise<{status: number | null, signal: NodeJS.Signals | null, output: string}>}
36
+ */
37
+ function runHeld(argv, cwd, deadline = DEADLINE_MS) {
38
+ return new Promise((resolve, reject) => {
39
+ const child = spawn(process.execPath, argv, {
40
+ cwd,
41
+ stdio: ['pipe', 'pipe', 'pipe'],
42
+ env: {...process.env, FORCE_COLOR: '0', CI: ''},
43
+ });
44
+ // Never written or ended; a late EPIPE after exit is not a result.
45
+ child.stdin.on('error', () => {});
46
+ let output = '';
47
+ child.stdout.setEncoding('utf8').on('data', chunk => (output += chunk));
48
+ child.stderr.setEncoding('utf8').on('data', chunk => (output += chunk));
49
+ const timer = setTimeout(() => child.kill('SIGTERM'), deadline);
50
+ child.on('error', error => {
51
+ clearTimeout(timer);
52
+ reject(error);
53
+ });
54
+ child.on('close', (status, signal) => {
55
+ clearTimeout(timer);
56
+ resolve({status, signal, output});
57
+ });
58
+ });
59
+ }
22
60
 
23
- let tmpDir;
61
+ /**
62
+ * @param {string[]} args
63
+ * @param {string} cwd
64
+ */
65
+ function runCli(args, cwd) {
66
+ return runHeld(['--import', OFFLINE, CLI, ...args], cwd);
67
+ }
24
68
 
25
- function runCli(args) {
26
- return spawnSync(process.execPath, [CLI, ...args], {
27
- cwd: tmpDir,
69
+ /** @returns {Array<[string, string[]]>} */
70
+ function manifestInvocations() {
71
+ const manifest = spawnSync(process.execPath, [CLI, 'manifest', '--json'], {
28
72
  encoding: 'utf8',
29
- timeout: 20_000,
30
- stdio: ['ignore', 'pipe', 'pipe'],
31
- env: {...process.env, FORCE_COLOR: '0', CI: ''},
32
73
  });
74
+ /** @type {string[][]} */
75
+ const invocations = [[]];
76
+ /** @param {any[]} commands */
77
+ const walk = commands => {
78
+ for (const command of commands) {
79
+ invocations.push([
80
+ ...command.name.split(' '),
81
+ ...command.arguments.filter((/** @type {any} */ a) => a.required).map(() => 'x'),
82
+ ]);
83
+ walk(command.subcommands ?? []);
84
+ }
85
+ };
86
+ walk(JSON.parse(manifest.stdout).data.commands);
87
+ return invocations.map(args => [['astryx', ...args].join(' '), args]);
33
88
  }
34
89
 
35
- beforeEach(() => {
36
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-interactive-guard-'));
90
+ describe('the harness holds stdin open', () => {
91
+ it('so a stdin read is reported as a hang', async () => {
92
+ const r = await runHeld(['-e', 'process.stdin.resume()'], os.tmpdir(), 1_000);
93
+ expect(r.signal).toBe('SIGTERM');
94
+ expect(r.status).toBeNull();
95
+ });
37
96
  });
38
97
 
39
- afterEach(() => {
40
- fs.rmSync(tmpDir, {recursive: true, force: true});
98
+ describe('every command exits without waiting on stdin', () => {
99
+ it.concurrent.for(manifestInvocations())('%s', async ([label, args], {expect}) => {
100
+ const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-interactive-guard-'));
101
+ try {
102
+ const r = await runCli(args, cwd);
103
+ expect(r.signal, `${label} waited on stdin:\n${r.output.slice(-500)}`).toBeNull();
104
+ } finally {
105
+ fs.rmSync(cwd, {recursive: true, force: true});
106
+ }
107
+ });
41
108
  });
42
109
 
43
110
  describe('init is non-interactive by default (no TTY needed)', () => {
44
- it('runs cleanly (exit 0, no hang) and writes agent docs', () => {
45
- const r = runCli(['init']);
111
+ let tmpDir;
112
+
113
+ beforeEach(() => {
114
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-interactive-guard-'));
115
+ });
116
+
117
+ afterEach(() => {
118
+ fs.rmSync(tmpDir, {recursive: true, force: true});
119
+ });
120
+
121
+ it('runs cleanly (exit 0, no hang) and writes agent docs', async () => {
122
+ const r = await runCli(['init'], tmpDir);
46
123
  expect(r.signal).toBeNull(); // did not hang
47
124
  expect(r.status).toBe(0); // succeeded without a TTY
48
125
  expect(fs.readdirSync(tmpDir).length).toBeGreaterThan(0); // wrote the cheat sheet
49
126
  });
50
127
 
51
- it('writes an agent-doc file containing the ASTRYX cheat-sheet marker', () => {
52
- runCli(['init']);
128
+ it('writes an agent-doc file containing the ASTRYX cheat-sheet marker', async () => {
129
+ await runCli(['init'], tmpDir);
53
130
  // init injects into existing agent-doc files if present, else creates AGENTS.md
54
131
  const candidates = ['AGENTS.md', 'CLAUDE.md', '.cursorrules', path.join('.claude', 'CLAUDE.md')];
55
132
  const doc = candidates.find(f => fs.existsSync(path.join(tmpDir, f)));
@@ -57,8 +134,8 @@ describe('init is non-interactive by default (no TTY needed)', () => {
57
134
  expect(fs.readFileSync(path.join(tmpDir, doc), 'utf8')).toMatch(/ASTRYX:START/);
58
135
  });
59
136
 
60
- it('still runs --features agents non-interactively', () => {
61
- const r = runCli(['init', '--features', 'agents']);
137
+ it('still runs --features agents non-interactively', async () => {
138
+ const r = await runCli(['init', '--features', 'agents'], tmpDir);
62
139
  expect(r.signal).toBeNull();
63
140
  expect(r.status).toBe(0);
64
141
  expect(fs.readdirSync(tmpDir).length).toBeGreaterThan(0);
@@ -172,3 +172,36 @@ describe('--json contract: supported commands emit valid envelopes', () => {
172
172
  expect(parsed.data.summary.fail).toBeGreaterThan(0);
173
173
  });
174
174
  });
175
+
176
+ describe('--json contract: the flag describes the envelopes it emits', () => {
177
+ /** @param {string} shape e.g. ` apiVersion, error, code, suggestions? ` */
178
+ const fields = shape => shape.split(',').map(f => f.trim().replace(/\?$/, ''));
179
+
180
+ it('names every field of the success and error envelopes', async () => {
181
+ const manifest = parseJson((await runCli(['manifest', '--json'], {cwd: tmpDir})).stdout);
182
+ const {description} = manifest.data.globalOptions.find(o => o.flag === '--json');
183
+ const match = /Success envelope: \{([^}]*)\}.*Error envelope: \{([^}]*)\}/.exec(description);
184
+ expect(match, description).not.toBeNull();
185
+ const [, success, error] = /** @type {RegExpExecArray} */ (match);
186
+
187
+ for (const key of Object.keys(manifest)) expect(fields(success)).toContain(key);
188
+ expect(fields(success)).toContain('meta');
189
+
190
+ // An unknown command's envelope carries every error field, suggestions included.
191
+ const failure = parseJson((await runCli(['bogus-cmd', '--json'], {cwd: tmpDir})).stdout);
192
+ expect(Object.keys(failure)).toContain('suggestions');
193
+ for (const key of Object.keys(failure)) expect(fields(error)).toContain(key);
194
+ });
195
+
196
+ it('the CLI README describes the same envelopes', () => {
197
+ const readme = fs.readFileSync(new URL('../../../README.md', import.meta.url), 'utf8');
198
+ const line = readme
199
+ .split('\n')
200
+ .find(l => l.startsWith('- `--json`: Output as typed JSON envelope:'));
201
+ expect(line, 'README global --json line').toBeDefined();
202
+ const [success, error] = [...String(line).matchAll(/`\{([^}]*)\}`/g)].map(m => fields(m[1]));
203
+ expect(success).toEqual(expect.arrayContaining(['apiVersion', 'type', 'data', 'meta']));
204
+ expect(error).toEqual(expect.arrayContaining(['apiVersion', 'error', 'code', 'suggestions']));
205
+ expect(readme).toContain('{"apiVersion": 1, "type": "component.detail"');
206
+ });
207
+ });
@@ -13,7 +13,7 @@ export const doc = {
13
13
  type: 'command',
14
14
  name: 'layout check',
15
15
  displayName: 'astryx layout check',
16
- namespace: 'cli',
16
+ namespace: 'cli/commands',
17
17
  summary:
18
18
  'Validate a layout expression and echo canonical compact/outline forms',
19
19
  description:
@@ -21,9 +21,20 @@ export const doc = {
21
21
  'echoes it back in both canonical surfaces (compact and outline). An invalid but ' +
22
22
  'parseable expression is reported with line/col and suggestions, and exits non-zero.',
23
23
  fn: 'layoutCheck',
24
- args: [{name: 'expression', param: 'expression', required: false}],
24
+ args: [
25
+ {
26
+ name: 'expression',
27
+ param: 'expression',
28
+ required: false,
29
+ description:
30
+ 'The XLE/XLO expression. Pass - to read it from stdin; --file reads it from a file instead.',
31
+ },
32
+ ],
25
33
  options: [
26
- {flag: '--file <file>', description: 'Read the expression from a file'},
34
+ {
35
+ flag: '--file <file>',
36
+ description: 'Read the expression from a file (used instead of the argument when both are given)',
37
+ },
27
38
  {
28
39
  flag: '--form <form>',
29
40
  param: 'options.form',
@@ -47,7 +58,7 @@ export const doc = {
47
58
  {code: 0, when: 'the expression is valid'},
48
59
  {
49
60
  code: 1,
50
- when: 'the expression is invalid or empty, has a syntax error, or a bad --form',
61
+ when: 'the expression is invalid, empty or over 5 MB (from stdin or --file), has a syntax error, or a bad --form',
51
62
  },
52
63
  ],
53
64
  related: ['layout expand', 'layout grammar'],
@@ -13,7 +13,7 @@ export const doc = {
13
13
  type: 'command',
14
14
  name: 'layout expand',
15
15
  displayName: 'astryx layout expand',
16
- namespace: 'cli',
16
+ namespace: 'cli/commands',
17
17
  summary: 'Expand a layout expression into validated XDS TSX',
18
18
  description:
19
19
  'Parses and validates a compressed XLE/XLO expression, then expands it into ' +
@@ -21,11 +21,28 @@ export const doc = {
21
21
  'interactive controls, and splicing referenced blocks. Writes to a path, or returns the code.',
22
22
  fn: 'layoutExpand',
23
23
  args: [
24
- {name: 'expression', param: 'expression', required: false},
25
- {name: 'path', param: 'options.targetPath', required: false},
24
+ {
25
+ name: 'expression',
26
+ param: 'expression',
27
+ required: false,
28
+ description:
29
+ 'The XLE/XLO expression. Pass - to read it from stdin; --file reads it from a file instead.',
30
+ },
31
+ {
32
+ name: 'path',
33
+ param: 'options.targetPath',
34
+ required: false,
35
+ description:
36
+ 'Where to write the TSX, relative to the project root. A path that ends in .tsx, .ts, .jsx, .js, .mjs, .cjs, .css, .scss, .json, .md or .html is the file to write; ' +
37
+ 'any other path is a directory, which receives <Name>.tsx (see --name). An existing file there is replaced. ' +
38
+ 'Omit it to print the code.',
39
+ },
26
40
  ],
27
41
  options: [
28
- {flag: '--file <file>', description: 'Read the expression from a file'},
42
+ {
43
+ flag: '--file <file>',
44
+ description: 'Read the expression from a file (used instead of the argument when both are given)',
45
+ },
29
46
  {
30
47
  flag: '--form <form>',
31
48
  param: 'options.form',
@@ -59,7 +76,7 @@ export const doc = {
59
76
  {code: 0, when: 'success'},
60
77
  {
61
78
  code: 1,
62
- when: 'a missing/empty expression, a bad --name or --form, a parse/validation error, or a path escape',
79
+ when: 'a missing, empty or over-5 MB expression (from stdin or --file), a bad --name or --form, a parse/validation error, or a path escape',
63
80
  },
64
81
  ],
65
82
  related: ['layout check', 'layout grammar'],
@@ -13,7 +13,7 @@ export const doc = {
13
13
  type: 'command',
14
14
  name: 'layout grammar',
15
15
  displayName: 'astryx layout grammar',
16
- namespace: 'cli',
16
+ namespace: 'cli/commands',
17
17
  summary:
18
18
  'Print the XLE/XLO cheatsheet (alias table generated from this branch)',
19
19
  description:
@@ -12,7 +12,7 @@ export const doc = {
12
12
  type: 'command',
13
13
  name: 'layout',
14
14
  displayName: 'astryx layout',
15
- namespace: 'cli',
15
+ namespace: 'cli/commands',
16
16
  summary: 'Generate XDS layouts from compressed expressions (XLE/XLO)',
17
17
  description:
18
18
  'The layout command group. Running astryx layout with no subcommand prints the ' +
@@ -27,8 +27,8 @@ export const doc = {
27
27
  },
28
28
  ],
29
29
  exitCodes: [
30
- {code: 0, when: 'success (help shown, or a subcommand succeeded)'},
31
- {code: 1, when: 'an unknown subcommand'},
30
+ {code: 0, when: 'a subcommand succeeded, or --help'},
31
+ {code: 1, when: 'no subcommand (help is printed) or an unknown subcommand'},
32
32
  ],
33
33
  related: ['template', 'build'],
34
34
  };
@@ -59,6 +59,9 @@ import {NO_RESULT_SET, resultSet} from '../../../foundation/debug/index.mjs';
59
59
  * @property {boolean} [loose]
60
60
  */
61
61
 
62
+ /** The largest layout expression read from --file or stdin. */
63
+ const MAX_EXPRESSION_BYTES = 5 * 1024 * 1024; // 5 MB
64
+
62
65
  /**
63
66
  * Resolve the expression from arg, --file, or stdin ('-').
64
67
  * @param {string} [expr]
@@ -78,8 +81,7 @@ async function readExpression(expr, options = {}) {
78
81
  code: ERROR_CODES.ERR_FILE_NOT_FOUND,
79
82
  });
80
83
  }
81
- const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5 MB
82
- if (stat.size > MAX_FILE_SIZE) {
84
+ if (stat.size > MAX_EXPRESSION_BYTES) {
83
85
  cliError(
84
86
  `File "${options.file}" is too large (${(stat.size / 1024 / 1024).toFixed(1)} MB, max 5 MB)`,
85
87
  {code: ERROR_CODES.ERR_FILE_NOT_FOUND},
@@ -100,9 +102,19 @@ async function readExpression(expr, options = {}) {
100
102
  }
101
103
  }
102
104
  if (expr === '-') {
105
+ // Capped like --file: an endless stream must not be buffered whole.
103
106
  /** @type {Buffer[]} */
104
107
  const chunks = [];
105
- for await (const chunk of process.stdin) chunks.push(/** @type {Buffer} */ (chunk));
108
+ let size = 0;
109
+ for await (const chunk of process.stdin) {
110
+ size += /** @type {Buffer} */ (chunk).length;
111
+ if (size > MAX_EXPRESSION_BYTES) {
112
+ cliError('The layout expression on stdin is too large (max 5 MB)', {
113
+ code: ERROR_CODES.ERR_INVALID_ARGUMENT,
114
+ });
115
+ }
116
+ chunks.push(/** @type {Buffer} */ (chunk));
117
+ }
106
118
  return Buffer.concat(chunks).toString('utf-8');
107
119
  }
108
120
  return expr ?? '';
@@ -154,15 +166,15 @@ export function registerLayout(program) {
154
166
  if (result.data.written) {
155
167
  out.push(
156
168
  text(`[ok] Expanded to ${result.data.written}`),
169
+ // Field names are the JSON keys; todos are summarised, not listed.
157
170
  record(
171
+ {componentsUsed: result.data.componentsUsed, todos: result.data.todos},
158
172
  {
159
- components: result.data.componentsUsed,
160
- todos:
161
- result.data.todos.length > 0
162
- ? `${result.data.todos.length} (search for "TODO(xle)")`
163
- : '',
173
+ format: {
174
+ todos: (/** @type {string[]} */ todos) =>
175
+ `${todos.length} (search for "TODO(xle)")`,
176
+ },
164
177
  },
165
- {labels: {components: 'Components', todos: 'TODOs'}},
166
178
  ),
167
179
  );
168
180
  } else {
@@ -0,0 +1,33 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file `layout expand` and `layout check` document their input modes (the
5
+ * argument, `-` for stdin, --file) and where `layout expand` writes.
6
+ */
7
+
8
+ import {describe, it, expect} from 'vitest';
9
+ import {runCli} from '../../../test-utils/run-cli.mjs';
10
+ import {isFilePathArg} from '../../../foundation/fs/path-safety.mjs';
11
+
12
+ const SLOW = 30_000;
13
+
14
+ describe('layout argument behavior is documented', () => {
15
+ it('layout expand help covers stdin, --file precedence and the output path rule', async () => {
16
+ const {status, stdout} = await runCli(['layout', 'expand', '--help']);
17
+ expect(status).toBe(0);
18
+ expect(stdout).toMatch(/Pass - to read it from stdin/);
19
+ expect(stdout).toMatch(/instead of the argument/);
20
+ expect(stdout).toContain('<Name>.tsx');
21
+ expect(stdout).toMatch(/existing file there is replaced/);
22
+ const exts = [...(stdout.match(/ends in ([.\w, ]+ or \.\w+)/)?.[1] ?? '').matchAll(/\.\w+/g)];
23
+ expect(exts.length).toBeGreaterThan(0);
24
+ for (const [ext] of exts) expect(isFilePathArg(`src/Page${ext}`)).toBe(true);
25
+ }, SLOW);
26
+
27
+ it('layout check help covers stdin and --file precedence', async () => {
28
+ const {status, stdout} = await runCli(['layout', 'check', '--help']);
29
+ expect(status).toBe(0);
30
+ expect(stdout).toMatch(/Pass - to read it from stdin/);
31
+ expect(stdout).toMatch(/instead of the argument/);
32
+ }, SLOW);
33
+ });
@@ -0,0 +1,47 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file `layout check -` reads the expression from stdin under the same 5 MB
5
+ * cap as --file, instead of buffering whatever the pipe delivers. Spawns the
6
+ * real bin, since the in-process harness has no stdin.
7
+ */
8
+
9
+ import {describe, it, expect} from 'vitest';
10
+ import {spawnSync} from 'node:child_process';
11
+ import * as path from 'node:path';
12
+ import {fileURLToPath} from 'node:url';
13
+
14
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
15
+ const BIN = path.resolve(HERE, '../bin/astryx.mjs');
16
+ // Run against the monorepo root so @astryxdesign/core is discoverable.
17
+ const REPO = path.resolve(HERE, '../../../..');
18
+ const SLOW = 60_000;
19
+
20
+ /** @param {string} input @param {string[]} [extra] */
21
+ function checkFromStdin(input, extra = []) {
22
+ return spawnSync(process.execPath, [BIN, 'layout', 'check', '-', ...extra], {
23
+ cwd: REPO,
24
+ input,
25
+ encoding: 'utf-8',
26
+ maxBuffer: 16 * 1024 * 1024,
27
+ });
28
+ }
29
+
30
+ describe('layout check - (stdin)', () => {
31
+ it('refuses more than 5 MB with ERR_INVALID_ARGUMENT, in both modes', () => {
32
+ // Valid once trimmed, so only the cap can reject it.
33
+ const big = ' '.repeat(5 * 1024 * 1024 + 1) + 'V';
34
+ const json = checkFromStdin(big, ['--json']);
35
+ expect(json.status).toBe(1);
36
+ expect(JSON.parse(json.stdout).code).toBe('ERR_INVALID_ARGUMENT');
37
+ const human = checkFromStdin(big);
38
+ expect(human.status).toBe(1);
39
+ expect(human.stderr + human.stdout).toMatch(/too large/);
40
+ }, SLOW);
41
+
42
+ it('still reads a normal expression', () => {
43
+ const r = checkFromStdin('V > B', ['--json']);
44
+ expect(r.status).toBe(0);
45
+ expect(JSON.parse(r.stdout).data.valid).toBe(true);
46
+ }, SLOW);
47
+ });
@@ -0,0 +1,39 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file `layout expand` text output names its fields after the JSON keys, so
5
+ * the two views stay greppable against each other.
6
+ */
7
+
8
+ import {describe, it, expect, beforeEach, afterEach} from 'vitest';
9
+ import * as fs from 'node:fs';
10
+ import * as path from 'node:path';
11
+ import {runCli} from '../../../test-utils/run-cli.mjs';
12
+
13
+ const SLOW = 60_000;
14
+
15
+ describe('layout expand text fields mirror the JSON keys', () => {
16
+ let cwd;
17
+ beforeEach(() => {
18
+ // Inside the workspace so @astryxdesign/core resolves.
19
+ cwd = fs.mkdtempSync(path.join(process.cwd(), '.xle-text-fields-'));
20
+ });
21
+ afterEach(() => fs.rmSync(cwd, {recursive: true, force: true}));
22
+
23
+ it('labels every record field with a key of the layout.expand data', async () => {
24
+ const expr = 'V > B + {not-a-real-block}';
25
+ const json = await runCli(['layout', 'expand', expr, './a', '--loose', '--json'], cwd);
26
+ expect(json.status).toBe(0);
27
+ const {data} = JSON.parse(json.stdout);
28
+ expect(data.todos.length).toBeGreaterThan(0);
29
+
30
+ const human = await runCli(['layout', 'expand', expr, './b', '--loose'], cwd);
31
+ expect(human.status).toBe(0);
32
+ const fields = human.stdout
33
+ .split('\n')
34
+ .map(line => /^([A-Za-z]+):\s/.exec(line)?.[1])
35
+ .filter(Boolean);
36
+ expect(fields).toEqual(['componentsUsed', 'todos']);
37
+ for (const field of fields) expect(Object.keys(data)).toContain(field);
38
+ }, SLOW);
39
+ });
@@ -12,7 +12,7 @@ export const doc = {
12
12
  type: 'command',
13
13
  name: 'manifest',
14
14
  displayName: 'astryx manifest',
15
- namespace: 'cli',
15
+ namespace: 'cli/commands',
16
16
  summary: 'Print the full CLI capability manifest (use with --json).',
17
17
  description:
18
18
  'Emits the complete CLI surface: every command with its arguments, options, ' +