@kubb/plugin-zod 5.0.0-beta.3 → 5.0.0-beta.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import "./chunk--u3MIqq1.js";
1
+ import { t as __name } from "./chunk--u3MIqq1.js";
2
2
  import { ast, defineGenerator, definePlugin, defineResolver } from "@kubb/core";
3
- import { Const, File, Type, jsxRenderer } from "@kubb/renderer-jsx";
3
+ import { Const, File, Type, jsxRendererSync } from "@kubb/renderer-jsx";
4
4
  import { Fragment, jsx, jsxs } from "@kubb/renderer-jsx/jsx-runtime";
5
5
  //#region ../../internals/utils/src/casing.ts
6
6
  /**
@@ -126,6 +126,129 @@ function toRegExpString(text, func = "RegExp") {
126
126
  return `new ${func}(${JSON.stringify(source)}${flags ? `, ${JSON.stringify(flags)}` : ""})`;
127
127
  }
128
128
  //#endregion
129
+ //#region ../../internals/utils/src/reserved.ts
130
+ /**
131
+ * JavaScript and Java reserved words.
132
+ * @link https://github.com/jonschlinkert/reserved/blob/master/index.js
133
+ */
134
+ const reservedWords = new Set([
135
+ "abstract",
136
+ "arguments",
137
+ "boolean",
138
+ "break",
139
+ "byte",
140
+ "case",
141
+ "catch",
142
+ "char",
143
+ "class",
144
+ "const",
145
+ "continue",
146
+ "debugger",
147
+ "default",
148
+ "delete",
149
+ "do",
150
+ "double",
151
+ "else",
152
+ "enum",
153
+ "eval",
154
+ "export",
155
+ "extends",
156
+ "false",
157
+ "final",
158
+ "finally",
159
+ "float",
160
+ "for",
161
+ "function",
162
+ "goto",
163
+ "if",
164
+ "implements",
165
+ "import",
166
+ "in",
167
+ "instanceof",
168
+ "int",
169
+ "interface",
170
+ "let",
171
+ "long",
172
+ "native",
173
+ "new",
174
+ "null",
175
+ "package",
176
+ "private",
177
+ "protected",
178
+ "public",
179
+ "return",
180
+ "short",
181
+ "static",
182
+ "super",
183
+ "switch",
184
+ "synchronized",
185
+ "this",
186
+ "throw",
187
+ "throws",
188
+ "transient",
189
+ "true",
190
+ "try",
191
+ "typeof",
192
+ "var",
193
+ "void",
194
+ "volatile",
195
+ "while",
196
+ "with",
197
+ "yield",
198
+ "Array",
199
+ "Date",
200
+ "hasOwnProperty",
201
+ "Infinity",
202
+ "isFinite",
203
+ "isNaN",
204
+ "isPrototypeOf",
205
+ "length",
206
+ "Math",
207
+ "name",
208
+ "NaN",
209
+ "Number",
210
+ "Object",
211
+ "prototype",
212
+ "String",
213
+ "toString",
214
+ "undefined",
215
+ "valueOf"
216
+ ]);
217
+ /**
218
+ * Returns `true` when `name` is a syntactically valid JavaScript variable name.
219
+ *
220
+ * @example
221
+ * ```ts
222
+ * isValidVarName('status') // true
223
+ * isValidVarName('class') // false (reserved word)
224
+ * isValidVarName('42foo') // false (starts with digit)
225
+ * ```
226
+ */
227
+ function isValidVarName(name) {
228
+ if (!name || reservedWords.has(name)) return false;
229
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
230
+ }
231
+ /**
232
+ * Returns `name` when it's a syntactically valid JavaScript variable name,
233
+ * otherwise prefixes it with `_` so the result is a valid identifier.
234
+ *
235
+ * Useful for sanitizing OpenAPI schema names or operation IDs that start with
236
+ * a digit (e.g. `409`, `504AccountCancel`) before using them as exported
237
+ * variable, type, or function names.
238
+ *
239
+ * @example
240
+ * ```ts
241
+ * ensureValidVarName('409') // '_409'
242
+ * ensureValidVarName('504AccountCancel') // '_504AccountCancel'
243
+ * ensureValidVarName('Pet') // 'Pet'
244
+ * ensureValidVarName('class') // '_class'
245
+ * ```
246
+ */
247
+ function ensureValidVarName(name) {
248
+ if (!name || isValidVarName(name)) return name;
249
+ return `_${name}`;
250
+ }
251
+ //#endregion
129
252
  //#region src/components/Operations.tsx
130
253
  function Operations({ name, operations }) {
131
254
  const operationsJSON = operations.reduce((prev, acc) => {
@@ -261,11 +384,11 @@ function buildSchemaNames(node, { params, resolver }) {
261
384
  }
262
385
  responses["default"] = resolver.resolveResponseName(node);
263
386
  return {
264
- request: node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : void 0,
387
+ request: node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null,
265
388
  parameters: {
266
- path: pathParam ? resolver.resolvePathParamsName(node, pathParam) : void 0,
267
- query: queryParam ? resolver.resolveQueryParamsName(node, queryParam) : void 0,
268
- header: headerParam ? resolver.resolveHeaderParamsName(node, headerParam) : void 0
389
+ path: pathParam ? resolver.resolvePathParamsName(node, pathParam) : null,
390
+ query: queryParam ? resolver.resolveQueryParamsName(node, queryParam) : null,
391
+ header: headerParam ? resolver.resolveHeaderParamsName(node, headerParam) : null
269
392
  },
270
393
  responses,
271
394
  errors
@@ -339,30 +462,44 @@ function lengthChecksMini({ min, max, pattern }) {
339
462
  * to a schema value string using the chainable Zod v4 API.
340
463
  */
341
464
  function applyModifiers({ value, nullable, optional, nullish, defaultValue, description }) {
342
- let result = value;
343
- if (nullish || nullable && optional) result = `${result}.nullish()`;
344
- else if (optional) result = `${result}.optional()`;
345
- else if (nullable) result = `${result}.nullable()`;
346
- if (defaultValue !== void 0) result = `${result}.default(${formatDefault(defaultValue)})`;
347
- if (description) result = `${result}.describe(${stringify(description)})`;
348
- return result;
465
+ const withModifier = (() => {
466
+ if (nullish || nullable && optional) return `${value}.nullish()`;
467
+ if (optional) return `${value}.optional()`;
468
+ if (nullable) return `${value}.nullable()`;
469
+ return value;
470
+ })();
471
+ const withDefault = defaultValue !== void 0 ? `${withModifier}.default(${formatDefault(defaultValue)})` : withModifier;
472
+ return description ? `${withDefault}.describe(${stringify(description)})` : withDefault;
349
473
  }
350
474
  /**
351
475
  * Apply nullable / optional / nullish modifiers using the functional `zod/mini` API
352
476
  * (`z.nullable()`, `z.optional()`, `z.nullish()`).
353
477
  */
354
478
  function applyMiniModifiers({ value, nullable, optional, nullish, defaultValue }) {
355
- let result = value;
356
- if (nullish) result = `z.nullish(${result})`;
357
- else {
358
- if (nullable) result = `z.nullable(${result})`;
359
- if (optional) result = `z.optional(${result})`;
360
- }
361
- if (defaultValue !== void 0) result = `z._default(${result}, ${formatDefault(defaultValue)})`;
362
- return result;
479
+ const withModifier = (() => {
480
+ if (nullish) return `z.nullish(${value})`;
481
+ const withNullable = nullable ? `z.nullable(${value})` : value;
482
+ return optional ? `z.optional(${withNullable})` : withNullable;
483
+ })();
484
+ return defaultValue !== void 0 ? `z._default(${withModifier}, ${formatDefault(defaultValue)})` : withModifier;
363
485
  }
364
486
  //#endregion
365
487
  //#region src/printers/printerZod.ts
488
+ function strictOneOfMember$1(member, node) {
489
+ if (node.type === "object" && node.additionalProperties === void 0) return `${member}.strict()`;
490
+ if (node.type === "ref") {
491
+ if (member.startsWith("z.lazy(")) return member;
492
+ const schema = ast.syncSchemaRef(node);
493
+ if (schema.type === "object" && (schema.additionalProperties === void 0 || schema.additionalProperties === false)) return `${member}.strict()`;
494
+ }
495
+ return member;
496
+ }
497
+ __name(strictOneOfMember$1, "strictOneOfMember");
498
+ function getMemberConstraint(member) {
499
+ if (member.primitive === "string") return lengthConstraints(ast.narrowSchema(member, "string") ?? {}) || void 0;
500
+ if (member.primitive === "number" || member.primitive === "integer") return numberConstraints(ast.narrowSchema(member, "number") ?? ast.narrowSchema(member, "integer") ?? {}) || void 0;
501
+ if (member.primitive === "array") return lengthConstraints(ast.narrowSchema(member, "array") ?? {}) || void 0;
502
+ }
366
503
  /**
367
504
  * Zod v4 printer built with `definePrinter`.
368
505
  *
@@ -435,29 +572,29 @@ const printerZod = ast.definePrinter((options) => {
435
572
  return `z.enum([${nonNullValues.map(formatLiteral).join(", ")}])`;
436
573
  },
437
574
  ref(node) {
438
- if (!node.name) return void 0;
575
+ if (!node.name) return null;
439
576
  const refName = node.ref ? ast.extractRefName(node.ref) ?? node.name : node.name;
440
577
  const resolvedName = node.ref ? this.options.resolver?.default(refName, "function") ?? refName : node.name;
441
578
  if (node.ref && this.options.cyclicSchemas?.has(refName)) return `z.lazy(() => ${resolvedName})`;
442
579
  return resolvedName;
443
580
  },
444
581
  object(node) {
445
- let result = `z.object({\n ${node.properties.map((prop) => {
582
+ const objectBase = `z.object({\n ${node.properties.map((prop) => {
446
583
  const { name: propName, schema } = prop;
447
584
  const meta = ast.syncSchemaRef(schema);
448
585
  const isNullable = meta.nullable;
449
586
  const isOptional = schema.optional;
450
587
  const isNullish = schema.nullish;
451
588
  const hasSelfRef = this.options.cyclicSchemas != null && ast.containsCircularRef(schema, { circularSchemas: this.options.cyclicSchemas });
589
+ const savedCyclicSchemas = this.options.cyclicSchemas;
452
590
  if (hasSelfRef) this.options.cyclicSchemas = void 0;
453
591
  const baseOutput = this.transform(schema) ?? this.transform(ast.createSchema({ type: "unknown" }));
454
- if (hasSelfRef) this.options.cyclicSchemas = options.cyclicSchemas;
592
+ if (hasSelfRef) this.options.cyclicSchemas = savedCyclicSchemas;
455
593
  const wrappedOutput = this.options.wrapOutput ? this.options.wrapOutput({
456
594
  output: baseOutput,
457
595
  schema
458
596
  }) || baseOutput : baseOutput;
459
- let descriptionToApply = meta.description;
460
- if (schema.type !== "ref" && meta.type === "ref") descriptionToApply = void 0;
597
+ const descriptionToApply = schema.type !== "ref" && meta.type === "ref" ? void 0 : meta.description;
461
598
  const value = applyModifiers({
462
599
  value: wrappedOutput,
463
600
  nullable: isNullable,
@@ -469,24 +606,29 @@ const printerZod = ast.definePrinter((options) => {
469
606
  if (hasSelfRef) return `get "${propName}"() { return ${value} }`;
470
607
  return `"${propName}": ${value}`;
471
608
  }).join(",\n ")}\n })`;
472
- if (node.additionalProperties && node.additionalProperties !== true) {
473
- const catchallType = this.transform(node.additionalProperties);
474
- if (catchallType) result += `.catchall(${catchallType})`;
475
- } else if (node.additionalProperties === true) result += `.catchall(${this.transform(ast.createSchema({ type: "unknown" }))})`;
476
- else if (node.additionalProperties === false) result += ".strict()";
477
- return result;
609
+ return (() => {
610
+ if (node.additionalProperties && node.additionalProperties !== true) {
611
+ const catchallType = this.transform(node.additionalProperties);
612
+ return catchallType ? `${objectBase}.catchall(${catchallType})` : objectBase;
613
+ }
614
+ if (node.additionalProperties === true) return `${objectBase}.catchall(${this.transform(ast.createSchema({ type: "unknown" }))})`;
615
+ if (node.additionalProperties === false) return `${objectBase}.strict()`;
616
+ return objectBase;
617
+ })();
478
618
  },
479
619
  array(node) {
480
- let result = `z.array(${(node.items ?? []).map((item) => this.transform(item)).filter(Boolean).join(", ") || this.transform(ast.createSchema({ type: "unknown" }))})${lengthConstraints(node)}`;
481
- if (node.unique) result += `.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })`;
482
- return result;
620
+ const base = `z.array(${(node.items ?? []).map((item) => this.transform(item)).filter(Boolean).join(", ") || this.transform(ast.createSchema({ type: "unknown" }))})${lengthConstraints(node)}`;
621
+ return node.unique ? `${base}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })` : base;
483
622
  },
484
623
  tuple(node) {
485
624
  return `z.tuple([${(node.items ?? []).map((item) => this.transform(item)).filter(Boolean).join(", ")}])`;
486
625
  },
487
626
  union(node) {
488
627
  const nodeMembers = node.members ?? [];
489
- const members = nodeMembers.map((m) => this.transform(m)).filter(Boolean);
628
+ const members = nodeMembers.map((memberNode) => {
629
+ const member = this.transform(memberNode);
630
+ return member && node.strategy === "one" ? strictOneOfMember$1(member, memberNode) : member;
631
+ }).filter(Boolean);
490
632
  if (members.length === 0) return "";
491
633
  if (members.length === 1) return members[0];
492
634
  if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === "intersection")) return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, [${members.join(", ")}])`;
@@ -497,47 +639,29 @@ const printerZod = ast.definePrinter((options) => {
497
639
  if (members.length === 0) return "";
498
640
  const [first, ...rest] = members;
499
641
  if (!first) return "";
500
- let base = this.transform(first);
501
- if (!base) return "";
502
- for (const member of rest) {
503
- if (member.primitive === "string") {
504
- const c = lengthConstraints(ast.narrowSchema(member, "string") ?? {});
505
- if (c) {
506
- base += c;
507
- continue;
508
- }
509
- } else if (member.primitive === "number" || member.primitive === "integer") {
510
- const c = numberConstraints(ast.narrowSchema(member, "number") ?? ast.narrowSchema(member, "integer") ?? {});
511
- if (c) {
512
- base += c;
513
- continue;
514
- }
515
- } else if (member.primitive === "array") {
516
- const c = lengthConstraints(ast.narrowSchema(member, "array") ?? {});
517
- if (c) {
518
- base += c;
519
- continue;
520
- }
521
- }
642
+ const firstBase = this.transform(first);
643
+ if (!firstBase) return "";
644
+ return rest.reduce((acc, member) => {
645
+ const constraint = getMemberConstraint(member);
646
+ if (constraint) return acc + constraint;
522
647
  const transformed = this.transform(member);
523
- if (transformed) base = `${base}.and(${transformed})`;
524
- }
525
- return base;
648
+ return transformed ? `${acc}.and(${transformed})` : acc;
649
+ }, firstBase);
526
650
  },
527
651
  ...options.nodes
528
652
  },
529
653
  print(node) {
530
654
  const { keysToOmit } = this.options;
531
- let base = this.transform(node);
532
- if (!base) return null;
655
+ const transformed = this.transform(node);
656
+ if (!transformed) return null;
533
657
  const meta = ast.syncSchemaRef(node);
534
- if (keysToOmit?.length && meta.primitive === "object" && !(meta.type === "union" && meta.discriminatorPropertyName)) {
535
- const lazyMatch = base.match(/^z\.lazy\(\(\)\s*=>\s*(.+)\)$/);
536
- if (lazyMatch) base = `z.lazy(() => ${lazyMatch[1]}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} }))`;
537
- else base = `${base}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} })`;
538
- }
539
658
  return applyModifiers({
540
- value: base,
659
+ value: (() => {
660
+ if (!keysToOmit?.length || meta.primitive !== "object" || meta.type === "union" && meta.discriminatorPropertyName) return transformed;
661
+ const lazyMatch = transformed.match(/^z\.lazy\(\(\)\s*=>\s*(.+)\)$/);
662
+ if (lazyMatch) return `z.lazy(() => ${lazyMatch[1]}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} }))`;
663
+ return `${transformed}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} })`;
664
+ })(),
541
665
  nullable: meta.nullable,
542
666
  optional: meta.optional,
543
667
  nullish: meta.nullish,
@@ -549,6 +673,15 @@ const printerZod = ast.definePrinter((options) => {
549
673
  });
550
674
  //#endregion
551
675
  //#region src/printers/printerZodMini.ts
676
+ function strictOneOfMember(member, node) {
677
+ if (node.type === "object" && (node.additionalProperties === void 0 || node.additionalProperties === false)) return member.replace(/^z\.object\(/, "z.strictObject(");
678
+ return member;
679
+ }
680
+ function getMemberConstraintMini(member) {
681
+ if (member.primitive === "string") return lengthChecksMini(ast.narrowSchema(member, "string") ?? {}) || void 0;
682
+ if (member.primitive === "number" || member.primitive === "integer") return numberChecksMini(ast.narrowSchema(member, "number") ?? ast.narrowSchema(member, "integer") ?? {}) || void 0;
683
+ if (member.primitive === "array") return lengthChecksMini(ast.narrowSchema(member, "array") ?? {}) || void 0;
684
+ }
552
685
  /**
553
686
  * Zod v4 **Mini** printer built with `definePrinter`.
554
687
  *
@@ -617,7 +750,7 @@ const printerZodMini = ast.definePrinter((options) => {
617
750
  return `z.enum([${nonNullValues.map(formatLiteral).join(", ")}])`;
618
751
  },
619
752
  ref(node) {
620
- if (!node.name) return void 0;
753
+ if (!node.name) return null;
621
754
  const refName = node.ref ? ast.extractRefName(node.ref) ?? node.name : node.name;
622
755
  const resolvedName = node.ref ? this.options.resolver?.default(refName, "function") ?? refName : node.name;
623
756
  if (node.ref && this.options.cyclicSchemas?.has(refName)) return `z.lazy(() => ${resolvedName})`;
@@ -631,9 +764,10 @@ const printerZodMini = ast.definePrinter((options) => {
631
764
  const isOptional = schema.optional;
632
765
  const isNullish = schema.nullish;
633
766
  const hasSelfRef = this.options.cyclicSchemas != null && ast.containsCircularRef(schema, { circularSchemas: this.options.cyclicSchemas });
767
+ const savedCyclicSchemas = this.options.cyclicSchemas;
634
768
  if (hasSelfRef) this.options.cyclicSchemas = void 0;
635
769
  const baseOutput = this.transform(schema) ?? this.transform(ast.createSchema({ type: "unknown" }));
636
- if (hasSelfRef) this.options.cyclicSchemas = options.cyclicSchemas;
770
+ if (hasSelfRef) this.options.cyclicSchemas = savedCyclicSchemas;
637
771
  const value = applyMiniModifiers({
638
772
  value: this.options.wrapOutput ? this.options.wrapOutput({
639
773
  output: baseOutput,
@@ -649,16 +783,18 @@ const printerZodMini = ast.definePrinter((options) => {
649
783
  }).join(",\n ")}\n })`;
650
784
  },
651
785
  array(node) {
652
- let result = `z.array(${(node.items ?? []).map((item) => this.transform(item)).filter(Boolean).join(", ") || this.transform(ast.createSchema({ type: "unknown" }))})${lengthChecksMini(node)}`;
653
- if (node.unique) result += `.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })`;
654
- return result;
786
+ const base = `z.array(${(node.items ?? []).map((item) => this.transform(item)).filter(Boolean).join(", ") || this.transform(ast.createSchema({ type: "unknown" }))})${lengthChecksMini(node)}`;
787
+ return node.unique ? `${base}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })` : base;
655
788
  },
656
789
  tuple(node) {
657
790
  return `z.tuple([${(node.items ?? []).map((item) => this.transform(item)).filter(Boolean).join(", ")}])`;
658
791
  },
659
792
  union(node) {
660
793
  const nodeMembers = node.members ?? [];
661
- const members = nodeMembers.map((m) => this.transform(m)).filter(Boolean);
794
+ const members = nodeMembers.map((memberNode) => {
795
+ const member = this.transform(memberNode);
796
+ return member && node.strategy === "one" ? strictOneOfMember(member, memberNode) : member;
797
+ }).filter(Boolean);
662
798
  if (members.length === 0) return "";
663
799
  if (members.length === 1) return members[0];
664
800
  if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === "intersection")) return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, [${members.join(", ")}])`;
@@ -669,47 +805,29 @@ const printerZodMini = ast.definePrinter((options) => {
669
805
  if (members.length === 0) return "";
670
806
  const [first, ...rest] = members;
671
807
  if (!first) return "";
672
- let base = this.transform(first);
673
- if (!base) return "";
674
- for (const member of rest) {
675
- if (member.primitive === "string") {
676
- const c = lengthChecksMini(ast.narrowSchema(member, "string") ?? {});
677
- if (c) {
678
- base += c;
679
- continue;
680
- }
681
- } else if (member.primitive === "number" || member.primitive === "integer") {
682
- const c = numberChecksMini(ast.narrowSchema(member, "number") ?? ast.narrowSchema(member, "integer") ?? {});
683
- if (c) {
684
- base += c;
685
- continue;
686
- }
687
- } else if (member.primitive === "array") {
688
- const c = lengthChecksMini(ast.narrowSchema(member, "array") ?? {});
689
- if (c) {
690
- base += c;
691
- continue;
692
- }
693
- }
808
+ const firstBase = this.transform(first);
809
+ if (!firstBase) return "";
810
+ return rest.reduce((acc, member) => {
811
+ const constraint = getMemberConstraintMini(member);
812
+ if (constraint) return acc + constraint;
694
813
  const transformed = this.transform(member);
695
- if (transformed) base = `z.intersection(${base}, ${transformed})`;
696
- }
697
- return base;
814
+ return transformed ? `z.intersection(${acc}, ${transformed})` : acc;
815
+ }, firstBase);
698
816
  },
699
817
  ...options.nodes
700
818
  },
701
819
  print(node) {
702
820
  const { keysToOmit } = this.options;
703
- let base = this.transform(node);
704
- if (!base) return null;
821
+ const transformed = this.transform(node);
822
+ if (!transformed) return null;
705
823
  const meta = ast.syncSchemaRef(node);
706
- if (keysToOmit?.length && meta.primitive === "object" && !(meta.type === "union" && meta.discriminatorPropertyName)) {
707
- const lazyMatch = base.match(/^z\.lazy\(\(\)\s*=>\s*(.+)\)$/);
708
- if (lazyMatch) base = `z.lazy(() => ${lazyMatch[1]}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} }))`;
709
- else base = `${base}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} })`;
710
- }
711
824
  return applyMiniModifiers({
712
- value: base,
825
+ value: (() => {
826
+ if (!keysToOmit?.length || meta.primitive !== "object" || meta.type === "union" && meta.discriminatorPropertyName) return transformed;
827
+ const lazyMatch = transformed.match(/^z\.lazy\(\(\)\s*=>\s*(.+)\)$/);
828
+ if (lazyMatch) return `z.lazy(() => ${lazyMatch[1]}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} }))`;
829
+ return `${transformed}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} })`;
830
+ })(),
713
831
  nullable: meta.nullable,
714
832
  optional: meta.optional,
715
833
  nullish: meta.nullish,
@@ -720,9 +838,17 @@ const printerZodMini = ast.definePrinter((options) => {
720
838
  });
721
839
  //#endregion
722
840
  //#region src/generators/zodGenerator.tsx
841
+ const zodPrinterCache = /* @__PURE__ */ new WeakMap();
842
+ const zodMiniPrinterCache = /* @__PURE__ */ new WeakMap();
843
+ /**
844
+ * Built-in generator for `@kubb/plugin-zod`. Emits one Zod schema per
845
+ * schema in the spec plus per-operation request/response/parameter schemas.
846
+ * When `mini: true`, schemas use the Zod Mini functional API instead of
847
+ * chainable methods.
848
+ */
723
849
  const zodGenerator = defineGenerator({
724
850
  name: "zod",
725
- renderer: jsxRenderer,
851
+ renderer: jsxRendererSync,
726
852
  schema(node, ctx) {
727
853
  const { adapter, config, resolver, root } = ctx;
728
854
  const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, printer } = ctx.options;
@@ -738,7 +864,7 @@ const zodGenerator = defineGenerator({
738
864
  }, {
739
865
  root,
740
866
  output,
741
- group
867
+ group: group ?? void 0
742
868
  }).path
743
869
  }));
744
870
  const meta = {
@@ -749,37 +875,67 @@ const zodGenerator = defineGenerator({
749
875
  }, {
750
876
  root,
751
877
  output,
752
- group
878
+ group: group ?? void 0
753
879
  })
754
880
  };
755
- const inferTypeName = inferred ? resolver.resolveSchemaTypeName(node.name) : void 0;
756
- const cyclicSchemas = adapter.inputNode ? ast.findCircularSchemas(adapter.inputNode.schemas) : void 0;
757
- const schemaPrinter = mini ? printerZodMini({
758
- guidType,
759
- wrapOutput,
760
- resolver,
761
- cyclicSchemas,
762
- nodes: printer?.nodes
763
- }) : printerZod({
764
- coercion,
765
- guidType,
766
- dateType,
767
- wrapOutput,
768
- resolver,
769
- cyclicSchemas,
770
- nodes: printer?.nodes
771
- });
881
+ const inferTypeName = inferred ? resolver.resolveSchemaTypeName(node.name) : null;
882
+ const cyclicSchemas = new Set(ctx.meta.circularNames);
883
+ const schemaPrinter = mini ? getCachedMiniPrinter() : getCachedStdPrinter();
884
+ function getCachedStdPrinter() {
885
+ const cached = zodPrinterCache.get(resolver);
886
+ if (cached && cached.coercion === coercion && cached.guidType === guidType && cached.dateType === dateType) return cached.printer;
887
+ const p = printerZod({
888
+ coercion,
889
+ guidType,
890
+ dateType,
891
+ wrapOutput,
892
+ resolver,
893
+ cyclicSchemas,
894
+ nodes: printer?.nodes
895
+ });
896
+ zodPrinterCache.set(resolver, {
897
+ printer: p,
898
+ coercion,
899
+ guidType,
900
+ dateType
901
+ });
902
+ return p;
903
+ }
904
+ function getCachedMiniPrinter() {
905
+ const cached = zodMiniPrinterCache.get(resolver);
906
+ if (cached && cached.guidType === guidType) return cached.printer;
907
+ const p = printerZodMini({
908
+ guidType,
909
+ wrapOutput,
910
+ resolver,
911
+ cyclicSchemas,
912
+ nodes: printer?.nodes
913
+ });
914
+ zodMiniPrinterCache.set(resolver, {
915
+ printer: p,
916
+ guidType
917
+ });
918
+ return p;
919
+ }
772
920
  return /* @__PURE__ */ jsxs(File, {
773
921
  baseName: meta.file.baseName,
774
922
  path: meta.file.path,
775
923
  meta: meta.file.meta,
776
- banner: resolver.resolveBanner(adapter.inputNode, {
924
+ banner: resolver.resolveBanner(ctx.meta, {
777
925
  output,
778
- config
926
+ config,
927
+ file: {
928
+ path: meta.file.path,
929
+ baseName: meta.file.baseName
930
+ }
779
931
  }),
780
- footer: resolver.resolveFooter(adapter.inputNode, {
932
+ footer: resolver.resolveFooter(ctx.meta, {
781
933
  output,
782
- config
934
+ config,
935
+ file: {
936
+ path: meta.file.path,
937
+ baseName: meta.file.baseName
938
+ }
783
939
  }),
784
940
  children: [
785
941
  /* @__PURE__ */ jsx(File.Import, {
@@ -816,12 +972,12 @@ const zodGenerator = defineGenerator({
816
972
  }, {
817
973
  root,
818
974
  output,
819
- group
975
+ group: group ?? void 0
820
976
  }) };
821
- const cyclicSchemas = adapter.inputNode ? ast.findCircularSchemas(adapter.inputNode.schemas) : void 0;
977
+ const cyclicSchemas = new Set(ctx.meta.circularNames);
822
978
  function renderSchemaEntry({ schema, name, keysToOmit }) {
823
979
  if (!schema) return null;
824
- const inferTypeName = inferred ? resolver.resolveTypeName(name) : void 0;
980
+ const inferTypeName = inferred ? resolver.resolveTypeName(name) : null;
825
981
  const imports = adapter.getImports(schema, (schemaName) => ({
826
982
  name: resolver.resolveSchemaName(schemaName),
827
983
  path: resolver.resolveFile({
@@ -830,17 +986,25 @@ const zodGenerator = defineGenerator({
830
986
  }, {
831
987
  root,
832
988
  output,
833
- group
989
+ group: group ?? void 0
834
990
  }).path
835
991
  }));
836
- const schemaPrinter = mini ? printerZodMini({
992
+ const cachedStd = zodPrinterCache.get(resolver);
993
+ const cachedMini = zodMiniPrinterCache.get(resolver);
994
+ const schemaPrinter = mini ? keysToOmit?.length ? printerZodMini({
837
995
  guidType,
838
996
  wrapOutput,
839
997
  resolver,
840
998
  keysToOmit,
841
999
  cyclicSchemas,
842
1000
  nodes: printer?.nodes
843
- }) : printerZod({
1001
+ }) : cachedMini?.guidType === guidType ? cachedMini.printer : printerZodMini({
1002
+ guidType,
1003
+ wrapOutput,
1004
+ resolver,
1005
+ cyclicSchemas,
1006
+ nodes: printer?.nodes
1007
+ }) : keysToOmit?.length ? printerZod({
844
1008
  coercion,
845
1009
  guidType,
846
1010
  dateType,
@@ -849,6 +1013,14 @@ const zodGenerator = defineGenerator({
849
1013
  keysToOmit,
850
1014
  cyclicSchemas,
851
1015
  nodes: printer?.nodes
1016
+ }) : cachedStd?.coercion === coercion && cachedStd?.guidType === guidType && cachedStd?.dateType === dateType ? cachedStd.printer : printerZod({
1017
+ coercion,
1018
+ guidType,
1019
+ dateType,
1020
+ wrapOutput,
1021
+ resolver,
1022
+ cyclicSchemas,
1023
+ nodes: printer?.nodes
852
1024
  });
853
1025
  return /* @__PURE__ */ jsxs(Fragment, { children: [mode === "split" && imports.map((imp) => /* @__PURE__ */ jsx(File.Import, {
854
1026
  root: meta.file.path,
@@ -870,14 +1042,14 @@ const zodGenerator = defineGenerator({
870
1042
  name: resolver.resolveParamName(node, param)
871
1043
  }));
872
1044
  const responseSchemas = node.responses.map((res) => renderSchemaEntry({
873
- schema: res.schema,
1045
+ schema: res.content?.[0]?.schema ?? null,
874
1046
  name: resolver.resolveResponseStatusName(node, res.statusCode),
875
- keysToOmit: res.keysToOmit
1047
+ keysToOmit: res.content?.[0]?.keysToOmit
876
1048
  }));
877
- const responsesWithSchema = node.responses.filter((res) => res.schema);
1049
+ const responsesWithSchema = node.responses.filter((res) => res.content?.[0]?.schema);
878
1050
  const responseUnionSchema = responsesWithSchema.length > 0 ? (() => {
879
1051
  const responseUnionName = resolver.resolveResponseName(node);
880
- if (new Set(responsesWithSchema.flatMap((res) => res.schema ? adapter.getImports(res.schema, (schemaName) => ({
1052
+ if (new Set(responsesWithSchema.flatMap((res) => res.content?.[0]?.schema ? adapter.getImports(res.content[0].schema, (schemaName) => ({
881
1053
  name: resolver.resolveSchemaName(schemaName),
882
1054
  path: ""
883
1055
  })).flatMap((imp) => Array.isArray(imp.name) ? imp.name : [imp.name]) : [])).has(responseUnionName)) return null;
@@ -905,13 +1077,21 @@ const zodGenerator = defineGenerator({
905
1077
  baseName: meta.file.baseName,
906
1078
  path: meta.file.path,
907
1079
  meta: meta.file.meta,
908
- banner: resolver.resolveBanner(adapter.inputNode, {
1080
+ banner: resolver.resolveBanner(ctx.meta, {
909
1081
  output,
910
- config
1082
+ config,
1083
+ file: {
1084
+ path: meta.file.path,
1085
+ baseName: meta.file.baseName
1086
+ }
911
1087
  }),
912
- footer: resolver.resolveFooter(adapter.inputNode, {
1088
+ footer: resolver.resolveFooter(ctx.meta, {
913
1089
  output,
914
- config
1090
+ config,
1091
+ file: {
1092
+ path: meta.file.path,
1093
+ baseName: meta.file.baseName
1094
+ }
915
1095
  }),
916
1096
  children: [
917
1097
  /* @__PURE__ */ jsx(File.Import, {
@@ -927,7 +1107,7 @@ const zodGenerator = defineGenerator({
927
1107
  });
928
1108
  },
929
1109
  operations(nodes, ctx) {
930
- const { adapter, config, resolver, root } = ctx;
1110
+ const { config, resolver, root } = ctx;
931
1111
  const { output, importPath, group, operations, paramsCasing } = ctx.options;
932
1112
  if (!operations) return;
933
1113
  const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath);
@@ -937,7 +1117,7 @@ const zodGenerator = defineGenerator({
937
1117
  }, {
938
1118
  root,
939
1119
  output,
940
- group
1120
+ group: group ?? void 0
941
1121
  }) };
942
1122
  const transformedOperations = nodes.map((node) => {
943
1123
  return {
@@ -962,7 +1142,7 @@ const zodGenerator = defineGenerator({
962
1142
  }, {
963
1143
  root,
964
1144
  output,
965
- group
1145
+ group: group ?? void 0
966
1146
  });
967
1147
  return names.map((name) => /* @__PURE__ */ jsx(File.Import, {
968
1148
  name: [name],
@@ -974,13 +1154,21 @@ const zodGenerator = defineGenerator({
974
1154
  baseName: meta.file.baseName,
975
1155
  path: meta.file.path,
976
1156
  meta: meta.file.meta,
977
- banner: resolver.resolveBanner(adapter.inputNode, {
1157
+ banner: resolver.resolveBanner(ctx.meta, {
978
1158
  output,
979
- config
1159
+ config,
1160
+ file: {
1161
+ path: meta.file.path,
1162
+ baseName: meta.file.baseName
1163
+ }
980
1164
  }),
981
- footer: resolver.resolveFooter(adapter.inputNode, {
1165
+ footer: resolver.resolveFooter(ctx.meta, {
982
1166
  output,
983
- config
1167
+ config,
1168
+ file: {
1169
+ path: meta.file.path,
1170
+ baseName: meta.file.baseName
1171
+ }
984
1172
  }),
985
1173
  children: [
986
1174
  /* @__PURE__ */ jsx(File.Import, {
@@ -1001,77 +1189,96 @@ const zodGenerator = defineGenerator({
1001
1189
  //#endregion
1002
1190
  //#region src/resolvers/resolverZod.ts
1003
1191
  /**
1004
- * Naming convention resolver for Zod plugin.
1192
+ * Default resolver used by `@kubb/plugin-zod`. Decides the names and file
1193
+ * paths for every generated Zod schema. Schemas use camelCase with a
1194
+ * `Schema` suffix (`listPetsSchema`); their inferred types use PascalCase.
1005
1195
  *
1006
- * Provides default naming helpers using camelCase with a `Schema` suffix for schemas.
1196
+ * @example Resolve schema and type names
1197
+ * ```ts
1198
+ * import { resolverZod } from '@kubb/plugin-zod'
1007
1199
  *
1008
- * @example
1009
- * `resolverZod.default('list pets', 'function') // 'listPetsSchema'`
1200
+ * resolverZod.default('list pets', 'function') // 'listPetsSchema'
1201
+ * resolverZod.resolveSchemaTypeName('pet') // 'PetSchema'
1202
+ * ```
1010
1203
  */
1011
- const resolverZod = defineResolver((ctx) => {
1204
+ const resolverZod = defineResolver(() => {
1012
1205
  return {
1013
1206
  name: "default",
1014
1207
  pluginName: "plugin-zod",
1015
1208
  default(name, type) {
1016
- return camelCase(name, {
1209
+ const resolved = camelCase(name, {
1017
1210
  isFile: type === "file",
1018
1211
  suffix: type ? "schema" : void 0
1019
1212
  });
1213
+ return type === "file" ? resolved : ensureValidVarName(resolved);
1020
1214
  },
1021
1215
  resolveSchemaName(name) {
1022
- return camelCase(name, { suffix: "schema" });
1216
+ return ensureValidVarName(camelCase(name, { suffix: "schema" }));
1023
1217
  },
1024
1218
  resolveSchemaTypeName(name) {
1025
- return pascalCase(name, { suffix: "schema" });
1219
+ return ensureValidVarName(pascalCase(name, { suffix: "schema" }));
1026
1220
  },
1027
1221
  resolveTypeName(name) {
1028
- return pascalCase(name);
1222
+ return ensureValidVarName(pascalCase(name));
1029
1223
  },
1030
1224
  resolvePathName(name, type) {
1031
- return ctx.default(name, type);
1225
+ return this.default(name, type);
1032
1226
  },
1033
1227
  resolveParamName(node, param) {
1034
- return ctx.resolveSchemaName(`${node.operationId} ${param.in} ${param.name}`);
1228
+ return this.resolveSchemaName(`${node.operationId} ${param.in} ${param.name}`);
1035
1229
  },
1036
1230
  resolveResponseStatusName(node, statusCode) {
1037
- return ctx.resolveSchemaName(`${node.operationId} Status ${statusCode}`);
1231
+ return this.resolveSchemaName(`${node.operationId} Status ${statusCode}`);
1038
1232
  },
1039
1233
  resolveDataName(node) {
1040
- return ctx.resolveSchemaName(`${node.operationId} Data`);
1234
+ return this.resolveSchemaName(`${node.operationId} Data`);
1041
1235
  },
1042
1236
  resolveResponsesName(node) {
1043
- return ctx.resolveSchemaName(`${node.operationId} Responses`);
1237
+ return this.resolveSchemaName(`${node.operationId} Responses`);
1044
1238
  },
1045
1239
  resolveResponseName(node) {
1046
- return ctx.resolveSchemaName(`${node.operationId} Response`);
1240
+ return this.resolveSchemaName(`${node.operationId} Response`);
1047
1241
  },
1048
1242
  resolvePathParamsName(node, param) {
1049
- return ctx.resolveParamName(node, param);
1243
+ return this.resolveParamName(node, param);
1050
1244
  },
1051
1245
  resolveQueryParamsName(node, param) {
1052
- return ctx.resolveParamName(node, param);
1246
+ return this.resolveParamName(node, param);
1053
1247
  },
1054
1248
  resolveHeaderParamsName(node, param) {
1055
- return ctx.resolveParamName(node, param);
1249
+ return this.resolveParamName(node, param);
1056
1250
  }
1057
1251
  };
1058
1252
  });
1059
1253
  //#endregion
1060
1254
  //#region src/plugin.ts
1061
1255
  /**
1062
- * Canonical plugin name for `@kubb/plugin-zod`, used in driver lookups and warnings.
1256
+ * Canonical plugin name for `@kubb/plugin-zod`. Used for driver lookups and
1257
+ * cross-plugin dependency references.
1063
1258
  */
1064
1259
  const pluginZodName = "plugin-zod";
1065
1260
  /**
1066
- * Generates Zod validation schemas from an OpenAPI specification.
1067
- * Walks schemas and operations, delegates to generators, and writes barrel files
1068
- * based on the configured `barrelType`.
1261
+ * Generates Zod v4 schemas from an OpenAPI spec. Use them to validate API
1262
+ * responses at runtime, build form schemas, or feed back into router libraries
1263
+ * that consume Zod (tRPC, Hono, Elysia). Pair with `@kubb/plugin-client` and
1264
+ * set the client's `parser: 'zod'` to validate every response automatically.
1069
1265
  *
1070
- * @example Zod schema generator
1266
+ * @example
1071
1267
  * ```ts
1072
- * import pluginZod from '@kubb/plugin-zod'
1268
+ * import { defineConfig } from 'kubb'
1269
+ * import { pluginTs } from '@kubb/plugin-ts'
1270
+ * import { pluginZod } from '@kubb/plugin-zod'
1271
+ *
1073
1272
  * export default defineConfig({
1074
- * plugins: [pluginZod({ output: { path: 'zod' } })]
1273
+ * input: { path: './petStore.yaml' },
1274
+ * output: { path: './src/gen' },
1275
+ * plugins: [
1276
+ * pluginTs(),
1277
+ * pluginZod({
1278
+ * output: { path: './zod' },
1279
+ * typed: true,
1280
+ * }),
1281
+ * ],
1075
1282
  * })
1076
1283
  * ```
1077
1284
  */
@@ -1086,7 +1293,7 @@ const pluginZod = definePlugin((options) => {
1086
1293
  if (group.type === "path") return `${ctx.group.split("/")[1]}`;
1087
1294
  return `${camelCase(ctx.group)}Controller`;
1088
1295
  }
1089
- } : void 0;
1296
+ } : null;
1090
1297
  return {
1091
1298
  name: pluginZodName,
1092
1299
  options,