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

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