@kubb/core 5.0.0-beta.8 → 5.0.0-beta.81

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.
Files changed (44) hide show
  1. package/LICENSE +17 -10
  2. package/README.md +20 -123
  3. package/dist/diagnostics-DcFh-77r.d.ts +2889 -0
  4. package/dist/index.cjs +2349 -1107
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.d.ts +79 -138
  7. package/dist/index.js +2344 -1101
  8. package/dist/index.js.map +1 -1
  9. package/dist/memoryStorage-BjUIqpYE.js +883 -0
  10. package/dist/memoryStorage-BjUIqpYE.js.map +1 -0
  11. package/dist/memoryStorage-DQ6qXJ8o.cjs +1021 -0
  12. package/dist/memoryStorage-DQ6qXJ8o.cjs.map +1 -0
  13. package/dist/mocks.cjs +84 -24
  14. package/dist/mocks.cjs.map +1 -1
  15. package/dist/mocks.d.ts +37 -11
  16. package/dist/mocks.js +86 -28
  17. package/dist/mocks.js.map +1 -1
  18. package/package.json +8 -26
  19. package/dist/PluginDriver-Cu1Kj9S-.cjs +0 -1075
  20. package/dist/PluginDriver-Cu1Kj9S-.cjs.map +0 -1
  21. package/dist/PluginDriver-D8Z0Htid.js +0 -978
  22. package/dist/PluginDriver-D8Z0Htid.js.map +0 -1
  23. package/dist/createKubb-Cagd4PIe.d.ts +0 -2082
  24. package/src/FileManager.ts +0 -115
  25. package/src/FileProcessor.ts +0 -86
  26. package/src/PluginDriver.ts +0 -457
  27. package/src/constants.ts +0 -35
  28. package/src/createAdapter.ts +0 -108
  29. package/src/createKubb.ts +0 -1268
  30. package/src/createRenderer.ts +0 -57
  31. package/src/createStorage.ts +0 -70
  32. package/src/defineGenerator.ts +0 -175
  33. package/src/defineLogger.ts +0 -58
  34. package/src/defineMiddleware.ts +0 -62
  35. package/src/defineParser.ts +0 -44
  36. package/src/definePlugin.ts +0 -379
  37. package/src/defineResolver.ts +0 -654
  38. package/src/devtools.ts +0 -66
  39. package/src/index.ts +0 -20
  40. package/src/mocks.ts +0 -177
  41. package/src/storages/fsStorage.ts +0 -90
  42. package/src/storages/memoryStorage.ts +0 -55
  43. package/src/types.ts +0 -41
  44. /package/dist/{chunk--u3MIqq1.js → rolldown-runtime-C0LytTxp.js} +0 -0
@@ -0,0 +1,1021 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __name = (target, value) => __defProp(target, "name", {
5
+ value,
6
+ configurable: true
7
+ });
8
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
9
+ var __getOwnPropNames = Object.getOwnPropertyNames;
10
+ var __getProtoOf = Object.getPrototypeOf;
11
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
14
+ key = keys[i];
15
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
16
+ get: ((k) => from[k]).bind(null, key),
17
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
18
+ });
19
+ }
20
+ return to;
21
+ };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
23
+ value: mod,
24
+ enumerable: true
25
+ }) : target, mod));
26
+ //#endregion
27
+ let node_events = require("node:events");
28
+ let node_fs_promises = require("node:fs/promises");
29
+ let node_path = require("node:path");
30
+ let _kubb_ast = require("@kubb/ast");
31
+ //#region ../../internals/utils/src/errors.ts
32
+ /**
33
+ * Thrown when one or more errors occur during a Kubb build.
34
+ * Carries the full list of underlying errors on `errors`.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * throw new BuildError('Build failed', { errors: [err1, err2] })
39
+ * ```
40
+ */
41
+ var BuildError = class extends Error {
42
+ errors;
43
+ constructor(message, options) {
44
+ super(message, { cause: options.cause });
45
+ this.name = "BuildError";
46
+ this.errors = options.errors;
47
+ }
48
+ };
49
+ /**
50
+ * Coerces an unknown thrown value to an `Error` instance.
51
+ * Returns the value as-is when it is already an `Error`; otherwise wraps it with `String(value)`.
52
+ *
53
+ * @example
54
+ * ```ts
55
+ * try { ... } catch(err) {
56
+ * throw new BuildError('Build failed', { cause: toError(err), errors: [] })
57
+ * }
58
+ * ```
59
+ */
60
+ function toError(value) {
61
+ return value instanceof Error ? value : new Error(String(value));
62
+ }
63
+ /**
64
+ * Extracts a human-readable message from any thrown value.
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * getErrorMessage(new Error('oops')) // 'oops'
69
+ * getErrorMessage('plain string') // 'plain string'
70
+ * ```
71
+ */
72
+ function getErrorMessage(value) {
73
+ return value instanceof Error ? value.message : String(value);
74
+ }
75
+ //#endregion
76
+ //#region ../../internals/utils/src/asyncEventEmitter.ts
77
+ /**
78
+ * Typed `EventEmitter` that awaits all async listeners before resolving.
79
+ * Wraps Node's `EventEmitter` with full TypeScript event-map inference.
80
+ *
81
+ * @example
82
+ * ```ts
83
+ * const emitter = new AsyncEventEmitter<{ build: [name: string] }>()
84
+ * emitter.on('build', async (name) => { console.log(name) })
85
+ * await emitter.emit('build', 'petstore') // all listeners awaited
86
+ * ```
87
+ */
88
+ var AsyncEventEmitter = class {
89
+ /**
90
+ * Maximum number of listeners per event before Node emits a memory-leak warning.
91
+ * @default 10
92
+ */
93
+ constructor(maxListener = 10) {
94
+ this.#emitter.setMaxListeners(maxListener);
95
+ }
96
+ #emitter = new node_events.EventEmitter();
97
+ /**
98
+ * Emits `eventName` and awaits all registered listeners sequentially.
99
+ * Throws if any listener rejects, wrapping the cause with the event name and serialized arguments.
100
+ *
101
+ * @example
102
+ * ```ts
103
+ * await emitter.emit('build', 'petstore')
104
+ * ```
105
+ */
106
+ emit(eventName, ...eventArgs) {
107
+ const listeners = this.#emitter.listeners(eventName);
108
+ if (listeners.length === 0) return;
109
+ return this.#emitAll(eventName, listeners, eventArgs);
110
+ }
111
+ async #emitAll(eventName, listeners, eventArgs) {
112
+ for (const listener of listeners) try {
113
+ await listener(...eventArgs);
114
+ } catch (err) {
115
+ let serializedArgs;
116
+ try {
117
+ serializedArgs = JSON.stringify(eventArgs);
118
+ } catch {
119
+ serializedArgs = String(eventArgs);
120
+ }
121
+ throw new Error(`Error in async listener for "${eventName}" with eventArgs ${serializedArgs}`, { cause: toError(err) });
122
+ }
123
+ }
124
+ /**
125
+ * Registers a persistent listener for `eventName`.
126
+ *
127
+ * @example
128
+ * ```ts
129
+ * emitter.on('build', async (name) => { console.log(name) })
130
+ * ```
131
+ */
132
+ on(eventName, handler) {
133
+ this.#emitter.on(eventName, handler);
134
+ }
135
+ /**
136
+ * Removes a previously registered listener.
137
+ *
138
+ * @example
139
+ * ```ts
140
+ * emitter.off('build', handler)
141
+ * ```
142
+ */
143
+ off(eventName, handler) {
144
+ this.#emitter.off(eventName, handler);
145
+ }
146
+ /**
147
+ * Returns the number of listeners registered for `eventName`.
148
+ *
149
+ * @example
150
+ * ```ts
151
+ * emitter.on('build', handler)
152
+ * emitter.listenerCount('build') // 1
153
+ * ```
154
+ */
155
+ listenerCount(eventName) {
156
+ return this.#emitter.listenerCount(eventName);
157
+ }
158
+ /**
159
+ * Raises or lowers the per-event listener ceiling before Node warns about a memory leak.
160
+ * Set this above the expected listener count when many listeners attach by design.
161
+ *
162
+ * @example
163
+ * ```ts
164
+ * emitter.setMaxListeners(40)
165
+ * ```
166
+ */
167
+ setMaxListeners(max) {
168
+ this.#emitter.setMaxListeners(max);
169
+ }
170
+ /**
171
+ * Removes all listeners from every event channel.
172
+ *
173
+ * @example
174
+ * ```ts
175
+ * emitter.removeAll()
176
+ * ```
177
+ */
178
+ removeAll() {
179
+ this.#emitter.removeAllListeners();
180
+ }
181
+ };
182
+ //#endregion
183
+ //#region ../../internals/utils/src/casing.ts
184
+ /**
185
+ * Shared implementation for camelCase and PascalCase conversion.
186
+ * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
187
+ * and capitalizes each word according to `pascal`.
188
+ *
189
+ * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
190
+ */
191
+ function toCamelOrPascal(text, pascal) {
192
+ 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) => {
193
+ if (word.length > 1 && word === word.toUpperCase()) return word;
194
+ return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
195
+ }).join("").replace(/[^a-zA-Z0-9]/g, "");
196
+ }
197
+ /**
198
+ * Converts `text` to camelCase.
199
+ *
200
+ * @example Word boundaries
201
+ * `camelCase('hello-world') // 'helloWorld'`
202
+ *
203
+ * @example With a prefix
204
+ * `camelCase('tag', { prefix: 'create' }) // 'createTag'`
205
+ */
206
+ function camelCase(text, { prefix = "", suffix = "" } = {}) {
207
+ return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
208
+ }
209
+ /**
210
+ * Converts `text` to PascalCase.
211
+ *
212
+ * @example Word boundaries
213
+ * `pascalCase('hello-world') // 'HelloWorld'`
214
+ *
215
+ * @example With a suffix
216
+ * `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`
217
+ */
218
+ function pascalCase(text, { prefix = "", suffix = "" } = {}) {
219
+ return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
220
+ }
221
+ //#endregion
222
+ //#region ../../internals/utils/src/runtime.ts
223
+ /**
224
+ * Detects the JavaScript runtime executing the current process and exposes its name and version.
225
+ *
226
+ * Prefer the shared {@link runtime} instance over constructing your own.
227
+ */
228
+ var Runtime = class {
229
+ /**
230
+ * `true` when the current process is running under Bun.
231
+ *
232
+ * Detection keys off the global `Bun` object rather than `process.versions`,
233
+ * because Bun polyfills `process.versions.node` for Node compatibility and would
234
+ * otherwise look like Node.
235
+ *
236
+ * @example
237
+ * ```ts
238
+ * if (runtime.isBun) {
239
+ * await Bun.write(path, data)
240
+ * }
241
+ * ```
242
+ */
243
+ get isBun() {
244
+ return typeof Bun !== "undefined";
245
+ }
246
+ /**
247
+ * `true` when the current process is running under Deno.
248
+ */
249
+ get isDeno() {
250
+ return typeof globalThis.Deno !== "undefined";
251
+ }
252
+ /**
253
+ * `true` when the current process is running under Node.
254
+ *
255
+ * Bun and Deno are excluded first so a polyfilled `process` does not register as Node.
256
+ */
257
+ get isNode() {
258
+ return !this.isBun && !this.isDeno && typeof process !== "undefined" && process.versions?.node != null;
259
+ }
260
+ /**
261
+ * Name of the runtime executing the current process.
262
+ *
263
+ * @example
264
+ * ```ts
265
+ * runtime.name // 'bun' when run with `bun kubb`, 'node' otherwise
266
+ * ```
267
+ */
268
+ get name() {
269
+ if (this.isBun) return "bun";
270
+ if (this.isDeno) return "deno";
271
+ return "node";
272
+ }
273
+ /**
274
+ * Version of the active runtime, or an empty string when it cannot be read.
275
+ *
276
+ * @example
277
+ * ```ts
278
+ * runtime.version // '1.3.11' under Bun, '22.22.2' under Node
279
+ * ```
280
+ */
281
+ get version() {
282
+ if (this.isBun) return process.versions.bun ?? "";
283
+ if (this.isDeno) return globalThis.Deno?.version?.deno ?? "";
284
+ return process.versions?.node ?? "";
285
+ }
286
+ };
287
+ /**
288
+ * Shared {@link Runtime} instance describing the JavaScript runtime executing the current process.
289
+ */
290
+ const runtime = new Runtime();
291
+ //#endregion
292
+ //#region ../../internals/utils/src/fs.ts
293
+ /**
294
+ * Reads the file at `path` as a UTF-8 string.
295
+ * Uses `Bun.file().text()` when running under Bun, `fs.readFile` otherwise.
296
+ *
297
+ * @example
298
+ * ```ts
299
+ * const source = await read('./src/Pet.ts')
300
+ * ```
301
+ */
302
+ async function read(path) {
303
+ if (runtime.isBun) return Bun.file(path).text();
304
+ return (0, node_fs_promises.readFile)(path, { encoding: "utf8" });
305
+ }
306
+ /**
307
+ * Writes `data` to `path`, trimming leading/trailing whitespace before saving.
308
+ * Skips the write when the trimmed content is empty or identical to what is already on disk.
309
+ * Creates any missing parent directories automatically.
310
+ * When `sanity` is `true`, re-reads the file after writing and throws if the content does not match.
311
+ *
312
+ * @example
313
+ * ```ts
314
+ * await write('./src/Pet.ts', source) // writes and returns trimmed content
315
+ * await write('./src/Pet.ts', source) // null — file unchanged
316
+ * await write('./src/Pet.ts', ' ') // null — empty content skipped
317
+ * ```
318
+ */
319
+ async function write(path, data, options = {}) {
320
+ const trimmed = data.trim();
321
+ if (trimmed === "") return null;
322
+ const resolved = (0, node_path.resolve)(path);
323
+ if (runtime.isBun) {
324
+ const file = Bun.file(resolved);
325
+ if ((await file.exists() ? await file.text() : null) === trimmed) return null;
326
+ await Bun.write(resolved, trimmed);
327
+ return trimmed;
328
+ }
329
+ try {
330
+ if (await (0, node_fs_promises.readFile)(resolved, { encoding: "utf-8" }) === trimmed) return null;
331
+ } catch {}
332
+ await (0, node_fs_promises.mkdir)((0, node_path.dirname)(resolved), { recursive: true });
333
+ await (0, node_fs_promises.writeFile)(resolved, trimmed, { encoding: "utf-8" });
334
+ if (options.sanity) {
335
+ const savedData = await (0, node_fs_promises.readFile)(resolved, { encoding: "utf-8" });
336
+ if (savedData !== trimmed) throw new Error(`Sanity check failed for ${path}\n\nData[${data.length}]:\n${data}\n\nSaved[${savedData.length}]:\n${savedData}\n`);
337
+ return savedData;
338
+ }
339
+ return trimmed;
340
+ }
341
+ /**
342
+ * Recursively removes `path`. Silently succeeds when `path` does not exist.
343
+ *
344
+ * @example
345
+ * ```ts
346
+ * await clean('./dist')
347
+ * ```
348
+ */
349
+ async function clean(path) {
350
+ return (0, node_fs_promises.rm)(path, {
351
+ recursive: true,
352
+ force: true
353
+ });
354
+ }
355
+ /**
356
+ * Converts a filesystem path to use POSIX (`/`) separators.
357
+ *
358
+ * Most of the codebase compares and composes paths as strings (prefix matching, joining for
359
+ * import specifiers, splitting on `/`). On POSIX `path.resolve` already returns `/`-separated
360
+ * paths, but on Windows it returns `\`-separated paths, which breaks every such comparison.
361
+ *
362
+ * Routing every path that crosses a module boundary through `toPosixPath` keeps the rest of the
363
+ * code platform-agnostic. The conversion runs unconditionally so Windows-specific behavior is
364
+ * exercisable from POSIX CI.
365
+ *
366
+ * @example
367
+ * toPosixPath('C:\\repo\\src\\pet.ts') // 'C:/repo/src/pet.ts'
368
+ */
369
+ function toPosixPath(filePath) {
370
+ return filePath.replaceAll("\\", "/");
371
+ }
372
+ /**
373
+ * Builds a nested file path from a dotted name. Splits on dots that precede a letter
374
+ * (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
375
+ * every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
376
+ *
377
+ * Empty segments are dropped before joining. They arise when the name starts with a dot
378
+ * followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
379
+ * an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
380
+ * absolute path, letting generated files escape the configured output directory.
381
+ *
382
+ * @example Nested path from a dotted name
383
+ * `toFilePath('pet.petId') // 'pet/petId'`
384
+ *
385
+ * @example PascalCase the final segment
386
+ * `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
387
+ *
388
+ * @example Suffix applied to the final segment only
389
+ * `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
390
+ */
391
+ function toFilePath(name, caseLast = camelCase) {
392
+ const parts = name.split(/\.(?=[a-zA-Z])/);
393
+ return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
394
+ }
395
+ //#endregion
396
+ //#region src/constants.ts
397
+ /**
398
+ * Plugin `include` filter types that select operations directly. When one of these is set
399
+ * without a `schemaName` include, the generate phase pre-scans operations to compute the set
400
+ * of schemas they reach, so unreachable schemas can be pruned for that plugin.
401
+ */
402
+ const OPERATION_FILTER_TYPES = /* @__PURE__ */ new Set([
403
+ "tag",
404
+ "operationId",
405
+ "path",
406
+ "method",
407
+ "contentType"
408
+ ]);
409
+ /**
410
+ * Stable codes Kubb attaches to a `Diagnostic`. Each maps to a known failure mode
411
+ * and stays stable so it can be referenced in tooling and (later) docs. Reference
412
+ * these instead of inlining the string at a throw site.
413
+ */
414
+ const diagnosticCode = {
415
+ /**
416
+ * Fallback for an unstructured error with no specific code.
417
+ */
418
+ unknown: "KUBB_UNKNOWN",
419
+ /**
420
+ * The `input.path` file or URL could not be read.
421
+ */
422
+ inputNotFound: "KUBB_INPUT_NOT_FOUND",
423
+ /**
424
+ * An adapter was configured without an `input`.
425
+ */
426
+ inputRequired: "KUBB_INPUT_REQUIRED",
427
+ /**
428
+ * A `$ref` (or equivalent reference) could not be resolved in the source document.
429
+ */
430
+ refNotFound: "KUBB_REF_NOT_FOUND",
431
+ /**
432
+ * A server variable value is not allowed by its `enum`.
433
+ */
434
+ invalidServerVariable: "KUBB_INVALID_SERVER_VARIABLE",
435
+ /**
436
+ * A required plugin is missing from the config.
437
+ */
438
+ pluginNotFound: "KUBB_PLUGIN_NOT_FOUND",
439
+ /**
440
+ * A plugin threw while generating.
441
+ */
442
+ pluginFailed: "KUBB_PLUGIN_FAILED",
443
+ /**
444
+ * A plugin reported a non-fatal warning through `ctx.warn`.
445
+ */
446
+ pluginWarning: "KUBB_PLUGIN_WARNING",
447
+ /**
448
+ * A plugin reported an informational message through `ctx.info`.
449
+ */
450
+ pluginInfo: "KUBB_PLUGIN_INFO",
451
+ /**
452
+ * A schema uses a `format` Kubb does not map to a specific type. Reserved for
453
+ * adapters to emit as a `warning`.
454
+ */
455
+ unsupportedFormat: "KUBB_UNSUPPORTED_FORMAT",
456
+ /**
457
+ * A referenced schema or operation is marked `deprecated`. Reserved for adapters
458
+ * to emit as an `info`.
459
+ */
460
+ deprecated: "KUBB_DEPRECATED",
461
+ /**
462
+ * An adapter is required but the config has none. The build cannot read the input
463
+ * without one.
464
+ */
465
+ adapterRequired: "KUBB_ADAPTER_REQUIRED",
466
+ /**
467
+ * A resolved output path escapes the output directory, which can stem from a path
468
+ * traversal in the spec or a misconfigured `group.name`.
469
+ */
470
+ pathTraversal: "KUBB_PATH_TRAVERSAL",
471
+ /**
472
+ * A plugin's options are invalid, for example `output.mode: 'file'` paired with a `group` option.
473
+ */
474
+ invalidPluginOptions: "KUBB_INVALID_PLUGIN_OPTIONS",
475
+ /**
476
+ * A post-generate shell hook (`hooks.done`) exited with a failure.
477
+ */
478
+ hookFailed: "KUBB_HOOK_FAILED",
479
+ /**
480
+ * The formatter pass over the generated files failed.
481
+ */
482
+ formatFailed: "KUBB_FORMAT_FAILED",
483
+ /**
484
+ * The linter pass over the generated files failed.
485
+ */
486
+ lintFailed: "KUBB_LINT_FAILED",
487
+ /**
488
+ * Not a failure. Carries a plugin's elapsed time, summed into the run total.
489
+ */
490
+ performance: "KUBB_PERFORMANCE",
491
+ /**
492
+ * Not a failure. A newer Kubb version is available on npm.
493
+ */
494
+ updateAvailable: "KUBB_UPDATE_AVAILABLE"
495
+ };
496
+ //#endregion
497
+ //#region src/createStorage.ts
498
+ /**
499
+ * Defines a custom storage backend. The builder receives user options and
500
+ * returns a `Storage` implementation. Kubb ships with filesystem and in-memory
501
+ * storages. A custom backend writes generated files elsewhere, such as cloud
502
+ * storage or a database.
503
+ *
504
+ * @example In-memory storage (the built-in implementation)
505
+ * ```ts
506
+ * import { createStorage } from '@kubb/core'
507
+ *
508
+ * export const memoryStorage = createStorage(() => {
509
+ * const store = new Map<string, string>()
510
+ *
511
+ * return {
512
+ * name: 'memory',
513
+ * async hasItem(key) {
514
+ * return store.has(key)
515
+ * },
516
+ * async getItem(key) {
517
+ * return store.get(key) ?? null
518
+ * },
519
+ * async setItem(key, value) {
520
+ * store.set(key, value)
521
+ * },
522
+ * async removeItem(key) {
523
+ * store.delete(key)
524
+ * },
525
+ * async getKeys(base) {
526
+ * const keys = [...store.keys()]
527
+ * return base ? keys.filter((k) => k.startsWith(base)) : keys
528
+ * },
529
+ * async clear(base) {
530
+ * if (!base) store.clear()
531
+ * },
532
+ * }
533
+ * })
534
+ * ```
535
+ */
536
+ function createStorage(build) {
537
+ return (options) => build(options ?? {});
538
+ }
539
+ //#endregion
540
+ //#region src/FileManager.ts
541
+ function mergeFile(a, b) {
542
+ return {
543
+ ...a,
544
+ banner: b.banner,
545
+ footer: b.footer,
546
+ copy: b.copy ?? a.copy,
547
+ sources: a.sources.length ? b.sources.length ? [...a.sources, ...b.sources] : a.sources : b.sources,
548
+ imports: a.imports.length ? b.imports.length ? [...a.imports, ...b.imports] : a.imports : b.imports,
549
+ exports: a.exports.length ? b.exports.length ? [...a.exports, ...b.exports] : a.exports : b.exports
550
+ };
551
+ }
552
+ function isIndexPath(path) {
553
+ return path.endsWith("/index.ts") || path === "index.ts";
554
+ }
555
+ function compareFiles(a, b) {
556
+ const lenDiff = a.path.length - b.path.length;
557
+ if (lenDiff !== 0) return lenDiff;
558
+ const aIsIndex = isIndexPath(a.path);
559
+ const bIsIndex = isIndexPath(b.path);
560
+ if (aIsIndex && !bIsIndex) return 1;
561
+ if (!aIsIndex && bIsIndex) return -1;
562
+ return 0;
563
+ }
564
+ /**
565
+ * In-memory file store for generated files. Files sharing a `path` are merged
566
+ * (sources/imports/exports concatenated). The `files` getter is sorted by
567
+ * path length (barrel `index.ts` last within a bucket).
568
+ *
569
+ * @example
570
+ * ```ts
571
+ * const manager = new FileManager()
572
+ * manager.upsert(myFile)
573
+ * manager.files // sorted view
574
+ * ```
575
+ */
576
+ var FileManager = class {
577
+ /**
578
+ * Subscribe to file-store changes. Listeners on `upsert` see each resolved file as it lands
579
+ * through `add` or `upsert`.
580
+ */
581
+ hooks = new AsyncEventEmitter();
582
+ #cache = /* @__PURE__ */ new Map();
583
+ #sorted = null;
584
+ add(...files) {
585
+ return this.#store(files, false);
586
+ }
587
+ upsert(...files) {
588
+ return this.#store(files, true);
589
+ }
590
+ #store(files, mergeExisting) {
591
+ const batch = files.length > 1 ? this.#dedupe(files) : files;
592
+ const resolved = [];
593
+ for (const file of batch) {
594
+ const existing = this.#cache.get(file.path);
595
+ const merged = existing && mergeExisting ? _kubb_ast.ast.factory.createFile(mergeFile(existing, file)) : _kubb_ast.ast.factory.createFile(file);
596
+ this.#cache.set(merged.path, merged);
597
+ resolved.push(merged);
598
+ this.hooks.emit("upsert", merged);
599
+ }
600
+ if (resolved.length > 0) this.#sorted = null;
601
+ return resolved;
602
+ }
603
+ #dedupe(files) {
604
+ const seen = /* @__PURE__ */ new Map();
605
+ for (const file of files) {
606
+ const prev = seen.get(file.path);
607
+ seen.set(file.path, prev ? mergeFile(prev, file) : file);
608
+ }
609
+ return [...seen.values()];
610
+ }
611
+ getByPath(path) {
612
+ return this.#cache.get(path) ?? null;
613
+ }
614
+ deleteByPath(path) {
615
+ if (!this.#cache.delete(path)) return;
616
+ this.#sorted = null;
617
+ }
618
+ clear() {
619
+ this.#cache.clear();
620
+ this.#sorted = null;
621
+ }
622
+ /**
623
+ * Releases all stored files and clears every `hooks` listener. Called by the core after
624
+ * `kubb:build:end`.
625
+ */
626
+ dispose() {
627
+ this.clear();
628
+ this.hooks.removeAll();
629
+ }
630
+ [Symbol.dispose]() {
631
+ this.dispose();
632
+ }
633
+ /**
634
+ * All stored files in stable sort order (shortest path first, barrel files
635
+ * last within a length bucket). Returns a cached view, do not mutate.
636
+ */
637
+ get files() {
638
+ return this.#sorted ??= [...this.#cache.values()].sort(compareFiles);
639
+ }
640
+ };
641
+ //#endregion
642
+ //#region src/FileProcessor.ts
643
+ function joinSources(file) {
644
+ const sources = file.sources;
645
+ if (sources.length === 0) return "";
646
+ const parts = [];
647
+ for (const source of sources) {
648
+ const text = (0, _kubb_ast.extractStringsFromNodes)(source.nodes);
649
+ if (text) parts.push(text);
650
+ }
651
+ return parts.join("\n\n");
652
+ }
653
+ async function parseCopy(file) {
654
+ let content;
655
+ try {
656
+ content = await read(file.copy);
657
+ } catch (err) {
658
+ throw new Error(`[kubb] Could not copy file into output: ${file.copy}`, { cause: err });
659
+ }
660
+ return [
661
+ file.banner,
662
+ content,
663
+ file.footer
664
+ ].filter((segment) => Boolean(segment)).map((segment) => segment.trimEnd()).join("\n");
665
+ }
666
+ /**
667
+ * Turns `FileNode`s into source strings and writes them to storage.
668
+ *
669
+ * Two modes share the same instance. Stateless mode (`parse`, `stream`, `run`) just runs the
670
+ * conversion. Queue mode (`enqueue`, `flush`, `drain`) buffers files deduped by path and
671
+ * writes each batch through storage with up to `STREAM_FLUSH_EVERY` requests in flight.
672
+ *
673
+ * `flush` does not wait for its batch to finish, so dispatch can overlap with IO. The next
674
+ * `flush` or `drain` picks the in-flight batch up. `drain` blocks until everything has been
675
+ * written and is meant for the end of a build.
676
+ *
677
+ * To surface build-level hook signals (`kubb:files:processing:*` and friends) subscribe to
678
+ * `hooks` and re-emit on the kubb bus.
679
+ */
680
+ var FileProcessor = class {
681
+ hooks = new AsyncEventEmitter();
682
+ #parsers;
683
+ #storage;
684
+ #extension;
685
+ #pending = /* @__PURE__ */ new Map();
686
+ #runningFlush = null;
687
+ constructor(options) {
688
+ this.#parsers = options.parsers ?? null;
689
+ this.#storage = options.storage;
690
+ this.#extension = options.extension ?? null;
691
+ }
692
+ /**
693
+ * Files waiting in the queue.
694
+ */
695
+ get size() {
696
+ return this.#pending.size;
697
+ }
698
+ async parse(file) {
699
+ if (file.copy) return parseCopy(file);
700
+ const parsers = this.#parsers;
701
+ const parseExtName = this.#extension?.[file.extname] || void 0;
702
+ if (!parsers || !file.extname) return joinSources(file);
703
+ const parser = parsers.get(file.extname);
704
+ if (!parser) return joinSources(file);
705
+ return parser.parse(file, { extname: parseExtName });
706
+ }
707
+ async *stream(files) {
708
+ const total = files.length;
709
+ if (total === 0) return;
710
+ let processed = 0;
711
+ for (const file of files) {
712
+ const source = await this.parse(file);
713
+ processed++;
714
+ yield {
715
+ file,
716
+ source,
717
+ processed,
718
+ total,
719
+ percentage: processed / total * 100
720
+ };
721
+ }
722
+ }
723
+ async run(files) {
724
+ await this.hooks.emit("start", files);
725
+ for await (const { file, source, processed, total, percentage } of this.stream(files)) await this.hooks.emit("update", {
726
+ file,
727
+ source,
728
+ processed,
729
+ percentage,
730
+ total
731
+ });
732
+ await this.hooks.emit("end", files);
733
+ return files;
734
+ }
735
+ /**
736
+ * Adds a file to the next flush. A later `enqueue` for the same path replaces the previous
737
+ * entry, matching `FileManager.upsert`. Fires the `enqueue` event.
738
+ */
739
+ enqueue(file) {
740
+ this.#pending.set(file.path, file);
741
+ this.hooks.emit("enqueue", file);
742
+ }
743
+ /**
744
+ * Starts processing the queued files. Waits for any previous flush to finish (so two
745
+ * batches never run together) and then returns without waiting for the new one. The next
746
+ * `flush` or `drain` picks up the in-flight task.
747
+ */
748
+ async flush() {
749
+ if (this.#runningFlush) await this.#runningFlush;
750
+ if (this.#pending.size === 0) return;
751
+ const batch = [...this.#pending.values()];
752
+ this.#pending.clear();
753
+ this.#runningFlush = this.#processAndWrite(batch).finally(() => {
754
+ this.#runningFlush = null;
755
+ });
756
+ }
757
+ /**
758
+ * Waits for the in-flight flush and writes any files still queued. Fires the `drain` event
759
+ * when both are done.
760
+ */
761
+ async drain() {
762
+ if (this.#runningFlush) await this.#runningFlush;
763
+ if (this.#pending.size > 0) {
764
+ const batch = [...this.#pending.values()];
765
+ this.#pending.clear();
766
+ await this.#processAndWrite(batch);
767
+ }
768
+ await this.hooks.emit("drain");
769
+ }
770
+ async #processAndWrite(files) {
771
+ const storage = this.#storage;
772
+ await this.hooks.emit("start", files);
773
+ const queue = [];
774
+ for await (const item of this.stream(files)) {
775
+ await this.hooks.emit("update", item);
776
+ if (item.source) {
777
+ queue.push(storage.setItem(item.file.path, item.source));
778
+ if (queue.length >= 50) await Promise.all(queue.splice(0));
779
+ }
780
+ }
781
+ await Promise.all(queue);
782
+ await this.hooks.emit("end", files);
783
+ }
784
+ /**
785
+ * Clears every listener and the pending queue.
786
+ */
787
+ dispose() {
788
+ this.hooks.removeAll();
789
+ this.#pending.clear();
790
+ }
791
+ [Symbol.dispose]() {
792
+ this.dispose();
793
+ }
794
+ };
795
+ //#endregion
796
+ //#region \0@oxc-project+runtime@0.137.0/helpers/esm/usingCtx.js
797
+ function _usingCtx() {
798
+ var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
799
+ var n = Error();
800
+ return n.name = "SuppressedError", n.error = r, n.suppressed = e, n;
801
+ };
802
+ var e = {};
803
+ var n = [];
804
+ function using(r, e) {
805
+ if (null != e) {
806
+ if (Object(e) !== e) throw new TypeError("using declarations can only be used with objects, functions, null, or undefined.");
807
+ if (r) var o = e[Symbol.asyncDispose || Symbol["for"]("Symbol.asyncDispose")];
808
+ if (void 0 === o && (o = e[Symbol.dispose || Symbol["for"]("Symbol.dispose")], r)) var t = o;
809
+ if ("function" != typeof o) throw new TypeError("Object is not disposable.");
810
+ t && (o = function o() {
811
+ try {
812
+ t.call(e);
813
+ } catch (r) {
814
+ return Promise.reject(r);
815
+ }
816
+ }), n.push({
817
+ v: e,
818
+ d: o,
819
+ a: r
820
+ });
821
+ } else r && n.push({
822
+ d: e,
823
+ a: r
824
+ });
825
+ return e;
826
+ }
827
+ return {
828
+ e,
829
+ u: using.bind(null, !1),
830
+ a: using.bind(null, !0),
831
+ d: function d() {
832
+ var o;
833
+ var t = this.e;
834
+ var s = 0;
835
+ function next() {
836
+ for (; o = n.pop();) try {
837
+ if (!o.a && 1 === s) return s = 0, n.push(o), Promise.resolve().then(next);
838
+ if (o.d) {
839
+ var r = o.d.call(o.v);
840
+ if (o.a) return s |= 2, Promise.resolve(r).then(next, err);
841
+ } else s |= 1;
842
+ } catch (r) {
843
+ return err(r);
844
+ }
845
+ if (1 === s) return t !== e ? Promise.reject(t) : Promise.resolve();
846
+ if (t !== e) throw t;
847
+ }
848
+ function err(n) {
849
+ return t = t !== e ? new r(n, t) : n, next();
850
+ }
851
+ return next();
852
+ }
853
+ };
854
+ }
855
+ //#endregion
856
+ //#region src/storages/memoryStorage.ts
857
+ /**
858
+ * In-memory storage driver. Useful for testing and dry-run scenarios where
859
+ * generated output should be captured without touching the filesystem.
860
+ *
861
+ * All data lives in a `Map` scoped to the storage instance and is discarded
862
+ * when the instance is garbage-collected.
863
+ *
864
+ * @example
865
+ * ```ts
866
+ * import { memoryStorage } from '@kubb/core'
867
+ * import { defineConfig } from 'kubb'
868
+ *
869
+ * export default defineConfig({
870
+ * input: { path: './petStore.yaml' },
871
+ * output: { path: './src/gen' },
872
+ * storage: memoryStorage(),
873
+ * })
874
+ * ```
875
+ */
876
+ const memoryStorage = createStorage(() => {
877
+ const store = /* @__PURE__ */ new Map();
878
+ return {
879
+ name: "memory",
880
+ async hasItem(key) {
881
+ return store.has(key);
882
+ },
883
+ async getItem(key) {
884
+ return store.get(key) ?? null;
885
+ },
886
+ async setItem(key, value) {
887
+ store.set(key, value);
888
+ },
889
+ async removeItem(key) {
890
+ store.delete(key);
891
+ },
892
+ async getKeys(base) {
893
+ const keys = [...store.keys()];
894
+ return base ? keys.filter((k) => k.startsWith(base)) : keys;
895
+ },
896
+ async clear(base) {
897
+ if (!base) {
898
+ store.clear();
899
+ return;
900
+ }
901
+ for (const key of store.keys()) if (key.startsWith(base)) store.delete(key);
902
+ }
903
+ };
904
+ });
905
+ //#endregion
906
+ Object.defineProperty(exports, "AsyncEventEmitter", {
907
+ enumerable: true,
908
+ get: function() {
909
+ return AsyncEventEmitter;
910
+ }
911
+ });
912
+ Object.defineProperty(exports, "BuildError", {
913
+ enumerable: true,
914
+ get: function() {
915
+ return BuildError;
916
+ }
917
+ });
918
+ Object.defineProperty(exports, "FileManager", {
919
+ enumerable: true,
920
+ get: function() {
921
+ return FileManager;
922
+ }
923
+ });
924
+ Object.defineProperty(exports, "FileProcessor", {
925
+ enumerable: true,
926
+ get: function() {
927
+ return FileProcessor;
928
+ }
929
+ });
930
+ Object.defineProperty(exports, "OPERATION_FILTER_TYPES", {
931
+ enumerable: true,
932
+ get: function() {
933
+ return OPERATION_FILTER_TYPES;
934
+ }
935
+ });
936
+ Object.defineProperty(exports, "__name", {
937
+ enumerable: true,
938
+ get: function() {
939
+ return __name;
940
+ }
941
+ });
942
+ Object.defineProperty(exports, "__toESM", {
943
+ enumerable: true,
944
+ get: function() {
945
+ return __toESM;
946
+ }
947
+ });
948
+ Object.defineProperty(exports, "_usingCtx", {
949
+ enumerable: true,
950
+ get: function() {
951
+ return _usingCtx;
952
+ }
953
+ });
954
+ Object.defineProperty(exports, "camelCase", {
955
+ enumerable: true,
956
+ get: function() {
957
+ return camelCase;
958
+ }
959
+ });
960
+ Object.defineProperty(exports, "clean", {
961
+ enumerable: true,
962
+ get: function() {
963
+ return clean;
964
+ }
965
+ });
966
+ Object.defineProperty(exports, "createStorage", {
967
+ enumerable: true,
968
+ get: function() {
969
+ return createStorage;
970
+ }
971
+ });
972
+ Object.defineProperty(exports, "diagnosticCode", {
973
+ enumerable: true,
974
+ get: function() {
975
+ return diagnosticCode;
976
+ }
977
+ });
978
+ Object.defineProperty(exports, "getErrorMessage", {
979
+ enumerable: true,
980
+ get: function() {
981
+ return getErrorMessage;
982
+ }
983
+ });
984
+ Object.defineProperty(exports, "memoryStorage", {
985
+ enumerable: true,
986
+ get: function() {
987
+ return memoryStorage;
988
+ }
989
+ });
990
+ Object.defineProperty(exports, "pascalCase", {
991
+ enumerable: true,
992
+ get: function() {
993
+ return pascalCase;
994
+ }
995
+ });
996
+ Object.defineProperty(exports, "runtime", {
997
+ enumerable: true,
998
+ get: function() {
999
+ return runtime;
1000
+ }
1001
+ });
1002
+ Object.defineProperty(exports, "toFilePath", {
1003
+ enumerable: true,
1004
+ get: function() {
1005
+ return toFilePath;
1006
+ }
1007
+ });
1008
+ Object.defineProperty(exports, "toPosixPath", {
1009
+ enumerable: true,
1010
+ get: function() {
1011
+ return toPosixPath;
1012
+ }
1013
+ });
1014
+ Object.defineProperty(exports, "write", {
1015
+ enumerable: true,
1016
+ get: function() {
1017
+ return write;
1018
+ }
1019
+ });
1020
+
1021
+ //# sourceMappingURL=memoryStorage-DQ6qXJ8o.cjs.map