@kubb/core 5.0.0-beta.20 → 5.0.0-beta.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,187 +1,10 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_KubbDriver = require("./KubbDriver-BXSnJ3qM.cjs");
3
- let node_events = require("node:events");
2
+ const require_KubbDriver = require("./KubbDriver-BBRa5CH2.cjs");
4
3
  let node_fs_promises = require("node:fs/promises");
5
4
  let node_path = require("node:path");
6
5
  let _kubb_ast = require("@kubb/ast");
7
6
  _kubb_ast = require_KubbDriver.__toESM(_kubb_ast, 1);
8
7
  let node_process = require("node:process");
9
- //#region ../../internals/utils/src/errors.ts
10
- /**
11
- * Thrown when one or more errors occur during a Kubb build.
12
- * Carries the full list of underlying errors on `errors`.
13
- *
14
- * @example
15
- * ```ts
16
- * throw new BuildError('Build failed', { errors: [err1, err2] })
17
- * ```
18
- */
19
- var BuildError = class extends Error {
20
- errors;
21
- constructor(message, options) {
22
- super(message, { cause: options.cause });
23
- this.name = "BuildError";
24
- this.errors = options.errors;
25
- }
26
- };
27
- /**
28
- * Coerces an unknown thrown value to an `Error` instance.
29
- * Returns the value as-is when it is already an `Error`; otherwise wraps it with `String(value)`.
30
- *
31
- * @example
32
- * ```ts
33
- * try { ... } catch(err) {
34
- * throw new BuildError('Build failed', { cause: toError(err), errors: [] })
35
- * }
36
- * ```
37
- */
38
- function toError(value) {
39
- return value instanceof Error ? value : new Error(String(value));
40
- }
41
- //#endregion
42
- //#region ../../internals/utils/src/asyncEventEmitter.ts
43
- /**
44
- * Typed `EventEmitter` that awaits all async listeners before resolving.
45
- * Wraps Node's `EventEmitter` with full TypeScript event-map inference.
46
- *
47
- * @example
48
- * ```ts
49
- * const emitter = new AsyncEventEmitter<{ build: [name: string] }>()
50
- * emitter.on('build', async (name) => { console.log(name) })
51
- * await emitter.emit('build', 'petstore') // all listeners awaited
52
- * ```
53
- */
54
- var AsyncEventEmitter = class {
55
- /**
56
- * Maximum number of listeners per event before Node emits a memory-leak warning.
57
- * @default 10
58
- */
59
- constructor(maxListener = 10) {
60
- this.#emitter.setMaxListeners(maxListener);
61
- }
62
- #emitter = new node_events.EventEmitter();
63
- /**
64
- * Emits `eventName` and awaits all registered listeners sequentially.
65
- * Throws if any listener rejects, wrapping the cause with the event name and serialized arguments.
66
- *
67
- * @example
68
- * ```ts
69
- * await emitter.emit('build', 'petstore')
70
- * ```
71
- */
72
- emit(eventName, ...eventArgs) {
73
- const listeners = this.#emitter.listeners(eventName);
74
- if (listeners.length === 0) return;
75
- return this.#emitAll(eventName, listeners, eventArgs);
76
- }
77
- async #emitAll(eventName, listeners, eventArgs) {
78
- for (const listener of listeners) try {
79
- await listener(...eventArgs);
80
- } catch (err) {
81
- let serializedArgs;
82
- try {
83
- serializedArgs = JSON.stringify(eventArgs);
84
- } catch {
85
- serializedArgs = String(eventArgs);
86
- }
87
- throw new Error(`Error in async listener for "${eventName}" with eventArgs ${serializedArgs}`, { cause: toError(err) });
88
- }
89
- }
90
- /**
91
- * Registers a persistent listener for `eventName`.
92
- *
93
- * @example
94
- * ```ts
95
- * emitter.on('build', async (name) => { console.log(name) })
96
- * ```
97
- */
98
- on(eventName, handler) {
99
- this.#emitter.on(eventName, handler);
100
- }
101
- /**
102
- * Registers a one-shot listener that removes itself after the first invocation.
103
- *
104
- * @example
105
- * ```ts
106
- * emitter.onOnce('build', async (name) => { console.log(name) })
107
- * ```
108
- */
109
- onOnce(eventName, handler) {
110
- const wrapper = (...args) => {
111
- this.off(eventName, wrapper);
112
- return handler(...args);
113
- };
114
- this.on(eventName, wrapper);
115
- }
116
- /**
117
- * Removes a previously registered listener.
118
- *
119
- * @example
120
- * ```ts
121
- * emitter.off('build', handler)
122
- * ```
123
- */
124
- off(eventName, handler) {
125
- this.#emitter.off(eventName, handler);
126
- }
127
- /**
128
- * Returns the number of listeners registered for `eventName`.
129
- *
130
- * @example
131
- * ```ts
132
- * emitter.on('build', handler)
133
- * emitter.listenerCount('build') // 1
134
- * ```
135
- */
136
- listenerCount(eventName) {
137
- return this.#emitter.listenerCount(eventName);
138
- }
139
- /**
140
- * Removes all listeners from every event channel.
141
- *
142
- * @example
143
- * ```ts
144
- * emitter.removeAll()
145
- * ```
146
- */
147
- removeAll() {
148
- this.#emitter.removeAllListeners();
149
- }
150
- };
151
- //#endregion
152
- //#region ../../internals/utils/src/time.ts
153
- /**
154
- * Calculates elapsed time in milliseconds from a high-resolution `process.hrtime` start time.
155
- * Rounds to 2 decimal places for sub-millisecond precision without noise.
156
- *
157
- * @example
158
- * ```ts
159
- * const start = process.hrtime()
160
- * doWork()
161
- * getElapsedMs(start) // 42.35
162
- * ```
163
- */
164
- function getElapsedMs(hrStart) {
165
- const [seconds, nanoseconds] = process.hrtime(hrStart);
166
- const ms = seconds * 1e3 + nanoseconds / 1e6;
167
- return Math.round(ms * 100) / 100;
168
- }
169
- /**
170
- * Converts a millisecond duration into a human-readable string (`ms`, `s`, or `m s`).
171
- *
172
- * @example
173
- * ```ts
174
- * formatMs(250) // '250ms'
175
- * formatMs(1500) // '1.50s'
176
- * formatMs(90000) // '1m 30.0s'
177
- * ```
178
- */
179
- function formatMs(ms) {
180
- if (ms >= 6e4) return `${Math.floor(ms / 6e4)}m ${(ms % 6e4 / 1e3).toFixed(1)}s`;
181
- if (ms >= 1e3) return `${(ms / 1e3).toFixed(2)}s`;
182
- return `${Math.round(ms)}ms`;
183
- }
184
- //#endregion
185
8
  //#region ../../internals/utils/src/fs.ts
186
9
  /**
187
10
  * Resolves to `true` when the file or directory at `path` exists.
@@ -279,7 +102,7 @@ function createAdapter(build) {
279
102
  }
280
103
  //#endregion
281
104
  //#region package.json
282
- var version = "5.0.0-beta.20";
105
+ var version = "5.0.0-beta.21";
283
106
  //#endregion
284
107
  //#region src/createStorage.ts
285
108
  /**
@@ -318,62 +141,6 @@ function createStorage(build) {
318
141
  return (options) => build(options ?? {});
319
142
  }
320
143
  //#endregion
321
- //#region src/FileProcessor.ts
322
- function joinSources(file) {
323
- const sources = file.sources;
324
- if (sources.length === 0) return "";
325
- const parts = [];
326
- for (const source of sources) {
327
- const s = (0, _kubb_ast.extractStringsFromNodes)(source.nodes);
328
- if (s) parts.push(s);
329
- }
330
- return parts.join("\n\n");
331
- }
332
- /**
333
- * Converts a single file to a string using the registered parsers.
334
- * Falls back to joining source values when no matching parser is found.
335
- *
336
- * @internal
337
- */
338
- var FileProcessor = class {
339
- events = new AsyncEventEmitter();
340
- parse(file, { parsers, extension } = {}) {
341
- const parseExtName = extension?.[file.extname] || void 0;
342
- if (!parsers || !file.extname) return joinSources(file);
343
- const parser = parsers.get(file.extname);
344
- if (!parser) return joinSources(file);
345
- return parser.parse(file, { extname: parseExtName });
346
- }
347
- *stream(files, options = {}) {
348
- const total = files.length;
349
- if (total === 0) return;
350
- let processed = 0;
351
- for (const file of files) {
352
- const source = this.parse(file, options);
353
- processed++;
354
- yield {
355
- file,
356
- source,
357
- processed,
358
- total,
359
- percentage: processed / total * 100
360
- };
361
- }
362
- }
363
- async run(files, options = {}) {
364
- await this.events.emit("start", files);
365
- for (const { file, source, processed, total, percentage } of this.stream(files, options)) await this.events.emit("update", {
366
- file,
367
- source,
368
- processed,
369
- percentage,
370
- total
371
- });
372
- await this.events.emit("end", files);
373
- return files;
374
- }
375
- };
376
- //#endregion
377
144
  //#region src/storages/fsStorage.ts
378
145
  /**
379
146
  * Built-in filesystem storage driver.
@@ -448,74 +215,12 @@ const fsStorage = createStorage(() => ({
448
215
  }
449
216
  }));
450
217
  //#endregion
451
- //#region \0@oxc-project+runtime@0.129.0/helpers/usingCtx.js
452
- function _usingCtx() {
453
- var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
454
- var n = Error();
455
- return n.name = "SuppressedError", n.error = r, n.suppressed = e, n;
456
- };
457
- var e = {};
458
- var n = [];
459
- function using(r, e) {
460
- if (null != e) {
461
- if (Object(e) !== e) throw new TypeError("using declarations can only be used with objects, functions, null, or undefined.");
462
- if (r) var o = e[Symbol.asyncDispose || Symbol["for"]("Symbol.asyncDispose")];
463
- if (void 0 === o && (o = e[Symbol.dispose || Symbol["for"]("Symbol.dispose")], r)) var t = o;
464
- if ("function" != typeof o) throw new TypeError("Object is not disposable.");
465
- t && (o = function o() {
466
- try {
467
- t.call(e);
468
- } catch (r) {
469
- return Promise.reject(r);
470
- }
471
- }), n.push({
472
- v: e,
473
- d: o,
474
- a: r
475
- });
476
- } else r && n.push({
477
- d: e,
478
- a: r
479
- });
480
- return e;
481
- }
482
- return {
483
- e,
484
- u: using.bind(null, !1),
485
- a: using.bind(null, !0),
486
- d: function d() {
487
- var o;
488
- var t = this.e;
489
- var s = 0;
490
- function next() {
491
- for (; o = n.pop();) try {
492
- if (!o.a && 1 === s) return s = 0, n.push(o), Promise.resolve().then(next);
493
- if (o.d) {
494
- var r = o.d.call(o.v);
495
- if (o.a) return s |= 2, Promise.resolve(r).then(next, err);
496
- } else s |= 1;
497
- } catch (r) {
498
- return err(r);
499
- }
500
- if (1 === s) return t !== e ? Promise.reject(t) : Promise.resolve();
501
- if (t !== e) throw t;
502
- }
503
- function err(n) {
504
- return t = t !== e ? new r(n, t) : n, next();
505
- }
506
- return next();
507
- }
508
- };
509
- }
510
- //#endregion
511
218
  //#region src/createKubb.ts
512
219
  /**
513
220
  * Builds a `Storage` view scoped to the file paths produced by the current build.
514
- *
515
- * Reads delegate to the underlying `storage` (typically `fsStorage()`) so source bytes
516
- * stay where they were written instead of being held in an extra in-memory map.
517
- * Writing via `setItem` stores the content in the underlying storage and registers the
518
- * key so subsequent reads and `getKeys` are scoped to this build's output.
221
+ * Reads delegate to the underlying `storage` so source bytes stay where they were
222
+ * written; writes register the key so subsequent reads and `getKeys` are scoped
223
+ * to this build's output.
519
224
  */
520
225
  function createSourcesView(storage) {
521
226
  const paths = /* @__PURE__ */ new Set();
@@ -547,9 +252,8 @@ function createSourcesView(storage) {
547
252
  }
548
253
  }))();
549
254
  }
550
- async function setup(userConfig, options = {}) {
551
- const hooks = options.hooks ?? new AsyncEventEmitter();
552
- const config = {
255
+ function resolveConfig(userConfig) {
256
+ return {
553
257
  ...userConfig,
554
258
  root: userConfig.root || process.cwd(),
555
259
  parsers: userConfig.parsers ?? [],
@@ -567,422 +271,6 @@ async function setup(userConfig, options = {}) {
567
271
  } : void 0,
568
272
  plugins: userConfig.plugins ?? []
569
273
  };
570
- const driver = new require_KubbDriver.KubbDriver(config, { hooks });
571
- const storage = createSourcesView(config.storage);
572
- const diagnosticInfo = getDiagnosticInfo();
573
- await hooks.emit("kubb:debug", {
574
- date: /* @__PURE__ */ new Date(),
575
- logs: [
576
- "Configuration:",
577
- ` • Name: ${userConfig.name || "unnamed"}`,
578
- ` • Root: ${userConfig.root || process.cwd()}`,
579
- ` • Output: ${userConfig.output?.path || "not specified"}`,
580
- ` • Plugins: ${userConfig.plugins?.length || 0}`,
581
- "Output Settings:",
582
- ` • Storage: ${config.storage.name}`,
583
- ` • Formatter: ${userConfig.output?.format || "none"}`,
584
- ` • Linter: ${userConfig.output?.lint || "none"}`,
585
- `Running adapter: ${config.adapter?.name || "none"}`,
586
- "Environment:",
587
- Object.entries(diagnosticInfo).map(([key, value]) => ` • ${key}: ${value}`).join("\n")
588
- ]
589
- });
590
- try {
591
- if (isInputPath(userConfig) && !new require_KubbDriver.URLPath(userConfig.input.path).isURL) {
592
- await exists(userConfig.input.path);
593
- await hooks.emit("kubb:debug", {
594
- date: /* @__PURE__ */ new Date(),
595
- logs: [`✓ Input file validated: ${userConfig.input.path}`]
596
- });
597
- }
598
- } catch (caughtError) {
599
- if (isInputPath(userConfig)) {
600
- const error = caughtError;
601
- throw new Error(`Cannot read file/URL defined in \`input.path\` or set with \`kubb generate PATH\` in the CLI of your Kubb config ${userConfig.input.path}`, { cause: error });
602
- }
603
- }
604
- if (config.output.clean) {
605
- await hooks.emit("kubb:debug", {
606
- date: /* @__PURE__ */ new Date(),
607
- logs: ["Cleaning output directories", ` • Output: ${config.output.path}`]
608
- });
609
- await config.storage.clear((0, node_path.resolve)(config.root, config.output.path));
610
- }
611
- await driver.setup();
612
- return {
613
- config,
614
- hooks,
615
- driver,
616
- storage,
617
- dispose,
618
- [Symbol.dispose]: dispose
619
- };
620
- function dispose() {
621
- driver.dispose();
622
- }
623
- }
624
- async function safeBuild(setupResult) {
625
- try {
626
- var _usingCtx$1 = _usingCtx();
627
- _usingCtx$1.u(setupResult);
628
- const { driver, hooks, storage } = setupResult;
629
- const failedPlugins = /* @__PURE__ */ new Set();
630
- const pluginTimings = /* @__PURE__ */ new Map();
631
- const config = driver.config;
632
- const writtenPaths = /* @__PURE__ */ new Set();
633
- const parsersMap = /* @__PURE__ */ new Map();
634
- const fileProcessor = new FileProcessor();
635
- for (const parser of config.parsers) if (parser.extNames) for (const extname of parser.extNames) parsersMap.set(extname, parser);
636
- async function flushPendingFiles() {
637
- const files = driver.fileManager.files.filter((f) => !writtenPaths.has(f.path));
638
- if (files.length === 0) return;
639
- await hooks.emit("kubb:debug", {
640
- date: /* @__PURE__ */ new Date(),
641
- logs: [`Writing ${files.length} files...`]
642
- });
643
- await hooks.emit("kubb:files:processing:start", { files });
644
- const stream = fileProcessor.stream(files, {
645
- parsers: parsersMap,
646
- extension: config.output.extension
647
- });
648
- const queue = [];
649
- for (const { file, source, processed, total, percentage } of stream) {
650
- writtenPaths.add(file.path);
651
- queue.push((async () => {
652
- await hooks.emit("kubb:file:processing:update", {
653
- file,
654
- source,
655
- processed,
656
- total,
657
- percentage,
658
- config
659
- });
660
- if (source) await storage.setItem(file.path, source);
661
- })());
662
- if (queue.length >= 50) await Promise.all(queue.splice(0));
663
- }
664
- await Promise.all(queue);
665
- await hooks.emit("kubb:files:processing:end", { files });
666
- await hooks.emit("kubb:debug", {
667
- date: /* @__PURE__ */ new Date(),
668
- logs: [`✓ File write process completed for ${files.length} files`]
669
- });
670
- }
671
- async function dispatchOperationsToGenerators(generators, collectedOperations, ctx, rendererFor) {
672
- for (const gen of generators) {
673
- if (!gen.operations) continue;
674
- await require_KubbDriver.applyHookResult({
675
- result: await gen.operations(collectedOperations, ctx),
676
- driver,
677
- rendererFactory: rendererFor(gen)
678
- });
679
- }
680
- await driver.hooks.emit("kubb:generate:operations", collectedOperations, ctx);
681
- }
682
- /**
683
- * Single-pass fan-out: iterates all schemas and operations once, distributing each node
684
- * to every generator-plugin in parallel. This replaces the N-pass-per-plugin pattern
685
- * (each plugin getting its own iterator) with one parse pass fanned to all plugins,
686
- * eliminating the N×parse-time overhead for multi-plugin builds.
687
- */
688
- async function runPlugins(entries) {
689
- const { schemas, operations } = driver.inputNode;
690
- const operationFilterTypes = new Set([
691
- "tag",
692
- "operationId",
693
- "path",
694
- "method",
695
- "contentType"
696
- ]);
697
- const states = entries.map(({ plugin, context, hrStart }) => {
698
- const { exclude, include, override } = plugin.options;
699
- const hasExclude = Array.isArray(exclude) && exclude.length > 0;
700
- const hasInclude = Array.isArray(include) && include.length > 0;
701
- const hasOverride = Array.isArray(override) && override.length > 0;
702
- return {
703
- plugin,
704
- generatorContext: {
705
- ...context,
706
- resolver: driver.getResolver(plugin.name)
707
- },
708
- generators: plugin.generators ?? [],
709
- hrStart,
710
- failed: false,
711
- error: void 0,
712
- optionsAreStatic: !hasExclude && !hasInclude && !hasOverride,
713
- allowedSchemaNames: void 0
714
- };
715
- });
716
- const pruningStates = states.filter(({ plugin }) => {
717
- const { include } = plugin.options;
718
- return (include?.some(({ type }) => operationFilterTypes.has(type)) ?? false) && !(include?.some(({ type }) => type === "schemaName") ?? false);
719
- });
720
- if (pruningStates.length > 0) {
721
- const allSchemas = [];
722
- for await (const schema of schemas) allSchemas.push(schema);
723
- const includedOpsByState = new Map(pruningStates.map((s) => [s, []]));
724
- for await (const operation of operations) for (const state of pruningStates) {
725
- const { exclude, include, override } = state.plugin.options;
726
- if (state.generatorContext.resolver.resolveOptions(operation, {
727
- options: state.plugin.options,
728
- exclude,
729
- include,
730
- override
731
- }) !== null) includedOpsByState.get(state)?.push(operation);
732
- }
733
- for (const state of pruningStates) state.allowedSchemaNames = (0, _kubb_ast.collectUsedSchemaNames)(includedOpsByState.get(state) ?? [], allSchemas);
734
- }
735
- function resolveRendererFor(gen, state) {
736
- return gen.renderer === null ? void 0 : gen.renderer ?? state.plugin.renderer ?? state.generatorContext.config.renderer;
737
- }
738
- async function dispatchSchema(state, node) {
739
- if (state.failed) return;
740
- try {
741
- const { plugin, generatorContext, generators } = state;
742
- const transformedNode = plugin.transformer ? (0, _kubb_ast.transform)(node, plugin.transformer) : node;
743
- if (state.allowedSchemaNames !== void 0 && transformedNode.name && !state.allowedSchemaNames.has(transformedNode.name)) return;
744
- const { exclude, include, override } = plugin.options;
745
- const options = state.optionsAreStatic ? plugin.options : generatorContext.resolver.resolveOptions(transformedNode, {
746
- options: plugin.options,
747
- exclude,
748
- include,
749
- override
750
- });
751
- if (options === null) return;
752
- const ctx = {
753
- ...generatorContext,
754
- options
755
- };
756
- for (const gen of generators) {
757
- if (!gen.schema) continue;
758
- const raw = gen.schema(transformedNode, ctx);
759
- const applied = require_KubbDriver.applyHookResult({
760
- result: require_KubbDriver.isPromise(raw) ? await raw : raw,
761
- driver,
762
- rendererFactory: resolveRendererFor(gen, state)
763
- });
764
- if (require_KubbDriver.isPromise(applied)) await applied;
765
- }
766
- await driver.hooks.emit("kubb:generate:schema", transformedNode, ctx);
767
- } catch (caughtError) {
768
- state.failed = true;
769
- state.error = caughtError;
770
- }
771
- }
772
- async function dispatchOperation(state, node) {
773
- if (state.failed) return;
774
- try {
775
- const { plugin, generatorContext, generators } = state;
776
- const transformedNode = plugin.transformer ? (0, _kubb_ast.transform)(node, plugin.transformer) : node;
777
- const { exclude, include, override } = plugin.options;
778
- const options = state.optionsAreStatic ? plugin.options : generatorContext.resolver.resolveOptions(transformedNode, {
779
- options: plugin.options,
780
- exclude,
781
- include,
782
- override
783
- });
784
- if (options === null) return;
785
- const ctx = {
786
- ...generatorContext,
787
- options
788
- };
789
- for (const gen of generators) {
790
- if (!gen.operation) continue;
791
- const raw = gen.operation(transformedNode, ctx);
792
- const applied = require_KubbDriver.applyHookResult({
793
- result: require_KubbDriver.isPromise(raw) ? await raw : raw,
794
- driver,
795
- rendererFactory: resolveRendererFor(gen, state)
796
- });
797
- if (require_KubbDriver.isPromise(applied)) await applied;
798
- }
799
- await driver.hooks.emit("kubb:generate:operation", transformedNode, ctx);
800
- } catch (caughtError) {
801
- state.failed = true;
802
- state.error = caughtError;
803
- }
804
- }
805
- await require_KubbDriver.forBatches(schemas, (nodes) => Promise.all(nodes.flatMap((n) => states.map((state) => dispatchSchema(state, n)))), {
806
- concurrency: 8,
807
- flush: flushPendingFiles
808
- });
809
- const collectedOperations = [];
810
- await require_KubbDriver.forBatches(operations, (nodes) => {
811
- collectedOperations.push(...nodes);
812
- return Promise.all(nodes.flatMap((n) => states.map((state) => dispatchOperation(state, n))));
813
- }, {
814
- concurrency: 8,
815
- flush: flushPendingFiles
816
- });
817
- for (const state of states) {
818
- if (!state.failed) try {
819
- const { plugin, generatorContext, generators } = state;
820
- await dispatchOperationsToGenerators(generators, collectedOperations, {
821
- ...generatorContext,
822
- options: plugin.options
823
- }, (gen) => resolveRendererFor(gen, state));
824
- } catch (caughtError) {
825
- state.failed = true;
826
- state.error = caughtError;
827
- }
828
- const duration = getElapsedMs(state.hrStart);
829
- pluginTimings.set(state.plugin.name, duration);
830
- await driver.hooks.emit("kubb:plugin:end", {
831
- plugin: state.plugin,
832
- duration,
833
- success: !state.failed,
834
- ...state.failed && state.error ? { error: state.error } : {},
835
- config: driver.config,
836
- get files() {
837
- return driver.fileManager.files;
838
- },
839
- upsertFile: (...files) => driver.fileManager.upsert(...files)
840
- });
841
- if (state.failed && state.error) failedPlugins.add({
842
- plugin: state.plugin,
843
- error: state.error
844
- });
845
- await driver.hooks.emit("kubb:debug", {
846
- date: /* @__PURE__ */ new Date(),
847
- logs: [state.failed ? "✗ Plugin start failed" : `✓ Plugin started successfully (${formatMs(duration)})`]
848
- });
849
- }
850
- }
851
- try {
852
- await driver.emitSetupHooks();
853
- if (driver.adapter && driver.inputNode) await hooks.emit("kubb:build:start", {
854
- config,
855
- adapter: driver.adapter,
856
- meta: driver.inputNode.meta,
857
- getPlugin: driver.getPlugin.bind(driver),
858
- get files() {
859
- return driver.fileManager.files;
860
- },
861
- upsertFile: (...files) => driver.fileManager.upsert(...files)
862
- });
863
- const generatorPlugins = [];
864
- for (const plugin of driver.plugins.values()) {
865
- const context = driver.getContext(plugin);
866
- const hrStart = process.hrtime();
867
- try {
868
- await hooks.emit("kubb:plugin:start", { plugin });
869
- await hooks.emit("kubb:debug", {
870
- date: /* @__PURE__ */ new Date(),
871
- logs: ["Starting plugin...", ` • Plugin Name: ${plugin.name}`]
872
- });
873
- } catch (caughtError) {
874
- const error = caughtError;
875
- const duration = getElapsedMs(hrStart);
876
- pluginTimings.set(plugin.name, duration);
877
- await hooks.emit("kubb:plugin:end", {
878
- plugin,
879
- duration,
880
- success: false,
881
- error,
882
- config,
883
- get files() {
884
- return driver.fileManager.files;
885
- },
886
- upsertFile: (...files) => driver.fileManager.upsert(...files)
887
- });
888
- failedPlugins.add({
889
- plugin,
890
- error
891
- });
892
- continue;
893
- }
894
- if (plugin.generators?.length || driver.hasEventGenerators(plugin.name)) {
895
- generatorPlugins.push({
896
- plugin,
897
- context,
898
- hrStart
899
- });
900
- continue;
901
- }
902
- const duration = getElapsedMs(hrStart);
903
- pluginTimings.set(plugin.name, duration);
904
- await hooks.emit("kubb:plugin:end", {
905
- plugin,
906
- duration,
907
- success: true,
908
- config,
909
- get files() {
910
- return driver.fileManager.files;
911
- },
912
- upsertFile: (...files) => driver.fileManager.upsert(...files)
913
- });
914
- await hooks.emit("kubb:debug", {
915
- date: /* @__PURE__ */ new Date(),
916
- logs: [`✓ Plugin started successfully (${formatMs(duration)})`]
917
- });
918
- }
919
- if (generatorPlugins.length > 0) if (driver.inputNode) await require_KubbDriver.withDrain(() => runPlugins(generatorPlugins), flushPendingFiles);
920
- else for (const { plugin, hrStart } of generatorPlugins) {
921
- const duration = getElapsedMs(hrStart);
922
- pluginTimings.set(plugin.name, duration);
923
- await hooks.emit("kubb:plugin:end", {
924
- plugin,
925
- duration,
926
- success: true,
927
- config,
928
- get files() {
929
- return driver.fileManager.files;
930
- },
931
- upsertFile: (...files) => driver.fileManager.upsert(...files)
932
- });
933
- }
934
- await hooks.emit("kubb:plugins:end", {
935
- config,
936
- get files() {
937
- return driver.fileManager.files;
938
- },
939
- upsertFile: (...files) => driver.fileManager.upsert(...files)
940
- });
941
- await flushPendingFiles();
942
- const files = driver.fileManager.files;
943
- await hooks.emit("kubb:build:end", {
944
- files,
945
- config,
946
- outputDir: (0, node_path.resolve)(config.root, config.output.path)
947
- });
948
- return {
949
- failedPlugins,
950
- files,
951
- driver,
952
- pluginTimings,
953
- storage
954
- };
955
- } catch (error) {
956
- return {
957
- failedPlugins,
958
- files: [],
959
- driver,
960
- pluginTimings,
961
- error,
962
- storage
963
- };
964
- }
965
- } catch (_) {
966
- _usingCtx$1.e = _;
967
- } finally {
968
- _usingCtx$1.d();
969
- }
970
- }
971
- async function build(setupResult) {
972
- const { files, driver, failedPlugins, pluginTimings, error, storage } = await safeBuild(setupResult);
973
- if (error) throw error;
974
- if (failedPlugins.size > 0) {
975
- const errors = [...failedPlugins].map(({ error }) => error);
976
- throw new BuildError(`Build Error with ${failedPlugins.size} failed plugins`, { errors });
977
- }
978
- return {
979
- failedPlugins,
980
- files,
981
- driver,
982
- pluginTimings,
983
- error: void 0,
984
- storage
985
- };
986
274
  }
987
275
  /**
988
276
  * Returns a snapshot of the current runtime environment.
@@ -1003,56 +291,143 @@ function isInputPath(config) {
1003
291
  return typeof config?.input === "object" && config.input !== null && "path" in config.input;
1004
292
  }
1005
293
  /**
1006
- * Creates a Kubb instance bound to a single config entry.
294
+ * Kubb code-generation instance bound to a single config entry. Resolves the user
295
+ * config during `setup()` and shares `hooks`, `storage`, `driver`, and `config` across
296
+ * the `setup → build` lifecycle.
1007
297
  *
1008
- * Accepts a user-facing config shape and resolves it to a full {@link Config} during
1009
- * `setup()`. The instance then holds shared state (`hooks`, `storage`, `driver`, `config`)
1010
- * across the `setup → build` lifecycle. Attach event listeners to `kubb.hooks` before
1011
- * calling `setup()` or `build()`.
298
+ * Attach event listeners to `.hooks` before calling `setup()` or `build()`.
1012
299
  *
1013
300
  * @example
1014
301
  * ```ts
1015
302
  * const kubb = createKubb(userConfig)
1016
- *
1017
- * kubb.hooks.on('kubb:plugin:end', ({ plugin, duration }) => {
1018
- * console.log(`${plugin.name} completed in ${duration}ms`)
1019
- * })
1020
- *
303
+ * kubb.hooks.on('kubb:plugin:end', ({ plugin, duration }) => console.log(plugin.name, duration))
1021
304
  * const { files, failedPlugins } = await kubb.safeBuild()
1022
305
  * ```
1023
306
  */
1024
- function createKubb(userConfig, options = {}) {
1025
- const hooks = options.hooks ?? new AsyncEventEmitter();
1026
- let setupResult;
1027
- const instance = {
1028
- get hooks() {
1029
- return hooks;
1030
- },
1031
- get storage() {
1032
- if (!setupResult) throw new Error("[kubb] setup() must be called before accessing storage");
1033
- return setupResult.storage;
1034
- },
1035
- get driver() {
1036
- if (!setupResult) throw new Error("[kubb] setup() must be called before accessing driver");
1037
- return setupResult.driver;
1038
- },
1039
- get config() {
1040
- if (!setupResult) throw new Error("[kubb] setup() must be called before accessing config");
1041
- return setupResult.config;
1042
- },
1043
- async setup() {
1044
- setupResult = await setup(userConfig, { hooks });
1045
- },
1046
- async build() {
1047
- if (!setupResult) await instance.setup();
1048
- return build(setupResult);
1049
- },
1050
- async safeBuild() {
1051
- if (!setupResult) await instance.setup();
1052
- return safeBuild(setupResult);
307
+ var Kubb = class {
308
+ hooks;
309
+ #userConfig;
310
+ #config = null;
311
+ #driver = null;
312
+ #storage = null;
313
+ constructor(userConfig, options = {}) {
314
+ this.#userConfig = userConfig;
315
+ this.hooks = options.hooks ?? new require_KubbDriver.AsyncEventEmitter();
316
+ }
317
+ get storage() {
318
+ if (!this.#storage) throw new Error("[kubb] setup() must be called before accessing storage");
319
+ return this.#storage;
320
+ }
321
+ get driver() {
322
+ if (!this.#driver) throw new Error("[kubb] setup() must be called before accessing driver");
323
+ return this.#driver;
324
+ }
325
+ get config() {
326
+ if (!this.#config) throw new Error("[kubb] setup() must be called before accessing config");
327
+ return this.#config;
328
+ }
329
+ /**
330
+ * Resolves config and initializes the driver. `build()` calls this automatically.
331
+ */
332
+ async setup() {
333
+ const config = resolveConfig(this.#userConfig);
334
+ const driver = new require_KubbDriver.KubbDriver(config, { hooks: this.hooks });
335
+ const storage = createSourcesView(config.storage);
336
+ await this.hooks.emit("kubb:debug", {
337
+ date: /* @__PURE__ */ new Date(),
338
+ logs: this.#configLogs(config)
339
+ });
340
+ if (isInputPath(this.#userConfig) && !new require_KubbDriver.URLPath(this.#userConfig.input.path).isURL) try {
341
+ await exists(this.#userConfig.input.path);
342
+ await this.hooks.emit("kubb:debug", {
343
+ date: /* @__PURE__ */ new Date(),
344
+ logs: [`✓ Input file validated: ${this.#userConfig.input.path}`]
345
+ });
346
+ } catch (caughtError) {
347
+ throw new Error(`Cannot read file/URL defined in \`input.path\` or set with \`kubb generate PATH\` in the CLI of your Kubb config ${this.#userConfig.input.path}`, { cause: caughtError });
1053
348
  }
1054
- };
1055
- return instance;
349
+ if (config.output.clean) {
350
+ await this.hooks.emit("kubb:debug", {
351
+ date: /* @__PURE__ */ new Date(),
352
+ logs: ["Cleaning output directories", ` • Output: ${config.output.path}`]
353
+ });
354
+ await config.storage.clear((0, node_path.resolve)(config.root, config.output.path));
355
+ }
356
+ await driver.setup();
357
+ this.#config = config;
358
+ this.#driver = driver;
359
+ this.#storage = storage;
360
+ }
361
+ /**
362
+ * Runs the full pipeline and throws on any plugin error.
363
+ * Automatically calls `setup()` if needed.
364
+ */
365
+ async build() {
366
+ const out = await this.safeBuild();
367
+ if (out.error) throw out.error;
368
+ if (out.failedPlugins.size > 0) {
369
+ const errors = [...out.failedPlugins].map(({ error }) => error);
370
+ throw new require_KubbDriver.BuildError(`Build Error with ${out.failedPlugins.size} failed plugins`, { errors });
371
+ }
372
+ return out;
373
+ }
374
+ /**
375
+ * Runs the full pipeline and captures errors in `BuildOutput` instead of throwing.
376
+ * Automatically calls `setup()` if needed.
377
+ */
378
+ async safeBuild() {
379
+ try {
380
+ var _usingCtx$1 = require_KubbDriver._usingCtx();
381
+ if (!this.#driver) await this.setup();
382
+ const cleanup = _usingCtx$1.u(this);
383
+ const driver = cleanup.driver;
384
+ const storage = cleanup.storage;
385
+ const { failedPlugins, pluginTimings, error } = await driver.run({ storage });
386
+ return {
387
+ failedPlugins,
388
+ files: driver.fileManager.files,
389
+ driver,
390
+ pluginTimings,
391
+ storage,
392
+ ...error ? { error } : {}
393
+ };
394
+ } catch (_) {
395
+ _usingCtx$1.e = _;
396
+ } finally {
397
+ _usingCtx$1.d();
398
+ }
399
+ }
400
+ dispose() {
401
+ this.#driver?.dispose();
402
+ }
403
+ [Symbol.dispose]() {
404
+ this.dispose();
405
+ }
406
+ #configLogs(config) {
407
+ const u = this.#userConfig;
408
+ const diag = getDiagnosticInfo();
409
+ return [
410
+ "Configuration:",
411
+ ` • Name: ${u.name || "unnamed"}`,
412
+ ` • Root: ${u.root || process.cwd()}`,
413
+ ` • Output: ${u.output?.path || "not specified"}`,
414
+ ` • Plugins: ${u.plugins?.length || 0}`,
415
+ "Output Settings:",
416
+ ` • Storage: ${config.storage.name}`,
417
+ ` • Formatter: ${u.output?.format || "none"}`,
418
+ ` • Linter: ${u.output?.lint || "none"}`,
419
+ `Running adapter: ${config.adapter?.name || "none"}`,
420
+ "Environment:",
421
+ Object.entries(diag).map(([key, value]) => ` • ${key}: ${value}`).join("\n")
422
+ ];
423
+ }
424
+ };
425
+ /**
426
+ * Factory for {@link Kubb}. Equivalent to `new Kubb(userConfig, options)` and kept
427
+ * as the canonical public entry point.
428
+ */
429
+ function createKubb(userConfig, options = {}) {
430
+ return new Kubb(userConfig, options);
1056
431
  }
1057
432
  //#endregion
1058
433
  //#region src/createRenderer.ts
@@ -1066,6 +441,7 @@ function createKubb(userConfig, options = {}) {
1066
441
  * return {
1067
442
  * async render(element) { await runtime.render(element) },
1068
443
  * get files() { return runtime.nodes },
444
+ * dispose() { runtime.unmount() },
1069
445
  * unmount(error) { runtime.unmount(error) },
1070
446
  * }
1071
447
  * })
@@ -1234,9 +610,9 @@ const memoryStorage = createStorage(() => {
1234
610
  };
1235
611
  });
1236
612
  //#endregion
1237
- exports.AsyncEventEmitter = AsyncEventEmitter;
613
+ exports.AsyncEventEmitter = require_KubbDriver.AsyncEventEmitter;
1238
614
  exports.FileManager = require_KubbDriver.FileManager;
1239
- exports.FileProcessor = FileProcessor;
615
+ exports.FileProcessor = require_KubbDriver.FileProcessor;
1240
616
  exports.KubbDriver = require_KubbDriver.KubbDriver;
1241
617
  exports.URLPath = require_KubbDriver.URLPath;
1242
618
  Object.defineProperty(exports, "ast", {