@astryxdesign/cli 0.6.5-canary.197cb5e → 0.6.5-canary.1bbbf63

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 (54) hide show
  1. package/README.md +2 -2
  2. package/api/build/_adapter.d.mts +0 -14
  3. package/api/build/_adapter.mjs +1 -58
  4. package/api/build/build.test.mjs +1 -74
  5. package/api/build/kit/kit.mjs +7 -39
  6. package/api/build/kit/rank.d.mts +0 -8
  7. package/api/build/kit/rank.mjs +0 -14
  8. package/api/docs/docs.d.mts +0 -6
  9. package/api/docs/docs.doc.mjs +2 -31
  10. package/api/docs/docs.mjs +1 -44
  11. package/api/docs/docs.type.d.mts +1 -38
  12. package/api/docs/docs.type.mjs +1 -18
  13. package/api/docs/node/node.d.mts +3 -28
  14. package/api/docs/node/node.mjs +23 -146
  15. package/api/gap-report/gap-report.d.mts +1 -3
  16. package/api/gap-report/gap-report.mjs +2 -12
  17. package/api/gap-report/gap-report.test.mjs +0 -106
  18. package/api/hook/list/list.d.mts +1 -1
  19. package/api/integration/add-theme.mjs +3 -93
  20. package/api/integration/add-theme.test.mjs +0 -100
  21. package/api/integration/integrationAddTheme.doc.mjs +1 -1
  22. package/api/search/search.d.mts +5 -12
  23. package/api/search/search.doc.mjs +4 -4
  24. package/api/search/search.mjs +53 -123
  25. package/api/search/search.test.mjs +8 -123
  26. package/api/search/search.type.d.mts +5 -5
  27. package/api/search/search.type.mjs +5 -5
  28. package/assets/docs/theme.doc.mjs +1 -2
  29. package/assets/docs/tokens.doc.mjs +3 -3
  30. package/assets/docs/tree/add-a-theme.doc.mjs +0 -4
  31. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +1 -1
  32. package/clients/cli/commands/build-theme.mjs +2 -8
  33. package/clients/cli/commands/discover.mjs +1 -29
  34. package/clients/cli/commands/docs.doc.mjs +2 -15
  35. package/clients/cli/commands/docs.mjs +5 -222
  36. package/clients/cli/commands/gap-report.mjs +0 -12
  37. package/clients/cli/commands/gap-report.test.mjs +0 -63
  38. package/clients/cli/commands/integration-add.doc.mjs +1 -1
  39. package/clients/cli/commands/search.doc.mjs +4 -5
  40. package/clients/cli/commands/search.mjs +6 -30
  41. package/clients/cli/commands/search.test.mjs +16 -60
  42. package/foundation/agent-docs/agent-docs.mjs +1 -2
  43. package/foundation/agent-docs/agent-docs.test.mjs +0 -8
  44. package/foundation/discovery/theme-discovery.d.mts +0 -6
  45. package/foundation/discovery/theme-discovery.mjs +0 -9
  46. package/package.json +9 -9
  47. package/api/build/kit/weights.d.mts +0 -82
  48. package/api/build/kit/weights.json +0 -1
  49. package/api/build/kit/weights.mjs +0 -305
  50. package/api/build/kit/weights.test.mjs +0 -190
  51. package/api/docs/docs.depth.test.mjs +0 -132
  52. package/clients/cli/commands/discover.no-source.test.mjs +0 -75
  53. package/clients/cli/commands/docs.depth.test.mjs +0 -219
  54. package/clients/cli/commands/theme-list.behavior.test.mjs +0 -41
package/README.md CHANGED
@@ -69,7 +69,7 @@ Results for "button" (20 of 239):
69
69
 
70
70
  Options:
71
71
 
72
- - `--type <component|hook|doc|template|theme>`: restrict to a single domain (`doc` and `theme` work outside an app too)
72
+ - `--type <component|hook|doc|template>`: restrict to a single domain
73
73
  - `--limit <n>`: cap the number of results (default 20)
74
74
  - `--verbose`: also print each result's match score and reason
75
75
  - `--json`: typed `{ apiVersion, type: 'search', data: { query, matchCount, results } }` envelope — `matchCount` is how many candidates matched in total, `results` the slice `--limit` allowed
@@ -91,7 +91,7 @@ Options:
91
91
  | `init` | Initialize the design system in your project |
92
92
  | `integration` | Author and verify an Astryx integration package |
93
93
  | `layout` | Generate XDS layouts from compressed expressions (XLE/XLO) |
94
- | `search` | Search components, hooks, docs, templates, and themes in one ranked list |
94
+ | `search` | Search components, hooks, docs, and templates in one ranked list |
95
95
  | `swizzle` | Copy component source for customization |
96
96
  | `template` | List, show, or scaffold page and block templates |
97
97
  | `theme` | Create and build themes: add a shipped one, compile to CSS, or list what a theme can override |
@@ -40,20 +40,6 @@ export function loadPageTemplates(cwd: string): Promise<PageTemplate[]>;
40
40
  * @returns {Promise<ComponentWords[]>}
41
41
  */
42
42
  export function loadComponents(cwd: string): Promise<ComponentWords[]>;
43
- /**
44
- * The matcher weights checked in beside the kit (`kit/weights.json`), read
45
- * once; null when the file is absent or unreadable.
46
- * @returns {import('./kit/weights.mjs').WeightsFile | null}
47
- */
48
- export function loadWeights(): import("./kit/weights.mjs").WeightsFile | null;
49
- /**
50
- * Whether a parsed weights file has the shape the kit reads: every row has a
51
- * weight per candidate, every bias a number per candidate, and three blend
52
- * numbers per member (the tables plus the ranker) and one for the shell.
53
- * @param {any} file
54
- * @returns {file is import('./kit/weights.mjs').WeightsFile}
55
- */
56
- export function isWeightsFile(file: any): file is import("./kit/weights.mjs").WeightsFile;
57
43
  /**
58
44
  * A page template the kit can recommend starting from.
59
45
  */
@@ -2,7 +2,7 @@
2
2
 
3
3
  /**
4
4
  * @file The build subject's environment access: the page templates a project
5
- * can scaffold, the components it can use, and the checked-in matcher weights.
5
+ * can scaffold, and the components it can use.
6
6
  *
7
7
  * @input Template and component discovery for `cwd` — the CLI's own templates
8
8
  * and Core's components, plus any that the project's configured integrations
@@ -16,8 +16,6 @@
16
16
  * from the template subject's, components from search's.
17
17
  */
18
18
 
19
- import fs from 'node:fs';
20
-
21
19
  import {discoverTemplates} from '../template/template.mjs';
22
20
  import {componentKeywords} from '../search/search.mjs';
23
21
  import {findCoreDir} from '../../foundation/fs/paths.mjs';
@@ -91,58 +89,3 @@ export async function loadComponents(cwd) {
91
89
  return [];
92
90
  }
93
91
  }
94
-
95
- /** @type {import('./kit/weights.mjs').WeightsFile | null | undefined} */
96
- let weights;
97
-
98
- /**
99
- * The matcher weights checked in beside the kit (`kit/weights.json`), read
100
- * once; null when the file is absent or unreadable.
101
- * @returns {import('./kit/weights.mjs').WeightsFile | null}
102
- */
103
- export function loadWeights() {
104
- if (weights === undefined) {
105
- try {
106
- const file = JSON.parse(
107
- fs.readFileSync(new URL('./kit/weights.json', import.meta.url), 'utf8'),
108
- );
109
- weights = isWeightsFile(file) ? file : null;
110
- } catch {
111
- weights = null;
112
- }
113
- }
114
- return weights ?? null;
115
- }
116
-
117
- /**
118
- * Whether a parsed weights file has the shape the kit reads: every row has a
119
- * weight per candidate, every bias a number per candidate, and three blend
120
- * numbers per member (the tables plus the ranker) and one for the shell.
121
- * @param {any} file
122
- * @returns {file is import('./kit/weights.mjs').WeightsFile}
123
- */
124
- export function isWeightsFile(file) {
125
- const n = Array.isArray(file?.candidates) ? file.candidates.length : 0;
126
- return (
127
- n > 0 &&
128
- Array.isArray(file.tables) &&
129
- file.tables.length > 0 &&
130
- file.tables.every(
131
- (/** @type {any} */ t) =>
132
- Array.isArray(t?.words) &&
133
- Array.isArray(t.rows) &&
134
- t.rows.length === t.words.length &&
135
- t.rows.every(
136
- (/** @type {any} */ r) => typeof r === 'string' && r.length === n,
137
- ) &&
138
- Array.isArray(t.bias) &&
139
- t.bias.length === n &&
140
- t.bias.every((/** @type {any} */ b) => Number.isFinite(b)) &&
141
- Number.isFinite(t.clip) &&
142
- Number.isFinite(t.step),
143
- ) &&
144
- Array.isArray(file.blend) &&
145
- file.blend.length === 3 * (file.tables.length + 1) + 1 &&
146
- file.blend.every((/** @type {any} */ x) => Number.isFinite(x))
147
- );
148
- }
@@ -269,7 +269,7 @@ describe('build kit — a thin kit says what to try next', () => {
269
269
  // A skeleton is a 35-line excerpt: a reader who studies it and composes
270
270
  // the rest loses the spacing the template exists to carry. A loose match
271
271
  // is still the best start there is, so `start` scaffolds it.
272
- const r = await build('weekly business review with targets', {cwd: REPO});
272
+ const r = await build('quarterly business review', {cwd: REPO});
273
273
  expect(r.type).toBe('build.kit');
274
274
  if (r.type !== 'build.kit') return;
275
275
  expect(r.data.directMatch).toBe(false);
@@ -477,79 +477,6 @@ describe('build kit — every page starts from a template', () => {
477
477
  expect(r.data.pages.map(p => p.name)).not.toContain('side-gallery');
478
478
  });
479
479
 
480
- it('names a direct match the start does not use, and calls the start direct only when it is', async () => {
481
- for (const idea of ['a login form', 'a docs site for our API', 'contact form']) {
482
- const r = await build(idea, {cwd: REPO});
483
- if (r.type !== 'build.kit') throw new Error(r.type);
484
- expect(r.data.directMatch).toBe(true);
485
- const match = r.data.pages[0].name;
486
- if (r.data.start?.name === match) {
487
- expect(r.data.start?.basis).toBe('direct');
488
- } else {
489
- expect(['closest', 'fallback']).toContain(r.data.start?.basis);
490
- expect(r.data.start?.reason).toContain(`\`${match}\``);
491
- }
492
- }
493
- });
494
-
495
- it('keeps a template search matched directly rather than the app shell', async () => {
496
- for (const [idea, name] of [
497
- ['a login screen', 'login'],
498
- ['a checkout wizard', 'checkout-wizard'],
499
- ]) {
500
- const r = await build(idea, {cwd: REPO});
501
- if (r.type !== 'build.kit') throw new Error(r.type);
502
- expect(r.data.directMatch).toBe(true);
503
- expect(r.data.start?.name).toBe(name);
504
- }
505
- });
506
-
507
- it('lets the weights choose another template over a direct match', async () => {
508
- const r = await build('a login form', {cwd: REPO});
509
- if (r.type !== 'build.kit') throw new Error(r.type);
510
- expect(r.data.directMatch).toBe(true);
511
- const match = r.data.pages[0].name;
512
- expect(r.data.start?.name).not.toBe('shell-top-nav');
513
- expect(r.data.start?.name).not.toBe(match);
514
- expect(r.data.start?.reason).toContain(`\`${match}\``);
515
- });
516
-
517
- it('starts a part that names no page from the app shell', async () => {
518
- for (const idea of ['a kanban card', 'a date range picker']) {
519
- const r = await build(idea, {cwd: REPO});
520
- if (r.type !== 'build.kit') throw new Error(r.type);
521
- expect(r.data.start).toMatchObject({name: 'shell-top-nav', basis: 'fallback'});
522
- expect(r.data.start?.reason).toMatch(/part of a page/);
523
- }
524
- });
525
-
526
- it('does not keep a loose match over the app shell', async () => {
527
- // Search matches no template directly, so the ranker's closest page does
528
- // not override the shell the weights choose.
529
- const r = await build('quarterly business review', {cwd: REPO});
530
- if (r.type !== 'build.kit') throw new Error(r.type);
531
- expect(r.data.directMatch).toBe(false);
532
- expect(r.data.start).toMatchObject({name: 'shell-top-nav', basis: 'fallback'});
533
- expect(r.data.start?.reason).toMatch(/closest/);
534
- });
535
-
536
- it('starts a new page with no matching template from the app shell', async () => {
537
- const r = await build('a new page', {cwd: REPO});
538
- if (r.type !== 'build.kit') throw new Error(r.type);
539
- expect(r.data.start?.name).toBe('shell-top-nav');
540
- });
541
-
542
- it('starts a page the words describe from its template', async () => {
543
- for (const [idea, name] of [
544
- ['a weekly report of sales by region', 'dashboard-scorecard'],
545
- ['a pricing page with three plans and a comparison table', 'table-page'],
546
- ]) {
547
- const r = await build(idea, {cwd: REPO});
548
- if (r.type !== 'build.kit') throw new Error(r.type);
549
- expect(r.data.start?.name).toBe(name);
550
- }
551
- });
552
-
553
480
  it('starts a component in a container from a template with that frame', async () => {
554
481
  // "in a modal": the modal is the frame, so the dialog template leads
555
482
  // instead of the app shell.
@@ -32,15 +32,8 @@ import {findCoreDir} from '../../../foundation/fs/paths.mjs';
32
32
  import {AstryxError} from '../../error.mjs';
33
33
  import {ERROR_CODES} from '../../../foundation/response/error-codes.mjs';
34
34
  import {getResultCoverage} from '../../search/coverage.mjs';
35
- import {loadComponents, loadPageTemplates, loadWeights} from '../_adapter.mjs';
36
- import {
37
- asksForNewPage,
38
- ideaKind,
39
- pickAlternatives,
40
- pickStart,
41
- rankPages,
42
- } from './rank.mjs';
43
- import {weighStart} from './weights.mjs';
35
+ import {loadComponents, loadPageTemplates} from '../_adapter.mjs';
36
+ import {ideaKind, pickAlternatives, pickStart, rankPages} from './rank.mjs';
44
37
 
45
38
  /** A page at/above this score is a confident direct match. */
46
39
  const PAGE_DIRECT = 95;
@@ -158,10 +151,9 @@ function placement(kind, inPage) {
158
151
  * @param {SearchResultEntry[]} pages
159
152
  * @param {boolean} directMatch
160
153
  * @param {PageTemplate[]} catalog
161
- * @param {string} idea
162
154
  * @returns {Omit<BuildStart, 'alternatives'> | null}
163
155
  */
164
- function chooseStart(ranked, kind, pages, directMatch, catalog, idea) {
156
+ function chooseStart(ranked, kind, pages, directMatch, catalog) {
165
157
  const direct = directMatch ? pages[0].name : null;
166
158
  const unready =
167
159
  direct && !catalog.some(t => t.name === direct) ? direct : null;
@@ -178,27 +170,7 @@ function chooseStart(ranked, kind, pages, directMatch, catalog, idea) {
178
170
  direct && direct !== startName
179
171
  ? `Search matched \`${direct}\` by name, but ${place}`
180
172
  : place[0].toUpperCase() + place.slice(1);
181
- const proposed = pickStart(ranked, kind);
182
- // The checked-in word weights (weights.mjs), blended with the ranker's
183
- // scores, decide the start of a whole page. A part or an edit starts where
184
- // the ranker's placement rules put it (spec:AST-048/FR3).
185
- const weighed =
186
- kind === 'page'
187
- ? weighStart(idea, ranked, proposed, catalog, {
188
- weights: loadWeights(),
189
- newPage: asksForNewPage(idea, catalog),
190
- })
191
- : undefined;
192
- // A shell start keeps the shell the ranker named, if any, and never replaces
193
- // the template the ranker chose for a page search matched directly.
194
- const pick =
195
- weighed === undefined
196
- ? proposed
197
- : weighed === null
198
- ? proposed?.family === 'Shell' || (proposed && direct && !unready)
199
- ? proposed
200
- : null
201
- : (ranked.find(r => r.name === weighed) ?? proposed);
173
+ const pick = pickStart(ranked, kind);
202
174
  const closest = pick && catalog.find(t => t.name === pick.name);
203
175
  if (pick && closest) {
204
176
  const agrees = closest.name === direct;
@@ -222,9 +194,7 @@ function chooseStart(ranked, kind, pages, directMatch, catalog, idea) {
222
194
  if (shell) {
223
195
  // The shell can also be the ranker's best guess without the evidence to
224
196
  // lead ("horizontal site navigation"); say so rather than "no match".
225
- const nearest =
226
- (ranked[0]?.name === shell.name && ranked[0].hits > 0) ||
227
- (weighed === null && !!proposed && proposed.family !== 'Shell');
197
+ const nearest = ranked[0]?.name === shell.name && ranked[0].hits > 0;
228
198
  const place = placement(kind, false);
229
199
  return {
230
200
  ...asTemplate(shell),
@@ -234,9 +204,7 @@ function chooseStart(ranked, kind, pages, directMatch, catalog, idea) {
234
204
  : place
235
205
  ? placed(place, shell.name)
236
206
  : direct
237
- ? weighed === null
238
- ? `Search matched \`${direct}\` by name, but the app shell is the closer start.`
239
- : `Search matched \`${direct}\` by name, but too little of the idea fits it, so start from the app shell.`
207
+ ? `Search matched \`${direct}\` by name, but too little of the idea fits it, so start from the app shell.`
240
208
  : nearest
241
209
  ? 'No template is a clear match; the app shell is the closest.'
242
210
  : loose
@@ -378,7 +346,7 @@ export async function buildKit(query, options = {}) {
378
346
  )
379
347
  : 'page';
380
348
  const chosen = wantsPages
381
- ? chooseStart(ranked, kind, matchedPages, directMatch, catalog, query)
349
+ ? chooseStart(ranked, kind, matchedPages, directMatch, catalog)
382
350
  : null;
383
351
  // Name the ranker's next two templates beside the start: the reader judges
384
352
  // meaning better than keywords do, and an acceptable template is in these
@@ -21,14 +21,6 @@ export function rankPages(query: string, pages: PageTemplate[]): RankedPage[];
21
21
  * @returns {IdeaKind}
22
22
  */
23
23
  export function ideaKind(query: string, pages: PageTemplate[], components: ComponentWords[]): IdeaKind;
24
- /**
25
- * Whether an idea asks for a new page (see `asksNewPage`), from the idea and
26
- * the project's page templates.
27
- * @param {string} query
28
- * @param {PageTemplate[]} pages
29
- * @returns {boolean}
30
- */
31
- export function asksForNewPage(query: string, pages: PageTemplate[]): boolean;
32
24
  /**
33
25
  * The next closest templates after the start, best first: the ones a reader
34
26
  * should check the idea against when the start's shape is wrong. Each matched
@@ -543,20 +543,6 @@ function asksNewPage(phrases, familyWords) {
543
543
  });
544
544
  }
545
545
 
546
- /**
547
- * Whether an idea asks for a new page (see `asksNewPage`), from the idea and
548
- * the project's page templates.
549
- * @param {string} query
550
- * @param {PageTemplate[]} pages
551
- * @returns {boolean}
552
- */
553
- export function asksForNewPage(query, pages) {
554
- return asksNewPage(
555
- String(query).toLowerCase().split(PHRASE_END),
556
- familyWordsOf(pages),
557
- );
558
- }
559
-
560
546
  /**
561
547
  * The words of a component's name: "DateRangeInput" is date, range, input.
562
548
  * @param {ComponentWords} component
@@ -10,10 +10,6 @@
10
10
  * @param {boolean} [options.dense]
11
11
  * @param {boolean} [options.index] return the topic's section index instead of
12
12
  * the whole doc
13
- * @param {number | 'all'} [options.depth] how many levels below a docs-tree
14
- * namespace to read; a doc with nothing below it reads the same at any depth
15
- * @param {'brief' | 'compact' | 'full'} [options.detail] how much of each doc
16
- * below the named one a depth read returns (brief by default)
17
13
  * @param {string} [options.cwd]
18
14
  * @returns {Promise<
19
15
  * import('./docs.type.mjs').DocsListResponse |
@@ -28,8 +24,6 @@ export function docs(topic?: string, section?: string, options?: {
28
24
  zh?: boolean | undefined;
29
25
  dense?: boolean | undefined;
30
26
  index?: boolean | undefined;
31
- depth?: number | "all" | undefined;
32
- detail?: "compact" | "full" | "brief" | undefined;
33
27
  cwd?: string | undefined;
34
28
  }): Promise<import("./docs.type.mjs").DocsListResponse | import("./docs.type.mjs").DocsIndexResponse | import("./docs.type.mjs").DocsDetailResponse | import("./docs.type.mjs").DocsDetailSectionResponse | import("./docs.type.mjs").DocsNodeResponse>;
35
29
  import { list } from './list/list.mjs';
@@ -25,8 +25,7 @@ export const doc = {
25
25
  'ones the project\'s configured integrations contribute, including any ' +
26
26
  'topic an integration replaces or extends, so it depends on the cwd. ' +
27
27
  'A route opens a node of the docs tree instead: a namespace such as ' +
28
- '`cli/api` returns its children one level down (`depth` reads as many ' +
29
- 'levels as asked, and `detail` how much of each doc below), a typed doc such as ' +
28
+ "`cli/api` returns its children one level down, a typed doc such as " +
30
29
  "`cli/api/functions/search` returns its content, and a guide the tree " +
31
30
  'places (`cli/integrations/quick-start`) reads like any topic. ' +
32
31
  'Every read but the list carries `links`, the commands that move from it: ' +
@@ -83,18 +82,6 @@ export const doc = {
83
82
  description:
84
83
  "Return the topic's section index (each section's key, title, and summary), even for a topic with one section.",
85
84
  },
86
- {
87
- name: 'options.depth',
88
- type: "number | 'all'",
89
- description:
90
- "How many levels below a docs-tree namespace to read: 0 for the namespace alone, 1 for its children (the default), 'all' for every level. Where a read stops, a child with docs below it carries childCount. A doc with nothing below it reads the same at any depth.",
91
- },
92
- {
93
- name: 'options.detail',
94
- type: "'brief' | 'compact' | 'full'",
95
- description:
96
- "How much of each doc below the named one a depth read returns: brief (the default) is its identity; compact and full add its text (a guide's sections, a namespace's or typed doc's content). Given alone, it reads one level down.",
97
- },
98
85
  {
99
86
  name: 'options.cwd',
100
87
  type: 'string',
@@ -126,18 +113,10 @@ export const doc = {
126
113
  {
127
114
  type: 'docs.node',
128
115
  description:
129
- "A namespace or typed doc in the docs tree, read by its route: {id, route, kind, package, title, summary, breadcrumb, slots, content}. A namespace lists each slot's children one level down, or as deep as depth asks, each child carrying its own slots, childCount where the read stops, and its text at compact or full detail; a typed doc carries its content.",
116
+ "A namespace or typed doc in the docs tree, read by its route: {id, route, kind, package, title, summary, breadcrumb, slots, content}. A namespace lists each slot's children one level down; a typed doc carries its content.",
130
117
  },
131
118
  ],
132
119
  throws: [
133
- {
134
- code: 'ERR_INVALID_ARGUMENT',
135
- when: "depth is not a whole number of levels or 'all'",
136
- },
137
- {
138
- code: 'ERR_INVALID_DETAIL',
139
- when: 'detail is not brief, compact, or full',
140
- },
141
120
  {
142
121
  code: 'ERR_UNKNOWN_TOPIC',
143
122
  when: 'the topic is not a string, or matches no topic and no docs-tree route',
@@ -155,14 +134,6 @@ export const doc = {
155
134
  code: "await docs('principles', undefined, {index: true});",
156
135
  },
157
136
  {label: 'A docs-tree namespace', code: "await docs('cli/api');"},
158
- {
159
- label: 'Every doc below a namespace, one entry each',
160
- code: "await docs('cli', undefined, {depth: 'all'});",
161
- },
162
- {
163
- label: 'A namespace and everything below it, in full',
164
- code: "await docs('cli/integrations', undefined, {depth: 'all', detail: 'full'});",
165
- },
166
137
  {label: 'One API function', code: "await docs('cli/api/functions/search');"},
167
138
  {
168
139
  label: 'A whole guide from the docs tree',
package/api/docs/docs.mjs CHANGED
@@ -11,7 +11,6 @@
11
11
  * docs(topic, undefined, {index: true}) -> index -> docs.index
12
12
  * docs(topic, section) -> section -> docs.detail.section
13
13
  * docs(route) -> node -> docs.node
14
- * docs(route, undefined, {depth: 2}) -> node -> docs.node, two levels
15
14
  *
16
15
  * A topic read returns the whole doc, as it always has; `index` returns its
17
16
  * sections, so a reader can open one by its key (spec:AST-047). The CLI's text
@@ -34,43 +33,6 @@ import {ERROR_CODES} from '../../foundation/response/error-codes.mjs';
34
33
 
35
34
  export {list, index, detail, sectionLeaf as section, nodeLeaf as node};
36
35
 
37
- const DETAILS = ['brief', 'compact', 'full'];
38
-
39
- /**
40
- * How far a docs-tree read goes, from the options: `depth` levels below the
41
- * node, or every level for `'all'`, and `detail` for each doc below it (brief
42
- * by default). `detail` alone reads one level down. Neither: the plain read.
43
- * @param {import('./docs.type.mjs').DocsOptions} options
44
- * @returns {import('./node/node.mjs').DepthRead | undefined}
45
- */
46
- function depthRead(options) {
47
- const {depth, detail} = options;
48
- if (detail != null && !DETAILS.includes(detail)) {
49
- throw new AstryxError(
50
- `detail is brief, compact, or full, not ${JSON.stringify(detail)}.`,
51
- undefined,
52
- ERROR_CODES.ERR_INVALID_DETAIL,
53
- );
54
- }
55
- if (
56
- depth != null &&
57
- depth !== 'all' &&
58
- !(Number.isInteger(depth) && /** @type {number} */ (depth) >= 0)
59
- ) {
60
- throw new AstryxError(
61
- `depth is a number of levels (0, 1, 2, ...) or "all", not ${JSON.stringify(depth)}.`,
62
- undefined,
63
- ERROR_CODES.ERR_INVALID_ARGUMENT,
64
- );
65
- }
66
- if (depth == null && detail == null) return undefined;
67
- return {
68
- depth: depth === 'all' ? Infinity : (depth ?? 1),
69
- detail: /** @type {'brief' | 'compact' | 'full'} */ (detail ?? 'brief'),
70
- lang: options.lang || (options.dense ? 'dense' : options.zh ? 'zh' : null),
71
- };
72
- }
73
-
74
36
  /**
75
37
  * @param {string} [topic]
76
38
  * @param {string} [section]
@@ -80,10 +42,6 @@ function depthRead(options) {
80
42
  * @param {boolean} [options.dense]
81
43
  * @param {boolean} [options.index] return the topic's section index instead of
82
44
  * the whole doc
83
- * @param {number | 'all'} [options.depth] how many levels below a docs-tree
84
- * namespace to read; a doc with nothing below it reads the same at any depth
85
- * @param {'brief' | 'compact' | 'full'} [options.detail] how much of each doc
86
- * below the named one a depth read returns (brief by default)
87
45
  * @param {string} [options.cwd]
88
46
  * @returns {Promise<
89
47
  * import('./docs.type.mjs').DocsListResponse |
@@ -94,7 +52,6 @@ function depthRead(options) {
94
52
  * >}
95
53
  */
96
54
  export async function docs(topic, section, options = {}) {
97
- const read = depthRead(options);
98
55
  if (!topic) return list(options);
99
56
  const found = await resolveDocsArgument(topic, options);
100
57
  if (found.kind === 'node') {
@@ -120,7 +77,7 @@ export async function docs(topic, section, options = {}) {
120
77
  }
121
78
  return {
122
79
  type: 'docs.node',
123
- data: await nodeView(found.catalog, found.tree, found.node, read),
80
+ data: await nodeView(found.catalog, found.tree, found.node),
124
81
  };
125
82
  }
126
83
  if (section) return sectionLeaf(topic, section, options);
@@ -205,14 +205,9 @@ export type DocsNode = {
205
205
  breadcrumb: DocsNodeLink[];
206
206
  /**
207
207
  * a namespace's slots that hold children, in
208
- * order; empty for a typed doc, and for a depth read of 0
208
+ * order; empty for a typed doc
209
209
  */
210
210
  slots: DocsNodeSlot[];
211
- /**
212
- * with a depth read of 0, how many docs sit
213
- * right below the namespace
214
- */
215
- childCount?: number | undefined;
216
211
  /**
217
212
  * a typed doc's content; empty for a namespace
218
213
  */
@@ -251,26 +246,6 @@ export type DocsNodeChild = {
251
246
  kind: string;
252
247
  title: string;
253
248
  summary: string;
254
- /**
255
- * with a depth read, its own slots, while
256
- * the read goes deeper
257
- */
258
- slots?: DocsNodeSlot[] | undefined;
259
- /**
260
- * with a depth read, how many docs sit right
261
- * below it where the read stops
262
- */
263
- childCount?: number | undefined;
264
- /**
265
- * with a depth read at compact or full detail: a namespace's intro or a typed
266
- * doc's content
267
- */
268
- content?: import("@astryxdesign/cli/authoring").ReferenceContentBlock[] | undefined;
269
- /**
270
- * with a depth read at compact or full
271
- * detail: a guide's sections
272
- */
273
- sections?: DocsReadSection[] | undefined;
274
249
  };
275
250
  /**
276
251
  * Options for `docs()`.
@@ -284,18 +259,6 @@ export type DocsOptions = {
284
259
  * whole doc
285
260
  */
286
261
  index?: boolean | undefined;
287
- /**
288
- * how many levels below a docs-tree
289
- * namespace to read: 0 for the namespace alone, 1 for its children (the
290
- * default), 'all' for every level
291
- */
292
- depth?: number | "all" | undefined;
293
- /**
294
- * how much of each doc below
295
- * the named one a depth read returns: brief (the default) is its identity,
296
- * compact and full add its text
297
- */
298
- detail?: "compact" | "full" | "brief" | undefined;
299
262
  /**
300
263
  * project directory whose configured integrations
301
264
  * contribute topics; defaults to process.cwd()
@@ -153,9 +153,7 @@
153
153
  * @property {string} summary
154
154
  * @property {DocsNodeLink[]} breadcrumb the namespaces above it, top first
155
155
  * @property {DocsNodeSlot[]} slots a namespace's slots that hold children, in
156
- * order; empty for a typed doc, and for a depth read of 0
157
- * @property {number} [childCount] with a depth read of 0, how many docs sit
158
- * right below the namespace
156
+ * order; empty for a typed doc
159
157
  * @property {import('@astryxdesign/cli/authoring').ReferenceContentBlock[]} content
160
158
  * a typed doc's content; empty for a namespace
161
159
  * @property {DocsLinks} links the moves from the node: up to its parent (the
@@ -183,15 +181,6 @@
183
181
  * @property {string} kind
184
182
  * @property {string} title
185
183
  * @property {string} summary
186
- * @property {DocsNodeSlot[]} [slots] with a depth read, its own slots, while
187
- * the read goes deeper
188
- * @property {number} [childCount] with a depth read, how many docs sit right
189
- * below it where the read stops
190
- * @property {import('@astryxdesign/cli/authoring').ReferenceContentBlock[]} [content]
191
- * with a depth read at compact or full detail: a namespace's intro or a typed
192
- * doc's content
193
- * @property {DocsReadSection[]} [sections] with a depth read at compact or full
194
- * detail: a guide's sections
195
184
  */
196
185
 
197
186
  /**
@@ -202,12 +191,6 @@
202
191
  * @property {boolean} [dense]
203
192
  * @property {boolean} [index] return a topic's section index instead of its
204
193
  * whole doc
205
- * @property {number | 'all'} [depth] how many levels below a docs-tree
206
- * namespace to read: 0 for the namespace alone, 1 for its children (the
207
- * default), 'all' for every level
208
- * @property {'brief' | 'compact' | 'full'} [detail] how much of each doc below
209
- * the named one a depth read returns: brief (the default) is its identity,
210
- * compact and full add its text
211
194
  * @property {string} [cwd] project directory whose configured integrations
212
195
  * contribute topics; defaults to process.cwd()
213
196
  */
@@ -4,28 +4,15 @@
4
4
  /**
5
5
  * @typedef {import('../../../foundation/doc-compiler/tree.mjs').DocsTree} DocsTree
6
6
  * @typedef {import('../../../foundation/doc-compiler/tree.mjs').TreeNode} TreeNode
7
- * @typedef {import('../../../foundation/discovery/docs-discovery.mjs').DocsCatalog} DocsCatalog
8
7
  */
9
8
  /**
10
- * How far a read goes below the node it names, and how much of each doc below
11
- * it shows: `depth` levels (Infinity for every level), and `detail` brief (its
12
- * identity), or compact or full (with its text: a namespace's intro, a guide's
13
- * sections, a typed doc's content). `lang` selects the guides' language.
14
- * @typedef {object} DepthRead
15
- * @property {number} depth
16
- * @property {'brief' | 'compact' | 'full'} detail
17
- * @property {string | null} lang
18
- */
19
- /**
20
- * The docs.node view of one tree node. Without a depth read, a namespace lists
21
- * its children one level down.
22
- * @param {DocsCatalog} catalog
9
+ * The docs.node view of one tree node.
10
+ * @param {import('../../../foundation/discovery/docs-discovery.mjs').DocsCatalog} catalog
23
11
  * @param {DocsTree} tree
24
12
  * @param {TreeNode} node
25
- * @param {DepthRead} [read] how far down to go, and how much of each doc below
26
13
  * @returns {Promise<import('../docs.type.mjs').DocsNode>}
27
14
  */
28
- export function nodeView(catalog: DocsCatalog, tree: DocsTree, node: TreeNode, read?: DepthRead): Promise<import("../docs.type.mjs").DocsNode>;
15
+ export function nodeView(catalog: import("../../../foundation/discovery/docs-discovery.mjs").DocsCatalog, tree: DocsTree, node: TreeNode): Promise<import("../docs.type.mjs").DocsNode>;
29
16
  /**
30
17
  * The typed edges a doc declares (spec:AST-047 FR5), resolved to routes: a
31
18
  * function doc's `command` and `related`, and a command doc's `fn` and
@@ -54,15 +41,3 @@ export function node(route: string, options?: {
54
41
  }): Promise<import("../docs.type.mjs").DocsNodeResponse>;
55
42
  export type DocsTree = import("../../../foundation/doc-compiler/tree.mjs").DocsTree;
56
43
  export type TreeNode = import("../../../foundation/doc-compiler/tree.mjs").TreeNode;
57
- export type DocsCatalog = import("../../../foundation/discovery/docs-discovery.mjs").DocsCatalog;
58
- /**
59
- * How far a read goes below the node it names, and how much of each doc below
60
- * it shows: `depth` levels (Infinity for every level), and `detail` brief (its
61
- * identity), or compact or full (with its text: a namespace's intro, a guide's
62
- * sections, a typed doc's content). `lang` selects the guides' language.
63
- */
64
- export type DepthRead = {
65
- depth: number;
66
- detail: "brief" | "compact" | "full";
67
- lang: string | null;
68
- };