@crustjs/core 0.0.17 → 0.0.19
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
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The core library for the [Crust](https://crustjs.com) CLI framework.
|
|
4
4
|
|
|
5
|
-
Provides command definition, argument/flag parsing, subcommand routing, lifecycle hooks, and a plugin system
|
|
5
|
+
Provides command definition, argument/flag parsing, subcommand routing, lifecycle hooks, and a plugin system.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -16,19 +16,41 @@ bun add @crustjs/core
|
|
|
16
16
|
import { Crust } from "@crustjs/core";
|
|
17
17
|
|
|
18
18
|
const app = new Crust("greet")
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
19
|
+
.meta({ description: "Say hello" })
|
|
20
|
+
.args([{ name: "name", type: "string", default: "world" }] as const)
|
|
21
|
+
.flags({
|
|
22
|
+
loud: { type: "boolean", description: "Shout it", alias: "l" },
|
|
23
|
+
})
|
|
24
|
+
.run(({ args, flags }) => {
|
|
25
|
+
const msg = `Hello, ${args.name}!`;
|
|
26
|
+
console.log(flags.loud ? msg.toUpperCase() : msg);
|
|
27
|
+
});
|
|
28
28
|
|
|
29
29
|
app.execute();
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
## Built-in value types
|
|
33
|
+
|
|
34
|
+
Flags and positional arguments support six built-in `type` literals:
|
|
35
|
+
|
|
36
|
+
- `"string"` — raw string token
|
|
37
|
+
- `"number"` — coerced via `Number(raw)`
|
|
38
|
+
- `"boolean"` — toggle (`--flag` / `--no-flag`)
|
|
39
|
+
- `"url"` — `new URL(raw)` → `URL` instance
|
|
40
|
+
- `"path"` — `~` expanded, resolved to an absolute `string`
|
|
41
|
+
- `"json"` — `JSON.parse(raw)` → `unknown`
|
|
42
|
+
|
|
43
|
+
For formats that aren't built in, attach a synchronous `parse` to a `type: "string"` flag or arg. The inferred type flows from `ReturnType<parse>`:
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
flags: {
|
|
47
|
+
port: { type: "string", parse: (s) => Number(s), default: "3000" },
|
|
48
|
+
}
|
|
49
|
+
// flags.port: number
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
See the [Types](https://crustjs.com/docs/guide/types) guide for the full contract, error modes, and copy-paste recipes.
|
|
53
|
+
|
|
32
54
|
## Documentation
|
|
33
55
|
|
|
34
56
|
See the full docs at [crustjs.com](https://crustjs.com).
|
package/dist/index.d.ts
CHANGED
|
@@ -41,16 +41,39 @@ interface CommandRoute {
|
|
|
41
41
|
* @throws {CrustError} COMMAND_NOT_FOUND when an unknown subcommand is given and the parent has no run()
|
|
42
42
|
*/
|
|
43
43
|
declare function resolveCommand(command: CommandNode, argv: string[]): CommandRoute;
|
|
44
|
-
|
|
45
|
-
type ValueType = "string" | "number" | "boolean";
|
|
44
|
+
import { BaseValueType, ResolvePrimitive } from "@crustjs/utils";
|
|
46
45
|
/**
|
|
47
|
-
*
|
|
46
|
+
* Supported type literals for args and flags.
|
|
48
47
|
*
|
|
49
|
-
*
|
|
50
|
-
* -
|
|
51
|
-
*
|
|
48
|
+
* Extends `BaseValueType` (`"string" | "number" | "boolean"`) with three
|
|
49
|
+
* formatted built-ins:
|
|
50
|
+
*
|
|
51
|
+
* - `"url"` — the raw value is parsed via `new URL()` into a {@link URL}
|
|
52
|
+
* - `"path"` — the raw value is expanded (`~`) and resolved against
|
|
53
|
+
* `process.cwd()` into an absolute `string`
|
|
54
|
+
* - `"json"` — the raw value is parsed via `JSON.parse()` into `unknown`
|
|
52
55
|
*/
|
|
53
|
-
type
|
|
56
|
+
type ValueType = BaseValueType | "url" | "path" | "json";
|
|
57
|
+
/**
|
|
58
|
+
* Resolve a {@link ValueType} literal to its runtime TypeScript type.
|
|
59
|
+
*
|
|
60
|
+
* Delegates to `ResolvePrimitive<T>` for the three base types; maps the
|
|
61
|
+
* three formatted types (`"url"`, `"path"`, `"json"`) to `URL`, `string`,
|
|
62
|
+
* and `unknown` respectively.
|
|
63
|
+
*/
|
|
64
|
+
type Resolve<T extends ValueType> = T extends BaseValueType ? ResolvePrimitive<T> : T extends "url" ? URL : T extends "path" ? string : T extends "json" ? unknown : never;
|
|
65
|
+
/**
|
|
66
|
+
* Resolve the inferred runtime type for a flag/arg definition.
|
|
67
|
+
*
|
|
68
|
+
* When the def declares a `parse` escape hatch (only allowed on `"string"`
|
|
69
|
+
* variants), the inferred type is `ReturnType<typeof parse>`. Otherwise it
|
|
70
|
+
* delegates to {@link Resolve} on the declared `type`.
|
|
71
|
+
*/
|
|
72
|
+
type ResolveBaseType<F> = F extends {
|
|
73
|
+
parse: (raw: string) => infer R;
|
|
74
|
+
} ? R : F extends {
|
|
75
|
+
type: infer T extends ValueType;
|
|
76
|
+
} ? Resolve<T> : never;
|
|
54
77
|
/** Shared fields present on every positional argument definition */
|
|
55
78
|
interface ArgDefBase {
|
|
56
79
|
/** The argument name (used as the key in the parsed result and in help text) */
|
|
@@ -82,13 +105,10 @@ interface StringArgDef extends ArgDefBase {
|
|
|
82
105
|
/**
|
|
83
106
|
* Static enum of valid values for this argument.
|
|
84
107
|
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
* not throw, the value is still parsed as a string and delivered to your
|
|
90
|
-
* `run` handler. Validate explicitly inside your handler if you need
|
|
91
|
-
* runtime rejection today.
|
|
108
|
+
* Validated at parse time before `parse` runs. Passing a value outside
|
|
109
|
+
* `choices` throws `CrustError("PARSE", …)` before any `parse` transform
|
|
110
|
+
* is applied. Also consumed by shell-completion plugins
|
|
111
|
+
* (e.g. `@crustjs/plugins/completion`) to emit value candidates.
|
|
92
112
|
*
|
|
93
113
|
* Only available on string-typed args; not supported on number/boolean.
|
|
94
114
|
*
|
|
@@ -96,18 +116,54 @@ interface StringArgDef extends ArgDefBase {
|
|
|
96
116
|
* { name: "target", type: "string", choices: ["browser", "bun", "node"] }
|
|
97
117
|
*/
|
|
98
118
|
choices?: readonly string[];
|
|
119
|
+
/**
|
|
120
|
+
* Custom synchronous parser for the raw argv string. Runs per element
|
|
121
|
+
* for variadic args. See {@link StringFlagDef.parse} for full semantics.
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* { name: "port", type: "string", parse: (s) => Number(s) }
|
|
125
|
+
*/
|
|
126
|
+
parse?: (raw: string) => unknown;
|
|
99
127
|
}
|
|
100
128
|
/** A positional argument whose value is a number */
|
|
101
129
|
interface NumberArgDef extends ArgDefBase {
|
|
102
130
|
type: "number";
|
|
103
131
|
/** Default number value when the argument is not provided */
|
|
104
132
|
default?: number;
|
|
133
|
+
/** Not supported on number args — use `type: "string"` with `parse`. */
|
|
134
|
+
parse?: never;
|
|
105
135
|
}
|
|
106
136
|
/** A positional argument whose value is a boolean */
|
|
107
137
|
interface BooleanArgDef extends ArgDefBase {
|
|
108
138
|
type: "boolean";
|
|
109
139
|
/** Default boolean value when the argument is not provided */
|
|
110
140
|
default?: boolean;
|
|
141
|
+
/** Not supported on boolean args — use `type: "string"` with `parse`. */
|
|
142
|
+
parse?: never;
|
|
143
|
+
}
|
|
144
|
+
/** A positional argument whose value is a {@link URL} */
|
|
145
|
+
interface UrlArgDef extends ArgDefBase {
|
|
146
|
+
type: "url";
|
|
147
|
+
/** Default URL value when the argument is not provided */
|
|
148
|
+
default?: URL;
|
|
149
|
+
/** Not supported on url args — use `type: "string"` with `parse`. */
|
|
150
|
+
parse?: never;
|
|
151
|
+
}
|
|
152
|
+
/** A positional argument whose value is an absolute filesystem path */
|
|
153
|
+
interface PathArgDef extends ArgDefBase {
|
|
154
|
+
type: "path";
|
|
155
|
+
/** Default path string when the argument is not provided */
|
|
156
|
+
default?: string;
|
|
157
|
+
/** Not supported on path args — use `type: "string"` with `parse`. */
|
|
158
|
+
parse?: never;
|
|
159
|
+
}
|
|
160
|
+
/** A positional argument whose value is JSON parsed to `unknown` */
|
|
161
|
+
interface JsonArgDef extends ArgDefBase {
|
|
162
|
+
type: "json";
|
|
163
|
+
/** Default parsed JSON value when the argument is not provided */
|
|
164
|
+
default?: unknown;
|
|
165
|
+
/** Not supported on json args — use `type: "string"` with `parse`. */
|
|
166
|
+
parse?: never;
|
|
111
167
|
}
|
|
112
168
|
/**
|
|
113
169
|
* Defines a single positional argument for a CLI command.
|
|
@@ -124,7 +180,16 @@ interface BooleanArgDef extends ArgDefBase {
|
|
|
124
180
|
* ] as const satisfies ArgsDef;
|
|
125
181
|
* ```
|
|
126
182
|
*/
|
|
127
|
-
|
|
183
|
+
interface RawArgDef extends ArgDefBase {
|
|
184
|
+
/** Optional parser hint. Omit for raw schema-backed validation. */
|
|
185
|
+
type?: never;
|
|
186
|
+
/** Raw default value when the argument is not provided */
|
|
187
|
+
default?: unknown;
|
|
188
|
+
choices?: readonly string[];
|
|
189
|
+
/** Not supported on raw args — schema validators own the transform. */
|
|
190
|
+
parse?: never;
|
|
191
|
+
}
|
|
192
|
+
type ArgDef = StringArgDef | NumberArgDef | BooleanArgDef | UrlArgDef | PathArgDef | JsonArgDef | RawArgDef;
|
|
128
193
|
/** Ordered tuple of positional argument definitions */
|
|
129
194
|
type ArgsDef = readonly ArgDef[];
|
|
130
195
|
/** Shared fields present on every flag definition */
|
|
@@ -153,13 +218,10 @@ interface StringFlagDef extends SingleFlagBase {
|
|
|
153
218
|
/**
|
|
154
219
|
* Static enum of valid values for this flag.
|
|
155
220
|
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
* not throw, the value is still parsed as a string and delivered to your
|
|
161
|
-
* `run` handler. Validate explicitly inside your handler if you need
|
|
162
|
-
* runtime rejection today.
|
|
221
|
+
* Validated at parse time before `parse` runs. Passing a value outside
|
|
222
|
+
* `choices` throws `CrustError("PARSE", …)` before any `parse` transform
|
|
223
|
+
* is applied. Also consumed by shell-completion plugins
|
|
224
|
+
* (e.g. `@crustjs/plugins/completion`) to emit value candidates.
|
|
163
225
|
*
|
|
164
226
|
* Only available on string-typed flags; not supported on number/boolean.
|
|
165
227
|
*
|
|
@@ -167,12 +229,35 @@ interface StringFlagDef extends SingleFlagBase {
|
|
|
167
229
|
* { type: "string", choices: ["browser", "bun", "node"] }
|
|
168
230
|
*/
|
|
169
231
|
choices?: readonly string[];
|
|
232
|
+
/**
|
|
233
|
+
* Custom synchronous parser for the raw argv string.
|
|
234
|
+
*
|
|
235
|
+
* Receives the raw token as it appeared on the command line (after
|
|
236
|
+
* `choices` validation, when present) and returns the resolved value
|
|
237
|
+
* that flows to the `run` handler. The return type is inferred and
|
|
238
|
+
* becomes the flag's runtime type.
|
|
239
|
+
*
|
|
240
|
+
* Constraints:
|
|
241
|
+
* - Synchronous only. `async` parsers are rejected at command setup
|
|
242
|
+
* with `CrustError("CONFIG", …)`.
|
|
243
|
+
* - Only allowed on `type: "string"` (single + multi) and string args.
|
|
244
|
+
* `parse?: never` on every non-string variant prevents misuse at
|
|
245
|
+
* compile time.
|
|
246
|
+
* - When `default` is set and argv is absent, `parse(String(default))`
|
|
247
|
+
* runs so the runtime value matches the inferred type.
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* { type: "string", parse: (s) => Number(s) }
|
|
251
|
+
*/
|
|
252
|
+
parse?: (raw: string) => unknown;
|
|
170
253
|
}
|
|
171
254
|
/** A single-value number flag */
|
|
172
255
|
interface NumberFlagDef extends SingleFlagBase {
|
|
173
256
|
type: "number";
|
|
174
257
|
/** Default number value */
|
|
175
258
|
default?: number;
|
|
259
|
+
/** Not supported on number flags — use `type: "string"` with `parse`. */
|
|
260
|
+
parse?: never;
|
|
176
261
|
}
|
|
177
262
|
/** A single-value boolean flag */
|
|
178
263
|
interface BooleanFlagDef extends SingleFlagBase {
|
|
@@ -181,6 +266,32 @@ interface BooleanFlagDef extends SingleFlagBase {
|
|
|
181
266
|
default?: boolean;
|
|
182
267
|
/** When `true`, hide the generated `--no-{name}` help label */
|
|
183
268
|
noNegate?: true;
|
|
269
|
+
/** Not supported on boolean flags — use `type: "string"` with `parse`. */
|
|
270
|
+
parse?: never;
|
|
271
|
+
}
|
|
272
|
+
/** A single-value URL flag (parsed via `new URL()`) */
|
|
273
|
+
interface UrlFlagDef extends SingleFlagBase {
|
|
274
|
+
type: "url";
|
|
275
|
+
/** Default URL value */
|
|
276
|
+
default?: URL;
|
|
277
|
+
/** Not supported on url flags — use `type: "string"` with `parse`. */
|
|
278
|
+
parse?: never;
|
|
279
|
+
}
|
|
280
|
+
/** A single-value path flag (expanded `~` + resolved against `process.cwd()`) */
|
|
281
|
+
interface PathFlagDef extends SingleFlagBase {
|
|
282
|
+
type: "path";
|
|
283
|
+
/** Default path string value */
|
|
284
|
+
default?: string;
|
|
285
|
+
/** Not supported on path flags — use `type: "string"` with `parse`. */
|
|
286
|
+
parse?: never;
|
|
287
|
+
}
|
|
288
|
+
/** A single-value JSON flag (parsed via `JSON.parse()` to `unknown`) */
|
|
289
|
+
interface JsonFlagDef extends SingleFlagBase {
|
|
290
|
+
type: "json";
|
|
291
|
+
/** Default parsed JSON value */
|
|
292
|
+
default?: unknown;
|
|
293
|
+
/** Not supported on json flags — use `type: "string"` with `parse`. */
|
|
294
|
+
parse?: never;
|
|
184
295
|
}
|
|
185
296
|
/** Base for multi-value flags — `multiple` is required as `true` */
|
|
186
297
|
interface MultiFlagBase extends FlagDefBase {
|
|
@@ -195,13 +306,10 @@ interface StringMultiFlagDef extends MultiFlagBase {
|
|
|
195
306
|
/**
|
|
196
307
|
* Static enum of valid values for each occurrence of this flag.
|
|
197
308
|
*
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
* not throw, the value is still parsed as a string and collected into the
|
|
203
|
-
* array. Validate explicitly inside your handler if you need runtime
|
|
204
|
-
* rejection today.
|
|
309
|
+
* Each element is validated at parse time before `parse` runs. Passing
|
|
310
|
+
* a value outside `choices` throws `CrustError("PARSE", …)` before any
|
|
311
|
+
* `parse` transform is applied. Also consumed by shell-completion
|
|
312
|
+
* plugins (e.g. `@crustjs/plugins/completion`) to emit value candidates.
|
|
205
313
|
*
|
|
206
314
|
* Only available on string-typed multi-flags; not supported on number/boolean.
|
|
207
315
|
*
|
|
@@ -209,12 +317,20 @@ interface StringMultiFlagDef extends MultiFlagBase {
|
|
|
209
317
|
* { type: "string", multiple: true, choices: ["unit", "integration"] }
|
|
210
318
|
*/
|
|
211
319
|
choices?: readonly string[];
|
|
320
|
+
/**
|
|
321
|
+
* Custom synchronous per-element parser for each raw argv string.
|
|
322
|
+
* See {@link StringFlagDef.parse} for full semantics. Runs once per
|
|
323
|
+
* occurrence; the resolved value is `ReturnType<typeof parse>[]`.
|
|
324
|
+
*/
|
|
325
|
+
parse?: (raw: string) => unknown;
|
|
212
326
|
}
|
|
213
327
|
/** A multi-value number flag (collects repeated values into an array) */
|
|
214
328
|
interface NumberMultiFlagDef extends MultiFlagBase {
|
|
215
329
|
type: "number";
|
|
216
330
|
/** Default number array value */
|
|
217
331
|
default?: number[];
|
|
332
|
+
/** Not supported — use `type: "string"`, `multiple: true`, with `parse`. */
|
|
333
|
+
parse?: never;
|
|
218
334
|
}
|
|
219
335
|
/** A multi-value boolean flag (collects repeated values into an array) */
|
|
220
336
|
interface BooleanMultiFlagDef extends MultiFlagBase {
|
|
@@ -223,6 +339,32 @@ interface BooleanMultiFlagDef extends MultiFlagBase {
|
|
|
223
339
|
default?: boolean[];
|
|
224
340
|
/** When `true`, hide the generated `--no-{name}` help label */
|
|
225
341
|
noNegate?: true;
|
|
342
|
+
/** Not supported — use `type: "string"`, `multiple: true`, with `parse`. */
|
|
343
|
+
parse?: never;
|
|
344
|
+
}
|
|
345
|
+
/** A multi-value URL flag (collects repeated URL values into an array) */
|
|
346
|
+
interface UrlMultiFlagDef extends MultiFlagBase {
|
|
347
|
+
type: "url";
|
|
348
|
+
/** Default URL array value */
|
|
349
|
+
default?: URL[];
|
|
350
|
+
/** Not supported — use `type: "string"`, `multiple: true`, with `parse`. */
|
|
351
|
+
parse?: never;
|
|
352
|
+
}
|
|
353
|
+
/** A multi-value path flag (collects repeated path strings into an array) */
|
|
354
|
+
interface PathMultiFlagDef extends MultiFlagBase {
|
|
355
|
+
type: "path";
|
|
356
|
+
/** Default path array value */
|
|
357
|
+
default?: string[];
|
|
358
|
+
/** Not supported — use `type: "string"`, `multiple: true`, with `parse`. */
|
|
359
|
+
parse?: never;
|
|
360
|
+
}
|
|
361
|
+
/** A multi-value JSON flag (collects repeated parsed JSON values) */
|
|
362
|
+
interface JsonMultiFlagDef extends MultiFlagBase {
|
|
363
|
+
type: "json";
|
|
364
|
+
/** Default parsed JSON array value */
|
|
365
|
+
default?: unknown[];
|
|
366
|
+
/** Not supported — use `type: "string"`, `multiple: true`, with `parse`. */
|
|
367
|
+
parse?: never;
|
|
226
368
|
}
|
|
227
369
|
/**
|
|
228
370
|
* Defines a single named flag for a CLI command.
|
|
@@ -239,7 +381,7 @@ interface BooleanMultiFlagDef extends MultiFlagBase {
|
|
|
239
381
|
* } satisfies FlagsDef;
|
|
240
382
|
* ```
|
|
241
383
|
*/
|
|
242
|
-
type FlagDef = StringFlagDef | NumberFlagDef | BooleanFlagDef | StringMultiFlagDef | NumberMultiFlagDef | BooleanMultiFlagDef;
|
|
384
|
+
type FlagDef = StringFlagDef | NumberFlagDef | BooleanFlagDef | UrlFlagDef | PathFlagDef | JsonFlagDef | StringMultiFlagDef | NumberMultiFlagDef | BooleanMultiFlagDef | UrlMultiFlagDef | PathMultiFlagDef | JsonMultiFlagDef;
|
|
243
385
|
/** Record mapping flag names to their definitions */
|
|
244
386
|
type FlagsDef = Record<string, FlagDef>;
|
|
245
387
|
/**
|
|
@@ -467,12 +609,20 @@ type EffectiveFlags<
|
|
|
467
609
|
* `required` only gates empty-array validation, not the type.
|
|
468
610
|
*/
|
|
469
611
|
type InferArgValue<A extends ArgDef> = A extends {
|
|
612
|
+
type: infer _T extends ValueType;
|
|
613
|
+
} ? A extends {
|
|
614
|
+
variadic: true;
|
|
615
|
+
} ? ResolveBaseType<A>[] : A extends {
|
|
616
|
+
required: true;
|
|
617
|
+
} ? ResolveBaseType<A> : A extends {
|
|
618
|
+
default: unknown;
|
|
619
|
+
} ? ResolveBaseType<A> : ResolveBaseType<A> | undefined : A extends {
|
|
470
620
|
variadic: true;
|
|
471
|
-
} ?
|
|
621
|
+
} ? unknown[] : A extends {
|
|
472
622
|
required: true;
|
|
473
|
-
}
|
|
474
|
-
default:
|
|
475
|
-
} ?
|
|
623
|
+
} | {
|
|
624
|
+
default: unknown;
|
|
625
|
+
} ? unknown : unknown;
|
|
476
626
|
/**
|
|
477
627
|
* Recursively converts an ArgsDef tuple into a named object type.
|
|
478
628
|
*
|
|
@@ -504,16 +654,18 @@ type InferArgs<A> = A extends ArgsDef ? Simplify<InferArgsTuple<A>> : Record<str
|
|
|
504
654
|
* - otherwise → `primitive | undefined`
|
|
505
655
|
*/
|
|
506
656
|
type InferFlagValue<F extends FlagDef> = F extends {
|
|
657
|
+
type: infer _T extends ValueType;
|
|
658
|
+
} ? F extends {
|
|
507
659
|
multiple: true;
|
|
508
660
|
} ? F extends {
|
|
509
661
|
required: true;
|
|
510
|
-
} ?
|
|
511
|
-
default:
|
|
512
|
-
} ?
|
|
662
|
+
} ? ResolveBaseType<F>[] : F extends {
|
|
663
|
+
default: readonly unknown[];
|
|
664
|
+
} ? ResolveBaseType<F>[] : ResolveBaseType<F>[] | undefined : F extends {
|
|
513
665
|
required: true;
|
|
514
|
-
} ?
|
|
515
|
-
default:
|
|
516
|
-
} ?
|
|
666
|
+
} ? ResolveBaseType<F> : F extends {
|
|
667
|
+
default: unknown;
|
|
668
|
+
} ? ResolveBaseType<F> : ResolveBaseType<F> | undefined : never;
|
|
517
669
|
/**
|
|
518
670
|
* Maps a full FlagsDef record to resolved flag types.
|
|
519
671
|
*
|
|
@@ -1001,6 +1153,7 @@ interface CrustErrorDetailsMap {
|
|
|
1001
1153
|
PARSE: undefined;
|
|
1002
1154
|
EXECUTION: undefined;
|
|
1003
1155
|
COMMAND_NOT_FOUND: CommandNotFoundErrorDetails;
|
|
1156
|
+
CONFIG: undefined;
|
|
1004
1157
|
}
|
|
1005
1158
|
/**
|
|
1006
1159
|
* All possible error codes emitted by Crust.
|
|
@@ -1009,6 +1162,8 @@ interface CrustErrorDetailsMap {
|
|
|
1009
1162
|
* - `VALIDATION` — Missing required arguments or flags
|
|
1010
1163
|
* - `PARSE` — Argv parsing failures (unknown flags, type coercion)
|
|
1011
1164
|
* - `EXECUTION` — Runtime command/middleware failures
|
|
1165
|
+
* - `COMMAND_NOT_FOUND` — Unrecognised subcommand at the current level
|
|
1166
|
+
* - `CONFIG` — Unsupported command/flag definition surfaced at setup time (e.g. async `parse`)
|
|
1012
1167
|
*
|
|
1013
1168
|
* @example
|
|
1014
1169
|
* ```ts
|
|
@@ -1092,4 +1247,4 @@ declare function parseArgs<
|
|
|
1092
1247
|
* @throws {CrustError} On missing required args or flags
|
|
1093
1248
|
*/
|
|
1094
1249
|
declare function validateParsed(command: CommandNode, parsed: ParseResult): void;
|
|
1095
|
-
export { validateParsed, resolveCommand, parseArgs, ValueType, ValidateVariadicArgs, ValidateNoPrefixedFlags, ValidateFlagAliases, ValidateCrossCollisions, VALIDATION_MODE_ENV, VALIDATION_FORCE_EXIT_ENV, SetupContext, SetupActions, PluginMiddleware, ParseResult, MiddlewareContext, MergeFlags, InheritableFlags, InferFlags, InferArgs, FlagsDef, FlagDef, EffectiveFlags, CrustPlugin, CrustErrorCode, CrustError, CrustCommandContext, Crust, CommandRoute, CommandNode, CommandMeta, ArgsDef, ArgDef };
|
|
1250
|
+
export { validateParsed, resolveCommand, parseArgs, ValueType, ValidateVariadicArgs, ValidateNoPrefixedFlags, ValidateFlagAliases, ValidateCrossCollisions, VALIDATION_MODE_ENV, VALIDATION_FORCE_EXIT_ENV, SetupContext, SetupActions, ResolveBaseType, Resolve, PluginMiddleware, ParseResult, MiddlewareContext, MergeFlags, InheritableFlags, InferFlags, InferArgs, FlagsDef, FlagDef, EffectiveFlags, CrustPlugin, CrustErrorCode, CrustError, CrustCommandContext, Crust, CommandRoute, CommandNode, CommandMeta, ArgsDef, ArgDef };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{a as O,b as K,c as T,d as I,f as B}from"./shared/chunk-
|
|
2
|
+
import{a as O,b as K,c as T,d as I,f as B}from"./shared/chunk-q8y07jw2.js";function P(j){return{meta:{name:j},localFlags:{},effectiveFlags:{},args:void 0,subCommands:{},plugins:[],preRun:void 0,run:void 0,postRun:void 0}}function X(j,J){let q={};for(let[Q,Z]of Object.entries(j))if(Z.inherit===!0)q[Q]=Z;for(let[Q,Z]of Object.entries(J))q[Q]=Z;return q}function E(j,J){for(let[q,Q]of Object.entries(j)){let Z=Q.meta.aliases;if(!Z)continue;if(Z.includes(J))return{canonicalName:q,node:Q}}return null}function D(j,J){let q=[j.meta.name],Q=j,Z=J;while(Z.length>0){let $=Q.subCommands;if(!$||Object.keys($).length===0)break;let G=Z[0];if(!G||G.startsWith("-"))break;if(G in $&&$[G]){Q=$[G],q.push(G),Z=Z.slice(1);continue}let H=E($,G);if(H){Q=H.node,q.push(H.canonicalName),Z=Z.slice(1);continue}if(Q.run)break;throw new K("COMMAND_NOT_FOUND",`Unknown command "${G}".`,{input:G,available:Object.keys($),commandPath:[...q],parentCommand:Q})}return{command:Q,argv:Z,commandPath:q}}function f(j){for(let[J,q]of Object.entries(j)){if(J.startsWith("no-")){let Q=J.slice(3);throw new K("DEFINITION",`Flag "--${J}" must not use "no-" prefix; define "${Q}" and negate with "--no-${Q}"`)}if(q.short?.startsWith("no-"))throw new K("DEFINITION",`Short alias "-${q.short}" on "--${J}" must not use "no-" prefix (reserved for negation)`);if(q.aliases){for(let Q of q.aliases)if(Q.startsWith("no-"))throw new K("DEFINITION",`Alias "--${Q}" on "--${J}" must not use "no-" prefix (reserved for negation)`)}}}var N="CRUST_INTERNAL_VALIDATE_ONLY",b="CRUST_INTERNAL_VALIDATE_FORCE_EXIT",x=130,C="__CRUST_VALIDATE_RESULT__";function k(){let j=new Map;return{get(J){return j.get(J)},has(J){return j.has(J)},set(J,q){j.set(J,q)},delete(J){return j.delete(J)}}}function L(j){if(!(j instanceof Error))return!1;return j.name==="CancelledError"}function h(j,J){j.effectiveFlags=X(J,j.localFlags);for(let q of Object.values(j.subCommands))h(q,j.effectiveFlags)}function y(j){return{addFlag(J,q,Q){if(q in J.effectiveFlags)j?.push(`Plugin flag "--${q}" on "${J.meta.name}" overrides existing flag`);J.effectiveFlags[q]=Q},addSubCommand(J,q,Q){if(!q.trim())throw new K("DEFINITION","addSubCommand: name must be a non-empty string");if(J.subCommands[q]){j?.push(`Plugin subcommand "${q}" on "${J.meta.name}" skipped (already exists)`);return}try{B({canonicalName:q,aliases:Q.meta.aliases},J.subCommands,q)}catch(Z){let $=Z instanceof Error?Z.message:String(Z);j?.push(`Plugin subcommand "${q}" on "${J.meta.name}" skipped: ${$}`);return}h(Q,J.effectiveFlags),J.subCommands[q]=Q}}}async function F(j,J,q){for(let Q of j){if(!Q.setup)continue;await Q.setup(J,q)}}async function A(j,J,q){let Q=j.map((G)=>G.middleware).filter((G)=>Boolean(G)),Z=-1,$=async(G)=>{if(G<=Z)throw new K("DEFINITION","Plugin middleware called next() multiple times");if(Z=G,G===Q.length){await q();return}let H=Q[G];if(!H)throw new K("DEFINITION","Plugin middleware stack is invalid");await H(J,()=>$(G+1))};await $(0)}function S(j){let J=[...j.plugins];for(let q of Object.values(j.subCommands))J.push(...S(q));return J}function w(j){let J={};for(let[q,Q]of Object.entries(j))J[q]={...Q,aliases:Q.aliases?[...Q.aliases]:void 0};return J}function v(j){let J={};for(let[q,Q]of Object.entries(j.subCommands))J[q]=v(Q);return{meta:{...j.meta},localFlags:w(j.localFlags),effectiveFlags:w(j.effectiveFlags),args:j.args?j.args.map((q)=>({...q})):void 0,subCommands:J,plugins:[...j.plugins],preRun:j.preRun,run:j.run,postRun:j.postRun}}function _(j){if(Object.freeze(j),Object.freeze(j.localFlags),Object.freeze(j.effectiveFlags),Object.freeze(j.meta),Object.freeze(j.plugins),j.args)Object.freeze(j.args);for(let J of Object.values(j.subCommands))_(J);Object.freeze(j.subCommands)}class M{_node;_inheritedFlags;constructor(j){if(!j.trim())throw new K("DEFINITION","meta.name must be a non-empty string");this._node=P(j),this._inheritedFlags={}}static _createChild(j,J){let q=new M(j);return q._inheritedFlags=J,q}_clone(j){let J=Object.create(Object.getPrototypeOf(this)),q={...this._node,localFlags:{...this._node.localFlags},effectiveFlags:{...this._node.effectiveFlags},subCommands:{...this._node.subCommands},plugins:[...this._node.plugins],meta:{...this._node.meta},args:this._node.args?[...this._node.args]:void 0,...j};return J._node=q,J._inheritedFlags=this._inheritedFlags,J}meta(j){return this._clone({meta:{...this._node.meta,...j}})}flags(j){f(j);let J={};for(let[q,Q]of Object.entries(j))J[q]={...Q};return this._clone({localFlags:J,effectiveFlags:X(this._inheritedFlags,J)})}args(j){let J=j.map((q)=>({...q}));return this._clone({args:J})}run(j){return this._clone({run:j})}preRun(j){return this._clone({preRun:j})}postRun(j){return this._clone({postRun:j})}use(j){return this._clone({plugins:[...this._node.plugins,j]})}sub(j){if(!j.trim())throw new K("DEFINITION","Subcommand name must be a non-empty string");let J=X(this._inheritedFlags,this._node.localFlags);return M._createChild(j,J)}command(j,J){if(typeof j==="string"){let $=j;if(!J)throw new K("DEFINITION","command(name, cb) requires a callback");if(!$.trim())throw new K("DEFINITION","Subcommand name must be a non-empty string");if(this._node.subCommands[$])throw new K("DEFINITION",`Subcommand "${$}" is already registered`);let G=X(this._inheritedFlags,this._node.localFlags),H=M._createChild($,G),z=J(H);B({canonicalName:$,aliases:z._node.meta.aliases},this._node.subCommands,$);let W={...z._node,effectiveFlags:X(z._inheritedFlags,z._node.localFlags)};return this._clone({subCommands:{...this._node.subCommands,[$]:W}})}let q=j,Q=q._node.meta.name;if(!Q.trim())throw new K("DEFINITION","Subcommand name must be a non-empty string");if(this._node.subCommands[Q])throw new K("DEFINITION",`Subcommand "${Q}" is already registered`);B({canonicalName:Q,aliases:q._node.meta.aliases},this._node.subCommands,Q);let Z={...q._node,effectiveFlags:X(q._inheritedFlags,q._node.localFlags)};return this._clone({subCommands:{...this._node.subCommands,[Q]:Z}})}async prepareCommandTree(j){let J=j?.argv??[],q=v(this._node),Q=S(q),Z=[],$=k(),G={argv:[...J],rootCommand:q,state:$},H=y(Z);try{await F(Q,G,H)}catch(W){if(L(W))throw W;if(W instanceof K)throw W;if(W instanceof Error)throw W;throw new K("DEFINITION",String(W))}_(q);let{validateCommandTree:z}=await import("./shared/chunk-1670njz2.js");return z(q),{root:q,warnings:Z}}async execute(j){let J=j?.argv??process.argv.slice(2),q=this._node,Q=S(q),Z=[],$=k(),G={argv:[...J],rootCommand:q,state:$},H=y(Z);try{await F(Q,G,H)}catch(W){if(L(W)){process.exitCode=x;return}if(W instanceof K){console.error(`Error: ${W.message}`),process.exitCode=1;return}let U=W instanceof Error?W.message:String(W);console.error(`Error: ${U}`),process.exitCode=1;return}if(_(q),process.env[N]==="1"){let W=(async()=>{try{let{validateCommandTree:U}=await import("./shared/chunk-1670njz2.js");U(q);for(let Y of Z)console.warn(`Warning: ${Y}`);return{ok:!0}}catch(U){let Y=U instanceof Error?U.message:String(U);return console.error(Y),process.exitCode=1,{ok:!1,error:U}}})();if(globalThis[C]=W,await W,process.env[b]==="1")return process.exit(process.exitCode??0);return}for(let W of Z)console.warn(`Warning: ${W}`);let z={argv:[...J],rootCommand:q,state:$,route:null,input:null};try{let W,U;try{let Y=D(q,[...J]);z.route=Y,W=Y.command,U=T(W,Y.argv),z.input=U}catch(Y){await A(Q,z,async()=>{throw Y});return}await A(Q,z,async()=>{if(I(W,U),!W.run)return;let Y={args:U.args,flags:U.flags,rawArgs:U.rawArgs,command:W},R;try{if(W.preRun)await W.preRun(Y);await W.run(Y)}catch(V){R=V}if(W.postRun)try{await W.postRun(Y)}catch(V){if(!R)R=V;else console.error(`Error in postRun: ${V instanceof Error?V.message:String(V)}`)}if(R)throw R})}catch(W){if(L(W)){process.exitCode=x;return}if(W instanceof K){console.error(`Error: ${W.message}`),process.exitCode=1;return}if(W instanceof Error){let U=new K("EXECUTION",W.message).withCause(W);console.error(`Error: ${U.message}`),process.exitCode=1;return}console.error(`Error: ${String(W)}`),process.exitCode=1}}}export{I as validateParsed,D as resolveCommand,T as parseArgs,N as VALIDATION_MODE_ENV,b as VALIDATION_FORCE_EXIT_ENV,K as CrustError,M as Crust};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{e as a,f as b,g as c}from"./chunk-
|
|
2
|
+
import{e as a,f as b,g as c}from"./chunk-q8y07jw2.js";export{b as validateIncomingAliases,c as validateCommandTree,a as validateAliasString};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var f=import.meta.require;class Y extends Error{code;details;cause;constructor(H,K,...z){super(K);this.name="CrustError",this.code=H,this.details=z[0]}is(H){return this.code===H}withCause(H){return this.cause=H,this}}import{parseArgs as L}from"util";import{coerceBooleanString as x,tryCoerceNumber as y}from"@crustjs/utils";import{homedir as k}from"os";import{resolve as T}from"path";function R(H){try{return new URL(H)}catch{let z=/^[a-z][a-z0-9+.-]*:/i.test(H)?"":" (missing protocol \u2014 e.g. https://example.com)";throw new Y("PARSE",`Invalid URL "${H}"${z}`)}}function J(H){if(H==="")throw new Y("PARSE","Path cannot be empty");let K=H.replace(/^~(?=\/|$)/,k());return T(process.cwd(),K)}function O(H){try{return JSON.parse(H)}catch(K){let z=K instanceof Error?K.message:String(K);throw new Y("PARSE",`Invalid JSON: ${z}. Tip: wrap JSON in single quotes on the command line, e.g. --flag '{"k":1}'`)}}function w(H){let K={},z={};if(!H)return{options:K,aliasToName:z};let $=new Map;for(let Q of Object.keys(H))$.set(Q,Q);for(let[Q,Z]of Object.entries(H)){if(Q.startsWith("no-")){let X=Q.slice(3);throw new Y("DEFINITION",`Flag "--${Q}" must not use "no-" prefix; define "${X}" and negate with "--no-${X}"`)}if(!E.has(Z.type))throw new Y("DEFINITION",`Flag "--${Q}" must declare a parser type ("string", "number", "boolean", "url", "path", or "json")`);let B=Z.type==="boolean"?"boolean":"string",W={type:B};if(Z.multiple)W.multiple=!0;if(Z.short){if(Z.short.startsWith("no-"))throw new Y("DEFINITION",`Short alias "-${Z.short}" on "--${Q}" must not use "no-" prefix (reserved for negation)`);let X=$.get(Z.short);if(X)throw new Y("DEFINITION",`Alias collision: "-${Z.short}" is used by both "--${X}" and "--${Q}"`);$.set(Z.short,Q),z[Z.short]=Q,W.short=Z.short}if(Z.aliases)for(let X of Z.aliases){if(X.startsWith("no-"))throw new Y("DEFINITION",`Alias "--${X}" on "--${Q}" must not use "no-" prefix (reserved for negation)`);let G=$.get(X);if(G)throw new Y("DEFINITION",`Alias collision: "${X.length===1?"-":"--"}${X}" is used by both "--${G}" and "--${Q}"`);$.set(X,Q),z[X]=Q;let M={type:B};if(Z.multiple)M.multiple=!0;K[X]=M}K[Q]=W}return{options:K,aliasToName:z}}var E=new Set(["string","number","boolean","url","path","json"]);function F(H,K,z){if(K==="number"){let $=y(H);if($===void 0)throw new Y("PARSE",`Expected number for ${z}, got "${H}"`);return $}if(K==="boolean")return x(H);if(K==="url")return R(H);if(K==="path")return J(H);if(K==="json")return O(H);return H}function I(H,K,z){if(!K.includes(H))throw new Y("PARSE",`Invalid value "${H}" for ${z}. Expected one of: ${K.join(", ")}`)}function _(H,K,z,$){try{return H(K)}catch(Q){let Z=$===void 0?z:`${z} element [${$}]`,B=Q instanceof Error?Q.message:String(Q);throw new Y("PARSE",`Failed to parse ${Z}: ${B}`).withCause(Q)}}function P(H,K){let{default:z,choices:$,parse:Q}=H;if(z===void 0)return;if($)if(Array.isArray(z))for(let Z of z)I(String(Z),$,K);else I(String(z),$,K);if(Q){if(Array.isArray(z))return z.map((Z,B)=>_(Q,String(Z),K,B));return _(Q,String(z),K)}if(H.type==="path"){if(Array.isArray(z))return z.map((Z)=>J(String(Z)));return J(String(z))}return z}function N(H,K){if(H)for(let[z,$]of Object.entries(H)){let Q=$.parse;if(Q&&Q.constructor.name==="AsyncFunction")throw new Y("CONFIG",`Async parse not supported for flag --${z}. Use a sync parser; do async work in run().`)}if(K)for(let z of K){let $=z.parse;if($&&$.constructor.name==="AsyncFunction")throw new Y("CONFIG",`Async parse not supported for argument <${z.name}>. Use a sync parser; do async work in run().`)}}function h(H,K,z){let $=`--${H}`,Q=K.choices,Z=K.parse;if(K.multiple&&Array.isArray(z)){if(K.type==="boolean")return z.filter((B)=>typeof B==="boolean");return z.map((B,W)=>{if(Q)I(B,Q,$);if(Z)return _(Z,B,$,W);return F(B,K.type,$)})}if(K.type==="boolean"){if(typeof z==="boolean")return z;throw new Y("PARSE",`Expected boolean value for flag "${$}", got ${typeof z}`)}if(typeof z==="string"){if(Q)I(z,Q,$);if(Z)return _(Z,z,$);return F(z,K.type,$)}throw new Y("PARSE",`Internal: unexpected value shape for flag "${$}" (got ${typeof z})`)}function C(H,K,z){let $={};for(let Q in H){let Z=K[Q]??Q;if(!(Z in z))continue;let B=H[Q],W=$[Z];if(W!==void 0&&Array.isArray(W)&&Array.isArray(B))W.push(...B);else $[Z]=B}return $}function V(H,K,z){if(!H)return{};let $=C(K,z,H),Q={};for(let[Z,B]of Object.entries(H)){let W=$[Z];if(W!==void 0){Q[Z]=h(Z,B,W);continue}Q[Z]=P(B,`--${Z}`)}return Q}function D(H,K){if(!H)return;for(let[z,$]of Object.entries(H))if($.required===!0&&$.default===void 0){if(K[z]===void 0)throw new Y("VALIDATION",`Missing required flag "--${z}"`)}}function b(H,K){if(!H)return{};let z={},$=0;for(let Q of H){let{name:Z}=Q,B=`<${Z}>`,W=Q.choices,X=Q.parse,G=(M,U)=>{if(W)I(M,W,B);if(X)return _(X,M,B,U);return Q.type===void 0?M:F(M,Q.type,B)};if(Q.variadic){let M=K.slice($);z[Z]=M.map((U,q)=>G(U,q)),$=K.length}else if($<K.length)z[Z]=G(K[$]),$++;else z[Z]=P(Q,B)}return z}function v(H,K,z){if(!K)return;for(let $ of H){if($==="--")return;if(!$.startsWith("--no-"))continue;let Q=$.indexOf("="),Z=Q===-1?$.slice(5):$.slice(5,Q);if(!Z)continue;let B=z[Z];if(!B)continue;if(B===Z)continue;if(K[B]?.type!=="boolean")continue;throw new Y("PARSE",`Cannot negate alias "--no-${Z}"; use "--no-${B}" instead`)}}function S(H,K){let{args:z,effectiveFlags:$}=H;N($,z);let{options:Q,aliasToName:Z}=w($);v(K,$,Z);let B;try{B=L({args:K,options:Q,strict:!0,allowPositionals:!0,allowNegative:!0,tokens:!0})}catch(U){if(U instanceof Error){let q=U.message.match(/Unknown option '(.+?)'/);if(q)throw new Y("PARSE",`Unknown flag "${q[1]}"`).withCause(U)}throw new Y("PARSE","Failed to parse command arguments").withCause(U)}let W=[],X=[];if(B.tokens){let U=!1;for(let q of B.tokens){if(q.kind==="option-terminator"){U=!0;continue}if(q.kind==="positional")(U?W:X).push(q.value??"")}}else X.push(...B.positionals);let G=V($,B.values,Z);return{args:b(z,X),flags:G,rawArgs:W}}function A(H,K){let{args:z,effectiveFlags:$}=H,Q=K.args,Z=K.flags;if(z)for(let B of z){let{name:W}=B,X=`argument "<${W}>"`,G=Q[W];if(B.required===!0&&B.default===void 0){if(B.variadic){if(!Array.isArray(G)||G.length===0)throw new Y("VALIDATION",`Missing required ${X}`)}else if(G===void 0)throw new Y("VALIDATION",`Missing required ${X}`)}}D($,Z)}function p(H,K,z){if(typeof H!=="string"||H.length===0)throw new Y("DEFINITION",`Subcommand "${z}" has an invalid alias: must be a non-empty string`);if(/\s/.test(H))throw new Y("DEFINITION",`Subcommand "${z}" alias "${H}" must not contain whitespace`);if(H.startsWith("-"))throw new Y("DEFINITION",`Subcommand "${z}" alias "${H}" must not start with "-" (reserved for flags)`);if(H===K)throw new Y("DEFINITION",`Subcommand "${z}" alias "${H}" must not equal its own canonical name`)}function g(H,K,z){let{canonicalName:$,aliases:Q}=H;if(Q){let Z=new Set;for(let B of Q){if(p(B,$,z),Z.has(B))throw new Y("DEFINITION",`Subcommand "${z}" lists alias "${B}" more than once`);Z.add(B)}}for(let[Z,B]of Object.entries(K)){let W=B.meta.aliases;if(W?.includes($))throw new Y("DEFINITION",`Subcommand "${z}" canonical name "${$}" collides with alias of sibling "${Z}"`);if(!Q)continue;for(let X of Q){if(X===Z)throw new Y("DEFINITION",`Subcommand "${z}" alias "${X}" collides with sibling canonical name "${Z}"`);if(W?.includes(X))throw new Y("DEFINITION",`Subcommand "${z}" alias "${X}" collides with alias of sibling "${Z}"`)}}}function j(H){let K=H.choices;if(K!==void 0&&K.length>0)return K[0];switch(H.type){case"number":return"1";case"boolean":return"true";case"url":return"https://example.com";case"json":return"null";default:return"sample"}}function m(H){let K=[],z=H.effectiveFlags;for(let[Q,Z]of Object.entries(z)){if(Z.required!==!0||Z.default!==void 0)continue;if(K.push(`--${Q}`),Z.type!=="boolean")K.push(j(Z))}let $=H.args;if($)for(let Q of $){if(Q.required!==!0||Q.default!==void 0)continue;K.push(j(Q))}return K}function Hz(H){let K=[{command:H,path:[H.meta.name]}],z=new Set;while(K.length>0){let $=K.pop();if(!$)break;let{command:Q,path:Z}=$;if(z.has(Q))continue;z.add(Q);try{let W=S(Q,m(Q));A(Q,W)}catch(W){let X=W instanceof Error?W.message:"Unknown validation error";throw new Y("DEFINITION",`Command "${Z.join(" ")}" failed runtime validation: ${X}`).withCause(W)}let B={};for(let[W,X]of Object.entries(Q.subCommands))g({canonicalName:W,aliases:X.meta.aliases},B,[...Z,W].join(" ")),B[W]=X;for(let[W,X]of Object.entries(Q.subCommands))K.push({command:X,path:[...Z,W]})}}
|
|
3
|
+
export{f as a,Y as b,S as c,A as d,p as e,g as f,Hz as g};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crustjs/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.19",
|
|
4
4
|
"description": "Core library for the Crust CLI framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -41,6 +41,9 @@
|
|
|
41
41
|
"test": "bun test",
|
|
42
42
|
"publish": "bun publish --no-git-checks || true"
|
|
43
43
|
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@crustjs/utils": "0.0.3"
|
|
46
|
+
},
|
|
44
47
|
"devDependencies": {
|
|
45
48
|
"@crustjs/config": "0.0.0",
|
|
46
49
|
"bunup": "^0.16.31"
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
var j=import.meta.require;class Y extends Error{code;details;cause;constructor(B,J,...H){super(J);this.name="CrustError",this.code=B,this.details=H[0]}is(B){return this.code===B}withCause(B){return this.cause=B,this}}import{parseArgs as F}from"util";function P(B){let J={},H={};if(!B)return{options:J,aliasToName:H};let K=new Map;for(let z of Object.keys(B))K.set(z,z);for(let[z,G]of Object.entries(B)){if(z.startsWith("no-")){let X=z.slice(3);throw new Y("DEFINITION",`Flag "--${z}" must not use "no-" prefix; define "${X}" and negate with "--no-${X}"`)}let Q=G.type==="boolean"?"boolean":"string",W={type:Q};if(G.multiple)W.multiple=!0;if(G.short){if(G.short.startsWith("no-"))throw new Y("DEFINITION",`Short alias "-${G.short}" on "--${z}" must not use "no-" prefix (reserved for negation)`);let X=K.get(G.short);if(X)throw new Y("DEFINITION",`Alias collision: "-${G.short}" is used by both "--${X}" and "--${z}"`);K.set(G.short,z),H[G.short]=z,W.short=G.short}if(G.aliases)for(let X of G.aliases){if(X.startsWith("no-"))throw new Y("DEFINITION",`Alias "--${X}" on "--${z}" must not use "no-" prefix (reserved for negation)`);let Z=K.get(X);if(Z)throw new Y("DEFINITION",`Alias collision: "${X.length===1?"-":"--"}${X}" is used by both "--${Z}" and "--${z}"`);K.set(X,z),H[X]=z;let q={type:Q};if(G.multiple)q.multiple=!0;J[X]=q}J[z]=W}return{options:J,aliasToName:H}}function U(B,J,H){if(J==="number"){let K=Number(B);if(Number.isNaN(K))throw new Y("PARSE",`Expected number for ${H}, got "${B}"`);return K}if(J==="boolean")return B==="true"||B==="1";return B}function S(B,J,H){let K=`--${B}`;if(J.multiple&&Array.isArray(H))return J.type==="boolean"?H.filter((z)=>typeof z==="boolean"):H.map((z)=>U(z,J.type,K));if(J.type==="boolean"){if(typeof H==="boolean")return H;throw new Y("PARSE",`Expected boolean value for flag "${K}", got ${typeof H}`)}if(typeof H==="string")return U(H,J.type,K);if(H===!0)return J.default??void 0;return H}function h(B,J,H){let K={};for(let z in B){let G=J[z]??z;if(!(G in H))continue;let Q=B[z],W=K[G];if(W!==void 0&&Array.isArray(W)&&Array.isArray(Q))W.push(...Q);else K[G]=Q}return K}function R(B,J,H){if(!B)return{};let K=h(J,H,B),z={};for(let[G,Q]of Object.entries(B)){let W=K[G];if(W!==void 0){z[G]=S(G,Q,W);continue}z[G]=Q.default??void 0}return z}function w(B,J){if(!B)return;for(let[H,K]of Object.entries(B))if(K.required===!0&&K.default===void 0){if(J[H]===void 0)throw new Y("VALIDATION",`Missing required flag "--${H}"`)}}function A(B,J){if(!B)return{};let H={},K=0;for(let z of B){let{name:G}=z;if(z.variadic){let Q=J.slice(K);H[G]=z.type==="string"?Q:Q.map((W)=>U(W,z.type,`<${G}>`)),K=J.length}else if(K<J.length)H[G]=U(J[K],z.type,`<${G}>`),K++;else H[G]=z.default??void 0}return H}function k(B,J,H){if(!J)return;for(let K of B){if(K==="--")return;if(!K.startsWith("--no-"))continue;let z=K.indexOf("="),G=z===-1?K.slice(5):K.slice(5,z);if(!G)continue;let Q=H[G];if(!Q)continue;if(Q===G)continue;if(J[Q]?.type!=="boolean")continue;throw new Y("PARSE",`Cannot negate alias "--no-${G}"; use "--no-${Q}" instead`)}}function M(B,J){let{args:H,effectiveFlags:K}=B,{options:z,aliasToName:G}=P(K);k(J,K,G);let Q;try{Q=F({args:J,options:z,strict:!0,allowPositionals:!0,allowNegative:!0,tokens:!0})}catch(_){if(_ instanceof Error){let $=_.message.match(/Unknown option '(.+?)'/);if($)throw new Y("PARSE",`Unknown flag "${$[1]}"`).withCause(_)}throw new Y("PARSE","Failed to parse command arguments").withCause(_)}let W=[],X=[];if(Q.tokens){let _=!1;for(let $ of Q.tokens){if($.kind==="option-terminator"){_=!0;continue}if($.kind==="positional")(_?W:X).push($.value??"")}}else X.push(...Q.positionals);let Z=R(K,Q.values,G);return{args:A(H,X),flags:Z,rawArgs:W}}function I(B,J){let{args:H,effectiveFlags:K}=B,z=J.args,G=J.flags;if(H)for(let Q of H){let{name:W}=Q,X=`argument "<${W}>"`,Z=z[W];if(Q.required===!0&&Q.default===void 0){if(Q.variadic){if(!Array.isArray(Z)||Z.length===0)throw new Y("VALIDATION",`Missing required ${X}`)}else if(Z===void 0)throw new Y("VALIDATION",`Missing required ${X}`)}}w(K,G)}function y(B,J,H){if(typeof B!=="string"||B.length===0)throw new Y("DEFINITION",`Subcommand "${H}" has an invalid alias: must be a non-empty string`);if(/\s/.test(B))throw new Y("DEFINITION",`Subcommand "${H}" alias "${B}" must not contain whitespace`);if(B.startsWith("-"))throw new Y("DEFINITION",`Subcommand "${H}" alias "${B}" must not start with "-" (reserved for flags)`);if(B===J)throw new Y("DEFINITION",`Subcommand "${H}" alias "${B}" must not equal its own canonical name`)}function x(B,J,H){let{canonicalName:K,aliases:z}=B;if(z){let G=new Set;for(let Q of z){if(y(Q,K,H),G.has(Q))throw new Y("DEFINITION",`Subcommand "${H}" lists alias "${Q}" more than once`);G.add(Q)}}for(let[G,Q]of Object.entries(J)){let W=Q.meta.aliases;if(W?.includes(K))throw new Y("DEFINITION",`Subcommand "${H}" canonical name "${K}" collides with alias of sibling "${G}"`);if(!z)continue;for(let X of z){if(X===G)throw new Y("DEFINITION",`Subcommand "${H}" alias "${X}" collides with sibling canonical name "${G}"`);if(W?.includes(X))throw new Y("DEFINITION",`Subcommand "${H}" alias "${X}" collides with alias of sibling "${G}"`)}}}function O(B){switch(B.type){case"number":return"1";case"boolean":return"true";default:return"sample"}}function T(B){let J=[],H=B.effectiveFlags;for(let[z,G]of Object.entries(H)){if(G.required!==!0||G.default!==void 0)continue;if(J.push(`--${z}`),G.type!=="boolean")J.push(O(G))}let K=B.args;if(K)for(let z of K){if(z.required!==!0||z.default!==void 0)continue;J.push(O(z))}return J}function b(B){let J=[{command:B,path:[B.meta.name]}],H=new Set;while(J.length>0){let K=J.pop();if(!K)break;let{command:z,path:G}=K;if(H.has(z))continue;H.add(z);try{let W=M(z,T(z));I(z,W)}catch(W){let X=W instanceof Error?W.message:"Unknown validation error";throw new Y("DEFINITION",`Command "${G.join(" ")}" failed runtime validation: ${X}`).withCause(W)}let Q={};for(let[W,X]of Object.entries(z.subCommands))x({canonicalName:W,aliases:X.meta.aliases},Q,[...G,W].join(" ")),Q[W]=X;for(let[W,X]of Object.entries(z.subCommands))J.push({command:X,path:[...G,W]})}}
|
|
3
|
-
export{j as a,Y as b,M as c,I as d,y as e,x as f,b as g};
|