@astryxdesign/cli 0.6.3-canary.db4e378 → 0.6.3-canary.db7b338

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 (276) hide show
  1. package/README.md +22 -1
  2. package/api/blog/blog.doc.mjs +1 -0
  3. package/api/build/build.doc.mjs +1 -0
  4. package/api/component/component.doc.mjs +1 -0
  5. package/api/discover/discover.doc.mjs +1 -0
  6. package/api/docs/_adapter.d.mts +3 -8
  7. package/api/docs/_adapter.mjs +13 -105
  8. package/api/docs/detail/section/section.mjs +9 -15
  9. package/api/docs/detail/section/section.test.mjs +15 -6
  10. package/api/docs/docs.doc.mjs +1 -0
  11. package/api/docs/list/list.mjs +3 -7
  12. package/api/doctor/doctor.d.mts +25 -3
  13. package/api/doctor/doctor.doc.mjs +1 -0
  14. package/api/doctor/doctor.mjs +186 -14
  15. package/api/doctor/doctor.test.mjs +332 -7
  16. package/api/gap-report/gap-report.doc.mjs +1 -0
  17. package/api/hook/_adapter.mjs +19 -5
  18. package/api/hook/hook.doc.mjs +1 -0
  19. package/api/hook/list/list.d.mts +1 -1
  20. package/api/hook/list/list.mjs +69 -17
  21. package/api/init/init.doc.mjs +1 -0
  22. package/api/integration/add-contribution.mjs +7 -7
  23. package/api/integration/add-contribution.test.mjs +37 -3
  24. package/api/integration/add-theme.mjs +34 -64
  25. package/api/integration/add-theme.test.mjs +105 -21
  26. package/api/integration/authoring-checks.test.mjs +8 -4
  27. package/api/integration/integration-block-exports.test.mjs +10 -6
  28. package/api/integration/integrationAdd.doc.mjs +1 -0
  29. package/api/integration/integrationAddAgentDoc.doc.mjs +1 -0
  30. package/api/integration/integrationAddCodemod.doc.mjs +1 -0
  31. package/api/integration/integrationAddComponent.doc.mjs +1 -0
  32. package/api/integration/integrationAddDoc.doc.mjs +1 -0
  33. package/api/integration/integrationAddTemplate.doc.mjs +1 -0
  34. package/api/integration/integrationAddTheme.doc.mjs +6 -5
  35. package/api/integration/integrationComponentConflicts.doc.mjs +1 -0
  36. package/api/integration/integrationDocConflicts.doc.mjs +1 -0
  37. package/api/integration/integrationPackCheck.doc.mjs +1 -0
  38. package/api/integration/integrationTemplateConflicts.doc.mjs +1 -0
  39. package/api/integration/pack-check.test.mjs +17 -47
  40. package/api/integration/summarizeIssues.doc.mjs +1 -0
  41. package/api/integration/validate-integration-fixes.test.mjs +1389 -0
  42. package/api/integration/validate-integration.mjs +50 -102
  43. package/api/integration/validate-integration.test.mjs +85 -23
  44. package/api/integration/validate-unread-theme-folders.test.mjs +110 -0
  45. package/api/integration/validateIntegration.doc.mjs +1 -0
  46. package/api/json/assertResponse.doc.mjs +1 -0
  47. package/api/json/isError.doc.mjs +1 -0
  48. package/api/json/parseResponse.doc.mjs +1 -0
  49. package/api/layout/layoutCheck.doc.mjs +1 -0
  50. package/api/layout/layoutExpand.doc.mjs +1 -0
  51. package/api/layout/layoutGrammar.doc.mjs +1 -0
  52. package/api/search/search.doc.mjs +1 -0
  53. package/api/search/search.mjs +16 -6
  54. package/api/swizzle/swizzle.doc.mjs +1 -0
  55. package/api/template/template-suffix.test.mjs +41 -21
  56. package/api/template/template.doc.mjs +1 -0
  57. package/api/theme/_adapter.d.mts +2 -3
  58. package/api/theme/_adapter.mjs +4 -5
  59. package/api/theme/add/add.binary.test.mjs +10 -17
  60. package/api/theme/add/add.test.mjs +14 -1
  61. package/api/theme/generateTonalPalette.doc.mjs +1 -0
  62. package/api/theme/integration-themes.test.mjs +39 -28
  63. package/api/theme/list/list.test.mjs +19 -20
  64. package/api/theme/listThemes.doc.mjs +6 -5
  65. package/api/theme/themeAdd.doc.mjs +4 -3
  66. package/api/theme/themeBuild.doc.mjs +1 -0
  67. package/api/theme/themeList.doc.mjs +6 -3
  68. package/api/theme/themeListAvailable.doc.mjs +5 -3
  69. package/api/theme/themePaletteGenerate.doc.mjs +1 -0
  70. package/api/theme/themeTargets.doc.mjs +1 -0
  71. package/api/theme/themeTemplate.doc.mjs +1 -0
  72. package/api/upgrade/_adapter.d.mts +32 -5
  73. package/api/upgrade/_adapter.mjs +97 -69
  74. package/api/upgrade/provider-agreement.test.mjs +152 -0
  75. package/api/upgrade/run/run.mjs +356 -59
  76. package/api/upgrade/upgrade.doc.mjs +6 -1
  77. package/api/upgrade/upgrade.type.d.mts +34 -0
  78. package/api/upgrade/upgrade.type.mjs +15 -0
  79. package/assets/codemods/__tests__/runner.test.mjs +330 -8
  80. package/assets/codemods/integration-discovery.mjs +8 -2
  81. package/assets/codemods/integration-discovery.test.mjs +15 -0
  82. package/assets/codemods/integration-runner.mjs +56 -4
  83. package/assets/codemods/integration-runner.protection.test.mjs +153 -0
  84. package/assets/codemods/run-codemod.mjs +177 -34
  85. package/assets/codemods/runner.mjs +350 -102
  86. package/assets/codemods/transforms/next/__tests__/migrate-native-picker-to-presentation.test.mjs +63 -0
  87. package/assets/codemods/transforms/next/__tests__/migrate-theme-catalog-to-descriptors.test.mjs +220 -0
  88. package/assets/codemods/transforms/next/index.mjs +19 -1
  89. package/assets/codemods/transforms/next/migrate-native-picker-to-presentation.mjs +148 -0
  90. package/assets/codemods/transforms/next/migrate-theme-catalog-to-descriptors.mjs +141 -0
  91. package/assets/docs/cli-integrations.doc.mjs +30 -30
  92. package/assets/docs/cli.doc.mjs +15 -0
  93. package/assets/docs/theme.doc.mjs +1 -1
  94. package/assets/templates/blocks/components/DateInput/DateInputDateRange.tsx +1 -1
  95. package/assets/templates/blocks/components/Item/ItemDocumentTabs.doc.mjs +14 -0
  96. package/assets/templates/blocks/components/Item/ItemDocumentTabs.tsx +100 -0
  97. package/assets/templates/blocks/components/TimeInput/TimeInputConstrained.tsx +1 -0
  98. package/assets/templates/themes/butter/butterTheme.doc.mjs +11 -0
  99. package/assets/templates/themes/chocolate/chocolateTheme.doc.mjs +11 -0
  100. package/assets/templates/themes/gothic/gothicTheme.doc.mjs +11 -0
  101. package/assets/templates/themes/matcha/matchaTheme.doc.mjs +11 -0
  102. package/assets/templates/themes/neutral/neutralTheme.doc.mjs +11 -0
  103. package/assets/templates/themes/stone/stoneTheme.doc.mjs +11 -0
  104. package/assets/templates/themes/y2k/y2kTheme.doc.mjs +11 -0
  105. package/authoring/codemod/codemod.doc.mjs +1 -1
  106. package/authoring/codemod/type.ts +12 -0
  107. package/authoring/config/config.doc.mjs +1 -1
  108. package/authoring/debug/debug.doc.d.mts +11 -0
  109. package/authoring/debug/debug.doc.mjs +182 -0
  110. package/authoring/doctypes/_schema.d.mts +71 -141
  111. package/authoring/doctypes/_schema.mjs +29 -2
  112. package/authoring/doctypes/base/graph-fields.doc.mjs +1 -1
  113. package/authoring/doctypes/command/command.doc.mjs +1 -1
  114. package/authoring/doctypes/command/type.ts +1 -1
  115. package/authoring/doctypes/component/type.ts +2 -2
  116. package/authoring/doctypes/doctypes-new.test.mjs +48 -6
  117. package/authoring/doctypes/enum/enum.doc.mjs +1 -1
  118. package/authoring/doctypes/enum/type.ts +1 -1
  119. package/authoring/doctypes/function/function.doc.mjs +1 -1
  120. package/authoring/doctypes/function/type.ts +1 -1
  121. package/authoring/doctypes/hook/type.ts +2 -2
  122. package/authoring/doctypes/load-contract.test.mjs +3 -2
  123. package/authoring/doctypes/namespace/namespace.doc.mjs +2 -2
  124. package/authoring/doctypes/namespace/parse.test.mjs +23 -25
  125. package/authoring/doctypes/namespace/type.ts +5 -2
  126. package/authoring/doctypes/parse.d.mts +4 -2
  127. package/authoring/doctypes/parse.mjs +10 -5
  128. package/authoring/doctypes/reference/reference.doc.mjs +6 -4
  129. package/authoring/doctypes/reference/type.ts +9 -10
  130. package/authoring/doctypes/schema/schema.doc.mjs +1 -1
  131. package/authoring/doctypes/schema/type.ts +1 -2
  132. package/authoring/doctypes/template/template.doc.mjs +3 -3
  133. package/authoring/doctypes/theme/parse.d.mts +35 -0
  134. package/authoring/doctypes/theme/parse.mjs +76 -0
  135. package/authoring/doctypes/theme/theme.doc.d.mts +9 -0
  136. package/authoring/doctypes/theme/theme.doc.mjs +79 -0
  137. package/authoring/doctypes/theme/type.ts +42 -0
  138. package/authoring/doctypes/types.ts +2 -1
  139. package/authoring/gap-report/gap-report.doc.d.mts +12 -0
  140. package/authoring/gap-report/gap-report.doc.mjs +183 -0
  141. package/authoring/index.d.mts +1 -0
  142. package/authoring/index.d.ts +7 -4
  143. package/authoring/index.mjs +2 -1
  144. package/authoring/integration/integration.doc.mjs +2 -2
  145. package/authoring/integration/type.ts +3 -9
  146. package/clients/cli/__tests__/cliManifest.test.ts +27 -29
  147. package/clients/cli/commands/blog.doc.mjs +1 -1
  148. package/clients/cli/commands/build-theme.adaptations.test.mjs +100 -1
  149. package/clients/cli/commands/build-theme.mjs +10 -44
  150. package/clients/cli/commands/build.doc.mjs +1 -1
  151. package/clients/cli/commands/component.doc.mjs +1 -1
  152. package/clients/cli/commands/discover.doc.mjs +1 -1
  153. package/clients/cli/commands/docs.doc.mjs +1 -1
  154. package/clients/cli/commands/docs.mjs +15 -58
  155. package/clients/cli/commands/docs.test.mjs +11 -14
  156. package/clients/cli/commands/doctor-integration-components.doc.mjs +1 -1
  157. package/clients/cli/commands/doctor-integration-docs.doc.mjs +1 -1
  158. package/clients/cli/commands/doctor-integration-templates.doc.mjs +1 -1
  159. package/clients/cli/commands/doctor-integration-validate.doc.mjs +1 -1
  160. package/clients/cli/commands/doctor-integration.doc.mjs +1 -1
  161. package/clients/cli/commands/doctor-integration.test.mjs +15 -7
  162. package/clients/cli/commands/doctor.doc.mjs +1 -1
  163. package/clients/cli/commands/gap-report.doc.mjs +1 -1
  164. package/clients/cli/commands/hook.doc.mjs +1 -1
  165. package/clients/cli/commands/init.doc.mjs +1 -1
  166. package/clients/cli/commands/integration-add.controls.test.mjs +1 -1
  167. package/clients/cli/commands/integration-add.doc.mjs +1 -1
  168. package/clients/cli/commands/integration-pack.doc.mjs +1 -1
  169. package/clients/cli/commands/integration-real-world.test.mjs +3 -9
  170. package/clients/cli/commands/integration.doc.mjs +1 -1
  171. package/clients/cli/commands/layout-check.doc.mjs +1 -1
  172. package/clients/cli/commands/layout-expand.doc.mjs +1 -1
  173. package/clients/cli/commands/layout-grammar.doc.mjs +1 -1
  174. package/clients/cli/commands/layout.doc.mjs +1 -1
  175. package/clients/cli/commands/manifest.doc.mjs +1 -1
  176. package/clients/cli/commands/search.doc.mjs +1 -1
  177. package/clients/cli/commands/swizzle.doc.mjs +1 -1
  178. package/clients/cli/commands/template.doc.mjs +1 -1
  179. package/clients/cli/commands/text-json-parity.test.mjs +718 -0
  180. package/clients/cli/commands/theme-add.doc.mjs +2 -2
  181. package/clients/cli/commands/theme-build.doc.mjs +1 -1
  182. package/clients/cli/commands/theme-list.doc.mjs +2 -2
  183. package/clients/cli/commands/theme-palette-generate.doc.mjs +3 -3
  184. package/clients/cli/commands/theme-palette.doc.mjs +1 -1
  185. package/clients/cli/commands/theme-targets.behavior.test.mjs +4 -3
  186. package/clients/cli/commands/theme-targets.doc.mjs +1 -1
  187. package/clients/cli/commands/theme-template.doc.mjs +1 -1
  188. package/clients/cli/commands/theme.doc.mjs +1 -1
  189. package/clients/cli/commands/upgrade.doc.mjs +3 -2
  190. package/clients/cli/commands/upgrade.file-protection.test.mjs +228 -0
  191. package/clients/cli/commands/upgrade.mjs +29 -7
  192. package/clients/cli/formatters/index.mjs +2 -0
  193. package/clients/cli/formatters/index.test.mjs +6 -0
  194. package/clients/cli/lib/hook-format.mjs +14 -5
  195. package/foundation/config/project-themes.test.mjs +11 -19
  196. package/foundation/config/project.d.mts +8 -0
  197. package/foundation/config/project.mjs +50 -21
  198. package/foundation/config/project.test.mjs +3 -16
  199. package/foundation/discovery/authoring-self-docs.d.mts +6 -0
  200. package/foundation/discovery/authoring-self-docs.mjs +17 -7
  201. package/foundation/discovery/authoring-self-docs.test.mjs +3 -2
  202. package/foundation/discovery/authoring-surface.d.mts +74 -0
  203. package/foundation/discovery/authoring-surface.mjs +525 -0
  204. package/foundation/discovery/authoring-surface.test.mjs +392 -0
  205. package/foundation/discovery/cli-self-docs.d.mts +113 -0
  206. package/foundation/discovery/cli-self-docs.mjs +514 -0
  207. package/foundation/discovery/cli-self-docs.test.mjs +437 -0
  208. package/foundation/discovery/component-loader.d.mts +35 -38
  209. package/foundation/discovery/component-loader.mjs +53 -222
  210. package/foundation/discovery/docs-discovery.d.mts +3 -2
  211. package/foundation/discovery/docs-discovery.mjs +14 -17
  212. package/foundation/discovery/docs-discovery.test.mjs +15 -15
  213. package/foundation/discovery/docs-section-key.d.mts +19 -8
  214. package/foundation/discovery/docs-section-key.mjs +119 -32
  215. package/foundation/discovery/docs-section-key.test.mjs +41 -19
  216. package/foundation/discovery/template-adapter.d.mts +7 -0
  217. package/foundation/discovery/template-adapter.mjs +27 -40
  218. package/foundation/discovery/template-adapter.test.mjs +15 -0
  219. package/foundation/discovery/theme-discovery.d.mts +67 -7
  220. package/foundation/discovery/theme-discovery.mjs +916 -186
  221. package/foundation/discovery/theme-discovery.test.mjs +613 -219
  222. package/foundation/doc-compiler/bundle.d.mts +47 -0
  223. package/foundation/doc-compiler/bundle.mjs +218 -0
  224. package/foundation/doc-compiler/bundle.test.mjs +255 -0
  225. package/foundation/doc-compiler/compile.d.mts +200 -0
  226. package/foundation/doc-compiler/compile.mjs +259 -9
  227. package/foundation/doc-compiler/diagnostics.d.mts +126 -0
  228. package/foundation/doc-compiler/diagnostics.mjs +277 -0
  229. package/foundation/doc-compiler/doc-compiler.test.mjs +28 -1
  230. package/foundation/doc-compiler/doc-loads.test.mjs +1642 -0
  231. package/foundation/doc-compiler/import.d.mts +24 -0
  232. package/foundation/doc-compiler/import.mjs +59 -0
  233. package/foundation/doc-compiler/inputs.d.mts +102 -0
  234. package/foundation/doc-compiler/inputs.mjs +286 -0
  235. package/foundation/doc-compiler/inputs.test.mjs +299 -0
  236. package/foundation/doc-compiler/ir.d.mts +13 -0
  237. package/foundation/doc-compiler/ir.mjs +189 -12
  238. package/foundation/doc-compiler/lower-doc.test.mjs +492 -0
  239. package/foundation/doc-compiler/overlays.d.mts +37 -0
  240. package/foundation/doc-compiler/overlays.mjs +206 -0
  241. package/foundation/doc-compiler/parse-readable.d.mts +9 -0
  242. package/foundation/doc-compiler/parse-readable.mjs +29 -0
  243. package/foundation/doc-compiler/read.d.mts +126 -0
  244. package/foundation/doc-compiler/read.mjs +320 -0
  245. package/foundation/doc-compiler/read.test.mjs +313 -0
  246. package/foundation/doc-compiler/source.d.mts +33 -0
  247. package/foundation/doc-compiler/source.mjs +128 -0
  248. package/foundation/fs/file-protection.d.mts +33 -0
  249. package/foundation/fs/file-protection.mjs +825 -0
  250. package/foundation/fs/file-protection.test.mjs +250 -0
  251. package/foundation/integrations/autolink.d.mts +58 -1
  252. package/foundation/integrations/autolink.mjs +143 -57
  253. package/foundation/integrations/contribution-fixes.d.mts +145 -0
  254. package/foundation/integrations/contribution-fixes.mjs +1284 -0
  255. package/foundation/integrations/contribution-inventory.d.mts +1 -1
  256. package/foundation/integrations/contribution-inventory.mjs +17 -20
  257. package/foundation/integrations/contribution-inventory.test.mjs +67 -27
  258. package/foundation/integrations/integrations.d.mts +3 -0
  259. package/foundation/integrations/integrations.mjs +11 -97
  260. package/foundation/integrations/provider-ledger.test.mjs +275 -0
  261. package/foundation/integrations/provider-resolution.d.mts +152 -0
  262. package/foundation/integrations/provider-resolution.mjs +576 -0
  263. package/foundation/integrations/provider-resolution.test.mjs +369 -0
  264. package/foundation/integrations/theme-descriptor.d.mts +8 -0
  265. package/foundation/integrations/theme-descriptor.mjs +44 -0
  266. package/foundation/integrations/validate-contributions.mjs +104 -10
  267. package/foundation/response/error-codes.d.mts +3 -1
  268. package/foundation/response/error-codes.d.ts +2 -0
  269. package/foundation/response/error-codes.doc.mjs +12 -2
  270. package/foundation/response/error-codes.mjs +7 -1
  271. package/foundation/response/error-codes.test.mjs +55 -11
  272. package/foundation/response/response-types.doc.mjs +1 -1
  273. package/foundation/response/response.doc.mjs +1 -1
  274. package/foundation/text/string-utils.mjs +22 -10
  275. package/package.json +10 -9
  276. package/assets/templates/themes/manifest.json +0 -95
@@ -0,0 +1,514 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file The CLI's own docs, read by their `namespace`: every command, API
5
+ * function, schema, and enum doc the CLI ships says which `astryx docs` topic
6
+ * reads it, and the `cli` topic is built from the ones that name it.
7
+ *
8
+ * @input The command, function, schema, and enum docs under
9
+ * clients/cli/commands, api, authoring, and foundation.
10
+ * @output {@link buildCliTopic} for `astryx docs cli`, and
11
+ * {@link auditCliSelfDocs}, which `astryx doctor` runs.
12
+ * @position foundation/discovery — the reader for docs whose namespace starts
13
+ * with `cli/`. The authoring topic reads the `authoring` namespace from its
14
+ * own list (authoring-self-docs.mjs).
15
+ */
16
+
17
+ import * as fs from 'node:fs';
18
+ import * as path from 'node:path';
19
+ import {CLI_ROOT} from '../fs/paths.mjs';
20
+ import {readDocView} from '../doc-compiler/read.mjs';
21
+ import {AUTHORING_SELF_DOCS, selfDocSection} from './authoring-self-docs.mjs';
22
+ import {
23
+ DOC_OUTPUT_BUDGET_BYTES,
24
+ oversizedDocSections,
25
+ } from './docs-output-budget.mjs';
26
+ import {sectionKeyProblems} from './docs-section-key.mjs';
27
+
28
+ /** The directories, relative to the CLI root, that hold the CLI's own docs. */
29
+ export const CLI_SELF_DOC_DIRS = [
30
+ 'clients/cli/commands',
31
+ 'api',
32
+ 'authoring',
33
+ 'foundation',
34
+ ];
35
+
36
+ /** The doc kinds that declare a namespace. */
37
+ const NAMESPACED_KINDS = new Set(['command', 'function', 'schema', 'enum']);
38
+
39
+ /**
40
+ * Every namespace a CLI doc may declare, and the topic that reads it. A doc in
41
+ * a `cli/` namespace is the section `<key prefix>-<name>` of `astryx docs cli`;
42
+ * a doc in `authoring` is a section of `astryx docs authoring`.
43
+ * @type {Record<string, {topic: string, keyPrefix?: string}>}
44
+ */
45
+ export const CLI_DOC_NAMESPACES = {
46
+ 'cli/commands': {topic: 'cli', keyPrefix: 'commands'},
47
+ 'cli/api': {topic: 'cli', keyPrefix: 'api'},
48
+ authoring: {topic: 'authoring'},
49
+ };
50
+
51
+ /** Blocks a doc's notes may carry that a topic section can render. */
52
+ const TOPIC_BLOCKS = new Set(['prose', 'list', 'code', 'heading', 'table']);
53
+
54
+ /** Section order within the `cli` topic: commands, then the API by kind. */
55
+ const KIND_ORDER = ['command', 'function', 'schema', 'enum'];
56
+
57
+ /**
58
+ * Every `*.doc.mjs` under the CLI's doc directories, relative to `root` and
59
+ * sorted. Fixture, test, and dependency directories are skipped.
60
+ * @param {string} [root]
61
+ * @returns {string[]}
62
+ */
63
+ export function discoverCliSelfDocSources(root = CLI_ROOT) {
64
+ /** @type {string[]} */
65
+ const found = [];
66
+ /** @param {string} dir */
67
+ const walk = dir => {
68
+ if (!fs.existsSync(dir)) return;
69
+ for (const entry of fs.readdirSync(dir, {withFileTypes: true})) {
70
+ if (
71
+ entry.name === 'node_modules' ||
72
+ entry.name.startsWith('__') ||
73
+ entry.name.startsWith('.')
74
+ ) {
75
+ continue;
76
+ }
77
+ const full = path.join(dir, entry.name);
78
+ if (entry.isDirectory()) walk(full);
79
+ else if (entry.name.endsWith('.doc.mjs')) {
80
+ found.push(path.relative(root, full).split(path.sep).join('/'));
81
+ }
82
+ }
83
+ };
84
+ for (const dir of CLI_SELF_DOC_DIRS) walk(path.join(root, dir));
85
+ return found.sort();
86
+ }
87
+
88
+ /**
89
+ * Read each doc through the compiler and keep the kinds that declare a
90
+ * namespace. One that fails is reported, never thrown, so one bad file cannot
91
+ * take the rest of the topic down with it.
92
+ * @param {string[]} [sources]
93
+ * @param {string} [root]
94
+ * @returns {Promise<{loaded: {source: string, doc: any}[], failed: {source: string, error: string}[]}>}
95
+ */
96
+ export async function loadCliSelfDocs(
97
+ sources = discoverCliSelfDocSources(),
98
+ root = CLI_ROOT,
99
+ ) {
100
+ const loaded = [];
101
+ const failed = [];
102
+ for (const source of sources) {
103
+ try {
104
+ const doc = await readDocView(path.join(root, source), {
105
+ root: 'self-docs',
106
+ loader: 'native',
107
+ });
108
+ if (!NAMESPACED_KINDS.has(doc?.type)) continue;
109
+ if (typeof doc.name !== 'string' || doc.name === '') {
110
+ throw new Error('exports no doc with a name');
111
+ }
112
+ loaded.push({source, doc});
113
+ } catch (error) {
114
+ failed.push({
115
+ source,
116
+ error: error instanceof Error ? error.message : String(error),
117
+ });
118
+ }
119
+ }
120
+ return {loaded, failed};
121
+ }
122
+
123
+ /**
124
+ * A name as a section-key segment: lowercase words joined by hyphens.
125
+ * `integrationPackCheck` and `integration pack` both read naturally.
126
+ * @param {string} name
127
+ * @returns {string}
128
+ */
129
+ export function keySegment(name) {
130
+ return name
131
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
132
+ .toLowerCase()
133
+ .replace(/[^a-z0-9]+/g, '-')
134
+ .replace(/^-+|-+$/g, '');
135
+ }
136
+
137
+ /**
138
+ * The section key a doc is read by in `astryx docs cli`, or null when its
139
+ * namespace is not one of the topic's.
140
+ * @param {any} doc
141
+ * @returns {string | null}
142
+ */
143
+ export function cliSectionKey(doc) {
144
+ const prefix = CLI_DOC_NAMESPACES[doc?.namespace]?.keyPrefix;
145
+ if (prefix == null) return null;
146
+ return `${prefix}-${keySegment(String(doc.name))}`;
147
+ }
148
+
149
+ /** @param {unknown} value */
150
+ function defaultText(value) {
151
+ if (value === undefined || value === false || value === '') return null;
152
+ if (Array.isArray(value)) return value.length > 0 ? value.join(' ') : null;
153
+ return String(value);
154
+ }
155
+
156
+ /**
157
+ * @param {any} cmd
158
+ * @returns {string}
159
+ */
160
+ function usage(cmd) {
161
+ const args = (cmd.args ?? []).map((/** @type {any} */ arg) => {
162
+ const name = arg.variadic ? `${arg.name}...` : arg.name;
163
+ return arg.required ? `<${name}>` : `[${name}]`;
164
+ });
165
+ return ['astryx', cmd.name, ...args].join(' ');
166
+ }
167
+
168
+ /**
169
+ * The text for a command option or argument: its own, or else the text of the
170
+ * API parameter it maps to, as `--help` shows.
171
+ * @param {{description?: string, param?: string}} entry
172
+ * @param {any} fn the FunctionDoc the command calls, if any
173
+ * @returns {string}
174
+ */
175
+ function entryText(entry, fn) {
176
+ if (typeof entry.description === 'string' && entry.description !== '') {
177
+ return entry.description;
178
+ }
179
+ const param = (fn?.params ?? []).find(
180
+ (/** @type {any} */ p) => p.name === entry.param,
181
+ );
182
+ return typeof param?.description === 'string' ? param.description : '';
183
+ }
184
+
185
+ /**
186
+ * `text`, ending in a full stop when more text follows it in the same cell.
187
+ * @param {string} text
188
+ * @param {boolean} more
189
+ */
190
+ function sentence(text, more) {
191
+ return more && text !== '' && !/[.!?:]$/.test(text) ? `${text}.` : text;
192
+ }
193
+
194
+ /** @param {any[]} notes */
195
+ function noteBlocks(notes) {
196
+ return (notes ?? []).filter(note => TOPIC_BLOCKS.has(note?.type));
197
+ }
198
+
199
+ /**
200
+ * One command doc as a section of `astryx docs cli`.
201
+ * @param {any} cmd
202
+ * @param {{functions: Map<string, any>, commands: Set<string>}} index
203
+ */
204
+ function commandSection(cmd, index) {
205
+ const fn = cmd.fn == null ? undefined : index.functions.get(cmd.fn);
206
+ /** @type {any[]} */
207
+ const content = [{type: 'prose', text: cmd.summary}];
208
+ if (cmd.description) content.push({type: 'prose', text: cmd.description});
209
+ content.push({type: 'code', lang: 'bash', code: usage(cmd)});
210
+ const args = cmd.args ?? [];
211
+ if (args.length > 0) {
212
+ content.push({
213
+ type: 'table',
214
+ headers: ['Argument', 'Description'],
215
+ rows: args.map((/** @type {any} */ arg) => [
216
+ `\`${arg.name}\``,
217
+ entryText(arg, fn),
218
+ ]),
219
+ });
220
+ }
221
+ const options = cmd.options ?? [];
222
+ if (options.length > 0) {
223
+ content.push({
224
+ type: 'table',
225
+ headers: ['Option', 'Description'],
226
+ rows: options.map((/** @type {any} */ option) => {
227
+ const extra = [
228
+ option.choices?.length
229
+ ? `One of: ${option.choices.map((/** @type {string} */ c) => `\`${c}\``).join(', ')}.`
230
+ : null,
231
+ defaultText(option.default) == null
232
+ ? null
233
+ : `Default: \`${defaultText(option.default)}\`.`,
234
+ ].filter(Boolean);
235
+ return [
236
+ `\`${option.flag}\``,
237
+ [sentence(entryText(option, fn), extra.length > 0), ...extra]
238
+ .filter(Boolean)
239
+ .join(' '),
240
+ ];
241
+ }),
242
+ });
243
+ }
244
+ const examples = (cmd.examples ?? []).flatMap((/** @type {any} */ e) =>
245
+ e.label ? [`# ${e.label}`, e.cli] : [e.cli],
246
+ );
247
+ if (examples.length > 0) {
248
+ content.push({type: 'code', lang: 'bash', code: examples.join('\n')});
249
+ }
250
+ const exitCodes = cmd.exitCodes ?? [];
251
+ if (exitCodes.length > 0) {
252
+ content.push({
253
+ type: 'table',
254
+ headers: ['Exit code', 'When'],
255
+ rows: exitCodes.map((/** @type {any} */ e) => [String(e.code), e.when]),
256
+ });
257
+ }
258
+ const subcommands = (cmd.subcommands ?? []).filter(
259
+ (/** @type {string} */ sub) => index.commands.has(`${cmd.name} ${sub}`),
260
+ );
261
+ if (subcommands.length > 0) {
262
+ content.push({
263
+ type: 'list',
264
+ style: 'unordered',
265
+ items: subcommands.map(
266
+ (/** @type {string} */ sub) =>
267
+ `\`astryx ${cmd.name} ${sub}\`: \`astryx docs cli commands-${keySegment(`${cmd.name} ${sub}`)}\``,
268
+ ),
269
+ });
270
+ }
271
+ if (fn != null) {
272
+ content.push({
273
+ type: 'prose',
274
+ text: `It runs \`${fn.name}()\` from \`${fn.importPath}\`. Read it with \`astryx docs cli ${cliSectionKey(fn)}\`.`,
275
+ });
276
+ }
277
+ content.push(...noteBlocks(cmd.notes));
278
+ return {
279
+ id: cliSectionKey(cmd),
280
+ title: cmd.displayName ?? `astryx ${cmd.name}`,
281
+ content,
282
+ };
283
+ }
284
+
285
+ /**
286
+ * One API function doc as a section of `astryx docs cli`.
287
+ * @param {any} fn
288
+ * @param {{functions: Map<string, any>, commands: Set<string>}} index
289
+ */
290
+ function functionSection(fn, index) {
291
+ /** @type {any[]} */
292
+ const content = [{type: 'prose', text: fn.summary ?? fn.description ?? ''}];
293
+ if (fn.summary && fn.description) {
294
+ content.push({type: 'prose', text: fn.description});
295
+ }
296
+ if (fn.signature) {
297
+ content.push({type: 'code', lang: 'ts', code: fn.signature});
298
+ }
299
+ if (fn.importPath) {
300
+ content.push({type: 'prose', text: `Import it from \`${fn.importPath}\`.`});
301
+ }
302
+ const params = fn.params ?? [];
303
+ if (params.length > 0) {
304
+ content.push({
305
+ type: 'table',
306
+ headers: ['Parameter', 'Type', 'Description'],
307
+ rows: params.map((/** @type {any} */ p) => [
308
+ `\`${p.name}\``,
309
+ `\`${p.type ?? ''}\``,
310
+ p.description ?? '',
311
+ ]),
312
+ });
313
+ }
314
+ const returns = fn.returns ?? [];
315
+ if (returns.length > 0) {
316
+ content.push({
317
+ type: 'table',
318
+ headers: ['Returns', 'Description'],
319
+ rows: returns.map((/** @type {any} */ r) => [
320
+ `\`${r.name ? `${r.name}: ` : ''}${r.type}\``,
321
+ r.description ?? '',
322
+ ]),
323
+ });
324
+ }
325
+ const throws = fn.throws ?? [];
326
+ if (throws.length > 0) {
327
+ content.push({
328
+ type: 'table',
329
+ headers: ['Throws', 'When'],
330
+ rows: throws.map((/** @type {any} */ t) => [`\`${t.code}\``, t.when]),
331
+ });
332
+ }
333
+ for (const example of fn.examples ?? []) {
334
+ if (typeof example?.code !== 'string' || example.code.trim() === '') {
335
+ continue;
336
+ }
337
+ content.push({
338
+ type: 'code',
339
+ lang: 'ts',
340
+ ...(example.label ? {label: example.label} : {}),
341
+ code: example.code,
342
+ });
343
+ }
344
+ if (fn.command != null && index.commands.has(fn.command)) {
345
+ content.push({
346
+ type: 'prose',
347
+ text: `\`astryx ${fn.command}\` runs it. Read it with \`astryx docs cli commands-${keySegment(fn.command)}\`.`,
348
+ });
349
+ }
350
+ return {
351
+ id: cliSectionKey(fn),
352
+ title: fn.displayName ?? `${fn.name}()`,
353
+ content,
354
+ };
355
+ }
356
+
357
+ /**
358
+ * One enum doc as a section of `astryx docs cli`.
359
+ * @param {any} doc
360
+ */
361
+ function enumSection(doc) {
362
+ return {
363
+ id: cliSectionKey(doc),
364
+ title: doc.displayName ?? doc.name,
365
+ content: [
366
+ {type: 'prose', text: doc.description},
367
+ {
368
+ type: 'table',
369
+ headers: ['Value', 'Meaning'],
370
+ rows: (doc.members ?? []).map((/** @type {any} */ m) => [
371
+ `\`${m.value}\``,
372
+ m.deprecated
373
+ ? `${m.description} Deprecated: ${m.deprecated}`
374
+ : m.description,
375
+ ]),
376
+ },
377
+ ],
378
+ };
379
+ }
380
+
381
+ /**
382
+ * The docs a `cli/` namespace names, in reading order: commands by name (a
383
+ * group before its subcommands), then API functions, schemas, and enums.
384
+ * @param {any[]} docs
385
+ * @returns {any[]}
386
+ */
387
+ function cliDocsInOrder(docs) {
388
+ return docs
389
+ .filter(doc => CLI_DOC_NAMESPACES[doc.namespace]?.topic === 'cli')
390
+ .sort(
391
+ (a, b) =>
392
+ KIND_ORDER.indexOf(a.type) - KIND_ORDER.indexOf(b.type) ||
393
+ (a.name < b.name ? -1 : a.name > b.name ? 1 : 0),
394
+ );
395
+ }
396
+
397
+ /**
398
+ * The `cli` topic: one section per doc whose namespace is `cli/…`.
399
+ * @param {any[]} docs every loaded CLI doc; the ones in other namespaces are
400
+ * left out
401
+ * @returns {import('../../authoring/doctypes/reference/type').ReferenceDoc}
402
+ */
403
+ export function buildCliReferenceDoc(docs) {
404
+ const ordered = cliDocsInOrder(docs);
405
+ const index = {
406
+ functions: new Map(
407
+ ordered.filter(d => d.type === 'function').map(d => [d.name, d]),
408
+ ),
409
+ commands: new Set(
410
+ ordered.filter(d => d.type === 'command').map(d => d.name),
411
+ ),
412
+ };
413
+ return /** @type {any} */ ({
414
+ name: 'cli',
415
+ title: 'CLI Reference',
416
+ category: 'guide',
417
+ description:
418
+ 'Every command and API function of the CLI, with the JSON output envelope, error codes, and response types, read from the docs the CLI ships.',
419
+ sections: ordered.map(doc =>
420
+ doc.type === 'command'
421
+ ? commandSection(doc, index)
422
+ : doc.type === 'function'
423
+ ? functionSection(doc, index)
424
+ : doc.type === 'enum'
425
+ ? enumSection(doc)
426
+ : {...selfDocSection(doc), id: cliSectionKey(doc)},
427
+ ),
428
+ });
429
+ }
430
+
431
+ /**
432
+ * The `cli` topic from every CLI doc that loads. One that fails is left out
433
+ * here and reported by {@link auditCliSelfDocs}.
434
+ * @returns {Promise<import('../../authoring/doctypes/reference/type').ReferenceDoc>}
435
+ */
436
+ export async function buildCliTopic() {
437
+ const {loaded} = await loadCliSelfDocs();
438
+ return buildCliReferenceDoc(loaded.map(entry => entry.doc));
439
+ }
440
+
441
+ /**
442
+ * What stands between a CLI doc and a reader: a missing namespace, one no
443
+ * topic reads, one that disagrees with the authoring topic's list, a doc that
444
+ * fails to load, and a `cli` section that clashes or is too large.
445
+ * @param {{root?: string, sources?: string[], budget?: number, authoringSources?: string[]}} [options]
446
+ * @returns {Promise<{
447
+ * docs: number,
448
+ * sections: number,
449
+ * authoring: number,
450
+ * missing: string[],
451
+ * unknown: {source: string, namespace: string}[],
452
+ * misfiled: {source: string, message: string}[],
453
+ * failed: {source: string, error: string}[],
454
+ * keyProblems: string[],
455
+ * oversized: {key: string, title: string, bytes: number}[],
456
+ * }>}
457
+ */
458
+ export async function auditCliSelfDocs({
459
+ root = CLI_ROOT,
460
+ sources,
461
+ budget = DOC_OUTPUT_BUDGET_BYTES,
462
+ authoringSources = AUTHORING_SELF_DOCS,
463
+ } = {}) {
464
+ const {loaded, failed} = await loadCliSelfDocs(
465
+ sources ?? discoverCliSelfDocSources(root),
466
+ root,
467
+ );
468
+ const listed = new Set(authoringSources.map(source => `authoring/${source}`));
469
+ /** @type {string[]} */
470
+ const missing = [];
471
+ /** @type {{source: string, namespace: string}[]} */
472
+ const unknown = [];
473
+ /** @type {{source: string, message: string}[]} */
474
+ const misfiled = [];
475
+ let authoring = 0;
476
+ for (const {source, doc} of loaded) {
477
+ const namespace = doc.namespace;
478
+ if (typeof namespace !== 'string' || namespace === '') {
479
+ missing.push(source);
480
+ continue;
481
+ }
482
+ if (!Object.hasOwn(CLI_DOC_NAMESPACES, namespace)) {
483
+ unknown.push({source, namespace});
484
+ continue;
485
+ }
486
+ const inAuthoring = listed.has(source);
487
+ if (namespace === 'authoring') {
488
+ if (inAuthoring) authoring++;
489
+ else {
490
+ misfiled.push({
491
+ source,
492
+ message: `${source} has namespace "authoring", but \`astryx docs authoring\` does not list it`,
493
+ });
494
+ }
495
+ } else if (inAuthoring) {
496
+ misfiled.push({
497
+ source,
498
+ message: `${source} is read in \`astryx docs authoring\`, but its namespace is "${namespace}"`,
499
+ });
500
+ }
501
+ }
502
+ const topic = buildCliReferenceDoc(loaded.map(entry => entry.doc));
503
+ return {
504
+ docs: loaded.length,
505
+ sections: topic.sections.length,
506
+ authoring,
507
+ missing,
508
+ unknown,
509
+ misfiled,
510
+ failed,
511
+ keyProblems: sectionKeyProblems(topic.sections),
512
+ oversized: oversizedDocSections(topic.sections, budget),
513
+ };
514
+ }