@kubb/plugin-zod 5.0.0-alpha.9 → 5.0.0-beta.100

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/LICENSE +17 -10
  2. package/README.md +38 -23
  3. package/dist/index.cjs +1964 -133
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.ts +482 -6
  6. package/dist/index.js +1950 -132
  7. package/dist/index.js.map +1 -1
  8. package/package.json +36 -81
  9. package/dist/components-B7zUFnAm.cjs +0 -890
  10. package/dist/components-B7zUFnAm.cjs.map +0 -1
  11. package/dist/components-eECfXVou.js +0 -842
  12. package/dist/components-eECfXVou.js.map +0 -1
  13. package/dist/components.cjs +0 -4
  14. package/dist/components.d.ts +0 -56
  15. package/dist/components.js +0 -2
  16. package/dist/generators-BjPDdJUz.cjs +0 -301
  17. package/dist/generators-BjPDdJUz.cjs.map +0 -1
  18. package/dist/generators-lTWPS6oN.js +0 -290
  19. package/dist/generators-lTWPS6oN.js.map +0 -1
  20. package/dist/generators.cjs +0 -4
  21. package/dist/generators.d.ts +0 -508
  22. package/dist/generators.js +0 -2
  23. package/dist/templates/ToZod.source.cjs +0 -7
  24. package/dist/templates/ToZod.source.cjs.map +0 -1
  25. package/dist/templates/ToZod.source.d.ts +0 -7
  26. package/dist/templates/ToZod.source.js +0 -6
  27. package/dist/templates/ToZod.source.js.map +0 -1
  28. package/dist/types-CoCoOc2u.d.ts +0 -172
  29. package/src/components/Operations.tsx +0 -70
  30. package/src/components/Zod.tsx +0 -142
  31. package/src/components/index.ts +0 -2
  32. package/src/generators/index.ts +0 -2
  33. package/src/generators/operationsGenerator.tsx +0 -50
  34. package/src/generators/zodGenerator.tsx +0 -202
  35. package/src/index.ts +0 -2
  36. package/src/parser.ts +0 -909
  37. package/src/plugin.ts +0 -183
  38. package/src/templates/ToZod.source.ts +0 -4
  39. package/src/types.ts +0 -172
  40. package/templates/ToZod.ts +0 -61
  41. /package/dist/{chunk--u3MIqq1.js → rolldown-runtime-C0LytTxp.js} +0 -0
package/dist/index.cjs CHANGED
@@ -1,12 +1,179 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_components = require("./components-B7zUFnAm.cjs");
3
- const require_generators = require("./generators-BjPDdJUz.cjs");
4
- const require_templates_ToZod_source = require("./templates/ToZod.source.cjs");
5
- let node_path = require("node:path");
6
- node_path = require_components.__toESM(node_path);
7
- let _kubb_core = require("@kubb/core");
8
- let _kubb_plugin_oas = require("@kubb/plugin-oas");
9
- let _kubb_plugin_ts = require("@kubb/plugin-ts");
1
+ Object.defineProperties(exports, {
2
+ __esModule: { value: true },
3
+ [Symbol.toStringTag]: { value: "Module" }
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
+ });
11
+ //#endregion
12
+ let kubb_kit = require("kubb/kit");
13
+ let kubb_jsx = require("kubb/jsx");
14
+ let kubb_jsx_jsx_runtime = require("kubb/jsx/jsx-runtime");
15
+ //#region ../../internals/shared/src/params.ts
16
+ /**
17
+ * Drops parameters that share the same name, keeping the first.
18
+ *
19
+ * A malformed spec can declare the same parameter name twice within one `in` location. Both would
20
+ * resolve to the same output property, so emitting both would yield an object type with a duplicate
21
+ * member, which TypeScript rejects. This is a defensive guard against that case, not a casing guard:
22
+ * parameter names flow through unchanged, so no two distinct names ever collide here anymore.
23
+ */
24
+ function dedupeParams(params) {
25
+ const seen = /* @__PURE__ */ new Set();
26
+ return params.filter((param) => {
27
+ if (seen.has(param.name)) return false;
28
+ seen.add(param.name);
29
+ return true;
30
+ });
31
+ }
32
+ //#endregion
33
+ //#region ../../internals/shared/src/operation.ts
34
+ /**
35
+ * Maps a content type to the PascalCase suffix used to name per-content-type variants
36
+ * (e.g. `application/json` → `Json`, `application/xml` → `Xml`, `multipart/form-data` → `FormData`).
37
+ */
38
+ function getContentTypeSuffix(contentType) {
39
+ const baseType = contentType.split(";")[0].trim();
40
+ if (baseType === "application/json") return "Json";
41
+ if (baseType === "multipart/form-data") return "FormData";
42
+ if (baseType === "application/x-www-form-urlencoded") return "FormUrlEncoded";
43
+ const parts = (baseType.split("/").pop() ?? baseType).split(/[^a-zA-Z0-9]+/).filter(Boolean);
44
+ if (parts.length === 0) return "Unknown";
45
+ return parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
46
+ }
47
+ /**
48
+ * Appends a content-type suffix to a base name, keeping a trailing `Data` segment last
49
+ * (e.g. `AddPetData` + `Json` → `AddPetJsonData`, `AddPetStatus200` + `Xml` → `AddPetStatus200Xml`).
50
+ */
51
+ function getPerContentTypeName(baseName, suffix) {
52
+ if (baseName.endsWith("Data")) return suffix.endsWith("Data") ? baseName.slice(0, -4) + suffix : `${baseName.slice(0, -4)}${suffix}Data`;
53
+ return baseName + suffix;
54
+ }
55
+ /**
56
+ * Resolves per-content-type variant names for a set of content entries, deduplicating suffix
57
+ * collisions with a numeric counter. Entries without a schema are skipped. The returned `suffix` is
58
+ * the final (possibly counter-augmented) value, so callers can derive parallel names in another
59
+ * namespace (e.g. plugin-faker deriving the matching plugin-ts type name).
60
+ */
61
+ function resolveContentTypeVariants(entries, baseName) {
62
+ const usedNames = /* @__PURE__ */ new Set();
63
+ return entries.filter((entry) => entry.schema).map((entry) => {
64
+ const baseSuffix = getContentTypeSuffix(entry.contentType);
65
+ let suffix = baseSuffix;
66
+ let name = getPerContentTypeName(baseName, suffix);
67
+ let counter = 2;
68
+ while (usedNames.has(name)) {
69
+ suffix = `${baseSuffix}${counter++}`;
70
+ name = getPerContentTypeName(baseName, suffix);
71
+ }
72
+ usedNames.add(name);
73
+ return {
74
+ name,
75
+ suffix,
76
+ schema: entry.schema,
77
+ keysToOmit: entry.keysToOmit,
78
+ contentType: entry.contentType
79
+ };
80
+ });
81
+ }
82
+ function getOperationParameters(node) {
83
+ return {
84
+ path: dedupeParams(node.parameters.filter((param) => param.in === "path")),
85
+ query: dedupeParams(node.parameters.filter((param) => param.in === "query")),
86
+ header: dedupeParams(node.parameters.filter((param) => param.in === "header")),
87
+ cookie: dedupeParams(node.parameters.filter((param) => param.in === "cookie"))
88
+ };
89
+ }
90
+ /**
91
+ * Builds the combined `{ body, path, query, headers }` options object schema for an operation,
92
+ * referencing the already-resolved body and grouped param names. Shared by `@kubb/plugin-ts`'s
93
+ * `Options` type and `@kubb/plugin-zod`'s inferred options schema, so both printers emit the same
94
+ * shape from the same inputs. `primitive: 'object'` is a no-op for the TS printer and tells the Zod
95
+ * printer to emit `z.object(…)` rather than a record.
96
+ */
97
+ function buildOptionsSchema(node, resolver) {
98
+ const { path, query, header } = getOperationParameters(node);
99
+ const hasBody = Boolean(node.requestBody?.content?.[0]?.schema);
100
+ const createNever = () => kubb_kit.ast.factory.createSchema({
101
+ type: "never",
102
+ primitive: void 0,
103
+ optional: true
104
+ });
105
+ const groups = [
106
+ {
107
+ name: "path",
108
+ params: path,
109
+ resolve: resolver.param.path
110
+ },
111
+ {
112
+ name: "query",
113
+ params: query,
114
+ resolve: resolver.param.query
115
+ },
116
+ {
117
+ name: "headers",
118
+ params: header,
119
+ resolve: resolver.param.headers
120
+ }
121
+ ];
122
+ return kubb_kit.ast.factory.createSchema({
123
+ type: "object",
124
+ primitive: "object",
125
+ deprecated: node.deprecated,
126
+ properties: [kubb_kit.ast.factory.createProperty({
127
+ name: "body",
128
+ required: hasBody,
129
+ schema: hasBody ? kubb_kit.ast.factory.createSchema({
130
+ type: "ref",
131
+ name: resolver.response.body(node)
132
+ }) : createNever()
133
+ }), ...groups.map(({ name, params, resolve }) => {
134
+ const required = params.some((param) => param.required);
135
+ return kubb_kit.ast.factory.createProperty({
136
+ name,
137
+ required,
138
+ schema: params.length > 0 ? kubb_kit.ast.factory.createSchema({
139
+ type: "ref",
140
+ name: resolve.call(resolver.param, node, params[0]),
141
+ optional: !required
142
+ }) : createNever()
143
+ });
144
+ })]
145
+ });
146
+ }
147
+ function getStatusCodeNumber(statusCode) {
148
+ const code = Number(statusCode);
149
+ return Number.isNaN(code) ? null : code;
150
+ }
151
+ function isSuccessStatusCode(statusCode) {
152
+ const code = getStatusCodeNumber(statusCode);
153
+ return code !== null && code >= 200 && code < 300;
154
+ }
155
+ function getSuccessResponses(responses) {
156
+ return responses.filter((response) => isSuccessStatusCode(response.statusCode));
157
+ }
158
+ //#endregion
159
+ //#region ../../internals/shared/src/adapter.ts
160
+ /**
161
+ * Narrows the generic `Adapter` from a generator context to the OpenAPI adapter,
162
+ * so OAS-only options (`dateType`, `enums`) and the parsed `document` are typed.
163
+ *
164
+ * Throws when a non-OAS adapter is configured, turning a silently wrong cast into a
165
+ * clear, actionable error at the point of use.
166
+ *
167
+ * @example
168
+ * ```ts
169
+ * const { dateType } = getOasAdapter(ctx.adapter).options
170
+ * ```
171
+ */
172
+ function getOasAdapter(adapter) {
173
+ if (adapter.name !== "oas") throw new Error(`Expected the OpenAPI adapter (adapterOas), but received "${adapter.name}". Configure \`adapter: adapterOas()\` in your Kubb config.`);
174
+ return adapter;
175
+ }
176
+ //#endregion
10
177
  //#region ../../internals/utils/src/casing.ts
11
178
  /**
12
179
  * Shared implementation for camelCase and PascalCase conversion.
@@ -18,161 +185,1825 @@ let _kubb_plugin_ts = require("@kubb/plugin-ts");
18
185
  function toCamelOrPascal(text, pascal) {
19
186
  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) => {
20
187
  if (word.length > 1 && word === word.toUpperCase()) return word;
21
- if (i === 0 && !pascal) return word.charAt(0).toLowerCase() + word.slice(1);
22
- return word.charAt(0).toUpperCase() + word.slice(1);
188
+ return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
23
189
  }).join("").replace(/[^a-zA-Z0-9]/g, "");
24
190
  }
25
191
  /**
26
- * Splits `text` on `.` and applies `transformPart` to each segment.
27
- * The last segment receives `isLast = true`, all earlier segments receive `false`.
28
- * Segments are joined with `/` to form a file path.
192
+ * Converts `text` to camelCase.
193
+ *
194
+ * @example Word boundaries
195
+ * `camelCase('hello-world') // 'helloWorld'`
196
+ *
197
+ * @example With a prefix
198
+ * `camelCase('tag', { prefix: 'create' }) // 'createTag'`
29
199
  */
30
- function applyToFileParts(text, transformPart) {
31
- const parts = text.split(".");
32
- return parts.map((part, i) => transformPart(part, i === parts.length - 1)).join("/");
200
+ function camelCase(text, { prefix = "", suffix = "" } = {}) {
201
+ return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
33
202
  }
34
203
  /**
35
- * Converts `text` to camelCase.
36
- * When `isFile` is `true`, dot-separated segments are each cased independently and joined with `/`.
204
+ * Converts `text` to PascalCase.
205
+ *
206
+ * @example Word boundaries
207
+ * `pascalCase('hello-world') // 'HelloWorld'`
208
+ *
209
+ * @example With a suffix
210
+ * `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`
211
+ */
212
+ function pascalCase(text, { prefix = "", suffix = "" } = {}) {
213
+ return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
214
+ }
215
+ //#endregion
216
+ //#region ../../internals/utils/src/reserved.ts
217
+ /**
218
+ * JavaScript and Java reserved words.
219
+ * @link https://github.com/jonschlinkert/reserved/blob/master/index.js
220
+ */
221
+ const reservedWords = /* @__PURE__ */ new Set([
222
+ "abstract",
223
+ "arguments",
224
+ "boolean",
225
+ "break",
226
+ "byte",
227
+ "case",
228
+ "catch",
229
+ "char",
230
+ "class",
231
+ "const",
232
+ "continue",
233
+ "debugger",
234
+ "default",
235
+ "delete",
236
+ "do",
237
+ "double",
238
+ "else",
239
+ "enum",
240
+ "eval",
241
+ "export",
242
+ "extends",
243
+ "false",
244
+ "final",
245
+ "finally",
246
+ "float",
247
+ "for",
248
+ "function",
249
+ "goto",
250
+ "if",
251
+ "implements",
252
+ "import",
253
+ "in",
254
+ "instanceof",
255
+ "int",
256
+ "interface",
257
+ "let",
258
+ "long",
259
+ "native",
260
+ "new",
261
+ "null",
262
+ "package",
263
+ "private",
264
+ "protected",
265
+ "public",
266
+ "return",
267
+ "short",
268
+ "static",
269
+ "super",
270
+ "switch",
271
+ "synchronized",
272
+ "this",
273
+ "throw",
274
+ "throws",
275
+ "transient",
276
+ "true",
277
+ "try",
278
+ "typeof",
279
+ "var",
280
+ "void",
281
+ "volatile",
282
+ "while",
283
+ "with",
284
+ "yield",
285
+ "Array",
286
+ "Date",
287
+ "hasOwnProperty",
288
+ "Infinity",
289
+ "isFinite",
290
+ "isNaN",
291
+ "isPrototypeOf",
292
+ "length",
293
+ "Math",
294
+ "name",
295
+ "NaN",
296
+ "Number",
297
+ "Object",
298
+ "prototype",
299
+ "String",
300
+ "toString",
301
+ "undefined",
302
+ "valueOf"
303
+ ]);
304
+ /**
305
+ * Returns `true` when `name` is a syntactically valid JavaScript variable name.
37
306
  *
38
307
  * @example
39
- * camelCase('hello-world') // 'helloWorld'
40
- * camelCase('pet.petId', { isFile: true }) // 'pet/petId'
308
+ * ```ts
309
+ * isValidVarName('status') // true
310
+ * isValidVarName('class') // false (reserved word)
311
+ * isValidVarName('42foo') // false (starts with digit)
312
+ * ```
41
313
  */
42
- function camelCase(text, { isFile, prefix = "", suffix = "" } = {}) {
43
- if (isFile) return applyToFileParts(text, (part, isLast) => camelCase(part, isLast ? {
44
- prefix,
45
- suffix
46
- } : {}));
47
- return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
314
+ function isValidVarName(name) {
315
+ if (!name || reservedWords.has(name)) return false;
316
+ return isIdentifier(name);
48
317
  }
49
318
  /**
50
- * Converts `text` to PascalCase.
51
- * When `isFile` is `true`, the last dot-separated segment is PascalCased and earlier segments are camelCased.
319
+ * Returns `name` when it's a syntactically valid JavaScript variable name,
320
+ * otherwise prefixes it with `_` so the result is a valid identifier.
321
+ *
322
+ * Useful for sanitizing OpenAPI schema names or operation IDs that start with
323
+ * a digit (e.g. `409`, `504AccountCancel`) before using them as exported
324
+ * variable, type, or function names.
52
325
  *
53
326
  * @example
54
- * pascalCase('hello-world') // 'HelloWorld'
55
- * pascalCase('pet.petId', { isFile: true }) // 'pet/PetId'
327
+ * ```ts
328
+ * ensureValidVarName('409') // '_409'
329
+ * ensureValidVarName('504AccountCancel') // '_504AccountCancel'
330
+ * ensureValidVarName('Pet') // 'Pet'
331
+ * ensureValidVarName('class') // '_class'
332
+ * ```
56
333
  */
57
- function pascalCase(text, { isFile, prefix = "", suffix = "" } = {}) {
58
- if (isFile) return applyToFileParts(text, (part, isLast) => isLast ? pascalCase(part, {
59
- prefix,
60
- suffix
61
- }) : camelCase(part));
62
- return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
334
+ function ensureValidVarName(name) {
335
+ if (!name || isValidVarName(name)) return name;
336
+ return `_${name}`;
337
+ }
338
+ /**
339
+ * Returns `true` when `name` is syntactically a valid identifier, ignoring reserved words.
340
+ *
341
+ * Reserved words and globals (`class`, `name`, `Date`, …) are valid as bare object-literal keys
342
+ * even though they are not valid variable names, so use this (not {@link isValidVarName}) when
343
+ * deciding whether an object key needs quoting.
344
+ *
345
+ * @example
346
+ * ```ts
347
+ * isIdentifier('name') // true
348
+ * isIdentifier('x-total')// false
349
+ * ```
350
+ */
351
+ function isIdentifier(name) {
352
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
63
353
  }
64
354
  //#endregion
65
- //#region src/plugin.ts
66
- const pluginZodName = "plugin-zod";
67
- const pluginZod = (0, _kubb_core.createPlugin)((options) => {
68
- const { output = {
69
- path: "zod",
70
- barrelType: "named"
71
- }, group, exclude = [], include, override = [], transformers = {}, dateType = "string", unknownType = "any", emptySchemaType = unknownType, integerType = "number", typed = false, mapper = {}, operations = false, mini = false, version = mini ? "4" : (0, _kubb_core.satisfiesDependency)("zod", ">=4") ? "4" : "3", guidType = "uuid", importPath = mini ? "zod/mini" : version === "4" ? "zod/v4" : "zod", coercion = false, inferred = false, generators = [require_generators.zodGenerator, operations ? require_generators.operationsGenerator : void 0].filter(Boolean), wrapOutput = void 0, contentType } = options;
355
+ //#region ../../internals/utils/src/strings.ts
356
+ /**
357
+ * Wraps a value in single quotes for emitting a single-quoted JavaScript string literal, escaping
358
+ * any backslash or single quote in the content.
359
+ *
360
+ * @example
361
+ * ```ts
362
+ * singleQuote('foo') // "'foo'"
363
+ * singleQuote("o'clock") // "'o\\'clock'"
364
+ * ```
365
+ */
366
+ function singleQuote(value) {
367
+ if (value === void 0 || value === null) return "''";
368
+ return `'${String(value).replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
369
+ }
370
+ /**
371
+ * Strips a single matching pair of `"..."`, `'...'`, or `` `...` `` from both ends of `text`.
372
+ * Returns the string unchanged when no balanced quote pair is found.
373
+ *
374
+ * @example
375
+ * ```ts
376
+ * trimQuotes('"hello"') // 'hello'
377
+ * trimQuotes('hello') // 'hello'
378
+ * ```
379
+ */
380
+ function trimQuotes(text) {
381
+ if (text.length >= 2) {
382
+ const first = text[0];
383
+ const last = text[text.length - 1];
384
+ if (first === "\"" && last === "\"" || first === "'" && last === "'" || first === "`" && last === "`") return text.slice(1, -1);
385
+ }
386
+ return text;
387
+ }
388
+ /**
389
+ * Serializes a primitive to a single-quoted string literal, stripping any surrounding quotes first.
390
+ *
391
+ * Escaping runs through `JSON.stringify`, then the result switches to single quotes so the generated
392
+ * code matches the repo style without a formatter.
393
+ *
394
+ * @example
395
+ * ```ts
396
+ * stringify('hello') // "'hello'"
397
+ * stringify('"hello"') // "'hello'"
398
+ * ```
399
+ */
400
+ function stringify(value) {
401
+ if (value === void 0 || value === null) return "''";
402
+ return `'${JSON.stringify(trimQuotes(value.toString())).slice(1, -1).replace(/\\"/g, "\"").replace(/'/g, "\\'")}'`;
403
+ }
404
+ /**
405
+ * Converts a pattern string into a `new RegExp(...)` constructor call or a regex literal string.
406
+ * Inline flags expressed as a `^(?im)` prefix are extracted and applied to the resulting expression.
407
+ * Pass `null` as the second argument to emit a `/pattern/flags` literal instead.
408
+ *
409
+ * @example
410
+ * ```ts
411
+ * toRegExpString('^(?im)foo') // 'new RegExp("^foo", "im")'
412
+ * toRegExpString('^(?im)foo', null) // '/^foo/im'
413
+ * ```
414
+ */
415
+ function toRegExpString(text, func = "RegExp") {
416
+ const raw = trimQuotes(text);
417
+ const match = raw.match(/^\^(\(\?([igmsuy]+)\))/i);
418
+ const replacementTarget = match?.[1] ?? "";
419
+ const matchedFlags = match?.[2];
420
+ const cleaned = raw.replace(/^\\?\//, "").replace(/\\?\/$/, "").replace(replacementTarget, "");
421
+ const { source, flags } = new RegExp(cleaned, matchedFlags);
422
+ if (func === null) return `/${source}/${flags}`;
423
+ return `new ${func}(${JSON.stringify(source)}${flags ? `, ${JSON.stringify(flags)}` : ""})`;
424
+ }
425
+ //#endregion
426
+ //#region ../../internals/utils/src/codegen.ts
427
+ const INDENT = " ";
428
+ /**
429
+ * Indents every non-empty line of `text` by one indent level, leaving blank lines empty.
430
+ */
431
+ function indentLines(text) {
432
+ if (!text) return "";
433
+ return text.split("\n").map((line) => line.trim() ? `${INDENT}${line}` : "").join("\n");
434
+ }
435
+ /**
436
+ * Renders an object key, quoting it with single quotes only when it is not a valid identifier.
437
+ * Reserved words and globals (`name`, `class`, …) are valid bare keys and stay unquoted.
438
+ *
439
+ * @example
440
+ * ```ts
441
+ * objectKey('name') // 'name'
442
+ * objectKey('x-total') // "'x-total'"
443
+ * ```
444
+ */
445
+ function objectKey(name) {
446
+ return isIdentifier(name) ? name : singleQuote(name);
447
+ }
448
+ /**
449
+ * Assembles a multi-line object literal from already-rendered `entries`, indenting each entry one
450
+ * level and closing the brace at column zero. Entries that are themselves multi-line objects indent
451
+ * cumulatively. Each entry ends with a trailing comma to match the formatter's multi-line style.
452
+ *
453
+ * @example
454
+ * ```ts
455
+ * buildObject(['id: z.number()', 'name: z.string()'])
456
+ * // '{\n id: z.number(),\n name: z.string(),\n}'
457
+ * ```
458
+ */
459
+ function buildObject(entries) {
460
+ if (entries.length === 0) return "{}";
461
+ return `{\n${entries.map((entry) => `${indentLines(entry)},`).join("\n")}\n}`;
462
+ }
463
+ /**
464
+ * Assembles a bracketed list (array by default) from already-rendered `items`. Keeps everything on
465
+ * one line when no item spans multiple lines, and otherwise puts each item on its own line, indented
466
+ * one level with a trailing comma and the closing bracket at column zero. Used for member lists such
467
+ * as `z.union([…])` and `z.array([…])`.
468
+ *
469
+ * @example
470
+ * ```ts
471
+ * buildList(['z.string()', 'z.number()'])
472
+ * // '[z.string(), z.number()]'
473
+ * ```
474
+ */
475
+ function buildList(items, brackets = ["[", "]"]) {
476
+ const [open, close] = brackets;
477
+ if (items.length === 0) return `${open}${close}`;
478
+ if (!items.some((item) => item.includes("\n"))) return `${open}${items.join(", ")}${close}`;
479
+ return `${open}\n${items.map((item) => `${indentLines(item)},`).join("\n")}\n${close}`;
480
+ }
481
+ /**
482
+ * Emits a lazy getter for a circular-ref property position, `get name() { return body }`. The key
483
+ * is quoted only when it is not a valid identifier. Used by the string printers to defer evaluation
484
+ * of a recursive schema until first access.
485
+ *
486
+ * @example
487
+ * ```ts
488
+ * lazyGetter({ name: 'parent', body: 'z.lazy(() => Pet)' })
489
+ * // "get parent() { return z.lazy(() => Pet) }"
490
+ * ```
491
+ */
492
+ function lazyGetter({ name, body }) {
493
+ return `get ${objectKey(name)}() { return ${body} }`;
494
+ }
495
+ //#endregion
496
+ //#region ../../internals/utils/src/fs.ts
497
+ /**
498
+ * Builds a nested file path from a dotted name. Splits on dots that precede a letter
499
+ * (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
500
+ * every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
501
+ *
502
+ * Empty segments are dropped before joining. They arise when the name starts with a dot
503
+ * followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
504
+ * an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
505
+ * absolute path, letting generated files escape the configured output directory.
506
+ *
507
+ * @example Nested path from a dotted name
508
+ * `toFilePath('pet.petId') // 'pet/petId'`
509
+ *
510
+ * @example PascalCase the final segment
511
+ * `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
512
+ *
513
+ * @example Suffix applied to the final segment only
514
+ * `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
515
+ */
516
+ function toFilePath(name, caseLast = camelCase) {
517
+ const parts = name.split(/\.(?=[a-zA-Z])/);
518
+ return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
519
+ }
520
+ //#endregion
521
+ //#region ../../internals/shared/src/resolver.ts
522
+ /**
523
+ * Resolves a single operation parameter name with the
524
+ * `<operationId> <in> <name>` template.
525
+ *
526
+ * @example
527
+ * `operationParamName.call(resolver, node, param) // → 'DeletePetPathPetId'`
528
+ */
529
+ function operationParamName(node, param) {
530
+ return this.name(`${node.operationId} ${param.in} ${param.name}`);
531
+ }
532
+ /**
533
+ * Builds the shared `param` namespace. Spread the result into `createResolver`
534
+ * and override individual methods next to it when a plugin deviates.
535
+ *
536
+ * @example
537
+ * ```ts
538
+ * createResolver<PluginTs>({ param: createOperationParamResolver(), ... })
539
+ * ```
540
+ */
541
+ function createOperationParamResolver() {
72
542
  return {
73
- name: pluginZodName,
74
- options: {
75
- output,
76
- transformers,
77
- include,
78
- exclude,
79
- override,
80
- typed,
81
- dateType,
82
- unknownType,
83
- emptySchemaType,
84
- integerType,
85
- mapper,
86
- importPath,
87
- coercion,
88
- operations,
89
- inferred,
90
- group,
91
- wrapOutput,
92
- version,
93
- guidType,
94
- mini,
95
- usedEnumNames: {}
543
+ name: operationParamName,
544
+ path(node) {
545
+ return this.name(`${node.operationId} Path`);
96
546
  },
97
- pre: [_kubb_plugin_oas.pluginOasName, typed ? _kubb_plugin_ts.pluginTsName : void 0].filter(Boolean),
98
- resolvePath(baseName, pathMode, options) {
99
- const root = node_path.default.resolve(this.config.root, this.config.output.path);
100
- if ((pathMode ?? (0, _kubb_core.getMode)(node_path.default.resolve(root, output.path))) === "single")
101
- /**
102
- * when output is a file then we will always append to the same file(output file), see fileManager.addOrAppend
103
- * Other plugins then need to call addOrAppend instead of just add from the fileManager class
104
- */
105
- return node_path.default.resolve(root, output.path);
106
- if (group && (options?.group?.path || options?.group?.tag)) {
107
- const groupName = group?.name ? group.name : (ctx) => {
108
- if (group?.type === "path") return `${ctx.group.split("/")[1]}`;
109
- return `${camelCase(ctx.group)}Controller`;
110
- };
111
- return node_path.default.resolve(root, output.path, groupName({ group: group.type === "path" ? options.group.path : options.group.tag }), baseName);
547
+ query(node) {
548
+ return this.name(`${node.operationId} Query`);
549
+ },
550
+ headers(node) {
551
+ return this.name(`${node.operationId} Headers`);
552
+ }
553
+ };
554
+ }
555
+ /**
556
+ * Builds the shared `response` namespace. Spread the result into
557
+ * `createResolver` and add plugin-specific methods (`options`, `error`) next
558
+ * to it.
559
+ *
560
+ * @example
561
+ * ```ts
562
+ * createResolver<PluginTs>({ response: { ...createOperationResponseResolver(), options(node) {...} }, ... })
563
+ * ```
564
+ */
565
+ function createOperationResponseResolver() {
566
+ return {
567
+ status(node, statusCode) {
568
+ return this.name(`${node.operationId} Status ${statusCode}`);
569
+ },
570
+ body(node) {
571
+ return this.name(`${node.operationId} Body`);
572
+ },
573
+ responses(node) {
574
+ return this.name(`${node.operationId} Responses`);
575
+ },
576
+ response(node) {
577
+ return this.name(`${node.operationId} Response`);
578
+ }
579
+ };
580
+ }
581
+ /**
582
+ * Builds a resolver `file` override whose base name runs every path segment
583
+ * through `toFilePath`, casing the final segment with `caseLast`.
584
+ *
585
+ * @example
586
+ * ```ts
587
+ * createResolver<PluginTs>({ file: createCasedFile(pascalCase), ... })
588
+ * ```
589
+ */
590
+ function createCasedFile(caseLast) {
591
+ return { baseName({ name, extname }) {
592
+ return `${toFilePath(name, caseLast)}${extname}`;
593
+ } };
594
+ }
595
+ //#endregion
596
+ //#region ../../internals/shared/src/refs.ts
597
+ /**
598
+ * Collects the resolved target name of every pointer-carrying ref in a schema tree, in
599
+ * first-occurrence order. Use this for name-only checks (e.g. redeclaration detection) where
600
+ * `resolver.imports` would resolve file paths that are then discarded.
601
+ */
602
+ function collectRefNames(schema) {
603
+ return kubb_kit.ast.collectSync(schema, { schema: (node) => {
604
+ const refNode = kubb_kit.ast.narrowSchema(node, "ref");
605
+ if (!refNode?.ref) return null;
606
+ return kubb_kit.ast.resolveRefName(refNode);
607
+ } });
608
+ }
609
+ //#endregion
610
+ //#region ../../internals/shared/src/group.ts
611
+ /**
612
+ * Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
613
+ * shared default naming so every plugin groups output consistently:
614
+ *
615
+ * - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
616
+ * - other groups use the camelCased group (`pet store` → `petStore`).
617
+ *
618
+ * A user-provided `group.name` always wins over the default namer, so callers stay in
619
+ * control of their output folders. Returns `null` when grouping is disabled, matching the
620
+ * per-plugin convention.
621
+ *
622
+ * @param group - The user-supplied group option, or `undefined` to disable grouping.
623
+ *
624
+ * @example
625
+ * ```ts
626
+ * createGroupConfig(group) // shared across every plugin
627
+ * ```
628
+ */
629
+ function createGroupConfig(group) {
630
+ if (!group) return null;
631
+ const defaultName = (ctx) => {
632
+ if (group.type === "path") return `${ctx.group.split("/")[1]}`;
633
+ return camelCase(ctx.group);
634
+ };
635
+ return {
636
+ ...group,
637
+ name: group.name ? group.name : defaultName
638
+ };
639
+ }
640
+ //#endregion
641
+ //#region ../../internals/shared/src/schemaTraversal.ts
642
+ /**
643
+ * Maps each property of an object schema to its transformed output. Pairs every result with the
644
+ * original property so the printer keeps full control over modifiers, getters, and key syntax.
645
+ *
646
+ * @example
647
+ * ```ts
648
+ * const entries = mapSchemaProperties(node, (schema) => this.transform(schema))
649
+ * // entries: [{ name: 'id', property, output: 'z.number()' }, ...]
650
+ * ```
651
+ */
652
+ function mapSchemaProperties(node, transform) {
653
+ return node.properties.map((property) => ({
654
+ name: property.name,
655
+ property,
656
+ output: transform(property.schema)
657
+ }));
658
+ }
659
+ /**
660
+ * Maps each member of a union or intersection schema to its transformed output, pairing every
661
+ * result with the original member.
662
+ */
663
+ function mapSchemaMembers(node, transform) {
664
+ return (node.members ?? []).map((schema) => ({
665
+ schema,
666
+ output: transform(schema)
667
+ }));
668
+ }
669
+ /**
670
+ * Maps each item of an array or tuple schema to its transformed output, pairing every result with
671
+ * the original item.
672
+ */
673
+ function mapSchemaItems(node, transform) {
674
+ return (node.items ?? []).map((schema) => ({
675
+ schema,
676
+ output: transform(schema)
677
+ }));
678
+ }
679
+ //#endregion
680
+ //#region src/components/Zod.tsx
681
+ function Zod({ name, node, printer, inferTypeName, cyclic }) {
682
+ const output = printer.print(node);
683
+ if (!output) return;
684
+ const needsAnnotation = cyclic && node.type !== "object";
685
+ return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Source, {
686
+ name,
687
+ isExportable: true,
688
+ isIndexable: true,
689
+ children: /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.Const, {
690
+ export: true,
691
+ name,
692
+ type: needsAnnotation ? "z.ZodType" : void 0,
693
+ children: output
694
+ })
695
+ }), inferTypeName && /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Source, {
696
+ name: inferTypeName,
697
+ isExportable: true,
698
+ isIndexable: true,
699
+ isTypeOnly: true,
700
+ children: /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.Type, {
701
+ export: true,
702
+ name: inferTypeName,
703
+ children: `z.infer<typeof ${name}>`
704
+ })
705
+ })] });
706
+ }
707
+ //#endregion
708
+ //#region src/constants.ts
709
+ /**
710
+ * Import paths that use a namespace import (`import * as z from '...'`).
711
+ * All other import paths use a named import (`import { z } from '...'`).
712
+ */
713
+ const ZOD_NAMESPACE_IMPORTS = /* @__PURE__ */ new Set(["zod", "zod/mini"]);
714
+ //#endregion
715
+ //#region src/utils.ts
716
+ /**
717
+ * Returns `true` when the given coercion option enables coercion for the specified type.
718
+ */
719
+ function shouldCoerce(coercion, type) {
720
+ if (coercion === void 0 || coercion === false) return false;
721
+ if (coercion === true) return true;
722
+ return !!coercion[type];
723
+ }
724
+ /**
725
+ * Registered codecs, checked in order.
726
+ */
727
+ const codecs = [{
728
+ matches(node) {
729
+ return node.type === "date" && node.representation === "date";
730
+ },
731
+ decode(node) {
732
+ return node.format === "date" ? "z.iso.date().transform((value) => new Date(value))" : "z.iso.datetime().transform((value) => new Date(value))";
733
+ },
734
+ encode(node) {
735
+ return node.format === "date" ? "z.date().transform((value) => value.toISOString().slice(0, 10))" : "z.date().transform((value) => value.toISOString())";
736
+ }
737
+ }];
738
+ /**
739
+ * Returns the codec for this node, or `undefined` when the node needs no
740
+ * encode/decode (its wire and runtime types match).
741
+ */
742
+ function getCodec(node) {
743
+ if (!node) return void 0;
744
+ return codecs.find((codec) => codec.matches(node));
745
+ }
746
+ /**
747
+ * Returns `true` when the node itself is encoded/decoded by a codec.
748
+ */
749
+ function hasCodec(node) {
750
+ return getCodec(node) !== void 0;
751
+ }
752
+ /**
753
+ * Returns `true` when the schema transitively contains a codec node —
754
+ * a value whose runtime type differs from its wire type (see {@link hasCodec}),
755
+ * so it must be decoded (response) or encoded (request) at the validation boundary.
756
+ * `$ref`s are followed via their resolved schema; a `seen` set guards cycles.
757
+ */
758
+ function containsCodec(node, seen = /* @__PURE__ */ new Set()) {
759
+ if (!node) return false;
760
+ if (hasCodec(node)) return true;
761
+ if (node.type === "ref") {
762
+ if (!node.ref) return false;
763
+ const refName = (0, kubb_kit.extractRefName)(node.ref);
764
+ if (refName) {
765
+ if (seen.has(refName)) return false;
766
+ seen.add(refName);
767
+ }
768
+ const resolved = (0, kubb_kit.syncSchemaRef)(node);
769
+ if (resolved.type === "ref") return false;
770
+ return containsCodec(resolved, seen);
771
+ }
772
+ const children = [];
773
+ if ("properties" in node && node.properties) children.push(...node.properties.map((prop) => prop.schema));
774
+ if ("items" in node && node.items) children.push(...node.items);
775
+ if ("members" in node && node.members) children.push(...node.members);
776
+ if ("additionalProperties" in node && node.additionalProperties && node.additionalProperties !== true) children.push(node.additionalProperties);
777
+ return children.some((child) => containsCodec(child, seen));
778
+ }
779
+ /**
780
+ * Collects the names of `$ref` schemas that transitively contain a codec, so the generator can route
781
+ * them to their input (encode) variant.
782
+ */
783
+ function collectCodecRefNames(node) {
784
+ return kubb_kit.ast.collectSync(node, { schema: (n) => n.type === "ref" && n.ref && containsCodec(n) ? kubb_kit.ast.resolveRefName(n) ?? void 0 : void 0 });
785
+ }
786
+ /**
787
+ * Whether the node is a plain inline object whose shape can be lifted into an `.extend({ … })`
788
+ * argument. A catchall, `patternProperties`, or a nullable/optional wrapper cannot, so those stay
789
+ * on `.and(…)`.
790
+ */
791
+ function isPlainInlineObject(node) {
792
+ return node.type === "object" && !node.nullable && !node.optional && !node.nullish && node.additionalProperties === void 0 && !node.patternProperties;
793
+ }
794
+ /**
795
+ * Whether a node renders as a bare Zod object — one that accepts `.extend(…)` and can be a
796
+ * `z.discriminatedUnion` option. Covers object nodes, `$ref`s that resolve to (or are still
797
+ * unresolved) objects, single-member unions of an object, and object-composable `allOf`.
798
+ *
799
+ * `cyclicSchemas` bounds the recursion: a ref into a cycle renders as `z.lazy(…)`, not an object.
800
+ */
801
+ function isObjectSchemaNode(node, cyclicSchemas) {
802
+ if (node.nullable || node.optional || node.nullish) return false;
803
+ if (node.type === "object") return true;
804
+ if (node.type === "ref") {
805
+ const refName = kubb_kit.ast.resolveRefName(node);
806
+ if (refName && cyclicSchemas?.has(refName)) return false;
807
+ const resolved = (0, kubb_kit.syncSchemaRef)(node);
808
+ return resolved.type === "ref" || isObjectSchemaNode(resolved, cyclicSchemas);
809
+ }
810
+ if (node.type === "union") {
811
+ const members = node.members ?? [];
812
+ return members.length === 1 && isObjectSchemaNode(members[0], cyclicSchemas);
813
+ }
814
+ return isObjectComposableIntersection(node, cyclicSchemas);
815
+ }
816
+ /**
817
+ * Whether an `allOf` is a pure object composition — an object base plus inline object members whose
818
+ * shapes merge with `.extend(…)`. These stay a Zod object instead of a `ZodIntersection`, which
819
+ * `z.discriminatedUnion` rejects.
820
+ */
821
+ function isObjectComposableIntersection(node, cyclicSchemas) {
822
+ if (node.type !== "intersection") return false;
823
+ const [first, ...rest] = node.members ?? [];
824
+ return !!first && isObjectSchemaNode(first, cyclicSchemas) && rest.every(isPlainInlineObject);
825
+ }
826
+ /**
827
+ * Format a default value as a code-level literal.
828
+ * Objects become `{}`, primitives become their string representation, strings are quoted.
829
+ */
830
+ function formatDefault(value) {
831
+ if (typeof value === "string") return stringify(value);
832
+ if (typeof value === "object" && value !== null) return "{}";
833
+ return String(value ?? "");
834
+ }
835
+ /**
836
+ * Format a default for `.default(...)` so the literal matches the generated schema's type.
837
+ *
838
+ * An OpenAPI `default` does not always agree with the schema it sits on: a `bigint` field
839
+ * (`format: int64`) carries a `number` default, and a spec may put `default: {}` on an `array`.
840
+ * Emitting those verbatim produces a Zod schema that does not typecheck, so coerce the bigint case
841
+ * to a `BigInt(...)` literal and emit an array literal for arrays (dropping a non-array default).
842
+ * Returns `null` when no `.default(...)` should be emitted. Keys off the schema node's type.
843
+ */
844
+ function defaultLiteral(node, value) {
845
+ if (value === null) return null;
846
+ if (node && kubb_kit.ast.narrowSchema(node, "bigint")) {
847
+ if (typeof value === "bigint") return `BigInt(${value})`;
848
+ if (typeof value === "number" && Number.isInteger(value)) return `BigInt(${value})`;
849
+ return null;
850
+ }
851
+ if (node && kubb_kit.ast.narrowSchema(node, "array")) return Array.isArray(value) ? JSON.stringify(value) : null;
852
+ const enumNode = node ? kubb_kit.ast.narrowSchema(node, "enum") : void 0;
853
+ if (enumNode) {
854
+ const values = enumNode.namedEnumValues?.map((member) => member.value) ?? enumNode.enumValues ?? [];
855
+ if (values.length) {
856
+ const match = values.find((member) => member === value || String(member) === String(value));
857
+ return match != null ? formatLiteral(match) : null;
858
+ }
859
+ }
860
+ return formatDefault(value);
861
+ }
862
+ /**
863
+ * Format a primitive enum/literal value.
864
+ * Strings are quoted; numbers and booleans are emitted raw.
865
+ */
866
+ function formatLiteral(v) {
867
+ if (typeof v === "string") return stringify(v);
868
+ return String(v);
869
+ }
870
+ /**
871
+ * Build the Zod schema for a set of literal enum values.
872
+ * `z.enum()` only accepts string members in Zod v4, so a numeric, boolean, or
873
+ * mixed set is emitted as a single `z.literal(…)` or a `z.union([z.literal(…), …])`.
874
+ * An all-string set keeps the more compact `z.enum([…])`.
875
+ */
876
+ function buildEnum(values) {
877
+ if (values.every((v) => typeof v === "string")) return `z.enum([${values.map(formatLiteral).join(", ")}])`;
878
+ const literals = values.map((v) => `z.literal(${formatLiteral(v)})`);
879
+ if (literals.length === 1) return literals[0];
880
+ return `z.union([${literals.join(", ")}])`;
881
+ }
882
+ /**
883
+ * Map a `regexType` to the `func` argument of `toRegExpString`: `'constructor'` emits
884
+ * `new RegExp(...)`, while `'literal'` (the default) emits a regex literal.
885
+ */
886
+ function regexFunc(regexType) {
887
+ return regexType === "constructor" ? "RegExp" : null;
888
+ }
889
+ /**
890
+ * Builds a Zod record key schema that enforces the `patternProperties` regexes a plain
891
+ * `.catchall()` would drop. Several patterns combine into one alternation (`(^a)|(^b)`).
892
+ */
893
+ function patternKeySchema({ patterns, regexType }) {
894
+ return `z.string().regex(${patternKeySource({
895
+ patterns,
896
+ regexType
897
+ })})`;
898
+ }
899
+ /**
900
+ * `zod/mini` variant of {@link patternKeySchema}, wrapping the regex in `.check(z.regex(...))`.
901
+ */
902
+ function patternKeySchemaMini({ patterns, regexType }) {
903
+ return `z.string().check(z.regex(${patternKeySource({
904
+ patterns,
905
+ regexType
906
+ })}))`;
907
+ }
908
+ function patternKeySource({ patterns, regexType }) {
909
+ return toRegExpString(patterns.length === 1 ? patterns[0] : patterns.map((pattern) => `(${pattern})`).join("|"), regexFunc(regexType));
910
+ }
911
+ /**
912
+ * Build `.min()` / `.max()` / `.gt()` / `.lt()` constraint chains for numbers
913
+ * using the standard chainable Zod v4 API.
914
+ */
915
+ function numberConstraints({ min, max, exclusiveMinimum, exclusiveMaximum, multipleOf }) {
916
+ return [
917
+ min !== void 0 ? `.min(${min})` : "",
918
+ max !== void 0 ? `.max(${max})` : "",
919
+ exclusiveMinimum !== void 0 ? `.gt(${exclusiveMinimum})` : "",
920
+ exclusiveMaximum !== void 0 ? `.lt(${exclusiveMaximum})` : "",
921
+ multipleOf !== void 0 ? `.multipleOf(${multipleOf})` : ""
922
+ ].join("");
923
+ }
924
+ /**
925
+ * Build `.min()` / `.max()` / `.regex()` chains for strings/arrays
926
+ * using the standard chainable Zod v4 API.
927
+ */
928
+ function lengthConstraints({ min, max, pattern, regexType }) {
929
+ return [
930
+ min !== void 0 ? `.min(${min})` : "",
931
+ max !== void 0 ? `.max(${max})` : "",
932
+ pattern !== void 0 ? `.regex(${toRegExpString(pattern, regexFunc(regexType))})` : ""
933
+ ].join("");
934
+ }
935
+ /**
936
+ * Build `.check(z.minimum(), z.maximum())` for `zod/mini` numeric constraints.
937
+ */
938
+ function numberChecksMini({ min, max, exclusiveMinimum, exclusiveMaximum, multipleOf }) {
939
+ const checks = [];
940
+ if (min !== void 0) checks.push(`z.minimum(${min})`);
941
+ if (max !== void 0) checks.push(`z.maximum(${max})`);
942
+ if (exclusiveMinimum !== void 0) checks.push(`z.minimum(${exclusiveMinimum}, { exclusive: true })`);
943
+ if (exclusiveMaximum !== void 0) checks.push(`z.maximum(${exclusiveMaximum}, { exclusive: true })`);
944
+ if (multipleOf !== void 0) checks.push(`z.multipleOf(${multipleOf})`);
945
+ return checks.length ? `.check(${checks.join(", ")})` : "";
946
+ }
947
+ /**
948
+ * Build `.check(z.minLength(), z.maxLength(), z.regex())` for `zod/mini` length constraints.
949
+ */
950
+ function lengthChecksMini({ min, max, pattern, regexType }) {
951
+ const checks = [];
952
+ if (min !== void 0) checks.push(`z.minLength(${min})`);
953
+ if (max !== void 0) checks.push(`z.maxLength(${max})`);
954
+ if (pattern !== void 0) checks.push(`z.regex(${toRegExpString(pattern, regexFunc(regexType))})`);
955
+ return checks.length ? `.check(${checks.join(", ")})` : "";
956
+ }
957
+ /**
958
+ * Apply nullable / optional / nullish modifiers, an optional `.describe()` call, and an
959
+ * optional `.meta({ examples })` call to a schema value string using the chainable Zod v4 API.
960
+ */
961
+ function applyModifiers({ value, schema, nullable, optional, nullish, defaultValue, description, examples }) {
962
+ const withModifier = (() => {
963
+ if (nullish || nullable && optional) return `${value}.nullish()`;
964
+ if (optional) return `${value}.optional()`;
965
+ if (nullable) return `${value}.nullable()`;
966
+ return value;
967
+ })();
968
+ const literal = defaultValue !== void 0 ? defaultLiteral(schema, defaultValue) : null;
969
+ const withDefault = literal !== null ? `${withModifier}.default(${literal})` : withModifier;
970
+ const withDescription = description ? `${withDefault}.describe(${stringify(description)})` : withDefault;
971
+ return examples?.length ? `${withDescription}.meta({ examples: [${examples.map(formatDefault).join(", ")}] })` : withDescription;
972
+ }
973
+ function modifierDepth(schema) {
974
+ if (schema.nullish || schema.nullable && schema.optional) return 2;
975
+ if (schema.nullable || schema.optional) return 1;
976
+ return 0;
977
+ }
978
+ /**
979
+ * Build the `.unwrap()` chain to insert before `.omit()` when the schema is a `$ref`.
980
+ *
981
+ * A `$ref` resolves to a named schema variable that already carries its own `.nullable()` /
982
+ * `.optional()` / `.nullish()` / `.default()` wrappers. `.omit()` lives on the inner `ZodObject`,
983
+ * not on those `ZodNullable` / `ZodOptional` / `ZodDefault` wrappers, so the omit has to unwrap
984
+ * down to the object first. This mirrors plugin-ts emitting `Omit<NonNullable<T>, …>`. Inline
985
+ * objects need no unwrap because the printer adds their modifiers after `.omit()`.
986
+ */
987
+ function omitUnwrapChain(node) {
988
+ const ref = kubb_kit.ast.narrowSchema(node, "ref");
989
+ if (!ref) return "";
990
+ const target = ref.schema ?? ref;
991
+ const depth = modifierDepth(target) + (target.default !== void 0 ? 1 : 0);
992
+ return ".unwrap()".repeat(depth);
993
+ }
994
+ /**
995
+ * Apply nullable / optional / nullish modifiers using the functional `zod/mini` API
996
+ * (`z.nullable()`, `z.optional()`, `z.nullish()`).
997
+ */
998
+ function applyMiniModifiers({ value, schema, nullable, optional, nullish, defaultValue }) {
999
+ const withModifier = (() => {
1000
+ if (nullish) return `z.nullish(${value})`;
1001
+ const withNullable = nullable ? `z.nullable(${value})` : value;
1002
+ return optional ? `z.optional(${withNullable})` : withNullable;
1003
+ })();
1004
+ const literal = defaultValue !== void 0 ? defaultLiteral(schema, defaultValue) : null;
1005
+ return literal !== null ? `z._default(${withModifier}, ${literal})` : withModifier;
1006
+ }
1007
+ /**
1008
+ * Builds an `object` schema node grouping the given parameter nodes.
1009
+ * The `primitive: 'object'` marker ensures the Zod printer emits `z.object(…)` rather than a record.
1010
+ */
1011
+ function buildGroupedParamsSchema({ params, optional }) {
1012
+ return kubb_kit.ast.factory.createSchema({
1013
+ type: "object",
1014
+ optional,
1015
+ primitive: "object",
1016
+ properties: params.map((param) => kubb_kit.ast.factory.createProperty({
1017
+ name: param.name,
1018
+ required: param.required,
1019
+ schema: param.schema
1020
+ }))
1021
+ });
1022
+ }
1023
+ //#endregion
1024
+ //#region src/printers/printerZod.ts
1025
+ function strictOneOfMember$1(member, node, cyclicSchemas) {
1026
+ if (node.type === "object" && node.additionalProperties === void 0) return `${member}.strict()`;
1027
+ if (node.type === "ref") {
1028
+ if (member.startsWith("z.lazy(")) return member;
1029
+ const refName = kubb_kit.ast.resolveRefName(node);
1030
+ if (refName && cyclicSchemas?.has(refName)) return member;
1031
+ const schema = (0, kubb_kit.syncSchemaRef)(node);
1032
+ if (schema.nullable || schema.optional || node.nullable || node.optional) return member;
1033
+ if (schema.type === "object" && (schema.additionalProperties === void 0 || schema.additionalProperties === false)) return `${member}.strict()`;
1034
+ }
1035
+ return member;
1036
+ }
1037
+ __name(strictOneOfMember$1, "strictOneOfMember");
1038
+ function getMemberConstraint({ member, regexType }) {
1039
+ if (member.primitive === "string") return lengthConstraints({
1040
+ ...kubb_kit.ast.narrowSchema(member, "string") ?? {},
1041
+ regexType
1042
+ }) || void 0;
1043
+ if (member.primitive === "number" || member.primitive === "integer") return numberConstraints(kubb_kit.ast.narrowSchema(member, "number") ?? kubb_kit.ast.narrowSchema(member, "integer") ?? {}) || void 0;
1044
+ if (member.primitive === "array") return lengthConstraints({
1045
+ ...kubb_kit.ast.narrowSchema(member, "array") ?? {},
1046
+ regexType
1047
+ }) || void 0;
1048
+ }
1049
+ /**
1050
+ * Builds the `{ key: value, … }` shape for an object node, shared by the `z.object(...)` and
1051
+ * `.extend(...)` renderings so they stay in lockstep.
1052
+ */
1053
+ function buildZodObjectShape(ctx, node) {
1054
+ const objectNode = kubb_kit.ast.narrowSchema(node, "object");
1055
+ if (!objectNode) return "{}";
1056
+ const isCyclic = (schema) => ctx.options.cyclicSchemas != null && (0, kubb_kit.containsCircularRef)(schema, { circularSchemas: ctx.options.cyclicSchemas });
1057
+ return buildObject(mapSchemaProperties(objectNode, (schema) => {
1058
+ const hasSelfRef = isCyclic(schema);
1059
+ const savedCyclicSchemas = ctx.options.cyclicSchemas;
1060
+ if (hasSelfRef) ctx.options.cyclicSchemas = void 0;
1061
+ const baseOutput = ctx.transform(schema) ?? ctx.transform(kubb_kit.ast.factory.createSchema({ type: "unknown" }));
1062
+ if (hasSelfRef) ctx.options.cyclicSchemas = savedCyclicSchemas;
1063
+ return baseOutput;
1064
+ }).map(({ name: propName, property, output: baseOutput }) => {
1065
+ const { schema } = property;
1066
+ const meta = (0, kubb_kit.syncSchemaRef)(schema);
1067
+ const descriptionToApply = schema.type !== "ref" && meta.type === "ref" ? void 0 : meta.description;
1068
+ const value = applyModifiers({
1069
+ value: baseOutput,
1070
+ schema,
1071
+ nullable: meta.nullable,
1072
+ optional: schema.optional || property.required === false,
1073
+ nullish: schema.nullish,
1074
+ defaultValue: meta.default,
1075
+ description: descriptionToApply,
1076
+ examples: meta.examples
1077
+ });
1078
+ return isCyclic(schema) ? lazyGetter({
1079
+ name: propName,
1080
+ body: value
1081
+ }) : `${objectKey(propName)}: ${value}`;
1082
+ }));
1083
+ }
1084
+ /**
1085
+ * Zod v4 printer built with `definePrinter`.
1086
+ *
1087
+ * Converts a `SchemaNode` AST into a Zod v4 code string using the chainable API
1088
+ * (`.optional()`, `.nullable()`, `.omit()`, etc.). For improved tree-shaking, see {@link printerZodMini}.
1089
+ *
1090
+ * @example Chainable API
1091
+ * ```ts
1092
+ * const printer = printerZod({ coercion: false })
1093
+ * const code = printer.print(stringNode) // "z.string()"
1094
+ * ```
1095
+ */
1096
+ const printerZod = kubb_kit.ast.createPrinter((options) => {
1097
+ const cyclicSchemaNames = options.cyclicSchemas;
1098
+ return {
1099
+ name: "zod",
1100
+ options,
1101
+ nodes: {
1102
+ any: () => "z.any()",
1103
+ unknown: () => "z.unknown()",
1104
+ void: () => "z.void()",
1105
+ never: () => "z.never()",
1106
+ boolean: () => "z.boolean()",
1107
+ null: () => "z.null()",
1108
+ string(node) {
1109
+ return `${shouldCoerce(this.options.coercion, "strings") ? "z.coerce.string()" : "z.string()"}${lengthConstraints({
1110
+ ...node,
1111
+ regexType: this.options.regexType
1112
+ })}`;
1113
+ },
1114
+ number(node) {
1115
+ return `${shouldCoerce(this.options.coercion, "numbers") ? "z.coerce.number()" : "z.number()"}${numberConstraints(node)}`;
1116
+ },
1117
+ integer(node) {
1118
+ return `${shouldCoerce(this.options.coercion, "numbers") ? "z.coerce.number().int()" : "z.int()"}${numberConstraints(node)}`;
1119
+ },
1120
+ bigint() {
1121
+ return shouldCoerce(this.options.coercion, "numbers") ? "z.coerce.bigint()" : "z.bigint()";
1122
+ },
1123
+ date(node) {
1124
+ const codec = getCodec(node);
1125
+ if (codec) {
1126
+ if (this.options.direction === "input") return codec.encode(node);
1127
+ return shouldCoerce(this.options.coercion, "dates") ? "z.coerce.date()" : codec.decode(node);
1128
+ }
1129
+ return "z.iso.date()";
1130
+ },
1131
+ datetime(node) {
1132
+ const offset = node.offset || this.options.dateType === "stringOffset";
1133
+ const local = node.local || this.options.dateType === "stringLocal";
1134
+ if (offset) return "z.iso.datetime({ offset: true })";
1135
+ if (local) return "z.iso.datetime({ local: true })";
1136
+ return "z.iso.datetime()";
1137
+ },
1138
+ time(node) {
1139
+ if (node.representation === "string") return "z.iso.time()";
1140
+ return shouldCoerce(this.options.coercion, "dates") ? "z.coerce.date()" : "z.date()";
1141
+ },
1142
+ uuid(node) {
1143
+ return `${this.options.guidType === "guid" ? "z.guid()" : "z.uuid()"}${lengthConstraints({
1144
+ ...node,
1145
+ regexType: this.options.regexType
1146
+ })}`;
1147
+ },
1148
+ email(node) {
1149
+ return `z.email()${lengthConstraints({
1150
+ ...node,
1151
+ regexType: this.options.regexType
1152
+ })}`;
1153
+ },
1154
+ url(node) {
1155
+ return `z.url()${lengthConstraints({
1156
+ ...node,
1157
+ regexType: this.options.regexType
1158
+ })}`;
1159
+ },
1160
+ ipv4: () => "z.ipv4()",
1161
+ ipv6: () => "z.ipv6()",
1162
+ blob: () => "z.instanceof(File)",
1163
+ enum(node) {
1164
+ const nonNullValues = (node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? []).filter((v) => v !== null);
1165
+ if (node.namedEnumValues?.length) {
1166
+ const literals = nonNullValues.map((v) => `z.literal(${formatLiteral(v)})`);
1167
+ if (literals.length === 1) return literals[0];
1168
+ return `z.union([${literals.join(", ")}])`;
1169
+ }
1170
+ return buildEnum(nonNullValues);
1171
+ },
1172
+ ref(node) {
1173
+ if (!node.name) return null;
1174
+ const refName = kubb_kit.ast.resolveRefName(node);
1175
+ if (!refName) return null;
1176
+ const useInputVariant = node.ref != null && this.options.direction === "input" && containsCodec(node);
1177
+ const resolvedName = node.ref ? useInputVariant ? this.options.resolver?.schema.inputName(refName) ?? refName : this.options.resolver?.name(refName) ?? refName : node.name;
1178
+ if (node.ref && this.options.cyclicSchemas?.has(refName)) return `z.lazy(() => ${resolvedName})`;
1179
+ return resolvedName;
1180
+ },
1181
+ object(node) {
1182
+ const entries = node.properties ?? [];
1183
+ const objectBase = `z.object(${buildZodObjectShape(this, node)})`;
1184
+ return (() => {
1185
+ const patterns = node.patternProperties ? Object.entries(node.patternProperties) : [];
1186
+ if (node.additionalProperties && node.additionalProperties !== true) {
1187
+ const catchallType = this.transform(node.additionalProperties);
1188
+ return catchallType ? `${objectBase}.catchall(${catchallType})` : objectBase;
1189
+ }
1190
+ if (node.additionalProperties === true) return `${objectBase}.catchall(${this.transform(kubb_kit.ast.factory.createSchema({ type: "unknown" }))})`;
1191
+ if (node.additionalProperties === false && patterns.length === 0) return `${objectBase}.strict()`;
1192
+ if (patterns.length > 0) {
1193
+ const values = patterns.map(([, valueSchema]) => {
1194
+ const valueType = this.transform(valueSchema) ?? this.transform(kubb_kit.ast.factory.createSchema({ type: "unknown" }));
1195
+ return valueSchema.nullable ? `${valueType}.nullable()` : valueType;
1196
+ });
1197
+ const distinct = [...new Set(values)];
1198
+ const value = distinct.length === 1 ? distinct[0] : `z.union([${distinct.join(", ")}])`;
1199
+ if (entries.length > 0) return `${objectBase}.catchall(${value})`;
1200
+ return `z.record(${patternKeySchema({
1201
+ patterns: patterns.map(([pattern]) => pattern),
1202
+ regexType: this.options.regexType
1203
+ })}, ${value})`;
1204
+ }
1205
+ return objectBase;
1206
+ })();
1207
+ },
1208
+ array(node) {
1209
+ const base = `z.array(${mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean).join(", ") || this.transform(kubb_kit.ast.factory.createSchema({ type: "unknown" }))})${lengthConstraints({
1210
+ ...node,
1211
+ regexType: this.options.regexType
1212
+ })}`;
1213
+ return node.unique ? `${base}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })` : base;
1214
+ },
1215
+ tuple(node) {
1216
+ return `z.tuple(${buildList(mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean))})`;
1217
+ },
1218
+ union(node) {
1219
+ const nodeMembers = node.members ?? [];
1220
+ const members = mapSchemaMembers(node, (memberNode) => this.transform(memberNode)).map(({ schema, output }) => output && node.strategy === "one" ? strictOneOfMember$1(output, schema, cyclicSchemaNames) : output).filter(Boolean);
1221
+ if (members.length === 0) return "";
1222
+ if (members.length === 1) return members[0];
1223
+ const allDiscriminable = nodeMembers.every((m) => isObjectSchemaNode(m, cyclicSchemaNames));
1224
+ if (node.discriminatorPropertyName && allDiscriminable) return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, ${buildList(members)})`;
1225
+ return `z.union(${buildList(members)})`;
1226
+ },
1227
+ intersection(node) {
1228
+ const members = node.members ?? [];
1229
+ if (members.length === 0) return "";
1230
+ const [first, ...rest] = members;
1231
+ if (!first) return "";
1232
+ const firstBase = this.transform(first);
1233
+ if (!firstBase) return "";
1234
+ if (rest.length > 0 && isObjectComposableIntersection(node, cyclicSchemaNames)) return rest.reduce((acc, member) => `${acc}.extend(${buildZodObjectShape(this, member)})`, firstBase);
1235
+ return rest.reduce((acc, member) => {
1236
+ const constraint = getMemberConstraint({
1237
+ member,
1238
+ regexType: this.options.regexType
1239
+ });
1240
+ if (constraint) return acc + constraint;
1241
+ const transformed = this.transform(member);
1242
+ return transformed ? `${acc}.and(${transformed})` : acc;
1243
+ }, firstBase);
112
1244
  }
113
- return node_path.default.resolve(root, output.path, baseName);
114
1245
  },
115
- resolveName(name, type) {
116
- let resolvedName = camelCase(name, {
117
- suffix: type ? "schema" : void 0,
118
- isFile: type === "file"
1246
+ overrides: options.nodes,
1247
+ print(node) {
1248
+ const { keysToOmit } = this.options;
1249
+ const transformed = this.transform(node);
1250
+ if (!transformed) return null;
1251
+ const meta = (0, kubb_kit.syncSchemaRef)(node);
1252
+ return applyModifiers({
1253
+ value: (() => {
1254
+ if (!keysToOmit?.length || meta.primitive !== "object" || meta.type === "union" && meta.discriminatorPropertyName) return transformed;
1255
+ const unwrap = omitUnwrapChain(node);
1256
+ const omit = `.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} })`;
1257
+ const lazyMatch = transformed.match(/^z\.lazy\(\(\)\s*=>\s*(.+)\)$/);
1258
+ if (lazyMatch) return `z.lazy(() => ${lazyMatch[1]}${unwrap}${omit})`;
1259
+ return `${transformed}${unwrap}${omit}`;
1260
+ })(),
1261
+ schema: node,
1262
+ nullable: meta.nullable,
1263
+ optional: meta.optional,
1264
+ nullish: meta.nullish,
1265
+ defaultValue: meta.default,
1266
+ description: meta.description,
1267
+ examples: meta.examples
119
1268
  });
120
- if (type === "type") resolvedName = pascalCase(resolvedName);
121
- if (type) return transformers?.name?.(resolvedName, type) || resolvedName;
122
- return resolvedName;
1269
+ }
1270
+ };
1271
+ });
1272
+ //#endregion
1273
+ //#region src/printers/printerZodMini.ts
1274
+ function strictOneOfMember(member, node) {
1275
+ if (node.type === "object" && (node.additionalProperties === void 0 || node.additionalProperties === false)) return member.replace(/^z\.object\(/, "z.strictObject(");
1276
+ return member;
1277
+ }
1278
+ function getMemberConstraintMini({ member, regexType }) {
1279
+ if (member.primitive === "string") return lengthChecksMini({
1280
+ ...kubb_kit.ast.narrowSchema(member, "string") ?? {},
1281
+ regexType
1282
+ }) || void 0;
1283
+ if (member.primitive === "number" || member.primitive === "integer") return numberChecksMini(kubb_kit.ast.narrowSchema(member, "number") ?? kubb_kit.ast.narrowSchema(member, "integer") ?? {}) || void 0;
1284
+ if (member.primitive === "array") return lengthChecksMini({
1285
+ ...kubb_kit.ast.narrowSchema(member, "array") ?? {},
1286
+ regexType
1287
+ }) || void 0;
1288
+ }
1289
+ /**
1290
+ * Builds the `{ key: value, … }` shape for an object node with the functional `zod/mini` modifiers,
1291
+ * shared by the `z.object(...)` and `z.extend(...)` renderings so they stay in lockstep.
1292
+ */
1293
+ function buildZodMiniObjectShape(ctx, node) {
1294
+ const objectNode = kubb_kit.ast.narrowSchema(node, "object");
1295
+ if (!objectNode) return "{}";
1296
+ const isCyclic = (schema) => ctx.options.cyclicSchemas != null && (0, kubb_kit.containsCircularRef)(schema, { circularSchemas: ctx.options.cyclicSchemas });
1297
+ return buildObject(mapSchemaProperties(objectNode, (schema) => {
1298
+ const hasSelfRef = isCyclic(schema);
1299
+ const savedCyclicSchemas = ctx.options.cyclicSchemas;
1300
+ if (hasSelfRef) ctx.options.cyclicSchemas = void 0;
1301
+ const baseOutput = ctx.transform(schema) ?? ctx.transform(kubb_kit.ast.factory.createSchema({ type: "unknown" }));
1302
+ if (hasSelfRef) ctx.options.cyclicSchemas = savedCyclicSchemas;
1303
+ return baseOutput;
1304
+ }).map(({ name: propName, property, output: baseOutput }) => {
1305
+ const { schema } = property;
1306
+ const meta = (0, kubb_kit.syncSchemaRef)(schema);
1307
+ const value = applyMiniModifiers({
1308
+ value: baseOutput,
1309
+ schema,
1310
+ nullable: meta.nullable,
1311
+ optional: schema.optional || property.required === false,
1312
+ nullish: schema.nullish,
1313
+ defaultValue: meta.default
1314
+ });
1315
+ return isCyclic(schema) ? lazyGetter({
1316
+ name: propName,
1317
+ body: value
1318
+ }) : `${objectKey(propName)}: ${value}`;
1319
+ }));
1320
+ }
1321
+ /**
1322
+ * Zod v4 Mini printer built with `definePrinter`.
1323
+ *
1324
+ * Converts a `SchemaNode` AST into a Zod v4 code string using the functional API
1325
+ * (`z.optional(z.string())`) for improved tree-shaking. See {@link printerZod} for the chainable API.
1326
+ *
1327
+ * @example Functional Mini API
1328
+ * ```ts
1329
+ * const printer = printerZodMini({})
1330
+ * const code = printer.print(optionalStringNode) // "z.optional(z.string())"
1331
+ * ```
1332
+ */
1333
+ const printerZodMini = kubb_kit.ast.createPrinter((options) => {
1334
+ const cyclicSchemaNames = options.cyclicSchemas;
1335
+ return {
1336
+ name: "zod-mini",
1337
+ options,
1338
+ nodes: {
1339
+ any: () => "z.any()",
1340
+ unknown: () => "z.unknown()",
1341
+ void: () => "z.void()",
1342
+ never: () => "z.never()",
1343
+ boolean: () => "z.boolean()",
1344
+ null: () => "z.null()",
1345
+ string(node) {
1346
+ return `z.string()${lengthChecksMini({
1347
+ ...node,
1348
+ regexType: this.options.regexType
1349
+ })}`;
1350
+ },
1351
+ number(node) {
1352
+ return `z.number()${numberChecksMini(node)}`;
1353
+ },
1354
+ integer(node) {
1355
+ return `z.int()${numberChecksMini(node)}`;
1356
+ },
1357
+ bigint(node) {
1358
+ return `z.bigint()${numberChecksMini(node)}`;
1359
+ },
1360
+ date(node) {
1361
+ if (node.representation === "string") return "z.iso.date()";
1362
+ return "z.date()";
1363
+ },
1364
+ datetime() {
1365
+ return "z.string()";
1366
+ },
1367
+ time(node) {
1368
+ if (node.representation === "string") return "z.iso.time()";
1369
+ return "z.date()";
1370
+ },
1371
+ uuid(node) {
1372
+ return `${this.options.guidType === "guid" ? "z.guid()" : "z.uuid()"}${lengthChecksMini({
1373
+ ...node,
1374
+ regexType: this.options.regexType
1375
+ })}`;
1376
+ },
1377
+ email(node) {
1378
+ return `z.email()${lengthChecksMini({
1379
+ ...node,
1380
+ regexType: this.options.regexType
1381
+ })}`;
1382
+ },
1383
+ url(node) {
1384
+ return `z.url()${lengthChecksMini({
1385
+ ...node,
1386
+ regexType: this.options.regexType
1387
+ })}`;
1388
+ },
1389
+ ipv4: () => "z.ipv4()",
1390
+ ipv6: () => "z.ipv6()",
1391
+ blob: () => "z.instanceof(File)",
1392
+ enum(node) {
1393
+ const nonNullValues = (node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? []).filter((v) => v !== null);
1394
+ if (node.namedEnumValues?.length) {
1395
+ const literals = nonNullValues.map((v) => `z.literal(${formatLiteral(v)})`);
1396
+ if (literals.length === 1) return literals[0];
1397
+ return `z.union([${literals.join(", ")}])`;
1398
+ }
1399
+ return buildEnum(nonNullValues);
1400
+ },
1401
+ ref(node) {
1402
+ if (!node.name) return null;
1403
+ const refName = kubb_kit.ast.resolveRefName(node);
1404
+ if (!refName) return null;
1405
+ const resolvedName = node.ref ? this.options.resolver?.name(refName) ?? refName : node.name;
1406
+ if (node.ref && this.options.cyclicSchemas?.has(refName)) return `z.lazy(() => ${resolvedName})`;
1407
+ return resolvedName;
1408
+ },
1409
+ object(node) {
1410
+ const entries = node.properties ?? [];
1411
+ const objectBase = `z.object(${buildZodMiniObjectShape(this, node)})`;
1412
+ const patterns = node.patternProperties ? Object.entries(node.patternProperties) : [];
1413
+ if (node.additionalProperties && node.additionalProperties !== true) {
1414
+ const catchallType = this.transform(node.additionalProperties);
1415
+ return catchallType ? `z.catchall(${objectBase}, ${catchallType})` : objectBase;
1416
+ }
1417
+ if (node.additionalProperties === true) return `z.catchall(${objectBase}, ${this.transform(kubb_kit.ast.factory.createSchema({ type: "unknown" }))})`;
1418
+ if (node.additionalProperties === false && patterns.length === 0) return objectBase.replace(/^z\.object\(/, "z.strictObject(");
1419
+ if (patterns.length > 0) {
1420
+ const values = patterns.map(([, valueSchema]) => {
1421
+ const valueType = this.transform(valueSchema) ?? this.transform(kubb_kit.ast.factory.createSchema({ type: "unknown" }));
1422
+ return valueSchema.nullable ? `z.nullable(${valueType})` : valueType;
1423
+ });
1424
+ const distinct = [...new Set(values)];
1425
+ const value = distinct.length === 1 ? distinct[0] : `z.union([${distinct.join(", ")}])`;
1426
+ if (entries.length > 0) return `z.catchall(${objectBase}, ${value})`;
1427
+ return `z.record(${patternKeySchemaMini({
1428
+ patterns: patterns.map(([pattern]) => pattern),
1429
+ regexType: this.options.regexType
1430
+ })}, ${value})`;
1431
+ }
1432
+ return objectBase;
1433
+ },
1434
+ array(node) {
1435
+ const base = `z.array(${mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean).join(", ") || this.transform(kubb_kit.ast.factory.createSchema({ type: "unknown" }))})${lengthChecksMini({
1436
+ ...node,
1437
+ regexType: this.options.regexType
1438
+ })}`;
1439
+ return node.unique ? `${base}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })` : base;
1440
+ },
1441
+ tuple(node) {
1442
+ return `z.tuple(${buildList(mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean))})`;
1443
+ },
1444
+ union(node) {
1445
+ const nodeMembers = node.members ?? [];
1446
+ const members = mapSchemaMembers(node, (memberNode) => this.transform(memberNode)).map(({ schema, output }) => output && node.strategy === "one" ? strictOneOfMember(output, schema) : output).filter(Boolean);
1447
+ if (members.length === 0) return "";
1448
+ if (members.length === 1) return members[0];
1449
+ const allDiscriminable = nodeMembers.every((m) => isObjectSchemaNode(m, cyclicSchemaNames));
1450
+ if (node.discriminatorPropertyName && allDiscriminable) return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, ${buildList(members)})`;
1451
+ return `z.union(${buildList(members)})`;
1452
+ },
1453
+ intersection(node) {
1454
+ const members = node.members ?? [];
1455
+ if (members.length === 0) return "";
1456
+ const [first, ...rest] = members;
1457
+ if (!first) return "";
1458
+ const firstBase = this.transform(first);
1459
+ if (!firstBase) return "";
1460
+ if (rest.length > 0 && isObjectComposableIntersection(node, cyclicSchemaNames)) return rest.reduce((acc, member) => `z.extend(${acc}, ${buildZodMiniObjectShape(this, member)})`, firstBase);
1461
+ return rest.reduce((acc, member) => {
1462
+ const constraint = getMemberConstraintMini({
1463
+ member,
1464
+ regexType: this.options.regexType
1465
+ });
1466
+ if (constraint) return acc + constraint;
1467
+ const transformed = this.transform(member);
1468
+ return transformed ? `z.intersection(${acc}, ${transformed})` : acc;
1469
+ }, firstBase);
1470
+ }
123
1471
  },
124
- async install() {
125
- const root = node_path.default.resolve(this.config.root, this.config.output.path);
126
- const mode = (0, _kubb_core.getMode)(node_path.default.resolve(root, output.path));
127
- const oas = await this.getOas();
128
- if (this.plugin.options.typed && this.plugin.options.version === "3") await this.addFile({
129
- baseName: "ToZod.ts",
130
- path: node_path.default.resolve(root, ".kubb/ToZod.ts"),
131
- sources: [{
132
- name: "ToZod",
133
- value: require_templates_ToZod_source.source
134
- }],
135
- imports: [],
136
- exports: []
1472
+ overrides: options.nodes,
1473
+ print(node) {
1474
+ const { keysToOmit } = this.options;
1475
+ const transformed = this.transform(node);
1476
+ if (!transformed) return null;
1477
+ const meta = (0, kubb_kit.syncSchemaRef)(node);
1478
+ return applyMiniModifiers({
1479
+ value: (() => {
1480
+ if (!keysToOmit?.length || meta.primitive !== "object" || meta.type === "union" && meta.discriminatorPropertyName) return transformed;
1481
+ const unwrap = omitUnwrapChain(node);
1482
+ const omit = `.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} })`;
1483
+ const lazyMatch = transformed.match(/^z\.lazy\(\(\)\s*=>\s*(.+)\)$/);
1484
+ if (lazyMatch) return `z.lazy(() => ${lazyMatch[1]}${unwrap}${omit})`;
1485
+ return `${transformed}${unwrap}${omit}`;
1486
+ })(),
1487
+ schema: node,
1488
+ nullable: meta.nullable,
1489
+ optional: meta.optional,
1490
+ nullish: meta.nullish,
1491
+ defaultValue: meta.default
137
1492
  });
138
- const schemaFiles = await new _kubb_plugin_oas.SchemaGenerator(this.plugin.options, {
139
- fabric: this.fabric,
140
- oas,
141
- driver: this.driver,
142
- events: this.events,
143
- plugin: this.plugin,
144
- contentType,
145
- include: void 0,
146
- override,
147
- mode,
148
- output: output.path
149
- }).build(...generators);
150
- await this.upsertFile(...schemaFiles);
151
- const operationFiles = await new _kubb_plugin_oas.OperationGenerator(this.plugin.options, {
152
- fabric: this.fabric,
153
- oas,
154
- driver: this.driver,
155
- events: this.events,
156
- plugin: this.plugin,
157
- contentType,
158
- exclude,
159
- include,
160
- override,
161
- mode
162
- }).build(...generators);
163
- await this.upsertFile(...operationFiles);
164
- const barrelFiles = await (0, _kubb_core.getBarrelFiles)(this.fabric.files, {
165
- type: output.barrelType ?? "named",
1493
+ }
1494
+ };
1495
+ });
1496
+ //#endregion
1497
+ //#region src/generators/zodGenerator.tsx
1498
+ const zodPrinterCache = /* @__PURE__ */ new WeakMap();
1499
+ const zodMiniPrinterCache = /* @__PURE__ */ new WeakMap();
1500
+ /**
1501
+ * Returns the cached `output`/`input` direction printers for a resolver, building them on
1502
+ * first use. The `input` printer encodes `Date → string` for request bodies, and `output` decodes
1503
+ * `string → Date` for responses. Schemas without `dateType: 'date'` fields print identically.
1504
+ */
1505
+ function getStdPrinters(resolver, params) {
1506
+ const cached = zodPrinterCache.get(resolver);
1507
+ if (cached && cached.coercion === params.coercion && cached.guidType === params.guidType && cached.regexType === params.regexType && cached.dateType === params.dateType && cached.nodes === params.nodes) return {
1508
+ output: cached.output,
1509
+ input: cached.input
1510
+ };
1511
+ const output = printerZod({
1512
+ ...params,
1513
+ resolver,
1514
+ direction: "output"
1515
+ });
1516
+ const input = printerZod({
1517
+ ...params,
1518
+ resolver,
1519
+ direction: "input"
1520
+ });
1521
+ zodPrinterCache.set(resolver, {
1522
+ output,
1523
+ input,
1524
+ coercion: params.coercion,
1525
+ guidType: params.guidType,
1526
+ regexType: params.regexType,
1527
+ dateType: params.dateType,
1528
+ nodes: params.nodes
1529
+ });
1530
+ return {
1531
+ output,
1532
+ input
1533
+ };
1534
+ }
1535
+ function getMiniPrinter(resolver, params) {
1536
+ const cached = zodMiniPrinterCache.get(resolver);
1537
+ if (cached && cached.guidType === params.guidType && cached.regexType === params.regexType && cached.nodes === params.nodes) return cached.printer;
1538
+ const p = printerZodMini({
1539
+ ...params,
1540
+ resolver
1541
+ });
1542
+ zodMiniPrinterCache.set(resolver, {
1543
+ printer: p,
1544
+ guidType: params.guidType,
1545
+ regexType: params.regexType,
1546
+ nodes: params.nodes
1547
+ });
1548
+ return p;
1549
+ }
1550
+ /**
1551
+ * Built-in generator for `@kubb/plugin-zod`. Emits one Zod schema per
1552
+ * schema in the spec plus per-operation request/response/parameter schemas.
1553
+ * When `mini: true`, schemas use the Zod Mini functional API instead of
1554
+ * chainable methods.
1555
+ */
1556
+ const zodGenerator = (0, kubb_kit.defineGenerator)({
1557
+ name: "zod",
1558
+ renderer: kubb_jsx.jsxRenderer,
1559
+ schema(node, ctx) {
1560
+ const { adapter, config, resolver, root } = ctx;
1561
+ const { output, coercion, guidType, regexType, mini, inferred, importPath, group, printer } = ctx.options;
1562
+ const dateType = getOasAdapter(adapter).options.dateType;
1563
+ if (!node.name) return;
1564
+ const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath);
1565
+ const cyclicSchemas = new Set(ctx.meta.circularNames);
1566
+ const hasCodec = !mini && containsCodec(node);
1567
+ const codecRefNames = new Set(hasCodec ? collectCodecRefNames(node) : []);
1568
+ const importEntries = resolver.imports({
1569
+ node,
1570
+ root,
1571
+ output,
1572
+ group: group ?? void 0
1573
+ });
1574
+ const inputImportEntries = hasCodec ? [...codecRefNames].map((schemaName) => ({
1575
+ name: [resolver.schema.inputName(schemaName)],
1576
+ path: resolver.file({
1577
+ name: schemaName,
1578
+ extname: ".ts",
166
1579
  root,
167
1580
  output,
168
- meta: { pluginName: this.plugin.name }
1581
+ group: group ?? void 0
1582
+ }).path
1583
+ })) : [];
1584
+ const seenImports = /* @__PURE__ */ new Set();
1585
+ const imports = [...importEntries, ...inputImportEntries].filter((imp) => {
1586
+ const key = `${Array.isArray(imp.name) ? imp.name.join(",") : imp.name}|${imp.path}`;
1587
+ if (seenImports.has(key)) return false;
1588
+ seenImports.add(key);
1589
+ return true;
1590
+ });
1591
+ const meta = {
1592
+ name: resolver.name(node.name),
1593
+ file: resolver.file({
1594
+ name: node.name,
1595
+ extname: ".ts",
1596
+ root,
1597
+ output,
1598
+ group: group ?? void 0
1599
+ })
1600
+ };
1601
+ const inferTypeName = inferred ? resolver.schema.typeName(node.name) : null;
1602
+ const stdPrinters = mini ? null : getStdPrinters(resolver, {
1603
+ coercion,
1604
+ guidType,
1605
+ regexType,
1606
+ dateType,
1607
+ cyclicSchemas,
1608
+ nodes: printer?.nodes
1609
+ });
1610
+ const schemaPrinter = mini ? getMiniPrinter(resolver, {
1611
+ guidType,
1612
+ regexType,
1613
+ cyclicSchemas,
1614
+ nodes: printer?.nodes
1615
+ }) : stdPrinters.output;
1616
+ return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx.File, {
1617
+ baseName: meta.file.baseName,
1618
+ path: meta.file.path,
1619
+ meta: meta.file.meta,
1620
+ banner: resolver.default.banner(ctx.meta, {
1621
+ output,
1622
+ config,
1623
+ file: {
1624
+ path: meta.file.path,
1625
+ baseName: meta.file.baseName
1626
+ }
1627
+ }),
1628
+ footer: resolver.default.footer(ctx.meta, {
1629
+ output,
1630
+ config,
1631
+ file: {
1632
+ path: meta.file.path,
1633
+ baseName: meta.file.baseName
1634
+ }
1635
+ }),
1636
+ children: [
1637
+ /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
1638
+ name: isZodImport ? "z" : ["z"],
1639
+ path: importPath,
1640
+ isNameSpace: isZodImport
1641
+ }),
1642
+ imports.map((imp) => /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
1643
+ root: meta.file.path,
1644
+ path: imp.path,
1645
+ name: imp.name
1646
+ }, [
1647
+ node.name,
1648
+ imp.path,
1649
+ imp.name
1650
+ ].join("-"))),
1651
+ /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(Zod, {
1652
+ name: meta.name,
1653
+ node,
1654
+ printer: schemaPrinter,
1655
+ inferTypeName,
1656
+ cyclic: cyclicSchemas.has(node.name)
1657
+ }),
1658
+ hasCodec && stdPrinters && /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(Zod, {
1659
+ name: resolver.schema.inputName(node.name),
1660
+ node,
1661
+ printer: stdPrinters.input,
1662
+ inferTypeName: inferred ? resolver.schema.inputTypeName(node.name) : null,
1663
+ cyclic: cyclicSchemas.has(node.name)
1664
+ })
1665
+ ]
1666
+ });
1667
+ },
1668
+ operation(node, ctx) {
1669
+ if (!kubb_kit.ast.isHttpOperationNode(node)) return null;
1670
+ const { adapter, config, resolver, root } = ctx;
1671
+ const { output, coercion, guidType, regexType, mini, inferred, importPath, group, printer } = ctx.options;
1672
+ const dateType = getOasAdapter(adapter).options.dateType;
1673
+ const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath);
1674
+ const meta = { file: resolver.file({
1675
+ name: node.operationId,
1676
+ extname: ".ts",
1677
+ tag: node.tags[0] ?? "default",
1678
+ path: node.path,
1679
+ root,
1680
+ output,
1681
+ group: group ?? void 0
1682
+ }) };
1683
+ const cyclicSchemas = new Set(ctx.meta.circularNames);
1684
+ function renderSchemaEntry({ schema, name, keysToOmit, direction = "output" }) {
1685
+ if (!schema) return null;
1686
+ const inferTypeName = inferred ? resolver.schema.type(name) : null;
1687
+ const codecRefNames = direction === "input" && !mini ? new Set(collectCodecRefNames(schema)) : null;
1688
+ const imports = resolver.imports({
1689
+ node: schema,
1690
+ root,
1691
+ output,
1692
+ group: group ?? void 0,
1693
+ name: (schemaName) => codecRefNames?.has(schemaName) ? resolver.schema.inputName(schemaName) : resolver.name(schemaName)
1694
+ });
1695
+ const schemaPrinter = mini ? keysToOmit?.length ? printerZodMini({
1696
+ guidType,
1697
+ regexType,
1698
+ resolver,
1699
+ keysToOmit,
1700
+ cyclicSchemas,
1701
+ nodes: printer?.nodes
1702
+ }) : getMiniPrinter(resolver, {
1703
+ guidType,
1704
+ regexType,
1705
+ cyclicSchemas,
1706
+ nodes: printer?.nodes
1707
+ }) : keysToOmit?.length ? printerZod({
1708
+ coercion,
1709
+ guidType,
1710
+ regexType,
1711
+ dateType,
1712
+ resolver,
1713
+ keysToOmit,
1714
+ cyclicSchemas,
1715
+ nodes: printer?.nodes,
1716
+ direction
1717
+ }) : getStdPrinters(resolver, {
1718
+ coercion,
1719
+ guidType,
1720
+ regexType,
1721
+ dateType,
1722
+ cyclicSchemas,
1723
+ nodes: printer?.nodes
1724
+ })[direction];
1725
+ return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx_jsx_runtime.Fragment, { children: [imports.map((imp) => /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
1726
+ root: meta.file.path,
1727
+ path: imp.path,
1728
+ name: imp.name
1729
+ }, [
1730
+ name,
1731
+ imp.path,
1732
+ imp.name
1733
+ ].join("-"))), /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(Zod, {
1734
+ name,
1735
+ node: schema,
1736
+ printer: schemaPrinter,
1737
+ inferTypeName,
1738
+ cyclic: false
1739
+ })] });
1740
+ }
1741
+ function buildContentTypeVariants(entries, baseName, decorate, direction) {
1742
+ const variants = resolveContentTypeVariants(entries, baseName);
1743
+ const unionSchema = kubb_kit.ast.factory.createSchema({
1744
+ type: "union",
1745
+ members: variants.map((variant) => kubb_kit.ast.factory.createSchema({
1746
+ type: "ref",
1747
+ name: variant.name
1748
+ }))
1749
+ });
1750
+ return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx_jsx_runtime.Fragment, { children: [variants.map((variant) => renderSchemaEntry({
1751
+ schema: decorate ? decorate(variant.schema) : variant.schema,
1752
+ name: variant.name,
1753
+ keysToOmit: variant.keysToOmit,
1754
+ direction
1755
+ })), renderSchemaEntry({
1756
+ schema: unionSchema,
1757
+ name: baseName,
1758
+ direction
1759
+ })] });
1760
+ }
1761
+ function buildResponseUnion({ responses, name, fallbackUnknown }) {
1762
+ if (new Set(responses.flatMap((res) => (res.content ?? []).flatMap((entry) => entry.schema ? collectRefNames(entry.schema).map((refName) => resolver.name(refName)) : []))).has(name)) return null;
1763
+ const members = responses.map((res) => kubb_kit.ast.factory.createSchema({
1764
+ type: "ref",
1765
+ name: resolver.response.status(node, res.statusCode)
1766
+ }));
1767
+ if (fallbackUnknown && members.length === 0) return renderSchemaEntry({
1768
+ schema: kubb_kit.ast.factory.createSchema({ type: "unknown" }),
1769
+ name
1770
+ });
1771
+ return renderSchemaEntry({
1772
+ schema: members.length === 1 ? members[0] : kubb_kit.ast.factory.createSchema({
1773
+ type: "union",
1774
+ members
1775
+ }),
1776
+ name
1777
+ });
1778
+ }
1779
+ const paramSchemas = node.parameters.map((param) => renderSchemaEntry({
1780
+ schema: param.schema,
1781
+ name: resolver.param.name(node, param),
1782
+ direction: "input"
1783
+ }));
1784
+ const responseSchemas = node.responses.map((res) => {
1785
+ const variants = (res.content ?? []).filter((entry) => entry.schema);
1786
+ if (variants.length > 1) return buildContentTypeVariants(res.content, resolver.response.status(node, res.statusCode));
1787
+ const primary = variants[0] ?? res.content?.[0];
1788
+ return renderSchemaEntry({
1789
+ schema: primary?.schema ?? null,
1790
+ name: resolver.response.status(node, res.statusCode),
1791
+ keysToOmit: primary?.keysToOmit
169
1792
  });
170
- await this.upsertFile(...barrelFiles);
1793
+ });
1794
+ const responsesWithSchema = node.responses.filter((res) => res.content?.some((entry) => entry.schema));
1795
+ const successResponsesWithSchema = getSuccessResponses(responsesWithSchema);
1796
+ const responseUnionSchema = responsesWithSchema.length > 0 ? buildResponseUnion({
1797
+ responses: successResponsesWithSchema,
1798
+ name: resolver.response.response(node),
1799
+ fallbackUnknown: true
1800
+ }) : null;
1801
+ const errorResponsesWithSchema = responsesWithSchema.filter((res) => !isSuccessStatusCode(res.statusCode));
1802
+ const errorUnionSchema = errorResponsesWithSchema.length > 0 ? buildResponseUnion({
1803
+ responses: errorResponsesWithSchema,
1804
+ name: resolver.response.error(node),
1805
+ fallbackUnknown: false
1806
+ }) : null;
1807
+ const requestBodyContent = node.requestBody?.content ?? [];
1808
+ const requestSchema = (() => {
1809
+ if (requestBodyContent.length === 0) return null;
1810
+ if (requestBodyContent.length === 1) {
1811
+ const entry = requestBodyContent[0];
1812
+ if (!entry.schema) return null;
1813
+ return renderSchemaEntry({
1814
+ schema: {
1815
+ ...entry.schema,
1816
+ description: node.requestBody.description ?? entry.schema.description
1817
+ },
1818
+ name: resolver.response.body(node),
1819
+ keysToOmit: entry.keysToOmit,
1820
+ direction: "input"
1821
+ });
1822
+ }
1823
+ return buildContentTypeVariants(requestBodyContent, resolver.response.body(node), (schema) => ({
1824
+ ...schema,
1825
+ description: node.requestBody.description ?? schema.description
1826
+ }), "input");
1827
+ })();
1828
+ const { path, query, header } = getOperationParameters(node);
1829
+ const paramGroupSchemas = inferred ? [
1830
+ {
1831
+ kind: "path",
1832
+ params: path
1833
+ },
1834
+ {
1835
+ kind: "query",
1836
+ params: query
1837
+ },
1838
+ {
1839
+ kind: "headers",
1840
+ params: header
1841
+ }
1842
+ ].filter(({ params }) => params.length > 0).map(({ kind, params }) => renderSchemaEntry({
1843
+ schema: buildGroupedParamsSchema({ params }),
1844
+ name: resolver.param[kind](node, params[0]),
1845
+ direction: "input"
1846
+ })) : [];
1847
+ const optionsSchema = inferred ? renderSchemaEntry({
1848
+ schema: buildOptionsSchema(node, resolver),
1849
+ name: resolver.name(`${node.operationId} Options`),
1850
+ direction: "input"
1851
+ }) : null;
1852
+ return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx.File, {
1853
+ baseName: meta.file.baseName,
1854
+ path: meta.file.path,
1855
+ meta: meta.file.meta,
1856
+ banner: resolver.default.banner(ctx.meta, {
1857
+ output,
1858
+ config,
1859
+ file: {
1860
+ path: meta.file.path,
1861
+ baseName: meta.file.baseName
1862
+ }
1863
+ }),
1864
+ footer: resolver.default.footer(ctx.meta, {
1865
+ output,
1866
+ config,
1867
+ file: {
1868
+ path: meta.file.path,
1869
+ baseName: meta.file.baseName
1870
+ }
1871
+ }),
1872
+ children: [
1873
+ /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
1874
+ name: isZodImport ? "z" : ["z"],
1875
+ path: importPath,
1876
+ isNameSpace: isZodImport
1877
+ }),
1878
+ paramSchemas,
1879
+ responseSchemas,
1880
+ responseUnionSchema,
1881
+ errorUnionSchema,
1882
+ requestSchema,
1883
+ paramGroupSchemas,
1884
+ optionsSchema
1885
+ ]
1886
+ });
1887
+ }
1888
+ });
1889
+ //#endregion
1890
+ //#region src/resolvers/resolverZod.ts
1891
+ /**
1892
+ * Default resolver used by `@kubb/plugin-zod`. Decides the names and file
1893
+ * paths for every generated Zod schema. Schemas use camelCase with a
1894
+ * `Schema` suffix (`listPetsSchema`); their inferred types use PascalCase
1895
+ * with a `SchemaType` suffix (`PetSchemaType`), so the value and the type
1896
+ * never share an identifier even when the schema name is all-uppercase.
1897
+ *
1898
+ * @example Resolve schema and type names
1899
+ * ```ts
1900
+ * import { resolverZod } from '@kubb/plugin-zod'
1901
+ *
1902
+ * resolverZod.name('list pets') // 'listPetsSchema'
1903
+ * resolverZod.schema.typeName('pet') // 'PetSchemaType'
1904
+ * ```
1905
+ */
1906
+ const resolverZod = (0, kubb_kit.createResolver)({
1907
+ pluginName: "plugin-zod",
1908
+ name(name) {
1909
+ return ensureValidVarName(camelCase(name, { suffix: "schema" }));
1910
+ },
1911
+ file: createCasedFile((part) => camelCase(part, { suffix: "schema" })),
1912
+ schema: {
1913
+ typeName(name) {
1914
+ return ensureValidVarName(pascalCase(name, { suffix: "schema type" }));
1915
+ },
1916
+ type(name) {
1917
+ return ensureValidVarName(pascalCase(name, { suffix: "type" }));
1918
+ },
1919
+ inputName(name) {
1920
+ return this.name(`${name} input`);
1921
+ },
1922
+ inputTypeName(name) {
1923
+ return this.schema.typeName(`${name} input`);
171
1924
  }
1925
+ },
1926
+ param: createOperationParamResolver(),
1927
+ response: {
1928
+ ...createOperationResponseResolver(),
1929
+ error(node) {
1930
+ return this.name(`${node.operationId} Error`);
1931
+ },
1932
+ options(node) {
1933
+ return this.schema.type(this.name(`${node.operationId} Options`));
1934
+ }
1935
+ }
1936
+ });
1937
+ //#endregion
1938
+ //#region src/plugin.ts
1939
+ /**
1940
+ * Canonical plugin name for `@kubb/plugin-zod`. Used for driver lookups and
1941
+ * cross-plugin dependency references.
1942
+ */
1943
+ const pluginZodName = "plugin-zod";
1944
+ /**
1945
+ * Generates Zod v4 schemas from an OpenAPI spec. Use them to validate API
1946
+ * responses at runtime, build form schemas, or feed back into router libraries
1947
+ * that consume Zod (tRPC, Hono, Elysia). Pair with `@kubb/plugin-axios` or
1948
+ * `@kubb/plugin-fetch` and set the client's `validator: 'zod'` to validate every response
1949
+ * automatically.
1950
+ *
1951
+ * @example
1952
+ * ```ts
1953
+ * import { defineConfig } from 'kubb/config'
1954
+ * import { pluginTs } from '@kubb/plugin-ts'
1955
+ * import { pluginZod } from '@kubb/plugin-zod'
1956
+ *
1957
+ * export default defineConfig({
1958
+ * input: './petStore.yaml',
1959
+ * output: { path: './src/gen' },
1960
+ * plugins: [
1961
+ * pluginTs(),
1962
+ * pluginZod({
1963
+ * output: { path: './zod' },
1964
+ * inferred: true,
1965
+ * }),
1966
+ * ],
1967
+ * })
1968
+ * ```
1969
+ */
1970
+ const pluginZod = (0, kubb_kit.definePlugin)((options) => {
1971
+ const { output = {
1972
+ path: "zod",
1973
+ barrel: { type: "named" }
1974
+ }, group, exclude = [], include, override = [], mini = false, guidType = "uuid", regexType = "literal", importPath = mini ? "zod/mini" : "zod", coercion = false, inferred = false, printer, resolver: userResolver, macros: userMacros } = options;
1975
+ const groupConfig = createGroupConfig(group);
1976
+ return {
1977
+ name: pluginZodName,
1978
+ options,
1979
+ hooks: { "kubb:plugin:setup"(ctx) {
1980
+ ctx.setOptions({
1981
+ output,
1982
+ exclude,
1983
+ include,
1984
+ override,
1985
+ group: groupConfig,
1986
+ importPath,
1987
+ coercion,
1988
+ inferred,
1989
+ guidType,
1990
+ regexType,
1991
+ mini,
1992
+ printer
1993
+ });
1994
+ ctx.setResolver(userResolver ? kubb_kit.Resolver.merge(resolverZod, userResolver) : resolverZod);
1995
+ if (userMacros?.length) ctx.setMacros(userMacros);
1996
+ ctx.addGenerator(zodGenerator);
1997
+ } }
172
1998
  };
173
1999
  });
174
2000
  //#endregion
2001
+ exports.default = pluginZod;
175
2002
  exports.pluginZod = pluginZod;
176
2003
  exports.pluginZodName = pluginZodName;
2004
+ exports.printerZod = printerZod;
2005
+ exports.printerZodMini = printerZodMini;
2006
+ exports.resolverZod = resolverZod;
2007
+ exports.zodGenerator = zodGenerator;
177
2008
 
178
2009
  //# sourceMappingURL=index.cjs.map