@astryxdesign/cli 0.6.3-canary.6ef7b74 → 0.6.3-canary.8492dde

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 (128) hide show
  1. package/README.md +1 -2
  2. package/api/docs/_adapter.d.mts +0 -10
  3. package/api/docs/_adapter.mjs +11 -67
  4. package/api/docs/detail/detail.d.mts +0 -15
  5. package/api/docs/detail/detail.mjs +8 -23
  6. package/api/docs/detail/section/section.d.mts +1 -1
  7. package/api/docs/detail/section/section.mjs +17 -37
  8. package/api/docs/detail/section/section.test.mjs +0 -40
  9. package/api/docs/docs.d.mts +2 -7
  10. package/api/docs/docs.doc.mjs +10 -27
  11. package/api/docs/docs.mjs +9 -16
  12. package/api/docs/docs.test.mjs +0 -6
  13. package/api/docs/docs.type.d.mts +0 -37
  14. package/api/docs/docs.type.mjs +0 -28
  15. package/api/docs/integrationDocs.test.mjs +0 -106
  16. package/api/doctor/doctor.d.mts +0 -48
  17. package/api/doctor/doctor.mjs +0 -236
  18. package/api/doctor/doctor.test.mjs +0 -196
  19. package/api/hook/list/list.d.mts +1 -1
  20. package/api/integration/integration-authoring.type.d.mts +1 -1
  21. package/api/search/search.d.mts +1 -1
  22. package/api/search/search.type.d.mts +1 -1
  23. package/api/template/template.d.mts +1 -1
  24. package/api/template/template.type.d.mts +1 -1
  25. package/api/upgrade/_adapter.mjs +5 -71
  26. package/api/upgrade/upgrade.doc.mjs +3 -4
  27. package/api/upgrade/upgrade.type.d.mts +1 -1
  28. package/api/upgrade/upgrade.type.mjs +1 -1
  29. package/assets/docs/README.md +0 -9
  30. package/assets/docs/cli-integrations.doc.mjs +15 -77
  31. package/assets/templates/blocks/components/Toolbar/ToolbarTableFilter.doc.mjs +3 -19
  32. package/assets/templates/blocks/components/Toolbar/ToolbarTableFilter.tsx +65 -383
  33. package/authoring/debug/parse.d.mts +1 -1
  34. package/authoring/doctypes/_schema.d.mts +21 -676
  35. package/authoring/doctypes/_schema.mjs +38 -360
  36. package/authoring/doctypes/base/type.ts +0 -36
  37. package/authoring/doctypes/command/type.ts +1 -2
  38. package/authoring/doctypes/component/component.doc.mjs +1 -1
  39. package/authoring/doctypes/component/type.ts +2 -3
  40. package/authoring/doctypes/enum/type.ts +1 -3
  41. package/authoring/doctypes/function/type.ts +2 -6
  42. package/authoring/doctypes/hook/type.ts +1 -2
  43. package/authoring/doctypes/legacy.d.mts +2 -4
  44. package/authoring/doctypes/legacy.mjs +2 -3
  45. package/authoring/doctypes/parse.d.mts +2 -4
  46. package/authoring/doctypes/parse.mjs +2 -8
  47. package/authoring/doctypes/parse.test.mjs +3 -77
  48. package/authoring/doctypes/reference/parse.mjs +4 -7
  49. package/authoring/doctypes/reference/reference.doc.mjs +4 -13
  50. package/authoring/doctypes/reference/type.ts +5 -51
  51. package/authoring/doctypes/schema/type.ts +1 -2
  52. package/authoring/doctypes/template/parse.mjs +1 -3
  53. package/authoring/doctypes/template/parse.test.mjs +2 -8
  54. package/authoring/doctypes/template/type.ts +2 -2
  55. package/authoring/doctypes/types.ts +0 -1
  56. package/authoring/gap-report/parse.d.mts +1 -1
  57. package/authoring/index.d.mts +0 -1
  58. package/authoring/index.d.ts +0 -32
  59. package/authoring/index.mjs +0 -1
  60. package/authoring/integration/integration.doc.mjs +6 -13
  61. package/authoring/integration/parse.test.mjs +1 -10
  62. package/authoring/integration/schema.d.mts +0 -2
  63. package/authoring/integration/schema.mjs +0 -6
  64. package/authoring/integration/type.ts +5 -22
  65. package/authoring/shadcn/receipt.d.mts +6 -6
  66. package/clients/cli/commands/docs.doc.mjs +3 -13
  67. package/clients/cli/commands/docs.mjs +21 -121
  68. package/clients/cli/commands/docs.test.mjs +0 -88
  69. package/clients/cli/commands/theme-palette-generate.doc.mjs +4 -8
  70. package/clients/cli/commands/upgrade.doc.mjs +2 -2
  71. package/clients/cli/formatters/index.mjs +1 -162
  72. package/clients/cli/formatters/index.test.mjs +0 -91
  73. package/clients/cli/lib/manifest.mjs +2 -7
  74. package/foundation/config/project.mjs +6 -21
  75. package/foundation/discovery/component-discovery.d.mts +1 -1
  76. package/foundation/discovery/component-discovery.mjs +1 -2
  77. package/foundation/discovery/docs-discovery.d.mts +4 -7
  78. package/foundation/discovery/docs-discovery.mjs +88 -194
  79. package/foundation/discovery/docs-discovery.test.mjs +13 -243
  80. package/foundation/discovery/template-adapter.mjs +1 -2
  81. package/foundation/integrations/autolink.mjs +5 -12
  82. package/foundation/integrations/integration-warnings.mjs +0 -6
  83. package/foundation/integrations/integrations.d.mts +2 -46
  84. package/foundation/integrations/integrations.mjs +8 -167
  85. package/foundation/integrations/integrations.test.mjs +1 -384
  86. package/foundation/integrations/validate-contributions.d.mts +0 -2
  87. package/foundation/integrations/validate-contributions.mjs +0 -10
  88. package/foundation/response/json-contract.test.mjs +17 -46
  89. package/foundation/response/response-types.doc.mjs +1 -6
  90. package/package.json +9 -9
  91. package/api/docs/index/index.d.mts +0 -18
  92. package/api/docs/index/index.mjs +0 -31
  93. package/api/docs/index/index.test.mjs +0 -62
  94. package/api/upgrade/project-context.test.mjs +0 -272
  95. package/assets/docs/authoring.doc.mjs +0 -14
  96. package/assets/templates/blocks/components/Timer/TimerFormats.doc.mjs +0 -14
  97. package/assets/templates/blocks/components/Timer/TimerFormats.tsx +0 -34
  98. package/assets/templates/blocks/components/Timer/TimerInline.doc.mjs +0 -14
  99. package/assets/templates/blocks/components/Timer/TimerInline.tsx +0 -14
  100. package/assets/templates/blocks/components/Timer/TimerShowcase.doc.mjs +0 -13
  101. package/assets/templates/blocks/components/Timer/TimerShowcase.tsx +0 -47
  102. package/assets/templates/blocks/components/Timer/TimerTypography.doc.mjs +0 -14
  103. package/assets/templates/blocks/components/Timer/TimerTypography.tsx +0 -31
  104. package/authoring/doctypes/base/graph-fields.doc.d.mts +0 -9
  105. package/authoring/doctypes/base/graph-fields.doc.mjs +0 -55
  106. package/authoring/doctypes/namespace/namespace.doc.d.mts +0 -9
  107. package/authoring/doctypes/namespace/namespace.doc.mjs +0 -128
  108. package/authoring/doctypes/namespace/parse.d.mts +0 -12
  109. package/authoring/doctypes/namespace/parse.mjs +0 -25
  110. package/authoring/doctypes/namespace/parse.test.mjs +0 -165
  111. package/authoring/doctypes/namespace/type.ts +0 -71
  112. package/authoring/identity/identity.doc.d.mts +0 -9
  113. package/authoring/identity/identity.doc.mjs +0 -60
  114. package/authoring/identity/type.ts +0 -132
  115. package/foundation/discovery/authoring-self-docs.d.mts +0 -69
  116. package/foundation/discovery/authoring-self-docs.mjs +0 -214
  117. package/foundation/discovery/authoring-self-docs.test.mjs +0 -95
  118. package/foundation/discovery/docs-output-budget.d.mts +0 -28
  119. package/foundation/discovery/docs-output-budget.mjs +0 -50
  120. package/foundation/discovery/docs-section-key.d.mts +0 -98
  121. package/foundation/discovery/docs-section-key.mjs +0 -221
  122. package/foundation/discovery/docs-section-key.test.mjs +0 -224
  123. package/foundation/identity/provider-identity.d.mts +0 -90
  124. package/foundation/identity/provider-identity.mjs +0 -320
  125. package/foundation/identity/provider-identity.test.mjs +0 -254
  126. package/foundation/identity/providers.d.mts +0 -7
  127. package/foundation/identity/providers.mjs +0 -16
  128. package/foundation/integrations/provider-conflicts.test.mjs +0 -125
package/README.md CHANGED
@@ -403,8 +403,7 @@ Every response has a `type` discriminant. The full set is below (generated from
403
403
  | `component.detail.blocks` | One component's example blocks, as {component, showcase, examples, related} of BlockEntry. |
404
404
  | `docs.list` | All reference-doc topics as DocsListEntry[] ({topic, description}), in discovery order. |
405
405
  | `docs.detail` | One topic's full ReferenceDoc, with token-ref blocks inlined. |
406
- | `docs.index` | One topic's section index (--index): each section's key, title, and one-line summary. |
407
- | `docs.detail.section` | One ReferenceSection of a topic, found by key or title, with token-ref blocks inlined. |
406
+ | `docs.detail.section` | A single ReferenceSection of a topic: the first whose title contains the section query. |
408
407
  | `blog.list` | The feed URL plus every post parsed from the RSS feed, each with slug, title, description, date, type, authors, link, and plaintext URL. |
409
408
  | `blog.detail` | One post's metadata plus the feed URL and the post's full plaintext body. |
410
409
  | `discover.list` | The configured external packages (name, category, components, version, description); when empty it carries meta.configured to tell "nothing configured" from "nothing discovered". |
@@ -39,12 +39,6 @@ export function loadReferenceDocs(docPath: string, { lang }?: {
39
39
  export function loadTopicDoc(entry: import("../../foundation/discovery/docs-discovery.mjs").DocsTopicEntry, { lang }?: {
40
40
  lang?: string | null;
41
41
  }): Promise<import("./docs.type.mjs").DocsDetailResponse["data"]>;
42
- /**
43
- * The overlay languages a topic ships for its own file or any extension.
44
- * @param {import('../../foundation/discovery/docs-discovery.mjs').DocsTopicEntry} entry
45
- * @returns {string[]}
46
- */
47
- export function overlayLanguages(entry: import("../../foundation/discovery/docs-discovery.mjs").DocsTopicEntry): string[];
48
42
  /**
49
43
  * Resolve `topic` against the project's catalog (throwing `ERR_UNKNOWN_TOPIC`
50
44
  * when unmatched), and load it with any --dense/--zh overlay and any
@@ -60,7 +54,6 @@ export function overlayLanguages(entry: import("../../foundation/discovery/docs-
60
54
  * @returns {Promise<{
61
55
  * catalog: DocsCatalog,
62
56
  * docsData: import('./docs.type.mjs').DocsDetailResponse['data'],
63
- * lang: string | null,
64
57
  * }>}
65
58
  */
66
59
  export function resolveTopicDocs(topic: string, options?: {
@@ -71,8 +64,5 @@ export function resolveTopicDocs(topic: string, options?: {
71
64
  }): Promise<{
72
65
  catalog: DocsCatalog;
73
66
  docsData: import("./docs.type.mjs").DocsDetailResponse["data"];
74
- lang: string | null;
75
67
  }>;
76
- /** The localized overlays a docs read can apply. */
77
- export const OVERLAY_LANGUAGES: string[];
78
68
  import { DocsCatalog } from '../../foundation/discovery/docs-discovery.mjs';
@@ -23,16 +23,9 @@ import {Project} from '../../foundation/config/project.mjs';
23
23
  import {
24
24
  DocsCatalog,
25
25
  mergeTopic,
26
- problemsInTopic,
27
- withSourceTitle,
28
26
  } from '../../foundation/discovery/docs-discovery.mjs';
29
- import {
30
- sectionKeyProblems,
31
- withSectionKeys,
32
- } from '../../foundation/discovery/docs-section-key.mjs';
33
27
  import {AstryxError} from '../error.mjs';
34
28
  import {ERROR_CODES} from '../../foundation/response/error-codes.mjs';
35
- import {parseDoc} from '../../authoring/doctypes/parse.mjs';
36
29
 
37
30
  /**
38
31
  * The project's topics: the built-in ones plus whatever the configured
@@ -63,20 +56,13 @@ export async function loadDocsCatalog(cwd = process.cwd()) {
63
56
  */
64
57
  export async function loadReferenceDocs(docPath, {lang} = {}) {
65
58
  const mod = await import(pathToFileURL(docPath).href);
66
- const parsed = parseDoc(mod.docs ?? mod.default, path.basename(docPath));
67
- if (!('sections' in parsed)) {
68
- throw new Error(`${path.basename(docPath)} is not a reference document.`);
69
- }
70
- const problems = problemsInTopic(parsed);
71
- if (problems.length > 0) {
72
- throw new Error(
73
- `${path.basename(docPath)} is invalid: ${problems.join('; ')}`,
74
- );
75
- }
76
- const docs = parsed;
59
+ const docs = mod.docs ?? mod.default;
77
60
  if (!lang || lang === 'en') return docs;
78
61
 
79
- const translationPath = overlayPath(docPath, lang);
62
+ const dir = path.dirname(docPath);
63
+ const base = path.basename(docPath, '.doc.mjs');
64
+ const locale = lang === 'dense' ? 'dense' : lang;
65
+ const translationPath = path.join(dir, `${base}.doc.${locale}.mjs`);
80
66
  if (!fs.existsSync(translationPath)) return docs;
81
67
 
82
68
  const translationMod = await import(pathToFileURL(translationPath).href);
@@ -100,12 +86,10 @@ export async function loadReferenceDocs(docPath, {lang} = {}) {
100
86
  ...docs,
101
87
  description: translation.description || docs.description,
102
88
  sections: docs.sections.map(
103
- (
104
- /** @type {import('@astryxdesign/cli/authoring').ReferenceSection} */ section,
105
- ) => {
89
+ (/** @type {import('@astryxdesign/cli/authoring').ReferenceSection} */ section) => {
106
90
  const ts = bySection.get(section.title);
107
91
  if (!ts) return section;
108
- const localized = {
92
+ return {
109
93
  ...section,
110
94
  title: ts.title || section.title,
111
95
  content: section.content.map(
@@ -115,15 +99,12 @@ export async function loadReferenceDocs(docPath, {lang} = {}) {
115
99
  ) => {
116
100
  const tb = ts.content?.[bi];
117
101
  if (!tb) return block;
118
- if (tb.type === 'prose' && block.type === 'prose')
119
- return {...block, text: tb.text};
120
- if (tb.type === 'list' && block.type === 'list')
121
- return {...block, items: tb.items};
102
+ if (tb.type === 'prose' && block.type === 'prose') return {...block, text: tb.text};
103
+ if (tb.type === 'list' && block.type === 'list') return {...block, items: tb.items};
122
104
  return block;
123
105
  },
124
106
  ),
125
107
  };
126
- return withSourceTitle(localized, section.title);
127
108
  },
128
109
  ),
129
110
  };
@@ -146,44 +127,8 @@ export async function loadTopicDoc(entry, {lang} = {}) {
146
127
  let doc = await loadReferenceDocs(entry.path, {lang});
147
128
  for (const extension of entry.extensions) {
148
129
  doc = mergeTopic(doc, await loadReferenceDocs(extension.path, {lang}));
149
- // Merging matches on keys, so this holds unless merge itself regresses.
150
- const problems = sectionKeyProblems(doc.sections);
151
- if (problems.length > 0) {
152
- throw new Error(
153
- `${path.basename(extension.path)}, extending ${entry.name}, leaves two sections with one key: ${problems.join('; ')}`,
154
- );
155
- }
156
130
  }
157
- // Derived keys are stamped only now, so they never take part in merging.
158
- return withSectionKeys(doc);
159
- }
160
-
161
- /** The localized overlays a docs read can apply. */
162
- export const OVERLAY_LANGUAGES = ['zh', 'dense'];
163
-
164
- /**
165
- * Where the `lang` overlay of a doc file lives: `{topic}.doc.{lang}.mjs`.
166
- * @param {string} docPath
167
- * @param {string} lang
168
- * @returns {string}
169
- */
170
- function overlayPath(docPath, lang) {
171
- return path.join(
172
- path.dirname(docPath),
173
- `${path.basename(docPath, '.doc.mjs')}.doc.${lang}.mjs`,
174
- );
175
- }
176
-
177
- /**
178
- * The overlay languages a topic ships for its own file or any extension.
179
- * @param {import('../../foundation/discovery/docs-discovery.mjs').DocsTopicEntry} entry
180
- * @returns {string[]}
181
- */
182
- export function overlayLanguages(entry) {
183
- const files = [entry.path, ...entry.extensions.map(ext => ext.path)];
184
- return OVERLAY_LANGUAGES.filter(lang =>
185
- files.some(file => fs.existsSync(overlayPath(file, lang))),
186
- );
131
+ return doc;
187
132
  }
188
133
 
189
134
  /**
@@ -201,7 +146,6 @@ export function overlayLanguages(entry) {
201
146
  * @returns {Promise<{
202
147
  * catalog: DocsCatalog,
203
148
  * docsData: import('./docs.type.mjs').DocsDetailResponse['data'],
204
- * lang: string | null,
205
149
  * }>}
206
150
  */
207
151
  export async function resolveTopicDocs(topic, options = {}) {
@@ -222,5 +166,5 @@ export async function resolveTopicDocs(topic, options = {}) {
222
166
  }
223
167
 
224
168
  const docsData = await loadTopicDoc(entry, {lang: effectiveLang});
225
- return {catalog, docsData, lang: effectiveLang};
169
+ return {catalog, docsData};
226
170
  }
@@ -1,21 +1,6 @@
1
1
  // @generated by scripts/sync-api-types.mjs from the JSDoc in api/**/*.mjs.
2
2
  // DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
3
3
 
4
- /**
5
- * Resolve token-ref blocks by inlining the referenced section's table.
6
- * This allows section docs to reference token tables without duplicating data.
7
- *
8
- * The reference is resolved through the catalog, so a topic may point at one
9
- * an integration contributed (or replaced) rather than only at a built-in. It
10
- * names the section by key or authored title, so it resolves in every language.
11
- * @param {import('../docs.type.mjs').DocsDetailResponse['data']} docsData
12
- * @param {import('../../../foundation/discovery/docs-discovery.mjs').DocsCatalog} catalog
13
- * @param {{lang?: string | null}} [options]
14
- * @returns {Promise<import('../docs.type.mjs').DocsDetailResponse['data']>}
15
- */
16
- export function resolveTokenRefs(docsData: import("../docs.type.mjs").DocsDetailResponse["data"], catalog: import("../../../foundation/discovery/docs-discovery.mjs").DocsCatalog, { lang }?: {
17
- lang?: string | null;
18
- }): Promise<import("../docs.type.mjs").DocsDetailResponse["data"]>;
19
4
  /**
20
5
  * @param {string} topic
21
6
  * @param {object} [options]
@@ -12,25 +12,18 @@
12
12
  */
13
13
 
14
14
  import {loadTopicDoc, resolveTopicDocs} from '../_adapter.mjs';
15
- import {
16
- sectionKey,
17
- sourceTitle,
18
- withSourceTitle,
19
- } from '../../../foundation/discovery/docs-section-key.mjs';
20
15
 
21
16
  /**
22
17
  * Resolve token-ref blocks by inlining the referenced section's table.
23
18
  * This allows section docs to reference token tables without duplicating data.
24
19
  *
25
20
  * The reference is resolved through the catalog, so a topic may point at one
26
- * an integration contributed (or replaced) rather than only at a built-in. It
27
- * names the section by key or authored title, so it resolves in every language.
21
+ * an integration contributed (or replaced) rather than only at a built-in.
28
22
  * @param {import('../docs.type.mjs').DocsDetailResponse['data']} docsData
29
23
  * @param {import('../../../foundation/discovery/docs-discovery.mjs').DocsCatalog} catalog
30
- * @param {{lang?: string | null}} [options]
31
24
  * @returns {Promise<import('../docs.type.mjs').DocsDetailResponse['data']>}
32
25
  */
33
- export async function resolveTokenRefs(docsData, catalog, {lang = null} = {}) {
26
+ async function resolveTokenRefs(docsData, catalog) {
34
27
  const resolved = {...docsData, sections: [...docsData.sections]};
35
28
  for (let si = 0; si < resolved.sections.length; si++) {
36
29
  const section = resolved.sections[si];
@@ -43,12 +36,10 @@ export async function resolveTokenRefs(docsData, catalog, {lang = null} = {}) {
43
36
  newContent.push({type: 'prose', text: `[token-ref: unknown topic "${block.topic}"]`});
44
37
  continue;
45
38
  }
46
- const refDocs = await loadTopicDoc(refEntry, {lang});
47
- const wanted = block.section.toLowerCase();
39
+ const refDocs = await loadTopicDoc(refEntry);
48
40
  const refSection = refDocs.sections.find(
49
41
  (/** @type {import('@astryxdesign/cli/authoring').ReferenceSection} */ s) =>
50
- sectionKey(s) === block.section ||
51
- sourceTitle(s).toLowerCase() === wanted,
42
+ s.title.toLowerCase() === block.section.toLowerCase(),
52
43
  );
53
44
  if (!refSection) {
54
45
  newContent.push({type: 'prose', text: `[token-ref: section "${block.section}" not found in "${block.topic}"]`});
@@ -61,20 +52,14 @@ export async function resolveTokenRefs(docsData, catalog, {lang = null} = {}) {
61
52
  }
62
53
  // If the referenced section has a previewType, attach it to our section
63
54
  if (refSection.previewType && !section.previewType) {
64
- resolved.sections[si] = withSourceTitle(
65
- {...section, previewType: refSection.previewType, content: newContent},
66
- sourceTitle(section),
67
- );
55
+ resolved.sections[si] = {...section, previewType: refSection.previewType, content: newContent};
68
56
  }
69
57
  } else {
70
58
  newContent.push(block);
71
59
  }
72
60
  }
73
61
  if (resolved.sections[si] === section) {
74
- resolved.sections[si] = withSourceTitle(
75
- {...section, content: newContent},
76
- sourceTitle(section),
77
- );
62
+ resolved.sections[si] = {...section, content: newContent};
78
63
  } else {
79
64
  resolved.sections[si].content = newContent;
80
65
  }
@@ -92,7 +77,7 @@ export async function resolveTokenRefs(docsData, catalog, {lang = null} = {}) {
92
77
  * @returns {Promise<import('../docs.type.mjs').DocsDetailResponse>}
93
78
  */
94
79
  export async function detail(topic, options = {}) {
95
- const {catalog, docsData, lang} = await resolveTopicDocs(topic, options);
96
- const resolved = await resolveTokenRefs(docsData, catalog, {lang});
80
+ const {catalog, docsData} = await resolveTopicDocs(topic, options);
81
+ const resolved = await resolveTokenRefs(docsData, catalog);
97
82
  return {type: 'docs.detail', data: resolved};
98
83
  }
@@ -3,7 +3,7 @@
3
3
 
4
4
  /**
5
5
  * @param {string} topic
6
- * @param {string} sectionName a section key, or a title (or part of one)
6
+ * @param {string} sectionName
7
7
  * @param {object} [options]
8
8
  * @param {string} [options.lang]
9
9
  * @param {boolean} [options.zh]
@@ -1,31 +1,25 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
 
3
3
  /**
4
- * @file docs.detail.section leaf — load a single section of a topic.
4
+ * @file docs.detail.section leaf — load a single named section of a topic.
5
5
  *
6
6
  * @input A topic name, a section query, and optional {lang, zh, dense}. Resolves
7
- * and loads the topic via the shared adapter, then finds the section by its
8
- * stable key, then by exact title, then by a title that contains the query.
9
- * @output { type: 'docs.detail.section', data: ReferenceSection } with any
10
- * token-ref blocks inlined — matching `astryx --json docs <topic> <section>`.
11
- * Throws ERR_UNKNOWN_SECTION when nothing matches, or when the query matches
12
- * more than one section (the candidates come back as suggestions).
7
+ * and loads the topic via the shared adapter, then finds the first section
8
+ * whose title contains the (case-insensitive) query.
9
+ * @output { type: 'docs.detail.section', data: ReferenceSection } — matching
10
+ * `xds --json docs <topic> <section>`. Throws ERR_UNKNOWN_SECTION when no
11
+ * section title matches.
13
12
  * @position Leaf nested under api/docs/detail. Shares discovery/loading/
14
13
  * topic-resolution with the detail leaf via _adapter.mjs.
15
14
  */
16
15
 
17
16
  import {AstryxError} from '../../../error.mjs';
18
17
  import {ERROR_CODES} from '../../../../foundation/response/error-codes.mjs';
19
- import {
20
- findDocSection,
21
- sectionKey,
22
- } from '../../../../foundation/discovery/docs-section-key.mjs';
23
18
  import {resolveTopicDocs} from '../../_adapter.mjs';
24
- import {resolveTokenRefs} from '../detail.mjs';
25
19
 
26
20
  /**
27
21
  * @param {string} topic
28
- * @param {string} sectionName a section key, or a title (or part of one)
22
+ * @param {string} sectionName
29
23
  * @param {object} [options]
30
24
  * @param {string} [options.lang]
31
25
  * @param {boolean} [options.zh]
@@ -34,9 +28,10 @@ import {resolveTokenRefs} from '../detail.mjs';
34
28
  * @returns {Promise<import('../../docs.type.mjs').DocsDetailSectionResponse>}
35
29
  */
36
30
  export async function section(topic, sectionName, options = {}) {
37
- // An empty section name must error, not resolve to the first section. The
38
- // docs() dispatcher routes a falsy section to the topic, but the leaf must be
39
- // safe on its own; a non-string would otherwise throw a raw TypeError.
31
+ // An empty section name must error, not resolve to the first section via
32
+ // `.includes('')`. The docs() dispatcher routes a falsy section to detail, but
33
+ // the leaf must be safe on its own. A non-string would also throw a raw
34
+ // TypeError below (`.toLowerCase()`) → downgrades to ERR_UNKNOWN.
40
35
  if (typeof sectionName !== 'string' || !sectionName.trim()) {
41
36
  throw new AstryxError(
42
37
  'A section name is required',
@@ -44,31 +39,16 @@ export async function section(topic, sectionName, options = {}) {
44
39
  ERROR_CODES.ERR_UNKNOWN_SECTION,
45
40
  );
46
41
  }
47
- const {catalog, docsData, lang} = await resolveTopicDocs(topic, options);
42
+ const {docsData} = await resolveTopicDocs(topic, options);
48
43
 
49
- const {section: match, candidates} = findDocSection(
50
- docsData.sections,
51
- sectionName,
52
- );
44
+ const normalizedSection = sectionName.toLowerCase();
45
+ const match = docsData.sections.find(s => s.title.toLowerCase().includes(normalizedSection));
53
46
  if (!match) {
54
- const ambiguous = candidates.length > 1;
55
47
  throw new AstryxError(
56
- ambiguous
57
- ? `Section "${sectionName}" matches ${candidates.length} sections in "${topic}". Read one by its key.`
58
- : `Section "${sectionName}" not found in "${topic}"`,
59
- (ambiguous ? candidates : docsData.sections).map(s => ({
60
- name: sectionKey(s),
61
- reason: s.title,
62
- })),
48
+ `Section "${sectionName}" not found in "${topic}"`,
49
+ docsData.sections.map(s => ({name: s.title, reason: 'available section'})),
63
50
  ERROR_CODES.ERR_UNKNOWN_SECTION,
64
51
  );
65
52
  }
66
- // A section read on its own inlines its token refs, as the whole topic does;
67
- // otherwise a section that is only a token-ref prints blank.
68
- const {sections} = await resolveTokenRefs(
69
- {...docsData, sections: [match]},
70
- catalog,
71
- {lang},
72
- );
73
- return {type: 'docs.detail.section', data: sections[0]};
53
+ return {type: 'docs.detail.section', data: match};
74
54
  }
@@ -10,7 +10,6 @@
10
10
  import {describe, it, expect} from 'vitest';
11
11
  import {section} from './section.mjs';
12
12
  import {AstryxError} from '../../../error.mjs';
13
- import {loadDocsCatalog, loadTopicDoc} from '../../_adapter.mjs';
14
13
 
15
14
  const SLOW = 30_000;
16
15
 
@@ -40,43 +39,4 @@ describe('docs.detail.section leaf', () => {
40
39
  code: 'ERR_UNKNOWN_SECTION',
41
40
  });
42
41
  }, SLOW);
43
-
44
- it('reads a section by its stable key', async () => {
45
- const doc = await loadTopicDoc((await loadDocsCatalog()).resolve('theme'));
46
- const target = doc.sections[doc.sections.length - 1];
47
- const res = await section('theme', target.id);
48
- expect(res.data.title).toBe(target.title);
49
- expect(res.data.id).toBe(target.id);
50
- }, SLOW);
51
-
52
- it('refuses a query that matches more than one section', async () => {
53
- const err = await section('theme', 'e').catch(e => e);
54
- expect(err).toBeInstanceOf(AstryxError);
55
- expect(err.code).toBe('ERR_UNKNOWN_SECTION');
56
- expect(err.message).toMatch(/matches \d+ sections/);
57
- expect(err.suggestions.length).toBeGreaterThan(1);
58
- }, SLOW);
59
-
60
- it.each([null, 'zh', 'dense'])(
61
- 'inlines token refs when a section is read on its own (lang %s)',
62
- async lang => {
63
- const catalog = await loadDocsCatalog();
64
- let checked = 0;
65
- for (const entry of catalog.entries()) {
66
- const doc = await loadTopicDoc(entry);
67
- for (const own of doc.sections) {
68
- if (!own.content.some(block => block.type === 'token-ref')) continue;
69
- const res = await section(entry.name, own.id, lang ? {lang} : {});
70
- expect(res.data.content.length).toBeGreaterThan(0);
71
- expect(res.data.content.some(block => block.type === 'token-ref')).toBe(
72
- false,
73
- );
74
- expect(JSON.stringify(res.data.content)).not.toContain('[token-ref:');
75
- checked += 1;
76
- }
77
- }
78
- expect(checked).toBeGreaterThan(0);
79
- },
80
- SLOW,
81
- );
82
42
  });
@@ -8,12 +8,9 @@
8
8
  * @param {string} [options.lang]
9
9
  * @param {boolean} [options.zh]
10
10
  * @param {boolean} [options.dense]
11
- * @param {boolean} [options.index] return the topic's section index instead of
12
- * the whole doc
13
11
  * @param {string} [options.cwd]
14
12
  * @returns {Promise<
15
13
  * import('./docs.type.mjs').DocsListResponse |
16
- * import('./docs.type.mjs').DocsIndexResponse |
17
14
  * import('./docs.type.mjs').DocsDetailResponse |
18
15
  * import('./docs.type.mjs').DocsDetailSectionResponse
19
16
  * >}
@@ -22,11 +19,9 @@ export function docs(topic?: string, section?: string, options?: {
22
19
  lang?: string | undefined;
23
20
  zh?: boolean | undefined;
24
21
  dense?: boolean | undefined;
25
- index?: boolean | undefined;
26
22
  cwd?: string | undefined;
27
- }): Promise<import("./docs.type.mjs").DocsListResponse | import("./docs.type.mjs").DocsIndexResponse | import("./docs.type.mjs").DocsDetailResponse | import("./docs.type.mjs").DocsDetailSectionResponse>;
23
+ }): Promise<import("./docs.type.mjs").DocsListResponse | import("./docs.type.mjs").DocsDetailResponse | import("./docs.type.mjs").DocsDetailSectionResponse>;
28
24
  import { list } from './list/list.mjs';
29
- import { index } from './index/index.mjs';
30
25
  import { detail } from './detail/detail.mjs';
31
26
  import { section as sectionLeaf } from './detail/section/section.mjs';
32
- export { list, index, detail, sectionLeaf as section };
27
+ export { list, detail, sectionLeaf as section };
@@ -13,20 +13,18 @@ export const doc = {
13
13
  name: 'docs',
14
14
  displayName: 'docs()',
15
15
  summary:
16
- 'Read the reference docs: list every topic, one topic\'s sections, one section, or a whole topic.',
16
+ 'Read the reference docs: list every topic, one topic, or a single section of a topic.',
17
17
  description:
18
- 'No topic lists every reference-doc topic; a topic returns that full ' +
19
- 'ReferenceDoc; `index: true` returns the topic\'s section index instead ' +
20
- '(each section\'s key, title, and summary); a topic plus a section returns ' +
21
- 'that one section, found by its key, then its exact title, then a unique ' +
22
- 'part of its title (an ambiguous query is refused). Token-ref blocks are ' +
23
- 'inlined in every read. The topic set is the CLI\'s own docs plus the ' +
18
+ 'Routes on its arguments: no topic lists every reference-doc topic; a topic ' +
19
+ 'returns that full ReferenceDoc (with token-ref blocks inlined); a topic ' +
20
+ 'plus a section returns the first section whose title contains the ' +
21
+ '(case-insensitive) query. The topic set is the CLI\'s own docs plus the ' +
24
22
  'ones the project\'s configured integrations contribute, including any ' +
25
23
  'topic an integration replaces or extends, so it depends on the cwd. ' +
26
24
  'Overlay options select localized or dense variants.',
27
25
  importPath: '@astryxdesign/cli/api',
28
26
  signature:
29
- 'docs(topic?: string, section?: string, options?: DocsOptions): Promise<DocsListResponse | DocsIndexResponse | DocsDetailResponse | DocsDetailSectionResponse>',
27
+ 'docs(topic?: string, section?: string, options?: DocsOptions): Promise<DocsListResponse | DocsDetailResponse | DocsDetailSectionResponse>',
30
28
  keywords: [
31
29
  'docs',
32
30
  'documentation',
@@ -48,7 +46,7 @@ export const doc = {
48
46
  name: 'section',
49
47
  type: 'string',
50
48
  description:
51
- "Section to return: its key (from the topic's index), its title, or a unique part of its title (case-insensitive).",
49
+ 'Section within the topic to return; matches the first section title that contains this (case-insensitive).',
52
50
  },
53
51
  {
54
52
  name: 'options.lang',
@@ -65,12 +63,6 @@ export const doc = {
65
63
  type: 'boolean',
66
64
  description: 'Return the token-efficient dense doc variant.',
67
65
  },
68
- {
69
- name: 'options.index',
70
- type: 'boolean',
71
- description:
72
- "Return the topic's section index (each section's key, title, and summary) instead of the whole doc.",
73
- },
74
66
  {
75
67
  name: 'options.cwd',
76
68
  type: 'string',
@@ -89,15 +81,10 @@ export const doc = {
89
81
  description:
90
82
  "One topic's full ReferenceDoc, with token-ref blocks inlined.",
91
83
  },
92
- {
93
- type: 'docs.index',
94
- description:
95
- "One topic's section index (index: true): {name, title, description, sections: [{id, title, summary}]}.",
96
- },
97
84
  {
98
85
  type: 'docs.detail.section',
99
86
  description:
100
- 'One ReferenceSection of the topic, found by key or title, with token-ref blocks inlined.',
87
+ 'A single ReferenceSection of the topic: the first whose title contains the section query.',
101
88
  },
102
89
  ],
103
90
  throws: [
@@ -107,17 +94,13 @@ export const doc = {
107
94
  },
108
95
  {
109
96
  code: 'ERR_UNKNOWN_SECTION',
110
- when: 'a section is requested but is empty, matches no section, or matches more than one',
97
+ when: 'a section is requested but is empty or matches no section title in the topic',
111
98
  },
112
99
  ],
113
100
  examples: [
114
101
  {label: 'List topics', code: 'const r = await docs();'},
115
102
  {label: 'Load a topic', code: "await docs('principles');"},
116
- {
117
- label: "A topic's sections",
118
- code: "await docs('principles', undefined, {index: true});",
119
- },
120
- {label: 'One section by key', code: "await docs('tokens', 'spacing');"},
103
+ {label: 'One section', code: "await docs('tokens', 'spacing');"},
121
104
  ],
122
105
  command: 'docs',
123
106
  related: ['search', 'component', 'hook', 'template'],
package/api/docs/docs.mjs CHANGED
@@ -3,27 +3,24 @@
3
3
  /**
4
4
  * @file Programmatic API for the docs command.
5
5
  *
6
- * Dispatcher + barrel. `docs()` routes by argument shape into one of four
6
+ * Dispatcher + barrel. `docs()` routes by argument shape into one of three
7
7
  * leaves, each projecting into a single { type, data } envelope:
8
8
  *
9
- * docs() -> list -> docs.list
10
- * docs(topic) -> detail -> docs.detail
11
- * docs(topic, undefined, {index: true}) -> index -> docs.index
12
- * docs(topic, section) -> section -> docs.detail.section
9
+ * docs() -> list -> docs.list
10
+ * docs(topic) -> detail -> docs.detail
11
+ * docs(topic, section) -> section -> docs.detail.section
13
12
  *
14
- * A topic read returns the whole doc, as it always has. The index is how a
15
- * reader works progressively instead: list the topic's sections, then read one
16
- * by its key. The leaves live in list/, index/, detail/, and detail/section/;
17
- * the discovery, overlay loading, and topic resolution they share sit in
18
- * _adapter.mjs.
13
+ * The leaves live in list/, detail/, and detail/section/; the discovery,
14
+ * overlay loading, and topic resolution they share sit in _adapter.mjs. This
15
+ * module keeps the same `docs` export (and re-exports the leaves) so
16
+ * api/index.mjs and the CLI consumer import from here unchanged.
19
17
  */
20
18
 
21
19
  import {list} from './list/list.mjs';
22
- import {index} from './index/index.mjs';
23
20
  import {detail} from './detail/detail.mjs';
24
21
  import {section as sectionLeaf} from './detail/section/section.mjs';
25
22
 
26
- export {list, index, detail, sectionLeaf as section};
23
+ export {list, detail, sectionLeaf as section};
27
24
 
28
25
  /**
29
26
  * @param {string} [topic]
@@ -32,12 +29,9 @@ export {list, index, detail, sectionLeaf as section};
32
29
  * @param {string} [options.lang]
33
30
  * @param {boolean} [options.zh]
34
31
  * @param {boolean} [options.dense]
35
- * @param {boolean} [options.index] return the topic's section index instead of
36
- * the whole doc
37
32
  * @param {string} [options.cwd]
38
33
  * @returns {Promise<
39
34
  * import('./docs.type.mjs').DocsListResponse |
40
- * import('./docs.type.mjs').DocsIndexResponse |
41
35
  * import('./docs.type.mjs').DocsDetailResponse |
42
36
  * import('./docs.type.mjs').DocsDetailSectionResponse
43
37
  * >}
@@ -45,6 +39,5 @@ export {list, index, detail, sectionLeaf as section};
45
39
  export async function docs(topic, section, options = {}) {
46
40
  if (!topic) return list(options);
47
41
  if (section) return sectionLeaf(topic, section, options);
48
- if (options.index) return index(topic, options);
49
42
  return detail(topic, options);
50
43
  }
@@ -33,12 +33,6 @@ describe('docs() dispatcher routing', () => {
33
33
  expect(r.type).toBe('docs.detail');
34
34
  }, SLOW);
35
35
 
36
- it('topic + index -> docs.index', async () => {
37
- const {data} = await docs();
38
- const r = await docs(data[0].topic, undefined, {index: true});
39
- expect(r.type).toBe('docs.index');
40
- }, SLOW);
41
-
42
36
  it('topic + section -> docs.detail.section', async () => {
43
37
  const {data} = await docs();
44
38
  let routed = null;