@kubb/plugin-zod 5.0.0-beta.4 → 5.0.0-beta.56

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -2,8 +2,15 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
+ //#region \0rolldown/runtime.js
6
+ var __defProp = Object.defineProperty;
7
+ var __name = (target, value) => __defProp(target, "name", {
8
+ value,
9
+ configurable: true
10
+ });
5
11
  //#endregion
6
12
  let _kubb_core = require("@kubb/core");
13
+ let _kubb_ast_utils = require("@kubb/ast/utils");
7
14
  let _kubb_renderer_jsx = require("@kubb/renderer-jsx");
8
15
  let _kubb_renderer_jsx_jsx_runtime = require("@kubb/renderer-jsx/jsx-runtime");
9
16
  //#region ../../internals/utils/src/casing.ts
@@ -17,117 +24,261 @@ let _kubb_renderer_jsx_jsx_runtime = require("@kubb/renderer-jsx/jsx-runtime");
17
24
  function toCamelOrPascal(text, pascal) {
18
25
  return text.trim().replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/(\d)([a-z])/g, "$1 $2").split(/[\s\-_./\\:]+/).filter(Boolean).map((word, i) => {
19
26
  if (word.length > 1 && word === word.toUpperCase()) return word;
20
- if (i === 0 && !pascal) return word.charAt(0).toLowerCase() + word.slice(1);
21
- return word.charAt(0).toUpperCase() + word.slice(1);
27
+ return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
22
28
  }).join("").replace(/[^a-zA-Z0-9]/g, "");
23
29
  }
24
30
  /**
25
- * Splits `text` on `.` and applies `transformPart` to each segment.
26
- * The last segment receives `isLast = true`, all earlier segments receive `false`.
27
- * Segments are joined with `/` to form a file path.
28
- *
29
- * Only splits on dots followed by a letter so that version numbers
30
- * embedded in operationIds (e.g. `v2025.0`) are kept intact.
31
- */
32
- function applyToFileParts(text, transformPart) {
33
- const parts = text.split(/\.(?=[a-zA-Z])/);
34
- return parts.map((part, i) => transformPart(part, i === parts.length - 1)).join("/");
35
- }
36
- /**
37
31
  * Converts `text` to camelCase.
38
- * When `isFile` is `true`, dot-separated segments are each cased independently and joined with `/`.
39
32
  *
40
- * @example
41
- * camelCase('hello-world') // 'helloWorld'
42
- * camelCase('pet.petId', { isFile: true }) // 'pet/petId'
33
+ * @example Word boundaries
34
+ * `camelCase('hello-world') // 'helloWorld'`
35
+ *
36
+ * @example With a prefix
37
+ * `camelCase('tag', { prefix: 'create' }) // 'createTag'`
43
38
  */
44
- function camelCase(text, { isFile, prefix = "", suffix = "" } = {}) {
45
- if (isFile) return applyToFileParts(text, (part, isLast) => camelCase(part, isLast ? {
46
- prefix,
47
- suffix
48
- } : {}));
39
+ function camelCase(text, { prefix = "", suffix = "" } = {}) {
49
40
  return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
50
41
  }
51
42
  /**
52
43
  * Converts `text` to PascalCase.
53
- * When `isFile` is `true`, the last dot-separated segment is PascalCased and earlier segments are camelCased.
54
44
  *
55
- * @example
56
- * pascalCase('hello-world') // 'HelloWorld'
57
- * pascalCase('pet.petId', { isFile: true }) // 'pet/PetId'
45
+ * @example Word boundaries
46
+ * `pascalCase('hello-world') // 'HelloWorld'`
47
+ *
48
+ * @example With a suffix
49
+ * `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`
58
50
  */
59
- function pascalCase(text, { isFile, prefix = "", suffix = "" } = {}) {
60
- if (isFile) return applyToFileParts(text, (part, isLast) => isLast ? pascalCase(part, {
61
- prefix,
62
- suffix
63
- }) : camelCase(part));
51
+ function pascalCase(text, { prefix = "", suffix = "" } = {}) {
64
52
  return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
65
53
  }
66
54
  //#endregion
67
- //#region ../../internals/utils/src/string.ts
55
+ //#region ../../internals/utils/src/fs.ts
68
56
  /**
69
- * Strips a single matching pair of `"..."`, `'...'`, or `` `...` `` from both ends of `text`.
70
- * Returns the string unchanged when no balanced quote pair is found.
57
+ * Builds a nested file path from a dotted name. Splits on dots that precede a letter
58
+ * (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
59
+ * every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
71
60
  *
72
- * @example
73
- * trimQuotes('"hello"') // 'hello'
74
- * trimQuotes('hello') // 'hello'
61
+ * Empty segments are dropped before joining. They arise when the name starts with a dot
62
+ * followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
63
+ * an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
64
+ * absolute path, letting generated files escape the configured output directory.
65
+ *
66
+ * @example Nested path from a dotted name
67
+ * `toFilePath('pet.petId') // 'pet/petId'`
68
+ *
69
+ * @example PascalCase the final segment
70
+ * `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
71
+ *
72
+ * @example Suffix applied to the final segment only
73
+ * `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
75
74
  */
76
- function trimQuotes(text) {
77
- if (text.length >= 2) {
78
- const first = text[0];
79
- const last = text[text.length - 1];
80
- if (first === "\"" && last === "\"" || first === "'" && last === "'" || first === "`" && last === "`") return text.slice(1, -1);
81
- }
82
- return text;
75
+ function toFilePath(name, caseLast = camelCase) {
76
+ const parts = name.split(/\.(?=[a-zA-Z])/);
77
+ return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
83
78
  }
84
79
  //#endregion
85
- //#region ../../internals/utils/src/object.ts
80
+ //#region ../../internals/utils/src/reserved.ts
86
81
  /**
87
- * Serializes a primitive value to a JSON string literal, stripping any surrounding quote characters first.
82
+ * JavaScript and Java reserved words.
83
+ * @link https://github.com/jonschlinkert/reserved/blob/master/index.js
84
+ */
85
+ const reservedWords = new Set([
86
+ "abstract",
87
+ "arguments",
88
+ "boolean",
89
+ "break",
90
+ "byte",
91
+ "case",
92
+ "catch",
93
+ "char",
94
+ "class",
95
+ "const",
96
+ "continue",
97
+ "debugger",
98
+ "default",
99
+ "delete",
100
+ "do",
101
+ "double",
102
+ "else",
103
+ "enum",
104
+ "eval",
105
+ "export",
106
+ "extends",
107
+ "false",
108
+ "final",
109
+ "finally",
110
+ "float",
111
+ "for",
112
+ "function",
113
+ "goto",
114
+ "if",
115
+ "implements",
116
+ "import",
117
+ "in",
118
+ "instanceof",
119
+ "int",
120
+ "interface",
121
+ "let",
122
+ "long",
123
+ "native",
124
+ "new",
125
+ "null",
126
+ "package",
127
+ "private",
128
+ "protected",
129
+ "public",
130
+ "return",
131
+ "short",
132
+ "static",
133
+ "super",
134
+ "switch",
135
+ "synchronized",
136
+ "this",
137
+ "throw",
138
+ "throws",
139
+ "transient",
140
+ "true",
141
+ "try",
142
+ "typeof",
143
+ "var",
144
+ "void",
145
+ "volatile",
146
+ "while",
147
+ "with",
148
+ "yield",
149
+ "Array",
150
+ "Date",
151
+ "hasOwnProperty",
152
+ "Infinity",
153
+ "isFinite",
154
+ "isNaN",
155
+ "isPrototypeOf",
156
+ "length",
157
+ "Math",
158
+ "name",
159
+ "NaN",
160
+ "Number",
161
+ "Object",
162
+ "prototype",
163
+ "String",
164
+ "toString",
165
+ "undefined",
166
+ "valueOf"
167
+ ]);
168
+ /**
169
+ * Returns `true` when `name` is a syntactically valid JavaScript variable name.
88
170
  *
89
171
  * @example
90
- * stringify('hello') // '"hello"'
91
- * stringify('"hello"') // '"hello"'
172
+ * ```ts
173
+ * isValidVarName('status') // true
174
+ * isValidVarName('class') // false (reserved word)
175
+ * isValidVarName('42foo') // false (starts with digit)
176
+ * ```
92
177
  */
93
- function stringify(value) {
94
- if (value === void 0 || value === null) return "\"\"";
95
- return JSON.stringify(trimQuotes(value.toString()));
178
+ function isValidVarName(name) {
179
+ if (!name || reservedWords.has(name)) return false;
180
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
96
181
  }
97
182
  /**
98
- * Converts a plain object into a multiline key-value string suitable for embedding in generated code.
99
- * Nested objects are recursively stringified with indentation.
183
+ * Returns `name` when it's a syntactically valid JavaScript variable name,
184
+ * otherwise prefixes it with `_` so the result is a valid identifier.
185
+ *
186
+ * Useful for sanitizing OpenAPI schema names or operation IDs that start with
187
+ * a digit (e.g. `409`, `504AccountCancel`) before using them as exported
188
+ * variable, type, or function names.
100
189
  *
101
190
  * @example
102
- * stringifyObject({ foo: 'bar', nested: { a: 1 } })
103
- * // 'foo: bar,\nnested: {\n a: 1\n }'
191
+ * ```ts
192
+ * ensureValidVarName('409') // '_409'
193
+ * ensureValidVarName('504AccountCancel') // '_504AccountCancel'
194
+ * ensureValidVarName('Pet') // 'Pet'
195
+ * ensureValidVarName('class') // '_class'
196
+ * ```
104
197
  */
105
- function stringifyObject(value) {
106
- return Object.entries(value).map(([key, val]) => {
107
- if (val !== null && typeof val === "object") return `${key}: {\n ${stringifyObject(val)}\n }`;
108
- return `${key}: ${val}`;
109
- }).filter(Boolean).join(",\n");
198
+ function ensureValidVarName(name) {
199
+ if (!name || isValidVarName(name)) return name;
200
+ return `_${name}`;
110
201
  }
111
202
  //#endregion
112
- //#region ../../internals/utils/src/regexp.ts
203
+ //#region ../../internals/shared/src/operation.ts
204
+ /**
205
+ * Maps a content type to the PascalCase suffix used to name per-content-type variants
206
+ * (e.g. `application/json` → `Json`, `application/xml` → `Xml`, `multipart/form-data` → `FormData`).
207
+ */
208
+ function getContentTypeSuffix(contentType) {
209
+ const baseType = contentType.split(";")[0].trim();
210
+ if (baseType === "application/json") return "Json";
211
+ if (baseType === "multipart/form-data") return "FormData";
212
+ if (baseType === "application/x-www-form-urlencoded") return "FormUrlEncoded";
213
+ const parts = (baseType.split("/").pop() ?? baseType).split(/[^a-zA-Z0-9]+/).filter(Boolean);
214
+ if (parts.length === 0) return "Unknown";
215
+ return parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
216
+ }
217
+ /**
218
+ * Appends a content-type suffix to a base name, keeping a trailing `Data` segment last
219
+ * (e.g. `AddPetData` + `Json` → `AddPetJsonData`, `AddPetStatus200` + `Xml` → `AddPetStatus200Xml`).
220
+ */
221
+ function getPerContentTypeName(baseName, suffix) {
222
+ if (baseName.endsWith("Data")) return suffix.endsWith("Data") ? baseName.slice(0, -4) + suffix : `${baseName.slice(0, -4)}${suffix}Data`;
223
+ return baseName + suffix;
224
+ }
113
225
  /**
114
- * Converts a pattern string into a `new RegExp(...)` constructor call or a regex literal string.
115
- * Inline flags expressed as `^(?im)` prefixes are extracted and applied to the resulting expression.
116
- * Pass `null` as the second argument to emit a `/pattern/flags` literal instead.
226
+ * Resolves per-content-type variant names for a set of content entries, deduplicating suffix
227
+ * collisions with a numeric counter. Entries without a schema are skipped. The returned `suffix` is
228
+ * the final (possibly counter-augmented) value, so callers can derive parallel names in another
229
+ * namespace (e.g. plugin-faker deriving the matching plugin-ts type name).
230
+ */
231
+ function resolveContentTypeVariants(entries, baseName) {
232
+ const usedNames = /* @__PURE__ */ new Set();
233
+ return entries.filter((entry) => entry.schema).map((entry) => {
234
+ const baseSuffix = getContentTypeSuffix(entry.contentType);
235
+ let suffix = baseSuffix;
236
+ let name = getPerContentTypeName(baseName, suffix);
237
+ let counter = 2;
238
+ while (usedNames.has(name)) {
239
+ suffix = `${baseSuffix}${counter++}`;
240
+ name = getPerContentTypeName(baseName, suffix);
241
+ }
242
+ usedNames.add(name);
243
+ return {
244
+ name,
245
+ suffix,
246
+ schema: entry.schema,
247
+ keysToOmit: entry.keysToOmit,
248
+ contentType: entry.contentType
249
+ };
250
+ });
251
+ }
252
+ //#endregion
253
+ //#region ../../internals/shared/src/group.ts
254
+ /**
255
+ * Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
256
+ * shared default naming so every plugin groups output consistently:
257
+ *
258
+ * - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
259
+ * - other groups use the camelCased group (`pet store` → `petStore`).
260
+ *
261
+ * A user-provided `group.name` always wins over the default namer, so callers stay in
262
+ * control of their output folders. Returns `null` when grouping is disabled, matching the
263
+ * per-plugin convention.
264
+ *
265
+ * @param group - The user-supplied group option, or `undefined` to disable grouping.
117
266
  *
118
267
  * @example
119
- * toRegExpString('^(?im)foo') // → 'new RegExp("foo", "im")'
120
- * toRegExpString('^(?im)foo', null) // '/foo/im'
268
+ * ```ts
269
+ * createGroupConfig(group) // shared across every plugin
270
+ * ```
121
271
  */
122
- function toRegExpString(text, func = "RegExp") {
123
- const raw = trimQuotes(text);
124
- const match = raw.match(/^\^(\(\?([igmsuy]+)\))/i);
125
- const replacementTarget = match?.[1] ?? "";
126
- const matchedFlags = match?.[2];
127
- const cleaned = raw.replace(/^\\?\//, "").replace(/\\?\/$/, "").replace(replacementTarget, "");
128
- const { source, flags } = new RegExp(cleaned, matchedFlags);
129
- if (func === null) return `/${source}/${flags}`;
130
- return `new ${func}(${JSON.stringify(source)}${flags ? `, ${JSON.stringify(flags)}` : ""})`;
272
+ function createGroupConfig(group) {
273
+ if (!group) return null;
274
+ const defaultName = (ctx) => {
275
+ if (group.type === "path") return `${ctx.group.split("/")[1]}`;
276
+ return camelCase(ctx.group);
277
+ };
278
+ return {
279
+ ...group,
280
+ name: group.name ? group.name : defaultName
281
+ };
131
282
  }
132
283
  //#endregion
133
284
  //#region src/components/Operations.tsx
@@ -137,6 +288,7 @@ function Operations({ name, operations }) {
137
288
  return prev;
138
289
  }, {});
139
290
  const pathsJSON = operations.reduce((prev, acc) => {
291
+ if (!_kubb_core.ast.isHttpOperationNode(acc.node)) return prev;
140
292
  prev[`"${acc.node.path}"`] = {
141
293
  ...prev[`"${acc.node.path}"`] ?? {},
142
294
  [acc.node.method]: `operations["${acc.node.operationId}"]`
@@ -186,7 +338,7 @@ function Operations({ name, operations }) {
186
338
  export: true,
187
339
  name,
188
340
  asConst: true,
189
- children: `{${stringifyObject(operationsJSON)}}`
341
+ children: `{${(0, _kubb_ast_utils.stringifyObject)(operationsJSON)}}`
190
342
  })
191
343
  }),
192
344
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
@@ -197,7 +349,7 @@ function Operations({ name, operations }) {
197
349
  export: true,
198
350
  name: "paths",
199
351
  asConst: true,
200
- children: `{${stringifyObject(pathsJSON)}}`
352
+ children: `{${(0, _kubb_ast_utils.stringifyObject)(pathsJSON)}}`
201
353
  })
202
354
  })
203
355
  ] });
@@ -246,6 +398,61 @@ function shouldCoerce(coercion, type) {
246
398
  return !!coercion[type];
247
399
  }
248
400
  /**
401
+ * Registered codecs, checked in order.
402
+ */
403
+ const codecs = [{
404
+ matches(node) {
405
+ return node.type === "date" && node.representation === "date";
406
+ },
407
+ decode(node) {
408
+ return node.format === "date" ? "z.iso.date().transform((value) => new Date(value))" : "z.iso.datetime().transform((value) => new Date(value))";
409
+ },
410
+ encode(node) {
411
+ return node.format === "date" ? "z.date().transform((value) => value.toISOString().slice(0, 10))" : "z.date().transform((value) => value.toISOString())";
412
+ }
413
+ }];
414
+ /**
415
+ * Returns the codec for this node, or `undefined` when the node needs no
416
+ * encode/decode (its wire and runtime types match).
417
+ */
418
+ function getCodec(node) {
419
+ if (!node) return void 0;
420
+ return codecs.find((codec) => codec.matches(node));
421
+ }
422
+ /**
423
+ * Returns `true` when the node itself is encoded/decoded by a codec.
424
+ */
425
+ function hasCodec(node) {
426
+ return getCodec(node) !== void 0;
427
+ }
428
+ /**
429
+ * Returns `true` when the schema transitively contains a codec node —
430
+ * a value whose runtime type differs from its wire type (see {@link hasCodec}),
431
+ * so it must be decoded (response) or encoded (request) at the validation boundary.
432
+ * `$ref`s are followed via their resolved schema; a `seen` set guards cycles.
433
+ */
434
+ function containsCodec(node, seen = /* @__PURE__ */ new Set()) {
435
+ if (!node) return false;
436
+ if (hasCodec(node)) return true;
437
+ if (node.type === "ref") {
438
+ if (!node.ref) return false;
439
+ const refName = (0, _kubb_ast_utils.extractRefName)(node.ref);
440
+ if (refName) {
441
+ if (seen.has(refName)) return false;
442
+ seen.add(refName);
443
+ }
444
+ const resolved = _kubb_core.ast.syncSchemaRef(node);
445
+ if (resolved.type === "ref") return false;
446
+ return containsCodec(resolved, seen);
447
+ }
448
+ const children = [];
449
+ if ("properties" in node && node.properties) children.push(...node.properties.map((prop) => prop.schema));
450
+ if ("items" in node && node.items) children.push(...node.items);
451
+ if ("members" in node && node.members) children.push(...node.members);
452
+ if ("additionalProperties" in node && node.additionalProperties && node.additionalProperties !== true) children.push(node.additionalProperties);
453
+ return children.some((child) => containsCodec(child, seen));
454
+ }
455
+ /**
249
456
  * Collects all resolved schema names for an operation's parameters and responses
250
457
  * into a single lookup object, useful for building imports and type references.
251
458
  */
@@ -265,11 +472,11 @@ function buildSchemaNames(node, { params, resolver }) {
265
472
  }
266
473
  responses["default"] = resolver.resolveResponseName(node);
267
474
  return {
268
- request: node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : void 0,
475
+ request: node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null,
269
476
  parameters: {
270
- path: pathParam ? resolver.resolvePathParamsName(node, pathParam) : void 0,
271
- query: queryParam ? resolver.resolveQueryParamsName(node, queryParam) : void 0,
272
- header: headerParam ? resolver.resolveHeaderParamsName(node, headerParam) : void 0
477
+ path: pathParam ? resolver.resolvePathParamsName(node, pathParam) : null,
478
+ query: queryParam ? resolver.resolveQueryParamsName(node, queryParam) : null,
479
+ header: headerParam ? resolver.resolveHeaderParamsName(node, headerParam) : null
273
480
  },
274
481
  responses,
275
482
  errors
@@ -280,7 +487,7 @@ function buildSchemaNames(node, { params, resolver }) {
280
487
  * Objects become `{}`, primitives become their string representation, strings are quoted.
281
488
  */
282
489
  function formatDefault(value) {
283
- if (typeof value === "string") return stringify(value);
490
+ if (typeof value === "string") return (0, _kubb_ast_utils.stringify)(value);
284
491
  if (typeof value === "object" && value !== null) return "{}";
285
492
  return String(value ?? "");
286
493
  }
@@ -289,7 +496,7 @@ function formatDefault(value) {
289
496
  * Strings are quoted; numbers and booleans are emitted raw.
290
497
  */
291
498
  function formatLiteral(v) {
292
- if (typeof v === "string") return stringify(v);
499
+ if (typeof v === "string") return (0, _kubb_ast_utils.stringify)(v);
293
500
  return String(v);
294
501
  }
295
502
  /**
@@ -313,7 +520,7 @@ function lengthConstraints({ min, max, pattern }) {
313
520
  return [
314
521
  min !== void 0 ? `.min(${min})` : "",
315
522
  max !== void 0 ? `.max(${max})` : "",
316
- pattern !== void 0 ? `.regex(${toRegExpString(pattern, null)})` : ""
523
+ pattern !== void 0 ? `.regex(${(0, _kubb_ast_utils.toRegExpString)(pattern, null)})` : ""
317
524
  ].join("");
318
525
  }
319
526
  /**
@@ -335,7 +542,7 @@ function lengthChecksMini({ min, max, pattern }) {
335
542
  const checks = [];
336
543
  if (min !== void 0) checks.push(`z.minLength(${min})`);
337
544
  if (max !== void 0) checks.push(`z.maxLength(${max})`);
338
- if (pattern !== void 0) checks.push(`z.regex(${toRegExpString(pattern, null)})`);
545
+ if (pattern !== void 0) checks.push(`z.regex(${(0, _kubb_ast_utils.toRegExpString)(pattern, null)})`);
339
546
  return checks.length ? `.check(${checks.join(", ")})` : "";
340
547
  }
341
548
  /**
@@ -343,30 +550,44 @@ function lengthChecksMini({ min, max, pattern }) {
343
550
  * to a schema value string using the chainable Zod v4 API.
344
551
  */
345
552
  function applyModifiers({ value, nullable, optional, nullish, defaultValue, description }) {
346
- let result = value;
347
- if (nullish || nullable && optional) result = `${result}.nullish()`;
348
- else if (optional) result = `${result}.optional()`;
349
- else if (nullable) result = `${result}.nullable()`;
350
- if (defaultValue !== void 0) result = `${result}.default(${formatDefault(defaultValue)})`;
351
- if (description) result = `${result}.describe(${stringify(description)})`;
352
- return result;
553
+ const withModifier = (() => {
554
+ if (nullish || nullable && optional) return `${value}.nullish()`;
555
+ if (optional) return `${value}.optional()`;
556
+ if (nullable) return `${value}.nullable()`;
557
+ return value;
558
+ })();
559
+ const withDefault = defaultValue !== void 0 ? `${withModifier}.default(${formatDefault(defaultValue)})` : withModifier;
560
+ return description ? `${withDefault}.describe(${(0, _kubb_ast_utils.stringify)(description)})` : withDefault;
353
561
  }
354
562
  /**
355
563
  * Apply nullable / optional / nullish modifiers using the functional `zod/mini` API
356
564
  * (`z.nullable()`, `z.optional()`, `z.nullish()`).
357
565
  */
358
566
  function applyMiniModifiers({ value, nullable, optional, nullish, defaultValue }) {
359
- let result = value;
360
- if (nullish) result = `z.nullish(${result})`;
361
- else {
362
- if (nullable) result = `z.nullable(${result})`;
363
- if (optional) result = `z.optional(${result})`;
364
- }
365
- if (defaultValue !== void 0) result = `z._default(${result}, ${formatDefault(defaultValue)})`;
366
- return result;
567
+ const withModifier = (() => {
568
+ if (nullish) return `z.nullish(${value})`;
569
+ const withNullable = nullable ? `z.nullable(${value})` : value;
570
+ return optional ? `z.optional(${withNullable})` : withNullable;
571
+ })();
572
+ return defaultValue !== void 0 ? `z._default(${withModifier}, ${formatDefault(defaultValue)})` : withModifier;
367
573
  }
368
574
  //#endregion
369
575
  //#region src/printers/printerZod.ts
576
+ function strictOneOfMember$1(member, node) {
577
+ if (node.type === "object" && node.additionalProperties === void 0) return `${member}.strict()`;
578
+ if (node.type === "ref") {
579
+ if (member.startsWith("z.lazy(")) return member;
580
+ const schema = _kubb_core.ast.syncSchemaRef(node);
581
+ if (schema.type === "object" && (schema.additionalProperties === void 0 || schema.additionalProperties === false)) return `${member}.strict()`;
582
+ }
583
+ return member;
584
+ }
585
+ __name(strictOneOfMember$1, "strictOneOfMember");
586
+ function getMemberConstraint(member) {
587
+ if (member.primitive === "string") return lengthConstraints(_kubb_core.ast.narrowSchema(member, "string") ?? {}) || void 0;
588
+ if (member.primitive === "number" || member.primitive === "integer") return numberConstraints(_kubb_core.ast.narrowSchema(member, "number") ?? _kubb_core.ast.narrowSchema(member, "integer") ?? {}) || void 0;
589
+ if (member.primitive === "array") return lengthConstraints(_kubb_core.ast.narrowSchema(member, "array") ?? {}) || void 0;
590
+ }
370
591
  /**
371
592
  * Zod v4 printer built with `definePrinter`.
372
593
  *
@@ -403,8 +624,9 @@ const printerZod = _kubb_core.ast.definePrinter((options) => {
403
624
  return shouldCoerce(this.options.coercion, "numbers") ? "z.coerce.bigint()" : "z.bigint()";
404
625
  },
405
626
  date(node) {
406
- if (node.representation === "string") return "z.iso.date()";
407
- return shouldCoerce(this.options.coercion, "dates") ? "z.coerce.date()" : "z.date()";
627
+ const codec = getCodec(node);
628
+ if (codec) return this.options.direction === "input" ? codec.encode(node) : codec.decode(node);
629
+ return "z.iso.date()";
408
630
  },
409
631
  datetime(node) {
410
632
  const offset = node.offset || this.options.dateType === "stringOffset";
@@ -439,29 +661,30 @@ const printerZod = _kubb_core.ast.definePrinter((options) => {
439
661
  return `z.enum([${nonNullValues.map(formatLiteral).join(", ")}])`;
440
662
  },
441
663
  ref(node) {
442
- if (!node.name) return void 0;
443
- const refName = node.ref ? _kubb_core.ast.extractRefName(node.ref) ?? node.name : node.name;
444
- const resolvedName = node.ref ? this.options.resolver?.default(refName, "function") ?? refName : node.name;
664
+ if (!node.name) return null;
665
+ const refName = node.ref ? (0, _kubb_ast_utils.extractRefName)(node.ref) ?? node.name : node.name;
666
+ const useInputVariant = node.ref != null && this.options.direction === "input" && containsCodec(node);
667
+ const resolvedName = node.ref ? useInputVariant ? this.options.resolver?.resolveInputSchemaName(refName) ?? refName : this.options.resolver?.default(refName, "function") ?? refName : node.name;
445
668
  if (node.ref && this.options.cyclicSchemas?.has(refName)) return `z.lazy(() => ${resolvedName})`;
446
669
  return resolvedName;
447
670
  },
448
671
  object(node) {
449
- let result = `z.object({\n ${node.properties.map((prop) => {
672
+ const objectBase = `z.object(${(0, _kubb_ast_utils.buildObject)(node.properties.map((prop) => {
450
673
  const { name: propName, schema } = prop;
451
674
  const meta = _kubb_core.ast.syncSchemaRef(schema);
452
675
  const isNullable = meta.nullable;
453
676
  const isOptional = schema.optional;
454
677
  const isNullish = schema.nullish;
455
678
  const hasSelfRef = this.options.cyclicSchemas != null && _kubb_core.ast.containsCircularRef(schema, { circularSchemas: this.options.cyclicSchemas });
679
+ const savedCyclicSchemas = this.options.cyclicSchemas;
456
680
  if (hasSelfRef) this.options.cyclicSchemas = void 0;
457
681
  const baseOutput = this.transform(schema) ?? this.transform(_kubb_core.ast.createSchema({ type: "unknown" }));
458
- if (hasSelfRef) this.options.cyclicSchemas = options.cyclicSchemas;
682
+ if (hasSelfRef) this.options.cyclicSchemas = savedCyclicSchemas;
459
683
  const wrappedOutput = this.options.wrapOutput ? this.options.wrapOutput({
460
684
  output: baseOutput,
461
685
  schema
462
686
  }) || baseOutput : baseOutput;
463
- let descriptionToApply = meta.description;
464
- if (schema.type !== "ref" && meta.type === "ref") descriptionToApply = void 0;
687
+ const descriptionToApply = schema.type !== "ref" && meta.type === "ref" ? void 0 : meta.description;
465
688
  const value = applyModifiers({
466
689
  value: wrappedOutput,
467
690
  nullable: isNullable,
@@ -470,78 +693,65 @@ const printerZod = _kubb_core.ast.definePrinter((options) => {
470
693
  defaultValue: meta.default,
471
694
  description: descriptionToApply
472
695
  });
473
- if (hasSelfRef) return `get "${propName}"() { return ${value} }`;
474
- return `"${propName}": ${value}`;
475
- }).join(",\n ")}\n })`;
476
- if (node.additionalProperties && node.additionalProperties !== true) {
477
- const catchallType = this.transform(node.additionalProperties);
478
- if (catchallType) result += `.catchall(${catchallType})`;
479
- } else if (node.additionalProperties === true) result += `.catchall(${this.transform(_kubb_core.ast.createSchema({ type: "unknown" }))})`;
480
- else if (node.additionalProperties === false) result += ".strict()";
481
- return result;
696
+ if (hasSelfRef) return `get ${(0, _kubb_ast_utils.objectKey)(propName)}() { return ${value} }`;
697
+ return `${(0, _kubb_ast_utils.objectKey)(propName)}: ${value}`;
698
+ }))})`;
699
+ return (() => {
700
+ if (node.additionalProperties && node.additionalProperties !== true) {
701
+ const catchallType = this.transform(node.additionalProperties);
702
+ return catchallType ? `${objectBase}.catchall(${catchallType})` : objectBase;
703
+ }
704
+ if (node.additionalProperties === true) return `${objectBase}.catchall(${this.transform(_kubb_core.ast.createSchema({ type: "unknown" }))})`;
705
+ if (node.additionalProperties === false) return `${objectBase}.strict()`;
706
+ return objectBase;
707
+ })();
482
708
  },
483
709
  array(node) {
484
- let result = `z.array(${(node.items ?? []).map((item) => this.transform(item)).filter(Boolean).join(", ") || this.transform(_kubb_core.ast.createSchema({ type: "unknown" }))})${lengthConstraints(node)}`;
485
- if (node.unique) result += `.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })`;
486
- return result;
710
+ const base = `z.array(${(node.items ?? []).map((item) => this.transform(item)).filter(Boolean).join(", ") || this.transform(_kubb_core.ast.createSchema({ type: "unknown" }))})${lengthConstraints(node)}`;
711
+ return node.unique ? `${base}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })` : base;
487
712
  },
488
713
  tuple(node) {
489
- return `z.tuple([${(node.items ?? []).map((item) => this.transform(item)).filter(Boolean).join(", ")}])`;
714
+ return `z.tuple(${(0, _kubb_ast_utils.buildList)((node.items ?? []).map((item) => this.transform(item)).filter(Boolean))})`;
490
715
  },
491
716
  union(node) {
492
717
  const nodeMembers = node.members ?? [];
493
- const members = nodeMembers.map((m) => this.transform(m)).filter(Boolean);
718
+ const members = nodeMembers.map((memberNode) => {
719
+ const member = this.transform(memberNode);
720
+ return member && node.strategy === "one" ? strictOneOfMember$1(member, memberNode) : member;
721
+ }).filter(Boolean);
494
722
  if (members.length === 0) return "";
495
723
  if (members.length === 1) return members[0];
496
- if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === "intersection")) return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, [${members.join(", ")}])`;
497
- return `z.union([${members.join(", ")}])`;
724
+ if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === "intersection")) return `z.discriminatedUnion(${(0, _kubb_ast_utils.stringify)(node.discriminatorPropertyName)}, ${(0, _kubb_ast_utils.buildList)(members)})`;
725
+ return `z.union(${(0, _kubb_ast_utils.buildList)(members)})`;
498
726
  },
499
727
  intersection(node) {
500
728
  const members = node.members ?? [];
501
729
  if (members.length === 0) return "";
502
730
  const [first, ...rest] = members;
503
731
  if (!first) return "";
504
- let base = this.transform(first);
505
- if (!base) return "";
506
- for (const member of rest) {
507
- if (member.primitive === "string") {
508
- const c = lengthConstraints(_kubb_core.ast.narrowSchema(member, "string") ?? {});
509
- if (c) {
510
- base += c;
511
- continue;
512
- }
513
- } else if (member.primitive === "number" || member.primitive === "integer") {
514
- const c = numberConstraints(_kubb_core.ast.narrowSchema(member, "number") ?? _kubb_core.ast.narrowSchema(member, "integer") ?? {});
515
- if (c) {
516
- base += c;
517
- continue;
518
- }
519
- } else if (member.primitive === "array") {
520
- const c = lengthConstraints(_kubb_core.ast.narrowSchema(member, "array") ?? {});
521
- if (c) {
522
- base += c;
523
- continue;
524
- }
525
- }
732
+ const firstBase = this.transform(first);
733
+ if (!firstBase) return "";
734
+ return rest.reduce((acc, member) => {
735
+ const constraint = getMemberConstraint(member);
736
+ if (constraint) return acc + constraint;
526
737
  const transformed = this.transform(member);
527
- if (transformed) base = `${base}.and(${transformed})`;
528
- }
529
- return base;
738
+ return transformed ? `${acc}.and(${transformed})` : acc;
739
+ }, firstBase);
530
740
  },
531
741
  ...options.nodes
532
742
  },
533
743
  print(node) {
534
744
  const { keysToOmit } = this.options;
535
- let base = this.transform(node);
536
- if (!base) return null;
745
+ const transformed = this.transform(node);
746
+ if (!transformed) return null;
537
747
  const meta = _kubb_core.ast.syncSchemaRef(node);
538
- if (keysToOmit?.length && meta.primitive === "object" && !(meta.type === "union" && meta.discriminatorPropertyName)) {
539
- const lazyMatch = base.match(/^z\.lazy\(\(\)\s*=>\s*(.+)\)$/);
540
- if (lazyMatch) base = `z.lazy(() => ${lazyMatch[1]}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} }))`;
541
- else base = `${base}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} })`;
542
- }
543
748
  return applyModifiers({
544
- value: base,
749
+ value: (() => {
750
+ if (!keysToOmit?.length || meta.primitive !== "object" || meta.type === "union" && meta.discriminatorPropertyName) return transformed;
751
+ const lazyMatch = transformed.match(/^z\.lazy\(\(\)\s*=>\s*(.+)\)$/);
752
+ if (lazyMatch) return `z.lazy(() => ${lazyMatch[1]}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} }))`;
753
+ return `${transformed}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} })`;
754
+ })(),
545
755
  nullable: meta.nullable,
546
756
  optional: meta.optional,
547
757
  nullish: meta.nullish,
@@ -553,6 +763,15 @@ const printerZod = _kubb_core.ast.definePrinter((options) => {
553
763
  });
554
764
  //#endregion
555
765
  //#region src/printers/printerZodMini.ts
766
+ function strictOneOfMember(member, node) {
767
+ if (node.type === "object" && (node.additionalProperties === void 0 || node.additionalProperties === false)) return member.replace(/^z\.object\(/, "z.strictObject(");
768
+ return member;
769
+ }
770
+ function getMemberConstraintMini(member) {
771
+ if (member.primitive === "string") return lengthChecksMini(_kubb_core.ast.narrowSchema(member, "string") ?? {}) || void 0;
772
+ if (member.primitive === "number" || member.primitive === "integer") return numberChecksMini(_kubb_core.ast.narrowSchema(member, "number") ?? _kubb_core.ast.narrowSchema(member, "integer") ?? {}) || void 0;
773
+ if (member.primitive === "array") return lengthChecksMini(_kubb_core.ast.narrowSchema(member, "array") ?? {}) || void 0;
774
+ }
556
775
  /**
557
776
  * Zod v4 **Mini** printer built with `definePrinter`.
558
777
  *
@@ -621,23 +840,24 @@ const printerZodMini = _kubb_core.ast.definePrinter((options) => {
621
840
  return `z.enum([${nonNullValues.map(formatLiteral).join(", ")}])`;
622
841
  },
623
842
  ref(node) {
624
- if (!node.name) return void 0;
625
- const refName = node.ref ? _kubb_core.ast.extractRefName(node.ref) ?? node.name : node.name;
843
+ if (!node.name) return null;
844
+ const refName = node.ref ? (0, _kubb_ast_utils.extractRefName)(node.ref) ?? node.name : node.name;
626
845
  const resolvedName = node.ref ? this.options.resolver?.default(refName, "function") ?? refName : node.name;
627
846
  if (node.ref && this.options.cyclicSchemas?.has(refName)) return `z.lazy(() => ${resolvedName})`;
628
847
  return resolvedName;
629
848
  },
630
849
  object(node) {
631
- return `z.object({\n ${node.properties.map((prop) => {
850
+ return `z.object(${(0, _kubb_ast_utils.buildObject)(node.properties.map((prop) => {
632
851
  const { name: propName, schema } = prop;
633
852
  const meta = _kubb_core.ast.syncSchemaRef(schema);
634
853
  const isNullable = meta.nullable;
635
854
  const isOptional = schema.optional;
636
855
  const isNullish = schema.nullish;
637
856
  const hasSelfRef = this.options.cyclicSchemas != null && _kubb_core.ast.containsCircularRef(schema, { circularSchemas: this.options.cyclicSchemas });
857
+ const savedCyclicSchemas = this.options.cyclicSchemas;
638
858
  if (hasSelfRef) this.options.cyclicSchemas = void 0;
639
859
  const baseOutput = this.transform(schema) ?? this.transform(_kubb_core.ast.createSchema({ type: "unknown" }));
640
- if (hasSelfRef) this.options.cyclicSchemas = options.cyclicSchemas;
860
+ if (hasSelfRef) this.options.cyclicSchemas = savedCyclicSchemas;
641
861
  const value = applyMiniModifiers({
642
862
  value: this.options.wrapOutput ? this.options.wrapOutput({
643
863
  output: baseOutput,
@@ -648,72 +868,56 @@ const printerZodMini = _kubb_core.ast.definePrinter((options) => {
648
868
  nullish: isNullish,
649
869
  defaultValue: meta.default
650
870
  });
651
- if (hasSelfRef) return `get "${propName}"() { return ${value} }`;
652
- return `"${propName}": ${value}`;
653
- }).join(",\n ")}\n })`;
871
+ if (hasSelfRef) return `get ${(0, _kubb_ast_utils.objectKey)(propName)}() { return ${value} }`;
872
+ return `${(0, _kubb_ast_utils.objectKey)(propName)}: ${value}`;
873
+ }))})`;
654
874
  },
655
875
  array(node) {
656
- let result = `z.array(${(node.items ?? []).map((item) => this.transform(item)).filter(Boolean).join(", ") || this.transform(_kubb_core.ast.createSchema({ type: "unknown" }))})${lengthChecksMini(node)}`;
657
- if (node.unique) result += `.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })`;
658
- return result;
876
+ const base = `z.array(${(node.items ?? []).map((item) => this.transform(item)).filter(Boolean).join(", ") || this.transform(_kubb_core.ast.createSchema({ type: "unknown" }))})${lengthChecksMini(node)}`;
877
+ return node.unique ? `${base}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })` : base;
659
878
  },
660
879
  tuple(node) {
661
- return `z.tuple([${(node.items ?? []).map((item) => this.transform(item)).filter(Boolean).join(", ")}])`;
880
+ return `z.tuple(${(0, _kubb_ast_utils.buildList)((node.items ?? []).map((item) => this.transform(item)).filter(Boolean))})`;
662
881
  },
663
882
  union(node) {
664
883
  const nodeMembers = node.members ?? [];
665
- const members = nodeMembers.map((m) => this.transform(m)).filter(Boolean);
884
+ const members = nodeMembers.map((memberNode) => {
885
+ const member = this.transform(memberNode);
886
+ return member && node.strategy === "one" ? strictOneOfMember(member, memberNode) : member;
887
+ }).filter(Boolean);
666
888
  if (members.length === 0) return "";
667
889
  if (members.length === 1) return members[0];
668
- if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === "intersection")) return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, [${members.join(", ")}])`;
669
- return `z.union([${members.join(", ")}])`;
890
+ if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === "intersection")) return `z.discriminatedUnion(${(0, _kubb_ast_utils.stringify)(node.discriminatorPropertyName)}, ${(0, _kubb_ast_utils.buildList)(members)})`;
891
+ return `z.union(${(0, _kubb_ast_utils.buildList)(members)})`;
670
892
  },
671
893
  intersection(node) {
672
894
  const members = node.members ?? [];
673
895
  if (members.length === 0) return "";
674
896
  const [first, ...rest] = members;
675
897
  if (!first) return "";
676
- let base = this.transform(first);
677
- if (!base) return "";
678
- for (const member of rest) {
679
- if (member.primitive === "string") {
680
- const c = lengthChecksMini(_kubb_core.ast.narrowSchema(member, "string") ?? {});
681
- if (c) {
682
- base += c;
683
- continue;
684
- }
685
- } else if (member.primitive === "number" || member.primitive === "integer") {
686
- const c = numberChecksMini(_kubb_core.ast.narrowSchema(member, "number") ?? _kubb_core.ast.narrowSchema(member, "integer") ?? {});
687
- if (c) {
688
- base += c;
689
- continue;
690
- }
691
- } else if (member.primitive === "array") {
692
- const c = lengthChecksMini(_kubb_core.ast.narrowSchema(member, "array") ?? {});
693
- if (c) {
694
- base += c;
695
- continue;
696
- }
697
- }
898
+ const firstBase = this.transform(first);
899
+ if (!firstBase) return "";
900
+ return rest.reduce((acc, member) => {
901
+ const constraint = getMemberConstraintMini(member);
902
+ if (constraint) return acc + constraint;
698
903
  const transformed = this.transform(member);
699
- if (transformed) base = `z.intersection(${base}, ${transformed})`;
700
- }
701
- return base;
904
+ return transformed ? `z.intersection(${acc}, ${transformed})` : acc;
905
+ }, firstBase);
702
906
  },
703
907
  ...options.nodes
704
908
  },
705
909
  print(node) {
706
910
  const { keysToOmit } = this.options;
707
- let base = this.transform(node);
708
- if (!base) return null;
911
+ const transformed = this.transform(node);
912
+ if (!transformed) return null;
709
913
  const meta = _kubb_core.ast.syncSchemaRef(node);
710
- if (keysToOmit?.length && meta.primitive === "object" && !(meta.type === "union" && meta.discriminatorPropertyName)) {
711
- const lazyMatch = base.match(/^z\.lazy\(\(\)\s*=>\s*(.+)\)$/);
712
- if (lazyMatch) base = `z.lazy(() => ${lazyMatch[1]}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} }))`;
713
- else base = `${base}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} })`;
714
- }
715
914
  return applyMiniModifiers({
716
- value: base,
915
+ value: (() => {
916
+ if (!keysToOmit?.length || meta.primitive !== "object" || meta.type === "union" && meta.discriminatorPropertyName) return transformed;
917
+ const lazyMatch = transformed.match(/^z\.lazy\(\(\)\s*=>\s*(.+)\)$/);
918
+ if (lazyMatch) return `z.lazy(() => ${lazyMatch[1]}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} }))`;
919
+ return `${transformed}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} })`;
920
+ })(),
717
921
  nullable: meta.nullable,
718
922
  optional: meta.optional,
719
923
  nullish: meta.nullish,
@@ -724,6 +928,62 @@ const printerZodMini = _kubb_core.ast.definePrinter((options) => {
724
928
  });
725
929
  //#endregion
726
930
  //#region src/generators/zodGenerator.tsx
931
+ const zodPrinterCache = /* @__PURE__ */ new WeakMap();
932
+ const zodMiniPrinterCache = /* @__PURE__ */ new WeakMap();
933
+ /**
934
+ * Returns the cached `output`/`input` direction printers for a resolver, building them on
935
+ * first use. The `input` printer encodes `Date → string` for request bodies; `output` decodes
936
+ * `string → Date` for responses. Schemas without `dateType: 'date'` fields print identically.
937
+ */
938
+ function getStdPrinters(resolver, params) {
939
+ const cached = zodPrinterCache.get(resolver);
940
+ if (cached && cached.coercion === params.coercion && cached.guidType === params.guidType && cached.dateType === params.dateType) return {
941
+ output: cached.output,
942
+ input: cached.input
943
+ };
944
+ const base = {
945
+ ...params,
946
+ resolver
947
+ };
948
+ const output = printerZod({
949
+ ...base,
950
+ direction: "output"
951
+ });
952
+ const input = printerZod({
953
+ ...base,
954
+ direction: "input"
955
+ });
956
+ zodPrinterCache.set(resolver, {
957
+ output,
958
+ input,
959
+ coercion: params.coercion,
960
+ guidType: params.guidType,
961
+ dateType: params.dateType
962
+ });
963
+ return {
964
+ output,
965
+ input
966
+ };
967
+ }
968
+ function getMiniPrinter(resolver, params) {
969
+ const cached = zodMiniPrinterCache.get(resolver);
970
+ if (cached && cached.guidType === params.guidType) return cached.printer;
971
+ const p = printerZodMini({
972
+ ...params,
973
+ resolver
974
+ });
975
+ zodMiniPrinterCache.set(resolver, {
976
+ printer: p,
977
+ guidType: params.guidType
978
+ });
979
+ return p;
980
+ }
981
+ /**
982
+ * Built-in generator for `@kubb/plugin-zod`. Emits one Zod schema per
983
+ * schema in the spec plus per-operation request/response/parameter schemas.
984
+ * When `mini: true`, schemas use the Zod Mini functional API instead of
985
+ * chainable methods.
986
+ */
727
987
  const zodGenerator = (0, _kubb_core.defineGenerator)({
728
988
  name: "zod",
729
989
  renderer: _kubb_renderer_jsx.jsxRenderer,
@@ -732,9 +992,11 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
732
992
  const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, printer } = ctx.options;
733
993
  const dateType = adapter.options.dateType;
734
994
  if (!node.name) return;
735
- const mode = ctx.getMode(output);
736
995
  const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath);
737
- const imports = adapter.getImports(node, (schemaName) => ({
996
+ const cyclicSchemas = new Set(ctx.meta.circularNames);
997
+ const hasCodec = !mini && containsCodec(node);
998
+ const codecRefNames = new Set(hasCodec ? _kubb_core.ast.collect(node, { schema: (n) => n.type === "ref" && n.ref && containsCodec(n) ? (0, _kubb_ast_utils.extractRefName)(n.ref) ?? void 0 : void 0 }) : []);
999
+ const importEntries = adapter.getImports(node, (schemaName) => ({
738
1000
  name: resolver.resolveSchemaName(schemaName),
739
1001
  path: resolver.resolveFile({
740
1002
  name: schemaName,
@@ -742,9 +1004,27 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
742
1004
  }, {
743
1005
  root,
744
1006
  output,
745
- group
1007
+ group: group ?? void 0
746
1008
  }).path
747
1009
  }));
1010
+ const inputImportEntries = hasCodec ? [...codecRefNames].map((schemaName) => ({
1011
+ name: [resolver.resolveInputSchemaName(schemaName)],
1012
+ path: resolver.resolveFile({
1013
+ name: schemaName,
1014
+ extname: ".ts"
1015
+ }, {
1016
+ root,
1017
+ output,
1018
+ group: group ?? void 0
1019
+ }).path
1020
+ })) : [];
1021
+ const seenImports = /* @__PURE__ */ new Set();
1022
+ const imports = [...importEntries, ...inputImportEntries].filter((imp) => {
1023
+ const key = `${Array.isArray(imp.name) ? imp.name.join(",") : imp.name}|${imp.path}`;
1024
+ if (seenImports.has(key)) return false;
1025
+ seenImports.add(key);
1026
+ return true;
1027
+ });
748
1028
  const meta = {
749
1029
  name: resolver.resolveSchemaName(node.name),
750
1030
  file: resolver.resolveFile({
@@ -753,37 +1033,43 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
753
1033
  }, {
754
1034
  root,
755
1035
  output,
756
- group
1036
+ group: group ?? void 0
757
1037
  })
758
1038
  };
759
- const inferTypeName = inferred ? resolver.resolveSchemaTypeName(node.name) : void 0;
760
- const cyclicSchemas = adapter.inputNode ? _kubb_core.ast.findCircularSchemas(adapter.inputNode.schemas) : void 0;
761
- const schemaPrinter = mini ? printerZodMini({
1039
+ const inferTypeName = inferred ? resolver.resolveSchemaTypeName(node.name) : null;
1040
+ const stdPrinters = mini ? null : getStdPrinters(resolver, {
1041
+ coercion,
762
1042
  guidType,
1043
+ dateType,
763
1044
  wrapOutput,
764
- resolver,
765
1045
  cyclicSchemas,
766
1046
  nodes: printer?.nodes
767
- }) : printerZod({
768
- coercion,
1047
+ });
1048
+ const schemaPrinter = mini ? getMiniPrinter(resolver, {
769
1049
  guidType,
770
- dateType,
771
1050
  wrapOutput,
772
- resolver,
773
1051
  cyclicSchemas,
774
1052
  nodes: printer?.nodes
775
- });
1053
+ }) : stdPrinters.output;
776
1054
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File, {
777
1055
  baseName: meta.file.baseName,
778
1056
  path: meta.file.path,
779
1057
  meta: meta.file.meta,
780
- banner: resolver.resolveBanner(adapter.inputNode, {
1058
+ banner: resolver.resolveBanner(ctx.meta, {
781
1059
  output,
782
- config
1060
+ config,
1061
+ file: {
1062
+ path: meta.file.path,
1063
+ baseName: meta.file.baseName
1064
+ }
783
1065
  }),
784
- footer: resolver.resolveFooter(adapter.inputNode, {
1066
+ footer: resolver.resolveFooter(ctx.meta, {
785
1067
  output,
786
- config
1068
+ config,
1069
+ file: {
1070
+ path: meta.file.path,
1071
+ baseName: meta.file.baseName
1072
+ }
787
1073
  }),
788
1074
  children: [
789
1075
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
@@ -791,25 +1077,35 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
791
1077
  path: importPath,
792
1078
  isNameSpace: isZodImport
793
1079
  }),
794
- mode === "split" && imports.map((imp) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1080
+ imports.map((imp) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
795
1081
  root: meta.file.path,
796
1082
  path: imp.path,
797
1083
  name: imp.name
798
- }, [node.name, imp.path].join("-"))),
1084
+ }, [
1085
+ node.name,
1086
+ imp.path,
1087
+ imp.name
1088
+ ].join("-"))),
799
1089
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Zod, {
800
1090
  name: meta.name,
801
1091
  node,
802
1092
  printer: schemaPrinter,
803
1093
  inferTypeName
1094
+ }),
1095
+ hasCodec && stdPrinters && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Zod, {
1096
+ name: resolver.resolveInputSchemaName(node.name),
1097
+ node,
1098
+ printer: stdPrinters.input,
1099
+ inferTypeName: inferred ? resolver.resolveInputSchemaTypeName(node.name) : null
804
1100
  })
805
1101
  ]
806
1102
  });
807
1103
  },
808
1104
  operation(node, ctx) {
1105
+ if (!_kubb_core.ast.isHttpOperationNode(node)) return null;
809
1106
  const { adapter, config, resolver, root } = ctx;
810
1107
  const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, paramsCasing, printer } = ctx.options;
811
1108
  const dateType = adapter.options.dateType;
812
- const mode = ctx.getMode(output);
813
1109
  const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath);
814
1110
  const params = _kubb_core.ast.caseParams(node.parameters, paramsCasing);
815
1111
  const meta = { file: resolver.resolveFile({
@@ -820,31 +1116,37 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
820
1116
  }, {
821
1117
  root,
822
1118
  output,
823
- group
1119
+ group: group ?? void 0
824
1120
  }) };
825
- const cyclicSchemas = adapter.inputNode ? _kubb_core.ast.findCircularSchemas(adapter.inputNode.schemas) : void 0;
826
- function renderSchemaEntry({ schema, name, keysToOmit }) {
1121
+ const cyclicSchemas = new Set(ctx.meta.circularNames);
1122
+ function renderSchemaEntry({ schema, name, keysToOmit, direction = "output" }) {
827
1123
  if (!schema) return null;
828
- const inferTypeName = inferred ? resolver.resolveTypeName(name) : void 0;
1124
+ const inferTypeName = inferred ? resolver.resolveTypeName(name) : null;
1125
+ const codecRefNames = direction === "input" && !mini ? new Set(_kubb_core.ast.collect(schema, { schema: (n) => n.type === "ref" && n.ref && containsCodec(n) ? (0, _kubb_ast_utils.extractRefName)(n.ref) ?? void 0 : void 0 })) : null;
829
1126
  const imports = adapter.getImports(schema, (schemaName) => ({
830
- name: resolver.resolveSchemaName(schemaName),
1127
+ name: codecRefNames?.has(schemaName) ? resolver.resolveInputSchemaName(schemaName) : resolver.resolveSchemaName(schemaName),
831
1128
  path: resolver.resolveFile({
832
1129
  name: schemaName,
833
1130
  extname: ".ts"
834
1131
  }, {
835
1132
  root,
836
1133
  output,
837
- group
1134
+ group: group ?? void 0
838
1135
  }).path
839
1136
  }));
840
- const schemaPrinter = mini ? printerZodMini({
1137
+ const schemaPrinter = mini ? keysToOmit?.length ? printerZodMini({
841
1138
  guidType,
842
1139
  wrapOutput,
843
1140
  resolver,
844
1141
  keysToOmit,
845
1142
  cyclicSchemas,
846
1143
  nodes: printer?.nodes
847
- }) : printerZod({
1144
+ }) : getMiniPrinter(resolver, {
1145
+ guidType,
1146
+ wrapOutput,
1147
+ cyclicSchemas,
1148
+ nodes: printer?.nodes
1149
+ }) : keysToOmit?.length ? printerZod({
848
1150
  coercion,
849
1151
  guidType,
850
1152
  dateType,
@@ -852,9 +1154,17 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
852
1154
  resolver,
853
1155
  keysToOmit,
854
1156
  cyclicSchemas,
1157
+ nodes: printer?.nodes,
1158
+ direction
1159
+ }) : getStdPrinters(resolver, {
1160
+ coercion,
1161
+ guidType,
1162
+ dateType,
1163
+ wrapOutput,
1164
+ cyclicSchemas,
855
1165
  nodes: printer?.nodes
856
- });
857
- return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [mode === "split" && imports.map((imp) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1166
+ })[direction];
1167
+ return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [imports.map((imp) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
858
1168
  root: meta.file.path,
859
1169
  path: imp.path,
860
1170
  name: imp.name
@@ -869,22 +1179,48 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
869
1179
  inferTypeName
870
1180
  })] });
871
1181
  }
1182
+ function buildContentTypeVariants(entries, baseName, decorate, direction) {
1183
+ const variants = resolveContentTypeVariants(entries, baseName);
1184
+ const unionSchema = _kubb_core.ast.createSchema({
1185
+ type: "union",
1186
+ members: variants.map((variant) => _kubb_core.ast.createSchema({
1187
+ type: "ref",
1188
+ name: variant.name
1189
+ }))
1190
+ });
1191
+ return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [variants.map((variant) => renderSchemaEntry({
1192
+ schema: decorate ? decorate(variant.schema) : variant.schema,
1193
+ name: variant.name,
1194
+ keysToOmit: variant.keysToOmit,
1195
+ direction
1196
+ })), renderSchemaEntry({
1197
+ schema: unionSchema,
1198
+ name: baseName,
1199
+ direction
1200
+ })] });
1201
+ }
872
1202
  const paramSchemas = params.map((param) => renderSchemaEntry({
873
1203
  schema: param.schema,
874
- name: resolver.resolveParamName(node, param)
1204
+ name: resolver.resolveParamName(node, param),
1205
+ direction: "input"
875
1206
  }));
876
- const responseSchemas = node.responses.map((res) => renderSchemaEntry({
877
- schema: res.schema,
878
- name: resolver.resolveResponseStatusName(node, res.statusCode),
879
- keysToOmit: res.keysToOmit
880
- }));
881
- const responsesWithSchema = node.responses.filter((res) => res.schema);
1207
+ const responseSchemas = node.responses.map((res) => {
1208
+ const variants = (res.content ?? []).filter((entry) => entry.schema);
1209
+ if (variants.length > 1) return buildContentTypeVariants(res.content, resolver.resolveResponseStatusName(node, res.statusCode));
1210
+ const primary = variants[0] ?? res.content?.[0];
1211
+ return renderSchemaEntry({
1212
+ schema: primary?.schema ?? null,
1213
+ name: resolver.resolveResponseStatusName(node, res.statusCode),
1214
+ keysToOmit: primary?.keysToOmit
1215
+ });
1216
+ });
1217
+ const responsesWithSchema = node.responses.filter((res) => res.content?.some((entry) => entry.schema));
882
1218
  const responseUnionSchema = responsesWithSchema.length > 0 ? (() => {
883
1219
  const responseUnionName = resolver.resolveResponseName(node);
884
- if (new Set(responsesWithSchema.flatMap((res) => res.schema ? adapter.getImports(res.schema, (schemaName) => ({
1220
+ if (new Set(responsesWithSchema.flatMap((res) => (res.content ?? []).flatMap((entry) => entry.schema ? adapter.getImports(entry.schema, (schemaName) => ({
885
1221
  name: resolver.resolveSchemaName(schemaName),
886
1222
  path: ""
887
- })).flatMap((imp) => Array.isArray(imp.name) ? imp.name : [imp.name]) : [])).has(responseUnionName)) return null;
1223
+ })).flatMap((imp) => Array.isArray(imp.name) ? imp.name : [imp.name]) : []))).has(responseUnionName)) return null;
888
1224
  const members = responsesWithSchema.map((res) => _kubb_core.ast.createSchema({
889
1225
  type: "ref",
890
1226
  name: resolver.resolveResponseStatusName(node, res.statusCode)
@@ -897,25 +1233,46 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
897
1233
  name: responseUnionName
898
1234
  });
899
1235
  })() : null;
900
- const requestSchema = node.requestBody?.content?.[0]?.schema ? renderSchemaEntry({
901
- schema: {
902
- ...node.requestBody.content[0].schema,
903
- description: node.requestBody.description ?? node.requestBody.content[0].schema.description
904
- },
905
- name: resolver.resolveDataName(node),
906
- keysToOmit: node.requestBody.content[0].keysToOmit
907
- }) : null;
1236
+ const requestBodyContent = node.requestBody?.content ?? [];
1237
+ const requestSchema = (() => {
1238
+ if (requestBodyContent.length === 0) return null;
1239
+ if (requestBodyContent.length === 1) {
1240
+ const entry = requestBodyContent[0];
1241
+ if (!entry.schema) return null;
1242
+ return renderSchemaEntry({
1243
+ schema: {
1244
+ ...entry.schema,
1245
+ description: node.requestBody.description ?? entry.schema.description
1246
+ },
1247
+ name: resolver.resolveDataName(node),
1248
+ keysToOmit: entry.keysToOmit,
1249
+ direction: "input"
1250
+ });
1251
+ }
1252
+ return buildContentTypeVariants(requestBodyContent, resolver.resolveDataName(node), (schema) => ({
1253
+ ...schema,
1254
+ description: node.requestBody.description ?? schema.description
1255
+ }), "input");
1256
+ })();
908
1257
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File, {
909
1258
  baseName: meta.file.baseName,
910
1259
  path: meta.file.path,
911
1260
  meta: meta.file.meta,
912
- banner: resolver.resolveBanner(adapter.inputNode, {
1261
+ banner: resolver.resolveBanner(ctx.meta, {
913
1262
  output,
914
- config
1263
+ config,
1264
+ file: {
1265
+ path: meta.file.path,
1266
+ baseName: meta.file.baseName
1267
+ }
915
1268
  }),
916
- footer: resolver.resolveFooter(adapter.inputNode, {
1269
+ footer: resolver.resolveFooter(ctx.meta, {
917
1270
  output,
918
- config
1271
+ config,
1272
+ file: {
1273
+ path: meta.file.path,
1274
+ baseName: meta.file.baseName
1275
+ }
919
1276
  }),
920
1277
  children: [
921
1278
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
@@ -931,7 +1288,7 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
931
1288
  });
932
1289
  },
933
1290
  operations(nodes, ctx) {
934
- const { adapter, config, resolver, root } = ctx;
1291
+ const { config, resolver, root } = ctx;
935
1292
  const { output, importPath, group, operations, paramsCasing } = ctx.options;
936
1293
  if (!operations) return;
937
1294
  const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath);
@@ -941,9 +1298,9 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
941
1298
  }, {
942
1299
  root,
943
1300
  output,
944
- group
1301
+ group: group ?? void 0
945
1302
  }) };
946
- const transformedOperations = nodes.map((node) => {
1303
+ const transformedOperations = nodes.filter(_kubb_core.ast.isHttpOperationNode).map((node) => {
947
1304
  return {
948
1305
  node,
949
1306
  data: buildSchemaNames(node, {
@@ -966,7 +1323,7 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
966
1323
  }, {
967
1324
  root,
968
1325
  output,
969
- group
1326
+ group: group ?? void 0
970
1327
  });
971
1328
  return names.map((name) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
972
1329
  name: [name],
@@ -978,13 +1335,21 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
978
1335
  baseName: meta.file.baseName,
979
1336
  path: meta.file.path,
980
1337
  meta: meta.file.meta,
981
- banner: resolver.resolveBanner(adapter.inputNode, {
1338
+ banner: resolver.resolveBanner(ctx.meta, {
982
1339
  output,
983
- config
1340
+ config,
1341
+ file: {
1342
+ path: meta.file.path,
1343
+ baseName: meta.file.baseName
1344
+ }
984
1345
  }),
985
- footer: resolver.resolveFooter(adapter.inputNode, {
1346
+ footer: resolver.resolveFooter(ctx.meta, {
986
1347
  output,
987
- config
1348
+ config,
1349
+ file: {
1350
+ path: meta.file.path,
1351
+ baseName: meta.file.baseName
1352
+ }
988
1353
  }),
989
1354
  children: [
990
1355
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
@@ -1005,92 +1370,110 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
1005
1370
  //#endregion
1006
1371
  //#region src/resolvers/resolverZod.ts
1007
1372
  /**
1008
- * Naming convention resolver for Zod plugin.
1373
+ * Default resolver used by `@kubb/plugin-zod`. Decides the names and file
1374
+ * paths for every generated Zod schema. Schemas use camelCase with a
1375
+ * `Schema` suffix (`listPetsSchema`); their inferred types use PascalCase
1376
+ * with a `SchemaType` suffix (`PetSchemaType`), so the value and the type
1377
+ * never share an identifier even when the schema name is all-uppercase.
1009
1378
  *
1010
- * Provides default naming helpers using camelCase with a `Schema` suffix for schemas.
1379
+ * @example Resolve schema and type names
1380
+ * ```ts
1381
+ * import { resolverZod } from '@kubb/plugin-zod'
1011
1382
  *
1012
- * @example
1013
- * `resolverZod.default('list pets', 'function') // 'listPetsSchema'`
1383
+ * resolverZod.default('list pets', 'function') // 'listPetsSchema'
1384
+ * resolverZod.resolveSchemaTypeName('pet') // 'PetSchemaType'
1385
+ * ```
1014
1386
  */
1015
- const resolverZod = (0, _kubb_core.defineResolver)((ctx) => {
1387
+ const resolverZod = (0, _kubb_core.defineResolver)(() => {
1016
1388
  return {
1017
1389
  name: "default",
1018
1390
  pluginName: "plugin-zod",
1019
1391
  default(name, type) {
1020
- return camelCase(name, {
1021
- isFile: type === "file",
1022
- suffix: type ? "schema" : void 0
1023
- });
1392
+ if (type === "file") return toFilePath(name, (part) => camelCase(part, { suffix: "schema" }));
1393
+ return ensureValidVarName(camelCase(name, { suffix: type ? "schema" : void 0 }));
1024
1394
  },
1025
1395
  resolveSchemaName(name) {
1026
- return camelCase(name, { suffix: "schema" });
1396
+ return ensureValidVarName(camelCase(name, { suffix: "schema" }));
1027
1397
  },
1028
1398
  resolveSchemaTypeName(name) {
1029
- return pascalCase(name, { suffix: "schema" });
1399
+ return ensureValidVarName(pascalCase(name, { suffix: "schema type" }));
1400
+ },
1401
+ resolveInputSchemaName(name) {
1402
+ return this.resolveSchemaName(`${name} input`);
1403
+ },
1404
+ resolveInputSchemaTypeName(name) {
1405
+ return this.resolveSchemaTypeName(`${name} input`);
1030
1406
  },
1031
1407
  resolveTypeName(name) {
1032
- return pascalCase(name);
1408
+ return ensureValidVarName(pascalCase(name, { suffix: "type" }));
1033
1409
  },
1034
1410
  resolvePathName(name, type) {
1035
- return ctx.default(name, type);
1411
+ return this.default(name, type);
1036
1412
  },
1037
1413
  resolveParamName(node, param) {
1038
- return ctx.resolveSchemaName(`${node.operationId} ${param.in} ${param.name}`);
1414
+ return this.resolveSchemaName(`${node.operationId} ${param.in} ${param.name}`);
1039
1415
  },
1040
1416
  resolveResponseStatusName(node, statusCode) {
1041
- return ctx.resolveSchemaName(`${node.operationId} Status ${statusCode}`);
1417
+ return this.resolveSchemaName(`${node.operationId} Status ${statusCode}`);
1042
1418
  },
1043
1419
  resolveDataName(node) {
1044
- return ctx.resolveSchemaName(`${node.operationId} Data`);
1420
+ return this.resolveSchemaName(`${node.operationId} Data`);
1045
1421
  },
1046
1422
  resolveResponsesName(node) {
1047
- return ctx.resolveSchemaName(`${node.operationId} Responses`);
1423
+ return this.resolveSchemaName(`${node.operationId} Responses`);
1048
1424
  },
1049
1425
  resolveResponseName(node) {
1050
- return ctx.resolveSchemaName(`${node.operationId} Response`);
1426
+ return this.resolveSchemaName(`${node.operationId} Response`);
1051
1427
  },
1052
1428
  resolvePathParamsName(node, param) {
1053
- return ctx.resolveParamName(node, param);
1429
+ return this.resolveParamName(node, param);
1054
1430
  },
1055
1431
  resolveQueryParamsName(node, param) {
1056
- return ctx.resolveParamName(node, param);
1432
+ return this.resolveParamName(node, param);
1057
1433
  },
1058
1434
  resolveHeaderParamsName(node, param) {
1059
- return ctx.resolveParamName(node, param);
1435
+ return this.resolveParamName(node, param);
1060
1436
  }
1061
1437
  };
1062
1438
  });
1063
1439
  //#endregion
1064
1440
  //#region src/plugin.ts
1065
1441
  /**
1066
- * Canonical plugin name for `@kubb/plugin-zod`, used in driver lookups and warnings.
1442
+ * Canonical plugin name for `@kubb/plugin-zod`. Used for driver lookups and
1443
+ * cross-plugin dependency references.
1067
1444
  */
1068
1445
  const pluginZodName = "plugin-zod";
1069
1446
  /**
1070
- * Generates Zod validation schemas from an OpenAPI specification.
1071
- * Walks schemas and operations, delegates to generators, and writes barrel files
1072
- * based on the configured `barrelType`.
1447
+ * Generates Zod v4 schemas from an OpenAPI spec. Use them to validate API
1448
+ * responses at runtime, build form schemas, or feed back into router libraries
1449
+ * that consume Zod (tRPC, Hono, Elysia). Pair with `@kubb/plugin-client` and
1450
+ * set the client's `parser: 'zod'` to validate every response automatically.
1073
1451
  *
1074
- * @example Zod schema generator
1452
+ * @example
1075
1453
  * ```ts
1076
- * import pluginZod from '@kubb/plugin-zod'
1454
+ * import { defineConfig } from 'kubb'
1455
+ * import { pluginTs } from '@kubb/plugin-ts'
1456
+ * import { pluginZod } from '@kubb/plugin-zod'
1457
+ *
1077
1458
  * export default defineConfig({
1078
- * plugins: [pluginZod({ output: { path: 'zod' } })]
1459
+ * input: { path: './petStore.yaml' },
1460
+ * output: { path: './src/gen' },
1461
+ * plugins: [
1462
+ * pluginTs(),
1463
+ * pluginZod({
1464
+ * output: { path: './zod' },
1465
+ * typed: true,
1466
+ * }),
1467
+ * ],
1079
1468
  * })
1080
1469
  * ```
1081
1470
  */
1082
1471
  const pluginZod = (0, _kubb_core.definePlugin)((options) => {
1083
1472
  const { output = {
1084
1473
  path: "zod",
1085
- barrelType: "named"
1474
+ barrel: { type: "named" }
1086
1475
  }, group, exclude = [], include, override = [], typed = false, operations = false, mini = false, guidType = "uuid", importPath = mini ? "zod/mini" : "zod", coercion = false, inferred = false, wrapOutput = void 0, paramsCasing, printer, resolver: userResolver, transformer: userTransformer, generators: userGenerators = [] } = options;
1087
- const groupConfig = group ? {
1088
- ...group,
1089
- name: (ctx) => {
1090
- if (group.type === "path") return `${ctx.group.split("/")[1]}`;
1091
- return `${camelCase(ctx.group)}Controller`;
1092
- }
1093
- } : void 0;
1476
+ const groupConfig = createGroupConfig(group);
1094
1477
  return {
1095
1478
  name: pluginZodName,
1096
1479
  options,