@astryxdesign/cli 0.7.2 → 0.7.3-canary.02f5fe4

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 (373) hide show
  1. package/README.md +104 -71
  2. package/api/build/_adapter.d.mts +58 -0
  3. package/api/build/_adapter.mjs +127 -3
  4. package/api/build/blocks/blocks.d.mts +92 -0
  5. package/api/build/blocks/blocks.mjs +528 -0
  6. package/api/build/blocks/blocks.test.mjs +238 -0
  7. package/api/build/blocks/blocks.weights.json +1 -0
  8. package/api/build/build.mjs +25 -6
  9. package/api/build/build.type.mjs +1 -1
  10. package/api/build/kit/answer.d.mts +18 -0
  11. package/api/build/kit/answer.mjs +195 -0
  12. package/api/build/kit/answer.test.mjs +108 -0
  13. package/api/build/kit/kind.d.mts +25 -0
  14. package/api/build/kit/kind.mjs +170 -0
  15. package/api/build/kit/kit.d.mts +2 -4
  16. package/api/build/kit/kit.mjs +8 -398
  17. package/api/build/kit/rank.d.mts +3 -32
  18. package/api/build/kit/rank.mjs +25 -342
  19. package/api/build/kit/rank.test.mjs +2 -1
  20. package/api/build/kit/signals.d.mts +79 -0
  21. package/api/build/kit/signals.mjs +295 -0
  22. package/api/build/kit/start.d.mts +32 -0
  23. package/api/build/kit/start.mjs +163 -0
  24. package/api/build/kit/words.d.mts +72 -0
  25. package/api/build/kit/words.mjs +202 -0
  26. package/api/build/tree.d.mts +20 -0
  27. package/api/build/tree.mjs +53 -0
  28. package/api/build/tree.test.mjs +185 -0
  29. package/api/build/tree.weights.json +1 -0
  30. package/api/component/_adapter.mjs +39 -5
  31. package/api/component/component-replacement.test.mjs +1 -0
  32. package/api/component/component.mjs +17 -3
  33. package/api/component/component.test.mjs +15 -0
  34. package/api/component/doc-links.test.mjs +2 -2
  35. package/api/discover/_adapter.d.mts +1 -2
  36. package/api/discover/_adapter.mjs +42 -17
  37. package/api/discover/_adapter.test.mjs +42 -0
  38. package/api/discover/discover.mjs +0 -1
  39. package/api/docs/_adapter.d.mts +2 -2
  40. package/api/docs/_adapter.mjs +82 -23
  41. package/api/docs/docs-homes.test.mjs +127 -0
  42. package/api/docs/docs.doc.mjs +3 -3
  43. package/api/docs/docs.test.mjs +35 -19
  44. package/api/docs/integration-tree.test.mjs +20 -20
  45. package/api/docs/integrationDocs.test.mjs +1 -1
  46. package/api/docs/namespace-section.test.mjs +6 -6
  47. package/api/docs/reference-blocks.test.mjs +2 -2
  48. package/api/doctor/_adapter.d.mts +33 -13
  49. package/api/doctor/_adapter.mjs +55 -25
  50. package/api/doctor/doctor.d.mts +23 -5
  51. package/api/doctor/doctor.mjs +183 -40
  52. package/api/doctor/doctor.test.mjs +95 -12
  53. package/api/hook/hook.mjs +1 -1
  54. package/api/init/init.doc.mjs +20 -6
  55. package/api/init/init.test.mjs +18 -2
  56. package/api/init/init.type.d.mts +3 -3
  57. package/api/init/init.type.mjs +3 -3
  58. package/api/init/remove/remove.mjs +17 -4
  59. package/api/init/remove/remove.test.mjs +114 -2
  60. package/api/init/run/run.mjs +69 -6
  61. package/api/integration/_adapter.d.mts +8 -0
  62. package/api/integration/_adapter.mjs +21 -1
  63. package/api/integration/authoring-checks.mjs +83 -9
  64. package/api/integration/authoring-checks.test.mjs +220 -0
  65. package/api/integration/pack-check.mjs +123 -4
  66. package/api/integration/pack-check.test.mjs +87 -0
  67. package/api/integration/validate-integration-case-disk.test.mjs +111 -0
  68. package/api/integration/validate-integration-fixes.test.mjs +6 -5
  69. package/api/integration/validate-integration-gaps.test.mjs +1975 -0
  70. package/api/integration/validate-integration.d.mts +15 -2
  71. package/api/integration/validate-integration.mjs +298 -39
  72. package/api/integration/validate-integration.type.d.mts +90 -1
  73. package/api/integration/validate-integration.type.mjs +67 -2
  74. package/api/integration/validateIntegration.doc.mjs +1 -1
  75. package/api/search/ORDERING.md +28 -0
  76. package/api/search/_adapter.d.mts +82 -2
  77. package/api/search/_adapter.mjs +157 -10
  78. package/api/search/_adapter.test.mjs +173 -0
  79. package/api/search/_candidate-score.d.mts +40 -0
  80. package/api/search/_candidate-score.mjs +238 -0
  81. package/api/search/_cards.json +6098 -0
  82. package/api/search/_compound.d.mts +50 -0
  83. package/api/search/_compound.mjs +170 -0
  84. package/api/search/_doc-searcher.d.mts +4 -0
  85. package/api/search/_doc-searcher.mjs +23 -0
  86. package/api/search/_doc-searcher.test.mjs +36 -0
  87. package/api/search/_doc-searcher.weights.json +1 -0
  88. package/api/search/_doc-text.d.mts +30 -0
  89. package/api/search/_doc-text.mjs +87 -0
  90. package/api/search/_entries.d.mts +63 -0
  91. package/api/search/_entries.mjs +232 -0
  92. package/api/search/_entries.test.mjs +48 -0
  93. package/api/search/_features.d.mts +35 -0
  94. package/api/search/_features.mjs +155 -0
  95. package/api/search/_gather-components.d.mts +26 -0
  96. package/api/search/_gather-components.mjs +284 -0
  97. package/api/search/_gather-docs.d.mts +20 -0
  98. package/api/search/_gather-docs.mjs +254 -0
  99. package/api/search/_gather-hooks.d.mts +12 -0
  100. package/api/search/_gather-hooks.mjs +52 -0
  101. package/api/search/_gather-templates.d.mts +25 -0
  102. package/api/search/_gather-templates.mjs +107 -0
  103. package/api/search/_lexical.d.mts +135 -0
  104. package/api/search/_lexical.mjs +351 -0
  105. package/api/search/_main-searcher.d.mts +19 -0
  106. package/api/search/_main-searcher.mjs +115 -0
  107. package/api/search/_main-searcher.test.mjs +106 -0
  108. package/api/search/_main-searcher.weights.json +1 -0
  109. package/api/search/_module-doc.d.mts +32 -0
  110. package/api/search/_module-doc.mjs +78 -0
  111. package/api/search/_order.d.mts +32 -0
  112. package/api/search/_order.mjs +162 -0
  113. package/api/search/_order.test.mjs +72 -0
  114. package/api/search/_priority.d.mts +50 -0
  115. package/api/search/_priority.mjs +139 -0
  116. package/api/search/_query-words.d.mts +25 -0
  117. package/api/search/_query-words.mjs +92 -0
  118. package/api/search/_query.d.mts +65 -0
  119. package/api/search/_query.mjs +176 -0
  120. package/api/search/_result.d.mts +97 -0
  121. package/api/search/_result.mjs +150 -0
  122. package/api/search/_score.d.mts +60 -0
  123. package/api/search/_score.mjs +283 -0
  124. package/api/search/_synonyms.d.mts +21 -0
  125. package/api/search/_synonyms.mjs +67 -0
  126. package/api/search/_template-searcher.d.mts +4 -0
  127. package/api/search/_template-searcher.mjs +23 -0
  128. package/api/search/_template-searcher.test.mjs +40 -0
  129. package/api/search/_template-searcher.weights.json +1 -0
  130. package/api/search/_titles.d.mts +33 -0
  131. package/api/search/_titles.mjs +65 -0
  132. package/api/search/_words.d.mts +64 -0
  133. package/api/search/_words.mjs +157 -0
  134. package/api/search/search.d.mts +22 -305
  135. package/api/search/search.doc.mjs +8 -6
  136. package/api/search/search.mjs +56 -2482
  137. package/api/search/search.test.mjs +14 -14
  138. package/api/search/search.type.d.mts +6 -1
  139. package/api/search/search.type.mjs +6 -1
  140. package/api/search/tree.d.mts +15 -0
  141. package/api/search/tree.mjs +40 -0
  142. package/api/search/tree.test.mjs +66 -0
  143. package/api/search/tree.weights.json +1 -0
  144. package/api/template/template.mjs +5 -1
  145. package/api/theme/build/build.packed-old-core.test.mjs +1 -0
  146. package/api/upgrade/_adapter.d.mts +57 -9
  147. package/api/upgrade/_adapter.mjs +198 -28
  148. package/api/upgrade/registry/registry.d.mts +10 -4
  149. package/api/upgrade/registry/registry.mjs +33 -16
  150. package/api/upgrade/registry/registry.test.mjs +85 -0
  151. package/api/upgrade/run/run.d.mts +4 -2
  152. package/api/upgrade/run/run.mjs +13 -2
  153. package/api/upgrade/run/run.test.mjs +417 -3
  154. package/api/upgrade/upgrade.doc.mjs +10 -5
  155. package/api/upgrade/upgrade.mjs +13 -2
  156. package/api/upgrade/upgrade.test.mjs +194 -0
  157. package/api/upgrade/upgrade.type.d.mts +5 -3
  158. package/api/upgrade/upgrade.type.mjs +8 -6
  159. package/assets/codemods/transforms/v0.7.0/__tests__/validate-data-token-ownership.test.mjs +11 -0
  160. package/assets/codemods/transforms/v0.7.0/validate-data-token-ownership.mjs +3 -3
  161. package/assets/docs/author-a-theme.doc.mjs +2 -2
  162. package/assets/docs/is-my-code-good.doc.mjs +287 -0
  163. package/assets/docs/tree/add-a-component.doc.mjs +1 -1
  164. package/assets/docs/tree/add-a-theme.doc.mjs +1 -1
  165. package/assets/docs/tree/add-a-topic.doc.mjs +2 -2
  166. package/assets/docs/tree/agent-guidance.doc.mjs +1 -1
  167. package/assets/docs/tree/api.doc.mjs +6 -0
  168. package/assets/docs/tree/{integrations.doc.mjs → authoring.doc.mjs} +15 -6
  169. package/assets/docs/tree/block-template.doc.mjs +1 -1
  170. package/assets/docs/tree/build-the-template.doc.mjs +1 -1
  171. package/assets/docs/tree/building-blocks.doc.mjs +2 -2
  172. package/assets/docs/tree/check-the-props.doc.mjs +1 -1
  173. package/assets/docs/tree/check-your-docs.doc.mjs +2 -2
  174. package/assets/docs/tree/checks.doc.mjs +3 -3
  175. package/assets/docs/tree/ci-and-containers.doc.mjs +13 -0
  176. package/assets/docs/tree/cli-command.doc.mjs +1 -1
  177. package/assets/docs/tree/cli.doc.mjs +1 -1
  178. package/assets/docs/tree/codemods.doc.mjs +1 -1
  179. package/assets/docs/tree/component-family.doc.mjs +1 -1
  180. package/assets/docs/tree/component-imports.doc.mjs +1 -1
  181. package/assets/docs/tree/components.doc.mjs +1 -1
  182. package/assets/docs/tree/configuration.doc.mjs +1 -1
  183. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +1 -1
  184. package/assets/docs/tree/define-the-theme.doc.mjs +1 -1
  185. package/assets/docs/tree/describe-the-component.doc.mjs +1 -1
  186. package/assets/docs/tree/docs.doc.mjs +1 -1
  187. package/assets/docs/tree/document-the-template.doc.mjs +1 -1
  188. package/assets/docs/tree/document-the-theme.doc.mjs +1 -1
  189. package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
  190. package/assets/docs/tree/extend-or-replace.doc.mjs +1 -1
  191. package/assets/docs/{authoring.doc.mjs → tree/field-reference.doc.mjs} +4 -3
  192. package/assets/docs/tree/fonts-and-assets.doc.mjs +1 -1
  193. package/assets/docs/tree/generate-a-palette.doc.mjs +1 -1
  194. package/assets/docs/tree/global-integrations.doc.mjs +2 -2
  195. package/assets/docs/tree/grade-template-with-agent.doc.mjs +4 -4
  196. package/assets/docs/tree/help.doc.mjs +2 -2
  197. package/assets/docs/tree/host-entry.doc.mjs +128 -0
  198. package/assets/docs/tree/links.doc.mjs +1 -1
  199. package/assets/docs/tree/package-and-test.doc.mjs +1 -1
  200. package/assets/docs/tree/page-template.doc.mjs +1 -1
  201. package/assets/docs/tree/publishing.doc.mjs +1 -1
  202. package/assets/docs/tree/quick-start.doc.mjs +1 -1
  203. package/assets/docs/tree/replace-a-core-component.doc.mjs +1 -1
  204. package/assets/docs/tree/replace-a-core-template.doc.mjs +1 -1
  205. package/assets/docs/tree/sections-and-placement.doc.mjs +1 -1
  206. package/assets/docs/tree/see-it-in-an-app.doc.mjs +1 -1
  207. package/assets/docs/tree/ship.doc.mjs +2 -2
  208. package/assets/docs/tree/short-and-findable.doc.mjs +1 -1
  209. package/assets/docs/tree/single-component.doc.mjs +1 -1
  210. package/assets/docs/tree/start-a-template.doc.mjs +1 -1
  211. package/assets/docs/tree/subcomponent.doc.mjs +1 -1
  212. package/assets/docs/tree/template-assets.doc.mjs +1 -1
  213. package/assets/docs/tree/template-doc-overview.doc.mjs +2 -2
  214. package/assets/docs/tree/template-fonts.doc.mjs +1 -1
  215. package/assets/docs/tree/template-grading-rubric.doc.mjs +17 -38
  216. package/assets/docs/tree/template-icons.doc.mjs +1 -1
  217. package/assets/docs/tree/template-images-media.doc.mjs +1 -1
  218. package/assets/docs/tree/template-styles.doc.mjs +1 -1
  219. package/assets/docs/tree/templates.doc.mjs +1 -1
  220. package/assets/docs/tree/test-in-an-app.doc.mjs +1 -1
  221. package/assets/docs/tree/test-template-in-app.doc.mjs +1 -1
  222. package/assets/docs/tree/themes.doc.mjs +1 -1
  223. package/assets/docs/tree/tokens-data.doc.mjs +2 -2
  224. package/assets/docs/tree/troubleshooting.doc.mjs +1 -1
  225. package/assets/docs/tree/upgrading.doc.mjs +1 -1
  226. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +1 -1
  227. package/assets/docs/tree/verify-packed-template.doc.mjs +1 -1
  228. package/assets/docs/tree/versioning.doc.mjs +5 -1
  229. package/assets/docs/tree/write-good-templates.doc.mjs +1 -1
  230. package/assets/docs/tree/write-the-template-file.doc.mjs +1 -1
  231. package/assets/docs/use-integrations.doc.mjs +120 -0
  232. package/assets/docs/working-with-ai.doc.mjs +6 -2
  233. package/assets/templates/themes/butter/butterTheme.ts +4 -2
  234. package/assets/templates/themes/stone/stoneTheme.ts +4 -2
  235. package/assets/templates/themes/y2k/y2kTheme.ts +4 -2
  236. package/authoring/debug/parse.d.mts +1 -1
  237. package/authoring/doctypes/schema/schema.doc.mjs +1 -1
  238. package/authoring/integration/integration.doc.mjs +1 -1
  239. package/authoring/integration/type.ts +1 -1
  240. package/clients/cli/bin/astryx.mjs +7 -78
  241. package/clients/cli/command-result-coverage.test.mjs +6 -5
  242. package/clients/cli/commands/build-theme.color-scheme.test.mjs +1 -1
  243. package/clients/cli/commands/{component/index.mjs → component.mjs} +37 -37
  244. package/clients/cli/commands/component.test.mjs +6 -6
  245. package/clients/cli/commands/docs.doc.mjs +2 -2
  246. package/clients/cli/commands/docs.mjs +1 -1
  247. package/clients/cli/commands/docs.test.mjs +14 -14
  248. package/clients/cli/commands/doctor-integration.test.mjs +49 -1
  249. package/clients/cli/commands/doctor.doc.mjs +1 -1
  250. package/clients/cli/commands/doctor.mjs +40 -0
  251. package/clients/cli/commands/doctor.test.mjs +65 -0
  252. package/clients/cli/commands/{hook/index.mjs → hook.mjs} +22 -22
  253. package/clients/cli/commands/import-hint-correctness.test.mjs +1 -1
  254. package/clients/cli/commands/init.behavior.test.mjs +230 -1
  255. package/clients/cli/commands/init.doc.mjs +28 -9
  256. package/clients/cli/commands/init.flag-help.test.mjs +1 -0
  257. package/clients/cli/commands/init.mjs +2 -2
  258. package/clients/cli/commands/integration-check.test.mjs +48 -0
  259. package/clients/cli/commands/integration.doc.mjs +1 -1
  260. package/clients/cli/commands/manifest.doc.mjs +1 -1
  261. package/clients/cli/commands/manifest.mjs +67 -0
  262. package/clients/cli/commands/outside-project.test.mjs +101 -1
  263. package/clients/cli/commands/postinstall.doc.mjs +23 -0
  264. package/clients/cli/commands/postinstall.mjs +31 -0
  265. package/clients/cli/commands/setup-nudge.test.mjs +78 -0
  266. package/clients/cli/commands/{build-theme.mjs → theme.mjs} +1 -1
  267. package/clients/cli/commands/upgrade.doc.mjs +4 -2
  268. package/clients/cli/commands/upgrade.mjs +9 -2
  269. package/clients/cli/commands/upgrade.subfolder-path.test.mjs +97 -0
  270. package/clients/cli/index.mjs +54 -94
  271. package/clients/cli/lib/component-format.mjs +1 -1
  272. package/clients/cli/lib/define-command.mjs +12 -7
  273. package/clients/cli/lib/exit-codes.test.mjs +5 -0
  274. package/clients/cli/lib/manifest.test.mjs +12 -0
  275. package/clients/cli/lib/welcome-box.mjs +50 -0
  276. package/clients/cli/run.mjs +108 -0
  277. package/foundation/agent-docs/agent-doc-state.d.mts +32 -7
  278. package/foundation/agent-docs/agent-doc-state.mjs +58 -9
  279. package/foundation/agent-docs/agent-docs.d.mts +100 -12
  280. package/foundation/agent-docs/agent-docs.mjs +1017 -105
  281. package/foundation/agent-docs/agent-docs.path-safety.test.mjs +1239 -17
  282. package/foundation/agent-docs/agent-docs.test.mjs +683 -39
  283. package/foundation/agent-docs/computer-agent-text.d.mts +39 -2
  284. package/foundation/agent-docs/computer-agent-text.mjs +135 -8
  285. package/foundation/agent-docs/computer-agent-text.test.mjs +99 -2
  286. package/foundation/config/project.mjs +20 -22
  287. package/foundation/config/project.test.mjs +56 -0
  288. package/foundation/discovery/authoring-self-docs.d.mts +7 -0
  289. package/foundation/discovery/authoring-self-docs.mjs +16 -4
  290. package/foundation/discovery/authoring-self-docs.test.mjs +15 -3
  291. package/foundation/discovery/authoring-surface.d.mts +1 -1
  292. package/foundation/discovery/authoring-surface.mjs +2 -2
  293. package/foundation/discovery/authoring-surface.test.mjs +1 -1
  294. package/foundation/discovery/cli-self-docs.d.mts +2 -1
  295. package/foundation/discovery/cli-self-docs.mjs +4 -3
  296. package/foundation/discovery/cli-self-docs.test.mjs +2 -2
  297. package/foundation/discovery/component-discovery.d.mts +37 -0
  298. package/foundation/discovery/component-discovery.mjs +80 -5
  299. package/foundation/discovery/component-loader.d.mts +1 -1
  300. package/foundation/discovery/docs-discovery.mjs +14 -1
  301. package/foundation/discovery/module-exports.d.mts +41 -0
  302. package/foundation/discovery/module-exports.mjs +253 -0
  303. package/foundation/discovery/runner-allowlist.d.mts +2 -4
  304. package/foundation/discovery/runner-allowlist.mjs +2 -4
  305. package/foundation/discovery/runner-commands.d.mts +77 -0
  306. package/foundation/discovery/runner-commands.mjs +294 -0
  307. package/foundation/discovery/runner-commands.test.mjs +128 -0
  308. package/foundation/discovery/stray-pointers.test.mjs +1 -1
  309. package/foundation/discovery/template-adapter.mjs +18 -1
  310. package/foundation/discovery/template-needs.mjs +1 -44
  311. package/foundation/discovery/theme-discovery.mjs +147 -3
  312. package/foundation/doc-compiler/doc-loads.test.mjs +33 -9
  313. package/foundation/doc-compiler/read.mjs +59 -1
  314. package/foundation/doc-compiler/read.test.mjs +11 -2
  315. package/foundation/doc-compiler/split-topic-keywords.test.mjs +3 -2
  316. package/foundation/doc-compiler/tree.d.mts +32 -6
  317. package/foundation/doc-compiler/tree.mjs +77 -43
  318. package/foundation/doc-compiler/tree.test.mjs +7 -7
  319. package/foundation/fs/paths.d.mts +70 -8
  320. package/foundation/fs/paths.mjs +556 -38
  321. package/foundation/fs/paths.test.mjs +430 -54
  322. package/foundation/fs/read-record.d.mts +18 -0
  323. package/foundation/fs/read-record.mjs +131 -0
  324. package/foundation/fs/read-record.test.mjs +77 -0
  325. package/foundation/integrations/autolink.d.mts +2 -1
  326. package/foundation/integrations/autolink.mjs +4 -13
  327. package/foundation/integrations/cli-peer-version.d.mts +36 -0
  328. package/foundation/integrations/cli-peer-version.mjs +132 -0
  329. package/foundation/integrations/cli-peer-version.test.mjs +354 -0
  330. package/foundation/integrations/computer-integrations.d.mts +6 -3
  331. package/foundation/integrations/computer-integrations.mjs +38 -9
  332. package/foundation/integrations/contribution-inventory.d.mts +15 -20
  333. package/foundation/integrations/contribution-inventory.mjs +41 -5
  334. package/foundation/integrations/exports-subpaths.d.mts +57 -0
  335. package/foundation/integrations/exports-subpaths.mjs +113 -0
  336. package/foundation/integrations/integrations.d.mts +27 -1
  337. package/foundation/integrations/integrations.mjs +61 -2
  338. package/foundation/integrations/public-imports.d.mts +118 -0
  339. package/foundation/integrations/public-imports.mjs +1769 -0
  340. package/foundation/integrations/public-imports.test.mjs +1793 -0
  341. package/foundation/integrations/validate-contributions.d.mts +115 -0
  342. package/foundation/integrations/validate-contributions.mjs +1168 -13
  343. package/foundation/release/cache.test.mjs +24 -21
  344. package/foundation/release/global-state.d.mts +8 -4
  345. package/foundation/release/global-state.mjs +4 -2
  346. package/foundation/release/handoff.d.mts +23 -4
  347. package/foundation/release/handoff.mjs +175 -8
  348. package/foundation/release/notices.d.mts +62 -8
  349. package/foundation/release/notices.mjs +136 -25
  350. package/foundation/release/notices.test.mjs +21 -0
  351. package/foundation/release/run-context.mjs +14 -0
  352. package/foundation/response/response-types.doc.mjs +1 -1
  353. package/foundation/route/route.d.mts +285 -0
  354. package/foundation/route/route.mjs +324 -0
  355. package/foundation/route/route.test.mjs +153 -0
  356. package/foundation/runtime/hosted-runtime.test.mjs +403 -0
  357. package/foundation/runtime/locked-versions.d.mts +32 -0
  358. package/foundation/runtime/locked-versions.mjs +65 -0
  359. package/foundation/runtime/package-root.d.mts +91 -0
  360. package/foundation/runtime/package-root.mjs +174 -0
  361. package/foundation/runtime/package-set.d.mts +62 -0
  362. package/foundation/runtime/package-set.mjs +130 -0
  363. package/foundation/runtime/package-set.test.mjs +432 -0
  364. package/foundation/text/string-utils.d.mts +2 -1
  365. package/foundation/text/string-utils.mjs +16 -5
  366. package/foundation/text/suggest-order.d.mts +72 -0
  367. package/foundation/text/suggest-order.mjs +363 -0
  368. package/foundation/text/suggest-order.test.mjs +259 -0
  369. package/host/door-conformance.test.mjs +274 -0
  370. package/host/index.d.ts +116 -0
  371. package/host/index.mjs +295 -0
  372. package/host/index.test.mjs +302 -0
  373. package/package.json +14 -9
@@ -0,0 +1,432 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file Matching a project to a host's package sets (architecture:cli-surface
5
+ * INV30): one root per run, the first that qualifies in the host's order, the
6
+ * versions a project would install, nothing filled that ships an integration
7
+ * manifest, and the integrations a host approves for use outside a project.
8
+ */
9
+
10
+ import {describe, it, expect, afterEach} from 'vitest';
11
+ import * as fs from 'node:fs';
12
+ import * as path from 'node:path';
13
+ import {
14
+ cleanupFixtures,
15
+ project,
16
+ tempDir,
17
+ writeJson,
18
+ } from '../../test-utils/release-fixtures.mjs';
19
+ import {projectAstryxPins} from './locked-versions.mjs';
20
+ import {qualifyingRoot, rootPackage, satisfiesInstall} from './package-set.mjs';
21
+ import {scanComputerIntegrations} from '../integrations/computer-integrations.mjs';
22
+ import {findCoreDir} from '../fs/paths.mjs';
23
+ import {
24
+ _resetHostedRun,
25
+ activateHostedRun,
26
+ recordResolved,
27
+ recordResolvedIntegrations,
28
+ resolvedPackages,
29
+ } from './package-root.mjs';
30
+
31
+ afterEach(cleanupFixtures);
32
+
33
+ /**
34
+ * Write a package into `dir`'s node_modules.
35
+ * @param {string} dir
36
+ * @param {string} name
37
+ * @param {Record<string, unknown>} pkg
38
+ * @param {{manifest?: boolean, bin?: boolean, hostEntry?: boolean}} [options]
39
+ * @returns {string} the package folder
40
+ */
41
+ function install(dir, name, pkg, options = {}) {
42
+ const pkgDir = path.join(dir, 'node_modules', ...name.split('/'));
43
+ writeJson(path.join(pkgDir, 'package.json'), {
44
+ name,
45
+ ...pkg,
46
+ ...(options.bin ? {bin: {astryx: './bin.cjs'}} : {}),
47
+ });
48
+ if (options.bin) fs.writeFileSync(path.join(pkgDir, 'bin.cjs'), '');
49
+ if (options.hostEntry) {
50
+ fs.mkdirSync(path.join(pkgDir, 'host'), {recursive: true});
51
+ fs.writeFileSync(path.join(pkgDir, 'host', 'index.mjs'), '');
52
+ }
53
+ if (options.manifest) {
54
+ fs.writeFileSync(
55
+ path.join(pkgDir, 'astryx.integration.mjs'),
56
+ 'export default {};\n',
57
+ );
58
+ }
59
+ return pkgDir;
60
+ }
61
+
62
+ /**
63
+ * A package set: a root holding a CLI and Core of `version`.
64
+ * @param {string} version
65
+ * @param {{hostEntry?: boolean, bin?: boolean}} [options]
66
+ * @returns {string}
67
+ */
68
+ function packageSet(version, options = {}) {
69
+ const root = tempDir('set');
70
+ install(
71
+ root,
72
+ '@astryxdesign/cli',
73
+ {version},
74
+ {bin: options.bin ?? true, hostEntry: options.hostEntry},
75
+ );
76
+ install(root, '@astryxdesign/core', {version});
77
+ return root;
78
+ }
79
+
80
+ describe('the versions a project would install', () => {
81
+ it('reads each declared @astryxdesign/* package with its lockfile and installed versions', () => {
82
+ const root = project(tempDir(), {
83
+ '@astryxdesign/core': '^0.7.0',
84
+ '@astryxdesign/cli': '0.7.0',
85
+ react: '^19.0.0',
86
+ });
87
+ install(root, '@astryxdesign/cli', {version: '0.7.0'});
88
+ writeJson(path.join(root, 'package-lock.json'), {
89
+ lockfileVersion: 3,
90
+ packages: {
91
+ '': {},
92
+ 'node_modules/@astryxdesign/core': {version: '0.7.1'},
93
+ 'node_modules/@astryxdesign/cli': {version: '0.7.0'},
94
+ },
95
+ });
96
+ expect(projectAstryxPins(root)).toEqual([
97
+ {
98
+ name: '@astryxdesign/core',
99
+ declared: '^0.7.0',
100
+ locked: '0.7.1',
101
+ installed: null,
102
+ },
103
+ {
104
+ name: '@astryxdesign/cli',
105
+ declared: '0.7.0',
106
+ locked: '0.7.0',
107
+ installed: '0.7.0',
108
+ },
109
+ ]);
110
+ });
111
+ });
112
+
113
+ describe('a version the project would install for a range', () => {
114
+ it.each([
115
+ ['0.7.1', '^0.7.0', true],
116
+ ['0.8.0', '^0.7.0', false],
117
+ ['0.7.1-canary.1', '^0.7.0', false],
118
+ ['0.7.1-canary.1', '^0.7.1-canary.0', true],
119
+ ['0.7.1', 'file:../core', false],
120
+ ['0.7.1', 'workspace:*', false],
121
+ [null, '^0.7.0', false],
122
+ ])('%s for %s: %s', (version, range, expected) => {
123
+ expect(satisfiesInstall(version, range)).toBe(expected);
124
+ });
125
+ });
126
+
127
+ describe('the package set that serves a run another release answers', () => {
128
+ it('serves from the first set whose CLI and Core are that release', () => {
129
+ const older = packageSet('0.6.8');
130
+ const first = packageSet('0.7.0');
131
+ const second = packageSet('0.7.0');
132
+ const root = project(tempDir(), {'@astryxdesign/core': '0.7.0'});
133
+ const served = qualifyingRoot({
134
+ release: '0.7.0',
135
+ projectDir: root,
136
+ roots: [older, first, second],
137
+ });
138
+ expect(served).toMatchObject({root: first, hostEntry: null});
139
+ expect(/** @type {any} */ (served).bin).toBe(
140
+ path.join(first, 'node_modules', '@astryxdesign', 'cli', 'bin.cjs'),
141
+ );
142
+ });
143
+
144
+ it('skips a set whose Core is another release, or missing', () => {
145
+ const mixed = tempDir('set');
146
+ install(mixed, '@astryxdesign/cli', {version: '0.7.0'}, {bin: true});
147
+ install(mixed, '@astryxdesign/core', {version: '0.7.1'});
148
+ const bare = tempDir('set');
149
+ install(bare, '@astryxdesign/cli', {version: '0.7.0'}, {bin: true});
150
+ const root = project(tempDir(), {'@astryxdesign/core': '0.7.0'});
151
+ expect(
152
+ qualifyingRoot({
153
+ release: '0.7.0',
154
+ projectDir: root,
155
+ roots: [mixed, bare],
156
+ }),
157
+ ).toEqual({
158
+ reason:
159
+ "no package set the host passed holds CLI 0.7.0 with this project's packages",
160
+ });
161
+ });
162
+
163
+ it("holds the lockfile's version of each package it fills, else one in the declared range", () => {
164
+ const locked = project(tempDir(), {
165
+ '@astryxdesign/core': '^0.7.0',
166
+ '@astryxdesign/charts': '^0.7.0',
167
+ });
168
+ writeJson(path.join(locked, 'package-lock.json'), {
169
+ lockfileVersion: 3,
170
+ packages: {
171
+ '': {},
172
+ 'node_modules/@astryxdesign/core': {version: '0.7.0'},
173
+ 'node_modules/@astryxdesign/charts': {version: '0.7.0'},
174
+ },
175
+ });
176
+ const newer = packageSet('0.7.0');
177
+ install(newer, '@astryxdesign/charts', {version: '0.7.2'});
178
+ const exact = packageSet('0.7.0');
179
+ install(exact, '@astryxdesign/charts', {version: '0.7.0'});
180
+ expect(
181
+ qualifyingRoot({
182
+ release: '0.7.0',
183
+ projectDir: locked,
184
+ roots: [newer, exact],
185
+ }),
186
+ ).toMatchObject({root: exact});
187
+
188
+ const ranged = project(tempDir(), {
189
+ '@astryxdesign/core': '^0.7.0',
190
+ '@astryxdesign/charts': '^0.7.0',
191
+ });
192
+ expect(
193
+ qualifyingRoot({release: '0.7.0', projectDir: ranged, roots: [newer]}),
194
+ ).toMatchObject({root: newer});
195
+ });
196
+
197
+ it('keeps out a set whose copy the project declares as a path and has not locked', () => {
198
+ const root = project(tempDir(), {
199
+ '@astryxdesign/core': '0.7.0',
200
+ '@astryxdesign/charts': 'file:../charts',
201
+ });
202
+ const set = packageSet('0.7.0');
203
+ install(set, '@astryxdesign/charts', {version: '0.7.0'});
204
+ expect(
205
+ qualifyingRoot({release: '0.7.0', projectDir: root, roots: [set]}),
206
+ ).toHaveProperty('reason');
207
+ });
208
+
209
+ it('never fills an @astryxdesign/* integration, so its version never keeps a set out', () => {
210
+ const root = project(tempDir(), {
211
+ '@astryxdesign/core': '0.7.0',
212
+ '@astryxdesign/richtext': '^0.9.0',
213
+ });
214
+ const set = packageSet('0.7.0');
215
+ install(
216
+ set,
217
+ '@astryxdesign/richtext',
218
+ {version: '0.7.0'},
219
+ {manifest: true},
220
+ );
221
+ expect(
222
+ qualifyingRoot({release: '0.7.0', projectDir: root, roots: [set]}),
223
+ ).toMatchObject({root: set});
224
+ });
225
+
226
+ it('fills nothing the project installed', () => {
227
+ const root = project(tempDir(), {
228
+ '@astryxdesign/core': '0.7.0',
229
+ '@astryxdesign/charts': '^0.6.0',
230
+ });
231
+ install(root, '@astryxdesign/charts', {version: '0.6.3'});
232
+ const set = packageSet('0.7.0');
233
+ install(set, '@astryxdesign/charts', {version: '0.7.0'});
234
+ expect(
235
+ qualifyingRoot({release: '0.7.0', projectDir: root, roots: [set]}),
236
+ ).toMatchObject({root: set});
237
+ });
238
+
239
+ it('needs a host entry when asked to', () => {
240
+ const plain = packageSet('0.7.0');
241
+ const hosted = packageSet('0.7.0', {hostEntry: true});
242
+ const root = project(tempDir(), {'@astryxdesign/core': '0.7.0'});
243
+ expect(
244
+ qualifyingRoot({
245
+ release: '0.7.0',
246
+ projectDir: root,
247
+ roots: [plain, hosted],
248
+ needsHostEntry: true,
249
+ }),
250
+ ).toMatchObject({
251
+ root: hosted,
252
+ hostEntry: path.join(
253
+ hosted,
254
+ 'node_modules',
255
+ '@astryxdesign',
256
+ 'cli',
257
+ 'host',
258
+ 'index.mjs',
259
+ ),
260
+ });
261
+ });
262
+
263
+ it("reads a root's own node_modules only", () => {
264
+ const outer = tempDir('outer');
265
+ install(outer, '@astryxdesign/core', {version: '0.7.0'});
266
+ const set = path.join(outer, 'set');
267
+ install(set, '@astryxdesign/cli', {version: '0.7.0'}, {bin: true});
268
+ expect(rootPackage(set, '@astryxdesign/core')).toBeNull();
269
+ expect(
270
+ qualifyingRoot({release: '0.7.0', projectDir: null, roots: [set]}),
271
+ ).toHaveProperty('reason');
272
+ });
273
+ });
274
+
275
+ describe('the integrations a host approves', () => {
276
+ it('load by name from the package set that holds the CLI, with the same checks', () => {
277
+ const set = tempDir('set');
278
+ const cliDir = install(set, '@astryxdesign/cli', {version: '0.7.2'});
279
+ // The set declares only the CLI and Core, so FR12's scan finds nothing.
280
+ writeJson(path.join(set, 'package.json'), {
281
+ dependencies: {'@astryxdesign/cli': '0.7.2'},
282
+ });
283
+ install(set, 'acme-widgets', {version: '1.0.0'}, {manifest: true});
284
+ install(
285
+ set,
286
+ 'acme-old',
287
+ {version: '1.0.0', peerDependencies: {'@astryxdesign/core': '^0.6.0'}},
288
+ {manifest: true},
289
+ );
290
+ install(set, 'acme-plain', {version: '1.0.0'});
291
+ expect(
292
+ scanComputerIntegrations({cliRoot: cliDir, release: '0.7.2'}),
293
+ ).toEqual([]);
294
+ const found = scanComputerIntegrations({
295
+ cliRoot: cliDir,
296
+ release: '0.7.2',
297
+ approved: ['acme-widgets', 'acme-old', 'acme-plain', 'acme-missing'],
298
+ });
299
+ expect(found.map(c => [c.name, c.incompatible])).toEqual([
300
+ ['acme-widgets', null],
301
+ [
302
+ 'acme-old',
303
+ "it needs @astryxdesign/core ^0.6.0, and this computer's CLI is 0.7.2",
304
+ ],
305
+ ]);
306
+ expect(found[0].packageDir).toBe(
307
+ path.join(set, 'node_modules', 'acme-widgets'),
308
+ );
309
+ });
310
+
311
+ it('never takes an approved name from a folder above the package set', () => {
312
+ const outer = tempDir('outer');
313
+ install(outer, 'acme-outer', {version: '1.0.0'}, {manifest: true});
314
+ const set = path.join(outer, 'set');
315
+ const cliDir = install(set, '@astryxdesign/cli', {version: '0.7.2'});
316
+ writeJson(path.join(set, 'package.json'), {
317
+ dependencies: {'@astryxdesign/cli': '0.7.2'},
318
+ });
319
+ expect(
320
+ scanComputerIntegrations({
321
+ cliRoot: cliDir,
322
+ release: '0.7.2',
323
+ approved: ['acme-outer'],
324
+ }),
325
+ ).toEqual([]);
326
+ });
327
+ });
328
+
329
+ describe('where each package a hosted run resolved came from', () => {
330
+ afterEach(() => _resetHostedRun());
331
+
332
+ /**
333
+ * @param {string} root
334
+ * @param {string} name
335
+ */
336
+ const at = (root, name) =>
337
+ path.join(root, 'node_modules', ...name.split('/'));
338
+
339
+ it("names the project's packages as the project's when the host's CLI is installed in the project", () => {
340
+ const app = tempDir('app');
341
+ activateHostedRun({startingRoot: app, projectRoot: app});
342
+ recordResolved('@astryxdesign/cli', '0.7.2', at(app, '@astryxdesign/cli'));
343
+ recordResolved(
344
+ '@astryxdesign/core',
345
+ '0.7.2',
346
+ at(app, '@astryxdesign/core'),
347
+ 'project',
348
+ );
349
+ recordResolvedIntegrations([
350
+ {
351
+ name: 'acme-widgets',
352
+ version: '1.0.0',
353
+ __packageDir: at(app, 'acme-widgets'),
354
+ },
355
+ ]);
356
+ expect(resolvedPackages()).toEqual([
357
+ {name: '@astryxdesign/cli', version: '0.7.2', source: 'project'},
358
+ {name: '@astryxdesign/core', version: '0.7.2', source: 'project'},
359
+ {name: 'acme-widgets', version: '1.0.0', source: 'project'},
360
+ ]);
361
+ });
362
+
363
+ it("names what a package set holds as the package set's, and the project's integrations as the project's", () => {
364
+ const set = tempDir('set');
365
+ const app = tempDir('app');
366
+ activateHostedRun({startingRoot: set, projectRoot: app});
367
+ recordResolved('@astryxdesign/cli', '0.7.2', at(set, '@astryxdesign/cli'));
368
+ recordResolved(
369
+ '@astryxdesign/core',
370
+ '0.7.2',
371
+ at(set, '@astryxdesign/core'),
372
+ );
373
+ recordResolvedIntegrations([
374
+ {
375
+ name: 'acme-tools',
376
+ version: '2.0.0',
377
+ __packageDir: at(set, 'acme-tools'),
378
+ __computer: true,
379
+ },
380
+ {
381
+ name: 'acme-widgets',
382
+ version: '1.0.0',
383
+ __packageDir: at(app, 'acme-widgets'),
384
+ },
385
+ ]);
386
+ expect(resolvedPackages()).toEqual([
387
+ {name: '@astryxdesign/cli', version: '0.7.2', source: 'package-set'},
388
+ {name: '@astryxdesign/core', version: '0.7.2', source: 'package-set'},
389
+ {name: 'acme-tools', version: '2.0.0', source: 'package-set'},
390
+ {name: 'acme-widgets', version: '1.0.0', source: 'project'},
391
+ ]);
392
+ });
393
+
394
+ it("names what the project loaded or found above itself, as a workspace hoists it, as the project's", () => {
395
+ // The host's CLI sits in the workspace root, which is its package set,
396
+ // and the project is a workspace package whose packages are hoisted there.
397
+ const mono = tempDir('mono');
398
+ writeJson(path.join(mono, 'package.json'), {
399
+ private: true,
400
+ workspaces: ['packages/*'],
401
+ });
402
+ const app = path.join(mono, 'packages', 'app');
403
+ writeJson(path.join(app, 'package.json'), {
404
+ name: 'app',
405
+ dependencies: {'@astryxdesign/core': '0.7.2', 'acme-widgets': '1.0.0'},
406
+ });
407
+ install(mono, '@astryxdesign/core', {version: '0.7.2'});
408
+ activateHostedRun({startingRoot: mono, projectRoot: app});
409
+ expect(findCoreDir(app)).toBe(at(mono, '@astryxdesign/core'));
410
+ recordResolvedIntegrations([
411
+ {
412
+ name: 'acme-widgets',
413
+ version: '1.0.0',
414
+ __packageDir: at(mono, 'acme-widgets'),
415
+ },
416
+ ]);
417
+ expect(resolvedPackages()).toEqual([
418
+ {name: '@astryxdesign/core', version: '0.7.2', source: 'project'},
419
+ {name: 'acme-widgets', version: '1.0.0', source: 'project'},
420
+ ]);
421
+ });
422
+
423
+ it('names a package set inside the project folder as a package set', () => {
424
+ const app = tempDir('app');
425
+ const set = path.join(app, '.tools', 'astryx');
426
+ activateHostedRun({startingRoot: set, projectRoot: app});
427
+ recordResolved('@astryxdesign/cli', '0.7.2', at(set, '@astryxdesign/cli'));
428
+ expect(resolvedPackages()).toEqual([
429
+ {name: '@astryxdesign/cli', version: '0.7.2', source: 'package-set'},
430
+ ]);
431
+ });
432
+ });
@@ -28,7 +28,8 @@ export function findClosestComponents(name: string, components: Record<string, s
28
28
  * 30 keyword Levenshtein distance 2
29
29
  * 20 name Levenshtein distance 3
30
30
  *
31
- * Each result: { name, score, reason }
31
+ * Each result: { name, score, reason }, plus `keywordAt` when a keyword set
32
+ * the component's score: the position of the keyword that set it.
32
33
  *
33
34
  * @param {string} needle
34
35
  * @param {string} coreDir
@@ -48,7 +48,8 @@ export function findClosestComponents(name, components, maxDistance = 3) {
48
48
  * 30 keyword Levenshtein distance 2
49
49
  * 20 name Levenshtein distance 3
50
50
  *
51
- * Each result: { name, score, reason }
51
+ * Each result: { name, score, reason }, plus `keywordAt` when a keyword set
52
+ * the component's score: the position of the keyword that set it.
52
53
  *
53
54
  * @param {string} needle
54
55
  * @param {string} coreDir
@@ -64,6 +65,9 @@ export async function searchComponents(needle, coreDir, components) {
64
65
  const allNames = Object.values(components).flat();
65
66
  /** @type {Map<string, {name: string, score: number, reason: string}>} */
66
67
  const scored = new Map();
68
+ /** Position of the keyword that set each component's score. */
69
+ /** @type {Map<string, number>} */
70
+ const keywordAt = new Map();
67
71
 
68
72
  /**
69
73
  * @param {string} name
@@ -146,12 +150,14 @@ export async function searchComponents(needle, coreDir, components) {
146
150
 
147
151
  // Keyword matching
148
152
  if (docs.keywords && Array.isArray(docs.keywords)) {
149
- for (const kw of docs.keywords) {
153
+ for (const [at, kw] of docs.keywords.entries()) {
150
154
  const kwLower = kw.toLowerCase();
155
+ const before = scored.get(comp)?.score ?? 0;
151
156
 
152
157
  // Exact keyword
153
158
  if (kwLower === term) {
154
159
  addMatch(comp, 90, 'keyword "' + kw + '"');
160
+ if (!keywordAt.has(comp) || before < 90) keywordAt.set(comp, at);
155
161
  break;
156
162
  }
157
163
 
@@ -170,6 +176,7 @@ export async function searchComponents(needle, coreDir, components) {
170
176
  addMatch(comp, 70, 'keyword "' + kw + '" (distance ' + dist + ')');
171
177
  else if (dist === 2)
172
178
  addMatch(comp, 30, 'keyword "' + kw + '" (distance ' + dist + ')');
179
+ if ((scored.get(comp)?.score ?? 0) > before) keywordAt.set(comp, at);
173
180
  }
174
181
  }
175
182
 
@@ -202,9 +209,13 @@ export async function searchComponents(needle, coreDir, components) {
202
209
  }
203
210
  }
204
211
 
205
- const results = Array.from(scored.values()).sort(
206
- (a, b) => b.score - a.score || a.name.localeCompare(b.name),
207
- );
212
+ const results = Array.from(scored.values())
213
+ .map(r =>
214
+ keywordAt.has(r.name)
215
+ ? {...r, keywordAt: /** @type {number} */ (keywordAt.get(r.name))}
216
+ : r,
217
+ )
218
+ .sort((a, b) => b.score - a.score || a.name.localeCompare(b.name));
208
219
 
209
220
  return results;
210
221
  }
@@ -0,0 +1,72 @@
1
+ // @generated by scripts/sync-api-types.mjs from the JSDoc in foundation/**/*.mjs.
2
+ // DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
3
+
4
+ /**
5
+ * A name's words, lowercase and singular: camelCase, PascalCase, kebab-case,
6
+ * snake_case, slashes and spaces all split.
7
+ * @param {string} text
8
+ * @returns {string[]}
9
+ */
10
+ export function nameWords(text: string): string[];
11
+ /**
12
+ * The same items, closest to the typed name first; each item is as close as
13
+ * its closest name. Equal matches keep their original order.
14
+ * @template T
15
+ * @param {string} typedName
16
+ * @param {T[]} items
17
+ * @param {(item: T) => Array<string | undefined>} namesOf
18
+ * @returns {T[]}
19
+ */
20
+ export function orderByAffinity<T>(typedName: string, items: T[], namesOf: (item: T) => Array<string | undefined>): T[];
21
+ /**
22
+ * Docs route suggestions, closest to the typed route first. When every
23
+ * suggestion sits under one namespace the typed route also starts with, that
24
+ * shared part is left out of the comparison: it says nothing about which page
25
+ * was meant. Equal matches keep their order.
26
+ * @template {{name: string}} T
27
+ * @param {string} topic - the typed route
28
+ * @param {T[]} suggestions
29
+ * @returns {T[]}
30
+ */
31
+ export function orderRouteSuggestions<T extends {
32
+ name: string;
33
+ }>(topic: string, suggestions: T[]): T[];
34
+ /**
35
+ * The components whose synonyms name what was typed. A synonym matches when
36
+ * its words, with or without a leading library name, are the typed words
37
+ * (`MUI TextField` matches `MUI TextField`, `TextField` and `text-field`).
38
+ * @param {string} needle
39
+ * @param {Record<string, {synonyms?: string[]}>} cards - component name → its search card
40
+ * @returns {Set<string>}
41
+ */
42
+ export function synonymMatches(needle: string, cards: Record<string, {
43
+ synonyms?: string[];
44
+ }>): Set<string>;
45
+ /**
46
+ * Rank component suggestions for a name that resolved to nothing.
47
+ *
48
+ * Every component `searchComponents` scored keeps its score. A component whose
49
+ * name has exactly the typed words (`chat-messages` for `ChatMessage`) scores
50
+ * like an exact name; one of whose synonyms is the typed name (see
51
+ * {@link synonymMatches}) like an exact keyword; one whose name shares the
52
+ * typed compound name's words (see {@link compoundMatch}) just below a
53
+ * one-letter typo of a name. Equal scores put synonym matches first, then order by how many of the typed words the
54
+ * name carries, then by how early the keyword that matched sits in the
55
+ * component's keywords, then by edit distance, then by shared leading letters,
56
+ * then by name. The caller decides how many to print.
57
+ * @param {string} needle - the typed component name (no `XDS` prefix)
58
+ * @param {Array<{name: string, score: number, reason: string, keywordAt?: number}>} results - `searchComponents` output
59
+ * @param {string[]} allNames - every component name
60
+ * @param {Set<string>} [synonyms] - {@link synonymMatches} for the typed name
61
+ * @returns {Array<{name: string, score: number, reason: string}>}
62
+ */
63
+ export function rankComponentSuggestions(needle: string, results: Array<{
64
+ name: string;
65
+ score: number;
66
+ reason: string;
67
+ keywordAt?: number;
68
+ }>, allNames: string[], synonyms?: Set<string>): Array<{
69
+ name: string;
70
+ score: number;
71
+ reason: string;
72
+ }>;