@astryxdesign/cli 0.6.5-canary.0cc31e9 → 0.6.5-canary.115f3e9
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.
- package/README.md +2 -2
- package/api/build/_adapter.d.mts +0 -14
- package/api/build/_adapter.mjs +1 -58
- package/api/build/build.test.mjs +1 -74
- package/api/build/kit/kit.mjs +7 -39
- package/api/build/kit/rank.d.mts +0 -8
- package/api/build/kit/rank.mjs +0 -14
- package/api/docs/docs.d.mts +0 -6
- package/api/docs/docs.doc.mjs +2 -31
- package/api/docs/docs.mjs +1 -44
- package/api/docs/docs.type.d.mts +1 -38
- package/api/docs/docs.type.mjs +1 -18
- package/api/docs/node/node.d.mts +3 -28
- package/api/docs/node/node.mjs +23 -146
- package/api/gap-report/gap-report.d.mts +1 -3
- package/api/gap-report/gap-report.mjs +2 -12
- package/api/gap-report/gap-report.test.mjs +0 -106
- package/api/hook/list/list.d.mts +1 -1
- package/api/integration/add-theme.mjs +3 -93
- package/api/integration/add-theme.test.mjs +0 -100
- package/api/integration/integrationAddTheme.doc.mjs +1 -1
- package/api/search/search.d.mts +5 -12
- package/api/search/search.doc.mjs +4 -4
- package/api/search/search.mjs +53 -123
- package/api/search/search.test.mjs +8 -123
- package/api/search/search.type.d.mts +5 -5
- package/api/search/search.type.mjs +5 -5
- package/api/theme/build/build.mjs +69 -146
- package/api/theme/build/build.test.mjs +1 -190
- package/api/theme/themeBuild.doc.d.mts +1 -4
- package/api/theme/themeBuild.doc.mjs +3 -14
- package/assets/docs/theme.doc.mjs +1 -2
- package/assets/docs/tree/add-a-theme.doc.mjs +0 -4
- package/assets/docs/tree/debug-and-gap-reports.doc.mjs +1 -1
- package/clients/cli/commands/build-theme.mjs +2 -8
- package/clients/cli/commands/discover.mjs +1 -29
- package/clients/cli/commands/docs.doc.mjs +2 -15
- package/clients/cli/commands/docs.mjs +5 -222
- package/clients/cli/commands/gap-report.mjs +0 -12
- package/clients/cli/commands/gap-report.test.mjs +0 -63
- package/clients/cli/commands/integration-add.doc.mjs +1 -1
- package/clients/cli/commands/search.doc.mjs +4 -5
- package/clients/cli/commands/search.mjs +6 -30
- package/clients/cli/commands/search.test.mjs +16 -60
- package/foundation/agent-docs/agent-docs.mjs +1 -2
- package/foundation/agent-docs/agent-docs.test.mjs +0 -8
- package/foundation/discovery/theme-discovery.d.mts +0 -6
- package/foundation/discovery/theme-discovery.mjs +0 -9
- package/foundation/doc-compiler/doc-loads.test.mjs +1 -1
- package/package.json +9 -9
- package/api/build/kit/weights.d.mts +0 -82
- package/api/build/kit/weights.json +0 -1
- package/api/build/kit/weights.mjs +0 -305
- package/api/build/kit/weights.test.mjs +0 -190
- package/api/docs/docs.depth.test.mjs +0 -132
- package/api/theme/build/build.icon-lineage.test.mjs +0 -117
- package/api/theme/build/build.icon-preservation.test.mjs +0 -639
- package/api/theme/build/icon-imports.d.mts +0 -47
- package/api/theme/build/icon-imports.mjs +0 -691
- package/assets/templates/blocks/components/DropdownMenuGroup/DropdownMenuGroupShowcase.doc.mjs +0 -15
- package/assets/templates/blocks/components/DropdownMenuGroup/DropdownMenuGroupShowcase.tsx +0 -38
- package/assets/templates/blocks/components/DropdownMenuGroup/DropdownMenuGroupWithSelectable.doc.mjs +0 -20
- package/assets/templates/blocks/components/DropdownMenuGroup/DropdownMenuGroupWithSelectable.tsx +0 -48
- package/clients/cli/commands/discover.no-source.test.mjs +0 -75
- package/clients/cli/commands/docs.depth.test.mjs +0 -219
- 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
|
|
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,
|
|
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 |
|
package/api/build/_adapter.d.mts
CHANGED
|
@@ -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
|
*/
|
package/api/build/_adapter.mjs
CHANGED
|
@@ -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
|
|
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
|
-
}
|
package/api/build/build.test.mjs
CHANGED
|
@@ -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('
|
|
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.
|
package/api/build/kit/kit.mjs
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
-
?
|
|
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
|
|
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
|
package/api/build/kit/rank.d.mts
CHANGED
|
@@ -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
|
package/api/build/kit/rank.mjs
CHANGED
|
@@ -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
|
package/api/docs/docs.d.mts
CHANGED
|
@@ -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';
|
package/api/docs/docs.doc.mjs
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
|
80
|
+
data: await nodeView(found.catalog, found.tree, found.node),
|
|
124
81
|
};
|
|
125
82
|
}
|
|
126
83
|
if (section) return sectionLeaf(topic, section, options);
|
package/api/docs/docs.type.d.mts
CHANGED
|
@@ -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
|
|
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()
|
package/api/docs/docs.type.mjs
CHANGED
|
@@ -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
|
|
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
|
*/
|
package/api/docs/node/node.d.mts
CHANGED
|
@@ -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
|
-
*
|
|
11
|
-
*
|
|
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
|
|
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
|
-
};
|