@astryxdesign/cli 0.6.3-canary.ea2f048 → 0.6.3-canary.ebaebc4

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 (188) hide show
  1. package/README.md +2 -1
  2. package/api/build/build.type.d.mts +2 -2
  3. package/api/build/build.type.mjs +2 -2
  4. package/api/component/component.type.d.mts +6 -6
  5. package/api/component/component.type.mjs +19 -19
  6. package/api/discover/discover.type.d.mts +4 -4
  7. package/api/discover/discover.type.mjs +10 -10
  8. package/api/docs/_adapter.d.mts +37 -24
  9. package/api/docs/_adapter.mjs +169 -83
  10. package/api/docs/compiled-topics.test.mjs +78 -0
  11. package/api/docs/detail/detail.mjs +14 -63
  12. package/api/docs/detail/section/section.d.mts +1 -1
  13. package/api/docs/detail/section/section.mjs +44 -20
  14. package/api/docs/detail/section/section.test.mjs +41 -0
  15. package/api/docs/docs.d.mts +7 -2
  16. package/api/docs/docs.doc.mjs +27 -10
  17. package/api/docs/docs.mjs +16 -9
  18. package/api/docs/docs.test.mjs +6 -0
  19. package/api/docs/docs.type.d.mts +40 -3
  20. package/api/docs/docs.type.mjs +36 -8
  21. package/api/docs/index/index.d.mts +18 -0
  22. package/api/docs/index/index.mjs +32 -0
  23. package/api/docs/index/index.test.mjs +62 -0
  24. package/api/docs/integrationDocs.test.mjs +106 -0
  25. package/api/doctor/doctor.d.mts +48 -0
  26. package/api/doctor/doctor.mjs +232 -0
  27. package/api/doctor/doctor.test.mjs +196 -0
  28. package/api/hook/hook.type.d.mts +3 -3
  29. package/api/hook/hook.type.mjs +11 -11
  30. package/api/hook/list/list.d.mts +1 -1
  31. package/api/integration/add-contribution.mjs +5 -3
  32. package/api/integration/add-contribution.test.mjs +4 -4
  33. package/api/integration/integration-authoring.type.d.mts +1 -1
  34. package/api/integration/pack-check.mjs +49 -7
  35. package/api/integration/pack-check.test.mjs +249 -0
  36. package/api/search/search.d.mts +1 -1
  37. package/api/search/search.mjs +5 -5
  38. package/api/search/search.type.d.mts +2 -2
  39. package/api/search/search.type.mjs +1 -1
  40. package/api/swizzle/swizzle.type.d.mts +2 -2
  41. package/api/swizzle/swizzle.type.mjs +2 -2
  42. package/api/template/template.d.mts +1 -1
  43. package/api/template/template.type.d.mts +6 -6
  44. package/api/template/template.type.mjs +12 -12
  45. package/api/theme/build/build.mjs +20 -6
  46. package/api/theme/build/build.test.mjs +127 -0
  47. package/api/theme/palette/generate/generate.mjs +1 -1
  48. package/api/theme/palette/generate/generator.d.mts +10 -13
  49. package/api/theme/palette/generate/generator.mjs +7 -3
  50. package/api/theme/theme.type.d.mts +170 -11
  51. package/api/theme/theme.type.mjs +94 -27
  52. package/api/upgrade/_adapter.mjs +71 -5
  53. package/api/upgrade/project-context.test.mjs +272 -0
  54. package/api/upgrade/upgrade.doc.mjs +4 -3
  55. package/api/upgrade/upgrade.type.d.mts +5 -5
  56. package/api/upgrade/upgrade.type.mjs +11 -11
  57. package/assets/codemods/integration-discovery.mjs +40 -2
  58. package/assets/codemods/integration-discovery.test.mjs +58 -0
  59. package/assets/codemods/transforms/v0.3.0/__tests__/unwrap-authoring-factories.test.mjs +27 -5
  60. package/assets/codemods/transforms/v0.3.0/unwrap-authoring-factories.mjs +20 -5
  61. package/assets/docs/README.md +9 -0
  62. package/assets/docs/authoring.doc.mjs +14 -0
  63. package/assets/docs/cli-integrations.doc.mjs +86 -15
  64. package/assets/docs/styling-libraries.doc.mjs +1 -1
  65. package/assets/docs/working-with-ai.doc.mjs +1 -1
  66. package/authoring/_shared/contract.ts +22 -0
  67. package/authoring/codemod/codemod.doc.mjs +6 -1
  68. package/authoring/codemod/parse.d.mts +8 -8
  69. package/authoring/codemod/parse.mjs +8 -6
  70. package/authoring/config/parse.d.mts +13 -13
  71. package/authoring/config/parse.mjs +8 -8
  72. package/authoring/config/type.ts +3 -3
  73. package/authoring/debug/parse.d.mts +5 -5
  74. package/authoring/debug/parse.mjs +3 -3
  75. package/authoring/doctypes/_schema.d.mts +788 -23
  76. package/authoring/doctypes/_schema.mjs +492 -39
  77. package/authoring/doctypes/base/graph-fields.doc.d.mts +9 -0
  78. package/authoring/doctypes/base/graph-fields.doc.mjs +62 -0
  79. package/authoring/doctypes/base/type.ts +40 -0
  80. package/authoring/doctypes/command/command.doc.mjs +3 -2
  81. package/authoring/doctypes/command/parse.d.mts +2 -2
  82. package/authoring/doctypes/command/parse.mjs +1 -1
  83. package/authoring/doctypes/command/type.ts +3 -2
  84. package/authoring/doctypes/component/component.doc.mjs +6 -3
  85. package/authoring/doctypes/component/parse.d.mts +2 -2
  86. package/authoring/doctypes/component/parse.mjs +1 -1
  87. package/authoring/doctypes/component/type.ts +4 -3
  88. package/authoring/doctypes/enum/parse.d.mts +2 -2
  89. package/authoring/doctypes/enum/parse.mjs +1 -1
  90. package/authoring/doctypes/enum/type.ts +3 -1
  91. package/authoring/doctypes/function/function.doc.mjs +4 -0
  92. package/authoring/doctypes/function/parse.d.mts +2 -2
  93. package/authoring/doctypes/function/parse.mjs +1 -1
  94. package/authoring/doctypes/function/type.ts +6 -2
  95. package/authoring/doctypes/hook/hook.doc.mjs +4 -0
  96. package/authoring/doctypes/hook/parse.d.mts +2 -2
  97. package/authoring/doctypes/hook/parse.mjs +1 -1
  98. package/authoring/doctypes/hook/type.ts +3 -2
  99. package/authoring/doctypes/legacy.d.mts +8 -6
  100. package/authoring/doctypes/legacy.mjs +5 -4
  101. package/authoring/doctypes/load-contract.test.mjs +207 -0
  102. package/authoring/doctypes/namespace/namespace.doc.d.mts +9 -0
  103. package/authoring/doctypes/namespace/namespace.doc.mjs +132 -0
  104. package/authoring/doctypes/namespace/parse.d.mts +12 -0
  105. package/authoring/doctypes/namespace/parse.mjs +25 -0
  106. package/authoring/doctypes/namespace/parse.test.mjs +165 -0
  107. package/authoring/doctypes/namespace/type.ts +71 -0
  108. package/authoring/doctypes/parse.d.mts +20 -18
  109. package/authoring/doctypes/parse.mjs +16 -10
  110. package/authoring/doctypes/parse.test.mjs +77 -3
  111. package/authoring/doctypes/reference/parse.d.mts +2 -2
  112. package/authoring/doctypes/reference/parse.mjs +8 -5
  113. package/authoring/doctypes/reference/reference.doc.mjs +17 -4
  114. package/authoring/doctypes/reference/type.ts +51 -5
  115. package/authoring/doctypes/schema/parse.d.mts +2 -2
  116. package/authoring/doctypes/schema/parse.mjs +1 -1
  117. package/authoring/doctypes/schema/type.ts +3 -2
  118. package/authoring/doctypes/template/parse.d.mts +92 -1
  119. package/authoring/doctypes/template/parse.mjs +36 -2
  120. package/authoring/doctypes/template/parse.test.mjs +8 -2
  121. package/authoring/doctypes/template/template.doc.mjs +4 -0
  122. package/authoring/doctypes/template/type.ts +5 -2
  123. package/authoring/doctypes/types.ts +10 -9
  124. package/authoring/gap-report/parse.d.mts +10 -10
  125. package/authoring/gap-report/parse.mjs +6 -6
  126. package/authoring/gap-report/type.ts +1 -1
  127. package/authoring/identity/identity.doc.d.mts +9 -0
  128. package/authoring/identity/identity.doc.mjs +61 -0
  129. package/authoring/identity/type.ts +132 -0
  130. package/authoring/index.d.mts +1 -0
  131. package/authoring/index.d.ts +49 -17
  132. package/authoring/index.mjs +1 -0
  133. package/authoring/integration/integration.doc.mjs +13 -6
  134. package/authoring/integration/parse.d.mts +2 -2
  135. package/authoring/integration/parse.mjs +1 -1
  136. package/authoring/integration/parse.test.mjs +10 -1
  137. package/authoring/integration/schema.d.mts +6 -4
  138. package/authoring/integration/schema.mjs +9 -3
  139. package/authoring/integration/type.ts +23 -6
  140. package/authoring/shadcn/receipt.d.mts +6 -6
  141. package/clients/cli/commands/docs.doc.mjs +13 -3
  142. package/clients/cli/commands/docs.mjs +121 -21
  143. package/clients/cli/commands/docs.test.mjs +88 -0
  144. package/clients/cli/commands/integration-authoring.test.mjs +13 -9
  145. package/clients/cli/commands/theme-palette-generate.doc.mjs +8 -4
  146. package/clients/cli/commands/upgrade.doc.mjs +2 -2
  147. package/clients/cli/formatters/index.mjs +162 -1
  148. package/clients/cli/formatters/index.test.mjs +91 -0
  149. package/clients/cli/lib/manifest.mjs +7 -2
  150. package/foundation/config/project.mjs +21 -6
  151. package/foundation/discovery/authoring-self-docs.d.mts +69 -0
  152. package/foundation/discovery/authoring-self-docs.mjs +214 -0
  153. package/foundation/discovery/authoring-self-docs.test.mjs +154 -0
  154. package/foundation/discovery/component-discovery.d.mts +1 -1
  155. package/foundation/discovery/component-discovery.mjs +2 -1
  156. package/foundation/discovery/docs-discovery.d.mts +11 -4
  157. package/foundation/discovery/docs-discovery.mjs +208 -88
  158. package/foundation/discovery/docs-discovery.test.mjs +279 -13
  159. package/foundation/discovery/docs-output-budget.d.mts +28 -0
  160. package/foundation/discovery/docs-output-budget.mjs +50 -0
  161. package/foundation/discovery/docs-section-key.d.mts +98 -0
  162. package/foundation/discovery/docs-section-key.mjs +221 -0
  163. package/foundation/discovery/docs-section-key.test.mjs +224 -0
  164. package/foundation/discovery/template-adapter.mjs +2 -1
  165. package/foundation/discovery/theming-targets.test.mjs +4 -0
  166. package/foundation/doc-compiler/compile.d.mts +162 -0
  167. package/foundation/doc-compiler/compile.mjs +262 -0
  168. package/foundation/doc-compiler/doc-compiler.test.mjs +687 -0
  169. package/foundation/doc-compiler/ir.d.mts +9 -0
  170. package/foundation/doc-compiler/ir.mjs +287 -0
  171. package/foundation/doc-compiler/lenses.d.mts +33 -0
  172. package/foundation/doc-compiler/lenses.mjs +127 -0
  173. package/foundation/identity/provider-identity.d.mts +90 -0
  174. package/foundation/identity/provider-identity.mjs +320 -0
  175. package/foundation/identity/provider-identity.test.mjs +254 -0
  176. package/foundation/identity/providers.d.mts +7 -0
  177. package/foundation/identity/providers.mjs +16 -0
  178. package/foundation/integrations/autolink.mjs +12 -5
  179. package/foundation/integrations/integration-warnings.mjs +6 -0
  180. package/foundation/integrations/integrations.d.mts +46 -2
  181. package/foundation/integrations/integrations.mjs +167 -8
  182. package/foundation/integrations/integrations.test.mjs +384 -1
  183. package/foundation/integrations/provider-conflicts.test.mjs +125 -0
  184. package/foundation/integrations/validate-contributions.d.mts +2 -0
  185. package/foundation/integrations/validate-contributions.mjs +10 -0
  186. package/foundation/response/json-contract.test.mjs +46 -17
  187. package/foundation/response/response-types.doc.mjs +6 -1
  188. package/package.json +9 -11
@@ -8,9 +8,12 @@
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
11
13
  * @param {string} [options.cwd]
12
14
  * @returns {Promise<
13
15
  * import('./docs.type.mjs').DocsListResponse |
16
+ * import('./docs.type.mjs').DocsIndexResponse |
14
17
  * import('./docs.type.mjs').DocsDetailResponse |
15
18
  * import('./docs.type.mjs').DocsDetailSectionResponse
16
19
  * >}
@@ -19,9 +22,11 @@ export function docs(topic?: string, section?: string, options?: {
19
22
  lang?: string | undefined;
20
23
  zh?: boolean | undefined;
21
24
  dense?: boolean | undefined;
25
+ index?: boolean | undefined;
22
26
  cwd?: string | undefined;
23
- }): Promise<import("./docs.type.mjs").DocsListResponse | import("./docs.type.mjs").DocsDetailResponse | import("./docs.type.mjs").DocsDetailSectionResponse>;
27
+ }): Promise<import("./docs.type.mjs").DocsListResponse | import("./docs.type.mjs").DocsIndexResponse | import("./docs.type.mjs").DocsDetailResponse | import("./docs.type.mjs").DocsDetailSectionResponse>;
24
28
  import { list } from './list/list.mjs';
29
+ import { index } from './index/index.mjs';
25
30
  import { detail } from './detail/detail.mjs';
26
31
  import { section as sectionLeaf } from './detail/section/section.mjs';
27
- export { list, detail, sectionLeaf as section };
32
+ export { list, index, detail, sectionLeaf as section };
@@ -13,18 +13,20 @@ export const doc = {
13
13
  name: 'docs',
14
14
  displayName: 'docs()',
15
15
  summary:
16
- 'Read the reference docs: list every topic, one topic, or a single section of a topic.',
16
+ 'Read the reference docs: list every topic, one topic\'s sections, one section, or a whole topic.',
17
17
  description:
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 ' +
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 ' +
22
24
  'ones the project\'s configured integrations contribute, including any ' +
23
25
  'topic an integration replaces or extends, so it depends on the cwd. ' +
24
26
  'Overlay options select localized or dense variants.',
25
27
  importPath: '@astryxdesign/cli/api',
26
28
  signature:
27
- 'docs(topic?: string, section?: string, options?: DocsOptions): Promise<DocsListResponse | DocsDetailResponse | DocsDetailSectionResponse>',
29
+ 'docs(topic?: string, section?: string, options?: DocsOptions): Promise<DocsListResponse | DocsIndexResponse | DocsDetailResponse | DocsDetailSectionResponse>',
28
30
  keywords: [
29
31
  'docs',
30
32
  'documentation',
@@ -46,7 +48,7 @@ export const doc = {
46
48
  name: 'section',
47
49
  type: 'string',
48
50
  description:
49
- 'Section within the topic to return; matches the first section title that contains this (case-insensitive).',
51
+ "Section to return: its key (from the topic's index), its title, or a unique part of its title (case-insensitive).",
50
52
  },
51
53
  {
52
54
  name: 'options.lang',
@@ -63,6 +65,12 @@ export const doc = {
63
65
  type: 'boolean',
64
66
  description: 'Return the token-efficient dense doc variant.',
65
67
  },
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
+ },
66
74
  {
67
75
  name: 'options.cwd',
68
76
  type: 'string',
@@ -81,10 +89,15 @@ export const doc = {
81
89
  description:
82
90
  "One topic's full ReferenceDoc, with token-ref blocks inlined.",
83
91
  },
92
+ {
93
+ type: 'docs.index',
94
+ description:
95
+ "One topic's section index (index: true): {name, title, description, sections: [{id, title, summary}]}.",
96
+ },
84
97
  {
85
98
  type: 'docs.detail.section',
86
99
  description:
87
- 'A single ReferenceSection of the topic: the first whose title contains the section query.',
100
+ 'One ReferenceSection of the topic, found by key or title, with token-ref blocks inlined.',
88
101
  },
89
102
  ],
90
103
  throws: [
@@ -94,13 +107,17 @@ export const doc = {
94
107
  },
95
108
  {
96
109
  code: 'ERR_UNKNOWN_SECTION',
97
- when: 'a section is requested but is empty or matches no section title in the topic',
110
+ when: 'a section is requested but is empty, matches no section, or matches more than one',
98
111
  },
99
112
  ],
100
113
  examples: [
101
114
  {label: 'List topics', code: 'const r = await docs();'},
102
115
  {label: 'Load a topic', code: "await docs('principles');"},
103
- {label: 'One section', code: "await docs('tokens', 'spacing');"},
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');"},
104
121
  ],
105
122
  command: 'docs',
106
123
  related: ['search', 'component', 'hook', 'template'],
package/api/docs/docs.mjs CHANGED
@@ -3,24 +3,27 @@
3
3
  /**
4
4
  * @file Programmatic API for the docs command.
5
5
  *
6
- * Dispatcher + barrel. `docs()` routes by argument shape into one of three
6
+ * Dispatcher + barrel. `docs()` routes by argument shape into one of four
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, section) -> section -> docs.detail.section
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
12
13
  *
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.
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.
17
19
  */
18
20
 
19
21
  import {list} from './list/list.mjs';
22
+ import {index} from './index/index.mjs';
20
23
  import {detail} from './detail/detail.mjs';
21
24
  import {section as sectionLeaf} from './detail/section/section.mjs';
22
25
 
23
- export {list, detail, sectionLeaf as section};
26
+ export {list, index, detail, sectionLeaf as section};
24
27
 
25
28
  /**
26
29
  * @param {string} [topic]
@@ -29,9 +32,12 @@ export {list, detail, sectionLeaf as section};
29
32
  * @param {string} [options.lang]
30
33
  * @param {boolean} [options.zh]
31
34
  * @param {boolean} [options.dense]
35
+ * @param {boolean} [options.index] return the topic's section index instead of
36
+ * the whole doc
32
37
  * @param {string} [options.cwd]
33
38
  * @returns {Promise<
34
39
  * import('./docs.type.mjs').DocsListResponse |
40
+ * import('./docs.type.mjs').DocsIndexResponse |
35
41
  * import('./docs.type.mjs').DocsDetailResponse |
36
42
  * import('./docs.type.mjs').DocsDetailSectionResponse
37
43
  * >}
@@ -39,5 +45,6 @@ export {list, detail, sectionLeaf as section};
39
45
  export async function docs(topic, section, options = {}) {
40
46
  if (!topic) return list(options);
41
47
  if (section) return sectionLeaf(topic, section, options);
48
+ if (options.index) return index(topic, options);
42
49
  return detail(topic, options);
43
50
  }
@@ -33,6 +33,12 @@ 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
+
36
42
  it('topic + section -> docs.detail.section', async () => {
37
43
  const {data} = await docs();
38
44
  let routed = null;
@@ -2,7 +2,7 @@
2
2
  // DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
3
3
 
4
4
  /**
5
- * xds --json docs
5
+ * astryx --json docs
6
6
  */
7
7
  export type DocsListResponse = {
8
8
  type: "docs.list";
@@ -23,14 +23,46 @@ export type DocsListEntry = {
23
23
  replaces?: string | undefined;
24
24
  };
25
25
  /**
26
- * xds --json docs <topic>
26
+ * astryx --json docs <topic> --index
27
+ */
28
+ export type DocsIndexResponse = {
29
+ type: "docs.index";
30
+ data: DocsIndex;
31
+ };
32
+ /**
33
+ * astryx --json docs <topic>
27
34
  */
28
35
  export type DocsDetailResponse = {
29
36
  type: "docs.detail";
30
37
  data: import("@astryxdesign/cli/authoring").ReferenceDoc;
31
38
  };
32
39
  /**
33
- * xds --json docs <topic> <section>
40
+ * The section index of one topic: what the topic is, and the key each section
41
+ * is read by.
42
+ */
43
+ export type DocsIndex = {
44
+ /**
45
+ * the topic
46
+ */
47
+ name: string;
48
+ title: string;
49
+ description: string;
50
+ sections: DocsIndexSection[];
51
+ };
52
+ export type DocsIndexSection = {
53
+ /**
54
+ * stable key; pass it as the section argument
55
+ */
56
+ id: string;
57
+ title: string;
58
+ /**
59
+ * the section's first line of text, at most 240
60
+ * characters
61
+ */
62
+ summary: string;
63
+ };
64
+ /**
65
+ * astryx --json docs <topic> <section>
34
66
  */
35
67
  export type DocsDetailSectionResponse = {
36
68
  type: "docs.detail.section";
@@ -43,6 +75,11 @@ export type DocsOptions = {
43
75
  lang?: string | undefined;
44
76
  zh?: boolean | undefined;
45
77
  dense?: boolean | undefined;
78
+ /**
79
+ * return a topic's section index instead of its
80
+ * whole doc
81
+ */
82
+ index?: boolean | undefined;
46
83
  /**
47
84
  * project directory whose configured integrations
48
85
  * contribute topics; defaults to process.cwd()
@@ -4,16 +4,17 @@
4
4
  * @file Colocated types for the `docs` command — source of truth for the docs
5
5
  * command JSON responses. `types/docs.d.ts` re-exports these.
6
6
  *
7
- * Invocation -> type discriminator
7
+ * Invocation -> type discriminator
8
8
  * ------------------------------------------------------------
9
- * xds --json docs -> docs.list
10
- * xds --json docs <topic> -> docs.detail
11
- * xds --json docs <topic> <section> -> docs.detail.section
12
- * (unknown topic/section) -> CLIError
9
+ * astryx --json docs -> docs.list
10
+ * astryx --json docs <topic> -> docs.detail
11
+ * astryx --json docs <topic> --index -> docs.index
12
+ * astryx --json docs <topic> <section> -> docs.detail.section
13
+ * (unknown topic/section) -> CLIError
13
14
  */
14
15
 
15
16
  /**
16
- * xds --json docs
17
+ * astryx --json docs
17
18
  * @typedef {object} DocsListResponse
18
19
  * @property {'docs.list'} type
19
20
  * @property {DocsListEntry[]} data
@@ -30,14 +31,39 @@
30
31
  */
31
32
 
32
33
  /**
33
- * xds --json docs <topic>
34
+ * astryx --json docs <topic> --index
35
+ * @typedef {object} DocsIndexResponse
36
+ * @property {'docs.index'} type
37
+ * @property {DocsIndex} data
38
+ */
39
+
40
+ /**
41
+ * astryx --json docs <topic>
34
42
  * @typedef {object} DocsDetailResponse
35
43
  * @property {'docs.detail'} type
36
44
  * @property {import('@astryxdesign/cli/authoring').ReferenceDoc} data
37
45
  */
38
46
 
39
47
  /**
40
- * xds --json docs <topic> <section>
48
+ * The section index of one topic: what the topic is, and the key each section
49
+ * is read by.
50
+ * @typedef {object} DocsIndex
51
+ * @property {string} name the topic
52
+ * @property {string} title
53
+ * @property {string} description
54
+ * @property {DocsIndexSection[]} sections
55
+ */
56
+
57
+ /**
58
+ * @typedef {object} DocsIndexSection
59
+ * @property {string} id stable key; pass it as the section argument
60
+ * @property {string} title
61
+ * @property {string} summary the section's first line of text, at most 240
62
+ * characters
63
+ */
64
+
65
+ /**
66
+ * astryx --json docs <topic> <section>
41
67
  * @typedef {object} DocsDetailSectionResponse
42
68
  * @property {'docs.detail.section'} type
43
69
  * @property {import('@astryxdesign/cli/authoring').ReferenceSection} data
@@ -49,6 +75,8 @@
49
75
  * @property {string} [lang]
50
76
  * @property {boolean} [zh]
51
77
  * @property {boolean} [dense]
78
+ * @property {boolean} [index] return a topic's section index instead of its
79
+ * whole doc
52
80
  * @property {string} [cwd] project directory whose configured integrations
53
81
  * contribute topics; defaults to process.cwd()
54
82
  */
@@ -0,0 +1,18 @@
1
+ // @generated by scripts/sync-api-types.mjs from the JSDoc in api/**/*.mjs.
2
+ // DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
3
+
4
+ /**
5
+ * @param {string} topic
6
+ * @param {object} [options]
7
+ * @param {string} [options.lang]
8
+ * @param {boolean} [options.zh]
9
+ * @param {boolean} [options.dense]
10
+ * @param {string} [options.cwd]
11
+ * @returns {Promise<import('../docs.type.mjs').DocsIndexResponse>}
12
+ */
13
+ export function index(topic: string, options?: {
14
+ lang?: string | undefined;
15
+ zh?: boolean | undefined;
16
+ dense?: boolean | undefined;
17
+ cwd?: string | undefined;
18
+ }): Promise<import("../docs.type.mjs").DocsIndexResponse>;
@@ -0,0 +1,32 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file docs.index leaf — the section index of one topic.
5
+ *
6
+ * @input A topic name plus optional {lang, zh, dense, cwd}. Resolves the topic
7
+ * via the shared adapter and reads its lowered compiled node; nothing is
8
+ * linked, since the index never inlines a token reference.
9
+ * @output { type: 'docs.index', data: DocsIndex } — the topic's name, title and
10
+ * description and, for each section, the key it is read by, its title, and a
11
+ * one-line summary. Matches `astryx --json docs <topic> --index`.
12
+ * @position Leaf under api/docs: a topic's opt-in progressive read. One section
13
+ * is the section leaf (`docs <topic> <key>`); the whole topic, a plain topic
14
+ * read, is the detail leaf.
15
+ */
16
+
17
+ import {indexView} from '../../../foundation/doc-compiler/lenses.mjs';
18
+ import {resolveTopicDocs} from '../_adapter.mjs';
19
+
20
+ /**
21
+ * @param {string} topic
22
+ * @param {object} [options]
23
+ * @param {string} [options.lang]
24
+ * @param {boolean} [options.zh]
25
+ * @param {boolean} [options.dense]
26
+ * @param {string} [options.cwd]
27
+ * @returns {Promise<import('../docs.type.mjs').DocsIndexResponse>}
28
+ */
29
+ export async function index(topic, options = {}) {
30
+ const {node} = await resolveTopicDocs(topic, options);
31
+ return {type: 'docs.index', data: indexView(node)};
32
+ }
@@ -0,0 +1,62 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ import {describe, expect, it} from 'vitest';
4
+ import {docs} from '../docs.mjs';
5
+ import {index} from './index.mjs';
6
+ import {loadDocsCatalog} from '../_adapter.mjs';
7
+
8
+ const SLOW = 60_000;
9
+
10
+ describe('docs.index leaf', () => {
11
+ it('lists each section by key, title, and summary', async () => {
12
+ const res = await index('theme');
13
+ expect(res.type).toBe('docs.index');
14
+ expect(res.data).toMatchObject({name: 'theme', title: expect.any(String)});
15
+ const keys = res.data.sections.map(s => s.id);
16
+ expect(new Set(keys).size).toBe(keys.length);
17
+ for (const entry of res.data.sections) {
18
+ expect(Object.keys(entry)).toEqual(['id', 'title', 'summary']);
19
+ expect(entry.summary.length).toBeLessThanOrEqual(240);
20
+ }
21
+ }, SLOW);
22
+
23
+ it('names the sections the full topic has, with the same keys', async () => {
24
+ const full = await docs('theme');
25
+ const {data} = await index('theme');
26
+ expect(data.sections.map(s => [s.id, s.title])).toEqual(
27
+ full.data.sections.map(s => [s.id, s.title]),
28
+ );
29
+ }, SLOW);
30
+
31
+ it('keeps every key the same in every language', async () => {
32
+ const english = (await index('theme')).data.sections.map(s => s.id);
33
+ for (const lang of ['zh', 'dense']) {
34
+ const localized = await index('theme', {lang});
35
+ expect(localized.data.sections.map(s => s.id)).toEqual(english);
36
+ }
37
+ }, SLOW);
38
+
39
+ it('lists keys every section can be read by', async () => {
40
+ const catalog = await loadDocsCatalog();
41
+ for (const entry of catalog.entries()) {
42
+ const {data} = await index(entry.name);
43
+ for (const {id, title} of data.sections) {
44
+ const read = await docs(entry.name, id);
45
+ expect(read.data.title).toBe(title);
46
+ }
47
+ }
48
+ }, SLOW);
49
+ });
50
+
51
+ describe('docs() topic reads', () => {
52
+ it('returns the whole topic by default, as before', async () => {
53
+ const res = await docs('theme');
54
+ expect(res.type).toBe('docs.detail');
55
+ expect(res.data.sections[0].content.length).toBeGreaterThan(0);
56
+ }, SLOW);
57
+
58
+ it('returns the section index on request', async () => {
59
+ const res = await docs('theme', undefined, {index: true});
60
+ expect(res.type).toBe('docs.index');
61
+ }, SLOW);
62
+ });
@@ -177,6 +177,112 @@ describe('integration-contributed topics', () => {
177
177
  expect(extended.data.sections.length).toBe(builtin.data.sections.length + 1);
178
178
  }, SLOW);
179
179
 
180
+ it('migrates a real built-in section to a stable ID without duplicating it', async () => {
181
+ const builtin = await docs('theme');
182
+ // Readers see a key on every section; the migration case is one whose
183
+ // source authors no id.
184
+ const {docs: authored} = await import('../../assets/docs/theme.doc.mjs');
185
+ const target = authored.sections.find(section => section.id == null);
186
+ expect(target).toBeDefined();
187
+ scaffold({
188
+ 'theme-internal.doc.mjs': topic({
189
+ name: 'theme-internal',
190
+ extends: 'theme',
191
+ sections: [
192
+ {
193
+ id: 'acme-theme-setup',
194
+ title: target.title,
195
+ content: [{type: 'prose', text: 'Use the Acme theme.'}],
196
+ },
197
+ ],
198
+ }),
199
+ });
200
+
201
+ const extended = await docs('theme', undefined, {cwd: tmpDir});
202
+ expect(extended.data.sections).toHaveLength(builtin.data.sections.length);
203
+ const matches = extended.data.sections.filter(
204
+ section => section.title === target.title,
205
+ );
206
+ expect(matches).toEqual([
207
+ expect.objectContaining({
208
+ id: 'acme-theme-setup',
209
+ content: [{type: 'prose', text: 'Use the Acme theme.'}],
210
+ }),
211
+ ]);
212
+ }, SLOW);
213
+
214
+ it('replaces a real built-in section by the key its index shows', async () => {
215
+ const index = await docs('theme', undefined, {index: true});
216
+ const target = index.data.sections[0];
217
+ scaffold({
218
+ 'theme-internal.doc.mjs': topic({
219
+ name: 'theme-internal',
220
+ extends: 'theme',
221
+ sections: [
222
+ {
223
+ id: target.id,
224
+ title: `${target.title} with Acme`,
225
+ content: [{type: 'prose', text: 'Acme first.'}],
226
+ },
227
+ ],
228
+ }),
229
+ });
230
+
231
+ const extended = await docs('theme', undefined, {cwd: tmpDir});
232
+ expect(extended.data.sections).toHaveLength(index.data.sections.length);
233
+ expect(extended.data.sections.filter(s => s.id === target.id)).toEqual([
234
+ expect.objectContaining({content: [{type: 'prose', text: 'Acme first.'}]}),
235
+ ]);
236
+ const read = await docs('theme', target.id, {cwd: tmpDir});
237
+ expect(read.data.title).toBe(`${target.title} with Acme`);
238
+ }, SLOW);
239
+
240
+ it.each(['zh', 'dense'])(
241
+ 'replaces translated real sections by their authored titles under --%s',
242
+ async lang => {
243
+ const english = await docs('theme');
244
+ const englishTitles = english.data.sections.map(section => section.title);
245
+ expect(englishTitles).toEqual(
246
+ expect.arrayContaining(['Quick Start', 'Theme Props']),
247
+ );
248
+ scaffold({
249
+ 'theme-internal.doc.mjs': topic({
250
+ name: 'theme-internal',
251
+ extends: 'theme',
252
+ sections: [
253
+ {
254
+ id: 'acme-quick-start',
255
+ title: 'Quick Start',
256
+ content: [{type: 'prose', text: 'Acme quick start.'}],
257
+ },
258
+ {
259
+ title: 'Theme Props',
260
+ content: [{type: 'prose', text: 'Acme props.'}],
261
+ },
262
+ ],
263
+ }),
264
+ });
265
+
266
+ const base = await docs('theme', undefined, {lang});
267
+ expect(base.data.sections.map(section => section.title)).not.toEqual(
268
+ englishTitles,
269
+ );
270
+ const extended = await docs('theme', undefined, {cwd: tmpDir, lang});
271
+ expect(extended.data.sections).toHaveLength(base.data.sections.length);
272
+ expect(
273
+ extended.data.sections.filter(
274
+ section => section.id === 'acme-quick-start',
275
+ ),
276
+ ).toHaveLength(1);
277
+ expect(
278
+ extended.data.sections.filter(
279
+ section => section.content[0]?.text === 'Acme props.',
280
+ ),
281
+ ).toHaveLength(1);
282
+ },
283
+ SLOW,
284
+ );
285
+
180
286
  it('offers the contributed topics as suggestions on an unknown one', async () => {
181
287
  scaffold({'deploying.doc.mjs': topic()});
182
288
  await expect(docs('nope-not-a-topic', undefined, {cwd: tmpDir})).rejects.toBeInstanceOf(
@@ -84,6 +84,36 @@ export function checkPeerDeps(ctx: DoctorContext): DoctorCheck;
84
84
  * @returns {DoctorCheck}
85
85
  */
86
86
  export function checkPackageManager(ctx: DoctorContext): DoctorCheck;
87
+ /**
88
+ * Check 6b — every contributing integration owns its provider identity.
89
+ *
90
+ * Artifact and document IDs are provider-scoped, so a package that claims a
91
+ * provider ID an earlier-loaded package already holds is loaded inert: its
92
+ * components, templates, themes, docs, and codemods are withdrawn while the
93
+ * earlier package keeps contributing. That can be a deliberate transition
94
+ * (a renamed package installed beside its predecessor), so it warns rather
95
+ * than fails, but it is never allowed to happen quietly.
96
+ *
97
+ * @param {DoctorContext} ctx
98
+ * @returns {DoctorCheck}
99
+ */
100
+ export function checkProviderIdentity(ctx: DoctorContext): DoctorCheck;
101
+ /**
102
+ * Every authoring self-doc is reachable from `astryx docs authoring`, loads,
103
+ * and fits in one read. The audit is imported here, inside the try, so a
104
+ * malformed self-doc is reported rather than taking Doctor down.
105
+ * @param {DoctorContext} [_ctx]
106
+ * @returns {Promise<DoctorCheck>}
107
+ */
108
+ export function checkAuthoringDocs(_ctx?: DoctorContext): Promise<DoctorCheck>;
109
+ /**
110
+ * Every topic reads progressively, in every language it ships: it loads, its
111
+ * section index and each of its sections fit in one read, and no contributed
112
+ * doc is invalid.
113
+ * @param {DoctorContext | Partial<DoctorContext>} ctx
114
+ * @returns {Promise<DoctorCheck>}
115
+ */
116
+ export function checkDocsProgressiveDisclosure(ctx: DoctorContext | Partial<DoctorContext>): Promise<DoctorCheck>;
87
117
  /**
88
118
  * Run all diagnostic checks and return a structured report.
89
119
  *
@@ -169,9 +199,27 @@ export type DoctorContext = {
169
199
  * read at all.
170
200
  */
171
201
  integrations?: import("../../foundation/integrations/integrations.mjs").LoadedIntegration[] | null | undefined;
202
+ /**
203
+ * - The topics a docs read sees.
204
+ */
205
+ docsCatalog?: DocsCatalog | null | undefined;
206
+ /**
207
+ * `invalid_doc` issues from the project's contributed docs.
208
+ */
209
+ docsCatalogIssues?: {
210
+ package?: string;
211
+ code: string;
212
+ message: string;
213
+ }[] | undefined;
214
+ /**
215
+ * - Why the project's docs catalog
216
+ * could not be built, when it could not.
217
+ */
218
+ docsCatalogError?: string | null | undefined;
172
219
  /**
173
220
  * - Error thrown while resolving the config
174
221
  * path (e.g. multiple config files present), surfaced by checkConfig as a FAIL.
175
222
  */
176
223
  configError?: Error | null | undefined;
177
224
  };
225
+ import { DocsCatalog } from '../../foundation/discovery/docs-discovery.mjs';