@astryxdesign/cli 0.6.3-canary.db4e378 → 0.6.3-canary.db52d98

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 (397) hide show
  1. package/README.md +38 -15
  2. package/api/blog/blog.doc.mjs +1 -0
  3. package/api/build/_adapter.d.mts +50 -0
  4. package/api/build/_adapter.mjs +60 -0
  5. package/api/build/build.doc.mjs +14 -7
  6. package/api/build/build.test.mjs +184 -6
  7. package/api/build/build.type.d.mts +57 -2
  8. package/api/build/build.type.mjs +30 -7
  9. package/api/build/help/help.d.mts +4 -0
  10. package/api/build/help/help.mjs +24 -11
  11. package/api/build/kit/kit.d.mts +4 -1
  12. package/api/build/kit/kit.mjs +165 -49
  13. package/api/build/kit/rank.d.mts +44 -0
  14. package/api/build/kit/rank.mjs +432 -0
  15. package/api/build/kit/rank.test.mjs +196 -0
  16. package/api/component/_adapter.d.mts +6 -12
  17. package/api/component/_adapter.mjs +20 -10
  18. package/api/component/component.doc.mjs +1 -0
  19. package/api/component/component.mjs +60 -12
  20. package/api/component/list/list.mjs +3 -2
  21. package/api/discover/discover.doc.mjs +1 -0
  22. package/api/docs/_adapter.d.mts +252 -34
  23. package/api/docs/_adapter.mjs +923 -132
  24. package/api/docs/detail/detail.mjs +11 -3
  25. package/api/docs/detail/section/section.mjs +24 -13
  26. package/api/docs/detail/section/section.test.mjs +15 -6
  27. package/api/docs/docs.d.mts +5 -3
  28. package/api/docs/docs.doc.mjs +39 -17
  29. package/api/docs/docs.mjs +44 -8
  30. package/api/docs/docs.test.mjs +158 -4
  31. package/api/docs/docs.type.d.mts +181 -2
  32. package/api/docs/docs.type.mjs +117 -3
  33. package/api/docs/index/index.mjs +11 -3
  34. package/api/docs/index/index.test.mjs +1 -1
  35. package/api/docs/integration-tree.test.mjs +555 -0
  36. package/api/docs/integrationDocs.test.mjs +14 -14
  37. package/api/docs/list/list.mjs +28 -12
  38. package/api/docs/node/node.d.mts +43 -0
  39. package/api/docs/node/node.mjs +192 -0
  40. package/api/docs/reference-blocks.test.mjs +406 -0
  41. package/api/doctor/doctor.d.mts +54 -4
  42. package/api/doctor/doctor.doc.mjs +1 -0
  43. package/api/doctor/doctor.mjs +331 -16
  44. package/api/doctor/doctor.test.mjs +420 -7
  45. package/api/gap-report/gap-report.doc.mjs +1 -0
  46. package/api/hook/_adapter.mjs +19 -5
  47. package/api/hook/hook.doc.mjs +1 -0
  48. package/api/hook/list/list.d.mts +1 -1
  49. package/api/hook/list/list.mjs +69 -17
  50. package/api/index.d.mts +1 -1
  51. package/api/index.mjs +1 -0
  52. package/api/init/init.doc.mjs +2 -1
  53. package/api/integration/add-contribution.d.mts +2 -1
  54. package/api/integration/add-contribution.mjs +114 -12
  55. package/api/integration/add-contribution.test.mjs +174 -3
  56. package/api/integration/add-theme.mjs +34 -64
  57. package/api/integration/add-theme.test.mjs +105 -21
  58. package/api/integration/authoring-checks.mjs +138 -28
  59. package/api/integration/authoring-checks.test.mjs +179 -7
  60. package/api/integration/authoring-checks.type.mjs +6 -1
  61. package/api/integration/integration-authoring.type.d.mts +2 -0
  62. package/api/integration/integration-authoring.type.mjs +2 -0
  63. package/api/integration/integration-block-exports.test.mjs +10 -6
  64. package/api/integration/integrationAdd.doc.mjs +7 -0
  65. package/api/integration/integrationAddAgentDoc.doc.mjs +1 -0
  66. package/api/integration/integrationAddCodemod.doc.mjs +1 -0
  67. package/api/integration/integrationAddComponent.doc.mjs +1 -0
  68. package/api/integration/integrationAddDoc.doc.mjs +8 -1
  69. package/api/integration/integrationAddTemplate.doc.mjs +1 -0
  70. package/api/integration/integrationAddTheme.doc.mjs +6 -5
  71. package/api/integration/integrationComponentConflicts.doc.mjs +1 -0
  72. package/api/integration/integrationDocConflicts.doc.mjs +2 -1
  73. package/api/integration/integrationPackCheck.doc.mjs +2 -1
  74. package/api/integration/integrationTemplateConflicts.doc.d.mts +3 -0
  75. package/api/integration/integrationTemplateConflicts.doc.mjs +4 -0
  76. package/api/integration/pack-check.mjs +34 -0
  77. package/api/integration/pack-check.test.mjs +138 -47
  78. package/api/integration/pack-check.type.d.mts +26 -2
  79. package/api/integration/pack-check.type.mjs +14 -1
  80. package/api/integration/summarizeIssues.doc.mjs +1 -0
  81. package/api/integration/template-conflict-compatibility.test.mjs +73 -0
  82. package/api/integration/validate-integration-fixes.test.mjs +1389 -0
  83. package/api/integration/validate-integration.mjs +50 -102
  84. package/api/integration/validate-integration.test.mjs +88 -26
  85. package/api/integration/validate-unread-theme-folders.test.mjs +110 -0
  86. package/api/integration/validateIntegration.doc.mjs +2 -1
  87. package/api/json/assertResponse.doc.mjs +1 -0
  88. package/api/json/index.ts +2 -0
  89. package/api/json/isError.doc.mjs +1 -0
  90. package/api/json/parseResponse.doc.mjs +1 -0
  91. package/api/layout/_adapter.mjs +20 -5
  92. package/api/layout/layoutCheck.doc.mjs +1 -0
  93. package/api/layout/layoutExpand.doc.mjs +1 -0
  94. package/api/layout/layoutGrammar.doc.mjs +1 -0
  95. package/api/search/search.d.mts +59 -1
  96. package/api/search/search.doc.mjs +5 -3
  97. package/api/search/search.mjs +458 -77
  98. package/api/search/search.test.mjs +91 -2
  99. package/api/search/search.type.d.mts +13 -1
  100. package/api/search/search.type.mjs +4 -1
  101. package/api/swizzle/swizzle.doc.mjs +1 -0
  102. package/api/template/list/list.mjs +1 -0
  103. package/api/template/table-floating-bulk-actions.test.mjs +66 -0
  104. package/api/template/template-integration.test.mjs +1072 -3
  105. package/api/template/template-suffix.test.mjs +41 -21
  106. package/api/template/template.doc.mjs +28 -7
  107. package/api/template/template.mjs +45 -8
  108. package/api/template/template.type.d.mts +6 -8
  109. package/api/template/template.type.mjs +3 -2
  110. package/api/theme/_adapter.d.mts +2 -3
  111. package/api/theme/_adapter.mjs +4 -5
  112. package/api/theme/add/add.binary.test.mjs +10 -17
  113. package/api/theme/add/add.test.mjs +14 -1
  114. package/api/theme/generateTonalPalette.doc.mjs +1 -0
  115. package/api/theme/integration-themes.test.mjs +39 -28
  116. package/api/theme/list/list.test.mjs +19 -20
  117. package/api/theme/listThemes.doc.mjs +6 -5
  118. package/api/theme/template/template.test.mjs +5 -0
  119. package/api/theme/themeAdd.doc.mjs +4 -3
  120. package/api/theme/themeBuild.doc.mjs +1 -0
  121. package/api/theme/themeList.doc.mjs +6 -3
  122. package/api/theme/themeListAvailable.doc.mjs +5 -3
  123. package/api/theme/themePaletteGenerate.doc.mjs +1 -0
  124. package/api/theme/themeTargets.doc.mjs +1 -0
  125. package/api/theme/themeTemplate.doc.mjs +2 -1
  126. package/api/upgrade/_adapter.d.mts +32 -5
  127. package/api/upgrade/_adapter.mjs +97 -69
  128. package/api/upgrade/provider-agreement.test.mjs +152 -0
  129. package/api/upgrade/run/run.mjs +356 -59
  130. package/api/upgrade/upgrade.doc.mjs +6 -1
  131. package/api/upgrade/upgrade.type.d.mts +34 -0
  132. package/api/upgrade/upgrade.type.mjs +15 -0
  133. package/assets/codemods/__tests__/runner.test.mjs +330 -8
  134. package/assets/codemods/integration-discovery.mjs +8 -2
  135. package/assets/codemods/integration-discovery.test.mjs +15 -0
  136. package/assets/codemods/integration-runner.mjs +56 -4
  137. package/assets/codemods/integration-runner.protection.test.mjs +153 -0
  138. package/assets/codemods/run-codemod.mjs +177 -34
  139. package/assets/codemods/runner.mjs +350 -102
  140. package/assets/codemods/transforms/next/__tests__/migrate-native-picker-to-presentation.test.mjs +63 -0
  141. package/assets/codemods/transforms/next/__tests__/migrate-theme-catalog-to-descriptors.test.mjs +220 -0
  142. package/assets/codemods/transforms/next/index.mjs +19 -1
  143. package/assets/codemods/transforms/next/migrate-native-picker-to-presentation.mjs +148 -0
  144. package/assets/codemods/transforms/next/migrate-theme-catalog-to-descriptors.mjs +141 -0
  145. package/assets/docs/getting-started.doc.mjs +2 -2
  146. package/assets/docs/layout.doc.dense.mjs +2 -2
  147. package/assets/docs/layout.doc.mjs +1 -1
  148. package/assets/docs/principles.doc.mjs +6 -6
  149. package/assets/docs/styling-libraries.doc.mjs +3 -3
  150. package/assets/docs/styling.doc.mjs +4 -4
  151. package/assets/docs/theme.doc.mjs +5 -5
  152. package/assets/docs/tokens.doc.mjs +1 -1
  153. package/assets/docs/tree/api.doc.mjs +30 -0
  154. package/assets/docs/tree/cli.doc.mjs +23 -0
  155. package/assets/docs/tree/commands.doc.mjs +25 -0
  156. package/assets/docs/{cli-integrations.doc.mjs → tree/integrations.doc.mjs} +66 -38
  157. package/assets/docs/tree/integrations.test.mjs +62 -0
  158. package/assets/docs/tree/writing-docs.doc.mjs +286 -0
  159. package/assets/docs/working-with-ai.doc.mjs +3 -3
  160. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.doc.mjs +1 -1
  161. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.tsx +1 -3
  162. package/assets/templates/blocks/components/DateInput/DateInputDateRange.tsx +1 -1
  163. package/assets/templates/blocks/components/Item/ItemDocumentTabs.doc.mjs +14 -0
  164. package/assets/templates/blocks/components/Item/ItemDocumentTabs.tsx +100 -0
  165. package/assets/templates/blocks/components/Table/TableBulkActionsTable.doc.mjs +14 -0
  166. package/assets/templates/blocks/components/Table/TableBulkActionsTable.tsx +71 -0
  167. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.doc.mjs +19 -0
  168. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.tsx +139 -0
  169. package/assets/templates/blocks/components/TimeInput/TimeInputConstrained.tsx +1 -0
  170. package/assets/templates/themes/butter/butterTheme.doc.mjs +11 -0
  171. package/assets/templates/themes/chocolate/chocolateTheme.doc.mjs +11 -0
  172. package/assets/templates/themes/gothic/gothicTheme.doc.mjs +11 -0
  173. package/assets/templates/themes/matcha/matchaTheme.doc.mjs +11 -0
  174. package/assets/templates/themes/neutral/neutralTheme.doc.mjs +11 -0
  175. package/assets/templates/themes/stone/stoneTheme.doc.mjs +11 -0
  176. package/assets/templates/themes/y2k/y2kTheme.doc.mjs +11 -0
  177. package/authoring/codemod/codemod.doc.mjs +1 -1
  178. package/authoring/codemod/type.ts +12 -0
  179. package/authoring/config/config.doc.mjs +1 -1
  180. package/authoring/debug/debug.doc.d.mts +11 -0
  181. package/authoring/debug/debug.doc.mjs +182 -0
  182. package/authoring/debug/parse.d.mts +3 -3
  183. package/authoring/doctypes/_schema.d.mts +119 -117
  184. package/authoring/doctypes/_schema.mjs +54 -3
  185. package/authoring/doctypes/base/graph-fields.doc.mjs +8 -6
  186. package/authoring/doctypes/base/type.ts +6 -5
  187. package/authoring/doctypes/command/command.doc.mjs +1 -1
  188. package/authoring/doctypes/command/type.ts +2 -2
  189. package/authoring/doctypes/component/type.ts +2 -2
  190. package/authoring/doctypes/doctypes-new.test.mjs +48 -6
  191. package/authoring/doctypes/enum/enum.doc.mjs +1 -1
  192. package/authoring/doctypes/enum/type.ts +1 -1
  193. package/authoring/doctypes/function/function.doc.mjs +3 -2
  194. package/authoring/doctypes/function/type.ts +3 -2
  195. package/authoring/doctypes/hook/type.ts +2 -2
  196. package/authoring/doctypes/load-contract.test.mjs +28 -2
  197. package/authoring/doctypes/namespace/namespace.doc.mjs +6 -10
  198. package/authoring/doctypes/namespace/parse.test.mjs +23 -25
  199. package/authoring/doctypes/namespace/type.ts +5 -2
  200. package/authoring/doctypes/parse.d.mts +4 -2
  201. package/authoring/doctypes/parse.mjs +10 -5
  202. package/authoring/doctypes/reference/reference.doc.mjs +35 -6
  203. package/authoring/doctypes/reference/type.ts +30 -13
  204. package/authoring/doctypes/schema/schema.doc.mjs +1 -1
  205. package/authoring/doctypes/schema/type.ts +1 -2
  206. package/authoring/doctypes/template/parse.d.mts +2 -0
  207. package/authoring/doctypes/template/parse.mjs +4 -0
  208. package/authoring/doctypes/template/parse.test.mjs +18 -0
  209. package/authoring/doctypes/template/template.doc.mjs +9 -3
  210. package/authoring/doctypes/template/type.ts +8 -0
  211. package/authoring/doctypes/theme/parse.d.mts +35 -0
  212. package/authoring/doctypes/theme/parse.mjs +76 -0
  213. package/authoring/doctypes/theme/theme.doc.d.mts +9 -0
  214. package/authoring/doctypes/theme/theme.doc.mjs +79 -0
  215. package/authoring/doctypes/theme/type.ts +42 -0
  216. package/authoring/doctypes/types.ts +2 -1
  217. package/authoring/gap-report/gap-report.doc.d.mts +12 -0
  218. package/authoring/gap-report/gap-report.doc.mjs +183 -0
  219. package/authoring/identity/identity.doc.mjs +2 -2
  220. package/authoring/index.d.mts +1 -0
  221. package/authoring/index.d.ts +7 -4
  222. package/authoring/index.mjs +2 -1
  223. package/authoring/integration/integration.doc.mjs +2 -2
  224. package/authoring/integration/type.ts +4 -10
  225. package/clients/cli/__tests__/cliManifest.test.ts +27 -29
  226. package/clients/cli/commands/blog.doc.mjs +1 -1
  227. package/clients/cli/commands/build-theme.adaptations.test.mjs +100 -1
  228. package/clients/cli/commands/build-theme.mjs +11 -45
  229. package/clients/cli/commands/build.doc.mjs +12 -7
  230. package/clients/cli/commands/build.mjs +105 -67
  231. package/clients/cli/commands/build.text-fields.test.mjs +41 -0
  232. package/clients/cli/commands/component/index.mjs +1 -1
  233. package/clients/cli/commands/component-ownership.test.mjs +3 -3
  234. package/clients/cli/commands/component.doc.mjs +1 -1
  235. package/clients/cli/commands/discover.broken-integration.test.mjs +7 -8
  236. package/clients/cli/commands/discover.doc.mjs +1 -1
  237. package/clients/cli/commands/discover.mjs +1 -1
  238. package/clients/cli/commands/docs.doc.mjs +21 -9
  239. package/clients/cli/commands/docs.mjs +184 -70
  240. package/clients/cli/commands/docs.test.mjs +120 -16
  241. package/clients/cli/commands/doctor-integration-components.doc.mjs +1 -1
  242. package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -3
  243. package/clients/cli/commands/doctor-integration-templates.doc.mjs +15 -8
  244. package/clients/cli/commands/doctor-integration-validate.doc.mjs +1 -1
  245. package/clients/cli/commands/doctor-integration.doc.mjs +1 -1
  246. package/clients/cli/commands/doctor-integration.test.mjs +90 -8
  247. package/clients/cli/commands/doctor.doc.mjs +1 -1
  248. package/clients/cli/commands/doctor.mjs +53 -16
  249. package/clients/cli/commands/gap-report.doc.mjs +1 -1
  250. package/clients/cli/commands/hook.doc.mjs +1 -1
  251. package/clients/cli/commands/init.doc.mjs +1 -1
  252. package/clients/cli/commands/integration-add.controls.test.mjs +1 -1
  253. package/clients/cli/commands/integration-add.doc.mjs +11 -1
  254. package/clients/cli/commands/integration-pack.doc.mjs +1 -1
  255. package/clients/cli/commands/integration-real-world.test.mjs +3 -9
  256. package/clients/cli/commands/integration.doc.mjs +1 -1
  257. package/clients/cli/commands/integration.mjs +1 -0
  258. package/clients/cli/commands/layout-check.doc.mjs +1 -1
  259. package/clients/cli/commands/layout-expand.doc.mjs +1 -1
  260. package/clients/cli/commands/layout-grammar.doc.mjs +1 -1
  261. package/clients/cli/commands/layout.doc.mjs +1 -1
  262. package/clients/cli/commands/manifest.doc.mjs +1 -1
  263. package/clients/cli/commands/search.doc.mjs +1 -1
  264. package/clients/cli/commands/search.mjs +11 -2
  265. package/clients/cli/commands/setup-nudge.test.mjs +6 -0
  266. package/clients/cli/commands/swizzle.doc.mjs +1 -1
  267. package/clients/cli/commands/template.doc.mjs +24 -7
  268. package/clients/cli/commands/template.mjs +4 -91
  269. package/clients/cli/commands/text-json-parity.test.mjs +719 -0
  270. package/clients/cli/commands/theme-add.doc.mjs +2 -2
  271. package/clients/cli/commands/theme-build.doc.mjs +1 -1
  272. package/clients/cli/commands/theme-list.doc.mjs +2 -2
  273. package/clients/cli/commands/theme-palette-generate.doc.mjs +3 -3
  274. package/clients/cli/commands/theme-palette.doc.mjs +1 -1
  275. package/clients/cli/commands/theme-targets.behavior.test.mjs +4 -3
  276. package/clients/cli/commands/theme-targets.doc.mjs +1 -1
  277. package/clients/cli/commands/theme-template.doc.mjs +1 -1
  278. package/clients/cli/commands/theme.doc.mjs +1 -1
  279. package/clients/cli/commands/upgrade.doc.mjs +3 -2
  280. package/clients/cli/commands/upgrade.file-protection.test.mjs +228 -0
  281. package/clients/cli/commands/upgrade.mjs +29 -7
  282. package/clients/cli/formatters/index.mjs +2 -0
  283. package/clients/cli/formatters/index.test.mjs +6 -0
  284. package/clients/cli/index.mjs +8 -0
  285. package/clients/cli/lib/hook-format.mjs +14 -5
  286. package/clients/cli/lib/manifest.mjs +9 -2
  287. package/foundation/agent-docs/agent-docs.d.mts +3 -2
  288. package/foundation/agent-docs/agent-docs.mjs +13 -9
  289. package/foundation/agent-docs/agent-docs.test.mjs +19 -1
  290. package/foundation/config/project-themes.test.mjs +11 -19
  291. package/foundation/config/project.d.mts +20 -11
  292. package/foundation/config/project.mjs +123 -80
  293. package/foundation/config/project.test.mjs +144 -21
  294. package/foundation/discovery/authoring-self-docs.d.mts +18 -0
  295. package/foundation/discovery/authoring-self-docs.mjs +37 -15
  296. package/foundation/discovery/authoring-self-docs.test.mjs +25 -9
  297. package/foundation/discovery/authoring-surface.d.mts +74 -0
  298. package/foundation/discovery/authoring-surface.mjs +525 -0
  299. package/foundation/discovery/authoring-surface.test.mjs +392 -0
  300. package/foundation/discovery/cli-self-docs.d.mts +119 -0
  301. package/foundation/discovery/cli-self-docs.mjs +490 -0
  302. package/foundation/discovery/cli-self-docs.test.mjs +375 -0
  303. package/foundation/discovery/component-discovery.d.mts +38 -0
  304. package/foundation/discovery/component-discovery.mjs +48 -0
  305. package/foundation/discovery/component-loader.d.mts +35 -38
  306. package/foundation/discovery/component-loader.mjs +53 -222
  307. package/foundation/discovery/docs-discovery.d.mts +112 -11
  308. package/foundation/discovery/docs-discovery.mjs +231 -36
  309. package/foundation/discovery/docs-discovery.test.mjs +111 -32
  310. package/foundation/discovery/docs-output-budget.d.mts +2 -2
  311. package/foundation/discovery/docs-output-budget.mjs +1 -1
  312. package/foundation/discovery/docs-section-key.d.mts +28 -10
  313. package/foundation/discovery/docs-section-key.mjs +134 -33
  314. package/foundation/discovery/docs-section-key.test.mjs +41 -19
  315. package/foundation/discovery/template-adapter.d.mts +99 -6
  316. package/foundation/discovery/template-adapter.mjs +480 -57
  317. package/foundation/discovery/template-adapter.test.mjs +57 -0
  318. package/foundation/discovery/template-conflict-release.d.mts +13 -0
  319. package/foundation/discovery/template-conflict-release.mjs +40 -0
  320. package/foundation/discovery/template-conflict-release.test.mjs +40 -0
  321. package/foundation/discovery/theme-discovery.d.mts +67 -7
  322. package/foundation/discovery/theme-discovery.mjs +916 -186
  323. package/foundation/discovery/theme-discovery.test.mjs +613 -219
  324. package/foundation/doc-compiler/bundle.d.mts +47 -0
  325. package/foundation/doc-compiler/bundle.mjs +278 -0
  326. package/foundation/doc-compiler/bundle.test.mjs +266 -0
  327. package/foundation/doc-compiler/compile.d.mts +220 -39
  328. package/foundation/doc-compiler/compile.mjs +311 -15
  329. package/foundation/doc-compiler/diagnostics.d.mts +126 -0
  330. package/foundation/doc-compiler/diagnostics.mjs +305 -0
  331. package/foundation/doc-compiler/doc-compiler.test.mjs +28 -1
  332. package/foundation/doc-compiler/doc-loads.test.mjs +1642 -0
  333. package/foundation/doc-compiler/import.d.mts +24 -0
  334. package/foundation/doc-compiler/import.mjs +59 -0
  335. package/foundation/doc-compiler/inputs.d.mts +102 -0
  336. package/foundation/doc-compiler/inputs.mjs +291 -0
  337. package/foundation/doc-compiler/inputs.test.mjs +299 -0
  338. package/foundation/doc-compiler/ir.d.mts +13 -0
  339. package/foundation/doc-compiler/ir.mjs +196 -12
  340. package/foundation/doc-compiler/lenses.d.mts +8 -5
  341. package/foundation/doc-compiler/lenses.mjs +50 -4
  342. package/foundation/doc-compiler/links.d.mts +162 -0
  343. package/foundation/doc-compiler/links.mjs +294 -0
  344. package/foundation/doc-compiler/links.test.mjs +192 -0
  345. package/foundation/doc-compiler/lower-doc.test.mjs +495 -0
  346. package/foundation/doc-compiler/overlays.d.mts +37 -0
  347. package/foundation/doc-compiler/overlays.mjs +206 -0
  348. package/foundation/doc-compiler/parse-readable.d.mts +9 -0
  349. package/foundation/doc-compiler/parse-readable.mjs +29 -0
  350. package/foundation/doc-compiler/read.d.mts +127 -0
  351. package/foundation/doc-compiler/read.mjs +325 -0
  352. package/foundation/doc-compiler/read.test.mjs +313 -0
  353. package/foundation/doc-compiler/source.d.mts +33 -0
  354. package/foundation/doc-compiler/source.mjs +128 -0
  355. package/foundation/doc-compiler/tree.d.mts +288 -0
  356. package/foundation/doc-compiler/tree.mjs +876 -0
  357. package/foundation/doc-compiler/tree.test.mjs +598 -0
  358. package/foundation/fs/file-protection.d.mts +33 -0
  359. package/foundation/fs/file-protection.mjs +825 -0
  360. package/foundation/fs/file-protection.test.mjs +250 -0
  361. package/foundation/integrations/autolink.d.mts +58 -1
  362. package/foundation/integrations/autolink.mjs +143 -57
  363. package/foundation/integrations/autolink.test.mjs +1 -1
  364. package/foundation/integrations/cli-requirement.d.mts +45 -0
  365. package/foundation/integrations/cli-requirement.mjs +154 -0
  366. package/foundation/integrations/cli-requirement.test.mjs +84 -0
  367. package/foundation/integrations/contribution-fixes.d.mts +145 -0
  368. package/foundation/integrations/contribution-fixes.mjs +1284 -0
  369. package/foundation/integrations/contribution-inventory.d.mts +3 -2
  370. package/foundation/integrations/contribution-inventory.mjs +27 -24
  371. package/foundation/integrations/contribution-inventory.test.mjs +86 -27
  372. package/foundation/integrations/integration-warnings.d.mts +9 -2
  373. package/foundation/integrations/integration-warnings.mjs +51 -26
  374. package/foundation/integrations/integration-warnings.test.mjs +74 -1
  375. package/foundation/integrations/integrations.d.mts +3 -0
  376. package/foundation/integrations/integrations.mjs +11 -97
  377. package/foundation/integrations/provider-ledger.test.mjs +275 -0
  378. package/foundation/integrations/provider-resolution.d.mts +152 -0
  379. package/foundation/integrations/provider-resolution.mjs +576 -0
  380. package/foundation/integrations/provider-resolution.test.mjs +369 -0
  381. package/foundation/integrations/theme-descriptor.d.mts +8 -0
  382. package/foundation/integrations/theme-descriptor.mjs +44 -0
  383. package/foundation/integrations/validate-contributions.mjs +121 -29
  384. package/foundation/response/error-codes.d.mts +3 -1
  385. package/foundation/response/error-codes.d.ts +2 -0
  386. package/foundation/response/error-codes.doc.mjs +12 -2
  387. package/foundation/response/error-codes.mjs +7 -1
  388. package/foundation/response/error-codes.test.mjs +55 -11
  389. package/foundation/response/response-types.doc.d.mts +5 -1
  390. package/foundation/response/response-types.doc.mjs +20 -11
  391. package/foundation/response/response.doc.mjs +1 -1
  392. package/foundation/text/string-utils.d.mts +8 -0
  393. package/foundation/text/string-utils.mjs +40 -10
  394. package/foundation/xle/expand.mjs +1 -1
  395. package/foundation/xle/xle.test.mjs +13 -0
  396. package/package.json +10 -9
  397. package/assets/templates/themes/manifest.json +0 -95
@@ -13,7 +13,10 @@
13
13
  * @position api/hook/list/list.mjs — dispatched from ../hook.mjs
14
14
  */
15
15
 
16
- import {discoverHooks, findHookDoc} from '../../../foundation/discovery/hook-discovery.mjs';
16
+ import {
17
+ discoverHooks,
18
+ findHookDoc,
19
+ } from '../../../foundation/discovery/hook-discovery.mjs';
17
20
  import {loadDocs} from '../../../foundation/discovery/component-loader.mjs';
18
21
  import {AstryxError} from '../../error.mjs';
19
22
  import {ERROR_CODES} from '../../../foundation/response/error-codes.mjs';
@@ -28,7 +31,13 @@ import {resolveCoreDir} from '../_adapter.mjs';
28
31
  * @param {string|null} [options.lang]
29
32
  * @returns {Promise<import('../hook.type.mjs').HookListResponse>}
30
33
  */
31
- export async function list({cwd = process.cwd(), category, detail = 'names', zh = false, lang = null} = {}) {
34
+ export async function list({
35
+ cwd = process.cwd(),
36
+ category,
37
+ detail = 'names',
38
+ zh = false,
39
+ lang = null,
40
+ } = {}) {
32
41
  const coreDir = resolveCoreDir(cwd);
33
42
  const hooks = discoverHooks(coreDir);
34
43
 
@@ -51,20 +60,31 @@ export async function list({cwd = process.cwd(), category, detail = 'names', zh
51
60
  const docPath = findHookDoc(coreDir, hookName);
52
61
  if (docPath) {
53
62
  try {
54
- const docs = await loadDocs(docPath, /** @type {{zh?: boolean, dense?: boolean, lang?: string}} */ ({zh, lang}));
63
+ const docs = await loadDocs(docPath, {zh, lang, root: 'hooks'});
55
64
  entries.push({
56
65
  name: hookName,
57
66
  description: docs.usage?.description || '',
58
67
  import: docs.importPath || '@astryxdesign/core/hooks',
59
68
  });
60
69
  } catch {
61
- entries.push({name: hookName, description: '', import: '@astryxdesign/core/hooks'});
70
+ entries.push({
71
+ name: hookName,
72
+ description: '',
73
+ import: '@astryxdesign/core/hooks',
74
+ });
62
75
  }
63
76
  } else {
64
- entries.push({name: hookName, description: '', import: '@astryxdesign/core/hooks'});
77
+ entries.push({
78
+ name: hookName,
79
+ description: '',
80
+ import: '@astryxdesign/core/hooks',
81
+ });
65
82
  }
66
83
  }
67
- return {type: 'hook.list', data: {detail: 'compact', components: {[match[0]]: entries}}};
84
+ return {
85
+ type: 'hook.list',
86
+ data: {detail: 'compact', components: {[match[0]]: entries}},
87
+ };
68
88
  }
69
89
 
70
90
  if (detail === 'full') {
@@ -74,19 +94,33 @@ export async function list({cwd = process.cwd(), category, detail = 'names', zh
74
94
  const docPath = findHookDoc(coreDir, hookName);
75
95
  if (docPath) {
76
96
  try {
77
- entries.push(await loadDocs(docPath, /** @type {{zh?: boolean, dense?: boolean, lang?: string}} */ ({zh, lang})));
97
+ entries.push(await loadDocs(docPath, {zh, lang, root: 'hooks'}));
78
98
  } catch {
79
- entries.push(/** @type {import('../hook.type.mjs').HookDoc} */ ({name: hookName}));
99
+ entries.push(
100
+ /** @type {import('../hook.type.mjs').HookDoc} */ ({
101
+ name: hookName,
102
+ }),
103
+ );
80
104
  }
81
105
  } else {
82
- entries.push(/** @type {import('../hook.type.mjs').HookDoc} */ ({name: hookName}));
106
+ entries.push(
107
+ /** @type {import('../hook.type.mjs').HookDoc} */ ({
108
+ name: hookName,
109
+ }),
110
+ );
83
111
  }
84
112
  }
85
- return {type: 'hook.list', data: {detail: 'full', components: {[match[0]]: entries}}};
113
+ return {
114
+ type: 'hook.list',
115
+ data: {detail: 'full', components: {[match[0]]: entries}},
116
+ };
86
117
  }
87
118
 
88
119
  // Default: names only
89
- return {type: 'hook.list', data: {detail: 'names', components: {[match[0]]: match[1]}}};
120
+ return {
121
+ type: 'hook.list',
122
+ data: {detail: 'names', components: {[match[0]]: match[1]}},
123
+ };
90
124
  }
91
125
 
92
126
  // All hooks
@@ -99,17 +133,25 @@ export async function list({cwd = process.cwd(), category, detail = 'names', zh
99
133
  const docPath = findHookDoc(coreDir, hookName);
100
134
  if (docPath) {
101
135
  try {
102
- const docs = await loadDocs(docPath, /** @type {{zh?: boolean, dense?: boolean, lang?: string}} */ ({zh, lang}));
136
+ const docs = await loadDocs(docPath, {zh, lang, root: 'hooks'});
103
137
  result[cat].push({
104
138
  name: hookName,
105
139
  description: docs.usage?.description || '',
106
140
  import: docs.importPath || '@astryxdesign/core/hooks',
107
141
  });
108
142
  } catch {
109
- result[cat].push({name: hookName, description: '', import: '@astryxdesign/core/hooks'});
143
+ result[cat].push({
144
+ name: hookName,
145
+ description: '',
146
+ import: '@astryxdesign/core/hooks',
147
+ });
110
148
  }
111
149
  } else {
112
- result[cat].push({name: hookName, description: '', import: '@astryxdesign/core/hooks'});
150
+ result[cat].push({
151
+ name: hookName,
152
+ description: '',
153
+ import: '@astryxdesign/core/hooks',
154
+ });
113
155
  }
114
156
  }
115
157
  }
@@ -125,12 +167,22 @@ export async function list({cwd = process.cwd(), category, detail = 'names', zh
125
167
  const docPath = findHookDoc(coreDir, hookName);
126
168
  if (docPath) {
127
169
  try {
128
- result[cat].push(await loadDocs(docPath, /** @type {{zh?: boolean, dense?: boolean, lang?: string}} */ ({zh, lang})));
170
+ result[cat].push(
171
+ await loadDocs(docPath, {zh, lang, root: 'hooks'}),
172
+ );
129
173
  } catch {
130
- result[cat].push(/** @type {import('../hook.type.mjs').HookDoc} */ ({name: hookName}));
174
+ result[cat].push(
175
+ /** @type {import('../hook.type.mjs').HookDoc} */ ({
176
+ name: hookName,
177
+ }),
178
+ );
131
179
  }
132
180
  } else {
133
- result[cat].push(/** @type {import('../hook.type.mjs').HookDoc} */ ({name: hookName}));
181
+ result[cat].push(
182
+ /** @type {import('../hook.type.mjs').HookDoc} */ ({
183
+ name: hookName,
184
+ }),
185
+ );
134
186
  }
135
187
  }
136
188
  }
package/api/index.d.mts CHANGED
@@ -38,7 +38,7 @@ export * from "./integration/pack-check.type.mjs";
38
38
  export * from "./integration/validate-integration.type.mjs";
39
39
  export * from "./integration/authoring-checks.type.mjs";
40
40
  export type Logger = import("./logger.mjs").Logger;
41
- export { themeBuild, themeAdd, themeList, themeListAvailable, themeTargets, themePaletteGenerate, generateTonalPalette, listThemes } from "./theme/theme.mjs";
41
+ export { themeBuild, themeAdd, themeTemplate, themeList, themeListAvailable, themeTargets, themePaletteGenerate, generateTonalPalette, listThemes } from "./theme/theme.mjs";
42
42
  export { layoutExpand, layoutCheck, layoutGrammar } from "./layout/layout.mjs";
43
43
  export { integrationAdd, integrationAddAgentDoc, integrationAddCodemod, integrationAddComponent, integrationAddDoc, integrationAddTemplate } from "./integration/add-contribution.mjs";
44
44
  export { validateIntegration, summarizeIssues } from "./integration/validate-integration.mjs";
package/api/index.mjs CHANGED
@@ -28,6 +28,7 @@ export {template} from './template/template.mjs';
28
28
  export {
29
29
  themeBuild,
30
30
  themeAdd,
31
+ themeTemplate,
31
32
  themeList,
32
33
  themeListAvailable,
33
34
  themeTargets,
@@ -11,6 +11,7 @@ export const doc = {
11
11
  type: 'function',
12
12
  kind: 'api',
13
13
  name: 'init',
14
+ namespace: 'cli/api',
14
15
  displayName: 'init()',
15
16
  summary:
16
17
  'Non-interactive project setup: install agent docs and point at the theme + build workflows.',
@@ -101,5 +102,5 @@ export const doc = {
101
102
  {label: 'Remove agent docs', code: 'await init({removeAgents: true});'},
102
103
  ],
103
104
  command: 'init',
104
- related: ['doctor', 'upgrade', 'build', 'theme'],
105
+ related: ['doctor', 'upgrade', 'build', 'themeTemplate'],
105
106
  };
@@ -31,7 +31,7 @@ export function integrationAddAgentDoc(line: string, options?: import("./integra
31
31
  *
32
32
  * @param {'component'|'doc'|'template'|'codemod'|'agent-doc'|'theme'} kind
33
33
  * @param {string} name
34
- * @param {{cwd?: string, dryRun?: boolean, templateType?: 'page'|'block', to?: string, replaces?: string, extends?: string}} [options]
34
+ * @param {{cwd?: string, dryRun?: boolean, templateType?: 'page'|'block', to?: string, replaces?: string, extends?: string, parent?: string}} [options]
35
35
  * @returns {Promise<import('./integration-authoring.type.mjs').IntegrationAddResponse>}
36
36
  */
37
37
  export function integrationAdd(kind: "component" | "doc" | "template" | "codemod" | "agent-doc" | "theme", name: string, options?: {
@@ -41,4 +41,5 @@ export function integrationAdd(kind: "component" | "doc" | "template" | "codemod
41
41
  to?: string;
42
42
  replaces?: string;
43
43
  extends?: string;
44
+ parent?: string;
44
45
  }): Promise<import("./integration-authoring.type.mjs").IntegrationAddResponse>;
@@ -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,11 @@ 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';
38
42
  import {findIntegrationComponentDoc} from '../../foundation/discovery/component-discovery.mjs';
39
43
  import {parseAgentDocsField} from '../../authoring/integration/schema.mjs';
40
44
  import {integrationAddTheme} from './add-theme.mjs';
@@ -84,6 +88,17 @@ function resolvePackage(cwd) {
84
88
  }
85
89
  const owner = typeof pkg.name === 'string' ? pkg.name : '(local integration)';
86
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
+ }
87
102
  const manifestFile =
88
103
  existingManifest ?? path.join(packageDir, 'astryx.integration.mjs');
89
104
  return {
@@ -263,7 +278,7 @@ async function addComponent(name, options) {
263
278
  const sourcePath = projectPath(path.relative(packageDir, sourceFile));
264
279
  const extensionlessPath = sourcePath.replace(/\.tsx?$/u, '');
265
280
  const importSpecifier = `${owner}/${extensionlessPath}`;
266
- const docContents = `export default {\n type: 'component',\n name: '${name}',\n import: ${JSON.stringify(importSpecifier)},\n description: '${name} component.',\n props: [],\n};\n`;
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`;
267
282
  const sourceContents = `export function ${name}() {\n return <div>${name}</div>;\n}\n`;
268
283
 
269
284
  /** @type {import('./add-helpers.mjs').WritePlan[]} */
@@ -354,7 +369,7 @@ async function addComponent(name, options) {
354
369
 
355
370
  /**
356
371
  * @param {string} name topic name (must match [\w-]+)
357
- * @param {{cwd?: string, dryRun?: boolean, replaces?: string, extends?: string}} options
372
+ * @param {{cwd?: string, dryRun?: boolean, replaces?: string, extends?: string, parent?: string}} options
358
373
  * @returns {Promise<import('./integration-authoring.type.mjs').IntegrationAddResponse>}
359
374
  */
360
375
  async function addDoc(name, options) {
@@ -373,6 +388,20 @@ async function addDoc(name, options) {
373
388
  ERROR_CODES.ERR_INVALID_ARGUMENT,
374
389
  );
375
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
+ }
376
405
  for (const [option, value] of [
377
406
  ['--replaces', options.replaces],
378
407
  ['--extends', options.extends],
@@ -433,20 +462,92 @@ async function addDoc(name, options) {
433
462
  }
434
463
 
435
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
+ }
436
512
  const relationship = options.replaces
437
513
  ? `\n replaces: '${options.replaces}',`
438
514
  : options.extends
439
515
  ? `\n extends: '${options.extends}',`
440
- : '';
441
- 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`;
442
523
 
443
524
  /** @type {import('./add-helpers.mjs').WritePlan[]} */
444
525
  const plans = [{path: docFile, contents: docContents, createOnly: true}];
445
- const pkgUpdate = packageJsonUpdate(
526
+ if (namespaceFile != null && writeNamespace) {
527
+ plans.push({path: namespaceFile, contents: namespaceContents, createOnly: true});
528
+ }
529
+ let pkgUpdate = packageJsonUpdate(
446
530
  packageFile,
447
531
  rootPath,
448
532
  path.basename(manifestFile),
449
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
+ }
450
551
  if (pkgUpdate != null) {
451
552
  plans.push({
452
553
  path: packageFile,
@@ -565,8 +666,8 @@ async function addTemplate(name, options) {
565
666
  throw error;
566
667
  }
567
668
 
568
- const specFile = assertWithin(`${name}.template.mjs`, root, {
569
- label: 'template spec',
669
+ const specFile = assertWithin(`${name}.doc.mjs`, root, {
670
+ label: 'template descriptor',
570
671
  });
571
672
  const sourceFile = assertWithin(`${name}.tsx`, root, {
572
673
  label: 'template source',
@@ -589,7 +690,8 @@ async function addTemplate(name, options) {
589
690
  const pascalName = kebabToPascal(name);
590
691
  const sourcePath = projectPath(path.relative(packageDir, sourceFile));
591
692
  const extensionlessPath = sourcePath.replace(/\.tsx?$/u, '');
592
- const specContents = `export default {\n type: '${templateType}',\n name: '${name}',\n description: '${kebabToTitle(name)} template.',\n};\n`;
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`;
593
695
  const sourceContents = `export default function ${pascalName}() {\n return <div>${kebabToTitle(name)}</div>;\n}\n`;
594
696
 
595
697
  /** @type {import('./add-helpers.mjs').WritePlan[]} */
@@ -1105,7 +1207,7 @@ export function integrationAddAgentDoc(line, options = {}) {
1105
1207
 
1106
1208
  const KIND_OPTIONS = {
1107
1209
  component: new Set(['cwd', 'dryRun']),
1108
- doc: new Set(['cwd', 'dryRun', 'replaces', 'extends']),
1210
+ doc: new Set(['cwd', 'dryRun', 'replaces', 'extends', 'parent']),
1109
1211
  template: new Set(['cwd', 'dryRun', 'templateType']),
1110
1212
  codemod: new Set(['cwd', 'dryRun', 'to']),
1111
1213
  'agent-doc': new Set(['cwd', 'dryRun']),
@@ -1136,7 +1238,7 @@ function validateKindOptions(kind, options) {
1136
1238
  *
1137
1239
  * @param {'component'|'doc'|'template'|'codemod'|'agent-doc'|'theme'} kind
1138
1240
  * @param {string} name
1139
- * @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]
1140
1242
  * @returns {Promise<import('./integration-authoring.type.mjs').IntegrationAddResponse>}
1141
1243
  */
1142
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: []');
@@ -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
 
@@ -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 () => {