@kubb/core 5.0.0-beta.1 → 5.0.0-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +9 -39
  2. package/dist/{PluginDriver-BXibeQk-.cjs → PluginDriver-C1OsqGBJ.cjs} +106 -56
  3. package/dist/PluginDriver-C1OsqGBJ.cjs.map +1 -0
  4. package/dist/{PluginDriver-DV3p2Hky.js → PluginDriver-CGypdXHg.js} +101 -57
  5. package/dist/PluginDriver-CGypdXHg.js.map +1 -0
  6. package/dist/{types-CuNocrbJ.d.ts → createKubb-BSfMDBwR.d.ts} +1533 -1505
  7. package/dist/index.cjs +249 -209
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.ts +2 -185
  10. package/dist/index.js +249 -209
  11. package/dist/index.js.map +1 -1
  12. package/dist/mocks.cjs +1 -1
  13. package/dist/mocks.cjs.map +1 -1
  14. package/dist/mocks.d.ts +1 -1
  15. package/dist/mocks.js +1 -1
  16. package/dist/mocks.js.map +1 -1
  17. package/package.json +5 -12
  18. package/src/FileManager.ts +8 -0
  19. package/src/FileProcessor.ts +12 -7
  20. package/src/PluginDriver.ts +49 -7
  21. package/src/constants.ts +6 -2
  22. package/src/createAdapter.ts +77 -1
  23. package/src/createKubb.ts +973 -141
  24. package/src/defineGenerator.ts +92 -4
  25. package/src/defineLogger.ts +42 -3
  26. package/src/defineMiddleware.ts +1 -1
  27. package/src/definePlugin.ts +304 -8
  28. package/src/defineResolver.ts +185 -52
  29. package/src/devtools.ts +8 -1
  30. package/src/index.ts +1 -1
  31. package/src/mocks.ts +1 -2
  32. package/src/storages/fsStorage.ts +6 -31
  33. package/src/types.ts +38 -1292
  34. package/dist/PluginDriver-BXibeQk-.cjs.map +0 -1
  35. package/dist/PluginDriver-DV3p2Hky.js.map +0 -1
  36. package/src/Kubb.ts +0 -300
  37. package/src/renderNode.ts +0 -35
  38. package/src/utils/diagnostics.ts +0 -18
  39. package/src/utils/isInputPath.ts +0 -10
  40. package/src/utils/packageJSON.ts +0 -99
package/dist/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_PluginDriver = require("./PluginDriver-BXibeQk-.cjs");
2
+ const require_PluginDriver = require("./PluginDriver-C1OsqGBJ.cjs");
3
3
  let node_events = require("node:events");
4
4
  let node_fs_promises = require("node:fs/promises");
5
5
  let node_path = require("node:path");
@@ -524,6 +524,46 @@ function createAdapter(build) {
524
524
  return (options) => build(options ?? {});
525
525
  }
526
526
  //#endregion
527
+ //#region package.json
528
+ var version = "5.0.0-beta.11";
529
+ //#endregion
530
+ //#region src/createStorage.ts
531
+ /**
532
+ * Factory for implementing custom storage backends that control where generated files are written.
533
+ *
534
+ * Takes a builder function `(options: TOptions) => Storage` and returns a factory `(options?: TOptions) => Storage`.
535
+ * Kubb provides filesystem and in-memory implementations out of the box.
536
+ *
537
+ * @note Call the returned factory with optional options to instantiate the storage adapter.
538
+ *
539
+ * @example
540
+ * ```ts
541
+ * import { createStorage } from '@kubb/core'
542
+ *
543
+ * export const memoryStorage = createStorage(() => {
544
+ * const store = new Map<string, string>()
545
+ * return {
546
+ * name: 'memory',
547
+ * async hasItem(key) { return store.has(key) },
548
+ * async getItem(key) { return store.get(key) ?? null },
549
+ * async setItem(key, value) { store.set(key, value) },
550
+ * async removeItem(key) { store.delete(key) },
551
+ * async getKeys(base) {
552
+ * const keys = [...store.keys()]
553
+ * return base ? keys.filter((k) => k.startsWith(base)) : keys
554
+ * },
555
+ * async clear(base) { if (!base) store.clear() },
556
+ * }
557
+ * })
558
+ *
559
+ * // Instantiate:
560
+ * const storage = memoryStorage()
561
+ * ```
562
+ */
563
+ function createStorage(build) {
564
+ return (options) => build(options ?? {});
565
+ }
566
+ //#endregion
527
567
  //#region ../../node_modules/.pnpm/yocto-queue@1.2.2/node_modules/yocto-queue/index.js
528
568
  var Node = class {
529
569
  value;
@@ -662,7 +702,8 @@ function joinSources(file) {
662
702
  * @internal
663
703
  */
664
704
  var FileProcessor = class {
665
- #limit = pLimit(100);
705
+ events = new AsyncEventEmitter();
706
+ #limit = pLimit(16);
666
707
  async parse(file, { parsers, extension } = {}) {
667
708
  const parseExtName = extension?.[file.extname] || void 0;
668
709
  if (!parsers || !file.extname) return joinSources(file);
@@ -670,8 +711,8 @@ var FileProcessor = class {
670
711
  if (!parser) return joinSources(file);
671
712
  return parser.parse(file, { extname: parseExtName });
672
713
  }
673
- async run(files, { parsers, mode = "sequential", extension, onStart, onEnd, onUpdate } = {}) {
674
- await onStart?.(files);
714
+ async run(files, { parsers, mode = "sequential", extension } = {}) {
715
+ await this.events.emit("start", files);
675
716
  const total = files.length;
676
717
  let processed = 0;
677
718
  const processOne = async (file) => {
@@ -681,7 +722,7 @@ var FileProcessor = class {
681
722
  });
682
723
  const currentProcessed = ++processed;
683
724
  const percentage = currentProcessed / total * 100;
684
- await onUpdate?.({
725
+ await this.events.emit("update", {
685
726
  file,
686
727
  source,
687
728
  processed: currentProcessed,
@@ -691,56 +732,13 @@ var FileProcessor = class {
691
732
  };
692
733
  if (mode === "sequential") for (const file of files) await processOne(file);
693
734
  else await Promise.all(files.map((file) => this.#limit(() => processOne(file))));
694
- await onEnd?.(files);
735
+ await this.events.emit("end", files);
695
736
  return files;
696
737
  }
697
738
  };
698
739
  //#endregion
699
- //#region src/createStorage.ts
700
- /**
701
- * Factory for implementing custom storage backends that control where generated files are written.
702
- *
703
- * Takes a builder function `(options: TOptions) => Storage` and returns a factory `(options?: TOptions) => Storage`.
704
- * Kubb provides filesystem and in-memory implementations out of the box.
705
- *
706
- * @note Call the returned factory with optional options to instantiate the storage adapter.
707
- *
708
- * @example
709
- * ```ts
710
- * import { createStorage } from '@kubb/core'
711
- *
712
- * export const memoryStorage = createStorage(() => {
713
- * const store = new Map<string, string>()
714
- * return {
715
- * name: 'memory',
716
- * async hasItem(key) { return store.has(key) },
717
- * async getItem(key) { return store.get(key) ?? null },
718
- * async setItem(key, value) { store.set(key, value) },
719
- * async removeItem(key) { store.delete(key) },
720
- * async getKeys(base) {
721
- * const keys = [...store.keys()]
722
- * return base ? keys.filter((k) => k.startsWith(base)) : keys
723
- * },
724
- * async clear(base) { if (!base) store.clear() },
725
- * }
726
- * })
727
- *
728
- * // Instantiate:
729
- * const storage = memoryStorage()
730
- * ```
731
- */
732
- function createStorage(build) {
733
- return (options) => build(options ?? {});
734
- }
735
- //#endregion
736
740
  //#region src/storages/fsStorage.ts
737
741
  /**
738
- * Detects the filesystem error used to indicate that a path does not exist.
739
- */
740
- function isMissingPathError(error) {
741
- return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
742
- }
743
- /**
744
742
  * Built-in filesystem storage driver.
745
743
  *
746
744
  * This is the default storage when no `storage` option is configured in the root config.
@@ -771,17 +769,15 @@ const fsStorage = createStorage(() => ({
771
769
  try {
772
770
  await (0, node_fs_promises.access)((0, node_path.resolve)(key));
773
771
  return true;
774
- } catch (error) {
775
- if (isMissingPathError(error)) return false;
776
- throw new Error(`Failed to access storage item "${key}"`, { cause: error });
772
+ } catch (_error) {
773
+ return false;
777
774
  }
778
775
  },
779
776
  async getItem(key) {
780
777
  try {
781
778
  return await (0, node_fs_promises.readFile)((0, node_path.resolve)(key), "utf8");
782
- } catch (error) {
783
- if (isMissingPathError(error)) return null;
784
- throw new Error(`Failed to read storage item "${key}"`, { cause: error });
779
+ } catch (_error) {
780
+ return null;
785
781
  }
786
782
  },
787
783
  async setItem(key, value) {
@@ -797,9 +793,8 @@ const fsStorage = createStorage(() => ({
797
793
  let entries;
798
794
  try {
799
795
  entries = await (0, node_fs_promises.readdir)(dir, { withFileTypes: true });
800
- } catch (error) {
801
- if (isMissingPathError(error)) return;
802
- throw new Error(`Failed to list storage keys under "${resolvedBase}"`, { cause: error });
796
+ } catch (_error) {
797
+ return;
803
798
  }
804
799
  for (const entry of entries) {
805
800
  const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
@@ -816,37 +811,69 @@ const fsStorage = createStorage(() => ({
816
811
  }
817
812
  }));
818
813
  //#endregion
819
- //#region package.json
820
- var version = "5.0.0-beta.1";
821
- //#endregion
822
- //#region src/utils/diagnostics.ts
814
+ //#region src/createKubb.ts
823
815
  /**
824
- * Returns a snapshot of the current runtime environment.
816
+ * Builds a `Storage` view scoped to the file paths produced by the current build.
825
817
  *
826
- * Useful for attaching context to debug logs and error reports so that
827
- * issues can be reproduced without manual information gathering.
818
+ * Reads delegate to the underlying `storage` (typically `fsStorage()`) so source bytes
819
+ * stay where they were written instead of being held in an extra in-memory map.
820
+ * Writing via `setItem` stores the content in the underlying storage and registers the
821
+ * key so subsequent reads and `getKeys` are scoped to this build's output.
828
822
  */
829
- function getDiagnosticInfo() {
830
- return {
831
- nodeVersion: node_process.version,
832
- KubbVersion: version,
833
- platform: process.platform,
834
- arch: process.arch,
835
- cwd: process.cwd()
836
- };
837
- }
838
- //#endregion
839
- //#region src/utils/isInputPath.ts
840
- function isInputPath(config) {
841
- return typeof config?.input === "object" && config.input !== null && "path" in config.input;
823
+ function createSourcesView(storage) {
824
+ const paths = /* @__PURE__ */ new Set();
825
+ return createStorage(() => ({
826
+ name: `${storage.name}:sources`,
827
+ async hasItem(key) {
828
+ return paths.has(key) && await storage.hasItem(key);
829
+ },
830
+ async getItem(key) {
831
+ return paths.has(key) ? storage.getItem(key) : null;
832
+ },
833
+ async setItem(key, value) {
834
+ paths.add(key);
835
+ await storage.setItem(key, value);
836
+ },
837
+ async removeItem(key) {
838
+ paths.delete(key);
839
+ await storage.removeItem(key);
840
+ },
841
+ async getKeys(base) {
842
+ if (!base) return [...paths];
843
+ const result = [];
844
+ for (const key of paths) if (key.startsWith(base)) result.push(key);
845
+ return result;
846
+ },
847
+ async clear() {
848
+ paths.clear();
849
+ await storage.clear();
850
+ }
851
+ }))();
842
852
  }
843
- //#endregion
844
- //#region src/createKubb.ts
845
853
  async function setup(userConfig, options = {}) {
846
854
  const hooks = options.hooks ?? new AsyncEventEmitter();
847
- const sources = /* @__PURE__ */ new Map();
855
+ const config = {
856
+ ...userConfig,
857
+ root: userConfig.root || process.cwd(),
858
+ parsers: userConfig.parsers ?? [],
859
+ adapter: userConfig.adapter,
860
+ output: {
861
+ format: false,
862
+ lint: false,
863
+ extension: require_PluginDriver.DEFAULT_EXTENSION,
864
+ defaultBanner: require_PluginDriver.DEFAULT_BANNER,
865
+ ...userConfig.output
866
+ },
867
+ storage: userConfig.storage ?? fsStorage(),
868
+ devtools: userConfig.devtools ? {
869
+ studioUrl: require_PluginDriver.DEFAULT_STUDIO_URL,
870
+ ...typeof userConfig.devtools === "boolean" ? {} : userConfig.devtools
871
+ } : void 0,
872
+ plugins: userConfig.plugins ?? []
873
+ };
874
+ const driver = new require_PluginDriver.PluginDriver(config, { hooks });
875
+ const storage = createSourcesView(config.storage);
848
876
  const diagnosticInfo = getDiagnosticInfo();
849
- if (Array.isArray(userConfig.input)) await hooks.emit("kubb:warn", { message: "This feature is still under development — use with caution" });
850
877
  await hooks.emit("kubb:debug", {
851
878
  date: /* @__PURE__ */ new Date(),
852
879
  logs: [
@@ -856,7 +883,7 @@ async function setup(userConfig, options = {}) {
856
883
  ` • Output: ${userConfig.output?.path || "not specified"}`,
857
884
  ` • Plugins: ${userConfig.plugins?.length || 0}`,
858
885
  "Output Settings:",
859
- ` • Storage: ${userConfig.storage ? `custom(${userConfig.storage.name})` : userConfig.output?.write === false ? "disabled" : "filesystem (default)"}`,
886
+ ` • Storage: ${config.storage.name}`,
860
887
  ` • Formatter: ${userConfig.output?.format || "none"}`,
861
888
  ` • Linter: ${userConfig.output?.lint || "none"}`,
862
889
  "Environment:",
@@ -877,73 +904,56 @@ async function setup(userConfig, options = {}) {
877
904
  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 });
878
905
  }
879
906
  }
880
- if (!userConfig.adapter) throw new Error("Adapter should be defined");
881
- const config = {
882
- ...userConfig,
883
- root: userConfig.root || process.cwd(),
884
- parsers: userConfig.parsers ?? [],
885
- adapter: userConfig.adapter,
886
- output: {
887
- format: false,
888
- lint: false,
889
- write: true,
890
- extension: require_PluginDriver.DEFAULT_EXTENSION,
891
- defaultBanner: require_PluginDriver.DEFAULT_BANNER,
892
- ...userConfig.output
893
- },
894
- devtools: userConfig.devtools ? {
895
- studioUrl: require_PluginDriver.DEFAULT_STUDIO_URL,
896
- ...typeof userConfig.devtools === "boolean" ? {} : userConfig.devtools
897
- } : void 0,
898
- plugins: userConfig.plugins
899
- };
900
- const storage = config.output.write === false ? null : config.storage ?? fsStorage();
901
907
  if (config.output.clean) {
902
908
  await hooks.emit("kubb:debug", {
903
909
  date: /* @__PURE__ */ new Date(),
904
910
  logs: ["Cleaning output directories", ` • Output: ${config.output.path}`]
905
911
  });
906
- await storage?.clear((0, node_path.resolve)(config.root, config.output.path));
912
+ await config.storage.clear((0, node_path.resolve)(config.root, config.output.path));
907
913
  }
908
- const driver = new require_PluginDriver.PluginDriver(config, { hooks });
909
914
  function registerMiddlewareHook(event, middlewareHooks) {
910
915
  const handler = middlewareHooks[event];
911
916
  if (handler) hooks.on(event, handler);
912
917
  }
913
918
  for (const middleware of config.middleware ?? []) for (const event of Object.keys(middleware.hooks)) registerMiddlewareHook(event, middleware.hooks);
914
- const adapter = config.adapter;
915
- if (!adapter) throw new Error("No adapter configured. Please provide an adapter in your kubb.config.ts.");
916
- const source = inputToAdapterSource(config);
917
- await hooks.emit("kubb:debug", {
918
- date: /* @__PURE__ */ new Date(),
919
- logs: [`Running adapter: ${adapter.name}`]
920
- });
921
- driver.adapter = adapter;
922
- driver.inputNode = await adapter.parse(source);
923
- await hooks.emit("kubb:debug", {
924
- date: /* @__PURE__ */ new Date(),
925
- logs: [
926
- `✓ Adapter '${adapter.name}' resolved InputNode`,
927
- ` • Schemas: ${driver.inputNode.schemas.length}`,
928
- ` • Operations: ${driver.inputNode.operations.length}`
929
- ]
930
- });
919
+ if (config.adapter) {
920
+ const source = inputToAdapterSource(config);
921
+ await hooks.emit("kubb:debug", {
922
+ date: /* @__PURE__ */ new Date(),
923
+ logs: [`Running adapter: ${config.adapter.name}`]
924
+ });
925
+ driver.adapter = config.adapter;
926
+ driver.inputNode = await config.adapter.parse(source);
927
+ await hooks.emit("kubb:debug", {
928
+ date: /* @__PURE__ */ new Date(),
929
+ logs: [
930
+ `✓ Adapter '${config.adapter.name}' resolved InputNode`,
931
+ ` • Schemas: ${driver.inputNode.schemas.length}`,
932
+ ` • Operations: ${driver.inputNode.operations.length}`
933
+ ]
934
+ });
935
+ }
931
936
  return {
932
937
  config,
933
938
  hooks,
934
939
  driver,
935
- sources,
936
940
  storage
937
941
  };
938
942
  }
939
943
  /**
940
944
  * Walks the AST and dispatches nodes to a plugin's direct AST hooks
941
945
  * (`schema`, `operation`, `operations`).
946
+ *
947
+ * When `include` contains only operation-scoped filters (`tag`, `operationId`, `path`,
948
+ * `method`, `contentType`) and no `schemaName` filter, the function pre-computes the set
949
+ * of top-level schema names transitively reachable from the included operations and skips
950
+ * schemas that fall outside that set. This ensures that component schemas referenced
951
+ * exclusively by excluded operations are not generated.
942
952
  */
943
953
  async function runPluginAstHooks(plugin, context) {
944
954
  const { adapter, inputNode, resolver, driver } = context;
945
955
  const { exclude, include, override } = plugin.options;
946
- if (!adapter || !inputNode) throw new Error(`[${plugin.name}] No adapter found. Add an OAS adapter (e.g. pluginOas()) before this plugin in your Kubb config.`);
956
+ if (!adapter || !inputNode) throw new Error(`[${plugin.name}] No adapter found. Add an OAS adapter (e.g. adapterOas()) before this plugin in your Kubb config.`);
947
957
  function resolveRenderer(gen) {
948
958
  return gen.renderer === null ? void 0 : gen.renderer ?? plugin.renderer ?? context.config.renderer;
949
959
  }
@@ -953,10 +963,27 @@ async function runPluginAstHooks(plugin, context) {
953
963
  ...context,
954
964
  resolver: driver.getResolver(plugin.name)
955
965
  };
966
+ const operationFilterTypes = new Set([
967
+ "tag",
968
+ "operationId",
969
+ "path",
970
+ "method",
971
+ "contentType"
972
+ ]);
973
+ const hasOperationBasedIncludes = include?.some(({ type }) => operationFilterTypes.has(type)) ?? false;
974
+ const hasSchemaNameIncludes = include?.some(({ type }) => type === "schemaName") ?? false;
975
+ let allowedSchemaNames;
976
+ if (hasOperationBasedIncludes && !hasSchemaNameIncludes) allowedSchemaNames = (0, _kubb_ast.collectUsedSchemaNames)(inputNode.operations.filter((op) => resolver.resolveOptions(op, {
977
+ options: plugin.options,
978
+ exclude,
979
+ include,
980
+ override
981
+ }) !== null), inputNode.schemas);
956
982
  await (0, _kubb_ast.walk)(inputNode, {
957
983
  depth: "shallow",
958
984
  async schema(node) {
959
985
  const transformedNode = plugin.transformer ? (0, _kubb_ast.transform)(node, plugin.transformer) : node;
986
+ if (allowedSchemaNames !== void 0 && transformedNode.name && !allowedSchemaNames.has(transformedNode.name)) return;
960
987
  const options = resolver.resolveOptions(transformedNode, {
961
988
  options: plugin.options,
962
989
  exclude,
@@ -1009,10 +1036,49 @@ async function runPluginAstHooks(plugin, context) {
1009
1036
  }
1010
1037
  }
1011
1038
  async function safeBuild(setupResult) {
1012
- const { driver, hooks, sources, storage } = setupResult;
1039
+ const { driver, hooks, storage } = setupResult;
1013
1040
  const failedPlugins = /* @__PURE__ */ new Set();
1014
1041
  const pluginTimings = /* @__PURE__ */ new Map();
1015
1042
  const config = driver.config;
1043
+ const writtenPaths = /* @__PURE__ */ new Set();
1044
+ const parsersMap = /* @__PURE__ */ new Map();
1045
+ for (const parser of config.parsers) if (parser.extNames) for (const extname of parser.extNames) parsersMap.set(extname, parser);
1046
+ const fileProcessor = new FileProcessor();
1047
+ fileProcessor.events.on("start", async (processingFiles) => {
1048
+ await hooks.emit("kubb:files:processing:start", { files: processingFiles });
1049
+ });
1050
+ fileProcessor.events.on("update", async ({ file, source, processed, total, percentage }) => {
1051
+ await hooks.emit("kubb:file:processing:update", {
1052
+ file,
1053
+ source,
1054
+ processed,
1055
+ total,
1056
+ percentage,
1057
+ config
1058
+ });
1059
+ if (source) await storage.setItem(file.path, source);
1060
+ });
1061
+ fileProcessor.events.on("end", async (processed) => {
1062
+ await hooks.emit("kubb:files:processing:end", { files: processed });
1063
+ await hooks.emit("kubb:debug", {
1064
+ date: /* @__PURE__ */ new Date(),
1065
+ logs: [`✓ File write process completed for ${processed.length} files`]
1066
+ });
1067
+ });
1068
+ async function flushPendingFiles() {
1069
+ const files = driver.fileManager.files.filter((f) => !writtenPaths.has(f.path));
1070
+ if (files.length === 0) return;
1071
+ await hooks.emit("kubb:debug", {
1072
+ date: /* @__PURE__ */ new Date(),
1073
+ logs: [`Writing ${files.length} files...`]
1074
+ });
1075
+ await fileProcessor.run(files, {
1076
+ parsers: parsersMap,
1077
+ mode: "parallel",
1078
+ extension: config.output.extension
1079
+ });
1080
+ for (const file of files) writtenPaths.add(file.path);
1081
+ }
1016
1082
  try {
1017
1083
  await driver.emitSetupHooks();
1018
1084
  if (driver.adapter && driver.inputNode) await hooks.emit("kubb:build:start", {
@@ -1048,6 +1114,7 @@ async function safeBuild(setupResult) {
1048
1114
  },
1049
1115
  upsertFile: (...files) => driver.fileManager.upsert(...files)
1050
1116
  });
1117
+ await flushPendingFiles();
1051
1118
  await hooks.emit("kubb:debug", {
1052
1119
  date: /* @__PURE__ */ new Date(),
1053
1120
  logs: [`✓ Plugin started successfully (${formatMs(duration)})`]
@@ -1067,6 +1134,7 @@ async function safeBuild(setupResult) {
1067
1134
  },
1068
1135
  upsertFile: (...files) => driver.fileManager.upsert(...files)
1069
1136
  });
1137
+ await flushPendingFiles();
1070
1138
  await hooks.emit("kubb:debug", {
1071
1139
  date: errorTimestamp,
1072
1140
  logs: [
@@ -1090,42 +1158,8 @@ async function safeBuild(setupResult) {
1090
1158
  },
1091
1159
  upsertFile: (...files) => driver.fileManager.upsert(...files)
1092
1160
  });
1161
+ await flushPendingFiles();
1093
1162
  const files = driver.fileManager.files;
1094
- const parsersMap = /* @__PURE__ */ new Map();
1095
- for (const parser of config.parsers) if (parser.extNames) for (const extname of parser.extNames) parsersMap.set(extname, parser);
1096
- const fileProcessor = new FileProcessor();
1097
- await hooks.emit("kubb:debug", {
1098
- date: /* @__PURE__ */ new Date(),
1099
- logs: [`Writing ${files.length} files...`]
1100
- });
1101
- await fileProcessor.run(files, {
1102
- parsers: parsersMap,
1103
- extension: config.output.extension,
1104
- onStart: async (processingFiles) => {
1105
- await hooks.emit("kubb:files:processing:start", { files: processingFiles });
1106
- },
1107
- onUpdate: async ({ file, source, processed, total, percentage }) => {
1108
- await hooks.emit("kubb:file:processing:update", {
1109
- file,
1110
- source,
1111
- processed,
1112
- total,
1113
- percentage,
1114
- config
1115
- });
1116
- if (source) {
1117
- await storage?.setItem(file.path, source);
1118
- sources.set(file.path, source);
1119
- }
1120
- },
1121
- onEnd: async (processedFiles) => {
1122
- await hooks.emit("kubb:files:processing:end", { files: processedFiles });
1123
- await hooks.emit("kubb:debug", {
1124
- date: /* @__PURE__ */ new Date(),
1125
- logs: [`✓ File write process completed for ${processedFiles.length} files`]
1126
- });
1127
- }
1128
- });
1129
1163
  await hooks.emit("kubb:build:end", {
1130
1164
  files,
1131
1165
  config,
@@ -1136,7 +1170,7 @@ async function safeBuild(setupResult) {
1136
1170
  files,
1137
1171
  driver,
1138
1172
  pluginTimings,
1139
- sources
1173
+ storage
1140
1174
  };
1141
1175
  } catch (error) {
1142
1176
  return {
@@ -1145,14 +1179,14 @@ async function safeBuild(setupResult) {
1145
1179
  driver,
1146
1180
  pluginTimings,
1147
1181
  error,
1148
- sources
1182
+ storage
1149
1183
  };
1150
1184
  } finally {
1151
1185
  driver.dispose();
1152
1186
  }
1153
1187
  }
1154
1188
  async function build(setupResult) {
1155
- const { files, driver, failedPlugins, pluginTimings, error, sources } = await safeBuild(setupResult);
1189
+ const { files, driver, failedPlugins, pluginTimings, error, storage } = await safeBuild(setupResult);
1156
1190
  if (error) throw error;
1157
1191
  if (failedPlugins.size > 0) {
1158
1192
  const errors = [...failedPlugins].map(({ error }) => error);
@@ -1164,32 +1198,48 @@ async function build(setupResult) {
1164
1198
  driver,
1165
1199
  pluginTimings,
1166
1200
  error: void 0,
1167
- sources
1201
+ storage
1168
1202
  };
1169
1203
  }
1170
- function inputToAdapterSource(config) {
1171
- if (Array.isArray(config.input)) return {
1172
- type: "paths",
1173
- paths: config.input.map((i) => new URLPath(i.path).isURL ? i.path : (0, node_path.resolve)(config.root, i.path))
1204
+ /**
1205
+ * Returns a snapshot of the current runtime environment.
1206
+ *
1207
+ * Useful for attaching context to debug logs and error reports so that
1208
+ * issues can be reproduced without manual information gathering.
1209
+ */
1210
+ function getDiagnosticInfo() {
1211
+ return {
1212
+ nodeVersion: node_process.version,
1213
+ KubbVersion: version,
1214
+ platform: process.platform,
1215
+ arch: process.arch,
1216
+ cwd: process.cwd()
1174
1217
  };
1175
- if ("data" in config.input) return {
1218
+ }
1219
+ function isInputPath(config) {
1220
+ return typeof config?.input === "object" && config.input !== null && "path" in config.input;
1221
+ }
1222
+ function inputToAdapterSource(config) {
1223
+ const input = config.input;
1224
+ if (!input) throw new Error("[kubb] input is required when using an adapter. Provide input.path or input.data in your config.");
1225
+ if ("data" in input) return {
1176
1226
  type: "data",
1177
- data: config.input.data
1227
+ data: input.data
1178
1228
  };
1179
- if (new URLPath(config.input.path).isURL) return {
1229
+ if (new URLPath(input.path).isURL) return {
1180
1230
  type: "path",
1181
- path: config.input.path
1231
+ path: input.path
1182
1232
  };
1183
1233
  return {
1184
1234
  type: "path",
1185
- path: (0, node_path.resolve)(config.root, config.input.path)
1235
+ path: (0, node_path.resolve)(config.root, input.path)
1186
1236
  };
1187
1237
  }
1188
1238
  /**
1189
1239
  * Creates a Kubb instance bound to a single config entry.
1190
1240
  *
1191
1241
  * Accepts a user-facing config shape and resolves it to a full {@link Config} during
1192
- * `setup()`. The instance then holds shared state (`hooks`, `sources`, `driver`, `config`)
1242
+ * `setup()`. The instance then holds shared state (`hooks`, `storage`, `driver`, `config`)
1193
1243
  * across the `setup → build` lifecycle. Attach event listeners to `kubb.hooks` before
1194
1244
  * calling `setup()` or `build()`.
1195
1245
  *
@@ -1211,14 +1261,17 @@ function createKubb(userConfig, options = {}) {
1211
1261
  get hooks() {
1212
1262
  return hooks;
1213
1263
  },
1214
- get sources() {
1215
- return setupResult?.sources ?? /* @__PURE__ */ new Map();
1264
+ get storage() {
1265
+ if (!setupResult) throw new Error("[kubb] setup() must be called before accessing storage");
1266
+ return setupResult.storage;
1216
1267
  },
1217
1268
  get driver() {
1218
- return setupResult?.driver;
1269
+ if (!setupResult) throw new Error("[kubb] setup() must be called before accessing driver");
1270
+ return setupResult.driver;
1219
1271
  },
1220
1272
  get config() {
1221
- return setupResult?.config;
1273
+ if (!setupResult) throw new Error("[kubb] setup() must be called before accessing config");
1274
+ return setupResult.config;
1222
1275
  },
1223
1276
  async setup() {
1224
1277
  setupResult = await setup(userConfig, { hooks });
@@ -1282,7 +1335,11 @@ function defineGenerator(generator) {
1282
1335
  /**
1283
1336
  * Wraps a logger definition into a typed {@link Logger}.
1284
1337
  *
1285
- * @example
1338
+ * The optional second type parameter `TInstallReturn` allows loggers to return
1339
+ * a value from `install` — for example, a sink factory that the caller can
1340
+ * forward to hook execution.
1341
+ *
1342
+ * @example Basic logger
1286
1343
  * ```ts
1287
1344
  * export const myLogger = defineLogger({
1288
1345
  * name: 'my-logger',
@@ -1292,6 +1349,17 @@ function defineGenerator(generator) {
1292
1349
  * },
1293
1350
  * })
1294
1351
  * ```
1352
+ *
1353
+ * @example Logger that returns a hook sink factory
1354
+ * ```ts
1355
+ * export const myLogger = defineLogger<LoggerOptions, HookSinkFactory>({
1356
+ * name: 'my-logger',
1357
+ * install(context, options) {
1358
+ * // … register event handlers …
1359
+ * return (commandWithArgs) => ({ onStdout: console.log })
1360
+ * },
1361
+ * })
1362
+ * ```
1295
1363
  */
1296
1364
  function defineLogger(logger) {
1297
1365
  return logger;
@@ -1362,34 +1430,6 @@ function defineParser(parser) {
1362
1430
  return parser;
1363
1431
  }
1364
1432
  //#endregion
1365
- //#region src/definePlugin.ts
1366
- /**
1367
- * Wraps a factory function and returns a typed `Plugin` with lifecycle handlers grouped under `hooks`.
1368
- *
1369
- * Handlers live in a single `hooks` object (inspired by Astro integrations).
1370
- * All lifecycle events from `KubbHooks` are available for subscription.
1371
- *
1372
- * @note For real plugins, use a `PluginFactoryOptions` type parameter to get type-safe context in `kubb:plugin:setup`.
1373
- * Plugin names should follow the convention `plugin-<feature>` (e.g., `plugin-react-query`, `plugin-zod`).
1374
- *
1375
- * @example
1376
- * ```ts
1377
- * import { definePlugin } from '@kubb/core'
1378
- *
1379
- * export const pluginTs = definePlugin((options: { prefix?: string } = {}) => ({
1380
- * name: 'plugin-ts',
1381
- * hooks: {
1382
- * 'kubb:plugin:setup'(ctx) {
1383
- * ctx.setResolver(resolverTs)
1384
- * },
1385
- * },
1386
- * }))
1387
- * ```
1388
- */
1389
- function definePlugin(factory) {
1390
- return (options) => factory(options ?? {});
1391
- }
1392
- //#endregion
1393
1433
  //#region src/storages/memoryStorage.ts
1394
1434
  /**
1395
1435
  * In-memory storage driver. Useful for testing and dry-run scenarios where
@@ -1459,7 +1499,7 @@ exports.defineGenerator = defineGenerator;
1459
1499
  exports.defineLogger = defineLogger;
1460
1500
  exports.defineMiddleware = defineMiddleware;
1461
1501
  exports.defineParser = defineParser;
1462
- exports.definePlugin = definePlugin;
1502
+ exports.definePlugin = require_PluginDriver.definePlugin;
1463
1503
  exports.defineResolver = require_PluginDriver.defineResolver;
1464
1504
  exports.fsStorage = fsStorage;
1465
1505
  exports.isInputPath = isInputPath;