@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,50 @@
|
|
|
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
|
+
* The strongest hit a run of two or more adjacent query words has when it is
|
|
7
|
+
* read as one joined word, and the query words it covers. Readers type a
|
|
8
|
+
* compound name as separate words (`table header cell` is TableHeaderCell,
|
|
9
|
+
* `number field` the keyword `numberfield`), and no single word of the run
|
|
10
|
+
* matches on its own.
|
|
11
|
+
*
|
|
12
|
+
* - 100: the run joins to a component, hook, or template name, or its plural
|
|
13
|
+
* (`table header cells` is TableHeaderCell), or a hook name without its
|
|
14
|
+
* `use` prefix (`table sticky columns` is useTableStickyColumns).
|
|
15
|
+
* - 90: the run joins to a one-word keyword (`number field` is
|
|
16
|
+
* `numberfield`).
|
|
17
|
+
* - 95: the run joins to most of a name, cut short (`dropdown menu radio` is
|
|
18
|
+
* DropdownMenuRadioItem); 92: it is a name's leading words, covering at least half
|
|
19
|
+
* of it (`top nav mega` is TopNavMegaMenu); 70: it is two or more adjacent
|
|
20
|
+
* words of a name that cover at least half of it (`header cell` in
|
|
21
|
+
* TableHeaderCell), the same coverage a word prefix needs. These three
|
|
22
|
+
* are for components and hooks only.
|
|
23
|
+
*
|
|
24
|
+
* A doc's name is a route or key, so docs are left out.
|
|
25
|
+
* @param {string[]} words - The query's words, stopwords included.
|
|
26
|
+
* @param {Candidate} candidate
|
|
27
|
+
* @returns {{score: number, reason: string, covered: Set<string>, alias?: 'partial' | 'keyword', aliasWords?: number} | null}
|
|
28
|
+
* `covered` holds the run's words and every stretch of them joined. `alias`
|
|
29
|
+
* marks a hit on part of a name or on a keyword, not on the whole name.
|
|
30
|
+
*/
|
|
31
|
+
export function compoundHit(words: string[], candidate: Candidate): {
|
|
32
|
+
score: number;
|
|
33
|
+
reason: string;
|
|
34
|
+
covered: Set<string>;
|
|
35
|
+
alias?: "partial" | "keyword";
|
|
36
|
+
aliasWords?: number;
|
|
37
|
+
} | null;
|
|
38
|
+
/**
|
|
39
|
+
* The longest run of adjacent query words read as one name. Core's longest
|
|
40
|
+
* names run to six words; a longer run is a sentence, not a name, and reading
|
|
41
|
+
* every run of a long build idea costs time on every candidate.
|
|
42
|
+
*/
|
|
43
|
+
export const MAX_RUN_WORDS: 6;
|
|
44
|
+
/**
|
|
45
|
+
* Each candidate's compound hit from its last scoreQuery, so domainPriority
|
|
46
|
+
* reads it without walking the query's runs again.
|
|
47
|
+
* @type {WeakMap<object, ReturnType<typeof compoundHit>>}
|
|
48
|
+
*/
|
|
49
|
+
export const compoundOf: WeakMap<object, ReturnType<typeof compoundHit>>;
|
|
50
|
+
export type Candidate = import("./_lexical.mjs").Candidate;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file Compound names: adjacent query words joined into a candidate's name
|
|
5
|
+
* or keyword (`icon button` and IconButton).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {wordsOf, joined} from './_words.mjs';
|
|
9
|
+
import {pluralOf} from './_query.mjs';
|
|
10
|
+
|
|
11
|
+
/** @typedef {import('./_lexical.mjs').Candidate} Candidate */
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The strongest hit a run of two or more adjacent query words has when it is
|
|
15
|
+
* read as one joined word, and the query words it covers. Readers type a
|
|
16
|
+
* compound name as separate words (`table header cell` is TableHeaderCell,
|
|
17
|
+
* `number field` the keyword `numberfield`), and no single word of the run
|
|
18
|
+
* matches on its own.
|
|
19
|
+
*
|
|
20
|
+
* - 100: the run joins to a component, hook, or template name, or its plural
|
|
21
|
+
* (`table header cells` is TableHeaderCell), or a hook name without its
|
|
22
|
+
* `use` prefix (`table sticky columns` is useTableStickyColumns).
|
|
23
|
+
* - 90: the run joins to a one-word keyword (`number field` is
|
|
24
|
+
* `numberfield`).
|
|
25
|
+
* - 95: the run joins to most of a name, cut short (`dropdown menu radio` is
|
|
26
|
+
* DropdownMenuRadioItem); 92: it is a name's leading words, covering at least half
|
|
27
|
+
* of it (`top nav mega` is TopNavMegaMenu); 70: it is two or more adjacent
|
|
28
|
+
* words of a name that cover at least half of it (`header cell` in
|
|
29
|
+
* TableHeaderCell), the same coverage a word prefix needs. These three
|
|
30
|
+
* are for components and hooks only.
|
|
31
|
+
*
|
|
32
|
+
* A doc's name is a route or key, so docs are left out.
|
|
33
|
+
* @param {string[]} words - The query's words, stopwords included.
|
|
34
|
+
* @param {Candidate} candidate
|
|
35
|
+
* @returns {{score: number, reason: string, covered: Set<string>, alias?: 'partial' | 'keyword', aliasWords?: number} | null}
|
|
36
|
+
* `covered` holds the run's words and every stretch of them joined. `alias`
|
|
37
|
+
* marks a hit on part of a name or on a keyword, not on the whole name.
|
|
38
|
+
*/
|
|
39
|
+
export function compoundHit(words, candidate) {
|
|
40
|
+
if (candidate.domain === 'doc' || words.length < 2) return null;
|
|
41
|
+
const names = [candidate.name, ...(candidate.aliases ?? [])]
|
|
42
|
+
.filter(Boolean)
|
|
43
|
+
.map(name => ({name, nameJoined: joined(name), nameWords: wordsOf(name)}));
|
|
44
|
+
// Part of a name counts only for a component or hook. A template's name
|
|
45
|
+
// starts with the component it shows (`ProgressBarShowcase`), so part of it
|
|
46
|
+
// names the component, not the template.
|
|
47
|
+
const partial =
|
|
48
|
+
candidate.domain === 'component' || candidate.domain === 'hook';
|
|
49
|
+
// One-word lowercase keywords: a keyword an author declares for a reader
|
|
50
|
+
// to type, joined (`numberfield`). A capitalized one names another entry (a
|
|
51
|
+
// block's `componentsUsed`), which matches by its own name, and a keyword of
|
|
52
|
+
// several words is a phrase, matched as one.
|
|
53
|
+
const keywords = (candidate.keywords ?? []).filter(k =>
|
|
54
|
+
/^[a-z0-9]+$/.test(String(k)),
|
|
55
|
+
);
|
|
56
|
+
/** @type {{score: number, reason: string, covered: Set<string>, alias?: 'partial' | 'keyword', aliasWords?: number} | null} */
|
|
57
|
+
let best = null;
|
|
58
|
+
for (const {run, word} of queryRuns(words)) {
|
|
59
|
+
let score = 0;
|
|
60
|
+
let reason = '';
|
|
61
|
+
/** @type {'partial' | 'keyword' | null} */
|
|
62
|
+
let kind = null;
|
|
63
|
+
for (const {name, nameJoined, nameWords} of names) {
|
|
64
|
+
if (
|
|
65
|
+
nameJoined === word ||
|
|
66
|
+
pluralOf(word, nameJoined) ||
|
|
67
|
+
(candidate.domain === 'hook' && nameJoined === `use${word}`)
|
|
68
|
+
) {
|
|
69
|
+
if (score < 100)
|
|
70
|
+
[score, reason, kind] = [100, `name ${name} spelled`, null];
|
|
71
|
+
} else if (!partial) {
|
|
72
|
+
continue;
|
|
73
|
+
} else if (
|
|
74
|
+
nameJoined.startsWith(word) &&
|
|
75
|
+
word.length / nameJoined.length >= 0.75
|
|
76
|
+
) {
|
|
77
|
+
// Most of the name, cut short: `dropdown menu radio` is
|
|
78
|
+
// DropdownMenuRadioItem.
|
|
79
|
+
if (score < 95)
|
|
80
|
+
[score, reason, kind] = [95, `name ${name} spelled`, 'partial'];
|
|
81
|
+
} else if (
|
|
82
|
+
word.length / nameJoined.length >= 0.5 &&
|
|
83
|
+
run.every(
|
|
84
|
+
(w, j) => nameWords[j + (nameWords[0] === 'use' ? 1 : 0)] === w,
|
|
85
|
+
)
|
|
86
|
+
) {
|
|
87
|
+
// The name's leading words: `top nav mega` is TopNavMegaMenu.
|
|
88
|
+
if (score < 92)
|
|
89
|
+
[score, reason, kind] = [
|
|
90
|
+
92,
|
|
91
|
+
`name ${name} starts "${run.join(' ')}"`,
|
|
92
|
+
'partial',
|
|
93
|
+
];
|
|
94
|
+
} else if (
|
|
95
|
+
word.length / nameJoined.length >= 0.5 &&
|
|
96
|
+
nameWords.some((_, k) => run.every((w, j) => nameWords[k + j] === w))
|
|
97
|
+
) {
|
|
98
|
+
if (score < 70)
|
|
99
|
+
[score, reason, kind] = [
|
|
100
|
+
70,
|
|
101
|
+
`name ${name} holds "${run.join(' ')}"`,
|
|
102
|
+
'partial',
|
|
103
|
+
];
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (score < 90 && keywords.includes(word)) {
|
|
107
|
+
[score, reason, kind] = [
|
|
108
|
+
90,
|
|
109
|
+
`keyword "${run.join(' ')}" spelled`,
|
|
110
|
+
'keyword',
|
|
111
|
+
];
|
|
112
|
+
}
|
|
113
|
+
if (score > (best?.score ?? 0)) {
|
|
114
|
+
// Each word of the run, and each identifier typed as several of its
|
|
115
|
+
// words joined (`dropdownMenu` in `dropdownMenu checkbox`).
|
|
116
|
+
const covered = new Set();
|
|
117
|
+
for (let a = 0; a < run.length; a++) {
|
|
118
|
+
for (let b = a + 1; b <= run.length; b++) {
|
|
119
|
+
covered.add(run.slice(a, b).join(''));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
best = {
|
|
123
|
+
score,
|
|
124
|
+
reason,
|
|
125
|
+
covered,
|
|
126
|
+
...(kind ? {alias: kind, aliasWords: run.length} : {}),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return best;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* The longest run of adjacent query words read as one name. Core's longest
|
|
135
|
+
* names run to six words; a longer run is a sentence, not a name, and reading
|
|
136
|
+
* every run of a long build idea costs time on every candidate.
|
|
137
|
+
*/
|
|
138
|
+
export const MAX_RUN_WORDS = 6;
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Each run of two to {@link MAX_RUN_WORDS} adjacent query words, longest
|
|
142
|
+
* first, with the run joined into one word. Computed once per query.
|
|
143
|
+
* @type {WeakMap<string[], {run: string[], word: string}[]>}
|
|
144
|
+
*/
|
|
145
|
+
const runsOf = new WeakMap();
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* @param {string[]} words
|
|
149
|
+
* @returns {{run: string[], word: string}[]}
|
|
150
|
+
*/
|
|
151
|
+
function queryRuns(words) {
|
|
152
|
+
let runs = runsOf.get(words);
|
|
153
|
+
if (runs) return runs;
|
|
154
|
+
runs = [];
|
|
155
|
+
for (let len = Math.min(words.length, MAX_RUN_WORDS); len >= 2; len--) {
|
|
156
|
+
for (let i = 0; i + len <= words.length; i++) {
|
|
157
|
+
const run = words.slice(i, i + len);
|
|
158
|
+
runs.push({run, word: run.join('')});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
runsOf.set(words, runs);
|
|
162
|
+
return runs;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Each candidate's compound hit from its last scoreQuery, so domainPriority
|
|
167
|
+
* reads it without walking the query's runs again.
|
|
168
|
+
* @type {WeakMap<object, ReturnType<typeof compoundHit>>}
|
|
169
|
+
*/
|
|
170
|
+
export const compoundOf = new WeakMap();
|
|
@@ -0,0 +1,4 @@
|
|
|
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
|
+
export const doc: import("../../foundation/route/route.mjs").Searcher<import("./tree.mjs").SearchRequest, import("./_adapter.mjs").SearchCatalog, import("./search.type.mjs").SearchResponse>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file The doc searcher: the main searcher's signals with its own weights,
|
|
5
|
+
* in _doc-searcher.weights.json.
|
|
6
|
+
* @input {query, options} and the gathered catalog with its static data
|
|
7
|
+
* @output the search response, the lexical response's shape and size
|
|
8
|
+
* @position api/search; a topic child of the root dispatcher in tree.mjs
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {routeKit} from '../../foundation/route/route.mjs';
|
|
12
|
+
import {asWeights, rankWith} from './_main-searcher.mjs';
|
|
13
|
+
|
|
14
|
+
/** @type {import('./tree.mjs').SearchKit} */
|
|
15
|
+
const {defineSearcher} = routeKit;
|
|
16
|
+
|
|
17
|
+
export const doc = defineSearcher({
|
|
18
|
+
id: 'doc',
|
|
19
|
+
summary:
|
|
20
|
+
'Ranks entries for queries that ask for a doc, with weights of its own.',
|
|
21
|
+
run: (request, catalog, weights) =>
|
|
22
|
+
rankWith(request, catalog, asWeights(weights) ?? catalog.data.doc),
|
|
23
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file Tests for the doc searcher (_doc-searcher.mjs): with its own weights
|
|
5
|
+
* it ranks a doc first for a query that asks for one. Run against the real
|
|
6
|
+
* @astryxdesign/core.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {describe, it, expect} from 'vitest';
|
|
10
|
+
import * as path from 'node:path';
|
|
11
|
+
import {fileURLToPath} from 'node:url';
|
|
12
|
+
import {gatherCandidates, loadSearchData} from './_adapter.mjs';
|
|
13
|
+
import {doc} from './_doc-searcher.mjs';
|
|
14
|
+
|
|
15
|
+
const REPO = path.resolve(
|
|
16
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
17
|
+
'../../../..',
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
describe('doc searcher', () => {
|
|
21
|
+
it('ranks a doc first for a doc query', async () => {
|
|
22
|
+
const catalog = {
|
|
23
|
+
...(await gatherCandidates({cwd: REPO})),
|
|
24
|
+
data: loadSearchData(),
|
|
25
|
+
};
|
|
26
|
+
const answer = /** @type {any} */ (
|
|
27
|
+
await doc.run(
|
|
28
|
+
{query: 'how do I install a theme', options: {cwd: REPO}},
|
|
29
|
+
catalog,
|
|
30
|
+
null,
|
|
31
|
+
)
|
|
32
|
+
);
|
|
33
|
+
expect(answer.data.results[0].domain).toBe('doc');
|
|
34
|
+
expect(answer.data.results.length).toBeLessThanOrEqual(20);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"w":[0.255034,-0.032567,0.309242,0.050403,-0.358275,0.280939,0.461358,-0.613439,0.397626,0.070136,0.168831,0.032204,0.147137,-0.010509,-0.139086,0.247925,0.0,-0.013425,0.0,0.271127,-0.15666,0.161205,0.275391,-0.017229,-0.003888,0.018954,0.006559,0.010396,-0.003214,0.027251,-0.005917,0.093235,0.101059,0.006095,-0.013116,-0.036481,0.209443,0.12958,0.032606,0.036493,0.082651,0.0,0.0,0.0,0.067563,0.110925,0.145737,0.046132,-0.014063,0.127665,0.014997,0.180173,0.312727,0.079946,0.015853,-0.006495,0.009776,0.005509,0.006095],"mu":[0.122953,0.004932,0.176043,0.017021,0.147234,0.050213,0.178723,0.617872,0.005957,0.170087,0.638276,0.058177,0.049519,0.001321,0.051274,0.176439,0.061188,0.001341,0.0,0.009766,0.004823,0.073882,0.034193,0.000289,0.000827,0.000398,0.002652,0.001028,2.6e-05,0.008501,0.002364,0.111253,0.047342,0.000627,0.073136,0.02573,0.390793,0.145756,0.002861,0.044053,0.014124,0.0,0.0,0.0,0.007995,0.002104,0.029954,0.009344,0.000122,0.021697,0.001864,0.11116,0.041329,0.00039,0.000531,1e-06,0.000703,4.6e-05,6e-05],"sd":[0.272558,0.037015,0.37131,0.129352,0.35434,0.218385,0.383122,0.485909,0.076955,0.825557,1.798503,0.476936,0.124531,0.036157,0.2159,0.232289,1e-06,0.036574,1e-06,0.079222,0.057495,0.229243,0.146561,0.014114,0.017094,0.010761,0.028927,0.011091,0.003965,0.198705,0.104828,0.662804,0.448885,0.080939,0.547129,0.361981,1.544968,0.799909,0.170388,0.407639,0.250897,1e-06,1e-06,1e-06,0.048112,0.025129,0.109037,0.046651,0.006134,0.063975,0.011564,0.248543,0.049449,0.008334,0.023035,0.001195,0.026489,0.006758,0.007742]}
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
* The words one section says: its prose, headings, and list items.
|
|
6
|
+
* @param {any} section
|
|
7
|
+
* @returns {string[]}
|
|
8
|
+
*/
|
|
9
|
+
export function sectionProse(section: any): string[];
|
|
10
|
+
/**
|
|
11
|
+
* The identifiers a doc part names in code ticks (`token-ref`,
|
|
12
|
+
* `ERR_UNKNOWN_SECTION`). Each is a keyword: a reader who types one exactly
|
|
13
|
+
* wants the part that defines or explains it.
|
|
14
|
+
* @param {any} part - a section, or `{content}` of a typed doc
|
|
15
|
+
* @returns {string[]}
|
|
16
|
+
*/
|
|
17
|
+
export function codeTerms(part: any): string[];
|
|
18
|
+
/**
|
|
19
|
+
* The headings inside a section. Each names a subsection, so a query that
|
|
20
|
+
* names one should find the section as surely as one that names its title.
|
|
21
|
+
* @param {any} section
|
|
22
|
+
* @returns {string[]}
|
|
23
|
+
*/
|
|
24
|
+
export function headings(section: any): string[];
|
|
25
|
+
/**
|
|
26
|
+
* A table cell as plain words, without its code ticks.
|
|
27
|
+
* @param {unknown} cell
|
|
28
|
+
* @returns {string}
|
|
29
|
+
*/
|
|
30
|
+
export function plain(cell: unknown): string;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file The text search reads from a doc section: its prose, code terms,
|
|
5
|
+
* headings, and table cells as plain words. Imported only by the adapter's
|
|
6
|
+
* gatherers.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {unlinkText} from '../../foundation/doc-compiler/links.mjs';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The words one section says: its prose, headings, and list items.
|
|
13
|
+
* @param {any} section
|
|
14
|
+
* @returns {string[]}
|
|
15
|
+
*/
|
|
16
|
+
export function sectionProse(section) {
|
|
17
|
+
/** @type {string[]} */
|
|
18
|
+
const prose = [];
|
|
19
|
+
if (section?.title) prose.push(section.title);
|
|
20
|
+
for (const block of section?.content || []) {
|
|
21
|
+
if ((block.type === 'prose' || block.type === 'heading') && block.text) {
|
|
22
|
+
prose.push(block.text);
|
|
23
|
+
} else if (block.type === 'list' && Array.isArray(block.items)) {
|
|
24
|
+
for (const item of block.items) {
|
|
25
|
+
const text = typeof item === 'string' ? item : item?.text;
|
|
26
|
+
if (typeof text === 'string') prose.push(text);
|
|
27
|
+
}
|
|
28
|
+
} else if (block.type === 'table' && Array.isArray(block.rows)) {
|
|
29
|
+
for (const row of block.rows) prose.push(row.map(plain).join(' '));
|
|
30
|
+
} else if (block.type === 'code' && typeof block.code === 'string') {
|
|
31
|
+
prose.push([block.label, block.code].filter(Boolean).join(' '));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return prose;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The identifiers a doc part names in code ticks (`token-ref`,
|
|
39
|
+
* `ERR_UNKNOWN_SECTION`). Each is a keyword: a reader who types one exactly
|
|
40
|
+
* wants the part that defines or explains it.
|
|
41
|
+
* @param {any} part - a section, or `{content}` of a typed doc
|
|
42
|
+
* @returns {string[]}
|
|
43
|
+
*/
|
|
44
|
+
export function codeTerms(part) {
|
|
45
|
+
/** @type {Set<string>} */
|
|
46
|
+
const terms = new Set();
|
|
47
|
+
/** @param {unknown} text */
|
|
48
|
+
const scan = text => {
|
|
49
|
+
for (const m of String(text ?? '').matchAll(/`([^`\s]{2,40})`/g)) {
|
|
50
|
+
terms.add(m[1]);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
for (const block of part?.content || []) {
|
|
54
|
+
if (block.type === 'prose') scan(block.text);
|
|
55
|
+
else if (block.type === 'list' && Array.isArray(block.items)) {
|
|
56
|
+
for (const item of block.items) {
|
|
57
|
+
scan(typeof item === 'string' ? item : item?.text);
|
|
58
|
+
}
|
|
59
|
+
} else if (block.type === 'table' && Array.isArray(block.rows)) {
|
|
60
|
+
for (const row of block.rows) for (const cell of row) scan(cell);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return [...terms];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The headings inside a section. Each names a subsection, so a query that
|
|
68
|
+
* names one should find the section as surely as one that names its title.
|
|
69
|
+
* @param {any} section
|
|
70
|
+
* @returns {string[]}
|
|
71
|
+
*/
|
|
72
|
+
export function headings(section) {
|
|
73
|
+
return (section?.content || [])
|
|
74
|
+
.filter(
|
|
75
|
+
(/** @type {any} */ block) => block.type === 'heading' && block.text,
|
|
76
|
+
)
|
|
77
|
+
.map((/** @type {any} */ block) => String(block.text));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* A table cell as plain words, without its code ticks.
|
|
82
|
+
* @param {unknown} cell
|
|
83
|
+
* @returns {string}
|
|
84
|
+
*/
|
|
85
|
+
export function plain(cell) {
|
|
86
|
+
return unlinkText(String(cell ?? '')).replaceAll('`', '');
|
|
87
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
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
|
+
* The entries search can answer with and their text, by field. Each doc
|
|
6
|
+
* section is its own entry. Built once per gathered catalog.
|
|
7
|
+
* @param {any[]} all the gathered candidates
|
|
8
|
+
* @param {Record<string, {what?: string, synonyms?: string[], props?: string[]}>} cards
|
|
9
|
+
* @returns {EntryTable}
|
|
10
|
+
*/
|
|
11
|
+
export function entryTable(all: any[], cards: Record<string, {
|
|
12
|
+
what?: string;
|
|
13
|
+
synonyms?: string[];
|
|
14
|
+
props?: string[];
|
|
15
|
+
}>): EntryTable;
|
|
16
|
+
export function entryOf(table: {
|
|
17
|
+
index: Map<string, number>;
|
|
18
|
+
}, res: {
|
|
19
|
+
domain: string;
|
|
20
|
+
name: string;
|
|
21
|
+
package?: string;
|
|
22
|
+
section?: string;
|
|
23
|
+
}): number | undefined;
|
|
24
|
+
export function entryId(c: any): string;
|
|
25
|
+
export type EntryTable = {
|
|
26
|
+
/**
|
|
27
|
+
* every returnable `<domain>:<name>`, in code-point order
|
|
28
|
+
*/
|
|
29
|
+
ids: string[];
|
|
30
|
+
/**
|
|
31
|
+
* id -> position
|
|
32
|
+
*/
|
|
33
|
+
index: Map<string, number>;
|
|
34
|
+
/**
|
|
35
|
+
* one candidate per id, for its result
|
|
36
|
+
*/
|
|
37
|
+
candidate: any[];
|
|
38
|
+
/**
|
|
39
|
+
* domain index per id
|
|
40
|
+
*/
|
|
41
|
+
domain: number[];
|
|
42
|
+
/**
|
|
43
|
+
* each id's name words
|
|
44
|
+
*/
|
|
45
|
+
name: string[][];
|
|
46
|
+
fields: Record<"name" | "desc" | "card", {
|
|
47
|
+
post: Map<string, [number, number][]>;
|
|
48
|
+
idf: Map<string, number>;
|
|
49
|
+
len: number[];
|
|
50
|
+
avg: number;
|
|
51
|
+
}>;
|
|
52
|
+
};
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file The entry table search's searchers rank over: every returnable entry
|
|
5
|
+
* with its name words and the BM25 postings of its name, docs text, and card.
|
|
6
|
+
* Built once per gathered catalog.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {DOMAINS, words, fold, nameWords} from './_query-words.mjs';
|
|
10
|
+
|
|
11
|
+
/** @param {string[]} list @returns {Map<string, number>} */
|
|
12
|
+
const counts = list => {
|
|
13
|
+
const m = new Map();
|
|
14
|
+
for (const w of list) m.set(w, (m.get(w) ?? 0) + 1);
|
|
15
|
+
return m;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {object} EntryTable
|
|
20
|
+
* @property {string[]} ids every returnable `<domain>:<name>`, in code-point order
|
|
21
|
+
* @property {Map<string, number>} index id -> position
|
|
22
|
+
* @property {any[]} candidate one candidate per id, for its result
|
|
23
|
+
* @property {number[]} domain domain index per id
|
|
24
|
+
* @property {string[][]} name each id's name words
|
|
25
|
+
* @property {Record<'name'|'desc'|'card', {post: Map<string, [number, number][]>, idf: Map<string, number>, len: number[], avg: number}>} fields
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
const tables = new WeakMap();
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The entry a lexical result answers as.
|
|
32
|
+
* @param {{index: Map<string, number>}} table
|
|
33
|
+
* @param {{domain: string, name: string, package?: string, section?: string}} res
|
|
34
|
+
* @returns {number | undefined}
|
|
35
|
+
*/
|
|
36
|
+
export const entryOf = (table, res) => {
|
|
37
|
+
const id = `${res.domain}:${res.name}${res.section ? `#${res.section}` : ''}`;
|
|
38
|
+
return table.index.get(`${id}|${res.package}`) ?? table.index.get(id);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The id a candidate answers as: a doc section is its own entry, beside its
|
|
43
|
+
* topic. @param {any} c
|
|
44
|
+
*/
|
|
45
|
+
export const entryId = c =>
|
|
46
|
+
c.domain === 'doc'
|
|
47
|
+
? `doc:${c._topic ?? c.name}${c._section ? `#${c._section}` : ''}`
|
|
48
|
+
: `${c.domain}:${c._resultName ?? c.name}`;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The entries search can answer with and their text, by field. Each doc
|
|
52
|
+
* section is its own entry. Built once per gathered catalog.
|
|
53
|
+
* @param {any[]} all the gathered candidates
|
|
54
|
+
* @param {Record<string, {what?: string, synonyms?: string[], props?: string[]}>} cards
|
|
55
|
+
* @returns {EntryTable}
|
|
56
|
+
*/
|
|
57
|
+
export function entryTable(all, cards) {
|
|
58
|
+
const hit = tables.get(all);
|
|
59
|
+
if (hit) return hit;
|
|
60
|
+
/** @type {Map<string, any[]>} */
|
|
61
|
+
const by = new Map();
|
|
62
|
+
/** @type {Map<string, string>} each id's first package */
|
|
63
|
+
const owner = new Map();
|
|
64
|
+
for (const c of all) {
|
|
65
|
+
// Two packages may each give an entry the same name (an integration's
|
|
66
|
+
// Dialog beside Core's): the later package's entry keeps its own id.
|
|
67
|
+
const plain = entryId(c);
|
|
68
|
+
if (!owner.has(plain)) owner.set(plain, c._package ?? '');
|
|
69
|
+
const id =
|
|
70
|
+
(c._package ?? '') === owner.get(plain)
|
|
71
|
+
? plain
|
|
72
|
+
: `${plain}|${c._package}`;
|
|
73
|
+
if (!by.has(id)) by.set(id, []);
|
|
74
|
+
/** @type {any[]} */ (by.get(id)).push(c);
|
|
75
|
+
}
|
|
76
|
+
const ids = [...by.keys()].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
|
77
|
+
/** @type {Record<'name'|'desc'|'card', Map<string, number>[]>} */
|
|
78
|
+
const docs = {name: [], desc: [], card: []};
|
|
79
|
+
const table = /** @type {EntryTable} */ (
|
|
80
|
+
/** @type {unknown} */ ({
|
|
81
|
+
ids,
|
|
82
|
+
index: new Map(ids.map((id, k) => [id, k])),
|
|
83
|
+
candidate: [],
|
|
84
|
+
domain: [],
|
|
85
|
+
name: [],
|
|
86
|
+
fields: {},
|
|
87
|
+
})
|
|
88
|
+
);
|
|
89
|
+
for (const id of ids) {
|
|
90
|
+
const group = /** @type {any[]} */ (by.get(id));
|
|
91
|
+
const first = group.find(c => !c._section) ?? group[0];
|
|
92
|
+
const plain = id.split('|')[0].split('#')[0];
|
|
93
|
+
const [domain, name] = [
|
|
94
|
+
plain.slice(0, plain.indexOf(':')),
|
|
95
|
+
plain.slice(plain.indexOf(':') + 1),
|
|
96
|
+
];
|
|
97
|
+
const nw = nameWords(name).map(fold);
|
|
98
|
+
table.candidate.push(first);
|
|
99
|
+
table.domain.push(DOMAINS.indexOf(domain));
|
|
100
|
+
table.name.push(nw);
|
|
101
|
+
const text = [];
|
|
102
|
+
for (const c of group) {
|
|
103
|
+
text.push(
|
|
104
|
+
c.description ?? '',
|
|
105
|
+
...(c.keywords ?? []),
|
|
106
|
+
...(c.aliases ?? []),
|
|
107
|
+
...(c.titles ?? []),
|
|
108
|
+
);
|
|
109
|
+
text.push(
|
|
110
|
+
...(c.guidance ?? []).slice(0, 10),
|
|
111
|
+
...(c.prose ?? []).slice(0, 4),
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
const card =
|
|
115
|
+
domain === 'component' || domain === 'hook' ? cards[name] : undefined;
|
|
116
|
+
docs.name.push(counts([...nw, nw.join('')]));
|
|
117
|
+
docs.desc.push(counts(words(text.join(' ')).map(fold)));
|
|
118
|
+
docs.card.push(
|
|
119
|
+
counts(
|
|
120
|
+
card
|
|
121
|
+
? words(
|
|
122
|
+
[
|
|
123
|
+
card.what ?? '',
|
|
124
|
+
...(card.synonyms ?? []),
|
|
125
|
+
...(card.props ?? []),
|
|
126
|
+
].join(' '),
|
|
127
|
+
).map(fold)
|
|
128
|
+
: [],
|
|
129
|
+
),
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
const n = ids.length;
|
|
133
|
+
for (const f of /** @type {const} */ (['name', 'desc', 'card'])) {
|
|
134
|
+
/** @type {Map<string, [number, number][]>} */
|
|
135
|
+
const post = new Map();
|
|
136
|
+
/** @type {Map<string, number>} */
|
|
137
|
+
const df = new Map();
|
|
138
|
+
/** @type {number[]} */
|
|
139
|
+
const len = [];
|
|
140
|
+
docs[f].forEach((m, k) => {
|
|
141
|
+
let l = 1;
|
|
142
|
+
for (const [w, c] of m) {
|
|
143
|
+
l += c;
|
|
144
|
+
df.set(w, (df.get(w) ?? 0) + 1);
|
|
145
|
+
const list = post.get(w) ?? [];
|
|
146
|
+
list.push([k, c]);
|
|
147
|
+
post.set(w, list);
|
|
148
|
+
}
|
|
149
|
+
len.push(l);
|
|
150
|
+
});
|
|
151
|
+
const idf = new Map();
|
|
152
|
+
for (const [w, d] of df)
|
|
153
|
+
idf.set(w, Math.log(1 + (n - d + 0.5) / (d + 0.5)));
|
|
154
|
+
table.fields[f] = {post, idf, len, avg: len.reduce((a, b) => a + b, 0) / n};
|
|
155
|
+
}
|
|
156
|
+
tables.set(all, table);
|
|
157
|
+
return table;
|
|
158
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
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
|
+
* Every entry's signals for one query, as rows of numbers.
|
|
6
|
+
* @param {EntryTable} table
|
|
7
|
+
* @param {string} query
|
|
8
|
+
* @param {{domain: string, name: string, score?: number}[]} lexical the lexical ranking, best first
|
|
9
|
+
* @param {number[] | null} route the router's class probabilities
|
|
10
|
+
* @returns {Float64Array[]}
|
|
11
|
+
*/
|
|
12
|
+
export function signals(table: EntryTable, query: string, lexical: {
|
|
13
|
+
domain: string;
|
|
14
|
+
name: string;
|
|
15
|
+
score?: number;
|
|
16
|
+
}[], route: number[] | null): Float64Array[];
|
|
17
|
+
/**
|
|
18
|
+
* Ranks the entries by a searcher's weights: the weighted sum of each entry's
|
|
19
|
+
* standardized signals and per-domain copies; ties go to the earlier id.
|
|
20
|
+
* @param {EntryTable} table
|
|
21
|
+
* @param {Float64Array[]} rows
|
|
22
|
+
* @param {{w: number[], mu: number[], sd: number[]}} weights
|
|
23
|
+
* @returns {number[]} entry positions, best first
|
|
24
|
+
*/
|
|
25
|
+
export function rank(table: EntryTable, rows: Float64Array[], weights: {
|
|
26
|
+
w: number[];
|
|
27
|
+
mu: number[];
|
|
28
|
+
sd: number[];
|
|
29
|
+
}): number[];
|
|
30
|
+
/** @typedef {import('./_entries.mjs').EntryTable} EntryTable */
|
|
31
|
+
/** The signal names, in weights-file order (before the per-domain copies). */
|
|
32
|
+
export const SIGNALS: string[];
|
|
33
|
+
/** The signals that also get one copy per domain. */
|
|
34
|
+
export const PER_DOMAIN: string[];
|
|
35
|
+
export type EntryTable = import("./_entries.mjs").EntryTable;
|