@loomcli/core 0.4.0 → 0.5.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/LICENSE +21 -0
- package/dist/application.d.ts +44 -31
- package/dist/application.js +122 -110
- package/dist/bindings.d.ts +26 -0
- package/dist/bindings.js +45 -0
- package/dist/chain.d.ts +11 -6
- package/dist/chain.js +28 -81
- package/dist/command.d.ts +161 -84
- package/dist/command.js +650 -349
- package/dist/errors.d.ts +7 -2
- package/dist/errors.js +9 -1
- package/dist/extension.d.ts +3 -1
- package/dist/extension.js +8 -11
- package/dist/facts.d.ts +5 -0
- package/dist/facts.js +10 -3
- package/dist/globals.d.ts +45 -12
- package/dist/globals.js +74 -18
- package/dist/index.d.ts +4 -2
- package/dist/index.js +1 -0
- package/dist/inspect.d.ts +25 -2
- package/dist/inspect.js +43 -5
- package/dist/locate.d.ts +41 -0
- package/dist/locate.js +121 -0
- package/dist/options.d.ts +73 -0
- package/dist/options.js +124 -27
- package/dist/output.d.ts +2 -0
- package/dist/output.js +5 -1
- package/dist/plugin.d.ts +107 -53
- package/dist/plugin.js +204 -66
- package/dist/sources.d.ts +56 -0
- package/dist/sources.js +249 -0
- package/dist/types.d.ts +65 -20
- package/dist/validation.d.ts +22 -8
- package/dist/validation.js +184 -77
- package/dist/view.d.ts +1 -1
- package/dist/view.js +2 -2
- package/package.json +3 -2
package/dist/sources.js
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { asSentence, InputError, InternalError, reasonOf, ResultError } from './errors.js';
|
|
2
|
+
import { isPlainObject, isProseLine } from './facts.js';
|
|
3
|
+
import { isSupplied } from './options.js';
|
|
4
|
+
import { loadDefault, pluginSentence, pluginValues } from './plugin.js';
|
|
5
|
+
/** The Boolean grammar a bound variable is read through: the whole value, case-insensitive. */
|
|
6
|
+
const truths = new Map([
|
|
7
|
+
['0', false],
|
|
8
|
+
['1', true],
|
|
9
|
+
['false', false],
|
|
10
|
+
['true', true],
|
|
11
|
+
]);
|
|
12
|
+
/**
|
|
13
|
+
* Reads one option's bound variable. An empty variable is unset and falls through, a string option
|
|
14
|
+
* receives the raw string, and a Boolean option reads the whole value through the grammar, so the
|
|
15
|
+
* value states the option's value and not a spelling.
|
|
16
|
+
*/
|
|
17
|
+
function readVariable(input, env) {
|
|
18
|
+
const variable = input.config.env;
|
|
19
|
+
const raw = variable !== undefined && Object.hasOwn(env, variable) ? env[variable] : undefined;
|
|
20
|
+
if (raw === undefined || raw === '') {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
if (input.config.type === 'string') {
|
|
24
|
+
return { kind: 'fill', value: raw };
|
|
25
|
+
}
|
|
26
|
+
const value = truths.get(raw.toLowerCase());
|
|
27
|
+
return value === undefined ? { kind: 'rejected' } : { kind: 'fill', value };
|
|
28
|
+
}
|
|
29
|
+
/** Writes one filled value where the option's own shape keeps it, a list as the run's own copy. */
|
|
30
|
+
function fill(values, name, value) {
|
|
31
|
+
if (typeof value === 'boolean') {
|
|
32
|
+
values.booleans.set(name, value);
|
|
33
|
+
}
|
|
34
|
+
else if (typeof value === 'string') {
|
|
35
|
+
values.strings.set(name, value);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
values.lists.set(name, [...value]);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** How a fault names the value an answer owed, by the shape the option takes. */
|
|
42
|
+
const owed = {
|
|
43
|
+
boolean: 'a Boolean',
|
|
44
|
+
list: 'an array of strings',
|
|
45
|
+
string: 'a string',
|
|
46
|
+
};
|
|
47
|
+
function rawKind(input) {
|
|
48
|
+
if (input.config.type === 'boolean') {
|
|
49
|
+
return 'boolean';
|
|
50
|
+
}
|
|
51
|
+
return input.config.multiple === true ? 'list' : 'string';
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The run's own copy of an answered list, or `undefined` when it is not a list of strings. Every
|
|
55
|
+
* index is read, so a hole fails the check as any entry that is not a string does.
|
|
56
|
+
*/
|
|
57
|
+
function stringList(value) {
|
|
58
|
+
if (!Array.isArray(value)) {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
const list = [];
|
|
62
|
+
// Iteration visits a hole as undefined, where every() skips it.
|
|
63
|
+
for (const entry of value) {
|
|
64
|
+
if (typeof entry !== 'string') {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
list.push(entry);
|
|
68
|
+
}
|
|
69
|
+
return list;
|
|
70
|
+
}
|
|
71
|
+
/** One answered value as the raw type its option takes, or `undefined` when it holds another. */
|
|
72
|
+
function rawValue(kind, value) {
|
|
73
|
+
if (kind === 'list') {
|
|
74
|
+
return stringList(value);
|
|
75
|
+
}
|
|
76
|
+
if (kind === 'boolean') {
|
|
77
|
+
return typeof value === 'boolean' ? value : undefined;
|
|
78
|
+
}
|
|
79
|
+
return typeof value === 'string' ? value : undefined;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* The faults the answers rule names. Reading the answers runs the plugin's own code, such as a
|
|
83
|
+
* getter, so a throw of any other kind while reading them is the plugin's failure.
|
|
84
|
+
*/
|
|
85
|
+
const ruleFaults = new WeakSet();
|
|
86
|
+
/** A fault the answers rule names, recorded so that reading the answers rethrows it unframed. */
|
|
87
|
+
function ruleFault(message) {
|
|
88
|
+
const fault = new InternalError(message, undefined);
|
|
89
|
+
ruleFaults.add(fault);
|
|
90
|
+
return fault;
|
|
91
|
+
}
|
|
92
|
+
/** One answer read under the answers rule: an object that holds a one-line label and a value. */
|
|
93
|
+
function readAnswer(sentence, input, answer) {
|
|
94
|
+
const shape = isPlainObject(answer) ? answer : undefined;
|
|
95
|
+
const label = shape?.label;
|
|
96
|
+
if (!shape || !isProseLine(label)) {
|
|
97
|
+
throw ruleFault(`${sentence} answered option "${input.name}" with an answer that is not { value, label }.`);
|
|
98
|
+
}
|
|
99
|
+
const kind = rawKind(input);
|
|
100
|
+
const value = rawValue(kind, shape.value);
|
|
101
|
+
if (value === undefined) {
|
|
102
|
+
throw ruleFault(`${sentence} answered option "${input.name}" with a value that is not ${owed[kind]}.`);
|
|
103
|
+
}
|
|
104
|
+
return { label, value };
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Every answer the resolver returned, read before any is filled, so an answer the rule rejects
|
|
108
|
+
* leaves every requested option unfilled. A key core did not request is the source's fault.
|
|
109
|
+
*/
|
|
110
|
+
function readAnswers(sentence, answers, requested) {
|
|
111
|
+
if (!isPlainObject(answers)) {
|
|
112
|
+
throw ruleFault(`${sentence} returned configuration answers that are not a record.`);
|
|
113
|
+
}
|
|
114
|
+
const byName = new Map(requested.map((target) => [target.input.name, target]));
|
|
115
|
+
return Object.entries(answers).map(([name, answer]) => {
|
|
116
|
+
const target = byName.get(name);
|
|
117
|
+
if (!target) {
|
|
118
|
+
throw ruleFault(`${sentence} answered option "${name}", which core did not request.`);
|
|
119
|
+
}
|
|
120
|
+
const { label, value } = readAnswer(sentence, target.input, answer);
|
|
121
|
+
return { label, target, value };
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* The default export a source loader must resolve to. A loaded module is data core never declared,
|
|
126
|
+
* so the check is the one runtime fact that decides it: the export is callable.
|
|
127
|
+
*/
|
|
128
|
+
function isResolverExport(value) {
|
|
129
|
+
return typeof value === 'function';
|
|
130
|
+
}
|
|
131
|
+
/** The fault of a source that threw, while it ran or while core read what it returned. */
|
|
132
|
+
function sourceFailure(sentence, error) {
|
|
133
|
+
return new InternalError(`${sentence} failed in its configuration source: ${asSentence(reasonOf(error))}`, error);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Asks the one configuration source about every requested option, and answers with what it said.
|
|
137
|
+
* Core loads the source here and calls it once, awaiting it; one cancelled during the call awaits
|
|
138
|
+
* it. The run checks its signal and then reaches the load with no await between, so a run
|
|
139
|
+
* cancelled before the load starts neither step, and one cancelled during the load calls nothing.
|
|
140
|
+
* Core builds the context before the call, so a fault of its own is never the plugin's.
|
|
141
|
+
*/
|
|
142
|
+
async function askSource(stage, call) {
|
|
143
|
+
const { owner, requested } = call;
|
|
144
|
+
const resolver = await loadDefault(owner.identity, owner.source.load, {
|
|
145
|
+
guard: isResolverExport,
|
|
146
|
+
noun: 'source',
|
|
147
|
+
});
|
|
148
|
+
if (stage.signal.aborted) {
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
const sentence = pluginSentence(owner.identity);
|
|
152
|
+
const context = {
|
|
153
|
+
graph: stage.inspected(),
|
|
154
|
+
host: stage.host,
|
|
155
|
+
options: pluginValues(owner.inputs, stage.globals.values),
|
|
156
|
+
out: stage.out,
|
|
157
|
+
requests: requested.map(({ input, scope }) => stage.request(input, scope.global)),
|
|
158
|
+
style: stage.style,
|
|
159
|
+
};
|
|
160
|
+
let answers = undefined;
|
|
161
|
+
try {
|
|
162
|
+
answers = await resolver(context);
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
// The resolver's own InputError is a usage failure.
|
|
166
|
+
// Its out.results() call is the results fault that names it.
|
|
167
|
+
// Every other throw is the plugin's fault.
|
|
168
|
+
if (error instanceof InputError || (error instanceof ResultError && error.kind === 'source')) {
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
throw sourceFailure(sentence, error);
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
return readAnswers(sentence, answers, requested);
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
if (error instanceof InternalError && ruleFaults.has(error)) {
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
throw sourceFailure(sentence, error);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* The environment tier: each option in scope that argv left unfilled reads its bound variable. A
|
|
185
|
+
* filled option is labeled with its variable, and a Boolean one outside the grammar is recorded
|
|
186
|
+
* as rejected, which fills nothing.
|
|
187
|
+
*/
|
|
188
|
+
function fillFromEnvironment(scopes, env) {
|
|
189
|
+
const labels = new Map();
|
|
190
|
+
const rejected = new Map();
|
|
191
|
+
for (const scope of scopes) {
|
|
192
|
+
for (const input of scope.inputs) {
|
|
193
|
+
const reading = isSupplied(scope.values, input.name) ? undefined : readVariable(input, env);
|
|
194
|
+
const variable = input.config.env ?? '';
|
|
195
|
+
if (reading?.kind === 'fill') {
|
|
196
|
+
fill(scope.values, input.name, reading.value);
|
|
197
|
+
labels.set(input.name, variable);
|
|
198
|
+
}
|
|
199
|
+
else if (reading?.kind === 'rejected') {
|
|
200
|
+
rejected.set(input.name, variable);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return { labels, rejected };
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* The options the configuration source is asked about: those still unfilled that carry its
|
|
208
|
+
* binding and hold no environment fault, the globals table first and then the routed Command's own,
|
|
209
|
+
* each in declaration order.
|
|
210
|
+
*/
|
|
211
|
+
function requestedOf(scopes, excluded, bound) {
|
|
212
|
+
return scopes.flatMap((scope) => scope.inputs
|
|
213
|
+
.filter((input) => !isSupplied(scope.values, input.name) &&
|
|
214
|
+
!excluded.has(input.name) &&
|
|
215
|
+
Object.hasOwn(bound.extensions.get(input) ?? {}, bound.binding))
|
|
216
|
+
.map((input) => ({ input, scope })));
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* The input-source stage: the environment, then the configuration source, for every option in
|
|
220
|
+
* scope that argv left unfilled. When no option is left to ask, the source never loads. A source
|
|
221
|
+
* fault stops the stage and fills nothing more.
|
|
222
|
+
*/
|
|
223
|
+
async function fillInputs(stage) {
|
|
224
|
+
const scopes = stage.locals ? [stage.globals, stage.locals] : [stage.globals];
|
|
225
|
+
const { labels, rejected } = fillFromEnvironment(scopes, stage.host.env);
|
|
226
|
+
const owner = stage.plugins.find((installed) => installed.source !== undefined);
|
|
227
|
+
const requested = owner
|
|
228
|
+
? requestedOf(scopes, rejected, { binding: owner.source.binding, extensions: stage.extensions })
|
|
229
|
+
: [];
|
|
230
|
+
if (!owner || requested.length === 0) {
|
|
231
|
+
return { fault: undefined, labels, rejected };
|
|
232
|
+
}
|
|
233
|
+
try {
|
|
234
|
+
for (const { label, target, value } of await askSource(stage, { owner, requested })) {
|
|
235
|
+
fill(target.scope.values, target.input.name, value);
|
|
236
|
+
labels.set(target.input.name, label);
|
|
237
|
+
}
|
|
238
|
+
return { fault: undefined, labels, rejected };
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
// The resolver's InputError reports as a usage failure.
|
|
242
|
+
// Every other fault above is raised as an internal error, and anything else is wrapped the same way.
|
|
243
|
+
const fault = error instanceof InternalError || error instanceof InputError
|
|
244
|
+
? error
|
|
245
|
+
: new InternalError(reasonOf(error), error);
|
|
246
|
+
return { fault, labels, rejected };
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
export { fillInputs };
|
package/dist/types.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import type { Readable, Writable } from 'node:stream';
|
|
3
3
|
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
4
4
|
import type { ExtensionValue } from './extension.js';
|
|
5
|
-
import type { ResultNode } from './inspect.js';
|
|
5
|
+
import type { CommandGraph, CommandNode, ResultNode } from './inspect.js';
|
|
6
6
|
import type { RenderingPolicy } from './rendering.js';
|
|
7
7
|
import type { ContextualStyle } from './style.js';
|
|
8
8
|
type LowercaseLetter = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z';
|
|
@@ -21,12 +21,23 @@ type Presence = {
|
|
|
21
21
|
required?: false;
|
|
22
22
|
default?: unknown;
|
|
23
23
|
};
|
|
24
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* The literal member keeps `multiple: true` exact under contextual typing, as `Presence` does.
|
|
26
|
+
* A multiple option takes its list from the configuration source, so it binds no variable.
|
|
27
|
+
*/
|
|
25
28
|
type Multiplicity = {
|
|
26
29
|
multiple: true;
|
|
27
|
-
|
|
30
|
+
env?: never;
|
|
31
|
+
} | ({
|
|
28
32
|
multiple?: false;
|
|
29
|
-
};
|
|
33
|
+
} & EnvBinding);
|
|
34
|
+
/**
|
|
35
|
+
* The environment binding: the variable the input-source stage fills the option from when argv
|
|
36
|
+
* supplies none. The declaration names it explicitly, and core derives no name.
|
|
37
|
+
*/
|
|
38
|
+
interface EnvBinding {
|
|
39
|
+
env?: string;
|
|
40
|
+
}
|
|
30
41
|
/**
|
|
31
42
|
* `validateOmitted: true` sends an omitted optional scalar to its own schema. The literal member
|
|
32
43
|
* keeps the flag exact under contextual typing, as `Multiplicity` does.
|
|
@@ -62,15 +73,22 @@ interface ArgumentExtensions {
|
|
|
62
73
|
extensions?: readonly ExtensionValue<'argument'>[];
|
|
63
74
|
}
|
|
64
75
|
/**
|
|
65
|
-
* The tokens one declaration collects before validation: one string, or
|
|
66
|
-
*
|
|
76
|
+
* The tokens one declaration collects before validation: one string, or several. A multiple option
|
|
77
|
+
* and a variadic argument collect alike, so they share this raw shape.
|
|
67
78
|
*/
|
|
68
79
|
type RawValue<Config> = Config extends {
|
|
69
80
|
multiple: true;
|
|
70
81
|
} | {
|
|
71
82
|
variadic: true;
|
|
72
83
|
} ? string[] : string;
|
|
73
|
-
|
|
84
|
+
/** A multiple option and a variadic argument pass each value through the same validator. */
|
|
85
|
+
type PerValue<Config, Value> = Config extends {
|
|
86
|
+
multiple: true;
|
|
87
|
+
} | {
|
|
88
|
+
variadic: true;
|
|
89
|
+
} ? Value[] : Value;
|
|
90
|
+
/** The validated value: the validator's output for each value, or the raw shape without one. */
|
|
91
|
+
type SchemaOutput<Config, Schema, Raw> = Schema extends StandardSchemaV1 ? PerValue<Config, StandardSchemaV1.InferOutput<Schema>> : Raw;
|
|
74
92
|
type SchemaInput<Schema> = Schema extends StandardSchemaV1 ? StandardSchemaV1.InferInput<Schema> : string;
|
|
75
93
|
/** The named key states the rule, so a rejected declaration name reads as its own diagnostic. */
|
|
76
94
|
interface LiteralNameFault {
|
|
@@ -324,19 +342,24 @@ export type ScalarArgument = Presence & Omission & Described & ArgumentExtension
|
|
|
324
342
|
validate?: StandardSchemaV1;
|
|
325
343
|
};
|
|
326
344
|
export type ArgumentConfig = VariadicArgument | ScalarArgument;
|
|
327
|
-
export type ValidatedValue<Config, Raw> = Config extends unknown ? 'validate' extends keyof Config ? SchemaOutput<Config['validate'], Raw> : Raw : never;
|
|
328
|
-
/** Keep each conditional declaration paired with its own
|
|
345
|
+
export type ValidatedValue<Config, Raw> = Config extends unknown ? 'validate' extends keyof Config ? SchemaOutput<Config, Config['validate'], Raw> : Raw : never;
|
|
346
|
+
/** Keep each conditional declaration paired with its own validator input type. */
|
|
329
347
|
export type DefaultConstraint<Config> = Config extends unknown ? Config & {
|
|
330
|
-
default?: 'validate' extends keyof Config ? SchemaInput<Config['validate']
|
|
348
|
+
default?: 'validate' extends keyof Config ? PerValue<Config, SchemaInput<Config['validate']>> : RawValue<Config>;
|
|
331
349
|
} : never;
|
|
332
350
|
/**
|
|
333
|
-
* A multiple option
|
|
334
|
-
* `string
|
|
351
|
+
* A multiple option or a variadic argument passes each value to its validator alone, so the
|
|
352
|
+
* declared validator must accept one `string`. The key names the fault, the way the other
|
|
353
|
+
* declaration constraints do.
|
|
335
354
|
*/
|
|
336
|
-
export type
|
|
355
|
+
export type PerValueConstraint<Config> = Config extends {
|
|
337
356
|
multiple: true;
|
|
338
|
-
}
|
|
339
|
-
|
|
357
|
+
} | {
|
|
358
|
+
variadic: true;
|
|
359
|
+
} ? 'validate' extends keyof Config ? [
|
|
360
|
+
SchemaInput<Config['validate']>
|
|
361
|
+
] extends [never] ? unknown : string extends SchemaInput<Config['validate']> ? unknown : {
|
|
362
|
+
'A validator of several values must accept one string input': Config['validate'];
|
|
340
363
|
} : unknown : unknown;
|
|
341
364
|
/**
|
|
342
365
|
* The declaration rules `validateOmitted: true` needs: a schema that accepts `undefined`, an
|
|
@@ -362,12 +385,28 @@ export type ValidateOmittedConstraint<Config> = Config extends {
|
|
|
362
385
|
} | {
|
|
363
386
|
variadic: true;
|
|
364
387
|
} ? {
|
|
365
|
-
'
|
|
388
|
+
'An input of several values receives no values as an empty array': never;
|
|
366
389
|
} : 'validate' extends keyof Config ? undefined extends SchemaInput<Config['validate']> ? unknown : {
|
|
367
|
-
'A validateOmitted
|
|
390
|
+
'A validateOmitted validator must accept an undefined input': Config['validate'];
|
|
368
391
|
} : {
|
|
369
|
-
'validateOmitted needs a
|
|
392
|
+
'validateOmitted needs a validator to receive the omission': never;
|
|
370
393
|
} : unknown;
|
|
394
|
+
/**
|
|
395
|
+
* A global option declares no presence rule, so its omission is always plain absence. A union
|
|
396
|
+
* config fails when any member declares the key, and a wide `OptionConfig` passes, because its
|
|
397
|
+
* members only allow the key.
|
|
398
|
+
*/
|
|
399
|
+
export type GlobalOmissionConstraint<Config> = [Extract<Config, {
|
|
400
|
+
required: unknown;
|
|
401
|
+
}>] extends [
|
|
402
|
+
never
|
|
403
|
+
] ? [Extract<Config, {
|
|
404
|
+
validateOmitted: unknown;
|
|
405
|
+
}>] extends [never] ? unknown : {
|
|
406
|
+
'A global option declares no validateOmitted; its omission is plain absence': never;
|
|
407
|
+
} : {
|
|
408
|
+
'A global option declares no required; the Commands that read it check for it': never;
|
|
409
|
+
};
|
|
371
410
|
export type ArgumentValue<Config extends ArgumentConfig> = Config extends {
|
|
372
411
|
variadic: true;
|
|
373
412
|
} ? ValidatedValue<Config, string[]> : ValidatedValue<Config, string> | (Config extends {
|
|
@@ -377,7 +416,7 @@ export type ArgumentValue<Config extends ArgumentConfig> = Config extends {
|
|
|
377
416
|
} | {
|
|
378
417
|
validateOmitted: true;
|
|
379
418
|
} ? never : undefined);
|
|
380
|
-
export type BooleanOption = (OptionSpelling & Described & Listed & OptionExtensions & {
|
|
419
|
+
export type BooleanOption = (OptionSpelling & Described & Listed & EnvBinding & OptionExtensions & {
|
|
381
420
|
type: 'boolean';
|
|
382
421
|
validate?: never;
|
|
383
422
|
default?: never;
|
|
@@ -385,7 +424,7 @@ export type BooleanOption = (OptionSpelling & Described & Listed & OptionExtensi
|
|
|
385
424
|
required?: never;
|
|
386
425
|
validateOmitted?: never;
|
|
387
426
|
polarity?: 'positive' | 'negative';
|
|
388
|
-
}) | (Described & Listed & OptionExtensions & {
|
|
427
|
+
}) | (Described & Listed & EnvBinding & OptionExtensions & {
|
|
389
428
|
type: 'boolean';
|
|
390
429
|
validate?: never;
|
|
391
430
|
default?: never;
|
|
@@ -422,10 +461,16 @@ export type OptionValue<Config extends OptionConfig> = Config extends StringOpti
|
|
|
422
461
|
} | {
|
|
423
462
|
validateOmitted: true;
|
|
424
463
|
} ? never : undefined) : boolean;
|
|
464
|
+
/**
|
|
465
|
+
* What an action receives. `graph` is the frozen graph `inspect()` returns for this run, and
|
|
466
|
+
* `command` is the routed node inside it: the same two values the run's middleware receive.
|
|
467
|
+
*/
|
|
425
468
|
export interface ActionContext<Args, Options = {}, Result = unknown> {
|
|
426
469
|
readonly style: ContextualStyle;
|
|
427
470
|
args: Args;
|
|
428
471
|
options: Options;
|
|
472
|
+
readonly graph: CommandGraph;
|
|
473
|
+
readonly command: CommandNode;
|
|
429
474
|
passthrough: string[];
|
|
430
475
|
out: Out<Result>;
|
|
431
476
|
host: Host;
|
package/dist/validation.d.ts
CHANGED
|
@@ -24,6 +24,14 @@ export interface ScopedInputs {
|
|
|
24
24
|
globals: readonly InputDeclaration[];
|
|
25
25
|
locals: readonly InputDeclaration[];
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* What the input-source stage leaves for validation's messages, by option name: the label of each
|
|
29
|
+
* value it filled, and the variable of each Boolean option whose value is outside the grammar.
|
|
30
|
+
*/
|
|
31
|
+
interface Provenance {
|
|
32
|
+
labels: ReadonlyMap<string, string>;
|
|
33
|
+
rejected: ReadonlyMap<string, string>;
|
|
34
|
+
}
|
|
27
35
|
/** The raw tokens one invocation collected, keyed by declaration and by option name. */
|
|
28
36
|
interface SuppliedValues {
|
|
29
37
|
args: ReadonlyMap<InputDeclaration, string | string[]>;
|
|
@@ -36,8 +44,14 @@ export interface Invocation {
|
|
|
36
44
|
host: Host;
|
|
37
45
|
inputs: ScopedInputs;
|
|
38
46
|
passthrough: readonly string[];
|
|
39
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* The plugin options, which are never validated. A Boolean one whose variable is outside the
|
|
49
|
+
* grammar is still a problem of this phase, reported after the globals and before the locals.
|
|
50
|
+
*/
|
|
51
|
+
plugins: readonly OptionInput[];
|
|
52
|
+
/** The run's cancellation signal, which stops this phase between two validator calls. */
|
|
40
53
|
signal: AbortSignal;
|
|
54
|
+
sources: Provenance;
|
|
41
55
|
supplied: SuppliedValues;
|
|
42
56
|
}
|
|
43
57
|
/**
|
|
@@ -66,7 +80,7 @@ export type { ValidatedInputs };
|
|
|
66
80
|
*/
|
|
67
81
|
export declare function captureConfig<Config extends ArgumentConfig | OptionConfig>(config: Config): Config;
|
|
68
82
|
/**
|
|
69
|
-
* The declaration flag that sends an omitted value to its own
|
|
83
|
+
* The declaration flag that sends an omitted value to its own validator. Every declaration reads it
|
|
70
84
|
* here, and the declaration rules below reject it wherever another rule already decides absence.
|
|
71
85
|
*/
|
|
72
86
|
export declare function validatesOmission(input: InputDeclaration): boolean;
|
|
@@ -77,16 +91,16 @@ export declare function validatesOmission(input: InputDeclaration): boolean;
|
|
|
77
91
|
*/
|
|
78
92
|
export declare function issuePath(issue: StandardSchemaV1.Issue): string | undefined;
|
|
79
93
|
/**
|
|
80
|
-
* Every declaration rule that reads the declaration alone. It is synchronous, so
|
|
81
|
-
*
|
|
82
|
-
* can be asynchronous, is left to `run()`. A
|
|
83
|
-
*
|
|
84
|
-
* declaration itself.
|
|
94
|
+
* Every declaration rule that reads the declaration alone. It is synchronous, so the call that
|
|
95
|
+
* declares an input applies it, and build applies it to an input a lifecycle hook declared; only
|
|
96
|
+
* validating a default through its validator, which can be asynchronous, is left to `run()`. A
|
|
97
|
+
* contributor that declares under its own name, such as a plugin, supplies the subject its
|
|
98
|
+
* diagnostics read with; every other caller is named by the declaration itself.
|
|
85
99
|
*/
|
|
86
100
|
export declare function checkDeclarations(inputs: readonly InputDeclaration[], named?: string): void;
|
|
87
101
|
/**
|
|
88
102
|
* Every declared default, validated before any token is read. The host is captured by then, so a
|
|
89
|
-
* default's
|
|
103
|
+
* default's validator reads the same Host its action will, under the `default` phase.
|
|
90
104
|
*/
|
|
91
105
|
export declare function prepareInputs(inputs: ScopedInputs, host: Host): Promise<DefaultValues>;
|
|
92
106
|
export declare function validateValues(invocation: Invocation): Promise<ValidatedInputs>;
|