@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
@@ -0,0 +1,369 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file Colocated tests for the provider resolver: one outcome per real package
5
+ * directory, precedence, and the wrappers that keep their old results.
6
+ *
7
+ * Fixtures live under a repo-local temp dir, not /tmp, because Vite refuses to
8
+ * dynamically import a module from outside the project root.
9
+ */
10
+
11
+ import {afterEach, beforeEach, describe, expect, it} from 'vitest';
12
+ import * as fs from 'node:fs';
13
+ import * as path from 'node:path';
14
+ import {packageKey, resolveProviders} from './provider-resolution.mjs';
15
+ import {markProviderConflicts} from './integrations.mjs';
16
+ import {autolinkIntegrations} from './autolink.mjs';
17
+ import {Project, providerLedgerOf} from '../config/project.mjs';
18
+
19
+ let tmpDir;
20
+
21
+ beforeEach(() => {
22
+ tmpDir = fs.mkdtempSync(
23
+ path.join(process.cwd(), '.astryx-provider-resolution-'),
24
+ );
25
+ });
26
+
27
+ afterEach(() => {
28
+ fs.rmSync(tmpDir, {recursive: true, force: true});
29
+ });
30
+
31
+ /**
32
+ * A loaded-integration record in a real directory.
33
+ * @param {string} dir directory under the temp root
34
+ * @param {Record<string, unknown>} fields
35
+ * @returns {any}
36
+ */
37
+ function record(dir, fields) {
38
+ const packageDir = path.join(tmpDir, dir);
39
+ fs.mkdirSync(packageDir, {recursive: true});
40
+ return {
41
+ providerId: fields.name,
42
+ __spec: fields.name,
43
+ __packageDir: packageDir,
44
+ __manifestFile: path.join(packageDir, 'astryx.integration.mjs'),
45
+ ...fields,
46
+ };
47
+ }
48
+
49
+ /**
50
+ * @param {string} source
51
+ * @param {any} integration
52
+ */
53
+ const candidate = (source, integration) => ({
54
+ source,
55
+ integration,
56
+ spec: integration.__spec,
57
+ });
58
+
59
+ /** @param {Map<string, any>} ledger */
60
+ function outcomes(ledger) {
61
+ return [...ledger.values()].map(entry => [entry.label, entry.outcome]);
62
+ }
63
+
64
+ describe('resolveProviders ledger', () => {
65
+ it('records one outcome for every candidate, keyed by real directory', () => {
66
+ const widgets = record('widgets', {
67
+ name: '@acme/widgets',
68
+ version: '1.0.0',
69
+ });
70
+ const renamed = record('renamed', {
71
+ name: '@acme/renamed',
72
+ version: '2.0.0',
73
+ providerId: '@acme/widgets',
74
+ });
75
+ const broken = record('broken', {
76
+ name: '@acme/broken',
77
+ version: '1.0.0',
78
+ __loadError: 'boom',
79
+ });
80
+ const {integrations, ledger} = resolveProviders([
81
+ candidate('configured', widgets),
82
+ candidate('configured', renamed),
83
+ candidate('configured', broken),
84
+ ]);
85
+
86
+ expect([...ledger.keys()]).toEqual(
87
+ [widgets, renamed, broken].map(entry => packageKey(entry.__packageDir)),
88
+ );
89
+ expect(outcomes(ledger)).toEqual([
90
+ ['@acme/widgets@1.0.0', 'contributes'],
91
+ ['@acme/renamed@2.0.0', 'set-aside'],
92
+ ['@acme/broken@1.0.0', 'load-failed'],
93
+ ]);
94
+ const setAside = ledger.get(packageKey(renamed.__packageDir));
95
+ expect(setAside).toMatchObject({
96
+ winner: packageKey(widgets.__packageDir),
97
+ reason: 'claimed-earlier',
98
+ integration: integrations[1],
99
+ });
100
+ expect(integrations[1].__providerConflict.claimedBy).toBe('@acme/widgets');
101
+ });
102
+
103
+ it('keeps two packages that share a name apart', () => {
104
+ const old = record('widgets-old', {
105
+ name: '@acme/widgets',
106
+ version: '1.0.0',
107
+ __spec: 'widgets-old',
108
+ });
109
+ const next = record('widgets-next', {
110
+ name: '@acme/widgets',
111
+ version: '2.0.0',
112
+ __spec: 'widgets-next',
113
+ });
114
+ const {ledger} = resolveProviders([
115
+ candidate('configured', old),
116
+ candidate('configured', next),
117
+ ]);
118
+
119
+ expect(outcomes(ledger)).toEqual([
120
+ ['@acme/widgets@1.0.0', 'contributes'],
121
+ ['@acme/widgets@2.0.0', 'set-aside'],
122
+ ]);
123
+ });
124
+
125
+ it('folds every spelling of one real directory into one entry', () => {
126
+ const ui = record('ui', {name: '@acme/ui', version: '0.1.22'});
127
+ const link = path.join(tmpDir, 'legacy-ui');
128
+ fs.symlinkSync(ui.__packageDir, link, 'dir');
129
+ const legacy = {...ui, __spec: '@acme/legacy-ui', __packageDir: link};
130
+ const {integrations, ledger} = resolveProviders([
131
+ candidate('configured', ui),
132
+ candidate('configured', legacy),
133
+ ]);
134
+
135
+ expect(integrations).toEqual([ui]);
136
+ expect([...ledger.keys()]).toEqual([packageKey(ui.__packageDir)]);
137
+ const [entry] = ledger.values();
138
+ expect(entry.outcome).toBe('contributes');
139
+ expect(entry.refs.map(ref => ref.spec)).toEqual([
140
+ '@acme/ui',
141
+ '@acme/legacy-ui',
142
+ ]);
143
+ });
144
+
145
+ it('records an autolinked copy of a loaded package as alias-of it', () => {
146
+ const ui = record('ui', {name: '@acme/ui', version: '0.1.22'});
147
+ const copy = record('legacy-ui', {
148
+ name: '@acme/ui',
149
+ version: '0.1.22',
150
+ __spec: '@acme/legacy-ui',
151
+ __autolinked: true,
152
+ });
153
+ const {integrations, ledger} = resolveProviders([
154
+ candidate('configured', ui),
155
+ candidate('autolinked', copy),
156
+ ]);
157
+
158
+ expect(integrations).toEqual([ui]);
159
+ expect(ledger.get(packageKey(copy.__packageDir))).toMatchObject({
160
+ outcome: 'alias-of',
161
+ aliasOf: packageKey(ui.__packageDir),
162
+ });
163
+ });
164
+
165
+ it('records an autolinked package that failed to load, and lists it nowhere', () => {
166
+ const packageDir = path.join(tmpDir, 'unreadable');
167
+ const {integrations, ledger} = resolveProviders([
168
+ {
169
+ source: 'autolinked',
170
+ spec: '@acme/unreadable',
171
+ packageDir,
172
+ error: 'bad json',
173
+ },
174
+ ]);
175
+
176
+ expect(integrations).toEqual([]);
177
+ expect(ledger.get(packageKey(packageDir))).toMatchObject({
178
+ outcome: 'load-failed',
179
+ error: 'bad json',
180
+ });
181
+ });
182
+
183
+ it('lets the package being authored replace its installed copy and claim first', () => {
184
+ const installed = record('installed', {
185
+ name: '@acme/widgets',
186
+ version: '1.0.0',
187
+ });
188
+ const rival = record('rival', {
189
+ name: '@acme/rival',
190
+ version: '1.0.0',
191
+ providerId: '@acme/widgets',
192
+ });
193
+ const local = record('local', {
194
+ name: '@acme/widgets',
195
+ version: '2.0.0-dev',
196
+ __local: true,
197
+ });
198
+ const {integrations, ledger} = resolveProviders([
199
+ candidate('configured', rival),
200
+ candidate('configured', installed),
201
+ candidate('local', local),
202
+ ]);
203
+
204
+ expect(integrations.map(entry => entry.name)).toEqual([
205
+ '@acme/rival',
206
+ '@acme/widgets',
207
+ ]);
208
+ expect(integrations[1]).toBe(local);
209
+ expect(ledger.get(packageKey(installed.__packageDir))).toMatchObject({
210
+ outcome: 'set-aside',
211
+ reason: 'replaced-by-authored',
212
+ winner: packageKey(local.__packageDir),
213
+ });
214
+ expect(ledger.get(packageKey(rival.__packageDir))).toMatchObject({
215
+ outcome: 'set-aside',
216
+ reason: 'authored-claims',
217
+ winner: packageKey(local.__packageDir),
218
+ });
219
+ });
220
+
221
+ it('keeps configured precedence ahead of autolinked', () => {
222
+ const configured = record('configured', {
223
+ name: '@acme/one',
224
+ version: '1.0.0',
225
+ });
226
+ const autolinked = record('autolinked', {
227
+ name: '@acme/two',
228
+ version: '1.0.0',
229
+ providerId: '@acme/one',
230
+ __autolinked: true,
231
+ });
232
+ const {ledger} = resolveProviders([
233
+ candidate('configured', configured),
234
+ candidate('autolinked', autolinked),
235
+ ]);
236
+
237
+ expect(outcomes(ledger)).toEqual([
238
+ ['@acme/one@1.0.0', 'contributes'],
239
+ ['@acme/two@1.0.0', 'set-aside'],
240
+ ]);
241
+ });
242
+
243
+ it('under the codemod policy, records the installed copy standing in', () => {
244
+ const local = record('local', {
245
+ name: '@acme/widgets',
246
+ version: '2.0.0-dev',
247
+ __local: true,
248
+ });
249
+ const installed = record('installed', {
250
+ name: '@acme/widgets',
251
+ version: '1.0.0',
252
+ });
253
+ const {integrations, ledger} = resolveProviders(
254
+ [candidate('configured', local), candidate('installed', installed)],
255
+ {codemods: true},
256
+ );
257
+
258
+ expect(integrations).toEqual([installed]);
259
+ expect(ledger.get(packageKey(local.__packageDir))).toMatchObject({
260
+ outcome: 'set-aside',
261
+ reason: 'installed-stands-in',
262
+ winner: packageKey(installed.__packageDir),
263
+ });
264
+ });
265
+ });
266
+
267
+ describe('markProviderConflicts', () => {
268
+ const a = {name: 'a', version: '1', providerId: 'x', __spec: 'a'};
269
+ const b = {name: 'b', version: '1', providerId: 'x', __spec: 'b'};
270
+ const alias = {name: 'a', version: '1', providerId: 'x', __spec: 'a-alias'};
271
+ const local = {
272
+ name: 'l',
273
+ version: '1',
274
+ providerId: 'x',
275
+ __spec: 'l',
276
+ __local: true,
277
+ };
278
+ /** @param {any[]} list */
279
+ const claimsOf = list =>
280
+ markProviderConflicts(list).map(entry => [
281
+ entry?.name ?? null,
282
+ entry?.__providerConflict?.claimedBy ?? null,
283
+ ]);
284
+
285
+ it('drops an alias of the winner and keeps a record listed twice', () => {
286
+ expect(claimsOf([a, b, alias, a, undefined])).toEqual([
287
+ ['a', null],
288
+ ['b', 'a'],
289
+ ['a', null],
290
+ [null, null],
291
+ ]);
292
+ });
293
+
294
+ it('lets the package being authored claim first', () => {
295
+ expect(claimsOf([a, undefined, b, alias, a, local])).toEqual([
296
+ ['a', 'l'],
297
+ [null, null],
298
+ ['b', 'l'],
299
+ ['a', 'l'],
300
+ ['a', 'l'],
301
+ ['l', null],
302
+ ]);
303
+ });
304
+ });
305
+
306
+ describe('Project.load keeps a ledger', () => {
307
+ /**
308
+ * @param {string} dir node_modules key
309
+ * @param {{name: string, version: string}} pkg
310
+ */
311
+ function install(dir, pkg) {
312
+ const pkgDir = path.join(tmpDir, 'node_modules', ...dir.split('/'));
313
+ fs.mkdirSync(pkgDir, {recursive: true});
314
+ fs.writeFileSync(path.join(pkgDir, 'package.json'), JSON.stringify(pkg));
315
+ fs.writeFileSync(
316
+ path.join(pkgDir, 'astryx.integration.mjs'),
317
+ 'export default {};\n',
318
+ );
319
+ return pkgDir;
320
+ }
321
+
322
+ it('records the autolink alias it drops as alias-of the loaded copy', async () => {
323
+ fs.writeFileSync(
324
+ path.join(tmpDir, 'package.json'),
325
+ JSON.stringify({
326
+ name: 'consumer',
327
+ dependencies: {
328
+ '@acme/legacy-ui': 'npm:@acme/ui@0.1.22',
329
+ '@acme/ui': '0.1.22',
330
+ },
331
+ }),
332
+ );
333
+ const legacy = install('@acme/legacy-ui', {
334
+ name: '@acme/ui',
335
+ version: '0.1.22',
336
+ });
337
+ const ui = install('@acme/ui', {name: '@acme/ui', version: '0.1.22'});
338
+
339
+ const project = await Project.load(tmpDir, {fresh: true});
340
+ expect(project.loadedIntegrations.map(entry => entry.__spec)).toEqual([
341
+ '@acme/legacy-ui',
342
+ ]);
343
+ const ledger = providerLedgerOf(project);
344
+ expect(ledger.get(packageKey(ui))).toMatchObject({
345
+ outcome: 'alias-of',
346
+ aliasOf: packageKey(legacy),
347
+ });
348
+ expect(await autolinkIntegrations({projectDir: tmpDir})).toHaveLength(1);
349
+ });
350
+
351
+ it('records a dependency key that repeats a configured package', async () => {
352
+ fs.writeFileSync(
353
+ path.join(tmpDir, 'package.json'),
354
+ JSON.stringify({name: 'consumer', dependencies: {'@acme/ui': '0.1.22'}}),
355
+ );
356
+ fs.writeFileSync(
357
+ path.join(tmpDir, 'astryx.config.mjs'),
358
+ "export default {integrations: ['@acme/ui']};\n",
359
+ );
360
+ const ui = install('@acme/ui', {name: '@acme/ui', version: '0.1.22'});
361
+
362
+ const ledger = providerLedgerOf(await Project.load(tmpDir, {fresh: true}));
363
+ expect([...ledger.keys()]).toEqual([packageKey(ui)]);
364
+ expect(ledger.get(packageKey(ui))?.refs.map(ref => ref.source)).toEqual([
365
+ 'configured',
366
+ 'autolinked',
367
+ ]);
368
+ });
369
+ });
@@ -0,0 +1,8 @@
1
+ // @generated by scripts/sync-api-types.mjs from the JSDoc in foundation/**/*.mjs.
2
+ // DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
3
+
4
+ /**
5
+ * @param {import('../../authoring/doctypes/theme/type.js').ThemeDoc} doc
6
+ * @returns {string}
7
+ */
8
+ export function themeDescriptorSource(doc: import("../../authoring/doctypes/theme/type.js").ThemeDoc): string;
@@ -0,0 +1,44 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file The source text of a typed integration theme descriptor.
5
+ *
6
+ * @input A theme's descriptor fields.
7
+ * @output The `.doc.mjs` source that declares them under their ThemeDoc type.
8
+ * @position Shared by `integration add theme`, which scaffolds a descriptor,
9
+ * and the codemod that turns a 0.6 theme catalog into descriptors, so both
10
+ * write the same bytes.
11
+ */
12
+
13
+ /**
14
+ * A single-quoted JavaScript string literal for any text.
15
+ * @param {string} value
16
+ */
17
+ function quote(value) {
18
+ const escaped = value
19
+ .replace(/\\/gu, '\\\\')
20
+ .replace(/'/gu, "\\'")
21
+ .replace(/\n/gu, '\\n')
22
+ .replace(/\r/gu, '\\r')
23
+ .replace(/\u2028/gu, '\\u2028')
24
+ .replace(/\u2029/gu, '\\u2029');
25
+ return `'${escaped}'`;
26
+ }
27
+
28
+ /**
29
+ * @param {import('../../authoring/doctypes/theme/type.js').ThemeDoc} doc
30
+ * @returns {string}
31
+ */
32
+ export function themeDescriptorSource(doc) {
33
+ return [
34
+ "/** @type {import('@astryxdesign/cli/authoring').ThemeDoc} */",
35
+ 'export default {',
36
+ " type: 'theme',",
37
+ ` name: ${quote(doc.name)},`,
38
+ ` displayName: ${quote(doc.displayName)},`,
39
+ ` description: ${quote(doc.description)},`,
40
+ ` maintained: ${doc.maintained},`,
41
+ '};',
42
+ '',
43
+ ].join('\n');
44
+ }
@@ -25,6 +25,23 @@ import * as fs from 'node:fs';
25
25
  import * as path from 'node:path';
26
26
  import {isValidSemver} from '../env/semver.mjs';
27
27
  import {findSourceOnlyCandidates} from './contribution-inventory.mjs';
28
+ import {
29
+ CODEMOD_FILE_RE,
30
+ CODEMOD_SKIP_DIRS,
31
+ CODEMOD_TEST_RE,
32
+ createFixContext,
33
+ DOC_CANDIDATE_RE,
34
+ heldRootFix,
35
+ newComponentDocNote,
36
+ notACodemodFix,
37
+ notAComponentFix,
38
+ readContributionStamp,
39
+ sharedCodemodsRootFix,
40
+ stampOf,
41
+ themeImporting,
42
+ TEMPLATE_CANDIDATE_RE,
43
+ } from './contribution-fixes.mjs';
44
+ import {MANIFEST_BASENAMES} from './integrations.mjs';
28
45
  import {discoverIntegrationCodemods} from '../../assets/codemods/integration-discovery.mjs';
29
46
  import {discoverIntegrationTemplatesForOne} from '../discovery/template-adapter.mjs';
30
47
  import * as componentDiscovery from '../discovery/component-discovery.mjs';
@@ -119,32 +136,82 @@ function checkRoots(resolved, issues) {
119
136
  */
120
137
  async function checkCodemods(integration, issues) {
121
138
  if (!integration.codemods || !fs.existsSync(integration.codemods)) return;
139
+ const context = integration.__packageDir
140
+ ? createFixContext(integration.__packageDir, integration)
141
+ : null;
142
+ const shared = context && sharedCodemodsRootFix(context);
122
143
  for (const entry of fs.readdirSync(integration.codemods, {
123
144
  withFileTypes: true,
124
145
  })) {
125
146
  if (
126
147
  entry.isFile() &&
127
- /\.(?:ts|mjs|js)$/u.test(entry.name) &&
128
- !/\.(?:test|spec|fixture)\.(?:ts|mjs|js)$/u.test(entry.name)
148
+ CODEMOD_FILE_RE.test(entry.name) &&
149
+ !CODEMOD_TEST_RE.test(entry.name) &&
150
+ // The manifest sits beside package.json and is never a codemod.
151
+ !(
152
+ integration.codemods === integration.__packageDir &&
153
+ MANIFEST_BASENAMES.includes(entry.name)
154
+ )
129
155
  ) {
156
+ const shown = path
157
+ .relative(
158
+ integration.__packageDir ?? path.dirname(integration.codemods),
159
+ path.join(integration.codemods, entry.name),
160
+ )
161
+ .split(path.sep)
162
+ .join('/');
163
+ const root = path.dirname(shown);
164
+ const full = path.join(integration.codemods, entry.name);
165
+ const stamp =
166
+ context &&
167
+ (DOC_CANDIDATE_RE.test(entry.name) ||
168
+ TEMPLATE_CANDIDATE_RE.test(entry.name))
169
+ ? stampOf(context, full)
170
+ : null;
171
+ // A theme's entry module may sit beside its descriptor.
172
+ const themeDoc = ['.doc.mjs', '.doc.ts', '.doc.js']
173
+ .map(suffix => full.replace(/\.(?:ts|mjs|js)$/u, suffix))
174
+ .find(candidate => candidate !== full && fs.existsSync(candidate));
175
+ const themeStamp =
176
+ context && !stamp && themeDoc ? stampOf(context, themeDoc) : null;
177
+ let fix = `Fix: move it into the folder named for the version it migrates to, for example ${root}/1.2.0/${entry.name}.`;
178
+ if (shared) fix = sharedCodemodsRootFix(context, entry.name) ?? fix;
179
+ else if (context && stamp) fix = notACodemodFix(context, full, stamp);
180
+ else if (context && themeDoc && themeStamp?.type === 'theme') {
181
+ fix = notACodemodFix(context, themeDoc, themeStamp, entry.name);
182
+ } else if (context) {
183
+ const owner = themeImporting(context, full);
184
+ if (owner) {
185
+ fix = notACodemodFix(
186
+ context,
187
+ owner.doc,
188
+ owner.stamp,
189
+ owner.source,
190
+ entry.name,
191
+ );
192
+ }
193
+ }
130
194
  issues.push(
131
195
  issueWarning(
132
196
  'codemod_outside_version',
133
- `Codemod file "${path.join(integration.codemods, entry.name)}" is outside a version folder, so upgrade will never load it.`,
197
+ `Codemod file "${shown}" is outside a version folder, so upgrade will never load it. ${fix}`,
134
198
  ),
135
199
  );
136
200
  }
137
201
  if (
138
202
  entry.isDirectory() &&
139
- !['node_modules', '.git', '__tests__', '__fixtures__'].includes(
140
- entry.name,
141
- ) &&
203
+ !CODEMOD_SKIP_DIRS.has(entry.name) &&
142
204
  !isValidSemver(entry.name)
143
205
  ) {
206
+ const fix =
207
+ shared ??
208
+ (context &&
209
+ heldRootFix(context, path.join(integration.codemods, entry.name))) ??
210
+ 'Fix: rename it to the exact version its codemods migrate to.';
144
211
  issues.push(
145
212
  issueError(
146
213
  'invalid_codemod_version',
147
- `Codemod folder "${entry.name}" is not an exact semver version such as 1.2.0.`,
214
+ `Codemod folder "${entry.name}" is not an exact semver version such as 1.2.0. ${fix}`,
148
215
  ),
149
216
  );
150
217
  }
@@ -169,7 +236,11 @@ async function checkTemplates(integration, issues) {
169
236
  try {
170
237
  const {errors} = await discoverIntegrationTemplatesForOne(integration);
171
238
  for (const e of errors) {
172
- issues.push(issueError('invalid_template', e.message));
239
+ issues.push({
240
+ code: e.code ?? 'invalid_template',
241
+ severity: e.severity ?? 'error',
242
+ message: e.message,
243
+ });
173
244
  }
174
245
  } catch (err) {
175
246
  issues.push(
@@ -179,41 +250,62 @@ async function checkTemplates(integration, issues) {
179
250
  }
180
251
 
181
252
  /**
182
- * Validate the integration's components via the landed ownership discovery.
183
- * Feature-detected: if the component-ownership export isn't present in this
184
- * build (sibling PR not yet merged), component validation is skipped rather
185
- * than hard-failing.
186
- *
187
- * `discoverIntegrationComponents` returns ownership records and does not throw
188
- * on a missing same-stem source — it records `sourcePath: null`. We surface
189
- * each such record as an `invalid_component` error.
253
+ * Validate the integration's components through the same load-and-parse boundary
254
+ * that component detail uses. Missing source and invalid metadata are reported
255
+ * per component so valid siblings remain available.
190
256
  * @param {LoadedIntegration} integration loaded-integration-shaped object
191
257
  * @param {Issue[]} issues
192
258
  */
193
259
  async function checkComponents(integration, issues) {
194
260
  if (!integration.components || !fs.existsSync(integration.components)) return;
195
- const discover = componentDiscovery.discoverIntegrationComponents;
261
+ const discover = componentDiscovery.discoverValidIntegrationComponents;
196
262
  if (typeof discover !== 'function') return; // feature not present yet
197
263
  try {
198
- const records = (await discover(integration)) ?? [];
199
- for (const record of records) {
200
- if (record?.sourcePath == null) {
201
- issues.push(
202
- issueError(
203
- 'invalid_component',
204
- `Component "${record?.name}" is missing its same-stem source file ${record?.name}.tsx.`,
205
- ),
206
- );
264
+ const {discovered, errors} = await discover(integration);
265
+ /** @type {import('./contribution-fixes.mjs').FixContext | undefined} */
266
+ let context;
267
+ const byName = new Map(discovered.map(record => [record.name, record]));
268
+ for (const error of errors) {
269
+ const record = byName.get(error.name);
270
+ if (record == null || record.sourcePath != null) {
271
+ issues.push(issueError('invalid_component', error.message));
272
+ continue;
207
273
  }
274
+ const docPath = record.docPath;
275
+ let fix = `Fix: add ${record.name}.tsx beside ${docPath ? path.basename(docPath) : `${record.name}.doc.mjs`}.`;
276
+ const stamp = docPath ? readContributionStamp(docPath, false) : null;
277
+ if (stamp && stamp.type !== 'component' && integration.__packageDir) {
278
+ context ??= createFixContext(integration.__packageDir, integration);
279
+ fix = notAComponentFix(context, docPath, stamp);
280
+ }
281
+ issues.push(issueError('invalid_component', `${error.message} ${fix}`));
208
282
  }
283
+ const overlap = integration.__packageDir
284
+ ? newComponentDocNote(
285
+ (context ??= createFixContext(integration.__packageDir, integration)),
286
+ )
287
+ : '';
209
288
  for (const name of findSourceOnlyCandidates(
210
289
  integration.components,
211
- records.map(record => record.name),
290
+ discovered.map(record => record.name),
212
291
  )) {
292
+ // A hidden doc still pairs with its source; discovery just skips it.
293
+ if (
294
+ ['.doc.ts', '.doc.mjs', '.doc.js'].some(suffix =>
295
+ fs.existsSync(
296
+ path.join(
297
+ /** @type {string} */ (integration.components),
298
+ `${name}${suffix}`,
299
+ ),
300
+ ),
301
+ )
302
+ ) {
303
+ continue;
304
+ }
213
305
  issues.push(
214
306
  issueWarning(
215
307
  'source_without_component_doc',
216
- `Component source "${name}.tsx" has no same-stem metadata file ${name}.doc.mjs, so Astryx ignores it.`,
308
+ `Component source "${name}.tsx" has no same-stem metadata file ${name}.doc.mjs, so Astryx ignores it. Fix: add ${name}.doc.mjs beside it with type: 'component'; \`astryx docs authoring component-doc\` lists its fields.${overlap}`,
217
309
  ),
218
310
  );
219
311
  }
@@ -250,7 +342,7 @@ async function checkDocs(integration, issues) {
250
342
  }
251
343
 
252
344
  /**
253
- * Validate the integration's source-theme catalog. The same discovery function
345
+ * Validate the integration's source-theme descriptors. The same discovery function
254
346
  * powers Project and theme list/add, so validation cannot accept a shape that
255
347
  * consumers later fail to use.
256
348
  * @param {LoadedIntegration} integration
@@ -75,6 +75,8 @@ export function allErrorCodes(): string[];
75
75
  * | 'ERR_UNKNOWN_FEATURE'
76
76
  * | 'ERR_UNKNOWN_CODEMOD'
77
77
  * | 'ERR_CODEMOD_FAILED'
78
+ * | 'ERR_CODEMOD_PROTECTED'
79
+ * | 'ERR_CODEMOD_PROTECTION_SOURCE'
78
80
  * | 'ERR_NOT_FOUND'
79
81
  * | 'ERR_NO_DOC'
80
82
  * | 'ERR_NO_SHOWCASE'
@@ -107,4 +109,4 @@ export const ERROR_CODES: Readonly<Record<ErrorCode, ErrorCode>>;
107
109
  /**
108
110
  * Stable error-code string union. Append-only.
109
111
  */
110
- export type ErrorCode = ("ERR_UNKNOWN" | "ERR_UNKNOWN_COMMAND" | "ERR_UNKNOWN_SUBCOMMAND" | "ERR_INVALID_OPTION" | "ERR_INVALID_ARGUMENT" | "ERR_MISSING_ARGUMENT" | "ERR_INVALID_LANG" | "ERR_INVALID_DETAIL" | "ERR_NODE_VERSION" | "ERR_CORE_NOT_FOUND" | "ERR_CORE_INCOMPATIBLE" | "ERR_UNKNOWN_COMPONENT" | "ERR_UNKNOWN_HOOK" | "ERR_UNKNOWN_TOPIC" | "ERR_UNKNOWN_SECTION" | "ERR_UNKNOWN_CATEGORY" | "ERR_UNKNOWN_TEMPLATE" | "ERR_AMBIGUOUS_TEMPLATE" | "ERR_AMBIGUOUS_COMPONENT" | "ERR_AMBIGUOUS_THEME" | "ERR_UNKNOWN_THEME" | "ERR_INTEGRATION_ROOT_CONFLICT" | "ERR_INTEGRATION_EXPORT_CONFLICT" | "ERR_UNKNOWN_PACKAGE" | "ERR_UNKNOWN_AGENT" | "ERR_UNKNOWN_FEATURE" | "ERR_UNKNOWN_CODEMOD" | "ERR_CODEMOD_FAILED" | "ERR_NOT_FOUND" | "ERR_NO_DOC" | "ERR_NO_SHOWCASE" | "ERR_NO_SOURCE" | "ERR_INVALID_DOC" | "ERR_FILE_NOT_FOUND" | "ERR_FILE_EXISTS" | "ERR_PATH_TRAVERSAL" | "ERR_WRITE_FAILED" | "ERR_THEME_INVALID" | "ERR_THEME_LOAD" | "ERR_PALETTE_GENERATION" | "ERR_VERSION_DETECT" | "ERR_INVALID_VERSION" | "ERR_DEP_MISSING" | "ERR_GH_CLI" | "ERR_UNKNOWN_POST" | "ERR_FETCH_FAILED" | "ERR_LAYOUT_PARSE" | "ERR_LAYOUT_INVALID" | "ERR_UNCLASSIFIED_EXIT" | "ERR_SIGNAL_TERMINATED");
112
+ export type ErrorCode = ("ERR_UNKNOWN" | "ERR_UNKNOWN_COMMAND" | "ERR_UNKNOWN_SUBCOMMAND" | "ERR_INVALID_OPTION" | "ERR_INVALID_ARGUMENT" | "ERR_MISSING_ARGUMENT" | "ERR_INVALID_LANG" | "ERR_INVALID_DETAIL" | "ERR_NODE_VERSION" | "ERR_CORE_NOT_FOUND" | "ERR_CORE_INCOMPATIBLE" | "ERR_UNKNOWN_COMPONENT" | "ERR_UNKNOWN_HOOK" | "ERR_UNKNOWN_TOPIC" | "ERR_UNKNOWN_SECTION" | "ERR_UNKNOWN_CATEGORY" | "ERR_UNKNOWN_TEMPLATE" | "ERR_AMBIGUOUS_TEMPLATE" | "ERR_AMBIGUOUS_COMPONENT" | "ERR_AMBIGUOUS_THEME" | "ERR_UNKNOWN_THEME" | "ERR_INTEGRATION_ROOT_CONFLICT" | "ERR_INTEGRATION_EXPORT_CONFLICT" | "ERR_UNKNOWN_PACKAGE" | "ERR_UNKNOWN_AGENT" | "ERR_UNKNOWN_FEATURE" | "ERR_UNKNOWN_CODEMOD" | "ERR_CODEMOD_FAILED" | "ERR_CODEMOD_PROTECTED" | "ERR_CODEMOD_PROTECTION_SOURCE" | "ERR_NOT_FOUND" | "ERR_NO_DOC" | "ERR_NO_SHOWCASE" | "ERR_NO_SOURCE" | "ERR_INVALID_DOC" | "ERR_FILE_NOT_FOUND" | "ERR_FILE_EXISTS" | "ERR_PATH_TRAVERSAL" | "ERR_WRITE_FAILED" | "ERR_THEME_INVALID" | "ERR_THEME_LOAD" | "ERR_PALETTE_GENERATION" | "ERR_VERSION_DETECT" | "ERR_INVALID_VERSION" | "ERR_DEP_MISSING" | "ERR_GH_CLI" | "ERR_UNKNOWN_POST" | "ERR_FETCH_FAILED" | "ERR_LAYOUT_PARSE" | "ERR_LAYOUT_INVALID" | "ERR_UNCLASSIFIED_EXIT" | "ERR_SIGNAL_TERMINATED");
@@ -34,6 +34,8 @@ export type ErrorCode =
34
34
  | 'ERR_UNKNOWN_FEATURE'
35
35
  | 'ERR_UNKNOWN_CODEMOD'
36
36
  | 'ERR_CODEMOD_FAILED'
37
+ | 'ERR_CODEMOD_PROTECTED'
38
+ | 'ERR_CODEMOD_PROTECTION_SOURCE'
37
39
  | 'ERR_NOT_FOUND'
38
40
  | 'ERR_NO_DOC'
39
41
  | 'ERR_NO_SHOWCASE'