@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,406 @@
|
|
|
1
|
+
import { schemaOptions } from './context.js';
|
|
2
|
+
import { DeclarationError, InputError } from './errors.js';
|
|
3
|
+
/** Every declaration in validation order: the globals first, then the reading Command's own. */
|
|
4
|
+
function scoped(inputs) {
|
|
5
|
+
return [
|
|
6
|
+
...inputs.globals.map((input) => ({ global: true, input })),
|
|
7
|
+
...inputs.locals.map((input) => ({ global: false, input })),
|
|
8
|
+
];
|
|
9
|
+
}
|
|
10
|
+
function identityOf({ global, input }) {
|
|
11
|
+
return { global, kind: input.kind, name: input.name };
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The validated values of one invocation, keyed by declaration. Only `validateValues` constructs
|
|
15
|
+
* one, and its two readers are the only places where a validated value takes its declared type, so
|
|
16
|
+
* every binder reads through them and none asserts on its own.
|
|
17
|
+
*/
|
|
18
|
+
class ValidatedInputs {
|
|
19
|
+
#values;
|
|
20
|
+
constructor(values) {
|
|
21
|
+
this.#values = values;
|
|
22
|
+
}
|
|
23
|
+
/** The one-key record this argument contributes to `args`, typed by its own config. */
|
|
24
|
+
argument(input) {
|
|
25
|
+
return this.#field(input);
|
|
26
|
+
}
|
|
27
|
+
/** The one-key record this option contributes to `options`, typed by its own config. */
|
|
28
|
+
option(input) {
|
|
29
|
+
return this.#field(input);
|
|
30
|
+
}
|
|
31
|
+
#field(input) {
|
|
32
|
+
// Last resort: no typed path exists. The map stores every validated value as `unknown`.
|
|
33
|
+
// An object literal with a generic computed key does not type as `Record<Name, _>` either.
|
|
34
|
+
// So neither the value nor the key can reach `Record<Name, Value>` without this assertion.
|
|
35
|
+
// It holds because validation stores the output the config declares under this declaration.
|
|
36
|
+
// The two callers above derive `Value` from that same config.
|
|
37
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
38
|
+
return { [input.name]: this.#values.get(input) };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Authoring's snapshot of one config. An array default is the one declared value core hands to an
|
|
43
|
+
* action as its own value, so the declaration keeps a copy and the caller keeps its array. Every
|
|
44
|
+
* other property is captured as declared, because core clones no library object.
|
|
45
|
+
*/
|
|
46
|
+
export function captureConfig(config) {
|
|
47
|
+
const value = config.default;
|
|
48
|
+
return Array.isArray(value) ? { ...config, default: [...value] } : { ...config };
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* A declaration error names the declaration, because the author reads the declaration to fix it.
|
|
52
|
+
* An argument declares and reads under one name, so the two namings differ for options alone.
|
|
53
|
+
*/
|
|
54
|
+
function declaredName(input) {
|
|
55
|
+
return input.kind === 'argument' ? `Argument "${input.name}"` : `Option "${input.name}"`;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The token an operator would type for one declaration: `--file` for an option, `-F` when the
|
|
59
|
+
* option declares `shortOnly`, and the declared name for an argument. Every input diagnostic and
|
|
60
|
+
* every reported problem names the declaration this way, so an omission and a rejected value read
|
|
61
|
+
* alike and a `shortOnly` option is never named by a long form it does not accept.
|
|
62
|
+
*/
|
|
63
|
+
function spellingOf(input) {
|
|
64
|
+
if (input.kind === 'argument') {
|
|
65
|
+
return input.name;
|
|
66
|
+
}
|
|
67
|
+
const { config } = input;
|
|
68
|
+
return config.shortOnly === true && config.short !== undefined
|
|
69
|
+
? `-${config.short}`
|
|
70
|
+
: `--${input.name}`;
|
|
71
|
+
}
|
|
72
|
+
/** An input diagnostic names the declaration by kind and by the spelling that reaches it. */
|
|
73
|
+
function suppliedName(input, spelling) {
|
|
74
|
+
return input.kind === 'argument' ? `Argument "${spelling}"` : `Option "${spelling}"`;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The default sentence for an omitted required input. An argument and an option keep the wording
|
|
78
|
+
* each phase used before omission became one problem, and a collected input asks for one value
|
|
79
|
+
* more than a scalar does.
|
|
80
|
+
*/
|
|
81
|
+
function missingMessage(input, spelling, collected) {
|
|
82
|
+
return input.kind === 'argument'
|
|
83
|
+
? `Argument "${spelling}" requires ${collected ? 'at least one value' : 'a value'}. Supply a value for "${spelling}".`
|
|
84
|
+
: `Option "${spelling}" is required. Supply ${collected ? 'at least one value' : 'a value'}.`;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* A multiple option collects its occurrences and a variadic argument collects the remaining
|
|
88
|
+
* tokens, so either one carries the whole `string[]` as its raw value.
|
|
89
|
+
*/
|
|
90
|
+
function collects(input) {
|
|
91
|
+
return input.kind === 'option' ? input.config.multiple === true : input.config.variadic === true;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* The declaration flag that sends an omitted value to its own schema. Every declaration reads it
|
|
95
|
+
* here, and the declaration rules below reject it wherever another rule already decides absence.
|
|
96
|
+
*/
|
|
97
|
+
export function validatesOmission(input) {
|
|
98
|
+
const { config } = input;
|
|
99
|
+
return 'validateOmitted' in config && config.validateOmitted;
|
|
100
|
+
}
|
|
101
|
+
/** One accessor for a supplied option value, so the collected and single shapes read alike. */
|
|
102
|
+
function suppliedOption(options, name, collected) {
|
|
103
|
+
return collected ? options.lists.get(name) : options.strings.get(name);
|
|
104
|
+
}
|
|
105
|
+
/** The copy a collected value is handed out as, because the parser's array is the action's. */
|
|
106
|
+
function copied(value) {
|
|
107
|
+
return Array.isArray(value) ? [...value] : value;
|
|
108
|
+
}
|
|
109
|
+
/** Without a schema the raw shape is the declared default's only contract. */
|
|
110
|
+
function holdsRawDefault(input) {
|
|
111
|
+
const value = input.config.default;
|
|
112
|
+
return collects(input)
|
|
113
|
+
? Array.isArray(value) && value.every((entry) => typeof entry === 'string')
|
|
114
|
+
: typeof value === 'string';
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* `validateOmitted: true` is the one way an omitted scalar reaches its schema, so every other rule
|
|
118
|
+
* that already decides absence rejects it, and the flag needs a schema to receive the omission.
|
|
119
|
+
*/
|
|
120
|
+
function checkOmissionValidation(input) {
|
|
121
|
+
const { config } = input;
|
|
122
|
+
const subject = declaredName(input);
|
|
123
|
+
if (config.required) {
|
|
124
|
+
throw new DeclarationError(`${subject} is required and declares validateOmitted. Remove validateOmitted or make the input optional.`);
|
|
125
|
+
}
|
|
126
|
+
if (hasDefault(input)) {
|
|
127
|
+
throw new DeclarationError(`${subject} declares a default and validateOmitted. Remove one; the default already fills an omitted value.`);
|
|
128
|
+
}
|
|
129
|
+
if (collects(input)) {
|
|
130
|
+
throw new DeclarationError(`${subject} collects its values and declares validateOmitted. Remove validateOmitted; an omitted collection reaches the schema as an empty array.`);
|
|
131
|
+
}
|
|
132
|
+
if (config.validate === undefined) {
|
|
133
|
+
throw new DeclarationError(`${subject} declares validateOmitted without a schema. Add validate or remove validateOmitted.`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function checkDeclaration(input) {
|
|
137
|
+
const { config } = input;
|
|
138
|
+
if (input.kind === 'option' && input.config.type === 'boolean') {
|
|
139
|
+
if ('validate' in config ||
|
|
140
|
+
'default' in config ||
|
|
141
|
+
'required' in config ||
|
|
142
|
+
'validateOmitted' in config) {
|
|
143
|
+
throw new DeclarationError(`${declaredName(input)} is Boolean. Remove validate, default, required, and validateOmitted; use polarity to control its absent value.`);
|
|
144
|
+
}
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (config.required !== undefined && typeof config.required !== 'boolean') {
|
|
148
|
+
throw new DeclarationError(`${declaredName(input)} required must be Boolean. Use true or false.`);
|
|
149
|
+
}
|
|
150
|
+
if (input.kind === 'argument' &&
|
|
151
|
+
input.config.variadic !== undefined &&
|
|
152
|
+
typeof input.config.variadic !== 'boolean') {
|
|
153
|
+
throw new DeclarationError(`${declaredName(input)} variadic must be Boolean. Use true or false.`);
|
|
154
|
+
}
|
|
155
|
+
// The test reads presence, not truth, so a declared `undefined` is a declaration to reject.
|
|
156
|
+
if ('validateOmitted' in config && typeof config.validateOmitted !== 'boolean') {
|
|
157
|
+
throw new DeclarationError(`${declaredName(input)} validateOmitted must be Boolean. Use true or false.`);
|
|
158
|
+
}
|
|
159
|
+
if (config.required && Object.hasOwn(config, 'default')) {
|
|
160
|
+
throw new DeclarationError(`${declaredName(input)} is required and declares a default. Remove the default or make the input optional.`);
|
|
161
|
+
}
|
|
162
|
+
if (validatesOmission(input)) {
|
|
163
|
+
checkOmissionValidation(input);
|
|
164
|
+
}
|
|
165
|
+
const schema = config.validate;
|
|
166
|
+
if (schema !== undefined &&
|
|
167
|
+
(schema === null ||
|
|
168
|
+
(typeof schema !== 'object' && typeof schema !== 'function') ||
|
|
169
|
+
!schema['~standard'] ||
|
|
170
|
+
schema['~standard'].version !== 1 ||
|
|
171
|
+
typeof schema['~standard'].vendor !== 'string' ||
|
|
172
|
+
typeof schema['~standard'].validate !== 'function')) {
|
|
173
|
+
throw new DeclarationError(`${declaredName(input)} validate must be a Standard Schema v1 object. Supply a compatible schema.`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
function readIssue(issue) {
|
|
177
|
+
if (issue === null || typeof issue !== 'object' || !('message' in issue)) {
|
|
178
|
+
throw new Error('The validator returned an invalid Standard Schema issue.');
|
|
179
|
+
}
|
|
180
|
+
const message = issue.message;
|
|
181
|
+
if (typeof message !== 'string') {
|
|
182
|
+
throw new Error('The validator returned an invalid Standard Schema issue message.');
|
|
183
|
+
}
|
|
184
|
+
const suppliedPath = 'path' in issue ? issue.path : undefined;
|
|
185
|
+
if (suppliedPath === undefined) {
|
|
186
|
+
return { message };
|
|
187
|
+
}
|
|
188
|
+
if (!Array.isArray(suppliedPath)) {
|
|
189
|
+
throw new Error('The validator returned an invalid Standard Schema issue path.');
|
|
190
|
+
}
|
|
191
|
+
const path = Array.from(suppliedPath, (segment) => {
|
|
192
|
+
const key = segment !== null && typeof segment === 'object' && 'key' in segment ? segment.key : segment;
|
|
193
|
+
if (typeof key !== 'string' && typeof key !== 'number' && typeof key !== 'symbol') {
|
|
194
|
+
throw new Error('The validator returned an invalid Standard Schema path key.');
|
|
195
|
+
}
|
|
196
|
+
return key;
|
|
197
|
+
});
|
|
198
|
+
return { message, path };
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* A broken validator is a fault in the declaration, whichever value reached it, so its diagnostic
|
|
202
|
+
* names the declaration. Returned issues belong to the value, so the caller names those.
|
|
203
|
+
*/
|
|
204
|
+
async function validate(input, raw, context) {
|
|
205
|
+
const schema = input.config.validate;
|
|
206
|
+
if (schema === undefined) {
|
|
207
|
+
return { value: raw };
|
|
208
|
+
}
|
|
209
|
+
try {
|
|
210
|
+
const result = await schema['~standard'].validate(raw, schemaOptions(context));
|
|
211
|
+
if (result === null || typeof result !== 'object') {
|
|
212
|
+
throw new Error('The validator returned an invalid Standard Schema result.');
|
|
213
|
+
}
|
|
214
|
+
const issues = 'issues' in result ? result.issues : undefined;
|
|
215
|
+
if (issues === undefined && 'value' in result) {
|
|
216
|
+
return { value: result.value };
|
|
217
|
+
}
|
|
218
|
+
if (!Array.isArray(issues)) {
|
|
219
|
+
throw new Error('The validator returned an invalid Standard Schema result.');
|
|
220
|
+
}
|
|
221
|
+
return { issues: Array.from(issues, readIssue) };
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
const reason = error instanceof Error ? error.message : 'Unknown validator failure.';
|
|
225
|
+
throw new DeclarationError(`${declaredName(input)} validator failed unexpectedly: ${reason} Fix the validator.`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* The dotted path an issue names inside a value, or `undefined` when the issue names the value
|
|
230
|
+
* itself. Core's default text and an application's own renderer read a position through this one
|
|
231
|
+
* helper, so a rejected item reads alike wherever its diagnostic is written.
|
|
232
|
+
*/
|
|
233
|
+
export function issuePath(issue) {
|
|
234
|
+
const path = issue.path
|
|
235
|
+
?.map((segment) => String(typeof segment === 'object' ? segment.key : segment))
|
|
236
|
+
.join('.');
|
|
237
|
+
return path === undefined || path === '' ? undefined : path;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* The issues one rejection reports. A schema that returned none still rejected the value, so the
|
|
241
|
+
* placeholder stands in for its silence. Reporting takes this list once: the reported problem
|
|
242
|
+
* carries it and the default text is derived from it, so a renderer and core read the same issues.
|
|
243
|
+
*/
|
|
244
|
+
function reported(issues) {
|
|
245
|
+
return issues.length === 0
|
|
246
|
+
? [{ message: 'The schema rejected this value without an explanation.' }]
|
|
247
|
+
: issues;
|
|
248
|
+
}
|
|
249
|
+
function messages(subject, issues) {
|
|
250
|
+
return issues.map((issue) => {
|
|
251
|
+
const path = issuePath(issue);
|
|
252
|
+
return `${subject}${path === undefined ? '' : ` at ${path}`}: ${issue.message}`;
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
/** A declared `default: undefined` is a default, so presence is the key, never the value. */
|
|
256
|
+
function hasDefault(input) {
|
|
257
|
+
return Object.hasOwn(input.config, 'default');
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Every declaration rule that reads the declaration alone. It is synchronous, so `inspect()` and
|
|
261
|
+
* `run()` apply exactly the same rules, and only validating a default through its schema, which
|
|
262
|
+
* can be asynchronous, is left to `run()`.
|
|
263
|
+
*/
|
|
264
|
+
export function checkDeclarations(inputs) {
|
|
265
|
+
for (const input of inputs) {
|
|
266
|
+
checkDeclaration(input);
|
|
267
|
+
}
|
|
268
|
+
for (const input of inputs.filter((entry) => hasDefault(entry))) {
|
|
269
|
+
if (input.config.validate === undefined && !holdsRawDefault(input)) {
|
|
270
|
+
const subject = declaredName(input);
|
|
271
|
+
throw new DeclarationError(collects(input)
|
|
272
|
+
? `${subject} default must be an array of strings without a schema. Supply a string array default.`
|
|
273
|
+
: `${subject} default must be a string without a schema. Supply a string default.`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Every declared default, validated before any token is read. The host is captured by then, so a
|
|
279
|
+
* default's schema reads the same Host its action will, under the `default` phase.
|
|
280
|
+
*/
|
|
281
|
+
export async function prepareInputs(inputs, host) {
|
|
282
|
+
const declarations = scoped(inputs);
|
|
283
|
+
checkDeclarations(declarations.map((entry) => entry.input));
|
|
284
|
+
const defaults = new Map();
|
|
285
|
+
for (const entry of declarations.filter(({ input }) => hasDefault(input))) {
|
|
286
|
+
const { input } = entry;
|
|
287
|
+
const subject = declaredName(input);
|
|
288
|
+
const result = await validate(input, input.config.default, {
|
|
289
|
+
host,
|
|
290
|
+
input: identityOf(entry),
|
|
291
|
+
phase: 'default',
|
|
292
|
+
});
|
|
293
|
+
if (result.issues !== undefined) {
|
|
294
|
+
throw new DeclarationError(`${subject} has an invalid default. Fix the default or its schema.\n${messages(subject, reported(result.issues)).join('\n')}`);
|
|
295
|
+
}
|
|
296
|
+
defaults.set(input, result.value);
|
|
297
|
+
}
|
|
298
|
+
return defaults;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* An array default reaches the action as its own copy, so an action that mutates its collection
|
|
302
|
+
* rewrites neither the declaration nor the next invocation. A schema that returns a new array is
|
|
303
|
+
* copied too, because a pass-through schema returns the declared array itself and cannot be told
|
|
304
|
+
* apart from one that built its own. Every other output passes through unchanged.
|
|
305
|
+
*/
|
|
306
|
+
function freshDefault(value) {
|
|
307
|
+
return Array.isArray(value) ? [...value] : value;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* The raw tokens of one invocation, keyed by declared name. Every declared input of the routed
|
|
311
|
+
* Command and every global appears, so absence reads as the shape its declaration collects. Each
|
|
312
|
+
* collected value is copied, because the parser's own array is what the action reads.
|
|
313
|
+
*/
|
|
314
|
+
function suppliedInputs(declarations, supplied) {
|
|
315
|
+
const args = {};
|
|
316
|
+
const options = {};
|
|
317
|
+
for (const input of declarations) {
|
|
318
|
+
const collected = collects(input);
|
|
319
|
+
if (input.kind === 'argument') {
|
|
320
|
+
args[input.name] = copied(supplied.args.get(input)) ?? (collected ? [] : undefined);
|
|
321
|
+
}
|
|
322
|
+
else if (input.config.type === 'boolean') {
|
|
323
|
+
options[input.name] = supplied.options.booleans.get(input.name);
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
options[input.name] =
|
|
327
|
+
copied(suppliedOption(supplied.options, input.name, collected)) ??
|
|
328
|
+
(collected ? [] : undefined);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return { args, options };
|
|
332
|
+
}
|
|
333
|
+
export async function validateValues(invocation) {
|
|
334
|
+
const { defaults, supplied } = invocation;
|
|
335
|
+
const declarations = scoped(invocation.inputs);
|
|
336
|
+
/**
|
|
337
|
+
* One reading of the tokens and the route, built anew for each schema call. The route, the
|
|
338
|
+
* tail, and every collected value are copies, so a schema that writes to them reaches neither
|
|
339
|
+
* the parser's collections, nor the tail the action receives, nor the next schema of this
|
|
340
|
+
* invocation. The host is the captured object itself, the one the action receives.
|
|
341
|
+
*/
|
|
342
|
+
const facts = () => ({
|
|
343
|
+
command: [...invocation.command],
|
|
344
|
+
host: invocation.host,
|
|
345
|
+
passthrough: [...invocation.passthrough],
|
|
346
|
+
supplied: suppliedInputs(declarations.map((entry) => entry.input), supplied),
|
|
347
|
+
});
|
|
348
|
+
const values = new Map();
|
|
349
|
+
const lines = [];
|
|
350
|
+
const problems = [];
|
|
351
|
+
/** One path for every value the schema reads, so a raw shape and its issues meet it once. */
|
|
352
|
+
const accept = async (entry, raw, spelling) => {
|
|
353
|
+
const result = await validate(entry.input, raw, {
|
|
354
|
+
...facts(),
|
|
355
|
+
input: identityOf(entry),
|
|
356
|
+
phase: 'invocation',
|
|
357
|
+
});
|
|
358
|
+
if (result.issues === undefined) {
|
|
359
|
+
values.set(entry.input, result.value);
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
const issues = reported(result.issues);
|
|
363
|
+
problems.push({ input: identityOf(entry), issues, reason: 'invalid', spelling });
|
|
364
|
+
lines.push(...messages(suppliedName(entry.input, spelling), issues));
|
|
365
|
+
};
|
|
366
|
+
for (const entry of declarations) {
|
|
367
|
+
const { input } = entry;
|
|
368
|
+
if (input.kind === 'option' && input.config.type === 'boolean') {
|
|
369
|
+
values.set(input, supplied.options.booleans.get(input.name) ?? input.config.polarity === 'negative');
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
const collected = collects(input);
|
|
373
|
+
const spelling = spellingOf(input);
|
|
374
|
+
const raw = input.kind === 'argument'
|
|
375
|
+
? supplied.args.get(input)
|
|
376
|
+
: suppliedOption(supplied.options, input.name, collected);
|
|
377
|
+
if (raw === undefined) {
|
|
378
|
+
if (input.config.required) {
|
|
379
|
+
// An omitted required argument arrives here too, so omission has one class.
|
|
380
|
+
// One aggregated diagnostic covers an omitted argument and an omitted option alike.
|
|
381
|
+
problems.push({ input: identityOf(entry), reason: 'missing', spelling });
|
|
382
|
+
lines.push(missingMessage(input, spelling, collected));
|
|
383
|
+
}
|
|
384
|
+
else if (collected && !defaults.has(input)) {
|
|
385
|
+
// No occurrence is an accurate empty collection, so it reads like a supplied value.
|
|
386
|
+
await accept(entry, [], spelling);
|
|
387
|
+
}
|
|
388
|
+
else if (validatesOmission(input)) {
|
|
389
|
+
// The flag sends the omission itself to the schema.
|
|
390
|
+
// An absence rule reads the context a supplied value reads, and reports input issues.
|
|
391
|
+
await accept(entry, undefined, spelling);
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
values.set(input, freshDefault(defaults.get(input)));
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
await accept(entry, raw, spelling);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
if (problems.length > 0) {
|
|
403
|
+
throw new InputError(lines.join('\n'), problems);
|
|
404
|
+
}
|
|
405
|
+
return new ValidatedInputs(values);
|
|
406
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@loomcli/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The Loom CLI core package. Provides the scaffolding for creating new Loom CLI applications.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/dbtlr/loomcli.git"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"type": "module",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"import": "./dist/index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@standard-schema/spec": "^1.1.0",
|
|
22
|
+
"@types/node": "22.20.1"
|
|
23
|
+
},
|
|
24
|
+
"engines": {
|
|
25
|
+
"bun": ">=1.4.0",
|
|
26
|
+
"node": ">=22.23.2"
|
|
27
|
+
}
|
|
28
|
+
}
|