@astryxdesign/cli 0.7.2 → 0.7.3-canary.06872b4
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 +99 -66
- package/api/build/_adapter.d.mts +58 -0
- package/api/build/_adapter.mjs +115 -3
- package/api/build/blocks/blocks.d.mts +42 -0
- package/api/build/blocks/blocks.mjs +169 -0
- package/api/build/blocks/blocks.test.mjs +108 -0
- package/api/build/blocks/blocks.weights.json +1 -0
- package/api/build/build.mjs +25 -6
- package/api/build/kit/answer.d.mts +14 -0
- package/api/build/kit/answer.mjs +85 -0
- package/api/build/kit/answer.test.mjs +38 -0
- package/api/build/kit/kind.d.mts +25 -0
- package/api/build/kit/kind.mjs +170 -0
- package/api/build/kit/kit.d.mts +2 -4
- package/api/build/kit/kit.mjs +8 -398
- package/api/build/kit/rank.d.mts +3 -32
- package/api/build/kit/rank.mjs +25 -342
- package/api/build/kit/rank.test.mjs +2 -1
- package/api/build/kit/signals.d.mts +60 -0
- package/api/build/kit/signals.mjs +284 -0
- package/api/build/kit/start.d.mts +32 -0
- package/api/build/kit/start.mjs +163 -0
- package/api/build/kit/words.d.mts +72 -0
- package/api/build/kit/words.mjs +202 -0
- package/api/build/tree.d.mts +20 -0
- package/api/build/tree.mjs +52 -0
- package/api/build/tree.test.mjs +177 -0
- package/api/build/tree.weights.json +1 -0
- package/api/component/component.mjs +16 -2
- package/api/component/component.test.mjs +15 -0
- package/api/discover/_adapter.mjs +36 -9
- package/api/discover/_adapter.test.mjs +42 -0
- package/api/docs/_adapter.mjs +17 -5
- package/api/docs/docs.test.mjs +7 -1
- package/api/doctor/doctor.mjs +86 -12
- package/api/doctor/doctor.test.mjs +28 -3
- package/api/init/init.doc.mjs +20 -6
- package/api/init/init.test.mjs +18 -2
- package/api/init/init.type.d.mts +3 -3
- package/api/init/init.type.mjs +3 -3
- package/api/init/remove/remove.mjs +17 -4
- package/api/init/remove/remove.test.mjs +114 -2
- package/api/init/run/run.mjs +69 -6
- package/api/integration/authoring-checks.mjs +83 -9
- package/api/integration/pack-check.mjs +123 -4
- package/api/integration/pack-check.test.mjs +87 -0
- package/api/integration/validate-integration-case-disk.test.mjs +111 -0
- package/api/integration/validate-integration-gaps.test.mjs +1964 -0
- package/api/integration/validate-integration.d.mts +11 -2
- package/api/integration/validate-integration.mjs +226 -32
- package/api/integration/validate-integration.type.d.mts +58 -1
- package/api/integration/validate-integration.type.mjs +45 -2
- package/api/integration/validateIntegration.doc.mjs +1 -1
- package/api/search/ORDERING.md +28 -0
- package/api/search/_adapter.d.mts +66 -2
- package/api/search/_adapter.mjs +118 -10
- package/api/search/_candidate-score.d.mts +40 -0
- package/api/search/_candidate-score.mjs +238 -0
- package/api/search/_cards.json +6098 -0
- package/api/search/_compound.d.mts +50 -0
- package/api/search/_compound.mjs +170 -0
- package/api/search/_doc-searcher.d.mts +4 -0
- package/api/search/_doc-searcher.mjs +23 -0
- package/api/search/_doc-searcher.test.mjs +36 -0
- package/api/search/_doc-searcher.weights.json +1 -0
- package/api/search/_doc-text.d.mts +30 -0
- package/api/search/_doc-text.mjs +87 -0
- package/api/search/_entries.d.mts +52 -0
- package/api/search/_entries.mjs +158 -0
- package/api/search/_features.d.mts +35 -0
- package/api/search/_features.mjs +155 -0
- package/api/search/_gather-components.d.mts +26 -0
- package/api/search/_gather-components.mjs +284 -0
- package/api/search/_gather-docs.d.mts +20 -0
- package/api/search/_gather-docs.mjs +254 -0
- package/api/search/_gather-hooks.d.mts +12 -0
- package/api/search/_gather-hooks.mjs +52 -0
- package/api/search/_gather-templates.d.mts +25 -0
- package/api/search/_gather-templates.mjs +107 -0
- package/api/search/_lexical.d.mts +135 -0
- package/api/search/_lexical.mjs +351 -0
- package/api/search/_main-searcher.d.mts +19 -0
- package/api/search/_main-searcher.mjs +115 -0
- package/api/search/_main-searcher.test.mjs +106 -0
- package/api/search/_main-searcher.weights.json +1 -0
- package/api/search/_module-doc.d.mts +32 -0
- package/api/search/_module-doc.mjs +78 -0
- package/api/search/_order.d.mts +32 -0
- package/api/search/_order.mjs +162 -0
- package/api/search/_order.test.mjs +72 -0
- package/api/search/_priority.d.mts +50 -0
- package/api/search/_priority.mjs +139 -0
- package/api/search/_query-words.d.mts +25 -0
- package/api/search/_query-words.mjs +92 -0
- package/api/search/_query.d.mts +65 -0
- package/api/search/_query.mjs +176 -0
- package/api/search/_result.d.mts +97 -0
- package/api/search/_result.mjs +150 -0
- package/api/search/_score.d.mts +60 -0
- package/api/search/_score.mjs +283 -0
- package/api/search/_synonyms.d.mts +21 -0
- package/api/search/_synonyms.mjs +67 -0
- package/api/search/_template-searcher.d.mts +4 -0
- package/api/search/_template-searcher.mjs +23 -0
- package/api/search/_template-searcher.test.mjs +40 -0
- package/api/search/_template-searcher.weights.json +1 -0
- package/api/search/_titles.d.mts +33 -0
- package/api/search/_titles.mjs +65 -0
- package/api/search/_words.d.mts +64 -0
- package/api/search/_words.mjs +157 -0
- package/api/search/search.d.mts +22 -305
- package/api/search/search.doc.mjs +7 -5
- package/api/search/search.mjs +53 -2483
- package/api/search/search.type.d.mts +6 -1
- package/api/search/search.type.mjs +6 -1
- package/api/search/tree.d.mts +15 -0
- package/api/search/tree.mjs +40 -0
- package/api/search/tree.test.mjs +66 -0
- package/api/search/tree.weights.json +1 -0
- package/api/theme/build/build.packed-old-core.test.mjs +1 -0
- package/api/upgrade/_adapter.d.mts +57 -9
- package/api/upgrade/_adapter.mjs +198 -28
- package/api/upgrade/registry/registry.d.mts +10 -4
- package/api/upgrade/registry/registry.mjs +33 -16
- package/api/upgrade/registry/registry.test.mjs +85 -0
- package/api/upgrade/run/run.d.mts +4 -2
- package/api/upgrade/run/run.mjs +13 -2
- package/api/upgrade/run/run.test.mjs +417 -3
- package/api/upgrade/upgrade.doc.mjs +10 -5
- package/api/upgrade/upgrade.mjs +13 -2
- package/api/upgrade/upgrade.test.mjs +194 -0
- package/api/upgrade/upgrade.type.d.mts +5 -3
- package/api/upgrade/upgrade.type.mjs +8 -6
- package/assets/codemods/transforms/v0.7.0/__tests__/validate-data-token-ownership.test.mjs +11 -0
- package/assets/codemods/transforms/v0.7.0/validate-data-token-ownership.mjs +3 -3
- package/assets/docs/is-my-code-good.doc.mjs +287 -0
- package/assets/docs/tree/api.doc.mjs +6 -0
- package/assets/docs/tree/host-entry.doc.mjs +128 -0
- package/assets/docs/tree/template-grading-rubric.doc.mjs +16 -37
- package/assets/docs/tree/tokens-data.doc.mjs +2 -2
- package/assets/docs/working-with-ai.doc.mjs +6 -2
- package/assets/templates/themes/butter/butterTheme.ts +4 -2
- package/assets/templates/themes/stone/stoneTheme.ts +4 -2
- package/assets/templates/themes/y2k/y2kTheme.ts +4 -2
- package/authoring/debug/parse.d.mts +1 -1
- package/clients/cli/bin/astryx.mjs +7 -78
- package/clients/cli/commands/doctor.mjs +30 -0
- package/clients/cli/commands/doctor.test.mjs +65 -0
- package/clients/cli/commands/init.behavior.test.mjs +230 -1
- package/clients/cli/commands/init.doc.mjs +28 -9
- package/clients/cli/commands/init.flag-help.test.mjs +1 -0
- package/clients/cli/commands/init.mjs +2 -2
- package/clients/cli/commands/outside-project.test.mjs +2 -1
- package/clients/cli/commands/setup-nudge.test.mjs +61 -0
- package/clients/cli/commands/upgrade.doc.mjs +4 -2
- package/clients/cli/commands/upgrade.mjs +9 -2
- package/clients/cli/commands/upgrade.subfolder-path.test.mjs +97 -0
- package/clients/cli/index.mjs +19 -1
- package/clients/cli/run.mjs +108 -0
- package/foundation/agent-docs/agent-doc-state.d.mts +20 -7
- package/foundation/agent-docs/agent-doc-state.mjs +35 -8
- package/foundation/agent-docs/agent-docs.d.mts +100 -12
- package/foundation/agent-docs/agent-docs.mjs +1010 -99
- package/foundation/agent-docs/agent-docs.path-safety.test.mjs +1239 -17
- package/foundation/agent-docs/agent-docs.test.mjs +678 -35
- package/foundation/config/project.mjs +20 -22
- package/foundation/config/project.test.mjs +56 -0
- package/foundation/discovery/component-discovery.d.mts +37 -0
- package/foundation/discovery/component-discovery.mjs +80 -5
- package/foundation/discovery/component-loader.d.mts +1 -1
- package/foundation/discovery/docs-discovery.mjs +14 -1
- package/foundation/discovery/module-exports.d.mts +41 -0
- package/foundation/discovery/module-exports.mjs +253 -0
- package/foundation/discovery/template-adapter.mjs +18 -1
- package/foundation/discovery/template-needs.mjs +1 -44
- package/foundation/discovery/theme-discovery.mjs +147 -3
- package/foundation/doc-compiler/doc-loads.test.mjs +8 -0
- package/foundation/doc-compiler/read.mjs +59 -1
- package/foundation/doc-compiler/read.test.mjs +11 -2
- package/foundation/fs/paths.d.mts +70 -8
- package/foundation/fs/paths.mjs +556 -38
- package/foundation/fs/paths.test.mjs +430 -54
- package/foundation/integrations/autolink.d.mts +2 -1
- package/foundation/integrations/autolink.mjs +4 -13
- package/foundation/integrations/computer-integrations.d.mts +6 -3
- package/foundation/integrations/computer-integrations.mjs +38 -9
- package/foundation/integrations/contribution-inventory.d.mts +15 -20
- package/foundation/integrations/contribution-inventory.mjs +41 -5
- package/foundation/integrations/integrations.d.mts +27 -1
- package/foundation/integrations/integrations.mjs +61 -2
- package/foundation/integrations/validate-contributions.d.mts +115 -0
- package/foundation/integrations/validate-contributions.mjs +1168 -13
- package/foundation/release/handoff.d.mts +23 -4
- package/foundation/release/handoff.mjs +175 -8
- package/foundation/release/run-context.mjs +14 -0
- package/foundation/response/response-types.doc.mjs +1 -1
- package/foundation/route/route.d.mts +285 -0
- package/foundation/route/route.mjs +324 -0
- package/foundation/route/route.test.mjs +153 -0
- package/foundation/runtime/hosted-runtime.test.mjs +403 -0
- package/foundation/runtime/locked-versions.d.mts +32 -0
- package/foundation/runtime/locked-versions.mjs +65 -0
- package/foundation/runtime/package-root.d.mts +91 -0
- package/foundation/runtime/package-root.mjs +174 -0
- package/foundation/runtime/package-set.d.mts +62 -0
- package/foundation/runtime/package-set.mjs +130 -0
- package/foundation/runtime/package-set.test.mjs +432 -0
- package/host/door-conformance.test.mjs +274 -0
- package/host/index.d.ts +116 -0
- package/host/index.mjs +295 -0
- package/host/index.test.mjs +302 -0
- package/package.json +14 -9
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file Search's doc candidates: every topic, section, and docs-tree node,
|
|
5
|
+
* with its titles, prose, and code terms. Imported only by the adapter.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
guideEntry,
|
|
10
|
+
loadDocsCatalog,
|
|
11
|
+
lowerTopic,
|
|
12
|
+
projectTree,
|
|
13
|
+
holdsOwnName,
|
|
14
|
+
} from '../docs/_adapter.mjs';
|
|
15
|
+
import {nodeView} from '../docs/node/node.mjs';
|
|
16
|
+
import {
|
|
17
|
+
sectionKey,
|
|
18
|
+
sectionSummary,
|
|
19
|
+
} from '../../foundation/discovery/docs-section-key.mjs';
|
|
20
|
+
import {sectionProse, codeTerms, headings, plain} from './_doc-text.mjs';
|
|
21
|
+
|
|
22
|
+
/** @typedef {import('./_lexical.mjs').Candidate} Candidate */
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Build doc candidates at the grain a reader reads them: each section of a
|
|
26
|
+
* topic, whose command reads just that section; each topic as a whole, whose
|
|
27
|
+
* command lists its sections; and each docs-tree node by its route. The tree's
|
|
28
|
+
* guides split into sections like topics, and its typed docs also match by
|
|
29
|
+
* their own name, so `assertResponse` finds `cli/api/functions/assert-response`.
|
|
30
|
+
*
|
|
31
|
+
* Reads the project's catalog rather than the CLI's own docs directory, so a
|
|
32
|
+
* topic an integration contributed (or replaced) is searchable exactly like a
|
|
33
|
+
* built-in one — otherwise the replacement is served by `astryx docs` but
|
|
34
|
+
* invisible to the command whose job is finding it.
|
|
35
|
+
* @param {string} cwd
|
|
36
|
+
* @returns {Promise<Candidate[]>}
|
|
37
|
+
*/
|
|
38
|
+
export async function gatherDocs(cwd) {
|
|
39
|
+
/** @type {Candidate[]} */
|
|
40
|
+
const candidates = [];
|
|
41
|
+
let catalog;
|
|
42
|
+
try {
|
|
43
|
+
catalog = await loadDocsCatalog(cwd);
|
|
44
|
+
} catch {
|
|
45
|
+
return candidates;
|
|
46
|
+
}
|
|
47
|
+
let tree = null;
|
|
48
|
+
try {
|
|
49
|
+
tree = await projectTree(catalog);
|
|
50
|
+
} catch {
|
|
51
|
+
// `astryx doctor` reports a tree that fails to build; search still
|
|
52
|
+
// indexes the topics.
|
|
53
|
+
}
|
|
54
|
+
for (const entry of catalog.entries()) {
|
|
55
|
+
// A topic whose name opens another doc (spec:AST-046 FR11) is not
|
|
56
|
+
// offered: every hit's command must open the hit.
|
|
57
|
+
if (tree && !holdsOwnName(tree, catalog, entry)) continue;
|
|
58
|
+
let lowered = null;
|
|
59
|
+
try {
|
|
60
|
+
lowered = await lowerTopic(catalog, entry);
|
|
61
|
+
} catch {
|
|
62
|
+
// A topic that cannot be loaded is reported by the commands that own
|
|
63
|
+
// integration issues; search just cannot index it.
|
|
64
|
+
}
|
|
65
|
+
const doc = lowered?.doc ?? null;
|
|
66
|
+
// A flat topic lives in the Unorganized level; its hits say so, and name
|
|
67
|
+
// the package each section came from.
|
|
68
|
+
const home = tree?.get(entry.name);
|
|
69
|
+
const placed = home?.ref?.flatTopic === entry.name ? home : null;
|
|
70
|
+
/** @type {Map<string, string>} */
|
|
71
|
+
const packages = new Map([
|
|
72
|
+
[entry.providerId ?? entry.package, entry.package],
|
|
73
|
+
...entry.extensions.map(
|
|
74
|
+
ext =>
|
|
75
|
+
/** @type {[string, string]} */ ([
|
|
76
|
+
ext.providerId ?? ext.package,
|
|
77
|
+
ext.package,
|
|
78
|
+
]),
|
|
79
|
+
),
|
|
80
|
+
]);
|
|
81
|
+
candidates.push(
|
|
82
|
+
...topicCandidates(
|
|
83
|
+
entry.name,
|
|
84
|
+
doc,
|
|
85
|
+
entry.title,
|
|
86
|
+
'',
|
|
87
|
+
placed && tree
|
|
88
|
+
? [
|
|
89
|
+
...tree.ancestors(placed).map(a => a.title),
|
|
90
|
+
doc?.title || entry.title || entry.name,
|
|
91
|
+
].join(' › ')
|
|
92
|
+
: undefined,
|
|
93
|
+
placed ? `astryx docs ${placed.parent}` : undefined,
|
|
94
|
+
entry.package,
|
|
95
|
+
key =>
|
|
96
|
+
packages.get(lowered?.sectionProviders?.[key] ?? '') ?? entry.package,
|
|
97
|
+
),
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
if (tree == null) return candidates;
|
|
101
|
+
for (const node of tree.nodes.values()) {
|
|
102
|
+
// A flat topic is indexed above, as a topic.
|
|
103
|
+
if (node.ref?.flatTopic) continue;
|
|
104
|
+
// A tree hit names where it lives: its ancestors' titles, then its own.
|
|
105
|
+
const path = [...tree.ancestors(node).map(a => a.title), node.title];
|
|
106
|
+
if (node.kind === 'generic') {
|
|
107
|
+
let doc = null;
|
|
108
|
+
try {
|
|
109
|
+
doc = (await lowerTopic(catalog, guideEntry(node))).doc;
|
|
110
|
+
} catch {
|
|
111
|
+
// As above: the owning commands report it.
|
|
112
|
+
}
|
|
113
|
+
candidates.push(
|
|
114
|
+
...topicCandidates(
|
|
115
|
+
node.route,
|
|
116
|
+
doc,
|
|
117
|
+
node.title,
|
|
118
|
+
node.summary,
|
|
119
|
+
path.join(' › '),
|
|
120
|
+
node.parent == null ? undefined : `astryx docs ${node.parent}`,
|
|
121
|
+
node.provider,
|
|
122
|
+
),
|
|
123
|
+
);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
const selfDoc = /** @type {any} */ (node.ref)?.selfDoc;
|
|
127
|
+
// A typed doc's content is what `astryx docs <route>` prints. The first
|
|
128
|
+
// column of its tables names what the doc defines (an error code, an
|
|
129
|
+
// option, a parameter), so each is a keyword the doc answers to.
|
|
130
|
+
/** @type {any[]} */
|
|
131
|
+
const content = (await nodeView(catalog, tree, node)).content ?? [];
|
|
132
|
+
/** @type {string[]} */
|
|
133
|
+
const defined = [];
|
|
134
|
+
for (const block of content) {
|
|
135
|
+
if (block.type !== 'table' || !Array.isArray(block.rows)) continue;
|
|
136
|
+
for (const row of block.rows)
|
|
137
|
+
if (row[0] != null) defined.push(plain(row[0]));
|
|
138
|
+
}
|
|
139
|
+
candidates.push({
|
|
140
|
+
domain: 'doc',
|
|
141
|
+
name: node.name,
|
|
142
|
+
keywords: [
|
|
143
|
+
node.route.slice(node.route.lastIndexOf('/') + 1),
|
|
144
|
+
...(Array.isArray(selfDoc?.keywords) ? selfDoc.keywords : []),
|
|
145
|
+
// A namespace doc's own keywords, which it declares for search.
|
|
146
|
+
...(Array.isArray(node.keywords) ? node.keywords : []),
|
|
147
|
+
...defined,
|
|
148
|
+
...codeTerms({content}),
|
|
149
|
+
],
|
|
150
|
+
_declared: [
|
|
151
|
+
...(Array.isArray(selfDoc?.keywords) ? selfDoc.keywords : []),
|
|
152
|
+
...(Array.isArray(node.keywords) ? node.keywords : []),
|
|
153
|
+
],
|
|
154
|
+
description: node.summary || '',
|
|
155
|
+
prose: sectionProse({title: node.title, content}),
|
|
156
|
+
titles: [node.title],
|
|
157
|
+
_topic: node.route,
|
|
158
|
+
_title: path.join(' › '),
|
|
159
|
+
_command: `astryx docs ${node.route}`,
|
|
160
|
+
_parent:
|
|
161
|
+
node.parent == null ? 'astryx docs' : `astryx docs ${node.parent}`,
|
|
162
|
+
_package: node.provider,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
return candidates;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* The candidates one topic yields: the topic itself, and one per section when
|
|
170
|
+
* it has more than one. A topic's command lists its sections, and a section's
|
|
171
|
+
* command reads only that section, so a hit never costs a whole-topic read.
|
|
172
|
+
* @param {string} name - the topic name, or a placed guide's route
|
|
173
|
+
* @param {any} doc - the lowered topic, or null when it did not load
|
|
174
|
+
* @param {string} [title]
|
|
175
|
+
* @param {string} [summary]
|
|
176
|
+
* @param {string} [path] - where the topic lives in the docs tree, as titles
|
|
177
|
+
* joined by ` › `; a flat topic is its own title
|
|
178
|
+
* @param {string} [parent] - the command that opens the level above the
|
|
179
|
+
* topic: its namespace, or the Unorganized level for a flat topic
|
|
180
|
+
* @param {string} [pkg] - the npm package that authored the topic
|
|
181
|
+
* @param {(key: string) => string} [sectionPackage] - the npm package a
|
|
182
|
+
* section came from: an extension's section names the extension's package
|
|
183
|
+
* @returns {Candidate[]}
|
|
184
|
+
*/
|
|
185
|
+
function topicCandidates(
|
|
186
|
+
name,
|
|
187
|
+
doc,
|
|
188
|
+
title,
|
|
189
|
+
summary = '',
|
|
190
|
+
path,
|
|
191
|
+
parent,
|
|
192
|
+
pkg,
|
|
193
|
+
sectionPackage,
|
|
194
|
+
) {
|
|
195
|
+
/** @type {any[]} */
|
|
196
|
+
const sections = doc?.sections ?? [];
|
|
197
|
+
const docTitle = path || doc?.title || title || name;
|
|
198
|
+
const split = sections.length > 1;
|
|
199
|
+
// A placed guide also answers to its last route segment's words:
|
|
200
|
+
// `quick start` is cli/integrations/quick-start.
|
|
201
|
+
const leaf = name.slice(name.lastIndexOf('/') + 1);
|
|
202
|
+
/** @type {Candidate[]} */
|
|
203
|
+
const out = [
|
|
204
|
+
{
|
|
205
|
+
domain: 'doc',
|
|
206
|
+
name,
|
|
207
|
+
keywords: [
|
|
208
|
+
...(leaf !== name ? [leaf.replaceAll('-', ' ')] : []),
|
|
209
|
+
...(doc?.title || title ? [doc?.title || title] : []),
|
|
210
|
+
...(Array.isArray(doc?.keywords) ? doc.keywords : []),
|
|
211
|
+
],
|
|
212
|
+
_declared: Array.isArray(doc?.keywords) ? doc.keywords : [],
|
|
213
|
+
description: doc?.description || summary,
|
|
214
|
+
prose: split
|
|
215
|
+
? sections.map(section => section.title).filter(Boolean)
|
|
216
|
+
: sections.flatMap(sectionProse),
|
|
217
|
+
titles: [
|
|
218
|
+
doc?.title || title || name,
|
|
219
|
+
// A topic read whole answers for the headings inside it.
|
|
220
|
+
...(split ? [] : sections.flatMap(s => [s.title, ...headings(s)])),
|
|
221
|
+
].filter(Boolean),
|
|
222
|
+
_topic: name,
|
|
223
|
+
_title: docTitle,
|
|
224
|
+
_command: split ? `astryx docs ${name} --index` : `astryx docs ${name}`,
|
|
225
|
+
...(parent ? {_parent: parent} : {}),
|
|
226
|
+
...(pkg ? {_package: pkg} : {}),
|
|
227
|
+
},
|
|
228
|
+
];
|
|
229
|
+
if (!split) return out;
|
|
230
|
+
for (const section of sections) {
|
|
231
|
+
const key = sectionKey(section);
|
|
232
|
+
out.push({
|
|
233
|
+
domain: 'doc',
|
|
234
|
+
name: key,
|
|
235
|
+
keywords: [
|
|
236
|
+
...(section.title ? [section.title] : []),
|
|
237
|
+
...headings(section),
|
|
238
|
+
...codeTerms(section),
|
|
239
|
+
],
|
|
240
|
+
description: sectionSummary(section),
|
|
241
|
+
prose: sectionProse(section),
|
|
242
|
+
titles: [section.title, ...headings(section)].filter(Boolean),
|
|
243
|
+
_topic: name,
|
|
244
|
+
_section: key,
|
|
245
|
+
_title: `${docTitle} › ${section.title}`,
|
|
246
|
+
_command: `astryx docs ${name} ${key}`,
|
|
247
|
+
_parent: `astryx docs ${name} --index`,
|
|
248
|
+
...((sectionPackage?.(key) ?? pkg)
|
|
249
|
+
? {_package: sectionPackage?.(key) ?? pkg}
|
|
250
|
+
: {}),
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
return out;
|
|
254
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
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
|
+
/** @typedef {import('./_lexical.mjs').Candidate} Candidate */
|
|
5
|
+
/**
|
|
6
|
+
* Build hook candidates: name + keywords + usage/description from the hook's
|
|
7
|
+
* .doc.mjs.
|
|
8
|
+
* @param {string} coreDir
|
|
9
|
+
* @returns {Promise<Candidate[]>}
|
|
10
|
+
*/
|
|
11
|
+
export function gatherHooks(coreDir: string): Promise<Candidate[]>;
|
|
12
|
+
export type Candidate = import("./_lexical.mjs").Candidate;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file Search's hook candidates, read from Core's hook docs. Imported only
|
|
5
|
+
* by the adapter.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {CORE_PACKAGE} from '../../foundation/discovery/component-discovery.mjs';
|
|
9
|
+
import {
|
|
10
|
+
discoverHooks,
|
|
11
|
+
findHookDoc,
|
|
12
|
+
} from '../../foundation/discovery/hook-discovery.mjs';
|
|
13
|
+
import {loadModuleDoc} from './_module-doc.mjs';
|
|
14
|
+
|
|
15
|
+
/** @typedef {import('./_lexical.mjs').Candidate} Candidate */
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Build hook candidates: name + keywords + usage/description from the hook's
|
|
19
|
+
* .doc.mjs.
|
|
20
|
+
* @param {string} coreDir
|
|
21
|
+
* @returns {Promise<Candidate[]>}
|
|
22
|
+
*/
|
|
23
|
+
export async function gatherHooks(coreDir) {
|
|
24
|
+
const grouped = discoverHooks(coreDir);
|
|
25
|
+
const names = Object.values(grouped).flat();
|
|
26
|
+
/** @type {Candidate[]} */
|
|
27
|
+
const candidates = [];
|
|
28
|
+
for (const hookName of names) {
|
|
29
|
+
const docPath = findHookDoc(coreDir, hookName);
|
|
30
|
+
/** @type {string[]} */
|
|
31
|
+
let keywords = [];
|
|
32
|
+
let description = '';
|
|
33
|
+
let importPath = '@astryxdesign/core/hooks';
|
|
34
|
+
if (docPath) {
|
|
35
|
+
const doc = await loadModuleDoc(docPath, 'docs', 'hooks');
|
|
36
|
+
if (doc) {
|
|
37
|
+
keywords = Array.isArray(doc.keywords) ? doc.keywords : [];
|
|
38
|
+
description = doc.usage?.description || doc.description || '';
|
|
39
|
+
importPath = doc.importPath || importPath;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
candidates.push({
|
|
43
|
+
domain: 'hook',
|
|
44
|
+
name: hookName,
|
|
45
|
+
keywords,
|
|
46
|
+
description,
|
|
47
|
+
_package: CORE_PACKAGE,
|
|
48
|
+
_import: importPath,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return candidates;
|
|
52
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
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
|
+
/** @typedef {import('./_lexical.mjs').Candidate} Candidate */
|
|
5
|
+
/**
|
|
6
|
+
* Build template candidates (page + block) from the template discovery API.
|
|
7
|
+
* @param {string} cwd
|
|
8
|
+
* @returns {Promise<Candidate[]>}
|
|
9
|
+
*/
|
|
10
|
+
export function gatherTemplates(cwd: string): Promise<Candidate[]>;
|
|
11
|
+
/**
|
|
12
|
+
* Build theme candidates from bundled and integration-provided themes. Without
|
|
13
|
+
* this, an integration's themes are invisible to `search` even though
|
|
14
|
+
* `theme list` and `discover` already resolve them.
|
|
15
|
+
*
|
|
16
|
+
* A theme's slug and display name are its names, and its description is prose
|
|
17
|
+
* (`spec:AST-050/FR14`). A theme declares no keywords, so a word it shares with
|
|
18
|
+
* the query only through its description is a description mention: read as a
|
|
19
|
+
* keyword, every word of the description would outrank the components and docs
|
|
20
|
+
* that declare that word.
|
|
21
|
+
* @param {string} cwd
|
|
22
|
+
* @returns {Promise<Candidate[]>}
|
|
23
|
+
*/
|
|
24
|
+
export function gatherThemes(cwd: string): Promise<Candidate[]>;
|
|
25
|
+
export type Candidate = import("./_lexical.mjs").Candidate;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file Search's template and theme candidates. Imported only by the adapter.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {CORE_PACKAGE} from '../../foundation/discovery/component-discovery.mjs';
|
|
8
|
+
import {discoverTemplates, extractComponents} from '../template/template.mjs';
|
|
9
|
+
import {listAvailableThemes} from '../theme/_adapter.mjs';
|
|
10
|
+
import {templateLookupIds} from '../../foundation/discovery/template-adapter.mjs';
|
|
11
|
+
|
|
12
|
+
/** @typedef {import('./_lexical.mjs').Candidate} Candidate */
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Build template candidates (page + block) from the template discovery API.
|
|
16
|
+
* @param {string} cwd
|
|
17
|
+
* @returns {Promise<Candidate[]>}
|
|
18
|
+
*/
|
|
19
|
+
export async function gatherTemplates(cwd) {
|
|
20
|
+
let templates;
|
|
21
|
+
try {
|
|
22
|
+
templates = await discoverTemplates(cwd);
|
|
23
|
+
} catch {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
return templates.map(t => {
|
|
27
|
+
// A replacement's target is its canonical unqualified lookup id. Keep the
|
|
28
|
+
// integration-owned id as a keyword and response label, but score and print
|
|
29
|
+
// commands against the id that `template()` resolves back to this entry.
|
|
30
|
+
// This matters for replacement chains: one replacement's own id can be the
|
|
31
|
+
// target of another, so using that shadowed id as the command would select
|
|
32
|
+
// the other template.
|
|
33
|
+
const commandName = t.replaces ?? t.dirName;
|
|
34
|
+
// Blocks ship an authored componentsUsed; page templates don't, so derive
|
|
35
|
+
// them from the source. Category words (e.g. "Dashboard - Analytics") are
|
|
36
|
+
// strong intent signal for pages, which otherwise only index on name +
|
|
37
|
+
// description.
|
|
38
|
+
//
|
|
39
|
+
// Authored signal and derived signal are kept apart: componentsUsed and
|
|
40
|
+
// category words are a deliberate statement of what the template is for,
|
|
41
|
+
// while scraped JSX tags only say what it happens to render. See the
|
|
42
|
+
// scoring table at the top of this file for why the derived set is capped.
|
|
43
|
+
const keywords = Array.isArray(t.componentsUsed)
|
|
44
|
+
? [...t.componentsUsed]
|
|
45
|
+
: [];
|
|
46
|
+
keywords.push(...templateLookupIds(t).filter(id => id !== commandName));
|
|
47
|
+
/** @type {string[]} */
|
|
48
|
+
let weakKeywords = [];
|
|
49
|
+
if (t.type === 'page') {
|
|
50
|
+
if (t.filePath) {
|
|
51
|
+
try {
|
|
52
|
+
weakKeywords = extractComponents(t.filePath);
|
|
53
|
+
} catch {
|
|
54
|
+
// Best-effort: skip keyword enrichment if the source can't be read.
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (t.category)
|
|
58
|
+
keywords.push(...t.category.split(/[^A-Za-z0-9]+/).filter(Boolean));
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
domain: 'template',
|
|
62
|
+
name: commandName,
|
|
63
|
+
keywords,
|
|
64
|
+
weakKeywords,
|
|
65
|
+
description: t.description || '',
|
|
66
|
+
// A template's own keywords name the many ideas it serves, so they score
|
|
67
|
+
// as its prose does, not as a name: one of them matching one word of a
|
|
68
|
+
// short idea is not a direct match.
|
|
69
|
+
prose: t.keywords ?? [],
|
|
70
|
+
_displayName: t.name,
|
|
71
|
+
_package: t.package ?? CORE_PACKAGE,
|
|
72
|
+
_kind: t.type, // 'page' | 'block'
|
|
73
|
+
_resultName: t.dirName,
|
|
74
|
+
_commandName: commandName,
|
|
75
|
+
};
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Build theme candidates from bundled and integration-provided themes. Without
|
|
81
|
+
* this, an integration's themes are invisible to `search` even though
|
|
82
|
+
* `theme list` and `discover` already resolve them.
|
|
83
|
+
*
|
|
84
|
+
* A theme's slug and display name are its names, and its description is prose
|
|
85
|
+
* (`spec:AST-050/FR14`). A theme declares no keywords, so a word it shares with
|
|
86
|
+
* the query only through its description is a description mention: read as a
|
|
87
|
+
* keyword, every word of the description would outrank the components and docs
|
|
88
|
+
* that declare that word.
|
|
89
|
+
* @param {string} cwd
|
|
90
|
+
* @returns {Promise<Candidate[]>}
|
|
91
|
+
*/
|
|
92
|
+
export async function gatherThemes(cwd) {
|
|
93
|
+
let themes;
|
|
94
|
+
try {
|
|
95
|
+
themes = await listAvailableThemes(cwd);
|
|
96
|
+
} catch {
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
return themes.map(t => ({
|
|
100
|
+
domain: 'theme',
|
|
101
|
+
name: t.slug,
|
|
102
|
+
aliases: t.displayName ? [t.displayName] : [],
|
|
103
|
+
description: t.description || '',
|
|
104
|
+
_displayName: t.displayName,
|
|
105
|
+
_package: t.package,
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
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
|
+
* A search candidate gathered from one content domain. Extra underscore-
|
|
6
|
+
* prefixed fields carry domain-specific payload used only by {@link toResult}.
|
|
7
|
+
* @typedef {object} Candidate
|
|
8
|
+
* @property {'component'|'hook'|'doc'|'template'|'theme'} domain
|
|
9
|
+
* @property {string} name
|
|
10
|
+
* @property {string[]} [aliases] - Other names the candidate answers to, scored
|
|
11
|
+
* with the same name signals: a theme's display name.
|
|
12
|
+
* @property {string[]} [keywords]
|
|
13
|
+
* @property {string[]} [_declared] - A doc's own declared keywords: what its
|
|
14
|
+
* author lists in `keywords`, without the code terms and table cells its
|
|
15
|
+
* other keywords add.
|
|
16
|
+
* @property {string[]} [weakKeywords]
|
|
17
|
+
* @property {string} [description]
|
|
18
|
+
* @property {string[]} [prose]
|
|
19
|
+
* @property {string[]} [guidance]
|
|
20
|
+
* @property {string[]} [titles] - A doc's title and the headings inside it:
|
|
21
|
+
* the lines a reader scans to pick it. The whole query standing in one of
|
|
22
|
+
* them, or one of them standing whole in the query, is a top-tier match.
|
|
23
|
+
* @property {string} [_import]
|
|
24
|
+
* @property {string} [_title]
|
|
25
|
+
* @property {string} [_topic] - A doc result's topic or docs-tree route.
|
|
26
|
+
* @property {string} [_section] - A doc result's section key, when it is one section.
|
|
27
|
+
* @property {string} [_command] - The command that reads exactly this doc part.
|
|
28
|
+
* @property {string} [_parent] - The command that opens the level above a doc
|
|
29
|
+
* part: its topic's section list, or the docs-tree namespace it sits in.
|
|
30
|
+
* @property {string} [_package] - The npm package that owns the candidate:
|
|
31
|
+
* Core's package for its components, hooks, and templates, the integration's
|
|
32
|
+
* for what it contributed, and for a doc part the package that wrote it.
|
|
33
|
+
* @property {string} [_displayName]
|
|
34
|
+
* @property {'page'|'block'} [_kind]
|
|
35
|
+
* @property {string} [_resultName]
|
|
36
|
+
* @property {string} [_commandName]
|
|
37
|
+
* @property {'file'|'doc'} [_hold] - How an integration component holds its
|
|
38
|
+
* name, for the owner rule.
|
|
39
|
+
* @property {string} [_docPath] - An integration component's doc.
|
|
40
|
+
*/
|
|
41
|
+
/**
|
|
42
|
+
* True when the query is the candidate's whole name or another name of it,
|
|
43
|
+
* typed as is or, for anything but a doc, with its spaces removed. Such a
|
|
44
|
+
* query names that entry outright.
|
|
45
|
+
* @param {Candidate} candidate
|
|
46
|
+
* @param {string} term the query, trimmed and lowercased
|
|
47
|
+
* @returns {boolean}
|
|
48
|
+
*/
|
|
49
|
+
export function namesWhole(candidate: Candidate, term: string): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Today's lexical ranking of a gathered catalog: the search response
|
|
52
|
+
* `searchCore` returns.
|
|
53
|
+
*
|
|
54
|
+
* @param {string} query
|
|
55
|
+
* @param {{limit?: number}} options
|
|
56
|
+
* @param {{all: Candidate[], components: Candidate[], searchedComponents: boolean, outside?: boolean, coreSkipped?: string | null}} catalog
|
|
57
|
+
* @returns {import('./search.type.mjs').SearchResponse}
|
|
58
|
+
*/
|
|
59
|
+
export function rankLexical(query: string, options: {
|
|
60
|
+
limit?: number;
|
|
61
|
+
}, catalog: {
|
|
62
|
+
all: Candidate[];
|
|
63
|
+
components: Candidate[];
|
|
64
|
+
searchedComponents: boolean;
|
|
65
|
+
outside?: boolean;
|
|
66
|
+
coreSkipped?: string | null;
|
|
67
|
+
}): import("./search.type.mjs").SearchResponse;
|
|
68
|
+
/**
|
|
69
|
+
* A search candidate gathered from one content domain. Extra underscore-
|
|
70
|
+
* prefixed fields carry domain-specific payload used only by {@link toResult}.
|
|
71
|
+
*/
|
|
72
|
+
export type Candidate = {
|
|
73
|
+
domain: "component" | "hook" | "doc" | "template" | "theme";
|
|
74
|
+
name: string;
|
|
75
|
+
/**
|
|
76
|
+
* - Other names the candidate answers to, scored
|
|
77
|
+
* with the same name signals: a theme's display name.
|
|
78
|
+
*/
|
|
79
|
+
aliases?: string[] | undefined;
|
|
80
|
+
keywords?: string[] | undefined;
|
|
81
|
+
/**
|
|
82
|
+
* - A doc's own declared keywords: what its
|
|
83
|
+
* author lists in `keywords`, without the code terms and table cells its
|
|
84
|
+
* other keywords add.
|
|
85
|
+
*/
|
|
86
|
+
_declared?: string[] | undefined;
|
|
87
|
+
weakKeywords?: string[] | undefined;
|
|
88
|
+
description?: string | undefined;
|
|
89
|
+
prose?: string[] | undefined;
|
|
90
|
+
guidance?: string[] | undefined;
|
|
91
|
+
/**
|
|
92
|
+
* - A doc's title and the headings inside it:
|
|
93
|
+
* the lines a reader scans to pick it. The whole query standing in one of
|
|
94
|
+
* them, or one of them standing whole in the query, is a top-tier match.
|
|
95
|
+
*/
|
|
96
|
+
titles?: string[] | undefined;
|
|
97
|
+
_import?: string | undefined;
|
|
98
|
+
_title?: string | undefined;
|
|
99
|
+
/**
|
|
100
|
+
* - A doc result's topic or docs-tree route.
|
|
101
|
+
*/
|
|
102
|
+
_topic?: string | undefined;
|
|
103
|
+
/**
|
|
104
|
+
* - A doc result's section key, when it is one section.
|
|
105
|
+
*/
|
|
106
|
+
_section?: string | undefined;
|
|
107
|
+
/**
|
|
108
|
+
* - The command that reads exactly this doc part.
|
|
109
|
+
*/
|
|
110
|
+
_command?: string | undefined;
|
|
111
|
+
/**
|
|
112
|
+
* - The command that opens the level above a doc
|
|
113
|
+
* part: its topic's section list, or the docs-tree namespace it sits in.
|
|
114
|
+
*/
|
|
115
|
+
_parent?: string | undefined;
|
|
116
|
+
/**
|
|
117
|
+
* - The npm package that owns the candidate:
|
|
118
|
+
* Core's package for its components, hooks, and templates, the integration's
|
|
119
|
+
* for what it contributed, and for a doc part the package that wrote it.
|
|
120
|
+
*/
|
|
121
|
+
_package?: string | undefined;
|
|
122
|
+
_displayName?: string | undefined;
|
|
123
|
+
_kind?: "page" | "block" | undefined;
|
|
124
|
+
_resultName?: string | undefined;
|
|
125
|
+
_commandName?: string | undefined;
|
|
126
|
+
/**
|
|
127
|
+
* - How an integration component holds its
|
|
128
|
+
* name, for the owner rule.
|
|
129
|
+
*/
|
|
130
|
+
_hold?: "file" | "doc" | undefined;
|
|
131
|
+
/**
|
|
132
|
+
* - An integration component's doc.
|
|
133
|
+
*/
|
|
134
|
+
_docPath?: string | undefined;
|
|
135
|
+
};
|