@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
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
*/
|
|
11
11
|
export function validateLocalIntegration(cwd?: string): Promise<ValidateResult>;
|
|
12
12
|
/**
|
|
13
|
-
* Validate an INSTALLED integration package
|
|
13
|
+
* Validate an INSTALLED integration package: the nearest `node_modules/<spec>`
|
|
14
|
+
* at or above `cwd`, resolved the way the project loads it.
|
|
14
15
|
* @param {string} spec package name
|
|
15
16
|
* @param {string} [cwd]
|
|
16
17
|
* @returns {Promise<ValidateResult>}
|
|
@@ -43,7 +44,6 @@ export function summarizeIssues(issues: Issue[]): {
|
|
|
43
44
|
errors: number;
|
|
44
45
|
warnings: number;
|
|
45
46
|
};
|
|
46
|
-
export { validateLoadedIntegration };
|
|
47
47
|
export type Issue = import("../../foundation/integrations/issue").AstryxIntegrationIssue;
|
|
48
48
|
export type ValidateResult = {
|
|
49
49
|
/**
|
|
@@ -75,6 +75,15 @@ export type ValidateResult = {
|
|
|
75
75
|
code: string;
|
|
76
76
|
message: string;
|
|
77
77
|
}[] | undefined;
|
|
78
|
+
/**
|
|
79
|
+
* Layout findings added after the last minor
|
|
80
|
+
* release: validate lists them as notes, which count nowhere, since they
|
|
81
|
+
* may not fail `--strict` until the next (architecture:cli-surface INV35);
|
|
82
|
+
* `integration verify` still fails on a package.json npm cannot pack.
|
|
83
|
+
*/
|
|
84
|
+
listed?: import("../../foundation/integrations/issue").AstryxIntegrationIssue[] | undefined;
|
|
78
85
|
integration?: import("../../foundation/integrations/integrations.mjs").LoadedIntegration | undefined;
|
|
79
86
|
};
|
|
80
87
|
import { validateLoadedIntegration } from '../../foundation/integrations/validate-contributions.mjs';
|
|
88
|
+
import { isListedIssue } from '../../foundation/integrations/validate-contributions.mjs';
|
|
89
|
+
export { validateLoadedIntegration, isListedIssue };
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* - validateLocalIntegration(cwd) — the package rooted at `cwd` (nearest
|
|
13
13
|
* package.json + sibling astryx.integration.{ts,mjs,js}).
|
|
14
14
|
* - validateInstalledIntegration(spec, cwd) — an installed package resolved
|
|
15
|
-
* from `cwd
|
|
15
|
+
* from the node_modules chain at or above `cwd`.
|
|
16
16
|
*
|
|
17
17
|
* Both return a { found, name, version, manifestFile, issues } result. `found`
|
|
18
18
|
* is false only for the no-manifest local case, which is guidance (not an
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
import * as fs from 'node:fs';
|
|
35
35
|
import * as path from 'node:path';
|
|
36
36
|
import {assertWithin} from '../../foundation/fs/path-safety.mjs';
|
|
37
|
+
import {normalizeProviderId} from '../../foundation/identity/provider-identity.mjs';
|
|
37
38
|
import {integrationDocTypes} from '../docs/_adapter.mjs';
|
|
38
39
|
import {integrationDocConsistency} from '../../foundation/discovery/doc-consistency.mjs';
|
|
39
40
|
import {
|
|
@@ -51,20 +52,25 @@ import {
|
|
|
51
52
|
import {
|
|
52
53
|
findManifestPaths,
|
|
53
54
|
loadManifest,
|
|
54
|
-
|
|
55
|
+
resolveInstalledIntegrationDir,
|
|
55
56
|
} from '../../foundation/integrations/integrations.mjs';
|
|
56
57
|
// The on-disk contribution validators live in foundation: Project and
|
|
57
58
|
// integration-warnings need them too, and foundation must not depend on api.
|
|
58
59
|
import {
|
|
60
|
+
blockComponentsCheck,
|
|
61
|
+
componentDocNamesCheck,
|
|
59
62
|
INVALID_CLI_COMMAND,
|
|
63
|
+
isListedIssue,
|
|
64
|
+
issueListed as listed,
|
|
60
65
|
validateLoadedIntegration,
|
|
66
|
+
validateLoadedIntegrationListed,
|
|
61
67
|
issueError as error,
|
|
62
68
|
issueWarning as warning,
|
|
63
69
|
rootProblem,
|
|
64
70
|
} from '../../foundation/integrations/validate-contributions.mjs';
|
|
65
71
|
import {formatCliCommand} from '../../foundation/env/package-manager.mjs';
|
|
66
72
|
|
|
67
|
-
export {validateLoadedIntegration};
|
|
73
|
+
export {validateLoadedIntegration, isListedIssue};
|
|
68
74
|
|
|
69
75
|
/**
|
|
70
76
|
* @typedef {import('../../foundation/integrations/issue').AstryxIntegrationIssue} Issue
|
|
@@ -80,6 +86,10 @@ export {validateLoadedIntegration};
|
|
|
80
86
|
* @property {Issue[]} issues
|
|
81
87
|
* @property {Array<{code: string, message: string}>} [notes] Information that
|
|
82
88
|
* fails nothing, such as `invalid_cli_command` (a malformed `cliCommand`).
|
|
89
|
+
* @property {Issue[]} [listed] Layout findings added after the last minor
|
|
90
|
+
* release: validate lists them as notes, which count nowhere, since they
|
|
91
|
+
* may not fail `--strict` until the next (architecture:cli-surface INV35);
|
|
92
|
+
* `integration verify` still fails on a package.json npm cannot pack.
|
|
83
93
|
* @property {import('../../foundation/integrations/integrations.mjs').LoadedIntegration} [integration]
|
|
84
94
|
*/
|
|
85
95
|
|
|
@@ -99,6 +109,14 @@ function findNearestPackageJson(cwd) {
|
|
|
99
109
|
}
|
|
100
110
|
}
|
|
101
111
|
|
|
112
|
+
const ROOT_KINDS = /** @type {const} */ ([
|
|
113
|
+
'components',
|
|
114
|
+
'templates',
|
|
115
|
+
'codemods',
|
|
116
|
+
'docs',
|
|
117
|
+
'themes',
|
|
118
|
+
]);
|
|
119
|
+
|
|
102
120
|
const UNREACHABLE_SCAN_LIMIT = 5_000;
|
|
103
121
|
const UNREACHABLE_SKIP_DIRS = new Set([
|
|
104
122
|
'node_modules',
|
|
@@ -148,27 +166,51 @@ function unreadThemeFolderIssues(packageDir, loaded) {
|
|
|
148
166
|
*
|
|
149
167
|
* @param {string} packageDir
|
|
150
168
|
* @param {import('../../foundation/integrations/integrations.mjs').LoadedIntegration} loaded
|
|
169
|
+
* @param {string[]} [reported] folders whose files another issue already covers
|
|
151
170
|
* @returns {Promise<Issue[]>}
|
|
152
171
|
*/
|
|
153
|
-
async function findUnreachableContributionIssues(
|
|
172
|
+
async function findUnreachableContributionIssues(
|
|
173
|
+
packageDir,
|
|
174
|
+
loaded,
|
|
175
|
+
reported = [],
|
|
176
|
+
) {
|
|
177
|
+
// A root that is not a readable folder holds nothing: a fix must never
|
|
178
|
+
// send a file into it.
|
|
179
|
+
const usable = {...loaded};
|
|
180
|
+
for (const kind of ROOT_KINDS) {
|
|
181
|
+
const root = usable[kind];
|
|
182
|
+
if (root != null && rootProblem(root)) usable[kind] = undefined;
|
|
183
|
+
}
|
|
184
|
+
// Every declared root is skipped, readable or not: one that cannot be read
|
|
185
|
+
// has its own issue.
|
|
154
186
|
const roots = /** @type {string[]} */ (
|
|
155
|
-
[
|
|
156
|
-
loaded.components,
|
|
157
|
-
loaded.templates,
|
|
158
|
-
loaded.codemods,
|
|
159
|
-
loaded.docs,
|
|
160
|
-
loaded.themes,
|
|
161
|
-
].filter(Boolean)
|
|
187
|
+
ROOT_KINDS.map(kind => loaded[kind]).filter(Boolean)
|
|
162
188
|
);
|
|
189
|
+
// A folder a symlinked root points at is read through that root.
|
|
190
|
+
const realRoots = roots.map(root => {
|
|
191
|
+
try {
|
|
192
|
+
return fs.realpathSync(root);
|
|
193
|
+
} catch {
|
|
194
|
+
return root;
|
|
195
|
+
}
|
|
196
|
+
});
|
|
163
197
|
/** @type {Issue[]} */
|
|
164
198
|
const issues = [];
|
|
165
|
-
const context = createFixContext(packageDir,
|
|
199
|
+
const context = createFixContext(packageDir, usable);
|
|
166
200
|
let scanned = 0;
|
|
167
201
|
let truncated = false;
|
|
168
202
|
|
|
169
203
|
/** @param {string} dir */
|
|
170
204
|
async function walk(dir) {
|
|
171
205
|
if (roots.some(root => pathIsInside(dir, root))) return;
|
|
206
|
+
if (reported.some(folder => pathIsInside(dir, folder))) return;
|
|
207
|
+
let realDir = dir;
|
|
208
|
+
try {
|
|
209
|
+
realDir = fs.realpathSync(dir);
|
|
210
|
+
} catch {
|
|
211
|
+
// Unreadable: reported below.
|
|
212
|
+
}
|
|
213
|
+
if (realRoots.some(root => pathIsInside(realDir, root))) return;
|
|
172
214
|
let entries;
|
|
173
215
|
try {
|
|
174
216
|
entries = fs
|
|
@@ -286,12 +328,14 @@ async function validateAtPackageDir(
|
|
|
286
328
|
let manifest;
|
|
287
329
|
/** @type {string[]} */
|
|
288
330
|
let unknownKeys;
|
|
331
|
+
/** @type {string[]} */
|
|
332
|
+
let declaredKeys;
|
|
289
333
|
/** @type {string | undefined} */
|
|
290
334
|
let agentDocsError;
|
|
291
335
|
/** @type {string | undefined} */
|
|
292
336
|
let cliCommandError;
|
|
293
337
|
try {
|
|
294
|
-
({manifest, unknownKeys, agentDocsError, cliCommandError} =
|
|
338
|
+
({manifest, unknownKeys, declaredKeys, agentDocsError, cliCommandError} =
|
|
295
339
|
await loadManifest(
|
|
296
340
|
manifestFile,
|
|
297
341
|
`Integration manifest (${path.basename(manifestFile)})`,
|
|
@@ -301,9 +345,36 @@ async function validateAtPackageDir(
|
|
|
301
345
|
return result;
|
|
302
346
|
}
|
|
303
347
|
|
|
304
|
-
/** @
|
|
305
|
-
const
|
|
348
|
+
/** @type {string[]} absolute roots inside the package */
|
|
349
|
+
const absoluteRoots = [];
|
|
350
|
+
/**
|
|
351
|
+
* @param {string | null | undefined} value
|
|
352
|
+
* @param {string} key the manifest key that declares it
|
|
353
|
+
*/
|
|
354
|
+
const resolveRoot = (value, key, kind = 'contribution root') => {
|
|
306
355
|
if (value == null) return undefined;
|
|
356
|
+
if (path.isAbsolute(value)) {
|
|
357
|
+
// An absolute root names this machine's disk, which no install shares,
|
|
358
|
+
// even when it happens to point inside the package.
|
|
359
|
+
const inside = path.relative(packageDir, value);
|
|
360
|
+
const relative =
|
|
361
|
+
inside === '' || (!inside.startsWith('..') && !path.isAbsolute(inside))
|
|
362
|
+
? `./${inside.split(path.sep).join('/')}`.replace(/\/$/u, '')
|
|
363
|
+
: null;
|
|
364
|
+
// The code an absolute root has always carried, reworded.
|
|
365
|
+
result.issues.push(
|
|
366
|
+
error(
|
|
367
|
+
'root_outside_package',
|
|
368
|
+
`The ${key} root "${value}" is an absolute path, so Astryx reads nothing from it: a root is relative to the package, so it resolves the same wherever the package is installed.` +
|
|
369
|
+
(relative
|
|
370
|
+
? ` Fix: set \`${key}: '${relative === './' ? '.' : relative}'\` in ${path.basename(manifestFile)}.`
|
|
371
|
+
: ` It also points outside the package. Fix: move the files into the package and set \`${key}\` to their folder relative to package.json, such as './${key}'.`),
|
|
372
|
+
),
|
|
373
|
+
);
|
|
374
|
+
// Its files are reported through this issue, not one by one.
|
|
375
|
+
if (relative) absoluteRoots.push(path.resolve(value));
|
|
376
|
+
return undefined;
|
|
377
|
+
}
|
|
307
378
|
try {
|
|
308
379
|
return assertWithin(value, packageDir, {label: kind});
|
|
309
380
|
} catch {
|
|
@@ -320,18 +391,20 @@ async function validateAtPackageDir(
|
|
|
320
391
|
const loaded = {
|
|
321
392
|
name: identity.name,
|
|
322
393
|
version: identity.version,
|
|
323
|
-
components: resolveRoot(manifest.components),
|
|
324
|
-
templates: resolveRoot(manifest.templates),
|
|
325
|
-
codemods: resolveRoot(manifest.codemods),
|
|
326
|
-
docs: resolveRoot(manifest.docs),
|
|
327
|
-
themes: resolveRoot(manifest.themes),
|
|
394
|
+
components: resolveRoot(manifest.components, 'components'),
|
|
395
|
+
templates: resolveRoot(manifest.templates, 'templates'),
|
|
396
|
+
codemods: resolveRoot(manifest.codemods, 'codemods'),
|
|
397
|
+
docs: resolveRoot(manifest.docs, 'docs'),
|
|
398
|
+
themes: resolveRoot(manifest.themes, 'themes'),
|
|
328
399
|
issuesUrl: manifest.issuesUrl,
|
|
329
400
|
agentDocs: manifest.agentDocs,
|
|
330
401
|
cliCommand: manifest.cliCommand,
|
|
331
402
|
__cliCommandError: cliCommandError,
|
|
332
403
|
__agentDocsError: agentDocsError,
|
|
333
404
|
__unknownKeys: unknownKeys,
|
|
405
|
+
__declaredKeys: declaredKeys,
|
|
334
406
|
__spec: identity.name,
|
|
407
|
+
__declaredProviderId: manifest.providerId,
|
|
335
408
|
__packageDir: packageDir,
|
|
336
409
|
__manifestFile: manifestFile,
|
|
337
410
|
};
|
|
@@ -343,7 +416,9 @@ async function validateAtPackageDir(
|
|
|
343
416
|
// `integration check --strict` fails on any warning, and a new failure code
|
|
344
417
|
// joins only in a minor release (architecture:cli-surface INV35). Doctor's
|
|
345
418
|
// project check still warns about it.
|
|
346
|
-
|
|
419
|
+
const contribution = await validateLoadedIntegrationListed(loaded);
|
|
420
|
+
(result.listed ??= []).push(...contribution.listed);
|
|
421
|
+
for (const issue of contribution.issues) {
|
|
347
422
|
if (issue.code === INVALID_CLI_COMMAND) {
|
|
348
423
|
(result.notes ??= []).push({code: issue.code, message: issue.message});
|
|
349
424
|
} else {
|
|
@@ -370,7 +445,11 @@ async function validateAtPackageDir(
|
|
|
370
445
|
);
|
|
371
446
|
if (scanUnreachable) {
|
|
372
447
|
issues.push(
|
|
373
|
-
...(await findUnreachableContributionIssues(
|
|
448
|
+
...(await findUnreachableContributionIssues(
|
|
449
|
+
packageDir,
|
|
450
|
+
loaded,
|
|
451
|
+
absoluteRoots,
|
|
452
|
+
)),
|
|
374
453
|
);
|
|
375
454
|
}
|
|
376
455
|
|
|
@@ -397,7 +476,7 @@ export async function validateLocalIntegration(cwd = process.cwd()) {
|
|
|
397
476
|
return {found: false, issues: []};
|
|
398
477
|
}
|
|
399
478
|
|
|
400
|
-
/** @type {{name?: string, version?: string}} */
|
|
479
|
+
/** @type {{name?: string, version?: string, private?: boolean}} */
|
|
401
480
|
let pkg;
|
|
402
481
|
try {
|
|
403
482
|
pkg = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));
|
|
@@ -416,31 +495,121 @@ export async function validateLocalIntegration(cwd = process.cwd()) {
|
|
|
416
495
|
};
|
|
417
496
|
}
|
|
418
497
|
|
|
419
|
-
|
|
498
|
+
// npm publishes nothing without a name and a version, and the name is the
|
|
499
|
+
// provider ID every reader keys the package's contributions on.
|
|
500
|
+
// A package without one is still checked, under a placeholder name.
|
|
501
|
+
const fields = packageJsonFieldIssues(pkg, pkgJsonPath);
|
|
502
|
+
const name =
|
|
503
|
+
typeof pkg.name === 'string' && pkg.name !== ''
|
|
504
|
+
? pkg.name
|
|
505
|
+
: '(local package)';
|
|
506
|
+
const result = await validateAtPackageDir(
|
|
420
507
|
packageDir,
|
|
421
|
-
{
|
|
422
|
-
name: pkg.name ?? '(local package)',
|
|
423
|
-
version: pkg.version,
|
|
424
|
-
},
|
|
508
|
+
{name, version: pkg.version},
|
|
425
509
|
{scanUnreachable: true},
|
|
426
510
|
);
|
|
511
|
+
(result.listed ??= []).unshift(
|
|
512
|
+
...fields,
|
|
513
|
+
// Only a manifest that loaded says whether it declares a providerId; one
|
|
514
|
+
// that did not is reported as invalid_manifest.
|
|
515
|
+
...(result.integration
|
|
516
|
+
? providerIdIssues(
|
|
517
|
+
pkg.name,
|
|
518
|
+
result.integration.__declaredProviderId,
|
|
519
|
+
result.integration.__manifestFile,
|
|
520
|
+
)
|
|
521
|
+
: []),
|
|
522
|
+
);
|
|
523
|
+
return result;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Whether `name` is a bare npm package name, the form a provider ID takes.
|
|
528
|
+
* @param {string} name
|
|
529
|
+
*/
|
|
530
|
+
function isPackageName(name) {
|
|
531
|
+
try {
|
|
532
|
+
normalizeProviderId(name);
|
|
533
|
+
return true;
|
|
534
|
+
} catch {
|
|
535
|
+
return false;
|
|
536
|
+
}
|
|
427
537
|
}
|
|
428
538
|
|
|
429
539
|
/**
|
|
430
|
-
*
|
|
540
|
+
* A warning when no provider ID can be derived for the package: its
|
|
541
|
+
* package.json `name` is not a bare package name and its manifest declares
|
|
542
|
+
* no `providerId` that is one. Readers key the package's contributions on
|
|
543
|
+
* that ID; a missing name is its own warning.
|
|
544
|
+
* @param {unknown} name package.json `name`
|
|
545
|
+
* @param {unknown} declared the manifest's `providerId`
|
|
546
|
+
* @param {string} manifestFile
|
|
547
|
+
* @returns {Issue[]}
|
|
548
|
+
*/
|
|
549
|
+
function providerIdIssues(name, declared, manifestFile) {
|
|
550
|
+
if (typeof name !== 'string' || name === '') return [];
|
|
551
|
+
if (isPackageName(name)) return [];
|
|
552
|
+
if (typeof declared === 'string' && isPackageName(declared)) return [];
|
|
553
|
+
const manifest = path.basename(manifestFile);
|
|
554
|
+
return [
|
|
555
|
+
listed(
|
|
556
|
+
'no_provider_id',
|
|
557
|
+
`No provider ID can be derived for this package: package.json "name" ${JSON.stringify(name)} is not a bare package name (an optional @scope/ and a name, each starting with a lowercase letter or digit and holding only lowercase letters, digits, "-", ".", "_" and "~"), and ${manifest} declares no providerId that is one. Astryx files the package's components, templates and docs under that ID, so links to them and selecting the package cannot work. Fix: rename the package, such as "@your-scope/your-package", or set \`providerId: '@your-scope/your-package'\` in ${manifest}.`,
|
|
558
|
+
),
|
|
559
|
+
];
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* A warning for each field npm needs that package.json lacks.
|
|
564
|
+
* @param {{name?: unknown, version?: unknown, private?: unknown}} pkg
|
|
565
|
+
* @param {string} pkgJsonPath
|
|
566
|
+
* @returns {Issue[]}
|
|
567
|
+
*/
|
|
568
|
+
function packageJsonFieldIssues(pkg, pkgJsonPath) {
|
|
569
|
+
/** @type {Issue[]} */
|
|
570
|
+
const issues = [];
|
|
571
|
+
const shown = path.basename(pkgJsonPath);
|
|
572
|
+
// Listed, so validate and `--strict` keep the result they had for these
|
|
573
|
+
// packages; `integration verify`, which packs, reports them as errors.
|
|
574
|
+
if (typeof pkg.name !== 'string' || pkg.name === '') {
|
|
575
|
+
issues.push(
|
|
576
|
+
listed(
|
|
577
|
+
'invalid_package_json',
|
|
578
|
+
`${shown} has no "name", so npm cannot pack or publish the package, and a project that installs it has no name to select it by. Fix: add "name": "@your-scope/your-package" to ${shown}.`,
|
|
579
|
+
),
|
|
580
|
+
);
|
|
581
|
+
}
|
|
582
|
+
// A private package is never published, so it needs no version.
|
|
583
|
+
if (
|
|
584
|
+
pkg.private !== true &&
|
|
585
|
+
(typeof pkg.version !== 'string' || pkg.version === '')
|
|
586
|
+
) {
|
|
587
|
+
issues.push(
|
|
588
|
+
listed(
|
|
589
|
+
'invalid_package_json',
|
|
590
|
+
`${shown} has no "version", so npm cannot pack or publish the package. Fix: add "version": "0.1.0" to ${shown}.`,
|
|
591
|
+
),
|
|
592
|
+
);
|
|
593
|
+
}
|
|
594
|
+
return issues;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Validate an INSTALLED integration package: the nearest `node_modules/<spec>`
|
|
599
|
+
* at or above `cwd`, resolved the way the project loads it.
|
|
431
600
|
* @param {string} spec package name
|
|
432
601
|
* @param {string} [cwd]
|
|
433
602
|
* @returns {Promise<ValidateResult>}
|
|
434
603
|
*/
|
|
435
604
|
export async function validateInstalledIntegration(spec, cwd = process.cwd()) {
|
|
436
|
-
//
|
|
605
|
+
// The resolver throws (path-safety guard) on a spec with path segments,
|
|
437
606
|
// `..`, or an absolute path. Every other malformed input to this command
|
|
438
607
|
// degrades into a diagnostic — so catch it here and return an issue instead
|
|
439
608
|
// of letting the throw escape to a raw stack (human) / generic ERR_UNKNOWN
|
|
440
609
|
// (--json).
|
|
441
610
|
let packageDir;
|
|
442
611
|
try {
|
|
443
|
-
packageDir =
|
|
612
|
+
packageDir = resolveInstalledIntegrationDir(spec, cwd);
|
|
444
613
|
} catch (err) {
|
|
445
614
|
return {
|
|
446
615
|
found: true,
|
|
@@ -504,7 +673,30 @@ export async function validateIntegration(pkg, options = {}) {
|
|
|
504
673
|
const docTypes = integration
|
|
505
674
|
? await integrationDocTypes(integration, cwd)
|
|
506
675
|
: undefined;
|
|
507
|
-
|
|
676
|
+
// The component names docs and blocks give: those that cannot be judged
|
|
677
|
+
// are listed in their reports, and those judged wrong are notes.
|
|
678
|
+
const names = integration
|
|
679
|
+
? [
|
|
680
|
+
await componentDocNamesCheck(integration),
|
|
681
|
+
await blockComponentsCheck(integration),
|
|
682
|
+
]
|
|
683
|
+
: undefined;
|
|
684
|
+
const docNames = names?.[0].report;
|
|
685
|
+
const blockComponents = names?.[1].report;
|
|
686
|
+
// Notes fail nothing: a malformed `cliCommand`, and the findings added
|
|
687
|
+
// after the last minor release, each with its fix in the message, which
|
|
688
|
+
// may not fail `--strict` until the next (architecture:cli-surface INV35):
|
|
689
|
+
// the layout findings and the component names judged wrong.
|
|
690
|
+
const notes = result.found
|
|
691
|
+
? [
|
|
692
|
+
...(result.notes ?? []),
|
|
693
|
+
...(result.listed ?? []).map(issue => ({
|
|
694
|
+
code: issue.code,
|
|
695
|
+
message: issue.message,
|
|
696
|
+
})),
|
|
697
|
+
...(names ?? []).flatMap(each => each.notes),
|
|
698
|
+
]
|
|
699
|
+
: [];
|
|
508
700
|
return {
|
|
509
701
|
type: 'integration.validate',
|
|
510
702
|
data: {
|
|
@@ -526,6 +718,8 @@ export async function validateIntegration(pkg, options = {}) {
|
|
|
526
718
|
: result.issues,
|
|
527
719
|
...(notes.length > 0 ? {notes} : {}),
|
|
528
720
|
...(docTypes ? {docTypes} : {}),
|
|
721
|
+
...(docNames ? {docNames} : {}),
|
|
722
|
+
...(blockComponents ? {blockComponents} : {}),
|
|
529
723
|
},
|
|
530
724
|
};
|
|
531
725
|
}
|
|
@@ -14,6 +14,7 @@ export type LoadedIntegration = {
|
|
|
14
14
|
codemods?: string | undefined;
|
|
15
15
|
docs?: string | undefined;
|
|
16
16
|
issuesUrl?: string | undefined;
|
|
17
|
+
__declaredKeys?: string[] | undefined;
|
|
17
18
|
__unknownKeys?: string[] | undefined;
|
|
18
19
|
__spec: string;
|
|
19
20
|
__packageDir: string;
|
|
@@ -36,7 +37,17 @@ export type ValidateIntegrationResponse = {
|
|
|
36
37
|
type: "integration.validate";
|
|
37
38
|
/**
|
|
38
39
|
* `notes`: information beside the issues that fails nothing, such as
|
|
39
|
-
* `invalid_cli_command`, a `cliCommand` that was ignored as malformed
|
|
40
|
+
* `invalid_cli_command`, a `cliCommand` that was ignored as malformed, and
|
|
41
|
+
* the package layout findings added after the last minor release, each with
|
|
42
|
+
* its fix in the message: two docs for one component
|
|
43
|
+
* (`duplicate_component_doc`), a root reached through a symlink
|
|
44
|
+
* (`symlinked_root`), a contribution file that cannot be read
|
|
45
|
+
* (`unreadable_file`), a package.json without `name` or `version`
|
|
46
|
+
* (`invalid_package_json`), no provider ID (`no_provider_id`), and a doc
|
|
47
|
+
* whose file name differs from its source only in letter case where the two
|
|
48
|
+
* still pair (`source_without_component_doc`); and `doc_name_mismatch` and
|
|
49
|
+
* `unknown_block_component` (see `docNames` and `blockComponents`); they
|
|
50
|
+
* become warnings in the next minor release
|
|
40
51
|
*/
|
|
41
52
|
data: {
|
|
42
53
|
validated: boolean;
|
|
@@ -48,6 +59,8 @@ export type ValidateIntegrationResponse = {
|
|
|
48
59
|
message: string;
|
|
49
60
|
}>;
|
|
50
61
|
docTypes?: DocTypesReport;
|
|
62
|
+
docNames?: DocNamesReport;
|
|
63
|
+
blockComponents?: BlockComponentsReport;
|
|
51
64
|
};
|
|
52
65
|
};
|
|
53
66
|
/**
|
|
@@ -57,3 +70,47 @@ export type ValidateIntegrationResponse = {
|
|
|
57
70
|
export type DocTypesReport = import("../../foundation/discovery/doc-types.mjs").DocTypesReport & {
|
|
58
71
|
fixtures?: number;
|
|
59
72
|
};
|
|
73
|
+
/**
|
|
74
|
+
* Each component doc whose `name` differs from the name it is listed under,
|
|
75
|
+
* judged against its source. Present when the manifest loads. A name the
|
|
76
|
+
* module its printed import names provably does not export is a
|
|
77
|
+
* `doc_name_mismatch` note beside the issues, with its fix in the message
|
|
78
|
+
* (it becomes a warning in the next minor release), so `findings` is always
|
|
79
|
+
* empty; `unchecked` lists each doc whose source's
|
|
80
|
+
* exports could not all be read (a re-export from another package, `export
|
|
81
|
+
* =`, a destructured binding, a namespace): `file` (the doc, package-relative),
|
|
82
|
+
* `name` (what the doc says), and `reason`.
|
|
83
|
+
*/
|
|
84
|
+
export type DocNamesReport = {
|
|
85
|
+
status: "checked";
|
|
86
|
+
findings: [];
|
|
87
|
+
unchecked: Array<{
|
|
88
|
+
file: string;
|
|
89
|
+
name: string;
|
|
90
|
+
reason: string;
|
|
91
|
+
}>;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Each component name a template gives in `exampleFor` or `componentsUsed`
|
|
95
|
+
* that could not be judged. Present when the manifest loads. A name neither
|
|
96
|
+
* the package nor Core has, and the template does not import, is an
|
|
97
|
+
* `unknown_block_component` note beside the issues, with its fix in the
|
|
98
|
+
* message (it becomes a warning in the next minor release), so `findings` is
|
|
99
|
+
* always empty;
|
|
100
|
+
* `unchecked` lists each name that cannot be judged: one the template may
|
|
101
|
+
* reach through anything other than named imports and re-exports from
|
|
102
|
+
* Core, React, StyleX, local helpers or the package's own entry (an import from another
|
|
103
|
+
* package, a default or namespace import, an alias of or member read off an imported
|
|
104
|
+
* binding, require(), a dynamic import(), a local module this check cannot
|
|
105
|
+
* read), or, without Core installed, any name Core would have to resolve: `file` (the template doc, package-relative),
|
|
106
|
+
* `name`, and `reason`.
|
|
107
|
+
*/
|
|
108
|
+
export type BlockComponentsReport = {
|
|
109
|
+
status: "checked";
|
|
110
|
+
findings: [];
|
|
111
|
+
unchecked: Array<{
|
|
112
|
+
file: string;
|
|
113
|
+
name: string;
|
|
114
|
+
reason: string;
|
|
115
|
+
}>;
|
|
116
|
+
};
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
* @property {string} [codemods]
|
|
19
19
|
* @property {string} [docs]
|
|
20
20
|
* @property {string} [issuesUrl]
|
|
21
|
+
* @property {string[]} [__declaredKeys]
|
|
21
22
|
* @property {string[]} [__unknownKeys]
|
|
22
23
|
* @property {string} __spec
|
|
23
24
|
* @property {string} __packageDir
|
|
@@ -38,9 +39,19 @@
|
|
|
38
39
|
* "healthy" — the two are otherwise byte-identical.
|
|
39
40
|
* @typedef {object} ValidateIntegrationResponse
|
|
40
41
|
* @property {'integration.validate'} type
|
|
41
|
-
* @property {{validated: boolean, name: string | null, version: string | null, issues: import('../../foundation/integrations/issue').AstryxIntegrationIssue[], notes?: Array<{code: string, message: string}>, docTypes?: DocTypesReport}} data
|
|
42
|
+
* @property {{validated: boolean, name: string | null, version: string | null, issues: import('../../foundation/integrations/issue').AstryxIntegrationIssue[], notes?: Array<{code: string, message: string}>, docTypes?: DocTypesReport, docNames?: DocNamesReport, blockComponents?: BlockComponentsReport}} data
|
|
42
43
|
* `notes`: information beside the issues that fails nothing, such as
|
|
43
|
-
* `invalid_cli_command`, a `cliCommand` that was ignored as malformed
|
|
44
|
+
* `invalid_cli_command`, a `cliCommand` that was ignored as malformed, and
|
|
45
|
+
* the package layout findings added after the last minor release, each with
|
|
46
|
+
* its fix in the message: two docs for one component
|
|
47
|
+
* (`duplicate_component_doc`), a root reached through a symlink
|
|
48
|
+
* (`symlinked_root`), a contribution file that cannot be read
|
|
49
|
+
* (`unreadable_file`), a package.json without `name` or `version`
|
|
50
|
+
* (`invalid_package_json`), no provider ID (`no_provider_id`), and a doc
|
|
51
|
+
* whose file name differs from its source only in letter case where the two
|
|
52
|
+
* still pair (`source_without_component_doc`); and `doc_name_mismatch` and
|
|
53
|
+
* `unknown_block_component` (see `docNames` and `blockComponents`); they
|
|
54
|
+
* become warnings in the next minor release
|
|
44
55
|
*/
|
|
45
56
|
|
|
46
57
|
/**
|
|
@@ -54,4 +65,36 @@
|
|
|
54
65
|
* components or docs root, which those readers skip) were not checked
|
|
55
66
|
*/
|
|
56
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Each component doc whose `name` differs from the name it is listed under,
|
|
70
|
+
* judged against its source. Present when the manifest loads. A name the
|
|
71
|
+
* module its printed import names provably does not export is a
|
|
72
|
+
* `doc_name_mismatch` note beside the issues, with its fix in the message
|
|
73
|
+
* (it becomes a warning in the next minor release), so `findings` is always
|
|
74
|
+
* empty; `unchecked` lists each doc whose source's
|
|
75
|
+
* exports could not all be read (a re-export from another package, `export
|
|
76
|
+
* =`, a destructured binding, a namespace): `file` (the doc, package-relative),
|
|
77
|
+
* `name` (what the doc says), and `reason`.
|
|
78
|
+
*
|
|
79
|
+
* @typedef {{status: 'checked', findings: [], unchecked: Array<{file: string, name: string, reason: string}>}} DocNamesReport
|
|
80
|
+
*/
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Each component name a template gives in `exampleFor` or `componentsUsed`
|
|
84
|
+
* that could not be judged. Present when the manifest loads. A name neither
|
|
85
|
+
* the package nor Core has, and the template does not import, is an
|
|
86
|
+
* `unknown_block_component` note beside the issues, with its fix in the
|
|
87
|
+
* message (it becomes a warning in the next minor release), so `findings` is
|
|
88
|
+
* always empty;
|
|
89
|
+
* `unchecked` lists each name that cannot be judged: one the template may
|
|
90
|
+
* reach through anything other than named imports and re-exports from
|
|
91
|
+
* Core, React, StyleX, local helpers or the package's own entry (an import from another
|
|
92
|
+
* package, a default or namespace import, an alias of or member read off an imported
|
|
93
|
+
* binding, require(), a dynamic import(), a local module this check cannot
|
|
94
|
+
* read), or, without Core installed, any name Core would have to resolve: `file` (the template doc, package-relative),
|
|
95
|
+
* `name`, and `reason`.
|
|
96
|
+
*
|
|
97
|
+
* @typedef {{status: 'checked', findings: [], unchecked: Array<{file: string, name: string, reason: string}>}} BlockComponentsReport
|
|
98
|
+
*/
|
|
99
|
+
|
|
57
100
|
export {};
|
|
@@ -62,7 +62,7 @@ export const doc = {
|
|
|
62
62
|
{
|
|
63
63
|
type: 'integration.validate',
|
|
64
64
|
description:
|
|
65
|
-
'The result envelope: `data.validated`, false when nothing was validated — with no `package.json` at all, `data.issues` carries one `no_package` error-severity issue so the command exits 1; with a `package.json` but no integration manifest, `data.issues` is empty, which means "not looked at", not "healthy"; `data.name` and `data.version` of the validated package (both null when `data.validated` is false; `data.name` is `(local package)` when the local package.json has no readable name), plus `data.issues`, an AstryxIntegrationIssue[] of {code, severity: `warning` | `error`, message, fix?, file?, line?}: `fix` is the change that resolves the issue, and `file` and `line` where it is, relative to the package, when the check knows them; and `data.notes` when there are any: {code, message} information that fails nothing, such as `invalid_cli_command` for a `cliCommand` that was ignored as malformed. When the manifest loads, `data.docTypes` holds the doc type check: `status` (`checked` | `skipped`), `reason` when skipped, `typescript`, `docs` (doc values checked), `findings` ({doc, line, export, type, field, problem: `unknown_field` | `missing_field` | `wrong_type`, severity: `warning`, message, fix}), `unchecked` ({doc, reason}: a doc that could not be fully checked, or a root its reader cannot read), and `fixtures` when the components or docs root holds test fixture docs under `__tests__`: how many, none of them checked.',
|
|
65
|
+
'The result envelope: `data.validated`, false when nothing was validated — with no `package.json` at all, `data.issues` carries one `no_package` error-severity issue so the command exits 1; with a `package.json` but no integration manifest, `data.issues` is empty, which means "not looked at", not "healthy"; `data.name` and `data.version` of the validated package (both null when `data.validated` is false; `data.name` is `(local package)` when the local package.json has no readable name), plus `data.issues`, an AstryxIntegrationIssue[] of {code, severity: `warning` | `error`, message, fix?, file?, line?}: `fix` is the change that resolves the issue, and `file` and `line` where it is, relative to the package, when the check knows them; and `data.notes` when there are any: {code, message} information that fails nothing, such as `invalid_cli_command` for a `cliCommand` that was ignored as malformed, and the package layout findings added after the last minor release, each with its fix in the message: `duplicate_component_doc`, `symlinked_root`, `unreadable_file`, `invalid_package_json` (no `name` or `version`), `no_provider_id`, and `source_without_component_doc` for a doc whose file name differs from its source only in letter case where the two still pair; and `doc_name_mismatch` and `unknown_block_component` (see `docNames` and `blockComponents`); they become warnings in the next minor release. When the manifest loads, `data.docTypes` holds the doc type check: `status` (`checked` | `skipped`), `reason` when skipped, `typescript`, `docs` (doc values checked), `findings` ({doc, line, export, type, field, problem: `unknown_field` | `missing_field` | `wrong_type`, severity: `warning`, message, fix}), `unchecked` ({doc, reason}: a doc that could not be fully checked, or a root its reader cannot read), and `fixtures` when the components or docs root holds test fixture docs under `__tests__`: how many, none of them checked. When the manifest loads, `data.docNames` lists the component doc names it could not judge: `status` (`checked`), `findings` (always empty; a doc name the module its import names provably does not export is a `doc_name_mismatch` note, with its fix in the message) and `unchecked` ({file, name, reason}: a doc whose `name` differs from the name it is listed under and whose source exports cannot all be read, such as a re-export from another package). When the manifest loads, `data.blockComponents` lists the component names templates give in `exampleFor` or `componentsUsed` that could not be judged: `status` (`checked`), `findings` (always empty; a name neither the package nor Core has, and the template does not import, is an `unknown_block_component` note, with its fix in the message) and `unchecked` ({file, name, reason}: a name the template may reach through anything other than named imports and re-exports from Core, React, StyleX, local helpers or the entry of the package itself, such as an import from another package, a default or namespace import, an alias of or member read off an imported binding, require() or a dynamic import(), or a local module that cannot be read, or any name Core would have to resolve when Core is not installed).',
|
|
66
66
|
},
|
|
67
67
|
],
|
|
68
68
|
examples: [
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Search ordering data
|
|
2
|
+
|
|
3
|
+
`astryx search` decides **what matches**, and each match's score and reason, with the lexical ranker (`_lexical.mjs` and the files it imports). The files in this folder only decide **the order of matches inside one score band**: they never add or drop a match, change a score, or move a match past one of another band or kind (doc or not). The bands are scores 195 and up, 188–194, 170–187, 160–169, 151–159, 100–150, 55–99, and under 55. A band can be wide (55–99 holds keyword matches beside one-word description hits), so inside a band a lower score can lead a higher one.
|
|
4
|
+
|
|
5
|
+
## Rule
|
|
6
|
+
|
|
7
|
+
`spec:AST-083` owns search's result order; this file describes the data that implements it. Inside one score band, a searcher's static weights decide the order. Matches with the same score move together as one block, in the lexical ranker's order, placed by the learned position of the block's first match: moving each one on its own breaks search's existing rules for tied matches. Matches never cross bands or kinds. There is no model runtime: every number is a plain static value read at load time.
|
|
8
|
+
|
|
9
|
+
## Files
|
|
10
|
+
|
|
11
|
+
| File | What it holds |
|
|
12
|
+
| ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
13
|
+
| `tree.weights.json` | The root classifier: for each query word or word pair, one number per class (component, hook, doc, template, theme, none). The query's tokens vote, and a topic searcher takes the query only when its class wins by the floor in `tree.mjs`. |
|
|
14
|
+
| `_main-searcher.weights.json`, `_doc-searcher.weights.json`, `_template-searcher.weights.json` | One weight, mean and spread per signal for each searcher (see `SIGNALS` and `PER_DOMAIN` in `_features.mjs`). A match's place inside its band is the weighted sum of its standardized signals. |
|
|
15
|
+
| `_cards.json` | One card per Core component and hook: what it is, the words people use for it, and its main props, written from the item's own source. Cards feed one signal, word overlap with the card. |
|
|
16
|
+
|
|
17
|
+
The signals are counts over the query and each entry's own text: the lexical score and rank, BM25 over the entry's name, docs text and card, how many query words the entry covers, whether the query holds the entry's name, and the classifier's guess of the domain.
|
|
18
|
+
|
|
19
|
+
## Status
|
|
20
|
+
|
|
21
|
+
These files are static and reviewed. They are not computed when the CLI runs, and nothing at run time writes them.
|
|
22
|
+
|
|
23
|
+
## Refreshing
|
|
24
|
+
|
|
25
|
+
- A Core component or hook without a card is still matched and scored as usual; inside its band it is ordered by its other signals.
|
|
26
|
+
- When Core adds or renames items, a reviewed change adds or updates their cards and, if needed, refreshed weights.
|
|
27
|
+
- Any refresh must keep every test that pins search's behavior passing, including the ordering invariants in `_order.test.mjs`. It must also keep the `astryx search` output's matches, scores and reasons identical to the lexical ranker's.
|
|
28
|
+
- Any refresh must also be measured as `spec:AST-083` FR6 requires: against the held-out, fresh and synthetic evaluation sets, with no row worse than the previous order (a row is worse when the paired 95% confidence interval of its change lies entirely below zero).
|