@astryxdesign/cli 0.6.3 → 0.6.4-canary.0e1fbdb

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 (728) hide show
  1. package/CHANGELOG.md +194 -0
  2. package/README.md +152 -107
  3. package/api/blog/blog.doc.mjs +1 -0
  4. package/api/build/_adapter.d.mts +50 -0
  5. package/api/build/_adapter.mjs +60 -0
  6. package/api/build/build.doc.mjs +22 -10
  7. package/api/build/build.test.mjs +219 -8
  8. package/api/build/build.type.d.mts +91 -2
  9. package/api/build/build.type.mjs +52 -8
  10. package/api/build/help/help.d.mts +12 -5
  11. package/api/build/help/help.mjs +69 -6
  12. package/api/build/kit/kit.d.mts +4 -1
  13. package/api/build/kit/kit.mjs +208 -53
  14. package/api/build/kit/rank.d.mts +44 -0
  15. package/api/build/kit/rank.mjs +432 -0
  16. package/api/build/kit/rank.test.mjs +196 -0
  17. package/api/component/_adapter.d.mts +31 -12
  18. package/api/component/_adapter.mjs +79 -15
  19. package/api/component/component.d.mts +6 -3
  20. package/api/component/component.doc.mjs +49 -19
  21. package/api/component/component.mjs +339 -22
  22. package/api/component/component.test.mjs +38 -0
  23. package/api/component/component.type.d.mts +47 -11
  24. package/api/component/component.type.mjs +76 -24
  25. package/api/component/detail/blocks/blocks.d.mts +2 -1
  26. package/api/component/detail/blocks/blocks.mjs +4 -3
  27. package/api/component/list/list.d.mts +0 -5
  28. package/api/component/list/list.mjs +40 -11
  29. package/api/discover/_adapter.d.mts +114 -6
  30. package/api/discover/_adapter.mjs +372 -17
  31. package/api/discover/_adapter.test.mjs +215 -0
  32. package/api/discover/_catalog-view.d.mts +115 -0
  33. package/api/discover/_catalog-view.mjs +203 -0
  34. package/api/discover/_catalog-view.test.mjs +128 -0
  35. package/api/discover/detail/detail.d.mts +18 -6
  36. package/api/discover/detail/detail.mjs +67 -13
  37. package/api/discover/detail/detail.test.mjs +85 -0
  38. package/api/discover/detail/item/item.d.mts +26 -0
  39. package/api/discover/detail/item/item.mjs +78 -0
  40. package/api/discover/detail/item/item.test.mjs +73 -0
  41. package/api/discover/discover.d.mts +3 -9
  42. package/api/discover/discover.doc.mjs +62 -18
  43. package/api/discover/discover.mjs +220 -36
  44. package/api/discover/discover.test.mjs +11 -2
  45. package/api/discover/discover.type.d.mts +150 -11
  46. package/api/discover/discover.type.mjs +107 -17
  47. package/api/discover/list/list.d.mts +20 -6
  48. package/api/discover/list/list.mjs +45 -12
  49. package/api/discover/list/list.test.mjs +46 -0
  50. package/api/discover/search/search.d.mts +18 -16
  51. package/api/discover/search/search.mjs +102 -56
  52. package/api/discover/search/search.test.mjs +144 -10
  53. package/api/docs/_adapter.d.mts +277 -41
  54. package/api/docs/_adapter.mjs +993 -108
  55. package/api/docs/compiled-topics.test.mjs +78 -0
  56. package/api/docs/detail/detail.mjs +22 -63
  57. package/api/docs/detail/section/section.d.mts +1 -1
  58. package/api/docs/detail/section/section.mjs +54 -19
  59. package/api/docs/detail/section/section.test.mjs +50 -0
  60. package/api/docs/docOverlays.test.mjs +27 -1
  61. package/api/docs/docs.d.mts +10 -3
  62. package/api/docs/docs.doc.mjs +55 -16
  63. package/api/docs/docs.mjs +53 -10
  64. package/api/docs/docs.type.d.mts +221 -5
  65. package/api/docs/docs.type.mjs +153 -11
  66. package/api/docs/index/index.d.mts +18 -0
  67. package/api/docs/index/index.mjs +40 -0
  68. package/api/docs/index/index.test.mjs +62 -0
  69. package/api/docs/list/list.mjs +28 -12
  70. package/api/docs/node/node.d.mts +43 -0
  71. package/api/docs/node/node.mjs +192 -0
  72. package/api/docs/reference-blocks.test.mjs +406 -0
  73. package/api/doctor/doctor.d.mts +104 -1
  74. package/api/doctor/doctor.doc.mjs +18 -8
  75. package/api/doctor/doctor.mjs +635 -7
  76. package/api/doctor/doctor.test.mjs +732 -11
  77. package/api/doctor/doctor.type.d.mts +1 -1
  78. package/api/doctor/doctor.type.mjs +1 -1
  79. package/api/gap-report/gap-report.doc.mjs +27 -14
  80. package/api/hook/_adapter.mjs +19 -5
  81. package/api/hook/hook.doc.mjs +7 -3
  82. package/api/hook/hook.type.d.mts +3 -3
  83. package/api/hook/hook.type.mjs +11 -11
  84. package/api/hook/list/list.d.mts +2 -2
  85. package/api/hook/list/list.mjs +69 -17
  86. package/api/index.d.mts +2 -1
  87. package/api/index.mjs +6 -3
  88. package/api/init/init.doc.mjs +22 -12
  89. package/api/init/init.test.mjs +41 -1
  90. package/api/init/remove/remove.mjs +1 -1
  91. package/api/init/run/run.mjs +20 -10
  92. package/api/integration/add-contribution.component-names.test.mjs +120 -0
  93. package/api/integration/add-contribution.d.mts +2 -1
  94. package/api/integration/add-contribution.mjs +130 -15
  95. package/api/integration/add-contribution.test.mjs +258 -7
  96. package/api/integration/add-helpers.d.mts +5 -2
  97. package/api/integration/add-helpers.mjs +36 -9
  98. package/api/integration/add-theme.mjs +56 -65
  99. package/api/integration/add-theme.test.mjs +139 -21
  100. package/api/integration/authoring-checks.mjs +138 -28
  101. package/api/integration/authoring-checks.test.mjs +179 -7
  102. package/api/integration/authoring-checks.type.mjs +6 -1
  103. package/api/integration/integration-authoring.type.d.mts +3 -1
  104. package/api/integration/integration-authoring.type.mjs +2 -0
  105. package/api/integration/integration-block-exports.test.mjs +10 -6
  106. package/api/integration/integrationAdd.doc.mjs +14 -4
  107. package/api/integration/integrationAddAgentDoc.doc.mjs +1 -0
  108. package/api/integration/integrationAddCodemod.doc.mjs +1 -0
  109. package/api/integration/integrationAddComponent.doc.mjs +2 -1
  110. package/api/integration/integrationAddDoc.doc.mjs +8 -1
  111. package/api/integration/integrationAddTemplate.doc.mjs +1 -0
  112. package/api/integration/integrationAddTheme.doc.mjs +6 -5
  113. package/api/integration/integrationComponentConflicts.doc.mjs +1 -0
  114. package/api/integration/integrationDocConflicts.doc.mjs +2 -1
  115. package/api/integration/integrationPackCheck.doc.mjs +5 -4
  116. package/api/integration/integrationTemplateConflicts.doc.d.mts +3 -0
  117. package/api/integration/integrationTemplateConflicts.doc.mjs +4 -0
  118. package/api/integration/pack-check.lifecycle-output.test.mjs +107 -0
  119. package/api/integration/pack-check.mjs +160 -11
  120. package/api/integration/pack-check.test.mjs +477 -47
  121. package/api/integration/pack-check.type.d.mts +26 -2
  122. package/api/integration/pack-check.type.mjs +15 -2
  123. package/api/integration/summarizeIssues.doc.mjs +1 -0
  124. package/api/integration/template-conflict-compatibility.test.mjs +73 -0
  125. package/api/integration/validate-integration-fixes.test.mjs +1389 -0
  126. package/api/integration/validate-integration.mjs +52 -102
  127. package/api/integration/validate-integration.test.mjs +179 -26
  128. package/api/integration/validate-unread-theme-folders.test.mjs +110 -0
  129. package/api/integration/validateIntegration.doc.mjs +3 -2
  130. package/api/json/assertResponse.doc.mjs +2 -1
  131. package/api/json/envelope-types.test.mjs +76 -0
  132. package/api/json/index.ts +2 -0
  133. package/api/json/isError.doc.mjs +2 -1
  134. package/api/json/parseResponse.doc.mjs +3 -2
  135. package/api/layout/_adapter.mjs +20 -5
  136. package/api/layout/expand/expand.mjs +7 -5
  137. package/api/layout/expand/expand.path-safety.test.mjs +53 -0
  138. package/api/layout/grammar/grammar.mjs +2 -1
  139. package/api/layout/layoutCheck.doc.mjs +1 -0
  140. package/api/layout/layoutExpand.doc.mjs +2 -1
  141. package/api/layout/layoutGrammar.doc.mjs +1 -0
  142. package/api/search/search-return-type.test.mjs +54 -0
  143. package/api/search/search.d.mts +89 -12
  144. package/api/search/search.doc.mjs +8 -2
  145. package/api/search/search.mjs +697 -97
  146. package/api/search/search.type.d.mts +15 -3
  147. package/api/search/search.type.mjs +5 -2
  148. package/api/swizzle/copy/copy.mjs +28 -11
  149. package/api/swizzle/swizzle.doc.mjs +8 -5
  150. package/api/swizzle/swizzle.type.d.mts +2 -2
  151. package/api/swizzle/swizzle.type.mjs +2 -2
  152. package/api/template/copy/copy.mjs +18 -24
  153. package/api/template/copy/copy.test.mjs +26 -0
  154. package/api/template/list/list.mjs +1 -0
  155. package/api/template/table-floating-bulk-actions.test.mjs +66 -0
  156. package/api/template/template-integration.test.mjs +1072 -3
  157. package/api/template/template-suffix.test.mjs +41 -21
  158. package/api/template/template.d.mts +1 -1
  159. package/api/template/template.doc.mjs +32 -9
  160. package/api/template/template.mjs +45 -8
  161. package/api/template/template.type.d.mts +12 -14
  162. package/api/template/template.type.mjs +15 -14
  163. package/api/theme/_adapter.d.mts +2 -3
  164. package/api/theme/_adapter.mjs +4 -5
  165. package/api/theme/add/add.binary.test.mjs +84 -0
  166. package/api/theme/add/add.mjs +31 -22
  167. package/api/theme/add/add.rollback.test.mjs +158 -0
  168. package/api/theme/add/add.staging.test.mjs +83 -0
  169. package/api/theme/add/add.test.mjs +14 -1
  170. package/api/theme/build/build.family.test.mjs +7 -12
  171. package/api/theme/build/build.mjs +140 -59
  172. package/api/theme/build/build.public-component-vars.test.mjs +1 -1
  173. package/api/theme/build/build.receipt-doc.test.mjs +111 -0
  174. package/api/theme/build/build.rollback.test.mjs +148 -0
  175. package/api/theme/build/build.test.mjs +127 -0
  176. package/api/theme/build/font-warning.mjs +3 -3
  177. package/api/theme/build/font-warning.test.mjs +5 -2
  178. package/api/theme/generateTonalPalette.doc.mjs +2 -2
  179. package/api/theme/integration-themes.test.mjs +39 -28
  180. package/api/theme/list/list.test.mjs +19 -20
  181. package/api/theme/listThemes.doc.mjs +6 -5
  182. package/api/theme/palette/generate/generate.mjs +8 -3
  183. package/api/theme/palette/generate/generate.test.mjs +96 -0
  184. package/api/theme/palette/generate/generator.d.mts +10 -13
  185. package/api/theme/palette/generate/generator.mjs +15 -4
  186. package/api/theme/palette/generate/generator.test.mjs +10 -0
  187. package/api/theme/template/template.mjs +11 -2
  188. package/api/theme/template/template.test.mjs +20 -0
  189. package/api/theme/theme.type.d.mts +170 -11
  190. package/api/theme/theme.type.mjs +94 -27
  191. package/api/theme/themeAdd.doc.mjs +12 -12
  192. package/api/theme/themeBuild.doc.mjs +21 -17
  193. package/api/theme/themeList.doc.mjs +6 -3
  194. package/api/theme/themeListAvailable.doc.mjs +6 -3
  195. package/api/theme/themePaletteGenerate.doc.mjs +16 -8
  196. package/api/theme/themeTargets.doc.mjs +4 -2
  197. package/api/theme/themeTemplate.doc.mjs +8 -3
  198. package/api/upgrade/_adapter.d.mts +32 -5
  199. package/api/upgrade/_adapter.mjs +139 -22
  200. package/api/upgrade/list/list.mjs +2 -1
  201. package/api/upgrade/list/list.test.mjs +73 -0
  202. package/api/upgrade/project-context.test.mjs +272 -0
  203. package/api/upgrade/provider-agreement.test.mjs +152 -0
  204. package/api/upgrade/run/files-changed.test.mjs +111 -0
  205. package/api/upgrade/run/run.mjs +358 -59
  206. package/api/upgrade/status/status.mjs +2 -2
  207. package/api/upgrade/upgrade.doc.mjs +32 -23
  208. package/api/upgrade/upgrade.type.d.mts +43 -5
  209. package/api/upgrade/upgrade.type.mjs +29 -13
  210. package/assets/codemods/__tests__/registry.test.mjs +1 -0
  211. package/assets/codemods/__tests__/runner.test.mjs +332 -8
  212. package/assets/codemods/file-count.test.mjs +163 -0
  213. package/assets/codemods/integration-discovery.mjs +48 -4
  214. package/assets/codemods/integration-discovery.test.mjs +73 -0
  215. package/assets/codemods/integration-runner.mjs +59 -7
  216. package/assets/codemods/integration-runner.protection.test.mjs +153 -0
  217. package/assets/codemods/registry.mjs +1 -0
  218. package/assets/codemods/run-codemod.mjs +177 -34
  219. package/assets/codemods/runner.mjs +353 -104
  220. package/assets/codemods/term-log.mjs +32 -8
  221. package/assets/codemods/term-log.test.mjs +19 -1
  222. package/assets/codemods/transform-prop.mjs +109 -0
  223. package/assets/codemods/transform-prop.test.mjs +95 -0
  224. package/assets/codemods/transforms/v0.0.14/__tests__/rename-status-variants.test.mjs +86 -165
  225. package/assets/codemods/transforms/v0.0.14/rename-status-variants.mjs +72 -210
  226. package/assets/codemods/transforms/v0.1.8/__tests__/rename-avatar-size-scale.test.mjs +83 -115
  227. package/assets/codemods/transforms/v0.1.8/rename-avatar-size-scale.mjs +57 -186
  228. package/assets/codemods/transforms/v0.3.0/__tests__/unwrap-authoring-factories.test.mjs +27 -5
  229. package/assets/codemods/transforms/v0.3.0/unwrap-authoring-factories.mjs +20 -5
  230. package/assets/codemods/transforms/v0.6.0/__tests__/next-codemods.test.mjs +47 -0
  231. package/assets/codemods/transforms/v0.6.0/rename-resizable-pixel-bounds.mjs +57 -10
  232. package/assets/codemods/transforms/v0.6.4/__tests__/migrate-native-picker-to-presentation.test.mjs +63 -0
  233. package/assets/codemods/transforms/v0.6.4/__tests__/migrate-theme-catalog-to-descriptors.test.mjs +220 -0
  234. package/assets/codemods/transforms/v0.6.4/index.mjs +31 -0
  235. package/assets/codemods/transforms/v0.6.4/migrate-native-picker-to-presentation.mjs +148 -0
  236. package/assets/codemods/transforms/v0.6.4/migrate-theme-catalog-to-descriptors.mjs +141 -0
  237. package/assets/docs/README.md +12 -1
  238. package/assets/docs/authoring.doc.mjs +14 -0
  239. package/assets/docs/browser-support.doc.mjs +11 -11
  240. package/assets/docs/color.doc.mjs +8 -2
  241. package/assets/docs/elevation.doc.mjs +6 -4
  242. package/assets/docs/getting-started.doc.mjs +6 -17
  243. package/assets/docs/icons.doc.mjs +2 -21
  244. package/assets/docs/illustrations.doc.mjs +7 -15
  245. package/assets/docs/internationalization.doc.mjs +7 -5
  246. package/assets/docs/layout.doc.dense.mjs +132 -84
  247. package/assets/docs/layout.doc.mjs +134 -78
  248. package/assets/docs/migration.doc.mjs +19 -21
  249. package/assets/docs/motion.doc.mjs +16 -3
  250. package/assets/docs/principles.doc.dense.mjs +5 -5
  251. package/assets/docs/principles.doc.mjs +14 -6
  252. package/assets/docs/principles.doc.zh.mjs +6 -6
  253. package/assets/docs/shape.doc.mjs +8 -3
  254. package/assets/docs/spacing.doc.mjs +7 -2
  255. package/assets/docs/styling-libraries.doc.mjs +10 -6
  256. package/assets/docs/styling.doc.mjs +22 -26
  257. package/assets/docs/theme.doc.dense.mjs +58 -18
  258. package/assets/docs/theme.doc.mjs +60 -50
  259. package/assets/docs/theme.doc.zh.mjs +9 -8
  260. package/assets/docs/tokens.doc.dense.mjs +2 -2
  261. package/assets/docs/tokens.doc.mjs +390 -9
  262. package/assets/docs/tokens.doc.zh.mjs +2 -2
  263. package/assets/docs/tree/add-a-component.doc.mjs +75 -0
  264. package/assets/docs/tree/add-a-theme.doc.mjs +85 -0
  265. package/assets/docs/tree/add-a-topic.doc.mjs +144 -0
  266. package/assets/docs/tree/agent-guidance.doc.mjs +138 -0
  267. package/assets/docs/tree/api.doc.mjs +30 -0
  268. package/assets/docs/tree/block-template.doc.mjs +130 -0
  269. package/assets/docs/tree/build-the-template.doc.mjs +28 -0
  270. package/assets/docs/tree/building-blocks.doc.mjs +46 -0
  271. package/assets/docs/tree/check-your-docs.doc.mjs +137 -0
  272. package/assets/docs/tree/checks.doc.mjs +119 -0
  273. package/assets/docs/tree/cli.doc.mjs +23 -0
  274. package/assets/docs/tree/codemods.doc.mjs +147 -0
  275. package/assets/docs/tree/commands.doc.mjs +25 -0
  276. package/assets/docs/tree/component-family.doc.mjs +113 -0
  277. package/assets/docs/tree/component-imports.doc.mjs +69 -0
  278. package/assets/docs/tree/component-lookups.doc.mjs +149 -0
  279. package/assets/docs/tree/components.doc.mjs +23 -0
  280. package/assets/docs/tree/configuration.doc.mjs +23 -0
  281. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +182 -0
  282. package/assets/docs/tree/define-the-theme.doc.mjs +118 -0
  283. package/assets/docs/tree/describe-the-component.doc.mjs +57 -0
  284. package/assets/docs/tree/docs.doc.mjs +21 -0
  285. package/assets/docs/tree/document-the-template.doc.mjs +28 -0
  286. package/assets/docs/tree/document-the-theme.doc.mjs +68 -0
  287. package/assets/docs/tree/export-template-assets.doc.mjs +147 -0
  288. package/assets/docs/tree/extend-or-replace.doc.mjs +103 -0
  289. package/assets/docs/tree/fonts-and-assets.doc.mjs +106 -0
  290. package/assets/docs/tree/generate-a-palette.doc.mjs +66 -0
  291. package/assets/docs/tree/grade-template-with-agent.doc.mjs +105 -0
  292. package/assets/docs/tree/help.doc.mjs +16 -0
  293. package/assets/docs/tree/integrations.doc.mjs +40 -0
  294. package/assets/docs/tree/links.doc.mjs +98 -0
  295. package/assets/docs/tree/package-and-test.doc.mjs +32 -0
  296. package/assets/docs/tree/page-template.doc.mjs +71 -0
  297. package/assets/docs/tree/publishing.doc.mjs +111 -0
  298. package/assets/docs/tree/quick-start.doc.mjs +272 -0
  299. package/assets/docs/tree/replace-a-core-component.doc.mjs +104 -0
  300. package/assets/docs/tree/replace-a-core-template.doc.mjs +172 -0
  301. package/assets/docs/tree/sections-and-placement.doc.mjs +108 -0
  302. package/assets/docs/tree/see-it-in-an-app.doc.mjs +59 -0
  303. package/assets/docs/tree/ship.doc.mjs +16 -0
  304. package/assets/docs/tree/short-and-findable.doc.mjs +108 -0
  305. package/assets/docs/tree/single-component.doc.mjs +165 -0
  306. package/assets/docs/tree/start-a-template.doc.mjs +143 -0
  307. package/assets/docs/tree/subcomponent.doc.mjs +115 -0
  308. package/assets/docs/tree/template-assets.doc.mjs +64 -0
  309. package/assets/docs/tree/template-doc-overview.doc.mjs +109 -0
  310. package/assets/docs/tree/template-fonts.doc.mjs +102 -0
  311. package/assets/docs/tree/template-grading-rubric.doc.mjs +452 -0
  312. package/assets/docs/tree/template-icons.doc.mjs +97 -0
  313. package/assets/docs/tree/template-images-media.doc.mjs +127 -0
  314. package/assets/docs/tree/template-styles.doc.mjs +93 -0
  315. package/assets/docs/tree/templates.doc.mjs +34 -0
  316. package/assets/docs/tree/test-in-an-app.doc.mjs +115 -0
  317. package/assets/docs/tree/test-template-in-app.doc.mjs +128 -0
  318. package/assets/docs/tree/themes.doc.mjs +39 -0
  319. package/assets/docs/tree/troubleshooting.doc.mjs +149 -0
  320. package/assets/docs/tree/upgrading.doc.mjs +103 -0
  321. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +51 -0
  322. package/assets/docs/tree/verify-packed-template.doc.mjs +77 -0
  323. package/assets/docs/tree/versioning.doc.mjs +161 -0
  324. package/assets/docs/tree/write-good-templates.doc.mjs +64 -0
  325. package/assets/docs/tree/write-the-template-file.doc.mjs +154 -0
  326. package/assets/docs/typography.doc.mjs +24 -4
  327. package/assets/docs/working-with-ai.doc.mjs +34 -26
  328. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.doc.mjs +1 -1
  329. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.tsx +1 -3
  330. package/assets/templates/blocks/components/DateInput/DateInputDateRange.tsx +1 -1
  331. package/assets/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.tsx +1 -1
  332. package/assets/templates/blocks/components/Item/ItemDocumentTabs.doc.mjs +14 -0
  333. package/assets/templates/blocks/components/Item/ItemDocumentTabs.tsx +100 -0
  334. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.doc.mjs +14 -0
  335. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.tsx +61 -0
  336. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.doc.mjs +14 -0
  337. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.tsx +126 -0
  338. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.doc.mjs +15 -0
  339. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.tsx +86 -0
  340. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.doc.mjs +14 -0
  341. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.tsx +99 -0
  342. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.doc.mjs +14 -0
  343. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.tsx +122 -0
  344. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.doc.mjs +14 -0
  345. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.tsx +95 -0
  346. package/assets/templates/blocks/components/Table/TableBulkActionsTable.doc.mjs +14 -0
  347. package/assets/templates/blocks/components/Table/TableBulkActionsTable.tsx +71 -0
  348. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.doc.mjs +19 -0
  349. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.tsx +139 -0
  350. package/assets/templates/blocks/components/TimeInput/TimeInputConstrained.tsx +1 -0
  351. package/assets/templates/blocks/components/Timer/TimerFormats.doc.mjs +14 -0
  352. package/assets/templates/blocks/components/Timer/TimerFormats.tsx +34 -0
  353. package/assets/templates/blocks/components/Timer/TimerInline.doc.mjs +14 -0
  354. package/assets/templates/blocks/components/Timer/TimerInline.tsx +14 -0
  355. package/assets/templates/blocks/components/Timer/TimerShowcase.doc.mjs +13 -0
  356. package/assets/templates/blocks/components/Timer/TimerShowcase.tsx +47 -0
  357. package/assets/templates/blocks/components/Timer/TimerTypography.doc.mjs +14 -0
  358. package/assets/templates/blocks/components/Timer/TimerTypography.tsx +31 -0
  359. package/assets/templates/blocks/components/Toolbar/ToolbarTableFilter.doc.mjs +19 -3
  360. package/assets/templates/blocks/components/Toolbar/ToolbarTableFilter.tsx +383 -65
  361. package/assets/templates/pages/table-tree/page.tsx +1704 -0
  362. package/assets/templates/pages/table-tree/template.doc.mjs +12 -0
  363. package/assets/templates/themes/butter/butterTheme.doc.mjs +11 -0
  364. package/assets/templates/themes/chocolate/chocolateTheme.doc.mjs +11 -0
  365. package/assets/templates/themes/gothic/gothicTheme.doc.mjs +11 -0
  366. package/assets/templates/themes/matcha/matchaTheme.doc.mjs +11 -0
  367. package/assets/templates/themes/neutral/neutralTheme.doc.mjs +11 -0
  368. package/assets/templates/themes/stone/stoneTheme.doc.mjs +11 -0
  369. package/assets/templates/themes/y2k/y2kTheme.doc.mjs +11 -0
  370. package/authoring/_shared/contract.ts +22 -0
  371. package/authoring/codemod/codemod.doc.mjs +7 -2
  372. package/authoring/codemod/parse.d.mts +8 -8
  373. package/authoring/codemod/parse.mjs +8 -6
  374. package/authoring/codemod/type.ts +12 -0
  375. package/authoring/config/config.doc.mjs +11 -3
  376. package/authoring/config/debug-composition.test.mjs +92 -0
  377. package/authoring/config/parse.d.mts +15 -13
  378. package/authoring/config/parse.mjs +27 -8
  379. package/authoring/config/parse.test.mjs +8 -0
  380. package/authoring/config/type.ts +29 -6
  381. package/authoring/debug/debug.doc.d.mts +11 -0
  382. package/authoring/debug/debug.doc.mjs +182 -0
  383. package/authoring/debug/parse.d.mts +8 -8
  384. package/authoring/debug/parse.mjs +3 -3
  385. package/authoring/discover/discover.doc.d.mts +13 -0
  386. package/authoring/discover/discover.doc.mjs +138 -0
  387. package/authoring/discover/parse.d.mts +24 -0
  388. package/authoring/discover/parse.mjs +128 -0
  389. package/authoring/discover/parse.test.mjs +124 -0
  390. package/authoring/discover/type.ts +87 -0
  391. package/authoring/doctypes/_schema.d.mts +792 -24
  392. package/authoring/doctypes/_schema.mjs +549 -39
  393. package/authoring/doctypes/base/graph-fields.doc.d.mts +9 -0
  394. package/authoring/doctypes/base/graph-fields.doc.mjs +64 -0
  395. package/authoring/doctypes/base/type.ts +43 -0
  396. package/authoring/doctypes/command/command.doc.mjs +4 -3
  397. package/authoring/doctypes/command/parse.d.mts +2 -2
  398. package/authoring/doctypes/command/parse.mjs +1 -1
  399. package/authoring/doctypes/command/type.ts +5 -4
  400. package/authoring/doctypes/component/component.doc.mjs +12 -3
  401. package/authoring/doctypes/component/parse.d.mts +2 -2
  402. package/authoring/doctypes/component/parse.mjs +1 -1
  403. package/authoring/doctypes/component/type.ts +14 -5
  404. package/authoring/doctypes/doctypes-new.test.mjs +48 -6
  405. package/authoring/doctypes/enum/enum.doc.mjs +1 -1
  406. package/authoring/doctypes/enum/parse.d.mts +2 -2
  407. package/authoring/doctypes/enum/parse.mjs +1 -1
  408. package/authoring/doctypes/enum/type.ts +4 -2
  409. package/authoring/doctypes/function/function.doc.mjs +7 -2
  410. package/authoring/doctypes/function/parse.d.mts +2 -2
  411. package/authoring/doctypes/function/parse.mjs +1 -1
  412. package/authoring/doctypes/function/type.ts +9 -4
  413. package/authoring/doctypes/hook/hook.doc.mjs +4 -0
  414. package/authoring/doctypes/hook/parse.d.mts +2 -2
  415. package/authoring/doctypes/hook/parse.mjs +1 -1
  416. package/authoring/doctypes/hook/type.ts +5 -4
  417. package/authoring/doctypes/legacy.d.mts +8 -6
  418. package/authoring/doctypes/legacy.mjs +5 -4
  419. package/authoring/doctypes/load-contract.test.mjs +233 -0
  420. package/authoring/doctypes/namespace/namespace.doc.d.mts +9 -0
  421. package/authoring/doctypes/namespace/namespace.doc.mjs +128 -0
  422. package/authoring/doctypes/namespace/parse.d.mts +12 -0
  423. package/authoring/doctypes/namespace/parse.mjs +25 -0
  424. package/authoring/doctypes/namespace/parse.test.mjs +163 -0
  425. package/authoring/doctypes/namespace/type.ts +74 -0
  426. package/authoring/doctypes/parse.d.mts +22 -18
  427. package/authoring/doctypes/parse.mjs +22 -11
  428. package/authoring/doctypes/parse.test.mjs +77 -3
  429. package/authoring/doctypes/reference/parse.d.mts +2 -2
  430. package/authoring/doctypes/reference/parse.mjs +8 -5
  431. package/authoring/doctypes/reference/reference.doc.mjs +55 -6
  432. package/authoring/doctypes/reference/type.ts +75 -7
  433. package/authoring/doctypes/schema/parse.d.mts +2 -2
  434. package/authoring/doctypes/schema/parse.mjs +1 -1
  435. package/authoring/doctypes/schema/schema.doc.mjs +2 -2
  436. package/authoring/doctypes/schema/type.ts +4 -4
  437. package/authoring/doctypes/template/parse.d.mts +94 -1
  438. package/authoring/doctypes/template/parse.mjs +40 -2
  439. package/authoring/doctypes/template/parse.test.mjs +26 -2
  440. package/authoring/doctypes/template/template.doc.mjs +13 -3
  441. package/authoring/doctypes/template/type.ts +13 -2
  442. package/authoring/doctypes/theme/parse.d.mts +35 -0
  443. package/authoring/doctypes/theme/parse.mjs +76 -0
  444. package/authoring/doctypes/theme/theme.doc.d.mts +9 -0
  445. package/authoring/doctypes/theme/theme.doc.mjs +79 -0
  446. package/authoring/doctypes/theme/type.ts +42 -0
  447. package/authoring/doctypes/types.ts +12 -10
  448. package/authoring/gap-report/gap-report.doc.d.mts +12 -0
  449. package/authoring/gap-report/gap-report.doc.mjs +183 -0
  450. package/authoring/gap-report/parse.d.mts +10 -10
  451. package/authoring/gap-report/parse.mjs +6 -6
  452. package/authoring/gap-report/type.ts +1 -1
  453. package/authoring/identity/identity.doc.d.mts +9 -0
  454. package/authoring/identity/identity.doc.mjs +61 -0
  455. package/authoring/identity/type.ts +132 -0
  456. package/authoring/index.d.mts +3 -0
  457. package/authoring/index.d.ts +62 -17
  458. package/authoring/index.mjs +4 -1
  459. package/authoring/integration/integration.doc.mjs +22 -13
  460. package/authoring/integration/parse.d.mts +2 -2
  461. package/authoring/integration/parse.mjs +1 -1
  462. package/authoring/integration/parse.test.mjs +10 -1
  463. package/authoring/integration/schema.d.mts +6 -4
  464. package/authoring/integration/schema.mjs +9 -3
  465. package/authoring/integration/type.ts +19 -8
  466. package/authoring/shadcn/receipt.d.mts +6 -6
  467. package/clients/cli/__tests__/cliManifest.test.ts +27 -29
  468. package/clients/cli/command-load-failure.test.mjs +83 -0
  469. package/clients/cli/commands/blog.doc.mjs +1 -1
  470. package/clients/cli/commands/blog.mjs +23 -8
  471. package/clients/cli/commands/blog.test.mjs +42 -1
  472. package/clients/cli/commands/build-theme.adaptations.test.mjs +100 -1
  473. package/clients/cli/commands/build-theme.ascii-output.test.mjs +161 -0
  474. package/clients/cli/commands/build-theme.flag-docs.test.mjs +110 -0
  475. package/clients/cli/commands/build-theme.mjs +16 -50
  476. package/clients/cli/commands/build-theme.path-safety.test.mjs +86 -1
  477. package/clients/cli/commands/build-theme.variants.test.mjs +76 -0
  478. package/clients/cli/commands/build.doc.mjs +16 -8
  479. package/clients/cli/commands/build.exit-codes-doc.test.mjs +50 -0
  480. package/clients/cli/commands/build.mjs +137 -114
  481. package/clients/cli/commands/build.playbook.test.mjs +75 -0
  482. package/clients/cli/commands/build.text-fields.test.mjs +81 -0
  483. package/clients/cli/commands/component/index.mjs +153 -61
  484. package/clients/cli/commands/component-batch.test.mjs +341 -0
  485. package/clients/cli/commands/component-ownership.test.mjs +92 -3
  486. package/clients/cli/commands/component-package.test.mjs +46 -0
  487. package/clients/cli/commands/component-resolution.test.mjs +21 -0
  488. package/clients/cli/commands/component.doc.mjs +28 -10
  489. package/clients/cli/commands/component.test.mjs +19 -0
  490. package/clients/cli/commands/detail-levels.test.mjs +2 -2
  491. package/clients/cli/commands/discover.broken-integration.test.mjs +52 -5
  492. package/clients/cli/commands/discover.components-flag.test.mjs +97 -0
  493. package/clients/cli/commands/discover.doc.mjs +55 -9
  494. package/clients/cli/commands/discover.mjs +393 -118
  495. package/clients/cli/commands/discover.sources.test.mjs +267 -0
  496. package/clients/cli/commands/discover.text-projection.test.mjs +103 -0
  497. package/clients/cli/commands/docs.doc.mjs +28 -6
  498. package/clients/cli/commands/docs.mjs +295 -38
  499. package/clients/cli/commands/doctor-integration-components.doc.mjs +1 -1
  500. package/clients/cli/commands/doctor-integration-docs.doc.mjs +6 -5
  501. package/clients/cli/commands/doctor-integration-templates.doc.mjs +15 -8
  502. package/clients/cli/commands/doctor-integration-validate.doc.mjs +1 -1
  503. package/clients/cli/commands/doctor-integration.doc.mjs +1 -1
  504. package/clients/cli/commands/doctor-integration.package-json.test.mjs +53 -0
  505. package/clients/cli/commands/doctor-integration.test.mjs +143 -8
  506. package/clients/cli/commands/doctor.doc.mjs +4 -2
  507. package/clients/cli/commands/doctor.mjs +108 -37
  508. package/clients/cli/commands/doctor.test.mjs +42 -0
  509. package/clients/cli/commands/gap-report.doc.mjs +27 -15
  510. package/clients/cli/commands/gap-report.test.mjs +72 -0
  511. package/clients/cli/commands/hook/index.mjs +7 -17
  512. package/clients/cli/commands/hook.doc.mjs +1 -1
  513. package/clients/cli/commands/hook.text-projection.test.mjs +45 -0
  514. package/clients/cli/commands/init.doc.mjs +24 -10
  515. package/clients/cli/commands/init.flag-help.test.mjs +153 -0
  516. package/clients/cli/commands/integration-add.controls.test.mjs +132 -0
  517. package/clients/cli/commands/integration-add.doc.mjs +39 -13
  518. package/clients/cli/commands/integration-authoring.test.mjs +74 -19
  519. package/clients/cli/commands/integration-pack.doc.mjs +6 -10
  520. package/clients/cli/commands/integration-real-world.test.mjs +4 -10
  521. package/clients/cli/commands/integration-verify.doc.mjs +22 -0
  522. package/clients/cli/commands/integration.doc.mjs +5 -5
  523. package/clients/cli/commands/integration.mjs +75 -43
  524. package/clients/cli/commands/interactive-guard.test.mjs +101 -24
  525. package/clients/cli/commands/json-contract.test.mjs +33 -0
  526. package/clients/cli/commands/layout-check.doc.mjs +15 -4
  527. package/clients/cli/commands/layout-expand.doc.mjs +22 -5
  528. package/clients/cli/commands/layout-grammar.doc.mjs +1 -1
  529. package/clients/cli/commands/layout.doc.mjs +3 -3
  530. package/clients/cli/commands/layout.mjs +21 -9
  531. package/clients/cli/commands/layout.path-help.test.mjs +33 -0
  532. package/clients/cli/commands/layout.stdin-cap.test.mjs +47 -0
  533. package/clients/cli/commands/layout.text-fields.test.mjs +39 -0
  534. package/clients/cli/commands/manifest.doc.mjs +2 -2
  535. package/clients/cli/commands/no-prompt-wording.test.mjs +94 -0
  536. package/clients/cli/commands/search.doc.mjs +16 -6
  537. package/clients/cli/commands/search.mjs +49 -11
  538. package/clients/cli/commands/search.test.mjs +92 -0
  539. package/clients/cli/commands/setup-nudge.test.mjs +6 -0
  540. package/clients/cli/commands/swizzle.doc.mjs +4 -3
  541. package/clients/cli/commands/swizzle.path-safety.test.mjs +42 -0
  542. package/clients/cli/commands/template.doc.mjs +53 -14
  543. package/clients/cli/commands/template.flag-help.test.mjs +117 -0
  544. package/clients/cli/commands/template.mjs +4 -91
  545. package/clients/cli/commands/template.path-help.test.mjs +40 -0
  546. package/clients/cli/commands/text-json-parity.test.mjs +725 -0
  547. package/clients/cli/commands/theme-add.doc.mjs +5 -4
  548. package/clients/cli/commands/theme-build.doc.mjs +8 -7
  549. package/clients/cli/commands/theme-list.doc.mjs +2 -2
  550. package/clients/cli/commands/theme-palette-generate.doc.mjs +12 -7
  551. package/clients/cli/commands/theme-palette-generate.test.mjs +19 -0
  552. package/clients/cli/commands/theme-palette.doc.mjs +2 -3
  553. package/clients/cli/commands/theme-targets.behavior.test.mjs +4 -3
  554. package/clients/cli/commands/theme-targets.doc.mjs +3 -3
  555. package/clients/cli/commands/theme-template.behavior.test.mjs +12 -0
  556. package/clients/cli/commands/theme-template.doc.mjs +2 -2
  557. package/clients/cli/commands/theme.doc.mjs +3 -2
  558. package/clients/cli/commands/upgrade.ascii-output.test.mjs +87 -0
  559. package/clients/cli/commands/upgrade.doc.mjs +83 -12
  560. package/clients/cli/commands/upgrade.file-protection.test.mjs +228 -0
  561. package/clients/cli/commands/upgrade.flag-help.test.mjs +188 -0
  562. package/clients/cli/commands/upgrade.hook-output.test.mjs +88 -0
  563. package/clients/cli/commands/upgrade.mjs +29 -7
  564. package/clients/cli/formatters/index.mjs +164 -1
  565. package/clients/cli/formatters/index.test.mjs +97 -0
  566. package/clients/cli/index.mjs +47 -34
  567. package/clients/cli/latest-version-env.test.mjs +50 -0
  568. package/clients/cli/lib/cli-error.test.mjs +7 -0
  569. package/clients/cli/lib/component-format.mjs +9 -9
  570. package/clients/cli/lib/component-format.test.mjs +1 -1
  571. package/clients/cli/lib/define-command.mjs +56 -6
  572. package/clients/cli/lib/define-command.test.mjs +54 -0
  573. package/clients/cli/lib/doc-text-ascii.test.mjs +82 -0
  574. package/clients/cli/lib/exit-codes.test.mjs +113 -0
  575. package/clients/cli/lib/hook-format.mjs +19 -10
  576. package/clients/cli/lib/json-shim.mjs +62 -16
  577. package/clients/cli/lib/json-shim.test.mjs +83 -0
  578. package/clients/cli/lib/manifest.d.ts +2 -0
  579. package/clients/cli/lib/manifest.mjs +53 -6
  580. package/clients/cli/lib/manifest.test.mjs +22 -2
  581. package/clients/cli/lib/parse-error-format.test.mjs +81 -0
  582. package/foundation/agent-docs/agent-docs.d.mts +7 -2
  583. package/foundation/agent-docs/agent-docs.mjs +83 -13
  584. package/foundation/agent-docs/agent-docs.path-safety.test.mjs +266 -4
  585. package/foundation/config/integration-debug.test.mjs +28 -3
  586. package/foundation/config/project-themes.test.mjs +11 -19
  587. package/foundation/config/project.d.mts +20 -11
  588. package/foundation/config/project.mjs +263 -91
  589. package/foundation/config/project.test.mjs +270 -21
  590. package/foundation/discovery/authoring-self-docs.d.mts +87 -0
  591. package/foundation/discovery/authoring-self-docs.mjs +237 -0
  592. package/foundation/discovery/authoring-self-docs.test.mjs +174 -0
  593. package/foundation/discovery/authoring-surface.d.mts +74 -0
  594. package/foundation/discovery/authoring-surface.mjs +525 -0
  595. package/foundation/discovery/authoring-surface.test.mjs +392 -0
  596. package/foundation/discovery/cli-self-docs.d.mts +119 -0
  597. package/foundation/discovery/cli-self-docs.mjs +504 -0
  598. package/foundation/discovery/cli-self-docs.test.mjs +395 -0
  599. package/foundation/discovery/component-discovery.d.mts +39 -1
  600. package/foundation/discovery/component-discovery.mjs +50 -1
  601. package/foundation/discovery/component-loader.d.mts +35 -38
  602. package/foundation/discovery/component-loader.mjs +53 -222
  603. package/foundation/discovery/docs-discovery.d.mts +119 -11
  604. package/foundation/discovery/docs-discovery.mjs +427 -108
  605. package/foundation/discovery/docs-discovery.test.mjs +386 -20
  606. package/foundation/discovery/docs-output-budget.d.mts +28 -0
  607. package/foundation/discovery/docs-output-budget.mjs +50 -0
  608. package/foundation/discovery/docs-section-key.d.mts +116 -0
  609. package/foundation/discovery/docs-section-key.mjs +322 -0
  610. package/foundation/discovery/docs-section-key.test.mjs +246 -0
  611. package/foundation/discovery/template-adapter.d.mts +113 -11
  612. package/foundation/discovery/template-adapter.fixture-refs.test.mjs +248 -0
  613. package/foundation/discovery/template-adapter.integration-isolation.test.mjs +94 -0
  614. package/foundation/discovery/template-adapter.mjs +774 -83
  615. package/foundation/discovery/template-adapter.test.mjs +57 -0
  616. package/foundation/discovery/template-conflict-release.d.mts +13 -0
  617. package/foundation/discovery/template-conflict-release.mjs +40 -0
  618. package/foundation/discovery/template-conflict-release.test.mjs +40 -0
  619. package/foundation/discovery/theme-discovery.d.mts +67 -7
  620. package/foundation/discovery/theme-discovery.mjs +916 -186
  621. package/foundation/discovery/theme-discovery.test.mjs +613 -219
  622. package/foundation/discovery/theming-targets.test.mjs +4 -0
  623. package/foundation/doc-compiler/bundle.d.mts +47 -0
  624. package/foundation/doc-compiler/bundle.mjs +278 -0
  625. package/foundation/doc-compiler/bundle.test.mjs +266 -0
  626. package/foundation/doc-compiler/compile.d.mts +343 -0
  627. package/foundation/doc-compiler/compile.mjs +558 -0
  628. package/foundation/doc-compiler/diagnostics.d.mts +126 -0
  629. package/foundation/doc-compiler/diagnostics.mjs +305 -0
  630. package/foundation/doc-compiler/doc-compiler.test.mjs +714 -0
  631. package/foundation/doc-compiler/doc-loads.test.mjs +1643 -0
  632. package/foundation/doc-compiler/import.d.mts +24 -0
  633. package/foundation/doc-compiler/import.mjs +59 -0
  634. package/foundation/doc-compiler/inputs.d.mts +102 -0
  635. package/foundation/doc-compiler/inputs.mjs +291 -0
  636. package/foundation/doc-compiler/inputs.test.mjs +298 -0
  637. package/foundation/doc-compiler/ir.d.mts +22 -0
  638. package/foundation/doc-compiler/ir.mjs +471 -0
  639. package/foundation/doc-compiler/lenses.d.mts +36 -0
  640. package/foundation/doc-compiler/lenses.mjs +173 -0
  641. package/foundation/doc-compiler/links.d.mts +162 -0
  642. package/foundation/doc-compiler/links.mjs +294 -0
  643. package/foundation/doc-compiler/links.test.mjs +192 -0
  644. package/foundation/doc-compiler/lower-doc.test.mjs +495 -0
  645. package/foundation/doc-compiler/overlays.d.mts +37 -0
  646. package/foundation/doc-compiler/overlays.mjs +206 -0
  647. package/foundation/doc-compiler/parse-readable.d.mts +9 -0
  648. package/foundation/doc-compiler/parse-readable.mjs +29 -0
  649. package/foundation/doc-compiler/read.d.mts +127 -0
  650. package/foundation/doc-compiler/read.mjs +325 -0
  651. package/foundation/doc-compiler/read.test.mjs +313 -0
  652. package/foundation/doc-compiler/source.d.mts +33 -0
  653. package/foundation/doc-compiler/source.mjs +128 -0
  654. package/foundation/doc-compiler/tree.d.mts +292 -0
  655. package/foundation/doc-compiler/tree.mjs +881 -0
  656. package/foundation/fs/file-protection.d.mts +33 -0
  657. package/foundation/fs/file-protection.mjs +825 -0
  658. package/foundation/fs/file-protection.test.mjs +250 -0
  659. package/foundation/fs/module-loader.d.mts +1 -0
  660. package/foundation/fs/module-loader.mjs +50 -1
  661. package/foundation/fs/module-loader.stdout.test.mjs +332 -0
  662. package/foundation/fs/path-safety.d.mts +3 -2
  663. package/foundation/fs/path-safety.mjs +49 -19
  664. package/foundation/fs/path-safety.test.mjs +50 -0
  665. package/foundation/fs/publish-file-hardlink-unavailable.test.mjs +129 -94
  666. package/foundation/identity/provider-identity.d.mts +90 -0
  667. package/foundation/identity/provider-identity.mjs +320 -0
  668. package/foundation/identity/provider-identity.test.mjs +254 -0
  669. package/foundation/identity/providers.d.mts +7 -0
  670. package/foundation/identity/providers.mjs +16 -0
  671. package/foundation/integrations/autolink.d.mts +58 -1
  672. package/foundation/integrations/autolink.mjs +143 -45
  673. package/foundation/integrations/autolink.test.mjs +1 -1
  674. package/foundation/integrations/cli-requirement.d.mts +65 -0
  675. package/foundation/integrations/cli-requirement.mjs +189 -0
  676. package/foundation/integrations/cli-requirement.test.mjs +89 -0
  677. package/foundation/integrations/contribution-fixes.d.mts +145 -0
  678. package/foundation/integrations/contribution-fixes.mjs +1284 -0
  679. package/foundation/integrations/contribution-inventory.d.mts +3 -2
  680. package/foundation/integrations/contribution-inventory.mjs +28 -25
  681. package/foundation/integrations/contribution-inventory.test.mjs +86 -27
  682. package/foundation/integrations/integration-warnings.d.mts +9 -2
  683. package/foundation/integrations/integration-warnings.mjs +52 -21
  684. package/foundation/integrations/integration-warnings.test.mjs +74 -1
  685. package/foundation/integrations/integrations.d.mts +63 -3
  686. package/foundation/integrations/integrations.mjs +122 -9
  687. package/foundation/integrations/integrations.test.mjs +415 -1
  688. package/foundation/integrations/provider-conflicts.test.mjs +125 -0
  689. package/foundation/integrations/provider-ledger.test.mjs +275 -0
  690. package/foundation/integrations/provider-resolution.d.mts +152 -0
  691. package/foundation/integrations/provider-resolution.mjs +576 -0
  692. package/foundation/integrations/provider-resolution.test.mjs +369 -0
  693. package/foundation/integrations/theme-descriptor.d.mts +8 -0
  694. package/foundation/integrations/theme-descriptor.mjs +44 -0
  695. package/foundation/integrations/validate-contributions.d.mts +2 -0
  696. package/foundation/integrations/validate-contributions.mjs +131 -29
  697. package/foundation/response/base.d.ts +8 -4
  698. package/foundation/response/batch.type.d.mts +33 -0
  699. package/foundation/response/batch.type.mjs +34 -0
  700. package/foundation/response/error-codes.d.mts +3 -1
  701. package/foundation/response/error-codes.d.ts +2 -0
  702. package/foundation/response/error-codes.doc.mjs +19 -12
  703. package/foundation/response/error-codes.mjs +8 -2
  704. package/foundation/response/error-codes.test.mjs +166 -14
  705. package/foundation/response/json-contract.test.mjs +57 -17
  706. package/foundation/response/json.d.mts +4 -2
  707. package/foundation/response/json.mjs +8 -10
  708. package/foundation/response/response-types.doc.d.mts +7 -2
  709. package/foundation/response/response-types.doc.mjs +69 -25
  710. package/foundation/response/response-types.doc.test.mjs +181 -0
  711. package/foundation/response/response.doc.mjs +12 -11
  712. package/foundation/text/string-utils.d.mts +8 -0
  713. package/foundation/text/string-utils.mjs +40 -10
  714. package/foundation/xle/expand.d.mts +2 -0
  715. package/foundation/xle/expand.mjs +4 -3
  716. package/foundation/xle/expand.test.mjs +54 -0
  717. package/foundation/xle/xle.test.mjs +13 -0
  718. package/package.json +10 -11
  719. package/api/docs/docs.test.mjs +0 -83
  720. package/api/docs/integrationDocs.test.mjs +0 -208
  721. package/api/search/search.test.mjs +0 -389
  722. package/assets/docs/cli-integrations.doc.mjs +0 -367
  723. package/assets/templates/themes/manifest.json +0 -95
  724. package/clients/cli/commands/docs.test.mjs +0 -102
  725. package/clients/cli/lib/update-check.mjs +0 -83
  726. package/clients/cli/lib/update-check.test.mjs +0 -137
  727. package/clients/cli/update-hint-commands.test.mjs +0 -54
  728. package/foundation/agent-docs/agent-docs.test.mjs +0 -1141
@@ -0,0 +1,1389 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file Every `Fix:` that `astryx doctor integration validate` prints must hold
5
+ * when followed literally: each test builds a package with one problem, reads
6
+ * the fix, carries it out on a copy of the package, and validates again.
7
+ *
8
+ * The copy lives at a new path so the edited manifest is imported fresh rather
9
+ * than served from the module cache. Temp dirs live under the repo root, as in
10
+ * validate-integration.test.mjs.
11
+ */
12
+
13
+ import {afterEach, beforeEach, describe, expect, it} from 'vitest';
14
+ import * as fs from 'node:fs';
15
+ import * as path from 'node:path';
16
+ import {validateLocalIntegration} from './validate-integration.mjs';
17
+
18
+ let tmpDir;
19
+ let copies = 0;
20
+
21
+ beforeEach(() => {
22
+ tmpDir = fs.mkdtempSync(path.join(process.cwd(), '.astryx-validate-fix-'));
23
+ });
24
+
25
+ afterEach(() => {
26
+ fs.rmSync(tmpDir, {recursive: true, force: true});
27
+ });
28
+
29
+ const MANIFEST = 'astryx.integration.mjs';
30
+ const THEME_TYPE =
31
+ "/** @type {import('@astryxdesign/cli/authoring').ThemeDoc} */\n";
32
+
33
+ /** @param {Record<string, string>} roots */
34
+ const manifest = roots => `export default ${JSON.stringify(roots)};\n`;
35
+ /** @param {string} name */
36
+ const componentDoc = name =>
37
+ `export default {type: 'component', name: '${name}', displayName: '${name}', usage: {description: '${name}.'}, props: []};\n`;
38
+ /** @param {string} name */
39
+ const componentSource = name => `export function ${name}() { return null; }\n`;
40
+ /** @param {string} name */
41
+ const topic = name =>
42
+ `export default ${JSON.stringify({
43
+ type: 'generic',
44
+ name,
45
+ title: name,
46
+ description: `The ${name} topic.`,
47
+ sections: [{title: 'Overview', content: [{type: 'prose', text: name}]}],
48
+ })};\n`;
49
+ /** @param {string} name */
50
+ const themeDoc = name =>
51
+ `${THEME_TYPE}export default {type: 'theme', name: '${name}', displayName: '${name}', description: 'A theme.', maintained: true};\n`;
52
+ /** @param {string} exportName */
53
+ const themeSource = exportName => `export const ${exportName} = {};\n`;
54
+ const codemod = `export default {type: 'code', title: 'Rename', transform: file => file.source};\n`;
55
+
56
+ /**
57
+ * @param {Record<string, string>} files package-relative path -> contents
58
+ * @returns {string} the package directory
59
+ */
60
+ function writePackage(files) {
61
+ const dir = path.join(tmpDir, 'pkg');
62
+ fs.mkdirSync(dir, {recursive: true});
63
+ fs.writeFileSync(
64
+ path.join(dir, 'package.json'),
65
+ JSON.stringify({name: '@acme/kit', version: '1.0.0'}),
66
+ );
67
+ for (const [file, contents] of Object.entries(files)) {
68
+ fs.mkdirSync(path.dirname(path.join(dir, file)), {recursive: true});
69
+ fs.writeFileSync(path.join(dir, file), contents);
70
+ }
71
+ return dir;
72
+ }
73
+
74
+ /**
75
+ * Carry out a fix on a fresh copy of the package.
76
+ * @param {string} dir
77
+ * @param {{move?: Array<[string, string]>, write?: Record<string, string>, mkdir?: string[]}} steps
78
+ * @returns {string} the copy's directory
79
+ */
80
+ function follow(dir, {move = [], write = {}, mkdir = []}) {
81
+ copies += 1;
82
+ const copy = path.join(tmpDir, `followed-${copies}`);
83
+ fs.cpSync(dir, copy, {recursive: true});
84
+ for (const folder of mkdir) {
85
+ fs.mkdirSync(path.join(copy, folder), {recursive: true});
86
+ }
87
+ for (const [from, to] of move) {
88
+ fs.mkdirSync(path.dirname(path.join(copy, to)), {recursive: true});
89
+ fs.renameSync(path.join(copy, from), path.join(copy, to));
90
+ }
91
+ for (const [file, contents] of Object.entries(write)) {
92
+ fs.writeFileSync(path.join(copy, file), contents);
93
+ }
94
+ return copy;
95
+ }
96
+
97
+ /** @param {string} dir */
98
+ async function issuesOf(dir) {
99
+ return (await validateLocalIntegration(dir)).issues;
100
+ }
101
+
102
+ /**
103
+ * The one issue with this code, which the test is about.
104
+ * @param {Array<{code: string, message: string}>} issues
105
+ * @param {string} code
106
+ */
107
+ function only(issues, code) {
108
+ const found = issues.filter(issue => issue.code === code);
109
+ expect(found).toHaveLength(1);
110
+ return found[0].message;
111
+ }
112
+
113
+ const unreachable = file =>
114
+ `Found contribution metadata "${file}" outside every declared integration root, so it contributes nothing.`;
115
+ const good = {
116
+ 'components/Good/Good.doc.mjs': componentDoc('Good'),
117
+ 'components/Good/Good.tsx': componentSource('Good'),
118
+ };
119
+
120
+ describe('unreachable_contribution fixes hold when followed', () => {
121
+ it('moves a theme at the package root into themes/<slug>/, never to a folder outside the package', async () => {
122
+ const dir = writePackage({
123
+ [MANIFEST]: manifest({components: './components'}),
124
+ ...good,
125
+ 'oceanTheme.doc.mjs': themeDoc('ocean'),
126
+ 'oceanTheme.ts': `import {tokens} from './oceanTokens';\nexport const oceanTheme = {tokens};\n`,
127
+ 'oceanTokens.ts': 'export const tokens = {};\n',
128
+ });
129
+
130
+ const message = only(await issuesOf(dir), 'unreachable_contribution');
131
+ expect(message).toBe(
132
+ `${unreachable('oceanTheme.doc.mjs')} Fix: move oceanTheme.doc.mjs and oceanTheme.ts (with any local files it imports) into themes/ocean/ and set \`themes: './themes'\` in astryx.integration.mjs.`,
133
+ );
134
+
135
+ const fixed = follow(dir, {
136
+ move: [
137
+ ['oceanTheme.doc.mjs', 'themes/ocean/oceanTheme.doc.mjs'],
138
+ ['oceanTheme.ts', 'themes/ocean/oceanTheme.ts'],
139
+ ['oceanTokens.ts', 'themes/ocean/oceanTokens.ts'],
140
+ ],
141
+ write: {
142
+ [MANIFEST]: manifest({components: './components', themes: './themes'}),
143
+ },
144
+ });
145
+ expect(await issuesOf(fixed)).toEqual([]);
146
+ });
147
+
148
+ it('moves a theme folder at the top level under themes/, never making the package root the themes root', async () => {
149
+ const dir = writePackage({
150
+ [MANIFEST]: manifest({components: './components'}),
151
+ ...good,
152
+ 'sand/sandTheme.doc.mjs': themeDoc('sand'),
153
+ 'sand/sandTheme.ts': themeSource('sandTheme'),
154
+ });
155
+
156
+ const message = only(await issuesOf(dir), 'unreachable_contribution');
157
+ expect(message).toBe(
158
+ `${unreachable('sand/sandTheme.doc.mjs')} Fix: move sand/ to themes/sand/ and set \`themes: './themes'\` in astryx.integration.mjs.`,
159
+ );
160
+
161
+ const fixed = follow(dir, {
162
+ move: [['sand', 'themes/sand']],
163
+ write: {
164
+ [MANIFEST]: manifest({components: './components', themes: './themes'}),
165
+ },
166
+ });
167
+ expect(await issuesOf(fixed)).toEqual([]);
168
+ });
169
+
170
+ it('moves a stray theme folder into the declared themes root under its slug', async () => {
171
+ const dir = writePackage({
172
+ [MANIFEST]: manifest({themes: './themes'}),
173
+ 'themes/ocean/oceanTheme.doc.mjs': themeDoc('ocean'),
174
+ 'themes/ocean/oceanTheme.ts': themeSource('oceanTheme'),
175
+ 'extra/seaTheme.doc.mjs': themeDoc('sea'),
176
+ 'extra/seaTheme.ts': themeSource('seaTheme'),
177
+ });
178
+
179
+ const message = only(await issuesOf(dir), 'unreachable_contribution');
180
+ expect(message).toBe(
181
+ `${unreachable('extra/seaTheme.doc.mjs')} Fix: move extra/ to themes/sea/.`,
182
+ );
183
+
184
+ const fixed = follow(dir, {move: [['extra', 'themes/sea']]});
185
+ expect(await issuesOf(fixed)).toEqual([]);
186
+ });
187
+
188
+ it('keeps declaring a folder that holds only themes as the themes root', async () => {
189
+ const dir = writePackage({
190
+ [MANIFEST]: manifest({components: './components'}),
191
+ ...good,
192
+ 'palettes/sand/sandTheme.doc.mjs': themeDoc('sand'),
193
+ 'palettes/sand/sandTheme.ts': themeSource('sandTheme'),
194
+ });
195
+
196
+ const message = only(await issuesOf(dir), 'unreachable_contribution');
197
+ expect(message).toBe(
198
+ `${unreachable('palettes/sand/sandTheme.doc.mjs')} Fix: set \`themes: './palettes'\` in astryx.integration.mjs.`,
199
+ );
200
+
201
+ const fixed = follow(dir, {
202
+ write: {
203
+ [MANIFEST]: manifest({
204
+ components: './components',
205
+ themes: './palettes',
206
+ }),
207
+ },
208
+ });
209
+ expect(await issuesOf(fixed)).toEqual([]);
210
+ });
211
+
212
+ it('offers either root for a theme when the declared themes root is empty', async () => {
213
+ const dir = writePackage({
214
+ [MANIFEST]: manifest({themes: './themes'}),
215
+ 'themes/.keep': '',
216
+ 'palettes/sand/sandTheme.doc.mjs': themeDoc('sand'),
217
+ 'palettes/sand/sandTheme.ts': themeSource('sandTheme'),
218
+ });
219
+
220
+ const message = only(await issuesOf(dir), 'unreachable_contribution');
221
+ expect(message).toBe(
222
+ `${unreachable('palettes/sand/sandTheme.doc.mjs')} Fix: move palettes/sand/ to themes/sand/, or set \`themes: './palettes'\` in astryx.integration.mjs.`,
223
+ );
224
+
225
+ const moved = follow(dir, {move: [['palettes/sand', 'themes/sand']]});
226
+ expect(await issuesOf(moved)).toEqual([]);
227
+ const declared = follow(dir, {
228
+ write: {[MANIFEST]: manifest({themes: './palettes'})},
229
+ });
230
+ expect(await issuesOf(declared)).toEqual([]);
231
+ });
232
+
233
+ it('declares themes/ as the root when its other folders are not themes', async () => {
234
+ const dir = writePackage({
235
+ [MANIFEST]: manifest({components: './components'}),
236
+ ...good,
237
+ 'themes/ocean/oceanTheme.doc.mjs': themeDoc('ocean'),
238
+ 'themes/ocean/oceanTheme.ts': themeSource('oceanTheme'),
239
+ 'themes/shared/colors.ts': 'export const colors = {};\n',
240
+ });
241
+
242
+ const message = only(await issuesOf(dir), 'unreachable_contribution');
243
+ expect(message).toBe(
244
+ `${unreachable('themes/ocean/oceanTheme.doc.mjs')} Fix: set \`themes: './themes'\` in astryx.integration.mjs.`,
245
+ );
246
+
247
+ const fixed = follow(dir, {
248
+ write: {
249
+ [MANIFEST]: manifest({components: './components', themes: './themes'}),
250
+ },
251
+ });
252
+ expect(await issuesOf(fixed)).toEqual([]);
253
+ });
254
+
255
+ it('puts a theme in a folder of its own when themes/ holds a broken theme', async () => {
256
+ const dir = writePackage({
257
+ [MANIFEST]: manifest({components: './components'}),
258
+ ...good,
259
+ 'themes/ocean/oceanTheme.doc.mjs': themeDoc('ocean'),
260
+ 'themes/ocean/oceanTheme.ts': themeSource('oceanTheme'),
261
+ 'themes/draft/draftTheme.ts': themeSource('draftTheme'),
262
+ });
263
+
264
+ const message = only(await issuesOf(dir), 'unreachable_contribution');
265
+ expect(message).toBe(
266
+ `${unreachable('themes/ocean/oceanTheme.doc.mjs')} Fix: move themes/ocean/ into a folder that holds only themes, as ocean/, and set \`themes\` to that folder in astryx.integration.mjs.`,
267
+ );
268
+
269
+ const fixed = follow(dir, {
270
+ move: [['themes/ocean', 'palettes/ocean']],
271
+ write: {
272
+ [MANIFEST]: manifest({
273
+ components: './components',
274
+ themes: './palettes',
275
+ }),
276
+ },
277
+ });
278
+ expect(await issuesOf(fixed)).toEqual([]);
279
+ });
280
+
281
+ it('moves a component doc at the package root with its source, never making the package root the components root', async () => {
282
+ const dir = writePackage({
283
+ [MANIFEST]: manifest({components: './components'}),
284
+ ...good,
285
+ 'Stray.doc.mjs': componentDoc('Stray'),
286
+ 'Stray.tsx': componentSource('Stray'),
287
+ });
288
+
289
+ const message = only(await issuesOf(dir), 'unreachable_contribution');
290
+ expect(message).toBe(
291
+ `${unreachable('Stray.doc.mjs')} Fix: move it and Stray.tsx under components/ (the components root).`,
292
+ );
293
+
294
+ const fixed = follow(dir, {
295
+ move: [
296
+ ['Stray.doc.mjs', 'components/Stray/Stray.doc.mjs'],
297
+ ['Stray.tsx', 'components/Stray/Stray.tsx'],
298
+ ],
299
+ });
300
+ expect(await issuesOf(fixed)).toEqual([]);
301
+ });
302
+
303
+ it('moves a topic at the package root into docs/, never making the package root the docs root', async () => {
304
+ const dir = writePackage({
305
+ [MANIFEST]: manifest({components: './components'}),
306
+ ...good,
307
+ 'guide.doc.mjs': topic('guide'),
308
+ });
309
+
310
+ const message = only(await issuesOf(dir), 'unreachable_contribution');
311
+ expect(message).toBe(
312
+ `${unreachable('guide.doc.mjs')} Fix: move it into docs/ and set \`docs: './docs'\` in astryx.integration.mjs.`,
313
+ );
314
+
315
+ const fixed = follow(dir, {
316
+ move: [['guide.doc.mjs', 'docs/guide.doc.mjs']],
317
+ write: {
318
+ [MANIFEST]: manifest({components: './components', docs: './docs'}),
319
+ },
320
+ });
321
+ expect(await issuesOf(fixed)).toEqual([]);
322
+ });
323
+
324
+ it('declares a folder that holds only topics as the docs root', async () => {
325
+ const dir = writePackage({
326
+ [MANIFEST]: manifest({}),
327
+ 'src/guide.doc.mjs': topic('guide'),
328
+ 'src/faq.doc.mjs': topic('faq'),
329
+ });
330
+
331
+ const issues = await issuesOf(dir);
332
+ expect(issues.map(issue => issue.message)).toEqual([
333
+ `${unreachable('src/faq.doc.mjs')} Fix: set \`docs: './src'\` in astryx.integration.mjs.`,
334
+ `${unreachable('src/guide.doc.mjs')} Fix: set \`docs: './src'\` in astryx.integration.mjs.`,
335
+ ]);
336
+
337
+ const fixed = follow(dir, {write: {[MANIFEST]: manifest({docs: './src'})}});
338
+ expect(await issuesOf(fixed)).toEqual([]);
339
+ });
340
+
341
+ it('never declares a folder that also holds another kind of doc as a root', async () => {
342
+ const dir = writePackage({
343
+ [MANIFEST]: manifest({}),
344
+ 'src/Button.doc.mjs': componentDoc('Button'),
345
+ 'src/Button.tsx': componentSource('Button'),
346
+ 'src/guide.doc.mjs': topic('guide'),
347
+ });
348
+
349
+ const issues = await issuesOf(dir);
350
+ expect(issues.map(issue => issue.message)).toEqual([
351
+ `${unreachable('src/Button.doc.mjs')} Fix: move it and Button.tsx into components/ and set \`components: './components'\` in astryx.integration.mjs.`,
352
+ `${unreachable('src/guide.doc.mjs')} Fix: move it into docs/ and set \`docs: './docs'\` in astryx.integration.mjs.`,
353
+ ]);
354
+
355
+ const fixed = follow(dir, {
356
+ move: [
357
+ ['src/Button.doc.mjs', 'components/Button.doc.mjs'],
358
+ ['src/Button.tsx', 'components/Button.tsx'],
359
+ ['src/guide.doc.mjs', 'docs/guide.doc.mjs'],
360
+ ],
361
+ write: {
362
+ [MANIFEST]: manifest({components: './components', docs: './docs'}),
363
+ },
364
+ });
365
+ expect(await issuesOf(fixed)).toEqual([]);
366
+ });
367
+
368
+ it('names a fresh folder when docs/ already holds another kind of doc', async () => {
369
+ const dir = writePackage({
370
+ [MANIFEST]: manifest({}),
371
+ 'docs/Button.doc.mjs': componentDoc('Button'),
372
+ 'docs/Button.tsx': componentSource('Button'),
373
+ 'docs/guide.doc.mjs': topic('guide'),
374
+ });
375
+
376
+ const issues = await issuesOf(dir);
377
+ expect(issues.map(issue => issue.message)).toEqual([
378
+ `${unreachable('docs/Button.doc.mjs')} Fix: move it and Button.tsx into components/ and set \`components: './components'\` in astryx.integration.mjs.`,
379
+ `${unreachable('docs/guide.doc.mjs')} Fix: move it into a folder that holds only topics, and set \`docs\` to that folder in astryx.integration.mjs.`,
380
+ ]);
381
+
382
+ const fixed = follow(dir, {
383
+ move: [
384
+ ['docs/Button.doc.mjs', 'components/Button.doc.mjs'],
385
+ ['docs/Button.tsx', 'components/Button.tsx'],
386
+ ['docs/guide.doc.mjs', 'guides/guide.doc.mjs'],
387
+ ],
388
+ write: {
389
+ [MANIFEST]: manifest({components: './components', docs: './guides'}),
390
+ },
391
+ });
392
+ expect(await issuesOf(fixed)).toEqual([]);
393
+ });
394
+
395
+ it('only moves metadata into a declared root that already holds contributions', async () => {
396
+ const dir = writePackage({
397
+ [MANIFEST]: manifest({templates: './templates'}),
398
+ 'templates/hero.doc.mjs': `export default {type: 'page', name: 'hero', displayName: 'Hero'};\n`,
399
+ 'templates/hero.tsx': componentSource('Hero'),
400
+ 'src/blocks/Carousel.doc.mjs': `export default {type: 'block', name: 'Carousel', displayName: 'Carousel', aspectRatio: 1};\n`,
401
+ 'src/blocks/Carousel.tsx': componentSource('Carousel'),
402
+ });
403
+
404
+ const message = only(await issuesOf(dir), 'unreachable_contribution');
405
+ // Pointing `templates` at src/blocks would orphan templates/hero.
406
+ expect(message).toBe(
407
+ `${unreachable('src/blocks/Carousel.doc.mjs')} Fix: move it and Carousel.tsx under templates/ (the templates root).`,
408
+ );
409
+
410
+ const fixed = follow(dir, {
411
+ move: [
412
+ ['src/blocks/Carousel.doc.mjs', 'templates/Carousel.doc.mjs'],
413
+ ['src/blocks/Carousel.tsx', 'templates/Carousel.tsx'],
414
+ ],
415
+ });
416
+ expect(await issuesOf(fixed)).toEqual([]);
417
+ });
418
+
419
+ it('offers either root when the declared root is empty, and both hold', async () => {
420
+ const dir = writePackage({
421
+ [MANIFEST]: manifest({templates: './templates'}),
422
+ 'templates/.keep': '',
423
+ 'src/blocks/Carousel.doc.mjs': `export default {type: 'block', name: 'Carousel', displayName: 'Carousel', aspectRatio: 1};\n`,
424
+ 'src/blocks/Carousel.tsx': componentSource('Carousel'),
425
+ });
426
+
427
+ const message = only(await issuesOf(dir), 'unreachable_contribution');
428
+ expect(message).toBe(
429
+ `${unreachable('src/blocks/Carousel.doc.mjs')} Fix: move it and Carousel.tsx under templates/ (the templates root), or set \`templates: './src/blocks'\` in astryx.integration.mjs.`,
430
+ );
431
+
432
+ const moved = follow(dir, {
433
+ move: [
434
+ ['src/blocks/Carousel.doc.mjs', 'templates/Carousel.doc.mjs'],
435
+ ['src/blocks/Carousel.tsx', 'templates/Carousel.tsx'],
436
+ ],
437
+ });
438
+ expect(await issuesOf(moved)).toEqual([]);
439
+ const declared = follow(dir, {
440
+ write: {[MANIFEST]: manifest({templates: './src/blocks'})},
441
+ });
442
+ expect(await issuesOf(declared)).toEqual([]);
443
+ });
444
+ });
445
+
446
+ describe('unreachable_contribution fixes cover what the move leaves behind', () => {
447
+ it('asks for the missing source a component doc needs once a root reads it', async () => {
448
+ const dir = writePackage({
449
+ [MANIFEST]: manifest({components: './components'}),
450
+ ...good,
451
+ 'Stray.doc.mjs': componentDoc('Stray'),
452
+ });
453
+
454
+ const message = only(await issuesOf(dir), 'unreachable_contribution');
455
+ expect(message).toBe(
456
+ `${unreachable('Stray.doc.mjs')} Fix: move it under components/ (the components root). Also add Stray.tsx beside it.`,
457
+ );
458
+
459
+ const fixed = follow(dir, {
460
+ move: [['Stray.doc.mjs', 'components/Stray.doc.mjs']],
461
+ write: {'components/Stray.tsx': componentSource('Stray')},
462
+ });
463
+ expect(await issuesOf(fixed)).toEqual([]);
464
+ });
465
+
466
+ it('asks for the missing source a theme needs', async () => {
467
+ const dir = writePackage({
468
+ [MANIFEST]: manifest({components: './components'}),
469
+ ...good,
470
+ 'sand/sandTheme.doc.mjs': themeDoc('sand'),
471
+ });
472
+
473
+ const message = only(await issuesOf(dir), 'unreachable_contribution');
474
+ expect(message).toBe(
475
+ `${unreachable('sand/sandTheme.doc.mjs')} Fix: move sand/ to themes/sand/ and set \`themes: './themes'\` in astryx.integration.mjs. Also add its same-stem theme source, such as sandTheme.ts, beside it.`,
476
+ );
477
+
478
+ const fixed = follow(dir, {
479
+ move: [['sand', 'themes/sand']],
480
+ write: {
481
+ [MANIFEST]: manifest({components: './components', themes: './themes'}),
482
+ 'themes/sand/sandTheme.ts': themeSource('sandTheme'),
483
+ },
484
+ });
485
+ expect(await issuesOf(fixed)).toEqual([]);
486
+ });
487
+
488
+ it('asks to rename a theme descriptor that is not a .doc.mjs file', async () => {
489
+ const dir = writePackage({
490
+ [MANIFEST]: manifest({components: './components'}),
491
+ ...good,
492
+ 'sand/sandTheme.doc.ts': themeDoc('sand'),
493
+ 'sand/sandTheme.ts': themeSource('sandTheme'),
494
+ });
495
+
496
+ const message = only(await issuesOf(dir), 'unreachable_contribution');
497
+ expect(message).toBe(
498
+ `${unreachable('sand/sandTheme.doc.ts')} Fix: move sand/ to themes/sand/ and set \`themes: './themes'\` in astryx.integration.mjs. Also rename sandTheme.doc.ts to sandTheme.doc.mjs: a theme descriptor is a .doc.mjs file.`,
499
+ );
500
+
501
+ const fixed = follow(dir, {
502
+ move: [
503
+ ['sand', 'themes/sand'],
504
+ ['themes/sand/sandTheme.doc.ts', 'themes/sand/sandTheme.doc.mjs'],
505
+ ],
506
+ write: {
507
+ [MANIFEST]: manifest({components: './components', themes: './themes'}),
508
+ },
509
+ });
510
+ expect(await issuesOf(fixed)).toEqual([]);
511
+ });
512
+
513
+ it('never declares a folder as a root when another doc in it would lose its source', async () => {
514
+ const dir = writePackage({
515
+ [MANIFEST]: manifest({}),
516
+ 'src/Card.doc.mjs': componentDoc('Card'),
517
+ 'src/Card.tsx': componentSource('Card'),
518
+ 'src/Badge.doc.mjs': componentDoc('Badge'),
519
+ });
520
+
521
+ const issues = await issuesOf(dir);
522
+ const card = issues.find(issue =>
523
+ issue.message.includes('src/Card.doc.mjs'),
524
+ );
525
+ // A components root at src/ would read Badge, which has no source.
526
+ expect(card?.message).toBe(
527
+ `${unreachable('src/Card.doc.mjs')} Fix: move it and Card.tsx into components/ and set \`components: './components'\` in astryx.integration.mjs.`,
528
+ );
529
+ const badge = issues.find(issue =>
530
+ issue.message.includes('src/Badge.doc.mjs'),
531
+ );
532
+ expect(badge?.message).toBe(
533
+ `${unreachable('src/Badge.doc.mjs')} Fix: set \`components: './src'\` in astryx.integration.mjs. Also add Badge.tsx beside it.`,
534
+ );
535
+
536
+ const cardMoved = follow(dir, {
537
+ move: [
538
+ ['src/Card.doc.mjs', 'components/Card.doc.mjs'],
539
+ ['src/Card.tsx', 'components/Card.tsx'],
540
+ ],
541
+ write: {[MANIFEST]: manifest({components: './components'})},
542
+ });
543
+ const left = await issuesOf(cardMoved);
544
+ expect(left.map(issue => issue.code)).toEqual(['unreachable_contribution']);
545
+ expect(left[0].message).toContain('src/Badge.doc.mjs');
546
+
547
+ const badgeDeclared = follow(dir, {
548
+ write: {
549
+ [MANIFEST]: manifest({components: './src'}),
550
+ 'src/Badge.tsx': componentSource('Badge'),
551
+ },
552
+ });
553
+ expect(await issuesOf(badgeDeclared)).toEqual([]);
554
+ });
555
+
556
+ it('moves the target root out of another root that also reads it', async () => {
557
+ const dir = writePackage({
558
+ [MANIFEST]: manifest({components: './src/components', docs: './src'}),
559
+ 'src/overview.doc.mjs': topic('overview'),
560
+ 'src/components/Good.doc.mjs': componentDoc('Good'),
561
+ 'src/components/Good.tsx': componentSource('Good'),
562
+ 'lib/Card.doc.mjs': componentDoc('Card'),
563
+ 'lib/Card.tsx': componentSource('Card'),
564
+ });
565
+
566
+ const message = only(await issuesOf(dir), 'unreachable_contribution');
567
+ expect(message).toBe(
568
+ `${unreachable('lib/Card.doc.mjs')} Fix: move it and Card.tsx under src/components/ (the components root). The docs root src/ also reads src/components/; move src/components/ to components/ and set \`components: './components'\` in astryx.integration.mjs.`,
569
+ );
570
+
571
+ const fixed = follow(dir, {
572
+ move: [
573
+ ['lib/Card.doc.mjs', 'src/components/Card.doc.mjs'],
574
+ ['lib/Card.tsx', 'src/components/Card.tsx'],
575
+ ['src/components', 'components'],
576
+ ],
577
+ write: {
578
+ [MANIFEST]: manifest({components: './components', docs: './src'}),
579
+ },
580
+ });
581
+ expect(await issuesOf(fixed)).toEqual([]);
582
+ });
583
+ });
584
+
585
+ describe('fixes never move a contribution onto a name its root already has', () => {
586
+ it('asks for a new slug when the themes root already has that theme', async () => {
587
+ const dir = writePackage({
588
+ [MANIFEST]: manifest({themes: './themes'}),
589
+ 'themes/ocean/oceanTheme.doc.mjs': themeDoc('ocean'),
590
+ 'themes/ocean/oceanTheme.ts': themeSource('oceanTheme'),
591
+ 'extra/ocean/oceanTheme.doc.mjs': themeDoc('ocean'),
592
+ 'extra/ocean/oceanTheme.ts': themeSource('oceanTheme'),
593
+ });
594
+
595
+ const message = only(await issuesOf(dir), 'unreachable_contribution');
596
+ expect(message).toBe(
597
+ `${unreachable('extra/ocean/oceanTheme.doc.mjs')} Fix: themes/ocean/ is taken, so give this theme a new lower-kebab slug: move extra/ocean/ to themes/<slug>/ and set \`name\` in oceanTheme.doc.mjs to <slug>.`,
598
+ );
599
+
600
+ const fixed = follow(dir, {
601
+ move: [['extra/ocean', 'themes/deep-ocean']],
602
+ write: {'themes/deep-ocean/oceanTheme.doc.mjs': themeDoc('deep-ocean')},
603
+ });
604
+ expect(await issuesOf(fixed)).toEqual([]);
605
+ });
606
+
607
+ it('asks for a new slug for a theme at the top of the codemods root', async () => {
608
+ const dir = writePackage({
609
+ [MANIFEST]: manifest({codemods: './codemods', themes: './themes'}),
610
+ 'themes/ocean/oceanTheme.doc.mjs': themeDoc('ocean'),
611
+ 'themes/ocean/oceanTheme.ts': themeSource('oceanTheme'),
612
+ 'codemods/1.2.0/rename.mjs': codemod,
613
+ 'codemods/oceanTheme.doc.mjs': themeDoc('ocean'),
614
+ 'codemods/oceanTheme.ts': themeSource('oceanTheme'),
615
+ });
616
+
617
+ const move =
618
+ 'themes/ocean/ is taken, so give this theme a new lower-kebab slug: move oceanTheme.doc.mjs and oceanTheme.ts (with any local files it imports) into themes/<slug>/ and set `name` in oceanTheme.doc.mjs to <slug>.';
619
+ expect(
620
+ (await issuesOf(dir))
621
+ .filter(issue => issue.code === 'codemod_outside_version')
622
+ .map(issue => issue.message),
623
+ ).toEqual([
624
+ `Codemod file "codemods/oceanTheme.doc.mjs" is outside a version folder, so upgrade will never load it. Fix: oceanTheme.doc.mjs has type: 'theme', so it is not a codemod; ${move}`,
625
+ `Codemod file "codemods/oceanTheme.ts" is outside a version folder, so upgrade will never load it. Fix: oceanTheme.ts is the source of oceanTheme.doc.mjs, which has type: 'theme', so neither is a codemod; ${move}`,
626
+ ]);
627
+
628
+ const fixed = follow(dir, {
629
+ move: [
630
+ ['codemods/oceanTheme.doc.mjs', 'themes/deep-ocean/oceanTheme.doc.mjs'],
631
+ ['codemods/oceanTheme.ts', 'themes/deep-ocean/oceanTheme.ts'],
632
+ ],
633
+ write: {'themes/deep-ocean/oceanTheme.doc.mjs': themeDoc('deep-ocean')},
634
+ });
635
+ expect(await issuesOf(fixed)).toEqual([]);
636
+ });
637
+
638
+ it('asks for a new name when the docs root already has that topic', async () => {
639
+ const dir = writePackage({
640
+ [MANIFEST]: manifest({docs: './docs'}),
641
+ 'docs/guide.doc.mjs': topic('guide'),
642
+ 'extra/guide.doc.mjs': topic('guide'),
643
+ });
644
+
645
+ const message = only(await issuesOf(dir), 'unreachable_contribution');
646
+ expect(message).toBe(
647
+ `${unreachable('extra/guide.doc.mjs')} Fix: docs/ already has a topic named "guide" (docs/guide.doc.mjs), so give this one a new name: rename it to <name>.doc.mjs, set its \`name\` to <name>, and move it under docs/ (the docs root).`,
648
+ );
649
+
650
+ const fixed = follow(dir, {
651
+ move: [['extra/guide.doc.mjs', 'docs/more-guide.doc.mjs']],
652
+ write: {'docs/more-guide.doc.mjs': topic('more-guide')},
653
+ });
654
+ expect(await issuesOf(fixed)).toEqual([]);
655
+ });
656
+
657
+ it('asks for a new name when the components root already has that component', async () => {
658
+ const dir = writePackage({
659
+ [MANIFEST]: manifest({components: './components'}),
660
+ 'components/Foo/Foo.doc.mjs': componentDoc('Foo'),
661
+ 'components/Foo/Foo.tsx': componentSource('Foo'),
662
+ 'Foo.doc.mjs': componentDoc('Foo'),
663
+ 'Foo.tsx': componentSource('Foo'),
664
+ });
665
+
666
+ const message = only(await issuesOf(dir), 'unreachable_contribution');
667
+ expect(message).toBe(
668
+ `${unreachable('Foo.doc.mjs')} Fix: components/ already has a component named Foo (components/Foo/Foo.doc.mjs), so give this one a new name: rename it and Foo.tsx to <Name>.doc.mjs and <Name>.tsx, set its \`name\` to <Name>, and move them under components/ (the components root).`,
669
+ );
670
+
671
+ const fixed = follow(dir, {
672
+ move: [
673
+ ['Foo.doc.mjs', 'components/FooPanel.doc.mjs'],
674
+ ['Foo.tsx', 'components/FooPanel.tsx'],
675
+ ],
676
+ write: {'components/FooPanel.doc.mjs': componentDoc('FooPanel')},
677
+ });
678
+ expect(await issuesOf(fixed)).toEqual([]);
679
+ expect(
680
+ fs.readFileSync(path.join(fixed, 'components/Foo/Foo.doc.mjs'), 'utf-8'),
681
+ ).toBe(componentDoc('Foo'));
682
+ });
683
+
684
+ it('asks for a new name when the templates root already has that template', async () => {
685
+ const dir = writePackage({
686
+ [MANIFEST]: manifest({templates: './templates'}),
687
+ 'templates/hero.doc.mjs': `export default {type: 'page', name: 'hero', displayName: 'Hero'};\n`,
688
+ 'templates/hero.tsx': componentSource('Hero'),
689
+ 'extra/hero.doc.mjs': `export default {type: 'page', name: 'hero', displayName: 'Hero'};\n`,
690
+ 'extra/hero.tsx': componentSource('Hero'),
691
+ });
692
+
693
+ const message = only(await issuesOf(dir), 'unreachable_contribution');
694
+ expect(message).toBe(
695
+ `${unreachable('extra/hero.doc.mjs')} Fix: templates/ already has a template named hero (templates/hero.doc.mjs), so give this one a new name: rename it and hero.tsx to <name>.doc.mjs and <name>.tsx, set its \`name\` to <name>, and move them under templates/ (the templates root).`,
696
+ );
697
+
698
+ const fixed = follow(dir, {
699
+ move: [
700
+ ['extra/hero.doc.mjs', 'templates/hero-wide.doc.mjs'],
701
+ ['extra/hero.tsx', 'templates/hero-wide.tsx'],
702
+ ],
703
+ write: {
704
+ 'templates/hero-wide.doc.mjs': `export default {type: 'page', name: 'hero-wide', displayName: 'Hero'};\n`,
705
+ },
706
+ });
707
+ expect(await issuesOf(fixed)).toEqual([]);
708
+ });
709
+
710
+ it('asks for a new name when the root a fix moves out already has that topic', async () => {
711
+ const dir = writePackage({
712
+ [MANIFEST]: manifest({components: './src', docs: './src/zdocs'}),
713
+ 'src/zdocs/guide.doc.mjs': topic('guide'),
714
+ 'src/faq.doc.mjs': topic('guide'),
715
+ });
716
+
717
+ const [faq] = (await issuesOf(dir)).filter(issue =>
718
+ issue.message.includes('Component "faq"'),
719
+ );
720
+ expect(faq.message).toBe(
721
+ `Component "faq" is missing its same-stem source file faq.tsx. Fix: faq.doc.mjs has type: 'generic', so it is not a component, and the components root src/ also reads the docs root src/zdocs/ inside it; move src/zdocs/ to docs/ and set \`docs: './docs'\` in astryx.integration.mjs, then give it a new name: rename it to <name>.doc.mjs, set its \`name\` to <name>, and move it into docs/.`,
722
+ );
723
+
724
+ const fixed = follow(dir, {
725
+ move: [
726
+ ['src/zdocs', 'docs'],
727
+ ['src/faq.doc.mjs', 'docs/more-guide.doc.mjs'],
728
+ ],
729
+ write: {
730
+ [MANIFEST]: manifest({components: './src', docs: './docs'}),
731
+ 'docs/more-guide.doc.mjs': topic('more-guide'),
732
+ },
733
+ });
734
+ expect(await issuesOf(fixed)).toEqual([]);
735
+ });
736
+
737
+ it('never declares a folder that holds one topic name twice', async () => {
738
+ const dir = writePackage({
739
+ [MANIFEST]: manifest({}),
740
+ 'src/guide.doc.mjs': topic('guide'),
741
+ 'src/more/guide.doc.mjs': topic('guide'),
742
+ });
743
+
744
+ const [first] = (await issuesOf(dir)).filter(issue =>
745
+ issue.message.includes('"src/guide.doc.mjs"'),
746
+ );
747
+ expect(first.message).toBe(
748
+ `${unreachable('src/guide.doc.mjs')} Fix: move it into docs/ and set \`docs: './docs'\` in astryx.integration.mjs.`,
749
+ );
750
+
751
+ const fixed = follow(dir, {
752
+ move: [['src/guide.doc.mjs', 'docs/guide.doc.mjs']],
753
+ write: {[MANIFEST]: manifest({docs: './docs'})},
754
+ });
755
+ const left = await issuesOf(fixed);
756
+ expect(left.map(issue => issue.code)).toEqual(['unreachable_contribution']);
757
+ expect(left[0].message).toContain('"src/more/guide.doc.mjs"');
758
+ });
759
+ });
760
+
761
+ describe('theme fixes bring along what the theme imports', () => {
762
+ it('copies a file a moved theme folder imports from outside it', async () => {
763
+ const dir = writePackage({
764
+ [MANIFEST]: manifest({components: './components'}),
765
+ ...good,
766
+ 'lib/oceanTheme.doc.mjs': themeDoc('ocean'),
767
+ 'lib/oceanTheme.ts': `import {tokens} from '../shared/tokens';\nexport const oceanTheme = {tokens};\n`,
768
+ 'shared/tokens.ts': 'export const tokens = {};\n',
769
+ });
770
+
771
+ const message = only(await issuesOf(dir), 'unreachable_contribution');
772
+ expect(message).toBe(
773
+ `${unreachable('lib/oceanTheme.doc.mjs')} Fix: move lib/ to themes/ocean/ and set \`themes: './themes'\` in astryx.integration.mjs. Also copy shared/tokens.ts into themes/ocean/ and change the import of ../shared/tokens in oceanTheme.ts to ./tokens: a theme can import only files inside its own folder.`,
774
+ );
775
+
776
+ const fixed = follow(dir, {
777
+ move: [['lib', 'themes/ocean']],
778
+ write: {
779
+ [MANIFEST]: manifest({components: './components', themes: './themes'}),
780
+ 'themes/ocean/tokens.ts': 'export const tokens = {};\n',
781
+ 'themes/ocean/oceanTheme.ts': `import {tokens} from './tokens';\nexport const oceanTheme = {tokens};\n`,
782
+ },
783
+ });
784
+ expect(await issuesOf(fixed)).toEqual([]);
785
+ });
786
+
787
+ it('copies what a theme imports from outside the folder a fix declares', async () => {
788
+ const dir = writePackage({
789
+ [MANIFEST]: manifest({components: './components'}),
790
+ ...good,
791
+ 'palettes/sand/sandTheme.doc.mjs': themeDoc('sand'),
792
+ 'palettes/sand/sandTheme.ts': `import {tokens} from '../../shared/tokens.ts';\nexport const sandTheme = {tokens};\n`,
793
+ 'shared/tokens.ts': 'export const tokens = {};\n',
794
+ });
795
+
796
+ const message = only(await issuesOf(dir), 'unreachable_contribution');
797
+ expect(message).toBe(
798
+ `${unreachable('palettes/sand/sandTheme.doc.mjs')} Fix: set \`themes: './palettes'\` in astryx.integration.mjs. Also copy shared/tokens.ts into palettes/sand/ and change the import of ../../shared/tokens.ts in sandTheme.ts to ./tokens.ts: a theme can import only files inside its own folder.`,
799
+ );
800
+
801
+ const fixed = follow(dir, {
802
+ write: {
803
+ [MANIFEST]: manifest({
804
+ components: './components',
805
+ themes: './palettes',
806
+ }),
807
+ 'palettes/sand/tokens.ts': 'export const tokens = {};\n',
808
+ 'palettes/sand/sandTheme.ts': `import {tokens} from './tokens.ts';\nexport const sandTheme = {tokens};\n`,
809
+ },
810
+ });
811
+ expect(await issuesOf(fixed)).toEqual([]);
812
+ });
813
+ });
814
+
815
+ describe('a components root is offered only where every source has a doc', () => {
816
+ it('moves the component instead of declaring a folder with a source no doc covers', async () => {
817
+ const dir = writePackage({
818
+ [MANIFEST]: manifest({docs: './docs'}),
819
+ 'docs/guide.doc.mjs': topic('guide'),
820
+ 'ui/Button.doc.mjs': componentDoc('Button'),
821
+ 'ui/Button.tsx': componentSource('Button'),
822
+ 'ui/Icon.tsx': componentSource('Icon'),
823
+ });
824
+
825
+ const message = only(await issuesOf(dir), 'unreachable_contribution');
826
+ expect(message).toBe(
827
+ `${unreachable('ui/Button.doc.mjs')} Fix: move it and Button.tsx into components/ and set \`components: './components'\` in astryx.integration.mjs.`,
828
+ );
829
+
830
+ const fixed = follow(dir, {
831
+ move: [
832
+ ['ui/Button.doc.mjs', 'components/Button.doc.mjs'],
833
+ ['ui/Button.tsx', 'components/Button.tsx'],
834
+ ],
835
+ write: {
836
+ [MANIFEST]: manifest({docs: './docs', components: './components'}),
837
+ },
838
+ });
839
+ expect(await issuesOf(fixed)).toEqual([]);
840
+ });
841
+ });
842
+
843
+ describe('invalid_component fixes hold when followed', () => {
844
+ it('names the real doc file for a .doc.ts component, and never says to delete it', async () => {
845
+ const dir = writePackage({
846
+ [MANIFEST]: manifest({components: './components'}),
847
+ 'components/Foo/Foo.doc.ts': componentDoc('Foo'),
848
+ });
849
+
850
+ const message = only(await issuesOf(dir), 'invalid_component');
851
+ expect(message).toBe(
852
+ 'Component "Foo" is missing its same-stem source file Foo.tsx. Fix: add Foo.tsx beside Foo.doc.ts.',
853
+ );
854
+
855
+ const fixed = follow(dir, {
856
+ write: {'components/Foo/Foo.tsx': componentSource('Foo')},
857
+ });
858
+ expect(await issuesOf(fixed)).toEqual([]);
859
+ });
860
+
861
+ it('says to add the source beside a .doc.mjs component, and never to delete the doc', async () => {
862
+ const dir = writePackage({
863
+ [MANIFEST]: manifest({components: './components'}),
864
+ 'components/Widget.doc.mjs': componentDoc('Widget'),
865
+ });
866
+
867
+ const message = only(await issuesOf(dir), 'invalid_component');
868
+ expect(message).toBe(
869
+ 'Component "Widget" is missing its same-stem source file Widget.tsx. Fix: add Widget.tsx beside Widget.doc.mjs.',
870
+ );
871
+
872
+ const fixed = follow(dir, {
873
+ write: {'components/Widget.tsx': componentSource('Widget')},
874
+ });
875
+ expect(await issuesOf(fixed)).toEqual([]);
876
+ });
877
+
878
+ it('moves a docs root out of the components root that also reads it', async () => {
879
+ const dir = writePackage({
880
+ [MANIFEST]: manifest({components: './src', docs: './src/docs'}),
881
+ 'src/Good/Good.doc.mjs': componentDoc('Good'),
882
+ 'src/Good/Good.tsx': componentSource('Good'),
883
+ 'src/docs/intro.doc.mjs': topic('intro'),
884
+ });
885
+
886
+ const message = only(await issuesOf(dir), 'invalid_component');
887
+ expect(message).toBe(
888
+ `Component "intro" is missing its same-stem source file intro.tsx. Fix: intro.doc.mjs has type: 'generic', so it is not a component, and the components root src/ also reads the docs root src/docs/ inside it; move src/docs/ to docs/ and set \`docs: './docs'\` in astryx.integration.mjs.`,
889
+ );
890
+
891
+ const fixed = follow(dir, {
892
+ move: [['src/docs', 'docs']],
893
+ write: {[MANIFEST]: manifest({components: './src', docs: './docs'})},
894
+ });
895
+ expect(await issuesOf(fixed)).toEqual([]);
896
+ });
897
+
898
+ it('also moves a topic that sits beside the overlapping docs root', async () => {
899
+ const dir = writePackage({
900
+ [MANIFEST]: manifest({components: './src', docs: './src/docs'}),
901
+ 'src/Good/Good.doc.mjs': componentDoc('Good'),
902
+ 'src/Good/Good.tsx': componentSource('Good'),
903
+ 'src/docs/intro.doc.mjs': topic('intro'),
904
+ 'src/guide.doc.mjs': topic('guide'),
905
+ });
906
+
907
+ const issues = (await issuesOf(dir)).filter(
908
+ issue => issue.code === 'invalid_component',
909
+ );
910
+ expect(issues.map(issue => issue.message)).toContain(
911
+ `Component "guide" is missing its same-stem source file guide.tsx. Fix: guide.doc.mjs has type: 'generic', so it is not a component, and the components root src/ also reads the docs root src/docs/ inside it; move src/docs/ to docs/ and set \`docs: './docs'\` in astryx.integration.mjs, then move it into docs/.`,
912
+ );
913
+
914
+ const fixed = follow(dir, {
915
+ move: [
916
+ ['src/docs', 'docs'],
917
+ ['src/guide.doc.mjs', 'docs/guide.doc.mjs'],
918
+ ],
919
+ write: {[MANIFEST]: manifest({components: './src', docs: './docs'})},
920
+ });
921
+ expect(await issuesOf(fixed)).toEqual([]);
922
+ });
923
+
924
+ it('gives topics their own folder when the docs root and the components root are one folder', async () => {
925
+ const dir = writePackage({
926
+ [MANIFEST]: manifest({components: './src', docs: './src'}),
927
+ 'src/Good.doc.mjs': componentDoc('Good'),
928
+ 'src/Good.tsx': componentSource('Good'),
929
+ 'src/guide.doc.mjs': topic('guide'),
930
+ });
931
+
932
+ const message = only(await issuesOf(dir), 'invalid_component');
933
+ expect(message).toBe(
934
+ `Component "guide" is missing its same-stem source file guide.tsx. Fix: guide.doc.mjs has type: 'generic', so it is not a component, and the docs root and the components root are both src/; move the topics into docs/ and set \`docs: './docs'\` in astryx.integration.mjs.`,
935
+ );
936
+
937
+ const fixed = follow(dir, {
938
+ move: [['src/guide.doc.mjs', 'docs/guide.doc.mjs']],
939
+ write: {[MANIFEST]: manifest({components: './src', docs: './docs'})},
940
+ });
941
+ expect(await issuesOf(fixed)).toEqual([]);
942
+ });
943
+
944
+ it('moves a topic out of a components root that sits inside the docs root', async () => {
945
+ const dir = writePackage({
946
+ [MANIFEST]: manifest({components: './src/components', docs: './src'}),
947
+ 'src/overview.doc.mjs': topic('overview'),
948
+ 'src/components/guide.doc.mjs': topic('guide'),
949
+ });
950
+
951
+ const before = await issuesOf(dir);
952
+ const message = only(before, 'invalid_component');
953
+ expect(message).toBe(
954
+ `Component "guide" is missing its same-stem source file guide.tsx. Fix: guide.doc.mjs has type: 'generic', so it is not a component; move it out of src/components/ (the components root) to another folder under src/ (the docs root).`,
955
+ );
956
+
957
+ const fixed = follow(dir, {
958
+ move: [['src/components/guide.doc.mjs', 'src/guides/guide.doc.mjs']],
959
+ });
960
+ expect(await issuesOf(fixed)).toEqual([]);
961
+ });
962
+
963
+ it('moves a topic in the components root under the declared docs root', async () => {
964
+ const dir = writePackage({
965
+ [MANIFEST]: manifest({components: './components', docs: './docs'}),
966
+ ...good,
967
+ 'components/guide.doc.mjs': topic('guide'),
968
+ 'docs/faq.doc.mjs': topic('faq'),
969
+ });
970
+
971
+ const message = only(await issuesOf(dir), 'invalid_component');
972
+ expect(message).toBe(
973
+ `Component "guide" is missing its same-stem source file guide.tsx. Fix: guide.doc.mjs has type: 'generic', so it is not a component; move it under docs/ (the docs root).`,
974
+ );
975
+
976
+ const fixed = follow(dir, {
977
+ move: [['components/guide.doc.mjs', 'docs/guide.doc.mjs']],
978
+ });
979
+ expect(await issuesOf(fixed)).toEqual([]);
980
+ });
981
+
982
+ it('moves a topic in the components root into a new docs root', async () => {
983
+ const dir = writePackage({
984
+ [MANIFEST]: manifest({components: './components'}),
985
+ ...good,
986
+ 'components/guide.doc.mjs': topic('guide'),
987
+ });
988
+
989
+ const message = only(await issuesOf(dir), 'invalid_component');
990
+ expect(message).toBe(
991
+ `Component "guide" is missing its same-stem source file guide.tsx. Fix: guide.doc.mjs has type: 'generic', so it is not a component; move it into docs/ and set \`docs: './docs'\` in astryx.integration.mjs.`,
992
+ );
993
+
994
+ const fixed = follow(dir, {
995
+ move: [['components/guide.doc.mjs', 'docs/guide.doc.mjs']],
996
+ write: {
997
+ [MANIFEST]: manifest({components: './components', docs: './docs'}),
998
+ },
999
+ });
1000
+ expect(await issuesOf(fixed)).toEqual([]);
1001
+ });
1002
+
1003
+ it('moves a theme in the components root into themes/<slug>/', async () => {
1004
+ const dir = writePackage({
1005
+ [MANIFEST]: manifest({components: './components'}),
1006
+ ...good,
1007
+ 'components/ocean/oceanTheme.doc.mjs': themeDoc('ocean'),
1008
+ 'components/ocean/oceanTheme.ts': themeSource('oceanTheme'),
1009
+ });
1010
+
1011
+ const message = only(await issuesOf(dir), 'invalid_component');
1012
+ expect(message).toBe(
1013
+ `Component "oceanTheme" is missing its same-stem source file oceanTheme.tsx. Fix: oceanTheme.doc.mjs has type: 'theme', so it is not a component; move components/ocean/ to themes/ocean/ and set \`themes: './themes'\` in astryx.integration.mjs.`,
1014
+ );
1015
+
1016
+ const fixed = follow(dir, {
1017
+ move: [['components/ocean', 'themes/ocean']],
1018
+ write: {
1019
+ [MANIFEST]: manifest({components: './components', themes: './themes'}),
1020
+ },
1021
+ });
1022
+ expect(await issuesOf(fixed)).toEqual([]);
1023
+ });
1024
+
1025
+ it('moves the components out of a components root that is the package root', async () => {
1026
+ const dir = writePackage({
1027
+ [MANIFEST]: manifest({components: './', docs: './docs'}),
1028
+ 'Button.doc.mjs': componentDoc('Button'),
1029
+ 'Button.tsx': componentSource('Button'),
1030
+ 'docs/guide.doc.mjs': topic('guide'),
1031
+ });
1032
+
1033
+ const message = only(await issuesOf(dir), 'invalid_component');
1034
+ expect(message).toBe(
1035
+ `Component "guide" is missing its same-stem source file guide.tsx. Fix: guide.doc.mjs has type: 'generic', so it is not a component, and the components root is the package root, which reads every doc in the package; create components/, move the components into it, and set \`components: './components'\` in astryx.integration.mjs.`,
1036
+ );
1037
+
1038
+ const fixed = follow(dir, {
1039
+ move: [
1040
+ ['Button.doc.mjs', 'components/Button.doc.mjs'],
1041
+ ['Button.tsx', 'components/Button.tsx'],
1042
+ ],
1043
+ write: {
1044
+ [MANIFEST]: manifest({components: './components', docs: './docs'}),
1045
+ },
1046
+ });
1047
+ expect(await issuesOf(fixed)).toEqual([]);
1048
+ });
1049
+ });
1050
+
1051
+ describe('component fixes never leave another root reading the new files', () => {
1052
+ it('moves the components out of a folder another root also reads before a doc is added', async () => {
1053
+ const dir = writePackage({
1054
+ [MANIFEST]: manifest({components: './src', docs: './src'}),
1055
+ 'src/Good.doc.mjs': componentDoc('Good'),
1056
+ 'src/Good.tsx': componentSource('Good'),
1057
+ 'src/Helper.tsx': componentSource('Helper'),
1058
+ });
1059
+
1060
+ const message = only(await issuesOf(dir), 'source_without_component_doc');
1061
+ expect(message).toBe(
1062
+ `Component source "Helper.tsx" has no same-stem metadata file Helper.doc.mjs, so Astryx ignores it. Fix: add Helper.doc.mjs beside it with type: 'component'; \`astryx docs authoring component-doc\` lists its fields. The docs root is also src/; move the components there into components/ and set \`components: './components'\` in astryx.integration.mjs.`,
1063
+ );
1064
+
1065
+ const fixed = follow(dir, {
1066
+ move: [
1067
+ ['src/Good.doc.mjs', 'components/Good.doc.mjs'],
1068
+ ['src/Good.tsx', 'components/Good.tsx'],
1069
+ ['src/Helper.tsx', 'components/Helper.tsx'],
1070
+ ],
1071
+ write: {
1072
+ [MANIFEST]: manifest({components: './components', docs: './src'}),
1073
+ 'components/Helper.doc.mjs': componentDoc('Helper'),
1074
+ },
1075
+ });
1076
+ expect(await issuesOf(fixed)).toEqual([]);
1077
+ });
1078
+
1079
+ it('never moves the components into a components/ that holds a source without a doc', async () => {
1080
+ const dir = writePackage({
1081
+ [MANIFEST]: manifest({components: './', docs: './docs'}),
1082
+ 'Button.doc.mjs': componentDoc('Button'),
1083
+ 'Button.tsx': componentSource('Button'),
1084
+ 'docs/guide.doc.mjs': topic('guide'),
1085
+ 'components/Helper.tsx': componentSource('Helper'),
1086
+ });
1087
+
1088
+ const message = only(await issuesOf(dir), 'invalid_component');
1089
+ expect(message).toBe(
1090
+ `Component "guide" is missing its same-stem source file guide.tsx. Fix: guide.doc.mjs has type: 'generic', so it is not a component, and the components root is the package root, which reads every doc in the package; move the components into a folder that holds only components, and set \`components\` to that folder in astryx.integration.mjs.`,
1091
+ );
1092
+
1093
+ const fixed = follow(dir, {
1094
+ move: [
1095
+ ['Button.doc.mjs', 'ui/Button.doc.mjs'],
1096
+ ['Button.tsx', 'ui/Button.tsx'],
1097
+ ],
1098
+ write: {[MANIFEST]: manifest({components: './ui', docs: './docs'})},
1099
+ });
1100
+ expect(await issuesOf(fixed)).toEqual([]);
1101
+ });
1102
+ });
1103
+
1104
+ describe('source_without_component_doc fixes hold when followed', () => {
1105
+ it('adds the named doc, which resolves the warning', async () => {
1106
+ const dir = writePackage({
1107
+ [MANIFEST]: manifest({components: './components'}),
1108
+ 'components/Widget.tsx': componentSource('Widget'),
1109
+ });
1110
+
1111
+ const message = only(await issuesOf(dir), 'source_without_component_doc');
1112
+ expect(message).toBe(
1113
+ 'Component source "Widget.tsx" has no same-stem metadata file Widget.doc.mjs, so Astryx ignores it. Fix: add Widget.doc.mjs beside it with type: \'component\'; `astryx docs authoring component-doc` lists its fields.',
1114
+ );
1115
+
1116
+ const fixed = follow(dir, {
1117
+ write: {'components/Widget.doc.mjs': componentDoc('Widget')},
1118
+ });
1119
+ expect(await issuesOf(fixed)).toEqual([]);
1120
+ });
1121
+
1122
+ it('never asks for a doc that is already there but hidden', async () => {
1123
+ const dir = writePackage({
1124
+ [MANIFEST]: manifest({components: './components'}),
1125
+ 'components/Secret.doc.mjs':
1126
+ "export default {\n type: 'component',\n name: 'Secret',\n hidden: true,\n};\n",
1127
+ 'components/Secret.tsx': componentSource('Secret'),
1128
+ });
1129
+
1130
+ expect(await issuesOf(dir)).toEqual([]);
1131
+ });
1132
+ });
1133
+
1134
+ describe('codemod fixes hold when followed', () => {
1135
+ it('moves a stray codemod into a version folder, and renames a mis-named one', async () => {
1136
+ const dir = writePackage({
1137
+ [MANIFEST]: manifest({codemods: './codemods'}),
1138
+ 'codemods/forgotten.mjs': codemod,
1139
+ 'codemods/v1/rename.mjs': codemod,
1140
+ });
1141
+
1142
+ const issues = await issuesOf(dir);
1143
+ expect(only(issues, 'codemod_outside_version')).toBe(
1144
+ 'Codemod file "codemods/forgotten.mjs" is outside a version folder, so upgrade will never load it. Fix: move it into the folder named for the version it migrates to, for example codemods/1.2.0/forgotten.mjs.',
1145
+ );
1146
+ expect(only(issues, 'invalid_codemod_version')).toBe(
1147
+ 'Codemod folder "v1" is not an exact semver version such as 1.2.0. Fix: rename it to the exact version its codemods migrate to.',
1148
+ );
1149
+
1150
+ const fixed = follow(dir, {
1151
+ move: [
1152
+ ['codemods/forgotten.mjs', 'codemods/1.2.0/forgotten.mjs'],
1153
+ ['codemods/v1', 'codemods/1.0.0'],
1154
+ ],
1155
+ });
1156
+ expect(await issuesOf(fixed)).toEqual([]);
1157
+ });
1158
+
1159
+ it('never offers the manifest as a codemod when the codemods root is the package root', async () => {
1160
+ const dir = writePackage({
1161
+ [MANIFEST]: manifest({codemods: './', components: './components'}),
1162
+ ...good,
1163
+ '1.2.0/rename.mjs': codemod,
1164
+ 'index.mjs': 'export const version = 1;\n',
1165
+ });
1166
+
1167
+ const issues = await issuesOf(dir);
1168
+ expect(issues.map(issue => issue.message).join('\n')).not.toContain(
1169
+ `Codemod file "${MANIFEST}"`,
1170
+ );
1171
+ const rootFix =
1172
+ "Fix: the codemods root is the package root, so everything in the package is read as a codemod or a version folder. Create codemods/, move each version folder into it, and set `codemods: './codemods'` in astryx.integration.mjs.";
1173
+ expect(only(issues, 'codemod_outside_version')).toBe(
1174
+ `Codemod file "index.mjs" is outside a version folder, so upgrade will never load it. ${rootFix} If it is a codemod, move it into the folder named for the version it migrates to, for example codemods/1.2.0/index.mjs.`,
1175
+ );
1176
+ expect(only(issues, 'invalid_codemod_version')).toBe(
1177
+ `Codemod folder "components" is not an exact semver version such as 1.2.0. ${rootFix}`,
1178
+ );
1179
+
1180
+ // index.mjs is the package entry, not a codemod, so it stays.
1181
+ const fixed = follow(dir, {
1182
+ mkdir: ['codemods'],
1183
+ move: [['1.2.0', 'codemods/1.2.0']],
1184
+ write: {
1185
+ [MANIFEST]: manifest({
1186
+ codemods: './codemods',
1187
+ components: './components',
1188
+ }),
1189
+ },
1190
+ });
1191
+ expect(await issuesOf(fixed)).toEqual([]);
1192
+ });
1193
+
1194
+ it('gives codemods their own folder when the codemods root holds another root', async () => {
1195
+ const dir = writePackage({
1196
+ [MANIFEST]: manifest({
1197
+ codemods: './src',
1198
+ components: './src/components',
1199
+ }),
1200
+ 'src/components/Good.doc.mjs': componentDoc('Good'),
1201
+ 'src/components/Good.tsx': componentSource('Good'),
1202
+ 'src/1.2.0/rename.mjs': codemod,
1203
+ });
1204
+
1205
+ const message = only(await issuesOf(dir), 'invalid_codemod_version');
1206
+ expect(message).toBe(
1207
+ 'Codemod folder "components" is not an exact semver version such as 1.2.0. Fix: the codemods root src/ also holds the components root src/components/. Create codemods/, move each version folder into it, and set `codemods: \'./codemods\'` in astryx.integration.mjs.',
1208
+ );
1209
+
1210
+ const fixed = follow(dir, {
1211
+ mkdir: ['codemods'],
1212
+ move: [['src/1.2.0', 'codemods/1.2.0']],
1213
+ write: {
1214
+ [MANIFEST]: manifest({
1215
+ codemods: './codemods',
1216
+ components: './src/components',
1217
+ }),
1218
+ },
1219
+ });
1220
+ expect(await issuesOf(fixed)).toEqual([]);
1221
+ });
1222
+
1223
+ it('moves a root out of codemods/ instead of renaming it as a version', async () => {
1224
+ const dir = writePackage({
1225
+ [MANIFEST]: manifest({
1226
+ codemods: './codemods',
1227
+ components: './codemods/components',
1228
+ }),
1229
+ 'codemods/components/Good.doc.mjs': componentDoc('Good'),
1230
+ 'codemods/components/Good.tsx': componentSource('Good'),
1231
+ 'codemods/1.2.0/rename.mjs': codemod,
1232
+ });
1233
+
1234
+ const message = only(await issuesOf(dir), 'invalid_codemod_version');
1235
+ expect(message).toBe(
1236
+ 'Codemod folder "components" is not an exact semver version such as 1.2.0. Fix: it holds the components root codemods/components/; move that root out of codemods/ and update `components` in astryx.integration.mjs.',
1237
+ );
1238
+
1239
+ const fixed = follow(dir, {
1240
+ move: [['codemods/components', 'components']],
1241
+ write: {
1242
+ [MANIFEST]: manifest({
1243
+ codemods: './codemods',
1244
+ components: './components',
1245
+ }),
1246
+ },
1247
+ });
1248
+ expect(await issuesOf(fixed)).toEqual([]);
1249
+ });
1250
+ it('picks a new folder for codemods when codemods/ already holds something else', async () => {
1251
+ const dir = writePackage({
1252
+ [MANIFEST]: manifest({codemods: './', components: './components'}),
1253
+ ...good,
1254
+ '1.2.0/rename.mjs': codemod,
1255
+ 'codemods/notes.mjs': 'export const notes = 1;\n',
1256
+ });
1257
+
1258
+ const issues = await issuesOf(dir);
1259
+ const rootFix =
1260
+ 'Fix: the codemods root is the package root, so everything in the package is read as a codemod or a version folder. Move each version folder into a new folder of their own, and set `codemods` to that folder in astryx.integration.mjs.';
1261
+ expect(
1262
+ issues
1263
+ .filter(issue => issue.code === 'invalid_codemod_version')
1264
+ .map(issue => issue.message),
1265
+ ).toEqual([
1266
+ `Codemod folder "codemods" is not an exact semver version such as 1.2.0. ${rootFix}`,
1267
+ `Codemod folder "components" is not an exact semver version such as 1.2.0. ${rootFix}`,
1268
+ ]);
1269
+
1270
+ const fixed = follow(dir, {
1271
+ move: [['1.2.0', 'migrations/1.2.0']],
1272
+ write: {
1273
+ [MANIFEST]: manifest({
1274
+ codemods: './migrations',
1275
+ components: './components',
1276
+ }),
1277
+ },
1278
+ });
1279
+ expect(await issuesOf(fixed)).toEqual([]);
1280
+ });
1281
+
1282
+ it('moves contribution metadata out of the codemods root instead of into a version', async () => {
1283
+ const dir = writePackage({
1284
+ [MANIFEST]: manifest({codemods: './codemods'}),
1285
+ 'codemods/1.2.0/rename.mjs': codemod,
1286
+ 'codemods/guide.doc.mjs': topic('guide'),
1287
+ });
1288
+
1289
+ const message = only(await issuesOf(dir), 'codemod_outside_version');
1290
+ expect(message).toBe(
1291
+ `Codemod file "codemods/guide.doc.mjs" is outside a version folder, so upgrade will never load it. Fix: guide.doc.mjs has type: 'generic', so it is not a codemod; move it into docs/ and set \`docs: './docs'\` in astryx.integration.mjs.`,
1292
+ );
1293
+
1294
+ const fixed = follow(dir, {
1295
+ move: [['codemods/guide.doc.mjs', 'docs/guide.doc.mjs']],
1296
+ write: {[MANIFEST]: manifest({codemods: './codemods', docs: './docs'})},
1297
+ });
1298
+ expect(await issuesOf(fixed)).toEqual([]);
1299
+ });
1300
+
1301
+ it('names a theme source whose descriptor is a .doc.ts file', async () => {
1302
+ const dir = writePackage({
1303
+ [MANIFEST]: manifest({codemods: './codemods'}),
1304
+ 'codemods/1.2.0/rename.mjs': codemod,
1305
+ 'codemods/oceanTheme.doc.ts': themeDoc('ocean'),
1306
+ 'codemods/oceanTheme.ts': themeSource('oceanTheme'),
1307
+ });
1308
+
1309
+ const issues = (await issuesOf(dir)).filter(
1310
+ issue => issue.code === 'codemod_outside_version',
1311
+ );
1312
+ const move =
1313
+ "move oceanTheme.doc.ts and oceanTheme.ts (with any local files it imports) into themes/ocean/ and set `themes: './themes'` in astryx.integration.mjs. Also rename oceanTheme.doc.ts to oceanTheme.doc.mjs: a theme descriptor is a .doc.mjs file.";
1314
+ expect(issues.map(issue => issue.message)).toEqual([
1315
+ `Codemod file "codemods/oceanTheme.doc.ts" is outside a version folder, so upgrade will never load it. Fix: oceanTheme.doc.ts has type: 'theme', so it is not a codemod; ${move}`,
1316
+ `Codemod file "codemods/oceanTheme.ts" is outside a version folder, so upgrade will never load it. Fix: oceanTheme.ts is the source of oceanTheme.doc.ts, which has type: 'theme', so neither is a codemod; ${move}`,
1317
+ ]);
1318
+
1319
+ const fixed = follow(dir, {
1320
+ move: [
1321
+ ['codemods/oceanTheme.doc.ts', 'themes/ocean/oceanTheme.doc.mjs'],
1322
+ ['codemods/oceanTheme.ts', 'themes/ocean/oceanTheme.ts'],
1323
+ ],
1324
+ write: {
1325
+ [MANIFEST]: manifest({codemods: './codemods', themes: './themes'}),
1326
+ },
1327
+ });
1328
+ expect(await issuesOf(fixed)).toEqual([]);
1329
+ });
1330
+
1331
+ it('moves a file a theme imports out of the codemods root with the theme', async () => {
1332
+ const dir = writePackage({
1333
+ [MANIFEST]: manifest({codemods: './codemods'}),
1334
+ 'codemods/1.2.0/rename.mjs': codemod,
1335
+ 'codemods/oceanTheme.doc.mjs': themeDoc('ocean'),
1336
+ 'codemods/oceanTheme.ts': `import {tokens} from './oceanTokens';\nexport const oceanTheme = {tokens};\n`,
1337
+ 'codemods/oceanTokens.ts': 'export const tokens = {};\n',
1338
+ });
1339
+
1340
+ const [tokens] = (await issuesOf(dir)).filter(issue =>
1341
+ issue.message.includes('"codemods/oceanTokens.ts"'),
1342
+ );
1343
+ expect(tokens.message).toBe(
1344
+ `Codemod file "codemods/oceanTokens.ts" is outside a version folder, so upgrade will never load it. Fix: oceanTokens.ts is imported by oceanTheme.ts, the source of oceanTheme.doc.mjs, which has type: 'theme', so none of them is a codemod; move oceanTheme.doc.mjs and oceanTheme.ts (with any local files it imports) into themes/ocean/ and set \`themes: './themes'\` in astryx.integration.mjs.`,
1345
+ );
1346
+
1347
+ const fixed = follow(dir, {
1348
+ move: [
1349
+ ['codemods/oceanTheme.doc.mjs', 'themes/ocean/oceanTheme.doc.mjs'],
1350
+ ['codemods/oceanTheme.ts', 'themes/ocean/oceanTheme.ts'],
1351
+ ['codemods/oceanTokens.ts', 'themes/ocean/oceanTokens.ts'],
1352
+ ],
1353
+ write: {
1354
+ [MANIFEST]: manifest({codemods: './codemods', themes: './themes'}),
1355
+ },
1356
+ });
1357
+ expect(await issuesOf(fixed)).toEqual([]);
1358
+ });
1359
+
1360
+ it('moves a theme source out of the codemods root with its descriptor', async () => {
1361
+ const dir = writePackage({
1362
+ [MANIFEST]: manifest({codemods: './codemods'}),
1363
+ 'codemods/1.2.0/rename.mjs': codemod,
1364
+ 'codemods/oceanTheme.doc.mjs': themeDoc('ocean'),
1365
+ 'codemods/oceanTheme.ts': themeSource('oceanTheme'),
1366
+ });
1367
+
1368
+ const issues = (await issuesOf(dir)).filter(
1369
+ issue => issue.code === 'codemod_outside_version',
1370
+ );
1371
+ const move =
1372
+ "move oceanTheme.doc.mjs and oceanTheme.ts (with any local files it imports) into themes/ocean/ and set `themes: './themes'` in astryx.integration.mjs.";
1373
+ expect(issues.map(issue => issue.message)).toEqual([
1374
+ `Codemod file "codemods/oceanTheme.doc.mjs" is outside a version folder, so upgrade will never load it. Fix: oceanTheme.doc.mjs has type: 'theme', so it is not a codemod; ${move}`,
1375
+ `Codemod file "codemods/oceanTheme.ts" is outside a version folder, so upgrade will never load it. Fix: oceanTheme.ts is the source of oceanTheme.doc.mjs, which has type: 'theme', so neither is a codemod; ${move}`,
1376
+ ]);
1377
+
1378
+ const fixed = follow(dir, {
1379
+ move: [
1380
+ ['codemods/oceanTheme.doc.mjs', 'themes/ocean/oceanTheme.doc.mjs'],
1381
+ ['codemods/oceanTheme.ts', 'themes/ocean/oceanTheme.ts'],
1382
+ ],
1383
+ write: {
1384
+ [MANIFEST]: manifest({codemods: './codemods', themes: './themes'}),
1385
+ },
1386
+ });
1387
+ expect(await issuesOf(fixed)).toEqual([]);
1388
+ });
1389
+ });