@kubb/core 5.0.0-beta.14 → 5.0.0-beta.16

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.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as __name } from "./chunk--u3MIqq1.js";
2
- import { $ as ResolverPathParams, A as isInputPath, B as KubbPluginSetupContext, C as KubbPluginsEndContext, Ct as AsyncEventEmitter, D as PossibleConfig, E as KubbWarnContext, F as FileManager, G as Plugin, H as NormalizedPlugin, I as Exclude, J as ResolveBannerContext, K as PluginFactoryOptions, L as Group, M as GeneratorContext, N as defineGenerator, O as UserConfig, P as PluginDriver, Q as ResolverFileParams, R as Include, S as KubbLifecycleStartContext, St as logLevel, T as KubbVersionNewContext, U as Output, V as KubbPluginStartContext, W as Override, X as Resolver, Y as ResolveOptionsContext, Z as ResolverContext, _ as KubbGenerationSummaryContext, _t as createRenderer, a as InputPath, at as LoggerOptions, b as KubbHooks, bt as AdapterSource, c as KubbBuildStartContext, ct as FileProcessor, d as KubbErrorContext, dt as defineParser, et as defineResolver, f as KubbFileProcessingUpdateContext, ft as DevtoolsOptions, g as KubbGenerationStartContext, gt as RendererFactory, h as KubbGenerationEndContext, ht as Renderer, i as InputData, it as LoggerContext, j as Generator, k as createKubb, l as KubbConfigEndContext, lt as FileProcessorEvents, m as KubbFilesProcessingStartContext, mt as createStorage, n as CLIOptions, nt as defineMiddleware, o as Kubb, ot as UserLogger, p as KubbFilesProcessingEndContext, pt as Storage, q as definePlugin, r as Config, rt as Logger, s as KubbBuildEndContext, st as defineLogger, t as BuildOutput, tt as Middleware, u as KubbDebugContext, ut as Parser, v as KubbHookEndContext, vt as Adapter, w as KubbSuccessContext, x as KubbInfoContext, xt as createAdapter, y as KubbHookStartContext, yt as AdapterFactoryOptions, z as KubbPluginEndContext } from "./createKubb-uVWTlN_w.js";
2
+ import { $ as ResolverPathParams, A as isInputPath, B as KubbPluginSetupContext, C as KubbPluginsEndContext, Ct as logLevel, D as PossibleConfig, E as KubbWarnContext, F as FileManager, G as Plugin, H as NormalizedPlugin, I as Exclude, J as ResolveBannerContext, K as PluginFactoryOptions, L as Group, M as GeneratorContext, N as defineGenerator, O as UserConfig, P as PluginDriver, Q as ResolverFileParams, R as Include, S as KubbLifecycleStartContext, St as createAdapter, T as KubbVersionNewContext, U as Output, V as KubbPluginStartContext, W as Override, X as Resolver, Y as ResolveOptionsContext, Z as ResolverContext, _ as KubbGenerationSummaryContext, _t as RendererFactory, a as InputPath, at as LoggerOptions, b as KubbHooks, bt as AdapterFactoryOptions, c as KubbBuildStartContext, ct as FileProcessor, d as KubbErrorContext, dt as Parser, et as defineResolver, f as KubbFileProcessingUpdateContext, ft as defineParser, g as KubbGenerationStartContext, gt as Renderer, h as KubbGenerationEndContext, ht as createStorage, i as InputData, it as LoggerContext, j as Generator, k as createKubb, l as KubbConfigEndContext, lt as FileProcessorEvents, m as KubbFilesProcessingStartContext, mt as Storage, n as CLIOptions, nt as defineMiddleware, o as Kubb, ot as UserLogger, p as KubbFilesProcessingEndContext, pt as DevtoolsOptions, q as definePlugin, r as Config, rt as Logger, s as KubbBuildEndContext, st as defineLogger, t as BuildOutput, tt as Middleware, u as KubbDebugContext, ut as ParsedFile, v as KubbHookEndContext, vt as createRenderer, w as KubbSuccessContext, wt as AsyncEventEmitter, x as KubbInfoContext, xt as AdapterSource, y as KubbHookStartContext, yt as Adapter, z as KubbPluginEndContext } from "./createKubb-BncBLGm_.js";
3
3
  import * as ast from "@kubb/ast";
4
4
 
5
5
  //#region ../../internals/utils/src/urlPath.d.ts
@@ -188,5 +188,5 @@ declare const fsStorage: (options?: Record<string, never> | undefined) => Storag
188
188
  */
189
189
  declare const memoryStorage: (options?: Record<string, never> | undefined) => Storage;
190
190
  //#endregion
191
- export { Adapter, AdapterFactoryOptions, AdapterSource, AsyncEventEmitter, BuildOutput, CLIOptions, Config, DevtoolsOptions, Exclude, FileManager, FileProcessor, FileProcessorEvents, Generator, GeneratorContext, Group, Include, InputData, InputPath, Kubb, KubbBuildEndContext, KubbBuildStartContext, KubbConfigEndContext, KubbDebugContext, KubbErrorContext, KubbFileProcessingUpdateContext, KubbFilesProcessingEndContext, KubbFilesProcessingStartContext, KubbGenerationEndContext, KubbGenerationStartContext, KubbGenerationSummaryContext, KubbHookEndContext, KubbHookStartContext, KubbHooks, KubbInfoContext, KubbLifecycleStartContext, KubbPluginEndContext, KubbPluginSetupContext, KubbPluginStartContext, KubbPluginsEndContext, KubbSuccessContext, KubbVersionNewContext, KubbWarnContext, Logger, LoggerContext, LoggerOptions, Middleware, NormalizedPlugin, Output, Override, Parser, Plugin, PluginDriver, PluginFactoryOptions, PossibleConfig, Renderer, RendererFactory, ResolveBannerContext, ResolveOptionsContext, Resolver, ResolverContext, ResolverFileParams, ResolverPathParams, Storage, URLPath, UserConfig, UserLogger, ast, createAdapter, createKubb, createRenderer, createStorage, defineGenerator, defineLogger, defineMiddleware, defineParser, definePlugin, defineResolver, fsStorage, isInputPath, logLevel, memoryStorage };
191
+ export { Adapter, AdapterFactoryOptions, AdapterSource, AsyncEventEmitter, BuildOutput, CLIOptions, Config, DevtoolsOptions, Exclude, FileManager, FileProcessor, FileProcessorEvents, Generator, GeneratorContext, Group, Include, InputData, InputPath, Kubb, KubbBuildEndContext, KubbBuildStartContext, KubbConfigEndContext, KubbDebugContext, KubbErrorContext, KubbFileProcessingUpdateContext, KubbFilesProcessingEndContext, KubbFilesProcessingStartContext, KubbGenerationEndContext, KubbGenerationStartContext, KubbGenerationSummaryContext, KubbHookEndContext, KubbHookStartContext, KubbHooks, KubbInfoContext, KubbLifecycleStartContext, KubbPluginEndContext, KubbPluginSetupContext, KubbPluginStartContext, KubbPluginsEndContext, KubbSuccessContext, KubbVersionNewContext, KubbWarnContext, Logger, LoggerContext, LoggerOptions, Middleware, NormalizedPlugin, Output, Override, ParsedFile, Parser, Plugin, PluginDriver, PluginFactoryOptions, PossibleConfig, Renderer, RendererFactory, ResolveBannerContext, ResolveOptionsContext, Resolver, ResolverContext, ResolverFileParams, ResolverPathParams, Storage, URLPath, UserConfig, UserLogger, ast, createAdapter, createKubb, createRenderer, createStorage, defineGenerator, defineLogger, defineMiddleware, defineParser, definePlugin, defineResolver, fsStorage, isInputPath, logLevel, memoryStorage };
192
192
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { t as __name } from "./chunk--u3MIqq1.js";
2
- import { a as definePlugin, c as DEFAULT_STUDIO_URL, i as defineResolver, l as logLevel, n as applyHookResult, o as DEFAULT_BANNER, r as FileManager, s as DEFAULT_EXTENSION, t as PluginDriver, u as camelCase } from "./PluginDriver-CGypdXHg.js";
1
+ import "./chunk--u3MIqq1.js";
2
+ import { a as definePlugin, c as DEFAULT_STUDIO_URL, i as defineResolver, l as logLevel, n as applyHookResult, o as DEFAULT_BANNER, r as FileManager, s as DEFAULT_EXTENSION, t as PluginDriver, u as camelCase } from "./PluginDriver-CT33kVoQ.js";
3
3
  import { EventEmitter } from "node:events";
4
4
  import { access, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
5
5
  import { dirname, join, resolve } from "node:path";
@@ -525,7 +525,7 @@ function createAdapter(build) {
525
525
  }
526
526
  //#endregion
527
527
  //#region package.json
528
- var version$1 = "5.0.0-beta.14";
528
+ var version$1 = "5.0.0-beta.16";
529
529
  //#endregion
530
530
  //#region src/createStorage.ts
531
531
  /**
@@ -564,136 +564,6 @@ function createStorage(build) {
564
564
  return (options) => build(options ?? {});
565
565
  }
566
566
  //#endregion
567
- //#region ../../node_modules/.pnpm/yocto-queue@1.2.2/node_modules/yocto-queue/index.js
568
- var Node$1 = class {
569
- static {
570
- __name(this, "Node");
571
- }
572
- value;
573
- next;
574
- constructor(value) {
575
- this.value = value;
576
- }
577
- };
578
- var Queue = class {
579
- #head;
580
- #tail;
581
- #size;
582
- constructor() {
583
- this.clear();
584
- }
585
- enqueue(value) {
586
- const node = new Node$1(value);
587
- if (this.#head) {
588
- this.#tail.next = node;
589
- this.#tail = node;
590
- } else {
591
- this.#head = node;
592
- this.#tail = node;
593
- }
594
- this.#size++;
595
- }
596
- dequeue() {
597
- const current = this.#head;
598
- if (!current) return;
599
- this.#head = this.#head.next;
600
- this.#size--;
601
- if (!this.#head) this.#tail = void 0;
602
- return current.value;
603
- }
604
- peek() {
605
- if (!this.#head) return;
606
- return this.#head.value;
607
- }
608
- clear() {
609
- this.#head = void 0;
610
- this.#tail = void 0;
611
- this.#size = 0;
612
- }
613
- get size() {
614
- return this.#size;
615
- }
616
- *[Symbol.iterator]() {
617
- let current = this.#head;
618
- while (current) {
619
- yield current.value;
620
- current = current.next;
621
- }
622
- }
623
- *drain() {
624
- while (this.#head) yield this.dequeue();
625
- }
626
- };
627
- //#endregion
628
- //#region ../../node_modules/.pnpm/p-limit@7.3.0/node_modules/p-limit/index.js
629
- function pLimit(concurrency) {
630
- let rejectOnClear = false;
631
- if (typeof concurrency === "object") ({concurrency, rejectOnClear = false} = concurrency);
632
- validateConcurrency(concurrency);
633
- if (typeof rejectOnClear !== "boolean") throw new TypeError("Expected `rejectOnClear` to be a boolean");
634
- const queue = new Queue();
635
- let activeCount = 0;
636
- const resumeNext = () => {
637
- if (activeCount < concurrency && queue.size > 0) {
638
- activeCount++;
639
- queue.dequeue().run();
640
- }
641
- };
642
- const next = () => {
643
- activeCount--;
644
- resumeNext();
645
- };
646
- const run = async (function_, resolve, arguments_) => {
647
- const result = (async () => function_(...arguments_))();
648
- resolve(result);
649
- try {
650
- await result;
651
- } catch {}
652
- next();
653
- };
654
- const enqueue = (function_, resolve, reject, arguments_) => {
655
- const queueItem = { reject };
656
- new Promise((internalResolve) => {
657
- queueItem.run = internalResolve;
658
- queue.enqueue(queueItem);
659
- }).then(run.bind(void 0, function_, resolve, arguments_));
660
- if (activeCount < concurrency) resumeNext();
661
- };
662
- const generator = (function_, ...arguments_) => new Promise((resolve, reject) => {
663
- enqueue(function_, resolve, reject, arguments_);
664
- });
665
- Object.defineProperties(generator, {
666
- activeCount: { get: () => activeCount },
667
- pendingCount: { get: () => queue.size },
668
- clearQueue: { value() {
669
- if (!rejectOnClear) {
670
- queue.clear();
671
- return;
672
- }
673
- const abortError = AbortSignal.abort().reason;
674
- while (queue.size > 0) queue.dequeue().reject(abortError);
675
- } },
676
- concurrency: {
677
- get: () => concurrency,
678
- set(newConcurrency) {
679
- validateConcurrency(newConcurrency);
680
- concurrency = newConcurrency;
681
- queueMicrotask(() => {
682
- while (activeCount < concurrency && queue.size > 0) resumeNext();
683
- });
684
- }
685
- },
686
- map: { async value(iterable, function_) {
687
- const promises = Array.from(iterable, (value, index) => this(function_, value, index));
688
- return Promise.all(promises);
689
- } }
690
- });
691
- return generator;
692
- }
693
- function validateConcurrency(concurrency) {
694
- if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) throw new TypeError("Expected `concurrency` to be a number from 1 and up");
695
- }
696
- //#endregion
697
567
  //#region src/FileProcessor.ts
698
568
  function joinSources(file) {
699
569
  return file.sources.map((item) => extractStringsFromNodes(item.nodes)).filter(Boolean).join("\n\n");
@@ -706,7 +576,6 @@ function joinSources(file) {
706
576
  */
707
577
  var FileProcessor = class {
708
578
  events = new AsyncEventEmitter();
709
- #limit = pLimit(16);
710
579
  async parse(file, { parsers, extension } = {}) {
711
580
  const parseExtName = extension?.[file.extname] || void 0;
712
581
  if (!parsers || !file.extname) return joinSources(file);
@@ -714,27 +583,37 @@ var FileProcessor = class {
714
583
  if (!parser) return joinSources(file);
715
584
  return parser.parse(file, { extname: parseExtName });
716
585
  }
717
- async run(files, { parsers, mode = "sequential", extension } = {}) {
718
- await this.events.emit("start", files);
586
+ /**
587
+ * Streams parsed files one at a time as each is processed.
588
+ *
589
+ * Unlike `run()`, files are yielded immediately after parsing rather than batched.
590
+ * Storage writes can begin as soon as the first file is ready, keeping peak
591
+ * memory proportional to one file at a time instead of the full batch.
592
+ */
593
+ async *stream(files, options = {}) {
719
594
  const total = files.length;
720
595
  let processed = 0;
721
- const processOne = async (file) => {
722
- const source = await this.parse(file, {
723
- extension,
724
- parsers
725
- });
726
- const currentProcessed = ++processed;
727
- const percentage = currentProcessed / total * 100;
728
- await this.events.emit("update", {
596
+ for (const file of files) {
597
+ const source = await this.parse(file, options);
598
+ processed++;
599
+ yield {
729
600
  file,
730
601
  source,
731
- processed: currentProcessed,
732
- percentage,
733
- total
734
- });
735
- };
736
- if (mode === "sequential") for (const file of files) await processOne(file);
737
- else await Promise.all(files.map((file) => this.#limit(() => processOne(file))));
602
+ processed,
603
+ total,
604
+ percentage: processed / total * 100
605
+ };
606
+ }
607
+ }
608
+ async run(files, options = {}) {
609
+ await this.events.emit("start", files);
610
+ for await (const { file, source, processed, total, percentage } of this.stream(files, options)) await this.events.emit("update", {
611
+ file,
612
+ source,
613
+ processed,
614
+ percentage,
615
+ total
616
+ });
738
617
  await this.events.emit("end", files);
739
618
  return files;
740
619
  }
@@ -790,9 +669,8 @@ const fsStorage = createStorage(() => ({
790
669
  await rm(resolve(key), { force: true });
791
670
  },
792
671
  async getKeys(base) {
793
- const keys = [];
794
672
  const resolvedBase = resolve(base ?? process.cwd());
795
- async function walk(dir, prefix) {
673
+ async function* walk(dir, prefix) {
796
674
  let entries;
797
675
  try {
798
676
  entries = await readdir(dir, { withFileTypes: true });
@@ -801,11 +679,12 @@ const fsStorage = createStorage(() => ({
801
679
  }
802
680
  for (const entry of entries) {
803
681
  const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
804
- if (entry.isDirectory()) await walk(join(dir, entry.name), rel);
805
- else keys.push(rel);
682
+ if (entry.isDirectory()) yield* walk(join(dir, entry.name), rel);
683
+ else yield rel;
806
684
  }
807
685
  }
808
- await walk(resolvedBase, "");
686
+ const keys = [];
687
+ for await (const key of walk(resolvedBase, "")) keys.push(key);
809
688
  return keys;
810
689
  },
811
690
  async clear(base) {
@@ -930,15 +809,40 @@ async function setup(userConfig, options = {}) {
930
809
  logs: [`Running adapter: ${config.adapter.name}`]
931
810
  });
932
811
  driver.adapter = config.adapter;
933
- driver.inputNode = await config.adapter.parse(source);
934
- await hooks.emit("kubb:debug", {
935
- date: /* @__PURE__ */ new Date(),
936
- logs: [
937
- `✓ Adapter '${config.adapter.name}' resolved InputNode`,
938
- ` • Schemas: ${driver.inputNode.schemas.length}`,
939
- ` • Operations: ${driver.inputNode.operations.length}`
940
- ]
941
- });
812
+ if (config.adapter.count && config.adapter.stream) {
813
+ const { schemas: schemaCount, operations: operationCount } = await config.adapter.count(source);
814
+ if (schemaCount > 100) {
815
+ driver.inputStreamNode = await config.adapter.stream(source);
816
+ await hooks.emit("kubb:debug", {
817
+ date: /* @__PURE__ */ new Date(),
818
+ logs: [
819
+ `✓ Adapter '${config.adapter.name}' streaming InputStreamNode`,
820
+ ` • Schemas: ${schemaCount} (threshold: 100)`,
821
+ ` • Operations: ${operationCount}`
822
+ ]
823
+ });
824
+ } else {
825
+ driver.inputNode = await config.adapter.parse(source);
826
+ await hooks.emit("kubb:debug", {
827
+ date: /* @__PURE__ */ new Date(),
828
+ logs: [
829
+ `✓ Adapter '${config.adapter.name}' resolved InputNode`,
830
+ ` • Schemas: ${driver.inputNode.schemas.length}`,
831
+ ` • Operations: ${driver.inputNode.operations.length}`
832
+ ]
833
+ });
834
+ }
835
+ } else {
836
+ driver.inputNode = await config.adapter.parse(source);
837
+ await hooks.emit("kubb:debug", {
838
+ date: /* @__PURE__ */ new Date(),
839
+ logs: [
840
+ `✓ Adapter '${config.adapter.name}' resolved InputNode`,
841
+ ` • Schemas: ${driver.inputNode.schemas.length}`,
842
+ ` • Operations: ${driver.inputNode.operations.length}`
843
+ ]
844
+ });
845
+ }
942
846
  }
943
847
  return {
944
848
  config,
@@ -952,15 +856,134 @@ async function setup(userConfig, options = {}) {
952
856
  };
953
857
  }
954
858
  /**
955
- * Walks the AST and dispatches nodes to a plugin's direct AST hooks
956
- * (`schema`, `operation`, `operations`).
859
+ * Single-pass fan-out for streaming mode.
957
860
  *
958
- * When `include` contains only operation-scoped filters (`tag`, `operationId`, `path`,
959
- * `method`, `contentType`) and no `schemaName` filter, the function pre-computes the set
960
- * of top-level schema names transitively reachable from the included operations and skips
961
- * schemas that fall outside that set. This ensures that component schemas referenced
962
- * exclusively by excluded operations are not generated.
861
+ * Iterates `inputStreamNode.schemas` and `.operations` exactly once, distributing each
862
+ * node to every plugin in parallel. This replaces the N-pass-per-plugin pattern (where
863
+ * each plugin got its own `for await` iterator) with a single parse pass fanned to all
864
+ * plugins eliminating the N×parse-time overhead for multi-plugin builds.
865
+ *
866
+ * Each plugin still gets independent `plugin:start` / `plugin:end` events and its own
867
+ * timing, but the schema and operation nodes are parsed only once total.
963
868
  */
869
+ async function runPluginStreamHooks(inputStreamNode, entries, driver, hooks, config, pluginTimings, failedPlugins, flushPendingFiles) {
870
+ function resolveRendererFor(gen, state) {
871
+ return gen.renderer === null ? void 0 : gen.renderer ?? state.plugin.renderer ?? state.generatorContext.config.renderer;
872
+ }
873
+ const states = entries.map(({ plugin, context, hrStart }) => ({
874
+ plugin,
875
+ generatorContext: {
876
+ ...context,
877
+ resolver: driver.getResolver(plugin.name)
878
+ },
879
+ generators: plugin.generators ?? [],
880
+ hrStart,
881
+ failed: false,
882
+ error: void 0
883
+ }));
884
+ let schemasProcessed = 0;
885
+ for await (const node of inputStreamNode.schemas) {
886
+ for (const state of states) {
887
+ if (state.failed) continue;
888
+ try {
889
+ const { plugin, generatorContext, generators } = state;
890
+ const { exclude, include, override } = plugin.options;
891
+ const transformedNode = plugin.transformer ? transform(node, plugin.transformer) : node;
892
+ const options = generatorContext.resolver.resolveOptions(transformedNode, {
893
+ options: plugin.options,
894
+ exclude,
895
+ include,
896
+ override
897
+ });
898
+ if (options === null) continue;
899
+ const ctx = {
900
+ ...generatorContext,
901
+ options
902
+ };
903
+ for (const gen of generators) {
904
+ if (!gen.schema) continue;
905
+ await applyHookResult(await gen.schema(transformedNode, ctx), driver, resolveRendererFor(gen, state));
906
+ }
907
+ await driver.hooks.emit("kubb:generate:schema", transformedNode, ctx);
908
+ } catch (caughtError) {
909
+ state.failed = true;
910
+ state.error = caughtError;
911
+ }
912
+ }
913
+ schemasProcessed++;
914
+ if (schemasProcessed % 50 === 0) await flushPendingFiles();
915
+ }
916
+ const collectedOperations = [];
917
+ for await (const node of inputStreamNode.operations) {
918
+ collectedOperations.push(node);
919
+ for (const state of states) {
920
+ if (state.failed) continue;
921
+ try {
922
+ const { plugin, generatorContext, generators } = state;
923
+ const { exclude, include, override } = plugin.options;
924
+ const transformedNode = plugin.transformer ? transform(node, plugin.transformer) : node;
925
+ const options = generatorContext.resolver.resolveOptions(transformedNode, {
926
+ options: plugin.options,
927
+ exclude,
928
+ include,
929
+ override
930
+ });
931
+ if (options === null) continue;
932
+ const ctx = {
933
+ ...generatorContext,
934
+ options
935
+ };
936
+ for (const gen of generators) {
937
+ if (!gen.operation) continue;
938
+ await applyHookResult(await gen.operation(transformedNode, ctx), driver, resolveRendererFor(gen, state));
939
+ }
940
+ await driver.hooks.emit("kubb:generate:operation", transformedNode, ctx);
941
+ } catch (caughtError) {
942
+ state.failed = true;
943
+ state.error = caughtError;
944
+ }
945
+ }
946
+ }
947
+ for (const state of states) {
948
+ if (!state.failed) try {
949
+ const { plugin, generatorContext, generators } = state;
950
+ const ctx = {
951
+ ...generatorContext,
952
+ options: plugin.options
953
+ };
954
+ for (const gen of generators) {
955
+ if (!gen.operations) continue;
956
+ await applyHookResult(await gen.operations(collectedOperations, ctx), driver, resolveRendererFor(gen, state));
957
+ }
958
+ await driver.hooks.emit("kubb:generate:operations", collectedOperations, ctx);
959
+ } catch (caughtError) {
960
+ state.failed = true;
961
+ state.error = caughtError;
962
+ }
963
+ const duration = getElapsedMs(state.hrStart);
964
+ pluginTimings.set(state.plugin.name, duration);
965
+ await hooks.emit("kubb:plugin:end", {
966
+ plugin: state.plugin,
967
+ duration,
968
+ success: !state.failed,
969
+ ...state.failed && state.error ? { error: state.error } : {},
970
+ config,
971
+ get files() {
972
+ return driver.fileManager.files;
973
+ },
974
+ upsertFile: (...files) => driver.fileManager.upsert(...files)
975
+ });
976
+ if (state.failed && state.error) failedPlugins.add({
977
+ plugin: state.plugin,
978
+ error: state.error
979
+ });
980
+ await hooks.emit("kubb:debug", {
981
+ date: /* @__PURE__ */ new Date(),
982
+ logs: [state.failed ? "✗ Plugin start failed" : `✓ Plugin started successfully (${formatMs(duration)})`]
983
+ });
984
+ }
985
+ await flushPendingFiles();
986
+ }
964
987
  async function runPluginAstHooks(plugin, context) {
965
988
  const { adapter, inputNode, resolver, driver } = context;
966
989
  const { exclude, include, override } = plugin.options;
@@ -1006,10 +1029,7 @@ async function runPluginAstHooks(plugin, context) {
1006
1029
  ...generatorContext,
1007
1030
  options
1008
1031
  };
1009
- for (const gen of generators) {
1010
- if (!gen.schema) continue;
1011
- await applyHookResult(await gen.schema(transformedNode, ctx), driver, resolveRenderer(gen));
1012
- }
1032
+ await Promise.all(generators.filter((gen) => gen.schema).map((gen) => Promise.resolve(gen.schema(transformedNode, ctx)).then((result) => applyHookResult(result, driver, resolveRenderer(gen)))));
1013
1033
  await driver.hooks.emit("kubb:generate:schema", transformedNode, ctx);
1014
1034
  },
1015
1035
  async operation(node) {
@@ -1026,10 +1046,7 @@ async function runPluginAstHooks(plugin, context) {
1026
1046
  ...generatorContext,
1027
1047
  options
1028
1048
  };
1029
- for (const gen of generators) {
1030
- if (!gen.operation) continue;
1031
- await applyHookResult(await gen.operation(transformedNode, ctx), driver, resolveRenderer(gen));
1032
- }
1049
+ await Promise.all(generators.filter((gen) => gen.operation).map((gen) => Promise.resolve(gen.operation(transformedNode, ctx)).then((result) => applyHookResult(result, driver, resolveRenderer(gen)))));
1033
1050
  await driver.hooks.emit("kubb:generate:operation", transformedNode, ctx);
1034
1051
  }
1035
1052
  }
@@ -1055,54 +1072,90 @@ async function safeBuild(setupResult) {
1055
1072
  const parsersMap = /* @__PURE__ */ new Map();
1056
1073
  for (const parser of config.parsers) if (parser.extNames) for (const extname of parser.extNames) parsersMap.set(extname, parser);
1057
1074
  const fileProcessor = new FileProcessor();
1058
- fileProcessor.events.on("start", async (processingFiles) => {
1059
- await hooks.emit("kubb:files:processing:start", { files: processingFiles });
1060
- });
1061
- fileProcessor.events.on("update", async ({ file, source, processed, total, percentage }) => {
1062
- await hooks.emit("kubb:file:processing:update", {
1063
- file,
1064
- source,
1065
- processed,
1066
- total,
1067
- percentage,
1068
- config
1069
- });
1070
- if (source) await storage.setItem(file.path, source);
1071
- });
1072
- fileProcessor.events.on("end", async (processed) => {
1073
- await hooks.emit("kubb:files:processing:end", { files: processed });
1074
- await hooks.emit("kubb:debug", {
1075
- date: /* @__PURE__ */ new Date(),
1076
- logs: [`✓ File write process completed for ${processed.length} files`]
1077
- });
1078
- });
1079
- async function flushPendingFiles() {
1080
- const files = driver.fileManager.files.filter((f) => !writtenPaths.has(f.path));
1075
+ async function flushPendingFiles(snapshot) {
1076
+ const files = driver.fileManager.files.filter((f) => !writtenPaths.has(f.path) && (!snapshot || !snapshot.has(f.path)));
1081
1077
  if (files.length === 0) return;
1082
1078
  await hooks.emit("kubb:debug", {
1083
1079
  date: /* @__PURE__ */ new Date(),
1084
1080
  logs: [`Writing ${files.length} files...`]
1085
1081
  });
1086
- await fileProcessor.run(files, {
1082
+ await hooks.emit("kubb:files:processing:start", { files });
1083
+ const stream = fileProcessor.stream(files, {
1087
1084
  parsers: parsersMap,
1088
- mode: "parallel",
1089
1085
  extension: config.output.extension
1090
1086
  });
1091
- for (const file of files) writtenPaths.add(file.path);
1087
+ for await (const { file, source, processed, total, percentage } of stream) {
1088
+ await hooks.emit("kubb:file:processing:update", {
1089
+ file,
1090
+ source,
1091
+ processed,
1092
+ total,
1093
+ percentage,
1094
+ config
1095
+ });
1096
+ if (source) await storage.setItem(file.path, source);
1097
+ writtenPaths.add(file.path);
1098
+ }
1099
+ await hooks.emit("kubb:files:processing:end", { files });
1100
+ await hooks.emit("kubb:debug", {
1101
+ date: /* @__PURE__ */ new Date(),
1102
+ logs: [`✓ File write process completed for ${files.length} files`]
1103
+ });
1092
1104
  }
1093
1105
  try {
1094
1106
  await driver.emitSetupHooks();
1095
- if (driver.adapter && driver.inputNode) await hooks.emit("kubb:build:start", {
1107
+ if (driver.adapter && (driver.inputNode || driver.inputStreamNode)) await hooks.emit("kubb:build:start", {
1096
1108
  config,
1097
1109
  adapter: driver.adapter,
1098
- inputNode: driver.inputNode,
1110
+ inputNode: driver.inputNode ?? {
1111
+ kind: "Input",
1112
+ schemas: [],
1113
+ operations: [],
1114
+ meta: driver.inputStreamNode?.meta
1115
+ },
1099
1116
  getPlugin: driver.getPlugin.bind(driver),
1100
1117
  get files() {
1101
1118
  return driver.fileManager.files;
1102
1119
  },
1103
1120
  upsertFile: (...files) => driver.fileManager.upsert(...files)
1104
1121
  });
1105
- for (const plugin of driver.plugins.values()) {
1122
+ const inputStreamNode = driver.inputStreamNode;
1123
+ if (inputStreamNode) {
1124
+ const streamPluginEntries = [];
1125
+ for (const plugin of driver.plugins.values()) {
1126
+ const context = driver.getContext(plugin);
1127
+ const hrStart = process.hrtime();
1128
+ await hooks.emit("kubb:plugin:start", { plugin });
1129
+ await hooks.emit("kubb:debug", {
1130
+ date: /* @__PURE__ */ new Date(),
1131
+ logs: ["Starting plugin...", ` • Plugin Name: ${plugin.name}`]
1132
+ });
1133
+ if (plugin.generators?.length || driver.hasRegisteredGenerators(plugin.name)) streamPluginEntries.push({
1134
+ plugin,
1135
+ context,
1136
+ hrStart
1137
+ });
1138
+ else {
1139
+ const duration = getElapsedMs(hrStart);
1140
+ pluginTimings.set(plugin.name, duration);
1141
+ await hooks.emit("kubb:plugin:end", {
1142
+ plugin,
1143
+ duration,
1144
+ success: true,
1145
+ config,
1146
+ get files() {
1147
+ return driver.fileManager.files;
1148
+ },
1149
+ upsertFile: (...files) => driver.fileManager.upsert(...files)
1150
+ });
1151
+ await hooks.emit("kubb:debug", {
1152
+ date: /* @__PURE__ */ new Date(),
1153
+ logs: [`✓ Plugin started successfully (${formatMs(duration)})`]
1154
+ });
1155
+ }
1156
+ }
1157
+ if (streamPluginEntries.length > 0) await runPluginStreamHooks(inputStreamNode, streamPluginEntries, driver, hooks, config, pluginTimings, failedPlugins, flushPendingFiles);
1158
+ } else for (const plugin of driver.plugins.values()) {
1106
1159
  const context = driver.getContext(plugin);
1107
1160
  const hrStart = process.hrtime();
1108
1161
  try {
@@ -1125,7 +1178,6 @@ async function safeBuild(setupResult) {
1125
1178
  },
1126
1179
  upsertFile: (...files) => driver.fileManager.upsert(...files)
1127
1180
  });
1128
- await flushPendingFiles();
1129
1181
  await hooks.emit("kubb:debug", {
1130
1182
  date: /* @__PURE__ */ new Date(),
1131
1183
  logs: [`✓ Plugin started successfully (${formatMs(duration)})`]
@@ -1145,7 +1197,6 @@ async function safeBuild(setupResult) {
1145
1197
  },
1146
1198
  upsertFile: (...files) => driver.fileManager.upsert(...files)
1147
1199
  });
1148
- await flushPendingFiles();
1149
1200
  await hooks.emit("kubb:debug", {
1150
1201
  date: errorTimestamp,
1151
1202
  logs: [
@@ -1161,6 +1212,7 @@ async function safeBuild(setupResult) {
1161
1212
  error
1162
1213
  });
1163
1214
  }
1215
+ await flushPendingFiles();
1164
1216
  }
1165
1217
  await hooks.emit("kubb:plugins:end", {
1166
1218
  config,