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

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,7 +1,7 @@
1
- import { t as __name } from "./chunk-C0LytTxp.js";
1
+ import { t as __name } from "./rolldown-runtime-C0LytTxp.js";
2
2
  import { ast, defineGenerator, definePlugin, defineResolver } from "@kubb/core";
3
- import { buildList, buildObject, extractRefName, objectKey, stringify, stringifyObject, toRegExpString } from "@kubb/ast/utils";
4
3
  import { Const, File, Type, jsxRenderer } from "@kubb/renderer-jsx";
4
+ import { buildList, buildObject, containsCircularRef, extractRefName, lazyGetter, mapSchemaItems, mapSchemaMembers, mapSchemaProperties, objectKey, stringify, stringifyObject, syncSchemaRef, toRegExpString } from "@kubb/ast/utils";
5
5
  import { Fragment, jsx, jsxs } from "@kubb/renderer-jsx/jsx-runtime";
6
6
  //#region ../../internals/utils/src/casing.ts
7
7
  /**
@@ -190,6 +190,27 @@ function ensureValidVarName(name) {
190
190
  return `_${name}`;
191
191
  }
192
192
  //#endregion
193
+ //#region ../../internals/shared/src/params.ts
194
+ const caseParamsCache = /* @__PURE__ */ new WeakMap();
195
+ /**
196
+ * Applies camelCase to parameter names and returns a new array without mutating the input.
197
+ *
198
+ * Run it before handing parameters to schema builders so output property keys get the right casing
199
+ * while `OperationNode.parameters` stays intact for other consumers. When `casing` is unset, the
200
+ * original array is returned unchanged. Results are cached per input array.
201
+ */
202
+ function caseParams(params, casing) {
203
+ if (!casing) return params;
204
+ const cached = caseParamsCache.get(params);
205
+ if (cached) return cached;
206
+ const result = params.map((param) => ({
207
+ ...param,
208
+ name: camelCase(param.name)
209
+ }));
210
+ caseParamsCache.set(params, result);
211
+ return result;
212
+ }
213
+ //#endregion
193
214
  //#region ../../internals/shared/src/operation.ts
194
215
  /**
195
216
  * Maps a content type to the PascalCase suffix used to name per-content-type variants
@@ -239,6 +260,17 @@ function resolveContentTypeVariants(entries, baseName) {
239
260
  };
240
261
  });
241
262
  }
263
+ function getStatusCodeNumber(statusCode) {
264
+ const code = Number(statusCode);
265
+ return Number.isNaN(code) ? null : code;
266
+ }
267
+ function isSuccessStatusCode(statusCode) {
268
+ const code = getStatusCodeNumber(statusCode);
269
+ return code !== null && code >= 200 && code < 300;
270
+ }
271
+ function getSuccessResponses(responses) {
272
+ return responses.filter((response) => isSuccessStatusCode(response.statusCode));
273
+ }
242
274
  //#endregion
243
275
  //#region ../../internals/shared/src/group.ts
244
276
  /**
@@ -431,7 +463,7 @@ function containsCodec(node, seen = /* @__PURE__ */ new Set()) {
431
463
  if (seen.has(refName)) return false;
432
464
  seen.add(refName);
433
465
  }
434
- const resolved = ast.syncSchemaRef(node);
466
+ const resolved = syncSchemaRef(node);
435
467
  if (resolved.type === "ref") return false;
436
468
  return containsCodec(resolved, seen);
437
469
  }
@@ -443,6 +475,13 @@ function containsCodec(node, seen = /* @__PURE__ */ new Set()) {
443
475
  return children.some((child) => containsCodec(child, seen));
444
476
  }
445
477
  /**
478
+ * Collects the names of `$ref` schemas that transitively contain a codec, so the generator can route
479
+ * them to their input (encode) variant.
480
+ */
481
+ function collectCodecRefNames(node) {
482
+ return ast.collect(node, { schema: (n) => n.type === "ref" && n.ref && containsCodec(n) ? extractRefName(n.ref) ?? void 0 : void 0 });
483
+ }
484
+ /**
446
485
  * Collects all resolved schema names for an operation's parameters and responses
447
486
  * into a single lookup object, useful for building imports and type references.
448
487
  */
@@ -490,6 +529,13 @@ function formatLiteral(v) {
490
529
  return String(v);
491
530
  }
492
531
  /**
532
+ * Map a `regexType` to the `func` argument of `toRegExpString`: `'constructor'` emits
533
+ * `new RegExp(...)`, while `'literal'` (the default) emits a regex literal.
534
+ */
535
+ function regexFunc(regexType) {
536
+ return regexType === "constructor" ? "RegExp" : null;
537
+ }
538
+ /**
493
539
  * Build `.min()` / `.max()` / `.gt()` / `.lt()` constraint chains for numbers
494
540
  * using the standard chainable Zod v4 API.
495
541
  */
@@ -506,11 +552,11 @@ function numberConstraints({ min, max, exclusiveMinimum, exclusiveMaximum, multi
506
552
  * Build `.min()` / `.max()` / `.regex()` chains for strings/arrays
507
553
  * using the standard chainable Zod v4 API.
508
554
  */
509
- function lengthConstraints({ min, max, pattern }) {
555
+ function lengthConstraints({ min, max, pattern, regexType }) {
510
556
  return [
511
557
  min !== void 0 ? `.min(${min})` : "",
512
558
  max !== void 0 ? `.max(${max})` : "",
513
- pattern !== void 0 ? `.regex(${toRegExpString(pattern, null)})` : ""
559
+ pattern !== void 0 ? `.regex(${toRegExpString(pattern, regexFunc(regexType))})` : ""
514
560
  ].join("");
515
561
  }
516
562
  /**
@@ -528,18 +574,18 @@ function numberChecksMini({ min, max, exclusiveMinimum, exclusiveMaximum, multip
528
574
  /**
529
575
  * Build `.check(z.minLength(), z.maxLength(), z.regex())` for `zod/mini` length constraints.
530
576
  */
531
- function lengthChecksMini({ min, max, pattern }) {
577
+ function lengthChecksMini({ min, max, pattern, regexType }) {
532
578
  const checks = [];
533
579
  if (min !== void 0) checks.push(`z.minLength(${min})`);
534
580
  if (max !== void 0) checks.push(`z.maxLength(${max})`);
535
- if (pattern !== void 0) checks.push(`z.regex(${toRegExpString(pattern, null)})`);
581
+ if (pattern !== void 0) checks.push(`z.regex(${toRegExpString(pattern, regexFunc(regexType))})`);
536
582
  return checks.length ? `.check(${checks.join(", ")})` : "";
537
583
  }
538
584
  /**
539
- * Apply nullable / optional / nullish modifiers and an optional `.describe()` call
540
- * to a schema value string using the chainable Zod v4 API.
585
+ * Apply nullable / optional / nullish modifiers, an optional `.describe()` call, and an
586
+ * optional `.meta({ examples })` call to a schema value string using the chainable Zod v4 API.
541
587
  */
542
- function applyModifiers({ value, nullable, optional, nullish, defaultValue, description }) {
588
+ function applyModifiers({ value, nullable, optional, nullish, defaultValue, description, examples }) {
543
589
  const withModifier = (() => {
544
590
  if (nullish || nullable && optional) return `${value}.nullish()`;
545
591
  if (optional) return `${value}.optional()`;
@@ -547,7 +593,8 @@ function applyModifiers({ value, nullable, optional, nullish, defaultValue, desc
547
593
  return value;
548
594
  })();
549
595
  const withDefault = defaultValue !== void 0 ? `${withModifier}.default(${formatDefault(defaultValue)})` : withModifier;
550
- return description ? `${withDefault}.describe(${stringify(description)})` : withDefault;
596
+ const withDescription = description ? `${withDefault}.describe(${stringify(description)})` : withDefault;
597
+ return examples?.length ? `${withDescription}.meta({ examples: [${examples.map(formatDefault).join(", ")}] })` : withDescription;
551
598
  }
552
599
  /**
553
600
  * Apply nullable / optional / nullish modifiers using the functional `zod/mini` API
@@ -567,16 +614,22 @@ function strictOneOfMember$1(member, node) {
567
614
  if (node.type === "object" && node.additionalProperties === void 0) return `${member}.strict()`;
568
615
  if (node.type === "ref") {
569
616
  if (member.startsWith("z.lazy(")) return member;
570
- const schema = ast.syncSchemaRef(node);
617
+ const schema = syncSchemaRef(node);
571
618
  if (schema.type === "object" && (schema.additionalProperties === void 0 || schema.additionalProperties === false)) return `${member}.strict()`;
572
619
  }
573
620
  return member;
574
621
  }
575
622
  __name(strictOneOfMember$1, "strictOneOfMember");
576
- function getMemberConstraint(member) {
577
- if (member.primitive === "string") return lengthConstraints(ast.narrowSchema(member, "string") ?? {}) || void 0;
623
+ function getMemberConstraint({ member, regexType }) {
624
+ if (member.primitive === "string") return lengthConstraints({
625
+ ...ast.narrowSchema(member, "string") ?? {},
626
+ regexType
627
+ }) || void 0;
578
628
  if (member.primitive === "number" || member.primitive === "integer") return numberConstraints(ast.narrowSchema(member, "number") ?? ast.narrowSchema(member, "integer") ?? {}) || void 0;
579
- if (member.primitive === "array") return lengthConstraints(ast.narrowSchema(member, "array") ?? {}) || void 0;
629
+ if (member.primitive === "array") return lengthConstraints({
630
+ ...ast.narrowSchema(member, "array") ?? {},
631
+ regexType
632
+ }) || void 0;
580
633
  }
581
634
  /**
582
635
  * Zod v4 printer built with `definePrinter`.
@@ -590,7 +643,7 @@ function getMemberConstraint(member) {
590
643
  * const code = printer.print(stringNode) // "z.string()"
591
644
  * ```
592
645
  */
593
- const printerZod = ast.definePrinter((options) => {
646
+ const printerZod = ast.createPrinter((options) => {
594
647
  return {
595
648
  name: "zod",
596
649
  options,
@@ -602,7 +655,10 @@ const printerZod = ast.definePrinter((options) => {
602
655
  boolean: () => "z.boolean()",
603
656
  null: () => "z.null()",
604
657
  string(node) {
605
- return `${shouldCoerce(this.options.coercion, "strings") ? "z.coerce.string()" : "z.string()"}${lengthConstraints(node)}`;
658
+ return `${shouldCoerce(this.options.coercion, "strings") ? "z.coerce.string()" : "z.string()"}${lengthConstraints({
659
+ ...node,
660
+ regexType: this.options.regexType
661
+ })}`;
606
662
  },
607
663
  number(node) {
608
664
  return `${shouldCoerce(this.options.coercion, "numbers") ? "z.coerce.number()" : "z.number()"}${numberConstraints(node)}`;
@@ -630,13 +686,22 @@ const printerZod = ast.definePrinter((options) => {
630
686
  return shouldCoerce(this.options.coercion, "dates") ? "z.coerce.date()" : "z.date()";
631
687
  },
632
688
  uuid(node) {
633
- return `${this.options.guidType === "guid" ? "z.guid()" : "z.uuid()"}${lengthConstraints(node)}`;
689
+ return `${this.options.guidType === "guid" ? "z.guid()" : "z.uuid()"}${lengthConstraints({
690
+ ...node,
691
+ regexType: this.options.regexType
692
+ })}`;
634
693
  },
635
694
  email(node) {
636
- return `z.email()${lengthConstraints(node)}`;
695
+ return `z.email()${lengthConstraints({
696
+ ...node,
697
+ regexType: this.options.regexType
698
+ })}`;
637
699
  },
638
700
  url(node) {
639
- return `z.url()${lengthConstraints(node)}`;
701
+ return `z.url()${lengthConstraints({
702
+ ...node,
703
+ regexType: this.options.regexType
704
+ })}`;
640
705
  },
641
706
  ipv4: () => "z.ipv4()",
642
707
  ipv6: () => "z.ipv6()",
@@ -659,17 +724,17 @@ const printerZod = ast.definePrinter((options) => {
659
724
  return resolvedName;
660
725
  },
661
726
  object(node) {
662
- const objectBase = `z.object(${buildObject(node.properties.map((prop) => {
663
- const { name: propName, schema } = prop;
664
- const meta = ast.syncSchemaRef(schema);
665
- const isNullable = meta.nullable;
666
- const isOptional = schema.optional;
667
- const isNullish = schema.nullish;
668
- const hasSelfRef = this.options.cyclicSchemas != null && ast.containsCircularRef(schema, { circularSchemas: this.options.cyclicSchemas });
727
+ const isCyclic = (schema) => this.options.cyclicSchemas != null && containsCircularRef(schema, { circularSchemas: this.options.cyclicSchemas });
728
+ const objectBase = `z.object(${buildObject(mapSchemaProperties(node, (schema) => {
729
+ const hasSelfRef = isCyclic(schema);
669
730
  const savedCyclicSchemas = this.options.cyclicSchemas;
670
731
  if (hasSelfRef) this.options.cyclicSchemas = void 0;
671
- const baseOutput = this.transform(schema) ?? this.transform(ast.createSchema({ type: "unknown" }));
732
+ const baseOutput = this.transform(schema) ?? this.transform(ast.factory.createSchema({ type: "unknown" }));
672
733
  if (hasSelfRef) this.options.cyclicSchemas = savedCyclicSchemas;
734
+ return baseOutput;
735
+ }).map(({ name: propName, property, output: baseOutput }) => {
736
+ const { schema } = property;
737
+ const meta = syncSchemaRef(schema);
673
738
  const wrappedOutput = this.options.wrapOutput ? this.options.wrapOutput({
674
739
  output: baseOutput,
675
740
  schema
@@ -677,38 +742,41 @@ const printerZod = ast.definePrinter((options) => {
677
742
  const descriptionToApply = schema.type !== "ref" && meta.type === "ref" ? void 0 : meta.description;
678
743
  const value = applyModifiers({
679
744
  value: wrappedOutput,
680
- nullable: isNullable,
681
- optional: isOptional,
682
- nullish: isNullish,
745
+ nullable: meta.nullable,
746
+ optional: schema.optional || property.required === false,
747
+ nullish: schema.nullish,
683
748
  defaultValue: meta.default,
684
- description: descriptionToApply
749
+ description: descriptionToApply,
750
+ examples: meta.examples
685
751
  });
686
- if (hasSelfRef) return `get ${objectKey(propName)}() { return ${value} }`;
687
- return `${objectKey(propName)}: ${value}`;
752
+ return isCyclic(schema) ? lazyGetter({
753
+ name: propName,
754
+ body: value
755
+ }) : `${objectKey(propName)}: ${value}`;
688
756
  }))})`;
689
757
  return (() => {
690
758
  if (node.additionalProperties && node.additionalProperties !== true) {
691
759
  const catchallType = this.transform(node.additionalProperties);
692
760
  return catchallType ? `${objectBase}.catchall(${catchallType})` : objectBase;
693
761
  }
694
- if (node.additionalProperties === true) return `${objectBase}.catchall(${this.transform(ast.createSchema({ type: "unknown" }))})`;
762
+ if (node.additionalProperties === true) return `${objectBase}.catchall(${this.transform(ast.factory.createSchema({ type: "unknown" }))})`;
695
763
  if (node.additionalProperties === false) return `${objectBase}.strict()`;
696
764
  return objectBase;
697
765
  })();
698
766
  },
699
767
  array(node) {
700
- const base = `z.array(${(node.items ?? []).map((item) => this.transform(item)).filter(Boolean).join(", ") || this.transform(ast.createSchema({ type: "unknown" }))})${lengthConstraints(node)}`;
768
+ const base = `z.array(${mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean).join(", ") || this.transform(ast.factory.createSchema({ type: "unknown" }))})${lengthConstraints({
769
+ ...node,
770
+ regexType: this.options.regexType
771
+ })}`;
701
772
  return node.unique ? `${base}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })` : base;
702
773
  },
703
774
  tuple(node) {
704
- return `z.tuple(${buildList((node.items ?? []).map((item) => this.transform(item)).filter(Boolean))})`;
775
+ return `z.tuple(${buildList(mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean))})`;
705
776
  },
706
777
  union(node) {
707
778
  const nodeMembers = node.members ?? [];
708
- const members = nodeMembers.map((memberNode) => {
709
- const member = this.transform(memberNode);
710
- return member && node.strategy === "one" ? strictOneOfMember$1(member, memberNode) : member;
711
- }).filter(Boolean);
779
+ const members = mapSchemaMembers(node, (memberNode) => this.transform(memberNode)).map(({ schema, output }) => output && node.strategy === "one" ? strictOneOfMember$1(output, schema) : output).filter(Boolean);
712
780
  if (members.length === 0) return "";
713
781
  if (members.length === 1) return members[0];
714
782
  if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === "intersection")) return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, ${buildList(members)})`;
@@ -722,7 +790,10 @@ const printerZod = ast.definePrinter((options) => {
722
790
  const firstBase = this.transform(first);
723
791
  if (!firstBase) return "";
724
792
  return rest.reduce((acc, member) => {
725
- const constraint = getMemberConstraint(member);
793
+ const constraint = getMemberConstraint({
794
+ member,
795
+ regexType: this.options.regexType
796
+ });
726
797
  if (constraint) return acc + constraint;
727
798
  const transformed = this.transform(member);
728
799
  return transformed ? `${acc}.and(${transformed})` : acc;
@@ -734,7 +805,7 @@ const printerZod = ast.definePrinter((options) => {
734
805
  const { keysToOmit } = this.options;
735
806
  const transformed = this.transform(node);
736
807
  if (!transformed) return null;
737
- const meta = ast.syncSchemaRef(node);
808
+ const meta = syncSchemaRef(node);
738
809
  return applyModifiers({
739
810
  value: (() => {
740
811
  if (!keysToOmit?.length || meta.primitive !== "object" || meta.type === "union" && meta.discriminatorPropertyName) return transformed;
@@ -746,7 +817,8 @@ const printerZod = ast.definePrinter((options) => {
746
817
  optional: meta.optional,
747
818
  nullish: meta.nullish,
748
819
  defaultValue: meta.default,
749
- description: meta.description
820
+ description: meta.description,
821
+ examples: meta.examples
750
822
  });
751
823
  }
752
824
  };
@@ -757,10 +829,16 @@ function strictOneOfMember(member, node) {
757
829
  if (node.type === "object" && (node.additionalProperties === void 0 || node.additionalProperties === false)) return member.replace(/^z\.object\(/, "z.strictObject(");
758
830
  return member;
759
831
  }
760
- function getMemberConstraintMini(member) {
761
- if (member.primitive === "string") return lengthChecksMini(ast.narrowSchema(member, "string") ?? {}) || void 0;
832
+ function getMemberConstraintMini({ member, regexType }) {
833
+ if (member.primitive === "string") return lengthChecksMini({
834
+ ...ast.narrowSchema(member, "string") ?? {},
835
+ regexType
836
+ }) || void 0;
762
837
  if (member.primitive === "number" || member.primitive === "integer") return numberChecksMini(ast.narrowSchema(member, "number") ?? ast.narrowSchema(member, "integer") ?? {}) || void 0;
763
- if (member.primitive === "array") return lengthChecksMini(ast.narrowSchema(member, "array") ?? {}) || void 0;
838
+ if (member.primitive === "array") return lengthChecksMini({
839
+ ...ast.narrowSchema(member, "array") ?? {},
840
+ regexType
841
+ }) || void 0;
764
842
  }
765
843
  /**
766
844
  * Zod v4 **Mini** printer built with `definePrinter`.
@@ -774,7 +852,7 @@ function getMemberConstraintMini(member) {
774
852
  * const code = printer.print(optionalStringNode) // "z.optional(z.string())"
775
853
  * ```
776
854
  */
777
- const printerZodMini = ast.definePrinter((options) => {
855
+ const printerZodMini = ast.createPrinter((options) => {
778
856
  return {
779
857
  name: "zod-mini",
780
858
  options,
@@ -786,7 +864,10 @@ const printerZodMini = ast.definePrinter((options) => {
786
864
  boolean: () => "z.boolean()",
787
865
  null: () => "z.null()",
788
866
  string(node) {
789
- return `z.string()${lengthChecksMini(node)}`;
867
+ return `z.string()${lengthChecksMini({
868
+ ...node,
869
+ regexType: this.options.regexType
870
+ })}`;
790
871
  },
791
872
  number(node) {
792
873
  return `z.number()${numberChecksMini(node)}`;
@@ -809,13 +890,22 @@ const printerZodMini = ast.definePrinter((options) => {
809
890
  return "z.date()";
810
891
  },
811
892
  uuid(node) {
812
- return `${this.options.guidType === "guid" ? "z.guid()" : "z.uuid()"}${lengthChecksMini(node)}`;
893
+ return `${this.options.guidType === "guid" ? "z.guid()" : "z.uuid()"}${lengthChecksMini({
894
+ ...node,
895
+ regexType: this.options.regexType
896
+ })}`;
813
897
  },
814
898
  email(node) {
815
- return `z.email()${lengthChecksMini(node)}`;
899
+ return `z.email()${lengthChecksMini({
900
+ ...node,
901
+ regexType: this.options.regexType
902
+ })}`;
816
903
  },
817
904
  url(node) {
818
- return `z.url()${lengthChecksMini(node)}`;
905
+ return `z.url()${lengthChecksMini({
906
+ ...node,
907
+ regexType: this.options.regexType
908
+ })}`;
819
909
  },
820
910
  ipv4: () => "z.ipv4()",
821
911
  ipv6: () => "z.ipv6()",
@@ -837,44 +927,46 @@ const printerZodMini = ast.definePrinter((options) => {
837
927
  return resolvedName;
838
928
  },
839
929
  object(node) {
840
- return `z.object(${buildObject(node.properties.map((prop) => {
841
- const { name: propName, schema } = prop;
842
- const meta = ast.syncSchemaRef(schema);
843
- const isNullable = meta.nullable;
844
- const isOptional = schema.optional;
845
- const isNullish = schema.nullish;
846
- const hasSelfRef = this.options.cyclicSchemas != null && ast.containsCircularRef(schema, { circularSchemas: this.options.cyclicSchemas });
930
+ const isCyclic = (schema) => this.options.cyclicSchemas != null && containsCircularRef(schema, { circularSchemas: this.options.cyclicSchemas });
931
+ return `z.object(${buildObject(mapSchemaProperties(node, (schema) => {
932
+ const hasSelfRef = isCyclic(schema);
847
933
  const savedCyclicSchemas = this.options.cyclicSchemas;
848
934
  if (hasSelfRef) this.options.cyclicSchemas = void 0;
849
- const baseOutput = this.transform(schema) ?? this.transform(ast.createSchema({ type: "unknown" }));
935
+ const baseOutput = this.transform(schema) ?? this.transform(ast.factory.createSchema({ type: "unknown" }));
850
936
  if (hasSelfRef) this.options.cyclicSchemas = savedCyclicSchemas;
937
+ return baseOutput;
938
+ }).map(({ name: propName, property, output: baseOutput }) => {
939
+ const { schema } = property;
940
+ const meta = syncSchemaRef(schema);
851
941
  const value = applyMiniModifiers({
852
942
  value: this.options.wrapOutput ? this.options.wrapOutput({
853
943
  output: baseOutput,
854
944
  schema
855
945
  }) || baseOutput : baseOutput,
856
- nullable: isNullable,
857
- optional: isOptional,
858
- nullish: isNullish,
946
+ nullable: meta.nullable,
947
+ optional: schema.optional || property.required === false,
948
+ nullish: schema.nullish,
859
949
  defaultValue: meta.default
860
950
  });
861
- if (hasSelfRef) return `get ${objectKey(propName)}() { return ${value} }`;
862
- return `${objectKey(propName)}: ${value}`;
951
+ return isCyclic(schema) ? lazyGetter({
952
+ name: propName,
953
+ body: value
954
+ }) : `${objectKey(propName)}: ${value}`;
863
955
  }))})`;
864
956
  },
865
957
  array(node) {
866
- const base = `z.array(${(node.items ?? []).map((item) => this.transform(item)).filter(Boolean).join(", ") || this.transform(ast.createSchema({ type: "unknown" }))})${lengthChecksMini(node)}`;
958
+ const base = `z.array(${mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean).join(", ") || this.transform(ast.factory.createSchema({ type: "unknown" }))})${lengthChecksMini({
959
+ ...node,
960
+ regexType: this.options.regexType
961
+ })}`;
867
962
  return node.unique ? `${base}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })` : base;
868
963
  },
869
964
  tuple(node) {
870
- return `z.tuple(${buildList((node.items ?? []).map((item) => this.transform(item)).filter(Boolean))})`;
965
+ return `z.tuple(${buildList(mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean))})`;
871
966
  },
872
967
  union(node) {
873
968
  const nodeMembers = node.members ?? [];
874
- const members = nodeMembers.map((memberNode) => {
875
- const member = this.transform(memberNode);
876
- return member && node.strategy === "one" ? strictOneOfMember(member, memberNode) : member;
877
- }).filter(Boolean);
969
+ const members = mapSchemaMembers(node, (memberNode) => this.transform(memberNode)).map(({ schema, output }) => output && node.strategy === "one" ? strictOneOfMember(output, schema) : output).filter(Boolean);
878
970
  if (members.length === 0) return "";
879
971
  if (members.length === 1) return members[0];
880
972
  if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === "intersection")) return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, ${buildList(members)})`;
@@ -888,7 +980,10 @@ const printerZodMini = ast.definePrinter((options) => {
888
980
  const firstBase = this.transform(first);
889
981
  if (!firstBase) return "";
890
982
  return rest.reduce((acc, member) => {
891
- const constraint = getMemberConstraintMini(member);
983
+ const constraint = getMemberConstraintMini({
984
+ member,
985
+ regexType: this.options.regexType
986
+ });
892
987
  if (constraint) return acc + constraint;
893
988
  const transformed = this.transform(member);
894
989
  return transformed ? `z.intersection(${acc}, ${transformed})` : acc;
@@ -900,7 +995,7 @@ const printerZodMini = ast.definePrinter((options) => {
900
995
  const { keysToOmit } = this.options;
901
996
  const transformed = this.transform(node);
902
997
  if (!transformed) return null;
903
- const meta = ast.syncSchemaRef(node);
998
+ const meta = syncSchemaRef(node);
904
999
  return applyMiniModifiers({
905
1000
  value: (() => {
906
1001
  if (!keysToOmit?.length || meta.primitive !== "object" || meta.type === "union" && meta.discriminatorPropertyName) return transformed;
@@ -927,7 +1022,7 @@ const zodMiniPrinterCache = /* @__PURE__ */ new WeakMap();
927
1022
  */
928
1023
  function getStdPrinters(resolver, params) {
929
1024
  const cached = zodPrinterCache.get(resolver);
930
- if (cached && cached.coercion === params.coercion && cached.guidType === params.guidType && cached.dateType === params.dateType) return {
1025
+ if (cached && cached.coercion === params.coercion && cached.guidType === params.guidType && cached.regexType === params.regexType && cached.dateType === params.dateType) return {
931
1026
  output: cached.output,
932
1027
  input: cached.input
933
1028
  };
@@ -948,6 +1043,7 @@ function getStdPrinters(resolver, params) {
948
1043
  input,
949
1044
  coercion: params.coercion,
950
1045
  guidType: params.guidType,
1046
+ regexType: params.regexType,
951
1047
  dateType: params.dateType
952
1048
  });
953
1049
  return {
@@ -957,14 +1053,15 @@ function getStdPrinters(resolver, params) {
957
1053
  }
958
1054
  function getMiniPrinter(resolver, params) {
959
1055
  const cached = zodMiniPrinterCache.get(resolver);
960
- if (cached && cached.guidType === params.guidType) return cached.printer;
1056
+ if (cached && cached.guidType === params.guidType && cached.regexType === params.regexType) return cached.printer;
961
1057
  const p = printerZodMini({
962
1058
  ...params,
963
1059
  resolver
964
1060
  });
965
1061
  zodMiniPrinterCache.set(resolver, {
966
1062
  printer: p,
967
- guidType: params.guidType
1063
+ guidType: params.guidType,
1064
+ regexType: params.regexType
968
1065
  });
969
1066
  return p;
970
1067
  }
@@ -979,13 +1076,13 @@ const zodGenerator = defineGenerator({
979
1076
  renderer: jsxRenderer,
980
1077
  schema(node, ctx) {
981
1078
  const { adapter, config, resolver, root } = ctx;
982
- const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, printer } = ctx.options;
1079
+ const { output, coercion, guidType, regexType, mini, wrapOutput, inferred, importPath, group, printer } = ctx.options;
983
1080
  const dateType = adapter.options.dateType;
984
1081
  if (!node.name) return;
985
1082
  const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath);
986
1083
  const cyclicSchemas = new Set(ctx.meta.circularNames);
987
1084
  const hasCodec = !mini && containsCodec(node);
988
- const codecRefNames = new Set(hasCodec ? ast.collect(node, { schema: (n) => n.type === "ref" && n.ref && containsCodec(n) ? extractRefName(n.ref) ?? void 0 : void 0 }) : []);
1085
+ const codecRefNames = new Set(hasCodec ? collectCodecRefNames(node) : []);
989
1086
  const importEntries = adapter.getImports(node, (schemaName) => ({
990
1087
  name: resolver.resolveSchemaName(schemaName),
991
1088
  path: resolver.resolveFile({
@@ -1030,6 +1127,7 @@ const zodGenerator = defineGenerator({
1030
1127
  const stdPrinters = mini ? null : getStdPrinters(resolver, {
1031
1128
  coercion,
1032
1129
  guidType,
1130
+ regexType,
1033
1131
  dateType,
1034
1132
  wrapOutput,
1035
1133
  cyclicSchemas,
@@ -1037,6 +1135,7 @@ const zodGenerator = defineGenerator({
1037
1135
  });
1038
1136
  const schemaPrinter = mini ? getMiniPrinter(resolver, {
1039
1137
  guidType,
1138
+ regexType,
1040
1139
  wrapOutput,
1041
1140
  cyclicSchemas,
1042
1141
  nodes: printer?.nodes
@@ -1094,10 +1193,10 @@ const zodGenerator = defineGenerator({
1094
1193
  operation(node, ctx) {
1095
1194
  if (!ast.isHttpOperationNode(node)) return null;
1096
1195
  const { adapter, config, resolver, root } = ctx;
1097
- const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, paramsCasing, printer } = ctx.options;
1196
+ const { output, coercion, guidType, regexType, mini, wrapOutput, inferred, importPath, group, printer } = ctx.options;
1098
1197
  const dateType = adapter.options.dateType;
1099
1198
  const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath);
1100
- const params = ast.caseParams(node.parameters, paramsCasing);
1199
+ const params = caseParams(node.parameters, "camelcase");
1101
1200
  const meta = { file: resolver.resolveFile({
1102
1201
  name: node.operationId,
1103
1202
  extname: ".ts",
@@ -1112,7 +1211,7 @@ const zodGenerator = defineGenerator({
1112
1211
  function renderSchemaEntry({ schema, name, keysToOmit, direction = "output" }) {
1113
1212
  if (!schema) return null;
1114
1213
  const inferTypeName = inferred ? resolver.resolveTypeName(name) : null;
1115
- const codecRefNames = direction === "input" && !mini ? new Set(ast.collect(schema, { schema: (n) => n.type === "ref" && n.ref && containsCodec(n) ? extractRefName(n.ref) ?? void 0 : void 0 })) : null;
1214
+ const codecRefNames = direction === "input" && !mini ? new Set(collectCodecRefNames(schema)) : null;
1116
1215
  const imports = adapter.getImports(schema, (schemaName) => ({
1117
1216
  name: codecRefNames?.has(schemaName) ? resolver.resolveInputSchemaName(schemaName) : resolver.resolveSchemaName(schemaName),
1118
1217
  path: resolver.resolveFile({
@@ -1126,6 +1225,7 @@ const zodGenerator = defineGenerator({
1126
1225
  }));
1127
1226
  const schemaPrinter = mini ? keysToOmit?.length ? printerZodMini({
1128
1227
  guidType,
1228
+ regexType,
1129
1229
  wrapOutput,
1130
1230
  resolver,
1131
1231
  keysToOmit,
@@ -1133,12 +1233,14 @@ const zodGenerator = defineGenerator({
1133
1233
  nodes: printer?.nodes
1134
1234
  }) : getMiniPrinter(resolver, {
1135
1235
  guidType,
1236
+ regexType,
1136
1237
  wrapOutput,
1137
1238
  cyclicSchemas,
1138
1239
  nodes: printer?.nodes
1139
1240
  }) : keysToOmit?.length ? printerZod({
1140
1241
  coercion,
1141
1242
  guidType,
1243
+ regexType,
1142
1244
  dateType,
1143
1245
  wrapOutput,
1144
1246
  resolver,
@@ -1149,6 +1251,7 @@ const zodGenerator = defineGenerator({
1149
1251
  }) : getStdPrinters(resolver, {
1150
1252
  coercion,
1151
1253
  guidType,
1254
+ regexType,
1152
1255
  dateType,
1153
1256
  wrapOutput,
1154
1257
  cyclicSchemas,
@@ -1171,9 +1274,9 @@ const zodGenerator = defineGenerator({
1171
1274
  }
1172
1275
  function buildContentTypeVariants(entries, baseName, decorate, direction) {
1173
1276
  const variants = resolveContentTypeVariants(entries, baseName);
1174
- const unionSchema = ast.createSchema({
1277
+ const unionSchema = ast.factory.createSchema({
1175
1278
  type: "union",
1176
- members: variants.map((variant) => ast.createSchema({
1279
+ members: variants.map((variant) => ast.factory.createSchema({
1177
1280
  type: "ref",
1178
1281
  name: variant.name
1179
1282
  }))
@@ -1205,18 +1308,23 @@ const zodGenerator = defineGenerator({
1205
1308
  });
1206
1309
  });
1207
1310
  const responsesWithSchema = node.responses.filter((res) => res.content?.some((entry) => entry.schema));
1311
+ const successResponsesWithSchema = getSuccessResponses(responsesWithSchema);
1208
1312
  const responseUnionSchema = responsesWithSchema.length > 0 ? (() => {
1209
1313
  const responseUnionName = resolver.resolveResponseName(node);
1210
- if (new Set(responsesWithSchema.flatMap((res) => (res.content ?? []).flatMap((entry) => entry.schema ? adapter.getImports(entry.schema, (schemaName) => ({
1314
+ if (new Set(successResponsesWithSchema.flatMap((res) => (res.content ?? []).flatMap((entry) => entry.schema ? adapter.getImports(entry.schema, (schemaName) => ({
1211
1315
  name: resolver.resolveSchemaName(schemaName),
1212
1316
  path: ""
1213
1317
  })).flatMap((imp) => Array.isArray(imp.name) ? imp.name : [imp.name]) : []))).has(responseUnionName)) return null;
1214
- const members = responsesWithSchema.map((res) => ast.createSchema({
1318
+ const members = successResponsesWithSchema.map((res) => ast.factory.createSchema({
1215
1319
  type: "ref",
1216
1320
  name: resolver.resolveResponseStatusName(node, res.statusCode)
1217
1321
  }));
1322
+ if (members.length === 0) return renderSchemaEntry({
1323
+ schema: ast.factory.createSchema({ type: "unknown" }),
1324
+ name: responseUnionName
1325
+ });
1218
1326
  return renderSchemaEntry({
1219
- schema: members.length === 1 ? members[0] : ast.createSchema({
1327
+ schema: members.length === 1 ? members[0] : ast.factory.createSchema({
1220
1328
  type: "union",
1221
1329
  members
1222
1330
  }),
@@ -1279,7 +1387,7 @@ const zodGenerator = defineGenerator({
1279
1387
  },
1280
1388
  operations(nodes, ctx) {
1281
1389
  const { config, resolver, root } = ctx;
1282
- const { output, importPath, group, operations, paramsCasing } = ctx.options;
1390
+ const { output, importPath, group, operations } = ctx.options;
1283
1391
  if (!operations) return;
1284
1392
  const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath);
1285
1393
  const meta = { file: resolver.resolveFile({
@@ -1294,7 +1402,7 @@ const zodGenerator = defineGenerator({
1294
1402
  return {
1295
1403
  node,
1296
1404
  data: buildSchemaNames(node, {
1297
- params: ast.caseParams(node.parameters, paramsCasing),
1405
+ params: caseParams(node.parameters, "camelcase"),
1298
1406
  resolver
1299
1407
  })
1300
1408
  };
@@ -1436,8 +1544,9 @@ const pluginZodName = "plugin-zod";
1436
1544
  /**
1437
1545
  * Generates Zod v4 schemas from an OpenAPI spec. Use them to validate API
1438
1546
  * responses at runtime, build form schemas, or feed back into router libraries
1439
- * that consume Zod (tRPC, Hono, Elysia). Pair with `@kubb/plugin-client` and
1440
- * set the client's `parser: 'zod'` to validate every response automatically.
1547
+ * that consume Zod (tRPC, Hono, Elysia). Pair with `@kubb/plugin-axios` or
1548
+ * `@kubb/plugin-fetch` and set the client's `parser: 'zod'` to validate every response
1549
+ * automatically.
1441
1550
  *
1442
1551
  * @example
1443
1552
  * ```ts
@@ -1462,7 +1571,7 @@ const pluginZod = definePlugin((options) => {
1462
1571
  const { output = {
1463
1572
  path: "zod",
1464
1573
  barrel: { type: "named" }
1465
- }, group, exclude = [], include, override = [], typed = false, operations = false, mini = false, guidType = "uuid", importPath = mini ? "zod/mini" : "zod", coercion = false, inferred = false, wrapOutput = void 0, paramsCasing, printer, resolver: userResolver, transformer: userTransformer, generators: userGenerators = [] } = options;
1574
+ }, group, exclude = [], include, override = [], typed = false, operations = false, mini = false, guidType = "uuid", regexType = "literal", importPath = mini ? "zod/mini" : "zod", coercion = false, inferred = false, wrapOutput = void 0, printer, resolver: userResolver, macros: userMacros } = options;
1466
1575
  const groupConfig = createGroupConfig(group);
1467
1576
  return {
1468
1577
  name: pluginZodName,
@@ -1480,18 +1589,17 @@ const pluginZod = definePlugin((options) => {
1480
1589
  operations,
1481
1590
  inferred,
1482
1591
  guidType,
1592
+ regexType,
1483
1593
  mini,
1484
1594
  wrapOutput,
1485
- paramsCasing,
1486
1595
  printer
1487
1596
  });
1488
1597
  ctx.setResolver(userResolver ? {
1489
1598
  ...resolverZod,
1490
1599
  ...userResolver
1491
1600
  } : resolverZod);
1492
- if (userTransformer) ctx.setTransformer(userTransformer);
1601
+ if (userMacros?.length) ctx.setMacros(userMacros);
1493
1602
  ctx.addGenerator(zodGenerator);
1494
- for (const gen of userGenerators) ctx.addGenerator(gen);
1495
1603
  } }
1496
1604
  };
1497
1605
  });