@kubb/core 5.0.0-beta.82 → 5.0.0-beta.85

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.
@@ -24,10 +24,37 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  enumerable: true
25
25
  }) : target, mod));
26
26
  //#endregion
27
- let node_events = require("node:events");
28
27
  let node_fs_promises = require("node:fs/promises");
29
28
  let node_path = require("node:path");
30
29
  let _kubb_ast = require("@kubb/ast");
30
+ let node_events = require("node:events");
31
+ //#region ../../internals/utils/src/casing.ts
32
+ /**
33
+ * Shared implementation for camelCase and PascalCase conversion.
34
+ * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
35
+ * and capitalizes each word according to `pascal`.
36
+ *
37
+ * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
38
+ */
39
+ function toCamelOrPascal(text, pascal) {
40
+ 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) => {
41
+ if (word.length > 1 && word === word.toUpperCase()) return word;
42
+ return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
43
+ }).join("").replace(/[^a-zA-Z0-9]/g, "");
44
+ }
45
+ /**
46
+ * Converts `text` to camelCase.
47
+ *
48
+ * @example Word boundaries
49
+ * `camelCase('hello-world') // 'helloWorld'`
50
+ *
51
+ * @example With a prefix
52
+ * `camelCase('tag', { prefix: 'create' }) // 'createTag'`
53
+ */
54
+ function camelCase(text, { prefix = "", suffix = "" } = {}) {
55
+ return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
56
+ }
57
+ //#endregion
31
58
  //#region ../../internals/utils/src/errors.ts
32
59
  /**
33
60
  * Thrown when one or more errors occur during a Kubb build.
@@ -73,152 +100,6 @@ function getErrorMessage(value) {
73
100
  return value instanceof Error ? value.message : String(value);
74
101
  }
75
102
  //#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
103
  //#region ../../internals/utils/src/runtime.ts
223
104
  /**
224
105
  * Detects the JavaScript runtime executing the current process and exposes its name and version.
@@ -393,151 +274,130 @@ function toFilePath(name, caseLast = camelCase) {
393
274
  return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
394
275
  }
395
276
  //#endregion
396
- //#region src/constants.ts
277
+ //#region src/asyncEventEmitter.ts
397
278
  /**
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.
279
+ * Typed `EventEmitter` that awaits all async listeners before resolving.
280
+ * Wraps Node's `EventEmitter` with full TypeScript event-map inference.
281
+ *
282
+ * @example
283
+ * ```ts
284
+ * const emitter = new AsyncEventEmitter<{ build: [name: string] }>()
285
+ * emitter.on('build', async (name) => { console.log(name) })
286
+ * await emitter.emit('build', 'petstore') // all listeners awaited
287
+ * ```
413
288
  */
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",
289
+ var AsyncEventEmitter = class {
466
290
  /**
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`.
291
+ * Maximum number of listeners per event before Node emits a memory-leak warning.
292
+ * @default 10
469
293
  */
470
- pathTraversal: "KUBB_PATH_TRAVERSAL",
294
+ constructor(maxListener = 10) {
295
+ this.#emitter.setMaxListeners(maxListener);
296
+ }
297
+ #emitter = new node_events.EventEmitter();
471
298
  /**
472
- * A plugin's options are invalid, for example `output.mode: 'file'` paired with a `group` option.
299
+ * Emits `eventName` and awaits all registered listeners sequentially.
300
+ * Throws if any listener rejects, wrapping the cause with the event name and serialized arguments.
301
+ *
302
+ * @example
303
+ * ```ts
304
+ * await emitter.emit('build', 'petstore')
305
+ * ```
473
306
  */
474
- invalidPluginOptions: "KUBB_INVALID_PLUGIN_OPTIONS",
307
+ emit(eventName, ...eventArgs) {
308
+ const listeners = this.#emitter.listeners(eventName);
309
+ if (listeners.length === 0) return;
310
+ return this.#emitAll(eventName, listeners, eventArgs);
311
+ }
312
+ async #emitAll(eventName, listeners, eventArgs) {
313
+ for (const listener of listeners) try {
314
+ await listener(...eventArgs);
315
+ } catch (err) {
316
+ let serializedArgs;
317
+ try {
318
+ serializedArgs = JSON.stringify(eventArgs);
319
+ } catch {
320
+ serializedArgs = String(eventArgs);
321
+ }
322
+ throw new Error(`Error in async listener for "${eventName}" with eventArgs ${serializedArgs}`, { cause: toError(err) });
323
+ }
324
+ }
475
325
  /**
476
- * A post-generate shell hook (`hooks.done`) exited with a failure.
326
+ * Registers a persistent listener for `eventName`.
327
+ *
328
+ * @example
329
+ * ```ts
330
+ * emitter.on('build', async (name) => { console.log(name) })
331
+ * ```
477
332
  */
478
- hookFailed: "KUBB_HOOK_FAILED",
333
+ on(eventName, handler) {
334
+ this.#emitter.on(eventName, handler);
335
+ }
479
336
  /**
480
- * The formatter pass over the generated files failed.
337
+ * Removes a previously registered listener.
338
+ *
339
+ * @example
340
+ * ```ts
341
+ * emitter.off('build', handler)
342
+ * ```
481
343
  */
482
- formatFailed: "KUBB_FORMAT_FAILED",
344
+ off(eventName, handler) {
345
+ this.#emitter.off(eventName, handler);
346
+ }
483
347
  /**
484
- * The linter pass over the generated files failed.
348
+ * Returns the number of listeners registered for `eventName`.
349
+ *
350
+ * @example
351
+ * ```ts
352
+ * emitter.on('build', handler)
353
+ * emitter.listenerCount('build') // 1
354
+ * ```
485
355
  */
486
- lintFailed: "KUBB_LINT_FAILED",
356
+ listenerCount(eventName) {
357
+ return this.#emitter.listenerCount(eventName);
358
+ }
487
359
  /**
488
- * Not a failure. Carries a plugin's elapsed time, summed into the run total.
360
+ * Raises or lowers the per-event listener ceiling before Node warns about a memory leak.
361
+ * Set this above the expected listener count when many listeners attach by design.
362
+ *
363
+ * @example
364
+ * ```ts
365
+ * emitter.setMaxListeners(40)
366
+ * ```
489
367
  */
490
- performance: "KUBB_PERFORMANCE",
368
+ setMaxListeners(max) {
369
+ this.#emitter.setMaxListeners(max);
370
+ }
491
371
  /**
492
- * Not a failure. A newer Kubb version is available on npm.
372
+ * Removes all listeners from every event channel.
373
+ *
374
+ * @example
375
+ * ```ts
376
+ * emitter.removeAll()
377
+ * ```
493
378
  */
494
- updateAvailable: "KUBB_UPDATE_AVAILABLE"
379
+ removeAll() {
380
+ this.#emitter.removeAllListeners();
381
+ }
495
382
  };
496
383
  //#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
384
  //#region src/FileManager.ts
385
+ function joinSources(file) {
386
+ return file.sources.map((source) => (0, _kubb_ast.extractStringsFromNodes)(source.nodes)).filter(Boolean).join("\n\n");
387
+ }
388
+ async function parseCopy(file) {
389
+ let content;
390
+ try {
391
+ content = await read(file.copy);
392
+ } catch (err) {
393
+ throw new Error(`[kubb] Could not copy file into output: ${file.copy}`, { cause: err });
394
+ }
395
+ return [
396
+ file.banner,
397
+ content,
398
+ file.footer
399
+ ].filter((segment) => Boolean(segment)).map((segment) => segment.trimEnd()).join("\n");
400
+ }
541
401
  function mergeFile(a, b) {
542
402
  return {
543
403
  ...a,
@@ -562,22 +422,20 @@ function compareFiles(a, b) {
562
422
  return 0;
563
423
  }
564
424
  /**
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).
425
+ * In-memory file store for generated files, and the writer that turns them into source
426
+ * strings on `storage`. Files sharing a `path` are merged (sources/imports/exports
427
+ * concatenated). The `files` getter is sorted by path length (barrel `index.ts` last
428
+ * within a bucket).
568
429
  *
569
430
  * @example
570
431
  * ```ts
571
432
  * const manager = new FileManager()
572
433
  * manager.upsert(myFile)
573
434
  * manager.files // sorted view
435
+ * await manager.write(manager.files, { storage: fsStorage() })
574
436
  * ```
575
437
  */
576
438
  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
439
  hooks = new AsyncEventEmitter();
582
440
  #cache = /* @__PURE__ */ new Map();
583
441
  #sorted = null;
@@ -595,7 +453,6 @@ var FileManager = class {
595
453
  const merged = existing && mergeExisting ? _kubb_ast.ast.factory.createFile(mergeFile(existing, file)) : _kubb_ast.ast.factory.createFile(file);
596
454
  this.#cache.set(merged.path, merged);
597
455
  resolved.push(merged);
598
- this.hooks.emit("upsert", merged);
599
456
  }
600
457
  if (resolved.length > 0) this.#sorted = null;
601
458
  return resolved;
@@ -608,13 +465,6 @@ var FileManager = class {
608
465
  }
609
466
  return [...seen.values()];
610
467
  }
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
468
  clear() {
619
469
  this.#cache.clear();
620
470
  this.#sorted = null;
@@ -627,9 +477,6 @@ var FileManager = class {
627
477
  this.clear();
628
478
  this.hooks.removeAll();
629
479
  }
630
- [Symbol.dispose]() {
631
- this.dispose();
632
- }
633
480
  /**
634
481
  * All stored files in stable sort order (shortest path first, barrel files
635
482
  * last within a length bucket). Returns a cached view, do not mutate.
@@ -637,163 +484,46 @@ var FileManager = class {
637
484
  get files() {
638
485
  return this.#sorted ??= [...this.#cache.values()].sort(compareFiles);
639
486
  }
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
487
  /**
693
- * Files waiting in the queue.
488
+ * Converts a file's AST sources (or its `copy` source) into the final on-disk string.
694
489
  */
695
- get size() {
696
- return this.#pending.size;
697
- }
698
- async parse(file) {
490
+ async parse(file, { parsers, extension } = {}) {
699
491
  if (file.copy) return parseCopy(file);
700
- const parsers = this.#parsers;
701
- const parseExtName = this.#extension?.[file.extname] || void 0;
492
+ const parseExtName = extension?.[file.extname] || void 0;
702
493
  if (!parsers || !file.extname) return joinSources(file);
703
494
  const parser = parsers.get(file.extname);
704
495
  if (!parser) return joinSources(file);
705
496
  return parser.parse(file, { extname: parseExtName });
706
497
  }
707
- async *stream(files) {
498
+ /**
499
+ * Converts and writes every file at once, letting `storage.setItem` decide how much of
500
+ * that runs concurrently.
501
+ */
502
+ async write(files, { storage, parsers, extension }) {
503
+ if (files.length === 0) return;
504
+ await this.hooks.emit("start", files);
708
505
  const total = files.length;
709
- if (total === 0) return;
710
506
  let processed = 0;
711
- for (const file of files) {
712
- const source = await this.parse(file);
507
+ await Promise.all(files.map(async (file) => {
508
+ const source = await this.parse(file, {
509
+ parsers,
510
+ extension
511
+ });
713
512
  processed++;
714
- yield {
513
+ await this.hooks.emit("update", {
715
514
  file,
716
515
  source,
717
516
  processed,
718
517
  total,
719
518
  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);
519
+ });
520
+ if (source) await storage.setItem(file.path, source);
521
+ }));
782
522
  await this.hooks.emit("end", files);
783
523
  }
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
524
  };
795
525
  //#endregion
796
- //#region \0@oxc-project+runtime@0.137.0/helpers/esm/usingCtx.js
526
+ //#region \0@oxc-project+runtime@0.138.0/helpers/esm/usingCtx.js
797
527
  function _usingCtx() {
798
528
  var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
799
529
  var n = Error();
@@ -853,56 +583,6 @@ function _usingCtx() {
853
583
  };
854
584
  }
855
585
  //#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
586
  Object.defineProperty(exports, "AsyncEventEmitter", {
907
587
  enumerable: true,
908
588
  get: function() {
@@ -921,18 +601,6 @@ Object.defineProperty(exports, "FileManager", {
921
601
  return FileManager;
922
602
  }
923
603
  });
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
604
  Object.defineProperty(exports, "__name", {
937
605
  enumerable: true,
938
606
  get: function() {
@@ -963,42 +631,12 @@ Object.defineProperty(exports, "clean", {
963
631
  return clean;
964
632
  }
965
633
  });
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
634
  Object.defineProperty(exports, "getErrorMessage", {
979
635
  enumerable: true,
980
636
  get: function() {
981
637
  return getErrorMessage;
982
638
  }
983
639
  });
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
640
  Object.defineProperty(exports, "toFilePath", {
1003
641
  enumerable: true,
1004
642
  get: function() {
@@ -1018,4 +656,4 @@ Object.defineProperty(exports, "write", {
1018
656
  }
1019
657
  });
1020
658
 
1021
- //# sourceMappingURL=memoryStorage-DQ6qXJ8o.cjs.map
659
+ //# sourceMappingURL=usingCtx-fxRpJw0P.cjs.map