@astryxdesign/cli 0.6.3 → 0.6.4-canary.10dd683

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 (643) hide show
  1. package/CHANGELOG.md +194 -0
  2. package/README.md +117 -78
  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 +6 -12
  18. package/api/component/_adapter.mjs +20 -10
  19. package/api/component/component.doc.mjs +13 -3
  20. package/api/component/component.mjs +91 -14
  21. package/api/component/component.test.mjs +38 -0
  22. package/api/component/component.type.d.mts +22 -11
  23. package/api/component/component.type.mjs +32 -24
  24. package/api/component/detail/blocks/blocks.d.mts +2 -1
  25. package/api/component/detail/blocks/blocks.mjs +4 -3
  26. package/api/component/list/list.d.mts +0 -5
  27. package/api/component/list/list.mjs +40 -11
  28. package/api/discover/_adapter.d.mts +114 -6
  29. package/api/discover/_adapter.mjs +372 -17
  30. package/api/discover/_adapter.test.mjs +215 -0
  31. package/api/discover/_catalog-view.d.mts +115 -0
  32. package/api/discover/_catalog-view.mjs +203 -0
  33. package/api/discover/_catalog-view.test.mjs +128 -0
  34. package/api/discover/detail/detail.d.mts +18 -6
  35. package/api/discover/detail/detail.mjs +67 -13
  36. package/api/discover/detail/detail.test.mjs +85 -0
  37. package/api/discover/detail/item/item.d.mts +26 -0
  38. package/api/discover/detail/item/item.mjs +78 -0
  39. package/api/discover/detail/item/item.test.mjs +73 -0
  40. package/api/discover/discover.d.mts +3 -9
  41. package/api/discover/discover.doc.mjs +62 -18
  42. package/api/discover/discover.mjs +220 -36
  43. package/api/discover/discover.test.mjs +11 -2
  44. package/api/discover/discover.type.d.mts +150 -11
  45. package/api/discover/discover.type.mjs +107 -17
  46. package/api/discover/list/list.d.mts +20 -6
  47. package/api/discover/list/list.mjs +45 -12
  48. package/api/discover/list/list.test.mjs +46 -0
  49. package/api/discover/search/search.d.mts +18 -16
  50. package/api/discover/search/search.mjs +102 -56
  51. package/api/discover/search/search.test.mjs +144 -10
  52. package/api/docs/_adapter.d.mts +272 -41
  53. package/api/docs/_adapter.mjs +985 -108
  54. package/api/docs/compiled-topics.test.mjs +78 -0
  55. package/api/docs/detail/detail.mjs +22 -63
  56. package/api/docs/detail/section/section.d.mts +1 -1
  57. package/api/docs/detail/section/section.mjs +54 -19
  58. package/api/docs/detail/section/section.test.mjs +50 -0
  59. package/api/docs/docs.d.mts +10 -3
  60. package/api/docs/docs.doc.mjs +55 -16
  61. package/api/docs/docs.mjs +53 -10
  62. package/api/docs/docs.test.mjs +164 -4
  63. package/api/docs/docs.type.d.mts +221 -5
  64. package/api/docs/docs.type.mjs +153 -11
  65. package/api/docs/index/index.d.mts +18 -0
  66. package/api/docs/index/index.mjs +40 -0
  67. package/api/docs/index/index.test.mjs +62 -0
  68. package/api/docs/integration-tree.test.mjs +555 -0
  69. package/api/docs/integrationDocs.test.mjs +114 -8
  70. package/api/docs/list/list.mjs +28 -12
  71. package/api/docs/node/node.d.mts +43 -0
  72. package/api/docs/node/node.mjs +192 -0
  73. package/api/docs/reference-blocks.test.mjs +406 -0
  74. package/api/doctor/doctor.d.mts +99 -1
  75. package/api/doctor/doctor.doc.mjs +1 -0
  76. package/api/doctor/doctor.mjs +548 -1
  77. package/api/doctor/doctor.test.mjs +610 -1
  78. package/api/gap-report/gap-report.doc.mjs +8 -4
  79. package/api/hook/_adapter.mjs +19 -5
  80. package/api/hook/hook.doc.mjs +1 -0
  81. package/api/hook/hook.type.d.mts +3 -3
  82. package/api/hook/hook.type.mjs +11 -11
  83. package/api/hook/list/list.d.mts +2 -2
  84. package/api/hook/list/list.mjs +69 -17
  85. package/api/index.d.mts +1 -1
  86. package/api/index.mjs +1 -0
  87. package/api/init/init.doc.mjs +6 -1
  88. package/api/init/init.test.mjs +41 -1
  89. package/api/init/remove/remove.mjs +1 -1
  90. package/api/init/run/run.mjs +20 -10
  91. package/api/integration/add-contribution.component-names.test.mjs +120 -0
  92. package/api/integration/add-contribution.d.mts +2 -1
  93. package/api/integration/add-contribution.mjs +130 -15
  94. package/api/integration/add-contribution.test.mjs +258 -7
  95. package/api/integration/add-helpers.d.mts +5 -2
  96. package/api/integration/add-helpers.mjs +36 -9
  97. package/api/integration/add-theme.mjs +34 -64
  98. package/api/integration/add-theme.test.mjs +105 -21
  99. package/api/integration/authoring-checks.mjs +138 -28
  100. package/api/integration/authoring-checks.test.mjs +179 -7
  101. package/api/integration/authoring-checks.type.mjs +6 -1
  102. package/api/integration/integration-authoring.type.d.mts +3 -1
  103. package/api/integration/integration-authoring.type.mjs +2 -0
  104. package/api/integration/integration-block-exports.test.mjs +10 -6
  105. package/api/integration/integrationAdd.doc.mjs +14 -4
  106. package/api/integration/integrationAddAgentDoc.doc.mjs +1 -0
  107. package/api/integration/integrationAddCodemod.doc.mjs +1 -0
  108. package/api/integration/integrationAddComponent.doc.mjs +2 -1
  109. package/api/integration/integrationAddDoc.doc.mjs +8 -1
  110. package/api/integration/integrationAddTemplate.doc.mjs +1 -0
  111. package/api/integration/integrationAddTheme.doc.mjs +6 -5
  112. package/api/integration/integrationComponentConflicts.doc.mjs +1 -0
  113. package/api/integration/integrationDocConflicts.doc.mjs +2 -1
  114. package/api/integration/integrationPackCheck.doc.mjs +2 -1
  115. package/api/integration/integrationTemplateConflicts.doc.d.mts +3 -0
  116. package/api/integration/integrationTemplateConflicts.doc.mjs +4 -0
  117. package/api/integration/pack-check.mjs +83 -7
  118. package/api/integration/pack-check.test.mjs +387 -47
  119. package/api/integration/pack-check.type.d.mts +26 -2
  120. package/api/integration/pack-check.type.mjs +14 -1
  121. package/api/integration/summarizeIssues.doc.mjs +1 -0
  122. package/api/integration/template-conflict-compatibility.test.mjs +73 -0
  123. package/api/integration/validate-integration-fixes.test.mjs +1389 -0
  124. package/api/integration/validate-integration.mjs +52 -102
  125. package/api/integration/validate-integration.test.mjs +179 -26
  126. package/api/integration/validate-unread-theme-folders.test.mjs +110 -0
  127. package/api/integration/validateIntegration.doc.mjs +3 -2
  128. package/api/json/assertResponse.doc.mjs +1 -0
  129. package/api/json/envelope-types.test.mjs +76 -0
  130. package/api/json/index.ts +2 -0
  131. package/api/json/isError.doc.mjs +1 -0
  132. package/api/json/parseResponse.doc.mjs +3 -2
  133. package/api/layout/_adapter.mjs +20 -5
  134. package/api/layout/expand/expand.mjs +7 -5
  135. package/api/layout/expand/expand.path-safety.test.mjs +53 -0
  136. package/api/layout/grammar/grammar.mjs +2 -1
  137. package/api/layout/layoutCheck.doc.mjs +1 -0
  138. package/api/layout/layoutExpand.doc.mjs +2 -1
  139. package/api/layout/layoutGrammar.doc.mjs +1 -0
  140. package/api/search/search-return-type.test.mjs +54 -0
  141. package/api/search/search.d.mts +62 -11
  142. package/api/search/search.doc.mjs +8 -2
  143. package/api/search/search.mjs +471 -83
  144. package/api/search/search.test.mjs +124 -1
  145. package/api/search/search.type.d.mts +15 -3
  146. package/api/search/search.type.mjs +5 -2
  147. package/api/swizzle/copy/copy.mjs +28 -11
  148. package/api/swizzle/swizzle.doc.mjs +2 -1
  149. package/api/swizzle/swizzle.type.d.mts +2 -2
  150. package/api/swizzle/swizzle.type.mjs +2 -2
  151. package/api/template/copy/copy.mjs +17 -23
  152. package/api/template/copy/copy.test.mjs +17 -0
  153. package/api/template/list/list.mjs +1 -0
  154. package/api/template/table-floating-bulk-actions.test.mjs +66 -0
  155. package/api/template/template-integration.test.mjs +1072 -3
  156. package/api/template/template-suffix.test.mjs +41 -21
  157. package/api/template/template.d.mts +1 -1
  158. package/api/template/template.doc.mjs +30 -8
  159. package/api/template/template.mjs +45 -8
  160. package/api/template/template.type.d.mts +12 -14
  161. package/api/template/template.type.mjs +15 -14
  162. package/api/theme/_adapter.d.mts +2 -3
  163. package/api/theme/_adapter.mjs +4 -5
  164. package/api/theme/add/add.binary.test.mjs +84 -0
  165. package/api/theme/add/add.mjs +31 -22
  166. package/api/theme/add/add.rollback.test.mjs +158 -0
  167. package/api/theme/add/add.staging.test.mjs +83 -0
  168. package/api/theme/add/add.test.mjs +14 -1
  169. package/api/theme/build/build.family.test.mjs +7 -12
  170. package/api/theme/build/build.mjs +140 -59
  171. package/api/theme/build/build.public-component-vars.test.mjs +1 -1
  172. package/api/theme/build/build.receipt-doc.test.mjs +111 -0
  173. package/api/theme/build/build.rollback.test.mjs +148 -0
  174. package/api/theme/build/build.test.mjs +127 -0
  175. package/api/theme/build/font-warning.mjs +3 -3
  176. package/api/theme/build/font-warning.test.mjs +5 -2
  177. package/api/theme/generateTonalPalette.doc.mjs +1 -0
  178. package/api/theme/integration-themes.test.mjs +39 -28
  179. package/api/theme/list/list.test.mjs +19 -20
  180. package/api/theme/listThemes.doc.mjs +6 -5
  181. package/api/theme/palette/generate/generate.mjs +8 -3
  182. package/api/theme/palette/generate/generate.test.mjs +96 -0
  183. package/api/theme/palette/generate/generator.d.mts +10 -13
  184. package/api/theme/palette/generate/generator.mjs +15 -4
  185. package/api/theme/palette/generate/generator.test.mjs +10 -0
  186. package/api/theme/template/template.mjs +11 -2
  187. package/api/theme/template/template.test.mjs +20 -0
  188. package/api/theme/theme.type.d.mts +170 -11
  189. package/api/theme/theme.type.mjs +94 -27
  190. package/api/theme/themeAdd.doc.mjs +4 -3
  191. package/api/theme/themeBuild.doc.mjs +8 -4
  192. package/api/theme/themeList.doc.mjs +6 -3
  193. package/api/theme/themeListAvailable.doc.mjs +5 -3
  194. package/api/theme/themePaletteGenerate.doc.mjs +1 -0
  195. package/api/theme/themeTargets.doc.mjs +1 -0
  196. package/api/theme/themeTemplate.doc.mjs +6 -2
  197. package/api/upgrade/_adapter.d.mts +32 -5
  198. package/api/upgrade/_adapter.mjs +139 -22
  199. package/api/upgrade/list/list.mjs +2 -1
  200. package/api/upgrade/list/list.test.mjs +73 -0
  201. package/api/upgrade/project-context.test.mjs +272 -0
  202. package/api/upgrade/provider-agreement.test.mjs +152 -0
  203. package/api/upgrade/run/run.mjs +356 -59
  204. package/api/upgrade/status/status.mjs +2 -2
  205. package/api/upgrade/upgrade.doc.mjs +12 -5
  206. package/api/upgrade/upgrade.type.d.mts +43 -5
  207. package/api/upgrade/upgrade.type.mjs +27 -11
  208. package/assets/codemods/__tests__/registry.test.mjs +1 -0
  209. package/assets/codemods/__tests__/runner.test.mjs +330 -8
  210. package/assets/codemods/integration-discovery.mjs +48 -4
  211. package/assets/codemods/integration-discovery.test.mjs +73 -0
  212. package/assets/codemods/integration-runner.mjs +56 -4
  213. package/assets/codemods/integration-runner.protection.test.mjs +153 -0
  214. package/assets/codemods/registry.mjs +1 -0
  215. package/assets/codemods/run-codemod.mjs +177 -34
  216. package/assets/codemods/runner.mjs +350 -102
  217. package/assets/codemods/term-log.mjs +32 -8
  218. package/assets/codemods/term-log.test.mjs +19 -1
  219. package/assets/codemods/transform-prop.mjs +109 -0
  220. package/assets/codemods/transform-prop.test.mjs +95 -0
  221. package/assets/codemods/transforms/v0.0.14/__tests__/rename-status-variants.test.mjs +86 -165
  222. package/assets/codemods/transforms/v0.0.14/rename-status-variants.mjs +72 -210
  223. package/assets/codemods/transforms/v0.1.8/__tests__/rename-avatar-size-scale.test.mjs +83 -115
  224. package/assets/codemods/transforms/v0.1.8/rename-avatar-size-scale.mjs +57 -186
  225. package/assets/codemods/transforms/v0.3.0/__tests__/unwrap-authoring-factories.test.mjs +27 -5
  226. package/assets/codemods/transforms/v0.3.0/unwrap-authoring-factories.mjs +20 -5
  227. package/assets/codemods/transforms/v0.6.0/__tests__/next-codemods.test.mjs +47 -0
  228. package/assets/codemods/transforms/v0.6.0/rename-resizable-pixel-bounds.mjs +57 -10
  229. package/assets/codemods/transforms/v0.6.4/__tests__/migrate-native-picker-to-presentation.test.mjs +63 -0
  230. package/assets/codemods/transforms/v0.6.4/__tests__/migrate-theme-catalog-to-descriptors.test.mjs +220 -0
  231. package/assets/codemods/transforms/v0.6.4/index.mjs +31 -0
  232. package/assets/codemods/transforms/v0.6.4/migrate-native-picker-to-presentation.mjs +148 -0
  233. package/assets/codemods/transforms/v0.6.4/migrate-theme-catalog-to-descriptors.mjs +141 -0
  234. package/assets/docs/README.md +9 -0
  235. package/assets/docs/authoring.doc.mjs +14 -0
  236. package/assets/docs/getting-started.doc.mjs +2 -2
  237. package/assets/docs/internationalization.doc.mjs +7 -5
  238. package/assets/docs/layout.doc.dense.mjs +2 -2
  239. package/assets/docs/layout.doc.mjs +1 -1
  240. package/assets/docs/principles.doc.mjs +6 -6
  241. package/assets/docs/styling-libraries.doc.mjs +4 -4
  242. package/assets/docs/styling.doc.mjs +4 -4
  243. package/assets/docs/theme.doc.mjs +5 -5
  244. package/assets/docs/tokens.doc.mjs +1 -1
  245. package/assets/docs/tree/api.doc.mjs +30 -0
  246. package/assets/docs/tree/cli.doc.mjs +23 -0
  247. package/assets/docs/tree/commands.doc.mjs +25 -0
  248. package/assets/docs/{cli-integrations.doc.mjs → tree/integrations.doc.mjs} +144 -26
  249. package/assets/docs/tree/integrations.test.mjs +62 -0
  250. package/assets/docs/tree/writing-docs.doc.mjs +286 -0
  251. package/assets/docs/working-with-ai.doc.mjs +4 -4
  252. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.doc.mjs +1 -1
  253. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.tsx +1 -3
  254. package/assets/templates/blocks/components/DateInput/DateInputDateRange.tsx +1 -1
  255. package/assets/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.tsx +1 -1
  256. package/assets/templates/blocks/components/Item/ItemDocumentTabs.doc.mjs +14 -0
  257. package/assets/templates/blocks/components/Item/ItemDocumentTabs.tsx +100 -0
  258. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.doc.mjs +14 -0
  259. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.tsx +61 -0
  260. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.doc.mjs +14 -0
  261. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.tsx +126 -0
  262. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.doc.mjs +15 -0
  263. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.tsx +86 -0
  264. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.doc.mjs +14 -0
  265. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.tsx +99 -0
  266. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.doc.mjs +14 -0
  267. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.tsx +122 -0
  268. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.doc.mjs +14 -0
  269. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.tsx +95 -0
  270. package/assets/templates/blocks/components/Table/TableBulkActionsTable.doc.mjs +14 -0
  271. package/assets/templates/blocks/components/Table/TableBulkActionsTable.tsx +71 -0
  272. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.doc.mjs +19 -0
  273. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.tsx +139 -0
  274. package/assets/templates/blocks/components/TimeInput/TimeInputConstrained.tsx +1 -0
  275. package/assets/templates/blocks/components/Timer/TimerFormats.doc.mjs +14 -0
  276. package/assets/templates/blocks/components/Timer/TimerFormats.tsx +34 -0
  277. package/assets/templates/blocks/components/Timer/TimerInline.doc.mjs +14 -0
  278. package/assets/templates/blocks/components/Timer/TimerInline.tsx +14 -0
  279. package/assets/templates/blocks/components/Timer/TimerShowcase.doc.mjs +13 -0
  280. package/assets/templates/blocks/components/Timer/TimerShowcase.tsx +47 -0
  281. package/assets/templates/blocks/components/Timer/TimerTypography.doc.mjs +14 -0
  282. package/assets/templates/blocks/components/Timer/TimerTypography.tsx +31 -0
  283. package/assets/templates/blocks/components/Toolbar/ToolbarTableFilter.doc.mjs +19 -3
  284. package/assets/templates/blocks/components/Toolbar/ToolbarTableFilter.tsx +383 -65
  285. package/assets/templates/pages/table-tree/page.tsx +1704 -0
  286. package/assets/templates/pages/table-tree/template.doc.mjs +12 -0
  287. package/assets/templates/themes/butter/butterTheme.doc.mjs +11 -0
  288. package/assets/templates/themes/chocolate/chocolateTheme.doc.mjs +11 -0
  289. package/assets/templates/themes/gothic/gothicTheme.doc.mjs +11 -0
  290. package/assets/templates/themes/matcha/matchaTheme.doc.mjs +11 -0
  291. package/assets/templates/themes/neutral/neutralTheme.doc.mjs +11 -0
  292. package/assets/templates/themes/stone/stoneTheme.doc.mjs +11 -0
  293. package/assets/templates/themes/y2k/y2kTheme.doc.mjs +11 -0
  294. package/authoring/_shared/contract.ts +22 -0
  295. package/authoring/codemod/codemod.doc.mjs +7 -2
  296. package/authoring/codemod/parse.d.mts +8 -8
  297. package/authoring/codemod/parse.mjs +8 -6
  298. package/authoring/codemod/type.ts +12 -0
  299. package/authoring/config/config.doc.mjs +10 -2
  300. package/authoring/config/debug-composition.test.mjs +92 -0
  301. package/authoring/config/parse.d.mts +15 -13
  302. package/authoring/config/parse.mjs +27 -8
  303. package/authoring/config/parse.test.mjs +8 -0
  304. package/authoring/config/type.ts +29 -6
  305. package/authoring/debug/debug.doc.d.mts +11 -0
  306. package/authoring/debug/debug.doc.mjs +182 -0
  307. package/authoring/debug/parse.d.mts +8 -8
  308. package/authoring/debug/parse.mjs +3 -3
  309. package/authoring/discover/discover.doc.d.mts +13 -0
  310. package/authoring/discover/discover.doc.mjs +138 -0
  311. package/authoring/discover/parse.d.mts +24 -0
  312. package/authoring/discover/parse.mjs +128 -0
  313. package/authoring/discover/parse.test.mjs +124 -0
  314. package/authoring/discover/type.ts +87 -0
  315. package/authoring/doctypes/_schema.d.mts +790 -23
  316. package/authoring/doctypes/_schema.mjs +543 -39
  317. package/authoring/doctypes/base/graph-fields.doc.d.mts +9 -0
  318. package/authoring/doctypes/base/graph-fields.doc.mjs +64 -0
  319. package/authoring/doctypes/base/type.ts +41 -0
  320. package/authoring/doctypes/command/command.doc.mjs +4 -3
  321. package/authoring/doctypes/command/parse.d.mts +2 -2
  322. package/authoring/doctypes/command/parse.mjs +1 -1
  323. package/authoring/doctypes/command/type.ts +5 -4
  324. package/authoring/doctypes/component/component.doc.mjs +6 -3
  325. package/authoring/doctypes/component/parse.d.mts +2 -2
  326. package/authoring/doctypes/component/parse.mjs +1 -1
  327. package/authoring/doctypes/component/type.ts +6 -5
  328. package/authoring/doctypes/doctypes-new.test.mjs +48 -6
  329. package/authoring/doctypes/enum/enum.doc.mjs +1 -1
  330. package/authoring/doctypes/enum/parse.d.mts +2 -2
  331. package/authoring/doctypes/enum/parse.mjs +1 -1
  332. package/authoring/doctypes/enum/type.ts +4 -2
  333. package/authoring/doctypes/function/function.doc.mjs +7 -2
  334. package/authoring/doctypes/function/parse.d.mts +2 -2
  335. package/authoring/doctypes/function/parse.mjs +1 -1
  336. package/authoring/doctypes/function/type.ts +9 -4
  337. package/authoring/doctypes/hook/hook.doc.mjs +4 -0
  338. package/authoring/doctypes/hook/parse.d.mts +2 -2
  339. package/authoring/doctypes/hook/parse.mjs +1 -1
  340. package/authoring/doctypes/hook/type.ts +5 -4
  341. package/authoring/doctypes/legacy.d.mts +8 -6
  342. package/authoring/doctypes/legacy.mjs +5 -4
  343. package/authoring/doctypes/load-contract.test.mjs +233 -0
  344. package/authoring/doctypes/namespace/namespace.doc.d.mts +9 -0
  345. package/authoring/doctypes/namespace/namespace.doc.mjs +128 -0
  346. package/authoring/doctypes/namespace/parse.d.mts +12 -0
  347. package/authoring/doctypes/namespace/parse.mjs +25 -0
  348. package/authoring/doctypes/namespace/parse.test.mjs +163 -0
  349. package/authoring/doctypes/namespace/type.ts +74 -0
  350. package/authoring/doctypes/parse.d.mts +22 -18
  351. package/authoring/doctypes/parse.mjs +22 -11
  352. package/authoring/doctypes/parse.test.mjs +77 -3
  353. package/authoring/doctypes/reference/parse.d.mts +2 -2
  354. package/authoring/doctypes/reference/parse.mjs +8 -5
  355. package/authoring/doctypes/reference/reference.doc.mjs +48 -6
  356. package/authoring/doctypes/reference/type.ts +70 -7
  357. package/authoring/doctypes/schema/parse.d.mts +2 -2
  358. package/authoring/doctypes/schema/parse.mjs +1 -1
  359. package/authoring/doctypes/schema/schema.doc.mjs +1 -1
  360. package/authoring/doctypes/schema/type.ts +4 -4
  361. package/authoring/doctypes/template/parse.d.mts +94 -1
  362. package/authoring/doctypes/template/parse.mjs +40 -2
  363. package/authoring/doctypes/template/parse.test.mjs +26 -2
  364. package/authoring/doctypes/template/template.doc.mjs +13 -3
  365. package/authoring/doctypes/template/type.ts +13 -2
  366. package/authoring/doctypes/theme/parse.d.mts +35 -0
  367. package/authoring/doctypes/theme/parse.mjs +76 -0
  368. package/authoring/doctypes/theme/theme.doc.d.mts +9 -0
  369. package/authoring/doctypes/theme/theme.doc.mjs +79 -0
  370. package/authoring/doctypes/theme/type.ts +42 -0
  371. package/authoring/doctypes/types.ts +12 -10
  372. package/authoring/gap-report/gap-report.doc.d.mts +12 -0
  373. package/authoring/gap-report/gap-report.doc.mjs +183 -0
  374. package/authoring/gap-report/parse.d.mts +10 -10
  375. package/authoring/gap-report/parse.mjs +6 -6
  376. package/authoring/gap-report/type.ts +1 -1
  377. package/authoring/identity/identity.doc.d.mts +9 -0
  378. package/authoring/identity/identity.doc.mjs +61 -0
  379. package/authoring/identity/type.ts +132 -0
  380. package/authoring/index.d.mts +3 -0
  381. package/authoring/index.d.ts +62 -17
  382. package/authoring/index.mjs +4 -1
  383. package/authoring/integration/integration.doc.mjs +15 -8
  384. package/authoring/integration/parse.d.mts +2 -2
  385. package/authoring/integration/parse.mjs +1 -1
  386. package/authoring/integration/parse.test.mjs +10 -1
  387. package/authoring/integration/schema.d.mts +6 -4
  388. package/authoring/integration/schema.mjs +9 -3
  389. package/authoring/integration/type.ts +19 -8
  390. package/authoring/shadcn/receipt.d.mts +6 -6
  391. package/clients/cli/__tests__/cliManifest.test.ts +27 -29
  392. package/clients/cli/command-load-failure.test.mjs +83 -0
  393. package/clients/cli/commands/blog.doc.mjs +1 -1
  394. package/clients/cli/commands/blog.mjs +23 -8
  395. package/clients/cli/commands/blog.test.mjs +42 -1
  396. package/clients/cli/commands/build-theme.adaptations.test.mjs +100 -1
  397. package/clients/cli/commands/build-theme.ascii-output.test.mjs +161 -0
  398. package/clients/cli/commands/build-theme.flag-docs.test.mjs +110 -0
  399. package/clients/cli/commands/build-theme.mjs +16 -50
  400. package/clients/cli/commands/build-theme.path-safety.test.mjs +86 -1
  401. package/clients/cli/commands/build-theme.variants.test.mjs +76 -0
  402. package/clients/cli/commands/build.doc.mjs +16 -8
  403. package/clients/cli/commands/build.exit-codes-doc.test.mjs +50 -0
  404. package/clients/cli/commands/build.mjs +137 -114
  405. package/clients/cli/commands/build.playbook.test.mjs +75 -0
  406. package/clients/cli/commands/build.text-fields.test.mjs +81 -0
  407. package/clients/cli/commands/component/index.mjs +3 -8
  408. package/clients/cli/commands/component-ownership.test.mjs +3 -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 +1 -1
  412. package/clients/cli/commands/component.test.mjs +19 -0
  413. package/clients/cli/commands/detail-levels.test.mjs +2 -2
  414. package/clients/cli/commands/discover.broken-integration.test.mjs +52 -5
  415. package/clients/cli/commands/discover.components-flag.test.mjs +97 -0
  416. package/clients/cli/commands/discover.doc.mjs +55 -9
  417. package/clients/cli/commands/discover.mjs +393 -118
  418. package/clients/cli/commands/discover.sources.test.mjs +267 -0
  419. package/clients/cli/commands/discover.text-projection.test.mjs +103 -0
  420. package/clients/cli/commands/docs.doc.mjs +28 -6
  421. package/clients/cli/commands/docs.mjs +240 -26
  422. package/clients/cli/commands/docs.test.mjs +193 -1
  423. package/clients/cli/commands/doctor-integration-components.doc.mjs +1 -1
  424. package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -3
  425. package/clients/cli/commands/doctor-integration-templates.doc.mjs +15 -8
  426. package/clients/cli/commands/doctor-integration-validate.doc.mjs +1 -1
  427. package/clients/cli/commands/doctor-integration.doc.mjs +1 -1
  428. package/clients/cli/commands/doctor-integration.package-json.test.mjs +53 -0
  429. package/clients/cli/commands/doctor-integration.test.mjs +90 -8
  430. package/clients/cli/commands/doctor.doc.mjs +1 -1
  431. package/clients/cli/commands/doctor.mjs +59 -32
  432. package/clients/cli/commands/doctor.test.mjs +42 -0
  433. package/clients/cli/commands/gap-report.doc.mjs +17 -6
  434. package/clients/cli/commands/gap-report.test.mjs +72 -0
  435. package/clients/cli/commands/hook/index.mjs +7 -17
  436. package/clients/cli/commands/hook.doc.mjs +1 -1
  437. package/clients/cli/commands/hook.text-projection.test.mjs +45 -0
  438. package/clients/cli/commands/init.doc.mjs +20 -9
  439. package/clients/cli/commands/init.flag-help.test.mjs +153 -0
  440. package/clients/cli/commands/integration-add.controls.test.mjs +132 -0
  441. package/clients/cli/commands/integration-add.doc.mjs +32 -6
  442. package/clients/cli/commands/integration-authoring.test.mjs +13 -9
  443. package/clients/cli/commands/integration-pack.doc.mjs +1 -1
  444. package/clients/cli/commands/integration-real-world.test.mjs +3 -9
  445. package/clients/cli/commands/integration.doc.mjs +1 -1
  446. package/clients/cli/commands/integration.mjs +1 -0
  447. package/clients/cli/commands/interactive-guard.test.mjs +101 -24
  448. package/clients/cli/commands/json-contract.test.mjs +33 -0
  449. package/clients/cli/commands/layout-check.doc.mjs +15 -4
  450. package/clients/cli/commands/layout-expand.doc.mjs +22 -5
  451. package/clients/cli/commands/layout-grammar.doc.mjs +1 -1
  452. package/clients/cli/commands/layout.doc.mjs +3 -3
  453. package/clients/cli/commands/layout.mjs +21 -9
  454. package/clients/cli/commands/layout.path-help.test.mjs +33 -0
  455. package/clients/cli/commands/layout.stdin-cap.test.mjs +47 -0
  456. package/clients/cli/commands/layout.text-fields.test.mjs +39 -0
  457. package/clients/cli/commands/manifest.doc.mjs +1 -1
  458. package/clients/cli/commands/no-prompt-wording.test.mjs +94 -0
  459. package/clients/cli/commands/search.doc.mjs +7 -4
  460. package/clients/cli/commands/search.mjs +28 -9
  461. package/clients/cli/commands/search.test.mjs +75 -0
  462. package/clients/cli/commands/setup-nudge.test.mjs +6 -0
  463. package/clients/cli/commands/swizzle.doc.mjs +3 -2
  464. package/clients/cli/commands/swizzle.path-safety.test.mjs +42 -0
  465. package/clients/cli/commands/template.doc.mjs +52 -13
  466. package/clients/cli/commands/template.flag-help.test.mjs +117 -0
  467. package/clients/cli/commands/template.mjs +4 -91
  468. package/clients/cli/commands/template.path-help.test.mjs +40 -0
  469. package/clients/cli/commands/text-json-parity.test.mjs +719 -0
  470. package/clients/cli/commands/theme-add.doc.mjs +4 -3
  471. package/clients/cli/commands/theme-build.doc.mjs +8 -7
  472. package/clients/cli/commands/theme-list.doc.mjs +2 -2
  473. package/clients/cli/commands/theme-palette-generate.doc.mjs +9 -5
  474. package/clients/cli/commands/theme-palette-generate.test.mjs +19 -0
  475. package/clients/cli/commands/theme-palette.doc.mjs +1 -1
  476. package/clients/cli/commands/theme-targets.behavior.test.mjs +4 -3
  477. package/clients/cli/commands/theme-targets.doc.mjs +1 -1
  478. package/clients/cli/commands/theme-template.behavior.test.mjs +12 -0
  479. package/clients/cli/commands/theme-template.doc.mjs +2 -2
  480. package/clients/cli/commands/theme.doc.mjs +1 -1
  481. package/clients/cli/commands/upgrade.ascii-output.test.mjs +87 -0
  482. package/clients/cli/commands/upgrade.doc.mjs +22 -10
  483. package/clients/cli/commands/upgrade.file-protection.test.mjs +228 -0
  484. package/clients/cli/commands/upgrade.flag-help.test.mjs +188 -0
  485. package/clients/cli/commands/upgrade.hook-output.test.mjs +88 -0
  486. package/clients/cli/commands/upgrade.mjs +29 -7
  487. package/clients/cli/formatters/index.mjs +164 -1
  488. package/clients/cli/formatters/index.test.mjs +97 -0
  489. package/clients/cli/index.mjs +21 -30
  490. package/clients/cli/latest-version-env.test.mjs +50 -0
  491. package/clients/cli/lib/cli-error.test.mjs +7 -0
  492. package/clients/cli/lib/component-format.mjs +9 -9
  493. package/clients/cli/lib/component-format.test.mjs +1 -1
  494. package/clients/cli/lib/define-command.mjs +32 -6
  495. package/clients/cli/lib/doc-text-ascii.test.mjs +82 -0
  496. package/clients/cli/lib/exit-codes.test.mjs +97 -0
  497. package/clients/cli/lib/hook-format.mjs +19 -10
  498. package/clients/cli/lib/json-shim.mjs +38 -2
  499. package/clients/cli/lib/json-shim.test.mjs +83 -0
  500. package/clients/cli/lib/manifest.d.ts +2 -0
  501. package/clients/cli/lib/manifest.mjs +37 -2
  502. package/clients/cli/lib/manifest.test.mjs +17 -0
  503. package/foundation/agent-docs/agent-docs.d.mts +7 -2
  504. package/foundation/agent-docs/agent-docs.mjs +82 -12
  505. package/foundation/agent-docs/agent-docs.path-safety.test.mjs +266 -4
  506. package/foundation/agent-docs/agent-docs.test.mjs +19 -1
  507. package/foundation/config/integration-debug.test.mjs +28 -3
  508. package/foundation/config/project-themes.test.mjs +11 -19
  509. package/foundation/config/project.d.mts +20 -11
  510. package/foundation/config/project.mjs +263 -91
  511. package/foundation/config/project.test.mjs +270 -21
  512. package/foundation/discovery/authoring-self-docs.d.mts +87 -0
  513. package/foundation/discovery/authoring-self-docs.mjs +237 -0
  514. package/foundation/discovery/authoring-self-docs.test.mjs +170 -0
  515. package/foundation/discovery/authoring-surface.d.mts +74 -0
  516. package/foundation/discovery/authoring-surface.mjs +525 -0
  517. package/foundation/discovery/authoring-surface.test.mjs +392 -0
  518. package/foundation/discovery/cli-self-docs.d.mts +119 -0
  519. package/foundation/discovery/cli-self-docs.mjs +490 -0
  520. package/foundation/discovery/cli-self-docs.test.mjs +375 -0
  521. package/foundation/discovery/component-discovery.d.mts +39 -1
  522. package/foundation/discovery/component-discovery.mjs +50 -1
  523. package/foundation/discovery/component-loader.d.mts +35 -38
  524. package/foundation/discovery/component-loader.mjs +53 -222
  525. package/foundation/discovery/docs-discovery.d.mts +119 -11
  526. package/foundation/discovery/docs-discovery.mjs +423 -108
  527. package/foundation/discovery/docs-discovery.test.mjs +365 -20
  528. package/foundation/discovery/docs-output-budget.d.mts +28 -0
  529. package/foundation/discovery/docs-output-budget.mjs +50 -0
  530. package/foundation/discovery/docs-section-key.d.mts +116 -0
  531. package/foundation/discovery/docs-section-key.mjs +322 -0
  532. package/foundation/discovery/docs-section-key.test.mjs +246 -0
  533. package/foundation/discovery/template-adapter.d.mts +113 -11
  534. package/foundation/discovery/template-adapter.fixture-refs.test.mjs +248 -0
  535. package/foundation/discovery/template-adapter.integration-isolation.test.mjs +94 -0
  536. package/foundation/discovery/template-adapter.mjs +774 -83
  537. package/foundation/discovery/template-adapter.test.mjs +57 -0
  538. package/foundation/discovery/template-conflict-release.d.mts +13 -0
  539. package/foundation/discovery/template-conflict-release.mjs +40 -0
  540. package/foundation/discovery/template-conflict-release.test.mjs +40 -0
  541. package/foundation/discovery/theme-discovery.d.mts +67 -7
  542. package/foundation/discovery/theme-discovery.mjs +916 -186
  543. package/foundation/discovery/theme-discovery.test.mjs +613 -219
  544. package/foundation/discovery/theming-targets.test.mjs +4 -0
  545. package/foundation/doc-compiler/bundle.d.mts +47 -0
  546. package/foundation/doc-compiler/bundle.mjs +278 -0
  547. package/foundation/doc-compiler/bundle.test.mjs +266 -0
  548. package/foundation/doc-compiler/compile.d.mts +343 -0
  549. package/foundation/doc-compiler/compile.mjs +558 -0
  550. package/foundation/doc-compiler/diagnostics.d.mts +126 -0
  551. package/foundation/doc-compiler/diagnostics.mjs +305 -0
  552. package/foundation/doc-compiler/doc-compiler.test.mjs +714 -0
  553. package/foundation/doc-compiler/doc-loads.test.mjs +1642 -0
  554. package/foundation/doc-compiler/import.d.mts +24 -0
  555. package/foundation/doc-compiler/import.mjs +59 -0
  556. package/foundation/doc-compiler/inputs.d.mts +102 -0
  557. package/foundation/doc-compiler/inputs.mjs +291 -0
  558. package/foundation/doc-compiler/inputs.test.mjs +299 -0
  559. package/foundation/doc-compiler/ir.d.mts +22 -0
  560. package/foundation/doc-compiler/ir.mjs +471 -0
  561. package/foundation/doc-compiler/lenses.d.mts +36 -0
  562. package/foundation/doc-compiler/lenses.mjs +173 -0
  563. package/foundation/doc-compiler/links.d.mts +162 -0
  564. package/foundation/doc-compiler/links.mjs +294 -0
  565. package/foundation/doc-compiler/links.test.mjs +192 -0
  566. package/foundation/doc-compiler/lower-doc.test.mjs +495 -0
  567. package/foundation/doc-compiler/overlays.d.mts +37 -0
  568. package/foundation/doc-compiler/overlays.mjs +206 -0
  569. package/foundation/doc-compiler/parse-readable.d.mts +9 -0
  570. package/foundation/doc-compiler/parse-readable.mjs +29 -0
  571. package/foundation/doc-compiler/read.d.mts +127 -0
  572. package/foundation/doc-compiler/read.mjs +325 -0
  573. package/foundation/doc-compiler/read.test.mjs +313 -0
  574. package/foundation/doc-compiler/source.d.mts +33 -0
  575. package/foundation/doc-compiler/source.mjs +128 -0
  576. package/foundation/doc-compiler/tree.d.mts +288 -0
  577. package/foundation/doc-compiler/tree.mjs +876 -0
  578. package/foundation/doc-compiler/tree.test.mjs +598 -0
  579. package/foundation/fs/file-protection.d.mts +33 -0
  580. package/foundation/fs/file-protection.mjs +825 -0
  581. package/foundation/fs/file-protection.test.mjs +250 -0
  582. package/foundation/fs/module-loader.d.mts +1 -0
  583. package/foundation/fs/module-loader.mjs +50 -1
  584. package/foundation/fs/module-loader.stdout.test.mjs +332 -0
  585. package/foundation/fs/path-safety.d.mts +3 -2
  586. package/foundation/fs/path-safety.mjs +49 -19
  587. package/foundation/fs/path-safety.test.mjs +50 -0
  588. package/foundation/fs/publish-file-hardlink-unavailable.test.mjs +129 -94
  589. package/foundation/identity/provider-identity.d.mts +90 -0
  590. package/foundation/identity/provider-identity.mjs +320 -0
  591. package/foundation/identity/provider-identity.test.mjs +254 -0
  592. package/foundation/identity/providers.d.mts +7 -0
  593. package/foundation/identity/providers.mjs +16 -0
  594. package/foundation/integrations/autolink.d.mts +58 -1
  595. package/foundation/integrations/autolink.mjs +143 -45
  596. package/foundation/integrations/autolink.test.mjs +1 -1
  597. package/foundation/integrations/cli-requirement.d.mts +45 -0
  598. package/foundation/integrations/cli-requirement.mjs +154 -0
  599. package/foundation/integrations/cli-requirement.test.mjs +84 -0
  600. package/foundation/integrations/contribution-fixes.d.mts +145 -0
  601. package/foundation/integrations/contribution-fixes.mjs +1284 -0
  602. package/foundation/integrations/contribution-inventory.d.mts +3 -2
  603. package/foundation/integrations/contribution-inventory.mjs +27 -24
  604. package/foundation/integrations/contribution-inventory.test.mjs +86 -27
  605. package/foundation/integrations/integration-warnings.d.mts +9 -2
  606. package/foundation/integrations/integration-warnings.mjs +52 -21
  607. package/foundation/integrations/integration-warnings.test.mjs +74 -1
  608. package/foundation/integrations/integrations.d.mts +63 -3
  609. package/foundation/integrations/integrations.mjs +122 -9
  610. package/foundation/integrations/integrations.test.mjs +415 -1
  611. package/foundation/integrations/provider-conflicts.test.mjs +125 -0
  612. package/foundation/integrations/provider-ledger.test.mjs +275 -0
  613. package/foundation/integrations/provider-resolution.d.mts +152 -0
  614. package/foundation/integrations/provider-resolution.mjs +576 -0
  615. package/foundation/integrations/provider-resolution.test.mjs +369 -0
  616. package/foundation/integrations/theme-descriptor.d.mts +8 -0
  617. package/foundation/integrations/theme-descriptor.mjs +44 -0
  618. package/foundation/integrations/validate-contributions.d.mts +2 -0
  619. package/foundation/integrations/validate-contributions.mjs +131 -29
  620. package/foundation/response/base.d.ts +8 -4
  621. package/foundation/response/error-codes.d.mts +3 -1
  622. package/foundation/response/error-codes.d.ts +2 -0
  623. package/foundation/response/error-codes.doc.mjs +13 -4
  624. package/foundation/response/error-codes.mjs +8 -2
  625. package/foundation/response/error-codes.test.mjs +137 -10
  626. package/foundation/response/json-contract.test.mjs +57 -17
  627. package/foundation/response/json.d.mts +4 -2
  628. package/foundation/response/json.mjs +8 -10
  629. package/foundation/response/response-types.doc.d.mts +5 -1
  630. package/foundation/response/response-types.doc.mjs +41 -21
  631. package/foundation/response/response-types.doc.test.mjs +158 -0
  632. package/foundation/response/response.doc.mjs +1 -1
  633. package/foundation/text/string-utils.d.mts +8 -0
  634. package/foundation/text/string-utils.mjs +40 -10
  635. package/foundation/xle/expand.d.mts +2 -0
  636. package/foundation/xle/expand.mjs +4 -3
  637. package/foundation/xle/expand.test.mjs +54 -0
  638. package/foundation/xle/xle.test.mjs +13 -0
  639. package/package.json +10 -11
  640. package/assets/templates/themes/manifest.json +0 -95
  641. package/clients/cli/lib/update-check.mjs +0 -83
  642. package/clients/cli/lib/update-check.test.mjs +0 -137
  643. package/clients/cli/update-hint-commands.test.mjs +0 -54
@@ -5,8 +5,7 @@
5
5
  * contribution into an integration package.
6
6
  *
7
7
  * Dispatches component, doc, template, codemod, and agent-doc. Theme
8
- * delegates to the existing `integrationAddTheme` (not modified here).
9
- * Every root-based kind shares: first add creates the manifest; one shared
8
+ * delegates to the dedicated same-stem descriptor writer. Every root-based kind shares: first add creates the manifest; one shared
10
9
  * writer; never creates `files`/`exports`; atomic staged writes; no clobber;
11
10
  * dry-run receipt predicts real run; post-write verify through the real
12
11
  * discovery/parser seam.
@@ -35,6 +34,12 @@ import {
35
34
  } from '../../foundation/integrations/integrations.mjs';
36
35
  import {isValidSemver} from '../../foundation/env/semver.mjs';
37
36
  import {assertContributionVisible} from '../../foundation/integrations/contribution-inventory.mjs';
37
+ import {
38
+ docsTreeCliProblem,
39
+ withDocsTreeCli,
40
+ } from '../../foundation/integrations/cli-requirement.mjs';
41
+ import {discoverIntegrationDocs} from '../../foundation/discovery/docs-discovery.mjs';
42
+ import {findIntegrationComponentDoc} from '../../foundation/discovery/component-discovery.mjs';
38
43
  import {parseAgentDocsField} from '../../authoring/integration/schema.mjs';
39
44
  import {integrationAddTheme} from './add-theme.mjs';
40
45
  import {
@@ -83,6 +88,17 @@ function resolvePackage(cwd) {
83
88
  }
84
89
  const owner = typeof pkg.name === 'string' ? pkg.name : '(local integration)';
85
90
  const existingManifest = findLocalIntegrationManifestOrNull(packageDir);
91
+ // The nearest package.json above the cwd may belong to another package (an
92
+ // app that contains the folder). Only a package that is already an
93
+ // integration is found from a subfolder; the first add runs in the package.
94
+ const here = path.resolve(cwd ?? process.cwd());
95
+ if (existingManifest == null && path.resolve(packageDir) !== here) {
96
+ throw new AstryxError(
97
+ `${here} has no package.json. The nearest is ${packageFile} (${owner}), which is not an Astryx integration yet. Run this in your integration package's own directory, next to its package.json.`,
98
+ undefined,
99
+ ERROR_CODES.ERR_INVALID_ARGUMENT,
100
+ );
101
+ }
86
102
  const manifestFile =
87
103
  existingManifest ?? path.join(packageDir, 'astryx.integration.mjs');
88
104
  return {
@@ -248,10 +264,21 @@ async function addComponent(name, options) {
248
264
  ERROR_CODES.ERR_FILE_EXISTS,
249
265
  );
250
266
  }
267
+ // Discovery keys components by doc stem anywhere under the root, so a second
268
+ // doc with this name would shadow one of the two.
269
+ const existingDoc = findIntegrationComponentDoc({components: root}, name);
270
+ if (existingDoc != null) {
271
+ throw new AstryxError(
272
+ `Refusing to add component "${name}": ${projectPath(path.relative(packageDir, existingDoc))} already defines it.`,
273
+ undefined,
274
+ ERROR_CODES.ERR_FILE_EXISTS,
275
+ );
276
+ }
251
277
 
252
278
  const sourcePath = projectPath(path.relative(packageDir, sourceFile));
253
- const importSpecifier = `${owner}/${sourcePath}`;
254
- const docContents = `export default {\n type: 'component',\n name: '${name}',\n import: ${JSON.stringify(importSpecifier)},\n description: '${name} component.',\n props: [],\n};\n`;
279
+ const extensionlessPath = sourcePath.replace(/\.tsx?$/u, '');
280
+ const importSpecifier = `${owner}/${extensionlessPath}`;
281
+ const docContents = `/** @type {import('@astryxdesign/cli/authoring').ComponentDoc} */\nexport default {\n type: 'component',\n name: '${name}',\n displayName: '${name}',\n import: ${JSON.stringify(importSpecifier)},\n usage: {description: '${name} component.'},\n props: [],\n};\n`;
255
282
  const sourceContents = `export function ${name}() {\n return <div>${name}</div>;\n}\n`;
256
283
 
257
284
  /** @type {import('./add-helpers.mjs').WritePlan[]} */
@@ -263,7 +290,7 @@ async function addComponent(name, options) {
263
290
  packageFile,
264
291
  rootPath,
265
292
  path.basename(manifestFile),
266
- [{subpath: sourcePath, target: sourcePath}],
293
+ [{subpath: extensionlessPath, target: sourcePath}],
267
294
  );
268
295
  if (pkgUpdate != null) {
269
296
  plans.push({
@@ -342,7 +369,7 @@ async function addComponent(name, options) {
342
369
 
343
370
  /**
344
371
  * @param {string} name topic name (must match [\w-]+)
345
- * @param {{cwd?: string, dryRun?: boolean, replaces?: string, extends?: string}} options
372
+ * @param {{cwd?: string, dryRun?: boolean, replaces?: string, extends?: string, parent?: string}} options
346
373
  * @returns {Promise<import('./integration-authoring.type.mjs').IntegrationAddResponse>}
347
374
  */
348
375
  async function addDoc(name, options) {
@@ -361,6 +388,20 @@ async function addDoc(name, options) {
361
388
  ERROR_CODES.ERR_INVALID_ARGUMENT,
362
389
  );
363
390
  }
391
+ if (options.parent != null && (options.replaces != null || options.extends != null)) {
392
+ throw new AstryxError(
393
+ 'A guide placed in a namespace has its own route: it cannot also replace or extend a topic.',
394
+ undefined,
395
+ ERROR_CODES.ERR_INVALID_ARGUMENT,
396
+ );
397
+ }
398
+ if (options.parent != null && !/^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(options.parent)) {
399
+ throw new AstryxError(
400
+ '--parent must name a namespace in lowercase letters and digits joined by single hyphens, such as "acme".',
401
+ undefined,
402
+ ERROR_CODES.ERR_INVALID_ARGUMENT,
403
+ );
404
+ }
364
405
  for (const [option, value] of [
365
406
  ['--replaces', options.replaces],
366
407
  ['--extends', options.extends],
@@ -421,20 +462,92 @@ async function addDoc(name, options) {
421
462
  }
422
463
 
423
464
  const title = kebabToTitle(name);
465
+ // --parent names a namespace of this package by its name, wherever its file
466
+ // is. An existing one is used as it is: its `guides` slot, or else its only
467
+ // slot that takes a guide. Otherwise the namespace doc is written, unless a
468
+ // file of that name is already something else (spec:AST-046 FR11).
469
+ const namespaceFile =
470
+ options.parent == null
471
+ ? null
472
+ : assertWithin(`${options.parent}.doc.mjs`, root, {
473
+ label: 'namespace doc file',
474
+ });
475
+ let parentSlot = 'guides';
476
+ let writeNamespace = false;
477
+ if (options.parent != null && namespaceFile != null) {
478
+ const {namespaces} = await discoverIntegrationDocs({
479
+ name: owner,
480
+ docs: root,
481
+ });
482
+ const existing = namespaces.find(ns => ns.doc.name === options.parent);
483
+ if (existing != null) {
484
+ const takesGuides = Object.entries(existing.doc.slots ?? {})
485
+ .filter(([, slot]) => slot?.accepts?.kinds?.includes('generic'))
486
+ .map(([slotName]) => slotName);
487
+ const slot = takesGuides.includes('guides')
488
+ ? 'guides'
489
+ : takesGuides.length === 1
490
+ ? takesGuides[0]
491
+ : null;
492
+ if (slot == null) {
493
+ throw new AstryxError(
494
+ takesGuides.length === 0
495
+ ? `Namespace "${options.parent}" (${existing.source}) has no slot that takes a guide. Add one, such as guides: {title: 'Guides', accepts: {kinds: ['generic']}}.`
496
+ : `Namespace "${options.parent}" (${existing.source}) has more than one slot that takes a guide (${takesGuides.join(', ')}). Write the guide yourself, and name its slot in its placement.`,
497
+ undefined,
498
+ ERROR_CODES.ERR_INVALID_ARGUMENT,
499
+ );
500
+ }
501
+ parentSlot = slot;
502
+ } else if (fs.existsSync(namespaceFile)) {
503
+ throw new AstryxError(
504
+ `${projectPath(path.relative(packageDir, namespaceFile))} exists but does not load as the namespace "${options.parent}". Fix or rename that file, or pass another --parent.`,
505
+ undefined,
506
+ ERROR_CODES.ERR_FILE_EXISTS,
507
+ );
508
+ } else {
509
+ writeNamespace = true;
510
+ }
511
+ }
424
512
  const relationship = options.replaces
425
513
  ? `\n replaces: '${options.replaces}',`
426
514
  : options.extends
427
515
  ? `\n extends: '${options.extends}',`
428
- : '';
429
- const docContents = `export default {\n type: 'generic',\n name: '${name}',\n title: '${title}',\n description: '${title} documentation.',${relationship}\n sections: [\n {\n title: 'Overview',\n content: [\n { type: 'prose', text: '${title} documentation.' },\n ],\n },\n ],\n};\n`;
516
+ : options.parent
517
+ ? `\n placement: {parent: 'namespace:${options.parent}', slot: '${parentSlot}'},`
518
+ : '';
519
+ const namespaceTitle =
520
+ options.parent == null ? '' : kebabToTitle(options.parent);
521
+ const namespaceContents = `/** @type {import('@astryxdesign/cli/authoring').NamespaceDoc} */\nexport default {\n type: 'namespace',\n name: '${options.parent}',\n title: '${namespaceTitle}',\n summary: 'Guides for ${namespaceTitle}.',\n slots: {\n guides: {title: 'Guides', accepts: {kinds: ['generic']}},\n },\n};\n`;
522
+ const docContents = `/** @type {import('@astryxdesign/cli/authoring').ReferenceDoc} */\nexport default {\n type: 'generic',\n name: '${name}',\n title: '${title}',\n description: '${title} documentation.',${relationship}\n sections: [\n {\n title: 'Overview',\n content: [\n { type: 'prose', text: '${title} documentation.' },\n ],\n },\n ],\n};\n`;
430
523
 
431
524
  /** @type {import('./add-helpers.mjs').WritePlan[]} */
432
525
  const plans = [{path: docFile, contents: docContents, createOnly: true}];
433
- const pkgUpdate = packageJsonUpdate(
526
+ if (namespaceFile != null && writeNamespace) {
527
+ plans.push({path: namespaceFile, contents: namespaceContents, createOnly: true});
528
+ }
529
+ let pkgUpdate = packageJsonUpdate(
434
530
  packageFile,
435
531
  rootPath,
436
532
  path.basename(manifestFile),
437
533
  );
534
+ // A namespace doc needs a CLI that reads it: an older one hides every doc
535
+ // topic the package ships. Declare that CLI as a peer, so an older one fails
536
+ // at install instead (spec:AST-046 FR11).
537
+ if (options.parent != null) {
538
+ const expectedOriginal =
539
+ pkgUpdate?.expectedOriginal ?? fs.readFileSync(packageFile);
540
+ const text = pkgUpdate?.contents ?? expectedOriginal.toString('utf-8');
541
+ const current = JSON.parse(text);
542
+ if (docsTreeCliProblem(current) != null) {
543
+ pkgUpdate = {
544
+ contents:
545
+ JSON.stringify(withDocsTreeCli(current), null, 2) +
546
+ (text.endsWith('\n') ? '\n' : ''),
547
+ expectedOriginal,
548
+ };
549
+ }
550
+ }
438
551
  if (pkgUpdate != null) {
439
552
  plans.push({
440
553
  path: packageFile,
@@ -553,8 +666,8 @@ async function addTemplate(name, options) {
553
666
  throw error;
554
667
  }
555
668
 
556
- const specFile = assertWithin(`${name}.template.mjs`, root, {
557
- label: 'template spec',
669
+ const specFile = assertWithin(`${name}.doc.mjs`, root, {
670
+ label: 'template descriptor',
558
671
  });
559
672
  const sourceFile = assertWithin(`${name}.tsx`, root, {
560
673
  label: 'template source',
@@ -576,7 +689,9 @@ async function addTemplate(name, options) {
576
689
 
577
690
  const pascalName = kebabToPascal(name);
578
691
  const sourcePath = projectPath(path.relative(packageDir, sourceFile));
579
- const specContents = `export default {\n type: '${templateType}',\n name: '${name}',\n description: '${kebabToTitle(name)} template.',\n};\n`;
692
+ const extensionlessPath = sourcePath.replace(/\.tsx?$/u, '');
693
+ const blockFields = templateType === 'block' ? '\n aspectRatio: 1,' : '';
694
+ const specContents = `/** @type {import('@astryxdesign/cli/authoring').TemplateDoc} */\nexport default {\n type: '${templateType}',\n name: '${name}',\n displayName: '${kebabToTitle(name)}',\n description: '${kebabToTitle(name)} template.',${blockFields}\n};\n`;
580
695
  const sourceContents = `export default function ${pascalName}() {\n return <div>${kebabToTitle(name)}</div>;\n}\n`;
581
696
 
582
697
  /** @type {import('./add-helpers.mjs').WritePlan[]} */
@@ -588,7 +703,7 @@ async function addTemplate(name, options) {
588
703
  packageFile,
589
704
  rootPath,
590
705
  path.basename(manifestFile),
591
- [{subpath: sourcePath, target: sourcePath}],
706
+ [{subpath: extensionlessPath, target: sourcePath}],
592
707
  );
593
708
  if (pkgUpdate != null) {
594
709
  plans.push({
@@ -1092,7 +1207,7 @@ export function integrationAddAgentDoc(line, options = {}) {
1092
1207
 
1093
1208
  const KIND_OPTIONS = {
1094
1209
  component: new Set(['cwd', 'dryRun']),
1095
- doc: new Set(['cwd', 'dryRun', 'replaces', 'extends']),
1210
+ doc: new Set(['cwd', 'dryRun', 'replaces', 'extends', 'parent']),
1096
1211
  template: new Set(['cwd', 'dryRun', 'templateType']),
1097
1212
  codemod: new Set(['cwd', 'dryRun', 'to']),
1098
1213
  'agent-doc': new Set(['cwd', 'dryRun']),
@@ -1123,7 +1238,7 @@ function validateKindOptions(kind, options) {
1123
1238
  *
1124
1239
  * @param {'component'|'doc'|'template'|'codemod'|'agent-doc'|'theme'} kind
1125
1240
  * @param {string} name
1126
- * @param {{cwd?: string, dryRun?: boolean, templateType?: 'page'|'block', to?: string, replaces?: string, extends?: string}} [options]
1241
+ * @param {{cwd?: string, dryRun?: boolean, templateType?: 'page'|'block', to?: string, replaces?: string, extends?: string, parent?: string}} [options]
1127
1242
  * @returns {Promise<import('./integration-authoring.type.mjs').IntegrationAddResponse>}
1128
1243
  */
1129
1244
  export async function integrationAdd(kind, name, options = {}) {
@@ -12,6 +12,7 @@ import {
12
12
  integrationAddTemplate,
13
13
  } from './add-contribution.mjs';
14
14
  import {validateLocalIntegration} from './validate-integration.mjs';
15
+ import {DOCS_TREE_CLI} from '../../foundation/integrations/cli-requirement.mjs';
15
16
 
16
17
  let tmpDir;
17
18
 
@@ -77,6 +78,7 @@ describe('integrationAdd component', () => {
77
78
  path.join(tmpDir, 'components/MyWidget.doc.mjs'),
78
79
  'utf-8',
79
80
  );
81
+ expect(doc).toContain("@astryxdesign/cli/authoring').ComponentDoc");
80
82
  expect(doc).toContain("type: 'component'");
81
83
  expect(doc).toContain("name: 'MyWidget'");
82
84
  expect(doc).toContain('props: []');
@@ -118,14 +120,14 @@ describe('integrationAdd component', () => {
118
120
  );
119
121
  expect(pkg.exports).toEqual({
120
122
  '.': './index.mjs',
121
- './components/MyWidget.tsx': './components/MyWidget.tsx',
123
+ './components/MyWidget': './components/MyWidget.tsx',
122
124
  });
123
125
  expect(
124
126
  fs.readFileSync(
125
127
  path.join(tmpDir, 'components/MyWidget.doc.mjs'),
126
128
  'utf-8',
127
129
  ),
128
- ).toContain('import: "@acme/integration/components/MyWidget.tsx"');
130
+ ).toContain('import: "@acme/integration/components/MyWidget"');
129
131
  });
130
132
 
131
133
  it('does not create exports when the package has no exports map', async () => {
@@ -143,7 +145,7 @@ describe('integrationAdd component', () => {
143
145
  setup({
144
146
  exports: {
145
147
  '.': './index.mjs',
146
- './components/MyWidget.tsx': './different.tsx',
148
+ './components/MyWidget': './different.tsx',
147
149
  },
148
150
  });
149
151
 
@@ -304,6 +306,7 @@ describe('integrationAdd doc', () => {
304
306
  path.join(tmpDir, 'docs/my-guide.doc.mjs'),
305
307
  'utf-8',
306
308
  );
309
+ expect(doc).toContain("@astryxdesign/cli/authoring').ReferenceDoc");
307
310
  expect(doc).toContain("type: 'generic'");
308
311
  expect(doc).toContain("name: 'my-guide'");
309
312
 
@@ -312,6 +315,142 @@ describe('integrationAdd doc', () => {
312
315
  expect(docErrors).toEqual([]);
313
316
  });
314
317
 
318
+ it('refuses to write from a folder with no package.json into the package above it', async () => {
319
+ setupBare();
320
+ const inside = path.join(tmpDir, 'kit');
321
+ fs.mkdirSync(inside);
322
+ await expect(
323
+ integrationAdd('doc', 'my-guide', {cwd: inside, parent: 'acme'}),
324
+ ).rejects.toMatchObject({
325
+ code: 'ERR_INVALID_ARGUMENT',
326
+ message: expect.stringContaining('has no package.json'),
327
+ });
328
+ expect(fs.existsSync(path.join(tmpDir, 'docs'))).toBe(false);
329
+ expect(fs.existsSync(path.join(tmpDir, 'astryx.integration.mjs'))).toBe(false);
330
+ });
331
+
332
+ it('finds an integration from a folder inside it', async () => {
333
+ setup();
334
+ const inside = path.join(tmpDir, 'src');
335
+ fs.mkdirSync(inside);
336
+ const result = await integrationAdd('doc', 'my-guide', {cwd: inside});
337
+ expect(result.data.files).toContain('docs/my-guide.doc.mjs');
338
+ expect(fs.existsSync(path.join(tmpDir, 'docs/my-guide.doc.mjs'))).toBe(true);
339
+ });
340
+
341
+ it('places a guide in a namespace of the package, writing the namespace once', async () => {
342
+ setup();
343
+ const first = await integrationAdd('doc', 'deploying', {
344
+ cwd: tmpDir,
345
+ parent: 'acme',
346
+ });
347
+ expect(first.data.files).toEqual(
348
+ expect.arrayContaining(['docs/deploying.doc.mjs', 'docs/acme.doc.mjs']),
349
+ );
350
+ const guide = fs.readFileSync(path.join(tmpDir, 'docs/deploying.doc.mjs'), 'utf-8');
351
+ expect(guide).toContain("placement: {parent: 'namespace:acme', slot: 'guides'}");
352
+ const namespace = fs.readFileSync(path.join(tmpDir, 'docs/acme.doc.mjs'), 'utf-8');
353
+ expect(namespace).toContain("type: 'namespace'");
354
+ expect(namespace).toContain("guides: {title: 'Guides', accepts: {kinds: ['generic']}}");
355
+
356
+ // The namespace doc needs a CLI that reads it, declared as an optional peer.
357
+ const pkg = JSON.parse(fs.readFileSync(path.join(tmpDir, 'package.json'), 'utf-8'));
358
+ expect(pkg.peerDependencies).toEqual({'@astryxdesign/cli': `>=${DOCS_TREE_CLI}`});
359
+ expect(pkg.peerDependenciesMeta).toEqual({'@astryxdesign/cli': {optional: true}});
360
+
361
+ const second = await integrationAdd('doc', 'upgrading', {
362
+ cwd: tmpDir,
363
+ parent: 'acme',
364
+ });
365
+ expect(second.data.files).not.toContain('docs/acme.doc.mjs');
366
+
367
+ const validation = await validateLocalIntegration(tmpDir);
368
+ expect(validation.issues.filter(i => i.code === 'invalid_doc')).toEqual([]);
369
+ });
370
+
371
+ it('finds the --parent namespace by its name, wherever its file is, and uses its slot', async () => {
372
+ setup({manifest: "export default {docs: './docs'};\n"});
373
+ fs.mkdirSync(path.join(tmpDir, 'docs', 'ns'), {recursive: true});
374
+ fs.writeFileSync(
375
+ path.join(tmpDir, 'docs', 'ns', 'acme.doc.mjs'),
376
+ "export default {type: 'namespace', name: 'acme', title: 'Acme', summary: 'Acme.', slots: {items: {title: 'Items', accepts: {kinds: ['generic']}}}};\n",
377
+ );
378
+ const result = await integrationAdd('doc', 'deploying', {cwd: tmpDir, parent: 'acme'});
379
+ expect(result.data.files).not.toContain('docs/acme.doc.mjs');
380
+ expect(fs.existsSync(path.join(tmpDir, 'docs', 'acme.doc.mjs'))).toBe(false);
381
+ expect(fs.readFileSync(path.join(tmpDir, 'docs', 'deploying.doc.mjs'), 'utf-8')).toContain(
382
+ "placement: {parent: 'namespace:acme', slot: 'items'}",
383
+ );
384
+ });
385
+
386
+ it('refuses --parent when its file is not that namespace, or the namespace has no slot for a guide', async () => {
387
+ setup({manifest: "export default {docs: './docs'};\n"});
388
+ fs.mkdirSync(path.join(tmpDir, 'docs'), {recursive: true});
389
+ const nsFile = path.join(tmpDir, 'docs', 'acme.doc.mjs');
390
+ fs.writeFileSync(
391
+ nsFile,
392
+ "export default {type: 'generic', name: 'acme', title: 'Acme', description: 'A topic.', sections: [{title: 'Only', content: [{type: 'prose', text: 'x'}]}]};\n",
393
+ );
394
+ await expect(
395
+ integrationAdd('doc', 'deploying', {cwd: tmpDir, parent: 'acme'}),
396
+ ).rejects.toMatchObject({code: 'ERR_FILE_EXISTS'});
397
+ // A fresh file: the module loader keeps the first import of a path.
398
+ fs.writeFileSync(
399
+ path.join(tmpDir, 'docs', 'beta.doc.mjs'),
400
+ "export default {type: 'namespace', name: 'beta', title: 'Beta', summary: 'Beta.', slots: {items: {title: 'Items', accepts: {kinds: ['function']}}}};\n",
401
+ );
402
+ await expect(
403
+ integrationAdd('doc', 'deploying', {cwd: tmpDir, parent: 'beta'}),
404
+ ).rejects.toMatchObject({
405
+ code: 'ERR_INVALID_ARGUMENT',
406
+ message: expect.stringContaining('no slot that takes a guide'),
407
+ });
408
+ fs.writeFileSync(
409
+ path.join(tmpDir, 'docs', 'gamma.doc.mjs'),
410
+ "export default {type: 'namespace', name: 'gamma', title: 'Gamma', summary: 'Gamma.', slots: {guides: {title: 'Guides', accepts: {kinds: ['function']}}, more: {title: 'More', accepts: {kinds: ['generic']}}, extra: {title: 'Extra', accepts: {kinds: ['generic']}}}};\n",
411
+ );
412
+ await expect(
413
+ integrationAdd('doc', 'deploying', {cwd: tmpDir, parent: 'gamma'}),
414
+ ).rejects.toMatchObject({
415
+ code: 'ERR_INVALID_ARGUMENT',
416
+ message: expect.stringContaining('more than one slot that takes a guide (more, extra)'),
417
+ });
418
+ expect(fs.existsSync(path.join(tmpDir, 'docs', 'deploying.doc.mjs'))).toBe(false);
419
+ });
420
+
421
+ it('raises a CLI peer that admits a CLI too old for namespace docs, and keeps one that does not', async () => {
422
+ setup();
423
+ const file = path.join(tmpDir, 'package.json');
424
+ const write = (/** @type {string} */ range) => {
425
+ const pkg = JSON.parse(fs.readFileSync(file, 'utf-8'));
426
+ pkg.peerDependencies = {'@astryxdesign/cli': range};
427
+ fs.writeFileSync(file, `${JSON.stringify(pkg, null, 2)}\n`);
428
+ };
429
+ write('^0.6.0');
430
+ await integrationAdd('doc', 'deploying', {cwd: tmpDir, parent: 'acme'});
431
+ expect(JSON.parse(fs.readFileSync(file, 'utf-8')).peerDependencies).toEqual({
432
+ '@astryxdesign/cli': `>=${DOCS_TREE_CLI}`,
433
+ });
434
+ write('^9.1.0');
435
+ await integrationAdd('doc', 'upgrading', {cwd: tmpDir, parent: 'acme'});
436
+ expect(JSON.parse(fs.readFileSync(file, 'utf-8')).peerDependencies).toEqual({
437
+ '@astryxdesign/cli': '^9.1.0',
438
+ });
439
+ });
440
+
441
+ it('refuses --parent with a relationship, or a namespace name that is not a route segment', async () => {
442
+ setup();
443
+ await expect(
444
+ integrationAdd('doc', 'x', {cwd: tmpDir, parent: 'acme', replaces: 'getting-started'}),
445
+ ).rejects.toMatchObject({code: 'ERR_INVALID_ARGUMENT'});
446
+ await expect(
447
+ integrationAdd('doc', 'x', {cwd: tmpDir, parent: 'Acme Kit'}),
448
+ ).rejects.toMatchObject({code: 'ERR_INVALID_ARGUMENT'});
449
+ await expect(
450
+ integrationAdd('template', 'x', {cwd: tmpDir, parent: 'acme'}),
451
+ ).rejects.toMatchObject({code: 'ERR_INVALID_ARGUMENT'});
452
+ });
453
+
315
454
  it('includes replaces in the generated doc', async () => {
316
455
  setup();
317
456
  const result = await integrationAdd('doc', 'my-tokens', {
@@ -390,13 +529,14 @@ describe('integrationAdd template', () => {
390
529
  expect(result.data.kind).toBe('template');
391
530
  expect(result.data.name).toBe('my-widget');
392
531
  expect(result.data.root).toEqual({path: './templates', created: true});
393
- expect(result.data.files).toContain('templates/my-widget.template.mjs');
532
+ expect(result.data.files).toContain('templates/my-widget.doc.mjs');
394
533
  expect(result.data.files).toContain('templates/my-widget.tsx');
395
534
 
396
535
  const spec = fs.readFileSync(
397
- path.join(tmpDir, 'templates/my-widget.template.mjs'),
536
+ path.join(tmpDir, 'templates/my-widget.doc.mjs'),
398
537
  'utf-8',
399
538
  );
539
+ expect(spec).toContain("@astryxdesign/cli/authoring').TemplateDoc");
400
540
  expect(spec).toContain("type: 'page'");
401
541
  expect(spec).toContain("name: 'my-widget'");
402
542
 
@@ -421,7 +561,7 @@ describe('integrationAdd template', () => {
421
561
  );
422
562
  expect(pkg.exports).toEqual({
423
563
  '.': './index.mjs',
424
- './templates/my-widget.tsx': './templates/my-widget.tsx',
564
+ './templates/my-widget': './templates/my-widget.tsx',
425
565
  });
426
566
  });
427
567
 
@@ -432,10 +572,11 @@ describe('integrationAdd template', () => {
432
572
  templateType: 'block',
433
573
  });
434
574
  const spec = fs.readFileSync(
435
- path.join(tmpDir, 'templates/my-card.template.mjs'),
575
+ path.join(tmpDir, 'templates/my-card.doc.mjs'),
436
576
  'utf-8',
437
577
  );
438
578
  expect(spec).toContain("type: 'block'");
579
+ expect(spec).toContain('aspectRatio: 1');
439
580
  });
440
581
 
441
582
  it('rejects an invalid template type', async () => {
@@ -676,6 +817,35 @@ describe('custom root preservation', () => {
676
817
  });
677
818
  });
678
819
 
820
+ describe('typed descriptor conformance', () => {
821
+ it('emits a strongly typed .doc.mjs for every canonical item writer', async () => {
822
+ setup();
823
+ await integrationAdd('component', 'MyWidget', {cwd: tmpDir});
824
+ await integrationAdd('doc', 'my-guide', {cwd: tmpDir});
825
+ await integrationAdd('template', 'my-page', {cwd: tmpDir});
826
+ await integrationAdd('theme', 'ocean', {cwd: tmpDir});
827
+
828
+ // Codemod and agent-doc keep their released formats for compatibility;
829
+ // no new kind may copy those exceptions.
830
+ for (const [relativePath, type] of [
831
+ ['components/MyWidget.doc.mjs', 'ComponentDoc'],
832
+ ['docs/my-guide.doc.mjs', 'ReferenceDoc'],
833
+ ['templates/my-page.doc.mjs', 'TemplateDoc'],
834
+ ['themes/ocean/oceanTheme.doc.mjs', 'ThemeDoc'],
835
+ ]) {
836
+ const source = fs.readFileSync(path.join(tmpDir, relativePath), 'utf-8');
837
+ expect(source).toContain(`@astryxdesign/cli/authoring').${type}`);
838
+ expect(source).toContain('export default {');
839
+ }
840
+ expect(
841
+ fs.existsSync(path.join(tmpDir, 'templates/my-page.template.mjs')),
842
+ ).toBe(false);
843
+ expect(fs.existsSync(path.join(tmpDir, 'themes/manifest.json'))).toBe(
844
+ false,
845
+ );
846
+ });
847
+ });
848
+
679
849
  describe('public per-kind APIs', () => {
680
850
  it('exposes narrow functions for every non-theme contribution kind', async () => {
681
851
  setup();
@@ -711,6 +881,7 @@ describe('public dispatcher', () => {
711
881
  const result = await integrationAdd('theme', 'ocean', {cwd: tmpDir});
712
882
  expect(result.data).toMatchObject({kind: 'theme', name: 'ocean'});
713
883
  expect(result.data.files).toContain('themes/ocean/oceanTheme.ts');
884
+ expect(result.data.files).toContain('themes/ocean/oceanTheme.doc.mjs');
714
885
  });
715
886
 
716
887
  it('refuses a kind-specific option on the wrong kind', async () => {
@@ -731,3 +902,83 @@ describe('public dispatcher', () => {
731
902
  ).rejects.toThrow(/component, doc, template, codemod, agent-doc, or theme/);
732
903
  });
733
904
  });
905
+
906
+ describe('dry-run receipts match the real write', () => {
907
+ /** Package states each writer has to plan for. */
908
+ const STATES = {
909
+ 'no manifest': {pkg: {files: ['dist']}},
910
+ 'an empty manifest': {pkg: {}, manifest: 'export default {};\n'},
911
+ 'declared roots and an exports map': {
912
+ pkg: {files: ['dist'], exports: {'.': './dist/index.js'}},
913
+ manifest:
914
+ "export default {\n components: './components',\n docs: './docs',\n templates: './templates',\n codemods: './codemods',\n themes: './themes',\n};\n",
915
+ },
916
+ };
917
+ /** @type {Array<[any, string, Record<string, unknown>]>} */
918
+ const KINDS = [
919
+ ['component', 'AcmeWidget', {}],
920
+ ['doc', 'deploying', {}],
921
+ ['template', 'account-page', {}],
922
+ ['template', 'account-card', {templateType: 'block'}],
923
+ ['codemod', 'rename-widget', {to: '1.2.0'}],
924
+ ['agent-doc', 'Run acme verify before finishing.', {}],
925
+ ['theme', 'ocean', {}],
926
+ ];
927
+
928
+ /** Every file under the package, keyed by project path, with its bytes. */
929
+ function snapshot() {
930
+ /** @type {Record<string, string>} */
931
+ const files = {};
932
+ /** @param {string} dir */
933
+ const walk = dir => {
934
+ for (const entry of fs.readdirSync(dir, {withFileTypes: true})) {
935
+ const full = path.join(dir, entry.name);
936
+ if (entry.isDirectory()) walk(full);
937
+ else {
938
+ files[path.relative(tmpDir, full).split(path.sep).join('/')] =
939
+ fs.readFileSync(full, 'base64');
940
+ }
941
+ }
942
+ };
943
+ walk(tmpDir);
944
+ return files;
945
+ }
946
+
947
+ for (const [stateName, state] of Object.entries(STATES)) {
948
+ for (const [kind, name, options] of KINDS) {
949
+ it(`${kind} ${name} in a package with ${stateName}`, async () => {
950
+ fs.writeFileSync(
951
+ path.join(tmpDir, 'package.json'),
952
+ `${JSON.stringify({name: '@acme/integration', version: '1.0.0', ...state.pkg}, null, 2)}\n`,
953
+ );
954
+ if (state.manifest != null) {
955
+ fs.writeFileSync(
956
+ path.join(tmpDir, 'astryx.integration.mjs'),
957
+ state.manifest,
958
+ );
959
+ }
960
+ const before = snapshot();
961
+
962
+ const plan = await integrationAdd(kind, name, {
963
+ cwd: tmpDir,
964
+ dryRun: true,
965
+ ...options,
966
+ });
967
+ expect(snapshot()).toEqual(before);
968
+
969
+ const written = await integrationAdd(kind, name, {
970
+ cwd: tmpDir,
971
+ ...options,
972
+ });
973
+ const after = snapshot();
974
+ const changed = Object.keys(after)
975
+ .filter(file => after[file] !== before[file])
976
+ .sort();
977
+
978
+ expect(written.data.root).toEqual(plan.data.root);
979
+ expect(written.data.files).toEqual(plan.data.files);
980
+ expect([...written.data.files].sort()).toEqual(changed);
981
+ });
982
+ }
983
+ }
984
+ });
@@ -8,7 +8,7 @@ export function findPackageDir(startDir: string): string;
8
8
  /**
9
9
  * @typedef {object} WritePlan
10
10
  * @property {string} path
11
- * @property {string} contents
11
+ * @property {string | Buffer} contents a Buffer is written byte for byte
12
12
  * @property {boolean} createOnly
13
13
  * @property {Buffer} [expectedOriginal] bytes captured before validation;
14
14
  * a different current file is a concurrent edit and must never be overwritten
@@ -56,7 +56,10 @@ export function packageJsonUpdate(packageFile: string, rootPath: string, manifes
56
56
  export function applyWrites(plans: WritePlan[]): () => void;
57
57
  export type WritePlan = {
58
58
  path: string;
59
- contents: string;
59
+ /**
60
+ * a Buffer is written byte for byte
61
+ */
62
+ contents: string | Buffer;
60
63
  createOnly: boolean;
61
64
  /**
62
65
  * bytes captured before validation;