@kubb/core 5.0.0-alpha.17 → 5.0.0-alpha.18

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,10 +1,9 @@
1
1
  import "./chunk--u3MIqq1.js";
2
- import { definePrinter, isOperationNode, isSchemaNode } from "@kubb/ast";
3
- import path, { basename, dirname, extname, join, posix, relative, resolve } from "node:path";
4
2
  import { EventEmitter } from "node:events";
5
- import { parseArgs, styleText } from "node:util";
6
3
  import { readFileSync } from "node:fs";
7
4
  import { access, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
5
+ import path, { basename, dirname, extname, join, posix, relative, resolve } from "node:path";
6
+ import { definePrinter, isOperationNode, isSchemaNode } from "@kubb/ast";
8
7
  import { Fabric, createFabric, createReactFabric } from "@kubb/react-fabric";
9
8
  import { typescriptParser } from "@kubb/react-fabric/parsers";
10
9
  import { fsPlugin } from "@kubb/react-fabric/plugins";
@@ -16,12 +15,23 @@ import { jsx } from "@kubb/react-fabric/jsx-runtime";
16
15
  import { sortBy } from "remeda";
17
16
  import * as pkg from "empathic/package";
18
17
  import { coerce, satisfies } from "semver";
19
- //#region ../../internals/utils/dist/index.js
20
- /** Thrown when a plugin's configuration or input fails validation. */
18
+ //#region ../../internals/utils/src/errors.ts
19
+ /** Thrown when a plugin's configuration or input fails validation.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * throw new ValidationPluginError('Invalid config: "output.path" is required')
24
+ * ```
25
+ */
21
26
  var ValidationPluginError = class extends Error {};
22
27
  /**
23
28
  * Thrown when one or more errors occur during a Kubb build.
24
29
  * Carries the full list of underlying errors on `errors`.
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * throw new BuildError('Build failed', { errors: [err1, err2] })
34
+ * ```
25
35
  */
26
36
  var BuildError = class extends Error {
27
37
  errors;
@@ -33,19 +43,34 @@ var BuildError = class extends Error {
33
43
  };
34
44
  /**
35
45
  * Coerces an unknown thrown value to an `Error` instance.
36
- * When the value is already an `Error` it is returned as-is;
37
- * otherwise a new `Error` is created whose message is `String(value)`.
46
+ * Returns the value as-is when it is already an `Error`; otherwise wraps it with `String(value)`.
47
+ *
48
+ * @example
49
+ * ```ts
50
+ * try { ... } catch(err) {
51
+ * throw new BuildError('Build failed', { cause: toError(err), errors: [] })
52
+ * }
53
+ * ```
38
54
  */
39
55
  function toError(value) {
40
56
  return value instanceof Error ? value : new Error(String(value));
41
57
  }
58
+ //#endregion
59
+ //#region ../../internals/utils/src/asyncEventEmitter.ts
42
60
  /**
43
- * A typed EventEmitter that awaits all async listeners before resolving.
61
+ * Typed `EventEmitter` that awaits all async listeners before resolving.
44
62
  * Wraps Node's `EventEmitter` with full TypeScript event-map inference.
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * const emitter = new AsyncEventEmitter<{ build: [name: string] }>()
67
+ * emitter.on('build', async (name) => { console.log(name) })
68
+ * await emitter.emit('build', 'petstore') // all listeners awaited
69
+ * ```
45
70
  */
46
71
  var AsyncEventEmitter = class {
47
72
  /**
48
- * `maxListener` controls the maximum number of listeners per event before Node emits a memory-leak warning.
73
+ * Maximum number of listeners per event before Node emits a memory-leak warning.
49
74
  * @default 10
50
75
  */
51
76
  constructor(maxListener = 10) {
@@ -53,8 +78,13 @@ var AsyncEventEmitter = class {
53
78
  }
54
79
  #emitter = new EventEmitter();
55
80
  /**
56
- * Emits an event and awaits all registered listeners in parallel.
81
+ * Emits `eventName` and awaits all registered listeners in parallel.
57
82
  * Throws if any listener rejects, wrapping the cause with the event name and serialized arguments.
83
+ *
84
+ * @example
85
+ * ```ts
86
+ * await emitter.emit('build', 'petstore')
87
+ * ```
58
88
  */
59
89
  async emit(eventName, ...eventArgs) {
60
90
  const listeners = this.#emitter.listeners(eventName);
@@ -73,11 +103,25 @@ var AsyncEventEmitter = class {
73
103
  }
74
104
  }));
75
105
  }
76
- /** Registers a persistent listener for the given event. */
106
+ /**
107
+ * Registers a persistent listener for `eventName`.
108
+ *
109
+ * @example
110
+ * ```ts
111
+ * emitter.on('build', async (name) => { console.log(name) })
112
+ * ```
113
+ */
77
114
  on(eventName, handler) {
78
115
  this.#emitter.on(eventName, handler);
79
116
  }
80
- /** Registers a one-shot listener that removes itself after the first invocation. */
117
+ /**
118
+ * Registers a one-shot listener that removes itself after the first invocation.
119
+ *
120
+ * @example
121
+ * ```ts
122
+ * emitter.onOnce('build', async (name) => { console.log(name) })
123
+ * ```
124
+ */
81
125
  onOnce(eventName, handler) {
82
126
  const wrapper = (...args) => {
83
127
  this.off(eventName, wrapper);
@@ -85,15 +129,31 @@ var AsyncEventEmitter = class {
85
129
  };
86
130
  this.on(eventName, wrapper);
87
131
  }
88
- /** Removes a previously registered listener. */
132
+ /**
133
+ * Removes a previously registered listener.
134
+ *
135
+ * @example
136
+ * ```ts
137
+ * emitter.off('build', handler)
138
+ * ```
139
+ */
89
140
  off(eventName, handler) {
90
141
  this.#emitter.off(eventName, handler);
91
142
  }
92
- /** Removes all listeners from every event channel. */
143
+ /**
144
+ * Removes all listeners from every event channel.
145
+ *
146
+ * @example
147
+ * ```ts
148
+ * emitter.removeAll()
149
+ * ```
150
+ */
93
151
  removeAll() {
94
152
  this.#emitter.removeAllListeners();
95
153
  }
96
154
  };
155
+ //#endregion
156
+ //#region ../../internals/utils/src/casing.ts
97
157
  /**
98
158
  * Shared implementation for camelCase and PascalCase conversion.
99
159
  * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
@@ -150,190 +210,18 @@ function pascalCase(text, { isFile, prefix = "", suffix = "" } = {}) {
150
210
  }) : camelCase(part));
151
211
  return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
152
212
  }
153
- /** Returns a `CLIAdapter` with type inference. Pass a different adapter to `createCLI` to swap the CLI engine. */
154
- function defineCLIAdapter(adapter) {
155
- return adapter;
156
- }
157
- /**
158
- * Serializes `CommandDefinition[]` to a plain, JSON-serializable structure.
159
- * Use to expose CLI capabilities to AI agents or MCP tools.
160
- */
161
- function getCommandSchema(defs) {
162
- return defs.map(serializeCommand);
163
- }
164
- function serializeCommand(def) {
165
- return {
166
- name: def.name,
167
- description: def.description,
168
- arguments: def.arguments,
169
- options: serializeOptions(def.options ?? {}),
170
- subCommands: def.subCommands ? def.subCommands.map(serializeCommand) : []
171
- };
172
- }
173
- function serializeOptions(options) {
174
- return Object.entries(options).map(([name, opt]) => {
175
- return {
176
- name,
177
- flags: `${opt.short ? `-${opt.short}, ` : ""}--${name}${opt.type === "string" ? ` <${opt.hint ?? name}>` : ""}`,
178
- type: opt.type,
179
- description: opt.description,
180
- ...opt.default !== void 0 ? { default: opt.default } : {},
181
- ...opt.hint ? { hint: opt.hint } : {},
182
- ...opt.enum ? { enum: opt.enum } : {},
183
- ...opt.required ? { required: opt.required } : {}
184
- };
185
- });
186
- }
187
- /** Prints formatted help output for a command using its `CommandDefinition`. */
188
- function renderHelp(def, parentName) {
189
- const schema = getCommandSchema([def])[0];
190
- const programName = parentName ? `${parentName} ${schema.name}` : schema.name;
191
- const argsPart = schema.arguments?.length ? ` ${schema.arguments.join(" ")}` : "";
192
- const subCmdPart = schema.subCommands.length ? " <command>" : "";
193
- console.log(`\n${styleText("bold", "Usage:")} ${programName}${argsPart}${subCmdPart} [options]\n`);
194
- if (schema.description) console.log(` ${schema.description}\n`);
195
- if (schema.subCommands.length) {
196
- console.log(styleText("bold", "Commands:"));
197
- for (const sub of schema.subCommands) console.log(` ${styleText("cyan", sub.name.padEnd(16))}${sub.description}`);
198
- console.log();
199
- }
200
- const options = [...schema.options, {
201
- name: "help",
202
- flags: "-h, --help",
203
- type: "boolean",
204
- description: "Show help"
205
- }];
206
- console.log(styleText("bold", "Options:"));
207
- for (const opt of options) {
208
- const flags = styleText("cyan", opt.flags.padEnd(30));
209
- const defaultPart = opt.default !== void 0 ? styleText("dim", ` (default: ${opt.default})`) : "";
210
- console.log(` ${flags}${opt.description}${defaultPart}`);
211
- }
212
- console.log();
213
- }
214
- function buildParseOptions(def) {
215
- const result = { help: {
216
- type: "boolean",
217
- short: "h"
218
- } };
219
- for (const [name, opt] of Object.entries(def.options ?? {})) result[name] = {
220
- type: opt.type,
221
- ...opt.short ? { short: opt.short } : {},
222
- ...opt.default !== void 0 ? { default: opt.default } : {}
223
- };
224
- return result;
225
- }
226
- async function runCommand(def, argv, parentName) {
227
- const parseOptions = buildParseOptions(def);
228
- let parsed;
229
- try {
230
- const result = parseArgs({
231
- args: argv,
232
- options: parseOptions,
233
- allowPositionals: true,
234
- strict: false
235
- });
236
- parsed = {
237
- values: result.values,
238
- positionals: result.positionals
239
- };
240
- } catch {
241
- renderHelp(def, parentName);
242
- process.exit(1);
243
- }
244
- if (parsed.values["help"]) {
245
- renderHelp(def, parentName);
246
- process.exit(0);
247
- }
248
- for (const [name, opt] of Object.entries(def.options ?? {})) if (opt.required && parsed.values[name] === void 0) {
249
- console.error(styleText("red", `Error: --${name} is required`));
250
- renderHelp(def, parentName);
251
- process.exit(1);
252
- }
253
- if (!def.run) {
254
- renderHelp(def, parentName);
255
- process.exit(0);
256
- }
257
- try {
258
- await def.run(parsed);
259
- } catch (err) {
260
- console.error(styleText("red", `Error: ${err instanceof Error ? err.message : String(err)}`));
261
- renderHelp(def, parentName);
262
- process.exit(1);
263
- }
264
- }
265
- function printRootHelp(programName, version, defs) {
266
- console.log(`\n${styleText("bold", "Usage:")} ${programName} <command> [options]\n`);
267
- console.log(` Kubb generation — v${version}\n`);
268
- console.log(styleText("bold", "Commands:"));
269
- for (const def of defs) console.log(` ${styleText("cyan", def.name.padEnd(16))}${def.description}`);
270
- console.log();
271
- console.log(styleText("bold", "Options:"));
272
- console.log(` ${styleText("cyan", "-v, --version".padEnd(30))}Show version number`);
273
- console.log(` ${styleText("cyan", "-h, --help".padEnd(30))}Show help`);
274
- console.log();
275
- console.log(`Run ${styleText("cyan", `${programName} <command> --help`)} for command-specific help.\n`);
276
- }
277
- defineCLIAdapter({
278
- renderHelp(def, parentName) {
279
- renderHelp(def, parentName);
280
- },
281
- async run(defs, argv, opts) {
282
- const { programName, defaultCommandName, version } = opts;
283
- const args = argv.length >= 2 && argv[0]?.includes("node") ? argv.slice(2) : argv;
284
- if (args[0] === "--version" || args[0] === "-v") {
285
- console.log(version);
286
- process.exit(0);
287
- }
288
- if (args[0] === "--help" || args[0] === "-h") {
289
- printRootHelp(programName, version, defs);
290
- process.exit(0);
291
- }
292
- if (args.length === 0) {
293
- const defaultDef = defs.find((d) => d.name === defaultCommandName);
294
- if (defaultDef?.run) await runCommand(defaultDef, [], programName);
295
- else printRootHelp(programName, version, defs);
296
- return;
297
- }
298
- const [first, ...rest] = args;
299
- const isKnownSubcommand = defs.some((d) => d.name === first);
300
- let def;
301
- let commandArgv;
302
- let parentName;
303
- if (isKnownSubcommand) {
304
- def = defs.find((d) => d.name === first);
305
- commandArgv = rest;
306
- parentName = programName;
307
- } else {
308
- def = defs.find((d) => d.name === defaultCommandName);
309
- commandArgv = args;
310
- parentName = programName;
311
- }
312
- if (!def) {
313
- console.error(`Unknown command: ${first}`);
314
- printRootHelp(programName, version, defs);
315
- process.exit(1);
316
- }
317
- if (def.subCommands?.length) {
318
- const [subName, ...subRest] = commandArgv;
319
- const subDef = def.subCommands.find((s) => s.name === subName);
320
- if (subName === "--help" || subName === "-h") {
321
- renderHelp(def, parentName);
322
- process.exit(0);
323
- }
324
- if (!subDef) {
325
- renderHelp(def, parentName);
326
- process.exit(subName ? 1 : 0);
327
- }
328
- await runCommand(subDef, subRest, `${parentName} ${def.name}`);
329
- return;
330
- }
331
- await runCommand(def, commandArgv, parentName);
332
- }
333
- });
213
+ //#endregion
214
+ //#region ../../internals/utils/src/time.ts
334
215
  /**
335
- * Calculates elapsed time in milliseconds from a high-resolution start time.
336
- * Rounds to 2 decimal places to provide sub-millisecond precision without noise.
216
+ * Calculates elapsed time in milliseconds from a high-resolution `process.hrtime` start time.
217
+ * Rounds to 2 decimal places for sub-millisecond precision without noise.
218
+ *
219
+ * @example
220
+ * ```ts
221
+ * const start = process.hrtime()
222
+ * doWork()
223
+ * getElapsedMs(start) // 42.35
224
+ * ```
337
225
  */
338
226
  function getElapsedMs(hrStart) {
339
227
  const [seconds, nanoseconds] = process.hrtime(hrStart);
@@ -341,39 +229,22 @@ function getElapsedMs(hrStart) {
341
229
  return Math.round(ms * 100) / 100;
342
230
  }
343
231
  /**
344
- * Converts a millisecond duration into a human-readable string.
345
- * Adjusts units (ms, s, m s) based on the magnitude of the duration.
232
+ * Converts a millisecond duration into a human-readable string (`ms`, `s`, or `m s`).
233
+ *
234
+ * @example
235
+ * ```ts
236
+ * formatMs(250) // '250ms'
237
+ * formatMs(1500) // '1.50s'
238
+ * formatMs(90000) // '1m 30.0s'
239
+ * ```
346
240
  */
347
241
  function formatMs(ms) {
348
242
  if (ms >= 6e4) return `${Math.floor(ms / 6e4)}m ${(ms % 6e4 / 1e3).toFixed(1)}s`;
349
243
  if (ms >= 1e3) return `${(ms / 1e3).toFixed(2)}s`;
350
244
  return `${Math.round(ms)}ms`;
351
245
  }
352
- /**
353
- * Parses a CSS hex color string (`#RGB`) into its RGB channels.
354
- * Falls back to `255` for any channel that cannot be parsed.
355
- */
356
- function parseHex(color) {
357
- const int = Number.parseInt(color.replace("#", ""), 16);
358
- return Number.isNaN(int) ? {
359
- r: 255,
360
- g: 255,
361
- b: 255
362
- } : {
363
- r: int >> 16 & 255,
364
- g: int >> 8 & 255,
365
- b: int & 255
366
- };
367
- }
368
- /**
369
- * Returns a function that wraps a string in a 24-bit ANSI true-color escape sequence
370
- * for the given hex color.
371
- */
372
- function hex(color) {
373
- const { r, g, b } = parseHex(color);
374
- return (text) => `\x1b[38;2;${r};${g};${b}m${text}\x1b[0m`;
375
- }
376
- hex("#F55A17"), hex("#F5A217"), hex("#F58517"), hex("#B45309"), hex("#FFFFFF"), hex("#adadc6"), hex("#FDA4AF");
246
+ //#endregion
247
+ //#region ../../internals/utils/src/fs.ts
377
248
  /**
378
249
  * Converts all backslashes to forward slashes.
379
250
  * Extended-length Windows paths (`\\?\...`) are left unchanged.
@@ -383,8 +254,14 @@ function toSlash(p) {
383
254
  return p.replaceAll("\\", "/");
384
255
  }
385
256
  /**
386
- * Returns the relative path from `rootDir` to `filePath`, always using
387
- * forward slashes and prefixed with `./` when not already traversing upward.
257
+ * Returns the relative path from `rootDir` to `filePath`, always using forward slashes
258
+ * and prefixed with `./` when not already traversing upward.
259
+ *
260
+ * @example
261
+ * ```ts
262
+ * getRelativePath('/src/components', '/src/components/Button.tsx') // './Button.tsx'
263
+ * getRelativePath('/src/components', '/src/utils/helpers.ts') // '../utils/helpers.ts'
264
+ * ```
388
265
  */
389
266
  function getRelativePath(rootDir, filePath) {
390
267
  if (!rootDir || !filePath) throw new Error(`Root and file should be filled in when retrieving the relativePath, ${rootDir || ""} ${filePath || ""}`);
@@ -394,35 +271,54 @@ function getRelativePath(rootDir, filePath) {
394
271
  /**
395
272
  * Resolves to `true` when the file or directory at `path` exists.
396
273
  * Uses `Bun.file().exists()` when running under Bun, `fs.access` otherwise.
274
+ *
275
+ * @example
276
+ * ```ts
277
+ * if (await exists('./kubb.config.ts')) {
278
+ * const content = await read('./kubb.config.ts')
279
+ * }
280
+ * ```
397
281
  */
398
282
  async function exists(path) {
399
283
  if (typeof Bun !== "undefined") return Bun.file(path).exists();
400
284
  return access(path).then(() => true, () => false);
401
285
  }
402
- /** Synchronous counterpart of `read`. */
286
+ /**
287
+ * Synchronous counterpart of `read`.
288
+ *
289
+ * @example
290
+ * ```ts
291
+ * const source = readSync('./src/Pet.ts')
292
+ * ```
293
+ */
403
294
  function readSync(path) {
404
295
  return readFileSync(path, { encoding: "utf8" });
405
296
  }
406
297
  /**
407
298
  * Writes `data` to `path`, trimming leading/trailing whitespace before saving.
408
- * Skips the write and returns `undefined` when the trimmed content is empty or
409
- * identical to what is already on disk.
299
+ * Skips the write when the trimmed content is empty or identical to what is already on disk.
410
300
  * Creates any missing parent directories automatically.
411
- * When `sanity` is `true`, re-reads the file after writing and throws if the
412
- * content does not match.
301
+ * When `sanity` is `true`, re-reads the file after writing and throws if the content does not match.
302
+ *
303
+ * @example
304
+ * ```ts
305
+ * await write('./src/Pet.ts', source) // writes and returns trimmed content
306
+ * await write('./src/Pet.ts', source) // null — file unchanged
307
+ * await write('./src/Pet.ts', ' ') // null — empty content skipped
308
+ * ```
413
309
  */
414
310
  async function write(path, data, options = {}) {
415
311
  const trimmed = data.trim();
416
- if (trimmed === "") return void 0;
312
+ if (trimmed === "") return null;
417
313
  const resolved = resolve(path);
418
314
  if (typeof Bun !== "undefined") {
419
315
  const file = Bun.file(resolved);
420
- if ((await file.exists() ? await file.text() : null) === trimmed) return void 0;
316
+ if ((await file.exists() ? await file.text() : null) === trimmed) return null;
421
317
  await Bun.write(resolved, trimmed);
422
318
  return trimmed;
423
319
  }
424
320
  try {
425
- if (await readFile(resolved, { encoding: "utf-8" }) === trimmed) return void 0;
321
+ if (await readFile(resolved, { encoding: "utf-8" }) === trimmed) return null;
426
322
  } catch {}
427
323
  await mkdir(dirname(resolved), { recursive: true });
428
324
  await writeFile(resolved, trimmed, { encoding: "utf-8" });
@@ -433,16 +329,32 @@ async function write(path, data, options = {}) {
433
329
  }
434
330
  return trimmed;
435
331
  }
436
- /** Recursively removes `path`. Silently succeeds when `path` does not exist. */
332
+ /**
333
+ * Recursively removes `path`. Silently succeeds when `path` does not exist.
334
+ *
335
+ * @example
336
+ * ```ts
337
+ * await clean('./dist')
338
+ * ```
339
+ */
437
340
  async function clean(path) {
438
341
  return rm(path, {
439
342
  recursive: true,
440
343
  force: true
441
344
  });
442
345
  }
346
+ //#endregion
347
+ //#region ../../internals/utils/src/names.ts
443
348
  /**
444
349
  * Registers `originalName` in `data` without altering the returned name.
445
- * Use this when you need to track usage frequency but always emit the original identifier.
350
+ * Use when you need to track usage frequency but always emit the original identifier.
351
+ *
352
+ * @example
353
+ * ```ts
354
+ * const seen: Record<string, number> = {}
355
+ * setUniqueName('Foo', seen) // 'Foo' (seen = { Foo: 1 })
356
+ * setUniqueName('Foo', seen) // 'Foo' (seen = { Foo: 2 })
357
+ * ```
446
358
  */
447
359
  function setUniqueName(originalName, data) {
448
360
  let used = data[originalName] || 0;
@@ -453,10 +365,21 @@ function setUniqueName(originalName, data) {
453
365
  data[originalName] = 1;
454
366
  return originalName;
455
367
  }
456
- /** Type guard for a rejected `Promise.allSettled` result with a typed `reason`. */
368
+ //#endregion
369
+ //#region ../../internals/utils/src/promise.ts
370
+ /** Returns `true` when `result` is a rejected `Promise.allSettled` result with a typed `reason`.
371
+ *
372
+ * @example
373
+ * ```ts
374
+ * const results = await Promise.allSettled([p1, p2])
375
+ * results.filter(isPromiseRejectedResult<Error>).map((r) => r.reason.message)
376
+ * ```
377
+ */
457
378
  function isPromiseRejectedResult(result) {
458
379
  return result.status === "rejected";
459
380
  }
381
+ //#endregion
382
+ //#region ../../internals/utils/src/reserved.ts
460
383
  /**
461
384
  * JavaScript and Java reserved words.
462
385
  * @link https://github.com/jonschlinkert/reserved/blob/master/index.js
@@ -545,8 +468,14 @@ const reservedWords = new Set([
545
468
  "valueOf"
546
469
  ]);
547
470
  /**
548
- * Prefixes a word with `_` when it is a reserved JavaScript/Java identifier
549
- * or starts with a digit.
471
+ * Prefixes `word` with `_` when it is a reserved JavaScript/Java identifier or starts with a digit.
472
+ *
473
+ * @example
474
+ * ```ts
475
+ * transformReservedWord('class') // '_class'
476
+ * transformReservedWord('42foo') // '_42foo'
477
+ * transformReservedWord('status') // 'status'
478
+ * ```
550
479
  */
551
480
  function transformReservedWord(word) {
552
481
  const firstChar = word.charCodeAt(0);
@@ -555,6 +484,13 @@ function transformReservedWord(word) {
555
484
  }
556
485
  /**
557
486
  * Returns `true` when `name` is a syntactically valid JavaScript variable name.
487
+ *
488
+ * @example
489
+ * ```ts
490
+ * isValidVarName('status') // true
491
+ * isValidVarName('class') // false (reserved word)
492
+ * isValidVarName('42foo') // false (starts with digit)
493
+ * ```
558
494
  */
559
495
  function isValidVarName(name) {
560
496
  try {
@@ -564,6 +500,8 @@ function isValidVarName(name) {
564
500
  }
565
501
  return true;
566
502
  }
503
+ //#endregion
504
+ //#region ../../internals/utils/src/urlPath.ts
567
505
  /**
568
506
  * Parses and transforms an OpenAPI/Swagger path string into various URL formats.
569
507
  *
@@ -573,18 +511,33 @@ function isValidVarName(name) {
573
511
  * p.template // '`/pet/${petId}`'
574
512
  */
575
513
  var URLPath = class {
576
- /** The raw OpenAPI/Swagger path string, e.g. `/pet/{petId}`. */
514
+ /**
515
+ * The raw OpenAPI/Swagger path string, e.g. `/pet/{petId}`.
516
+ */
577
517
  path;
578
518
  #options;
579
519
  constructor(path, options = {}) {
580
520
  this.path = path;
581
521
  this.#options = options;
582
522
  }
583
- /** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`. */
523
+ /** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`.
524
+ *
525
+ * @example
526
+ * ```ts
527
+ * new URLPath('/pet/{petId}').URL // '/pet/:petId'
528
+ * ```
529
+ */
584
530
  get URL() {
585
531
  return this.toURLPath();
586
532
  }
587
- /** Returns `true` when `path` is a fully-qualified URL (e.g. starts with `https://`). */
533
+ /** Returns `true` when `path` is a fully-qualified URL (e.g. starts with `https://`).
534
+ *
535
+ * @example
536
+ * ```ts
537
+ * new URLPath('https://petstore.swagger.io/v2/pet').isURL // true
538
+ * new URLPath('/pet/{petId}').isURL // false
539
+ * ```
540
+ */
588
541
  get isURL() {
589
542
  try {
590
543
  return !!new URL(this.path).href;
@@ -602,11 +555,25 @@ var URLPath = class {
602
555
  get template() {
603
556
  return this.toTemplateString();
604
557
  }
605
- /** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set. */
558
+ /** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set.
559
+ *
560
+ * @example
561
+ * ```ts
562
+ * new URLPath('/pet/{petId}').object
563
+ * // { url: '/pet/:petId', params: { petId: 'petId' } }
564
+ * ```
565
+ */
606
566
  get object() {
607
567
  return this.toObject();
608
568
  }
609
- /** Returns a map of path parameter names, or `undefined` when the path has no parameters. */
569
+ /** Returns a map of path parameter names, or `undefined` when the path has no parameters.
570
+ *
571
+ * @example
572
+ * ```ts
573
+ * new URLPath('/pet/{petId}').params // { petId: 'petId' }
574
+ * new URLPath('/pet').params // undefined
575
+ * ```
576
+ */
610
577
  get params() {
611
578
  return this.getParams();
612
579
  }
@@ -614,7 +581,9 @@ var URLPath = class {
614
581
  const param = isValidVarName(raw) ? raw : camelCase(raw);
615
582
  return this.#options.casing === "camelcase" ? camelCase(param) : param;
616
583
  }
617
- /** Iterates over every `{param}` token in `path`, calling `fn` with the raw token and transformed name. */
584
+ /**
585
+ * Iterates over every `{param}` token in `path`, calling `fn` with the raw token and transformed name.
586
+ */
618
587
  #eachParam(fn) {
619
588
  for (const match of this.path.matchAll(/\{([^}]+)\}/g)) {
620
589
  const raw = match[1];
@@ -651,6 +620,12 @@ var URLPath = class {
651
620
  * Extracts all `{param}` segments from the path and returns them as a key-value map.
652
621
  * An optional `replacer` transforms each parameter name in both key and value positions.
653
622
  * Returns `undefined` when no path parameters are found.
623
+ *
624
+ * @example
625
+ * ```ts
626
+ * new URLPath('/pet/{petId}/tag/{tagId}').getParams()
627
+ * // { petId: 'petId', tagId: 'tagId' }
628
+ * ```
654
629
  */
655
630
  getParams(replacer) {
656
631
  const params = {};
@@ -660,7 +635,13 @@ var URLPath = class {
660
635
  });
661
636
  return Object.keys(params).length > 0 ? params : void 0;
662
637
  }
663
- /** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`. */
638
+ /** Converts the OpenAPI path to Express-style colon syntax.
639
+ *
640
+ * @example
641
+ * ```ts
642
+ * new URLPath('/pet/{petId}').toURLPath() // '/pet/:petId'
643
+ * ```
644
+ */
664
645
  toURLPath() {
665
646
  return this.path.replace(/\{([^}]+)\}/g, ":$1");
666
647
  }
@@ -678,10 +659,27 @@ function isInputPath(config) {
678
659
  }
679
660
  //#endregion
680
661
  //#region src/constants.ts
662
+ /**
663
+ * Base URL for the Kubb Studio web app.
664
+ */
681
665
  const DEFAULT_STUDIO_URL = "https://studio.kubb.dev";
666
+ /**
667
+ * File name used for generated barrel (index) files.
668
+ */
682
669
  const BARREL_FILENAME = "index.ts";
670
+ /**
671
+ * Default banner style written at the top of every generated file.
672
+ */
683
673
  const DEFAULT_BANNER = "simple";
674
+ /**
675
+ * Default file-extension mapping used when no explicit mapping is configured.
676
+ */
684
677
  const DEFAULT_EXTENSION = { ".ts": ".ts" };
678
+ /**
679
+ * Numeric log-level thresholds used internally to compare verbosity.
680
+ *
681
+ * Higher numbers are more verbose.
682
+ */
685
683
  const logLevel = {
686
684
  silent: Number.NEGATIVE_INFINITY,
687
685
  error: 0,
@@ -690,6 +688,13 @@ const logLevel = {
690
688
  verbose: 4,
691
689
  debug: 5
692
690
  };
691
+ /**
692
+ * CLI command descriptors for each supported linter.
693
+ *
694
+ * Each entry contains the executable `command`, an `args` factory that maps an
695
+ * output path to the correct argument list, and an `errorMessage` shown when
696
+ * the linter is not found.
697
+ */
693
698
  const linters = {
694
699
  eslint: {
695
700
  command: "eslint",
@@ -711,6 +716,13 @@ const linters = {
711
716
  errorMessage: "Oxlint not found"
712
717
  }
713
718
  };
719
+ /**
720
+ * CLI command descriptors for each supported code formatter.
721
+ *
722
+ * Each entry contains the executable `command`, an `args` factory that maps an
723
+ * output path to the correct argument list, and an `errorMessage` shown when
724
+ * the formatter is not found.
725
+ */
714
726
  const formatters = {
715
727
  prettier: {
716
728
  command: "prettier",
@@ -910,7 +922,11 @@ function validateConcurrency(concurrency) {
910
922
  //#endregion
911
923
  //#region src/utils/executeStrategies.ts
912
924
  /**
913
- * Chains promises
925
+ * Runs promise functions in sequence, threading each result into the next call.
926
+ *
927
+ * - Each function receives the accumulated state from the previous call.
928
+ * - Skips functions that return a falsy value (acts as a no-op for that step).
929
+ * - Returns an array of all individual results.
914
930
  */
915
931
  function hookSeq(promises) {
916
932
  return promises.filter(Boolean).reduce((promise, func) => {
@@ -923,7 +939,10 @@ function hookSeq(promises) {
923
939
  }, Promise.resolve([]));
924
940
  }
925
941
  /**
926
- * Chains promises, first non-null result stops and returns
942
+ * Runs promise functions in sequence and returns the first non-null result.
943
+ *
944
+ * - Stops as soon as `nullCheck` passes for a result (default: `!== null`).
945
+ * - Subsequent functions are skipped once a match is found.
927
946
  */
928
947
  function hookFirst(promises, nullCheck = (state) => state !== null) {
929
948
  let promise = Promise.resolve(null);
@@ -934,7 +953,10 @@ function hookFirst(promises, nullCheck = (state) => state !== null) {
934
953
  return promise;
935
954
  }
936
955
  /**
937
- * Runs an array of promise functions with optional concurrency limit.
956
+ * Runs promise functions concurrently and returns all settled results.
957
+ *
958
+ * - Limits simultaneous executions to `concurrency` (default: unlimited).
959
+ * - Uses `Promise.allSettled` so individual failures do not cancel other tasks.
938
960
  */
939
961
  function hookParallel(promises, concurrency = Number.POSITIVE_INFINITY) {
940
962
  const limit = pLimit(concurrency);
@@ -1441,11 +1463,14 @@ const fsStorage = createStorage(() => ({
1441
1463
  }));
1442
1464
  //#endregion
1443
1465
  //#region package.json
1444
- var version$1 = "5.0.0-alpha.17";
1466
+ var version$1 = "5.0.0-alpha.18";
1445
1467
  //#endregion
1446
1468
  //#region src/utils/diagnostics.ts
1447
1469
  /**
1448
- * Get diagnostic information for debugging
1470
+ * Returns a snapshot of the current runtime environment.
1471
+ *
1472
+ * Useful for attaching context to debug logs and error reports so that
1473
+ * issues can be reproduced without manual information gathering.
1449
1474
  */
1450
1475
  function getDiagnosticInfo() {
1451
1476
  return {
@@ -1458,6 +1483,17 @@ function getDiagnosticInfo() {
1458
1483
  }
1459
1484
  //#endregion
1460
1485
  //#region src/build.ts
1486
+ /**
1487
+ * Initializes all Kubb infrastructure for a build without executing any plugins.
1488
+ *
1489
+ * - Validates the input path (when applicable).
1490
+ * - Applies config defaults (`root`, `output.*`, `devtools`).
1491
+ * - Creates the Fabric instance and wires storage, format, and lint hooks.
1492
+ * - Runs the adapter (if configured) to produce the universal `RootNode`.
1493
+ *
1494
+ * Pass the returned {@link SetupResult} directly to {@link safeBuild} or {@link build}
1495
+ * via the `overrides` argument to reuse the same infrastructure across multiple runs.
1496
+ */
1461
1497
  async function setup(options) {
1462
1498
  const { config: userConfig, events = new AsyncEventEmitter() } = options;
1463
1499
  const sources = /* @__PURE__ */ new Map();
@@ -1584,6 +1620,12 @@ async function setup(options) {
1584
1620
  sources
1585
1621
  };
1586
1622
  }
1623
+ /**
1624
+ * Runs a full Kubb build and throws on any error or plugin failure.
1625
+ *
1626
+ * Internally delegates to {@link safeBuild} and rethrows collected errors.
1627
+ * Pass an existing {@link SetupResult} via `overrides` to skip the setup phase.
1628
+ */
1587
1629
  async function build(options, overrides) {
1588
1630
  const { fabric, files, driver, failedPlugins, pluginTimings, error, sources } = await safeBuild(options, overrides);
1589
1631
  if (error) throw error;
@@ -1601,6 +1643,16 @@ async function build(options, overrides) {
1601
1643
  sources
1602
1644
  };
1603
1645
  }
1646
+ /**
1647
+ * Runs a full Kubb build and captures errors instead of throwing.
1648
+ *
1649
+ * - Installs each plugin in order, recording failures in `failedPlugins`.
1650
+ * - Generates the root barrel file when `output.barrelType` is set.
1651
+ * - Writes all files through Fabric.
1652
+ *
1653
+ * Returns a {@link BuildOutput} even on failure — inspect `error` and
1654
+ * `failedPlugins` to determine whether the build succeeded.
1655
+ */
1604
1656
  async function safeBuild(options, overrides) {
1605
1657
  const { fabric, driver, events, sources } = overrides ? overrides : await setup(options);
1606
1658
  const failedPlugins = /* @__PURE__ */ new Set();
@@ -2164,14 +2216,10 @@ var FunctionParams = class FunctionParams {
2164
2216
  //#endregion
2165
2217
  //#region src/utils/formatters.ts
2166
2218
  /**
2167
- * Check if a formatter command is available in the system.
2219
+ * Returns `true` when the given formatter is installed and callable.
2168
2220
  *
2169
- * @param formatter - The formatter to check ('biome', 'prettier', or 'oxfmt')
2170
- * @returns Promise that resolves to true if the formatter is available, false otherwise
2171
- *
2172
- * @remarks
2173
- * This function checks availability by running `<formatter> --version` command.
2174
- * All supported formatters (biome, prettier, oxfmt) implement the --version flag.
2221
+ * Availability is detected by running `<formatter> --version` and checking
2222
+ * that the process exits without error.
2175
2223
  */
2176
2224
  async function isFormatterAvailable(formatter) {
2177
2225
  try {
@@ -2182,22 +2230,16 @@ async function isFormatterAvailable(formatter) {
2182
2230
  }
2183
2231
  }
2184
2232
  /**
2185
- * Detect which formatter is available in the system.
2186
- *
2187
- * @returns Promise that resolves to the first available formatter or undefined if none are found
2233
+ * Detects the first available code formatter on the current system.
2188
2234
  *
2189
- * @remarks
2190
- * Checks in order of preference: biome, oxfmt, prettier.
2191
- * Uses the `--version` flag to detect if each formatter command is available.
2192
- * This is a reliable method as all supported formatters implement this flag.
2235
+ * - Checks in preference order: `biome`, `oxfmt`, `prettier`.
2236
+ * - Returns `null` when none are found.
2193
2237
  *
2194
2238
  * @example
2195
- * ```typescript
2239
+ * ```ts
2196
2240
  * const formatter = await detectFormatter()
2197
2241
  * if (formatter) {
2198
2242
  * console.log(`Using ${formatter} for formatting`)
2199
- * } else {
2200
- * console.log('No formatter found')
2201
2243
  * }
2202
2244
  * ```
2203
2245
  */
@@ -2208,9 +2250,19 @@ async function detectFormatter() {
2208
2250
  "prettier"
2209
2251
  ]);
2210
2252
  for (const formatter of formatterNames) if (await isFormatterAvailable(formatter)) return formatter;
2253
+ return null;
2211
2254
  }
2212
2255
  //#endregion
2213
2256
  //#region src/utils/TreeNode.ts
2257
+ /**
2258
+ * Tree structure used to build per-directory barrel (`index.ts`) files from a
2259
+ * flat list of generated {@link KubbFile.File} entries.
2260
+ *
2261
+ * Each node represents either a directory or a file within the output tree.
2262
+ * Use {@link TreeNode.build} to construct a root node from a file list, then
2263
+ * traverse with {@link TreeNode.forEach}, {@link TreeNode.leaves}, or the
2264
+ * `*Deep` helpers.
2265
+ */
2214
2266
  var TreeNode = class TreeNode {
2215
2267
  data;
2216
2268
  parent;
@@ -2226,10 +2278,18 @@ var TreeNode = class TreeNode {
2226
2278
  this.children.push(child);
2227
2279
  return child;
2228
2280
  }
2281
+ /**
2282
+ * Returns the root ancestor of this node, walking up via `parent` links.
2283
+ */
2229
2284
  get root() {
2230
2285
  if (!this.parent) return this;
2231
2286
  return this.parent.root;
2232
2287
  }
2288
+ /**
2289
+ * Returns all leaf descendants (nodes with no children) of this node.
2290
+ *
2291
+ * Results are cached after the first traversal.
2292
+ */
2233
2293
  get leaves() {
2234
2294
  if (!this.children || this.children.length === 0) return [this];
2235
2295
  if (this.#cachedLeaves) return this.#cachedLeaves;
@@ -2260,6 +2320,12 @@ var TreeNode = class TreeNode {
2260
2320
  if (typeof callback !== "function") throw new TypeError("map() callback must be a function");
2261
2321
  return this.leaves.map(callback);
2262
2322
  }
2323
+ /**
2324
+ * Builds a {@link TreeNode} tree from a flat list of files.
2325
+ *
2326
+ * - Filters to files under `root` (when provided) and skips `.json` files.
2327
+ * - Returns `null` when no files match.
2328
+ */
2263
2329
  static build(files, root) {
2264
2330
  try {
2265
2331
  const filteredTree = buildDirectoryTree(files, root);
@@ -2375,6 +2441,14 @@ function trimExtName(text) {
2375
2441
  if (dotIndex > 0 && !text.includes("/", dotIndex)) return text.slice(0, dotIndex);
2376
2442
  return text;
2377
2443
  }
2444
+ /**
2445
+ * Generates `index.ts` barrel files for all directories under `root/output.path`.
2446
+ *
2447
+ * - Returns an empty array when `type` is falsy or `'propagate'`.
2448
+ * - Skips generation when the output path itself ends with `index` (already a barrel).
2449
+ * - When `type` is `'all'`, strips named exports so every re-export becomes a wildcard (`export * from`).
2450
+ * - Attaches `meta` to each barrel file for downstream plugin identification.
2451
+ */
2378
2452
  async function getBarrelFiles(files, { type, meta = {}, root, output }) {
2379
2453
  if (!type || type === "propagate") return [];
2380
2454
  const pathToBuildFrom = join(root, output.path);
@@ -2401,7 +2475,11 @@ async function getBarrelFiles(files, { type, meta = {}, root, output }) {
2401
2475
  //#endregion
2402
2476
  //#region src/utils/getConfigs.ts
2403
2477
  /**
2404
- * Converting UserConfig to Config Array without a change in the object beside the JSON convert.
2478
+ * Resolves a {@link ConfigInput} into a normalized array of {@link Config} objects.
2479
+ *
2480
+ * - Awaits the config when it is a `Promise`.
2481
+ * - Calls the factory function with `args` when the config is a function.
2482
+ * - Wraps a single config object in an array for uniform downstream handling.
2405
2483
  */
2406
2484
  async function getConfigs(config, args) {
2407
2485
  const resolved = await (typeof config === "function" ? config(args) : config);
@@ -2420,6 +2498,13 @@ function mergeResolvers(...resolvers) {
2420
2498
  }
2421
2499
  //#endregion
2422
2500
  //#region src/utils/getPreset.ts
2501
+ /**
2502
+ * Resolves a named preset into merged resolvers and transformers.
2503
+ *
2504
+ * - Merges the preset's resolvers on top of the first (default) resolver to produce `baseResolver`.
2505
+ * - Merges any additional user-supplied resolvers on top of that to produce the final `resolver`.
2506
+ * - Concatenates preset transformers before user-supplied transformers.
2507
+ */
2423
2508
  function getPreset(params) {
2424
2509
  const { preset: presetName, presets, resolvers, transformers: userTransformers } = params;
2425
2510
  const [defaultResolver, ...userResolvers] = resolvers;
@@ -2434,6 +2519,12 @@ function getPreset(params) {
2434
2519
  }
2435
2520
  //#endregion
2436
2521
  //#region src/utils/linters.ts
2522
+ /**
2523
+ * Returns `true` when the given linter is installed and callable.
2524
+ *
2525
+ * Availability is detected by running `<linter> --version` and checking
2526
+ * that the process exits without error.
2527
+ */
2437
2528
  async function isLinterAvailable(linter) {
2438
2529
  try {
2439
2530
  await x(linter, ["--version"], { nodeOptions: { stdio: "ignore" } });
@@ -2442,6 +2533,20 @@ async function isLinterAvailable(linter) {
2442
2533
  return false;
2443
2534
  }
2444
2535
  }
2536
+ /**
2537
+ * Detects the first available linter on the current system.
2538
+ *
2539
+ * - Checks in preference order: `biome`, `oxlint`, `eslint`.
2540
+ * - Returns `null` when none are found.
2541
+ *
2542
+ * @example
2543
+ * ```ts
2544
+ * const linter = await detectLinter()
2545
+ * if (linter) {
2546
+ * console.log(`Using ${linter} for linting`)
2547
+ * }
2548
+ * ```
2549
+ */
2445
2550
  async function detectLinter() {
2446
2551
  const linterNames = new Set([
2447
2552
  "biome",
@@ -2449,12 +2554,13 @@ async function detectLinter() {
2449
2554
  "eslint"
2450
2555
  ]);
2451
2556
  for (const linter of linterNames) if (await isLinterAvailable(linter)) return linter;
2557
+ return null;
2452
2558
  }
2453
2559
  //#endregion
2454
2560
  //#region src/utils/packageJSON.ts
2455
2561
  function getPackageJSONSync(cwd) {
2456
2562
  const pkgPath = pkg.up({ cwd });
2457
- if (!pkgPath) return;
2563
+ if (!pkgPath) return null;
2458
2564
  return JSON.parse(readSync(pkgPath));
2459
2565
  }
2460
2566
  function match(packageJSON, dependency) {
@@ -2464,12 +2570,27 @@ function match(packageJSON, dependency) {
2464
2570
  };
2465
2571
  if (typeof dependency === "string" && dependencies[dependency]) return dependencies[dependency];
2466
2572
  const matched = Object.keys(dependencies).find((dep) => dep.match(dependency));
2467
- return matched ? dependencies[matched] : void 0;
2573
+ return matched ? dependencies[matched] ?? null : null;
2468
2574
  }
2469
2575
  function getVersionSync(dependency, cwd) {
2470
2576
  const packageJSON = getPackageJSONSync(cwd);
2471
- return packageJSON ? match(packageJSON, dependency) : void 0;
2577
+ return packageJSON ? match(packageJSON, dependency) : null;
2472
2578
  }
2579
+ /**
2580
+ * Returns `true` when the nearest `package.json` declares a dependency that
2581
+ * satisfies the given semver range.
2582
+ *
2583
+ * - Searches both `dependencies` and `devDependencies`.
2584
+ * - Accepts a string package name or a `RegExp` to match scoped/pattern packages.
2585
+ * - Uses `semver.satisfies` for range comparison; returns `false` when the
2586
+ * version string cannot be coerced into a valid semver.
2587
+ *
2588
+ * @example
2589
+ * ```ts
2590
+ * satisfiesDependency('react', '>=18') // true when react@18.x is installed
2591
+ * satisfiesDependency(/^@tanstack\//, '>=5') // true when any @tanstack/* >=5 is found
2592
+ * ```
2593
+ */
2473
2594
  function satisfiesDependency(dependency, version, cwd) {
2474
2595
  const packageVersion = getVersionSync(dependency, cwd);
2475
2596
  if (!packageVersion) return false;
@@ -2479,6 +2600,6 @@ function satisfiesDependency(dependency, version, cwd) {
2479
2600
  return satisfies(semVer, version);
2480
2601
  }
2481
2602
  //#endregion
2482
- export { FunctionParams, PluginDriver, build, build as default, createAdapter, createPlugin, createStorage, defaultResolveOptions, defineConfig, defineGenerator, defineLogger, definePreset, definePresets, definePrinter, defineResolver, detectFormatter, detectLinter, formatters, fsStorage, getBarrelFiles, getConfigs, getMode, getPreset, isInputPath, linters, logLevel, memoryStorage, mergeResolvers, renderOperation, renderOperations, renderSchema, safeBuild, satisfiesDependency, setup };
2603
+ export { AsyncEventEmitter, FunctionParams, PluginDriver, URLPath, build, build as default, createAdapter, createPlugin, createStorage, defaultResolveOptions, defineConfig, defineGenerator, defineLogger, definePreset, definePresets, definePrinter, defineResolver, detectFormatter, detectLinter, formatters, fsStorage, getBarrelFiles, getConfigs, getMode, getPreset, isInputPath, linters, logLevel, memoryStorage, mergeResolvers, renderOperation, renderOperations, renderSchema, safeBuild, satisfiesDependency, setup };
2483
2604
 
2484
2605
  //# sourceMappingURL=index.js.map