@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
package/host/index.d.ts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `@astryxdesign/cli/host`: the programmatic host entry. A tool that carries
|
|
5
|
+
* this CLI starts one run of it per process with explicit inputs
|
|
6
|
+
* (`spec:AST-081` FR1).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type {DiscoverSource} from '../authoring/discover/type.js';
|
|
10
|
+
|
|
11
|
+
/** The inputs of one hosted run. Every option is optional. */
|
|
12
|
+
export interface HostedCliOptions {
|
|
13
|
+
/** The arguments after `astryx`. Default: none. */
|
|
14
|
+
argv?: readonly string[];
|
|
15
|
+
/**
|
|
16
|
+
* The working folder of the run. The run changes the process's working
|
|
17
|
+
* folder to it, finds its project there, and writes where the command
|
|
18
|
+
* writes from there. Default: the process's working folder.
|
|
19
|
+
*/
|
|
20
|
+
cwd?: string;
|
|
21
|
+
/**
|
|
22
|
+
* The command the tool's users type to run the CLI, such as
|
|
23
|
+
* `acme astryx`. Printed commands start with it unless the integrations
|
|
24
|
+
* the run loads declare their own. A non-blank single line of at most 120
|
|
25
|
+
* characters, with no surrounding spaces and no Astryx agent-docs marker
|
|
26
|
+
* text. Default: none; printed commands use the package manager's name.
|
|
27
|
+
*/
|
|
28
|
+
cliCommand?: string;
|
|
29
|
+
/**
|
|
30
|
+
* When the tool found the project's own CLI and did not run it, why, in the
|
|
31
|
+
* tool's own words, such as `cannot-start` or `untrusted`: a non-blank
|
|
32
|
+
* single line of at most 120 characters, with no surrounding spaces. The
|
|
33
|
+
* run is then never handed to that CLI: its release answers in its place
|
|
34
|
+
* when this copy, or a package set's host entry, is that release;
|
|
35
|
+
* otherwise this copy answers with one warning line naming the reason and
|
|
36
|
+
* `meta.answeredBy.copy` set to `fallback`. Default: none; a project's own
|
|
37
|
+
* CLI answers its project.
|
|
38
|
+
*/
|
|
39
|
+
declinedProjectCli?: string;
|
|
40
|
+
/**
|
|
41
|
+
* The tool's package sets, in its order of preference: folders whose
|
|
42
|
+
* `node_modules` each hold a CLI, the Core equal to it, and the
|
|
43
|
+
* integrations installed with them. Inside a project, a set fills only the
|
|
44
|
+
* `@astryxdesign/*` packages the project leaves uninstalled that are not
|
|
45
|
+
* integrations. When a release other than this copy answers the folder,
|
|
46
|
+
* the first set whose CLI and Core are that release and that holds what the
|
|
47
|
+
* project would install for those packages serves the run. A run never
|
|
48
|
+
* installs a release: with no such set, this copy answers with one warning
|
|
49
|
+
* line. Default: none.
|
|
50
|
+
*/
|
|
51
|
+
roots?: readonly string[];
|
|
52
|
+
/**
|
|
53
|
+
* Integrations the tool approves for use outside a project, by package
|
|
54
|
+
* name. Each loads from the package set that serves the run, as an
|
|
55
|
+
* integration installed for the computer does, and a name that set does not
|
|
56
|
+
* hold is left out. Default: none.
|
|
57
|
+
*/
|
|
58
|
+
integrations?: readonly string[];
|
|
59
|
+
/**
|
|
60
|
+
* The tool's own discover sources. `astryx discover` calls them after every
|
|
61
|
+
* integration's source and before the official list, and reports each as
|
|
62
|
+
* the host's. Another package set's host entry that answers the run gets
|
|
63
|
+
* them too; a run handed to a CLI's bin does not. Default: none.
|
|
64
|
+
*/
|
|
65
|
+
discoverSources?: readonly DiscoverSource[];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** One package a hosted run resolved. */
|
|
69
|
+
export interface HostedPackage {
|
|
70
|
+
/** The package name. */
|
|
71
|
+
name: string;
|
|
72
|
+
/** Its version, or null when its package.json gives none. */
|
|
73
|
+
version: string | null;
|
|
74
|
+
/** Whether it came from the project or from a package set. */
|
|
75
|
+
source: 'project' | 'package-set';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The outcome of a hosted run that returned: this copy's, or, when another
|
|
80
|
+
* package set's release answered through its own host entry, that entry's.
|
|
81
|
+
*/
|
|
82
|
+
export interface HostedCliResult {
|
|
83
|
+
/** The run's exit status, as the `astryx` bin would exit. */
|
|
84
|
+
status: number;
|
|
85
|
+
/** Each package the run resolved: the CLI, its Core, and its integrations. */
|
|
86
|
+
packages: HostedPackage[];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Run the CLI once in this process, as the `astryx` bin would with the same
|
|
91
|
+
* arguments in the same folder.
|
|
92
|
+
*
|
|
93
|
+
* The run owns the process's working folder, arguments, standard streams,
|
|
94
|
+
* and exit. The call settles when the command returns. Like the bin, the run
|
|
95
|
+
* instead ends the process, with the exit status the bin would exit with, and
|
|
96
|
+
* the call never settles, when it:
|
|
97
|
+
* - prints help or the version (`--help`, `-h`, `help`, `--version`, `-V`,
|
|
98
|
+
* `--version --json`), with status 0;
|
|
99
|
+
* - rejects the command line, such as an unknown command or option;
|
|
100
|
+
* - reports an error it stops on, including every error in `--json` mode;
|
|
101
|
+
* - meets an unexpected error, with status 1;
|
|
102
|
+
* - hands the run to another CLI's bin: the project's own CLI when
|
|
103
|
+
* `declinedProjectCli` is absent, or a package set whose CLI ships no host
|
|
104
|
+
* entry.
|
|
105
|
+
*
|
|
106
|
+
* When another package set's release answers through its own host entry,
|
|
107
|
+
* that entry runs in this process with the same inputs, and the call settles
|
|
108
|
+
* with its result.
|
|
109
|
+
*
|
|
110
|
+
* Rejects with a `TypeError`, before anything runs, for an unknown option or
|
|
111
|
+
* an invalid value; and with an `Error`, before anything runs, when a run has
|
|
112
|
+
* already started in this process.
|
|
113
|
+
*/
|
|
114
|
+
export declare function runHostedCli(
|
|
115
|
+
options?: HostedCliOptions,
|
|
116
|
+
): Promise<HostedCliResult>;
|
package/host/index.mjs
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file `@astryxdesign/cli/host`, the programmatic host entry (spec AST-081
|
|
5
|
+
* FR1). A tool that carries this CLI starts one run of it with explicit
|
|
6
|
+
* inputs: the arguments, the working folder, the command name its users type,
|
|
7
|
+
* and, when it found the project's own CLI and did not run it, the reason.
|
|
8
|
+
*
|
|
9
|
+
* Its package sets, the integrations it approves, and its own discover sources
|
|
10
|
+
* are matched, loaded, and recorded as architecture:cli-surface INV30 says; a
|
|
11
|
+
* hosted run installs nothing. When another package set's release answers
|
|
12
|
+
* through its own host entry, that entry runs in this process with the same
|
|
13
|
+
* inputs, and the call settles with its result.
|
|
14
|
+
*
|
|
15
|
+
* @input `runHostedCli` options (see host/index.d.ts for the contract).
|
|
16
|
+
* @output The run's exit status and the packages it resolved, each with its
|
|
17
|
+
* source. The run owns the process's working folder, arguments, standard
|
|
18
|
+
* streams, and exit, exactly as a run of the `astryx` bin does: every path
|
|
19
|
+
* on which the bin exits the process exits it here too (host/index.d.ts
|
|
20
|
+
* lists them).
|
|
21
|
+
* @position host — the package's `./host` export; calls clients/cli/run.mjs.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import * as fs from 'node:fs';
|
|
25
|
+
import * as path from 'node:path';
|
|
26
|
+
import {fileURLToPath, pathToFileURL} from 'node:url';
|
|
27
|
+
|
|
28
|
+
const CLI_ROOT = path.resolve(
|
|
29
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
30
|
+
'..',
|
|
31
|
+
);
|
|
32
|
+
const BIN = path.join(CLI_ROOT, 'clients', 'cli', 'bin', 'astryx.mjs');
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The process, seen as the holder of one mark: whether a hosted run started in
|
|
36
|
+
* it, whichever copy of the CLI started it.
|
|
37
|
+
*/
|
|
38
|
+
const processMarks = /** @type {{__astryxHostRunStarted?: boolean}} */ (
|
|
39
|
+
/** @type {unknown} */ (process)
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
/** The longest reason a host may give for not running the project's CLI. */
|
|
43
|
+
const MAX_REASON_LENGTH = 120;
|
|
44
|
+
|
|
45
|
+
const OPTION_NAMES = new Set([
|
|
46
|
+
'argv',
|
|
47
|
+
'cwd',
|
|
48
|
+
'cliCommand',
|
|
49
|
+
'declinedProjectCli',
|
|
50
|
+
'roots',
|
|
51
|
+
'integrations',
|
|
52
|
+
'discoverSources',
|
|
53
|
+
]);
|
|
54
|
+
|
|
55
|
+
/** A bare npm package name, scoped or not. */
|
|
56
|
+
const PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._~-]*\/)?[a-z0-9][a-z0-9._~-]*$/u;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @param {string} message
|
|
60
|
+
* @returns {TypeError}
|
|
61
|
+
*/
|
|
62
|
+
function invalid(message) {
|
|
63
|
+
return new TypeError(`runHostedCli: ${message}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Check every input before anything runs.
|
|
68
|
+
* @param {unknown} options
|
|
69
|
+
* @returns {Promise<{argv: string[], cwd: string, cliCommand: string | undefined, declinedProjectCli: string | undefined, roots: string[], integrations: string[], discoverSources: Function[]}>}
|
|
70
|
+
*/
|
|
71
|
+
async function readOptions(options) {
|
|
72
|
+
if (options === undefined) options = {};
|
|
73
|
+
if (
|
|
74
|
+
options === null ||
|
|
75
|
+
typeof options !== 'object' ||
|
|
76
|
+
Array.isArray(options)
|
|
77
|
+
) {
|
|
78
|
+
throw invalid('options must be an object');
|
|
79
|
+
}
|
|
80
|
+
const record = /** @type {Record<string, unknown>} */ (options);
|
|
81
|
+
for (const key of Object.keys(record)) {
|
|
82
|
+
if (!OPTION_NAMES.has(key)) {
|
|
83
|
+
throw invalid(
|
|
84
|
+
`unknown option "${key}"; the options are ${[...OPTION_NAMES].join(', ')}`,
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const argv = record.argv ?? [];
|
|
90
|
+
if (!Array.isArray(argv) || !argv.every(arg => typeof arg === 'string')) {
|
|
91
|
+
throw invalid('argv must be an array of strings');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const cwdInput = record.cwd ?? process.cwd();
|
|
95
|
+
if (typeof cwdInput !== 'string' || cwdInput === '') {
|
|
96
|
+
throw invalid('cwd must be a folder path');
|
|
97
|
+
}
|
|
98
|
+
const cwd = path.resolve(cwdInput);
|
|
99
|
+
let isFolder = false;
|
|
100
|
+
try {
|
|
101
|
+
isFolder = fs.statSync(cwd).isDirectory();
|
|
102
|
+
} catch {
|
|
103
|
+
isFolder = false;
|
|
104
|
+
}
|
|
105
|
+
if (!isFolder) throw invalid(`cwd is not a folder: ${cwd}`);
|
|
106
|
+
|
|
107
|
+
let cliCommand;
|
|
108
|
+
if (record.cliCommand !== undefined) {
|
|
109
|
+
const {parseCliCommandField} =
|
|
110
|
+
await import('../authoring/integration/schema.mjs');
|
|
111
|
+
try {
|
|
112
|
+
cliCommand = parseCliCommandField(record.cliCommand, 'cliCommand');
|
|
113
|
+
} catch (err) {
|
|
114
|
+
throw invalid(err instanceof Error ? err.message : String(err));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const declinedProjectCli = record.declinedProjectCli;
|
|
119
|
+
if (
|
|
120
|
+
declinedProjectCli !== undefined &&
|
|
121
|
+
(typeof declinedProjectCli !== 'string' ||
|
|
122
|
+
declinedProjectCli === '' ||
|
|
123
|
+
declinedProjectCli !== declinedProjectCli.trim() ||
|
|
124
|
+
/[\r\n]/u.test(declinedProjectCli) ||
|
|
125
|
+
declinedProjectCli.length > MAX_REASON_LENGTH)
|
|
126
|
+
) {
|
|
127
|
+
throw invalid(
|
|
128
|
+
`declinedProjectCli must be a non-blank single line of at most ${MAX_REASON_LENGTH} characters, with no surrounding spaces`,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const rootsInput = record.roots ?? [];
|
|
133
|
+
if (
|
|
134
|
+
!Array.isArray(rootsInput) ||
|
|
135
|
+
!rootsInput.every(root => typeof root === 'string' && root !== '')
|
|
136
|
+
) {
|
|
137
|
+
throw invalid('roots must be an array of folder paths');
|
|
138
|
+
}
|
|
139
|
+
/** @type {string[]} */
|
|
140
|
+
const roots = [];
|
|
141
|
+
for (const root of rootsInput) {
|
|
142
|
+
const folder = path.resolve(root);
|
|
143
|
+
let isRoot = false;
|
|
144
|
+
try {
|
|
145
|
+
isRoot = fs.statSync(folder).isDirectory();
|
|
146
|
+
} catch {
|
|
147
|
+
isRoot = false;
|
|
148
|
+
}
|
|
149
|
+
if (!isRoot) {
|
|
150
|
+
throw invalid(`roots names a path that is not a folder: ${folder}`);
|
|
151
|
+
}
|
|
152
|
+
if (!roots.includes(folder)) roots.push(folder);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const integrations = record.integrations ?? [];
|
|
156
|
+
if (
|
|
157
|
+
!Array.isArray(integrations) ||
|
|
158
|
+
!integrations.every(
|
|
159
|
+
name => typeof name === 'string' && PACKAGE_NAME.test(name),
|
|
160
|
+
)
|
|
161
|
+
) {
|
|
162
|
+
throw invalid('integrations must be an array of package names');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const discoverSources = record.discoverSources ?? [];
|
|
166
|
+
if (
|
|
167
|
+
!Array.isArray(discoverSources) ||
|
|
168
|
+
!discoverSources.every(source => typeof source === 'function')
|
|
169
|
+
) {
|
|
170
|
+
throw invalid('discoverSources must be an array of functions');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
argv: [...argv],
|
|
175
|
+
cwd,
|
|
176
|
+
cliCommand,
|
|
177
|
+
declinedProjectCli: /** @type {string | undefined} */ (declinedProjectCli),
|
|
178
|
+
roots,
|
|
179
|
+
integrations: [...new Set(/** @type {string[]} */ (integrations))],
|
|
180
|
+
discoverSources: [...discoverSources],
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* The bin's Node version gate, for a host process: same message, same exit
|
|
186
|
+
* status, same JSON envelope.
|
|
187
|
+
* @param {string[]} argv
|
|
188
|
+
* @returns {Promise<boolean>} whether the run may go on
|
|
189
|
+
*/
|
|
190
|
+
async function nodeVersionGate(argv) {
|
|
191
|
+
const {isNodeVersionSupported, unsupportedNodeMessage} =
|
|
192
|
+
await import('../foundation/env/node-version.mjs');
|
|
193
|
+
if (isNodeVersionSupported(process.versions.node)) return true;
|
|
194
|
+
const msg = unsupportedNodeMessage(process.versions.node);
|
|
195
|
+
if (argv.includes('--json')) {
|
|
196
|
+
process.stdout.write(
|
|
197
|
+
`${JSON.stringify({apiVersion: 1, error: msg, code: 'ERR_NODE_VERSION'}, null, 2)}\n`,
|
|
198
|
+
);
|
|
199
|
+
} else {
|
|
200
|
+
process.stderr.write(`${msg}\n`);
|
|
201
|
+
}
|
|
202
|
+
process.exitCode = 1;
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Load another release's host entry, from one of the host's package sets, to
|
|
208
|
+
* hand this run over to in this process (architecture:cli-surface INV30). The
|
|
209
|
+
* run stays one call of the host's: the one-call mark passes to the entry that
|
|
210
|
+
* answers, and the call settles with that entry's result.
|
|
211
|
+
* @param {string} hostEntry the package set's `host/index.mjs`
|
|
212
|
+
* @returns {Promise<(options: import('./index.js').HostedCliOptions) => Promise<import('./index.js').HostedCliResult>>}
|
|
213
|
+
*/
|
|
214
|
+
async function loadHostEntry(hostEntry) {
|
|
215
|
+
const entry = await import(pathToFileURL(hostEntry).href);
|
|
216
|
+
if (typeof entry.runHostedCli !== 'function') {
|
|
217
|
+
throw new Error(`${hostEntry} exports no runHostedCli`);
|
|
218
|
+
}
|
|
219
|
+
return options => {
|
|
220
|
+
processMarks.__astryxHostRunStarted = false;
|
|
221
|
+
return entry.runHostedCli(options);
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Run the CLI once in this process, as the `astryx` bin would run with the
|
|
227
|
+
* same arguments in the same folder, for a tool that carries this CLI.
|
|
228
|
+
*
|
|
229
|
+
* @param {import('./index.js').HostedCliOptions} [options]
|
|
230
|
+
* @returns {Promise<import('./index.js').HostedCliResult>}
|
|
231
|
+
*/
|
|
232
|
+
export async function runHostedCli(options) {
|
|
233
|
+
const input = await readOptions(options);
|
|
234
|
+
if (processMarks.__astryxHostRunStarted) {
|
|
235
|
+
throw new Error(
|
|
236
|
+
'runHostedCli: a run already started in this process; start each run in a new process',
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
processMarks.__astryxHostRunStarted = true;
|
|
240
|
+
|
|
241
|
+
process.chdir(input.cwd);
|
|
242
|
+
process.argv = [process.argv[0] ?? process.execPath, BIN, ...input.argv];
|
|
243
|
+
if (!(await nodeVersionGate(input.argv))) return {status: 1, packages: []};
|
|
244
|
+
|
|
245
|
+
const {installPlaceOf, projectRootOf} =
|
|
246
|
+
await import('../foundation/release/choose.mjs');
|
|
247
|
+
const {readReleaseFacts} = await import('../foundation/release/project.mjs');
|
|
248
|
+
const {activateHostedRun, recordResolved, resolvedPackages} =
|
|
249
|
+
await import('../foundation/runtime/package-root.mjs');
|
|
250
|
+
const real = fs.realpathSync(CLI_ROOT);
|
|
251
|
+
let projectRoot = null;
|
|
252
|
+
try {
|
|
253
|
+
projectRoot = projectRootOf(readReleaseFacts(input.cwd));
|
|
254
|
+
} catch {
|
|
255
|
+
projectRoot = null;
|
|
256
|
+
}
|
|
257
|
+
activateHostedRun({
|
|
258
|
+
roots: input.roots,
|
|
259
|
+
integrations: input.integrations,
|
|
260
|
+
discoverSources: input.discoverSources,
|
|
261
|
+
// The root that holds this CLI, or, for a CLI that is not installed in a
|
|
262
|
+
// node_modules folder, the folder beside it that holds its Core.
|
|
263
|
+
startingRoot: installPlaceOf(real).owner ?? path.dirname(real),
|
|
264
|
+
projectRoot,
|
|
265
|
+
loadHostEntry,
|
|
266
|
+
});
|
|
267
|
+
recordResolved(
|
|
268
|
+
'@astryxdesign/cli',
|
|
269
|
+
JSON.parse(fs.readFileSync(path.join(CLI_ROOT, 'package.json'), 'utf8'))
|
|
270
|
+
.version,
|
|
271
|
+
CLI_ROOT,
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
const {runCli} = await import('../clients/cli/run.mjs');
|
|
275
|
+
const run = () =>
|
|
276
|
+
runCli({
|
|
277
|
+
cliRoot: CLI_ROOT,
|
|
278
|
+
argv: [...input.argv],
|
|
279
|
+
declinedProjectCli: input.declinedProjectCli,
|
|
280
|
+
});
|
|
281
|
+
let answered;
|
|
282
|
+
if (input.cliCommand === undefined) {
|
|
283
|
+
answered = await run();
|
|
284
|
+
} else {
|
|
285
|
+
const {runWithCliDoor} = await import('../foundation/env/cli-command.mjs');
|
|
286
|
+
answered = await runWithCliDoor(input.cliCommand, run);
|
|
287
|
+
}
|
|
288
|
+
// Another package set's host entry answered the run (INV30).
|
|
289
|
+
if (answered) return answered;
|
|
290
|
+
const code = process.exitCode;
|
|
291
|
+
return {
|
|
292
|
+
status: typeof code === 'number' ? code : Number(code ?? 0) || 0,
|
|
293
|
+
packages: resolvedPackages(),
|
|
294
|
+
};
|
|
295
|
+
}
|