@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,7 +5,8 @@
5
5
  *
6
6
  * Hosts the atomic staged-write transaction, package.json files-array
7
7
  * maintenance, package-dir resolution, and project-path normalization that
8
- * every `integration add <kind>` command needs. Stateless and side-effect-free
8
+ * every `integration add <kind>` command needs. `theme add` and `theme build`
9
+ * write through the same transaction. Stateless and side-effect-free
9
10
  * outside of {@link applyWrites}.
10
11
  */
11
12
 
@@ -40,7 +41,7 @@ export function findPackageDir(startDir) {
40
41
  /**
41
42
  * @typedef {object} WritePlan
42
43
  * @property {string} path
43
- * @property {string} contents
44
+ * @property {string | Buffer} contents a Buffer is written byte for byte
44
45
  * @property {boolean} createOnly
45
46
  * @property {Buffer} [expectedOriginal] bytes captured before validation;
46
47
  * a different current file is a concurrent edit and must never be overwritten
@@ -192,21 +193,29 @@ export function packageJsonUpdate(
192
193
 
193
194
  // ── Atomic staged-write transaction ─────────────────────────────────
194
195
 
195
- /** @param {string} file */
196
+ /**
197
+ * @param {string} file
198
+ * @returns {boolean} false when the file is still there
199
+ */
196
200
  function removeTemporary(file) {
197
201
  try {
198
202
  fs.rmSync(file, {force: true});
203
+ return true;
199
204
  } catch {
200
205
  // Best effort. The transaction error remains the actionable failure.
206
+ return false;
201
207
  }
202
208
  }
203
209
 
204
210
  /**
205
211
  * Restore writes that already published. Best-effort so callers preserve
206
- * the original actionable error.
212
+ * the original actionable error; returns every path it could not put back.
207
213
  * @param {Array<WritePlan & {temporary: string, original: Buffer|null, mode: number}>} published
214
+ * @returns {string[]}
208
215
  */
209
216
  function rollbackWrites(published) {
217
+ /** @type {string[]} */
218
+ const unrestored = [];
210
219
  for (const plan of [...published].reverse()) {
211
220
  let restore = null;
212
221
  try {
@@ -225,12 +234,18 @@ function rollbackWrites(published) {
225
234
  fs.renameSync(restore, plan.path);
226
235
  restore = null;
227
236
  }
228
- } catch {
229
- // Best effort. A concurrent edit belongs to its writer, not this rollback.
237
+ } catch (error) {
238
+ // A created file that is already gone needs nothing. Any other failure
239
+ // leaves this call's bytes, or no bytes, where the original was.
240
+ const gone =
241
+ error instanceof Error &&
242
+ /** @type {NodeJS.ErrnoException} */ (error).code === 'ENOENT';
243
+ if (!(gone && plan.original == null)) unrestored.push(plan.path);
230
244
  } finally {
231
245
  if (restore != null) removeTemporary(restore);
232
246
  }
233
247
  }
248
+ return unrestored;
234
249
  }
235
250
 
236
251
  /** @param {string} file */
@@ -325,10 +340,22 @@ export function applyWrites(plans) {
325
340
  }
326
341
  published.push(plan);
327
342
  }
328
- return () => rollbackWrites(published);
343
+ return () => {
344
+ rollbackWrites(published);
345
+ };
329
346
  } catch (error) {
330
- for (const plan of staged) removeTemporary(plan.temporary);
331
- rollbackWrites(published);
347
+ const leftovers = staged
348
+ .filter(plan => !removeTemporary(plan.temporary))
349
+ .map(plan => plan.temporary);
350
+ const unrestored = rollbackWrites(published);
351
+ if (error instanceof Error) {
352
+ if (unrestored.length > 0) {
353
+ error.message += ` Could not restore: ${unrestored.join(', ')}.`;
354
+ }
355
+ if (leftovers.length > 0) {
356
+ error.message += ` Could not remove temporary files: ${leftovers.join(', ')}.`;
357
+ }
358
+ }
332
359
  throw error;
333
360
  }
334
361
  }
@@ -1,9 +1,9 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
 
3
3
  /**
4
- * @file `astryx integration add theme` — scaffold one valid source theme into
5
- * an integration package, update its theme catalog, and declare the root on
6
- * first use.
4
+ * @file `astryx integration add theme` — scaffold one strongly typed,
5
+ * same-stem source/descriptor pair into an integration package and declare the
6
+ * themes root on first use.
7
7
  */
8
8
 
9
9
  import * as fs from 'node:fs';
@@ -15,16 +15,14 @@ import {
15
15
  PathSafetyError,
16
16
  sanitizeName,
17
17
  } from '../../foundation/fs/path-safety.mjs';
18
- import {
19
- discoverThemeCatalog,
20
- THEME_MANIFEST_BASENAME,
21
- } from '../../foundation/discovery/theme-discovery.mjs';
18
+ import {discoverThemeDirectory} from '../../foundation/discovery/theme-discovery.mjs';
22
19
  import {
23
20
  findLocalIntegrationManifestOrNull,
24
21
  IntegrationRootConflictError,
25
22
  patchIntegrationRoot,
26
23
  } from '../../foundation/integrations/manifest-writer.mjs';
27
24
  import {loadManifestObject} from '../../foundation/integrations/integrations.mjs';
25
+ import {themeDescriptorSource} from '../../foundation/integrations/theme-descriptor.mjs';
28
26
  import {assertContributionVisible} from '../../foundation/integrations/contribution-inventory.mjs';
29
27
  import {
30
28
  applyWrites,
@@ -70,6 +68,7 @@ function themeIdentity(slug) {
70
68
  displayName,
71
69
  exportName: `${identifier}Theme`,
72
70
  entry: `${identifier}Theme.ts`,
71
+ descriptor: `${identifier}Theme.doc.mjs`,
73
72
  };
74
73
  }
75
74
 
@@ -78,9 +77,20 @@ function themeSource(identity) {
78
77
  return `import {defineTheme} from '@astryxdesign/core/theme';\n\nexport const ${identity.exportName} = defineTheme({\n name: '${identity.slug}',\n});\n`;
79
78
  }
80
79
 
80
+ /** @param {{slug: string, displayName: string}} identity */
81
+ function themeDescriptor(identity) {
82
+ return themeDescriptorSource({
83
+ type: 'theme',
84
+ name: identity.slug,
85
+ displayName: identity.displayName,
86
+ description: `${identity.displayName} theme.`,
87
+ maintained: true,
88
+ });
89
+ }
90
+
81
91
  /**
82
- * Read the bytes back through the same integration-theme discovery seam Project
83
- * uses. A write is not successful until the requested slug resolves.
92
+ * Read bytes back through the same discovery seam Project uses. A write is not
93
+ * successful until the requested slug resolves.
84
94
  * @param {string} packageDir
85
95
  * @param {string} manifestFile
86
96
  * @param {string} owner
@@ -176,6 +186,9 @@ export async function integrationAddTheme(name, options = {}) {
176
186
  dryRun: true,
177
187
  createIfMissing: true,
178
188
  });
189
+ if (fs.existsSync(root)) {
190
+ discoverThemeDirectory(root, owner);
191
+ }
179
192
  } catch (error) {
180
193
  if (error instanceof IntegrationRootConflictError) {
181
194
  throw new AstryxError(
@@ -197,72 +210,29 @@ export async function integrationAddTheme(name, options = {}) {
197
210
  const sourceFile = assertWithin(identity.entry, themeDir, {
198
211
  label: 'theme source file',
199
212
  });
200
- if (fs.existsSync(sourceFile)) {
201
- throw new AstryxError(
202
- `Refusing to overwrite existing file ${projectPath(path.relative(packageDir, sourceFile))}.`,
203
- undefined,
204
- ERROR_CODES.ERR_FILE_EXISTS,
205
- );
206
- }
207
-
208
- const catalogFile = assertWithin(THEME_MANIFEST_BASENAME, root, {
209
- label: 'theme catalog manifest',
213
+ const descriptorFile = assertWithin(identity.descriptor, themeDir, {
214
+ label: 'theme descriptor file',
210
215
  });
211
- /** @type {{version: 1, themes: Array<Record<string, unknown>>, [key: string]: unknown}} */
212
- let catalog = {version: 1, themes: []};
213
- /** @type {Buffer|null} */
214
- let catalogOriginal = null;
215
- if (fs.existsSync(catalogFile)) {
216
- try {
217
- catalogOriginal = fs.readFileSync(catalogFile);
218
- catalog = JSON.parse(catalogOriginal.toString('utf-8'));
219
- // The discovery seam is the schema/source-of-truth validator. The write
220
- // plan carries catalogOriginal, so any concurrent change between this read
221
- // and publish is refused rather than overwritten with this snapshot.
222
- discoverThemeCatalog(root, owner);
223
- } catch (error) {
216
+ // An existing folder is filled; only a file this would write is refused.
217
+ for (const file of [sourceFile, descriptorFile]) {
218
+ if (fs.existsSync(file)) {
224
219
  throw new AstryxError(
225
- error instanceof Error ? error.message : String(error),
220
+ `Refusing to overwrite existing file ${projectPath(path.relative(packageDir, file))}.`,
226
221
  undefined,
227
- ERROR_CODES.ERR_THEME_INVALID,
222
+ ERROR_CODES.ERR_FILE_EXISTS,
228
223
  );
229
224
  }
230
225
  }
231
- if (
232
- catalog.themes.some(
233
- theme =>
234
- typeof theme.slug === 'string' &&
235
- theme.slug.toLowerCase() === identity.slug.toLowerCase(),
236
- )
237
- ) {
238
- throw new AstryxError(
239
- `Theme "${identity.slug}" already exists in ${projectPath(path.relative(packageDir, catalogFile))}.`,
240
- undefined,
241
- ERROR_CODES.ERR_FILE_EXISTS,
242
- );
243
- }
244
-
245
- catalog.themes.push({
246
- slug: identity.slug,
247
- displayName: identity.displayName,
248
- description: `${identity.displayName} theme.`,
249
- maintained: true,
250
- entry: identity.entry,
251
- exportName: identity.exportName,
252
- files: [identity.entry],
253
- });
254
226
 
255
227
  /** @type {import('./add-helpers.mjs').WritePlan[]} */
256
- const plans = [];
257
- plans.push(
228
+ const plans = [
258
229
  {path: sourceFile, contents: themeSource(identity), createOnly: true},
259
230
  {
260
- path: catalogFile,
261
- contents: `${JSON.stringify(catalog, null, 2)}\n`,
262
- createOnly: catalogOriginal == null,
263
- expectedOriginal: catalogOriginal ?? undefined,
231
+ path: descriptorFile,
232
+ contents: themeDescriptor(identity),
233
+ createOnly: true,
264
234
  },
265
- );
235
+ ];
266
236
  const packageUpdate = packageJsonUpdate(
267
237
  packageFile,
268
238
  rootPath,
@@ -32,7 +32,7 @@ afterEach(() => {
32
32
  });
33
33
 
34
34
  describe('integrationAddTheme', () => {
35
- it('writes a valid source theme, catalog, root, package files entry, and receipt', async () => {
35
+ it('writes a typed same-stem descriptor and source with no central catalog', async () => {
36
36
  setup();
37
37
  const result = await integrationAddTheme('ocean', {cwd: tmpDir});
38
38
 
@@ -45,7 +45,7 @@ describe('integrationAddTheme', () => {
45
45
  manifest: 'astryx.integration.mjs',
46
46
  files: [
47
47
  'themes/ocean/oceanTheme.ts',
48
- 'themes/manifest.json',
48
+ 'themes/ocean/oceanTheme.doc.mjs',
49
49
  'package.json',
50
50
  'astryx.integration.mjs',
51
51
  ],
@@ -56,21 +56,17 @@ describe('integrationAddTheme', () => {
56
56
  expect(
57
57
  fs.readFileSync(path.join(tmpDir, 'themes/ocean/oceanTheme.ts'), 'utf-8'),
58
58
  ).toContain('export const oceanTheme = defineTheme');
59
- expect(
60
- JSON.parse(
61
- fs.readFileSync(path.join(tmpDir, 'themes/manifest.json'), 'utf-8'),
62
- ),
63
- ).toMatchObject({
64
- version: 1,
65
- themes: [
66
- {
67
- slug: 'ocean',
68
- entry: 'oceanTheme.ts',
69
- exportName: 'oceanTheme',
70
- files: ['oceanTheme.ts'],
71
- },
72
- ],
73
- });
59
+
60
+ const descriptor = fs.readFileSync(
61
+ path.join(tmpDir, 'themes/ocean/oceanTheme.doc.mjs'),
62
+ 'utf-8',
63
+ );
64
+ expect(descriptor).toContain("@astryxdesign/cli/authoring').ThemeDoc");
65
+ expect(descriptor).toContain("type: 'theme'");
66
+ expect(descriptor).toContain("name: 'ocean'");
67
+ expect(fs.existsSync(path.join(tmpDir, 'themes/manifest.json'))).toBe(
68
+ false,
69
+ );
74
70
  expect(
75
71
  fs.readFileSync(path.join(tmpDir, 'astryx.integration.mjs'), 'utf-8'),
76
72
  ).toContain("themes: './themes'");
@@ -96,6 +92,7 @@ describe('integrationAddTheme', () => {
96
92
  expect(result.data.dryRun).toBe(true);
97
93
  expect(result.data.root).toEqual({path: './themes', created: true});
98
94
  expect(result.data.files).toContain('themes/ocean/oceanTheme.ts');
95
+ expect(result.data.files).toContain('themes/ocean/oceanTheme.doc.mjs');
99
96
  expect(fs.existsSync(path.join(tmpDir, 'themes'))).toBe(false);
100
97
  expect(
101
98
  fs.readFileSync(path.join(tmpDir, 'astryx.integration.mjs'), 'utf-8'),
@@ -125,22 +122,69 @@ describe('integrationAddTheme', () => {
125
122
  expect(
126
123
  fs.existsSync(path.join(tmpDir, 'src/themes/ocean/oceanTheme.ts')),
127
124
  ).toBe(true);
125
+ expect(
126
+ fs.existsSync(path.join(tmpDir, 'src/themes/ocean/oceanTheme.doc.mjs')),
127
+ ).toBe(true);
128
128
  });
129
129
 
130
- it('refuses an invalid existing catalog without changing its bytes', async () => {
130
+ it('keeps each theme to its own folder: a second theme touches no shared file', async () => {
131
+ setup({manifest: "export default {themes: './themes'};\n"});
132
+ await integrationAddTheme('ocean', {cwd: tmpDir});
133
+ const shared = ['package.json', 'astryx.integration.mjs'].map(file =>
134
+ fs.readFileSync(path.join(tmpDir, file), 'utf-8'),
135
+ );
136
+
137
+ const result = await integrationAddTheme('reef', {cwd: tmpDir});
138
+
139
+ expect(result.data.files).toEqual([
140
+ 'themes/reef/reefTheme.ts',
141
+ 'themes/reef/reefTheme.doc.mjs',
142
+ ]);
143
+ expect(fs.readdirSync(path.join(tmpDir, 'themes')).sort()).toEqual([
144
+ 'ocean',
145
+ 'reef',
146
+ ]);
147
+ expect(
148
+ ['package.json', 'astryx.integration.mjs'].map(file =>
149
+ fs.readFileSync(path.join(tmpDir, file), 'utf-8'),
150
+ ),
151
+ ).toEqual(shared);
152
+ });
153
+
154
+ it('refuses an obsolete central catalog without changing its bytes', async () => {
131
155
  setup();
132
156
  fs.mkdirSync(path.join(tmpDir, 'themes'));
133
157
  const catalog = path.join(tmpDir, 'themes', 'manifest.json');
134
- fs.writeFileSync(catalog, '{not-json\n');
158
+ fs.writeFileSync(catalog, '{"version":1}\n');
135
159
 
136
160
  await expect(
137
161
  integrationAddTheme('ocean', {cwd: tmpDir}),
138
162
  ).rejects.toMatchObject({code: 'ERR_THEME_INVALID'});
139
- expect(fs.readFileSync(catalog, 'utf-8')).toBe('{not-json\n');
163
+ expect(fs.readFileSync(catalog, 'utf-8')).toBe('{"version":1}\n');
140
164
  expect(fs.existsSync(path.join(tmpDir, 'themes', 'ocean'))).toBe(false);
141
165
  });
142
166
 
143
- it('refuses duplicate themes and existing source files', async () => {
167
+ it('refuses an invalid existing descriptor without changing it', async () => {
168
+ setup();
169
+ const existing = path.join(tmpDir, 'themes', 'forest');
170
+ fs.mkdirSync(existing, {recursive: true});
171
+ const descriptor = path.join(existing, 'forestTheme.doc.mjs');
172
+ fs.writeFileSync(descriptor, 'export default {type: "theme"};\n');
173
+ fs.writeFileSync(
174
+ path.join(existing, 'forestTheme.ts'),
175
+ 'export const forestTheme = {};\n',
176
+ );
177
+
178
+ await expect(
179
+ integrationAddTheme('ocean', {cwd: tmpDir}),
180
+ ).rejects.toMatchObject({code: 'ERR_THEME_INVALID'});
181
+ expect(fs.readFileSync(descriptor, 'utf-8')).toBe(
182
+ 'export default {type: "theme"};\n',
183
+ );
184
+ expect(fs.existsSync(path.join(tmpDir, 'themes', 'ocean'))).toBe(false);
185
+ });
186
+
187
+ it('refuses a duplicate valid theme directory', async () => {
144
188
  setup();
145
189
  await integrationAddTheme('ocean', {cwd: tmpDir});
146
190
  await expect(
@@ -148,6 +192,46 @@ describe('integrationAddTheme', () => {
148
192
  ).rejects.toMatchObject({code: 'ERR_FILE_EXISTS'});
149
193
  });
150
194
 
195
+ it('refuses an existing invalid theme directory', async () => {
196
+ setup();
197
+ fs.mkdirSync(path.join(tmpDir, 'themes', 'ocean'), {recursive: true});
198
+ fs.writeFileSync(
199
+ path.join(tmpDir, 'themes', 'ocean', 'oceanTheme.ts'),
200
+ 'export const oceanTheme = {};\n',
201
+ );
202
+ await expect(
203
+ integrationAddTheme('ocean', {cwd: tmpDir}),
204
+ ).rejects.toMatchObject({code: 'ERR_THEME_INVALID'});
205
+ });
206
+
207
+ it.each([
208
+ ['an empty folder', []],
209
+ ['a folder holding .gitkeep', ['.gitkeep']],
210
+ ['a folder holding NOTES.md', ['NOTES.md']],
211
+ ])('fills %s', async (_, files) => {
212
+ setup();
213
+ const dir = path.join(tmpDir, 'themes', 'ocean');
214
+ fs.mkdirSync(dir, {recursive: true});
215
+ for (const file of files) fs.writeFileSync(path.join(dir, file), 'x\n');
216
+ const result = await integrationAddTheme('ocean', {cwd: tmpDir});
217
+ expect(result.data.written).toBe(true);
218
+ expect(fs.readdirSync(dir).sort()).toEqual(
219
+ [...files, 'oceanTheme.doc.mjs', 'oceanTheme.ts'].sort(),
220
+ );
221
+ });
222
+
223
+ it('refuses to overwrite a file it would write', async () => {
224
+ setup();
225
+ await integrationAddTheme('ocean', {cwd: tmpDir});
226
+ await expect(
227
+ integrationAddTheme('ocean', {cwd: tmpDir}),
228
+ ).rejects.toMatchObject({
229
+ code: 'ERR_FILE_EXISTS',
230
+ message:
231
+ 'Refusing to overwrite existing file themes/ocean/oceanTheme.ts.',
232
+ });
233
+ });
234
+
151
235
  it.each(['../ocean', 'Ocean', 'ocean theme', '.ocean'])(
152
236
  'refuses unsafe or noncanonical name %s',
153
237
  async name => {
@@ -3,13 +3,22 @@
3
3
  /**
4
4
  * @file Integration authoring diagnostics against the built-in Core catalog.
5
5
  *
6
- * Components and templates may intentionally share a Core identity, but
7
- * unqualified lookup then fails closed and callers must select the package.
8
- * Docs have explicit `replaces` / `extends` relationships, so the check can
9
- * distinguish intentional ownership from an accidental same-name shadow.
6
+ * Templates may intentionally replace Core identities; undeclared same-id
7
+ * overlaps stay fail-closed and require package selection. The CLI package
8
+ * version gates replacement-specific fields so 0.6.x keeps its released shape.
9
+ * Docs have explicit `replaces` / `extends` relationships with parallel
10
+ * validation semantics.
11
+ *
12
+ * @input Integration packages, built-in catalogs, and the CLI release boundary.
13
+ * @output Typed template, component, and documentation authoring diagnostics.
14
+ * @position Public integration-authoring API over private discovery adapters.
10
15
  */
11
16
 
12
17
  import {getCliInvocation} from '../../foundation/env/package-manager.mjs';
18
+ import {
19
+ packageDocsProblems,
20
+ packageReferenceProblems,
21
+ } from '../docs/_adapter.mjs';
13
22
  import {findCoreDir} from '../../foundation/fs/paths.mjs';
14
23
  import {
15
24
  discoverIntegrationComponents,
@@ -20,9 +29,11 @@ import {
20
29
  discoverIntegrationDocs,
21
30
  } from '../../foundation/discovery/docs-discovery.mjs';
22
31
  import {
32
+ applyTemplateReplacements,
23
33
  discoverCoreTemplates,
24
34
  discoverIntegrationTemplatesForOne,
25
35
  } from '../../foundation/discovery/template-adapter.mjs';
36
+ import {expandedTemplateConflictSchemaActive} from '../../foundation/discovery/template-conflict-release.mjs';
26
37
  import {
27
38
  validateInstalledIntegration,
28
39
  validateLocalIntegration,
@@ -48,16 +59,27 @@ async function resolveIntegration(pkg, cwd) {
48
59
  /**
49
60
  * Add discovery errors once to an issue list.
50
61
  * @param {Array<{code: string, severity: 'warning' | 'error', message: string}>} issues
51
- * @param {Array<{message: string} | Error>} errors
62
+ * @param {Array<{message: string, code?: string, severity?: 'warning' | 'error'} | Error>} errors
52
63
  * @param {string} code
53
64
  */
54
65
  function addErrors(issues, errors, code) {
55
66
  for (const error of errors) {
56
67
  const message = error.message;
57
- if (issues.some(issue => issue.code === code && issue.message === message)) {
68
+ const issueCode =
69
+ 'code' in error && typeof error.code === 'string' ? error.code : code;
70
+ const severity =
71
+ 'severity' in error &&
72
+ (error.severity === 'warning' || error.severity === 'error')
73
+ ? error.severity
74
+ : 'error';
75
+ if (
76
+ issues.some(
77
+ issue => issue.code === issueCode && issue.message === message,
78
+ )
79
+ ) {
58
80
  continue;
59
81
  }
60
- issues.push({code, severity: 'error', message});
82
+ issues.push({code: issueCode, severity, message});
61
83
  }
62
84
  }
63
85
 
@@ -74,7 +96,7 @@ export async function integrationTemplateConflicts(pkg, options = {}) {
74
96
  const version = resolved.found ? (resolved.version ?? null) : null;
75
97
  const issues = [...resolved.issues];
76
98
 
77
- if (!resolved.integration?.templates || name == null) {
99
+ if (!resolved.integration || name == null) {
78
100
  return {
79
101
  type: 'integration.template-conflicts',
80
102
  data: {name, version, conflicts: [], issues},
@@ -87,6 +109,32 @@ export async function integrationTemplateConflicts(pkg, options = {}) {
87
109
  ]);
88
110
  addErrors(issues, errors, 'invalid_template');
89
111
 
112
+ const expandedConflictSchema = expandedTemplateConflictSchemaActive();
113
+ const replacementResolution = applyTemplateReplacements(
114
+ [...coreTemplates, ...templates],
115
+ errors.filter(error => error.replacementTarget != null),
116
+ );
117
+ for (const error of replacementResolution.errors) {
118
+ if (
119
+ !issues.some(
120
+ issue => issue.code === error.code && issue.message === error.message,
121
+ )
122
+ ) {
123
+ issues.push({
124
+ code: error.code,
125
+ severity: error.severity,
126
+ message: error.message,
127
+ });
128
+ }
129
+ }
130
+ const activeReplacementIds = new Set(
131
+ replacementResolution.templates
132
+ .filter(
133
+ template => template.package === name && template.replaces != null,
134
+ )
135
+ .map(template => template.dirName),
136
+ );
137
+
90
138
  /** @type {Map<string, Array<{type: 'page' | 'block', name: string}>>} */
91
139
  const coreById = new Map();
92
140
  for (const template of coreTemplates) {
@@ -101,30 +149,69 @@ export async function integrationTemplateConflicts(pkg, options = {}) {
101
149
  }
102
150
 
103
151
  const run = getCliInvocation(cwd);
104
- const conflicts = templates
105
- .flatMap(template => {
106
- const coreMatches = coreById.get(template.dirName);
107
- if (!coreMatches) return [];
108
- const command = `${run} template ${shellArg(template.dirName)} --package ${shellArg(name)}`;
109
- const coreKinds = coreMatches
110
- .map(match => `${match.type} "${match.name}"`)
111
- .join(', ');
112
- return [
113
- {
152
+ /** @type {import('./authoring-checks.type.mjs').IntegrationTemplateConflict[]} */
153
+ const conflicts = [];
154
+ for (const template of templates) {
155
+ const sameIdCore = coreById.get(template.dirName);
156
+ const replacementIsActive = activeReplacementIds.has(template.dirName);
157
+
158
+ if (
159
+ expandedConflictSchema &&
160
+ replacementIsActive &&
161
+ template.replaces != null
162
+ ) {
163
+ // This branch is unreachable before the package reaches 0.7.0. Keep the
164
+ // published 0.6.x conflict typedef narrow until the activation change.
165
+ conflicts.push(
166
+ /** @type {any} */ ({
114
167
  id: template.dirName,
115
- severity: /** @type {const} */ ('warning'),
168
+ severity: 'info',
169
+ relationship: 'replaces',
170
+ replaces: template.replaces,
116
171
  integrationPackage: name,
117
172
  integrationType: template.type,
118
173
  integrationName: template.name,
119
- coreMatches,
174
+ coreMatches: coreById.get(template.replaces) ?? [],
120
175
  message:
121
- `Template id "${template.dirName}" conflicts with Core (${coreKinds}). ` +
122
- 'Consider renaming the integration template. If you keep it, always select it with --package.',
123
- command,
124
- },
125
- ];
126
- })
127
- .sort((a, b) => a.id.localeCompare(b.id));
176
+ `Intentional replacement: "${template.dirName}" replaces the Core template ` +
177
+ `"${template.replaces}" for unqualified lookup.`,
178
+ command: `${run} template ${shellArg(template.replaces)} --package ${shellArg('@astryxdesign/core')}`,
179
+ }),
180
+ );
181
+ }
182
+
183
+ if (
184
+ sameIdCore &&
185
+ !(replacementIsActive && template.replaces === template.dirName)
186
+ ) {
187
+ const coreKinds = sameIdCore
188
+ .map(match => `${match.type} "${match.name}"`)
189
+ .join(', ');
190
+ const compatibleKind = sameIdCore.some(
191
+ match => match.type === template.type,
192
+ );
193
+ conflicts.push({
194
+ id: template.dirName,
195
+ severity: 'warning',
196
+ ...(expandedConflictSchema ? {relationship: 'accidental'} : {}),
197
+ integrationPackage: name,
198
+ integrationType: template.type,
199
+ integrationName: template.name,
200
+ coreMatches: sameIdCore,
201
+ message: expandedConflictSchema
202
+ ? compatibleKind
203
+ ? `Template id "${template.dirName}" conflicts with Core (${coreKinds}). Consider renaming it, or set replaces: ${JSON.stringify(template.dirName)} in its metadata to replace the Core template.`
204
+ : `Template id "${template.dirName}" conflicts with Core (${coreKinds}), but a ${template.type} template cannot replace a different template kind. Rename the integration template.`
205
+ : `Template id "${template.dirName}" conflicts with Core (${coreKinds}). Consider renaming the integration template. If you keep it, always select it with --package.`,
206
+ command: `${run} template ${shellArg(template.dirName)} --package ${shellArg(name)}`,
207
+ });
208
+ }
209
+ }
210
+ conflicts.sort((a, b) =>
211
+ `${a.id}:${'relationship' in a ? a.relationship : ''}`.localeCompare(
212
+ `${b.id}:${'relationship' in b ? b.relationship : ''}`,
213
+ ),
214
+ );
128
215
 
129
216
  return {
130
217
  type: 'integration.template-conflicts',
@@ -211,10 +298,11 @@ export async function integrationDocConflicts(pkg, options = {}) {
211
298
  };
212
299
  }
213
300
 
214
- const [{records, errors}, builtinTopics] = await Promise.all([
301
+ const [discovered, builtinTopics] = await Promise.all([
215
302
  discoverIntegrationDocs(resolved.integration),
216
303
  Promise.resolve(discoverBuiltinTopics()),
217
304
  ]);
305
+ const {records, errors} = discovered;
218
306
  addErrors(issues, errors, 'invalid_doc');
219
307
  const coreTopicsByKey = new Map(
220
308
  Object.keys(builtinTopics).map(topic => [topic.toLowerCase(), topic]),
@@ -276,6 +364,28 @@ export async function integrationDocConflicts(pkg, options = {}) {
276
364
  ),
277
365
  );
278
366
 
367
+ // The same graph checks `astryx doctor` runs in a project: the namespaces
368
+ // and placed guides this package adds to the docs tree, and every link in
369
+ // its docs (spec:AST-046, spec:AST-047).
370
+ if (errors.length === 0) {
371
+ for (const message of await packageDocsProblems(
372
+ /** @type {{name: string}} */ (resolved.integration),
373
+ discovered,
374
+ )) {
375
+ issues.push({code: 'invalid_doc_graph', severity: 'warning', message});
376
+ }
377
+
378
+ // A reference block includes content rather than linking to it, so one
379
+ // that cannot include what it names loses that content for every reader:
380
+ // an error, where a link that names no doc still prints as written.
381
+ for (const message of await packageReferenceProblems(
382
+ /** @type {{name: string}} */ (resolved.integration),
383
+ discovered,
384
+ )) {
385
+ issues.push({code: 'invalid_doc_reference', severity: 'error', message});
386
+ }
387
+ }
388
+
279
389
  return {
280
390
  type: 'integration.doc-conflicts',
281
391
  data: {name, version, findings, issues},