@astryxdesign/cli 0.1.8-canary.4a7ce02 → 0.1.8-canary.4fe3df0
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/package.json +9 -9
- package/src/api/error.mjs +2 -2
- package/src/commands/swizzle.mjs +1 -1
- package/src/lib/cli-error.mjs +2 -2
- package/src/lib/json-shim.mjs +1 -1
- package/src/lib/json.mjs +3 -3
- package/src/types/api.d.ts +4 -9
- package/src/types/base.d.ts +24 -3
- package/src/types/build.d.ts +23 -0
- package/src/types/index.d.ts +1 -0
- package/src/types/swizzle.d.ts +4 -0
- package/src/utils/package-manager.mjs +30 -4
- package/src/utils/update-check.mjs +3 -3
- package/src/utils/update-check.test.mjs +3 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/cli",
|
|
3
|
-
"version": "0.1.8-canary.
|
|
3
|
+
"version": "0.1.8-canary.4fe3df0",
|
|
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",
|
|
@@ -79,10 +79,10 @@
|
|
|
79
79
|
"zod": "^4.4.3"
|
|
80
80
|
},
|
|
81
81
|
"peerDependencies": {
|
|
82
|
-
"@astryxdesign/charts": "0.1.8-canary.
|
|
83
|
-
"@astryxdesign/core": "0.1.8-canary.
|
|
84
|
-
"@astryxdesign/lab": "0.1.8-canary.
|
|
85
|
-
"@astryxdesign/theme-neutral": "0.1.8-canary.
|
|
82
|
+
"@astryxdesign/charts": "0.1.8-canary.4fe3df0",
|
|
83
|
+
"@astryxdesign/core": "0.1.8-canary.4fe3df0",
|
|
84
|
+
"@astryxdesign/lab": "0.1.8-canary.4fe3df0",
|
|
85
|
+
"@astryxdesign/theme-neutral": "0.1.8-canary.4fe3df0",
|
|
86
86
|
"gpt-tokenizer": "^3.4.0"
|
|
87
87
|
},
|
|
88
88
|
"peerDependenciesMeta": {
|
|
@@ -100,10 +100,10 @@
|
|
|
100
100
|
}
|
|
101
101
|
},
|
|
102
102
|
"devDependencies": {
|
|
103
|
-
"@astryxdesign/charts": "0.1.8-canary.
|
|
104
|
-
"@astryxdesign/core": "0.1.8-canary.
|
|
105
|
-
"@astryxdesign/lab": "0.1.8-canary.
|
|
106
|
-
"@astryxdesign/theme-neutral": "0.1.8-canary.
|
|
103
|
+
"@astryxdesign/charts": "0.1.8-canary.4fe3df0",
|
|
104
|
+
"@astryxdesign/core": "0.1.8-canary.4fe3df0",
|
|
105
|
+
"@astryxdesign/lab": "0.1.8-canary.4fe3df0",
|
|
106
|
+
"@astryxdesign/theme-neutral": "0.1.8-canary.4fe3df0",
|
|
107
107
|
"gpt-tokenizer": "^3.4.0"
|
|
108
108
|
},
|
|
109
109
|
"scripts": {
|
package/src/api/error.mjs
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import {ERROR_CODES} from '../lib/error-codes.mjs';
|
|
15
15
|
|
|
16
16
|
export class AstryxError extends Error {
|
|
17
|
-
/** @type {
|
|
17
|
+
/** @type {import('../types/base').Suggestion[] | undefined} */
|
|
18
18
|
suggestions;
|
|
19
19
|
|
|
20
20
|
/**
|
|
@@ -26,7 +26,7 @@ export class AstryxError extends Error {
|
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* @param {string} message
|
|
29
|
-
* @param {
|
|
29
|
+
* @param {import('../types/base').Suggestion[]} [suggestions]
|
|
30
30
|
* @param {string} [code] - Stable error code. Defaults to ERR_UNKNOWN.
|
|
31
31
|
*/
|
|
32
32
|
constructor(message, suggestions, code) {
|
package/src/commands/swizzle.mjs
CHANGED
|
@@ -358,7 +358,7 @@ export function registerSwizzle(program) {
|
|
|
358
358
|
const feedback = buildFeedback(dirName, owner.issuesUrl);
|
|
359
359
|
|
|
360
360
|
if (json) {
|
|
361
|
-
/** @type {
|
|
361
|
+
/** @type {import('../types/swizzle').SwizzleCopyResponse['data']} */
|
|
362
362
|
const payload = {
|
|
363
363
|
component: dirName,
|
|
364
364
|
package: owner.package,
|
package/src/lib/cli-error.mjs
CHANGED
|
@@ -55,8 +55,8 @@ import {ERROR_CODES} from './error-codes.mjs';
|
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* Suggestion object — matches the shape used by API errors and the JSON
|
|
58
|
-
* envelope's `suggestions` field.
|
|
59
|
-
* @typedef {
|
|
58
|
+
* envelope's `suggestions` field. Canonical definition lives in types/base.
|
|
59
|
+
* @typedef {import('../types/base').Suggestion} Suggestion
|
|
60
60
|
*/
|
|
61
61
|
|
|
62
62
|
/**
|
package/src/lib/json-shim.mjs
CHANGED
|
@@ -102,7 +102,7 @@ export function buildHelpEnvelope(cmd) {
|
|
|
102
102
|
* for both success and error).
|
|
103
103
|
*
|
|
104
104
|
* @param {string} message
|
|
105
|
-
* @param {
|
|
105
|
+
* @param {import('../types/base').Suggestion[]} [suggestions]
|
|
106
106
|
* @param {string} [code] - Stable machine-readable error code (error-codes.mjs).
|
|
107
107
|
*/
|
|
108
108
|
function emitJsonError(message, suggestions, code) {
|
package/src/lib/json.mjs
CHANGED
|
@@ -112,10 +112,10 @@ export function jsonOut(type, data, meta) {
|
|
|
112
112
|
* consumers can branch on it unconditionally.
|
|
113
113
|
*
|
|
114
114
|
* @param {unknown} err
|
|
115
|
-
* @param {
|
|
115
|
+
* @param {import('../types/base').Suggestion[]} [suggestions]
|
|
116
116
|
* @param {string} [code] - Explicit stable error code. Overrides any code
|
|
117
117
|
* carried on a thrown Error.
|
|
118
|
-
* @returns {{apiVersion: number, error: string, code: string, suggestions?:
|
|
118
|
+
* @returns {{apiVersion: number, error: string, code: string, suggestions?: import('../types/base').Suggestion[]}}
|
|
119
119
|
*/
|
|
120
120
|
export function toErrorEnvelope(err, suggestions, code) {
|
|
121
121
|
const message =
|
|
@@ -136,7 +136,7 @@ export function toErrorEnvelope(err, suggestions, code) {
|
|
|
136
136
|
/**
|
|
137
137
|
* Output a structured JSON error and exit.
|
|
138
138
|
* @param {string} message
|
|
139
|
-
* @param {
|
|
139
|
+
* @param {import('../types/base').Suggestion[]} [suggestions]
|
|
140
140
|
* @param {string} [code] - Stable machine-readable error code (error-codes.mjs).
|
|
141
141
|
*/
|
|
142
142
|
export function jsonError(message, suggestions, code) {
|
package/src/types/api.d.ts
CHANGED
|
@@ -44,17 +44,14 @@ import type {
|
|
|
44
44
|
import type {SearchResponse, SearchDomain} from './search';
|
|
45
45
|
import type {ErrorCode} from './error-codes';
|
|
46
46
|
import type {DoctorResponse} from './doctor';
|
|
47
|
+
import type {Suggestion} from './base';
|
|
47
48
|
|
|
48
49
|
/** Structured API error with a stable machine-readable code. */
|
|
49
50
|
export declare class AstryxError extends Error {
|
|
50
51
|
/** Stable error code; consumers branch on this, never the message. */
|
|
51
52
|
code: ErrorCode;
|
|
52
|
-
suggestions?:
|
|
53
|
-
constructor(
|
|
54
|
-
message: string,
|
|
55
|
-
suggestions?: Array<{name: string; reason: string}>,
|
|
56
|
-
code?: ErrorCode,
|
|
57
|
-
);
|
|
53
|
+
suggestions?: Suggestion[];
|
|
54
|
+
constructor(message: string, suggestions?: Suggestion[], code?: ErrorCode);
|
|
58
55
|
}
|
|
59
56
|
|
|
60
57
|
// ── Component ────────────────────────────────────────────────────────
|
|
@@ -100,9 +97,7 @@ export interface DocsOptions {
|
|
|
100
97
|
}
|
|
101
98
|
|
|
102
99
|
type DocsResult =
|
|
103
|
-
|
|
|
104
|
-
| DocsDetailResponse
|
|
105
|
-
| DocsDetailSectionResponse;
|
|
100
|
+
DocsListResponse | DocsDetailResponse | DocsDetailSectionResponse;
|
|
106
101
|
|
|
107
102
|
export declare function docs(
|
|
108
103
|
topic?: string,
|
package/src/types/base.d.ts
CHANGED
|
@@ -11,10 +11,12 @@
|
|
|
11
11
|
import type {
|
|
12
12
|
ComponentListResponse,
|
|
13
13
|
ComponentBriefResponse,
|
|
14
|
+
ComponentFullResponse,
|
|
14
15
|
ComponentDetailResponse,
|
|
15
16
|
ComponentDetailPropsResponse,
|
|
16
17
|
ComponentDetailSourceResponse,
|
|
17
18
|
ComponentDetailShowcaseResponse,
|
|
19
|
+
ComponentDetailBlocksResponse,
|
|
18
20
|
} from './component';
|
|
19
21
|
import type {
|
|
20
22
|
DiscoverListResponse,
|
|
@@ -42,13 +44,28 @@ import type {
|
|
|
42
44
|
} from './hook';
|
|
43
45
|
import type {SwizzleListResponse, SwizzleCopyResponse} from './swizzle';
|
|
44
46
|
import type {ThemeBuildResponse} from './theme';
|
|
45
|
-
import type {
|
|
47
|
+
import type {
|
|
48
|
+
UpgradeListResponse,
|
|
49
|
+
UpgradeRunResponse,
|
|
50
|
+
UpgradeStatusResponse,
|
|
51
|
+
} from './upgrade';
|
|
46
52
|
import type {SearchResponse} from './search';
|
|
53
|
+
import type {BuildHelpResponse} from './build';
|
|
47
54
|
import type {ErrorCode} from './error-codes';
|
|
48
55
|
import type {ManifestResponse} from './manifest';
|
|
49
56
|
import type {DoctorResponse} from './doctor';
|
|
50
57
|
import type {ValidateIntegrationResponse} from './validate-integration';
|
|
51
58
|
|
|
59
|
+
/**
|
|
60
|
+
* A "did you mean…" suggestion attached to an error. `reason` is optional:
|
|
61
|
+
* some call sites emit bare `{name}` (e.g. a list of candidate component names)
|
|
62
|
+
* with no per-item explanation.
|
|
63
|
+
*/
|
|
64
|
+
export interface Suggestion {
|
|
65
|
+
name: string;
|
|
66
|
+
reason?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
52
69
|
/**
|
|
53
70
|
* Structured error. Check `'error' in result` to discriminate.
|
|
54
71
|
*
|
|
@@ -58,7 +75,7 @@ import type {ValidateIntegrationResponse} from './validate-integration';
|
|
|
58
75
|
export interface CLIError {
|
|
59
76
|
error: string;
|
|
60
77
|
code: ErrorCode;
|
|
61
|
-
suggestions?:
|
|
78
|
+
suggestions?: Suggestion[];
|
|
62
79
|
}
|
|
63
80
|
|
|
64
81
|
/** Returned by the fallback hook for commands without --json support. */
|
|
@@ -74,10 +91,12 @@ export type CLIResult<T> = T | CLIError | CLIUnsupportedError;
|
|
|
74
91
|
export type CLIAnyResponse =
|
|
75
92
|
| ComponentListResponse
|
|
76
93
|
| ComponentBriefResponse
|
|
94
|
+
| ComponentFullResponse
|
|
77
95
|
| ComponentDetailResponse
|
|
78
96
|
| ComponentDetailPropsResponse
|
|
79
97
|
| ComponentDetailSourceResponse
|
|
80
98
|
| ComponentDetailShowcaseResponse
|
|
99
|
+
| ComponentDetailBlocksResponse
|
|
81
100
|
| DiscoverListResponse
|
|
82
101
|
| DiscoverDetailResponse
|
|
83
102
|
| DiscoverDetailDocResponse
|
|
@@ -99,7 +118,9 @@ export type CLIAnyResponse =
|
|
|
99
118
|
| ThemeBuildResponse
|
|
100
119
|
| UpgradeListResponse
|
|
101
120
|
| UpgradeRunResponse
|
|
121
|
+
| UpgradeStatusResponse
|
|
102
122
|
| SearchResponse
|
|
123
|
+
| BuildHelpResponse
|
|
103
124
|
| ManifestResponse
|
|
104
125
|
| DoctorResponse
|
|
105
126
|
| ValidateIntegrationResponse;
|
|
@@ -129,7 +150,7 @@ export function jsonOut<T extends CLIResponseType>(
|
|
|
129
150
|
*/
|
|
130
151
|
export function jsonError(
|
|
131
152
|
message: string,
|
|
132
|
-
suggestions?:
|
|
153
|
+
suggestions?: Suggestion[],
|
|
133
154
|
code?: ErrorCode,
|
|
134
155
|
): never;
|
|
135
156
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Build command JSON responses.
|
|
5
|
+
*
|
|
6
|
+
* `astryx build` is the "assemble a page" verb. With no query it emits the
|
|
7
|
+
* workflow playbook; with a query it delegates to the search pipeline and
|
|
8
|
+
* emits a `search` response (see search.d.ts).
|
|
9
|
+
*
|
|
10
|
+
* Invocation -> type discriminator
|
|
11
|
+
* ---------------------------------------------------
|
|
12
|
+
* xds --json build -> build.help
|
|
13
|
+
* xds --json build "<idea>" -> search
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** xds --json build (no query) — the "how to build a page" playbook signal. */
|
|
17
|
+
export interface BuildHelpResponse {
|
|
18
|
+
type: 'build.help';
|
|
19
|
+
data: {
|
|
20
|
+
/** Always true; marks this envelope as the playbook rather than a result set. */
|
|
21
|
+
playbook: true;
|
|
22
|
+
};
|
|
23
|
+
}
|
package/src/types/index.d.ts
CHANGED
package/src/types/swizzle.d.ts
CHANGED
|
@@ -29,9 +29,13 @@ export interface SwizzleCopyResponse {
|
|
|
29
29
|
type: 'swizzle.copy';
|
|
30
30
|
data: {
|
|
31
31
|
component: string;
|
|
32
|
+
/** Owner package the component source was copied from. */
|
|
33
|
+
package: string;
|
|
32
34
|
outputDir: string;
|
|
33
35
|
filesCopied: number;
|
|
34
36
|
files: string[];
|
|
37
|
+
/** Whether any copied file uses StyleX (requires build-time setup). */
|
|
38
|
+
usesStyleX: boolean;
|
|
35
39
|
feedback?: SwizzleFeedback;
|
|
36
40
|
};
|
|
37
41
|
}
|
|
@@ -10,15 +10,41 @@
|
|
|
10
10
|
import * as fs from 'node:fs';
|
|
11
11
|
import * as path from 'node:path';
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* A package manager we can install with and run binaries through.
|
|
15
|
+
* @typedef {'yarn' | 'pnpm' | 'bun' | 'npm'} PackageManager
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Result of package-manager detection. `'npx'` is the sentinel for "nothing
|
|
20
|
+
* detected" — no lockfile, no `packageManager` field, no runner user-agent —
|
|
21
|
+
* so callers fall back to npm/npx. It is a distinct value from {@link PackageManager}
|
|
22
|
+
* because it means "undetected", not "npm was chosen".
|
|
23
|
+
* @typedef {PackageManager | 'npx'} DetectedPackageManager
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/** @type {readonly PackageManager[]} */
|
|
27
|
+
const KNOWN_PMS = ['yarn', 'pnpm', 'bun', 'npm'];
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Narrow an arbitrary string to a known {@link PackageManager}.
|
|
31
|
+
* @param {string} name
|
|
32
|
+
* @returns {name is PackageManager}
|
|
33
|
+
*/
|
|
34
|
+
function isKnownPackageManager(name) {
|
|
35
|
+
return /** @type {readonly string[]} */ (KNOWN_PMS).includes(name);
|
|
36
|
+
}
|
|
37
|
+
|
|
13
38
|
/**
|
|
14
39
|
* Detect the package manager used in a project directory.
|
|
15
40
|
* Walks up from targetDir looking for lockfiles.
|
|
16
41
|
*
|
|
42
|
+
* Returns `'npx'` when nothing can be detected — see {@link DetectedPackageManager}.
|
|
43
|
+
*
|
|
17
44
|
* @param {string} [targetDir=process.cwd()]
|
|
18
|
-
* @returns {
|
|
45
|
+
* @returns {DetectedPackageManager}
|
|
19
46
|
*/
|
|
20
47
|
export function detectPackageManager(targetDir = process.cwd()) {
|
|
21
|
-
const KNOWN_PMS = new Set(['yarn', 'pnpm', 'bun', 'npm']);
|
|
22
48
|
let dir = path.resolve(targetDir);
|
|
23
49
|
const root = path.parse(dir).root;
|
|
24
50
|
|
|
@@ -36,7 +62,7 @@ export function detectPackageManager(targetDir = process.cwd()) {
|
|
|
36
62
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
37
63
|
if (pkg.packageManager) {
|
|
38
64
|
const name = pkg.packageManager.split('@')[0];
|
|
39
|
-
if (
|
|
65
|
+
if (isKnownPackageManager(name)) return name;
|
|
40
66
|
}
|
|
41
67
|
} catch {
|
|
42
68
|
// Best-effort: unreadable/invalid package.json — keep walking up.
|
|
@@ -50,7 +76,7 @@ export function detectPackageManager(targetDir = process.cwd()) {
|
|
|
50
76
|
const ua = process.env.npm_config_user_agent;
|
|
51
77
|
if (ua) {
|
|
52
78
|
const name = ua.split('/')[0];
|
|
53
|
-
if (
|
|
79
|
+
if (isKnownPackageManager(name)) return name;
|
|
54
80
|
}
|
|
55
81
|
|
|
56
82
|
return 'npx';
|
|
@@ -18,9 +18,9 @@ import {getCliInvocation} from './package-manager.mjs';
|
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Read the latest available version from local signals.
|
|
21
|
-
* No network calls — purely
|
|
21
|
+
* No network calls — purely an env-var check ($ASTRYX_LATEST_VERSION),
|
|
22
|
+
* so it takes no arguments.
|
|
22
23
|
*
|
|
23
|
-
* @param {string} [cwd] - Project directory (default: process.cwd())
|
|
24
24
|
* @returns {string|null} Latest version string, or null if unknown
|
|
25
25
|
*/
|
|
26
26
|
export function getLatestVersion() {
|
|
@@ -64,7 +64,7 @@ export function getInstalledVersion(cwd = process.cwd()) {
|
|
|
64
64
|
* @returns {string|null} FYI hint string, or null if up to date / unknown
|
|
65
65
|
*/
|
|
66
66
|
export function checkForUpdate(cwd = process.cwd()) {
|
|
67
|
-
const latest = getLatestVersion(
|
|
67
|
+
const latest = getLatestVersion();
|
|
68
68
|
if (!latest) return null;
|
|
69
69
|
|
|
70
70
|
// Persist for subsequent commands in this shell session
|
|
@@ -33,12 +33,12 @@ afterEach(() => {
|
|
|
33
33
|
describe('getLatestVersion', () => {
|
|
34
34
|
it('reads from ASTRYX_LATEST_VERSION env var', () => {
|
|
35
35
|
process.env.ASTRYX_LATEST_VERSION = '0.0.8';
|
|
36
|
-
expect(getLatestVersion(
|
|
36
|
+
expect(getLatestVersion()).toBe('0.0.8');
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
it('ignores invalid env var values', () => {
|
|
40
40
|
process.env.ASTRYX_LATEST_VERSION = 'not-a-version';
|
|
41
|
-
expect(getLatestVersion(
|
|
41
|
+
expect(getLatestVersion()).toBeNull();
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
it('returns null when no signals exist', () => {
|
|
@@ -46,7 +46,7 @@ describe('getLatestVersion', () => {
|
|
|
46
46
|
path.join(tmpDir, 'package.json'),
|
|
47
47
|
JSON.stringify({name: 'test'}),
|
|
48
48
|
);
|
|
49
|
-
expect(getLatestVersion(
|
|
49
|
+
expect(getLatestVersion()).toBeNull();
|
|
50
50
|
});
|
|
51
51
|
});
|
|
52
52
|
|