@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,202 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file The words build's page ranker reads: a template's terms and families,
5
+ * an idea's modifiers, containers and phrase pairs, and search's synonyms.
6
+ *
7
+ * Family words come from the templates' own ids — a word in the ids of two or
8
+ * more templates of one family, plus the family's name when a template carries
9
+ * it — never from a list kept here or from synonyms, so an integration's
10
+ * templates join the vocabulary by being named like their family, and "a
11
+ * landing page" is not a hero because "landing" is a synonym of "hero".
12
+ */
13
+
14
+ import {stem, STOPWORDS, SYNONYMS} from '../../search/search.mjs';
15
+
16
+ /**
17
+ * Words search drops that name layout: "side navigation", "side by side".
18
+ */
19
+ const LAYOUT_WORDS = new Set(['side']);
20
+
21
+ /**
22
+ * Where an idea's head ends: its first clause, before the parts it lists.
23
+ */
24
+ export const HEAD_END =
25
+ /:|,|;|\(|\s[-\u2013\u2014]\s|\b(?:with|showing|listing|for|that|where|plus|including|containing|featuring|which|to|from|of)\b/i;
26
+
27
+ /**
28
+ * Frames a page can live in, above whatever is underneath. A container phrase
29
+ * counts only when it names one of these.
30
+ */
31
+ const OVERLAY_FRAMES = new Set([
32
+ 'modal',
33
+ 'dialog',
34
+ 'drawer',
35
+ 'sheet',
36
+ 'popup',
37
+ 'overlay',
38
+ ]);
39
+
40
+ /**
41
+ * The family whose templates are app chrome rather than page content: the
42
+ * explicit `Shell -` category (architecture:template-authoring/INV7).
43
+ */
44
+ export const SHELL_FAMILY = 'Shell';
45
+
46
+ /** A container phrase: "in a modal", "inside the side panel". */
47
+ export const CONTAINER_PHRASE =
48
+ /\b(?:in|inside|within)\s+(?:a|an|the)\s+([a-z-]+)(?:\s+([a-z-]+))?/gi;
49
+
50
+ /**
51
+ * Where a phrase ends, for finding the word that heads it: punctuation and
52
+ * the words that join phrases.
53
+ */
54
+ export const PHRASE_END =
55
+ /[^a-z0-9\s-]+|\s(?:and|or|with|for|of|to|in|on|at|by|from|the|a|an|plus)\s/;
56
+
57
+ /** @param {string} t */
58
+ const isContentWord = t =>
59
+ t.length >= 2 && (LAYOUT_WORDS.has(t) || !STOPWORDS.has(t));
60
+
61
+ /**
62
+ * Content terms of a text: lowercase alphanumeric words, stopwords removed,
63
+ * stemmed.
64
+ * @param {string} text
65
+ * @returns {string[]}
66
+ */
67
+ export function terms(text) {
68
+ return (
69
+ String(text)
70
+ .toLowerCase()
71
+ .match(/[a-z0-9]+/g) ?? []
72
+ )
73
+ .filter(isContentWord)
74
+ .map(stem);
75
+ }
76
+
77
+ /**
78
+ * The terms of an idea that only ever modify another word, each with the words
79
+ * it modifies: in each phrase the last content word is its head, and the ones
80
+ * before it describe the next.
81
+ * @param {string} query
82
+ * @returns {Map<string, Set<string>>}
83
+ */
84
+ export function modifiersOf(query) {
85
+ const heads = new Set();
86
+ /** @type {Map<string, Set<string>>} */
87
+ const modifiers = new Map();
88
+ for (const phrase of String(query).toLowerCase().split(PHRASE_END)) {
89
+ const words = terms(phrase);
90
+ words.forEach((w, i) => {
91
+ if (i === words.length - 1) heads.add(w);
92
+ else modifiers.set(w, (modifiers.get(w) ?? new Set()).add(words[i + 1]));
93
+ });
94
+ }
95
+ for (const h of heads) modifiers.delete(h);
96
+ return modifiers;
97
+ }
98
+
99
+ /**
100
+ * Adjacent term pairs a text names, "a b", for telling a template that names
101
+ * a whole compound ("executive summary") from one that names only its parts.
102
+ * @param {string[]} texts
103
+ * @returns {Set<string>}
104
+ */
105
+ export function pairsOf(texts) {
106
+ const pairs = new Set();
107
+ for (const text of texts) {
108
+ for (const phrase of String(text).toLowerCase().split(PHRASE_END)) {
109
+ const words = terms(phrase);
110
+ for (let i = 1; i < words.length; i++)
111
+ pairs.add(`${words[i - 1]} ${words[i]}`);
112
+ }
113
+ }
114
+ return pairs;
115
+ }
116
+
117
+ /**
118
+ * The terms of an idea that name its frame: "modal" in "in a modal". A card, a
119
+ * table or an existing page after "in a" is where a part goes, not a frame.
120
+ * @param {string} query
121
+ * @returns {Set<string>}
122
+ */
123
+ export function containersOf(query) {
124
+ const found = new Set();
125
+ for (const m of String(query).matchAll(CONTAINER_PHRASE)) {
126
+ for (const t of terms(`${m[1]} ${m[2] ?? ''}`)) {
127
+ if (OVERLAY_FRAMES.has(t)) found.add(t);
128
+ }
129
+ }
130
+ return found;
131
+ }
132
+
133
+ /** @param {string} text */
134
+ export const normalized = text =>
135
+ (
136
+ String(text)
137
+ .toLowerCase()
138
+ .match(/[a-z0-9]+/g) ?? []
139
+ ).join(' ');
140
+
141
+ /** Stemmed synonym lookup built from search's vocabulary, both directions. */
142
+ export const SYNONYMS_OF = (() => {
143
+ /** @type {Map<string, Set<string>>} */
144
+ const index = new Map();
145
+ /** @param {string} a @param {string} b */
146
+ const add = (a, b) => {
147
+ const set = index.get(a) ?? new Set();
148
+ set.add(b);
149
+ index.set(a, set);
150
+ };
151
+ for (const [key, values] of Object.entries(SYNONYMS)) {
152
+ const k = stem(key);
153
+ for (const value of values) {
154
+ const v = stem(value);
155
+ add(k, v);
156
+ add(v, k);
157
+ for (const other of values) if (other !== value) add(v, stem(other));
158
+ }
159
+ }
160
+ return index;
161
+ })();
162
+
163
+ /**
164
+ * @typedef {import('../_adapter.mjs').PageTemplate} PageTemplate
165
+ * @typedef {import('../_adapter.mjs').ComponentWords} ComponentWords
166
+ * @typedef {{name: string, score: number, hits: number, familyNamed: boolean, containerMatched: boolean, family: string, base: boolean, matched: Set<string>}} RankedPage
167
+ * @typedef {'page' | 'part' | 'edit'} IdeaKind
168
+ */
169
+
170
+ /** @param {PageTemplate} page */
171
+ const familyOf = page => (page.category.split(' - ')[0] ?? '').trim();
172
+
173
+ /**
174
+ * Family words, from the templates' own ids: a word in the ids of two or more
175
+ * templates of one family, plus the family's name when a template carries it.
176
+ * Each word maps to its family.
177
+ * @param {PageTemplate[]} pages
178
+ * @returns {Map<string, string>}
179
+ */
180
+ export function familyWordsOf(pages) {
181
+ /** @type {Map<string, Map<string, number>>} */
182
+ const idWords = new Map();
183
+ for (const page of pages) {
184
+ const family = familyOf(page);
185
+ const counts = idWords.get(family) ?? new Map();
186
+ for (const w of new Set(page.name.split('-'))) {
187
+ if (w.length >= 3) counts.set(stem(w), (counts.get(stem(w)) ?? 0) + 1);
188
+ }
189
+ idWords.set(family, counts);
190
+ }
191
+ /** @type {Map<string, string>} */
192
+ const familyOfWord = new Map();
193
+ for (const [family, counts] of idWords) {
194
+ const heads = [...counts].filter(([, c]) => c >= 2).map(([w]) => w);
195
+ const name = normalized(family).split(' ').pop() ?? '';
196
+ if (name.length >= 3 && counts.has(stem(name))) heads.push(stem(name));
197
+ for (const head of heads) {
198
+ if (!familyOfWord.has(head)) familyOfWord.set(head, family);
199
+ }
200
+ }
201
+ return familyOfWord;
202
+ }
@@ -0,0 +1,20 @@
1
+ // @generated by scripts/sync-api-types.mjs from the JSDoc in api/**/*.mjs.
2
+ // DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
3
+
4
+ export const BUILD_TREE: import("../../foundation/route/route.mjs").Tree<BuildRequest, BuildContext, import("./build.type.mjs").BuildKitResponse>;
5
+ export type BuildRequest = {
6
+ query: string;
7
+ options: NonNullable<Parameters<typeof kitSignals>[1]>;
8
+ };
9
+ export type BuildContext = {
10
+ /**
11
+ * the root's weights
12
+ */
13
+ weights: import("../../foundation/route/route.mjs").ClassifierWeights;
14
+ /**
15
+ * the block picker's weights; null: build names its own blocks
16
+ */
17
+ blocks: import("./blocks/blocks.mjs").BlockPickerWeights | null;
18
+ };
19
+ export type BuildAnswer = import("./build.type.mjs").BuildKitResponse;
20
+ import { kitSignals } from './kit/signals.mjs';
@@ -0,0 +1,53 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file How build answers: a root classifier sends each request to the
5
+ * searcher that should answer it. Its weights (tree.weights.json) have one
6
+ * class today, so every request goes to the main searcher: the kit's signals
7
+ * (kit/signals.mjs), the block picker's blocks (blocks/blocks.mjs), and the
8
+ * kit around them (kit/answer.mjs).
9
+ * @input {query, options} and the data build's adapter gathered: the root's
10
+ * weights and the block picker's
11
+ * @output the build.kit response
12
+ * @position api/build; build.mjs runs it with the shared route helper
13
+ */
14
+
15
+ import {routeKit} from '../../foundation/route/route.mjs';
16
+ import {kitAnswer} from './kit/answer.mjs';
17
+ import {kitSignals} from './kit/signals.mjs';
18
+ import {pickBlocks} from './blocks/blocks.mjs';
19
+
20
+ /**
21
+ * @typedef {{query: string, options: NonNullable<Parameters<typeof kitSignals>[1]>}} BuildRequest
22
+ * @typedef {object} BuildContext
23
+ * @property {import('../../foundation/route/route.mjs').ClassifierWeights} weights the root's weights
24
+ * @property {import('./blocks/blocks.mjs').BlockPickerWeights | null} blocks the block picker's weights; null: build names its own blocks
25
+ * @typedef {import('./build.type.mjs').BuildKitResponse} BuildAnswer
26
+ */
27
+
28
+ /** @type {import('../../foundation/route/route.mjs').RouteKit<BuildRequest, BuildContext, BuildAnswer>} */
29
+ const {defineSearcher, defineClassifier, defineTree} = routeKit;
30
+
31
+ const main = defineSearcher({
32
+ id: 'main',
33
+ summary:
34
+ 'Ranks page templates, blocks, and components by the words of the request, starts from the best page template, and lists its blocks in the order the block picker ranks them.',
35
+ run: async (request, ctx) => {
36
+ const signals = await kitSignals(request.query, request.options);
37
+ if (!ctx.blocks) return kitAnswer(signals);
38
+ const picked = pickBlocks(request.query, signals, ctx.blocks);
39
+ return kitAnswer(signals, picked.names, picked.entries);
40
+ },
41
+ });
42
+
43
+ const root = defineClassifier({
44
+ id: 'root',
45
+ summary: 'Classifies the request and hands it to the searcher for its class.',
46
+ searchers: {},
47
+ fallback: 'main',
48
+ floor: 1,
49
+ features: () => ['#bias'],
50
+ weights: ctx => ctx.weights,
51
+ });
52
+
53
+ export const BUILD_TREE = defineTree('root', [root, main]);
@@ -0,0 +1,185 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file build's tree end to end, offline: the network and subprocesses throw,
5
+ * and build still answers. The block picker changes only the kit's blocks:
6
+ * the start, its alternatives, and every other field stay build's own, the
7
+ * block count is build's own, and the blocks it names are pinned per phrase.
8
+ */
9
+
10
+ import * as path from 'node:path';
11
+ import {fileURLToPath} from 'node:url';
12
+ import {afterAll, beforeAll, describe, expect, it, vi} from 'vitest';
13
+
14
+ // vi.mock calls are hoisted above the imports; the stub maker is hoisted with them.
15
+ const {blocked} = vi.hoisted(() => ({
16
+ /** @param {string} name */
17
+ blocked: name => async (/** @type {() => Promise<any>} */ load) => {
18
+ const real = await load();
19
+ /** @type {Record<string, unknown>} */
20
+ const stub = {};
21
+ for (const [key, value] of Object.entries(real)) {
22
+ stub[key] =
23
+ typeof value === 'function'
24
+ ? () => {
25
+ throw new Error(`build reached ${name}.${key}`);
26
+ }
27
+ : value;
28
+ }
29
+ return {...stub, default: stub};
30
+ },
31
+ }));
32
+ vi.mock('node:http', blocked('http'));
33
+ vi.mock('node:https', blocked('https'));
34
+ vi.mock('node:net', blocked('net'));
35
+ vi.mock('node:dns', blocked('dns'));
36
+ vi.mock('node:child_process', blocked('child_process'));
37
+ vi.mock('http', blocked('http'));
38
+ vi.mock('https', blocked('https'));
39
+ vi.mock('net', blocked('net'));
40
+ vi.mock('dns', blocked('dns'));
41
+ vi.mock('child_process', blocked('child_process'));
42
+
43
+ const {build} = await import('./build.mjs');
44
+ const {buildKit} = await import('./kit/kit.mjs');
45
+ const {loadBlockPicker, withListingCache} = await import('./_adapter.mjs');
46
+
47
+ const REPO = path.resolve(
48
+ path.dirname(fileURLToPath(import.meta.url)),
49
+ '../../../..',
50
+ );
51
+
52
+ /**
53
+ * Hand-written phrases and the blocks the picker names for each. The rest of
54
+ * the answer carries search results, which change whenever the docs do, so it
55
+ * is compared with build's own kit rather than pinned.
56
+ * @type {[string, string[]][]}
57
+ */
58
+ const PINNED = [
59
+ [
60
+ 'analytics dashboard with a KPI row and a sortable table',
61
+ [
62
+ 'TableSortableTable',
63
+ 'TableGridDividersTable',
64
+ 'TableShowcase',
65
+ 'TableGroupedRowsTable',
66
+ 'TableStripedTable',
67
+ ],
68
+ ],
69
+ [
70
+ 'settings page with notification toggles and a save bar',
71
+ [
72
+ 'SwitchShowcase',
73
+ 'SwitchSettingsPanel',
74
+ 'ToggleButtonShowcase',
75
+ 'ToggleButtonStates',
76
+ 'SwitchWithDescription',
77
+ ],
78
+ ],
79
+ [
80
+ 'empty state for a question bank with an import button',
81
+ [
82
+ 'EmptyStateContainer',
83
+ 'EmptyStateShowcase',
84
+ 'EmptyStateCompact',
85
+ 'EmptyStateActions',
86
+ 'CommandPaletteEmptyShowcase',
87
+ ],
88
+ ],
89
+ [
90
+ 'login form with single sign-on buttons',
91
+ [
92
+ 'ButtonWithIcon',
93
+ 'CardWithInnerLayout',
94
+ 'ButtonVariants',
95
+ 'FormLayoutShowcase',
96
+ 'FormLayoutMixedControls',
97
+ ],
98
+ ],
99
+ [
100
+ 'calendar for booking appointments',
101
+ [
102
+ 'CalendarSingle',
103
+ 'CalendarShowcase',
104
+ 'CalendarConstraints',
105
+ 'CalendarRangeWithValue',
106
+ 'CalendarTwoMonths',
107
+ ],
108
+ ],
109
+ [
110
+ 'kanban board for sprint tasks with a filter bar',
111
+ ['ToolbarTableFilter', 'PowerSearchShowcase'],
112
+ ],
113
+ ];
114
+
115
+ /** @type {typeof globalThis.fetch} */
116
+ let realFetch;
117
+ beforeAll(() => {
118
+ realFetch = globalThis.fetch;
119
+ globalThis.fetch = () => {
120
+ throw new Error('build reached fetch');
121
+ };
122
+ });
123
+ afterAll(() => {
124
+ globalThis.fetch = realFetch;
125
+ });
126
+
127
+ describe('build tree', () => {
128
+ it('keeps a block the request names by its exact name in build’s slot', async () => {
129
+ const phrase = 'calendar for booking appointments with CalendarTwoMonths';
130
+ const answer = await build(phrase, {cwd: REPO});
131
+ const own = await withListingCache(() => buildKit(phrase, {cwd: REPO}));
132
+ if (answer?.type !== 'build.kit' || own.type !== 'build.kit')
133
+ throw new Error('expected a kit');
134
+ const k = own.data.blocks.findIndex(b => b.name === 'CalendarTwoMonths');
135
+ expect(k).toBeGreaterThanOrEqual(0);
136
+ expect(answer.data.blocks[k].name).toBe('CalendarTwoMonths');
137
+ });
138
+
139
+ it('runs with the network and subprocesses blocked', async () => {
140
+ const cp = await import('node:child_process');
141
+ const https = await import('node:https');
142
+ expect(() => cp.execFileSync('true')).toThrow(/build reached/);
143
+ expect(() => https.request('https://example.com')).toThrow(/build reached/);
144
+ expect(() => globalThis.fetch('https://example.com')).toThrow(
145
+ /build reached/,
146
+ );
147
+ });
148
+
149
+ it('loads the block picker weights as plain numbers', () => {
150
+ expect(loadBlockPicker()).not.toBeNull();
151
+ });
152
+
153
+ for (const [phrase, blocks] of PINNED) {
154
+ it(`answers "${phrase}" offline with the picker's blocks`, async () => {
155
+ const answer = await build(phrase, {cwd: REPO});
156
+ const own = await withListingCache(() => buildKit(phrase, {cwd: REPO}));
157
+ if (answer?.type !== 'build.kit' || own.type !== 'build.kit')
158
+ throw new Error('expected a kit');
159
+ // Only the blocks move: the start, its alternatives, and every other
160
+ // field are build's own.
161
+ expect({...answer.data, blocks: null}).toEqual({
162
+ ...own.data,
163
+ blocks: null,
164
+ });
165
+ expect(answer.data.start?.alternatives).toEqual(
166
+ own.data.start?.alternatives,
167
+ );
168
+ // Every block is build's own entry, one search returned, or the picker's
169
+ // own, each with its real score and reason; never more blocks than
170
+ // build's own.
171
+ expect(answer.data.blocks.length).toBe(own.data.blocks.length);
172
+ for (const b of answer.data.blocks) {
173
+ expect(b.score).toBeGreaterThan(0);
174
+ expect(typeof b.reason).toBe('string');
175
+ const mine = own.data.blocks.find(o => o.name === b.name);
176
+ if (mine) expect(mine).toEqual(b);
177
+ }
178
+ expect(answer.data.blocks.map(b => b.name)).toEqual(blocks);
179
+ // Never longer than build's own answer for the same request.
180
+ expect(Buffer.byteLength(JSON.stringify(answer))).toBeLessThanOrEqual(
181
+ Buffer.byteLength(JSON.stringify(own)),
182
+ );
183
+ });
184
+ }
185
+ });
@@ -0,0 +1 @@
1
+ {"classes": ["main"], "tokens": {"#bias": [0]}}
@@ -44,6 +44,10 @@ import {Project} from '../../foundation/config/project.mjs';
44
44
  import {loadComponentDoc as loadValidatedComponentDoc} from '../../foundation/discovery/component-loader.mjs';
45
45
  import {resolveComponentReplacements} from '../../foundation/discovery/component-replacement.mjs';
46
46
  import {searchComponents} from '../../foundation/text/string-utils.mjs';
47
+ import {
48
+ rankComponentSuggestions,
49
+ synonymMatches,
50
+ } from '../../foundation/text/suggest-order.mjs';
47
51
  import {AstryxError, coreDirOrThrow} from '../error.mjs';
48
52
  import {
49
53
  componentLinkProvider,
@@ -51,6 +55,28 @@ import {
51
55
  loadDocsCatalog,
52
56
  } from '../docs/_adapter.mjs';
53
57
 
58
+ /** @type {Record<string, {synonyms?: string[]}> | undefined} */
59
+ let cards;
60
+ /**
61
+ * Search's component cards (each component's other names), read once.
62
+ * @returns {Record<string, {synonyms?: string[]}>}
63
+ */
64
+ function searchCards() {
65
+ if (!cards) {
66
+ try {
67
+ cards = JSON.parse(
68
+ fs.readFileSync(
69
+ new URL('../search/_cards.json', import.meta.url),
70
+ 'utf8',
71
+ ),
72
+ );
73
+ } catch {
74
+ cards = {};
75
+ }
76
+ }
77
+ return /** @type {Record<string, {synonyms?: string[]}>} */ (cards);
78
+ }
79
+
54
80
  export {CORE_PACKAGE, unscopedComponentOwners};
55
81
 
56
82
  /**
@@ -463,12 +489,20 @@ export async function resolveUnscopedDoc(dirName, {coreDir, cwd, name}) {
463
489
  resolvedOwnerPackage = CORE_PACKAGE;
464
490
  resolvedSourcePath = findComponentSource(coreDir, resolvedName);
465
491
  } else {
492
+ // How many to suggest: the near-top results, at most five and at
493
+ // least two. Which ones, and in what order, is the suggestion
494
+ // ranking's call.
466
495
  const threshold = Math.max(topScore - 20, 1);
467
- const candidates = results
468
- .filter(r => r.score >= threshold)
469
- .slice(0, 5);
470
- if (candidates.length < 2)
471
- candidates.push(...results.slice(candidates.length, 2));
496
+ const count = Math.max(
497
+ results.filter(r => r.score >= threshold).slice(0, 5).length,
498
+ Math.min(2, results.length),
499
+ );
500
+ const candidates = rankComponentSuggestions(
501
+ dirName,
502
+ results,
503
+ Object.values(components).flat(),
504
+ synonymMatches(dirName, searchCards()),
505
+ ).slice(0, count);
472
506
  throw new AstryxError(
473
507
  `No component named "${name}"`,
474
508
  candidates.map(c => ({name: c.name, reason: c.reason})),
@@ -64,6 +64,7 @@ function writeIntegration(name, components, peers) {
64
64
  type: 'component',
65
65
  name: componentName,
66
66
  displayName: componentName,
67
+ import: `${name}/components/${componentName}`,
67
68
  ...(replaces ? {replaces} : {}),
68
69
  keywords: ['acme'],
69
70
  usage: {description: `${componentName} for Acme apps.`},
@@ -293,7 +293,7 @@ async function componentRead(name, options = {}) {
293
293
  const selectorList = Array.isArray(name) ? name : null;
294
294
  const noName = selectorList == null && !name;
295
295
 
296
- // Default detail level mirrors the CLI (see commands/component/index.mjs):
296
+ // Default detail level mirrors the CLI (see commands/component.mjs):
297
297
  // single-component views default to 'full', list-style views (--list,
298
298
  // --category, or no name) default to 'brief' (scannable name lists).
299
299
  // An explicit array remains a batch even when it is empty or a caller also
@@ -461,8 +461,22 @@ async function componentRead(name, options = {}) {
461
461
  return componentDetailBlocks(dirName, cwd, owner.package);
462
462
  }
463
463
  const loaded = await loadComponentDoc(owner.docPath, docOpts);
464
- // A family member an integration lists under its own name reads as that
465
- // member, the way a Core sub-component does below.
464
+ // A family member reads as that member, scoped the way the unscoped
465
+ // lookup scopes it: an integration member by its own entry, a Core
466
+ // sub-component (HStack in the Stack doc) by scopeSubComponent.
467
+ const sub =
468
+ scoped.kind === 'core'
469
+ ? scopeSubComponent(loaded, dirName, coreDir)
470
+ : null;
471
+ if (sub) {
472
+ if (props) {
473
+ return componentDetailProps(
474
+ {props: sub.matchingComponent.props},
475
+ owner.package,
476
+ );
477
+ }
478
+ return componentDetail(sub.scoped, owner, dirName, coreDir);
479
+ }
466
480
  const docs =
467
481
  scoped.kind === 'integration'
468
482
  ? scopeIntegrationMember(loaded, owner, dirName)
@@ -135,6 +135,21 @@ describe('component list — components whose docs live in a family folder', ()
135
135
  });
136
136
  }, SLOW);
137
137
 
138
+ it('answers a Core-scoped lookup of a member with that member, as the unscoped lookup does', async () => {
139
+ for (const name of ['HStack', 'VStack']) {
140
+ const unscoped = await component(name, {cwd});
141
+ const scoped = await component(name, {
142
+ cwd,
143
+ package: '@astryxdesign/core',
144
+ });
145
+ expect(scoped.data.name).toBe(name);
146
+ expect(scoped.data.parentDoc).toBe('Stack');
147
+ expect(scoped.data.import).toBe(`@astryxdesign/core/${name}`);
148
+ expect(scoped.data.props).toEqual(unscoped.data.props);
149
+ expect(scoped.data.props.length).toBeGreaterThan(0);
150
+ }
151
+ }, SLOW);
152
+
138
153
  it('prints each one\'s own import path in the list, the detail, and search', async () => {
139
154
  // The path `component <Name>` prints is the one docs and examples teach:
140
155
  // the component's own entry point, never its family's (/Stack, /Text) or
@@ -250,14 +250,14 @@ describe("a link in a guide's description, as the docs tree shows it", () => {
250
250
  'Document a theme by extending the core theme topic, so an app reads it in `astryx docs theme`.';
251
251
  const guide = /** @type {any} */ (
252
252
  await docs(
253
- 'cli/integrations/building-blocks/themes/document-the-theme',
253
+ 'authoring/building-blocks/themes/document-the-theme',
254
254
  undefined,
255
255
  {cwd: tmpDir, full: true},
256
256
  )
257
257
  );
258
258
  expect(guide.data.description).toBe(expected);
259
259
  const namespace = /** @type {any} */ (
260
- await docs('cli/integrations/building-blocks/themes', undefined, {
260
+ await docs('authoring/building-blocks/themes', undefined, {
261
261
  cwd: tmpDir,
262
262
  })
263
263
  );
@@ -43,12 +43,11 @@ export function hasSources(project: Project | undefined): boolean;
43
43
  * @param {Project | undefined} project
44
44
  * @param {Array<{name: string}>} installed
45
45
  * @param {Set<string>} declared
46
- * @param {(name: string, version?: string) => string} add
47
46
  * @returns {Promise<import('./discover.type.mjs').DiscoverAvailableEntry[]>}
48
47
  */
49
48
  export function computerAvailableEntries(project: Project | undefined, installed: Array<{
50
49
  name: string;
51
- }>, declared: Set<string>, add: (name: string, version?: string) => string): Promise<import("./discover.type.mjs").DiscoverAvailableEntry[]>;
50
+ }>, declared: Set<string>): Promise<import("./discover.type.mjs").DiscoverAvailableEntry[]>;
52
51
  /**
53
52
  * Package names the project's package.json declares in any dependency field.
54
53
  * @param {Project | undefined} project