@kubb/core 5.0.0-beta.84 → 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.
@@ -1,8 +1,35 @@
1
1
  import "./rolldown-runtime-C0LytTxp.js";
2
- import { EventEmitter } from "node:events";
3
2
  import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
4
3
  import { dirname, resolve } from "node:path";
5
4
  import { ast, extractStringsFromNodes } from "@kubb/ast";
5
+ import { EventEmitter } from "node:events";
6
+ //#region ../../internals/utils/src/casing.ts
7
+ /**
8
+ * Shared implementation for camelCase and PascalCase conversion.
9
+ * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
10
+ * and capitalizes each word according to `pascal`.
11
+ *
12
+ * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
13
+ */
14
+ function toCamelOrPascal(text, pascal) {
15
+ 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) => {
16
+ if (word.length > 1 && word === word.toUpperCase()) return word;
17
+ return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
18
+ }).join("").replace(/[^a-zA-Z0-9]/g, "");
19
+ }
20
+ /**
21
+ * Converts `text` to camelCase.
22
+ *
23
+ * @example Word boundaries
24
+ * `camelCase('hello-world') // 'helloWorld'`
25
+ *
26
+ * @example With a prefix
27
+ * `camelCase('tag', { prefix: 'create' }) // 'createTag'`
28
+ */
29
+ function camelCase(text, { prefix = "", suffix = "" } = {}) {
30
+ return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
31
+ }
32
+ //#endregion
6
33
  //#region ../../internals/utils/src/errors.ts
7
34
  /**
8
35
  * Thrown when one or more errors occur during a Kubb build.
@@ -48,152 +75,6 @@ function getErrorMessage(value) {
48
75
  return value instanceof Error ? value.message : String(value);
49
76
  }
50
77
  //#endregion
51
- //#region ../../internals/utils/src/asyncEventEmitter.ts
52
- /**
53
- * Typed `EventEmitter` that awaits all async listeners before resolving.
54
- * Wraps Node's `EventEmitter` with full TypeScript event-map inference.
55
- *
56
- * @example
57
- * ```ts
58
- * const emitter = new AsyncEventEmitter<{ build: [name: string] }>()
59
- * emitter.on('build', async (name) => { console.log(name) })
60
- * await emitter.emit('build', 'petstore') // all listeners awaited
61
- * ```
62
- */
63
- var AsyncEventEmitter = class {
64
- /**
65
- * Maximum number of listeners per event before Node emits a memory-leak warning.
66
- * @default 10
67
- */
68
- constructor(maxListener = 10) {
69
- this.#emitter.setMaxListeners(maxListener);
70
- }
71
- #emitter = new EventEmitter();
72
- /**
73
- * Emits `eventName` and awaits all registered listeners sequentially.
74
- * Throws if any listener rejects, wrapping the cause with the event name and serialized arguments.
75
- *
76
- * @example
77
- * ```ts
78
- * await emitter.emit('build', 'petstore')
79
- * ```
80
- */
81
- emit(eventName, ...eventArgs) {
82
- const listeners = this.#emitter.listeners(eventName);
83
- if (listeners.length === 0) return;
84
- return this.#emitAll(eventName, listeners, eventArgs);
85
- }
86
- async #emitAll(eventName, listeners, eventArgs) {
87
- for (const listener of listeners) try {
88
- await listener(...eventArgs);
89
- } catch (err) {
90
- let serializedArgs;
91
- try {
92
- serializedArgs = JSON.stringify(eventArgs);
93
- } catch {
94
- serializedArgs = String(eventArgs);
95
- }
96
- throw new Error(`Error in async listener for "${eventName}" with eventArgs ${serializedArgs}`, { cause: toError(err) });
97
- }
98
- }
99
- /**
100
- * Registers a persistent listener for `eventName`.
101
- *
102
- * @example
103
- * ```ts
104
- * emitter.on('build', async (name) => { console.log(name) })
105
- * ```
106
- */
107
- on(eventName, handler) {
108
- this.#emitter.on(eventName, handler);
109
- }
110
- /**
111
- * Removes a previously registered listener.
112
- *
113
- * @example
114
- * ```ts
115
- * emitter.off('build', handler)
116
- * ```
117
- */
118
- off(eventName, handler) {
119
- this.#emitter.off(eventName, handler);
120
- }
121
- /**
122
- * Returns the number of listeners registered for `eventName`.
123
- *
124
- * @example
125
- * ```ts
126
- * emitter.on('build', handler)
127
- * emitter.listenerCount('build') // 1
128
- * ```
129
- */
130
- listenerCount(eventName) {
131
- return this.#emitter.listenerCount(eventName);
132
- }
133
- /**
134
- * Raises or lowers the per-event listener ceiling before Node warns about a memory leak.
135
- * Set this above the expected listener count when many listeners attach by design.
136
- *
137
- * @example
138
- * ```ts
139
- * emitter.setMaxListeners(40)
140
- * ```
141
- */
142
- setMaxListeners(max) {
143
- this.#emitter.setMaxListeners(max);
144
- }
145
- /**
146
- * Removes all listeners from every event channel.
147
- *
148
- * @example
149
- * ```ts
150
- * emitter.removeAll()
151
- * ```
152
- */
153
- removeAll() {
154
- this.#emitter.removeAllListeners();
155
- }
156
- };
157
- //#endregion
158
- //#region ../../internals/utils/src/casing.ts
159
- /**
160
- * Shared implementation for camelCase and PascalCase conversion.
161
- * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
162
- * and capitalizes each word according to `pascal`.
163
- *
164
- * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
165
- */
166
- function toCamelOrPascal(text, pascal) {
167
- 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) => {
168
- if (word.length > 1 && word === word.toUpperCase()) return word;
169
- return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
170
- }).join("").replace(/[^a-zA-Z0-9]/g, "");
171
- }
172
- /**
173
- * Converts `text` to camelCase.
174
- *
175
- * @example Word boundaries
176
- * `camelCase('hello-world') // 'helloWorld'`
177
- *
178
- * @example With a prefix
179
- * `camelCase('tag', { prefix: 'create' }) // 'createTag'`
180
- */
181
- function camelCase(text, { prefix = "", suffix = "" } = {}) {
182
- return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
183
- }
184
- /**
185
- * Converts `text` to PascalCase.
186
- *
187
- * @example Word boundaries
188
- * `pascalCase('hello-world') // 'HelloWorld'`
189
- *
190
- * @example With a suffix
191
- * `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`
192
- */
193
- function pascalCase(text, { prefix = "", suffix = "" } = {}) {
194
- return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
195
- }
196
- //#endregion
197
78
  //#region ../../internals/utils/src/runtime.ts
198
79
  /**
199
80
  * Detects the JavaScript runtime executing the current process and exposes its name and version.
@@ -368,151 +249,130 @@ function toFilePath(name, caseLast = camelCase) {
368
249
  return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
369
250
  }
370
251
  //#endregion
371
- //#region src/constants.ts
252
+ //#region src/asyncEventEmitter.ts
372
253
  /**
373
- * Plugin `include` filter types that select operations directly. When one of these is set
374
- * without a `schemaName` include, the generate phase pre-scans operations to compute the set
375
- * of schemas they reach, so unreachable schemas can be pruned for that plugin.
376
- */
377
- const OPERATION_FILTER_TYPES = /* @__PURE__ */ new Set([
378
- "tag",
379
- "operationId",
380
- "path",
381
- "method",
382
- "contentType"
383
- ]);
384
- /**
385
- * Stable codes Kubb attaches to a `Diagnostic`. Each maps to a known failure mode
386
- * and stays stable so it can be referenced in tooling and (later) docs. Reference
387
- * these instead of inlining the string at a throw site.
254
+ * Typed `EventEmitter` that awaits all async listeners before resolving.
255
+ * Wraps Node's `EventEmitter` with full TypeScript event-map inference.
256
+ *
257
+ * @example
258
+ * ```ts
259
+ * const emitter = new AsyncEventEmitter<{ build: [name: string] }>()
260
+ * emitter.on('build', async (name) => { console.log(name) })
261
+ * await emitter.emit('build', 'petstore') // all listeners awaited
262
+ * ```
388
263
  */
389
- const diagnosticCode = {
390
- /**
391
- * Fallback for an unstructured error with no specific code.
392
- */
393
- unknown: "KUBB_UNKNOWN",
394
- /**
395
- * The `input.path` file or URL could not be read.
396
- */
397
- inputNotFound: "KUBB_INPUT_NOT_FOUND",
398
- /**
399
- * An adapter was configured without an `input`.
400
- */
401
- inputRequired: "KUBB_INPUT_REQUIRED",
402
- /**
403
- * A `$ref` (or equivalent reference) could not be resolved in the source document.
404
- */
405
- refNotFound: "KUBB_REF_NOT_FOUND",
406
- /**
407
- * A server variable value is not allowed by its `enum`.
408
- */
409
- invalidServerVariable: "KUBB_INVALID_SERVER_VARIABLE",
410
- /**
411
- * A required plugin is missing from the config.
412
- */
413
- pluginNotFound: "KUBB_PLUGIN_NOT_FOUND",
414
- /**
415
- * A plugin threw while generating.
416
- */
417
- pluginFailed: "KUBB_PLUGIN_FAILED",
418
- /**
419
- * A plugin reported a non-fatal warning through `ctx.warn`.
420
- */
421
- pluginWarning: "KUBB_PLUGIN_WARNING",
422
- /**
423
- * A plugin reported an informational message through `ctx.info`.
424
- */
425
- pluginInfo: "KUBB_PLUGIN_INFO",
426
- /**
427
- * A schema uses a `format` Kubb does not map to a specific type. Reserved for
428
- * adapters to emit as a `warning`.
429
- */
430
- unsupportedFormat: "KUBB_UNSUPPORTED_FORMAT",
431
- /**
432
- * A referenced schema or operation is marked `deprecated`. Reserved for adapters
433
- * to emit as an `info`.
434
- */
435
- deprecated: "KUBB_DEPRECATED",
436
- /**
437
- * An adapter is required but the config has none. The build cannot read the input
438
- * without one.
439
- */
440
- adapterRequired: "KUBB_ADAPTER_REQUIRED",
264
+ var AsyncEventEmitter = class {
441
265
  /**
442
- * A resolved output path escapes the output directory, which can stem from a path
443
- * traversal in the spec or a misconfigured `group.name`.
266
+ * Maximum number of listeners per event before Node emits a memory-leak warning.
267
+ * @default 10
444
268
  */
445
- pathTraversal: "KUBB_PATH_TRAVERSAL",
269
+ constructor(maxListener = 10) {
270
+ this.#emitter.setMaxListeners(maxListener);
271
+ }
272
+ #emitter = new EventEmitter();
446
273
  /**
447
- * A plugin's options are invalid, for example `output.mode: 'file'` paired with a `group` option.
274
+ * Emits `eventName` and awaits all registered listeners sequentially.
275
+ * Throws if any listener rejects, wrapping the cause with the event name and serialized arguments.
276
+ *
277
+ * @example
278
+ * ```ts
279
+ * await emitter.emit('build', 'petstore')
280
+ * ```
448
281
  */
449
- invalidPluginOptions: "KUBB_INVALID_PLUGIN_OPTIONS",
282
+ emit(eventName, ...eventArgs) {
283
+ const listeners = this.#emitter.listeners(eventName);
284
+ if (listeners.length === 0) return;
285
+ return this.#emitAll(eventName, listeners, eventArgs);
286
+ }
287
+ async #emitAll(eventName, listeners, eventArgs) {
288
+ for (const listener of listeners) try {
289
+ await listener(...eventArgs);
290
+ } catch (err) {
291
+ let serializedArgs;
292
+ try {
293
+ serializedArgs = JSON.stringify(eventArgs);
294
+ } catch {
295
+ serializedArgs = String(eventArgs);
296
+ }
297
+ throw new Error(`Error in async listener for "${eventName}" with eventArgs ${serializedArgs}`, { cause: toError(err) });
298
+ }
299
+ }
450
300
  /**
451
- * A post-generate shell hook (`hooks.done`) exited with a failure.
301
+ * Registers a persistent listener for `eventName`.
302
+ *
303
+ * @example
304
+ * ```ts
305
+ * emitter.on('build', async (name) => { console.log(name) })
306
+ * ```
452
307
  */
453
- hookFailed: "KUBB_HOOK_FAILED",
308
+ on(eventName, handler) {
309
+ this.#emitter.on(eventName, handler);
310
+ }
454
311
  /**
455
- * The formatter pass over the generated files failed.
312
+ * Removes a previously registered listener.
313
+ *
314
+ * @example
315
+ * ```ts
316
+ * emitter.off('build', handler)
317
+ * ```
456
318
  */
457
- formatFailed: "KUBB_FORMAT_FAILED",
319
+ off(eventName, handler) {
320
+ this.#emitter.off(eventName, handler);
321
+ }
458
322
  /**
459
- * The linter pass over the generated files failed.
323
+ * Returns the number of listeners registered for `eventName`.
324
+ *
325
+ * @example
326
+ * ```ts
327
+ * emitter.on('build', handler)
328
+ * emitter.listenerCount('build') // 1
329
+ * ```
460
330
  */
461
- lintFailed: "KUBB_LINT_FAILED",
331
+ listenerCount(eventName) {
332
+ return this.#emitter.listenerCount(eventName);
333
+ }
462
334
  /**
463
- * Not a failure. Carries a plugin's elapsed time, summed into the run total.
335
+ * Raises or lowers the per-event listener ceiling before Node warns about a memory leak.
336
+ * Set this above the expected listener count when many listeners attach by design.
337
+ *
338
+ * @example
339
+ * ```ts
340
+ * emitter.setMaxListeners(40)
341
+ * ```
464
342
  */
465
- performance: "KUBB_PERFORMANCE",
343
+ setMaxListeners(max) {
344
+ this.#emitter.setMaxListeners(max);
345
+ }
466
346
  /**
467
- * Not a failure. A newer Kubb version is available on npm.
347
+ * Removes all listeners from every event channel.
348
+ *
349
+ * @example
350
+ * ```ts
351
+ * emitter.removeAll()
352
+ * ```
468
353
  */
469
- updateAvailable: "KUBB_UPDATE_AVAILABLE"
354
+ removeAll() {
355
+ this.#emitter.removeAllListeners();
356
+ }
470
357
  };
471
358
  //#endregion
472
- //#region src/createStorage.ts
473
- /**
474
- * Defines a custom storage backend. The builder receives user options and
475
- * returns a `Storage` implementation. Kubb ships with filesystem and in-memory
476
- * storages. A custom backend writes generated files elsewhere, such as cloud
477
- * storage or a database.
478
- *
479
- * @example In-memory storage (the built-in implementation)
480
- * ```ts
481
- * import { createStorage } from '@kubb/core'
482
- *
483
- * export const memoryStorage = createStorage(() => {
484
- * const store = new Map<string, string>()
485
- *
486
- * return {
487
- * name: 'memory',
488
- * async hasItem(key) {
489
- * return store.has(key)
490
- * },
491
- * async getItem(key) {
492
- * return store.get(key) ?? null
493
- * },
494
- * async setItem(key, value) {
495
- * store.set(key, value)
496
- * },
497
- * async removeItem(key) {
498
- * store.delete(key)
499
- * },
500
- * async getKeys(base) {
501
- * const keys = [...store.keys()]
502
- * return base ? keys.filter((k) => k.startsWith(base)) : keys
503
- * },
504
- * async clear(base) {
505
- * if (!base) store.clear()
506
- * },
507
- * }
508
- * })
509
- * ```
510
- */
511
- function createStorage(build) {
512
- return (options) => build(options ?? {});
513
- }
514
- //#endregion
515
359
  //#region src/FileManager.ts
360
+ function joinSources(file) {
361
+ return file.sources.map((source) => extractStringsFromNodes(source.nodes)).filter(Boolean).join("\n\n");
362
+ }
363
+ async function parseCopy(file) {
364
+ let content;
365
+ try {
366
+ content = await read(file.copy);
367
+ } catch (err) {
368
+ throw new Error(`[kubb] Could not copy file into output: ${file.copy}`, { cause: err });
369
+ }
370
+ return [
371
+ file.banner,
372
+ content,
373
+ file.footer
374
+ ].filter((segment) => Boolean(segment)).map((segment) => segment.trimEnd()).join("\n");
375
+ }
516
376
  function mergeFile(a, b) {
517
377
  return {
518
378
  ...a,
@@ -537,22 +397,20 @@ function compareFiles(a, b) {
537
397
  return 0;
538
398
  }
539
399
  /**
540
- * In-memory file store for generated files. Files sharing a `path` are merged
541
- * (sources/imports/exports concatenated). The `files` getter is sorted by
542
- * path length (barrel `index.ts` last within a bucket).
400
+ * In-memory file store for generated files, and the writer that turns them into source
401
+ * strings on `storage`. Files sharing a `path` are merged (sources/imports/exports
402
+ * concatenated). The `files` getter is sorted by path length (barrel `index.ts` last
403
+ * within a bucket).
543
404
  *
544
405
  * @example
545
406
  * ```ts
546
407
  * const manager = new FileManager()
547
408
  * manager.upsert(myFile)
548
409
  * manager.files // sorted view
410
+ * await manager.write(manager.files, { storage: fsStorage() })
549
411
  * ```
550
412
  */
551
413
  var FileManager = class {
552
- /**
553
- * Subscribe to file-store changes. Listeners on `upsert` see each resolved file as it lands
554
- * through `add` or `upsert`.
555
- */
556
414
  hooks = new AsyncEventEmitter();
557
415
  #cache = /* @__PURE__ */ new Map();
558
416
  #sorted = null;
@@ -570,7 +428,6 @@ var FileManager = class {
570
428
  const merged = existing && mergeExisting ? ast.factory.createFile(mergeFile(existing, file)) : ast.factory.createFile(file);
571
429
  this.#cache.set(merged.path, merged);
572
430
  resolved.push(merged);
573
- this.hooks.emit("upsert", merged);
574
431
  }
575
432
  if (resolved.length > 0) this.#sorted = null;
576
433
  return resolved;
@@ -583,13 +440,6 @@ var FileManager = class {
583
440
  }
584
441
  return [...seen.values()];
585
442
  }
586
- getByPath(path) {
587
- return this.#cache.get(path) ?? null;
588
- }
589
- deleteByPath(path) {
590
- if (!this.#cache.delete(path)) return;
591
- this.#sorted = null;
592
- }
593
443
  clear() {
594
444
  this.#cache.clear();
595
445
  this.#sorted = null;
@@ -602,9 +452,6 @@ var FileManager = class {
602
452
  this.clear();
603
453
  this.hooks.removeAll();
604
454
  }
605
- [Symbol.dispose]() {
606
- this.dispose();
607
- }
608
455
  /**
609
456
  * All stored files in stable sort order (shortest path first, barrel files
610
457
  * last within a length bucket). Returns a cached view, do not mutate.
@@ -612,163 +459,46 @@ var FileManager = class {
612
459
  get files() {
613
460
  return this.#sorted ??= [...this.#cache.values()].sort(compareFiles);
614
461
  }
615
- };
616
- //#endregion
617
- //#region src/FileProcessor.ts
618
- function joinSources(file) {
619
- const sources = file.sources;
620
- if (sources.length === 0) return "";
621
- const parts = [];
622
- for (const source of sources) {
623
- const text = extractStringsFromNodes(source.nodes);
624
- if (text) parts.push(text);
625
- }
626
- return parts.join("\n\n");
627
- }
628
- async function parseCopy(file) {
629
- let content;
630
- try {
631
- content = await read(file.copy);
632
- } catch (err) {
633
- throw new Error(`[kubb] Could not copy file into output: ${file.copy}`, { cause: err });
634
- }
635
- return [
636
- file.banner,
637
- content,
638
- file.footer
639
- ].filter((segment) => Boolean(segment)).map((segment) => segment.trimEnd()).join("\n");
640
- }
641
- /**
642
- * Turns `FileNode`s into source strings and writes them to storage.
643
- *
644
- * Two modes share the same instance. Stateless mode (`parse`, `stream`, `run`) just runs the
645
- * conversion. Queue mode (`enqueue`, `flush`, `drain`) buffers files deduped by path and
646
- * writes each batch through storage with up to `STREAM_FLUSH_EVERY` requests in flight.
647
- *
648
- * `flush` does not wait for its batch to finish, so dispatch can overlap with IO. The next
649
- * `flush` or `drain` picks the in-flight batch up. `drain` blocks until everything has been
650
- * written and is meant for the end of a build.
651
- *
652
- * To surface build-level hook signals (`kubb:files:processing:*` and friends) subscribe to
653
- * `hooks` and re-emit on the kubb bus.
654
- */
655
- var FileProcessor = class {
656
- hooks = new AsyncEventEmitter();
657
- #parsers;
658
- #storage;
659
- #extension;
660
- #pending = /* @__PURE__ */ new Map();
661
- #runningFlush = null;
662
- constructor(options) {
663
- this.#parsers = options.parsers ?? null;
664
- this.#storage = options.storage;
665
- this.#extension = options.extension ?? null;
666
- }
667
462
  /**
668
- * Files waiting in the queue.
463
+ * Converts a file's AST sources (or its `copy` source) into the final on-disk string.
669
464
  */
670
- get size() {
671
- return this.#pending.size;
672
- }
673
- async parse(file) {
465
+ async parse(file, { parsers, extension } = {}) {
674
466
  if (file.copy) return parseCopy(file);
675
- const parsers = this.#parsers;
676
- const parseExtName = this.#extension?.[file.extname] || void 0;
467
+ const parseExtName = extension?.[file.extname] || void 0;
677
468
  if (!parsers || !file.extname) return joinSources(file);
678
469
  const parser = parsers.get(file.extname);
679
470
  if (!parser) return joinSources(file);
680
471
  return parser.parse(file, { extname: parseExtName });
681
472
  }
682
- async *stream(files) {
473
+ /**
474
+ * Converts and writes every file at once, letting `storage.setItem` decide how much of
475
+ * that runs concurrently.
476
+ */
477
+ async write(files, { storage, parsers, extension }) {
478
+ if (files.length === 0) return;
479
+ await this.hooks.emit("start", files);
683
480
  const total = files.length;
684
- if (total === 0) return;
685
481
  let processed = 0;
686
- for (const file of files) {
687
- const source = await this.parse(file);
482
+ await Promise.all(files.map(async (file) => {
483
+ const source = await this.parse(file, {
484
+ parsers,
485
+ extension
486
+ });
688
487
  processed++;
689
- yield {
488
+ await this.hooks.emit("update", {
690
489
  file,
691
490
  source,
692
491
  processed,
693
492
  total,
694
493
  percentage: processed / total * 100
695
- };
696
- }
697
- }
698
- async run(files) {
699
- await this.hooks.emit("start", files);
700
- for await (const { file, source, processed, total, percentage } of this.stream(files)) await this.hooks.emit("update", {
701
- file,
702
- source,
703
- processed,
704
- percentage,
705
- total
706
- });
707
- await this.hooks.emit("end", files);
708
- return files;
709
- }
710
- /**
711
- * Adds a file to the next flush. A later `enqueue` for the same path replaces the previous
712
- * entry, matching `FileManager.upsert`. Fires the `enqueue` event.
713
- */
714
- enqueue(file) {
715
- this.#pending.set(file.path, file);
716
- this.hooks.emit("enqueue", file);
717
- }
718
- /**
719
- * Starts processing the queued files. Waits for any previous flush to finish (so two
720
- * batches never run together) and then returns without waiting for the new one. The next
721
- * `flush` or `drain` picks up the in-flight task.
722
- */
723
- async flush() {
724
- if (this.#runningFlush) await this.#runningFlush;
725
- if (this.#pending.size === 0) return;
726
- const batch = [...this.#pending.values()];
727
- this.#pending.clear();
728
- this.#runningFlush = this.#processAndWrite(batch).finally(() => {
729
- this.#runningFlush = null;
730
- });
731
- }
732
- /**
733
- * Waits for the in-flight flush and writes any files still queued. Fires the `drain` event
734
- * when both are done.
735
- */
736
- async drain() {
737
- if (this.#runningFlush) await this.#runningFlush;
738
- if (this.#pending.size > 0) {
739
- const batch = [...this.#pending.values()];
740
- this.#pending.clear();
741
- await this.#processAndWrite(batch);
742
- }
743
- await this.hooks.emit("drain");
744
- }
745
- async #processAndWrite(files) {
746
- const storage = this.#storage;
747
- await this.hooks.emit("start", files);
748
- const queue = [];
749
- for await (const item of this.stream(files)) {
750
- await this.hooks.emit("update", item);
751
- if (item.source) {
752
- queue.push(storage.setItem(item.file.path, item.source));
753
- if (queue.length >= 50) await Promise.all(queue.splice(0));
754
- }
755
- }
756
- await Promise.all(queue);
494
+ });
495
+ if (source) await storage.setItem(file.path, source);
496
+ }));
757
497
  await this.hooks.emit("end", files);
758
498
  }
759
- /**
760
- * Clears every listener and the pending queue.
761
- */
762
- dispose() {
763
- this.hooks.removeAll();
764
- this.#pending.clear();
765
- }
766
- [Symbol.dispose]() {
767
- this.dispose();
768
- }
769
499
  };
770
500
  //#endregion
771
- //#region \0@oxc-project+runtime@0.137.0/helpers/esm/usingCtx.js
501
+ //#region \0@oxc-project+runtime@0.138.0/helpers/esm/usingCtx.js
772
502
  function _usingCtx() {
773
503
  var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
774
504
  var n = Error();
@@ -828,56 +558,6 @@ function _usingCtx() {
828
558
  };
829
559
  }
830
560
  //#endregion
831
- //#region src/storages/memoryStorage.ts
832
- /**
833
- * In-memory storage driver. Useful for testing and dry-run scenarios where
834
- * generated output should be captured without touching the filesystem.
835
- *
836
- * All data lives in a `Map` scoped to the storage instance and is discarded
837
- * when the instance is garbage-collected.
838
- *
839
- * @example
840
- * ```ts
841
- * import { memoryStorage } from '@kubb/core'
842
- * import { defineConfig } from 'kubb'
843
- *
844
- * export default defineConfig({
845
- * input: { path: './petStore.yaml' },
846
- * output: { path: './src/gen' },
847
- * storage: memoryStorage(),
848
- * })
849
- * ```
850
- */
851
- const memoryStorage = createStorage(() => {
852
- const store = /* @__PURE__ */ new Map();
853
- return {
854
- name: "memory",
855
- async hasItem(key) {
856
- return store.has(key);
857
- },
858
- async getItem(key) {
859
- return store.get(key) ?? null;
860
- },
861
- async setItem(key, value) {
862
- store.set(key, value);
863
- },
864
- async removeItem(key) {
865
- store.delete(key);
866
- },
867
- async getKeys(base) {
868
- const keys = [...store.keys()];
869
- return base ? keys.filter((k) => k.startsWith(base)) : keys;
870
- },
871
- async clear(base) {
872
- if (!base) {
873
- store.clear();
874
- return;
875
- }
876
- for (const key of store.keys()) if (key.startsWith(base)) store.delete(key);
877
- }
878
- };
879
- });
880
- //#endregion
881
- export { getErrorMessage as _, createStorage as a, clean as c, write as d, runtime as f, BuildError as g, AsyncEventEmitter as h, FileManager as i, toFilePath as l, pascalCase as m, _usingCtx as n, OPERATION_FILTER_TYPES as o, camelCase as p, FileProcessor as r, diagnosticCode as s, memoryStorage as t, toPosixPath as u };
561
+ export { toFilePath as a, BuildError as c, clean as i, getErrorMessage as l, FileManager as n, toPosixPath as o, AsyncEventEmitter as r, write as s, _usingCtx as t, camelCase as u };
882
562
 
883
- //# sourceMappingURL=memoryStorage-BjUIqpYE.js.map
563
+ //# sourceMappingURL=usingCtx-BABTfo1g.js.map