@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
@@ -15,7 +15,7 @@
15
15
  */
16
16
  /**
17
17
  * Compute the files the tarball must contain for an integration's
18
- * contributions to be visible. Reads catalogs and walks directories;
18
+ * contributions to be visible. Walks descriptor-owned directories;
19
19
  * never imports or executes authored modules.
20
20
  *
21
21
  * @param {import('./integrations.mjs').LoadedIntegration} loaded
@@ -26,7 +26,7 @@ export function computeRequiredFiles(loaded: import("./integrations.mjs").Loaded
26
26
  * @typedef {object} ContributionIdentities
27
27
  * @property {{slug: string, exportName: string}[]} themes
28
28
  * @property {string[]} components
29
- * @property {{id: string, type: string, name: string}[]} templates
29
+ * @property {{id: string, type: string, name: string, replaces?: string}[]} templates
30
30
  * @property {{version: string, id: string}[]} codemods
31
31
  * @property {string[]} docs
32
32
  * @property {string[]} agentDocsAppend
@@ -127,6 +127,7 @@ export type ContributionIdentities = {
127
127
  id: string;
128
128
  type: string;
129
129
  name: string;
130
+ replaces?: string;
130
131
  }[];
131
132
  codemods: {
132
133
  version: string;
@@ -14,8 +14,11 @@
14
14
  import * as fs from 'node:fs';
15
15
  import * as path from 'node:path';
16
16
  import {isDeepStrictEqual} from 'node:util';
17
- import {THEME_MANIFEST_BASENAME} from '../discovery/theme-discovery.mjs';
18
- import {discoverIntegrationThemes} from '../discovery/theme-discovery.mjs';
17
+ import {
18
+ discoverIntegrationThemes,
19
+ isThemeFolder,
20
+ listThemeFiles,
21
+ } from '../discovery/theme-discovery.mjs';
19
22
  import {discoverIntegrationComponents} from '../discovery/component-discovery.mjs';
20
23
  import {loadComponentDoc} from '../discovery/component-loader.mjs';
21
24
  import {discoverIntegrationTemplatesForOne} from '../discovery/template-adapter.mjs';
@@ -65,27 +68,21 @@ function rel(packageDir, file) {
65
68
 
66
69
  /** @param {string} root @param {string} pkgDir */
67
70
  function enumerateThemeFiles(root, pkgDir) {
71
+ if (!root || !fs.existsSync(root) || !fs.statSync(root).isDirectory()) {
72
+ return [];
73
+ }
74
+ // Exactly what `theme add` copies: each theme folder's files, including
75
+ // tests and fixtures, but no dot entry or file npm never publishes. Files
76
+ // beside the theme folders belong to no theme.
68
77
  /** @type {string[]} */
69
78
  const files = [];
70
- const catalog = path.join(root, THEME_MANIFEST_BASENAME);
71
- if (!fs.existsSync(catalog)) return files;
72
- files.push(rel(pkgDir, catalog));
73
- try {
74
- const data = JSON.parse(fs.readFileSync(catalog, 'utf-8'));
75
- if (data?.version !== 1 || !Array.isArray(data.themes)) return files;
76
- for (const theme of data.themes) {
77
- if (typeof theme.slug !== 'string' || !Array.isArray(theme.files))
78
- continue;
79
- for (const f of theme.files) {
80
- if (typeof f === 'string') {
81
- files.push(rel(pkgDir, path.join(root, theme.slug, f)));
82
- }
83
- }
79
+ for (const entry of fs.readdirSync(root, {withFileTypes: true})) {
80
+ const full = path.join(root, entry.name);
81
+ if (entry.isDirectory() && isThemeFolder(full)) {
82
+ files.push(...listThemeFiles(full).map(file => path.join(full, file)));
84
83
  }
85
- } catch {
86
- /* Phase 1 catches structural issues */
87
84
  }
88
- return files;
85
+ return files.sort().map(file => rel(pkgDir, file));
89
86
  }
90
87
 
91
88
  /** @param {string} root @param {string} pkgDir */
@@ -158,7 +155,7 @@ const ROOT_KINDS = /** @type {const} */ ([
158
155
 
159
156
  /**
160
157
  * Compute the files the tarball must contain for an integration's
161
- * contributions to be visible. Reads catalogs and walks directories;
158
+ * contributions to be visible. Walks descriptor-owned directories;
162
159
  * never imports or executes authored modules.
163
160
  *
164
161
  * @param {import('./integrations.mjs').LoadedIntegration} loaded
@@ -192,7 +189,7 @@ export function computeRequiredFiles(loaded) {
192
189
  * @typedef {object} ContributionIdentities
193
190
  * @property {{slug: string, exportName: string}[]} themes
194
191
  * @property {string[]} components
195
- * @property {{id: string, type: string, name: string}[]} templates
192
+ * @property {{id: string, type: string, name: string, replaces?: string}[]} templates
196
193
  * @property {{version: string, id: string}[]} codemods
197
194
  * @property {string[]} docs
198
195
  * @property {string[]} agentDocsAppend
@@ -281,7 +278,12 @@ export async function collectIdentities(loaded) {
281
278
  errors.push({kind: 'templates', message: templateError.message});
282
279
  }
283
280
  identities.templates = templates
284
- .map(t => ({id: t.dirName, type: t.type, name: t.name}))
281
+ .map(t => ({
282
+ id: t.dirName,
283
+ type: t.type,
284
+ name: t.name,
285
+ ...(t.replaces == null ? {} : {replaces: t.replaces}),
286
+ }))
285
287
  .sort((a, b) => a.id.localeCompare(b.id));
286
288
  } catch (err) {
287
289
  errors.push({
@@ -312,12 +314,13 @@ export async function collectIdentities(loaded) {
312
314
 
313
315
  if (loaded.docs && fs.existsSync(loaded.docs)) {
314
316
  try {
315
- const {records, errors: docErrors} =
317
+ const {records, guides, errors: docErrors} =
316
318
  await discoverIntegrationDocs(loaded);
317
319
  for (const docError of docErrors) {
318
320
  errors.push({kind: 'docs', message: docError.message});
319
321
  }
320
- identities.docs = records.map(r => r.name).sort();
322
+ // A guide placed in the docs tree is a doc contribution too.
323
+ identities.docs = [...records, ...guides].map(r => r.name).sort();
321
324
  } catch (err) {
322
325
  errors.push({kind: 'docs', message: /** @type {Error} */ (err).message});
323
326
  }
@@ -37,30 +37,37 @@ function loaded(overrides = {}) {
37
37
 
38
38
  function writeTheme(slug, files = [`${slug}Theme.ts`]) {
39
39
  const root = path.join(tmpDir, 'themes');
40
- fs.mkdirSync(path.join(root, slug), {recursive: true});
41
- for (const f of files) {
42
- fs.writeFileSync(path.join(root, slug, f), `// ${f}\n`);
40
+ const themeDir = path.join(root, slug);
41
+ fs.mkdirSync(themeDir, {recursive: true});
42
+ for (const file of files) {
43
+ const target = path.join(themeDir, file);
44
+ fs.mkdirSync(path.dirname(target), {recursive: true});
45
+ fs.writeFileSync(target, `// ${file}\n`);
43
46
  }
44
- const catalogPath = path.join(root, 'manifest.json');
45
- let catalog = {version: 1, themes: []};
46
- if (fs.existsSync(catalogPath)) {
47
- catalog = JSON.parse(fs.readFileSync(catalogPath, 'utf-8'));
48
- }
49
- catalog.themes.push({
50
- slug,
51
- displayName: slug,
52
- description: `${slug} theme.`,
53
- maintained: true,
54
- entry: files[0],
55
- exportName: `${slug}Theme`,
56
- files,
57
- });
58
- fs.writeFileSync(catalogPath, JSON.stringify(catalog, null, 2) + '\n');
47
+ fs.writeFileSync(
48
+ path.join(themeDir, `${slug}Theme.doc.mjs`),
49
+ `/** @type {import('@astryxdesign/cli/authoring').ThemeDoc} */\nexport default {type: 'theme', name: '${slug}', displayName: '${slug}', description: '${slug} theme.', maintained: true};\n`,
50
+ );
59
51
  return root;
60
52
  }
61
53
 
54
+ const UNPUBLISHED = [
55
+ '.DS_Store',
56
+ '._oceanTheme.ts',
57
+ '.gitignore',
58
+ '.npmignore',
59
+ '.npmrc',
60
+ 'oceanTheme.ts.orig',
61
+ 'npm-debug.log',
62
+ 'palette/.DS_Store',
63
+ 'palette/npm-debug.log',
64
+ '.git/HEAD',
65
+ 'CVS/Entries',
66
+ 'node_modules/dep/index.js',
67
+ ];
68
+
62
69
  describe('computeRequiredFiles', () => {
63
- it('enumerates manifest + theme catalog files', () => {
70
+ it('enumerates each complete descriptor-owned theme directory', () => {
64
71
  const root = writeTheme('ocean', ['oceanTheme.ts', 'tokens.ts']);
65
72
  const inv = computeRequiredFiles(loaded({themes: root}));
66
73
 
@@ -69,13 +76,46 @@ describe('computeRequiredFiles', () => {
69
76
  expect(inv.roots[0].kind).toBe('themes');
70
77
  expect(inv.roots[0].files).toEqual(
71
78
  expect.arrayContaining([
72
- 'themes/manifest.json',
79
+ 'themes/ocean/oceanTheme.doc.mjs',
73
80
  'themes/ocean/oceanTheme.ts',
74
81
  'themes/ocean/tokens.ts',
75
82
  ]),
76
83
  );
77
84
  expect(inv.allFiles).toContain('astryx.integration.mjs');
78
- expect(inv.allFiles).toContain('themes/manifest.json');
85
+ expect(inv.allFiles).toContain('themes/ocean/oceanTheme.doc.mjs');
86
+ expect(inv.allFiles).not.toContain('themes/manifest.json');
87
+ });
88
+
89
+ it('requires no file npm leaves out, at any depth, and none beside the themes', () => {
90
+ const root = writeTheme('ocean', ['oceanTheme.ts', 'palette/tokens.ts']);
91
+ for (const file of [
92
+ ...UNPUBLISHED.map(name => `ocean/${name}`),
93
+ 'README.md',
94
+ 'npm-debug.log',
95
+ ]) {
96
+ fs.mkdirSync(path.dirname(path.join(root, file)), {recursive: true});
97
+ fs.writeFileSync(path.join(root, file), 'x\n');
98
+ }
99
+ expect(computeRequiredFiles(loaded({themes: root})).roots[0].files).toEqual(
100
+ [
101
+ 'themes/ocean/oceanTheme.doc.mjs',
102
+ 'themes/ocean/oceanTheme.ts',
103
+ 'themes/ocean/palette/tokens.ts',
104
+ ],
105
+ );
106
+ });
107
+
108
+ it('requires only the folders theme discovery reads', () => {
109
+ const root = writeTheme('ocean');
110
+ for (const file of ['shared/palette.ts', '.cache/state.json']) {
111
+ fs.mkdirSync(path.dirname(path.join(root, file)), {recursive: true});
112
+ fs.writeFileSync(path.join(root, file), '{}\n');
113
+ }
114
+ const inv = computeRequiredFiles(loaded({themes: root}));
115
+ expect(inv.roots[0].files).toEqual([
116
+ 'themes/ocean/oceanTheme.doc.mjs',
117
+ 'themes/ocean/oceanTheme.ts',
118
+ ]);
79
119
  });
80
120
 
81
121
  it('enumerates paired component metadata and source files', () => {
@@ -139,31 +179,31 @@ describe('computeRequiredFiles', () => {
139
179
  const root = path.join(tmpDir, 'templates');
140
180
  fs.mkdirSync(path.join(root, 'dashboard'), {recursive: true});
141
181
  fs.writeFileSync(
142
- path.join(root, 'dashboard', 'dashboard.template.mjs'),
182
+ path.join(root, 'dashboard', 'dashboard.doc.mjs'),
143
183
  '// tmpl\n',
144
184
  );
145
185
  fs.writeFileSync(path.join(root, 'dashboard', 'dashboard.tsx'), '// src\n');
146
186
  const inv = computeRequiredFiles(loaded({templates: root}));
147
187
 
148
188
  expect(inv.roots[0].files).toContain(
149
- 'templates/dashboard/dashboard.template.mjs',
189
+ 'templates/dashboard/dashboard.doc.mjs',
150
190
  );
151
191
  expect(inv.roots[0].files).toContain('templates/dashboard/dashboard.tsx');
152
192
  });
153
193
 
154
- it('enumerates legacy .doc template metadata with paired sources', () => {
194
+ it('enumerates released .template compatibility metadata with paired sources', () => {
155
195
  const root = path.join(tmpDir, 'templates');
156
196
  fs.mkdirSync(path.join(root, 'dashboard'), {recursive: true});
157
197
  fs.writeFileSync(
158
- path.join(root, 'dashboard', 'dashboard.doc.mjs'),
159
- '// legacy template\n',
198
+ path.join(root, 'dashboard', 'dashboard.template.mjs'),
199
+ '// released compatibility template\n',
160
200
  );
161
201
  fs.writeFileSync(path.join(root, 'dashboard', 'dashboard.tsx'), '// src\n');
162
202
 
163
203
  const inv = computeRequiredFiles(loaded({templates: root}));
164
204
 
165
205
  expect(inv.roots[0].files).toEqual([
166
- 'templates/dashboard/dashboard.doc.mjs',
206
+ 'templates/dashboard/dashboard.template.mjs',
167
207
  'templates/dashboard/dashboard.tsx',
168
208
  ]);
169
209
  });
@@ -264,6 +304,25 @@ describe('compareIdentities', () => {
264
304
  expect(issues[0].message).toContain('dash');
265
305
  });
266
306
 
307
+ it('errors when a packed template replaces a different target', () => {
308
+ const template = {id: 'acme-shell', type: 'page', name: 'Acme shell'};
309
+ const local = {
310
+ ...base(),
311
+ templates: [{...template, replaces: 'shell-side-nav'}],
312
+ };
313
+ const packed = {
314
+ ...base(),
315
+ templates: [{...template, replaces: 'shell-top-nav'}],
316
+ };
317
+
318
+ expect(compareIdentities(local, packed)).toEqual([
319
+ expect.objectContaining({
320
+ code: 'identity_mismatch',
321
+ message: expect.stringContaining('acme-shell'),
322
+ }),
323
+ ]);
324
+ });
325
+
267
326
  it('errors when a codemod is missing from packed', () => {
268
327
  const local = {
269
328
  ...base(),
@@ -8,10 +8,17 @@
8
8
  *
9
9
  * Warning: <pkg> has N integration issue(s). Run: astryx doctor integration validate <pkg>
10
10
  *
11
- * @param {Array<import('./integrations.mjs').LoadedIntegration>} loadedIntegrations the Project's loaded integrations
11
+ * @param {Array<import('./integrations.mjs').LoadedIntegration> | {issues: () => Promise<Array<{package: string, code: string, severity: string, message: string}>>}} source loaded integrations or their Project owner
12
12
  * @param {{json?: boolean}} [options]
13
13
  * @returns {Promise<void>}
14
14
  */
15
- export function warnOnIntegrationIssues(loadedIntegrations: Array<import("./integrations.mjs").LoadedIntegration>, { json }?: {
15
+ export function warnOnIntegrationIssues(source: Array<import("./integrations.mjs").LoadedIntegration> | {
16
+ issues: () => Promise<Array<{
17
+ package: string;
18
+ code: string;
19
+ severity: string;
20
+ message: string;
21
+ }>>;
22
+ }, { json }?: {
16
23
  json?: boolean;
17
24
  }): Promise<void>;
@@ -10,8 +10,9 @@
10
10
  * contributions or spamming per-contribution diagnostics.
11
11
  *
12
12
  * Design constraints (all enforced here):
13
- * - Reuses the Doctor integration validators (validateLoadedIntegration);
14
- * no validation logic is duplicated.
13
+ * - Reuses Project.issues() when a Project is available, so cross-package
14
+ * replacement warnings are visible; accepts loaded integrations for legacy
15
+ * internal callers and tests.
15
16
  * - Writes to STDERR only, so it never corrupts a --json stdout envelope.
16
17
  * - Suppressed entirely in --json mode.
17
18
  * - Best-effort: never throws, never changes the exit code. Broken
@@ -28,38 +29,62 @@ import {validateLoadedIntegration} from './validate-contributions.mjs';
28
29
  *
29
30
  * Warning: <pkg> has N integration issue(s). Run: astryx doctor integration validate <pkg>
30
31
  *
31
- * @param {Array<import('./integrations.mjs').LoadedIntegration>} loadedIntegrations the Project's loaded integrations
32
+ * @param {Array<import('./integrations.mjs').LoadedIntegration> | {issues: () => Promise<Array<{package: string, code: string, severity: string, message: string}>>}} source loaded integrations or their Project owner
32
33
  * @param {{json?: boolean}} [options]
33
34
  * @returns {Promise<void>}
34
35
  */
35
- export async function warnOnIntegrationIssues(loadedIntegrations, {json = false} = {}) {
36
+ export async function warnOnIntegrationIssues(source, {json = false} = {}) {
36
37
  try {
37
- // Cheap guard: only do work when integrations are actually configured.
38
38
  if (json) return;
39
- if (!Array.isArray(loadedIntegrations) || loadedIntegrations.length === 0) {
40
- return;
41
- }
42
- for (const integration of loadedIntegrations) {
43
- if (!integration || typeof integration !== 'object') continue;
44
- // A package set aside for a provider-ID conflict is fine in isolation,
45
- // so `doctor integration validate` would not explain it. Say it here.
46
- if (integration.__providerConflict) {
47
- console.error(`Warning: ${integration.__providerConflict.message}`);
48
- continue;
39
+
40
+ /** @type {Map<string, number>} */
41
+ const issueCounts = new Map();
42
+ const fromProject =
43
+ source != null &&
44
+ !Array.isArray(source) &&
45
+ typeof source.issues === 'function';
46
+
47
+ if (fromProject) {
48
+ const issues = await source.issues();
49
+ for (const issue of issues) {
50
+ // A provider-conflict issue carries the specific message that
51
+ // `doctor integration validate` would not explain. Print it directly.
52
+ if (issue.code === 'duplicate_provider') {
53
+ console.error(`Warning: ${issue.message}`);
54
+ continue;
55
+ }
56
+ issueCounts.set(
57
+ issue.package,
58
+ (issueCounts.get(issue.package) ?? 0) + 1,
59
+ );
49
60
  }
50
- let issues;
51
- try {
52
- issues = await validateLoadedIntegration(integration);
53
- } catch {
54
- // Best-effort: a validator throwing must not break the host command.
55
- continue;
61
+ } else if (Array.isArray(source)) {
62
+ for (const integration of source) {
63
+ if (!integration || typeof integration !== 'object') continue;
64
+ // A package set aside for a provider-ID conflict is fine in isolation,
65
+ // so `doctor integration validate` would not explain it. Say it here.
66
+ if (integration.__providerConflict) {
67
+ console.error(`Warning: ${integration.__providerConflict.message}`);
68
+ continue;
69
+ }
70
+ let issues;
71
+ try {
72
+ issues = await validateLoadedIntegration(integration);
73
+ } catch {
74
+ continue;
75
+ }
76
+ if (!Array.isArray(issues) || issues.length === 0) continue;
77
+ const pkg = integration.name ?? integration.__spec ?? '(integration)';
78
+ issueCounts.set(pkg, issues.length);
56
79
  }
57
- if (!Array.isArray(issues) || issues.length === 0) continue;
58
- const pkg = integration.name ?? integration.__spec ?? '(integration)';
59
- // Stderr only — keeps stdout (and any --json envelope) clean.
80
+ }
81
+
82
+ for (const [pkg, count] of issueCounts) {
83
+ const command = fromProject
84
+ ? 'astryx doctor'
85
+ : `astryx doctor integration validate ${pkg}`;
60
86
  console.error(
61
- `Warning: ${pkg} has ${issues.length} integration issue(s). ` +
62
- `Run: astryx doctor integration validate ${pkg}`,
87
+ `Warning: ${pkg} has ${count} integration issue(s). Run: ${command}`,
63
88
  );
64
89
  }
65
90
  } catch {
@@ -37,7 +37,12 @@ afterEach(() => {
37
37
  * Build a loaded-integration-shaped object. Roots are absolute (mirrors
38
38
  * loadIntegrations' resolveRoot).
39
39
  */
40
- function loaded({name = '@acme/widgets', components, templates, codemods} = {}) {
40
+ function loaded({
41
+ name = '@acme/widgets',
42
+ components,
43
+ templates,
44
+ codemods,
45
+ } = {}) {
41
46
  return {
42
47
  name,
43
48
  version: '1.0.0',
@@ -62,6 +67,25 @@ describe('warnOnIntegrationIssues', () => {
62
67
  );
63
68
  });
64
69
 
70
+ it('surfaces cross-package issues collected by Project', async () => {
71
+ const project = {
72
+ issues: async () => [
73
+ {
74
+ package: '@acme/later',
75
+ code: 'ambiguous_template_replacement',
76
+ severity: 'warning',
77
+ message: 'Later configured replacement wins.',
78
+ },
79
+ ],
80
+ };
81
+
82
+ await warnOnIntegrationIssues(project, {json: false});
83
+
84
+ expect(errLines).toEqual([
85
+ 'Warning: @acme/later has 1 integration issue(s). Run: astryx doctor',
86
+ ]);
87
+ });
88
+
65
89
  it('emits nothing in --json mode (keeps stdout/JSON clean)', async () => {
66
90
  const integration = loaded({components: 'does-not-exist'});
67
91
  await warnOnIntegrationIssues([integration], {json: true});
@@ -116,4 +140,53 @@ describe('warnOnIntegrationIssues', () => {
116
140
  ).resolves.toBeUndefined();
117
141
  expect(errLines).toHaveLength(0);
118
142
  });
143
+
144
+ it('prints the specific provider-conflict message via the array path', async () => {
145
+ const conflict = {
146
+ name: '@acme/charts',
147
+ version: '2.0.0',
148
+ __spec: '@acme/charts',
149
+ __packageDir: tmpDir,
150
+ __providerConflict: {
151
+ providerId: '@acme/lab',
152
+ claimedBy: '@acme/lab',
153
+ message:
154
+ '@acme/charts@2.0.0 and @acme/lab@1.0.0 both claim provider ID ' +
155
+ '"@acme/lab". @acme/lab@1.0.0 loads first and is used; ' +
156
+ '@acme/charts@2.0.0 contributes nothing until one package changes ' +
157
+ 'its providerId.',
158
+ },
159
+ };
160
+ await warnOnIntegrationIssues([conflict], {json: false});
161
+
162
+ expect(errLines).toHaveLength(1);
163
+ expect(errLines[0]).toBe(`Warning: ${conflict.__providerConflict.message}`);
164
+ // Must NOT print the generic validate pointer for a provider conflict.
165
+ expect(errLines[0]).not.toContain('integration issue(s)');
166
+ });
167
+
168
+ it('prints the specific provider-conflict message via the Project path', async () => {
169
+ const conflictMessage =
170
+ '@acme/charts@2.0.0 and @acme/lab@1.0.0 both claim provider ID ' +
171
+ '"@acme/lab". @acme/lab@1.0.0 loads first and is used; ' +
172
+ '@acme/charts@2.0.0 contributes nothing until one package changes ' +
173
+ 'its providerId.';
174
+ const project = {
175
+ issues: async () => [
176
+ {
177
+ package: '@acme/charts@2.0.0',
178
+ code: 'duplicate_provider',
179
+ severity: 'warning',
180
+ message: conflictMessage,
181
+ },
182
+ ],
183
+ };
184
+
185
+ await warnOnIntegrationIssues(project, {json: false});
186
+
187
+ expect(errLines).toHaveLength(1);
188
+ expect(errLines[0]).toBe(`Warning: ${conflictMessage}`);
189
+ // Must NOT print the generic validate pointer for a provider conflict.
190
+ expect(errLines[0]).not.toContain('integration issue(s)');
191
+ });
119
192
  });
@@ -14,6 +14,9 @@
14
14
  * issues, Doctor, and the per-command warning name the one set aside instead of
15
15
  * dropping it without a word.
16
16
  *
17
+ * The conflict pass of {@link resolveProviders}, for a list that is already
18
+ * assembled.
19
+ *
17
20
  * @param {LoadedIntegration[]} integrations in precedence order
18
21
  * @returns {LoadedIntegration[]}
19
22
  */
@@ -29,6 +29,7 @@ import {
29
29
  } from '../../authoring/integration/schema.mjs';
30
30
  import {importUserModule, findPresentFiles} from '../fs/module-loader.mjs';
31
31
  import {parseGapReportHandler} from '../../authoring/gap-report/parse.mjs';
32
+ import {resolveProviders} from './provider-resolution.mjs';
32
33
 
33
34
  /**
34
35
  * A fully-resolved, loaded integration. Identity (`name`, `version`) comes from
@@ -116,107 +117,20 @@ function providerIdForPackage(name) {
116
117
  * issues, Doctor, and the per-command warning name the one set aside instead of
117
118
  * dropping it without a word.
118
119
  *
120
+ * The conflict pass of {@link resolveProviders}, for a list that is already
121
+ * assembled.
122
+ *
119
123
  * @param {LoadedIntegration[]} integrations in precedence order
120
124
  * @returns {LoadedIntegration[]}
121
125
  */
122
126
  export function markProviderConflicts(integrations) {
123
- /** @type {Map<string, LoadedIntegration>} */
124
- const claims = new Map();
125
- for (const integration of integrations) {
126
- if (integration?.__local && claimsProvider(integration)) {
127
- const providerId = /** @type {string} */ (integration.providerId);
128
- if (!claims.has(providerId)) claims.set(providerId, integration);
129
- }
130
- }
131
- /** @type {LoadedIntegration[]} */
132
- const resolved = [];
133
- for (const integration of integrations) {
134
- if (!claimsProvider(integration)) {
135
- resolved.push(integration);
136
- continue;
137
- }
138
- const providerId = /** @type {string} */ (integration.providerId);
139
- const claimant = claims.get(providerId);
140
- if (claimant == null) {
141
- claims.set(providerId, integration);
142
- resolved.push(integration);
143
- } else if (claimant === integration) {
144
- resolved.push(integration);
145
- } else if (
146
- claimant.name !== integration.name ||
147
- claimant.version !== integration.version
148
- ) {
149
- resolved.push(providerConflict(integration, claimant));
150
- }
151
- }
152
- return resolved;
153
- }
154
-
155
- /**
156
- * Whether an entry contributes under its provider ID: it has one, its manifest
157
- * loaded, and it has not already been set aside.
158
- * @param {LoadedIntegration | undefined} integration
159
- * @returns {boolean}
160
- */
161
- function claimsProvider(integration) {
162
- return (
163
- integration?.providerId != null &&
164
- integration.__loadError == null &&
165
- integration.__providerConflict == null
166
- );
167
- }
168
-
169
- /**
170
- * @param {LoadedIntegration} integration
171
- * @returns {string}
172
- */
173
- function describeIntegration(integration) {
174
- const id = integration.version
175
- ? `${integration.name}@${integration.version}`
176
- : integration.name;
177
- return integration.__spec && integration.__spec !== integration.name
178
- ? `${id} (from "${integration.__spec}")`
179
- : id;
180
- }
181
-
182
- /**
183
- * An inert record for a package whose provider ID is already claimed. It keeps
184
- * the package's identity and location for reporting, and drops every
185
- * contribution root and handler.
186
- * @param {LoadedIntegration} integration
187
- * @param {LoadedIntegration} claimant
188
- * @returns {LoadedIntegration}
189
- */
190
- function providerConflict(integration, claimant) {
191
- const providerId =
192
- /** @type {import('../../authoring/identity/type').ProviderId} */ (
193
- integration.providerId
194
- );
195
- const winner = describeIntegration(claimant);
196
- const setAside = describeIntegration(integration);
197
- const reason = claimant.__local
198
- ? 'is the package being authored, so it is used'
199
- : 'loads first and is used';
200
- return {
201
- name: integration.name,
202
- providerId,
203
- version: integration.version,
204
- __spec: integration.__spec,
205
- __packageDir: integration.__packageDir,
206
- __manifestFile: integration.__manifestFile,
207
- ...(integration.__local ? {__local: true} : {}),
208
- ...(integration.__autolinked
209
- ? {__autolinked: true, __dependencyField: integration.__dependencyField}
210
- : {}),
211
- __providerConflict: {
212
- providerId,
213
- claimedBy: claimant.name,
214
- message:
215
- `${setAside} and ${winner} both claim provider ID "${providerId}". ` +
216
- `${winner} ${reason}; ${setAside} contributes nothing ` +
217
- 'until one package changes its providerId.',
218
- },
219
- };
127
+ return resolveProviders(
128
+ integrations.map(integration => ({
129
+ source: /** @type {const} */ ('configured'),
130
+ integration,
131
+ spec: integration?.__spec,
132
+ })),
133
+ ).integrations;
220
134
  }
221
135
 
222
136
  /**