@loomcli/core 0.1.0
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/dist/application.d.ts +72 -0
- package/dist/application.js +188 -0
- package/dist/command.d.ts +176 -0
- package/dist/command.js +452 -0
- package/dist/context.d.ts +9 -0
- package/dist/context.js +18 -0
- package/dist/errors.d.ts +158 -0
- package/dist/errors.js +261 -0
- package/dist/globals.d.ts +36 -0
- package/dist/globals.js +71 -0
- package/dist/host.d.ts +3 -0
- package/dist/host.js +37 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +6 -0
- package/dist/inspect.d.ts +61 -0
- package/dist/inspect.js +98 -0
- package/dist/options.d.ts +38 -0
- package/dist/options.js +219 -0
- package/dist/output.d.ts +52 -0
- package/dist/output.js +188 -0
- package/dist/types.d.ts +266 -0
- package/dist/types.js +1 -0
- package/dist/validation.d.ts +83 -0
- package/dist/validation.js +406 -0
- package/package.json +28 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { AfterAction, AfterArgument, AfterCommand, Command, CommandMethod, CommandState } from './command.js';
|
|
2
|
+
import type { FailureRenderer } from './errors.js';
|
|
3
|
+
import type { GlobalOptions } from './globals.js';
|
|
4
|
+
import type { CommandGraph } from './inspect.js';
|
|
5
|
+
import type { Action, ArgumentConfig, ArgumentValue, declaredTypes, DeclaredTypes, DefaultConstraint, GlobalNameConstraint, MultipleConstraint, NameConstraint, ExitCode, OptionConfig, OptionValue, RunOptions, ValidateOmittedConstraint } from './types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Every authoring call an Application can publish, beside `run()` and `name`, which always remain.
|
|
8
|
+
* An Application's type state is a subset of these, and each call removes the names it invalidates.
|
|
9
|
+
* The unnamed root declares what a named Command declares, except for `alias()`: the root answers
|
|
10
|
+
* to no bare token, so it has no name to alias.
|
|
11
|
+
*/
|
|
12
|
+
export type ApplicationMethod = Exclude<CommandMethod, 'alias'>;
|
|
13
|
+
/**
|
|
14
|
+
* Everything an application configures beside its declarations: the options every Command shares,
|
|
15
|
+
* and the renderers that answer the failure classes core throws.
|
|
16
|
+
*/
|
|
17
|
+
export interface ApplicationOptions<Globals = {}> {
|
|
18
|
+
globals?: GlobalOptions<Globals>;
|
|
19
|
+
failures?: readonly FailureRenderer[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The Application holds the unnamed root's declaration state and applies the same transitions a
|
|
23
|
+
* Command does, so each declaration call has one typed implementation and no builder to recover.
|
|
24
|
+
*/
|
|
25
|
+
declare class ApplicationBuilder<Args, Options, Globals, State extends ApplicationMethod = ApplicationMethod> {
|
|
26
|
+
#private;
|
|
27
|
+
readonly [declaredTypes]: DeclaredTypes<Args, Options, Globals>;
|
|
28
|
+
constructor(name: string, root: CommandState<Args, Options, Globals>, config: {
|
|
29
|
+
failures: readonly FailureRenderer[];
|
|
30
|
+
options?: unknown;
|
|
31
|
+
});
|
|
32
|
+
get name(): string;
|
|
33
|
+
argument<const Name extends string, const Config extends ArgumentConfig>(name: Name, config: Config & NameConstraint<Name> & NoInfer<DefaultConstraint<Config>> & NoInfer<ValidateOmittedConstraint<Config>>): Application<Args & Record<Name, ArgumentValue<Config>>, Options, Globals, AfterArgument<State>>;
|
|
34
|
+
option<const Name extends string, const Config extends OptionConfig>(name: Name, config: Config & NameConstraint<Name> & GlobalNameConstraint<Name, Globals> & NoInfer<DefaultConstraint<Config>> & NoInfer<MultipleConstraint<Config>> & NoInfer<ValidateOmittedConstraint<Config>>): Application<Args, Options & Record<Name, OptionValue<Config>>, Globals, State>;
|
|
35
|
+
/** The action is the last call, so it returns `AfterAction`: only `run()` and `name` remain. */
|
|
36
|
+
action(handler: Action<Args, Globals & Options>): Application<Args, Options, Globals>;
|
|
37
|
+
/** A child arrives in any type state, because its own action is the call that finished it. */
|
|
38
|
+
command(child: Command<unknown, unknown, Globals>): Application<Args, Options, Globals, AfterCommand<State>>;
|
|
39
|
+
/**
|
|
40
|
+
* One wrapper for every declaration call, so the Application keeps its name. The next state
|
|
41
|
+
* travels through this call: each method names its transition in its return type, and the
|
|
42
|
+
* wrapper publishes the same runtime value in exactly that state.
|
|
43
|
+
*/
|
|
44
|
+
private derive;
|
|
45
|
+
/**
|
|
46
|
+
* The built graph as plain, frozen data. It applies every rule `run()` applies without a schema,
|
|
47
|
+
* in the order `run()` applies them, and throws `DeclarationError` when one fails. Validating a
|
|
48
|
+
* declared default through its schema can be asynchronous, so that one rule stays in `run()`.
|
|
49
|
+
* Nothing is cached: each call builds the graph anew.
|
|
50
|
+
*/
|
|
51
|
+
inspect(): CommandGraph;
|
|
52
|
+
run(options?: RunOptions): Promise<ExitCode>;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The authoring surface of an Application in one type state: the root Command's calls, `command()`,
|
|
56
|
+
* `inspect()`, `run()`, and `name`. Every authoring call returns a new declaration value, leaves
|
|
57
|
+
* its receiver unchanged, and publishes only the calls that are still valid after it. `inspect()`,
|
|
58
|
+
* `run()`, and `name` survive every call. `State` lists the authoring calls a value still offers.
|
|
59
|
+
* It defaults to the state after `action()`, which publishes the fewest calls, so
|
|
60
|
+
* `Application<A, O, G>` accepts an application in any state, a finished one included.
|
|
61
|
+
*/
|
|
62
|
+
export type Application<Args = {}, Options = {}, Globals = {}, State extends ApplicationMethod = AfterAction> = Pick<ApplicationBuilder<Args, Options, Globals, State>, typeof declaredTypes | 'inspect' | 'name' | 'run' | State>;
|
|
63
|
+
interface ApplicationConstructor {
|
|
64
|
+
new (name: string): Application<{}, {}, {}, ApplicationMethod>;
|
|
65
|
+
new <Globals = {}>(name: string, options: ApplicationOptions<Globals>): Application<{}, {}, Globals, ApplicationMethod>;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The public constructor takes a name and one options object. The globals type narrows to the
|
|
69
|
+
* supplied value, and the failure renderers configure the application the way its commands do.
|
|
70
|
+
*/
|
|
71
|
+
export declare const Application: ApplicationConstructor;
|
|
72
|
+
export {};
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { attachChild, buildGraph, collectInputs, declareAction, declareArgument, declareOption, freshState, selectCommand, } from './command.js';
|
|
2
|
+
import { buildFailures, DeclarationError, describeFailure, InternalError, reasonOf, toFailure, } from './errors.js';
|
|
3
|
+
import { isGlobalOptions } from './globals.js';
|
|
4
|
+
import { captureHost } from './host.js';
|
|
5
|
+
import { inspectGraph } from './inspect.js';
|
|
6
|
+
import { Output, reportPlainly } from './output.js';
|
|
7
|
+
import { captureConfig, checkDeclarations, prepareInputs } from './validation.js';
|
|
8
|
+
/** The registry a failure is reported through when the application's own could not be built. */
|
|
9
|
+
const noRegistrations = new Map();
|
|
10
|
+
/**
|
|
11
|
+
* The Application holds the unnamed root's declaration state and applies the same transitions a
|
|
12
|
+
* Command does, so each declaration call has one typed implementation and no builder to recover.
|
|
13
|
+
*/
|
|
14
|
+
class ApplicationBuilder {
|
|
15
|
+
#name;
|
|
16
|
+
#root;
|
|
17
|
+
#failures;
|
|
18
|
+
// The constructor's raw options argument stays unexamined until build.
|
|
19
|
+
// The options-slot rules answer at the same point every other authoring fault does:
|
|
20
|
+
// `inspect()` and `run()`.
|
|
21
|
+
#options;
|
|
22
|
+
constructor(name, root, config) {
|
|
23
|
+
this.#failures = config.failures;
|
|
24
|
+
this.#name = name;
|
|
25
|
+
this.#options = config.options;
|
|
26
|
+
this.#root = root;
|
|
27
|
+
}
|
|
28
|
+
get name() {
|
|
29
|
+
return this.#name;
|
|
30
|
+
}
|
|
31
|
+
argument(name, config) {
|
|
32
|
+
const input = {
|
|
33
|
+
config: captureConfig(config),
|
|
34
|
+
kind: 'argument',
|
|
35
|
+
name,
|
|
36
|
+
};
|
|
37
|
+
return this.derive(declareArgument(this.#root, input));
|
|
38
|
+
}
|
|
39
|
+
option(name, config) {
|
|
40
|
+
const input = {
|
|
41
|
+
config: captureConfig(config),
|
|
42
|
+
kind: 'option',
|
|
43
|
+
name,
|
|
44
|
+
};
|
|
45
|
+
return this.derive(declareOption(this.#root, input));
|
|
46
|
+
}
|
|
47
|
+
/** The action is the last call, so it returns `AfterAction`: only `run()` and `name` remain. */
|
|
48
|
+
action(handler) {
|
|
49
|
+
return this.derive(declareAction(this.#root, handler));
|
|
50
|
+
}
|
|
51
|
+
/** A child arrives in any type state, because its own action is the call that finished it. */
|
|
52
|
+
command(child) {
|
|
53
|
+
return this.derive(attachChild(this.#root, child));
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* One wrapper for every declaration call, so the Application keeps its name. The next state
|
|
57
|
+
* travels through this call: each method names its transition in its return type, and the
|
|
58
|
+
* wrapper publishes the same runtime value in exactly that state.
|
|
59
|
+
*/
|
|
60
|
+
derive(root) {
|
|
61
|
+
return new ApplicationBuilder(this.#name, root, {
|
|
62
|
+
failures: this.#failures,
|
|
63
|
+
options: this.#options,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The built graph as plain, frozen data. It applies every rule `run()` applies without a schema,
|
|
68
|
+
* in the order `run()` applies them, and throws `DeclarationError` when one fails. Validating a
|
|
69
|
+
* declared default through its schema can be asynchronous, so that one rule stays in `run()`.
|
|
70
|
+
* Nothing is cached: each call builds the graph anew.
|
|
71
|
+
*/
|
|
72
|
+
inspect() {
|
|
73
|
+
checkOptions(this.#options);
|
|
74
|
+
buildFailures(this.#failures);
|
|
75
|
+
const graph = buildGraph(this.#root);
|
|
76
|
+
checkDeclarations([...graph.globals.inputs, ...collectInputs(graph.root)]);
|
|
77
|
+
return inspectGraph(this.#name, graph);
|
|
78
|
+
}
|
|
79
|
+
async run(options) {
|
|
80
|
+
let stderr = process.stderr;
|
|
81
|
+
let output = undefined;
|
|
82
|
+
let code = 0;
|
|
83
|
+
let reportingFailed = false;
|
|
84
|
+
// A registry that could not be built reports through core's defaults, not through itself.
|
|
85
|
+
let registry = undefined;
|
|
86
|
+
try {
|
|
87
|
+
const overrides = options?.host;
|
|
88
|
+
stderr = overrides?.stderr ?? stderr;
|
|
89
|
+
const host = captureHost(overrides, stderr);
|
|
90
|
+
output = new Output(host);
|
|
91
|
+
checkOptions(this.#options);
|
|
92
|
+
registry = buildFailures(this.#failures);
|
|
93
|
+
const graph = buildGraph(this.#root);
|
|
94
|
+
const inputs = { globals: graph.globals.inputs, locals: collectInputs(graph.root) };
|
|
95
|
+
const defaults = await prepareInputs(inputs, host);
|
|
96
|
+
const selected = await selectCommand(graph, { defaults, host });
|
|
97
|
+
await selected.dispatch({
|
|
98
|
+
host,
|
|
99
|
+
out: output.out,
|
|
100
|
+
passthrough: selected.passthrough,
|
|
101
|
+
values: selected.values,
|
|
102
|
+
});
|
|
103
|
+
// The fault check covers the same window the write accounting covers.
|
|
104
|
+
// A render failure an unawaited helper raised is still this invocation's failure.
|
|
105
|
+
await output.settle();
|
|
106
|
+
const fault = output.fault;
|
|
107
|
+
if (fault) {
|
|
108
|
+
// The action returned, so the renderer failure is this invocation's own failure.
|
|
109
|
+
throw new InternalError(`Rendering output failed: ${reasonOf(fault.cause)}`, fault.cause);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
try {
|
|
114
|
+
const failure = toFailure(error);
|
|
115
|
+
code = failure.exitCode;
|
|
116
|
+
output ??= new Output({ stderr, stdout: process.stdout });
|
|
117
|
+
const writes = await output.settle();
|
|
118
|
+
if (writes.kind === 'ok') {
|
|
119
|
+
const report = describeFailure(registry ?? noRegistrations, failure);
|
|
120
|
+
if (report.kind === 'rendered') {
|
|
121
|
+
// The renderer already owns every byte, trailing newline included: pass it through.
|
|
122
|
+
await output.report(report.text);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
code = 1;
|
|
126
|
+
// `report.text` is core's default text, which already ends in `\n`.
|
|
127
|
+
await reportPlainly(stderr, `${report.text}Internal error: Rendering the failure failed: ${report.reason}\n`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
code = 1;
|
|
133
|
+
reportingFailed = true;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (output) {
|
|
137
|
+
const writes = await output.settle();
|
|
138
|
+
if (writes.kind === 'failed') {
|
|
139
|
+
code = 1;
|
|
140
|
+
reportingFailed = true;
|
|
141
|
+
}
|
|
142
|
+
output.dispose();
|
|
143
|
+
}
|
|
144
|
+
if (reportingFailed) {
|
|
145
|
+
await reportPlainly(stderr, 'Internal error: Could not write invocation output.\n');
|
|
146
|
+
}
|
|
147
|
+
process.exitCode = code;
|
|
148
|
+
return code;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/** The options slot holds one object literal, so a declaration that carries state is not one. */
|
|
152
|
+
function isPlainObject(value) {
|
|
153
|
+
if (value === null || typeof value !== 'object') {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
const prototype = Object.getPrototypeOf(value);
|
|
157
|
+
return prototype === Object.prototype || prototype === null;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* The second argument, read where it is supplied. The retired positional form declares its globals
|
|
161
|
+
* on a value that holds no `globals` key, so without this rule the globals vanish silently and the
|
|
162
|
+
* operator, not the author, meets the consequence as an unknown-option error.
|
|
163
|
+
*/
|
|
164
|
+
function checkOptions(options) {
|
|
165
|
+
if (isGlobalOptions(options)) {
|
|
166
|
+
throw new DeclarationError('The Application takes an options object. Supply { globals } instead of a positional GlobalOptions value.');
|
|
167
|
+
}
|
|
168
|
+
if (options !== undefined && !isPlainObject(options)) {
|
|
169
|
+
throw new DeclarationError('The Application options must be an object. Supply { globals, failures }.');
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* The runtime class behind the public constructor. It is generic so that an instance's `Globals`
|
|
174
|
+
* is the type of the value it holds, with `{}` standing in when there is none, which is what each
|
|
175
|
+
* signature of the constructor interface publishes.
|
|
176
|
+
*/
|
|
177
|
+
class ApplicationDeclaration extends ApplicationBuilder {
|
|
178
|
+
constructor(name, options) {
|
|
179
|
+
// The options slot is read defensively, never inspected: an invalid value still yields
|
|
180
|
+
// `globals` and `failures` of some kind, and `checkOptions` reports it at build instead.
|
|
181
|
+
super(name, freshState(null, options?.globals), { failures: options?.failures ?? [], options });
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* The public constructor takes a name and one options object. The globals type narrows to the
|
|
186
|
+
* supplied value, and the failure renderers configure the application the way its commands do.
|
|
187
|
+
*/
|
|
188
|
+
export const Application = ApplicationDeclaration;
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import type { BuiltGlobals, GlobalOptions } from './globals.js';
|
|
2
|
+
import { compileOptions } from './options.js';
|
|
3
|
+
import type { Action, ArgumentConfig, ArgumentValue, declaredTypes, DeclaredTypes, DefaultConstraint, GlobalNameConstraint, Host, MultipleConstraint, NameConstraint, OptionConfig, OptionValue, Out, ValidateOmittedConstraint } from './types.js';
|
|
4
|
+
import type { ArgumentInput, DefaultValues, InputDeclaration, OptionInput, ValidatedInputs } from './validation.js';
|
|
5
|
+
/** One positional slot: the declaration it fills and whether it takes the remaining tokens. */
|
|
6
|
+
export interface ArgumentSlot {
|
|
7
|
+
input: InputDeclaration;
|
|
8
|
+
required: boolean;
|
|
9
|
+
variadic: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface DispatchInput {
|
|
12
|
+
host: Host;
|
|
13
|
+
out: Out;
|
|
14
|
+
passthrough: string[];
|
|
15
|
+
values: ValidatedInputs;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* One child a bare token reaches, under its canonical name or one of its hidden aliases.
|
|
19
|
+
* `name` repeats the key `children` holds, because `BuiltCommand.name` is `string | null` for the
|
|
20
|
+
* root and the routed path a child extends holds strings alone.
|
|
21
|
+
*/
|
|
22
|
+
export interface RoutedChild {
|
|
23
|
+
command: BuiltCommand;
|
|
24
|
+
name: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A group registers no action, so its `dispatch` is `undefined` and selection rejects it.
|
|
28
|
+
* `children` is keyed by canonical name, so every candidate list and every walk of the graph reads
|
|
29
|
+
* it, and `routes` adds the hidden aliases, so routing alone resolves them.
|
|
30
|
+
*/
|
|
31
|
+
export interface BuiltCommand {
|
|
32
|
+
aliases: readonly string[];
|
|
33
|
+
arguments: readonly ArgumentSlot[];
|
|
34
|
+
children: ReadonlyMap<string, BuiltCommand>;
|
|
35
|
+
dispatch: ((input: DispatchInput) => unknown) | undefined;
|
|
36
|
+
inputs: readonly InputDeclaration[];
|
|
37
|
+
name: string | null;
|
|
38
|
+
options: ReturnType<typeof compileOptions>;
|
|
39
|
+
routes: ReadonlyMap<string, RoutedChild>;
|
|
40
|
+
}
|
|
41
|
+
/** Phantom key. It marks a Command value, so only a Command can be attached as a child. */
|
|
42
|
+
export declare const commandValue: unique symbol;
|
|
43
|
+
/**
|
|
44
|
+
* Every authoring call a Command can publish. A Command's type state is a subset of these names,
|
|
45
|
+
* and each call removes the names it invalidates. A Command attaches children at any depth, so
|
|
46
|
+
* `command()` belongs to every Command and to the unnamed root alike.
|
|
47
|
+
*/
|
|
48
|
+
export type CommandMethod = 'action' | 'alias' | 'argument' | 'command' | 'option';
|
|
49
|
+
/** One Command declares arguments or attaches children, so the first call removes the other. */
|
|
50
|
+
export type AfterArgument<State> = Exclude<State, 'command'>;
|
|
51
|
+
/** The same rule read from the other side. */
|
|
52
|
+
export type AfterCommand<State> = Exclude<State, 'argument'>;
|
|
53
|
+
/** The action is the last declaration call, so no declaration call survives it. */
|
|
54
|
+
export type AfterAction = never;
|
|
55
|
+
/** A declaration made after the action, kept in authoring order so build reports the first. */
|
|
56
|
+
type LateDeclaration = {
|
|
57
|
+
alias: string;
|
|
58
|
+
kind: 'alias';
|
|
59
|
+
} | {
|
|
60
|
+
child: object;
|
|
61
|
+
kind: 'child';
|
|
62
|
+
} | {
|
|
63
|
+
input: InputDeclaration;
|
|
64
|
+
kind: 'input';
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* The names one `alias()` call declares. Each call keeps its own group, so a call that names none,
|
|
68
|
+
* which the types reject and a JavaScript author can still write, reports as the call it is.
|
|
69
|
+
*/
|
|
70
|
+
type AliasDeclaration = readonly string[];
|
|
71
|
+
/** The attachable shape of a Command, without its inferred declaration types. */
|
|
72
|
+
interface AttachedCommand {
|
|
73
|
+
readonly name: string | null;
|
|
74
|
+
build(context: BuildContext): BuiltCommand;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* One graph build's shared state. `globals` compiles once and every Command reads it. `owners`
|
|
78
|
+
* records the name of the parent that claimed each node, so a second parent holding the same value
|
|
79
|
+
* is building a graph with more than one path to that node, not a tree. The build is a depth-first
|
|
80
|
+
* walk in attachment order, and a parent claims each child as the walk reaches it, so the first
|
|
81
|
+
* owner is the parent whose attachment the walk meets first.
|
|
82
|
+
*/
|
|
83
|
+
interface BuildContext {
|
|
84
|
+
globals: BuiltGlobals;
|
|
85
|
+
owners: Map<AttachedCommand, string | null>;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Everything one Command declaration holds. The transitions below copy it with fields replaced, and
|
|
89
|
+
* the Command and Application builders share them, so one declaration call has one implementation.
|
|
90
|
+
* Absent globals stay `undefined`, so every declaration without globals agrees on identity.
|
|
91
|
+
*/
|
|
92
|
+
export interface CommandState<Args, Options, Globals> {
|
|
93
|
+
actions: readonly Action<Args, Globals & Options>[];
|
|
94
|
+
aliases: readonly AliasDeclaration[];
|
|
95
|
+
bind: (values: ValidatedInputs) => {
|
|
96
|
+
args: Args;
|
|
97
|
+
options: Options;
|
|
98
|
+
};
|
|
99
|
+
children: readonly object[];
|
|
100
|
+
globals: GlobalOptions<Globals> | undefined;
|
|
101
|
+
inputs: readonly InputDeclaration[];
|
|
102
|
+
late: readonly LateDeclaration[];
|
|
103
|
+
name: string | null;
|
|
104
|
+
}
|
|
105
|
+
/** The state every declaration starts from. The unnamed root and each named Command share it. */
|
|
106
|
+
export declare function freshState<Globals>(name: string | null, globals: GlobalOptions<Globals> | undefined): CommandState<{}, {}, Globals>;
|
|
107
|
+
/** The declared value joins `args` under its literal name, typed by its own config. */
|
|
108
|
+
export declare function declareArgument<Args, Options, Globals, Name extends string, Config extends ArgumentConfig>(state: CommandState<Args, Options, Globals>, input: ArgumentInput<Name, Config>): CommandState<Args & Record<Name, ArgumentValue<Config>>, Options, Globals>;
|
|
109
|
+
/** The declared value joins `options` under its literal name, typed by its own config. */
|
|
110
|
+
export declare function declareOption<Args, Options, Globals, Name extends string, Config extends OptionConfig>(state: CommandState<Args, Options, Globals>, input: OptionInput<Name, Config>): CommandState<Args, Options & Record<Name, OptionValue<Config>>, Globals>;
|
|
111
|
+
/** One call's names stay one group, so the empty call the types reject still reports as one. */
|
|
112
|
+
export declare function declareAlias<Args, Options, Globals>(state: CommandState<Args, Options, Globals>, names: AliasDeclaration): CommandState<Args, Options, Globals>;
|
|
113
|
+
export declare function declareAction<Args, Options, Globals>(state: CommandState<Args, Options, Globals>, handler: Action<Args, Globals & Options>): CommandState<Args, Options, Globals>;
|
|
114
|
+
/** Attaching is a declaration call too, so the receiver keeps the children it already had. */
|
|
115
|
+
export declare function attachChild<Args, Options, Globals>(state: CommandState<Args, Options, Globals>, child: object): CommandState<Args, Options, Globals>;
|
|
116
|
+
/** Validates one declaration against the shared globals table and compiles it for dispatch. */
|
|
117
|
+
export declare function buildCommand<Args, Options, Globals>(state: CommandState<Args, Options, Globals>, context: BuildContext): BuiltCommand;
|
|
118
|
+
/** The globals table and the owners record each compile once per invocation and the whole graph shares them. */
|
|
119
|
+
export declare function buildGraph<Args, Options, Globals>(root: CommandState<Args, Options, Globals>): {
|
|
120
|
+
globals: BuiltGlobals;
|
|
121
|
+
root: BuiltCommand;
|
|
122
|
+
};
|
|
123
|
+
export declare class CommandBuilder<Args, Options, Globals, State extends CommandMethod = CommandMethod> {
|
|
124
|
+
#private;
|
|
125
|
+
readonly [commandValue]: true;
|
|
126
|
+
readonly [declaredTypes]: DeclaredTypes<Args, Options, Globals>;
|
|
127
|
+
constructor(state: CommandState<Args, Options, Globals>);
|
|
128
|
+
get name(): string | null;
|
|
129
|
+
argument<const Name extends string, const Config extends ArgumentConfig>(name: Name, config: Config & NameConstraint<Name> & NoInfer<DefaultConstraint<Config>> & NoInfer<ValidateOmittedConstraint<Config>>): Command<Args & Record<Name, ArgumentValue<Config>>, Options, Globals, AfterArgument<State>>;
|
|
130
|
+
option<const Name extends string, const Config extends OptionConfig>(name: Name, config: Config & NameConstraint<Name> & GlobalNameConstraint<Name, Globals> & NoInfer<DefaultConstraint<Config>> & NoInfer<MultipleConstraint<Config>> & NoInfer<ValidateOmittedConstraint<Config>>): Command<Args, Options & Record<Name, OptionValue<Config>>, Globals, State>;
|
|
131
|
+
/**
|
|
132
|
+
* Hidden aliases are other bare tokens that route to this Command. They invalidate no call, and
|
|
133
|
+
* the tuple rest parameter rejects a call that names none.
|
|
134
|
+
*/
|
|
135
|
+
alias(...names: [string, ...string[]]): Command<Args, Options, Globals, State>;
|
|
136
|
+
/** A child arrives in any type state, because its own action is the call that finished it. */
|
|
137
|
+
command(child: Command<unknown, unknown, Globals>): Command<Args, Options, Globals, AfterCommand<State>>;
|
|
138
|
+
/** The action is the last declaration call, so the value it returns publishes `AfterAction`. */
|
|
139
|
+
action(handler: Action<Args, Globals & Options>): Command<Args, Options, Globals>;
|
|
140
|
+
build(context: BuildContext): BuiltCommand;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* The authoring surface of a Command in one type state. Every call returns a new declaration value,
|
|
144
|
+
* leaves its receiver unchanged, and publishes only the calls that are still valid after it. The
|
|
145
|
+
* declarations themselves stay private, so no consumer can reach them. `State` lists the authoring
|
|
146
|
+
* calls a value still offers. It defaults to the state after `action()`, which publishes the fewest
|
|
147
|
+
* calls, so `Command<A, O, G>` accepts a Command in any state, a finished one included.
|
|
148
|
+
*/
|
|
149
|
+
export type Command<Args = {}, Options = {}, Globals = {}, State extends CommandMethod = AfterAction> = Pick<CommandBuilder<Args, Options, Globals, State>, typeof commandValue | typeof declaredTypes | State>;
|
|
150
|
+
interface CommandConstructor {
|
|
151
|
+
new (name: string): Command<{}, {}, {}, CommandMethod>;
|
|
152
|
+
new <Globals>(name: string, globals: GlobalOptions<Globals>): Command<{}, {}, Globals, CommandMethod>;
|
|
153
|
+
}
|
|
154
|
+
/** The public constructor requires a name and narrows the globals type to the supplied value. */
|
|
155
|
+
export declare const Command: CommandConstructor;
|
|
156
|
+
/** Every declaration in the graph, so defaults are validated before any token is read. */
|
|
157
|
+
export declare function collectInputs(command: BuiltCommand): InputDeclaration[];
|
|
158
|
+
/** Bare tokens, names or aliases, select children until a Command has none; a hyphen commits. */
|
|
159
|
+
export declare function route(root: BuiltCommand, tokens: readonly string[]): {
|
|
160
|
+
command: BuiltCommand;
|
|
161
|
+
path: string[];
|
|
162
|
+
tokens: string[];
|
|
163
|
+
};
|
|
164
|
+
/** Consumes globals, routes to a Command, then validates globals and locals in one pass. */
|
|
165
|
+
export declare function selectCommand(graph: {
|
|
166
|
+
globals: BuiltGlobals;
|
|
167
|
+
root: BuiltCommand;
|
|
168
|
+
}, invocation: {
|
|
169
|
+
defaults: DefaultValues;
|
|
170
|
+
host: Host;
|
|
171
|
+
}): Promise<{
|
|
172
|
+
dispatch: (input: DispatchInput) => unknown;
|
|
173
|
+
passthrough: string[];
|
|
174
|
+
values: ValidatedInputs;
|
|
175
|
+
}>;
|
|
176
|
+
export {};
|