@astryxdesign/cli 0.6.3 → 0.6.4-canary.06c8fa3

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 (670) hide show
  1. package/CHANGELOG.md +194 -0
  2. package/README.md +121 -85
  3. package/api/blog/blog.doc.mjs +1 -0
  4. package/api/build/_adapter.d.mts +50 -0
  5. package/api/build/_adapter.mjs +60 -0
  6. package/api/build/build.doc.mjs +16 -9
  7. package/api/build/build.test.mjs +197 -8
  8. package/api/build/build.type.d.mts +91 -2
  9. package/api/build/build.type.mjs +52 -8
  10. package/api/build/help/help.d.mts +12 -5
  11. package/api/build/help/help.mjs +69 -6
  12. package/api/build/kit/kit.d.mts +4 -1
  13. package/api/build/kit/kit.mjs +165 -49
  14. package/api/build/kit/rank.d.mts +44 -0
  15. package/api/build/kit/rank.mjs +432 -0
  16. package/api/build/kit/rank.test.mjs +196 -0
  17. package/api/component/_adapter.d.mts +31 -12
  18. package/api/component/_adapter.mjs +79 -15
  19. package/api/component/component.d.mts +6 -3
  20. package/api/component/component.doc.mjs +36 -13
  21. package/api/component/component.mjs +339 -22
  22. package/api/component/component.test.mjs +38 -0
  23. package/api/component/component.type.d.mts +47 -11
  24. package/api/component/component.type.mjs +76 -24
  25. package/api/component/detail/blocks/blocks.d.mts +2 -1
  26. package/api/component/detail/blocks/blocks.mjs +4 -3
  27. package/api/component/list/list.d.mts +0 -5
  28. package/api/component/list/list.mjs +40 -11
  29. package/api/discover/_adapter.d.mts +114 -6
  30. package/api/discover/_adapter.mjs +372 -17
  31. package/api/discover/_adapter.test.mjs +215 -0
  32. package/api/discover/_catalog-view.d.mts +115 -0
  33. package/api/discover/_catalog-view.mjs +203 -0
  34. package/api/discover/_catalog-view.test.mjs +128 -0
  35. package/api/discover/detail/detail.d.mts +18 -6
  36. package/api/discover/detail/detail.mjs +67 -13
  37. package/api/discover/detail/detail.test.mjs +85 -0
  38. package/api/discover/detail/item/item.d.mts +26 -0
  39. package/api/discover/detail/item/item.mjs +78 -0
  40. package/api/discover/detail/item/item.test.mjs +73 -0
  41. package/api/discover/discover.d.mts +3 -9
  42. package/api/discover/discover.doc.mjs +62 -18
  43. package/api/discover/discover.mjs +220 -36
  44. package/api/discover/discover.test.mjs +11 -2
  45. package/api/discover/discover.type.d.mts +150 -11
  46. package/api/discover/discover.type.mjs +107 -17
  47. package/api/discover/list/list.d.mts +20 -6
  48. package/api/discover/list/list.mjs +45 -12
  49. package/api/discover/list/list.test.mjs +46 -0
  50. package/api/discover/search/search.d.mts +18 -16
  51. package/api/discover/search/search.mjs +102 -56
  52. package/api/discover/search/search.test.mjs +144 -10
  53. package/api/docs/_adapter.d.mts +272 -41
  54. package/api/docs/_adapter.mjs +985 -108
  55. package/api/docs/compiled-topics.test.mjs +78 -0
  56. package/api/docs/detail/detail.mjs +22 -63
  57. package/api/docs/detail/section/section.d.mts +1 -1
  58. package/api/docs/detail/section/section.mjs +54 -19
  59. package/api/docs/detail/section/section.test.mjs +50 -0
  60. package/api/docs/docs.d.mts +10 -3
  61. package/api/docs/docs.doc.mjs +55 -16
  62. package/api/docs/docs.mjs +53 -10
  63. package/api/docs/docs.test.mjs +166 -4
  64. package/api/docs/docs.type.d.mts +221 -5
  65. package/api/docs/docs.type.mjs +153 -11
  66. package/api/docs/index/index.d.mts +18 -0
  67. package/api/docs/index/index.mjs +40 -0
  68. package/api/docs/index/index.test.mjs +62 -0
  69. package/api/docs/integration-tree.test.mjs +555 -0
  70. package/api/docs/integrationDocs.test.mjs +114 -8
  71. package/api/docs/list/list.mjs +28 -12
  72. package/api/docs/node/node.d.mts +43 -0
  73. package/api/docs/node/node.mjs +192 -0
  74. package/api/docs/reference-blocks.test.mjs +406 -0
  75. package/api/doctor/doctor.d.mts +104 -1
  76. package/api/doctor/doctor.doc.mjs +1 -0
  77. package/api/doctor/doctor.mjs +635 -7
  78. package/api/doctor/doctor.test.mjs +732 -11
  79. package/api/gap-report/gap-report.doc.mjs +8 -4
  80. package/api/hook/_adapter.mjs +19 -5
  81. package/api/hook/hook.doc.mjs +1 -0
  82. package/api/hook/hook.type.d.mts +3 -3
  83. package/api/hook/hook.type.mjs +11 -11
  84. package/api/hook/list/list.d.mts +2 -2
  85. package/api/hook/list/list.mjs +69 -17
  86. package/api/index.d.mts +2 -3
  87. package/api/index.mjs +5 -4
  88. package/api/init/init.doc.mjs +6 -1
  89. package/api/init/init.test.mjs +41 -1
  90. package/api/init/remove/remove.mjs +1 -1
  91. package/api/init/run/run.mjs +20 -10
  92. package/api/integration/add-contribution.component-names.test.mjs +120 -0
  93. package/api/integration/add-contribution.d.mts +2 -1
  94. package/api/integration/add-contribution.mjs +130 -15
  95. package/api/integration/add-contribution.test.mjs +258 -7
  96. package/api/integration/add-helpers.d.mts +5 -2
  97. package/api/integration/add-helpers.mjs +36 -9
  98. package/api/integration/add-theme.mjs +34 -64
  99. package/api/integration/add-theme.test.mjs +105 -21
  100. package/api/integration/authoring-checks.mjs +138 -28
  101. package/api/integration/authoring-checks.test.mjs +179 -7
  102. package/api/integration/authoring-checks.type.mjs +6 -1
  103. package/api/integration/integration-authoring.type.d.mts +3 -1
  104. package/api/integration/integration-authoring.type.mjs +2 -0
  105. package/api/integration/integration-block-exports.test.mjs +10 -6
  106. package/api/integration/integrationAdd.doc.mjs +14 -4
  107. package/api/integration/integrationAddAgentDoc.doc.mjs +1 -0
  108. package/api/integration/integrationAddCodemod.doc.mjs +1 -0
  109. package/api/integration/integrationAddComponent.doc.mjs +2 -1
  110. package/api/integration/integrationAddDoc.doc.mjs +8 -1
  111. package/api/integration/integrationAddTemplate.doc.mjs +1 -0
  112. package/api/integration/integrationAddTheme.doc.mjs +6 -5
  113. package/api/integration/integrationComponentConflicts.doc.mjs +1 -0
  114. package/api/integration/integrationDocConflicts.doc.mjs +2 -1
  115. package/api/integration/integrationPackCheck.doc.mjs +2 -1
  116. package/api/integration/integrationTemplateConflicts.doc.d.mts +3 -0
  117. package/api/integration/integrationTemplateConflicts.doc.mjs +4 -0
  118. package/api/integration/pack-check.lifecycle-output.test.mjs +105 -0
  119. package/api/integration/pack-check.mjs +111 -10
  120. package/api/integration/pack-check.test.mjs +387 -47
  121. package/api/integration/pack-check.type.d.mts +26 -2
  122. package/api/integration/pack-check.type.mjs +14 -1
  123. package/api/integration/summarizeIssues.doc.mjs +1 -0
  124. package/api/integration/template-conflict-compatibility.test.mjs +73 -0
  125. package/api/integration/validate-integration-fixes.test.mjs +1389 -0
  126. package/api/integration/validate-integration.mjs +52 -102
  127. package/api/integration/validate-integration.test.mjs +179 -26
  128. package/api/integration/validate-unread-theme-folders.test.mjs +110 -0
  129. package/api/integration/validateIntegration.doc.mjs +3 -2
  130. package/api/json/assertResponse.doc.mjs +1 -0
  131. package/api/json/envelope-types.test.mjs +76 -0
  132. package/api/json/index.ts +2 -1
  133. package/api/json/isError.doc.mjs +1 -0
  134. package/api/json/parseResponse.doc.mjs +3 -2
  135. package/api/search/search-return-type.test.mjs +54 -0
  136. package/api/search/search.d.mts +62 -11
  137. package/api/search/search.doc.mjs +8 -2
  138. package/api/search/search.mjs +471 -83
  139. package/api/search/search.test.mjs +142 -1
  140. package/api/search/search.type.d.mts +15 -3
  141. package/api/search/search.type.mjs +5 -2
  142. package/api/swizzle/copy/copy.mjs +28 -11
  143. package/api/swizzle/swizzle.doc.mjs +2 -1
  144. package/api/swizzle/swizzle.type.d.mts +2 -2
  145. package/api/swizzle/swizzle.type.mjs +2 -2
  146. package/api/template/copy/copy.mjs +17 -23
  147. package/api/template/copy/copy.test.mjs +17 -0
  148. package/api/template/list/list.mjs +1 -0
  149. package/api/template/table-floating-bulk-actions.test.mjs +66 -0
  150. package/api/template/template-integration.test.mjs +1008 -3
  151. package/api/template/template-suffix.test.mjs +41 -21
  152. package/api/template/template.d.mts +1 -1
  153. package/api/template/template.doc.mjs +30 -8
  154. package/api/template/template.mjs +46 -9
  155. package/api/template/template.type.d.mts +12 -14
  156. package/api/template/template.type.mjs +15 -14
  157. package/api/theme/_adapter.d.mts +2 -3
  158. package/api/theme/_adapter.mjs +4 -5
  159. package/api/theme/add/add.binary.test.mjs +84 -0
  160. package/api/theme/add/add.mjs +31 -22
  161. package/api/theme/add/add.rollback.test.mjs +158 -0
  162. package/api/theme/add/add.staging.test.mjs +83 -0
  163. package/api/theme/add/add.test.mjs +14 -1
  164. package/api/theme/build/build.family.test.mjs +7 -12
  165. package/api/theme/build/build.mjs +140 -59
  166. package/api/theme/build/build.public-component-vars.test.mjs +1 -1
  167. package/api/theme/build/build.receipt-doc.test.mjs +111 -0
  168. package/api/theme/build/build.rollback.test.mjs +148 -0
  169. package/api/theme/build/build.test.mjs +127 -0
  170. package/api/theme/build/font-warning.mjs +3 -3
  171. package/api/theme/build/font-warning.test.mjs +5 -2
  172. package/api/theme/generateTonalPalette.doc.mjs +1 -0
  173. package/api/theme/integration-themes.test.mjs +39 -28
  174. package/api/theme/list/list.test.mjs +19 -20
  175. package/api/theme/listThemes.doc.mjs +6 -5
  176. package/api/theme/palette/generate/generate.mjs +8 -3
  177. package/api/theme/palette/generate/generate.test.mjs +96 -0
  178. package/api/theme/palette/generate/generator.d.mts +10 -13
  179. package/api/theme/palette/generate/generator.mjs +15 -4
  180. package/api/theme/palette/generate/generator.test.mjs +10 -0
  181. package/api/theme/template/template.mjs +11 -2
  182. package/api/theme/template/template.test.mjs +20 -0
  183. package/api/theme/theme.type.d.mts +170 -11
  184. package/api/theme/theme.type.mjs +94 -27
  185. package/api/theme/themeAdd.doc.mjs +4 -3
  186. package/api/theme/themeBuild.doc.mjs +8 -4
  187. package/api/theme/themeList.doc.mjs +6 -3
  188. package/api/theme/themeListAvailable.doc.mjs +5 -3
  189. package/api/theme/themePaletteGenerate.doc.mjs +1 -0
  190. package/api/theme/themeTargets.doc.mjs +1 -0
  191. package/api/theme/themeTemplate.doc.mjs +6 -2
  192. package/api/upgrade/_adapter.d.mts +32 -5
  193. package/api/upgrade/_adapter.mjs +139 -22
  194. package/api/upgrade/list/list.mjs +2 -1
  195. package/api/upgrade/list/list.test.mjs +73 -0
  196. package/api/upgrade/project-context.test.mjs +272 -0
  197. package/api/upgrade/provider-agreement.test.mjs +152 -0
  198. package/api/upgrade/run/files-changed.test.mjs +111 -0
  199. package/api/upgrade/run/run.mjs +359 -60
  200. package/api/upgrade/status/status.mjs +2 -2
  201. package/api/upgrade/upgrade.doc.mjs +12 -5
  202. package/api/upgrade/upgrade.type.d.mts +43 -5
  203. package/api/upgrade/upgrade.type.mjs +29 -13
  204. package/assets/codemods/__tests__/registry.test.mjs +1 -0
  205. package/assets/codemods/__tests__/runner.test.mjs +332 -8
  206. package/assets/codemods/file-count.test.mjs +163 -0
  207. package/assets/codemods/integration-discovery.mjs +48 -4
  208. package/assets/codemods/integration-discovery.test.mjs +73 -0
  209. package/assets/codemods/integration-runner.mjs +59 -7
  210. package/assets/codemods/integration-runner.protection.test.mjs +153 -0
  211. package/assets/codemods/registry.mjs +1 -0
  212. package/assets/codemods/run-codemod.mjs +177 -34
  213. package/assets/codemods/runner.mjs +353 -104
  214. package/assets/codemods/term-log.mjs +32 -8
  215. package/assets/codemods/term-log.test.mjs +19 -1
  216. package/assets/codemods/transform-prop.mjs +109 -0
  217. package/assets/codemods/transform-prop.test.mjs +95 -0
  218. package/assets/codemods/transforms/v0.0.14/__tests__/rename-status-variants.test.mjs +86 -165
  219. package/assets/codemods/transforms/v0.0.14/rename-status-variants.mjs +72 -210
  220. package/assets/codemods/transforms/v0.1.8/__tests__/rename-avatar-size-scale.test.mjs +83 -115
  221. package/assets/codemods/transforms/v0.1.8/rename-avatar-size-scale.mjs +57 -186
  222. package/assets/codemods/transforms/v0.3.0/__tests__/unwrap-authoring-factories.test.mjs +27 -5
  223. package/assets/codemods/transforms/v0.3.0/unwrap-authoring-factories.mjs +20 -5
  224. package/assets/codemods/transforms/v0.6.0/__tests__/next-codemods.test.mjs +47 -0
  225. package/assets/codemods/transforms/v0.6.0/rename-resizable-pixel-bounds.mjs +57 -10
  226. package/assets/codemods/transforms/v0.6.4/__tests__/migrate-native-picker-to-presentation.test.mjs +63 -0
  227. package/assets/codemods/transforms/v0.6.4/__tests__/migrate-theme-catalog-to-descriptors.test.mjs +220 -0
  228. package/assets/codemods/transforms/v0.6.4/index.mjs +31 -0
  229. package/assets/codemods/transforms/v0.6.4/migrate-native-picker-to-presentation.mjs +148 -0
  230. package/assets/codemods/transforms/v0.6.4/migrate-theme-catalog-to-descriptors.mjs +141 -0
  231. package/assets/docs/README.md +9 -0
  232. package/assets/docs/authoring.doc.mjs +14 -0
  233. package/assets/docs/getting-started.doc.mjs +2 -2
  234. package/assets/docs/internationalization.doc.mjs +7 -5
  235. package/assets/docs/layout.doc.dense.mjs +2 -2
  236. package/assets/docs/layout.doc.mjs +1 -1
  237. package/assets/docs/principles.doc.mjs +6 -6
  238. package/assets/docs/styling-libraries.doc.mjs +4 -4
  239. package/assets/docs/styling.doc.mjs +4 -4
  240. package/assets/docs/theme.doc.mjs +5 -5
  241. package/assets/docs/tokens.doc.mjs +1 -1
  242. package/assets/docs/tree/api.doc.mjs +30 -0
  243. package/assets/docs/tree/cli.doc.mjs +23 -0
  244. package/assets/docs/tree/commands.doc.mjs +25 -0
  245. package/assets/docs/tree/component-lookups.doc.mjs +149 -0
  246. package/assets/docs/{cli-integrations.doc.mjs → tree/integrations.doc.mjs} +144 -26
  247. package/assets/docs/tree/integrations.test.mjs +62 -0
  248. package/assets/docs/tree/writing-docs.doc.mjs +286 -0
  249. package/assets/docs/working-with-ai.doc.mjs +4 -4
  250. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.doc.mjs +1 -1
  251. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.tsx +1 -3
  252. package/assets/templates/blocks/components/DateInput/DateInputDateRange.tsx +1 -1
  253. package/assets/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.tsx +1 -1
  254. package/assets/templates/blocks/components/Item/ItemDocumentTabs.doc.mjs +14 -0
  255. package/assets/templates/blocks/components/Item/ItemDocumentTabs.tsx +100 -0
  256. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.doc.mjs +14 -0
  257. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.tsx +61 -0
  258. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.doc.mjs +14 -0
  259. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.tsx +126 -0
  260. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.doc.mjs +15 -0
  261. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.tsx +86 -0
  262. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.doc.mjs +14 -0
  263. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.tsx +99 -0
  264. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.doc.mjs +14 -0
  265. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.tsx +122 -0
  266. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.doc.mjs +14 -0
  267. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.tsx +95 -0
  268. package/assets/templates/blocks/components/Table/TableBulkActionsTable.doc.mjs +14 -0
  269. package/assets/templates/blocks/components/Table/TableBulkActionsTable.tsx +71 -0
  270. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.doc.mjs +19 -0
  271. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.tsx +139 -0
  272. package/assets/templates/blocks/components/TimeInput/TimeInputConstrained.tsx +1 -0
  273. package/assets/templates/blocks/components/Timer/TimerFormats.doc.mjs +14 -0
  274. package/assets/templates/blocks/components/Timer/TimerFormats.tsx +34 -0
  275. package/assets/templates/blocks/components/Timer/TimerInline.doc.mjs +14 -0
  276. package/assets/templates/blocks/components/Timer/TimerInline.tsx +14 -0
  277. package/assets/templates/blocks/components/Timer/TimerShowcase.doc.mjs +13 -0
  278. package/assets/templates/blocks/components/Timer/TimerShowcase.tsx +47 -0
  279. package/assets/templates/blocks/components/Timer/TimerTypography.doc.mjs +14 -0
  280. package/assets/templates/blocks/components/Timer/TimerTypography.tsx +31 -0
  281. package/assets/templates/blocks/components/Toolbar/ToolbarTableFilter.doc.mjs +19 -3
  282. package/assets/templates/blocks/components/Toolbar/ToolbarTableFilter.tsx +383 -65
  283. package/assets/templates/pages/table-tree/page.tsx +1704 -0
  284. package/assets/templates/pages/table-tree/template.doc.mjs +12 -0
  285. package/assets/templates/themes/butter/butterTheme.doc.mjs +11 -0
  286. package/assets/templates/themes/chocolate/chocolateTheme.doc.mjs +11 -0
  287. package/assets/templates/themes/gothic/gothicTheme.doc.mjs +11 -0
  288. package/assets/templates/themes/matcha/matchaTheme.doc.mjs +11 -0
  289. package/assets/templates/themes/neutral/neutralTheme.doc.mjs +11 -0
  290. package/assets/templates/themes/stone/stoneTheme.doc.mjs +11 -0
  291. package/assets/templates/themes/y2k/y2kTheme.doc.mjs +11 -0
  292. package/authoring/_shared/contract.ts +22 -0
  293. package/authoring/codemod/codemod.doc.mjs +7 -2
  294. package/authoring/codemod/parse.d.mts +8 -8
  295. package/authoring/codemod/parse.mjs +8 -6
  296. package/authoring/codemod/type.ts +12 -0
  297. package/authoring/config/config.doc.mjs +11 -3
  298. package/authoring/config/debug-composition.test.mjs +92 -0
  299. package/authoring/config/parse.d.mts +15 -13
  300. package/authoring/config/parse.mjs +27 -8
  301. package/authoring/config/parse.test.mjs +8 -0
  302. package/authoring/config/type.ts +31 -8
  303. package/authoring/debug/debug.doc.d.mts +11 -0
  304. package/authoring/debug/debug.doc.mjs +182 -0
  305. package/authoring/debug/parse.d.mts +8 -8
  306. package/authoring/debug/parse.mjs +3 -3
  307. package/authoring/discover/discover.doc.d.mts +13 -0
  308. package/authoring/discover/discover.doc.mjs +138 -0
  309. package/authoring/discover/parse.d.mts +24 -0
  310. package/authoring/discover/parse.mjs +128 -0
  311. package/authoring/discover/parse.test.mjs +124 -0
  312. package/authoring/discover/type.ts +87 -0
  313. package/authoring/doctypes/_schema.d.mts +790 -23
  314. package/authoring/doctypes/_schema.mjs +543 -39
  315. package/authoring/doctypes/base/graph-fields.doc.d.mts +9 -0
  316. package/authoring/doctypes/base/graph-fields.doc.mjs +64 -0
  317. package/authoring/doctypes/base/type.ts +41 -0
  318. package/authoring/doctypes/command/command.doc.mjs +5 -4
  319. package/authoring/doctypes/command/parse.d.mts +2 -2
  320. package/authoring/doctypes/command/parse.mjs +1 -1
  321. package/authoring/doctypes/command/type.ts +6 -5
  322. package/authoring/doctypes/component/component.doc.mjs +6 -3
  323. package/authoring/doctypes/component/parse.d.mts +2 -2
  324. package/authoring/doctypes/component/parse.mjs +1 -1
  325. package/authoring/doctypes/component/type.ts +6 -5
  326. package/authoring/doctypes/doctypes-new.test.mjs +48 -6
  327. package/authoring/doctypes/enum/enum.doc.mjs +1 -1
  328. package/authoring/doctypes/enum/parse.d.mts +2 -2
  329. package/authoring/doctypes/enum/parse.mjs +1 -1
  330. package/authoring/doctypes/enum/type.ts +4 -2
  331. package/authoring/doctypes/function/function.doc.mjs +7 -2
  332. package/authoring/doctypes/function/parse.d.mts +2 -2
  333. package/authoring/doctypes/function/parse.mjs +1 -1
  334. package/authoring/doctypes/function/type.ts +9 -4
  335. package/authoring/doctypes/hook/hook.doc.mjs +4 -0
  336. package/authoring/doctypes/hook/parse.d.mts +2 -2
  337. package/authoring/doctypes/hook/parse.mjs +1 -1
  338. package/authoring/doctypes/hook/type.ts +5 -4
  339. package/authoring/doctypes/legacy.d.mts +8 -6
  340. package/authoring/doctypes/legacy.mjs +5 -4
  341. package/authoring/doctypes/load-contract.test.mjs +233 -0
  342. package/authoring/doctypes/namespace/namespace.doc.d.mts +9 -0
  343. package/authoring/doctypes/namespace/namespace.doc.mjs +128 -0
  344. package/authoring/doctypes/namespace/parse.d.mts +12 -0
  345. package/authoring/doctypes/namespace/parse.mjs +25 -0
  346. package/authoring/doctypes/namespace/parse.test.mjs +163 -0
  347. package/authoring/doctypes/namespace/type.ts +74 -0
  348. package/authoring/doctypes/parse.d.mts +22 -18
  349. package/authoring/doctypes/parse.mjs +22 -11
  350. package/authoring/doctypes/parse.test.mjs +77 -3
  351. package/authoring/doctypes/reference/parse.d.mts +2 -2
  352. package/authoring/doctypes/reference/parse.mjs +8 -5
  353. package/authoring/doctypes/reference/reference.doc.mjs +48 -6
  354. package/authoring/doctypes/reference/type.ts +70 -7
  355. package/authoring/doctypes/schema/parse.d.mts +2 -2
  356. package/authoring/doctypes/schema/parse.mjs +1 -1
  357. package/authoring/doctypes/schema/schema.doc.mjs +1 -1
  358. package/authoring/doctypes/schema/type.ts +4 -4
  359. package/authoring/doctypes/template/parse.d.mts +94 -1
  360. package/authoring/doctypes/template/parse.mjs +40 -2
  361. package/authoring/doctypes/template/parse.test.mjs +26 -2
  362. package/authoring/doctypes/template/template.doc.mjs +13 -3
  363. package/authoring/doctypes/template/type.ts +13 -2
  364. package/authoring/doctypes/theme/parse.d.mts +35 -0
  365. package/authoring/doctypes/theme/parse.mjs +76 -0
  366. package/authoring/doctypes/theme/theme.doc.d.mts +9 -0
  367. package/authoring/doctypes/theme/theme.doc.mjs +79 -0
  368. package/authoring/doctypes/theme/type.ts +42 -0
  369. package/authoring/doctypes/types.ts +12 -10
  370. package/authoring/gap-report/gap-report.doc.d.mts +12 -0
  371. package/authoring/gap-report/gap-report.doc.mjs +183 -0
  372. package/authoring/gap-report/parse.d.mts +10 -10
  373. package/authoring/gap-report/parse.mjs +6 -6
  374. package/authoring/gap-report/type.ts +1 -1
  375. package/authoring/identity/identity.doc.d.mts +9 -0
  376. package/authoring/identity/identity.doc.mjs +61 -0
  377. package/authoring/identity/type.ts +132 -0
  378. package/authoring/index.d.mts +3 -0
  379. package/authoring/index.d.ts +62 -17
  380. package/authoring/index.mjs +4 -1
  381. package/authoring/integration/integration.doc.mjs +15 -8
  382. package/authoring/integration/parse.d.mts +2 -2
  383. package/authoring/integration/parse.mjs +1 -1
  384. package/authoring/integration/parse.test.mjs +10 -1
  385. package/authoring/integration/schema.d.mts +6 -4
  386. package/authoring/integration/schema.mjs +9 -3
  387. package/authoring/integration/type.ts +19 -8
  388. package/authoring/shadcn/receipt.d.mts +6 -6
  389. package/clients/cli/__tests__/cliManifest.test.ts +27 -29
  390. package/clients/cli/command-load-failure.test.mjs +83 -0
  391. package/clients/cli/command-result-coverage.test.mjs +7 -7
  392. package/clients/cli/commands/blog.doc.mjs +1 -1
  393. package/clients/cli/commands/blog.mjs +23 -8
  394. package/clients/cli/commands/blog.test.mjs +42 -1
  395. package/clients/cli/commands/build-theme.adaptations.test.mjs +100 -1
  396. package/clients/cli/commands/build-theme.ascii-output.test.mjs +161 -0
  397. package/clients/cli/commands/build-theme.flag-docs.test.mjs +110 -0
  398. package/clients/cli/commands/build-theme.mjs +16 -50
  399. package/clients/cli/commands/build-theme.path-safety.test.mjs +86 -1
  400. package/clients/cli/commands/build-theme.variants.test.mjs +76 -0
  401. package/clients/cli/commands/build.doc.mjs +16 -8
  402. package/clients/cli/commands/build.exit-codes-doc.test.mjs +50 -0
  403. package/clients/cli/commands/build.mjs +137 -114
  404. package/clients/cli/commands/build.playbook.test.mjs +75 -0
  405. package/clients/cli/commands/build.text-fields.test.mjs +81 -0
  406. package/clients/cli/commands/component/index.mjs +153 -61
  407. package/clients/cli/commands/component-batch.test.mjs +341 -0
  408. package/clients/cli/commands/component-ownership.test.mjs +92 -3
  409. package/clients/cli/commands/component-package.test.mjs +46 -0
  410. package/clients/cli/commands/component-resolution.test.mjs +21 -0
  411. package/clients/cli/commands/component.doc.mjs +24 -7
  412. package/clients/cli/commands/component.test.mjs +19 -0
  413. package/clients/cli/commands/debug-result-summary.test.mjs +2 -2
  414. package/clients/cli/commands/detail-levels.test.mjs +2 -2
  415. package/clients/cli/commands/discover.broken-integration.test.mjs +52 -5
  416. package/clients/cli/commands/discover.components-flag.test.mjs +97 -0
  417. package/clients/cli/commands/discover.doc.mjs +55 -9
  418. package/clients/cli/commands/discover.mjs +393 -118
  419. package/clients/cli/commands/discover.sources.test.mjs +267 -0
  420. package/clients/cli/commands/discover.text-projection.test.mjs +103 -0
  421. package/clients/cli/commands/docs.doc.mjs +28 -6
  422. package/clients/cli/commands/docs.mjs +240 -26
  423. package/clients/cli/commands/docs.test.mjs +222 -1
  424. package/clients/cli/commands/doctor-integration-components.doc.mjs +1 -1
  425. package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -3
  426. package/clients/cli/commands/doctor-integration-templates.doc.mjs +15 -8
  427. package/clients/cli/commands/doctor-integration-validate.doc.mjs +1 -1
  428. package/clients/cli/commands/doctor-integration.doc.mjs +1 -1
  429. package/clients/cli/commands/doctor-integration.package-json.test.mjs +53 -0
  430. package/clients/cli/commands/doctor-integration.test.mjs +90 -8
  431. package/clients/cli/commands/doctor.doc.mjs +1 -1
  432. package/clients/cli/commands/doctor.mjs +59 -32
  433. package/clients/cli/commands/doctor.test.mjs +42 -0
  434. package/clients/cli/commands/gap-report.doc.mjs +17 -6
  435. package/clients/cli/commands/gap-report.test.mjs +72 -0
  436. package/clients/cli/commands/hook/index.mjs +7 -17
  437. package/clients/cli/commands/hook.doc.mjs +1 -1
  438. package/clients/cli/commands/hook.text-projection.test.mjs +45 -0
  439. package/clients/cli/commands/init.doc.mjs +20 -9
  440. package/clients/cli/commands/init.flag-help.test.mjs +153 -0
  441. package/clients/cli/commands/integration-add.controls.test.mjs +132 -0
  442. package/clients/cli/commands/integration-add.doc.mjs +32 -6
  443. package/clients/cli/commands/integration-authoring.test.mjs +13 -9
  444. package/clients/cli/commands/integration-pack.doc.mjs +1 -1
  445. package/clients/cli/commands/integration-real-world.test.mjs +3 -9
  446. package/clients/cli/commands/integration.doc.mjs +1 -1
  447. package/clients/cli/commands/integration.mjs +1 -0
  448. package/clients/cli/commands/interactive-guard.test.mjs +101 -24
  449. package/clients/cli/commands/json-contract.test.mjs +33 -0
  450. package/clients/cli/commands/manifest.doc.mjs +1 -1
  451. package/clients/cli/commands/no-prompt-wording.test.mjs +94 -0
  452. package/clients/cli/commands/search.doc.mjs +7 -4
  453. package/clients/cli/commands/search.mjs +28 -9
  454. package/clients/cli/commands/search.test.mjs +75 -0
  455. package/clients/cli/commands/setup-nudge.test.mjs +6 -0
  456. package/clients/cli/commands/swizzle.doc.mjs +3 -2
  457. package/clients/cli/commands/swizzle.path-safety.test.mjs +42 -0
  458. package/clients/cli/commands/template.doc.mjs +52 -13
  459. package/clients/cli/commands/template.flag-help.test.mjs +117 -0
  460. package/clients/cli/commands/template.mjs +4 -91
  461. package/clients/cli/commands/template.path-help.test.mjs +40 -0
  462. package/clients/cli/commands/text-json-parity.test.mjs +702 -0
  463. package/clients/cli/commands/theme-add.doc.mjs +4 -3
  464. package/clients/cli/commands/theme-build.doc.mjs +8 -7
  465. package/clients/cli/commands/theme-list.doc.mjs +2 -2
  466. package/clients/cli/commands/theme-palette-generate.doc.mjs +9 -5
  467. package/clients/cli/commands/theme-palette-generate.test.mjs +19 -0
  468. package/clients/cli/commands/theme-palette.doc.mjs +1 -1
  469. package/clients/cli/commands/theme-targets.behavior.test.mjs +4 -3
  470. package/clients/cli/commands/theme-targets.doc.mjs +1 -1
  471. package/clients/cli/commands/theme-template.behavior.test.mjs +12 -0
  472. package/clients/cli/commands/theme-template.doc.mjs +2 -2
  473. package/clients/cli/commands/theme.doc.mjs +1 -1
  474. package/clients/cli/commands/upgrade.ascii-output.test.mjs +87 -0
  475. package/clients/cli/commands/upgrade.doc.mjs +22 -10
  476. package/clients/cli/commands/upgrade.file-protection.test.mjs +228 -0
  477. package/clients/cli/commands/upgrade.flag-help.test.mjs +188 -0
  478. package/clients/cli/commands/upgrade.hook-output.test.mjs +88 -0
  479. package/clients/cli/commands/upgrade.mjs +29 -7
  480. package/clients/cli/formatters/index.mjs +164 -1
  481. package/clients/cli/formatters/index.test.mjs +97 -0
  482. package/clients/cli/index.mjs +21 -34
  483. package/clients/cli/latest-version-env.test.mjs +50 -0
  484. package/clients/cli/lib/cli-error.test.mjs +7 -0
  485. package/clients/cli/lib/component-format.mjs +9 -9
  486. package/clients/cli/lib/component-format.test.mjs +1 -1
  487. package/clients/cli/lib/define-command.mjs +32 -6
  488. package/clients/cli/lib/doc-text-ascii.test.mjs +82 -0
  489. package/clients/cli/lib/exit-codes.test.mjs +90 -0
  490. package/clients/cli/lib/hook-format.mjs +19 -10
  491. package/clients/cli/lib/json-shim.mjs +62 -16
  492. package/clients/cli/lib/json-shim.test.mjs +69 -0
  493. package/clients/cli/lib/manifest.d.ts +2 -0
  494. package/clients/cli/lib/manifest.mjs +39 -10
  495. package/clients/cli/lib/manifest.test.mjs +22 -2
  496. package/clients/cli/lib/parse-error-format.test.mjs +81 -0
  497. package/foundation/agent-docs/agent-docs.d.mts +7 -2
  498. package/foundation/agent-docs/agent-docs.mjs +82 -12
  499. package/foundation/agent-docs/agent-docs.path-safety.test.mjs +266 -4
  500. package/foundation/agent-docs/agent-docs.test.mjs +19 -1
  501. package/foundation/config/integration-debug.test.mjs +28 -3
  502. package/foundation/config/project-themes.test.mjs +11 -19
  503. package/foundation/config/project.d.mts +20 -11
  504. package/foundation/config/project.mjs +263 -91
  505. package/foundation/config/project.test.mjs +270 -21
  506. package/foundation/discovery/authoring-self-docs.d.mts +87 -0
  507. package/foundation/discovery/authoring-self-docs.mjs +237 -0
  508. package/foundation/discovery/authoring-self-docs.test.mjs +170 -0
  509. package/foundation/discovery/authoring-surface.d.mts +74 -0
  510. package/foundation/discovery/authoring-surface.mjs +525 -0
  511. package/foundation/discovery/authoring-surface.test.mjs +392 -0
  512. package/foundation/discovery/cli-self-docs.d.mts +119 -0
  513. package/foundation/discovery/cli-self-docs.mjs +490 -0
  514. package/foundation/discovery/cli-self-docs.test.mjs +375 -0
  515. package/foundation/discovery/component-discovery.d.mts +39 -1
  516. package/foundation/discovery/component-discovery.mjs +50 -1
  517. package/foundation/discovery/component-loader.d.mts +35 -38
  518. package/foundation/discovery/component-loader.mjs +53 -222
  519. package/foundation/discovery/docs-discovery.d.mts +119 -11
  520. package/foundation/discovery/docs-discovery.mjs +423 -108
  521. package/foundation/discovery/docs-discovery.test.mjs +365 -20
  522. package/foundation/discovery/docs-output-budget.d.mts +28 -0
  523. package/foundation/discovery/docs-output-budget.mjs +50 -0
  524. package/foundation/discovery/docs-section-key.d.mts +116 -0
  525. package/foundation/discovery/docs-section-key.mjs +322 -0
  526. package/foundation/discovery/docs-section-key.test.mjs +246 -0
  527. package/foundation/discovery/template-adapter.d.mts +113 -11
  528. package/foundation/discovery/template-adapter.fixture-refs.test.mjs +248 -0
  529. package/foundation/discovery/template-adapter.integration-isolation.test.mjs +94 -0
  530. package/foundation/discovery/template-adapter.mjs +775 -84
  531. package/foundation/discovery/template-adapter.test.mjs +57 -0
  532. package/foundation/discovery/template-conflict-release.d.mts +13 -0
  533. package/foundation/discovery/template-conflict-release.mjs +40 -0
  534. package/foundation/discovery/template-conflict-release.test.mjs +40 -0
  535. package/foundation/discovery/theme-discovery.d.mts +67 -7
  536. package/foundation/discovery/theme-discovery.mjs +916 -186
  537. package/foundation/discovery/theme-discovery.test.mjs +613 -219
  538. package/foundation/discovery/theming-targets.test.mjs +4 -0
  539. package/foundation/doc-compiler/bundle.d.mts +47 -0
  540. package/foundation/doc-compiler/bundle.mjs +278 -0
  541. package/foundation/doc-compiler/bundle.test.mjs +266 -0
  542. package/foundation/doc-compiler/compile.d.mts +343 -0
  543. package/foundation/doc-compiler/compile.mjs +558 -0
  544. package/foundation/doc-compiler/diagnostics.d.mts +126 -0
  545. package/foundation/doc-compiler/diagnostics.mjs +305 -0
  546. package/foundation/doc-compiler/doc-compiler.test.mjs +714 -0
  547. package/foundation/doc-compiler/doc-loads.test.mjs +1630 -0
  548. package/foundation/doc-compiler/import.d.mts +24 -0
  549. package/foundation/doc-compiler/import.mjs +59 -0
  550. package/foundation/doc-compiler/inputs.d.mts +102 -0
  551. package/foundation/doc-compiler/inputs.mjs +291 -0
  552. package/foundation/doc-compiler/inputs.test.mjs +299 -0
  553. package/foundation/doc-compiler/ir.d.mts +22 -0
  554. package/foundation/doc-compiler/ir.mjs +471 -0
  555. package/foundation/doc-compiler/lenses.d.mts +36 -0
  556. package/foundation/doc-compiler/lenses.mjs +173 -0
  557. package/foundation/doc-compiler/links.d.mts +162 -0
  558. package/foundation/doc-compiler/links.mjs +294 -0
  559. package/foundation/doc-compiler/links.test.mjs +192 -0
  560. package/foundation/doc-compiler/lower-doc.test.mjs +495 -0
  561. package/foundation/doc-compiler/overlays.d.mts +37 -0
  562. package/foundation/doc-compiler/overlays.mjs +206 -0
  563. package/foundation/doc-compiler/parse-readable.d.mts +9 -0
  564. package/foundation/doc-compiler/parse-readable.mjs +29 -0
  565. package/foundation/doc-compiler/read.d.mts +127 -0
  566. package/foundation/doc-compiler/read.mjs +325 -0
  567. package/foundation/doc-compiler/read.test.mjs +313 -0
  568. package/foundation/doc-compiler/source.d.mts +33 -0
  569. package/foundation/doc-compiler/source.mjs +128 -0
  570. package/foundation/doc-compiler/tree.d.mts +288 -0
  571. package/foundation/doc-compiler/tree.mjs +876 -0
  572. package/foundation/doc-compiler/tree.test.mjs +606 -0
  573. package/foundation/fs/file-protection.d.mts +33 -0
  574. package/foundation/fs/file-protection.mjs +825 -0
  575. package/foundation/fs/file-protection.test.mjs +250 -0
  576. package/foundation/fs/module-loader.d.mts +1 -0
  577. package/foundation/fs/module-loader.mjs +50 -1
  578. package/foundation/fs/module-loader.stdout.test.mjs +332 -0
  579. package/foundation/fs/path-safety.d.mts +3 -2
  580. package/foundation/fs/path-safety.mjs +49 -19
  581. package/foundation/fs/path-safety.test.mjs +50 -0
  582. package/foundation/fs/publish-file-hardlink-unavailable.test.mjs +129 -94
  583. package/foundation/identity/provider-identity.d.mts +90 -0
  584. package/foundation/identity/provider-identity.mjs +320 -0
  585. package/foundation/identity/provider-identity.test.mjs +254 -0
  586. package/foundation/identity/providers.d.mts +7 -0
  587. package/foundation/identity/providers.mjs +16 -0
  588. package/foundation/integrations/autolink.d.mts +58 -1
  589. package/foundation/integrations/autolink.mjs +143 -45
  590. package/foundation/integrations/autolink.test.mjs +1 -1
  591. package/foundation/integrations/cli-requirement.d.mts +45 -0
  592. package/foundation/integrations/cli-requirement.mjs +154 -0
  593. package/foundation/integrations/cli-requirement.test.mjs +84 -0
  594. package/foundation/integrations/contribution-fixes.d.mts +145 -0
  595. package/foundation/integrations/contribution-fixes.mjs +1284 -0
  596. package/foundation/integrations/contribution-inventory.d.mts +3 -2
  597. package/foundation/integrations/contribution-inventory.mjs +27 -24
  598. package/foundation/integrations/contribution-inventory.test.mjs +86 -27
  599. package/foundation/integrations/integration-warnings.d.mts +9 -2
  600. package/foundation/integrations/integration-warnings.mjs +52 -21
  601. package/foundation/integrations/integration-warnings.test.mjs +74 -1
  602. package/foundation/integrations/integrations.d.mts +63 -3
  603. package/foundation/integrations/integrations.mjs +122 -9
  604. package/foundation/integrations/integrations.test.mjs +415 -1
  605. package/foundation/integrations/provider-conflicts.test.mjs +125 -0
  606. package/foundation/integrations/provider-ledger.test.mjs +275 -0
  607. package/foundation/integrations/provider-resolution.d.mts +152 -0
  608. package/foundation/integrations/provider-resolution.mjs +576 -0
  609. package/foundation/integrations/provider-resolution.test.mjs +369 -0
  610. package/foundation/integrations/theme-descriptor.d.mts +8 -0
  611. package/foundation/integrations/theme-descriptor.mjs +44 -0
  612. package/foundation/integrations/validate-contributions.d.mts +2 -0
  613. package/foundation/integrations/validate-contributions.mjs +131 -29
  614. package/foundation/response/base.d.ts +8 -4
  615. package/foundation/response/batch.type.d.mts +33 -0
  616. package/foundation/response/batch.type.mjs +34 -0
  617. package/foundation/response/error-codes.d.mts +3 -1
  618. package/foundation/response/error-codes.d.ts +2 -0
  619. package/foundation/response/error-codes.doc.mjs +13 -4
  620. package/foundation/response/error-codes.mjs +8 -2
  621. package/foundation/response/error-codes.test.mjs +137 -10
  622. package/foundation/response/json-contract.test.mjs +57 -17
  623. package/foundation/response/json.d.mts +4 -2
  624. package/foundation/response/json.mjs +8 -10
  625. package/foundation/response/response-types.doc.d.mts +5 -1
  626. package/foundation/response/response-types.doc.mjs +46 -38
  627. package/foundation/response/response-types.doc.test.mjs +181 -0
  628. package/foundation/response/response.doc.mjs +1 -1
  629. package/foundation/text/string-utils.d.mts +8 -0
  630. package/foundation/text/string-utils.mjs +40 -10
  631. package/foundation/xle/browser.d.mts +3 -3
  632. package/foundation/xle/browser.mjs +3 -3
  633. package/foundation/xle/expand.d.mts +2 -0
  634. package/foundation/xle/expand.mjs +6 -5
  635. package/foundation/xle/expand.test.mjs +54 -0
  636. package/foundation/xle/parse.mjs +1 -1
  637. package/foundation/xle/print.mjs +2 -2
  638. package/foundation/xle/splice.mjs +1 -1
  639. package/foundation/xle/xle.test.mjs +13 -0
  640. package/package.json +10 -11
  641. package/api/layout/_adapter.d.mts +0 -34
  642. package/api/layout/_adapter.mjs +0 -133
  643. package/api/layout/check/check.d.mts +0 -16
  644. package/api/layout/check/check.mjs +0 -40
  645. package/api/layout/expand/expand.d.mts +0 -22
  646. package/api/layout/expand/expand.mjs +0 -153
  647. package/api/layout/grammar/grammar.d.mts +0 -13
  648. package/api/layout/grammar/grammar.mjs +0 -86
  649. package/api/layout/layout.d.mts +0 -6
  650. package/api/layout/layout.mjs +0 -17
  651. package/api/layout/layout.test.mjs +0 -297
  652. package/api/layout/layout.type.d.mts +0 -89
  653. package/api/layout/layout.type.mjs +0 -103
  654. package/api/layout/layoutCheck.doc.d.mts +0 -11
  655. package/api/layout/layoutCheck.doc.mjs +0 -84
  656. package/api/layout/layoutExpand.doc.d.mts +0 -11
  657. package/api/layout/layoutExpand.doc.mjs +0 -106
  658. package/api/layout/layoutGrammar.doc.d.mts +0 -11
  659. package/api/layout/layoutGrammar.doc.mjs +0 -56
  660. package/assets/templates/themes/manifest.json +0 -95
  661. package/clients/cli/commands/layout-check.doc.mjs +0 -54
  662. package/clients/cli/commands/layout-expand.doc.mjs +0 -66
  663. package/clients/cli/commands/layout-grammar.doc.mjs +0 -30
  664. package/clients/cli/commands/layout.doc.mjs +0 -34
  665. package/clients/cli/commands/layout.error-codes.test.mjs +0 -66
  666. package/clients/cli/commands/layout.exit-parity.test.mjs +0 -41
  667. package/clients/cli/commands/layout.mjs +0 -263
  668. package/clients/cli/lib/update-check.mjs +0 -83
  669. package/clients/cli/lib/update-check.test.mjs +0 -137
  670. package/clients/cli/update-hint-commands.test.mjs +0 -54
@@ -11,14 +11,21 @@ export const doc = {
11
11
  type: 'schema',
12
12
  name: 'integration',
13
13
  displayName: 'Astryx Integration',
14
- namespace: 'cli',
14
+ namespace: 'authoring',
15
15
  description:
16
16
  'The astryx.integration.* manifest that sits beside an integration ' +
17
- "package's package.json. Points the CLI at the package's components, " +
18
- 'templates, codemods, doc topics, source themes, and managed agent guidance, ' +
19
- 'and where to file issues. Every field is optional.',
17
+ "package's package.json. It can preserve a stable provider identity across a package rename, " +
18
+ "and points the CLI at the package's components, templates, codemods, doc topics, " +
19
+ 'source themes, managed agent guidance, and issue tracker. Every field is optional.',
20
20
  appliesTo: 'astryx.integration.{ts,mjs,js}',
21
21
  fields: [
22
+ {
23
+ name: 'providerId',
24
+ type: 'string',
25
+ description:
26
+ 'Stable logical provider ID. Omit to use package.json#name; set it to the prior package name only when an explicit rename must preserve artifact IDs. If two packages claim the same ID, the package being authored is used, otherwise the first-loaded one, and the CLI warns about the other.',
27
+ example: "'@acme/widgets'",
28
+ },
22
29
  {
23
30
  name: 'components',
24
31
  type: 'string',
@@ -50,7 +57,7 @@ export const doc = {
50
57
  name: 'themes',
51
58
  type: 'string',
52
59
  description:
53
- 'Relative path to a source-theme catalog root containing manifest.json plus one directory per theme slug. Installed themes appear in `astryx theme list` and can be copied with `astryx theme add`.',
60
+ 'Relative path to a source-theme root with one directory per theme slug. Each directory contains a source module and mandatory same-stem, strongly typed .doc.mjs descriptor. Installed themes appear in `astryx theme list` and can be copied with `astryx theme add`.',
54
61
  example: "'./themes'",
55
62
  },
56
63
  {
@@ -87,9 +94,9 @@ export const doc = {
87
94
  {
88
95
  type: 'prose',
89
96
  text:
90
- "Identity, the integration's name and version, comes from the " +
91
- "package's package.json, not from this manifest. The manifest only " +
92
- 'declares where the CLI finds each kind of artifact.',
97
+ 'Provider identity defaults to package.json#name. During an explicit ' +
98
+ 'package rename, set `providerId` to the prior canonical package name so ' +
99
+ 'existing artifact IDs remain stable. Package version always comes from package.json.',
93
100
  },
94
101
  {
95
102
  type: 'prose',
@@ -1,7 +1,7 @@
1
1
  // @generated by scripts/sync-api-types.mjs from the JSDoc in authoring/**/*.mjs.
2
2
  // DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
3
3
 
4
- /** @typedef {import('./type').AstryxIntegration} AstryxIntegration */
4
+ /** @typedef {import('./type.js').AstryxIntegration} AstryxIntegration */
5
5
  /**
6
6
  * Validate an unknown value as an Astryx integration manifest, or throw.
7
7
  *
@@ -14,4 +14,4 @@
14
14
  * @returns {AstryxIntegration}
15
15
  */
16
16
  export function parseIntegration(input: unknown, label?: string): AstryxIntegration;
17
- export type AstryxIntegration = import("./type").AstryxIntegration;
17
+ export type AstryxIntegration = import("./type.js").AstryxIntegration;
@@ -14,7 +14,7 @@
14
14
  import {formatZodError} from '../_shared/errors.mjs';
15
15
  import {integrationSchema} from './schema.mjs';
16
16
 
17
- /** @typedef {import('./type').AstryxIntegration} AstryxIntegration */
17
+ /** @typedef {import('./type.js').AstryxIntegration} AstryxIntegration */
18
18
 
19
19
  /**
20
20
  * Validate an unknown value as an Astryx integration manifest, or throw.
@@ -28,6 +28,9 @@ describe('parseIntegration (load boundary)', () => {
28
28
  components: './src',
29
29
  });
30
30
  expect(parseIntegration({docs: './docs'})).toEqual({docs: './docs'});
31
+ expect(parseIntegration({providerId: '@acme/legacy-widgets'})).toEqual({
32
+ providerId: '@acme/legacy-widgets',
33
+ });
31
34
  expect(parseIntegration({themes: './themes'})).toEqual({
32
35
  themes: './themes',
33
36
  });
@@ -66,7 +69,9 @@ describe('parseIntegration (load boundary)', () => {
66
69
  ? 'https://example.com/issues'
67
70
  : key === 'agentDocs'
68
71
  ? {}
69
- : './x',
72
+ : key === 'providerId'
73
+ ? '@acme/widgets'
74
+ : './x',
70
75
  ]),
71
76
  );
72
77
  expect(unknownIntegrationKeys(everyKnownKey)).toEqual([]);
@@ -82,6 +87,10 @@ describe('parseIntegration (load boundary)', () => {
82
87
  expect(reason({issuesUrl: 'nope'})).toContain('issuesUrl');
83
88
  });
84
89
 
90
+ it('rejects a noncanonical providerId', () => {
91
+ expect(reason({providerId: 'Acme Widgets'})).toContain('providerId');
92
+ });
93
+
85
94
  it('accepts an optional append array as manifest data', () => {
86
95
  expect(
87
96
  parseIntegration({
@@ -32,6 +32,7 @@ export const agentDocsSchema: z.ZodObject<{
32
32
  append: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodString>>>;
33
33
  }, z.core.$strip>;
34
34
  export const integrationBaseSchema: z.ZodObject<{
35
+ providerId: z.ZodOptional<z.ZodString>;
35
36
  components: z.ZodOptional<z.ZodString>;
36
37
  templates: z.ZodOptional<z.ZodString>;
37
38
  codemods: z.ZodOptional<z.ZodString>;
@@ -40,6 +41,7 @@ export const integrationBaseSchema: z.ZodObject<{
40
41
  issuesUrl: z.ZodOptional<z.ZodString>;
41
42
  }, z.core.$strip>;
42
43
  export const integrationSchema: z.ZodObject<{
44
+ providerId: z.ZodOptional<z.ZodString>;
43
45
  components: z.ZodOptional<z.ZodString>;
44
46
  templates: z.ZodOptional<z.ZodString>;
45
47
  codemods: z.ZodOptional<z.ZodString>;
@@ -53,8 +55,8 @@ export const integrationSchema: z.ZodObject<{
53
55
  /**
54
56
  * Compile-time drift-lock: sealed schema must infer exactly {@link AstryxIntegration}.
55
57
  *
56
- * @typedef {import('../_shared/contract').Expect<
57
- * import('../_shared/contract').Equal<z.infer<typeof integrationSchema>, AstryxIntegration>
58
+ * @typedef {import('../_shared/contract.js').Expect<
59
+ * import('../_shared/contract.js').Equal<z.infer<typeof integrationSchema>, AstryxIntegration>
58
60
  * >} _IntegrationDriftLock
59
61
  */
60
62
  /**
@@ -67,9 +69,9 @@ export const integrationSchema: z.ZodObject<{
67
69
  * @type {string[]}
68
70
  */
69
71
  export const KNOWN_INTEGRATION_KEYS: string[];
70
- export type AstryxIntegration = import("./type").AstryxIntegration;
72
+ export type AstryxIntegration = import("./type.js").AstryxIntegration;
71
73
  /**
72
74
  * Compile-time drift-lock: sealed schema must infer exactly {@link AstryxIntegration}.
73
75
  */
74
- export type _IntegrationDriftLock = import("../_shared/contract").Expect<import("../_shared/contract").Equal<z.infer<typeof integrationSchema>, AstryxIntegration>>;
76
+ export type _IntegrationDriftLock = import("../_shared/contract.js").Expect<import("../_shared/contract.js").Equal<z.infer<typeof integrationSchema>, AstryxIntegration>>;
75
77
  import { z } from 'zod';
@@ -16,11 +16,13 @@
16
16
  import {z} from 'zod';
17
17
  import {formatZodError} from '../_shared/errors.mjs';
18
18
 
19
- /** @typedef {import('./type').AstryxIntegration} AstryxIntegration */
19
+ /** @typedef {import('./type.js').AstryxIntegration} AstryxIntegration */
20
20
 
21
21
  const MAX_AGENT_DOC_LINES = 8;
22
22
  const MAX_AGENT_DOC_LINE_CODE_POINTS = 240;
23
23
  const MANAGED_MARKER_TEXT = /(?:ASTRYX|XDS):(START|END)/u;
24
+ const PROVIDER_ID_RE =
25
+ /^(?:@[a-z0-9][a-z0-9._~!*'()-]*\/)?[a-z0-9][a-z0-9._~!*'()-]*$/u;
24
26
 
25
27
  /**
26
28
  * @param {string} value
@@ -66,6 +68,10 @@ export const agentDocsSchema = z.object({
66
68
  });
67
69
 
68
70
  export const integrationBaseSchema = z.object({
71
+ providerId: z
72
+ .string()
73
+ .regex(PROVIDER_ID_RE, 'providerId must be a canonical npm package name')
74
+ .optional(),
69
75
  components: z.string().optional(),
70
76
  templates: z.string().optional(),
71
77
  codemods: z.string().optional(),
@@ -115,8 +121,8 @@ export function parseAgentDocsField(input, label) {
115
121
  /**
116
122
  * Compile-time drift-lock: sealed schema must infer exactly {@link AstryxIntegration}.
117
123
  *
118
- * @typedef {import('../_shared/contract').Expect<
119
- * import('../_shared/contract').Equal<z.infer<typeof integrationSchema>, AstryxIntegration>
124
+ * @typedef {import('../_shared/contract.js').Expect<
125
+ * import('../_shared/contract.js').Equal<z.infer<typeof integrationSchema>, AstryxIntegration>
120
126
  * >} _IntegrationDriftLock
121
127
  */
122
128
 
@@ -3,29 +3,40 @@
3
3
  /**
4
4
  * Public type surface for an Astryx integration manifest
5
5
  * (`astryx.integration.{ts,mjs,js}`, sibling to the integration package's
6
- * package.json). Identity (name/version) comes from package.json, not the
7
- * manifest. Authors write a plain object against {@link AstryxIntegration};
8
- * the CLI validates it via `parseIntegration` at the load boundary.
6
+ * package.json). Identity defaults to the package name, but `providerId` can keep
7
+ * a stable logical identity across an explicit package rename. Authors write a
8
+ * plain object against {@link AstryxIntegration}; the CLI validates it via
9
+ * `parseIntegration` at the load boundary.
9
10
  *
10
11
  * The manifest module may also carry `debug` and `gapReport` NAMED exports.
11
12
  * They are not fields here on purpose: a CLI released before a given manifest
12
13
  * field existed could reject it and lose every contribution, while an unknown
13
- * named export is simply not read. See the `cli-integrations` doc topic.
14
+ * named export is simply not read. See `astryx docs cli/integrations`.
14
15
  */
15
16
  export interface AstryxIntegration {
17
+ /** Stable logical provider ID. Omit to use package.json#name. Set this only
18
+ * when a package rename must preserve existing artifact IDs. */
19
+ providerId?: string;
16
20
  /** Relative path to the components/docs root (resolved to absolute). */
17
21
  components?: string;
18
22
  /** Relative path to the templates root (resolved to absolute). */
19
23
  templates?: string;
20
- /** Relative path to the codemods root (resolved to absolute). */
24
+ /** Relative path to the codemods root (resolved to absolute).
25
+ * The root uses a version-folder-first layout:
26
+ * `<codemodsRoot>/<version>/<id>.<ext>`, where `<version>` is an exact
27
+ * semver string (e.g. `0.2.0`, no `v` prefix) and `<id>` is a kebab-case
28
+ * module basename. Each module default-exports a codemod envelope stamped
29
+ * `type: 'code'` or `type: 'config'`. Codemod ids must be unique within
30
+ * a package across all versions. */
21
31
  codemods?: string;
22
32
  /** Relative path to the reference-docs (topics) root (resolved to
23
33
  * absolute). Every `{topic}.doc.{ts,mjs,js}` under it is a topic the CLI
24
34
  * serves from `astryx docs`, alongside the built-in ones. A topic may also
25
35
  * `replace` or `extend` a built-in topic; see the ReferenceDoc type. */
26
36
  docs?: string;
27
- /** Relative path to the source-theme catalog root (resolved to absolute).
28
- * The root contains `manifest.json` plus one directory per theme slug. */
37
+ /** Relative path to the source-theme root (resolved to absolute). Each
38
+ * lower-kebab theme directory contains a source module and mandatory
39
+ * same-stem, strongly typed `.doc.mjs` descriptor. */
29
40
  themes?: string;
30
41
  /** Static package guidance appended to the CLI-owned managed agent block. */
31
42
  agentDocs?: {
@@ -49,4 +60,4 @@ export type {
49
60
  GapReportCategory,
50
61
  GapReportTarget,
51
62
  GapReportHandlerReceipt,
52
- } from '../gap-report/type';
63
+ } from '../gap-report/type.js';
@@ -63,7 +63,7 @@ export function createRegistryReceipt(input: {
63
63
  name: string;
64
64
  path: string;
65
65
  aliases: string[];
66
- kind: "block" | "page" | "example" | "showcase";
66
+ kind: "page" | "block" | "example" | "showcase";
67
67
  };
68
68
  source: {
69
69
  package: "@astryxdesign/cli";
@@ -83,7 +83,7 @@ export function createRegistryReceipt(input: {
83
83
  name: string;
84
84
  path: string;
85
85
  aliases: string[];
86
- kind: "block" | "page" | "example" | "showcase";
86
+ kind: "page" | "block" | "example" | "showcase";
87
87
  };
88
88
  source: {
89
89
  package: "@astryxdesign/cli";
@@ -112,7 +112,7 @@ export function parseRegistryReceipt(input: unknown): {
112
112
  name: string;
113
113
  path: string;
114
114
  aliases: string[];
115
- kind: "block" | "page" | "example" | "showcase";
115
+ kind: "page" | "block" | "example" | "showcase";
116
116
  };
117
117
  source: {
118
118
  package: "@astryxdesign/cli";
@@ -132,7 +132,7 @@ export function parseRegistryReceipt(input: unknown): {
132
132
  name: string;
133
133
  path: string;
134
134
  aliases: string[];
135
- kind: "block" | "page" | "example" | "showcase";
135
+ kind: "page" | "block" | "example" | "showcase";
136
136
  };
137
137
  source: {
138
138
  package: "@astryxdesign/cli";
@@ -165,8 +165,8 @@ export const registryReceiptSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
165
165
  path: z.ZodString;
166
166
  aliases: z.ZodArray<z.ZodString>;
167
167
  kind: z.ZodEnum<{
168
- block: "block";
169
168
  page: "page";
169
+ block: "block";
170
170
  example: "example";
171
171
  showcase: "showcase";
172
172
  }>;
@@ -190,8 +190,8 @@ export const registryReceiptSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
190
190
  path: z.ZodString;
191
191
  aliases: z.ZodArray<z.ZodString>;
192
192
  kind: z.ZodEnum<{
193
- block: "block";
194
193
  page: "page";
194
+ block: "block";
195
195
  example: "example";
196
196
  showcase: "showcase";
197
197
  }>;
@@ -3,37 +3,35 @@
3
3
  import fs from 'fs';
4
4
  import path from 'path';
5
5
  import {describe, it, expect} from 'vitest';
6
-
7
- describe('@astryxdesign/cli Theme Manifest & Template Validation', () => {
8
- const manifestPath = path.resolve(
9
- 'packages/cli/assets/templates/themes/manifest.json',
10
- );
11
-
12
- it('validates theme manifest existence and structure', () => {
13
- expect(fs.existsSync(manifestPath)).toBe(true);
14
- const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
15
-
16
- expect(manifest.version).toBe(1);
17
- expect(Array.isArray(manifest.themes)).toBe(true);
18
- expect(manifest.themes.length).toBeGreaterThan(0);
19
- });
20
-
21
- it('verifies all referenced theme files exist', () => {
22
- const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
23
- const themesDir = path.dirname(manifestPath);
24
-
25
- for (const theme of manifest.themes) {
26
- expect(theme.slug).toBeDefined();
27
- expect(theme.displayName).toBeDefined();
28
- expect(theme.entry).toBeDefined();
29
-
30
- const entryPath = path.join(themesDir, theme.slug, theme.entry);
31
- expect(fs.existsSync(entryPath)).toBe(true);
32
-
6
+ import {discoverBundledThemes} from '../../../foundation/discovery/theme-discovery.mjs';
7
+
8
+ describe('@astryxdesign/cli bundled theme descriptor validation', () => {
9
+ it('discovers typed descriptors and their same-stem source files', () => {
10
+ const themes = discoverBundledThemes();
11
+ expect(themes.length).toBeGreaterThan(0);
12
+
13
+ for (const theme of themes) {
14
+ expect(theme.slug).toBeTruthy();
15
+ expect(theme.displayName).toBeTruthy();
16
+ expect(theme.docPath).toBe(
17
+ path.join(
18
+ theme.sourceDir,
19
+ `${path.basename(theme.entry, path.extname(theme.entry))}.doc.mjs`,
20
+ ),
21
+ );
22
+ expect(fs.existsSync(theme.docPath)).toBe(true);
23
+ expect(fs.existsSync(path.join(theme.sourceDir, theme.entry))).toBe(true);
33
24
  for (const file of theme.files) {
34
- const filePath = path.join(themesDir, theme.slug, file);
35
- expect(fs.existsSync(filePath)).toBe(true);
25
+ expect(fs.existsSync(path.join(theme.sourceDir, file))).toBe(true);
36
26
  }
37
27
  }
38
28
  });
29
+
30
+ it('does not ship the obsolete central theme catalog', () => {
31
+ expect(
32
+ fs.existsSync(
33
+ path.resolve('packages/cli/assets/templates/themes/manifest.json'),
34
+ ),
35
+ ).toBe(false);
36
+ });
39
37
  });
@@ -0,0 +1,83 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file A command whose module fails to import still answers: with --json, one
5
+ * error envelope on stdout; without it, the error on stderr. Exit 1 either way.
6
+ *
7
+ * The failure is injected with a module-load hook in a real subprocess, so the
8
+ * lazy command registry in index.mjs sees what a broken install would give it.
9
+ */
10
+
11
+ import {describe, it, expect, beforeAll, afterAll} from 'vitest';
12
+ import {spawnSync} from 'node:child_process';
13
+ import * as fs from 'node:fs';
14
+ import * as os from 'node:os';
15
+ import * as path from 'node:path';
16
+ import {fileURLToPath, pathToFileURL} from 'node:url';
17
+
18
+ const BIN = fileURLToPath(new URL('./bin/astryx.mjs', import.meta.url));
19
+
20
+ /** @type {string} */
21
+ let dir;
22
+ /** @type {string} */
23
+ let register;
24
+
25
+ beforeAll(() => {
26
+ dir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-load-failure-'));
27
+ const hooks = path.join(dir, 'hooks.mjs');
28
+ fs.writeFileSync(
29
+ hooks,
30
+ [
31
+ 'export async function load(url, context, nextLoad) {',
32
+ " if (url.endsWith('/clients/cli/commands/blog.mjs')) {",
33
+ " throw new Error('simulated load failure');",
34
+ ' }',
35
+ ' return nextLoad(url, context);',
36
+ '}',
37
+ '',
38
+ ].join('\n'),
39
+ );
40
+ register = path.join(dir, 'register.mjs');
41
+ fs.writeFileSync(
42
+ register,
43
+ `import {register} from 'node:module';\nregister(${JSON.stringify(pathToFileURL(hooks).href)});\n`,
44
+ );
45
+ });
46
+
47
+ afterAll(() => {
48
+ fs.rmSync(dir, {recursive: true, force: true});
49
+ });
50
+
51
+ /** @param {string[]} args */
52
+ function run(args) {
53
+ const res = spawnSync(
54
+ process.execPath,
55
+ ['--import', pathToFileURL(register).href, BIN, ...args],
56
+ {cwd: dir, encoding: 'utf-8', timeout: 20_000},
57
+ );
58
+ return {status: res.status, stdout: res.stdout, stderr: res.stderr};
59
+ }
60
+
61
+ describe('a command that fails to load', () => {
62
+ it('answers --json with one error envelope, exit 1', () => {
63
+ const {status, stdout, stderr} = run(['blog', '--json']);
64
+ expect(status).toBe(1);
65
+ expect(stderr).toBe('');
66
+ const envelope = JSON.parse(stdout);
67
+ expect(envelope).toMatchObject({apiVersion: 1, code: 'ERR_UNKNOWN'});
68
+ expect(envelope.error).toMatch(/blog.*simulated load failure/);
69
+ });
70
+
71
+ it('reports the failure on stderr without --json, exit 1', () => {
72
+ const {status, stdout, stderr} = run(['blog']);
73
+ expect(status).toBe(1);
74
+ expect(stdout).toBe('');
75
+ expect(stderr).toMatch(/blog.*failed to load.*simulated load failure/s);
76
+ });
77
+
78
+ it('leaves the other commands working', () => {
79
+ const {status, stdout} = run(['docs', '--json']);
80
+ expect(status).toBe(0);
81
+ expect(JSON.parse(stdout).type).toMatch(/^docs\./);
82
+ });
83
+ });
@@ -29,17 +29,17 @@ import {program} from './index.mjs';
29
29
  import {reportsResult, reportsResultVia} from './lib/define-command.mjs';
30
30
 
31
31
  /**
32
- * Commands with no action of their own: bare `astryx layout` prints its
33
- * subcommand list and does nothing else, so there is no run to report on.
32
+ * Commands with no action of their own: a group that prints its subcommand
33
+ * list and does nothing else has no run to report on.
34
34
  *
35
35
  * Pinned as a SET rather than skipped silently — if a command joins this list,
36
36
  * that is a real change to what the CLI does and it should be read, not
37
- * absorbed. (`theme` is deliberately NOT here: it HAS an action — the one that
38
- * rejects an unknown subcommand — so it goes through the converter like any
39
- * other. Its bare form prints help and never returns from that action, which
40
- * the recorder covers where help is recorded, not here.)
37
+ * absorbed. Every group ships an action today: `theme`, for instance, HAS one
38
+ * — the one that rejects an unknown subcommand — so it goes through the
39
+ * converter like any other. Its bare form prints help and never returns from
40
+ * that action, which the recorder covers where help is recorded, not here.
41
41
  */
42
- const NO_ACTION_OF_THEIR_OWN = ['layout'];
42
+ const NO_ACTION_OF_THEIR_OWN = [];
43
43
 
44
44
  /**
45
45
  * Commands that record for themselves instead of returning a descriptor.
@@ -12,7 +12,7 @@ export const doc = {
12
12
  type: 'command',
13
13
  name: 'blog',
14
14
  displayName: 'astryx blog',
15
- namespace: 'cli',
15
+ namespace: 'cli/commands',
16
16
  summary: 'Read the Astryx blog from the published feed',
17
17
  description:
18
18
  'Reads the design-system blog over its published RSS feed (never the source ' +
@@ -24,6 +24,18 @@ import {resultSet} from '../../../foundation/debug/index.mjs';
24
24
  import {doc as blogCommand} from './blog.doc.mjs';
25
25
  import {doc as blogFn} from '../../../api/blog/blog.doc.mjs';
26
26
 
27
+ /** Every field of a post in the JSON, in order; record() skips empty ones. */
28
+ const POST_FIELDS = [
29
+ 'slug',
30
+ 'title',
31
+ 'description',
32
+ 'date',
33
+ 'type',
34
+ 'authors',
35
+ 'link',
36
+ 'textUrl',
37
+ ];
38
+
27
39
  /**
28
40
  * @param {import('commander').Command} program
29
41
  */
@@ -62,22 +74,25 @@ export function registerBlog(program) {
62
74
  const {feedUrl, posts} = result.data;
63
75
  if (posts.length === 0) {
64
76
  emit(
65
- section('Astryx blog', `feed: ${feedUrl}`),
77
+ section('Astryx blog'),
78
+ record({feedUrl}),
66
79
  text('No posts found in the feed.'),
67
80
  );
68
81
  return answered;
69
82
  }
70
- // One record per post — fields mirror the JSON post shape; empty
71
- // fields (type/textUrl) are skipped by record().
72
83
  emit(
73
- section('Astryx blog', `feed: ${feedUrl}`),
74
- records(posts, {fields: ['slug', 'title', 'type', 'textUrl']}),
84
+ section('Astryx blog'),
85
+ record({feedUrl}),
86
+ records(posts, {fields: POST_FIELDS}),
75
87
  text(`Read one: ${run} astryx blog <slug>`),
76
88
  );
77
89
  } else {
78
- // blog.detail — the feed URL, then the post body emitted verbatim
79
- // (code() so article typography/spacing isn't ASCII-normalized).
80
- emit(record({feed: result.data.feedUrl}), code(result.data.text));
90
+ // blog.detail — the post's fields and the feed URL, then the body
91
+ // verbatim (code() so article typography/spacing isn't ASCII-normalized).
92
+ emit(
93
+ record(result.data, {fields: [...POST_FIELDS, 'feedUrl']}),
94
+ code(result.data.text),
95
+ );
81
96
  }
82
97
  return answered;
83
98
  },
@@ -19,7 +19,10 @@ const FEED = `<?xml version="1.0" encoding="UTF-8"?>
19
19
  <item>
20
20
  <title>How Astryx works</title>
21
21
  <link>${SITE_URL}/blog/how-astryx-works</link>
22
+ <description>Under the hood &amp; more</description>
22
23
  <category>engineering</category>
24
+ <author>Ada</author>
25
+ <author>Grace</author>
23
26
  <pubDate>Mon, 29 Jun 2026 00:00:00 GMT</pubDate>
24
27
  <atom:link rel="alternate" type="text/plain" href="${SITE_URL}/blog/how-astryx-works.txt" />
25
28
  </item>
@@ -39,6 +42,23 @@ function stubFetch(routes) {
39
42
  });
40
43
  }
41
44
 
45
+ /**
46
+ * Every non-empty field prints as a `key: value` line under its JSON key.
47
+ * @param {string} stdout
48
+ * @param {Record<string, unknown>} fields
49
+ */
50
+ function expectFields(stdout, fields) {
51
+ const lines = stdout.split('\n');
52
+ for (const [key, value] of Object.entries(fields)) {
53
+ if (value == null || value === '' || (Array.isArray(value) && !value.length)) continue;
54
+ const shown = Array.isArray(value) ? value.join(', ') : String(value);
55
+ expect(
56
+ lines.some(line => line.startsWith(`${key}:`) && line.trimEnd().endsWith(shown)),
57
+ `${key}: ${shown}`,
58
+ ).toBe(true);
59
+ }
60
+ }
61
+
42
62
  beforeEach(() => {
43
63
  vi.stubGlobal(
44
64
  'fetch',
@@ -83,10 +103,31 @@ describe('blog CLI — json-enabled', () => {
83
103
  const {status, stdout} = await runCli(['blog']);
84
104
  expect(status).toBe(0);
85
105
  expect(stdout).toMatch(/Astryx blog/);
86
- expect(stdout).toMatch(/feed:/);
106
+ expect(stdout).toMatch(/^feedUrl:/m);
87
107
  expect(stdout).toMatch(/how-astryx-works/);
88
108
  });
89
109
 
110
+ it('the text list projects every JSON field under its JSON key', async () => {
111
+ const env = JSON.parse((await runCli(['blog', '--json'])).stdout);
112
+ const {status, stdout} = await runCli(['blog']);
113
+ expect(status).toBe(0);
114
+ expectFields(stdout, {feedUrl: env.data.feedUrl});
115
+ for (const post of env.data.posts) expectFields(stdout, post);
116
+ expect(stdout).not.toMatch(/^feed:/m);
117
+ });
118
+
119
+ it('the text detail projects every JSON field, then the post body', async () => {
120
+ const env = JSON.parse(
121
+ (await runCli(['blog', 'how-astryx-works', '--json'])).stdout,
122
+ );
123
+ const {status, stdout} = await runCli(['blog', 'how-astryx-works']);
124
+ expect(status).toBe(0);
125
+ const {text, ...fields} = env.data;
126
+ expectFields(stdout, fields);
127
+ expect(stdout).toContain(text);
128
+ expect(stdout).not.toMatch(/^feed:/m);
129
+ });
130
+
90
131
  it('surfaces the API error code and exits non-zero for an unknown slug (--json)', async () => {
91
132
  const {status, stdout} = await runCli(['blog', 'no-such-post', '--json']);
92
133
  expect(status).not.toBe(0);