@astryxdesign/cli 0.6.4 → 0.6.5-canary.00f1ed9

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 (405) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +103 -95
  3. package/api/build/_adapter.d.mts +36 -2
  4. package/api/build/_adapter.mjs +41 -10
  5. package/api/build/build.doc.mjs +8 -3
  6. package/api/build/build.test.mjs +60 -2
  7. package/api/build/kit/kit.mjs +109 -26
  8. package/api/build/kit/rank.d.mts +24 -8
  9. package/api/build/kit/rank.mjs +277 -97
  10. package/api/build/kit/rank.test.mjs +231 -48
  11. package/api/component/_adapter.d.mts +25 -0
  12. package/api/component/_adapter.mjs +59 -5
  13. package/api/component/component.d.mts +6 -3
  14. package/api/component/component.doc.mjs +37 -17
  15. package/api/component/component.mjs +249 -9
  16. package/api/component/component.type.d.mts +25 -0
  17. package/api/component/component.type.mjs +44 -0
  18. package/api/discover/_adapter.d.mts +114 -6
  19. package/api/discover/_adapter.mjs +372 -17
  20. package/api/discover/_adapter.test.mjs +215 -0
  21. package/api/discover/_catalog-view.d.mts +115 -0
  22. package/api/discover/_catalog-view.mjs +203 -0
  23. package/api/discover/_catalog-view.test.mjs +128 -0
  24. package/api/discover/detail/detail.d.mts +18 -6
  25. package/api/discover/detail/detail.mjs +67 -13
  26. package/api/discover/detail/detail.test.mjs +85 -0
  27. package/api/discover/detail/item/item.d.mts +26 -0
  28. package/api/discover/detail/item/item.mjs +78 -0
  29. package/api/discover/detail/item/item.test.mjs +73 -0
  30. package/api/discover/discover.d.mts +3 -9
  31. package/api/discover/discover.doc.mjs +61 -18
  32. package/api/discover/discover.mjs +220 -36
  33. package/api/discover/discover.test.mjs +11 -2
  34. package/api/discover/discover.type.d.mts +147 -8
  35. package/api/discover/discover.type.mjs +102 -12
  36. package/api/discover/list/list.d.mts +20 -6
  37. package/api/discover/list/list.mjs +45 -12
  38. package/api/discover/list/list.test.mjs +46 -0
  39. package/api/discover/search/search.d.mts +18 -16
  40. package/api/discover/search/search.mjs +102 -56
  41. package/api/discover/search/search.test.mjs +144 -10
  42. package/api/docs/_adapter.d.mts +8 -3
  43. package/api/docs/_adapter.mjs +14 -6
  44. package/api/docs/docOverlays.test.mjs +27 -1
  45. package/api/docs/docs.doc.mjs +2 -2
  46. package/api/docs/docs.test.mjs +54 -18
  47. package/api/docs/integration-tree.test.mjs +17 -0
  48. package/api/docs/integrationDocs.test.mjs +27 -5
  49. package/api/doctor/doctor.d.mts +8 -3
  50. package/api/doctor/doctor.doc.mjs +17 -8
  51. package/api/doctor/doctor.mjs +90 -9
  52. package/api/doctor/doctor.test.mjs +122 -10
  53. package/api/doctor/doctor.type.d.mts +1 -1
  54. package/api/doctor/doctor.type.mjs +1 -1
  55. package/api/error.d.mts +22 -0
  56. package/api/error.mjs +42 -0
  57. package/api/gap-report/gap-report.doc.mjs +19 -10
  58. package/api/hook/hook.doc.mjs +6 -3
  59. package/api/index.d.mts +1 -0
  60. package/api/index.mjs +5 -3
  61. package/api/init/init.doc.mjs +17 -12
  62. package/api/integration/add-contribution.d.mts +2 -1
  63. package/api/integration/add-contribution.mjs +7 -3
  64. package/api/integration/add-contribution.test.mjs +3 -3
  65. package/api/integration/add-helpers.d.mts +5 -2
  66. package/api/integration/add-helpers.mjs +36 -9
  67. package/api/integration/add-theme.mjs +266 -23
  68. package/api/integration/add-theme.test.mjs +247 -0
  69. package/api/integration/authoring-checks.mjs +12 -9
  70. package/api/integration/authoring-checks.type.d.mts +8 -0
  71. package/api/integration/authoring-checks.type.mjs +7 -3
  72. package/api/integration/integration-authoring.type.d.mts +4 -1
  73. package/api/integration/integration-authoring.type.mjs +6 -1
  74. package/api/integration/integrationAdd.doc.mjs +6 -0
  75. package/api/integration/integrationAddTheme.doc.mjs +10 -0
  76. package/api/integration/integrationComponentConflicts.doc.mjs +1 -1
  77. package/api/integration/integrationDocConflicts.doc.mjs +1 -1
  78. package/api/integration/integrationPackCheck.doc.mjs +3 -3
  79. package/api/integration/integrationTemplateConflicts.doc.mjs +1 -1
  80. package/api/integration/pack-check.lifecycle-output.test.mjs +107 -0
  81. package/api/integration/pack-check.mjs +92 -10
  82. package/api/integration/pack-check.test.mjs +140 -1
  83. package/api/integration/pack-check.type.mjs +1 -1
  84. package/api/integration/validate-integration.d.mts +4 -2
  85. package/api/integration/validate-integration.mjs +7 -2
  86. package/api/integration/validate-integration.test.mjs +55 -0
  87. package/api/integration/validate-integration.type.d.mts +5 -0
  88. package/api/integration/validate-integration.type.mjs +5 -1
  89. package/api/integration/validateIntegration.doc.mjs +1 -1
  90. package/api/json/assertResponse.doc.mjs +1 -1
  91. package/api/json/isError.doc.mjs +1 -1
  92. package/api/layout/expand/expand.mjs +12 -7
  93. package/api/layout/expand/expand.receipt.test.mjs +74 -0
  94. package/api/layout/layout.type.d.mts +1 -0
  95. package/api/layout/layout.type.mjs +1 -0
  96. package/api/layout/layoutExpand.doc.mjs +1 -1
  97. package/api/search/search.d.mts +51 -1
  98. package/api/search/search.doc.mjs +2 -2
  99. package/api/search/search.mjs +299 -17
  100. package/api/search/search.test.mjs +216 -18
  101. package/api/swizzle/copy/copy.mjs +66 -3
  102. package/api/swizzle/swizzle.doc.mjs +11 -5
  103. package/api/template/copy/copy.mjs +15 -9
  104. package/api/template/copy/copy.receipt.test.mjs +77 -0
  105. package/api/template/copy/copy.test.mjs +9 -0
  106. package/api/template/show/show.mjs +15 -4
  107. package/api/template/show/show.test.mjs +76 -0
  108. package/api/template/template-integration.test.mjs +14 -0
  109. package/api/template/template.d.mts +1 -1
  110. package/api/template/template.doc.mjs +8 -3
  111. package/api/template/template.mjs +1 -0
  112. package/api/template/template.type.d.mts +2 -0
  113. package/api/template/template.type.mjs +2 -0
  114. package/api/theme/add/add.mjs +17 -25
  115. package/api/theme/add/add.rollback.test.mjs +158 -0
  116. package/api/theme/add/add.staging.test.mjs +40 -23
  117. package/api/theme/build/build.d.mts +24 -0
  118. package/api/theme/build/build.family.test.mjs +7 -12
  119. package/api/theme/build/build.mjs +243 -26
  120. package/api/theme/build/build.project-core.test.mjs +165 -0
  121. package/api/theme/build/build.rollback.test.mjs +148 -0
  122. package/api/theme/generateTonalPalette.doc.mjs +1 -2
  123. package/api/theme/listThemes.doc.mjs +1 -1
  124. package/api/theme/themeAdd.doc.mjs +9 -10
  125. package/api/theme/themeBuild.doc.mjs +13 -13
  126. package/api/theme/themeList.doc.mjs +1 -1
  127. package/api/theme/themeListAvailable.doc.mjs +2 -1
  128. package/api/theme/themePaletteGenerate.doc.mjs +15 -8
  129. package/api/theme/themeTargets.doc.mjs +3 -2
  130. package/api/theme/themeTemplate.doc.mjs +2 -1
  131. package/api/upgrade/run/files-changed.test.mjs +111 -0
  132. package/api/upgrade/run/run.mjs +25 -6
  133. package/api/upgrade/run/run.test.mjs +45 -1
  134. package/api/upgrade/upgrade.doc.mjs +24 -22
  135. package/api/upgrade/upgrade.type.d.mts +1 -0
  136. package/api/upgrade/upgrade.type.mjs +3 -2
  137. package/assets/codemods/__tests__/runner.test.mjs +3 -1
  138. package/assets/codemods/file-count.test.mjs +163 -0
  139. package/assets/codemods/integration-runner.mjs +3 -3
  140. package/assets/codemods/runner.mjs +5 -4
  141. package/assets/docs/README.md +4 -2
  142. package/assets/docs/browser-support.doc.mjs +11 -11
  143. package/assets/docs/color.doc.mjs +8 -2
  144. package/assets/docs/elevation.doc.mjs +6 -4
  145. package/assets/docs/getting-started.doc.mjs +5 -16
  146. package/assets/docs/icons.doc.mjs +3 -21
  147. package/assets/docs/illustrations.doc.mjs +7 -15
  148. package/assets/docs/internationalization.doc.mjs +7 -5
  149. package/assets/docs/layout.doc.dense.mjs +130 -82
  150. package/assets/docs/layout.doc.mjs +133 -77
  151. package/assets/docs/migration.doc.mjs +19 -21
  152. package/assets/docs/motion.doc.mjs +16 -3
  153. package/assets/docs/principles.doc.dense.mjs +5 -5
  154. package/assets/docs/principles.doc.mjs +8 -0
  155. package/assets/docs/principles.doc.zh.mjs +6 -6
  156. package/assets/docs/shape.doc.mjs +8 -3
  157. package/assets/docs/spacing.doc.mjs +7 -2
  158. package/assets/docs/styling-libraries.doc.mjs +6 -2
  159. package/assets/docs/styling.doc.mjs +19 -23
  160. package/assets/docs/theme.doc.dense.mjs +58 -18
  161. package/assets/docs/theme.doc.mjs +57 -47
  162. package/assets/docs/theme.doc.zh.mjs +9 -8
  163. package/assets/docs/tokens.doc.dense.mjs +2 -2
  164. package/assets/docs/tokens.doc.mjs +389 -8
  165. package/assets/docs/tokens.doc.zh.mjs +2 -2
  166. package/assets/docs/tree/add-a-component.doc.mjs +75 -0
  167. package/assets/docs/tree/add-a-theme.doc.mjs +85 -0
  168. package/assets/docs/tree/add-a-topic.doc.mjs +144 -0
  169. package/assets/docs/tree/agent-guidance.doc.mjs +138 -0
  170. package/assets/docs/tree/block-template.doc.mjs +130 -0
  171. package/assets/docs/tree/build-the-template.doc.mjs +28 -0
  172. package/assets/docs/tree/building-blocks.doc.mjs +46 -0
  173. package/assets/docs/tree/check-your-docs.doc.mjs +137 -0
  174. package/assets/docs/tree/checks.doc.mjs +119 -0
  175. package/assets/docs/tree/codemods.doc.mjs +147 -0
  176. package/assets/docs/tree/component-family.doc.mjs +113 -0
  177. package/assets/docs/tree/component-imports.doc.mjs +69 -0
  178. package/assets/docs/tree/component-lookups.doc.mjs +149 -0
  179. package/assets/docs/tree/components.doc.mjs +23 -0
  180. package/assets/docs/tree/configuration.doc.mjs +23 -0
  181. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +182 -0
  182. package/assets/docs/tree/define-the-theme.doc.mjs +118 -0
  183. package/assets/docs/tree/describe-the-component.doc.mjs +57 -0
  184. package/assets/docs/tree/docs.doc.mjs +21 -0
  185. package/assets/docs/tree/document-the-template.doc.mjs +28 -0
  186. package/assets/docs/tree/document-the-theme.doc.mjs +68 -0
  187. package/assets/docs/tree/export-template-assets.doc.mjs +147 -0
  188. package/assets/docs/tree/extend-or-replace.doc.mjs +103 -0
  189. package/assets/docs/tree/fonts-and-assets.doc.mjs +106 -0
  190. package/assets/docs/tree/generate-a-palette.doc.mjs +66 -0
  191. package/assets/docs/tree/grade-template-with-agent.doc.mjs +105 -0
  192. package/assets/docs/tree/help.doc.mjs +16 -0
  193. package/assets/docs/tree/integrations.doc.mjs +25 -451
  194. package/assets/docs/tree/links.doc.mjs +98 -0
  195. package/assets/docs/tree/package-and-test.doc.mjs +32 -0
  196. package/assets/docs/tree/page-template.doc.mjs +71 -0
  197. package/assets/docs/tree/publishing.doc.mjs +111 -0
  198. package/assets/docs/tree/quick-start.doc.mjs +272 -0
  199. package/assets/docs/tree/replace-a-core-component.doc.mjs +104 -0
  200. package/assets/docs/tree/replace-a-core-template.doc.mjs +172 -0
  201. package/assets/docs/tree/sections-and-placement.doc.mjs +108 -0
  202. package/assets/docs/tree/see-it-in-an-app.doc.mjs +59 -0
  203. package/assets/docs/tree/ship.doc.mjs +16 -0
  204. package/assets/docs/tree/short-and-findable.doc.mjs +108 -0
  205. package/assets/docs/tree/single-component.doc.mjs +165 -0
  206. package/assets/docs/tree/start-a-template.doc.mjs +143 -0
  207. package/assets/docs/tree/subcomponent.doc.mjs +115 -0
  208. package/assets/docs/tree/template-assets.doc.mjs +64 -0
  209. package/assets/docs/tree/template-doc-overview.doc.mjs +121 -0
  210. package/assets/docs/tree/template-fonts.doc.mjs +102 -0
  211. package/assets/docs/tree/template-grading-rubric.doc.mjs +452 -0
  212. package/assets/docs/tree/template-icons.doc.mjs +97 -0
  213. package/assets/docs/tree/template-images-media.doc.mjs +127 -0
  214. package/assets/docs/tree/template-styles.doc.mjs +93 -0
  215. package/assets/docs/tree/templates.doc.mjs +34 -0
  216. package/assets/docs/tree/test-in-an-app.doc.mjs +115 -0
  217. package/assets/docs/tree/test-template-in-app.doc.mjs +128 -0
  218. package/assets/docs/tree/themes.doc.mjs +39 -0
  219. package/assets/docs/tree/troubleshooting.doc.mjs +153 -0
  220. package/assets/docs/tree/upgrading.doc.mjs +103 -0
  221. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +51 -0
  222. package/assets/docs/tree/verify-packed-template.doc.mjs +77 -0
  223. package/assets/docs/tree/versioning.doc.mjs +162 -0
  224. package/assets/docs/tree/write-good-templates.doc.mjs +64 -0
  225. package/assets/docs/tree/write-the-template-file.doc.mjs +154 -0
  226. package/assets/docs/typography.doc.mjs +24 -4
  227. package/assets/docs/working-with-ai.doc.mjs +30 -22
  228. package/assets/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.tsx +1 -1
  229. package/assets/templates/pages/ai-chat/template.doc.mjs +16 -1
  230. package/assets/templates/pages/ai-chat-landing/template.doc.mjs +9 -1
  231. package/assets/templates/pages/blank/template.doc.mjs +3 -1
  232. package/assets/templates/pages/canvas-editor/template.doc.mjs +9 -1
  233. package/assets/templates/pages/centered-hero/template.doc.mjs +3 -1
  234. package/assets/templates/pages/checkout-wizard/template.doc.mjs +1 -0
  235. package/assets/templates/pages/classic-gallery/template.doc.mjs +3 -1
  236. package/assets/templates/pages/contact-form/template.doc.mjs +16 -1
  237. package/assets/templates/pages/dashboard/template.doc.mjs +15 -1
  238. package/assets/templates/pages/dashboard-alert-rail/template.doc.mjs +23 -1
  239. package/assets/templates/pages/dashboard-cohort-funnel/template.doc.mjs +10 -1
  240. package/assets/templates/pages/dashboard-comparison/template.doc.mjs +9 -1
  241. package/assets/templates/pages/dashboard-composition/template.doc.mjs +10 -1
  242. package/assets/templates/pages/dashboard-progress/template.doc.mjs +17 -1
  243. package/assets/templates/pages/dashboard-scorecard/template.doc.mjs +2 -1
  244. package/assets/templates/pages/detail-page/template.doc.mjs +8 -0
  245. package/assets/templates/pages/documentation/template.doc.mjs +10 -1
  246. package/assets/templates/pages/documentation-design/template.doc.mjs +10 -1
  247. package/assets/templates/pages/documentation-technical/template.doc.mjs +10 -1
  248. package/assets/templates/pages/editor/template.doc.mjs +9 -1
  249. package/assets/templates/pages/file-explorer/template.doc.mjs +3 -1
  250. package/assets/templates/pages/form-two-column/template.doc.mjs +17 -1
  251. package/assets/templates/pages/form-wizard/template.doc.mjs +14 -1
  252. package/assets/templates/pages/form-wizard-dialog/template.doc.mjs +1 -0
  253. package/assets/templates/pages/gallery-hero/template.doc.mjs +10 -1
  254. package/assets/templates/pages/ide/template.doc.mjs +9 -1
  255. package/assets/templates/pages/incident-console/template.doc.mjs +10 -1
  256. package/assets/templates/pages/kanban-board/template.doc.mjs +11 -1
  257. package/assets/templates/pages/library/template.doc.mjs +15 -1
  258. package/assets/templates/pages/login/template.doc.mjs +10 -1
  259. package/assets/templates/pages/login-card/template.doc.mjs +11 -1
  260. package/assets/templates/pages/login-split/template.doc.mjs +10 -1
  261. package/assets/templates/pages/login-sso/template.doc.mjs +10 -1
  262. package/assets/templates/pages/messaging-shell/template.doc.mjs +11 -1
  263. package/assets/templates/pages/mixed-gallery/template.doc.mjs +10 -1
  264. package/assets/templates/pages/payment-form/template.doc.mjs +3 -1
  265. package/assets/templates/pages/product-detail/template.doc.mjs +9 -1
  266. package/assets/templates/pages/product-gallery/template.doc.mjs +10 -1
  267. package/assets/templates/pages/settings/template.doc.mjs +3 -1
  268. package/assets/templates/pages/settings-dialog/template.doc.mjs +9 -1
  269. package/assets/templates/pages/settings-sidebar/template.doc.mjs +9 -1
  270. package/assets/templates/pages/shell-nav/template.doc.mjs +10 -1
  271. package/assets/templates/pages/shell-side-nav/template.doc.mjs +14 -1
  272. package/assets/templates/pages/shell-top-nav/template.doc.mjs +11 -1
  273. package/assets/templates/pages/side-gallery/template.doc.mjs +3 -1
  274. package/assets/templates/pages/table/template.doc.mjs +11 -1
  275. package/assets/templates/pages/table-filter/template.doc.mjs +21 -1
  276. package/assets/templates/pages/table-grouped/template.doc.mjs +15 -1
  277. package/assets/templates/pages/table-inbox/template.doc.mjs +18 -6
  278. package/assets/templates/pages/table-page/template.doc.mjs +20 -1
  279. package/assets/templates/pages/table-tree/template.doc.mjs +14 -1
  280. package/assets/templates/pages/theme-showcase/template.doc.mjs +10 -1
  281. package/assets/templates/pages/work-item-detail/template.doc.mjs +10 -0
  282. package/assets/templates/themes/butter/icons.tsx +2 -0
  283. package/assets/templates/themes/chocolate/icons.tsx +2 -0
  284. package/assets/templates/themes/gothic/icons.tsx +2 -0
  285. package/assets/templates/themes/matcha/icons.tsx +2 -0
  286. package/assets/templates/themes/neutral/icons.tsx +2 -0
  287. package/assets/templates/themes/stone/icons.tsx +2 -0
  288. package/assets/templates/themes/y2k/icons.tsx +2 -0
  289. package/authoring/config/config.doc.mjs +9 -1
  290. package/authoring/config/parse.d.mts +2 -0
  291. package/authoring/config/parse.mjs +19 -0
  292. package/authoring/config/parse.test.mjs +8 -0
  293. package/authoring/config/type.ts +11 -0
  294. package/authoring/discover/discover.doc.d.mts +13 -0
  295. package/authoring/discover/discover.doc.mjs +138 -0
  296. package/authoring/discover/parse.d.mts +24 -0
  297. package/authoring/discover/parse.mjs +128 -0
  298. package/authoring/discover/parse.test.mjs +124 -0
  299. package/authoring/discover/type.ts +87 -0
  300. package/authoring/doctypes/_schema.d.mts +3 -2
  301. package/authoring/doctypes/_schema.mjs +6 -0
  302. package/authoring/doctypes/base/graph-fields.doc.mjs +3 -3
  303. package/authoring/doctypes/base/type.ts +4 -2
  304. package/authoring/doctypes/component/component.doc.mjs +6 -0
  305. package/authoring/doctypes/component/type.ts +8 -0
  306. package/authoring/doctypes/reference/reference.doc.mjs +7 -0
  307. package/authoring/doctypes/reference/type.ts +5 -0
  308. package/authoring/doctypes/schema/schema.doc.mjs +2 -2
  309. package/authoring/doctypes/template/parse.d.mts +2 -0
  310. package/authoring/doctypes/template/parse.mjs +1 -0
  311. package/authoring/doctypes/template/parse.test.mjs +21 -0
  312. package/authoring/doctypes/template/template.doc.mjs +7 -1
  313. package/authoring/doctypes/template/type.ts +12 -2
  314. package/authoring/index.d.mts +1 -0
  315. package/authoring/index.d.ts +10 -0
  316. package/authoring/index.mjs +1 -0
  317. package/authoring/integration/integration.doc.mjs +12 -10
  318. package/clients/cli/commands/component/index.mjs +152 -55
  319. package/clients/cli/commands/component-batch.test.mjs +341 -0
  320. package/clients/cli/commands/component-ownership.test.mjs +89 -0
  321. package/clients/cli/commands/component.doc.mjs +27 -9
  322. package/clients/cli/commands/discover.doc.mjs +53 -9
  323. package/clients/cli/commands/discover.mjs +393 -118
  324. package/clients/cli/commands/discover.sources.test.mjs +267 -0
  325. package/clients/cli/commands/docs.doc.mjs +1 -1
  326. package/clients/cli/commands/docs.mjs +60 -17
  327. package/clients/cli/commands/docs.test.mjs +113 -24
  328. package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -2
  329. package/clients/cli/commands/doctor-integration.test.mjs +53 -0
  330. package/clients/cli/commands/doctor.doc.mjs +3 -1
  331. package/clients/cli/commands/doctor.mjs +53 -9
  332. package/clients/cli/commands/gap-report.doc.mjs +10 -9
  333. package/clients/cli/commands/init.doc.mjs +9 -6
  334. package/clients/cli/commands/integration-add.doc.mjs +17 -7
  335. package/clients/cli/commands/integration-authoring.test.mjs +70 -9
  336. package/clients/cli/commands/integration-pack.doc.mjs +5 -9
  337. package/clients/cli/commands/integration-real-world.test.mjs +1 -1
  338. package/clients/cli/commands/integration-verify.doc.mjs +22 -0
  339. package/clients/cli/commands/integration.doc.mjs +4 -4
  340. package/clients/cli/commands/integration.mjs +76 -43
  341. package/clients/cli/commands/layout-expand.doc.mjs +3 -1
  342. package/clients/cli/commands/layout.expand-receipt.test.mjs +94 -0
  343. package/clients/cli/commands/layout.mjs +16 -0
  344. package/clients/cli/commands/manifest.doc.mjs +1 -1
  345. package/clients/cli/commands/search.doc.mjs +10 -3
  346. package/clients/cli/commands/search.mjs +21 -2
  347. package/clients/cli/commands/search.test.mjs +21 -4
  348. package/clients/cli/commands/swizzle.doc.mjs +1 -1
  349. package/clients/cli/commands/template.copy-receipt.test.mjs +60 -0
  350. package/clients/cli/commands/template.doc.mjs +1 -1
  351. package/clients/cli/commands/template.mjs +19 -5
  352. package/clients/cli/commands/template.show-media.test.mjs +62 -0
  353. package/clients/cli/commands/text-json-parity.test.mjs +7 -1
  354. package/clients/cli/commands/theme-add.doc.mjs +1 -1
  355. package/clients/cli/commands/theme-palette-generate.doc.mjs +3 -2
  356. package/clients/cli/commands/theme-palette.doc.mjs +1 -2
  357. package/clients/cli/commands/theme-targets.doc.mjs +2 -2
  358. package/clients/cli/commands/theme.doc.mjs +2 -1
  359. package/clients/cli/commands/upgrade.ascii-output.test.mjs +14 -0
  360. package/clients/cli/commands/upgrade.doc.mjs +62 -3
  361. package/clients/cli/commands/write-failure.test.mjs +175 -0
  362. package/clients/cli/index.mjs +28 -6
  363. package/clients/cli/lib/define-command.mjs +28 -4
  364. package/clients/cli/lib/define-command.test.mjs +54 -0
  365. package/clients/cli/lib/exit-codes.test.mjs +17 -1
  366. package/clients/cli/lib/json-shim.mjs +24 -14
  367. package/clients/cli/lib/manifest.mjs +48 -156
  368. package/clients/cli/lib/manifest.test.mjs +103 -9
  369. package/clients/cli/lib/parse-error-format.test.mjs +81 -0
  370. package/foundation/agent-docs/agent-docs.mjs +1 -1
  371. package/foundation/agent-docs/agent-docs.test.mjs +3 -2
  372. package/foundation/discovery/authoring-self-docs.mjs +1 -0
  373. package/foundation/discovery/authoring-self-docs.test.mjs +6 -2
  374. package/foundation/discovery/cli-self-docs.mjs +16 -2
  375. package/foundation/discovery/cli-self-docs.test.mjs +20 -0
  376. package/foundation/discovery/docs-discovery.mjs +5 -1
  377. package/foundation/discovery/docs-discovery.test.mjs +21 -0
  378. package/foundation/discovery/docs-section-key.d.mts +1 -1
  379. package/foundation/discovery/docs-section-key.mjs +1 -1
  380. package/foundation/discovery/template-adapter.d.mts +14 -0
  381. package/foundation/discovery/template-adapter.fixture-refs.test.mjs +37 -1
  382. package/foundation/discovery/template-adapter.mjs +21 -1
  383. package/foundation/doc-compiler/doc-loads.test.mjs +5 -4
  384. package/foundation/doc-compiler/inputs.test.mjs +0 -1
  385. package/foundation/doc-compiler/tree.d.mts +4 -0
  386. package/foundation/doc-compiler/tree.mjs +6 -1
  387. package/foundation/doc-compiler/tree.test.mjs +65 -14
  388. package/foundation/integrations/cli-requirement.d.mts +75 -11
  389. package/foundation/integrations/cli-requirement.mjs +120 -23
  390. package/foundation/integrations/cli-requirement.test.mjs +141 -9
  391. package/foundation/integrations/contribution-inventory.mjs +1 -1
  392. package/foundation/integrations/integrations.d.mts +14 -1
  393. package/foundation/integrations/integrations.mjs +41 -1
  394. package/foundation/integrations/integrations.test.mjs +31 -0
  395. package/foundation/response/batch.type.d.mts +33 -0
  396. package/foundation/response/batch.type.mjs +34 -0
  397. package/foundation/response/error-codes.doc.mjs +6 -8
  398. package/foundation/response/error-codes.test.mjs +30 -5
  399. package/foundation/response/response-types.doc.d.mts +5 -4
  400. package/foundation/response/response-types.doc.mjs +49 -19
  401. package/foundation/response/response-types.doc.test.mjs +23 -0
  402. package/foundation/response/response.doc.mjs +11 -10
  403. package/package.json +9 -9
  404. package/assets/docs/tree/integrations.test.mjs +0 -62
  405. package/assets/docs/tree/writing-docs.doc.mjs +0 -286
@@ -42,6 +42,18 @@
42
42
  * sentence, a near miss is usually a different word: "site" is not "side",
43
43
  * "cable" is not "table".
44
44
  *
45
+ * A multi-word query has a reserved top tier (see {@link scoreQuery}): the
46
+ * whole query as a candidate's name or keyword (190-200), then the whole query
47
+ * as a phrase inside a doc's title or one of its headings (170), then a whole
48
+ * title of two words or more inside the query (160-169), then a candidate that
49
+ * matches every word of the query, at least one of them by name or keyword
50
+ * (151-159). Below those sits everything else: a partial match, or every word
51
+ * matched only in prose or through the components a page renders. A section titled "Light/Dark Mode" answers `dark
52
+ * mode` better than any doc that merely names `mode` in code, however exactly;
53
+ * "Dark mode" answers `how do I add dark mode`; and a guide whose title and
54
+ * description hold both words of `troubleshoot integration` answers it better
55
+ * than a doc named `integration`.
56
+ *
45
57
  * Description and guidance are separate tiers on purpose. A component's own
46
58
  * one-line description saying "notification" is a claim about what it IS; the
47
59
  * same word inside another component's best-practice advice is a passing
@@ -105,6 +117,9 @@ import {setResultCoverage} from './coverage.mjs';
105
117
  * @property {string} [description]
106
118
  * @property {string[]} [prose]
107
119
  * @property {string[]} [guidance]
120
+ * @property {string[]} [titles] - A doc's title and the headings inside it:
121
+ * the lines a reader scans to pick it. The whole query standing in one of
122
+ * them, or one of them standing whole in the query, is a top-tier match.
108
123
  * @property {string} [_import]
109
124
  * @property {string} [_title]
110
125
  * @property {string} [_topic] - A doc result's topic or docs-tree route.
@@ -414,6 +429,120 @@ function bestForToken(tok, candidate, opts = {}) {
414
429
  return best;
415
430
  }
416
431
 
432
+ /**
433
+ * The score of a whole-query phrase inside a doc's title or heading: a keyword
434
+ * substring hit (70) promoted by the same 100 as the exact tier. Below an
435
+ * exact name or keyword (190-200), above the token-sum path (~151 at most).
436
+ */
437
+ const TITLE_PHRASE_SCORE = 170;
438
+
439
+ /**
440
+ * The score of a whole title inside a longer query, before its coverage bonus:
441
+ * one step below {@link TITLE_PHRASE_SCORE}. The bonus (one per query term the
442
+ * candidate matches, at most 9) orders the sections that share a common title,
443
+ * so "best practices for spacing" puts Spacing's Best Practices first.
444
+ */
445
+ const TITLE_IN_QUERY_SCORE = 160;
446
+
447
+ /**
448
+ * The score of a candidate that matches every content word of a multi-word
449
+ * query, before a bonus of up to 8 for how strong its strongest match is: just
450
+ * above anything that matches only some of the words. The token-sum path tops
451
+ * out near 150 for a partial match (a 100 on one word, the per-word bonus, and
452
+ * the coverage term), so an AND-match with one keyword-strength hit (see
453
+ * {@link STRONG_TOKEN_SCORE}) always outranks an OR-match, and stays below the
454
+ * title tiers.
455
+ */
456
+ const FULL_COVERAGE_SCORE = 151;
457
+
458
+ /**
459
+ * The strongest single-word hit an every-word match needs to take that tier: a
460
+ * keyword substring. Two passing mentions in prose, or the components a page
461
+ * happens to render, are breadth, not relevance; they stay on the token sum,
462
+ * below an exact name or keyword hit on one of the words.
463
+ */
464
+ const STRONG_TOKEN_SCORE = 70;
465
+
466
+ /**
467
+ * The words of a title or query, lowercased, without punctuation or code ticks.
468
+ * @param {string} text
469
+ * @returns {string[]}
470
+ */
471
+ function phraseWords(text) {
472
+ return unlinkText(text).toLowerCase().match(/[a-z0-9]+/g) ?? [];
473
+ }
474
+
475
+ /**
476
+ * Whether two words are the same word, allowing a plural on either side, so
477
+ * `data attributes selector` still reads "Data attribute selectors".
478
+ * @param {string} a
479
+ * @param {string} b
480
+ */
481
+ function samePhraseWord(a, b) {
482
+ return (
483
+ a === b ||
484
+ `${a}s` === b ||
485
+ `${b}s` === a ||
486
+ `${a}es` === b ||
487
+ `${b}es` === a
488
+ );
489
+ }
490
+
491
+ /**
492
+ * Whether `plural` is the plural of `word`: `integrations` of `integration`,
493
+ * `boxes` of `box`. `es` only follows s, x, z, ch, or sh, so `notes` is not a
494
+ * plural of `not`.
495
+ * @param {string} plural
496
+ * @param {string} word
497
+ */
498
+ function pluralOf(plural, word) {
499
+ if (word.length < 3) return false;
500
+ if (plural === `${word}s`) return true;
501
+ return /(?:s|x|z|ch|sh)$/.test(word) && plural === `${word}es`;
502
+ }
503
+
504
+ /**
505
+ * The first title or heading that holds every word of the query, in order and
506
+ * side by side, or null.
507
+ * @param {string} term - Lowercased full query.
508
+ * @param {string[] | undefined} titles
509
+ * @returns {string | null}
510
+ */
511
+ export function headingWithPhrase(term, titles) {
512
+ const query = phraseWords(term);
513
+ if (query.length < 2 || !titles) return null;
514
+ for (const title of titles) {
515
+ const words = phraseWords(String(title ?? ''));
516
+ for (let i = 0; i + query.length <= words.length; i++) {
517
+ if (query.every((word, j) => samePhraseWord(words[i + j], word)))
518
+ return title;
519
+ }
520
+ }
521
+ return null;
522
+ }
523
+
524
+ /**
525
+ * The first title or heading of two words or more that the query holds whole,
526
+ * in order and side by side, or null. A question such as "how do I add dark
527
+ * mode" names the "Dark mode" section outright, around words no title has.
528
+ * @param {string} term - Lowercased full query.
529
+ * @param {string[] | undefined} titles
530
+ * @returns {string | null}
531
+ */
532
+ export function titleInQuery(term, titles) {
533
+ const query = phraseWords(term);
534
+ if (!titles) return null;
535
+ for (const title of titles) {
536
+ const words = phraseWords(String(title ?? ''));
537
+ if (words.length < 2 || words.length > query.length) continue;
538
+ for (let i = 0; i + words.length <= query.length; i++) {
539
+ if (words.every((word, j) => samePhraseWord(query[i + j], word)))
540
+ return title;
541
+ }
542
+ }
543
+ return null;
544
+ }
545
+
417
546
  /**
418
547
  * @param {string} term - Lowercased full query.
419
548
  * @param {string[]} tokens - Content tokens from tokenizeQuery(term).
@@ -437,16 +566,22 @@ export function scoreQuery(term, tokens, candidate) {
437
566
  // is usually a different word, not a typo.
438
567
  const fuzzy = tokens.length <= 1;
439
568
  const full = scoreCandidate(term, candidate, {fuzzy});
569
+ // A query of several words keeps its phrase tiers below even when stopwords
570
+ // leave one content word: "make an integration" is still the phrase an
571
+ // author declares as a keyword, and "build an integration" still names a
572
+ // title outright, though each tokenizes to `integration` alone.
573
+ const phrase = phraseWords(term).length >= 2;
440
574
 
441
- // 0–1 content tokens: keep whole-phrase fuzzy matching (typo tolerance for
442
- // single words), but if stopwords left exactly one DIFFERENT token (e.g.
443
- // "pricing page" → "pricing"), score that token too and take the stronger.
444
- if (tokens.length <= 1) {
575
+ /** 0–1 content tokens: whole-phrase fuzzy matching (typo tolerance for
576
+ * single words), but if stopwords left exactly one DIFFERENT token (e.g.
577
+ * "pricing page" → "pricing"), score that token too and take the stronger. */
578
+ const fewTokens = () => {
445
579
  const single =
446
580
  tokens.length === 1 ? bestForToken(tokens[0], candidate, {fuzzy}) : null;
447
581
  if (full && (!single || full.score >= single.score)) return asFull(full);
448
582
  return single ? asFull(single) : null;
449
- }
583
+ };
584
+ if (tokens.length <= 1 && !phrase) return fewTokens();
450
585
 
451
586
  // The full (untokenized) query matching a candidate's name or a declared
452
587
  // keyword VERBATIM — full.score 90 or 100, the only two scoreCandidate
@@ -463,6 +598,22 @@ export function scoreQuery(term, tokens, candidate) {
463
598
  return asFull({score: full.score + 100, reason: full.reason});
464
599
  }
465
600
 
601
+ // The whole query standing as a phrase in a doc's title or one of its
602
+ // headings is the next tier down, and still above the token-sum path. The
603
+ // reader named what the section is about, in order: `dark mode` is the
604
+ // "Light/Dark Mode" section. Without this, the title scores a keyword
605
+ // substring (70) and loses to a doc that happens to name `mode` exactly in
606
+ // a code tick (90 on one token, 98 with coverage), so API enum docs outrank
607
+ // the guide section.
608
+ const heading = headingWithPhrase(term, candidate.titles);
609
+ if (heading != null) {
610
+ return asFull({
611
+ score: TITLE_PHRASE_SCORE,
612
+ reason: `title "${heading}" holds the whole query`,
613
+ });
614
+ }
615
+ if (tokens.length <= 1) return fewTokens();
616
+
466
617
  // Multi-word natural language: score each content token, counting only
467
618
  // strong hits, then reward coverage so candidates matching more terms win.
468
619
  let strongest = 0;
@@ -477,8 +628,42 @@ export function scoreQuery(term, tokens, candidate) {
477
628
  hitTerms.push(tok);
478
629
  }
479
630
  }
631
+ // The reverse of the title tier, a step lower: the query holds a whole title
632
+ // of two words or more, so the reader asked a question around the section's
633
+ // name ("how do I add dark mode"). Coverage breaks ties between sections
634
+ // that share a title such as "Best Practices".
635
+ const named = titleInQuery(term, candidate.titles);
636
+ if (named != null) {
637
+ return {
638
+ score: TITLE_IN_QUERY_SCORE + Math.min(matched, 9),
639
+ reason: `the query names the title "${named}"`,
640
+ matched,
641
+ total,
642
+ };
643
+ }
480
644
  if (matched === 0) return full ? asFull(full) : null;
481
645
 
646
+ const reason = `matches ${matched}/${tokens.length} terms: ${hitTerms.join(', ')}`;
647
+
648
+ // Every word matched is its own tier. Summed per word, a doc that matches
649
+ // both words of `troubleshoot integration` in its title and description
650
+ // (50 + bonus + coverage = 77) lost to thirty docs that each match
651
+ // `integration` alone, by name or in a code tick (98-108). The reader asked for
652
+ // both; a candidate that has both comes first, ordered among its peers by
653
+ // how strong its strongest match is. It needs one keyword-strength hit:
654
+ // every word mentioned in prose, or rendered by a page, is breadth, and
655
+ // stays on the token sum below an exact hit on one word.
656
+ if (matched === tokens.length && strongest >= STRONG_TOKEN_SCORE) {
657
+ return {
658
+ score:
659
+ FULL_COVERAGE_SCORE +
660
+ Math.floor((strongest - MIN_TOKEN_SCORE) / 6.25),
661
+ reason,
662
+ matched,
663
+ total,
664
+ };
665
+ }
666
+
482
667
  // Base the score on the STRONGEST concept that matched, plus a bonus per
483
668
  // additional matched concept and a coverage term.
484
669
  //
@@ -500,14 +685,8 @@ export function scoreQuery(term, tokens, candidate) {
500
685
  const tokenScore = Math.round(
501
686
  strongest + Math.min(matched - 1, 3) * 12 + coverage * 15,
502
687
  );
503
-
504
688
  if (full && full.score >= tokenScore) return asFull(full);
505
- return {
506
- score: tokenScore,
507
- reason: `matches ${matched}/${tokens.length} terms: ${hitTerms.join(', ')}`,
508
- matched,
509
- total,
510
- };
689
+ return {score: tokenScore, reason, matched, total};
511
690
  }
512
691
 
513
692
  /**
@@ -518,6 +697,8 @@ export function scoreQuery(term, tokens, candidate) {
518
697
  * @param {string} term - Lowercased search term.
519
698
  * @param {object} candidate
520
699
  * @param {string} candidate.name - Primary identifier (component/hook name, topic, template name).
700
+ * @param {string} [candidate.domain] - A component, hook, or template name
701
+ * also matches typed as words: `command palette` is CommandPalette.
521
702
  * @param {string[]} [candidate.keywords] - Authored intent (componentsUsed, category words).
522
703
  * @param {string[]} [candidate.weakKeywords] - Derived signal (components a page renders).
523
704
  * @param {string} [candidate.description]
@@ -530,6 +711,7 @@ export function scoreCandidate(
530
711
  term,
531
712
  {
532
713
  name,
714
+ domain,
533
715
  keywords = [],
534
716
  weakKeywords = [],
535
717
  description = '',
@@ -552,10 +734,26 @@ export function scoreCandidate(
552
734
  };
553
735
 
554
736
  const nameLower = name.toLowerCase();
737
+ // A placed guide's name is its route, and the route's last segment is its
738
+ // name too, as a flat topic's is: `codemods` is cli/integrations/codemods.
739
+ const leafLower = nameLower.slice(nameLower.lastIndexOf('/') + 1);
555
740
 
556
741
  // ── Name signals ────────────────────────────────────────────────
557
- if (nameLower === term) {
742
+ // A plural of the name is the name: `integration` is the `integrations`
743
+ // guides, `tab` the `tabs` doc.
744
+ // A component, hook, or template name typed as words is its name:
745
+ // `command palette` is CommandPalette. A doc's name is a route or key,
746
+ // matched as written.
747
+ const spelled =
748
+ domain !== 'doc' &&
749
+ !/[\s_-]/.test(nameLower) &&
750
+ nameLower === term.replace(/\s+/g, '');
751
+ if (nameLower === term || leafLower === term || spelled) {
558
752
  consider(100, 'exact name');
753
+ } else if (pluralOf(nameLower, term) || pluralOf(term, nameLower)) {
754
+ // One point under the exact spelling, so the doc named `tokens` still
755
+ // outranks the Token component for `tokens`.
756
+ consider(99, 'plural of the name');
559
757
  } else {
560
758
  if (sameWord(term, nameLower)) consider(95, `name "${name}"`);
561
759
  // The term is a word of the name, or starts one: "input" in TextInput.
@@ -809,6 +1007,64 @@ async function gatherComponents(coreDir, cwd) {
809
1007
  return [...core, ...integrations];
810
1008
  }
811
1009
 
1010
+ /**
1011
+ * The components each search response was scored against, by response:
1012
+ * `build` reads them to tell a part of a page from a page without gathering
1013
+ * them again. Module-private, so they never enter search's JSON.
1014
+ * @type {WeakMap<object, {name: string, keywords: string[]}[]>}
1015
+ */
1016
+ const searchedComponentsOf = new WeakMap();
1017
+
1018
+ /**
1019
+ * The components (name and keywords) a search response was scored against, or
1020
+ * null when that search was narrowed away from components.
1021
+ * @param {object} response
1022
+ * @returns {{name: string, keywords: string[]}[] | null}
1023
+ */
1024
+ export function searchedComponents(response) {
1025
+ return searchedComponentsOf.get(response) ?? null;
1026
+ }
1027
+
1028
+ /**
1029
+ * Every component the project can use, Core's and its integrations', with the
1030
+ * keywords its own doc declares: the discovery and doc reads search's own
1031
+ * candidates use, without the import paths and prose a result carries. `build`
1032
+ * reads it to tell a part of a page from a page when its search was narrowed
1033
+ * away from components.
1034
+ * @param {string} coreDir
1035
+ * @param {string} cwd
1036
+ * @returns {Promise<{name: string, keywords: string[]}[]>}
1037
+ */
1038
+ export async function componentKeywords(coreDir, cwd) {
1039
+ /** @param {any} doc */
1040
+ const keywordsOf = doc => (Array.isArray(doc?.keywords) ? doc.keywords : []);
1041
+ const core = Object.values(discoverComponents(coreDir))
1042
+ .flat()
1043
+ .map(async name => {
1044
+ const readme = findComponentReadme(coreDir, name);
1045
+ const doc =
1046
+ readme && readme.endsWith('.doc.mjs')
1047
+ ? await loadModuleDoc(readme)
1048
+ : null;
1049
+ return {name, keywords: keywordsOf(doc)};
1050
+ });
1051
+ const integrations = (await loadIntegrationsSafely(cwd)).map(
1052
+ async integration => {
1053
+ const {components} = await discoverValidIntegrationComponents(integration);
1054
+ return Promise.all(
1055
+ components.map(async rec => ({
1056
+ name: rec.name,
1057
+ keywords: keywordsOf(await loadModuleDoc(rec.docPath)),
1058
+ })),
1059
+ );
1060
+ },
1061
+ );
1062
+ return [
1063
+ ...(await Promise.all(core)),
1064
+ ...(await Promise.all(integrations)).flat(),
1065
+ ];
1066
+ }
1067
+
812
1068
  /**
813
1069
  * Build hook candidates: name + keywords + usage/description from the hook's
814
1070
  * .doc.mjs.
@@ -962,11 +1218,14 @@ async function gatherDocs(cwd) {
962
1218
  keywords: [
963
1219
  node.route.slice(node.route.lastIndexOf('/') + 1),
964
1220
  ...(Array.isArray(selfDoc?.keywords) ? selfDoc.keywords : []),
1221
+ // A namespace doc's own keywords, which it declares for search.
1222
+ ...(Array.isArray(node.keywords) ? node.keywords : []),
965
1223
  ...defined,
966
1224
  ...codeTerms({content}),
967
1225
  ],
968
1226
  description: node.summary || '',
969
1227
  prose: sectionProse({title: node.title, content}),
1228
+ titles: [node.title],
970
1229
  _topic: node.route,
971
1230
  _title: path.join(' › '),
972
1231
  _command: `astryx docs ${node.route}`,
@@ -1086,12 +1345,16 @@ function topicCandidates(
1086
1345
  const sections = doc?.sections ?? [];
1087
1346
  const docTitle = path || doc?.title || title || name;
1088
1347
  const split = sections.length > 1;
1348
+ // A placed guide also answers to its last route segment's words:
1349
+ // `quick start` is cli/integrations/quick-start.
1350
+ const leaf = name.slice(name.lastIndexOf('/') + 1);
1089
1351
  /** @type {Candidate[]} */
1090
1352
  const out = [
1091
1353
  {
1092
1354
  domain: 'doc',
1093
1355
  name,
1094
1356
  keywords: [
1357
+ ...(leaf !== name ? [leaf.replaceAll('-', ' ')] : []),
1095
1358
  ...(doc?.title || title ? [doc?.title || title] : []),
1096
1359
  ...(Array.isArray(doc?.keywords) ? doc.keywords : []),
1097
1360
  ],
@@ -1099,6 +1362,11 @@ function topicCandidates(
1099
1362
  prose: split
1100
1363
  ? sections.map(section => section.title).filter(Boolean)
1101
1364
  : sections.flatMap(sectionProse),
1365
+ titles: [
1366
+ doc?.title || title || name,
1367
+ // A topic read whole answers for the headings inside it.
1368
+ ...(split ? [] : sections.flatMap(s => [s.title, ...headings(s)])),
1369
+ ].filter(Boolean),
1102
1370
  _topic: name,
1103
1371
  _title: docTitle,
1104
1372
  _command: split ? `astryx docs ${name} --index` : `astryx docs ${name}`,
@@ -1119,6 +1387,7 @@ function topicCandidates(
1119
1387
  ],
1120
1388
  description: sectionSummary(section),
1121
1389
  prose: sectionProse(section),
1390
+ titles: [section.title, ...headings(section)].filter(Boolean),
1122
1391
  _topic: name,
1123
1392
  _section: key,
1124
1393
  _title: `${docTitle} › ${section.title}`,
@@ -1182,6 +1451,10 @@ async function gatherTemplates(cwd) {
1182
1451
  keywords,
1183
1452
  weakKeywords,
1184
1453
  description: t.description || '',
1454
+ // A template's own keywords name the many ideas it serves, so they score
1455
+ // as its prose does, not as a name: one of them matching one word of a
1456
+ // short idea is not a direct match.
1457
+ prose: t.keywords ?? [],
1185
1458
  _displayName: t.name,
1186
1459
  _kind: t.type, // 'page' | 'block'
1187
1460
  _resultName: t.dirName,
@@ -1296,10 +1569,11 @@ export async function search(query, options = {}) {
1296
1569
  const tokens = tokenizeQuery(term);
1297
1570
 
1298
1571
  // `astryx docs` reads docs without @astryxdesign/core, so a docs-only
1299
- // search must too. Every other domain reads core.
1572
+ // search must too. Every other domain reads core: asked for by name, it is
1573
+ // an error without core; an open search then covers the docs alone.
1300
1574
  const docsOnly = type === 'doc';
1301
1575
  const coreDir = docsOnly ? null : findCoreDir(cwd);
1302
- if (!docsOnly && !coreDir) {
1576
+ if (type && !docsOnly && !coreDir) {
1303
1577
  throw new AstryxError(
1304
1578
  'Could not find @astryxdesign/core package',
1305
1579
  undefined,
@@ -1309,7 +1583,7 @@ export async function search(query, options = {}) {
1309
1583
 
1310
1584
  // Gather candidates from each requested domain in parallel.
1311
1585
  /** @param {string} d */
1312
- const wants = d => !type || type === d;
1586
+ const wants = d => (!type && (coreDir != null || d === 'doc')) || type === d;
1313
1587
  const [components, hooks, docTopics, templates] = await Promise.all([
1314
1588
  wants('component')
1315
1589
  ? gatherComponents(/** @type {string} */ (coreDir), cwd)
@@ -1351,7 +1625,8 @@ export async function search(query, options = {}) {
1351
1625
  // 57 things and one matching exactly 20 would be indistinguishable.
1352
1626
  const limited = scored.slice(0, limit);
1353
1627
 
1354
- return {
1628
+ /** @type {import('./search.type.mjs').SearchResponse} */
1629
+ const response = {
1355
1630
  type: 'search',
1356
1631
  data: {
1357
1632
  query: String(query).trim(),
@@ -1362,4 +1637,11 @@ export async function search(query, options = {}) {
1362
1637
  ),
1363
1638
  },
1364
1639
  };
1640
+ if (wants('component')) {
1641
+ searchedComponentsOf.set(
1642
+ response,
1643
+ components.map(c => ({name: c.name, keywords: c.keywords ?? []})),
1644
+ );
1645
+ }
1646
+ return response;
1365
1647
  }