libretto 0.4.0 → 0.4.2

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,776 @@
1
+ import { z } from "zod";
2
+ function toCamelCase(input2) {
3
+ return input2.replace(
4
+ /-([a-zA-Z0-9])/g,
5
+ (_match, letter) => letter.toUpperCase()
6
+ );
7
+ }
8
+ function toKebabCase(input2) {
9
+ return input2.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
10
+ }
11
+ function zodObjectFromShape(shape) {
12
+ return z.object(shape);
13
+ }
14
+ function schemaAcceptsUndefined(schema) {
15
+ return schema.safeParse(void 0).success;
16
+ }
17
+ function pathToRouteKey(path) {
18
+ return path.join(".");
19
+ }
20
+ function pathStartsWith(path, prefix) {
21
+ if (prefix.length > path.length) return false;
22
+ return prefix.every((token, index) => path[index] === token);
23
+ }
24
+ function formatListEntry(label, description) {
25
+ return description ? ` ${label} ${description}` : ` ${label}`;
26
+ }
27
+ function isHelpFlag(arg) {
28
+ return arg === "--help" || arg === "-h";
29
+ }
30
+ function parseBooleanFlagValue(rawValue, flagName) {
31
+ if (rawValue === "true") return true;
32
+ if (rawValue === "false") return false;
33
+ throw new Error(`Invalid value for --${flagName}: expected true or false.`);
34
+ }
35
+ function buildUsagePositionalToken(positional2) {
36
+ return schemaAcceptsUndefined(positional2.schema) ? `[${positional2.key}]` : `<${positional2.key}>`;
37
+ }
38
+ function buildNamedArgFlagName(key, spec) {
39
+ if (spec.source === "--") return "--";
40
+ return spec.name ?? toKebabCase(key);
41
+ }
42
+ function buildNamedArgHelpLabel(key, spec) {
43
+ if (spec.source === "--") return "-- <args...>";
44
+ const flagName = buildNamedArgFlagName(key, spec);
45
+ if (spec.kind === "flag") {
46
+ return `--${flagName}`;
47
+ }
48
+ return `--${flagName} <value>`;
49
+ }
50
+ function normalizeNamedArgToken(token) {
51
+ return token.replace(/^-{1,2}/, "");
52
+ }
53
+ class SimpleCLIInput {
54
+ constructor(normalize, schema, definition) {
55
+ this.normalize = normalize;
56
+ this.schema = schema;
57
+ this.definition = definition;
58
+ }
59
+ parse(raw) {
60
+ try {
61
+ return this.schema.parse(this.normalize(raw));
62
+ } catch (error) {
63
+ if (error instanceof z.ZodError) {
64
+ const messages = error.issues.map((issue) => issue.message).filter((message) => message.length > 0);
65
+ if (messages.length > 0) {
66
+ throw new Error(messages.join("\n"));
67
+ }
68
+ }
69
+ throw error;
70
+ }
71
+ }
72
+ getDefinition() {
73
+ return this.definition;
74
+ }
75
+ refine(check, message) {
76
+ const nextSchema = this.schema.refine(
77
+ (value) => Boolean(check(value)),
78
+ message ? { message } : void 0
79
+ );
80
+ return new SimpleCLIInput(this.normalize, nextSchema, this.definition);
81
+ }
82
+ superRefine(check) {
83
+ const nextSchema = this.schema.superRefine(check);
84
+ return new SimpleCLIInput(this.normalize, nextSchema, this.definition);
85
+ }
86
+ }
87
+ class SimpleCLICommandBuilder {
88
+ constructor(definition) {
89
+ this.definition = definition;
90
+ }
91
+ input(input2) {
92
+ return new SimpleCLICommandBuilder({
93
+ config: this.definition.config,
94
+ input: input2,
95
+ middlewares: this.definition.middlewares,
96
+ handler: this.definition.handler
97
+ });
98
+ }
99
+ use(middleware) {
100
+ return new SimpleCLICommandBuilder({
101
+ config: this.definition.config,
102
+ input: this.definition.input,
103
+ middlewares: [...this.definition.middlewares, middleware],
104
+ handler: this.definition.handler
105
+ });
106
+ }
107
+ handle(handler) {
108
+ return new SimpleCLICommandBuilder({
109
+ config: this.definition.config,
110
+ input: this.definition.input,
111
+ middlewares: this.definition.middlewares,
112
+ handler
113
+ });
114
+ }
115
+ getDefinition() {
116
+ return this.definition;
117
+ }
118
+ }
119
+ class SimpleCLIApp {
120
+ constructor(name, routes, config = {}) {
121
+ this.name = name;
122
+ const resolution = resolveRouteTree(routes);
123
+ this.globalNamed = config.globalNamed ?? {};
124
+ for (const group2 of resolution.groups) {
125
+ if (this.resolvedGroups.has(group2.routeKey)) {
126
+ throw new Error(`Duplicate group route key: ${group2.routeKey}`);
127
+ }
128
+ this.resolvedGroups.set(group2.routeKey, group2);
129
+ }
130
+ for (const command2 of resolution.commands) {
131
+ if (this.resolvedCommands.has(command2.routeKey)) {
132
+ throw new Error(`Duplicate command route key: ${command2.routeKey}`);
133
+ }
134
+ this.resolvedCommands.set(command2.routeKey, command2);
135
+ }
136
+ this.routeEntries = resolution.routeEntries;
137
+ }
138
+ resolvedCommands = /* @__PURE__ */ new Map();
139
+ resolvedGroups = /* @__PURE__ */ new Map();
140
+ routeEntries;
141
+ globalNamed;
142
+ getCommands() {
143
+ return [...this.resolvedCommands.values()].map((command2) => ({
144
+ routeKey: command2.routeKey,
145
+ path: command2.path,
146
+ description: command2.description
147
+ }));
148
+ }
149
+ async invoke(routeKey, rawInput, initialContext = {}) {
150
+ const command2 = this.resolvedCommands.get(routeKey);
151
+ if (!command2) {
152
+ throw new Error(`Unknown command route key "${routeKey}".`);
153
+ }
154
+ const input2 = command2.input ? command2.input.parse(rawInput) : rawInput;
155
+ let ctx = { ...initialContext };
156
+ const meta = {
157
+ routeKey: command2.routeKey,
158
+ path: command2.path,
159
+ description: command2.description
160
+ };
161
+ for (const middleware of command2.middlewares) {
162
+ const next = await middleware({ input: input2, ctx, command: meta });
163
+ if (next !== void 0) {
164
+ ctx = next;
165
+ }
166
+ }
167
+ return command2.handler({ input: input2, ctx, command: meta });
168
+ }
169
+ async run(args) {
170
+ const extractedGlobalArgs = this.extractGlobalArgs(args);
171
+ const normalizedArgs = extractedGlobalArgs.args;
172
+ const helpPath = this.resolveHelpPath(normalizedArgs);
173
+ if (helpPath) {
174
+ return this.renderHelp(helpPath);
175
+ }
176
+ const exactGroup = this.findGroupByPath(normalizedArgs);
177
+ if (exactGroup) {
178
+ return this.renderGroupHelp(exactGroup);
179
+ }
180
+ const parsed = this.parseInvocation(normalizedArgs);
181
+ return this.invoke(
182
+ parsed.routeKey,
183
+ this.injectGlobalNamedArgs(
184
+ parsed.routeKey,
185
+ parsed.rawInput,
186
+ extractedGlobalArgs.named
187
+ )
188
+ );
189
+ }
190
+ renderHelp(path = []) {
191
+ if (path.length === 0) {
192
+ return this.renderRootHelp();
193
+ }
194
+ const group2 = this.findGroupByPath(path);
195
+ if (group2) {
196
+ return this.renderGroupHelp(group2);
197
+ }
198
+ const command2 = this.findCommandByPath(path);
199
+ if (command2) {
200
+ return this.renderCommandHelp(command2);
201
+ }
202
+ throw new Error(`Unknown help topic "${path.join(" ")}".`);
203
+ }
204
+ resolveHelpPath(args) {
205
+ if (args.length === 0) return [];
206
+ if (args[0] === "help") {
207
+ return args.slice(1);
208
+ }
209
+ const passthroughIndex = args.indexOf("--");
210
+ const argsBeforePassthrough = passthroughIndex >= 0 ? args.slice(0, passthroughIndex) : args;
211
+ if (argsBeforePassthrough.length === 0) {
212
+ return null;
213
+ }
214
+ if (isHelpFlag(argsBeforePassthrough[0])) {
215
+ return [];
216
+ }
217
+ const helpFlagIndex = argsBeforePassthrough.findIndex((arg) => isHelpFlag(arg));
218
+ if (helpFlagIndex >= 0) {
219
+ return argsBeforePassthrough.slice(0, helpFlagIndex);
220
+ }
221
+ return null;
222
+ }
223
+ parseInvocation(args) {
224
+ const command2 = this.findBestMatchingCommand(args);
225
+ if (!command2) {
226
+ const exactGroup = this.findGroupByPath(args);
227
+ if (exactGroup) {
228
+ throw new Error(this.renderGroupHelp(exactGroup));
229
+ }
230
+ throw new Error(`Unknown command: ${args.join(" ")}`);
231
+ }
232
+ const rawInput = this.parseCommandInput(command2, args.slice(command2.path.length));
233
+ return {
234
+ routeKey: command2.routeKey,
235
+ rawInput
236
+ };
237
+ }
238
+ parseCommandInput(command2, args) {
239
+ const inputDefinition = command2.input?.getDefinition();
240
+ if (!inputDefinition) {
241
+ if (args.length > 0) {
242
+ throw new Error(`Unexpected arguments for ${this.name} ${command2.path.join(" ")}.`);
243
+ }
244
+ return {
245
+ positionals: [],
246
+ named: {}
247
+ };
248
+ }
249
+ const positionals = [];
250
+ const named = {};
251
+ const namedSpecs = buildNamedArgLookup(inputDefinition.named);
252
+ const passthroughEntry = Object.entries(inputDefinition.named).find(
253
+ ([, spec]) => spec.source === "--"
254
+ );
255
+ for (let index = 0; index < args.length; index++) {
256
+ const arg = args[index];
257
+ if (arg === "--") {
258
+ if (!passthroughEntry) {
259
+ throw new Error(`Unexpected "--" for ${this.name} ${command2.path.join(" ")}.`);
260
+ }
261
+ named["--"] = args.slice(index + 1);
262
+ break;
263
+ }
264
+ if (arg.startsWith("--")) {
265
+ const [rawName, inlineValue] = splitNamedArg(arg.slice(2));
266
+ const namedEntry = namedSpecs.get(rawName);
267
+ if (!namedEntry) {
268
+ throw new Error(`Unknown option: --${rawName}`);
269
+ }
270
+ const storeKey = buildNamedArgFlagName(namedEntry.key, namedEntry.spec);
271
+ named[storeKey] = readNamedArgValue(
272
+ args,
273
+ index,
274
+ rawName,
275
+ `--${rawName}`,
276
+ namedEntry.spec,
277
+ inlineValue,
278
+ namedSpecs
279
+ );
280
+ if (inlineValue === void 0 && namedEntry.spec.kind !== "flag") {
281
+ index += 1;
282
+ }
283
+ continue;
284
+ }
285
+ if (arg.startsWith("-")) {
286
+ const [rawName, inlineValue] = splitNamedArg(arg.slice(1));
287
+ const namedEntry = namedSpecs.get(rawName);
288
+ if (!namedEntry) {
289
+ throw new Error(`Unknown option: ${arg}`);
290
+ }
291
+ const storeKey = buildNamedArgFlagName(namedEntry.key, namedEntry.spec);
292
+ named[storeKey] = readNamedArgValue(
293
+ args,
294
+ index,
295
+ rawName,
296
+ `-${rawName}`,
297
+ namedEntry.spec,
298
+ inlineValue,
299
+ namedSpecs
300
+ );
301
+ if (inlineValue === void 0 && namedEntry.spec.kind !== "flag") {
302
+ index += 1;
303
+ }
304
+ continue;
305
+ }
306
+ positionals.push(arg);
307
+ }
308
+ validateParsedPositionals(command2, inputDefinition.positionals, positionals);
309
+ validateRequiredNamedArgs(inputDefinition.named, named);
310
+ return {
311
+ positionals,
312
+ named
313
+ };
314
+ }
315
+ extractGlobalArgs(args) {
316
+ if (Object.keys(this.globalNamed).length === 0) {
317
+ return {
318
+ args,
319
+ named: {}
320
+ };
321
+ }
322
+ const remainingArgs = [];
323
+ const named = {};
324
+ const namedSpecs = buildNamedArgLookup(this.globalNamed);
325
+ for (let index = 0; index < args.length; index += 1) {
326
+ const arg = args[index];
327
+ if (arg === "--") {
328
+ remainingArgs.push(...args.slice(index));
329
+ break;
330
+ }
331
+ if (arg.startsWith("--")) {
332
+ const [rawName, inlineValue] = splitNamedArg(arg.slice(2));
333
+ const namedEntry = namedSpecs.get(rawName);
334
+ if (!namedEntry) {
335
+ remainingArgs.push(arg);
336
+ continue;
337
+ }
338
+ named[namedEntry.key] = readNamedArgValue(
339
+ args,
340
+ index,
341
+ rawName,
342
+ `--${rawName}`,
343
+ namedEntry.spec,
344
+ inlineValue,
345
+ namedSpecs
346
+ );
347
+ if (inlineValue === void 0 && namedEntry.spec.kind !== "flag") {
348
+ index += 1;
349
+ }
350
+ continue;
351
+ }
352
+ if (arg.startsWith("-")) {
353
+ const [rawName, inlineValue] = splitNamedArg(arg.slice(1));
354
+ const namedEntry = namedSpecs.get(rawName);
355
+ if (!namedEntry) {
356
+ remainingArgs.push(arg);
357
+ continue;
358
+ }
359
+ named[namedEntry.key] = readNamedArgValue(
360
+ args,
361
+ index,
362
+ rawName,
363
+ `-${rawName}`,
364
+ namedEntry.spec,
365
+ inlineValue,
366
+ namedSpecs
367
+ );
368
+ if (inlineValue === void 0 && namedEntry.spec.kind !== "flag") {
369
+ index += 1;
370
+ }
371
+ continue;
372
+ }
373
+ remainingArgs.push(arg);
374
+ }
375
+ return {
376
+ args: remainingArgs,
377
+ named
378
+ };
379
+ }
380
+ injectGlobalNamedArgs(routeKey, rawInput, globalNamed) {
381
+ if (Object.keys(globalNamed).length === 0) {
382
+ return rawInput;
383
+ }
384
+ const inputDefinition = this.resolvedCommands.get(routeKey)?.input?.getDefinition();
385
+ if (!inputDefinition) {
386
+ return rawInput;
387
+ }
388
+ const named = { ...rawInput.named ?? {} };
389
+ let changed = false;
390
+ for (const key of Object.keys(inputDefinition.named)) {
391
+ if (Object.prototype.hasOwnProperty.call(named, key)) continue;
392
+ if (!Object.prototype.hasOwnProperty.call(globalNamed, key)) continue;
393
+ named[key] = globalNamed[key];
394
+ changed = true;
395
+ }
396
+ if (!changed) {
397
+ return rawInput;
398
+ }
399
+ return {
400
+ positionals: rawInput.positionals ?? [],
401
+ named
402
+ };
403
+ }
404
+ renderRootHelp() {
405
+ const lines = [`Usage: ${this.name} <command>`, "", "Commands:"];
406
+ for (const entry of this.getImmediateRouteEntries([])) {
407
+ lines.push(formatListEntry(entry.label, entry.description));
408
+ }
409
+ return lines.join("\n");
410
+ }
411
+ renderGroupHelp(group2) {
412
+ const lines = [];
413
+ if (group2.description) {
414
+ lines.push(group2.description, "");
415
+ }
416
+ lines.push(`Usage: ${this.name} ${group2.path.join(" ")} <subcommand>`);
417
+ lines.push("", "Commands:");
418
+ for (const entry of this.getImmediateRouteEntries(group2.path)) {
419
+ lines.push(formatListEntry(entry.label, entry.description));
420
+ }
421
+ return lines.join("\n");
422
+ }
423
+ renderCommandHelp(command2) {
424
+ const lines = [
425
+ command2.description,
426
+ "",
427
+ `Usage: ${this.buildCommandUsage(command2)}`
428
+ ];
429
+ const inputDefinition = command2.input?.getDefinition();
430
+ if (!inputDefinition) {
431
+ return lines.join("\n");
432
+ }
433
+ const argumentLines = inputDefinition.positionals.map(
434
+ (positional2) => formatListEntry(buildUsagePositionalToken(positional2), positional2.help)
435
+ );
436
+ if (argumentLines.length > 0) {
437
+ lines.push("", "Arguments:");
438
+ lines.push(...argumentLines);
439
+ }
440
+ const optionLines = Object.entries(inputDefinition.named).map(
441
+ ([key, spec]) => formatListEntry(buildNamedArgHelpLabel(key, spec), spec.help)
442
+ );
443
+ if (optionLines.length > 0) {
444
+ lines.push("", "Options:");
445
+ lines.push(...optionLines);
446
+ }
447
+ return lines.join("\n");
448
+ }
449
+ buildCommandUsage(command2) {
450
+ const tokens = [this.name, ...command2.path];
451
+ const inputDefinition = command2.input?.getDefinition();
452
+ if (!inputDefinition) {
453
+ return tokens.join(" ");
454
+ }
455
+ tokens.push(...inputDefinition.positionals.map(buildUsagePositionalToken));
456
+ if (Object.keys(inputDefinition.named).length > 0) {
457
+ tokens.push("[options]");
458
+ }
459
+ return tokens.join(" ");
460
+ }
461
+ getImmediateRouteEntries(path) {
462
+ const seen = /* @__PURE__ */ new Set();
463
+ const entries = [];
464
+ for (const routeEntry of this.routeEntries) {
465
+ if (!pathStartsWith(routeEntry.path, path)) continue;
466
+ if (routeEntry.path.length !== path.length + 1) continue;
467
+ const token = routeEntry.path[path.length];
468
+ if (seen.has(token)) continue;
469
+ seen.add(token);
470
+ if (routeEntry.kind === "group") {
471
+ const group2 = this.findGroupByPath(routeEntry.path);
472
+ entries.push({
473
+ label: `${token} <subcommand>`,
474
+ description: group2?.description
475
+ });
476
+ continue;
477
+ }
478
+ const command2 = this.findCommandByPath(routeEntry.path);
479
+ entries.push({
480
+ label: token,
481
+ description: command2?.description
482
+ });
483
+ }
484
+ return entries;
485
+ }
486
+ findBestMatchingCommand(args) {
487
+ let bestMatch = null;
488
+ for (const command2 of this.resolvedCommands.values()) {
489
+ if (command2.path.length > args.length) continue;
490
+ if (!pathStartsWith(args, command2.path)) continue;
491
+ if (!bestMatch || command2.path.length > bestMatch.path.length) {
492
+ bestMatch = command2;
493
+ }
494
+ }
495
+ return bestMatch;
496
+ }
497
+ findCommandByPath(path) {
498
+ const routeKey = pathToRouteKey(path);
499
+ return this.resolvedCommands.get(routeKey) ?? null;
500
+ }
501
+ findGroupByPath(path) {
502
+ const routeKey = pathToRouteKey(path);
503
+ return this.resolvedGroups.get(routeKey) ?? null;
504
+ }
505
+ }
506
+ function splitNamedArg(arg) {
507
+ const separatorIndex = arg.indexOf("=");
508
+ if (separatorIndex < 0) return [arg, void 0];
509
+ return [
510
+ arg.slice(0, separatorIndex),
511
+ arg.slice(separatorIndex + 1)
512
+ ];
513
+ }
514
+ function readNamedArgValue(args, index, rawName, displayName, spec, inlineValue, namedSpecs) {
515
+ if (spec.kind === "flag") {
516
+ return inlineValue === void 0 ? true : parseBooleanFlagValue(inlineValue, rawName);
517
+ }
518
+ if (inlineValue !== void 0) {
519
+ return inlineValue;
520
+ }
521
+ const nextValue = args[index + 1];
522
+ if (nextValue === void 0 || nextValue === "--" || isRecognizedNamedArgToken(nextValue, namedSpecs)) {
523
+ throw new Error(`Missing value for ${displayName}.`);
524
+ }
525
+ return nextValue;
526
+ }
527
+ function isRecognizedNamedArgToken(token, namedSpecs) {
528
+ if (token === "-" || !token.startsWith("-")) {
529
+ return false;
530
+ }
531
+ const normalizedToken = token.startsWith("--") ? token.slice(2) : token.slice(1);
532
+ const [rawName] = splitNamedArg(normalizedToken);
533
+ return namedSpecs.has(rawName);
534
+ }
535
+ function buildNamedArgLookup(namedDefinition) {
536
+ const lookup = /* @__PURE__ */ new Map();
537
+ for (const [key, spec] of Object.entries(namedDefinition)) {
538
+ if (spec.source === "--") continue;
539
+ const flagName = buildNamedArgFlagName(key, spec);
540
+ lookup.set(flagName, { key, spec });
541
+ lookup.set(key, { key, spec });
542
+ lookup.set(toCamelCase(flagName), { key, spec });
543
+ for (const alias of spec.aliases ?? []) {
544
+ const normalizedAlias = normalizeNamedArgToken(alias);
545
+ lookup.set(normalizedAlias, { key, spec });
546
+ lookup.set(toCamelCase(normalizedAlias), { key, spec });
547
+ }
548
+ }
549
+ return lookup;
550
+ }
551
+ function validateParsedPositionals(command2, definitions, positionals) {
552
+ const variadicDefinition = definitions.find((definition) => definition.variadic);
553
+ if (!variadicDefinition && positionals.length > definitions.length) {
554
+ throw new Error(`Unexpected arguments for ${command2.path.join(" ")}.`);
555
+ }
556
+ definitions.forEach((definition, index) => {
557
+ const value = definition.variadic ? positionals.slice(index) : positionals[index];
558
+ if (value !== void 0 && (!Array.isArray(value) || value.length > 0)) return;
559
+ if (schemaAcceptsUndefined(definition.schema)) return;
560
+ throw new Error(`Missing required argument <${definition.key}>.`);
561
+ });
562
+ }
563
+ function validateInputDefinition(definition) {
564
+ const variadicIndex = definition.positionals.findIndex((positional2) => positional2.variadic);
565
+ if (variadicIndex < 0) return;
566
+ if (variadicIndex !== definition.positionals.length - 1) {
567
+ throw new Error("Variadic positional arguments must be the last positional.");
568
+ }
569
+ }
570
+ function validateRequiredNamedArgs(definitions, named) {
571
+ for (const [key, spec] of Object.entries(definitions)) {
572
+ if (schemaAcceptsUndefined(spec.schema)) continue;
573
+ const flagName = spec.source === "--" ? "--" : buildNamedArgFlagName(key, spec);
574
+ if (Object.prototype.hasOwnProperty.call(named, flagName)) continue;
575
+ if (spec.source === "--") {
576
+ throw new Error(`Missing required passthrough arguments after --.`);
577
+ }
578
+ throw new Error(`Missing required option --${flagName}.`);
579
+ }
580
+ }
581
+ function resolveRouteTree(routes, parentPath = [], parentMiddlewares = []) {
582
+ const resolved = {
583
+ commands: [],
584
+ groups: [],
585
+ routeEntries: []
586
+ };
587
+ for (const [token, routeValue] of Object.entries(routes)) {
588
+ if (isGroup(routeValue)) {
589
+ const groupPath = [...parentPath, token];
590
+ resolved.groups.push({
591
+ routeKey: pathToRouteKey(groupPath),
592
+ path: groupPath,
593
+ description: routeValue.description
594
+ });
595
+ resolved.routeEntries.push({
596
+ kind: "group",
597
+ path: groupPath
598
+ });
599
+ const nested = resolveRouteTree(
600
+ routeValue.routes,
601
+ groupPath,
602
+ [...parentMiddlewares, ...routeValue.middlewares]
603
+ );
604
+ resolved.commands.push(...nested.commands);
605
+ resolved.groups.push(...nested.groups);
606
+ resolved.routeEntries.push(...nested.routeEntries);
607
+ continue;
608
+ }
609
+ const command2 = routeValue.getDefinition();
610
+ if (!command2.handler) {
611
+ throw new Error(`Command "${[...parentPath, token].join(" ")}" is missing a handler.`);
612
+ }
613
+ const path = [...parentPath, token];
614
+ resolved.commands.push({
615
+ routeKey: pathToRouteKey(path),
616
+ path,
617
+ description: command2.config.description,
618
+ input: command2.input,
619
+ middlewares: mergeInheritedMiddlewares(parentMiddlewares, command2.middlewares),
620
+ handler: command2.handler
621
+ });
622
+ resolved.routeEntries.push({
623
+ kind: "command",
624
+ path
625
+ });
626
+ }
627
+ return resolved;
628
+ }
629
+ function mergeInheritedMiddlewares(parentMiddlewares, commandMiddlewares) {
630
+ if (parentMiddlewares.length === 0) {
631
+ return [...commandMiddlewares];
632
+ }
633
+ if (commandMiddlewares.length >= parentMiddlewares.length && parentMiddlewares.every((middleware, index) => commandMiddlewares[index] === middleware)) {
634
+ return [...commandMiddlewares];
635
+ }
636
+ return [...parentMiddlewares, ...commandMiddlewares];
637
+ }
638
+ function isGroup(value) {
639
+ return value.kind === "group";
640
+ }
641
+ function buildInputNormalizer(definition) {
642
+ return (raw) => {
643
+ const output = {};
644
+ const positionals = raw.positionals ?? [];
645
+ const named = raw.named ?? {};
646
+ definition.positionals.forEach((positional2, index) => {
647
+ output[positional2.key] = positional2.variadic ? positionals.slice(index) : positionals[index];
648
+ });
649
+ for (const [key, spec] of Object.entries(definition.named)) {
650
+ const sourceKey = spec.source === "--" ? "--" : spec.name ?? key;
651
+ const normalizedCandidates = [
652
+ sourceKey,
653
+ spec.name ? toCamelCase(spec.name) : "",
654
+ ...(spec.aliases ?? []).flatMap((alias) => {
655
+ const normalizedAlias = normalizeNamedArgToken(alias);
656
+ return [
657
+ normalizedAlias,
658
+ toCamelCase(normalizedAlias)
659
+ ];
660
+ }),
661
+ toKebabCase(key),
662
+ key
663
+ ].filter((candidate) => candidate.length > 0);
664
+ let value = void 0;
665
+ for (const candidate of normalizedCandidates) {
666
+ if (Object.prototype.hasOwnProperty.call(named, candidate)) {
667
+ value = named[candidate];
668
+ break;
669
+ }
670
+ }
671
+ output[key] = value;
672
+ }
673
+ return output;
674
+ };
675
+ }
676
+ function buildInputSchema(definition) {
677
+ const shape = {};
678
+ for (const positional2 of definition.positionals) {
679
+ shape[positional2.key] = positional2.schema;
680
+ }
681
+ for (const [key, named] of Object.entries(definition.named)) {
682
+ shape[key] = named.schema;
683
+ }
684
+ return zodObjectFromShape(shape);
685
+ }
686
+ function positional(key, schema, options) {
687
+ return {
688
+ kind: "positional",
689
+ key,
690
+ schema,
691
+ help: options?.help,
692
+ variadic: options?.variadic
693
+ };
694
+ }
695
+ function option(schema, options) {
696
+ return {
697
+ kind: "option",
698
+ schema,
699
+ help: options?.help,
700
+ name: options?.name,
701
+ aliases: options?.aliases,
702
+ source: options?.source
703
+ };
704
+ }
705
+ function flag(options) {
706
+ return {
707
+ kind: "flag",
708
+ schema: z.boolean().default(false),
709
+ help: options?.help,
710
+ name: options?.name,
711
+ aliases: options?.aliases
712
+ };
713
+ }
714
+ function input(definition) {
715
+ validateInputDefinition(definition);
716
+ return new SimpleCLIInput(
717
+ buildInputNormalizer(definition),
718
+ buildInputSchema(definition),
719
+ definition
720
+ );
721
+ }
722
+ function command(config) {
723
+ return new SimpleCLICommandBuilder({
724
+ config,
725
+ middlewares: []
726
+ });
727
+ }
728
+ function group(config) {
729
+ return createScope([]).group(config);
730
+ }
731
+ function createScope(middlewares) {
732
+ return {
733
+ use(middleware) {
734
+ return createScope([
735
+ ...middlewares,
736
+ middleware
737
+ ]);
738
+ },
739
+ group(config) {
740
+ return {
741
+ kind: "group",
742
+ description: config.description,
743
+ routes: config.routes,
744
+ middlewares: [...middlewares]
745
+ };
746
+ },
747
+ command(config) {
748
+ return new SimpleCLICommandBuilder({
749
+ config,
750
+ middlewares: [...middlewares]
751
+ });
752
+ }
753
+ };
754
+ }
755
+ function use(middleware) {
756
+ return createScope([middleware]);
757
+ }
758
+ function define(name, routes, config) {
759
+ return new SimpleCLIApp(name, routes, config);
760
+ }
761
+ const SimpleCLI = {
762
+ define,
763
+ command,
764
+ group,
765
+ use,
766
+ input,
767
+ positional,
768
+ option,
769
+ flag
770
+ };
771
+ export {
772
+ SimpleCLI,
773
+ SimpleCLIApp,
774
+ SimpleCLICommandBuilder,
775
+ SimpleCLIInput
776
+ };