@kubb/core 5.1.0 → 5.2.0

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/mocks.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_usingCtx = require("./usingCtx-khw91yct.cjs");
2
+ const require_usingCtx = require("./usingCtx-1W_18hcP.cjs");
3
3
  let node_path = require("node:path");
4
4
  node_path = require_usingCtx.__toESM(node_path, 1);
5
5
  let _kubb_ast = require("@kubb/ast");
package/dist/mocks.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import { t as __name } from "./rolldown-runtime-C0LytTxp.js";
2
- import { $ as PluginFactoryOptions, Ht as AdapterFactoryOptions, I as KubbDriver, M as Generator, R as Parser, Vt as Adapter, q as NormalizedPlugin, r as Config } from "./types-QndGMAwn.js";
2
+ import { $ as PluginFactoryOptions, Ht as AdapterFactoryOptions, I as KubbDriver, M as Generator, R as Parser, Vt as Adapter, q as NormalizedPlugin, r as Config } from "./types-D6fWCd9O.js";
3
3
  import { FileNode, InputMeta, Macro, OperationNode, SchemaNode } from "@kubb/ast";
4
4
  //#region src/mocks.d.ts
5
5
  /**
6
6
  * Creates a minimal `KubbDriver` mock for unit tests.
7
7
  */
8
- declare function createMockedPluginDriver(options?: {
8
+ export declare function createMockedPluginDriver(options?: {
9
9
  name?: string;
10
10
  plugin?: NormalizedPlugin;
11
11
  config?: Config;
@@ -14,7 +14,7 @@ declare function createMockedPluginDriver(options?: {
14
14
  * Creates a minimal `Adapter` mock for unit tests.
15
15
  * `parse` returns an empty `InputNode` by default. Override via `options.parse`.
16
16
  */
17
- declare function createMockedAdapter<TOptions extends AdapterFactoryOptions = AdapterFactoryOptions>(options?: {
17
+ export declare function createMockedAdapter<TOptions extends AdapterFactoryOptions = AdapterFactoryOptions>(options?: {
18
18
  name?: TOptions['name'];
19
19
  resolvedOptions?: TOptions['resolvedOptions'];
20
20
  parse?: Adapter<TOptions>['parse'];
@@ -25,7 +25,7 @@ declare function createMockedAdapter<TOptions extends AdapterFactoryOptions = Ad
25
25
  * @example
26
26
  * `const plugin = createMockedPlugin<PluginTs>({ name: '@kubb/plugin-ts', options })`
27
27
  */
28
- declare function createMockedPlugin<TOptions extends PluginFactoryOptions = PluginFactoryOptions>(params: {
28
+ export declare function createMockedPlugin<TOptions extends PluginFactoryOptions = PluginFactoryOptions>(params: {
29
29
  name: TOptions['name'];
30
30
  options: TOptions['resolvedOptions'];
31
31
  resolver?: TOptions['resolver'];
@@ -50,7 +50,7 @@ type RenderGeneratorOptions<TOptions extends PluginFactoryOptions> = {
50
50
  * await matchFiles(driver.fileManager.files)
51
51
  * ```
52
52
  */
53
- declare function renderGeneratorSchema<TOptions extends PluginFactoryOptions>(generator: Generator<TOptions>, node: SchemaNode, opts: RenderGeneratorOptions<TOptions>): Promise<void>;
53
+ export declare function renderGeneratorSchema<TOptions extends PluginFactoryOptions>(generator: Generator<TOptions>, node: SchemaNode, opts: RenderGeneratorOptions<TOptions>): Promise<void>;
54
54
  /**
55
55
  * Renders a generator's `operation` method in a test context.
56
56
  *
@@ -60,7 +60,7 @@ declare function renderGeneratorSchema<TOptions extends PluginFactoryOptions>(ge
60
60
  * await matchFiles(driver.fileManager.files)
61
61
  * ```
62
62
  */
63
- declare function renderGeneratorOperation<TOptions extends PluginFactoryOptions>(generator: Generator<TOptions>, node: OperationNode, opts: RenderGeneratorOptions<TOptions>): Promise<void>;
63
+ export declare function renderGeneratorOperation<TOptions extends PluginFactoryOptions>(generator: Generator<TOptions>, node: OperationNode, opts: RenderGeneratorOptions<TOptions>): Promise<void>;
64
64
  /**
65
65
  * Renders a generator's `operations` method in a test context.
66
66
  *
@@ -70,7 +70,7 @@ declare function renderGeneratorOperation<TOptions extends PluginFactoryOptions>
70
70
  * await matchFiles(driver.fileManager.files)
71
71
  * ```
72
72
  */
73
- declare function renderGeneratorOperations<TOptions extends PluginFactoryOptions>(generator: Generator<TOptions>, nodes: Array<OperationNode>, opts: RenderGeneratorOptions<TOptions>): Promise<void>;
73
+ export declare function renderGeneratorOperations<TOptions extends PluginFactoryOptions>(generator: Generator<TOptions>, nodes: Array<OperationNode>, opts: RenderGeneratorOptions<TOptions>): Promise<void>;
74
74
  type MatchFilesOptions = {
75
75
  /**
76
76
  * Parsers indexed by file extension, used to render each `FileNode` to source.
@@ -97,7 +97,6 @@ type MatchFilesOptions = {
97
97
  * await matchFiles(driver.fileManager.files, { parsers, format })
98
98
  * ```
99
99
  */
100
- declare function matchFiles(files: Array<FileNode> | undefined, options?: MatchFilesOptions): Promise<Map<string, string> | undefined>;
100
+ export declare function matchFiles(files: Array<FileNode> | undefined, options?: MatchFilesOptions): Promise<Map<string, string> | undefined>;
101
101
  //#endregion
102
- export { createMockedAdapter, createMockedPlugin, createMockedPluginDriver, matchFiles, renderGeneratorOperation, renderGeneratorOperations, renderGeneratorSchema };
103
102
  //# sourceMappingURL=mocks.d.ts.map
package/dist/mocks.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import "./rolldown-runtime-C0LytTxp.js";
2
- import { g as camelCase, i as Hookable, n as createNodeCache, r as FileManager, t as _usingCtx } from "./usingCtx-2r6N3STL.js";
2
+ import { i as Hookable, m as camelCase, n as createNodeCache, r as FileManager, t as _usingCtx } from "./usingCtx-xMZZh-09.js";
3
3
  import path, { resolve } from "node:path";
4
4
  import { applyMacros, ast } from "@kubb/ast";
5
5
  import { expect } from "vitest";
@@ -1867,7 +1867,7 @@ declare class KubbDriver {
1867
1867
  * so `run` can parse it later.
1868
1868
  */
1869
1869
  setup(): Promise<void>;
1870
- get hooks(): Hookable<KubbHooks>;
1870
+ get hooks(): Hookable<Kubb.KubbHooksRegistry>;
1871
1871
  /**
1872
1872
  * Runs each plugin's `kubb:plugin:setup` handler, in plugin order, with a context scoped to that
1873
1873
  * plugin so `addGenerator`, `setResolver`, `addMacro`, `setMacros`, and `setOptions` target its
@@ -2646,12 +2646,66 @@ declare global {
2646
2646
  * ```
2647
2647
  */
2648
2648
  interface PluginOptionsRegistry {}
2649
+ /**
2650
+ * Registry of hook names to their listener argument tuples.
2651
+ * Augment this interface to add hook events without touching core types.
2652
+ *
2653
+ * @example
2654
+ * ```ts
2655
+ * // packages/studio/src/hooks.ts
2656
+ * declare global {
2657
+ * namespace Kubb {
2658
+ * interface KubbHooksRegistry {
2659
+ * 'studio:connecting': [ctx: StudioConnectingContext]
2660
+ * }
2661
+ * }
2662
+ * }
2663
+ * ```
2664
+ */
2665
+ interface KubbHooksRegistry {
2666
+ 'kubb:lifecycle:start': [ctx: KubbLifecycleStartContext];
2667
+ 'kubb:lifecycle:end': [];
2668
+ 'kubb:generation:start': [ctx: KubbGenerationStartContext];
2669
+ 'kubb:generation:end': [ctx: KubbGenerationEndContext];
2670
+ 'kubb:setup:start': [];
2671
+ 'kubb:setup:end': [];
2672
+ 'kubb:format:start': [];
2673
+ 'kubb:format:end': [];
2674
+ 'kubb:lint:start': [];
2675
+ 'kubb:lint:end': [];
2676
+ 'kubb:hooks:start': [];
2677
+ 'kubb:hooks:end': [];
2678
+ 'kubb:hook:start': [ctx: KubbHookStartContext];
2679
+ 'kubb:hook:line': [ctx: KubbHookLineContext];
2680
+ 'kubb:hook:end': [ctx: KubbHookEndContext];
2681
+ 'kubb:info': [ctx: KubbInfoContext];
2682
+ 'kubb:error': [ctx: KubbErrorContext];
2683
+ 'kubb:success': [ctx: KubbSuccessContext];
2684
+ 'kubb:warn': [ctx: KubbWarnContext];
2685
+ 'kubb:diagnostic': [ctx: KubbDiagnosticContext];
2686
+ 'kubb:files:processing:start': [ctx: KubbFilesProcessingStartContext];
2687
+ 'kubb:files:processing:update': [ctx: KubbFilesProcessingUpdateContext];
2688
+ 'kubb:files:processing:end': [ctx: KubbFilesProcessingEndContext];
2689
+ 'kubb:plugin:start': [ctx: KubbPluginStartContext];
2690
+ 'kubb:plugin:end': [ctx: KubbPluginEndContext];
2691
+ 'kubb:plugin:setup': [ctx: KubbPluginSetupContext];
2692
+ 'kubb:build:start': [ctx: KubbBuildStartContext];
2693
+ 'kubb:plugins:end': [ctx: KubbPluginsEndContext];
2694
+ 'kubb:build:end': [ctx: KubbBuildEndContext];
2695
+ 'kubb:generate:schema': [node: SchemaNode, ctx: GeneratorContext];
2696
+ 'kubb:generate:operation': [node: OperationNode, ctx: GeneratorContext];
2697
+ 'kubb:generate:operations': [nodes: Array<OperationNode>, ctx: GeneratorContext];
2698
+ }
2649
2699
  }
2650
2700
  }
2651
2701
  /**
2652
2702
  * Lifecycle hooks emitted during Kubb code generation.
2653
2703
  * Attach listeners before calling `setup()` or `build()` to observe and react to build progress.
2654
2704
  *
2705
+ * A package can add its own hook names to this type by augmenting
2706
+ * {@link Kubb.KubbHooksRegistry} instead of exporting a second, separate hook type — `@kubb/studio`
2707
+ * does this for its `studio:*` session events, so a host only ever needs to know `KubbHooks`.
2708
+ *
2655
2709
  * @example
2656
2710
  * ```ts
2657
2711
  * kubb.hooks.hook('kubb:lifecycle:start', () => {
@@ -2663,40 +2717,7 @@ declare global {
2663
2717
  * })
2664
2718
  * ```
2665
2719
  */
2666
- interface KubbHooks {
2667
- 'kubb:lifecycle:start': [ctx: KubbLifecycleStartContext];
2668
- 'kubb:lifecycle:end': [];
2669
- 'kubb:generation:start': [ctx: KubbGenerationStartContext];
2670
- 'kubb:generation:end': [ctx: KubbGenerationEndContext];
2671
- 'kubb:setup:start': [];
2672
- 'kubb:setup:end': [];
2673
- 'kubb:format:start': [];
2674
- 'kubb:format:end': [];
2675
- 'kubb:lint:start': [];
2676
- 'kubb:lint:end': [];
2677
- 'kubb:hooks:start': [];
2678
- 'kubb:hooks:end': [];
2679
- 'kubb:hook:start': [ctx: KubbHookStartContext];
2680
- 'kubb:hook:line': [ctx: KubbHookLineContext];
2681
- 'kubb:hook:end': [ctx: KubbHookEndContext];
2682
- 'kubb:info': [ctx: KubbInfoContext];
2683
- 'kubb:error': [ctx: KubbErrorContext];
2684
- 'kubb:success': [ctx: KubbSuccessContext];
2685
- 'kubb:warn': [ctx: KubbWarnContext];
2686
- 'kubb:diagnostic': [ctx: KubbDiagnosticContext];
2687
- 'kubb:files:processing:start': [ctx: KubbFilesProcessingStartContext];
2688
- 'kubb:files:processing:update': [ctx: KubbFilesProcessingUpdateContext];
2689
- 'kubb:files:processing:end': [ctx: KubbFilesProcessingEndContext];
2690
- 'kubb:plugin:start': [ctx: KubbPluginStartContext];
2691
- 'kubb:plugin:end': [ctx: KubbPluginEndContext];
2692
- 'kubb:plugin:setup': [ctx: KubbPluginSetupContext];
2693
- 'kubb:build:start': [ctx: KubbBuildStartContext];
2694
- 'kubb:plugins:end': [ctx: KubbPluginsEndContext];
2695
- 'kubb:build:end': [ctx: KubbBuildEndContext];
2696
- 'kubb:generate:schema': [node: SchemaNode, ctx: GeneratorContext];
2697
- 'kubb:generate:operation': [node: OperationNode, ctx: GeneratorContext];
2698
- 'kubb:generate:operations': [nodes: Array<OperationNode>, ctx: GeneratorContext];
2699
- }
2720
+ type KubbHooks = Kubb.KubbHooksRegistry;
2700
2721
  type KubbBuildStartContext = {
2701
2722
  /**
2702
2723
  * Resolved configuration for this build.
@@ -3018,4 +3039,4 @@ type BuildOutput = {
3018
3039
  };
3019
3040
  //#endregion
3020
3041
  export { PluginFactoryOptions as $, Kubb$1 as A, DiagnosticDoc as At, Exclude$1 as B, Hookable as Bt, KubbWarnContext as C, Reporter as Ct, CreateKubbOptions as D, createReporter as Dt, UserConfig as E, UserReporter as Et, NodeCache as F, PerformanceDiagnostic as Ft, KubbPluginSetupContext as G, Group as H, AdapterFactoryOptions as Ht, KubbDriver as I, ProblemCode as It, Output as J, KubbPluginStartContext as K, FileManagerHooks as L, ProblemDiagnostic as Lt, Generator as M, DiagnosticLocation as Mt, GeneratorContext as N, DiagnosticSeverity as Nt, GenerateOptions as O, logLevel as Ot, defineGenerator as P, Diagnostics as Pt, Plugin as Q, Parser as R, SerializedDiagnostic as Rt, KubbSuccessContext as S, GenerationResult as St, PostGenerateCommand as T, ReporterName as Tt, Include as U, AdapterSource as Ut, Filter as V, Adapter as Vt, KubbPluginEndContext as W, createAdapter as Wt, OutputOptions as X, OutputMode as Y, Override as Z, KubbHookStartContext as _, Renderer as _t, KubbBuildEndContext as a, ResolveBannerFile as at, KubbLifecycleStartContext as b, Storage as bt, KubbErrorContext as c, ResolveOptionsContext as ct, KubbFilesProcessingStartContext as d, ResolverDefault as dt, PluginName as et, KubbFilesProcessingUpdateContext as f, ResolverFile as ft, KubbHookLineContext as g, ResolverPathParams as gt, KubbHookEndContext as h, ResolverPatch as ht, Input as i, ResolveBannerContext as it, createKubb as j, DiagnosticKind as jt, GenerateResult as k, Diagnostic as kt, KubbFileProcessingUpdate as l, ResolvePathOptions as lt, KubbGenerationStartContext as m, ResolverFilePathParams as mt, CLIOptions as n, definePlugin as nt, KubbBuildStartContext as o, ResolveFileOptions as ot, KubbGenerationEndContext as p, ResolverFileParams as pt, NormalizedPlugin as q, Config as r, BannerMeta as rt, KubbDiagnosticContext as s, ResolveImportsOptions as st, BuildOutput as t, ResolvePluginOptions as tt, KubbFilesProcessingEndContext as u, Resolver as ut, KubbHooks as v, RendererFactory as vt, PossibleConfig as w, ReporterContext as wt, KubbPluginsEndContext as x, createStorage as xt, KubbInfoContext as y, createRenderer as yt, defineParser as z, UpdateDiagnostic as zt };
3021
- //# sourceMappingURL=types-QndGMAwn.d.ts.map
3042
+ //# sourceMappingURL=types-D6fWCd9O.d.ts.map
@@ -57,30 +57,13 @@ function camelCase(text, { prefix = "", suffix = "" } = {}) {
57
57
  //#endregion
58
58
  //#region ../../internals/utils/src/errors.ts
59
59
  /**
60
- * Thrown when one or more errors occur during a Kubb build.
61
- * Carries the full list of underlying errors on `errors`.
62
- *
63
- * @example
64
- * ```ts
65
- * throw new BuildError('Build failed', { errors: [err1, err2] })
66
- * ```
67
- */
68
- var BuildError = class extends Error {
69
- errors;
70
- constructor(message, options) {
71
- super(message, { cause: options.cause });
72
- this.name = "BuildError";
73
- this.errors = options.errors;
74
- }
75
- };
76
- /**
77
60
  * Coerces an unknown thrown value to an `Error` instance.
78
61
  * Returns the value as-is when it is already an `Error`; otherwise wraps it with `String(value)`.
79
62
  *
80
63
  * @example
81
64
  * ```ts
82
65
  * try { ... } catch(err) {
83
- * throw new BuildError('Build failed', { cause: toError(err), errors: [] })
66
+ * throw new Error('Build failed', { cause: toError(err) })
84
67
  * }
85
68
  * ```
86
69
  */
@@ -217,13 +200,15 @@ async function write(path, data, options = {}) {
217
200
  const content = `${trimmed}\n`;
218
201
  const resolved = (0, node_path.resolve)(path);
219
202
  let stored = options.stored;
220
- if (stored === void 0) if (runtime.isBun) {
221
- const file = Bun.file(resolved);
222
- stored = await file.exists() ? await file.text() : null;
223
- } else try {
224
- stored = await (0, node_fs_promises.readFile)(resolved, { encoding: "utf-8" });
225
- } catch {
226
- stored = null;
203
+ if (stored === void 0) {
204
+ if (runtime.isBun) {
205
+ const file = Bun.file(resolved);
206
+ stored = await file.exists() ? await file.text() : null;
207
+ } else try {
208
+ stored = await (0, node_fs_promises.readFile)(resolved, { encoding: "utf-8" });
209
+ } catch {
210
+ stored = null;
211
+ }
227
212
  }
228
213
  if (matchesStored({
229
214
  stored: stored ?? "",
@@ -262,26 +247,6 @@ async function clean(path) {
262
247
  });
263
248
  }
264
249
  /**
265
- * Resolves to `true` when `path` is `parent` itself or nested inside it. Both sides are resolved
266
- * to absolute paths first, so relative and `..`-containing inputs compare correctly.
267
- *
268
- * Guards destructive operations: before wiping an output directory, check that it does not contain
269
- * the project root, otherwise a `clean` would delete `kubb.config` and every source file.
270
- *
271
- * @example
272
- * isPathInside('./src/gen', '.') // true — nested inside the root
273
- * isPathInside('.', '.') // true — the same directory counts as inside
274
- * isPathInside('.', './src/gen') // false — the root is not inside its own output
275
- * isPathInside('../other', '.') // false — escapes the root
276
- */
277
- function isPathInside(path, parent) {
278
- const resolvedPath = (0, node_path.resolve)(path);
279
- const resolvedParent = (0, node_path.resolve)(parent);
280
- if (resolvedPath === resolvedParent) return true;
281
- const rel = (0, node_path.relative)(resolvedParent, resolvedPath);
282
- return rel !== "" && !rel.startsWith("..") && !(0, node_path.isAbsolute)(rel);
283
- }
284
- /**
285
250
  * Converts a filesystem path to use POSIX (`/`) separators.
286
251
  *
287
252
  * Most of the codebase compares and composes paths as strings (prefix matching, joining for
@@ -299,27 +264,25 @@ function toPosixPath(filePath) {
299
264
  return filePath.replaceAll("\\", "/");
300
265
  }
301
266
  /**
302
- * Builds a nested file path from a dotted name. Splits on dots that precede a letter
303
- * (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
304
- * every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
305
- *
306
- * Empty segments are dropped before joining. They arise when the name starts with a dot
307
- * followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
308
- * an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
309
- * absolute path, letting generated files escape the configured output directory.
310
- *
311
- * @example Nested path from a dotted name
312
- * `toFilePath('pet.petId') // 'pet/petId'`
267
+ * Resolves to `true` when `path` is `parent` itself or nested inside it. Both sides are resolved
268
+ * to absolute paths first, so relative and `..`-containing inputs compare correctly.
313
269
  *
314
- * @example PascalCase the final segment
315
- * `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
270
+ * Guards a destructive or an out-of-tree operation: before wiping an output directory, check that
271
+ * it does not contain the project root, and before loading a path a caller supplied, check that it
272
+ * did not escape the directory it is allowed to read from.
316
273
  *
317
- * @example Suffix applied to the final segment only
318
- * `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
274
+ * @example
275
+ * isPathInside('./src/gen', '.') // true — nested inside the root
276
+ * isPathInside('.', '.') // true — the same directory counts as inside
277
+ * isPathInside('.', './src/gen') // false — the root is not inside its own output
278
+ * isPathInside('../other', '.') // false — escapes the root
319
279
  */
320
- function toFilePath(name, caseLast = camelCase) {
321
- const parts = name.split(/\.(?=[a-zA-Z])/);
322
- return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
280
+ function isPathInside(path, parent) {
281
+ const resolvedPath = (0, node_path.resolve)(path);
282
+ const resolvedParent = (0, node_path.resolve)(parent);
283
+ if (resolvedPath === resolvedParent) return true;
284
+ const rel = (0, node_path.relative)(resolvedParent, resolvedPath);
285
+ return rel !== "" && !rel.startsWith("..") && !(0, node_path.isAbsolute)(rel);
323
286
  }
324
287
  //#endregion
325
288
  //#region ../../internals/utils/src/promise.ts
@@ -785,7 +748,7 @@ function createNodeCache() {
785
748
  };
786
749
  }
787
750
  //#endregion
788
- //#region \0@oxc-project+runtime@0.142.0/helpers/esm/usingCtx.js
751
+ //#region \0@oxc-project+runtime@0.148.0/helpers/esm/usingCtx.js
789
752
  function _usingCtx() {
790
753
  var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
791
754
  var n = Error();
@@ -845,12 +808,6 @@ function _usingCtx() {
845
808
  };
846
809
  }
847
810
  //#endregion
848
- Object.defineProperty(exports, "BuildError", {
849
- enumerable: true,
850
- get: function() {
851
- return BuildError;
852
- }
853
- });
854
811
  Object.defineProperty(exports, "FileManager", {
855
812
  enumerable: true,
856
813
  get: function() {
@@ -935,12 +892,6 @@ Object.defineProperty(exports, "toError", {
935
892
  return toError;
936
893
  }
937
894
  });
938
- Object.defineProperty(exports, "toFilePath", {
939
- enumerable: true,
940
- get: function() {
941
- return toFilePath;
942
- }
943
- });
944
895
  Object.defineProperty(exports, "toPosixPath", {
945
896
  enumerable: true,
946
897
  get: function() {
@@ -954,4 +905,4 @@ Object.defineProperty(exports, "write", {
954
905
  }
955
906
  });
956
907
 
957
- //# sourceMappingURL=usingCtx-khw91yct.cjs.map
908
+ //# sourceMappingURL=usingCtx-1W_18hcP.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usingCtx-1W_18hcP.cjs","names":["readFile","resolve","writeFile","mkdir","dirname","rm","relative","isAbsolute","NodeEventEmitter","extractStringsFromNodes","ast"],"sources":["../../../internals/utils/src/casing.ts","../../../internals/utils/src/errors.ts","../../../internals/utils/src/runtime.ts","../../../internals/utils/src/fs.ts","../../../internals/utils/src/promise.ts","../src/constants.ts","../src/Hookable.ts","../src/FileManager.ts","../src/nodeCache.ts"],"sourcesContent":["type Options = {\n /**\n * Text prepended before casing is applied.\n */\n prefix?: string\n /**\n * Text appended before casing is applied.\n */\n suffix?: string\n}\n\n/**\n * Shared implementation for camelCase and PascalCase conversion.\n * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)\n * and capitalizes each word according to `pascal`.\n *\n * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.\n */\nfunction toCamelOrPascal(text: string, pascal: boolean): string {\n return text\n .trim()\n .replace(/([a-z\\d])([A-Z])/g, '$1 $2')\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')\n .replace(/(\\d)([a-z])/g, '$1 $2')\n .split(/[\\s\\-_./\\\\:]+/)\n .filter(Boolean)\n .map((word, i) => {\n if (word.length > 1 && word === word.toUpperCase()) return word\n const head = i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()\n return head + word.slice(1)\n })\n .join('')\n .replace(/[^a-zA-Z0-9]/g, '')\n}\n\n/**\n * Converts `text` to camelCase.\n *\n * @example Word boundaries\n * `camelCase('hello-world') // 'helloWorld'`\n *\n * @example With a prefix\n * `camelCase('tag', { prefix: 'create' }) // 'createTag'`\n */\nexport function camelCase(text: string, { prefix = '', suffix = '' }: Options = {}): string {\n return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false)\n}\n\n/**\n * Converts `text` to PascalCase.\n *\n * @example Word boundaries\n * `pascalCase('hello-world') // 'HelloWorld'`\n *\n * @example With a suffix\n * `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`\n */\nexport function pascalCase(text: string, { prefix = '', suffix = '' }: Options = {}): string {\n return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true)\n}\n","/**\n * Coerces an unknown thrown value to an `Error` instance.\n * Returns the value as-is when it is already an `Error`; otherwise wraps it with `String(value)`.\n *\n * @example\n * ```ts\n * try { ... } catch(err) {\n * throw new Error('Build failed', { cause: toError(err) })\n * }\n * ```\n */\nexport function toError(value: unknown): Error {\n return value instanceof Error ? value : new Error(String(value))\n}\n\n/**\n * Extracts a human-readable message from any thrown value.\n *\n * @example\n * ```ts\n * getErrorMessage(new Error('oops')) // 'oops'\n * getErrorMessage('plain string') // 'plain string'\n * ```\n */\nexport function getErrorMessage(value: unknown): string {\n return value instanceof Error ? value.message : String(value)\n}\n","/**\n * Name of the JavaScript runtime executing the current process.\n */\ntype RuntimeName = 'bun' | 'deno' | 'node'\n\n/**\n * Detects the JavaScript runtime executing the current process and exposes its name and version.\n *\n * Prefer the shared {@link runtime} instance over constructing your own.\n */\nclass Runtime {\n /**\n * `true` when the current process is running under Bun.\n *\n * Detection keys off the global `Bun` object rather than `process.versions`,\n * because Bun polyfills `process.versions.node` for Node compatibility and would\n * otherwise look like Node.\n *\n * @example\n * ```ts\n * if (runtime.isBun) {\n * await Bun.write(path, data)\n * }\n * ```\n */\n get isBun(): boolean {\n return typeof Bun !== 'undefined'\n }\n\n /**\n * `true` when the current process is running under Deno.\n */\n get isDeno(): boolean {\n return typeof (globalThis as { Deno?: unknown }).Deno !== 'undefined'\n }\n\n /**\n * `true` when the current process is running under Node.\n *\n * Bun and Deno are excluded first so a polyfilled `process` does not register as Node.\n */\n get isNode(): boolean {\n return !this.isBun && !this.isDeno && typeof process !== 'undefined' && process.versions?.node != null\n }\n\n /**\n * Name of the runtime executing the current process.\n *\n * @example\n * ```ts\n * runtime.name // 'bun' when run with `bun kubb`, 'node' otherwise\n * ```\n */\n get name(): RuntimeName {\n if (this.isBun) return 'bun'\n if (this.isDeno) return 'deno'\n\n return 'node'\n }\n\n /**\n * Version of the active runtime, or an empty string when it cannot be read.\n *\n * @example\n * ```ts\n * runtime.version // '1.3.11' under Bun, '22.22.2' under Node\n * ```\n */\n get version(): string {\n if (this.isBun) return process.versions.bun ?? ''\n if (this.isDeno) return (globalThis as { Deno?: { version?: { deno?: string } } }).Deno?.version?.deno ?? ''\n\n return process.versions?.node ?? ''\n }\n}\n\n/**\n * Shared {@link Runtime} instance describing the JavaScript runtime executing the current process.\n */\nexport const runtime = new Runtime()\n","import { access, mkdir, readFile, rm, writeFile } from 'node:fs/promises'\nimport { dirname, isAbsolute, relative, resolve } from 'node:path'\nimport { runtime } from './runtime.ts'\n\n/**\n * Resolves to `true` when the file or directory at `path` exists.\n * Uses `Bun.file().exists()` when running under Bun, `fs.access` otherwise.\n *\n * @example\n * ```ts\n * if (await exists('./kubb.config.ts')) {\n * const content = await read('./kubb.config.ts')\n * }\n * ```\n */\nexport async function exists(path: string): Promise<boolean> {\n if (runtime.isBun) {\n return Bun.file(path).exists()\n }\n return access(path).then(\n () => true,\n () => false,\n )\n}\n\n/**\n * Reads the file at `path` as a UTF-8 string.\n * Uses `Bun.file().text()` when running under Bun, `fs.readFile` otherwise.\n *\n * @example\n * ```ts\n * const source = await read('./src/Pet.ts')\n * ```\n */\nexport async function read(path: string): Promise<string> {\n if (runtime.isBun) {\n return Bun.file(path).text()\n }\n return readFile(path, { encoding: 'utf8' })\n}\n\ntype WriteOptions = {\n /**\n * Previously read content, or `null` when the file does not exist.\n * Omitting this value reads the file before writing.\n */\n stored?: string | null\n /**\n * When `true`, re-reads the file immediately after writing and throws if the\n * content does not match — useful for catching write failures on unreliable file systems.\n */\n sanity?: boolean\n}\n\n/**\n * Whether `stored` already holds `source`, comparing on the trimmed text rather than the exact\n * bytes. Surrounding whitespace is what a formatter adds and what editors strip, and neither is a\n * reason to rewrite the file.\n *\n * Both sides are trimmed, so a storage that keeps bytes verbatim settles on the same answer as one\n * that normalizes what it stores. Trimming only `stored` would leave a source with leading\n * whitespace rewritten on every build, since the stored copy keeps the whitespace the comparison\n * has already dropped.\n */\nexport function matchesStored({ stored, source }: { stored: string; source: string }): boolean {\n return stored.trim() === source.trim()\n}\n\n/**\n * Writes `data` to `path`, trimming surrounding whitespace and ending the file with a single newline\n * the way prettier, biome, and oxfmt all do.\n * Skips the write when the trimmed content is empty, or when the file already holds that content.\n * Creates any missing parent directories automatically.\n * When `sanity` is `true`, re-reads the file after writing and throws if the content does not match.\n *\n * @example\n * ```ts\n * await write('./src/Pet.ts', source) // writes and returns the trimmed content plus a newline\n * await write('./src/Pet.ts', source) // null — file unchanged\n * await write('./src/Pet.ts', ' ') // null — empty content skipped\n * ```\n */\nexport async function write(path: string, data: string, options: WriteOptions = {}): Promise<string | null> {\n const trimmed = data.trim()\n if (trimmed === '') return null\n\n const content = `${trimmed}\\n`\n const resolved = resolve(path)\n let stored = options.stored\n\n if (stored === undefined) {\n if (runtime.isBun) {\n const file = Bun.file(resolved)\n stored = (await file.exists()) ? await file.text() : null\n } else {\n try {\n stored = await readFile(resolved, { encoding: 'utf-8' })\n } catch {\n /* file doesn't exist yet */\n stored = null\n }\n }\n }\n if (matchesStored({ stored: stored ?? '', source: trimmed })) return null\n\n if (runtime.isBun) {\n await Bun.write(resolved, content)\n return content\n }\n\n // Creating the directory up front costs a syscall per file, and every file after the first in a\n // directory pays it for nothing. Write first and only fall back when the directory is missing,\n // which also stays correct when something removed it mid-run.\n try {\n await writeFile(resolved, content, { encoding: 'utf-8' })\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n\n await mkdir(dirname(resolved), { recursive: true })\n await writeFile(resolved, content, { encoding: 'utf-8' })\n }\n\n if (options.sanity) {\n const savedData = await readFile(resolved, { encoding: 'utf-8' })\n if (savedData !== content) {\n throw new Error(`Sanity check failed for ${path}\\n\\nData[${data.length}]:\\n${data}\\n\\nSaved[${savedData.length}]:\\n${savedData}\\n`)\n }\n return savedData\n }\n\n return content\n}\n\n/**\n * Recursively removes `path`. Silently succeeds when `path` does not exist.\n *\n * @example\n * ```ts\n * await clean('./dist')\n * ```\n */\nexport async function clean(path: string): Promise<void> {\n return rm(path, { recursive: true, force: true })\n}\n\n/**\n * Converts a filesystem path to use POSIX (`/`) separators.\n *\n * Most of the codebase compares and composes paths as strings (prefix matching, joining for\n * import specifiers, splitting on `/`). On POSIX `path.resolve` already returns `/`-separated\n * paths, but on Windows it returns `\\`-separated paths, which breaks every such comparison.\n *\n * Routing every path that crosses a module boundary through `toPosixPath` keeps the rest of the\n * code platform-agnostic. The conversion runs unconditionally so Windows-specific behavior is\n * exercisable from POSIX CI.\n *\n * @example\n * toPosixPath('C:\\\\repo\\\\src\\\\pet.ts') // 'C:/repo/src/pet.ts'\n */\nexport function toPosixPath(filePath: string): string {\n return filePath.replaceAll('\\\\', '/')\n}\n\n/**\n * Strips the file extension from a path or file name.\n * Only removes the last `.ext` segment when the dot is not part of a directory name.\n *\n * @example\n * trimExtName('petStore.ts') // 'petStore'\n * trimExtName('/src/models/pet.ts') // '/src/models/pet'\n * trimExtName('/project.v2/gen/pet.ts') // '/project.v2/gen/pet'\n * trimExtName('noExtension') // 'noExtension'\n */\nexport function trimExtName(text: string): string {\n const dotIndex = text.lastIndexOf('.')\n if (dotIndex > 0 && !text.includes('/', dotIndex)) {\n return text.slice(0, dotIndex)\n }\n return text\n}\n\n/**\n * Resolves to `true` when `path` is `parent` itself or nested inside it. Both sides are resolved\n * to absolute paths first, so relative and `..`-containing inputs compare correctly.\n *\n * Guards a destructive or an out-of-tree operation: before wiping an output directory, check that\n * it does not contain the project root, and before loading a path a caller supplied, check that it\n * did not escape the directory it is allowed to read from.\n *\n * @example\n * isPathInside('./src/gen', '.') // true — nested inside the root\n * isPathInside('.', '.') // true — the same directory counts as inside\n * isPathInside('.', './src/gen') // false — the root is not inside its own output\n * isPathInside('../other', '.') // false — escapes the root\n */\nexport function isPathInside(path: string, parent: string): boolean {\n const resolvedPath = resolve(path)\n const resolvedParent = resolve(parent)\n if (resolvedPath === resolvedParent) {\n return true\n }\n\n const rel = relative(resolvedParent, resolvedPath)\n\n return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel)\n}\n","/** A value that may already be resolved or still pending.\n *\n * @example\n * ```ts\n * function load(id: string): PossiblePromise<string> {\n * return cache.get(id) ?? fetchRemote(id)\n * }\n * ```\n */\nexport type PossiblePromise<T> = Promise<T> | T\n\n/** Returns `true` when `result` is a thenable `Promise`.\n *\n * @example\n * ```ts\n * isPromise(Promise.resolve(1)) // true\n * isPromise(42) // false\n * ```\n */\nexport function isPromise<T>(result: PossiblePromise<T>): result is Promise<T> {\n return result !== null && result !== undefined && typeof (result as Record<string, unknown>)['then'] === 'function'\n}\n\ntype Store<TKey, TValue> = {\n has(key: TKey): boolean\n get(key: TKey): TValue | undefined\n set(key: TKey, value: TValue): unknown\n}\n\n/**\n * Wraps `factory` with a keyed cache backed by the provided store.\n *\n * Pass a `WeakMap` for object keys (results are GC-eligible when the key is\n * collected) or a `Map` for primitive keys. For multi-argument functions,\n * nest two `memoize` calls — the outer keyed by the first argument, the\n * inner (created once per outer miss) keyed by the second.\n *\n * Because the cache is owned by the caller, it can be shared, inspected, or\n * cleared independently of the memoized function.\n *\n * @example Single WeakMap key\n * ```ts\n * const cache = new WeakMap<SchemaNode, Set<string>>()\n * const getRefs = memoize(cache, (node) => collectRefs(node))\n * ```\n *\n * @example Single Map key (primitive)\n * ```ts\n * const cache = new Map<string, Resolver>()\n * const getResolver = memoize(cache, (name) => buildResolver(name))\n * ```\n *\n * @example Two-level (object + primitive)\n * ```ts\n * const outer = new WeakMap<Params[], Map<string, Params[]>>()\n * const fn = memoize(outer, (params) => memoize(new Map(), (key) => transform(params, key)))\n * fn(params)('camelcase')\n * ```\n */\nexport function memoize<TKey, TValue>(store: Store<TKey, TValue>, factory: (key: TKey) => TValue): (key: TKey) => TValue {\n return (key: TKey): TValue => {\n if (store.has(key)) return store.get(key)!\n const value = factory(key)\n store.set(key, value)\n return value\n }\n}\n\ntype ParallelOptions<TItem> = {\n /**\n * Items to work through, handed out in order.\n */\n items: ReadonlyArray<TItem>\n /**\n * How many items may be in flight at once.\n */\n limit: number\n /**\n * Runs once per item, with the item's position so a caller can report progress.\n */\n run(item: TItem, index: number): Promise<void>\n}\n\n/**\n * Runs `run` over every item with at most `limit` in flight. Workers share one iterator, so each\n * takes the next item the moment it frees up instead of waiting for a batch to drain.\n *\n * @example\n * ```ts\n * await inParallel({ items: files, limit: 50, run: (file) => storage.writeItem(file.path, file.source) })\n * ```\n */\nexport async function inParallel<TItem>({ items, limit, run }: ParallelOptions<TItem>): Promise<void> {\n const queue = items.entries()\n\n const worker = async (): Promise<void> => {\n for (const [index, item] of queue) await run(item, index)\n }\n\n await Promise.all(Array.from({ length: Math.min(limit, items.length) }, () => worker()))\n}\n","/**\n * Maximum number of █ characters in a plugin timing bar.\n */\nexport const SUMMARY_MAX_BAR_LENGTH = 10 as const\n\n/**\n * Divides elapsed milliseconds into bar-length units (1 block per 100 ms).\n */\nexport const SUMMARY_TIME_SCALE_DIVISOR = 100 as const\n\n/**\n * Upper bound of hook listeners a single plugin can add to one hook (its schema, operation,\n * and operations generators, plus lifecycle hooks). Used to size the hooks emitter's\n * max-listener ceiling so a multi-generator plugin set does not trip Node's leak warning.\n */\nexport const HOOK_LISTENERS_PER_PLUGIN = 4\n\n/**\n * How many files Kubb keeps in flight at once, both writing the output and reading it back to\n * record what the output passes produced. Caps parsed sources held in memory and open file\n * descriptors alike.\n */\nexport const FILE_CONCURRENCY = 50\n\n/**\n * Plugin `include` filter types that select operations directly. When one of these is set\n * without a `schemaName` include, the generate phase pre-scans operations to compute the set\n * of schemas they reach, so unreachable schemas can be pruned for that plugin.\n */\nexport const OPERATION_FILTER_TYPES: ReadonlySet<string> = new Set(['tag', 'operationId', 'path', 'method', 'contentType'])\n\n/**\n * Stable codes Kubb attaches to a `Diagnostic`. Each maps to a known failure mode\n * and stays stable so it can be referenced in tooling and (later) docs. Reference\n * these instead of inlining the string at a throw site.\n */\nexport const diagnosticCode = {\n /**\n * Fallback for an unstructured error with no specific code.\n */\n unknown: 'KUBB_UNKNOWN',\n /**\n * The file or URL set as `input` could not be read.\n */\n inputNotFound: 'KUBB_INPUT_NOT_FOUND',\n /**\n * A URL set as `input` (or referenced by a `$ref`) answered with a 4xx or 5xx status\n * instead of the document.\n */\n inputRequestFailed: 'KUBB_INPUT_REQUEST_FAILED',\n /**\n * A URL set as `input` (or referenced by a `$ref`) never answered, so the request failed\n * before a status was returned.\n */\n inputUnreachable: 'KUBB_INPUT_UNREACHABLE',\n /**\n * An adapter was configured without an `input`.\n */\n inputRequired: 'KUBB_INPUT_REQUIRED',\n /**\n * `input` uses the v4 `{ path }` / `{ data }` wrapper, which v5 reads as a parsed\n * document instead of a pointer to one.\n */\n legacyInput: 'KUBB_LEGACY_INPUT',\n /**\n * The parsed `input` carries no `openapi` or `swagger` version, so it is not a\n * document the adapter can read.\n */\n invalidDocument: 'KUBB_INVALID_DOCUMENT',\n /**\n * A `$ref` (or equivalent reference) could not be resolved in the source document.\n */\n refNotFound: 'KUBB_REF_NOT_FOUND',\n /**\n * A server variable value is not allowed by its `enum`.\n */\n invalidServerVariable: 'KUBB_INVALID_SERVER_VARIABLE',\n /**\n * A required plugin is missing from the config.\n */\n pluginNotFound: 'KUBB_PLUGIN_NOT_FOUND',\n /**\n * A plugin threw while generating.\n */\n pluginFailed: 'KUBB_PLUGIN_FAILED',\n /**\n * A plugin reported a non-fatal warning through `ctx.warn`.\n */\n pluginWarning: 'KUBB_PLUGIN_WARNING',\n /**\n * A plugin reported an informational message through `ctx.info`.\n */\n pluginInfo: 'KUBB_PLUGIN_INFO',\n /**\n * A schema uses a `format` Kubb does not map to a specific type. Reserved for\n * adapters to emit as a `warning`.\n */\n unsupportedFormat: 'KUBB_UNSUPPORTED_FORMAT',\n /**\n * A referenced schema or operation is marked `deprecated`. Reserved for adapters\n * to emit as an `info`.\n */\n deprecated: 'KUBB_DEPRECATED',\n /**\n * An adapter is required but the config has none. The build cannot read the input\n * without one.\n */\n adapterRequired: 'KUBB_ADAPTER_REQUIRED',\n /**\n * A resolved output path escapes the output directory, which can stem from a path\n * traversal in the spec or a misconfigured `group.name`.\n */\n pathTraversal: 'KUBB_PATH_TRAVERSAL',\n /**\n * `output.clean` is enabled but `output.path` resolves to the project root or a parent of it,\n * so cleaning would delete kubb.config and every source file.\n */\n cleanRoot: 'KUBB_CLEAN_ROOT',\n /**\n * A plugin's options are invalid, for example `output.mode: 'file'` paired with a `group` option.\n */\n invalidPluginOptions: 'KUBB_INVALID_PLUGIN_OPTIONS',\n /**\n * A post-generate command (`output.postGenerate`) exited with a failure.\n */\n postGenerateFailed: 'KUBB_POST_GENERATE_FAILED',\n /**\n * The formatter pass over the generated files failed.\n */\n formatFailed: 'KUBB_FORMAT_FAILED',\n /**\n * The linter pass over the generated files failed.\n */\n lintFailed: 'KUBB_LINT_FAILED',\n /**\n * Not a failure. Carries a plugin's elapsed time, summed into the run total.\n */\n performance: 'KUBB_PERFORMANCE',\n /**\n * Not a failure. A newer Kubb version is available on npm.\n */\n updateAvailable: 'KUBB_UPDATE_AVAILABLE',\n} as const\n\n/**\n * Union of the stable {@link diagnosticCode} values.\n */\nexport type DiagnosticCode = (typeof diagnosticCode)[keyof typeof diagnosticCode]\n","import { EventEmitter as NodeEventEmitter } from 'node:events'\nimport { toError } from '@internals/utils'\n\n/**\n * A function that can be registered as a hook listener, synchronous or async. Any return value is\n * allowed and ignored, so handlers that return a result for their own callers still register.\n */\ntype AsyncListener<TArgs extends Array<unknown>> = (...args: TArgs) => unknown\n\n/**\n * Typed hook emitter that awaits all async listeners before resolving.\n * Wraps Node's `EventEmitter` with full TypeScript hook-map inference.\n *\n * @example\n * ```ts\n * const hooks = new Hookable<{ build: [name: string] }>()\n * hooks.hook('build', async (name) => { console.log(name) })\n * await hooks.callHook('build', 'petstore') // all listeners awaited\n * ```\n */\nexport class Hookable<THooks extends { [K in keyof THooks]: Array<unknown> }> {\n /**\n * Maximum number of listeners per hook before Node emits a memory-leak warning.\n * @default 10\n */\n constructor(maxListener = 10) {\n this.#emitter.setMaxListeners(maxListener)\n }\n\n #emitter = new NodeEventEmitter()\n\n /**\n * Calls `hookName` and awaits all registered listeners sequentially.\n * Throws if any listener rejects, wrapping the cause with the hook name and serialized arguments.\n *\n * @example\n * ```ts\n * await hooks.callHook('build', 'petstore')\n * ```\n */\n callHook<THookName extends keyof THooks & string>(hookName: THookName, ...hookArgs: THooks[THookName]): Promise<void> | void {\n const listeners = this.#emitter.listeners(hookName) as Array<AsyncListener<THooks[THookName]>>\n\n if (listeners.length === 0) {\n return\n }\n\n return this.#emitAll(hookName, listeners, hookArgs)\n }\n\n async #emitAll<THookName extends keyof THooks & string>(\n hookName: THookName,\n listeners: Array<AsyncListener<THooks[THookName]>>,\n hookArgs: THooks[THookName],\n ): Promise<void> {\n for (const listener of listeners) {\n try {\n await listener(...hookArgs)\n } catch (err) {\n let serializedArgs: string\n try {\n serializedArgs = JSON.stringify(hookArgs)\n } catch {\n serializedArgs = String(hookArgs)\n }\n throw new Error(`Error in async listener for \"${hookName}\" with hookArgs ${serializedArgs}`, { cause: toError(err) })\n }\n }\n }\n\n /**\n * Registers a persistent listener for `hookName` and returns a function that removes it.\n *\n * @example\n * ```ts\n * const unhook = hooks.hook('build', async (name) => { console.log(name) })\n * unhook() // removes it\n * ```\n */\n hook<THookName extends keyof THooks & string>(hookName: THookName, handler: AsyncListener<THooks[THookName]>): () => void {\n this.#emitter.on(hookName, handler as AsyncListener<Array<unknown>>)\n return () => this.removeHook(hookName, handler)\n }\n\n /**\n * Registers every handler in `configHooks` at once and returns a function that removes them\n * all. Undefined entries are skipped, so a partial hook object registers only its present keys.\n *\n * @example\n * ```ts\n * const unhook = hooks.addHooks({ build: onBuild, done: onDone })\n * unhook() // removes both\n * ```\n */\n addHooks(configHooks: Partial<{ [K in keyof THooks & string]: AsyncListener<THooks[K]> }>): () => void {\n const unhooks = (Object.keys(configHooks) as Array<keyof THooks & string>)\n .filter((name) => configHooks[name])\n .map((name) => this.hook(name, configHooks[name]!))\n\n return () => {\n for (const unhook of unhooks) unhook()\n }\n }\n\n /**\n * Removes a previously registered listener.\n *\n * @example\n * ```ts\n * hooks.removeHook('build', handler)\n * ```\n */\n removeHook<THookName extends keyof THooks & string>(hookName: THookName, handler: AsyncListener<THooks[THookName]>): void {\n this.#emitter.off(hookName, handler as AsyncListener<Array<unknown>>)\n }\n\n /**\n * Returns the number of listeners registered for `hookName`.\n *\n * @example\n * ```ts\n * hooks.hook('build', handler)\n * hooks.listenerCount('build') // 1\n * ```\n */\n listenerCount<THookName extends keyof THooks & string>(hookName: THookName): number {\n return this.#emitter.listenerCount(hookName)\n }\n\n /**\n * Raises or lowers the per-hook listener ceiling before Node warns about a memory leak.\n * Set this above the expected listener count when many listeners attach by design.\n *\n * @example\n * ```ts\n * hooks.setMaxListeners(40)\n * ```\n */\n setMaxListeners(max: number): void {\n this.#emitter.setMaxListeners(max)\n }\n\n /**\n * Removes all listeners from every hook channel.\n *\n * @example\n * ```ts\n * hooks.removeAllHooks()\n * ```\n */\n removeAllHooks(): void {\n this.#emitter.removeAllListeners()\n }\n}\n","import { inParallel, matchesStored, read } from '@internals/utils'\nimport { ast, extractStringsFromNodes, type CodeNode, type FileNode } from '@kubb/ast'\nimport { FILE_CONCURRENCY } from './constants.ts'\nimport type { Storage } from './createStorage.ts'\nimport type { Parser } from './defineParser.ts'\nimport type { OutputManifest } from './outputManifest.ts'\nimport { Hookable } from './Hookable.ts'\n\n/**\n * Hooks fired around a `FileManager#write` batch: `start` before it, `update` per file, `end` after.\n */\nexport type FileManagerHooks = {\n start: [files: Array<FileNode>]\n update: [params: { file: FileNode; source?: string; processed: number; total: number; percentage: number }]\n end: [files: Array<FileNode>]\n}\n\ntype ParseOptions = {\n parsers?: Map<FileNode['extname'], Parser>\n}\n\ntype WriteOptions = ParseOptions & {\n storage: Storage\n /**\n * Consulted before each write so a file the output passes already normalized is recognized as\n * unchanged. Omitted when no formatter, linter, or `postGenerate` step is configured.\n */\n manifest?: OutputManifest\n}\n\nfunction joinSources(file: FileNode): string {\n return file.sources\n .map((source) => extractStringsFromNodes(source.nodes as Array<CodeNode>))\n .filter(Boolean)\n .join('\\n\\n')\n}\n\nasync function parseCopy(file: FileNode): Promise<string> {\n let content: string\n try {\n content = await read(file.copy as string)\n } catch (err) {\n throw new Error(`[kubb] Could not copy file into output: ${file.copy}`, { cause: err })\n }\n\n return [file.banner, content, file.footer]\n .filter((segment): segment is string => Boolean(segment))\n .map((segment) => segment.trimEnd())\n .join('\\n')\n}\n\nfunction mergeFile<TMeta extends object = object>(a: FileNode<TMeta>, b: FileNode<TMeta>): FileNode<TMeta> {\n return {\n ...a,\n // Incoming file (b) takes precedence for banner/footer so a barrel file (whose\n // banner/footer the barrel plugin resolves last) wins over a plugin-generated\n // file at the same path.\n banner: b.banner,\n footer: b.footer,\n // A verbatim-copy file cannot be merged with rendered content; the incoming `copy` wins.\n copy: b.copy ?? a.copy,\n sources: a.sources.length ? (b.sources.length ? [...a.sources, ...b.sources] : a.sources) : b.sources,\n imports: a.imports.length ? (b.imports.length ? [...a.imports, ...b.imports] : a.imports) : b.imports,\n exports: a.exports.length ? (b.exports.length ? [...a.exports, ...b.exports] : a.exports) : b.exports,\n }\n}\n\nfunction isIndexPath(path: string): boolean {\n return path.endsWith('/index.ts') || path === 'index.ts'\n}\n\n// Sort order: shortest path first. Within a length bucket, index.ts barrels last.\nfunction compareFiles(a: FileNode, b: FileNode): number {\n const lenDiff = a.path.length - b.path.length\n if (lenDiff !== 0) return lenDiff\n const aIsIndex = isIndexPath(a.path)\n const bIsIndex = isIndexPath(b.path)\n if (aIsIndex && !bIsIndex) return 1\n if (!aIsIndex && bIsIndex) return -1\n\n return 0\n}\n\n// A file is unchanged either because the storage already holds this content, or because the\n// manifest knows the output passes turn this exact source into what the storage holds.\nfunction isUnchanged({ stored, source, key, manifest }: { stored: string | null; source: string; key: string; manifest?: OutputManifest }): boolean {\n if (stored === null) return false\n if (matchesStored({ stored, source })) return true\n\n return manifest?.isUpToDate({ key, source, disk: stored }) ?? false\n}\n\n/**\n * In-memory file store for generated files, and the writer that turns them into source\n * strings on `storage`. Files sharing a `path` are merged (sources/imports/exports\n * concatenated). The `files` getter is sorted by path length (barrel `index.ts` last\n * within a bucket).\n *\n * @example\n * ```ts\n * const manager = new FileManager()\n * manager.upsert(myFile)\n * manager.files // sorted view\n * await manager.write(manager.files, { storage: fsStorage() })\n * ```\n */\nexport class FileManager {\n readonly hooks = new Hookable<FileManagerHooks>()\n readonly #cache = new Map<string, FileNode>()\n // Cached sorted view. Null means stale and rebuilt lazily on next `files` read.\n // Nulled (not mutated) on every write so callers holding a prior reference keep\n // their snapshot. `dispose()` must not silently empty an array the consumer\n // already holds.\n #sorted: Array<FileNode> | null = null\n\n add(...files: Array<FileNode>): Array<FileNode> {\n return this.#store(files, false)\n }\n\n upsert(...files: Array<FileNode>): Array<FileNode> {\n return this.#store(files, true)\n }\n\n #store(files: ReadonlyArray<FileNode>, mergeExisting: boolean): Array<FileNode> {\n const batch = files.length > 1 ? this.#dedupe(files) : files\n const resolved: Array<FileNode> = []\n\n for (const file of batch) {\n const existing = this.#cache.get(file.path)\n const merged = existing && mergeExisting ? ast.factory.createFile(mergeFile(existing, file)) : ast.factory.createFile(file)\n this.#cache.set(merged.path, merged)\n resolved.push(merged)\n }\n\n if (resolved.length > 0) this.#sorted = null\n return resolved\n }\n\n // Merges same-path entries within a batch so the cache update loop stays\n // uniform. Only called for multi-file batches.\n #dedupe(files: ReadonlyArray<FileNode>): Array<FileNode> {\n const seen = new Map<string, FileNode>()\n for (const file of files) {\n const prev = seen.get(file.path)\n seen.set(file.path, prev ? mergeFile(prev, file) : file)\n }\n return [...seen.values()]\n }\n\n clear(): void {\n this.#cache.clear()\n this.#sorted = null\n }\n\n /**\n * Releases all stored files and clears every `hooks` listener. Called by the core after\n * `kubb:build:end`.\n */\n dispose(): void {\n this.clear()\n this.hooks.removeAllHooks()\n }\n\n /**\n * All stored files in stable sort order (shortest path first, barrel files\n * last within a length bucket). Returns a cached view, do not mutate.\n */\n get files(): Array<FileNode> {\n return (this.#sorted ??= [...this.#cache.values()].sort(compareFiles))\n }\n\n /**\n * Converts a file's AST sources (or its `copy` source) into the final on-disk string.\n */\n async parse(file: FileNode, { parsers }: ParseOptions = {}): Promise<string> {\n if (file.copy) {\n return parseCopy(file)\n }\n\n if (!parsers || !file.extname) {\n return joinSources(file)\n }\n\n const parser = parsers.get(file.extname)\n\n if (!parser) {\n return joinSources(file)\n }\n\n return parser.parse(file)\n }\n\n /**\n * Parses and writes every file through a bounded pool of workers. A small spec runs all its files\n * at once; a spec with thousands of files keeps at most {@link FILE_CONCURRENCY} parsed sources in\n * memory rather than holding every source, while still overlapping each file's write with the\n * next file's parse. Each `update` carries the file's input position, so a consumer can present\n * the files in generation order even though they finish in whatever order they parse.\n *\n * A file the storage already holds is skipped, so a rebuild that generates identical output\n * writes nothing and leaves every mtime where it was.\n */\n async write(files: Array<FileNode>, { storage, parsers, manifest }: WriteOptions): Promise<void> {\n if (files.length === 0) return\n\n await this.hooks.callHook('start', files)\n\n const total = files.length\n\n await inParallel({\n items: files,\n limit: FILE_CONCURRENCY,\n run: async (file, index) => {\n const source = await this.parse(file, { parsers })\n await this.hooks.callHook('update', { file, source, processed: index + 1, total, percentage: ((index + 1) / total) * 100 })\n if (!source) return\n\n // Checking here rather than inside a driver gives a storage writing to S3 or a database\n // the same guarantee `fsStorage` has, that unchanged content is never rewritten.\n const stored = await storage.readItem(file.path)\n if (isUnchanged({ stored, source, key: file.path, manifest })) return\n\n await storage.writeItem(file.path, source, { stored })\n\n // Only a file that was written can have been changed by the output passes.\n manifest?.track({ key: file.path, source })\n },\n })\n\n await this.hooks.callHook('end', files)\n }\n}\n","/**\n * Per-node memo shared by every plugin that generates from the same schema or operation node in\n * one generate pass. The driver creates one `NodeCache` per node during the walk and hands the\n * same instance to each plugin's generator context, so work derived purely from the node (its\n * resolved name, imports, parameters) is computed by the first plugin that needs it and reused by\n * the rest instead of being recomputed per plugin.\n *\n * Keys are namespaced by convention (`'plugin-ts:imports'`) so two plugins caching different\n * derivations of the same node never collide.\n *\n * @example Fill on first read, reuse afterwards\n * ```ts\n * const imports = ctx.cache.ensureItem('plugin-ts:imports', () => ctx.resolver.imports({ node, root, output }))\n * ```\n */\nexport type NodeCache = {\n /**\n * Returns the value stored under `key`, or `undefined` when nothing is stored yet.\n */\n readItem<TValue>(key: string): TValue | undefined\n /**\n * Stores `value` under `key`, overwriting any previous value, and returns it.\n */\n writeItem<TValue>(key: string, value: TValue): TValue\n /**\n * Returns the value stored under `key`, computing and storing it with `factory` on the first\n * call. Later calls with the same key return the stored value without running `factory` again.\n */\n ensureItem<TValue>(key: string, factory: () => TValue): TValue\n}\n\n/**\n * Builds an empty {@link NodeCache} backed by a plain `Map`. Called once per node in the generate\n * walk.\n */\nexport function createNodeCache(): NodeCache {\n const store = new Map<string, unknown>()\n\n return {\n readItem<TValue>(key: string): TValue | undefined {\n return store.get(key) as TValue | undefined\n },\n writeItem<TValue>(key: string, value: TValue): TValue {\n store.set(key, value)\n return value\n },\n ensureItem<TValue>(key: string, factory: () => TValue): TValue {\n if (store.has(key)) return store.get(key) as TValue\n\n const value = factory()\n store.set(key, value)\n return value\n },\n }\n}\n"],"mappings":"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"}
@@ -32,30 +32,13 @@ function camelCase(text, { prefix = "", suffix = "" } = {}) {
32
32
  //#endregion
33
33
  //#region ../../internals/utils/src/errors.ts
34
34
  /**
35
- * Thrown when one or more errors occur during a Kubb build.
36
- * Carries the full list of underlying errors on `errors`.
37
- *
38
- * @example
39
- * ```ts
40
- * throw new BuildError('Build failed', { errors: [err1, err2] })
41
- * ```
42
- */
43
- var BuildError = class extends Error {
44
- errors;
45
- constructor(message, options) {
46
- super(message, { cause: options.cause });
47
- this.name = "BuildError";
48
- this.errors = options.errors;
49
- }
50
- };
51
- /**
52
35
  * Coerces an unknown thrown value to an `Error` instance.
53
36
  * Returns the value as-is when it is already an `Error`; otherwise wraps it with `String(value)`.
54
37
  *
55
38
  * @example
56
39
  * ```ts
57
40
  * try { ... } catch(err) {
58
- * throw new BuildError('Build failed', { cause: toError(err), errors: [] })
41
+ * throw new Error('Build failed', { cause: toError(err) })
59
42
  * }
60
43
  * ```
61
44
  */
@@ -192,13 +175,15 @@ async function write(path, data, options = {}) {
192
175
  const content = `${trimmed}\n`;
193
176
  const resolved = resolve(path);
194
177
  let stored = options.stored;
195
- if (stored === void 0) if (runtime.isBun) {
196
- const file = Bun.file(resolved);
197
- stored = await file.exists() ? await file.text() : null;
198
- } else try {
199
- stored = await readFile(resolved, { encoding: "utf-8" });
200
- } catch {
201
- stored = null;
178
+ if (stored === void 0) {
179
+ if (runtime.isBun) {
180
+ const file = Bun.file(resolved);
181
+ stored = await file.exists() ? await file.text() : null;
182
+ } else try {
183
+ stored = await readFile(resolved, { encoding: "utf-8" });
184
+ } catch {
185
+ stored = null;
186
+ }
202
187
  }
203
188
  if (matchesStored({
204
189
  stored: stored ?? "",
@@ -237,26 +222,6 @@ async function clean(path) {
237
222
  });
238
223
  }
239
224
  /**
240
- * Resolves to `true` when `path` is `parent` itself or nested inside it. Both sides are resolved
241
- * to absolute paths first, so relative and `..`-containing inputs compare correctly.
242
- *
243
- * Guards destructive operations: before wiping an output directory, check that it does not contain
244
- * the project root, otherwise a `clean` would delete `kubb.config` and every source file.
245
- *
246
- * @example
247
- * isPathInside('./src/gen', '.') // true — nested inside the root
248
- * isPathInside('.', '.') // true — the same directory counts as inside
249
- * isPathInside('.', './src/gen') // false — the root is not inside its own output
250
- * isPathInside('../other', '.') // false — escapes the root
251
- */
252
- function isPathInside(path, parent) {
253
- const resolvedPath = resolve(path);
254
- const resolvedParent = resolve(parent);
255
- if (resolvedPath === resolvedParent) return true;
256
- const rel = relative(resolvedParent, resolvedPath);
257
- return rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
258
- }
259
- /**
260
225
  * Converts a filesystem path to use POSIX (`/`) separators.
261
226
  *
262
227
  * Most of the codebase compares and composes paths as strings (prefix matching, joining for
@@ -274,27 +239,25 @@ function toPosixPath(filePath) {
274
239
  return filePath.replaceAll("\\", "/");
275
240
  }
276
241
  /**
277
- * Builds a nested file path from a dotted name. Splits on dots that precede a letter
278
- * (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
279
- * every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
280
- *
281
- * Empty segments are dropped before joining. They arise when the name starts with a dot
282
- * followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
283
- * an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
284
- * absolute path, letting generated files escape the configured output directory.
285
- *
286
- * @example Nested path from a dotted name
287
- * `toFilePath('pet.petId') // 'pet/petId'`
242
+ * Resolves to `true` when `path` is `parent` itself or nested inside it. Both sides are resolved
243
+ * to absolute paths first, so relative and `..`-containing inputs compare correctly.
288
244
  *
289
- * @example PascalCase the final segment
290
- * `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
245
+ * Guards a destructive or an out-of-tree operation: before wiping an output directory, check that
246
+ * it does not contain the project root, and before loading a path a caller supplied, check that it
247
+ * did not escape the directory it is allowed to read from.
291
248
  *
292
- * @example Suffix applied to the final segment only
293
- * `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
249
+ * @example
250
+ * isPathInside('./src/gen', '.') // true — nested inside the root
251
+ * isPathInside('.', '.') // true — the same directory counts as inside
252
+ * isPathInside('.', './src/gen') // false — the root is not inside its own output
253
+ * isPathInside('../other', '.') // false — escapes the root
294
254
  */
295
- function toFilePath(name, caseLast = camelCase) {
296
- const parts = name.split(/\.(?=[a-zA-Z])/);
297
- return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
255
+ function isPathInside(path, parent) {
256
+ const resolvedPath = resolve(path);
257
+ const resolvedParent = resolve(parent);
258
+ if (resolvedPath === resolvedParent) return true;
259
+ const rel = relative(resolvedParent, resolvedPath);
260
+ return rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
298
261
  }
299
262
  //#endregion
300
263
  //#region ../../internals/utils/src/promise.ts
@@ -760,7 +723,7 @@ function createNodeCache() {
760
723
  };
761
724
  }
762
725
  //#endregion
763
- //#region \0@oxc-project+runtime@0.142.0/helpers/esm/usingCtx.js
726
+ //#region \0@oxc-project+runtime@0.148.0/helpers/esm/usingCtx.js
764
727
  function _usingCtx() {
765
728
  var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
766
729
  var n = Error();
@@ -820,6 +783,6 @@ function _usingCtx() {
820
783
  };
821
784
  }
822
785
  //#endregion
823
- export { OPERATION_FILTER_TYPES as a, clean as c, toPosixPath as d, write as f, camelCase as g, toError as h, Hookable as i, isPathInside as l, getErrorMessage as m, createNodeCache as n, diagnosticCode as o, BuildError as p, FileManager as r, inParallel as s, _usingCtx as t, toFilePath as u };
786
+ export { OPERATION_FILTER_TYPES as a, clean as c, write as d, getErrorMessage as f, Hookable as i, isPathInside as l, camelCase as m, createNodeCache as n, diagnosticCode as o, toError as p, FileManager as r, inParallel as s, _usingCtx as t, toPosixPath as u };
824
787
 
825
- //# sourceMappingURL=usingCtx-2r6N3STL.js.map
788
+ //# sourceMappingURL=usingCtx-xMZZh-09.js.map