@astryxdesign/cli 0.7.2 → 0.7.3-canary.0bf23bd

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 (337) hide show
  1. package/README.md +104 -71
  2. package/api/build/_adapter.d.mts +58 -0
  3. package/api/build/_adapter.mjs +115 -3
  4. package/api/build/blocks/blocks.d.mts +42 -0
  5. package/api/build/blocks/blocks.mjs +169 -0
  6. package/api/build/blocks/blocks.test.mjs +108 -0
  7. package/api/build/blocks/blocks.weights.json +1 -0
  8. package/api/build/build.mjs +25 -6
  9. package/api/build/kit/answer.d.mts +14 -0
  10. package/api/build/kit/answer.mjs +85 -0
  11. package/api/build/kit/answer.test.mjs +38 -0
  12. package/api/build/kit/kind.d.mts +25 -0
  13. package/api/build/kit/kind.mjs +170 -0
  14. package/api/build/kit/kit.d.mts +2 -4
  15. package/api/build/kit/kit.mjs +8 -398
  16. package/api/build/kit/rank.d.mts +3 -32
  17. package/api/build/kit/rank.mjs +25 -342
  18. package/api/build/kit/rank.test.mjs +2 -1
  19. package/api/build/kit/signals.d.mts +60 -0
  20. package/api/build/kit/signals.mjs +284 -0
  21. package/api/build/kit/start.d.mts +32 -0
  22. package/api/build/kit/start.mjs +163 -0
  23. package/api/build/kit/words.d.mts +72 -0
  24. package/api/build/kit/words.mjs +202 -0
  25. package/api/build/tree.d.mts +20 -0
  26. package/api/build/tree.mjs +52 -0
  27. package/api/build/tree.test.mjs +177 -0
  28. package/api/build/tree.weights.json +1 -0
  29. package/api/component/component.mjs +17 -3
  30. package/api/component/component.test.mjs +15 -0
  31. package/api/component/doc-links.test.mjs +2 -2
  32. package/api/discover/_adapter.mjs +36 -9
  33. package/api/discover/_adapter.test.mjs +42 -0
  34. package/api/docs/_adapter.d.mts +2 -2
  35. package/api/docs/_adapter.mjs +78 -23
  36. package/api/docs/docs-homes.test.mjs +127 -0
  37. package/api/docs/docs.doc.mjs +3 -3
  38. package/api/docs/docs.test.mjs +35 -19
  39. package/api/docs/integration-tree.test.mjs +20 -20
  40. package/api/docs/integrationDocs.test.mjs +1 -1
  41. package/api/docs/namespace-section.test.mjs +6 -6
  42. package/api/docs/reference-blocks.test.mjs +2 -2
  43. package/api/doctor/doctor.mjs +102 -24
  44. package/api/doctor/doctor.test.mjs +37 -12
  45. package/api/hook/hook.mjs +1 -1
  46. package/api/init/init.doc.mjs +20 -6
  47. package/api/init/init.test.mjs +18 -2
  48. package/api/init/init.type.d.mts +3 -3
  49. package/api/init/init.type.mjs +3 -3
  50. package/api/init/remove/remove.mjs +17 -4
  51. package/api/init/remove/remove.test.mjs +114 -2
  52. package/api/init/run/run.mjs +69 -6
  53. package/api/integration/_adapter.d.mts +8 -0
  54. package/api/integration/_adapter.mjs +21 -1
  55. package/api/integration/authoring-checks.mjs +83 -9
  56. package/api/integration/authoring-checks.test.mjs +220 -0
  57. package/api/integration/pack-check.mjs +123 -4
  58. package/api/integration/pack-check.test.mjs +87 -0
  59. package/api/integration/validate-integration-case-disk.test.mjs +111 -0
  60. package/api/integration/validate-integration-gaps.test.mjs +1964 -0
  61. package/api/integration/validate-integration.d.mts +11 -2
  62. package/api/integration/validate-integration.mjs +250 -32
  63. package/api/integration/validate-integration.type.d.mts +59 -1
  64. package/api/integration/validate-integration.type.mjs +46 -2
  65. package/api/integration/validateIntegration.doc.mjs +1 -1
  66. package/api/search/ORDERING.md +28 -0
  67. package/api/search/_adapter.d.mts +82 -2
  68. package/api/search/_adapter.mjs +157 -10
  69. package/api/search/_adapter.test.mjs +173 -0
  70. package/api/search/_candidate-score.d.mts +40 -0
  71. package/api/search/_candidate-score.mjs +238 -0
  72. package/api/search/_cards.json +6098 -0
  73. package/api/search/_compound.d.mts +50 -0
  74. package/api/search/_compound.mjs +170 -0
  75. package/api/search/_doc-searcher.d.mts +4 -0
  76. package/api/search/_doc-searcher.mjs +23 -0
  77. package/api/search/_doc-searcher.test.mjs +36 -0
  78. package/api/search/_doc-searcher.weights.json +1 -0
  79. package/api/search/_doc-text.d.mts +30 -0
  80. package/api/search/_doc-text.mjs +87 -0
  81. package/api/search/_entries.d.mts +52 -0
  82. package/api/search/_entries.mjs +214 -0
  83. package/api/search/_features.d.mts +35 -0
  84. package/api/search/_features.mjs +155 -0
  85. package/api/search/_gather-components.d.mts +26 -0
  86. package/api/search/_gather-components.mjs +284 -0
  87. package/api/search/_gather-docs.d.mts +20 -0
  88. package/api/search/_gather-docs.mjs +254 -0
  89. package/api/search/_gather-hooks.d.mts +12 -0
  90. package/api/search/_gather-hooks.mjs +52 -0
  91. package/api/search/_gather-templates.d.mts +25 -0
  92. package/api/search/_gather-templates.mjs +107 -0
  93. package/api/search/_lexical.d.mts +135 -0
  94. package/api/search/_lexical.mjs +351 -0
  95. package/api/search/_main-searcher.d.mts +19 -0
  96. package/api/search/_main-searcher.mjs +115 -0
  97. package/api/search/_main-searcher.test.mjs +106 -0
  98. package/api/search/_main-searcher.weights.json +1 -0
  99. package/api/search/_module-doc.d.mts +32 -0
  100. package/api/search/_module-doc.mjs +78 -0
  101. package/api/search/_order.d.mts +32 -0
  102. package/api/search/_order.mjs +162 -0
  103. package/api/search/_order.test.mjs +72 -0
  104. package/api/search/_priority.d.mts +50 -0
  105. package/api/search/_priority.mjs +139 -0
  106. package/api/search/_query-words.d.mts +25 -0
  107. package/api/search/_query-words.mjs +92 -0
  108. package/api/search/_query.d.mts +65 -0
  109. package/api/search/_query.mjs +176 -0
  110. package/api/search/_result.d.mts +97 -0
  111. package/api/search/_result.mjs +150 -0
  112. package/api/search/_score.d.mts +60 -0
  113. package/api/search/_score.mjs +283 -0
  114. package/api/search/_synonyms.d.mts +21 -0
  115. package/api/search/_synonyms.mjs +67 -0
  116. package/api/search/_template-searcher.d.mts +4 -0
  117. package/api/search/_template-searcher.mjs +23 -0
  118. package/api/search/_template-searcher.test.mjs +40 -0
  119. package/api/search/_template-searcher.weights.json +1 -0
  120. package/api/search/_titles.d.mts +33 -0
  121. package/api/search/_titles.mjs +65 -0
  122. package/api/search/_words.d.mts +64 -0
  123. package/api/search/_words.mjs +157 -0
  124. package/api/search/search.d.mts +22 -305
  125. package/api/search/search.doc.mjs +8 -6
  126. package/api/search/search.mjs +56 -2482
  127. package/api/search/search.test.mjs +14 -14
  128. package/api/search/search.type.d.mts +6 -1
  129. package/api/search/search.type.mjs +6 -1
  130. package/api/search/tree.d.mts +15 -0
  131. package/api/search/tree.mjs +40 -0
  132. package/api/search/tree.test.mjs +66 -0
  133. package/api/search/tree.weights.json +1 -0
  134. package/api/theme/build/build.packed-old-core.test.mjs +1 -0
  135. package/api/upgrade/_adapter.d.mts +57 -9
  136. package/api/upgrade/_adapter.mjs +198 -28
  137. package/api/upgrade/registry/registry.d.mts +10 -4
  138. package/api/upgrade/registry/registry.mjs +33 -16
  139. package/api/upgrade/registry/registry.test.mjs +85 -0
  140. package/api/upgrade/run/run.d.mts +4 -2
  141. package/api/upgrade/run/run.mjs +13 -2
  142. package/api/upgrade/run/run.test.mjs +417 -3
  143. package/api/upgrade/upgrade.doc.mjs +10 -5
  144. package/api/upgrade/upgrade.mjs +13 -2
  145. package/api/upgrade/upgrade.test.mjs +194 -0
  146. package/api/upgrade/upgrade.type.d.mts +5 -3
  147. package/api/upgrade/upgrade.type.mjs +8 -6
  148. package/assets/codemods/transforms/v0.7.0/__tests__/validate-data-token-ownership.test.mjs +11 -0
  149. package/assets/codemods/transforms/v0.7.0/validate-data-token-ownership.mjs +3 -3
  150. package/assets/docs/author-a-theme.doc.mjs +2 -2
  151. package/assets/docs/is-my-code-good.doc.mjs +287 -0
  152. package/assets/docs/tree/add-a-component.doc.mjs +1 -1
  153. package/assets/docs/tree/add-a-theme.doc.mjs +1 -1
  154. package/assets/docs/tree/add-a-topic.doc.mjs +2 -2
  155. package/assets/docs/tree/agent-guidance.doc.mjs +1 -1
  156. package/assets/docs/tree/api.doc.mjs +6 -0
  157. package/assets/docs/tree/{integrations.doc.mjs → authoring.doc.mjs} +15 -6
  158. package/assets/docs/tree/block-template.doc.mjs +1 -1
  159. package/assets/docs/tree/build-the-template.doc.mjs +1 -1
  160. package/assets/docs/tree/building-blocks.doc.mjs +2 -2
  161. package/assets/docs/tree/check-the-props.doc.mjs +1 -1
  162. package/assets/docs/tree/check-your-docs.doc.mjs +2 -2
  163. package/assets/docs/tree/checks.doc.mjs +2 -2
  164. package/assets/docs/tree/cli-command.doc.mjs +1 -1
  165. package/assets/docs/tree/cli.doc.mjs +1 -1
  166. package/assets/docs/tree/codemods.doc.mjs +1 -1
  167. package/assets/docs/tree/component-family.doc.mjs +1 -1
  168. package/assets/docs/tree/component-imports.doc.mjs +1 -1
  169. package/assets/docs/tree/components.doc.mjs +1 -1
  170. package/assets/docs/tree/configuration.doc.mjs +1 -1
  171. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +1 -1
  172. package/assets/docs/tree/define-the-theme.doc.mjs +1 -1
  173. package/assets/docs/tree/describe-the-component.doc.mjs +1 -1
  174. package/assets/docs/tree/docs.doc.mjs +1 -1
  175. package/assets/docs/tree/document-the-template.doc.mjs +1 -1
  176. package/assets/docs/tree/document-the-theme.doc.mjs +1 -1
  177. package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
  178. package/assets/docs/tree/extend-or-replace.doc.mjs +1 -1
  179. package/assets/docs/{authoring.doc.mjs → tree/field-reference.doc.mjs} +4 -3
  180. package/assets/docs/tree/fonts-and-assets.doc.mjs +1 -1
  181. package/assets/docs/tree/generate-a-palette.doc.mjs +1 -1
  182. package/assets/docs/tree/global-integrations.doc.mjs +2 -2
  183. package/assets/docs/tree/grade-template-with-agent.doc.mjs +4 -4
  184. package/assets/docs/tree/help.doc.mjs +2 -2
  185. package/assets/docs/tree/host-entry.doc.mjs +128 -0
  186. package/assets/docs/tree/links.doc.mjs +1 -1
  187. package/assets/docs/tree/package-and-test.doc.mjs +1 -1
  188. package/assets/docs/tree/page-template.doc.mjs +1 -1
  189. package/assets/docs/tree/publishing.doc.mjs +1 -1
  190. package/assets/docs/tree/quick-start.doc.mjs +1 -1
  191. package/assets/docs/tree/replace-a-core-component.doc.mjs +1 -1
  192. package/assets/docs/tree/replace-a-core-template.doc.mjs +1 -1
  193. package/assets/docs/tree/sections-and-placement.doc.mjs +1 -1
  194. package/assets/docs/tree/see-it-in-an-app.doc.mjs +1 -1
  195. package/assets/docs/tree/ship.doc.mjs +2 -2
  196. package/assets/docs/tree/short-and-findable.doc.mjs +1 -1
  197. package/assets/docs/tree/single-component.doc.mjs +1 -1
  198. package/assets/docs/tree/start-a-template.doc.mjs +1 -1
  199. package/assets/docs/tree/subcomponent.doc.mjs +1 -1
  200. package/assets/docs/tree/template-assets.doc.mjs +1 -1
  201. package/assets/docs/tree/template-doc-overview.doc.mjs +2 -2
  202. package/assets/docs/tree/template-fonts.doc.mjs +1 -1
  203. package/assets/docs/tree/template-grading-rubric.doc.mjs +17 -38
  204. package/assets/docs/tree/template-icons.doc.mjs +1 -1
  205. package/assets/docs/tree/template-images-media.doc.mjs +1 -1
  206. package/assets/docs/tree/template-styles.doc.mjs +1 -1
  207. package/assets/docs/tree/templates.doc.mjs +1 -1
  208. package/assets/docs/tree/test-in-an-app.doc.mjs +1 -1
  209. package/assets/docs/tree/test-template-in-app.doc.mjs +1 -1
  210. package/assets/docs/tree/themes.doc.mjs +1 -1
  211. package/assets/docs/tree/tokens-data.doc.mjs +2 -2
  212. package/assets/docs/tree/troubleshooting.doc.mjs +1 -1
  213. package/assets/docs/tree/upgrading.doc.mjs +1 -1
  214. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +1 -1
  215. package/assets/docs/tree/verify-packed-template.doc.mjs +1 -1
  216. package/assets/docs/tree/versioning.doc.mjs +1 -1
  217. package/assets/docs/tree/write-good-templates.doc.mjs +1 -1
  218. package/assets/docs/tree/write-the-template-file.doc.mjs +1 -1
  219. package/assets/docs/use-integrations.doc.mjs +120 -0
  220. package/assets/docs/working-with-ai.doc.mjs +6 -2
  221. package/assets/templates/themes/butter/butterTheme.ts +4 -2
  222. package/assets/templates/themes/stone/stoneTheme.ts +4 -2
  223. package/assets/templates/themes/y2k/y2kTheme.ts +4 -2
  224. package/authoring/debug/parse.d.mts +1 -1
  225. package/authoring/doctypes/schema/schema.doc.mjs +1 -1
  226. package/authoring/integration/integration.doc.mjs +1 -1
  227. package/authoring/integration/type.ts +1 -1
  228. package/clients/cli/bin/astryx.mjs +7 -78
  229. package/clients/cli/command-result-coverage.test.mjs +6 -5
  230. package/clients/cli/commands/build-theme.color-scheme.test.mjs +1 -1
  231. package/clients/cli/commands/{component/index.mjs → component.mjs} +37 -37
  232. package/clients/cli/commands/component.test.mjs +6 -6
  233. package/clients/cli/commands/docs.doc.mjs +2 -2
  234. package/clients/cli/commands/docs.mjs +1 -1
  235. package/clients/cli/commands/docs.test.mjs +14 -14
  236. package/clients/cli/commands/doctor.mjs +30 -0
  237. package/clients/cli/commands/doctor.test.mjs +65 -0
  238. package/clients/cli/commands/{hook/index.mjs → hook.mjs} +22 -22
  239. package/clients/cli/commands/import-hint-correctness.test.mjs +1 -1
  240. package/clients/cli/commands/init.behavior.test.mjs +230 -1
  241. package/clients/cli/commands/init.doc.mjs +28 -9
  242. package/clients/cli/commands/init.flag-help.test.mjs +1 -0
  243. package/clients/cli/commands/init.mjs +2 -2
  244. package/clients/cli/commands/integration-check.test.mjs +48 -0
  245. package/clients/cli/commands/integration.doc.mjs +1 -1
  246. package/clients/cli/commands/manifest.doc.mjs +1 -1
  247. package/clients/cli/commands/manifest.mjs +67 -0
  248. package/clients/cli/commands/outside-project.test.mjs +2 -1
  249. package/clients/cli/commands/postinstall.doc.mjs +23 -0
  250. package/clients/cli/commands/postinstall.mjs +31 -0
  251. package/clients/cli/commands/setup-nudge.test.mjs +61 -0
  252. package/clients/cli/commands/{build-theme.mjs → theme.mjs} +1 -1
  253. package/clients/cli/commands/upgrade.doc.mjs +4 -2
  254. package/clients/cli/commands/upgrade.mjs +9 -2
  255. package/clients/cli/commands/upgrade.subfolder-path.test.mjs +97 -0
  256. package/clients/cli/index.mjs +29 -86
  257. package/clients/cli/lib/component-format.mjs +1 -1
  258. package/clients/cli/lib/define-command.mjs +12 -7
  259. package/clients/cli/lib/exit-codes.test.mjs +5 -0
  260. package/clients/cli/lib/manifest.test.mjs +12 -0
  261. package/clients/cli/lib/welcome-box.mjs +50 -0
  262. package/clients/cli/run.mjs +108 -0
  263. package/foundation/agent-docs/agent-doc-state.d.mts +20 -7
  264. package/foundation/agent-docs/agent-doc-state.mjs +35 -8
  265. package/foundation/agent-docs/agent-docs.d.mts +100 -12
  266. package/foundation/agent-docs/agent-docs.mjs +1012 -100
  267. package/foundation/agent-docs/agent-docs.path-safety.test.mjs +1239 -17
  268. package/foundation/agent-docs/agent-docs.test.mjs +683 -39
  269. package/foundation/config/project.mjs +20 -22
  270. package/foundation/config/project.test.mjs +56 -0
  271. package/foundation/discovery/authoring-self-docs.d.mts +7 -0
  272. package/foundation/discovery/authoring-self-docs.mjs +16 -4
  273. package/foundation/discovery/authoring-self-docs.test.mjs +15 -3
  274. package/foundation/discovery/authoring-surface.d.mts +1 -1
  275. package/foundation/discovery/authoring-surface.mjs +2 -2
  276. package/foundation/discovery/authoring-surface.test.mjs +1 -1
  277. package/foundation/discovery/cli-self-docs.d.mts +2 -1
  278. package/foundation/discovery/cli-self-docs.mjs +4 -3
  279. package/foundation/discovery/cli-self-docs.test.mjs +2 -2
  280. package/foundation/discovery/component-discovery.d.mts +37 -0
  281. package/foundation/discovery/component-discovery.mjs +80 -5
  282. package/foundation/discovery/component-loader.d.mts +1 -1
  283. package/foundation/discovery/docs-discovery.mjs +14 -1
  284. package/foundation/discovery/module-exports.d.mts +41 -0
  285. package/foundation/discovery/module-exports.mjs +253 -0
  286. package/foundation/discovery/runner-allowlist.d.mts +2 -4
  287. package/foundation/discovery/runner-allowlist.mjs +2 -4
  288. package/foundation/discovery/runner-commands.d.mts +77 -0
  289. package/foundation/discovery/runner-commands.mjs +294 -0
  290. package/foundation/discovery/runner-commands.test.mjs +128 -0
  291. package/foundation/discovery/stray-pointers.test.mjs +1 -1
  292. package/foundation/discovery/template-adapter.mjs +18 -1
  293. package/foundation/discovery/template-needs.mjs +1 -44
  294. package/foundation/discovery/theme-discovery.mjs +147 -3
  295. package/foundation/doc-compiler/doc-loads.test.mjs +33 -9
  296. package/foundation/doc-compiler/read.mjs +59 -1
  297. package/foundation/doc-compiler/read.test.mjs +11 -2
  298. package/foundation/doc-compiler/split-topic-keywords.test.mjs +3 -2
  299. package/foundation/doc-compiler/tree.d.mts +32 -6
  300. package/foundation/doc-compiler/tree.mjs +77 -43
  301. package/foundation/doc-compiler/tree.test.mjs +7 -7
  302. package/foundation/fs/paths.d.mts +70 -8
  303. package/foundation/fs/paths.mjs +556 -38
  304. package/foundation/fs/paths.test.mjs +430 -54
  305. package/foundation/fs/read-record.d.mts +18 -0
  306. package/foundation/fs/read-record.mjs +131 -0
  307. package/foundation/fs/read-record.test.mjs +77 -0
  308. package/foundation/integrations/autolink.d.mts +2 -1
  309. package/foundation/integrations/autolink.mjs +4 -13
  310. package/foundation/integrations/computer-integrations.d.mts +6 -3
  311. package/foundation/integrations/computer-integrations.mjs +38 -9
  312. package/foundation/integrations/contribution-inventory.d.mts +15 -20
  313. package/foundation/integrations/contribution-inventory.mjs +41 -5
  314. package/foundation/integrations/integrations.d.mts +27 -1
  315. package/foundation/integrations/integrations.mjs +61 -2
  316. package/foundation/integrations/validate-contributions.d.mts +115 -0
  317. package/foundation/integrations/validate-contributions.mjs +1168 -13
  318. package/foundation/release/handoff.d.mts +23 -4
  319. package/foundation/release/handoff.mjs +175 -8
  320. package/foundation/release/run-context.mjs +14 -0
  321. package/foundation/response/response-types.doc.mjs +1 -1
  322. package/foundation/route/route.d.mts +285 -0
  323. package/foundation/route/route.mjs +324 -0
  324. package/foundation/route/route.test.mjs +153 -0
  325. package/foundation/runtime/hosted-runtime.test.mjs +403 -0
  326. package/foundation/runtime/locked-versions.d.mts +32 -0
  327. package/foundation/runtime/locked-versions.mjs +65 -0
  328. package/foundation/runtime/package-root.d.mts +91 -0
  329. package/foundation/runtime/package-root.mjs +174 -0
  330. package/foundation/runtime/package-set.d.mts +62 -0
  331. package/foundation/runtime/package-set.mjs +130 -0
  332. package/foundation/runtime/package-set.test.mjs +432 -0
  333. package/host/door-conformance.test.mjs +274 -0
  334. package/host/index.d.ts +116 -0
  335. package/host/index.mjs +295 -0
  336. package/host/index.test.mjs +302 -0
  337. package/package.json +14 -9
@@ -40,7 +40,7 @@ import {parseConfig} from '../../authoring/config/parse.mjs';
40
40
  import {
41
41
  loadIntegrations,
42
42
  loadLocalIntegration,
43
- resolvePackageDir,
43
+ resolveInstalledIntegrationDir,
44
44
  } from '../integrations/integrations.mjs';
45
45
  import {loadAutolinkCandidates} from '../integrations/autolink.mjs';
46
46
  import {
@@ -65,7 +65,7 @@ import {
65
65
  discoverValidIntegrationComponents,
66
66
  } from '../discovery/component-discovery.mjs';
67
67
  import {resolveComponentReplacements} from '../discovery/component-replacement.mjs';
68
- import {CLI_ROOT, findCoreDir, findProjectCoreDir} from '../fs/paths.mjs';
68
+ import {CLI_ROOT, findCoreDir, findPackageRoot, findProjectCoreDir} from '../fs/paths.mjs';
69
69
  import {
70
70
  applyTemplateReplacements,
71
71
  effectiveTemplateDiscovery,
@@ -89,6 +89,10 @@ import {
89
89
  } from '../../assets/codemods/integration-discovery.mjs';
90
90
  import {validateLoadedIntegration} from '../integrations/validate-contributions.mjs';
91
91
  import {recordDeclaredCliCommands} from '../env/cli-command.mjs';
92
+ import {
93
+ hostedRun,
94
+ recordResolvedIntegrations,
95
+ } from '../runtime/package-root.mjs';
92
96
  import {
93
97
  InMemoryConfigCache,
94
98
  cacheKey,
@@ -121,9 +125,16 @@ function errorMessage(err) {
121
125
  */
122
126
  export function computerIntegrationCandidates(cwd = process.cwd()) {
123
127
  try {
124
- const cliRoot = isOutsideProject(cwd) ? CLI_ROOT : globalCliRoot(cwd);
128
+ const outside = isOutsideProject(cwd);
129
+ const cliRoot = outside ? CLI_ROOT : globalCliRoot(cwd);
125
130
  return cliRoot
126
- ? scanComputerIntegrations({cliRoot, release: cliVersion()})
131
+ ? scanComputerIntegrations({
132
+ cliRoot,
133
+ release: cliVersion(),
134
+ // Outside a project, the integrations a host approves load from the
135
+ // package set that serves the run too (cli-surface INV30).
136
+ approved: outside ? (hostedRun()?.integrations ?? []) : [],
137
+ })
127
138
  : [];
128
139
  } catch {
129
140
  return [];
@@ -212,7 +223,7 @@ async function loadConfiguredIntegrations(specs, {cwd, fresh}) {
212
223
  /** @type {string|undefined} */
213
224
  let packageDir;
214
225
  try {
215
- packageDir = resolvePackageDir(spec, cwd);
226
+ packageDir = resolveInstalledIntegrationDir(spec, cwd);
216
227
  } catch {
217
228
  // Not a bare package name: there is no install location to record.
218
229
  }
@@ -315,22 +326,6 @@ export const CONFIG_BASENAMES = [
315
326
  export const DEFAULT_ISSUES_URL =
316
327
  'https://github.com/facebook/astryx/issues/new';
317
328
 
318
- /**
319
- * Find the directory of the nearest package.json walking up from startDir.
320
- * @param {string} startDir
321
- * @returns {string|null}
322
- */
323
- function findPackageRoot(startDir) {
324
- let dir = startDir;
325
- for (let i = 0; i < 50; i++) {
326
- if (fs.existsSync(path.join(dir, 'package.json'))) return dir;
327
- const parent = path.dirname(dir);
328
- if (parent === dir) break;
329
- dir = parent;
330
- }
331
- return null;
332
- }
333
-
334
329
  /**
335
330
  * Whether this project accepts `debug` handlers contributed by the
336
331
  * integrations it loads.
@@ -495,7 +490,8 @@ export class Project {
495
490
  // The project root: the config's directory when there is one (findConfigPath
496
491
  // resolves the config as a sibling of the nearest package.json, so the two
497
492
  // agree), otherwise that package.json's directory. Dependencies are declared
498
- // there, and node_modules sits there.
493
+ // there, and installed packages resolve from there up the node_modules
494
+ // chain.
499
495
  const projectDir = configPath
500
496
  ? path.dirname(configPath)
501
497
  : (findPackageRoot(cwd) ?? cwd);
@@ -580,6 +576,8 @@ export class Project {
580
576
  : []),
581
577
  ]);
582
578
  loadedIntegrations = resolution.integrations;
579
+ // A hosted run returns each package it resolved (cli-surface INV30).
580
+ recordResolvedIntegrations(loadedIntegrations);
583
581
  // The picker (getCliInvocation) is synchronous, so the commands these
584
582
  // integrations declare are recorded here, where they become known.
585
583
  recordDeclaredCliCommands(projectDir, loadedIntegrations);
@@ -291,6 +291,32 @@ describe('Project.load', () => {
291
291
  });
292
292
  });
293
293
 
294
+ describe('Project.issues and a near-miss manifest key', () => {
295
+ it.each([
296
+ [
297
+ 'alone, while the loaded integration fills providerId from the package name',
298
+ {providerID: '@acme/widgets'},
299
+ 'Fix: rename "providerID" to "providerId" in astryx.integration.mjs.',
300
+ ],
301
+ [
302
+ 'beside the key it suggests',
303
+ {providerID: '@acme/widgets', providerId: '@acme/widgets'},
304
+ 'Fix: remove "providerID" from astryx.integration.mjs; it already sets "providerId".',
305
+ ],
306
+ ])(
307
+ 'reads the keys the manifest declares: providerID %s',
308
+ async (_label, unknownKeys, fix) => {
309
+ scaffold({unknownKeys});
310
+ const project = await Project.load(tmpDir);
311
+ const keys = (await project.issues()).filter(
312
+ issue => issue.code === 'unknown_manifest_key',
313
+ );
314
+ expect(keys).toHaveLength(1);
315
+ expect(keys[0].message).toContain(fix);
316
+ },
317
+ );
318
+ });
319
+
294
320
  describe('Project.load and the cliCommand an integration declares', () => {
295
321
  it('records it, so the picker prints commands with it', async () => {
296
322
  scaffold({cliCommand: 'acme astryx'});
@@ -830,6 +856,36 @@ describe('Project loads each integration in isolation', () => {
830
856
  });
831
857
  });
832
858
 
859
+ describe('Project finds configured integrations a workspace hoists', () => {
860
+ // npm, Yarn and Bun workspaces install a member's dependencies at the
861
+ // workspace root, so the member's own node_modules does not hold them.
862
+ it('loads an integration named in a member config from the workspace root', async () => {
863
+ scaffold();
864
+ fs.writeFileSync(
865
+ path.join(tmpDir, 'package.json'),
866
+ JSON.stringify({name: 'root', private: true, workspaces: ['apps/*']}),
867
+ );
868
+ fs.rmSync(path.join(tmpDir, 'astryx.config.mjs'));
869
+ const member = path.join(tmpDir, 'apps', 'web');
870
+ fs.mkdirSync(member, {recursive: true});
871
+ fs.writeFileSync(path.join(member, 'package.json'), JSON.stringify({name: 'web'}));
872
+ fs.writeFileSync(
873
+ path.join(member, 'astryx.config.mjs'),
874
+ `export default ${JSON.stringify({integrations: ['@acme/widgets']})};\n`,
875
+ );
876
+
877
+ const project = await Project.load(member);
878
+
879
+ const widgets = (await project.components()).filter(
880
+ component => component.package === '@acme/widgets',
881
+ );
882
+ expect(widgets.map(component => component.name)).toEqual(['Widget']);
883
+ expect(await project.issues()).not.toContainEqual(
884
+ expect.objectContaining({package: '@acme/widgets', code: 'integration_error'}),
885
+ );
886
+ });
887
+ });
888
+
833
889
  describe('Project.issuesUrl routing', () => {
834
890
  it('routes core to config.issuesUrl, falling back to the default', async () => {
835
891
  scaffold({issuesUrl: 'https://example.com/core'});
@@ -83,5 +83,12 @@ export function auditAuthoringSelfDocs({ root, sources, budget, }?: {
83
83
  }>;
84
84
  /** The directory the self-docs live under. */
85
85
  export const AUTHORING_ROOT: string;
86
+ /** The guide that holds every authoring schema, one section each. */
87
+ export const AUTHORING_REFERENCE_NAME: "field-reference";
88
+ /**
89
+ * Its route. `astryx docs authoring <name>` reads one of its sections through
90
+ * the authoring namespace (spec:AST-046 FR6).
91
+ */
92
+ export const AUTHORING_REFERENCE_ROUTE: "authoring/field-reference";
86
93
  /** Every authoring self-doc, relative to {@link AUTHORING_ROOT}, in reading order. */
87
94
  export const AUTHORING_SELF_DOCS: string[];
@@ -9,7 +9,7 @@
9
9
  * `authoring` reference topic with one section per self-doc, and an audit
10
10
  * naming any self-doc the topic cannot reach, any that fails to load, and any
11
11
  * section over the docs output budget.
12
- * @position Read by assets/docs/authoring.doc.mjs (the topic) and by Doctor
12
+ * @position Read by assets/docs/tree/field-reference.doc.mjs (the guide) and by Doctor
13
13
  * (the audit). Lives in foundation because authoring/ holds only contracts.
14
14
  * A new `*.doc.mjs` under authoring/ must be added to the list, or Doctor and
15
15
  * the self-doc tests fail.
@@ -27,6 +27,15 @@ import {
27
27
  /** The directory the self-docs live under. */
28
28
  export const AUTHORING_ROOT = path.join(CLI_ROOT, 'authoring');
29
29
 
30
+ /** The guide that holds every authoring schema, one section each. */
31
+ export const AUTHORING_REFERENCE_NAME = 'field-reference';
32
+
33
+ /**
34
+ * Its route. `astryx docs authoring <name>` reads one of its sections through
35
+ * the authoring namespace (spec:AST-046 FR6).
36
+ */
37
+ export const AUTHORING_REFERENCE_ROUTE = `authoring/${AUTHORING_REFERENCE_NAME}`;
38
+
30
39
  /** Every authoring self-doc, relative to {@link AUTHORING_ROOT}, in reading order. */
31
40
  export const AUTHORING_SELF_DOCS = [
32
41
  'integration/integration.doc.mjs',
@@ -189,11 +198,14 @@ export function selfDocSection(doc) {
189
198
  */
190
199
  export function buildAuthoringReferenceDoc(docs) {
191
200
  return /** @type {any} */ ({
192
- name: 'authoring',
193
- title: 'Authoring Reference',
201
+ type: 'generic',
202
+ name: AUTHORING_REFERENCE_NAME,
203
+ placement: {parent: 'namespace:authoring', slot: 'reference', order: 10},
204
+ title: 'Field reference',
194
205
  category: 'guide',
206
+ keywords: ['authoring reference', 'field reference', 'every field'],
195
207
  description:
196
- 'Every file an integration author writes, field by field: the integration manifest, astryx.config, codemods, identity, and each doc type. The step-by-step guides are under `astryx docs cli/integrations`.',
208
+ 'Every file an integration author writes, field by field: the integration manifest, astryx.config, codemods, identity, and each doc type. The guides that use them are the rest of {@link namespace:authoring}.',
197
209
  sections: docs.map(selfDocSection),
198
210
  });
199
211
  }
@@ -38,20 +38,32 @@ describe('authoring self-docs', () => {
38
38
  });
39
39
  });
40
40
 
41
- it('builds a valid topic with one section per self-doc', async () => {
42
- const topic = await buildAuthoringTopic();
41
+ it('builds a valid guide with one section per self-doc, placed under authoring', async () => {
42
+ const {placement, ...topic} = await buildAuthoringTopic();
43
43
  expect(problemsInTopic(topic)).toEqual([]);
44
44
  expect(topic.sections).toHaveLength(AUTHORING_SELF_DOCS.length);
45
+ expect(placement).toEqual({
46
+ parent: 'namespace:authoring',
47
+ slot: 'reference',
48
+ order: 10,
49
+ });
45
50
  });
46
51
 
47
52
  it(
48
53
  'is readable progressively through the docs API',
49
54
  async () => {
50
- const index = await docs('authoring', undefined, {index: true});
55
+ const index = await docs('authoring/field-reference', undefined, {
56
+ index: true,
57
+ });
51
58
  expect(index.type).toBe('docs.index');
52
59
  expect(index.data.sections.map(s => s.id)).toContain('integration');
60
+ // The namespace answers a section read from the field reference, as
61
+ // `astryx docs authoring <name>` always has.
53
62
  const section = await docs('authoring', 'integration');
54
63
  expect(section.data.title).toBe('Astryx Integration');
64
+ expect(
65
+ (await docs('authoring/field-reference', 'integration')).data,
66
+ ).toEqual(section.data);
55
67
  expect(section.data.content.some(block => block.type === 'table')).toBe(
56
68
  true,
57
69
  );
@@ -30,7 +30,7 @@ export function tracePublicAuthoringTypes(root?: string): {
30
30
  * document something public: a public type declared beside it, and, when its
31
31
  * title is the name of a type declared beside it, that type.
32
32
  * @param {{root?: string, sources?: string[], topicKeys?: Set<string> | null}} [options]
33
- * `topicKeys` are the keys `astryx docs authoring --index` lists; null skips
33
+ * `topicKeys` are the keys `astryx docs authoring/field-reference --index` lists; null skips
34
34
  * that comparison.
35
35
  * @returns {Promise<{
36
36
  * types: number,
@@ -7,7 +7,7 @@
7
7
  * @input authoring/index.d.ts and the type modules it re-exports, parsed with
8
8
  * jscodeshift and never imported or type-checked (consumer installs have no
9
9
  * TypeScript); the self-docs on disk and in AUTHORING_SELF_DOCS; and the
10
- * section keys `astryx docs authoring --index` lists.
10
+ * section keys `astryx docs authoring/field-reference --index` lists.
11
11
  * @output {@link tracePublicAuthoringTypes}: each exported type and the module
12
12
  * that declares it. {@link auditAuthoringSurface}: each module whose exported
13
13
  * types a reader cannot reach and why, each export that cannot be traced,
@@ -424,7 +424,7 @@ function declaredIn(t, folder, name) {
424
424
  * document something public: a public type declared beside it, and, when its
425
425
  * title is the name of a type declared beside it, that type.
426
426
  * @param {{root?: string, sources?: string[], topicKeys?: Set<string> | null}} [options]
427
- * `topicKeys` are the keys `astryx docs authoring --index` lists; null skips
427
+ * `topicKeys` are the keys `astryx docs authoring/field-reference --index` lists; null skips
428
428
  * that comparison.
429
429
  * @returns {Promise<{
430
430
  * types: number,
@@ -86,7 +86,7 @@ describe('the public authoring surface of this repo', () => {
86
86
  it(
87
87
  'audits clean: every exported type has a readable doc and every self-doc documents one',
88
88
  async () => {
89
- const index = await docs('authoring', undefined, {index: true});
89
+ const index = await docs('authoring/field-reference', undefined, {index: true});
90
90
  const topicKeys = new Set(
91
91
  index.data.sections.map((/** @type {any} */ s) => s.id),
92
92
  );
@@ -102,7 +102,8 @@ export const CLI_SELF_DOC_DIRS: string[];
102
102
  /**
103
103
  * Every namespace a CLI doc may declare, and what reads it. The docs tree
104
104
  * adopts `cli/commands` (under `cli/commands`) and `cli/api` (under
105
- * `cli/api/<kind>s`); `astryx docs authoring` reads `authoring` from its list.
105
+ * `cli/api/<kind>s`); the field reference (`astryx docs authoring/field-reference`)
106
+ * reads `authoring` from its list.
106
107
  * @type {Record<string, {reader: 'tree' | 'authoring'}>}
107
108
  */
108
109
  export const CLI_DOC_NAMESPACES: Record<string, {
@@ -41,7 +41,8 @@ const NAMESPACED_KINDS = new Set(['command', 'function', 'schema', 'enum']);
41
41
  /**
42
42
  * Every namespace a CLI doc may declare, and what reads it. The docs tree
43
43
  * adopts `cli/commands` (under `cli/commands`) and `cli/api` (under
44
- * `cli/api/<kind>s`); `astryx docs authoring` reads `authoring` from its list.
44
+ * `cli/api/<kind>s`); the field reference (`astryx docs authoring/field-reference`)
45
+ * reads `authoring` from its list.
45
46
  * @type {Record<string, {reader: 'tree' | 'authoring'}>}
46
47
  */
47
48
  export const CLI_DOC_NAMESPACES = {
@@ -492,13 +493,13 @@ export async function auditCliSelfDocs({
492
493
  else {
493
494
  misfiled.push({
494
495
  source,
495
- message: `${source} has namespace "authoring", but \`${formatCliCommand('docs authoring')}\` does not list it`,
496
+ message: `${source} has namespace "authoring", but \`${formatCliCommand('docs authoring/field-reference')}\` does not list it`,
496
497
  });
497
498
  }
498
499
  } else if (inAuthoring) {
499
500
  misfiled.push({
500
501
  source,
501
- message: `${source} is read in \`${formatCliCommand('docs authoring')}\`, but its namespace is "${namespace}"`,
502
+ message: `${source} is read in \`${formatCliCommand('docs authoring/field-reference')}\`, but its namespace is "${namespace}"`,
502
503
  });
503
504
  } else {
504
505
  treeDocs.push(doc);
@@ -146,8 +146,8 @@ describe('auditCliSelfDocs', () => {
146
146
  });
147
147
  const result = await audit(root, ['y/y.doc.mjs', 'z/z.doc.mjs']);
148
148
  expect(result.misfiled.map(entry => entry.message)).toEqual([
149
- `authoring/x/x.doc.mjs has namespace "authoring", but \`${formatCliCommand('docs authoring')}\` does not list it`,
150
- `authoring/y/y.doc.mjs is read in \`${formatCliCommand('docs authoring')}\`, but its namespace is "cli/api"`,
149
+ `authoring/x/x.doc.mjs has namespace "authoring", but \`${formatCliCommand('docs authoring/field-reference')}\` does not list it`,
150
+ `authoring/y/y.doc.mjs is read in \`${formatCliCommand('docs authoring/field-reference')}\`, but its namespace is "cli/api"`,
151
151
  ]);
152
152
  expect(result.authoring).toBe(1);
153
153
  expect(result.tree).toBe(0);
@@ -1,6 +1,13 @@
1
1
  // @generated by scripts/sync-api-types.mjs from the JSDoc in foundation/**/*.mjs.
2
2
  // DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
3
3
 
4
+ /**
5
+ * Whether a component doc is hidden (`hidden: true`), read as discovery
6
+ * reads it: discovery skips a hidden doc.
7
+ * @param {string} docPath
8
+ * @returns {boolean}
9
+ */
10
+ export function isHiddenComponentDoc(docPath: string): boolean;
4
11
  /**
5
12
  * The entry a family doc gives `name` in its `components`, when the doc is not
6
13
  * the component's own (Stack's doc for VStack); null otherwise.
@@ -117,6 +124,36 @@ export function discoverExternalComponentsGrouped(docsDir: string): Record<strin
117
124
  export function findExternalComponentDoc(docsDir: string, name: string): string | null;
118
125
  /** @param {string} docPath */
119
126
  export function docStem(docPath: string): string;
127
+ /**
128
+ * The local module a doc's import names: the doc's own `import`, else the
129
+ * subpath its directory publishes, read through the package's `exports` (its
130
+ * `main` when it has none). Undefined when the package directory is unknown;
131
+ * null when the import names another package or resolves to no file.
132
+ *
133
+ * @param {{name?: string, __packageDir?: string, __packageExports?: Record<string, unknown>|null}} integration
134
+ * @param {string} docPath
135
+ * @param {Record<string, any> | null} doc
136
+ * @returns {string | null | undefined}
137
+ */
138
+ export function importedModule(integration: {
139
+ name?: string;
140
+ __packageDir?: string;
141
+ __packageExports?: Record<string, unknown> | null;
142
+ }, docPath: string, doc: Record<string, any> | null): string | null | undefined;
143
+ /**
144
+ * The file an import of the package's own name resolves to, through its
145
+ * package.json `exports` (or `main` when it has none): `@acme/kit` or
146
+ * `@acme/kit/widget`. Null when the specifier names another package, or the
147
+ * package publishes nothing at it.
148
+ * @param {{name?: string, __packageDir?: string, __packageExports?: Record<string, unknown> | null}} integration
149
+ * @param {string} specifier
150
+ * @returns {string | null}
151
+ */
152
+ export function ownPackageModule(integration: {
153
+ name?: string;
154
+ __packageDir?: string;
155
+ __packageExports?: Record<string, unknown> | null;
156
+ }, specifier: string): string | null;
120
157
  /**
121
158
  * The `.tsx` file in `folder`, or below it, that declares `name`, taking files
122
159
  * in path order: the source of a member whose import never reaches a `.tsx`
@@ -118,6 +118,16 @@ function scanDocText(text) {
118
118
  };
119
119
  }
120
120
 
121
+ /**
122
+ * Whether a component doc is hidden (`hidden: true`), read as discovery
123
+ * reads it: discovery skips a hidden doc.
124
+ * @param {string} docPath
125
+ * @returns {boolean}
126
+ */
127
+ export function isHiddenComponentDoc(docPath) {
128
+ return readDocMeta(docPath).hidden;
129
+ }
130
+
121
131
  /**
122
132
  * Read the `group`, `hiddenComponents`, and `hidden` fields from a
123
133
  * component's .doc.mjs file (synchronous), with what {@link scanDocText}
@@ -994,7 +1004,7 @@ function packageMain(packageDir) {
994
1004
  * @param {Record<string, any> | null} doc
995
1005
  * @returns {string | null | undefined}
996
1006
  */
997
- function importedModule(integration, docPath, doc) {
1007
+ export function importedModule(integration, docPath, doc) {
998
1008
  const packageName = integration?.name;
999
1009
  const packageDir = integration?.__packageDir;
1000
1010
  if (!packageName || !packageDir) return undefined;
@@ -1024,6 +1034,42 @@ function importedModule(integration, docPath, doc) {
1024
1034
  ).target;
1025
1035
  }
1026
1036
 
1037
+ /**
1038
+ * The file an import of the package's own name resolves to, through its
1039
+ * package.json `exports` (or `main` when it has none): `@acme/kit` or
1040
+ * `@acme/kit/widget`. Null when the specifier names another package, or the
1041
+ * package publishes nothing at it.
1042
+ * @param {{name?: string, __packageDir?: string, __packageExports?: Record<string, unknown> | null}} integration
1043
+ * @param {string} specifier
1044
+ * @returns {string | null}
1045
+ */
1046
+ export function ownPackageModule(integration, specifier) {
1047
+ const packageName = integration?.name;
1048
+ const packageDir = integration?.__packageDir;
1049
+ if (!packageName || !packageDir) return null;
1050
+ if (specifier !== packageName && !specifier.startsWith(`${packageName}/`)) {
1051
+ return null;
1052
+ }
1053
+ const exportsMap =
1054
+ integration.__packageExports === undefined
1055
+ ? readPackageExports(packageDir)
1056
+ : integration.__packageExports;
1057
+ if (specifier === packageName && exportsMap == null) {
1058
+ return packageMain(packageDir) ?? null;
1059
+ }
1060
+ const subpath =
1061
+ specifier === packageName
1062
+ ? '.'
1063
+ : `./${specifier.slice(packageName.length + 1)}`;
1064
+ return (
1065
+ inspectPackageExport(
1066
+ {name: packageName, exports: exportsMap ?? undefined},
1067
+ packageDir,
1068
+ subpath,
1069
+ ).target ?? null
1070
+ );
1071
+ }
1072
+
1027
1073
  /**
1028
1074
  * Whether `file` sits in `folder` or below it.
1029
1075
  * @param {string} folder
@@ -1211,6 +1257,19 @@ export function integrationMemberEntry(doc, record) {
1211
1257
  );
1212
1258
  }
1213
1259
 
1260
+ /**
1261
+ * Whether `dir` is a folder to list. A declared root that is a file is
1262
+ * reported by the root check; discovery finds nothing in it.
1263
+ * @param {string} dir
1264
+ */
1265
+ function isFolder(dir) {
1266
+ try {
1267
+ return fs.statSync(dir).isDirectory();
1268
+ } catch {
1269
+ return false;
1270
+ }
1271
+ }
1272
+
1214
1273
  /**
1215
1274
  * Visit every component doc under an integration's components root, hidden
1216
1275
  * ones included, in discovery order, with its stem.
@@ -1218,10 +1277,26 @@ export function integrationMemberEntry(doc, record) {
1218
1277
  * @param {(docPath: string, stem: string) => void} visit
1219
1278
  */
1220
1279
  export function forEachIntegrationDoc(componentsDir, visit) {
1221
- if (!componentsDir || !pathExists(componentsDir)) return;
1280
+ if (!componentsDir || !isFolder(componentsDir)) return;
1281
+ /** @param {string} name */
1282
+ const key = name => {
1283
+ const rank = INTEGRATION_DOC_SUFFIXES.findIndex(s => name.endsWith(s));
1284
+ return rank < 0
1285
+ ? {stem: name, rank}
1286
+ : {stem: name.slice(0, -INTEGRATION_DOC_SUFFIXES[rank].length), rank};
1287
+ };
1222
1288
  /** @param {string} dirPath */
1223
1289
  function scanDir(dirPath) {
1224
- for (const entry of listDirEntries(dirPath)) {
1290
+ // Name order, and for two docs of one stem the suffix precedence, so the
1291
+ // doc a stem records is the one a lookup by name finds, on every disk.
1292
+ const entries = listDirEntries(dirPath).sort((a, b) => {
1293
+ const left = key(a.name);
1294
+ const right = key(b.name);
1295
+ return left.stem === right.stem
1296
+ ? left.rank - right.rank
1297
+ : byCodeUnit(left.stem, right.stem);
1298
+ });
1299
+ for (const entry of entries) {
1225
1300
  if (entry.name === 'node_modules' || entry.name === '__tests__') continue;
1226
1301
  const fullPath = path.join(dirPath, entry.name);
1227
1302
  if (entry.isDirectory()) {
@@ -1270,7 +1345,7 @@ export function discoverIntegrationComponents(
1270
1345
  {documentedNames = true} = {},
1271
1346
  ) {
1272
1347
  const componentsDir = integration?.components;
1273
- if (!componentsDir || !pathExists(componentsDir)) return [];
1348
+ if (!componentsDir || !isFolder(componentsDir)) return [];
1274
1349
 
1275
1350
  /** @type {Map<string, {name: string, package: string, docPath: string, sourcePath: string|null, issuesUrl: string|undefined, group: string|null}>} */
1276
1351
  const byName = new Map();
@@ -1485,7 +1560,7 @@ function findDocumentedComponent(integration, componentsDir, name) {
1485
1560
  */
1486
1561
  export function findIntegrationComponent(integration, name) {
1487
1562
  const componentsDir = integration?.components;
1488
- if (!componentsDir || !pathExists(componentsDir)) return null;
1563
+ if (!componentsDir || !isFolder(componentsDir)) return null;
1489
1564
  const sameStem = findSameStemDoc(componentsDir, name);
1490
1565
  if (sameStem) {
1491
1566
  const exportName = otherExportName({
@@ -43,5 +43,5 @@ export type LoadDocOptions = {
43
43
  * which root reads the doc
44
44
  * (default: components)
45
45
  */
46
- root?: "hooks" | "components" | undefined;
46
+ root?: "components" | "hooks" | undefined;
47
47
  };
@@ -423,6 +423,19 @@ export function problemsInNamespace(doc) {
423
423
  return problems;
424
424
  }
425
425
 
426
+ /**
427
+ * Whether `dir` is a folder to list. A declared root that is a file is
428
+ * reported by the root check; discovery finds nothing in it.
429
+ * @param {string} dir
430
+ */
431
+ function isFolder(dir) {
432
+ try {
433
+ return fs.statSync(dir).isDirectory();
434
+ } catch {
435
+ return false;
436
+ }
437
+ }
438
+
426
439
  /**
427
440
  * Discover the topics contributed by a single loaded integration. Mirrors
428
441
  * `discoverIntegrationComponents`: walk the resolved root, take every
@@ -450,7 +463,7 @@ export async function discoverIntegrationDocs(integration) {
450
463
  const namespaces = [];
451
464
  /** @type {import('../doc-compiler/tree.mjs').TreeDocInput[]} */
452
465
  const guides = [];
453
- if (!docsDir || !fs.existsSync(docsDir)) {
466
+ if (!docsDir || !isFolder(docsDir)) {
454
467
  return {records, errors, namespaces, guides};
455
468
  }
456
469
  const providerId = integration.providerId ?? integration.name;
@@ -18,6 +18,39 @@ export function moduleExportsName(file: string, exportName: string, seen?: Set<s
18
18
  * @returns {string | null}
19
19
  */
20
20
  export function moduleExportSource(file: string, exportName: string, seen?: Set<string>): string | null;
21
+ /**
22
+ * Every runtime name a module exports that this read can see (`default` for
23
+ * a default export), or null when it cannot see them all: an `export *`, `export =`, CommonJS exports, or a
24
+ * file that does not parse.
25
+ * @param {string} file
26
+ * @returns {string[] | null}
27
+ */
28
+ export function moduleExportNames(file: string): string[] | null;
29
+ /**
30
+ * @typedef {{verdict: 'exports'} | {verdict: 'lacks'} | {verdict: 'unknown', reason: string}} ExportVerdict
31
+ */
32
+ /**
33
+ * Whether a module exports `exportName` at runtime, read without executing
34
+ * it: `exports` when a declaration or a followed re-export binds it, `lacks`
35
+ * only when every export the module writes was read and none binds it, and
36
+ * `unknown`, with the first reason, when some export could not be read: a
37
+ * re-export from another package or from a specifier that resolves to no
38
+ * file, a destructured binding, a namespace, an ambient or const enum
39
+ * declaration, `export =`, CommonJS exports, or a file that does not parse.
40
+ * @param {string} file
41
+ * @param {string} exportName
42
+ * @param {Set<string>} [seen]
43
+ * @returns {ExportVerdict}
44
+ */
45
+ export function moduleExportVerdict(file: string, exportName: string, seen?: Set<string>): ExportVerdict;
46
+ /**
47
+ * Whether a module provably does not export `exportName` at runtime: see
48
+ * {@link moduleExportVerdict}.
49
+ * @param {string} file
50
+ * @param {string} exportName
51
+ * @returns {boolean}
52
+ */
53
+ export function moduleLacksExport(file: string, exportName: string): boolean;
21
54
  /**
22
55
  * The object a module exports, as plain data, read without running it: the
23
56
  * first of `exportNames` it exports, an object literal in place or a top-level
@@ -35,3 +68,11 @@ export function staticExportObject(file: string, exportNames: readonly string[])
35
68
  * enumerable, so the object compares and serializes as its literal properties.
36
69
  */
37
70
  export const HAS_COMPUTED: unique symbol;
71
+ export type ExportVerdict = {
72
+ verdict: "exports";
73
+ } | {
74
+ verdict: "lacks";
75
+ } | {
76
+ verdict: "unknown";
77
+ reason: string;
78
+ };