@kubb/ast 4.36.1 → 5.0.0-alpha.10

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.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import "./chunk--u3MIqq1.js";
2
+ import { parseArgs, styleText } from "node:util";
2
3
  //#region src/constants.ts
3
4
  const visitorDepths = {
4
5
  shallow: "shallow",
@@ -10,7 +11,10 @@ const nodeKinds = {
10
11
  schema: "Schema",
11
12
  property: "Property",
12
13
  parameter: "Parameter",
13
- response: "Response"
14
+ response: "Response",
15
+ functionParameter: "FunctionParameter",
16
+ objectBindingParameter: "ObjectBindingParameter",
17
+ functionParameters: "FunctionParameters"
14
18
  };
15
19
  const schemaTypes = {
16
20
  string: "string",
@@ -35,7 +39,8 @@ const schemaTypes = {
35
39
  uuid: "uuid",
36
40
  email: "email",
37
41
  url: "url",
38
- blob: "blob"
42
+ blob: "blob",
43
+ never: "never"
39
44
  };
40
45
  const httpMethods = {
41
46
  get: "GET",
@@ -133,6 +138,288 @@ function createResponse(props) {
133
138
  kind: "Response"
134
139
  };
135
140
  }
141
+ /**
142
+ * Creates a `FunctionParameterNode`. `optional` defaults to `false`.
143
+ *
144
+ * @example Required typed param
145
+ * ```ts
146
+ * createFunctionParameter({ name: 'petId', type: 'string' })
147
+ * // → petId: string
148
+ * ```
149
+ *
150
+ * @example Optional param
151
+ * ```ts
152
+ * createFunctionParameter({ name: 'params', type: 'QueryParams', optional: true })
153
+ * // → params?: QueryParams
154
+ * ```
155
+ *
156
+ * @example Param with default (implicitly optional — cannot combine with `optional: true`)
157
+ * ```ts
158
+ * createFunctionParameter({ name: 'config', type: 'RequestConfig', default: '{}' })
159
+ * // → config: RequestConfig = {}
160
+ * ```
161
+ */
162
+ function createFunctionParameter(props) {
163
+ return {
164
+ optional: false,
165
+ ...props,
166
+ kind: "FunctionParameter"
167
+ };
168
+ }
169
+ /**
170
+ * Creates an `ObjectBindingParameterNode` — an object-destructured parameter group.
171
+ *
172
+ * @example Destructured object param
173
+ * ```ts
174
+ * createObjectBindingParameter({
175
+ * properties: [
176
+ * createFunctionParameter({ name: 'id', type: 'string', optional: false }),
177
+ * createFunctionParameter({ name: 'name', type: 'string', optional: true }),
178
+ * ],
179
+ * default: '{}',
180
+ * })
181
+ * // declaration → { id, name? }: { id: string; name?: string } = {}
182
+ * // call → { id, name }
183
+ * ```
184
+ *
185
+ * @example Inline — children emitted as individual top-level params
186
+ * ```ts
187
+ * createObjectBindingParameter({
188
+ * properties: [createFunctionParameter({ name: 'petId', type: 'string', optional: false })],
189
+ * inline: true,
190
+ * })
191
+ * // declaration → petId: string
192
+ * // call → petId
193
+ * ```
194
+ */
195
+ function createObjectBindingParameter(props) {
196
+ return {
197
+ ...props,
198
+ kind: "ObjectBindingParameter"
199
+ };
200
+ }
201
+ /**
202
+ * Creates a `FunctionParametersNode` from an ordered list of params.
203
+ *
204
+ * @example
205
+ * ```ts
206
+ * createFunctionParameters({
207
+ * params: [
208
+ * createFunctionParameter({ name: 'petId', type: 'string', optional: false }),
209
+ * createFunctionParameter({ name: 'config', type: 'RequestConfig', optional: false, default: '{}' }),
210
+ * ],
211
+ * })
212
+ * ```
213
+ */
214
+ function createFunctionParameters(props = {}) {
215
+ return {
216
+ params: [],
217
+ ...props,
218
+ kind: "FunctionParameters"
219
+ };
220
+ }
221
+ //#endregion
222
+ //#region src/printer.ts
223
+ /**
224
+ * Creates a named printer factory. Mirrors the `createPlugin` / `createAdapter` pattern
225
+ * from `@kubb/core` — wraps a builder to make options optional and separates raw options
226
+ * from resolved options.
227
+ *
228
+ * The builder receives resolved options and returns:
229
+ * - `name` — a unique identifier for the printer
230
+ * - `options` — options stored on the returned printer instance
231
+ * - `nodes` — a map of `SchemaType` → handler functions that convert a `SchemaNode` to `TOutput`
232
+ * - `print` _(optional)_ — a root-level override that becomes the public `printer.print`.
233
+ * Inside it, `this.print(node)` still dispatches to the `nodes` map — safe recursion, no infinite loop.
234
+ *
235
+ * When no `print` override is provided, `printer.print` is the node-level dispatcher directly.
236
+ *
237
+ * @example Basic usage — Zod schema printer
238
+ * ```ts
239
+ * type ZodPrinter = PrinterFactoryOptions<'zod', { strict?: boolean }, string>
240
+ *
241
+ * export const zodPrinter = definePrinter<ZodPrinter>((options) => ({
242
+ * name: 'zod',
243
+ * options: { strict: options.strict ?? true },
244
+ * nodes: {
245
+ * string: () => 'z.string()',
246
+ * object(node) {
247
+ * const props = node.properties.map(p => `${p.name}: ${this.print(p.schema)}`).join(', ')
248
+ * return `z.object({ ${props} })`
249
+ * },
250
+ * },
251
+ * }))
252
+ * ```
253
+ *
254
+ * @example With a root-level `print` override to wrap output in a full declaration
255
+ * ```ts
256
+ * type TsPrinter = PrinterFactoryOptions<'ts', { typeName?: string }, ts.TypeNode, ts.Node>
257
+ *
258
+ * export const printerTs = definePrinter<TsPrinter>((options) => ({
259
+ * name: 'ts',
260
+ * options,
261
+ * nodes: { string: () => factory.keywordTypeNodes.string },
262
+ * print(node) {
263
+ * const type = this.print(node) // calls the node-level dispatcher
264
+ * if (!type || !this.options.typeName) return type
265
+ * return factory.createTypeAliasDeclaration(this.options.typeName, type)
266
+ * },
267
+ * }))
268
+ * ```
269
+ */
270
+ function definePrinter(build) {
271
+ return createPrinterFactory((node) => node.type)(build);
272
+ }
273
+ /**
274
+ * Generic printer factory. Extracts the core dispatch + context logic so it can be reused
275
+ * for any node type — not just `SchemaNode`. `definePrinter` is built on top of this.
276
+ *
277
+ * @param getKey — derives the handler-map key from a node. Return `undefined` to skip.
278
+ *
279
+ * @example
280
+ * ```ts
281
+ * export const defineFunctionPrinter = createPrinterFactory<FunctionNode, FunctionNodeType, FunctionNodeByType>(
282
+ * (node) => kindToHandlerKey[node.kind],
283
+ * )
284
+ * ```
285
+ */
286
+ function createPrinterFactory(getKey) {
287
+ return function(build) {
288
+ return (options) => {
289
+ const { name, options: resolvedOptions, nodes, print: printOverride } = build(options ?? {});
290
+ const context = {
291
+ options: resolvedOptions,
292
+ print: (node) => {
293
+ const key = getKey(node);
294
+ if (key === void 0) return void 0;
295
+ const handler = nodes[key];
296
+ if (!handler) return void 0;
297
+ return handler.call(context, node);
298
+ }
299
+ };
300
+ return {
301
+ name,
302
+ options: resolvedOptions,
303
+ print: printOverride ? printOverride.bind(context) : context.print
304
+ };
305
+ };
306
+ };
307
+ }
308
+ //#endregion
309
+ //#region src/functionPrinter.ts
310
+ const kindToHandlerKey = {
311
+ FunctionParameter: "functionParameter",
312
+ ObjectBindingParameter: "objectBindingParameter",
313
+ FunctionParameters: "functionParameters"
314
+ };
315
+ /**
316
+ * Creates a named function-signature printer factory.
317
+ * Built on `createPrinterFactory` — dispatches on `node.kind` instead of `node.type`.
318
+ *
319
+ * @example
320
+ * ```ts
321
+ * type MyPrinter = PrinterFactoryOptions<'my', { mode: 'declaration' | 'call' }, string>
322
+ *
323
+ * export const myPrinter = defineFunctionPrinter<MyPrinter>((options) => ({
324
+ * name: 'my',
325
+ * options,
326
+ * nodes: {
327
+ * functionParameter(node) {
328
+ * return options.mode === 'declaration' && node.type ? `${node.name}: ${node.type}` : node.name
329
+ * },
330
+ * objectBindingParameter(node) {
331
+ * const inner = node.properties.map(p => this.print(p)).filter(Boolean).join(', ')
332
+ * return `{ ${inner} }`
333
+ * },
334
+ * functionParameters(node) {
335
+ * return node.params.map(p => this.print(p)).filter(Boolean).join(', ')
336
+ * },
337
+ * },
338
+ * }))
339
+ * ```
340
+ */
341
+ const defineFunctionPrinter = createPrinterFactory((node) => kindToHandlerKey[node.kind]);
342
+ function rank(param) {
343
+ if (param.kind === "ObjectBindingParameter") {
344
+ if (param.default) return 2;
345
+ return param.optional ?? param.properties.every((p) => p.optional || p.default !== void 0) ? 1 : 0;
346
+ }
347
+ if (param.rest) return 3;
348
+ if (param.default) return 2;
349
+ return param.optional ? 1 : 0;
350
+ }
351
+ function sortParams(params) {
352
+ return [...params].sort((a, b) => rank(a) - rank(b));
353
+ }
354
+ function sortChildParams(params) {
355
+ return [...params].sort((a, b) => rank(a) - rank(b));
356
+ }
357
+ /**
358
+ * Default function-signature printer. Covers the four standard output modes
359
+ * used throughout Kubb plugins.
360
+ *
361
+ * @example
362
+ * ```ts
363
+ * const printer = functionSignaturePrinter({ mode: 'declaration' })
364
+ *
365
+ * const sig = createFunctionParameters({
366
+ * params: [
367
+ * createFunctionParameter({ name: 'petId', type: 'string', optional: false }),
368
+ * createFunctionParameter({ name: 'config', type: 'Config', optional: false, default: '{}' }),
369
+ * ],
370
+ * })
371
+ *
372
+ * printer.print(sig) // → "petId: string, config: Config = {}"
373
+ * ```
374
+ */
375
+ const functionPrinter = defineFunctionPrinter((options) => ({
376
+ name: "functionParameters",
377
+ options,
378
+ nodes: {
379
+ functionParameter(node) {
380
+ const { mode, transformName, transformType } = this.options;
381
+ const name = transformName ? transformName(node.name) : node.name;
382
+ const type = node.type && transformType ? transformType(node.type) : node.type;
383
+ if (mode === "keys" || mode === "values") return node.rest ? `...${name}` : name;
384
+ if (mode === "call") return node.rest ? `...${name}` : name;
385
+ if (node.rest) return type ? `...${name}: ${type}` : `...${name}`;
386
+ if (type) {
387
+ if (node.optional) return `${name}?: ${type}`;
388
+ return node.default ? `${name}: ${type} = ${node.default}` : `${name}: ${type}`;
389
+ }
390
+ return node.default ? `${name} = ${node.default}` : name;
391
+ },
392
+ objectBindingParameter(node) {
393
+ const { mode, transformName, transformType } = this.options;
394
+ const sorted = sortChildParams(node.properties);
395
+ const isOptional = node.optional ?? sorted.every((p) => p.optional || p.default !== void 0);
396
+ if (node.inline) return sorted.map((p) => this.print(p)).filter(Boolean).join(", ");
397
+ if (mode === "keys" || mode === "values") return `{ ${sorted.map((p) => p.name).join(", ")} }`;
398
+ if (mode === "call") return `{ ${sorted.map((p) => p.name).join(", ")} }`;
399
+ const names = sorted.map((p) => {
400
+ return transformName ? transformName(p.name) : p.name;
401
+ });
402
+ const nameStr = names.length ? `{ ${names.join(", ")} }` : void 0;
403
+ if (!nameStr) return null;
404
+ let typeAnnotation = node.type;
405
+ if (!typeAnnotation) {
406
+ const typeParts = sorted.filter((p) => p.type).map((p) => {
407
+ const t = transformType && p.type ? transformType(p.type) : p.type;
408
+ return p.optional || p.default !== void 0 ? `${p.name}?: ${t}` : `${p.name}: ${t}`;
409
+ });
410
+ typeAnnotation = typeParts.length ? `{ ${typeParts.join("; ")} }` : void 0;
411
+ }
412
+ if (typeAnnotation) {
413
+ if (isOptional) return `${nameStr}: ${typeAnnotation} = ${node.default ?? "{}"}`;
414
+ return node.default ? `${nameStr}: ${typeAnnotation} = ${node.default}` : `${nameStr}: ${typeAnnotation}`;
415
+ }
416
+ return node.default ? `${nameStr} = ${node.default}` : nameStr;
417
+ },
418
+ functionParameters(node) {
419
+ return sortParams(node.params).map((p) => this.print(p)).filter(Boolean).join(", ");
420
+ }
421
+ }
422
+ }));
136
423
  //#endregion
137
424
  //#region src/guards.ts
138
425
  /**
@@ -168,60 +455,18 @@ const isParameterNode = isKind("Parameter");
168
455
  * Type guard for `ResponseNode`.
169
456
  */
170
457
  const isResponseNode = isKind("Response");
171
- //#endregion
172
- //#region src/printer.ts
173
458
  /**
174
- * Creates a named printer factory. Mirrors the `definePlugin` / `defineAdapter` pattern
175
- * from `@kubb/core` — wraps a builder to make options optional and separates raw options
176
- * from resolved options.
177
- *
178
- * @example
179
- * ```ts
180
- * type ZodPrinter = PrinterFactoryOptions<'zod', { strict?: boolean }, { strict: boolean }, string>
181
- *
182
- * export const zodPrinter = definePrinter<ZodPrinter>((options) => {
183
- * const { strict = true } = options
184
- * return {
185
- * name: 'zod',
186
- * options: { strict },
187
- * nodes: {
188
- * string(node) {
189
- * return `z.string()`
190
- * },
191
- * object(node) {
192
- * const props = node.properties
193
- * ?.map(p => `${p.name}: ${this.print(p)}`)
194
- * .join(', ') ?? ''
195
- * return `z.object({ ${props} })`
196
- * },
197
- * },
198
- * }
199
- * })
200
- *
201
- * const printer = zodPrinter({ strict: false })
202
- * printer.name // 'zod'
203
- * printer.options // { strict: false }
204
- * printer.print(node) // 'z.string()'
205
- * ```
459
+ * Type guard for `FunctionParameterNode`.
206
460
  */
207
- function definePrinter(build) {
208
- return (options) => {
209
- const { name, options: resolvedOptions, nodes } = build(options ?? {});
210
- const context = {
211
- options: resolvedOptions,
212
- print: (node) => {
213
- const handler = nodes[node.type];
214
- return handler ? handler.call(context, node) : void 0;
215
- }
216
- };
217
- return {
218
- name,
219
- options: resolvedOptions,
220
- print: context.print,
221
- for: (nodes) => nodes.map(context.print)
222
- };
223
- };
224
- }
461
+ const isFunctionParameterNode = isKind("FunctionParameter");
462
+ /**
463
+ * Type guard for `ObjectBindingParameterNode`.
464
+ */
465
+ const isObjectBindingParameterNode = isKind("ObjectBindingParameter");
466
+ /**
467
+ * Type guard for `FunctionParametersNode`.
468
+ */
469
+ const isFunctionParametersNode = isKind("FunctionParameters");
225
470
  //#endregion
226
471
  //#region src/refs.ts
227
472
  /**
@@ -245,7 +490,309 @@ function refMapToObject(refMap) {
245
490
  return Object.fromEntries(refMap);
246
491
  }
247
492
  //#endregion
493
+ //#region ../../internals/utils/dist/index.js
494
+ /**
495
+ * Shared implementation for camelCase and PascalCase conversion.
496
+ * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
497
+ * and capitalizes each word according to `pascal`.
498
+ *
499
+ * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
500
+ */
501
+ function toCamelOrPascal(text, pascal) {
502
+ return text.trim().replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/(\d)([a-z])/g, "$1 $2").split(/[\s\-_./\\:]+/).filter(Boolean).map((word, i) => {
503
+ if (word.length > 1 && word === word.toUpperCase()) return word;
504
+ if (i === 0 && !pascal) return word.charAt(0).toLowerCase() + word.slice(1);
505
+ return word.charAt(0).toUpperCase() + word.slice(1);
506
+ }).join("").replace(/[^a-zA-Z0-9]/g, "");
507
+ }
508
+ /**
509
+ * Splits `text` on `.` and applies `transformPart` to each segment.
510
+ * The last segment receives `isLast = true`, all earlier segments receive `false`.
511
+ * Segments are joined with `/` to form a file path.
512
+ */
513
+ function applyToFileParts(text, transformPart) {
514
+ const parts = text.split(".");
515
+ return parts.map((part, i) => transformPart(part, i === parts.length - 1)).join("/");
516
+ }
517
+ /**
518
+ * Converts `text` to camelCase.
519
+ * When `isFile` is `true`, dot-separated segments are each cased independently and joined with `/`.
520
+ *
521
+ * @example
522
+ * camelCase('hello-world') // 'helloWorld'
523
+ * camelCase('pet.petId', { isFile: true }) // 'pet/petId'
524
+ */
525
+ function camelCase(text, { isFile, prefix = "", suffix = "" } = {}) {
526
+ if (isFile) return applyToFileParts(text, (part, isLast) => camelCase(part, isLast ? {
527
+ prefix,
528
+ suffix
529
+ } : {}));
530
+ return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
531
+ }
532
+ /** Returns a `CLIAdapter` with type inference. Pass a different adapter to `createCLI` to swap the CLI engine. */
533
+ function defineCLIAdapter(adapter) {
534
+ return adapter;
535
+ }
536
+ /**
537
+ * Serializes `CommandDefinition[]` to a plain, JSON-serializable structure.
538
+ * Use to expose CLI capabilities to AI agents or MCP tools.
539
+ */
540
+ function getCommandSchema(defs) {
541
+ return defs.map(serializeCommand);
542
+ }
543
+ function serializeCommand(def) {
544
+ return {
545
+ name: def.name,
546
+ description: def.description,
547
+ arguments: def.arguments,
548
+ options: serializeOptions(def.options ?? {}),
549
+ subCommands: def.subCommands ? def.subCommands.map(serializeCommand) : []
550
+ };
551
+ }
552
+ function serializeOptions(options) {
553
+ return Object.entries(options).map(([name, opt]) => {
554
+ return {
555
+ name,
556
+ flags: `${opt.short ? `-${opt.short}, ` : ""}--${name}${opt.type === "string" ? ` <${opt.hint ?? name}>` : ""}`,
557
+ type: opt.type,
558
+ description: opt.description,
559
+ ...opt.default !== void 0 ? { default: opt.default } : {},
560
+ ...opt.hint ? { hint: opt.hint } : {},
561
+ ...opt.enum ? { enum: opt.enum } : {},
562
+ ...opt.required ? { required: opt.required } : {}
563
+ };
564
+ });
565
+ }
566
+ /** Prints formatted help output for a command using its `CommandDefinition`. */
567
+ function renderHelp(def, parentName) {
568
+ const schema = getCommandSchema([def])[0];
569
+ const programName = parentName ? `${parentName} ${schema.name}` : schema.name;
570
+ const argsPart = schema.arguments?.length ? ` ${schema.arguments.join(" ")}` : "";
571
+ const subCmdPart = schema.subCommands.length ? " <command>" : "";
572
+ console.log(`\n${styleText("bold", "Usage:")} ${programName}${argsPart}${subCmdPart} [options]\n`);
573
+ if (schema.description) console.log(` ${schema.description}\n`);
574
+ if (schema.subCommands.length) {
575
+ console.log(styleText("bold", "Commands:"));
576
+ for (const sub of schema.subCommands) console.log(` ${styleText("cyan", sub.name.padEnd(16))}${sub.description}`);
577
+ console.log();
578
+ }
579
+ const options = [...schema.options, {
580
+ name: "help",
581
+ flags: "-h, --help",
582
+ type: "boolean",
583
+ description: "Show help"
584
+ }];
585
+ console.log(styleText("bold", "Options:"));
586
+ for (const opt of options) {
587
+ const flags = styleText("cyan", opt.flags.padEnd(30));
588
+ const defaultPart = opt.default !== void 0 ? styleText("dim", ` (default: ${opt.default})`) : "";
589
+ console.log(` ${flags}${opt.description}${defaultPart}`);
590
+ }
591
+ console.log();
592
+ }
593
+ function buildParseOptions(def) {
594
+ const result = { help: {
595
+ type: "boolean",
596
+ short: "h"
597
+ } };
598
+ for (const [name, opt] of Object.entries(def.options ?? {})) result[name] = {
599
+ type: opt.type,
600
+ ...opt.short ? { short: opt.short } : {},
601
+ ...opt.default !== void 0 ? { default: opt.default } : {}
602
+ };
603
+ return result;
604
+ }
605
+ async function runCommand(def, argv, parentName) {
606
+ const parseOptions = buildParseOptions(def);
607
+ let parsed;
608
+ try {
609
+ const result = parseArgs({
610
+ args: argv,
611
+ options: parseOptions,
612
+ allowPositionals: true,
613
+ strict: false
614
+ });
615
+ parsed = {
616
+ values: result.values,
617
+ positionals: result.positionals
618
+ };
619
+ } catch {
620
+ renderHelp(def, parentName);
621
+ process.exit(1);
622
+ }
623
+ if (parsed.values["help"]) {
624
+ renderHelp(def, parentName);
625
+ process.exit(0);
626
+ }
627
+ for (const [name, opt] of Object.entries(def.options ?? {})) if (opt.required && parsed.values[name] === void 0) {
628
+ console.error(styleText("red", `Error: --${name} is required`));
629
+ renderHelp(def, parentName);
630
+ process.exit(1);
631
+ }
632
+ if (!def.run) {
633
+ renderHelp(def, parentName);
634
+ process.exit(0);
635
+ }
636
+ try {
637
+ await def.run(parsed);
638
+ } catch (err) {
639
+ console.error(styleText("red", `Error: ${err instanceof Error ? err.message : String(err)}`));
640
+ renderHelp(def, parentName);
641
+ process.exit(1);
642
+ }
643
+ }
644
+ function printRootHelp(programName, version, defs) {
645
+ console.log(`\n${styleText("bold", "Usage:")} ${programName} <command> [options]\n`);
646
+ console.log(` Kubb generation — v${version}\n`);
647
+ console.log(styleText("bold", "Commands:"));
648
+ for (const def of defs) console.log(` ${styleText("cyan", def.name.padEnd(16))}${def.description}`);
649
+ console.log();
650
+ console.log(styleText("bold", "Options:"));
651
+ console.log(` ${styleText("cyan", "-v, --version".padEnd(30))}Show version number`);
652
+ console.log(` ${styleText("cyan", "-h, --help".padEnd(30))}Show help`);
653
+ console.log();
654
+ console.log(`Run ${styleText("cyan", `${programName} <command> --help`)} for command-specific help.\n`);
655
+ }
656
+ defineCLIAdapter({
657
+ renderHelp(def, parentName) {
658
+ renderHelp(def, parentName);
659
+ },
660
+ async run(defs, argv, opts) {
661
+ const { programName, defaultCommandName, version } = opts;
662
+ const args = argv.length >= 2 && argv[0]?.includes("node") ? argv.slice(2) : argv;
663
+ if (args[0] === "--version" || args[0] === "-v") {
664
+ console.log(version);
665
+ process.exit(0);
666
+ }
667
+ if (args[0] === "--help" || args[0] === "-h") {
668
+ printRootHelp(programName, version, defs);
669
+ process.exit(0);
670
+ }
671
+ if (args.length === 0) {
672
+ const defaultDef = defs.find((d) => d.name === defaultCommandName);
673
+ if (defaultDef?.run) await runCommand(defaultDef, [], programName);
674
+ else printRootHelp(programName, version, defs);
675
+ return;
676
+ }
677
+ const [first, ...rest] = args;
678
+ const isKnownSubcommand = defs.some((d) => d.name === first);
679
+ let def;
680
+ let commandArgv;
681
+ let parentName;
682
+ if (isKnownSubcommand) {
683
+ def = defs.find((d) => d.name === first);
684
+ commandArgv = rest;
685
+ parentName = programName;
686
+ } else {
687
+ def = defs.find((d) => d.name === defaultCommandName);
688
+ commandArgv = args;
689
+ parentName = programName;
690
+ }
691
+ if (!def) {
692
+ console.error(`Unknown command: ${first}`);
693
+ printRootHelp(programName, version, defs);
694
+ process.exit(1);
695
+ }
696
+ if (def.subCommands?.length) {
697
+ const [subName, ...subRest] = commandArgv;
698
+ const subDef = def.subCommands.find((s) => s.name === subName);
699
+ if (subName === "--help" || subName === "-h") {
700
+ renderHelp(def, parentName);
701
+ process.exit(0);
702
+ }
703
+ if (!subDef) {
704
+ renderHelp(def, parentName);
705
+ process.exit(subName ? 1 : 0);
706
+ }
707
+ await runCommand(subDef, subRest, `${parentName} ${def.name}`);
708
+ return;
709
+ }
710
+ await runCommand(def, commandArgv, parentName);
711
+ }
712
+ });
713
+ /**
714
+ * Parses a CSS hex color string (`#RGB`) into its RGB channels.
715
+ * Falls back to `255` for any channel that cannot be parsed.
716
+ */
717
+ function parseHex(color) {
718
+ const int = Number.parseInt(color.replace("#", ""), 16);
719
+ return Number.isNaN(int) ? {
720
+ r: 255,
721
+ g: 255,
722
+ b: 255
723
+ } : {
724
+ r: int >> 16 & 255,
725
+ g: int >> 8 & 255,
726
+ b: int & 255
727
+ };
728
+ }
729
+ /**
730
+ * Returns a function that wraps a string in a 24-bit ANSI true-color escape sequence
731
+ * for the given hex color.
732
+ */
733
+ function hex(color) {
734
+ const { r, g, b } = parseHex(color);
735
+ return (text) => `\x1b[38;2;${r};${g};${b}m${text}\x1b[0m`;
736
+ }
737
+ hex("#F55A17"), hex("#F5A217"), hex("#F58517"), hex("#B45309"), hex("#FFFFFF"), hex("#adadc6"), hex("#FDA4AF");
738
+ /**
739
+ * Returns `true` when `name` is a syntactically valid JavaScript variable name.
740
+ */
741
+ function isValidVarName(name) {
742
+ try {
743
+ new Function(`var ${name}`);
744
+ } catch {
745
+ return false;
746
+ }
747
+ return true;
748
+ }
749
+ //#endregion
750
+ //#region src/utils.ts
751
+ const plainStringTypes = new Set([
752
+ "string",
753
+ "uuid",
754
+ "email",
755
+ "url",
756
+ "datetime"
757
+ ]);
758
+ /**
759
+ * Returns `true` when a schema node will be represented as a plain string in generated code.
760
+ *
761
+ * - `string`, `uuid`, `email`, `url`, `datetime` are always plain strings.
762
+ * - `date` and `time` are plain strings when their `representation` is `'string'` rather than `'date'`.
763
+ */
764
+ function isPlainStringType(node) {
765
+ if (plainStringTypes.has(node.type)) return true;
766
+ const temporal = narrowSchema(node, "date") ?? narrowSchema(node, "time");
767
+ if (temporal) return temporal.representation !== "date";
768
+ return false;
769
+ }
770
+ /**
771
+ * Transforms the `name` field of each parameter node according to the given casing strategy.
772
+ *
773
+ * The original `params` array is never mutated — a new array of cloned nodes is returned.
774
+ * When no `casing` is provided the original array is returned as-is.
775
+ *
776
+ * Use this before passing parameters to schema builders so that property keys
777
+ * in the generated output match the desired casing while the original
778
+ * `OperationNode.parameters` array remains untouched for other consumers.
779
+ */
780
+ function applyParamsCasing(params, casing) {
781
+ if (!casing) return params;
782
+ return params.map((param) => {
783
+ const transformed = casing === "camelcase" || !isValidVarName(param.name) ? camelCase(param.name) : param.name;
784
+ return {
785
+ ...param,
786
+ name: transformed
787
+ };
788
+ });
789
+ }
790
+ //#endregion
248
791
  //#region src/visitor.ts
792
+ /**
793
+ * Creates a concurrency-limiting wrapper. At most `concurrency` promises may be
794
+ * in-flight simultaneously; additional calls are queued and dispatched as slots free.
795
+ */
249
796
  function createLimit(concurrency) {
250
797
  let active = 0;
251
798
  const queue = [];
@@ -268,7 +815,10 @@ function createLimit(concurrency) {
268
815
  };
269
816
  }
270
817
  /**
271
- * Traversable children of `node`, respecting `recurse` for schema nodes.
818
+ * Returns the immediate traversable children of `node`.
819
+ *
820
+ * For `Schema` nodes, children (properties, items, members) are only included
821
+ * when `recurse` is `true`; shallow traversal omits them entirely.
272
822
  */
273
823
  function getChildren(node, recurse) {
274
824
  switch (node.kind) {
@@ -289,6 +839,9 @@ function getChildren(node, recurse) {
289
839
  case "Property": return [node.schema];
290
840
  case "Parameter": return [node.schema];
291
841
  case "Response": return node.schema ? [node.schema] : [];
842
+ case "FunctionParameter":
843
+ case "ObjectBindingParameter":
844
+ case "FunctionParameters": return [];
292
845
  }
293
846
  }
294
847
  /**
@@ -298,6 +851,9 @@ function getChildren(node, recurse) {
298
851
  async function walk(node, visitor, options = {}) {
299
852
  return _walk(node, visitor, (options.depth ?? visitorDepths.deep) === visitorDepths.deep, createLimit(options.concurrency ?? 30));
300
853
  }
854
+ /**
855
+ * Internal recursive walk implementation — calls visitor then recurses into children.
856
+ */
301
857
  async function _walk(node, visitor, recurse, limit) {
302
858
  switch (node.kind) {
303
859
  case "Root":
@@ -318,6 +874,9 @@ async function _walk(node, visitor, recurse, limit) {
318
874
  case "Response":
319
875
  await limit(() => visitor.response?.(node));
320
876
  break;
877
+ case "FunctionParameter":
878
+ case "ObjectBindingParameter":
879
+ case "FunctionParameters": break;
321
880
  }
322
881
  const children = getChildren(node, recurse);
323
882
  await Promise.all(children.map((child) => _walk(child, visitor, recurse, limit)));
@@ -381,9 +940,12 @@ function transform(node, visitor, options = {}) {
381
940
  if (replaced) response = replaced;
382
941
  return {
383
942
  ...response,
384
- schema: response.schema ? transform(response.schema, visitor, options) : void 0
943
+ schema: transform(response.schema, visitor, options)
385
944
  };
386
945
  }
946
+ case "FunctionParameter":
947
+ case "ObjectBindingParameter":
948
+ case "FunctionParameters": return node;
387
949
  }
388
950
  }
389
951
  /**
@@ -412,12 +974,15 @@ function collect(node, visitor, options = {}) {
412
974
  case "Response":
413
975
  v = visitor.response?.(node);
414
976
  break;
977
+ case "FunctionParameter":
978
+ case "ObjectBindingParameter":
979
+ case "FunctionParameters": break;
415
980
  }
416
981
  if (v !== void 0) results.push(v);
417
982
  for (const child of getChildren(node, recurse)) for (const item of collect(child, visitor, options)) results.push(item);
418
983
  return results;
419
984
  }
420
985
  //#endregion
421
- export { buildRefMap, collect, createOperation, createParameter, createProperty, createResponse, createRoot, createSchema, definePrinter, httpMethods, isOperationNode, isParameterNode, isPropertyNode, isResponseNode, isRootNode, isSchemaNode, mediaTypes, narrowSchema, nodeKinds, refMapToObject, resolveRef, schemaTypes, transform, walk };
986
+ export { applyParamsCasing, buildRefMap, collect, createFunctionParameter, createFunctionParameters, createObjectBindingParameter, createOperation, createParameter, createProperty, createResponse, createRoot, createSchema, definePrinter, functionPrinter, httpMethods, isFunctionParameterNode, isFunctionParametersNode, isObjectBindingParameterNode, isOperationNode, isParameterNode, isPlainStringType, isPropertyNode, isResponseNode, isRootNode, isSchemaNode, mediaTypes, narrowSchema, nodeKinds, refMapToObject, resolveRef, schemaTypes, transform, walk };
422
987
 
423
988
  //# sourceMappingURL=index.js.map