@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astryxdesign/cli",
3
- "version": "0.1.8-canary.4a7ce02",
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.4a7ce02",
83
- "@astryxdesign/core": "0.1.8-canary.4a7ce02",
84
- "@astryxdesign/lab": "0.1.8-canary.4a7ce02",
85
- "@astryxdesign/theme-neutral": "0.1.8-canary.4a7ce02",
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.4a7ce02",
104
- "@astryxdesign/core": "0.1.8-canary.4a7ce02",
105
- "@astryxdesign/lab": "0.1.8-canary.4a7ce02",
106
- "@astryxdesign/theme-neutral": "0.1.8-canary.4a7ce02",
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 {Array<{name: string, reason: string}> | undefined} */
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 {Array<{name: string, reason: string}>} [suggestions]
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) {
@@ -358,7 +358,7 @@ export function registerSwizzle(program) {
358
358
  const feedback = buildFeedback(dirName, owner.issuesUrl);
359
359
 
360
360
  if (json) {
361
- /** @type {Record<string, unknown>} */
361
+ /** @type {import('../types/swizzle').SwizzleCopyResponse['data']} */
362
362
  const payload = {
363
363
  component: dirName,
364
364
  package: owner.package,
@@ -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 {{name: string, reason?: string}} Suggestion
58
+ * envelope's `suggestions` field. Canonical definition lives in types/base.
59
+ * @typedef {import('../types/base').Suggestion} Suggestion
60
60
  */
61
61
 
62
62
  /**
@@ -102,7 +102,7 @@ export function buildHelpEnvelope(cmd) {
102
102
  * for both success and error).
103
103
  *
104
104
  * @param {string} message
105
- * @param {Array<{name: string, reason: string}>} [suggestions]
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 {Array<{name: string, reason: string}>} [suggestions]
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?: Array<{name: string, reason: string}>}}
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 {Array<{name: string, reason: string}>} [suggestions]
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) {
@@ -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?: Array<{name: string; reason: string}>;
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
- | DocsListResponse
104
- | DocsDetailResponse
105
- | DocsDetailSectionResponse;
100
+ DocsListResponse | DocsDetailResponse | DocsDetailSectionResponse;
106
101
 
107
102
  export declare function docs(
108
103
  topic?: string,
@@ -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 {UpgradeListResponse, UpgradeRunResponse} from './upgrade';
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?: Array<{name: string; reason: string}>;
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?: Array<{name: string; reason: string}>,
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
+ }
@@ -10,6 +10,7 @@ export * from './swizzle';
10
10
  export * from './theme';
11
11
  export * from './upgrade';
12
12
  export * from './search';
13
+ export * from './build';
13
14
  export * from './error-codes';
14
15
  export * from './manifest';
15
16
  export * from './doctor';
@@ -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 {'yarn'|'pnpm'|'bun'|'npm'}
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 (KNOWN_PMS.has(name)) return name;
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 (KNOWN_PMS.has(name)) return name;
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 filesystem and env var checks.
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(cwd);
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(tmpDir)).toBe('0.0.8');
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(tmpDir)).toBeNull();
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(tmpDir)).toBeNull();
49
+ expect(getLatestVersion()).toBeNull();
50
50
  });
51
51
  });
52
52