@crustjs/core 0.0.16 → 0.0.17
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/index.d.ts
CHANGED
|
@@ -21,10 +21,19 @@ interface CommandRoute {
|
|
|
21
21
|
*
|
|
22
22
|
* Resolution rules:
|
|
23
23
|
* 1. If `argv[0]` matches a subcommand key, recurse into that subcommand
|
|
24
|
-
* 2. If
|
|
25
|
-
*
|
|
24
|
+
* 2. If `argv[0]` matches a sibling's `meta.aliases` entry, recurse into
|
|
25
|
+
* that sibling and record the **canonical** name in `commandPath`
|
|
26
|
+
* 3. If no match and the current command has `run()`, return it (args passed to parser)
|
|
27
|
+
* 4. If no match and the current command has NO `run()`, it signals the caller
|
|
26
28
|
* should show help (the `showHelp` flag is set in the result)
|
|
27
|
-
*
|
|
29
|
+
* 5. Unknown subcommands produce a structured COMMAND_NOT_FOUND error whose
|
|
30
|
+
* `details.available` lists the canonical sibling names (aliases are
|
|
31
|
+
* discoverable via `details.parentCommand.subCommands[name].meta.aliases`)
|
|
32
|
+
*
|
|
33
|
+
* Implementation: linear scan over siblings on miss. Command trees are small
|
|
34
|
+
* and resolution runs once per invocation, so the cost is negligible compared
|
|
35
|
+
* to building/freezing a parallel alias→canonical map. The scan does NOT
|
|
36
|
+
* mutate `CommandNode`.
|
|
28
37
|
*
|
|
29
38
|
* @param command - The root command to resolve from
|
|
30
39
|
* @param argv - The argv array to resolve against
|
|
@@ -48,9 +57,21 @@ interface ArgDefBase {
|
|
|
48
57
|
name: string;
|
|
49
58
|
/** Human-readable description for help text */
|
|
50
59
|
description?: string;
|
|
51
|
-
/**
|
|
60
|
+
/**
|
|
61
|
+
* When `true`, the parser throws if the argument is not provided.
|
|
62
|
+
*
|
|
63
|
+
* For variadic args, this means the array cannot be empty — the runtime
|
|
64
|
+
* value is still `T[]`, just rejected when it has length 0.
|
|
65
|
+
*/
|
|
52
66
|
required?: true;
|
|
53
|
-
/**
|
|
67
|
+
/**
|
|
68
|
+
* When `true`, collects all remaining positional values into an array.
|
|
69
|
+
*
|
|
70
|
+
* The inferred TypeScript type is always `T[]` — never `T[] | undefined` —
|
|
71
|
+
* regardless of `required` or `default`. `required` only controls whether
|
|
72
|
+
* an empty array fails validation; it does not change the runtime shape
|
|
73
|
+
* or the inferred type.
|
|
74
|
+
*/
|
|
54
75
|
variadic?: true;
|
|
55
76
|
}
|
|
56
77
|
/** A positional argument whose value is a string */
|
|
@@ -58,6 +79,23 @@ interface StringArgDef extends ArgDefBase {
|
|
|
58
79
|
type: "string";
|
|
59
80
|
/** Default string value when the argument is not provided */
|
|
60
81
|
default?: string;
|
|
82
|
+
/**
|
|
83
|
+
* Static enum of valid values for this argument.
|
|
84
|
+
*
|
|
85
|
+
* `choices` is a **hint for tooling** — it is consumed by shell-completion
|
|
86
|
+
* plugins (e.g. `@crustjs/plugins/completion`) to emit value candidates
|
|
87
|
+
* and may be consumed by future opt-in validation. It is **NOT** enforced
|
|
88
|
+
* at parse time in this version: passing a value outside `choices` does
|
|
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.
|
|
92
|
+
*
|
|
93
|
+
* Only available on string-typed args; not supported on number/boolean.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* { name: "target", type: "string", choices: ["browser", "bun", "node"] }
|
|
97
|
+
*/
|
|
98
|
+
choices?: readonly string[];
|
|
61
99
|
}
|
|
62
100
|
/** A positional argument whose value is a number */
|
|
63
101
|
interface NumberArgDef extends ArgDefBase {
|
|
@@ -112,6 +150,23 @@ interface StringFlagDef extends SingleFlagBase {
|
|
|
112
150
|
type: "string";
|
|
113
151
|
/** Default string value */
|
|
114
152
|
default?: string;
|
|
153
|
+
/**
|
|
154
|
+
* Static enum of valid values for this flag.
|
|
155
|
+
*
|
|
156
|
+
* `choices` is a **hint for tooling** — it is consumed by shell-completion
|
|
157
|
+
* plugins (e.g. `@crustjs/plugins/completion`) to emit value candidates
|
|
158
|
+
* and may be consumed by future opt-in validation. It is **NOT** enforced
|
|
159
|
+
* at parse time in this version: passing a value outside `choices` does
|
|
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.
|
|
163
|
+
*
|
|
164
|
+
* Only available on string-typed flags; not supported on number/boolean.
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* { type: "string", choices: ["browser", "bun", "node"] }
|
|
168
|
+
*/
|
|
169
|
+
choices?: readonly string[];
|
|
115
170
|
}
|
|
116
171
|
/** A single-value number flag */
|
|
117
172
|
interface NumberFlagDef extends SingleFlagBase {
|
|
@@ -137,6 +192,23 @@ interface StringMultiFlagDef extends MultiFlagBase {
|
|
|
137
192
|
type: "string";
|
|
138
193
|
/** Default string array value */
|
|
139
194
|
default?: string[];
|
|
195
|
+
/**
|
|
196
|
+
* Static enum of valid values for each occurrence of this flag.
|
|
197
|
+
*
|
|
198
|
+
* `choices` is a **hint for tooling** — it is consumed by shell-completion
|
|
199
|
+
* plugins (e.g. `@crustjs/plugins/completion`) to emit value candidates
|
|
200
|
+
* and may be consumed by future opt-in validation. It is **NOT** enforced
|
|
201
|
+
* at parse time in this version: passing a value outside `choices` does
|
|
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.
|
|
205
|
+
*
|
|
206
|
+
* Only available on string-typed multi-flags; not supported on number/boolean.
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* { type: "string", multiple: true, choices: ["unit", "integration"] }
|
|
210
|
+
*/
|
|
211
|
+
choices?: readonly string[];
|
|
140
212
|
}
|
|
141
213
|
/** A multi-value number flag (collects repeated values into an array) */
|
|
142
214
|
interface NumberMultiFlagDef extends MultiFlagBase {
|
|
@@ -385,9 +457,14 @@ type EffectiveFlags<
|
|
|
385
457
|
/**
|
|
386
458
|
* Infer the resolved type for a single ArgDef:
|
|
387
459
|
*
|
|
388
|
-
* - **variadic** → `primitive[]`
|
|
460
|
+
* - **variadic** → `primitive[]` (always an array, never `undefined`,
|
|
461
|
+
* regardless of `required` or `default`)
|
|
389
462
|
* - **required** or **has default** → `primitive` (non-optional)
|
|
390
463
|
* - otherwise → `primitive | undefined`
|
|
464
|
+
*
|
|
465
|
+
* The variadic branch is checked first and takes precedence. Combining
|
|
466
|
+
* `variadic: true` with `required: true` keeps the inferred type as `T[]`;
|
|
467
|
+
* `required` only gates empty-array validation, not the type.
|
|
391
468
|
*/
|
|
392
469
|
type InferArgValue<A extends ArgDef> = A extends {
|
|
393
470
|
variadic: true;
|
|
@@ -458,6 +535,65 @@ interface CommandMeta {
|
|
|
458
535
|
description?: string;
|
|
459
536
|
/** Custom usage string (overrides auto-generated usage) */
|
|
460
537
|
usage?: string;
|
|
538
|
+
/**
|
|
539
|
+
* Alternative names that resolve to the same command.
|
|
540
|
+
*
|
|
541
|
+
* Each entry is a sibling-level alternative for `name`. For example,
|
|
542
|
+
* `meta: { name: "issue", aliases: ["issues", "i"] }` makes `cli issue`,
|
|
543
|
+
* `cli issues`, and `cli i` all route to the same command node.
|
|
544
|
+
*
|
|
545
|
+
* **Conflict policy.** Alias strings must not collide with this command's
|
|
546
|
+
* own canonical `name`, with any sibling's `name`, or with any sibling's
|
|
547
|
+
* own alias. Collisions throw a `CrustError("DEFINITION", …)` at
|
|
548
|
+
* registration time (or during `validateCommandTree` for plugin-installed
|
|
549
|
+
* subcommands). Each alias must also be a non-empty string with no
|
|
550
|
+
* whitespace and must not start with `-`.
|
|
551
|
+
*
|
|
552
|
+
* **Display contract.** Help output renders the canonical name with
|
|
553
|
+
* aliases inline as `name (a, b, c)`. The canonical `name` is what
|
|
554
|
+
* appears in `commandPath`, error messages, and suggestions from
|
|
555
|
+
* `didYouMeanPlugin` — it does not depend on which alias the user typed.
|
|
556
|
+
*
|
|
557
|
+
* @example
|
|
558
|
+
* meta: { name: "issue", aliases: ["issues", "i"] }
|
|
559
|
+
*/
|
|
560
|
+
aliases?: readonly string[];
|
|
561
|
+
/**
|
|
562
|
+
* When `true`, omit this command from every tooling surface that
|
|
563
|
+
* enumerates the command tree for users:
|
|
564
|
+
*
|
|
565
|
+
* - `helpPlugin` rendered output (subcommand list + USAGE token)
|
|
566
|
+
* - `@crustjs/man` generated man pages (`SUBCOMMANDS` section)
|
|
567
|
+
* - `completionPlugin` candidate lists (recursively — hidden
|
|
568
|
+
* subcommands and their descendants never appear in generated
|
|
569
|
+
* bash/zsh/fish scripts)
|
|
570
|
+
* - `didYouMeanPlugin` typo suggestions and "Available commands"
|
|
571
|
+
* list (so internal names never surface in error UX)
|
|
572
|
+
* - `skillPlugin` manifests
|
|
573
|
+
*
|
|
574
|
+
* The command is **only hidden from listings**: routing in
|
|
575
|
+
* `@crustjs/core` does not consult `meta.hidden`, so it stays fully
|
|
576
|
+
* invocable by direct name (or alias). The intended use case is
|
|
577
|
+
* internal/runtime commands like a `__complete` shell-completion
|
|
578
|
+
* entrypoint. Marking a user-facing command `hidden` is supported but
|
|
579
|
+
* unusual.
|
|
580
|
+
*
|
|
581
|
+
* **Scope: commands only.** There is no analogous `hidden` field on
|
|
582
|
+
* `FlagDef` or `ArgDef`; flags and positional arguments always surface
|
|
583
|
+
* in help, completion, and man output. If you need a flag that does
|
|
584
|
+
* not advertise itself, the workaround is to register it through a
|
|
585
|
+
* plugin's `setup()` hook without describing it (omit `description`),
|
|
586
|
+
* which suppresses its description body but still lists the spelling
|
|
587
|
+
* — there is intentionally no full hide mechanism at the flag layer.
|
|
588
|
+
*
|
|
589
|
+
* Tooling contract: any renderer or generator that walks
|
|
590
|
+
* `subCommands` to produce a user-facing listing should skip nodes
|
|
591
|
+
* where `meta.hidden === true`.
|
|
592
|
+
*
|
|
593
|
+
* @example
|
|
594
|
+
* meta: { name: "__complete", hidden: true, description: "Internal" }
|
|
595
|
+
*/
|
|
596
|
+
hidden?: boolean;
|
|
461
597
|
}
|
|
462
598
|
/**
|
|
463
599
|
* The result of parsing argv against a command's arg/flag definitions.
|
|
@@ -587,11 +723,30 @@ interface CrustCommandContext<
|
|
|
587
723
|
* Build-time validation protocol.
|
|
588
724
|
*
|
|
589
725
|
* `crust build` spawns the user's entrypoint as a subprocess with
|
|
590
|
-
* `CRUST_INTERNAL_VALIDATE_ONLY=1
|
|
591
|
-
*
|
|
726
|
+
* `CRUST_INTERNAL_VALIDATE_ONLY=1` (and the companion
|
|
727
|
+
* {@link VALIDATION_FORCE_EXIT_ENV}=`1`). When `.execute()` detects
|
|
728
|
+
* `VALIDATION_MODE_ENV` it runs the validation pipeline and surfaces errors
|
|
729
|
+
* via stderr and `process.exitCode`.
|
|
730
|
+
*
|
|
731
|
+
* Process termination is opt-in via {@link VALIDATION_FORCE_EXIT_ENV} so
|
|
732
|
+
* that in-process callers (tests, embedders) that set only this env get
|
|
733
|
+
* the validation result without having their host process killed.
|
|
592
734
|
*/
|
|
593
735
|
declare const VALIDATION_MODE_ENV = "CRUST_INTERNAL_VALIDATE_ONLY";
|
|
594
736
|
/**
|
|
737
|
+
* Companion to {@link VALIDATION_MODE_ENV}. When set to `"1"` _alongside_
|
|
738
|
+
* `VALIDATION_MODE_ENV`, `.execute()` calls `process.exit()` after the
|
|
739
|
+
* validation pipeline completes — ensuring any code that follows
|
|
740
|
+
* `await app.execute()` in the user's entrypoint does not run during
|
|
741
|
+
* `crust build`'s pre-compile validation subprocess.
|
|
742
|
+
*
|
|
743
|
+
* Without this flag, `.execute()` only sets `process.exitCode` and returns,
|
|
744
|
+
* matching the rest of `.execute()`'s error handling. This is the path
|
|
745
|
+
* in-process callers (tests that toggle `VALIDATION_MODE_ENV`, programmatic
|
|
746
|
+
* embedders) take so the host event loop is not terminated.
|
|
747
|
+
*/
|
|
748
|
+
declare const VALIDATION_FORCE_EXIT_ENV = "CRUST_INTERNAL_VALIDATE_FORCE_EXIT";
|
|
749
|
+
/**
|
|
595
750
|
* Chainable builder for defining CLI commands with full type inference.
|
|
596
751
|
*
|
|
597
752
|
* Generic parameters:
|
|
@@ -649,14 +804,20 @@ declare class Crust<
|
|
|
649
804
|
* Set metadata (description, usage) for this command.
|
|
650
805
|
*
|
|
651
806
|
* The command name is already set by the builder source (constructor,
|
|
652
|
-
* `.sub()`, or the child builder passed into `.command(name, cb)`)
|
|
653
|
-
*
|
|
807
|
+
* `.sub()`, or the child builder passed into `.command(name, cb)`).
|
|
808
|
+
* Provide `description`, `usage`, and/or `aliases` here.
|
|
654
809
|
*
|
|
655
810
|
* Returns a new builder with updated metadata. The original builder
|
|
656
811
|
* is not mutated.
|
|
657
812
|
*
|
|
658
|
-
* @param meta - Metadata fields to set (description, usage)
|
|
813
|
+
* @param meta - Metadata fields to set (description, usage, aliases)
|
|
659
814
|
* @returns A new `Crust` instance with updated metadata
|
|
815
|
+
* @example
|
|
816
|
+
* ```ts
|
|
817
|
+
* .command("issue", (cmd) =>
|
|
818
|
+
* cmd.meta({ aliases: ["issues", "i"] }).run(() => {})
|
|
819
|
+
* )
|
|
820
|
+
* ```
|
|
660
821
|
*/
|
|
661
822
|
meta(meta: Omit<CommandMeta, "name">): Crust<Inherited, Local, A, Eff>;
|
|
662
823
|
/**
|
|
@@ -931,4 +1092,4 @@ declare function parseArgs<
|
|
|
931
1092
|
* @throws {CrustError} On missing required args or flags
|
|
932
1093
|
*/
|
|
933
1094
|
declare function validateParsed(command: CommandNode, parsed: ParseResult): void;
|
|
934
|
-
export { validateParsed, resolveCommand, parseArgs, ValueType, ValidateVariadicArgs, ValidateNoPrefixedFlags, ValidateFlagAliases, ValidateCrossCollisions, VALIDATION_MODE_ENV, SetupContext, SetupActions, PluginMiddleware, ParseResult, MiddlewareContext, MergeFlags, InheritableFlags, InferFlags, InferArgs, FlagsDef, FlagDef, EffectiveFlags, CrustPlugin, CrustErrorCode, CrustError, CrustCommandContext, Crust, CommandRoute, CommandNode, CommandMeta, ArgsDef, ArgDef };
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{a as O,b as
|
|
2
|
+
import{a as O,b as K,c as T,d as I,f as B}from"./shared/chunk-w6ydwhzx.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-w4s4rg66.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-w4s4rg66.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};
|
|
@@ -0,0 +1,3 @@
|
|
|
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};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crustjs/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.17",
|
|
4
4
|
"description": "Core library for the Crust CLI framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -46,6 +46,6 @@
|
|
|
46
46
|
"bunup": "^0.16.31"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"typescript": "^6.0.
|
|
49
|
+
"typescript": "^6.0.3"
|
|
50
50
|
}
|
|
51
51
|
}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
import{b as Q,c as S,d as W}from"./chunk-vt64gs69.js";function M(B){switch(B.type){case"number":return"1";case"boolean":return"true";default:return"sample"}}function X(B){let z=[],K=B.effectiveFlags;for(let[j,G]of Object.entries(K)){if(G.required!==!0||G.default!==void 0)continue;if(z.push(`--${j}`),G.type!=="boolean")z.push(M(G))}let J=B.args;if(J)for(let j of J){if(j.required!==!0||j.default!==void 0)continue;z.push(M(j))}return z}function _(B){let z=[{command:B,path:[B.meta.name]}],K=new Set;while(z.length>0){let J=z.pop();if(!J)break;let{command:j,path:G}=J;if(K.has(j))continue;K.add(j);try{let H=S(j,X(j));W(j,H)}catch(H){let L=H instanceof Error?H.message:"Unknown validation error";throw new Q("DEFINITION",`Command "${G.join(" ")}" failed runtime validation: ${L}`).withCause(H)}for(let[H,L]of Object.entries(j.subCommands))z.push({command:L,path:[...G,H]})}}export{_ as validateCommandTree};
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
var A=import.meta.require;class W extends Error{code;details;cause;constructor(B,J,...G){super(J);this.name="CrustError",this.code=B,this.details=G[0]}is(B){return this.code===B}withCause(B){return this.cause=B,this}}import{parseArgs as U}from"util";function q(B){let J={},G={};if(!B)return{options:J,aliasToName:G};let z=new Map;for(let j of Object.keys(B))z.set(j,j);for(let[j,H]of Object.entries(B)){if(j.startsWith("no-")){let L=j.slice(3);throw new W("DEFINITION",`Flag "--${j}" must not use "no-" prefix; define "${L}" and negate with "--no-${L}"`)}let K=H.type==="boolean"?"boolean":"string",Q={type:K};if(H.multiple)Q.multiple=!0;if(H.short){if(H.short.startsWith("no-"))throw new W("DEFINITION",`Short alias "-${H.short}" on "--${j}" must not use "no-" prefix (reserved for negation)`);let L=z.get(H.short);if(L)throw new W("DEFINITION",`Alias collision: "-${H.short}" is used by both "--${L}" and "--${j}"`);z.set(H.short,j),G[H.short]=j,Q.short=H.short}if(H.aliases)for(let L of H.aliases){if(L.startsWith("no-"))throw new W("DEFINITION",`Alias "--${L}" on "--${j}" must not use "no-" prefix (reserved for negation)`);let X=z.get(L);if(X)throw new W("DEFINITION",`Alias collision: "${L.length===1?"-":"--"}${L}" is used by both "--${X}" and "--${j}"`);z.set(L,j),G[L]=j;let $={type:K};if(H.multiple)$.multiple=!0;J[L]=$}J[j]=Q}return{options:J,aliasToName:G}}function _(B,J,G){if(J==="number"){let z=Number(B);if(Number.isNaN(z))throw new W("PARSE",`Expected number for ${G}, got "${B}"`);return z}if(J==="boolean")return B==="true"||B==="1";return B}function I(B,J,G){let z=`--${B}`;if(J.multiple&&Array.isArray(G))return J.type==="boolean"?G.filter((j)=>typeof j==="boolean"):G.map((j)=>_(j,J.type,z));if(J.type==="boolean"){if(typeof G==="boolean")return G;throw new W("PARSE",`Expected boolean value for flag "${z}", got ${typeof G}`)}if(typeof G==="string")return _(G,J.type,z);if(G===!0)return J.default??void 0;return G}function M(B,J,G){let z={};for(let j in B){let H=J[j]??j;if(!(H in G))continue;let K=B[j],Q=z[H];if(Q!==void 0&&Array.isArray(Q)&&Array.isArray(K))Q.push(...K);else z[H]=K}return z}function h(B,J,G){if(!B)return{};let z=M(J,G,B),j={};for(let[H,K]of Object.entries(B)){let Q=z[H];if(Q!==void 0){j[H]=I(H,K,Q);continue}j[H]=K.default??void 0}return j}function O(B,J){if(!B)return;for(let[G,z]of Object.entries(B))if(z.required===!0&&z.default===void 0){if(J[G]===void 0)throw new W("VALIDATION",`Missing required flag "--${G}"`)}}function P(B,J){if(!B)return{};let G={},z=0;for(let j of B){let{name:H}=j;if(j.variadic){let K=J.slice(z);G[H]=j.type==="string"?K:K.map((Q)=>_(Q,j.type,`<${H}>`)),z=J.length}else if(z<J.length)G[H]=_(J[z],j.type,`<${H}>`),z++;else G[H]=j.default??void 0}return G}function S(B,J,G){if(!J)return;for(let z of B){if(z==="--")return;if(!z.startsWith("--no-"))continue;let j=z.indexOf("="),H=j===-1?z.slice(5):z.slice(5,j);if(!H)continue;let K=G[H];if(!K)continue;if(K===H)continue;if(J[K]?.type!=="boolean")continue;throw new W("PARSE",`Cannot negate alias "--no-${H}"; use "--no-${K}" instead`)}}function b(B,J){let{args:G,effectiveFlags:z}=B,{options:j,aliasToName:H}=q(z);S(J,z,H);let K;try{K=U({args:J,options:j,strict:!0,allowPositionals:!0,allowNegative:!0,tokens:!0})}catch(Y){if(Y instanceof Error){let Z=Y.message.match(/Unknown option '(.+?)'/);if(Z)throw new W("PARSE",`Unknown flag "${Z[1]}"`).withCause(Y)}throw new W("PARSE","Failed to parse command arguments").withCause(Y)}let Q=[],L=[];if(K.tokens){let Y=!1;for(let Z of K.tokens){if(Z.kind==="option-terminator"){Y=!0;continue}if(Z.kind==="positional")(Y?Q:L).push(Z.value??"")}}else L.push(...K.positionals);let X=h(z,K.values,H);return{args:P(G,L),flags:X,rawArgs:Q}}function E(B,J){let{args:G,effectiveFlags:z}=B,j=J.args,H=J.flags;if(G)for(let K of G){let{name:Q}=K,L=`argument "<${Q}>"`,X=j[Q];if(K.required===!0&&K.default===void 0){if(K.variadic){if(!Array.isArray(X)||X.length===0)throw new W("VALIDATION",`Missing required ${L}`)}else if(X===void 0)throw new W("VALIDATION",`Missing required ${L}`)}}O(z,H)}
|
|
3
|
-
export{A as a,W as b,b as c,E as d};
|