@crustjs/core 0.0.19 → 0.2.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.
@@ -0,0 +1,828 @@
1
+ //#region ../utils/src/json.d.ts
2
+ /** Any value representable in a JSON document. */
3
+ type JsonValue = string | number | boolean | null | readonly JsonValue[] | JsonObject;
4
+ interface JsonObject {
5
+ [key: string]: JsonValue;
6
+ }
7
+ /** Preserve a type when all of its properties are recursively JSON-compatible. */
8
+ type JsonCompatible<T> = T extends JsonValue ? T : T extends ((...arguments_: never[]) => void) ? never : T extends readonly unknown[] ? { [K in keyof T]: JsonCompatible<T[K]>; } : T extends object ? { [K in keyof T]: JsonCompatible<T[K]>; } : never;
9
+ //#endregion
10
+ //#region ../utils/src/primitive.d.ts
11
+ /** Supported primitive type literals shared by Crust packages. */
12
+ type BaseValueType = "string" | "number" | "boolean";
13
+ //#endregion
14
+ //#region ../utils/src/schema.d.ts
15
+ /** The Standard Typed types interface. */
16
+ interface StandardSchemaTypes<Input = unknown, Output = Input> {
17
+ /** The input type of the schema. */
18
+ readonly input: Input;
19
+ /** The output type of the schema. */
20
+ readonly output: Output;
21
+ }
22
+ /** The result interface if validation succeeds. */
23
+ interface StandardSchemaSuccessResult<Output> {
24
+ /** The typed output value. */
25
+ readonly value: Output;
26
+ /** A falsy value for `issues` indicates success. */
27
+ readonly issues?: undefined;
28
+ }
29
+ /** The result interface if validation fails. */
30
+ interface StandardSchemaFailureResult {
31
+ /** The issues of failed validation. */
32
+ readonly issues: ReadonlyArray<StandardSchemaIssue>;
33
+ }
34
+ /** The result interface of the validate function. */
35
+ type StandardSchemaResult<Output> = StandardSchemaSuccessResult<Output> | StandardSchemaFailureResult;
36
+ /** The issue interface of the failure output. */
37
+ interface StandardSchemaIssue {
38
+ /** The error message of the issue. */
39
+ readonly message: string;
40
+ /** The path of the issue, if any. */
41
+ readonly path?: ReadonlyArray<PropertyKey | StandardSchemaPathSegment> | undefined;
42
+ }
43
+ /** The path segment interface of the issue. */
44
+ interface StandardSchemaPathSegment {
45
+ /** The key representing a path segment. */
46
+ readonly key: PropertyKey;
47
+ }
48
+ /** The Standard Schema properties interface. */
49
+ interface StandardSchemaProps<Input = unknown, Output = Input> {
50
+ /** The version number of the standard. */
51
+ readonly version: 1;
52
+ /** The vendor name of the schema library. */
53
+ readonly vendor: string;
54
+ /** Inferred types associated with the schema. */
55
+ readonly types?: StandardSchemaTypes<Input, Output> | undefined;
56
+ /** Validates unknown input values. */
57
+ readonly validate: (value: StandardSchemaTypes["input"]) => StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>;
58
+ }
59
+ /**
60
+ * A Standard Schema-compatible schema object.
61
+ *
62
+ * Any schema library implementing the Standard Schema v1 spec
63
+ * (Zod, Effect, Valibot, ArkType, etc.) produces objects matching this type.
64
+ */
65
+ type StandardSchema<Input = unknown, Output = Input> = {
66
+ /** The Standard Schema properties. */
67
+ readonly "~standard": StandardSchemaProps<Input, Output>;
68
+ };
69
+ /** Infer the output type produced by a Standard Schema on success. */
70
+ type InferOutput<S extends StandardSchema> = NonNullable<S["~standard"]["types"]>["output"];
71
+ //#endregion
72
+ //#region src/identity.d.ts
73
+ declare const brand: unique symbol;
74
+ /** Stable identity shared by Extensions and documentation section renderers. */
75
+ type ExtensionId = string & {
76
+ readonly [brand]: true;
77
+ };
78
+ /** Mint an Extension identity from any non-blank, trimmed string. */
79
+ declare function defineExtensionId(id: string): ExtensionId;
80
+ //#endregion
81
+ //#region src/validation/shared.d.ts
82
+ type Awaitable<T> = T | Promise<T>;
83
+ type Simplify<T> = { [K in keyof T]: T[K]; };
84
+ type MergeContext<A, B> = A & B;
85
+ /** Provider replacement is last-write-wins; an open name may leave any earlier value in place. */
86
+ type MergeProviders<A, B> = keyof A extends never ? B : keyof B extends never ? A : string extends keyof B ? Record<string, A[keyof A] | B[string]> : [keyof A & keyof B] extends [never] ? A & B : Omit<A, keyof B> & B;
87
+ /**
88
+ * Extract the narrowed canonical `name` literal from a definition.
89
+ * Open name domains carry no spelling proof; attachment must retain
90
+ * their uncertainty rather than treating this absence as an empty namespace.
91
+ */
92
+ type DefName<T> = T extends {
93
+ name: infer N extends string;
94
+ } ? IsClosedName<N> extends true ? N : never : never;
95
+ type UnionToIntersection<U> = (U extends unknown ? (x: U) => void : never) extends ((x: infer I) => void) ? I : never;
96
+ type IsUnion<T> = [T] extends [UnionToIntersection<T>] ? false : true;
97
+ /**
98
+ * `true` only for a single statically known fixed-length tuple whose members
99
+ * are not unions. A conditionally assembled collection (`cond ? [a] : [b]` or
100
+ * `[cond ? a : b]`) infers as a union at the tuple or member level, and a
101
+ * variable-length array (`const xs: (typeof a)[]`) may be empty or partially
102
+ * populated at runtime; such contributions must stay runtime-only.
103
+ */
104
+ type IsStaticTuple<Cs extends readonly unknown[]> = number extends Cs["length"] ? false : IsUnion<Cs> extends true ? false : true extends { [I in keyof Cs]: IsUnion<Cs[I]>; }[number] ? false : true;
105
+ /** Whether a fixed tuple has one closed canonical name per slot. */
106
+ type HasClosedNames<Ds extends readonly unknown[]> = number extends Ds["length"] ? false : IsUnion<Ds> extends true ? false : false extends { [I in keyof Ds]: Ds[I] extends {
107
+ name: infer N extends string;
108
+ } ? IsUnion<N> extends true ? false : IsClosedName<N> : false; }[number] ? false : true;
109
+ /** Brand statically known spelling collisions while allowing open names. */
110
+ type CollisionBrand<S extends string, Existing extends string, Key extends string, Before extends string, After extends string> = string extends S | Existing ? {} : [S & Existing] extends [never] ? {} : { readonly [K in Key]: `${Before}"${S & Existing}"${After}`; };
111
+ /** Brand a statically known empty literal while allowing widened and generic names. */
112
+ type EmptyLiteralNameBrand<Name extends string, Err> = IsClosedName<Name> extends true ? ("" extends Name ? Err : {}) : {};
113
+ /**
114
+ * Brand a definition whose custom parser can return a Promise — parse results
115
+ * are consumed synchronously during argv parsing. `Extract` keeps the check
116
+ * union-aware (a sometimes-async `cond ? Promise.resolve(x) : x` parser is
117
+ * caught) while `any`-returning parsers stay unbranded.
118
+ */
119
+ type AsyncParseBrand<T> = T extends {
120
+ parse?: (...args: never[]) => infer R;
121
+ } ? Extract<R, Promise<unknown>> extends never ? {} : {
122
+ readonly FIX_ASYNC_PARSE: "parse must be synchronous; do async work in run()";
123
+ } : {};
124
+ /** Brand literal defaults that fall outside a literal `choices` tuple. */
125
+ type DefaultWithinChoicesBrand<T> = T extends {
126
+ choices: readonly (infer Choice extends string)[];
127
+ default: infer Default;
128
+ } ? string extends Choice ? {} : Default extends readonly string[] ? string extends Default[number] ? {} : Exclude<Default[number], Choice> extends never ? {} : {
129
+ readonly FIX_DEFAULT_CHOICE: "default must be one of choices";
130
+ } : Default extends string ? string extends Default ? {} : Exclude<Default, Choice> extends never ? {} : {
131
+ readonly FIX_DEFAULT_CHOICE: "default must be one of choices";
132
+ } : {} : {};
133
+ /** Finite literal domains have required record keys; infinite templates and branded strings do not.
134
+ * Distribute first so a finite union member cannot hide an open member's index signature.
135
+ */
136
+ type IsClosedName<N extends string> = false extends (N extends unknown ? ({} extends Record<N, true> ? false : true) : never) ? false : true;
137
+ /** Reject independently provable invalid local values, including members of uncertain definitions. */
138
+ type LocalValueBrand<T> = UnionToIntersection<T extends unknown ? AsyncParseBrand<T> & DefaultWithinChoicesBrand<T> : never>;
139
+ //#endregion
140
+ //#region src/command/snapshot.d.ts
141
+ /**
142
+ * Serializable projection of a positional argument definition.
143
+ *
144
+ * Carries everything help/man/tooling surfaces need; `parse` functions and
145
+ * schemas never cross the boundary.
146
+ *
147
+ * Fields with `undefined` values are dropped entirely (see `freezeCompact`),
148
+ * so absent means "not declared".
149
+ *
150
+ * @example
151
+ * For `{ name: "port", type: "number", default: 3000 }`:
152
+ * ```ts
153
+ * { name: "port", type: "number", default: 3000 }
154
+ * // `required`/`variadic` keys absent — not declared on the def
155
+ * ```
156
+ */
157
+ interface ArgSnapshot {
158
+ /** Argument name as defined, e.g. `"file"`. */
159
+ readonly name: string;
160
+ /** Value type (`"string"`, `"number"`, …); absent for schema-backed args. */
161
+ readonly type?: ValueType;
162
+ /** Human-readable description for help text. */
163
+ readonly description?: string;
164
+ /** `true` when parsing fails if the argument is missing. */
165
+ readonly required?: boolean;
166
+ /** `true` when the argument collects all remaining positionals into an array. */
167
+ readonly variadic?: boolean;
168
+ /** Static enum of accepted values, e.g. `["json", "text"]`. */
169
+ readonly choices?: readonly string[];
170
+ /** Declared default value; `URL` defaults are serialized to their `href` string. */
171
+ readonly default?: unknown;
172
+ }
173
+ /**
174
+ * Serializable projection of a flag definition. The flag's canonical name is
175
+ * the key it sits under in {@link CommandSnapshot.flags}, not a field here.
176
+ *
177
+ * @example
178
+ * For `{ verbose: { type: "boolean", short: "v", default: false } }`:
179
+ * ```ts
180
+ * snapshot.flags.verbose
181
+ * // => { type: "boolean", short: "v", default: false, negatable: true }
182
+ * ```
183
+ */
184
+ interface FlagSnapshot {
185
+ /** Value type, e.g. `"boolean"`, `"string"`, `"number"`. */
186
+ readonly type: ValueType;
187
+ /** Human-readable description for help text. */
188
+ readonly description?: string;
189
+ /** Single-character short alias without the dash, e.g. `"v"` for `-v`. */
190
+ readonly short?: string;
191
+ /** Additional aliases without dashes; one-character aliases accept one or two dashes. */
192
+ readonly aliases?: readonly string[];
193
+ /** `true` when parsing fails if the flag is not provided. */
194
+ readonly required?: boolean;
195
+ /** `true` when the flag can repeat and collects values into an array. */
196
+ readonly multiple?: boolean;
197
+ /** `true` when the flag accepts generated `--no-<name>` spellings. */
198
+ readonly negatable: boolean;
199
+ /** `true` when a boolean flag opted out of the `--no-<name>` spelling. */
200
+ readonly noNegate?: boolean;
201
+ /** Static enum of accepted values, e.g. `["debug", "info", "error"]`. */
202
+ readonly choices?: readonly string[];
203
+ /** Declared default value; `URL` defaults are serialized to their `href` string. */
204
+ readonly default?: unknown;
205
+ }
206
+ /**
207
+ * A readonly, serializable description of a command, exposed across public
208
+ * API boundaries (Command Action context, Extension hooks, and
209
+ * `COMMAND_NOT_FOUND` error details) instead of internal command nodes.
210
+ *
211
+ * `flags` contains the effective (Context-owned + local merged) flags — the same
212
+ * set the parser accepts for the command.
213
+ *
214
+ * @example
215
+ * ```ts
216
+ * defineCommand("add", (cmd) =>
217
+ * cmd
218
+ * .args({ name: "name", type: "string", required: true })
219
+ * .flags({ name: "force", type: "boolean", short: "f" })
220
+ * .action(() => {}),
221
+ * );
222
+ * // snapshots to:
223
+ * // {
224
+ * // meta: { name: "add" },
225
+ * // hasAction: true,
226
+ * // args: [{ name: "name", type: "string", required: true }],
227
+ * // flags: { force: { type: "boolean", short: "f", negatable: true } },
228
+ * // subCommands: {},
229
+ * // }
230
+ * ```
231
+ */
232
+ interface CommandSnapshot {
233
+ /** Command metadata, including routing and presentation options. */
234
+ readonly meta: Readonly<CommandMeta>;
235
+ /** Whether the command defines a Command Action */
236
+ readonly hasAction: boolean;
237
+ /** Positional argument snapshots in declaration order. */
238
+ readonly args: readonly ArgSnapshot[];
239
+ /** Effective flags keyed by canonical flag name. */
240
+ readonly flags: Readonly<Record<string, FlagSnapshot>>;
241
+ /** Direct subcommand snapshots keyed by canonical name (includes hidden ones). */
242
+ readonly subCommands: Readonly<Record<string, CommandSnapshot>>;
243
+ }
244
+ //#endregion
245
+ //#region src/parsing/parser.d.ts
246
+ type RunInputValue = URL | JsonValue | readonly RunInputValue[];
247
+ interface RunInputPayload {
248
+ readonly args?: Readonly<Record<string, RunInputValue | undefined>>;
249
+ readonly flags?: Readonly<Record<string, RunInputValue | undefined>>;
250
+ readonly raw?: readonly string[];
251
+ }
252
+ //#endregion
253
+ //#region src/types.d.ts
254
+ /** Injectable output callbacks threaded through one invocation. */
255
+ interface InvocationIO {
256
+ /** Write a line of standard output (injectable text callback) */
257
+ stdout: (text: string) => void;
258
+ /** Write a line of diagnostic output (injectable text callback) */
259
+ stderr: (text: string) => void;
260
+ }
261
+ /**
262
+ * Supported type literals for args and flags.
263
+ *
264
+ * Extends `BaseValueType` (`"string" | "number" | "boolean"`) with three
265
+ * formatted built-ins:
266
+ *
267
+ * - `"url"` — the raw value is parsed via `new URL()` into a {@link URL}
268
+ * - `"path"` — the raw value is expanded (`~`) and resolved against
269
+ * `process.cwd()` into an absolute `string`
270
+ * - `"json"` — the raw value is parsed via `JSON.parse()` into `unknown`
271
+ */
272
+ type ValueType = BaseValueType | "url" | "path" | "json";
273
+ /** Resolve a {@link ValueType} literal to its runtime TypeScript type. */
274
+ type Resolve<T extends ValueType> = {
275
+ string: string;
276
+ number: number;
277
+ boolean: boolean;
278
+ url: URL;
279
+ path: string;
280
+ json: unknown;
281
+ }[T];
282
+ /**
283
+ * Resolve the inferred runtime type for a flag/arg definition.
284
+ *
285
+ * When the def declares a `parse` escape hatch (only allowed on `"string"`
286
+ * variants), the inferred type is `ReturnType<typeof parse>`. Optional parsers
287
+ * also retain the unparsed output branch. String defs
288
+ * with a literal `choices` tuple narrow to the union of those literals.
289
+ * Otherwise it delegates to {@link Resolve} on the declared `type`.
290
+ */
291
+ type ResolveBaseType<F> = "parse" extends keyof F ? F["parse"] extends (infer Parse) ? Parse extends ((raw: string) => infer R) ? R : ResolveUnparsedType<F> : never : ResolveUnparsedType<F>;
292
+ type ResolveUnparsedType<F> = F extends {
293
+ type: "string";
294
+ choices: readonly (infer C extends string)[];
295
+ } ? C : F extends {
296
+ type: infer T extends ValueType;
297
+ } ? Resolve<T> : never;
298
+ /** Shared fields present on every positional argument definition */
299
+ interface ArgDefBase {
300
+ /** The argument name (used as the key in the parsed result and in help text) */
301
+ name: string;
302
+ /** Human-readable description for help text */
303
+ description?: string;
304
+ /**
305
+ * When `true`, the parser throws if the argument is not provided.
306
+ *
307
+ * For variadic args without a default, the validated output is a nonempty tuple.
308
+ */
309
+ required?: true;
310
+ /** Not supported with core value options — see {@link SchemaArgDef} */
311
+ schema?: never;
312
+ /**
313
+ * When `true`, collects all remaining positional values into an array.
314
+ *
315
+ * The validated output is always an array, never `undefined`.
316
+ * Required core variadics without defaults infer `[T, ...T[]]` after
317
+ * required validation; other core variadics infer `T[]`.
318
+ */
319
+ variadic?: true;
320
+ }
321
+ /** A positional argument whose value is a string */
322
+ interface StringArgDef<ParseOutput = unknown> extends ArgDefBase {
323
+ type: "string";
324
+ /** Default string value when the argument is not provided */
325
+ default?: string;
326
+ /**
327
+ * Static enum of valid values for this argument.
328
+ *
329
+ * Checked for argv and structured invocation input before `parse` runs.
330
+ * Typed input also proves membership statically. Passing a value outside
331
+ * `choices` throws `CrustError("PARSE", …)` before any `parse` transform
332
+ * is applied. Also consumed by shell-completion extensions
333
+ * (e.g. `@crustjs/extensions`) to emit value candidates.
334
+ *
335
+ * Only available on string-typed args; not supported on number/boolean.
336
+ *
337
+ * @example
338
+ * { name: "target", type: "string", choices: ["browser", "bun", "node"] }
339
+ */
340
+ choices?: readonly string[];
341
+ /**
342
+ * Custom synchronous parser for the raw argv string. Runs after `choices`
343
+ * validation and per element for variadic args. Its return type becomes the
344
+ * argument's inferred runtime type; declared defaults are parsed too.
345
+ *
346
+ * @example
347
+ * { name: "port", type: "string", parse: (s) => Number(s) }
348
+ */
349
+ parse?: (raw: string) => ParseOutput;
350
+ }
351
+ /** A positional argument whose value is a number */
352
+ interface NumberArgDef extends ArgDefBase {
353
+ type: "number";
354
+ choices?: never;
355
+ /** Default number value when the argument is not provided */
356
+ default?: number;
357
+ /** Not supported on number args — use `type: "string"` with `parse`. */
358
+ parse?: never;
359
+ }
360
+ /** A positional argument whose value is a boolean */
361
+ interface BooleanArgDef extends ArgDefBase {
362
+ type: "boolean";
363
+ choices?: never;
364
+ /** Default boolean value when the argument is not provided */
365
+ default?: boolean;
366
+ /** Not supported on boolean args — use `type: "string"` with `parse`. */
367
+ parse?: never;
368
+ }
369
+ /** A positional argument whose value is a {@link URL} */
370
+ interface UrlArgDef extends ArgDefBase {
371
+ type: "url";
372
+ choices?: never;
373
+ /** Default URL value when the argument is not provided */
374
+ default?: URL;
375
+ /** Not supported on url args — use `type: "string"` with `parse`. */
376
+ parse?: never;
377
+ }
378
+ /** A positional argument whose value is an absolute filesystem path */
379
+ interface PathArgDef extends ArgDefBase {
380
+ type: "path";
381
+ choices?: never;
382
+ /** Default path string when the argument is not provided */
383
+ default?: string;
384
+ /** Not supported on path args — use `type: "string"` with `parse`. */
385
+ parse?: never;
386
+ }
387
+ /** A positional argument whose value is JSON parsed to `unknown` */
388
+ interface JsonArgDef extends ArgDefBase {
389
+ type: "json";
390
+ choices?: never;
391
+ /** Default parsed JSON value when the argument is not provided */
392
+ default?: unknown;
393
+ /** Not supported on json args — use `type: "string"` with `parse`. */
394
+ parse?: never;
395
+ }
396
+ /**
397
+ * A positional argument validated by a Standard Schema (exclusive mode).
398
+ *
399
+ * The schema receives the raw string token (`string | undefined` when the
400
+ * argument is absent; `string[]` for variadic args) and exclusively owns
401
+ * coercion, defaults, requiredness, choices, and validation. Its inferred
402
+ * output type reaches the Command Action. Core value options (`type`,
403
+ * `default`, `required`, `choices`, `parse`) cannot be mixed in.
404
+ */
405
+ interface SchemaArgDef {
406
+ /** The argument name (used as the key in the parsed result and in help text) */
407
+ name: string;
408
+ /** Human-readable description for help text */
409
+ description?: string;
410
+ /** When `true`, collects all remaining raw tokens into a `string[]` for the schema */
411
+ variadic?: true;
412
+ /** Standard Schema that owns coercion, defaults, requiredness, and validation */
413
+ schema: StandardSchema;
414
+ type?: never;
415
+ required?: never;
416
+ default?: never;
417
+ choices?: never;
418
+ parse?: never;
419
+ }
420
+ /**
421
+ * Defines a single positional argument for a CLI command.
422
+ *
423
+ * Discriminated by `type` for type-safe `default` values. Boolean toggle
424
+ * fields (`required`, `variadic`) only accept `true`.
425
+ *
426
+ * @example
427
+ * ```ts
428
+ * const args = [
429
+ * { name: "port", type: "number", description: "Port number", default: 3000 },
430
+ * { name: "name", type: "string", required: true },
431
+ * { name: "files", type: "string", variadic: true },
432
+ * ] as const satisfies ArgsDef;
433
+ * ```
434
+ */
435
+ type ArgDef = StringArgDef | NumberArgDef | BooleanArgDef | UrlArgDef | PathArgDef | JsonArgDef | SchemaArgDef;
436
+ /** Ordered tuple of positional argument definitions */
437
+ type ArgsDef = readonly ArgDef[];
438
+ /** Shared fields present on every flag definition */
439
+ interface FlagDefBase {
440
+ /** Human-readable description for help text */
441
+ description?: string;
442
+ /** Single-character short alias (e.g. `"v"` → `-v`) */
443
+ short?: string;
444
+ /** Additional aliases (e.g. `["out"]` → `--out`); one-character aliases also accept one dash. */
445
+ aliases?: readonly string[];
446
+ /** When `true`, the parser throws if the flag is not provided */
447
+ required?: true;
448
+ /** Not supported with core value options — see {@link SchemaStringFlagDef} */
449
+ schema?: never;
450
+ }
451
+ /** Base for single-value flags — `multiple` must be omitted */
452
+ interface SingleFlagBase extends FlagDefBase {
453
+ /** Must be omitted for single-value flags — set to `true` for multi-value */
454
+ multiple?: never;
455
+ }
456
+ /** Base for multi-value flags — `multiple` is required as `true` */
457
+ interface MultiFlagBase extends FlagDefBase {
458
+ /** Collect repeated values into an array */
459
+ multiple: true;
460
+ }
461
+ type StringFlagFields<Default, ParseOutput> = {
462
+ /** Default string value, or string array for a multi-value flag. */
463
+ default?: Default;
464
+ /**
465
+ * Static enum of valid values for this flag.
466
+ *
467
+ * Checked for argv and structured invocation input before `parse` runs.
468
+ * Typed input also proves membership statically. Passing a value outside
469
+ * `choices` throws `CrustError("PARSE", …)` before any `parse` transform
470
+ * is applied. Also consumed by shell-completion extensions.
471
+ */
472
+ choices?: readonly string[];
473
+ /**
474
+ * Custom synchronous parser for the raw argv string. For multi-value
475
+ * flags it runs once per occurrence. The return type becomes the flag's
476
+ * inferred runtime value type.
477
+ */
478
+ parse?: (raw: string) => ParseOutput;
479
+ noNegate?: never;
480
+ };
481
+ type TypedFlagFields<T extends Exclude<ValueType, "string">, Default> = {
482
+ /** Default value, or value array for a multi-value flag. */
483
+ default?: Default;
484
+ choices?: never;
485
+ /** Only boolean flags support generated-negation opt-out. */
486
+ noNegate?: T extends "boolean" ? true : never;
487
+ /** Use `type: "string"` with `parse` for custom parsing. */
488
+ parse?: never;
489
+ };
490
+ type CoreFlagFields<T extends ValueType, Default, ParseOutput> = T extends "string" ? StringFlagFields<Default, ParseOutput> : T extends Exclude<ValueType, "string"> ? TypedFlagFields<T, Default> : never;
491
+ /** A single-value core flag for the declared value type. */
492
+ type TypedFlagDef<T extends ValueType, ParseOutput = unknown> = SingleFlagBase & {
493
+ type: T;
494
+ } & CoreFlagFields<T, Resolve<T>, ParseOutput>;
495
+ /** A repeatable core flag for the declared value type. */
496
+ type TypedMultiFlagDef<T extends ValueType, ParseOutput = unknown> = MultiFlagBase & {
497
+ type: T;
498
+ } & CoreFlagFields<T, readonly Resolve<T>[], ParseOutput>;
499
+ /** Shared fields for schema-backed flags (exclusive mode) */
500
+ interface SchemaFlagBase extends Omit<FlagDefBase, "schema" | "required"> {
501
+ /** Standard Schema that owns coercion, defaults, requiredness, and validation */
502
+ schema: StandardSchema;
503
+ required?: never;
504
+ default?: never;
505
+ choices?: never;
506
+ parse?: never;
507
+ }
508
+ /**
509
+ * A schema-backed flag that consumes a value token (`--flag value`).
510
+ * The schema receives the raw string (`string | undefined`, or
511
+ * `string[] | undefined` with `multiple: true`) and exclusively owns coercion,
512
+ * defaults, requiredness, and validation. `type` declares token consumption only.
513
+ */
514
+ interface SchemaStringFlagDef extends SchemaFlagBase {
515
+ type: "string";
516
+ /** When `true`, the schema receives `string[]` when present, or `undefined` when omitted. */
517
+ multiple?: true;
518
+ noNegate?: never;
519
+ }
520
+ /**
521
+ * A schema-backed toggle flag (no value token). The schema receives the raw
522
+ * `boolean | undefined` (or `boolean[] | undefined` with `multiple: true`).
523
+ */
524
+ interface SchemaBooleanFlagDef extends SchemaFlagBase {
525
+ type: "boolean";
526
+ /** When `true`, the schema receives `boolean[]` when present, or `undefined` when omitted. */
527
+ multiple?: true;
528
+ /** When `true`, reject `--no-{name}` (and negated aliases) at parse time and hide the generated help label */
529
+ noNegate?: true;
530
+ }
531
+ /**
532
+ * Defines a single named flag for a CLI command.
533
+ *
534
+ * Discriminated by `type` and `multiple` for type-safe `default` values.
535
+ * Boolean toggle fields (`required`, `multiple`) only accept `true`.
536
+ *
537
+ * @example
538
+ * ```ts
539
+ * const flags = {
540
+ * verbose: { type: "boolean", description: "Enable verbose logging", short: "v" },
541
+ * port: { type: "number", description: "Port number", default: 3000 },
542
+ * files: { type: "string", multiple: true, default: ["index.ts"] },
543
+ * } satisfies FlagsDef;
544
+ * ```
545
+ */
546
+ type FlagDef = { [T in ValueType]: TypedFlagDef<T> | TypedMultiFlagDef<T>; }[ValueType] | SchemaStringFlagDef | SchemaBooleanFlagDef;
547
+ /** Record mapping flag names to their definitions */
548
+ type FlagsDef = Record<string, FlagDef>;
549
+ /**
550
+ * A flag definition that carries its own name — the authoring shape
551
+ * produced by `defineFlag(name, def)` or written inline as an object
552
+ * literal (`{ name: "dry-run", type: "boolean" }`) and attached with the
553
+ * variadic `.flags(...defs)`.
554
+ */
555
+ type NamedFlagDef = FlagDef & {
556
+ readonly name: string;
557
+ };
558
+ /**
559
+ * Derive the internal `FlagsDef` record from a tuple of named flag
560
+ * definitions: each definition's `name` literal becomes a key, its value
561
+ * the definition without `name`.
562
+ *
563
+ * The `extends infer R extends FlagsDef` step defers evaluation so the
564
+ * result satisfies `FlagsDef` in generic positions.
565
+ */
566
+ type FlagWithoutName<D> = D extends unknown ? Omit<D, "name"> : never;
567
+ type NamedFlagsRecord<Defs extends readonly NamedFlagDef[]> = { [K in Defs[number]["name"]]: FlagWithoutName<Extract<Defs[number], {
568
+ name: K;
569
+ }>>; } extends (infer R extends FlagsDef) ? R : never;
570
+ /**
571
+ * Merges two flag sets as a flat intersection.
572
+ *
573
+ * Statically known shared keys are branded at compile time (`DuplicateNameBrand`,
574
+ * `ExistingFlagCollisionBrand`, `ProvideChecks`). A plain intersection stays
575
+ * flat in the checker — chained `.flags()`/`.provide()` calls cost constant
576
+ * instantiation depth, where per-call merge layers (mapped type or
577
+ * `Simplify<Omit & …>`) nested and hit TS2589 at ~47 / ~31 chained calls.
578
+ */
579
+ type MergeFlags<Base extends FlagsDef, Override extends FlagsDef> = Base & Override;
580
+ /**
581
+ * Infer the resolved type for a single ArgDef:
582
+ *
583
+ * - **variadic** → `[primitive, ...primitive[]]` when required without a default,
584
+ * otherwise `primitive[]` (always an array, never `undefined`)
585
+ * - **required** or **has default** → `primitive` (non-optional)
586
+ * - otherwise → `primitive | undefined`
587
+ *
588
+ * Required core variadics without defaults resolve to nonempty tuples.
589
+ * Schema-backed and default-backed arguments retain their own output contracts.
590
+ */
591
+ type InferArgValue<A extends ArgDef> = A extends {
592
+ schema: infer S extends StandardSchema;
593
+ } ? InferOutput<S> : A extends {
594
+ variadic: true;
595
+ } ? A extends {
596
+ required: true;
597
+ default?: never;
598
+ } ? [ResolveBaseType<A>, ...ResolveBaseType<A>[]] : ResolveBaseType<A>[] : ("variadic" extends keyof A ? true extends A["variadic"] ? ResolveBaseType<A>[] : never : never) | (A extends {
599
+ required: true;
600
+ } ? ResolveBaseType<A> : A extends {
601
+ default: infer Default;
602
+ } ? undefined extends Default ? ResolveBaseType<A> | undefined : ResolveBaseType<A> : ResolveBaseType<A> | undefined);
603
+ type DuplicateArgNames<A extends readonly ArgDef[], Seen extends string = never> = A extends readonly [infer Head extends ArgDef, ...infer Tail extends readonly ArgDef[]] ? (Head["name"] & Seen) | DuplicateArgNames<Tail, Seen | Head["name"]> : never;
604
+ type InferDuplicateArgs<A extends readonly ArgDef[]> = A extends readonly [infer Head extends ArgDef, ...infer Tail extends readonly ArgDef[]] ? { [K in Head["name"]]: InferArgValue<Head>; } & InferDuplicateArgs<Tail> : {};
605
+ /**
606
+ * Convert a literal ArgsDef tuple into resolved values keyed by argument name.
607
+ *
608
+ * Tuples with rest elements (`number extends A["length"]`) opt out to `{}`;
609
+ * the builder only produces fixed tuples. Unions of tuples distribute through
610
+ * `InferArgs`'s naked conditional, so each member is inferred separately.
611
+ */
612
+ type InferArgsTuple<A extends readonly ArgDef[]> = number extends A["length"] ? {} : [DuplicateArgNames<A>] extends [never] ? { [D in A[number] as D["name"]]: InferArgValue<D>; } : InferDuplicateArgs<A>;
613
+ /**
614
+ * Maps an ArgsDef tuple to resolved arg types keyed by each arg's `name`.
615
+ *
616
+ * @example
617
+ * ```ts
618
+ * type Result = InferArgs<readonly [
619
+ * { name: "port"; type: "number"; default: 3000 },
620
+ * { name: "name"; type: "string"; required: true },
621
+ * { name: "files"; type: "string"; variadic: true },
622
+ * ]>;
623
+ * // Result = { port: number; name: string; files: string[] }
624
+ * ```
625
+ */
626
+ type InferArgs<A> = A extends ArgsDef ? Simplify<InferArgsTuple<A>> : Record<string, never>;
627
+ /**
628
+ * Infer the resolved type for a single FlagDef:
629
+ *
630
+ * - **multiple** → wraps the resolved type in an array
631
+ * - **required** or **has default** → `primitive` (non-optional)
632
+ * - otherwise → `primitive | undefined`
633
+ */
634
+ type InferFlagValue<F extends FlagDef> = F extends {
635
+ schema: infer S extends StandardSchema;
636
+ } ? InferOutput<S> : F extends {
637
+ multiple: true;
638
+ } ? F extends {
639
+ required: true;
640
+ } ? ResolveBaseType<F>[] : F extends {
641
+ default: readonly unknown[];
642
+ } ? ResolveBaseType<F>[] : ResolveBaseType<F>[] | undefined : F extends {
643
+ required: true;
644
+ } ? ResolveBaseType<F> : F extends {
645
+ default: infer Default;
646
+ } ? undefined extends Default ? ResolveBaseType<F> | undefined : ResolveBaseType<F> : ResolveBaseType<F> | undefined;
647
+ /**
648
+ * Maps a full FlagsDef record to resolved flag types.
649
+ *
650
+ * @example
651
+ * ```ts
652
+ * type Result = InferFlags<{
653
+ * verbose: { type: "boolean" };
654
+ * port: { type: "number", default: 3000 };
655
+ * }>;
656
+ * // Result = { verbose: boolean | undefined; port: number }
657
+ * ```
658
+ */
659
+ type InferFlags<F> = F extends FlagsDef ? { [K in keyof F]: InferFlagValue<F[K]>; } : Record<string, never>;
660
+ type InputBaseValue<D> = true extends IsUnion<D> | IsUnion<D[keyof D & "type"]> ? never : D extends {
661
+ type: "boolean";
662
+ } ? "noNegate" extends keyof D ? true extends D["noNegate"] ? true : boolean : boolean : D extends {
663
+ schema: StandardSchema;
664
+ } ? D extends {
665
+ type: "boolean";
666
+ } ? boolean : string : D extends {
667
+ type: "json";
668
+ } ? JsonValue : "choices" extends keyof D ? Exclude<D["choices"], undefined> extends (infer Choices extends readonly string[]) ? [Choices] extends [never] ? D extends {
669
+ type: infer T extends ValueType;
670
+ } ? Resolve<T> : never : IsStaticTuple<Choices> extends true ? IsClosedName<Choices[number]> extends true ? Choices[number] : never : never : never : D extends {
671
+ parse: (raw: string) => infer _ParseOutput;
672
+ } ? string : D extends {
673
+ type: infer T extends ValueType;
674
+ } ? Resolve<T> : never;
675
+ type InputArgValue<D extends ArgDef, Value = InputBaseValue<D>> = [Value] extends [never] ? never : D extends {
676
+ variadic: true;
677
+ } ? RequiredArgNames<[D]> extends never ? Value[] : [Value, ...Value[]] : "variadic" extends keyof D ? true extends D["variadic"] ? never : Value : Value;
678
+ type InputFlagValue<D, Value = InputBaseValue<D>> = [Value] extends [never] ? never : D extends {
679
+ multiple: true;
680
+ } ? Value[] : "multiple" extends keyof D ? true extends D["multiple"] ? never : Value : Value;
681
+ type RequiredArgNames<A extends ArgsDef> = A[number] extends (infer D) ? D extends {
682
+ name: infer N extends string;
683
+ } ? "required" extends keyof D ? true extends D["required"] ? D extends {
684
+ default: infer Default;
685
+ } ? undefined extends Default ? N : never : N : never : never : never : never;
686
+ type InputArgsPrefixes<Remaining extends ArgsDef, Supplied = {}, Prefixes = never> = Remaining extends readonly [infer Head extends ArgDef, ...infer Tail extends ArgsDef] ? InputArgsPrefixes<Tail, Supplied & { [K in Head["name"]]: InputArgValue<Head>; }, Prefixes | (RequiredArgNames<Remaining> extends never ? Simplify<Supplied & { [D in Remaining[number] as D["name"]]?: never; }> : never)> : Prefixes | Simplify<Supplied>;
687
+ /** Supplied positional values form a prefix; defaults do not fill input gaps. */
688
+ type InputArgs<A extends ArgsDef> = number extends A["length"] ? never : IsUnion<A> extends true ? never : IsClosedName<A[number]["name"]> extends true ? InputArgsPrefixes<A> : never;
689
+ type RequiredFlagName<D, K> = D extends unknown ? "required" extends keyof D ? true extends D["required"] ? D extends {
690
+ default: infer Default;
691
+ } ? undefined extends Default ? K : never : K : never : never : never;
692
+ type RequiredFlagNames<F extends FlagsDef> = { [K in keyof F]-?: RequiredFlagName<F[K], K>; }[keyof F];
693
+ /** Flag values accepted by typed programmatic invocation before parsing/validation. */
694
+ type KnownFlags<F extends FlagsDef> = { [K in keyof F as string extends K ? never : K]: F[K]; };
695
+ type InputFlags<F extends FlagsDef> = IsUnion<F> extends true ? never : Simplify<{ [K in RequiredFlagNames<KnownFlags<F>>]-?: InputFlagValue<KnownFlags<F>[K]>; } & { [K in Exclude<keyof KnownFlags<F>, RequiredFlagNames<KnownFlags<F>>>]?: InputFlagValue<KnownFlags<F>[K]>; }> & (string extends keyof F ? NonNullable<RunInputPayload["flags"]> : {});
696
+ type SectionConsumer = ExtensionId | {
697
+ readonly id: ExtensionId;
698
+ };
699
+ type Audience<C> = {
700
+ readonly only: C;
701
+ readonly except?: never;
702
+ } | {
703
+ readonly except: C;
704
+ readonly only?: never;
705
+ } | {
706
+ readonly only?: never;
707
+ readonly except?: never;
708
+ };
709
+ type SectionAudience = Audience<readonly [SectionConsumer, ...SectionConsumer[]]>;
710
+ type SectionContent = {
711
+ readonly title: string;
712
+ readonly body: string;
713
+ };
714
+ /** A plain-text documentation section accepted from command and Extension authors. */
715
+ type CommandSectionInput = SectionContent & SectionAudience;
716
+ /** Typed dynamic audiences may be empty until their consuming operation checks them. */
717
+ type RuntimeCommandSectionInput = SectionContent & Audience<readonly SectionConsumer[]>;
718
+ /** A validated documentation section rendered after built-in command documentation. */
719
+ type CommandSection = SectionContent & Audience<readonly [ExtensionId, ...ExtensionId[]]>;
720
+ /** Metadata describing a CLI command */
721
+ interface CommandMeta {
722
+ /** The command name (used in help text and routing) */
723
+ name: string;
724
+ /** Human-readable description for help text */
725
+ description?: string;
726
+ /** Application version exposed to Extensions and tooling on the root command. */
727
+ version?: string;
728
+ /** Custom usage string (overrides auto-generated usage) */
729
+ usage?: string;
730
+ /** Plain-text sections rendered after built-in command documentation. */
731
+ sections?: readonly CommandSection[];
732
+ /**
733
+ * Alternative names that resolve to the same command.
734
+ *
735
+ * Each entry is a sibling-level alternative for `name`. For example,
736
+ * `defineCommand("issue", { aliases: ["issues", "i"] }, recipe)` makes
737
+ * `cli issue`, `cli issues`, and `cli i` all route to the same command node.
738
+ *
739
+ * **Conflict policy.** Alias strings must not collide with this command's
740
+ * own canonical `name`, with any sibling's `name`, or with any sibling's
741
+ * own alias. TypeScript reports statically known collisions. Each alias
742
+ * must also be a non-empty string with no
743
+ * whitespace and must not start with `-`.
744
+ *
745
+ * **Display contract.** Help output renders the canonical name with
746
+ * aliases inline as `name (a, b, c)`. The canonical `name` is what
747
+ * appears in `commandPath`, error messages, and suggestions from
748
+ * `didYouMean` — it does not depend on which alias the user typed.
749
+ *
750
+ * @example
751
+ * defineCommand("issue", { aliases: ["issues", "i"] }, recipe)
752
+ */
753
+ aliases?: readonly string[];
754
+ /**
755
+ * When `true`, omit this command from every tooling surface that
756
+ * enumerates the command tree for users:
757
+ *
758
+ * - `help` rendered output (subcommand list + USAGE token)
759
+ * - `@crustjs/man` generated man pages (`SUBCOMMANDS` section)
760
+ * - `completion` candidate lists (recursively — hidden
761
+ * subcommands and their descendants never appear in generated
762
+ * bash/zsh/fish scripts)
763
+ * - `didYouMean` typo suggestions and "Available commands"
764
+ * list (so internal names never surface in error UX)
765
+ * - `skill` manifests
766
+ *
767
+ * The command is **only hidden from listings**: routing in
768
+ * `@crustjs/core` does not consult `meta.hidden`, so it stays fully
769
+ * invocable by direct name (or alias). The intended use case is
770
+ * internal/runtime commands like a `__complete` shell-completion
771
+ * entrypoint. Marking a user-facing command `hidden` is supported but
772
+ * unusual.
773
+ *
774
+ * **Scope: commands only.** There is no analogous `hidden` field on
775
+ * `FlagDef` or `ArgDef`; flags and positional arguments always surface
776
+ * in help, completion, and man output. If you need a flag that does
777
+ * not advertise itself, the workaround is to register it as an Extension
778
+ * flags entry without a description (omit `description`),
779
+ * which suppresses its description body but still lists the spelling
780
+ * — there is intentionally no full hide mechanism at the flag layer.
781
+ *
782
+ * Tooling contract: any renderer or generator that walks
783
+ * `subCommands` to produce a user-facing listing should skip nodes
784
+ * where `meta.hidden === true`.
785
+ *
786
+ * @example
787
+ * meta: { name: "__complete", hidden: true, description: "Internal" }
788
+ */
789
+ hidden?: boolean;
790
+ }
791
+ /** Raw token shapes a Standard Schema receives before it runs. */
792
+ type RawSchemaFlagInput = string | boolean | readonly (string | boolean)[] | undefined;
793
+ /** One flag value after syntax parsing and before required/schema validation. */
794
+ type RawFlagValue<D extends FlagDef> = D extends {
795
+ schema: StandardSchema;
796
+ } ? RawSchemaFlagInput : InferFlagValue<D> | undefined;
797
+ /** Positional counterpart of {@link RawFlagValue}. */
798
+ type RawArgValue<D extends ArgDef> = D extends {
799
+ schema: StandardSchema;
800
+ } ? D extends {
801
+ variadic: true;
802
+ } ? string[] : string | undefined : D extends {
803
+ variadic: true;
804
+ } ? ResolveBaseType<D>[] | undefined : InferArgValue<D> | undefined;
805
+ /** Runtime-erased syntax-parsed flag value. */
806
+ type ParsedFlagValue = RawFlagValue<FlagDef>;
807
+ /** Runtime-erased syntax-parsed positional value. */
808
+ type ParsedArgValue = RawArgValue<ArgDef>;
809
+ type RawParsedFlags<F extends FlagsDef> = { [K in keyof F]: RawFlagValue<F[K]>; };
810
+ type RawParsedArgs<A extends ArgsDef> = number extends A["length"] ? Record<string, ParsedArgValue> : { [D in A[number] as D["name"]]: RawArgValue<D>; };
811
+ /** A declared default on any argument or flag definition. */
812
+ type DeclaredDefault = (ArgDef | FlagDef)["default"];
813
+ /** Syntax-parsed input, before required and Standard Schema validation. */
814
+ interface ParseResult<A extends ArgsDef = ArgsDef, F extends FlagsDef = FlagsDef> {
815
+ args: RawParsedArgs<A>;
816
+ flags: RawParsedFlags<F>;
817
+ /** Positionals before `--` that were not consumed by a declared argument. */
818
+ excessArgs: string[];
819
+ /** Arguments after the `--` separator. */
820
+ rawArgs: string[];
821
+ }
822
+ /** Fully validated input returned by the schema boundary. */
823
+ interface ValidatedInput<A extends ArgsDef = ArgsDef, F extends FlagsDef = FlagsDef> {
824
+ args: InferArgs<A>;
825
+ flags: InferFlags<F>;
826
+ }
827
+ //#endregion
828
+ export { CollisionBrand as A, UnionToIntersection as B, ValidatedInput as C, CommandSnapshot as D, ArgSnapshot as E, IsStaticTuple as F, defineExtensionId as H, IsUnion as I, LocalValueBrand as L, EmptyLiteralNameBrand as M, HasClosedNames as N, FlagSnapshot as O, IsClosedName as P, MergeContext as R, SectionConsumer as S, RunInputPayload as T, JsonCompatible as U, ExtensionId as V, JsonValue as W, ParseResult as _, CommandSectionInput as a, RuntimeCommandSectionInput as b, FlagsDef as c, InputArgs as d, InputFlags as f, NamedFlagsRecord as g, NamedFlagDef as h, CommandSection as i, DefName as j, Awaitable as k, InferArgs as l, MergeFlags as m, ArgsDef as n, DeclaredDefault as o, InvocationIO as p, CommandMeta as r, FlagDef as s, ArgDef as t, InferFlags as u, ParsedArgValue as v, ValueType as w, SectionAudience as x, ParsedFlagValue as y, MergeProviders as z };