@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,65 @@
|
|
|
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
|
+
* Split a query into meaningful content tokens (lowercased, stopwords + very
|
|
6
|
+
* short words removed). Empty for single-word queries (callers fall back to
|
|
7
|
+
* whole-phrase scoring).
|
|
8
|
+
* @param {string} term - Already-lowercased query.
|
|
9
|
+
* @returns {string[]}
|
|
10
|
+
*/
|
|
11
|
+
export function tokenizeQuery(term: string): string[];
|
|
12
|
+
/**
|
|
13
|
+
* Score a candidate against a query, handling multi-word natural language.
|
|
14
|
+
* Tries the whole phrase (so exact/near matches still win) AND a per-token
|
|
15
|
+
* pass (so "data table with filters" matches `table-page` via table+filter),
|
|
16
|
+
* and returns whichever is stronger.
|
|
17
|
+
*
|
|
18
|
+
* @param {string} term - Lowercased full query.
|
|
19
|
+
* @param {string[]} tokens - Content tokens from tokenizeQuery(term).
|
|
20
|
+
* @param {object} candidate
|
|
21
|
+
* @returns {{score: number, reason: string} | null}
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* The words of a title or query, lowercased, without punctuation or code ticks.
|
|
25
|
+
* @param {string} text
|
|
26
|
+
* @returns {string[]}
|
|
27
|
+
*/
|
|
28
|
+
export function phraseWords(text: string): string[];
|
|
29
|
+
/**
|
|
30
|
+
* Whether two words are the same word, allowing a plural on either side, so
|
|
31
|
+
* `data attributes selector` still reads "Data attribute selectors".
|
|
32
|
+
* @param {string} a
|
|
33
|
+
* @param {string} b
|
|
34
|
+
*/
|
|
35
|
+
export function samePhraseWord(a: string, b: string): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Whether two phrases are the same words, in order, ignoring case,
|
|
38
|
+
* punctuation, and a plural on any word: `side panel` is "Side panels", and
|
|
39
|
+
* `headers and footers` is `header-and-footer`.
|
|
40
|
+
* @param {string} a
|
|
41
|
+
* @param {string} b
|
|
42
|
+
* @returns {boolean}
|
|
43
|
+
*/
|
|
44
|
+
export function samePhrase(a: string, b: string): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Whether `plural` is the plural of `word`: `integrations` of `integration`,
|
|
47
|
+
* `boxes` of `box`. `es` only follows s, x, z, ch, or sh, so `notes` is not a
|
|
48
|
+
* plural of `not`.
|
|
49
|
+
* @param {string} plural
|
|
50
|
+
* @param {string} word
|
|
51
|
+
*/
|
|
52
|
+
export function pluralOf(plural: string, word: string): boolean;
|
|
53
|
+
/** Valid domain filters for `--type`. */
|
|
54
|
+
export const SEARCH_DOMAINS: string[];
|
|
55
|
+
/**
|
|
56
|
+
* The domains a search reads without @astryxdesign/core. An open search outside
|
|
57
|
+
* an app covers these alone.
|
|
58
|
+
*/
|
|
59
|
+
export const CORELESS_DOMAINS: string[];
|
|
60
|
+
/**
|
|
61
|
+
* Filler words stripped from multi-word queries so natural-language phrasing
|
|
62
|
+
* ("a page where you can see business stats") ranks on its content words.
|
|
63
|
+
* Exported for `build`, whose page ranker strips the same words.
|
|
64
|
+
*/
|
|
65
|
+
export const STOPWORDS: Set<string>;
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file Reading a search query: its domains, stopwords, content tokens, and
|
|
5
|
+
* phrase words.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {unlinkText} from '../../foundation/doc-compiler/links.mjs';
|
|
9
|
+
import {sameWord} from './_words.mjs';
|
|
10
|
+
|
|
11
|
+
/** Valid domain filters for `--type`. */
|
|
12
|
+
export const SEARCH_DOMAINS = ['component', 'hook', 'doc', 'template', 'theme'];
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The domains a search reads without @astryxdesign/core. An open search outside
|
|
16
|
+
* an app covers these alone.
|
|
17
|
+
*/
|
|
18
|
+
export const CORELESS_DOMAINS = ['doc', 'theme'];
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Filler words stripped from multi-word queries so natural-language phrasing
|
|
22
|
+
* ("a page where you can see business stats") ranks on its content words.
|
|
23
|
+
* Exported for `build`, whose page ranker strips the same words.
|
|
24
|
+
*/
|
|
25
|
+
export const STOPWORDS = new Set([
|
|
26
|
+
'a',
|
|
27
|
+
'an',
|
|
28
|
+
'the',
|
|
29
|
+
'of',
|
|
30
|
+
'for',
|
|
31
|
+
'to',
|
|
32
|
+
'with',
|
|
33
|
+
'and',
|
|
34
|
+
'or',
|
|
35
|
+
'in',
|
|
36
|
+
'on',
|
|
37
|
+
'at',
|
|
38
|
+
'by',
|
|
39
|
+
'that',
|
|
40
|
+
'this',
|
|
41
|
+
'my',
|
|
42
|
+
'your',
|
|
43
|
+
'our',
|
|
44
|
+
'their',
|
|
45
|
+
'is',
|
|
46
|
+
'are',
|
|
47
|
+
'be',
|
|
48
|
+
'it',
|
|
49
|
+
'its',
|
|
50
|
+
'as',
|
|
51
|
+
'from',
|
|
52
|
+
'page',
|
|
53
|
+
'screen',
|
|
54
|
+
'app',
|
|
55
|
+
'application',
|
|
56
|
+
'view',
|
|
57
|
+
'where',
|
|
58
|
+
'you',
|
|
59
|
+
'can',
|
|
60
|
+
'some',
|
|
61
|
+
'like',
|
|
62
|
+
'just',
|
|
63
|
+
'basically',
|
|
64
|
+
'kinda',
|
|
65
|
+
'want',
|
|
66
|
+
'wants',
|
|
67
|
+
'need',
|
|
68
|
+
'needs',
|
|
69
|
+
'something',
|
|
70
|
+
'thing',
|
|
71
|
+
'things',
|
|
72
|
+
'build',
|
|
73
|
+
'make',
|
|
74
|
+
'create',
|
|
75
|
+
'i',
|
|
76
|
+
'me',
|
|
77
|
+
'we',
|
|
78
|
+
'us',
|
|
79
|
+
'so',
|
|
80
|
+
'up',
|
|
81
|
+
'out',
|
|
82
|
+
'over',
|
|
83
|
+
'side',
|
|
84
|
+
'one',
|
|
85
|
+
'big',
|
|
86
|
+
]);
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Split a query into meaningful content tokens (lowercased, stopwords + very
|
|
90
|
+
* short words removed). Empty for single-word queries (callers fall back to
|
|
91
|
+
* whole-phrase scoring).
|
|
92
|
+
* @param {string} term - Already-lowercased query.
|
|
93
|
+
* @returns {string[]}
|
|
94
|
+
*/
|
|
95
|
+
export function tokenizeQuery(term) {
|
|
96
|
+
return (
|
|
97
|
+
term
|
|
98
|
+
.split(/\s+/)
|
|
99
|
+
// Strip only leading/trailing punctuation; keep joined identifiers intact
|
|
100
|
+
// (e.g. "foo_bar" stays one token) so gibberish stays gibberish.
|
|
101
|
+
.map(t => t.replace(/^[^a-z0-9]+|[^a-z0-9]+$/g, ''))
|
|
102
|
+
.filter(t => t.length >= 2 && !STOPWORDS.has(t))
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Score a candidate against a query, handling multi-word natural language.
|
|
108
|
+
* Tries the whole phrase (so exact/near matches still win) AND a per-token
|
|
109
|
+
* pass (so "data table with filters" matches `table-page` via table+filter),
|
|
110
|
+
* and returns whichever is stronger.
|
|
111
|
+
*
|
|
112
|
+
* @param {string} term - Lowercased full query.
|
|
113
|
+
* @param {string[]} tokens - Content tokens from tokenizeQuery(term).
|
|
114
|
+
* @param {object} candidate
|
|
115
|
+
* @returns {{score: number, reason: string} | null}
|
|
116
|
+
*/
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* The words of a title or query, lowercased, without punctuation or code ticks.
|
|
120
|
+
* @param {string} text
|
|
121
|
+
* @returns {string[]}
|
|
122
|
+
*/
|
|
123
|
+
export function phraseWords(text) {
|
|
124
|
+
return (
|
|
125
|
+
unlinkText(text)
|
|
126
|
+
.toLowerCase()
|
|
127
|
+
.match(/[a-z0-9]+/g) ?? []
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Whether two words are the same word, allowing a plural on either side, so
|
|
133
|
+
* `data attributes selector` still reads "Data attribute selectors".
|
|
134
|
+
* @param {string} a
|
|
135
|
+
* @param {string} b
|
|
136
|
+
*/
|
|
137
|
+
export function samePhraseWord(a, b) {
|
|
138
|
+
return (
|
|
139
|
+
a === b ||
|
|
140
|
+
`${a}s` === b ||
|
|
141
|
+
`${b}s` === a ||
|
|
142
|
+
`${a}es` === b ||
|
|
143
|
+
`${b}es` === a
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Whether two phrases are the same words, in order, ignoring case,
|
|
149
|
+
* punctuation, and a plural on any word: `side panel` is "Side panels", and
|
|
150
|
+
* `headers and footers` is `header-and-footer`.
|
|
151
|
+
* @param {string} a
|
|
152
|
+
* @param {string} b
|
|
153
|
+
* @returns {boolean}
|
|
154
|
+
*/
|
|
155
|
+
export function samePhrase(a, b) {
|
|
156
|
+
const x = phraseWords(a);
|
|
157
|
+
const y = phraseWords(b);
|
|
158
|
+
return (
|
|
159
|
+
x.length > 0 &&
|
|
160
|
+
x.length === y.length &&
|
|
161
|
+
x.every((w, k) => samePhraseWord(w, y[k]) || sameWord(w, y[k]))
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Whether `plural` is the plural of `word`: `integrations` of `integration`,
|
|
167
|
+
* `boxes` of `box`. `es` only follows s, x, z, ch, or sh, so `notes` is not a
|
|
168
|
+
* plural of `not`.
|
|
169
|
+
* @param {string} plural
|
|
170
|
+
* @param {string} word
|
|
171
|
+
*/
|
|
172
|
+
export function pluralOf(plural, word) {
|
|
173
|
+
if (word.length < 3) return false;
|
|
174
|
+
if (plural === `${word}s`) return true;
|
|
175
|
+
return /(?:s|x|z|ch|sh)$/.test(word) && plural === `${word}es`;
|
|
176
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
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
|
+
* Map a scored candidate to its public, actionable result shape. Each result
|
|
7
|
+
* carries enough to act on it: the domain, name, a one-line description, and
|
|
8
|
+
* the follow-up command (and import path where relevant).
|
|
9
|
+
*
|
|
10
|
+
* @param {Candidate} c - candidate
|
|
11
|
+
* @param {number} score
|
|
12
|
+
* @param {string} reason
|
|
13
|
+
* @param {number} matchedTerms - Query concepts this result answered.
|
|
14
|
+
* @param {number} queryTerms - Query concepts there were to answer.
|
|
15
|
+
* @param {boolean} [outside] - The search runs outside a project, where a
|
|
16
|
+
* theme cannot be added to an app, so its command forks the source instead
|
|
17
|
+
* (spec AST-081 FR3).
|
|
18
|
+
*/
|
|
19
|
+
export function toResult(c: Candidate, score: number, reason: string, matchedTerms: number, queryTerms: number, outside?: boolean): {
|
|
20
|
+
domain: "component" | "hook" | "theme" | "doc" | "template";
|
|
21
|
+
name: string;
|
|
22
|
+
package: string | undefined;
|
|
23
|
+
score: number;
|
|
24
|
+
reason: string;
|
|
25
|
+
description: string;
|
|
26
|
+
} | {
|
|
27
|
+
import: string | undefined;
|
|
28
|
+
command: string;
|
|
29
|
+
domain: "component" | "hook" | "theme" | "doc" | "template";
|
|
30
|
+
name: string;
|
|
31
|
+
package: string | undefined;
|
|
32
|
+
score: number;
|
|
33
|
+
reason: string;
|
|
34
|
+
description: string;
|
|
35
|
+
} | {
|
|
36
|
+
parent?: string | undefined;
|
|
37
|
+
title: string | undefined;
|
|
38
|
+
command: string;
|
|
39
|
+
section?: string | undefined;
|
|
40
|
+
name: string;
|
|
41
|
+
domain: "component" | "hook" | "theme" | "doc" | "template";
|
|
42
|
+
package: string | undefined;
|
|
43
|
+
score: number;
|
|
44
|
+
reason: string;
|
|
45
|
+
description: string;
|
|
46
|
+
} | {
|
|
47
|
+
displayName: string | undefined;
|
|
48
|
+
kind: "page" | "block" | undefined;
|
|
49
|
+
command: string;
|
|
50
|
+
domain: "component" | "hook" | "theme" | "doc" | "template";
|
|
51
|
+
name: string;
|
|
52
|
+
package: string | undefined;
|
|
53
|
+
score: number;
|
|
54
|
+
reason: string;
|
|
55
|
+
description: string;
|
|
56
|
+
} | {
|
|
57
|
+
displayName: string | undefined;
|
|
58
|
+
command: string;
|
|
59
|
+
domain: "component" | "hook" | "theme" | "doc" | "template";
|
|
60
|
+
name: string;
|
|
61
|
+
package: string | undefined;
|
|
62
|
+
score: number;
|
|
63
|
+
reason: string;
|
|
64
|
+
description: string;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Throws the search argument errors: an empty query, an unknown type, a limit
|
|
68
|
+
* that is not a positive integer.
|
|
69
|
+
*
|
|
70
|
+
* @param {string} query
|
|
71
|
+
* @param {{type?: string, limit?: number}} options
|
|
72
|
+
*/
|
|
73
|
+
export function checkSearchArgs(query: string, options: {
|
|
74
|
+
type?: string;
|
|
75
|
+
limit?: number;
|
|
76
|
+
}): void;
|
|
77
|
+
/**
|
|
78
|
+
* The components (name and keywords) a search response was scored against, or
|
|
79
|
+
* null when that search was narrowed away from components.
|
|
80
|
+
* @param {object} response
|
|
81
|
+
* @returns {{name: string, keywords: string[]}[] | null}
|
|
82
|
+
*/
|
|
83
|
+
export function searchedComponents(response: object): {
|
|
84
|
+
name: string;
|
|
85
|
+
keywords: string[];
|
|
86
|
+
}[] | null;
|
|
87
|
+
/**
|
|
88
|
+
* The components each search response was scored against, by response:
|
|
89
|
+
* `build` reads them to tell a part of a page from a page without gathering
|
|
90
|
+
* them again. Module-private, so they never enter search's JSON.
|
|
91
|
+
* @type {WeakMap<object, {name: string, keywords: string[]}[]>}
|
|
92
|
+
*/
|
|
93
|
+
export const searchedComponentsOf: WeakMap<object, {
|
|
94
|
+
name: string;
|
|
95
|
+
keywords: string[];
|
|
96
|
+
}[]>;
|
|
97
|
+
export type Candidate = import("./_lexical.mjs").Candidate;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file Search's arguments and results: argument checks, the result each
|
|
5
|
+
* candidate becomes, and the components a search read.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {AstryxError} from '../error.mjs';
|
|
9
|
+
import {ERROR_CODES} from '../../foundation/response/error-codes.mjs';
|
|
10
|
+
import {setResultCoverage} from './coverage.mjs';
|
|
11
|
+
import {SEARCH_DOMAINS} from './_query.mjs';
|
|
12
|
+
|
|
13
|
+
/** @typedef {import('./_lexical.mjs').Candidate} Candidate */
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Map a scored candidate to its public, actionable result shape. Each result
|
|
17
|
+
* carries enough to act on it: the domain, name, a one-line description, and
|
|
18
|
+
* the follow-up command (and import path where relevant).
|
|
19
|
+
*
|
|
20
|
+
* @param {Candidate} c - candidate
|
|
21
|
+
* @param {number} score
|
|
22
|
+
* @param {string} reason
|
|
23
|
+
* @param {number} matchedTerms - Query concepts this result answered.
|
|
24
|
+
* @param {number} queryTerms - Query concepts there were to answer.
|
|
25
|
+
* @param {boolean} [outside] - The search runs outside a project, where a
|
|
26
|
+
* theme cannot be added to an app, so its command forks the source instead
|
|
27
|
+
* (spec AST-081 FR3).
|
|
28
|
+
*/
|
|
29
|
+
export function toResult(
|
|
30
|
+
c,
|
|
31
|
+
score,
|
|
32
|
+
reason,
|
|
33
|
+
matchedTerms,
|
|
34
|
+
queryTerms,
|
|
35
|
+
outside = false,
|
|
36
|
+
) {
|
|
37
|
+
const base = {
|
|
38
|
+
domain: c.domain,
|
|
39
|
+
name: c._resultName ?? c.name,
|
|
40
|
+
package: c._package,
|
|
41
|
+
score,
|
|
42
|
+
reason,
|
|
43
|
+
description: c.description || '',
|
|
44
|
+
};
|
|
45
|
+
let result;
|
|
46
|
+
switch (c.domain) {
|
|
47
|
+
case 'component':
|
|
48
|
+
result = {
|
|
49
|
+
...base,
|
|
50
|
+
import: c._import,
|
|
51
|
+
command: c._command ?? `astryx component ${c.name}`,
|
|
52
|
+
};
|
|
53
|
+
break;
|
|
54
|
+
case 'hook':
|
|
55
|
+
result = {
|
|
56
|
+
...base,
|
|
57
|
+
import: c._import,
|
|
58
|
+
command: `astryx hook ${c.name}`,
|
|
59
|
+
};
|
|
60
|
+
break;
|
|
61
|
+
case 'doc':
|
|
62
|
+
// A doc result names its topic or route, plus the section when the
|
|
63
|
+
// hit is one section; its command reads exactly that part.
|
|
64
|
+
result = {
|
|
65
|
+
...base,
|
|
66
|
+
name: c._topic ?? c.name,
|
|
67
|
+
...(c._section ? {section: c._section} : {}),
|
|
68
|
+
title: c._title,
|
|
69
|
+
command: c._command ?? `astryx docs ${c.name}`,
|
|
70
|
+
...(c._parent ? {parent: c._parent} : {}),
|
|
71
|
+
};
|
|
72
|
+
break;
|
|
73
|
+
case 'template':
|
|
74
|
+
result = {
|
|
75
|
+
...base,
|
|
76
|
+
displayName: c._displayName,
|
|
77
|
+
kind: c._kind,
|
|
78
|
+
command: `astryx template ${c._commandName ?? c.name} --type ${c._kind}`,
|
|
79
|
+
};
|
|
80
|
+
break;
|
|
81
|
+
case 'theme':
|
|
82
|
+
result = {
|
|
83
|
+
...base,
|
|
84
|
+
displayName: c._displayName,
|
|
85
|
+
command: outside
|
|
86
|
+
? `astryx theme eject ${c.name}`
|
|
87
|
+
: `astryx theme add ${c.name}`,
|
|
88
|
+
};
|
|
89
|
+
break;
|
|
90
|
+
default:
|
|
91
|
+
result = base;
|
|
92
|
+
}
|
|
93
|
+
return setResultCoverage(result, matchedTerms, queryTerms);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Throws the search argument errors: an empty query, an unknown type, a limit
|
|
98
|
+
* that is not a positive integer.
|
|
99
|
+
*
|
|
100
|
+
* @param {string} query
|
|
101
|
+
* @param {{type?: string, limit?: number}} options
|
|
102
|
+
*/
|
|
103
|
+
export function checkSearchArgs(query, options) {
|
|
104
|
+
const {type, limit = 20} = options;
|
|
105
|
+
|
|
106
|
+
if (!query || !String(query).trim()) {
|
|
107
|
+
throw new AstryxError(
|
|
108
|
+
'A search query is required',
|
|
109
|
+
[{name: 'astryx search button', reason: 'example'}],
|
|
110
|
+
ERROR_CODES.ERR_INVALID_ARGUMENT,
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (type && !SEARCH_DOMAINS.includes(type)) {
|
|
115
|
+
throw new AstryxError(
|
|
116
|
+
`Unknown --type "${type}"`,
|
|
117
|
+
SEARCH_DOMAINS.map(d => ({name: d, reason: 'valid type'})),
|
|
118
|
+
ERROR_CODES.ERR_INVALID_ARGUMENT,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Validate limit here (not just in the CLI) so direct API callers get the same
|
|
123
|
+
// contract: a non-positive or non-integer limit is an error, never a silent
|
|
124
|
+
// "return everything". (Previously `limit <= 0` fell through to the full set.)
|
|
125
|
+
if (limit != null && (!Number.isInteger(limit) || limit <= 0)) {
|
|
126
|
+
throw new AstryxError(
|
|
127
|
+
`Invalid limit "${limit}". Must be a positive integer.`,
|
|
128
|
+
undefined,
|
|
129
|
+
ERROR_CODES.ERR_INVALID_ARGUMENT,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* The components each search response was scored against, by response:
|
|
136
|
+
* `build` reads them to tell a part of a page from a page without gathering
|
|
137
|
+
* them again. Module-private, so they never enter search's JSON.
|
|
138
|
+
* @type {WeakMap<object, {name: string, keywords: string[]}[]>}
|
|
139
|
+
*/
|
|
140
|
+
export const searchedComponentsOf = new WeakMap();
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* The components (name and keywords) a search response was scored against, or
|
|
144
|
+
* null when that search was narrowed away from components.
|
|
145
|
+
* @param {object} response
|
|
146
|
+
* @returns {{name: string, keywords: string[]}[] | null}
|
|
147
|
+
*/
|
|
148
|
+
export function searchedComponents(response) {
|
|
149
|
+
return searchedComponentsOf.get(response) ?? null;
|
|
150
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
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
|
+
* Best score for a token against a candidate, fanning out through synonyms
|
|
6
|
+
* (synonym hits are discounted so a direct hit always wins).
|
|
7
|
+
* @param {string} tok
|
|
8
|
+
* @param {Candidate} candidate
|
|
9
|
+
* @param {{fuzzy?: boolean}} [opts]
|
|
10
|
+
* @returns {{score: number, reason: string} | null}
|
|
11
|
+
*/
|
|
12
|
+
export function bestForToken(tok: string, candidate: Candidate, opts?: {
|
|
13
|
+
fuzzy?: boolean;
|
|
14
|
+
}): {
|
|
15
|
+
score: number;
|
|
16
|
+
reason: string;
|
|
17
|
+
} | null;
|
|
18
|
+
/**
|
|
19
|
+
* @param {string} term - Lowercased full query.
|
|
20
|
+
* @param {string[]} tokens - Content tokens from tokenizeQuery(term).
|
|
21
|
+
* @param {Candidate} candidate
|
|
22
|
+
* @param {string[]} [words] - The query's words for compound names (see
|
|
23
|
+
* compoundHit); empty when the query names an entry outright.
|
|
24
|
+
* @param {{plain?: boolean}} [opts] - `plain`: score without matches on part
|
|
25
|
+
* of a name or a joined keyword.
|
|
26
|
+
* @returns {{score: number, reason: string, matched: number, total: number, alias?: 'partial' | 'keyword', aliasWords?: number} | null}
|
|
27
|
+
* `matched`/`total` are the query concepts this candidate answered, out of
|
|
28
|
+
* the concepts the query had. Callers that must distinguish "matched one word
|
|
29
|
+
* of three" from "matched all three" — `build`, which gates its pages group
|
|
30
|
+
* on coverage — cannot recover that from the score, because a single strong
|
|
31
|
+
* hit and a broad weak one land on the same number.
|
|
32
|
+
*/
|
|
33
|
+
export function scoreQuery(term: string, tokens: string[], candidate: Candidate, words?: string[], { plain }?: {
|
|
34
|
+
plain?: boolean;
|
|
35
|
+
}): {
|
|
36
|
+
score: number;
|
|
37
|
+
reason: string;
|
|
38
|
+
matched: number;
|
|
39
|
+
total: number;
|
|
40
|
+
alias?: "partial" | "keyword";
|
|
41
|
+
aliasWords?: number;
|
|
42
|
+
} | null;
|
|
43
|
+
/**
|
|
44
|
+
* The score of a candidate that matches every content word of a multi-word
|
|
45
|
+
* query, before a bonus of up to 8 for how strong its strongest match is: just
|
|
46
|
+
* above anything that matches only some of the words. The token-sum path tops
|
|
47
|
+
* out near 150 for a partial match (a 100 on one word, the per-word bonus, and
|
|
48
|
+
* the coverage term), so an AND-match with one keyword-strength hit (see
|
|
49
|
+
* {@link STRONG_TOKEN_SCORE}) always outranks an OR-match, and stays below the
|
|
50
|
+
* title tiers.
|
|
51
|
+
*/
|
|
52
|
+
export const FULL_COVERAGE_SCORE: 151;
|
|
53
|
+
/**
|
|
54
|
+
* The strongest single-word hit an every-word match needs to take that tier: a
|
|
55
|
+
* keyword substring. Two passing mentions in prose, or the components a page
|
|
56
|
+
* happens to render, are breadth, not relevance; they stay on the token sum,
|
|
57
|
+
* below an exact name or keyword hit on one of the words.
|
|
58
|
+
*/
|
|
59
|
+
export const STRONG_TOKEN_SCORE: 70;
|
|
60
|
+
export type Candidate = import("./_lexical.mjs").Candidate;
|