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