@astryxdesign/cli 0.6.4-canary.ed2e54e → 0.6.4
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 +66 -64
- package/api/component/_adapter.d.mts +0 -25
- package/api/component/_adapter.mjs +5 -59
- package/api/component/component.d.mts +3 -6
- package/api/component/component.doc.mjs +10 -23
- package/api/component/component.mjs +9 -249
- package/api/component/component.type.d.mts +0 -25
- package/api/component/component.type.mjs +0 -44
- package/api/discover/_adapter.d.mts +6 -114
- package/api/discover/_adapter.mjs +17 -372
- package/api/discover/detail/detail.d.mts +6 -18
- package/api/discover/detail/detail.mjs +13 -67
- package/api/discover/detail/detail.test.mjs +0 -85
- package/api/discover/discover.d.mts +9 -3
- package/api/discover/discover.doc.mjs +18 -61
- package/api/discover/discover.mjs +36 -220
- package/api/discover/discover.test.mjs +2 -11
- package/api/discover/discover.type.d.mts +8 -147
- package/api/discover/discover.type.mjs +12 -102
- package/api/discover/list/list.d.mts +6 -20
- package/api/discover/list/list.mjs +12 -45
- package/api/discover/list/list.test.mjs +0 -46
- package/api/discover/search/search.d.mts +16 -18
- package/api/discover/search/search.mjs +56 -102
- package/api/discover/search/search.test.mjs +10 -144
- package/api/docs/docs.test.mjs +0 -2
- package/api/doctor/doctor.d.mts +3 -8
- package/api/doctor/doctor.mjs +9 -90
- package/api/doctor/doctor.test.mjs +10 -122
- package/api/index.d.mts +2 -1
- package/api/index.mjs +4 -4
- package/api/integration/add-helpers.d.mts +2 -5
- package/api/integration/add-helpers.mjs +9 -36
- package/api/integration/pack-check.mjs +3 -28
- package/api/json/index.ts +1 -0
- package/api/layout/_adapter.d.mts +34 -0
- package/api/layout/_adapter.mjs +148 -0
- package/api/layout/check/check.d.mts +16 -0
- package/api/layout/check/check.mjs +40 -0
- package/api/layout/expand/expand.d.mts +22 -0
- package/api/layout/expand/expand.mjs +155 -0
- package/api/layout/expand/expand.path-safety.test.mjs +53 -0
- package/api/layout/grammar/grammar.d.mts +13 -0
- package/api/layout/grammar/grammar.mjs +87 -0
- package/api/layout/layout.d.mts +6 -0
- package/api/layout/layout.mjs +17 -0
- package/api/layout/layout.test.mjs +297 -0
- package/api/layout/layout.type.d.mts +89 -0
- package/api/layout/layout.type.mjs +103 -0
- package/api/layout/layoutCheck.doc.d.mts +11 -0
- package/api/layout/layoutCheck.doc.mjs +85 -0
- package/api/layout/layoutExpand.doc.d.mts +11 -0
- package/api/layout/layoutExpand.doc.mjs +107 -0
- package/api/layout/layoutGrammar.doc.d.mts +11 -0
- package/api/layout/layoutGrammar.doc.mjs +57 -0
- package/api/search/search.test.mjs +0 -18
- package/api/template/template-integration.test.mjs +65 -1
- package/api/template/template.mjs +1 -1
- package/api/theme/add/add.mjs +25 -17
- package/api/theme/add/add.staging.test.mjs +23 -40
- package/api/theme/build/build.family.test.mjs +12 -7
- package/api/theme/build/build.mjs +18 -8
- package/api/upgrade/run/run.mjs +4 -6
- package/api/upgrade/upgrade.type.mjs +2 -2
- package/assets/codemods/__tests__/runner.test.mjs +1 -3
- package/assets/codemods/integration-runner.mjs +3 -3
- package/assets/codemods/runner.mjs +4 -5
- package/assets/docs/internationalization.doc.mjs +5 -7
- package/assets/docs/tree/integrations.doc.mjs +1 -20
- package/assets/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.tsx +1 -1
- package/authoring/config/config.doc.mjs +1 -9
- package/authoring/config/parse.d.mts +0 -2
- package/authoring/config/parse.mjs +0 -19
- package/authoring/config/parse.test.mjs +0 -8
- package/authoring/config/type.ts +2 -13
- package/authoring/doctypes/command/command.doc.mjs +1 -1
- package/authoring/doctypes/command/type.ts +1 -1
- package/authoring/index.d.mts +0 -1
- package/authoring/index.d.ts +0 -10
- package/authoring/index.mjs +0 -1
- package/clients/cli/command-result-coverage.test.mjs +7 -7
- package/clients/cli/commands/component/index.mjs +55 -152
- package/clients/cli/commands/component-ownership.test.mjs +0 -89
- package/clients/cli/commands/component.doc.mjs +6 -23
- package/clients/cli/commands/debug-result-summary.test.mjs +2 -2
- package/clients/cli/commands/discover.doc.mjs +9 -53
- package/clients/cli/commands/discover.mjs +118 -393
- package/clients/cli/commands/docs.test.mjs +0 -29
- package/clients/cli/commands/layout-check.doc.mjs +65 -0
- package/clients/cli/commands/layout-expand.doc.mjs +83 -0
- package/clients/cli/commands/layout-grammar.doc.mjs +30 -0
- package/clients/cli/commands/layout.doc.mjs +34 -0
- package/clients/cli/commands/layout.error-codes.test.mjs +66 -0
- package/clients/cli/commands/layout.exit-parity.test.mjs +41 -0
- package/clients/cli/commands/layout.mjs +275 -0
- package/clients/cli/commands/layout.path-help.test.mjs +33 -0
- package/clients/cli/commands/layout.stdin-cap.test.mjs +47 -0
- package/clients/cli/commands/layout.text-fields.test.mjs +39 -0
- package/clients/cli/commands/text-json-parity.test.mjs +17 -0
- package/clients/cli/index.mjs +4 -0
- package/clients/cli/lib/exit-codes.test.mjs +8 -1
- package/clients/cli/lib/json-shim.mjs +14 -24
- package/clients/cli/lib/json-shim.test.mjs +20 -6
- package/clients/cli/lib/manifest.mjs +8 -3
- package/clients/cli/lib/manifest.test.mjs +2 -5
- package/foundation/discovery/authoring-self-docs.mjs +0 -1
- package/foundation/discovery/template-adapter.mjs +1 -1
- package/foundation/doc-compiler/doc-loads.test.mjs +12 -0
- package/foundation/doc-compiler/tree.test.mjs +1 -9
- package/foundation/integrations/integrations.d.mts +1 -14
- package/foundation/integrations/integrations.mjs +1 -41
- package/foundation/integrations/integrations.test.mjs +0 -31
- package/foundation/response/response-types.doc.mjs +21 -15
- package/foundation/response/response-types.doc.test.mjs +0 -23
- package/foundation/xle/browser.d.mts +3 -3
- package/foundation/xle/browser.mjs +3 -3
- package/foundation/xle/expand.mjs +2 -2
- package/foundation/xle/parse.mjs +1 -1
- package/foundation/xle/print.mjs +2 -2
- package/foundation/xle/splice.mjs +1 -1
- package/package.json +9 -9
- package/api/discover/_adapter.test.mjs +0 -215
- package/api/discover/_catalog-view.d.mts +0 -115
- package/api/discover/_catalog-view.mjs +0 -203
- package/api/discover/_catalog-view.test.mjs +0 -128
- package/api/discover/detail/item/item.d.mts +0 -26
- package/api/discover/detail/item/item.mjs +0 -78
- package/api/discover/detail/item/item.test.mjs +0 -73
- package/api/integration/pack-check.lifecycle-output.test.mjs +0 -105
- package/api/theme/add/add.rollback.test.mjs +0 -158
- package/api/theme/build/build.rollback.test.mjs +0 -148
- package/api/upgrade/run/files-changed.test.mjs +0 -111
- package/assets/codemods/file-count.test.mjs +0 -163
- package/assets/docs/tree/component-lookups.doc.mjs +0 -149
- package/authoring/discover/discover.doc.d.mts +0 -13
- package/authoring/discover/discover.doc.mjs +0 -138
- package/authoring/discover/parse.d.mts +0 -24
- package/authoring/discover/parse.mjs +0 -128
- package/authoring/discover/parse.test.mjs +0 -124
- package/authoring/discover/type.ts +0 -87
- package/clients/cli/commands/component-batch.test.mjs +0 -341
- package/clients/cli/commands/discover.sources.test.mjs +0 -267
- package/clients/cli/lib/parse-error-format.test.mjs +0 -81
- package/foundation/response/batch.type.d.mts +0 -33
- package/foundation/response/batch.type.mjs +0 -34
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Validate an expression and return both canonical surfaces
|
|
6
|
-
* invalid input; returns the errors.
|
|
5
|
+
* Validate an expression and return both canonical surfaces — the browser
|
|
6
|
+
* twin of `layout check`. Never throws on invalid input; returns the errors.
|
|
7
7
|
*
|
|
8
8
|
* @param {string} expression
|
|
9
9
|
* @param {import('./xle-ast').Registry | import('./browser').SerializedRegistry | object} registry - serialized or hydrated
|
|
@@ -19,7 +19,7 @@ export function checkExpression(expression: string, registry: import("./xle-ast"
|
|
|
19
19
|
loose?: boolean | undefined;
|
|
20
20
|
}): import("./browser").CheckResult;
|
|
21
21
|
/**
|
|
22
|
-
* Validate + expand to TSX
|
|
22
|
+
* Validate + expand to TSX — the browser twin of `layout expand`.
|
|
23
23
|
* Returns either {code, ...} or {errors} (never writes files).
|
|
24
24
|
*
|
|
25
25
|
* @param {string} expression
|
|
@@ -44,8 +44,8 @@ function formatIssue(issue) {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
|
-
* Validate an expression and return both canonical surfaces
|
|
48
|
-
* invalid input; returns the errors.
|
|
47
|
+
* Validate an expression and return both canonical surfaces — the browser
|
|
48
|
+
* twin of `layout check`. Never throws on invalid input; returns the errors.
|
|
49
49
|
*
|
|
50
50
|
* @param {string} expression
|
|
51
51
|
* @param {import('./xle-ast').Registry | import('./browser').SerializedRegistry | object} registry - serialized or hydrated
|
|
@@ -85,7 +85,7 @@ export function checkExpression(expression, registry, opts = {}) {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
/**
|
|
88
|
-
* Validate + expand to TSX
|
|
88
|
+
* Validate + expand to TSX — the browser twin of `layout expand`.
|
|
89
89
|
* Returns either {code, ...} or {errors} (never writes files).
|
|
90
90
|
*
|
|
91
91
|
* @param {string} expression
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*
|
|
14
14
|
* @input validated doc (nodes carry .bound) + registry
|
|
15
15
|
* @output expand() → {code, componentsUsed, states, todos}
|
|
16
|
-
* @position lib/xle — last stage;
|
|
16
|
+
* @position lib/xle — last stage; api/layout.mjs orchestrates
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import {PAYLOAD_PROPS} from './validate.mjs';
|
|
@@ -751,7 +751,7 @@ export function expand(doc, registry, options = {}) {
|
|
|
751
751
|
: [];
|
|
752
752
|
|
|
753
753
|
const code = [
|
|
754
|
-
`// Generated by
|
|
754
|
+
`// Generated by \`astryx layout expand\` — this file is the artifact; edit freely.`,
|
|
755
755
|
...importLines,
|
|
756
756
|
...blockSection,
|
|
757
757
|
'',
|
package/foundation/xle/parse.mjs
CHANGED
|
@@ -302,7 +302,7 @@ function parseAttrToken(token, line) {
|
|
|
302
302
|
* Guard against unbounded recursion in the compact parser. A layout tree this
|
|
303
303
|
* deep is never hand-authored; without the cap the recursive descent blows the
|
|
304
304
|
* JS call stack and surfaces a raw RangeError (→ ERR_UNKNOWN, no line/col)
|
|
305
|
-
* instead of a located
|
|
305
|
+
* instead of a located ERR_LAYOUT_PARSE. The counter is module-level so it
|
|
306
306
|
* spans group / attribute sub-streams; each parse entry point resets it.
|
|
307
307
|
*/
|
|
308
308
|
const MAX_COMPACT_DEPTH = 512;
|
package/foundation/xle/print.mjs
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* @file XLE canonical printers — AST → compact (XLE) or outline (XLO) text.
|
|
5
5
|
*
|
|
6
|
-
* Used by `
|
|
7
|
-
* other surface, and by round-trip conformance tests.
|
|
6
|
+
* Used by `astryx layout check --form …` so either party (agent or human)
|
|
7
|
+
* can request the other surface, and by round-trip conformance tests.
|
|
8
8
|
* Printing is best-effort canonical: spelling variants the lexer
|
|
9
9
|
* normalized are emitted in canonical form.
|
|
10
10
|
*
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
*
|
|
19
19
|
* @input block TSX source (splice) or a name+importPath (import)
|
|
20
20
|
* @output prepareSpliceModule / parseImportStatements + import-merge helpers
|
|
21
|
-
* @position lib/xle — used by expand.mjs; sources gathered
|
|
21
|
+
* @position lib/xle — used by expand.mjs; sources gathered in api/layout.mjs
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
const IMPORT_RE =
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/cli",
|
|
3
|
-
"version": "0.6.4
|
|
3
|
+
"version": "0.6.4",
|
|
4
4
|
"displayName": "CLI",
|
|
5
5
|
"description": "Scaffold projects, browse templates, generate themes, and get agent-ready docs from the command line.",
|
|
6
6
|
"author": "Meta Open Source",
|
|
@@ -101,10 +101,10 @@
|
|
|
101
101
|
"zod": "^4.4.3"
|
|
102
102
|
},
|
|
103
103
|
"peerDependencies": {
|
|
104
|
-
"@astryxdesign/charts": "
|
|
105
|
-
"@astryxdesign/core": "
|
|
106
|
-
"@astryxdesign/lab": "
|
|
107
|
-
"@astryxdesign/theme-neutral": "
|
|
104
|
+
"@astryxdesign/charts": "*",
|
|
105
|
+
"@astryxdesign/core": "*",
|
|
106
|
+
"@astryxdesign/lab": "*",
|
|
107
|
+
"@astryxdesign/theme-neutral": "*"
|
|
108
108
|
},
|
|
109
109
|
"peerDependenciesMeta": {
|
|
110
110
|
"@astryxdesign/charts": {
|
|
@@ -121,10 +121,10 @@
|
|
|
121
121
|
}
|
|
122
122
|
},
|
|
123
123
|
"devDependencies": {
|
|
124
|
-
"@astryxdesign/charts": "
|
|
125
|
-
"@astryxdesign/core": "
|
|
126
|
-
"@astryxdesign/lab": "
|
|
127
|
-
"@astryxdesign/theme-neutral": "
|
|
124
|
+
"@astryxdesign/charts": "*",
|
|
125
|
+
"@astryxdesign/core": "*",
|
|
126
|
+
"@astryxdesign/lab": "*",
|
|
127
|
+
"@astryxdesign/theme-neutral": "*",
|
|
128
128
|
"@heroicons/react": "^2.2.0",
|
|
129
129
|
"@stylexjs/stylex": "^0.19.0",
|
|
130
130
|
"@types/babel__core": "^7.20.5",
|
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @file Tests for how the discover adapter calls discover sources: order,
|
|
5
|
-
* failure isolation, timeouts, and the saved copy. Sources are plain functions
|
|
6
|
-
* on a stand-in project, and the saved copies go to a temp directory.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
|
|
10
|
-
import * as fs from 'node:fs';
|
|
11
|
-
import * as os from 'node:os';
|
|
12
|
-
import * as path from 'node:path';
|
|
13
|
-
import {callSources} from './_adapter.mjs';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @param {string} name
|
|
17
|
-
* @param {object[]} packages
|
|
18
|
-
*/
|
|
19
|
-
function catalog(name, packages) {
|
|
20
|
-
return {
|
|
21
|
-
schemaVersion: 1,
|
|
22
|
-
source: {name, generatedAt: '2026-09-30T00:00:00.000Z', complete: true},
|
|
23
|
-
packages,
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* @param {string} name
|
|
29
|
-
* @param {object} [extra]
|
|
30
|
-
*/
|
|
31
|
-
function pkg(name, extra = {}) {
|
|
32
|
-
return {
|
|
33
|
-
package: name,
|
|
34
|
-
integration: name,
|
|
35
|
-
aliases: [],
|
|
36
|
-
latest: '1.0.0',
|
|
37
|
-
versions: [
|
|
38
|
-
{version: '1.0.0', publishedAt: null, prerelease: false, status: 'ok'},
|
|
39
|
-
],
|
|
40
|
-
contributions: [],
|
|
41
|
-
...extra,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* @param {{config?: object, integrations?: object[]}} parts
|
|
47
|
-
* @returns {any}
|
|
48
|
-
*/
|
|
49
|
-
function project({config = {}, integrations = []}) {
|
|
50
|
-
return {config, loadedIntegrations: integrations, cwd: '/virtual/project'};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
let cacheDir;
|
|
54
|
-
|
|
55
|
-
beforeEach(() => {
|
|
56
|
-
cacheDir = fs.mkdtempSync(path.join(os.tmpdir(), 'discover-cache-'));
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
afterEach(() => {
|
|
60
|
-
fs.rmSync(cacheDir, {recursive: true, force: true});
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
describe('callSources', () => {
|
|
64
|
-
it('asks the project source first, then each integration in load order, once per function', async () => {
|
|
65
|
-
/** @type {string[]} */
|
|
66
|
-
const calls = [];
|
|
67
|
-
const shared = vi.fn(async () => {
|
|
68
|
-
calls.push('shared');
|
|
69
|
-
return catalog('Shared', [pkg('@a/one')]);
|
|
70
|
-
});
|
|
71
|
-
const own = vi.fn(async () => {
|
|
72
|
-
calls.push('own');
|
|
73
|
-
return catalog('Own', [pkg('@a/one', {latest: '9.9.9'}), pkg('@a/two')]);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
const result = await callSources(
|
|
77
|
-
project({
|
|
78
|
-
config: {discover: own},
|
|
79
|
-
integrations: [
|
|
80
|
-
{name: '@x/first', __discover: shared},
|
|
81
|
-
{name: '@x/second', __discover: shared},
|
|
82
|
-
],
|
|
83
|
-
}),
|
|
84
|
-
{},
|
|
85
|
-
{cacheDir},
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
expect(calls).toEqual(['own', 'shared']);
|
|
89
|
-
expect(result.sources.map(s => [s.name, s.from, s.status])).toEqual([
|
|
90
|
-
['Own', 'astryx.config', 'fresh'],
|
|
91
|
-
['Shared', '@x/first', 'fresh'],
|
|
92
|
-
]);
|
|
93
|
-
expect(result.packages.map(p => [p.package, p.latest, p.source])).toEqual([
|
|
94
|
-
['@a/one', '9.9.9', 'Own'],
|
|
95
|
-
['@a/two', '1.0.0', 'Own'],
|
|
96
|
-
]);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('passes the package and version it wants, with an abort signal', async () => {
|
|
100
|
-
const source = vi.fn(async () => catalog('S', []));
|
|
101
|
-
|
|
102
|
-
await callSources(
|
|
103
|
-
project({integrations: [{name: '@x/src', __discover: source}]}),
|
|
104
|
-
{package: '@a/one', version: '1.0.0'},
|
|
105
|
-
{cacheDir},
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
expect(source).toHaveBeenCalledWith({
|
|
109
|
-
signal: expect.any(globalThis.AbortSignal),
|
|
110
|
-
package: '@a/one',
|
|
111
|
-
version: '1.0.0',
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it('keeps every other source when one throws, returns bad data, or is not a function', async () => {
|
|
116
|
-
const result = await callSources(
|
|
117
|
-
project({
|
|
118
|
-
integrations: [
|
|
119
|
-
{
|
|
120
|
-
name: '@x/throws',
|
|
121
|
-
__discover: async () => {
|
|
122
|
-
throw new Error('offline');
|
|
123
|
-
},
|
|
124
|
-
},
|
|
125
|
-
{name: '@x/bad', __discover: async () => ({schemaVersion: 2})},
|
|
126
|
-
{
|
|
127
|
-
name: '@x/broken',
|
|
128
|
-
__discoverError: 'named export "discover" must be a function',
|
|
129
|
-
},
|
|
130
|
-
{
|
|
131
|
-
name: '@x/good',
|
|
132
|
-
__discover: async () => catalog('Good', [pkg('@a/one')]),
|
|
133
|
-
},
|
|
134
|
-
],
|
|
135
|
-
}),
|
|
136
|
-
{},
|
|
137
|
-
{cacheDir},
|
|
138
|
-
);
|
|
139
|
-
|
|
140
|
-
expect(result.sources.map(s => [s.from, s.status])).toEqual([
|
|
141
|
-
['@x/throws', 'failed'],
|
|
142
|
-
['@x/bad', 'failed'],
|
|
143
|
-
['@x/broken', 'failed'],
|
|
144
|
-
['@x/good', 'fresh'],
|
|
145
|
-
]);
|
|
146
|
-
expect(result.sources[0].error).toContain('offline');
|
|
147
|
-
expect(result.sources[1].error).toContain('schemaVersion 2');
|
|
148
|
-
expect(result.sources[2].error).toContain('must be a function');
|
|
149
|
-
expect(result.packages.map(p => p.package)).toEqual(['@a/one']);
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it('gives up on a source after the timeout and aborts its signal', async () => {
|
|
153
|
-
/** @type {AbortSignal | undefined} */
|
|
154
|
-
let seen;
|
|
155
|
-
const slow = ({signal}) => {
|
|
156
|
-
seen = signal;
|
|
157
|
-
return new Promise(() => {});
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
const result = await callSources(
|
|
161
|
-
project({integrations: [{name: '@x/slow', __discover: slow}]}),
|
|
162
|
-
{},
|
|
163
|
-
{cacheDir, timeoutMs: 20},
|
|
164
|
-
);
|
|
165
|
-
|
|
166
|
-
expect(result.sources[0]).toMatchObject({
|
|
167
|
-
status: 'failed',
|
|
168
|
-
error: expect.stringContaining('did not answer'),
|
|
169
|
-
});
|
|
170
|
-
expect(seen?.aborted).toBe(true);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
it('uses the copy saved for the same request when a source fails, and says when it was saved', async () => {
|
|
174
|
-
let online = true;
|
|
175
|
-
const source = async () => {
|
|
176
|
-
if (!online) throw new Error('offline');
|
|
177
|
-
return catalog('S', [pkg('@a/one')]);
|
|
178
|
-
};
|
|
179
|
-
const p = project({integrations: [{name: '@x/src', __discover: source}]});
|
|
180
|
-
|
|
181
|
-
await callSources(p, {}, {cacheDir});
|
|
182
|
-
online = false;
|
|
183
|
-
const offline = await callSources(p, {}, {cacheDir});
|
|
184
|
-
|
|
185
|
-
expect(offline.sources[0]).toMatchObject({
|
|
186
|
-
name: 'S',
|
|
187
|
-
status: 'saved',
|
|
188
|
-
error: 'offline',
|
|
189
|
-
savedAt: expect.any(String),
|
|
190
|
-
});
|
|
191
|
-
expect(offline.packages.map(x => x.package)).toEqual(['@a/one']);
|
|
192
|
-
|
|
193
|
-
const otherRequest = await callSources(p, {package: '@a/one'}, {cacheDir});
|
|
194
|
-
expect(otherRequest.sources[0].status).toBe('failed');
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
it('keeps the saved copy in the cache directory, not in the project', async () => {
|
|
198
|
-
await callSources(
|
|
199
|
-
project({
|
|
200
|
-
integrations: [
|
|
201
|
-
{name: '@x/src', __discover: async () => catalog('S', [])},
|
|
202
|
-
],
|
|
203
|
-
}),
|
|
204
|
-
{},
|
|
205
|
-
{cacheDir},
|
|
206
|
-
);
|
|
207
|
-
|
|
208
|
-
expect(fs.readdirSync(path.join(cacheDir, 'discover'))).toHaveLength(1);
|
|
209
|
-
expect(fs.existsSync('/virtual/project')).toBe(false);
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
it('returns nothing when there is no project', async () => {
|
|
213
|
-
expect(await callSources(undefined)).toEqual({sources: [], packages: []});
|
|
214
|
-
});
|
|
215
|
-
});
|
|
@@ -1,115 +0,0 @@
|
|
|
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
|
-
* @typedef {import('./_adapter.mjs').InstalledPackage} InstalledPackage
|
|
6
|
-
* @typedef {import('./_adapter.mjs').CatalogResult} CatalogResult
|
|
7
|
-
* @typedef {CatalogResult['packages'][number]} CatalogPackage
|
|
8
|
-
* @typedef {import('../../authoring/discover/type').DiscoverKind} DiscoverKind
|
|
9
|
-
*/
|
|
10
|
-
/**
|
|
11
|
-
* An item search looks through. `kind: 'package'` is a package itself.
|
|
12
|
-
* @typedef {object} SearchItem
|
|
13
|
-
* @property {string} package
|
|
14
|
-
* @property {DiscoverKind | 'package'} kind
|
|
15
|
-
* @property {string} name
|
|
16
|
-
* @property {boolean} installed
|
|
17
|
-
* @property {string} [title]
|
|
18
|
-
* @property {string} [summary]
|
|
19
|
-
* @property {string[]} [keywords]
|
|
20
|
-
* @property {string} [description]
|
|
21
|
-
*/
|
|
22
|
-
/**
|
|
23
|
-
* Group a version's contributions into the entry fields, one list per kind.
|
|
24
|
-
* `components` is always present, like on an installed entry.
|
|
25
|
-
* @param {CatalogPackage['contributions']} contributions
|
|
26
|
-
* @returns {Record<string, string[]>}
|
|
27
|
-
*/
|
|
28
|
-
export function kindLists(contributions: CatalogPackage["contributions"]): Record<string, string[]>;
|
|
29
|
-
/**
|
|
30
|
-
* Whether an entry adds at least one item of `kind`.
|
|
31
|
-
* @param {Record<string, unknown>} entry
|
|
32
|
-
* @param {DiscoverKind} kind
|
|
33
|
-
* @returns {boolean}
|
|
34
|
-
*/
|
|
35
|
-
export function hasKind(entry: Record<string, unknown>, kind: DiscoverKind): boolean;
|
|
36
|
-
/**
|
|
37
|
-
* Where a catalog package stands against the project.
|
|
38
|
-
* @param {CatalogPackage} pkg
|
|
39
|
-
* @param {Set<string>} installedNames
|
|
40
|
-
* @param {Set<string>} declared
|
|
41
|
-
* @returns {{state: 'installed' | 'declared' | 'alias' | 'available', installedAs?: string}}
|
|
42
|
-
*/
|
|
43
|
-
export function catalogState(pkg: CatalogPackage, installedNames: Set<string>, declared: Set<string>): {
|
|
44
|
-
state: "installed" | "declared" | "alias" | "available";
|
|
45
|
-
installedAs?: string;
|
|
46
|
-
};
|
|
47
|
-
/**
|
|
48
|
-
* The version a catalog package shows by default.
|
|
49
|
-
* @param {CatalogPackage} pkg
|
|
50
|
-
* @returns {string | undefined}
|
|
51
|
-
*/
|
|
52
|
-
export function defaultVersion(pkg: CatalogPackage): string | undefined;
|
|
53
|
-
/**
|
|
54
|
-
* Project a catalog package into the entry shape installed packages use.
|
|
55
|
-
* @param {CatalogPackage} pkg
|
|
56
|
-
* @param {string | undefined} version
|
|
57
|
-
*/
|
|
58
|
-
export function catalogEntry(pkg: CatalogPackage, version: string | undefined): {
|
|
59
|
-
description?: string | undefined;
|
|
60
|
-
version?: string | undefined;
|
|
61
|
-
name: string;
|
|
62
|
-
category: string;
|
|
63
|
-
components: string[];
|
|
64
|
-
};
|
|
65
|
-
/**
|
|
66
|
-
* What the project could add: every catalog package it does not have, once per
|
|
67
|
-
* integration (the first name a source lists), never an alias of one it has.
|
|
68
|
-
* @param {CatalogResult} catalog
|
|
69
|
-
* @param {InstalledPackage[]} installed
|
|
70
|
-
* @param {Set<string>} declared
|
|
71
|
-
*/
|
|
72
|
-
export function availableEntries(catalog: CatalogResult, installed: InstalledPackage[], declared: Set<string>): {
|
|
73
|
-
source: string;
|
|
74
|
-
aliases?: string[] | undefined;
|
|
75
|
-
description?: string | undefined;
|
|
76
|
-
version?: string | undefined;
|
|
77
|
-
name: string;
|
|
78
|
-
components: string[];
|
|
79
|
-
}[];
|
|
80
|
-
/**
|
|
81
|
-
* Installed packages with the latest release a source knows for each.
|
|
82
|
-
* @param {InstalledPackage[]} installed
|
|
83
|
-
* @param {CatalogResult} catalog
|
|
84
|
-
* @returns {Array<InstalledPackage & {latest?: string}>}
|
|
85
|
-
*/
|
|
86
|
-
export function withLatest(installed: InstalledPackage[], catalog: CatalogResult): Array<InstalledPackage & {
|
|
87
|
-
latest?: string;
|
|
88
|
-
}>;
|
|
89
|
-
/**
|
|
90
|
-
* Everything search looks through besides installed components (which the
|
|
91
|
-
* search leaf keeps resolving the way it always has): installed packages and
|
|
92
|
-
* their other items, and every package the project could add with its items.
|
|
93
|
-
* @param {InstalledPackage[]} installed
|
|
94
|
-
* @param {CatalogResult} catalog
|
|
95
|
-
* @param {Set<string>} declared
|
|
96
|
-
* @returns {SearchItem[]}
|
|
97
|
-
*/
|
|
98
|
-
export function searchItems(installed: InstalledPackage[], catalog: CatalogResult, declared: Set<string>): SearchItem[];
|
|
99
|
-
export type InstalledPackage = import("./_adapter.mjs").InstalledPackage;
|
|
100
|
-
export type CatalogResult = import("./_adapter.mjs").CatalogResult;
|
|
101
|
-
export type CatalogPackage = CatalogResult["packages"][number];
|
|
102
|
-
export type DiscoverKind = import("../../authoring/discover/type").DiscoverKind;
|
|
103
|
-
/**
|
|
104
|
-
* An item search looks through. `kind: 'package'` is a package itself.
|
|
105
|
-
*/
|
|
106
|
-
export type SearchItem = {
|
|
107
|
-
package: string;
|
|
108
|
-
kind: DiscoverKind | "package";
|
|
109
|
-
name: string;
|
|
110
|
-
installed: boolean;
|
|
111
|
-
title?: string | undefined;
|
|
112
|
-
summary?: string | undefined;
|
|
113
|
-
keywords?: string[] | undefined;
|
|
114
|
-
description?: string | undefined;
|
|
115
|
-
};
|
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @file Pure views over discover sources' catalogs: which catalog packages the
|
|
5
|
-
* project already has, what it could add, and the items search looks through.
|
|
6
|
-
* No I/O; the adapter supplies every input.
|
|
7
|
-
*
|
|
8
|
-
* @position api/discover — shared by the list, detail, item, and search leaves.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import {KIND_FIELDS} from './_adapter.mjs';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* @typedef {import('./_adapter.mjs').InstalledPackage} InstalledPackage
|
|
15
|
-
* @typedef {import('./_adapter.mjs').CatalogResult} CatalogResult
|
|
16
|
-
* @typedef {CatalogResult['packages'][number]} CatalogPackage
|
|
17
|
-
* @typedef {import('../../authoring/discover/type').DiscoverKind} DiscoverKind
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* An item search looks through. `kind: 'package'` is a package itself.
|
|
22
|
-
* @typedef {object} SearchItem
|
|
23
|
-
* @property {string} package
|
|
24
|
-
* @property {DiscoverKind | 'package'} kind
|
|
25
|
-
* @property {string} name
|
|
26
|
-
* @property {boolean} installed
|
|
27
|
-
* @property {string} [title]
|
|
28
|
-
* @property {string} [summary]
|
|
29
|
-
* @property {string[]} [keywords]
|
|
30
|
-
* @property {string} [description]
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Group a version's contributions into the entry fields, one list per kind.
|
|
35
|
-
* `components` is always present, like on an installed entry.
|
|
36
|
-
* @param {CatalogPackage['contributions']} contributions
|
|
37
|
-
* @returns {Record<string, string[]>}
|
|
38
|
-
*/
|
|
39
|
-
export function kindLists(contributions) {
|
|
40
|
-
/** @type {Record<string, string[]>} */
|
|
41
|
-
const lists = {components: []};
|
|
42
|
-
for (const [kind, field] of KIND_FIELDS) {
|
|
43
|
-
const names = contributions.filter(c => c.kind === kind).map(c => c.name);
|
|
44
|
-
if (names.length > 0 || field === 'components') lists[field] = names;
|
|
45
|
-
}
|
|
46
|
-
return lists;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Whether an entry adds at least one item of `kind`.
|
|
51
|
-
* @param {Record<string, unknown>} entry
|
|
52
|
-
* @param {DiscoverKind} kind
|
|
53
|
-
* @returns {boolean}
|
|
54
|
-
*/
|
|
55
|
-
export function hasKind(entry, kind) {
|
|
56
|
-
const field = KIND_FIELDS.find(([k]) => k === kind)?.[1];
|
|
57
|
-
const values = field == null ? undefined : entry[field];
|
|
58
|
-
return Array.isArray(values) && values.length > 0;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Where a catalog package stands against the project.
|
|
63
|
-
* @param {CatalogPackage} pkg
|
|
64
|
-
* @param {Set<string>} installedNames
|
|
65
|
-
* @param {Set<string>} declared
|
|
66
|
-
* @returns {{state: 'installed' | 'declared' | 'alias' | 'available', installedAs?: string}}
|
|
67
|
-
*/
|
|
68
|
-
export function catalogState(pkg, installedNames, declared) {
|
|
69
|
-
if (installedNames.has(pkg.package)) return {state: 'installed'};
|
|
70
|
-
if (declared.has(pkg.package)) return {state: 'declared'};
|
|
71
|
-
const installedAs = pkg.aliases.find(
|
|
72
|
-
name => installedNames.has(name) || declared.has(name),
|
|
73
|
-
);
|
|
74
|
-
if (installedAs != null) return {state: 'alias', installedAs};
|
|
75
|
-
return {state: 'available'};
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* The version a catalog package shows by default.
|
|
80
|
-
* @param {CatalogPackage} pkg
|
|
81
|
-
* @returns {string | undefined}
|
|
82
|
-
*/
|
|
83
|
-
export function defaultVersion(pkg) {
|
|
84
|
-
return pkg.latest ?? pkg.versions[0]?.version;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Project a catalog package into the entry shape installed packages use.
|
|
89
|
-
* @param {CatalogPackage} pkg
|
|
90
|
-
* @param {string | undefined} version
|
|
91
|
-
*/
|
|
92
|
-
export function catalogEntry(pkg, version) {
|
|
93
|
-
const {components, ...others} = kindLists(pkg.contributions);
|
|
94
|
-
return {
|
|
95
|
-
name: pkg.package,
|
|
96
|
-
category: pkg.package,
|
|
97
|
-
components,
|
|
98
|
-
...(version ? {version} : {}),
|
|
99
|
-
...(pkg.description ? {description: pkg.description} : {}),
|
|
100
|
-
...others,
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* What the project could add: every catalog package it does not have, once per
|
|
106
|
-
* integration (the first name a source lists), never an alias of one it has.
|
|
107
|
-
* @param {CatalogResult} catalog
|
|
108
|
-
* @param {InstalledPackage[]} installed
|
|
109
|
-
* @param {Set<string>} declared
|
|
110
|
-
*/
|
|
111
|
-
export function availableEntries(catalog, installed, declared) {
|
|
112
|
-
const installedNames = new Set(installed.map(p => p.name));
|
|
113
|
-
/** @type {Set<string>} */
|
|
114
|
-
const integrations = new Set();
|
|
115
|
-
const entries = [];
|
|
116
|
-
for (const pkg of catalog.packages) {
|
|
117
|
-
if (catalogState(pkg, installedNames, declared).state !== 'available')
|
|
118
|
-
continue;
|
|
119
|
-
if (integrations.has(pkg.integration)) continue;
|
|
120
|
-
integrations.add(pkg.integration);
|
|
121
|
-
const version = defaultVersion(pkg);
|
|
122
|
-
const {category: _category, ...entry} = catalogEntry(pkg, version);
|
|
123
|
-
entries.push({
|
|
124
|
-
...entry,
|
|
125
|
-
...(pkg.aliases.length > 0 ? {aliases: pkg.aliases} : {}),
|
|
126
|
-
source: pkg.source,
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
return entries;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Installed packages with the latest release a source knows for each.
|
|
134
|
-
* @param {InstalledPackage[]} installed
|
|
135
|
-
* @param {CatalogResult} catalog
|
|
136
|
-
* @returns {Array<InstalledPackage & {latest?: string}>}
|
|
137
|
-
*/
|
|
138
|
-
export function withLatest(installed, catalog) {
|
|
139
|
-
const byName = new Map(catalog.packages.map(p => [p.package, p]));
|
|
140
|
-
return installed.map(pkg => {
|
|
141
|
-
const latest = byName.get(pkg.name)?.latest;
|
|
142
|
-
return latest ? {...pkg, latest} : pkg;
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Everything search looks through besides installed components (which the
|
|
148
|
-
* search leaf keeps resolving the way it always has): installed packages and
|
|
149
|
-
* their other items, and every package the project could add with its items.
|
|
150
|
-
* @param {InstalledPackage[]} installed
|
|
151
|
-
* @param {CatalogResult} catalog
|
|
152
|
-
* @param {Set<string>} declared
|
|
153
|
-
* @returns {SearchItem[]}
|
|
154
|
-
*/
|
|
155
|
-
export function searchItems(installed, catalog, declared) {
|
|
156
|
-
/** @type {SearchItem[]} */
|
|
157
|
-
const items = [];
|
|
158
|
-
for (const pkg of installed) {
|
|
159
|
-
items.push({
|
|
160
|
-
package: pkg.name,
|
|
161
|
-
kind: 'package',
|
|
162
|
-
name: pkg.name,
|
|
163
|
-
installed: true,
|
|
164
|
-
...(pkg.description ? {description: pkg.description} : {}),
|
|
165
|
-
});
|
|
166
|
-
for (const [kind, field] of KIND_FIELDS) {
|
|
167
|
-
if (kind === 'component') continue;
|
|
168
|
-
const names = /** @type {Record<string, unknown>} */ (pkg)[field];
|
|
169
|
-
if (!Array.isArray(names)) continue;
|
|
170
|
-
for (const name of names) {
|
|
171
|
-
items.push({package: pkg.name, kind, name, installed: true});
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
const installedNames = new Set(installed.map(p => p.name));
|
|
176
|
-
/** @type {Set<string>} */
|
|
177
|
-
const integrations = new Set();
|
|
178
|
-
for (const pkg of catalog.packages) {
|
|
179
|
-
if (catalogState(pkg, installedNames, declared).state !== 'available')
|
|
180
|
-
continue;
|
|
181
|
-
if (integrations.has(pkg.integration)) continue;
|
|
182
|
-
integrations.add(pkg.integration);
|
|
183
|
-
items.push({
|
|
184
|
-
package: pkg.package,
|
|
185
|
-
kind: 'package',
|
|
186
|
-
name: pkg.package,
|
|
187
|
-
installed: false,
|
|
188
|
-
...(pkg.description ? {description: pkg.description} : {}),
|
|
189
|
-
});
|
|
190
|
-
for (const c of pkg.contributions) {
|
|
191
|
-
items.push({
|
|
192
|
-
package: pkg.package,
|
|
193
|
-
kind: c.kind,
|
|
194
|
-
name: c.name,
|
|
195
|
-
installed: false,
|
|
196
|
-
...(c.title ? {title: c.title} : {}),
|
|
197
|
-
...(c.summary ? {summary: c.summary} : {}),
|
|
198
|
-
...(c.keywords ? {keywords: c.keywords} : {}),
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
return items;
|
|
203
|
-
}
|