@crustjs/core 0.0.7 → 0.0.9

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
@@ -375,8 +375,8 @@ interface Command<
375
375
  readonly args?: A;
376
376
  /** Flag definitions */
377
377
  readonly flags?: F;
378
- /** Named subcommands */
379
- readonly subCommands?: Record<string, AnyCommand>;
378
+ /** Named subcommands (always initialized, may be empty) */
379
+ readonly subCommands: Record<string, AnyCommand>;
380
380
  /** Called before `run()` — useful for initialization */
381
381
  preRun?(context: CommandContext<A, F>): void | Promise<void>;
382
382
  /** The main command handler */
@@ -567,6 +567,19 @@ interface SetupActions {
567
567
  * @param def - The flag definition
568
568
  */
569
569
  addFlag(command: AnyCommand, name: string, def: FlagDef): void;
570
+ /**
571
+ * Inject a subcommand into a command's `subCommands` record.
572
+ *
573
+ * Use this in plugin `setup()` hooks to register plugin-provided commands
574
+ * (e.g. a "skill" management command). If the parent already has a
575
+ * subcommand with the same name (user-defined), the call is silently
576
+ * skipped — user definitions always take priority over plugin injections.
577
+ *
578
+ * @param parent - The parent command to add the subcommand to
579
+ * @param name - The subcommand name (used for routing)
580
+ * @param command - The subcommand to register
581
+ */
582
+ addSubCommand(parent: AnyCommand, name: string, command: AnyCommand): void;
570
583
  }
571
584
  /** Shared context fields available in both setup and middleware phases. */
572
585
  interface BaseContext {
@@ -588,10 +601,21 @@ interface CrustPlugin {
588
601
  setup?: (context: SetupContext, actions: SetupActions) => void | Promise<void>;
589
602
  middleware?: PluginMiddleware;
590
603
  }
604
+ /**
605
+ * Build-time validation protocol.
606
+ *
607
+ * `crust build` spawns the user's entrypoint as a subprocess with
608
+ * `CRUST_INTERNAL_VALIDATE_ONLY=1`. When `runMain` detects this env flag it
609
+ * runs validation, surfaces errors via stderr/exitCode, then force-exits.
610
+ *
611
+ * `VALIDATION_RESULT_GLOBAL_KEY` stores the result on `globalThis` for
612
+ * in-process consumers (tests that call `runMain` directly).
613
+ */
614
+ declare const VALIDATION_MODE_ENV = "CRUST_INTERNAL_VALIDATE_ONLY";
591
615
  interface RunOptions {
592
616
  argv?: string[];
593
617
  plugins?: CrustPlugin[];
594
618
  }
595
619
  declare function runCommand(command: AnyCommand, options?: RunOptions): Promise<void>;
596
620
  declare function runMain(command: AnyCommand, options?: RunOptions): Promise<void>;
597
- export { runMain, runCommand, resolveCommand, parseArgs, defineCommand, ValueType, ValidateVariadicArgs, ValidateNoPrefixedFlags, ValidateFlagAliases, SetupContext, RunOptions, PluginMiddleware, ParseResult, MiddlewareContext, InferFlags, InferArgs, FlagsDef, FlagDef, CrustPlugin, CrustErrorCode, CrustError, CommandRoute, CommandMeta, CommandDef, CommandContext, Command, ArgsDef, ArgDef, AnyCommand };
621
+ export { runMain, runCommand, resolveCommand, parseArgs, defineCommand, ValueType, ValidateVariadicArgs, ValidateNoPrefixedFlags, ValidateFlagAliases, VALIDATION_MODE_ENV, SetupContext, SetupActions, RunOptions, PluginMiddleware, ParseResult, MiddlewareContext, InferFlags, InferArgs, FlagsDef, FlagDef, CrustPlugin, CrustErrorCode, CrustError, CommandRoute, CommandMeta, CommandDef, CommandContext, Command, ArgsDef, ArgDef, AnyCommand };
package/dist/index.js CHANGED
@@ -1,453 +1,2 @@
1
1
  // @bun
2
- // src/errors.ts
3
- class CrustError extends Error {
4
- code;
5
- details;
6
- cause;
7
- constructor(code, message, ...details) {
8
- super(message);
9
- this.name = "CrustError";
10
- this.code = code;
11
- this.details = details[0];
12
- }
13
- is(code) {
14
- return this.code === code;
15
- }
16
- withCause(cause) {
17
- this.cause = cause;
18
- return this;
19
- }
20
- }
21
-
22
- // src/command.ts
23
- function defineCommand(config) {
24
- if (!config.meta.name.trim()) {
25
- throw new CrustError("DEFINITION", "defineCommand: meta.name is required and must be a non-empty string");
26
- }
27
- if (config.flags) {
28
- for (const [name, def] of Object.entries(config.flags)) {
29
- if (name.startsWith("no-")) {
30
- const base = name.slice(3);
31
- throw new CrustError("DEFINITION", `Flag name "--${name}" must not start with "no-"; define "${base}" instead and use "--no-${base}" at runtime`);
32
- }
33
- if (def.alias) {
34
- const aliases = Array.isArray(def.alias) ? def.alias : [def.alias];
35
- for (const alias of aliases) {
36
- if (alias.startsWith("no-")) {
37
- throw new CrustError("DEFINITION", `Alias "--${alias}" on flag "--${name}" must not start with "no-"; the "no-" prefix is reserved for boolean negation`);
38
- }
39
- }
40
- }
41
- }
42
- }
43
- const copy = {
44
- ...config,
45
- meta: { ...config.meta },
46
- ...config.args && {
47
- args: config.args.map((def) => ({ ...def }))
48
- },
49
- flags: config.flags ? Object.fromEntries(Object.entries(config.flags).map(([k, v]) => [k, { ...v }])) : {},
50
- ...config.subCommands && {
51
- subCommands: { ...config.subCommands }
52
- }
53
- };
54
- return Object.freeze(copy);
55
- }
56
- // src/parser.ts
57
- import {
58
- parseArgs as nodeParseArgs
59
- } from "util";
60
- function buildParseArgsOptionDescriptor(flagsDef) {
61
- const options = {};
62
- const aliasToName = {};
63
- if (!flagsDef)
64
- return { options, aliasToName };
65
- const aliasRegistry = new Map;
66
- for (const name of Object.keys(flagsDef)) {
67
- aliasRegistry.set(name, name);
68
- }
69
- for (const [name, def] of Object.entries(flagsDef)) {
70
- if (name.startsWith("no-")) {
71
- const base = name.slice(3);
72
- throw new CrustError("DEFINITION", `Flag name "--${name}" must not start with "no-"; define "${base}" instead and use "--no-${base}" at runtime`);
73
- }
74
- const parseType = def.type === "boolean" ? "boolean" : "string";
75
- const opt = { type: parseType };
76
- if (def.multiple) {
77
- opt.multiple = true;
78
- }
79
- if (def.alias) {
80
- const aliases = Array.isArray(def.alias) ? def.alias : [def.alias];
81
- for (const alias of aliases) {
82
- if (alias.startsWith("no-")) {
83
- throw new CrustError("DEFINITION", `Alias "--${alias}" on flag "--${name}" must not start with "no-"; the "no-" prefix is reserved for boolean negation`);
84
- }
85
- const existing = aliasRegistry.get(alias);
86
- if (existing) {
87
- throw new CrustError("DEFINITION", `Alias collision: "${alias.length === 1 ? "-" : "--"}${alias}" is used by both "--${existing}" and "--${name}"`);
88
- }
89
- aliasRegistry.set(alias, name);
90
- aliasToName[alias] = name;
91
- if (alias.length === 1 && !opt.short) {
92
- opt.short = alias;
93
- } else {
94
- const aliasOpt = { type: parseType };
95
- if (def.multiple) {
96
- aliasOpt.multiple = true;
97
- }
98
- options[alias] = aliasOpt;
99
- }
100
- }
101
- }
102
- options[name] = opt;
103
- }
104
- return { options, aliasToName };
105
- }
106
- function coerceValue(value, type, label) {
107
- if (type === "number") {
108
- const num = Number(value);
109
- if (Number.isNaN(num)) {
110
- throw new CrustError("PARSE", `Expected number for ${label}, got "${value}"`);
111
- }
112
- return num;
113
- }
114
- if (type === "boolean") {
115
- return value === "true" || value === "1";
116
- }
117
- return value;
118
- }
119
- function applyDefaultOrThrow(def, label) {
120
- if (def.default !== undefined)
121
- return def.default;
122
- if (def.required === true) {
123
- throw new CrustError("VALIDATION", `Missing required ${label}`);
124
- }
125
- return;
126
- }
127
- function coerceFlagValue(name, def, parsedValue) {
128
- const label = `--${name}`;
129
- if (def.multiple && Array.isArray(parsedValue)) {
130
- return def.type === "boolean" ? parsedValue.filter((v) => typeof v === "boolean") : parsedValue.map((v) => coerceValue(v, def.type, label));
131
- }
132
- if (def.type === "boolean") {
133
- if (typeof parsedValue === "boolean") {
134
- return parsedValue;
135
- }
136
- throw new CrustError("PARSE", `Expected boolean value for flag "${label}", got ${typeof parsedValue}`);
137
- }
138
- if (typeof parsedValue === "string") {
139
- return coerceValue(parsedValue, def.type, label);
140
- }
141
- if (parsedValue === true) {
142
- return def.default ?? undefined;
143
- }
144
- return parsedValue;
145
- }
146
- function resolveAliases(parsedValues, aliasToName, flagsDef) {
147
- const canonical = {};
148
- for (const key in parsedValues) {
149
- const canonicalName = aliasToName[key] ?? key;
150
- if (!(canonicalName in flagsDef))
151
- continue;
152
- const value = parsedValues[key];
153
- const existing = canonical[canonicalName];
154
- if (existing !== undefined && Array.isArray(existing) && Array.isArray(value)) {
155
- existing.push(...value);
156
- } else {
157
- canonical[canonicalName] = value;
158
- }
159
- }
160
- return canonical;
161
- }
162
- function resolveFlags(flagsDef, parsedValues, aliasToName) {
163
- if (!flagsDef)
164
- return {};
165
- const canonical = resolveAliases(parsedValues, aliasToName, flagsDef);
166
- const resolved = {};
167
- for (const [name, def] of Object.entries(flagsDef)) {
168
- const parsedValue = canonical[name];
169
- if (parsedValue !== undefined) {
170
- resolved[name] = coerceFlagValue(name, def, parsedValue);
171
- continue;
172
- }
173
- resolved[name] = def.default ?? undefined;
174
- }
175
- return resolved;
176
- }
177
- function validateRequiredFlags(flagsDef, resolvedFlags) {
178
- if (!flagsDef)
179
- return;
180
- for (const [name, def] of Object.entries(flagsDef)) {
181
- if (def.required === true && def.default === undefined) {
182
- if (resolvedFlags[name] === undefined) {
183
- throw new CrustError("VALIDATION", `Missing required flag "--${name}"`);
184
- }
185
- }
186
- }
187
- }
188
- function resolveArgs(argsDef, positionals) {
189
- if (!argsDef)
190
- return {};
191
- const resolved = {};
192
- let index = 0;
193
- for (const def of argsDef) {
194
- const { name } = def;
195
- const label = `argument "<${name}>"`;
196
- if (def.variadic) {
197
- const remaining = positionals.slice(index);
198
- if (def.required === true && remaining.length === 0) {
199
- throw new CrustError("VALIDATION", `Missing required ${label}`);
200
- }
201
- resolved[name] = def.type === "string" ? remaining : remaining.map((v) => coerceValue(v, def.type, `<${name}>`));
202
- index = positionals.length;
203
- } else if (index < positionals.length) {
204
- resolved[name] = coerceValue(positionals[index], def.type, `<${name}>`);
205
- index++;
206
- } else {
207
- resolved[name] = applyDefaultOrThrow(def, label);
208
- }
209
- }
210
- return resolved;
211
- }
212
- function validateCanonicalNegationUsage(argv, flagsDef, aliasToName) {
213
- if (!flagsDef)
214
- return;
215
- for (const arg of argv) {
216
- if (arg === "--")
217
- return;
218
- if (!arg.startsWith("--no-"))
219
- continue;
220
- const assignmentIndex = arg.indexOf("=");
221
- const rawName = assignmentIndex === -1 ? arg.slice("--no-".length) : arg.slice("--no-".length, assignmentIndex);
222
- if (!rawName)
223
- continue;
224
- const canonical = aliasToName[rawName];
225
- if (!canonical)
226
- continue;
227
- if (canonical === rawName)
228
- continue;
229
- const def = flagsDef[canonical];
230
- if (def?.type !== "boolean")
231
- continue;
232
- throw new CrustError("PARSE", `Cannot negate alias "--no-${rawName}"; use "--no-${canonical}" instead`);
233
- }
234
- }
235
- function parseArgs(command, argv) {
236
- const argsDef = command.args;
237
- const flagsDef = command.flags;
238
- const { options: parseOptions, aliasToName } = buildParseArgsOptionDescriptor(flagsDef);
239
- validateCanonicalNegationUsage(argv, flagsDef, aliasToName);
240
- let parsed;
241
- try {
242
- parsed = nodeParseArgs({
243
- args: argv,
244
- options: parseOptions,
245
- strict: true,
246
- allowPositionals: true,
247
- allowNegative: true,
248
- tokens: true
249
- });
250
- } catch (error) {
251
- if (error instanceof Error) {
252
- const unknownMatch = error.message.match(/Unknown option '(.+?)'/);
253
- if (unknownMatch) {
254
- throw new CrustError("PARSE", `Unknown flag "${unknownMatch[1]}"`).withCause(error);
255
- }
256
- }
257
- throw new CrustError("PARSE", "Failed to parse command arguments").withCause(error);
258
- }
259
- const rawArgs = [];
260
- const preSeparatorPositionals = [];
261
- if (parsed.tokens) {
262
- let afterSeparator = false;
263
- for (const token of parsed.tokens) {
264
- if (token.kind === "option-terminator") {
265
- afterSeparator = true;
266
- continue;
267
- }
268
- if (token.kind === "positional") {
269
- (afterSeparator ? rawArgs : preSeparatorPositionals).push(token.value ?? "");
270
- }
271
- }
272
- } else {
273
- preSeparatorPositionals.push(...parsed.positionals);
274
- }
275
- const resolvedFlags = resolveFlags(flagsDef, parsed.values, aliasToName);
276
- const resolvedArgs = resolveArgs(argsDef, preSeparatorPositionals);
277
- validateRequiredFlags(flagsDef, resolvedFlags);
278
- return {
279
- args: resolvedArgs,
280
- flags: resolvedFlags,
281
- rawArgs
282
- };
283
- }
284
- // src/router.ts
285
- function resolveCommand(command, argv) {
286
- const path = [command.meta.name];
287
- let current = command;
288
- let routedArgv = argv;
289
- while (routedArgv.length > 0) {
290
- const subCommands = current.subCommands;
291
- if (!subCommands || Object.keys(subCommands).length === 0) {
292
- break;
293
- }
294
- const candidate = routedArgv[0];
295
- if (!candidate || candidate.startsWith("-")) {
296
- break;
297
- }
298
- if (candidate in subCommands) {
299
- current = subCommands[candidate];
300
- path.push(candidate);
301
- routedArgv = routedArgv.slice(1);
302
- continue;
303
- }
304
- if (current.run) {
305
- break;
306
- }
307
- const available = Object.keys(subCommands);
308
- throw new CrustError("COMMAND_NOT_FOUND", `Unknown command "${candidate}".`, {
309
- input: candidate,
310
- available,
311
- commandPath: [...path],
312
- parentCommand: current
313
- });
314
- }
315
- return {
316
- command: current,
317
- argv: routedArgv,
318
- commandPath: path
319
- };
320
- }
321
- // src/run.ts
322
- function createPluginState() {
323
- const map = new Map;
324
- return {
325
- get(key) {
326
- return map.get(key);
327
- },
328
- has(key) {
329
- return map.has(key);
330
- },
331
- set(key, value) {
332
- map.set(key, value);
333
- },
334
- delete(key) {
335
- return map.delete(key);
336
- }
337
- };
338
- }
339
- async function runSetupHooks(plugins, context, actions) {
340
- for (const plugin of plugins) {
341
- if (!plugin.setup)
342
- continue;
343
- await plugin.setup(context, actions);
344
- }
345
- }
346
- async function executeCommand(command, parsed) {
347
- if (!command.run)
348
- return;
349
- const context = {
350
- args: parsed.args,
351
- flags: parsed.flags,
352
- rawArgs: parsed.rawArgs,
353
- command
354
- };
355
- try {
356
- if (command.preRun) {
357
- await command.preRun(context);
358
- }
359
- await command.run(context);
360
- } finally {
361
- if (command.postRun) {
362
- await command.postRun(context);
363
- }
364
- }
365
- }
366
- async function runMiddlewareChain(plugins, context, terminal) {
367
- const stack = plugins.map((plugin) => plugin.middleware).filter((middleware) => Boolean(middleware));
368
- let index = -1;
369
- const dispatch = async (i) => {
370
- if (i <= index) {
371
- throw new CrustError("DEFINITION", "Plugin middleware called next() multiple times");
372
- }
373
- index = i;
374
- if (i === stack.length) {
375
- await terminal();
376
- return;
377
- }
378
- const middleware = stack[i];
379
- if (!middleware) {
380
- throw new CrustError("DEFINITION", "Plugin middleware stack is invalid");
381
- }
382
- await middleware(context, () => dispatch(i + 1));
383
- };
384
- await dispatch(0);
385
- }
386
- async function runCommand(command, options) {
387
- const argv = options?.argv ?? process.argv.slice(2);
388
- const plugins = options?.plugins ?? [];
389
- const actions = {
390
- addFlag(target, name, def) {
391
- if (!target.flags) {
392
- throw new CrustError("DEFINITION", `Cannot add flag "${name}": command "${target.meta.name}" has no flags object.`);
393
- }
394
- target.flags[name] = def;
395
- }
396
- };
397
- const middlewareContext = {
398
- argv: [...argv],
399
- rootCommand: command,
400
- state: createPluginState(),
401
- route: null,
402
- input: null
403
- };
404
- try {
405
- await runSetupHooks(plugins, middlewareContext, actions);
406
- let parsed;
407
- let resolvedCommand;
408
- try {
409
- const resolved = resolveCommand(command, [...argv]);
410
- middlewareContext.route = resolved;
411
- parsed = parseArgs(resolved.command, resolved.argv);
412
- middlewareContext.input = {
413
- args: parsed.args,
414
- flags: parsed.flags,
415
- rawArgs: parsed.rawArgs
416
- };
417
- resolvedCommand = resolved.command;
418
- } catch (error) {
419
- await runMiddlewareChain(plugins, middlewareContext, async () => {
420
- throw error;
421
- });
422
- return;
423
- }
424
- await runMiddlewareChain(plugins, middlewareContext, async () => {
425
- await executeCommand(resolvedCommand, parsed);
426
- });
427
- } catch (error) {
428
- if (error instanceof CrustError) {
429
- throw error;
430
- }
431
- if (error instanceof Error) {
432
- throw new CrustError("EXECUTION", error.message).withCause(error);
433
- }
434
- throw new CrustError("EXECUTION", String(error)).withCause(error);
435
- }
436
- }
437
- async function runMain(command, options) {
438
- try {
439
- await runCommand(command, options);
440
- } catch (error) {
441
- const message = error instanceof Error ? error.message : String(error);
442
- console.error(`Error: ${message}`);
443
- process.exitCode = 1;
444
- }
445
- }
446
- export {
447
- runMain,
448
- runCommand,
449
- resolveCommand,
450
- parseArgs,
451
- defineCommand,
452
- CrustError
453
- };
2
+ import{a as K,b as I,c as W}from"./shared/chunk-36287c08.js";function G(D){if(!D.meta.name.trim())throw new I("DEFINITION","meta.name must be a non-empty string");if(D.flags)for(let[F,M]of Object.entries(D.flags)){if(F.startsWith("no-")){let P=F.slice(3);throw new I("DEFINITION",`Flag "--${F}" must not use "no-" prefix; define "${P}" and negate with "--no-${P}"`)}if(M.alias){let P=Array.isArray(M.alias)?M.alias:[M.alias];for(let q of P)if(q.startsWith("no-"))throw new I("DEFINITION",`Alias "--${q}" on "--${F}" must not use "no-" prefix (reserved for negation)`)}}let V={...D,meta:{...D.meta},...D.args&&{args:D.args.map((F)=>({...F}))},flags:D.flags?Object.fromEntries(Object.entries(D.flags).map(([F,M])=>[F,{...M}])):{},subCommands:D.subCommands?{...D.subCommands}:{}};return Object.freeze(V)}function Q(D,V){let F=[D.meta.name],M=D,P=V;while(P.length>0){let q=M.subCommands;if(!q||Object.keys(q).length===0)break;let R=P[0];if(!R||R.startsWith("-"))break;if(R in q){M=q[R],F.push(R),P=P.slice(1);continue}if(M.run)break;let J=Object.keys(q);throw new I("COMMAND_NOT_FOUND",`Unknown command "${R}".`,{input:R,available:J,commandPath:[...F],parentCommand:M})}return{command:M,argv:P,commandPath:F}}var Z="CRUST_INTERNAL_VALIDATE_ONLY",O="__CRUST_VALIDATE_RESULT__";function $(){let D=new Map;return{get(V){return D.get(V)},has(V){return D.has(V)},set(V,F){D.set(V,F)},delete(V){return D.delete(V)}}}async function j(D,V,F){for(let M of D){if(!M.setup)continue;await M.setup(V,F)}}function z(D){return{addFlag(V,F,M){if(!V.flags)throw new I("DEFINITION",`Cannot add flag "${F}": "${V.meta.name}" has no flags object`);if(F in V.flags)D?.push(`Plugin flag "--${F}" on "${V.meta.name}" overrides existing flag`);V.flags[F]=M},addSubCommand(V,F,M){if(!F.trim())throw new I("DEFINITION","addSubCommand: name must be a non-empty string");if(V.subCommands[F]){D?.push(`Plugin subcommand "${F}" on "${V.meta.name}" skipped (already exists)`);return}V.subCommands[F]=M}}}async function Y(D,V){let F=V?.argv??process.argv.slice(2),M=V?.plugins??[],P=[],q={argv:[...F],rootCommand:D,state:$()};await j(M,q,z(P));let{validateCommandTree:R}=await import("./shared/chunk-n6bfy2wh.js");R(D);for(let J of P)console.warn(`Warning: ${J}`)}async function y(D,V){if(!D.run)return;let F={args:V.args,flags:V.flags,rawArgs:V.rawArgs,command:D};try{if(D.preRun)await D.preRun(F);await D.run(F)}finally{if(D.postRun)await D.postRun(F)}}async function X(D,V,F){let M=D.map((R)=>R.middleware).filter((R)=>Boolean(R)),P=-1,q=async(R)=>{if(R<=P)throw new I("DEFINITION","Plugin middleware called next() multiple times");if(P=R,R===M.length){await F();return}let J=M[R];if(!J)throw new I("DEFINITION","Plugin middleware stack is invalid");await J(V,()=>q(R+1))};await q(0)}async function B(D,V){let F=V?.argv??process.argv.slice(2),M=V?.plugins??[],P=z(),q={argv:[...F],rootCommand:D,state:$(),route:null,input:null};try{await j(M,q,P);let R,J;try{let N=Q(D,[...F]);q.route=N,R=W(N.command,N.argv),q.input={args:R.args,flags:R.flags,rawArgs:R.rawArgs},J=N.command}catch(N){await X(M,q,async()=>{throw N});return}await X(M,q,async()=>{await y(J,R)})}catch(R){if(R instanceof I)throw R;if(R instanceof Error)throw new I("EXECUTION",R.message).withCause(R);throw new I("EXECUTION",String(R)).withCause(R)}}async function H(D,V){if(process.env[Z]==="1"){let F=(async()=>{try{return await Y(D,V),{ok:!0}}catch(M){let P=M instanceof Error?M.message:String(M);return console.error(P),process.exitCode=1,{ok:!1,error:M}}})();return globalThis[O]=F,await F,process.exit(process.exitCode??0)}try{await B(D,V)}catch(F){let M=F instanceof Error?F.message:String(F);console.error(`Error: ${M}`),process.exitCode=1}}export{H as runMain,B as runCommand,Q as resolveCommand,W as parseArgs,G as defineCommand,Z as VALIDATION_MODE_ENV,I as CrustError};
@@ -0,0 +1,3 @@
1
+ // @bun
2
+ var A=import.meta.require;class W extends Error{code;details;cause;constructor(j,G,...H){super(G);this.name="CrustError",this.code=j,this.details=H[0]}is(j){return this.code===j}withCause(j){return this.cause=j,this}}import{parseArgs as q}from"util";function U(j){let G={},H={};if(!j)return{options:G,aliasToName:H};let z=new Map;for(let B of Object.keys(j))z.set(B,B);for(let[B,J]of Object.entries(j)){if(B.startsWith("no-")){let X=B.slice(3);throw new W("DEFINITION",`Flag "--${B}" must not use "no-" prefix; define "${X}" and negate with "--no-${X}"`)}let K=J.type==="boolean"?"boolean":"string",L={type:K};if(J.multiple)L.multiple=!0;if(J.alias){let X=Array.isArray(J.alias)?J.alias:[J.alias];for(let Q of X){if(Q.startsWith("no-"))throw new W("DEFINITION",`Alias "--${Q}" on "--${B}" must not use "no-" prefix (reserved for negation)`);let _=z.get(Q);if(_)throw new W("DEFINITION",`Alias collision: "${Q.length===1?"-":"--"}${Q}" is used by both "--${_}" and "--${B}"`);if(z.set(Q,B),H[Q]=B,Q.length===1&&!L.short)L.short=Q;else{let Y={type:K};if(J.multiple)Y.multiple=!0;G[Q]=Y}}}G[B]=L}return{options:G,aliasToName:H}}function $(j,G,H){if(G==="number"){let z=Number(j);if(Number.isNaN(z))throw new W("PARSE",`Expected number for ${H}, got "${j}"`);return z}if(G==="boolean")return j==="true"||j==="1";return j}function I(j,G){if(j.default!==void 0)return j.default;if(j.required===!0)throw new W("VALIDATION",`Missing required ${G}`);return}function M(j,G,H){let z=`--${j}`;if(G.multiple&&Array.isArray(H))return G.type==="boolean"?H.filter((B)=>typeof B==="boolean"):H.map((B)=>$(B,G.type,z));if(G.type==="boolean"){if(typeof H==="boolean")return H;throw new W("PARSE",`Expected boolean value for flag "${z}", got ${typeof H}`)}if(typeof H==="string")return $(H,G.type,z);if(H===!0)return G.default??void 0;return H}function R(j,G,H){let z={};for(let B in j){let J=G[B]??B;if(!(J in H))continue;let K=j[B],L=z[J];if(L!==void 0&&Array.isArray(L)&&Array.isArray(K))L.push(...K);else z[J]=K}return z}function h(j,G,H){if(!j)return{};let z=R(G,H,j),B={};for(let[J,K]of Object.entries(j)){let L=z[J];if(L!==void 0){B[J]=M(J,K,L);continue}B[J]=K.default??void 0}return B}function P(j,G){if(!j)return;for(let[H,z]of Object.entries(j))if(z.required===!0&&z.default===void 0){if(G[H]===void 0)throw new W("VALIDATION",`Missing required flag "--${H}"`)}}function S(j,G){if(!j)return{};let H={},z=0;for(let B of j){let{name:J}=B,K=`argument "<${J}>"`;if(B.variadic){let L=G.slice(z);if(B.required===!0&&L.length===0)throw new W("VALIDATION",`Missing required ${K}`);H[J]=B.type==="string"?L:L.map((X)=>$(X,B.type,`<${J}>`)),z=G.length}else if(z<G.length)H[J]=$(G[z],B.type,`<${J}>`),z++;else H[J]=I(B,K)}return H}function O(j,G,H){if(!G)return;for(let z of j){if(z==="--")return;if(!z.startsWith("--no-"))continue;let B=z.indexOf("="),J=B===-1?z.slice(5):z.slice(5,B);if(!J)continue;let K=H[J];if(!K)continue;if(K===J)continue;if(G[K]?.type!=="boolean")continue;throw new W("PARSE",`Cannot negate alias "--no-${J}"; use "--no-${K}" instead`)}}function x(j,G){let{args:H,flags:z}=j,{options:B,aliasToName:J}=U(z);O(G,z,J);let K;try{K=q({args:G,options:B,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 L=[],X=[];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?L:X).push(Z.value??"")}}else X.push(...K.positionals);let Q=h(z,K.values,J),_=S(H,X);return P(z,Q),{args:_,flags:Q,rawArgs:L}}
3
+ export{A as a,W as b,x as c};
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ import{b as G,c as H}from"./chunk-36287c08.js";function F(j){switch(j.type){case"number":return"1";case"boolean":return"true";default:return"sample"}}function I(j){let q=[];if(j.flags)for(let[w,x]of Object.entries(j.flags)){if(x.required!==!0||x.default!==void 0)continue;if(q.push(`--${w}`),x.type!=="boolean")q.push(F(x))}if(j.args)for(let w of j.args){if(w.required!==!0||w.default!==void 0)continue;q.push(F(w))}return q}function L(j){let q=[{command:j,path:[j.meta.name]}],w=new Set;while(q.length>0){let x=q.pop();if(!x)break;let{command:y,path:D}=x;if(w.has(y))continue;w.add(y);try{H(y,I(y))}catch(z){let B=z instanceof Error?z.message:"Unknown validation error";throw new G("DEFINITION",`Command "${D.join(" ")}" failed runtime validation: ${B}`).withCause(z)}for(let[z,B]of Object.entries(y.subCommands))q.push({command:B,path:[...D,z]})}}export{L as validateCommandTree};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crustjs/core",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "Core library for the Crust CLI framework",
5
5
  "type": "module",
6
6
  "license": "MIT",