@kubb/plugin-zod 5.0.0-beta.42 → 5.0.0-beta.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -10,6 +10,7 @@ var __name = (target, value) => __defProp(target, "name", {
10
10
  });
11
11
  //#endregion
12
12
  let _kubb_core = require("@kubb/core");
13
+ let _kubb_ast_utils = require("@kubb/ast/utils");
13
14
  let _kubb_renderer_jsx = require("@kubb/renderer-jsx");
14
15
  let _kubb_renderer_jsx_jsx_runtime = require("@kubb/renderer-jsx/jsx-runtime");
15
16
  //#region ../../internals/utils/src/casing.ts
@@ -23,117 +24,57 @@ let _kubb_renderer_jsx_jsx_runtime = require("@kubb/renderer-jsx/jsx-runtime");
23
24
  function toCamelOrPascal(text, pascal) {
24
25
  return text.trim().replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/(\d)([a-z])/g, "$1 $2").split(/[\s\-_./\\:]+/).filter(Boolean).map((word, i) => {
25
26
  if (word.length > 1 && word === word.toUpperCase()) return word;
26
- if (i === 0 && !pascal) return word.charAt(0).toLowerCase() + word.slice(1);
27
- return word.charAt(0).toUpperCase() + word.slice(1);
27
+ return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
28
28
  }).join("").replace(/[^a-zA-Z0-9]/g, "");
29
29
  }
30
30
  /**
31
- * Splits `text` on `.` and applies `transformPart` to each segment.
32
- * The last segment receives `isLast = true`, all earlier segments receive `false`.
33
- * Segments are joined with `/` to form a file path.
34
- *
35
- * Only splits on dots followed by a letter so that version numbers
36
- * embedded in operationIds (e.g. `v2025.0`) are kept intact.
37
- */
38
- function applyToFileParts(text, transformPart) {
39
- const parts = text.split(/\.(?=[a-zA-Z])/);
40
- return parts.map((part, i) => transformPart(part, i === parts.length - 1)).join("/");
41
- }
42
- /**
43
31
  * Converts `text` to camelCase.
44
- * When `isFile` is `true`, dot-separated segments are each cased independently and joined with `/`.
45
32
  *
46
- * @example
47
- * camelCase('hello-world') // 'helloWorld'
48
- * camelCase('pet.petId', { isFile: true }) // 'pet/petId'
33
+ * @example Word boundaries
34
+ * `camelCase('hello-world') // 'helloWorld'`
35
+ *
36
+ * @example With a prefix
37
+ * `camelCase('tag', { prefix: 'create' }) // 'createTag'`
49
38
  */
50
- function camelCase(text, { isFile, prefix = "", suffix = "" } = {}) {
51
- if (isFile) return applyToFileParts(text, (part, isLast) => camelCase(part, isLast ? {
52
- prefix,
53
- suffix
54
- } : {}));
39
+ function camelCase(text, { prefix = "", suffix = "" } = {}) {
55
40
  return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
56
41
  }
57
42
  /**
58
43
  * Converts `text` to PascalCase.
59
- * When `isFile` is `true`, the last dot-separated segment is PascalCased and earlier segments are camelCased.
60
44
  *
61
- * @example
62
- * pascalCase('hello-world') // 'HelloWorld'
63
- * pascalCase('pet.petId', { isFile: true }) // 'pet/PetId'
45
+ * @example Word boundaries
46
+ * `pascalCase('hello-world') // 'HelloWorld'`
47
+ *
48
+ * @example With a suffix
49
+ * `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`
64
50
  */
65
- function pascalCase(text, { isFile, prefix = "", suffix = "" } = {}) {
66
- if (isFile) return applyToFileParts(text, (part, isLast) => isLast ? pascalCase(part, {
67
- prefix,
68
- suffix
69
- }) : camelCase(part));
51
+ function pascalCase(text, { prefix = "", suffix = "" } = {}) {
70
52
  return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
71
53
  }
72
54
  //#endregion
73
- //#region ../../internals/utils/src/string.ts
55
+ //#region ../../internals/utils/src/fs.ts
74
56
  /**
75
- * Strips a single matching pair of `"..."`, `'...'`, or `` `...` `` from both ends of `text`.
76
- * Returns the string unchanged when no balanced quote pair is found.
57
+ * Builds a nested file path from a dotted name. Splits on dots that precede a letter
58
+ * (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
59
+ * every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
77
60
  *
78
- * @example
79
- * trimQuotes('"hello"') // 'hello'
80
- * trimQuotes('hello') // 'hello'
81
- */
82
- function trimQuotes(text) {
83
- if (text.length >= 2) {
84
- const first = text[0];
85
- const last = text[text.length - 1];
86
- if (first === "\"" && last === "\"" || first === "'" && last === "'" || first === "`" && last === "`") return text.slice(1, -1);
87
- }
88
- return text;
89
- }
90
- //#endregion
91
- //#region ../../internals/utils/src/object.ts
92
- /**
93
- * Serializes a primitive value to a JSON string literal, stripping any surrounding quote characters first.
61
+ * Empty segments are dropped before joining. They arise when the name starts with a dot
62
+ * followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
63
+ * an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
64
+ * absolute path, letting generated files escape the configured output directory.
94
65
  *
95
- * @example
96
- * stringify('hello') // '"hello"'
97
- * stringify('"hello"') // '"hello"'
98
- */
99
- function stringify(value) {
100
- if (value === void 0 || value === null) return "\"\"";
101
- return JSON.stringify(trimQuotes(value.toString()));
102
- }
103
- /**
104
- * Converts a plain object into a multiline key-value string suitable for embedding in generated code.
105
- * Nested objects are recursively stringified with indentation.
66
+ * @example Nested path from a dotted name
67
+ * `toFilePath('pet.petId') // 'pet/petId'`
106
68
  *
107
- * @example
108
- * stringifyObject({ foo: 'bar', nested: { a: 1 } })
109
- * // 'foo: bar,\nnested: {\n a: 1\n }'
110
- */
111
- function stringifyObject(value) {
112
- return Object.entries(value).map(([key, val]) => {
113
- if (val !== null && typeof val === "object") return `${key}: {\n ${stringifyObject(val)}\n }`;
114
- return `${key}: ${val}`;
115
- }).filter(Boolean).join(",\n");
116
- }
117
- //#endregion
118
- //#region ../../internals/utils/src/regexp.ts
119
- /**
120
- * Converts a pattern string into a `new RegExp(...)` constructor call or a regex literal string.
121
- * Inline flags expressed as `^(?im)` prefixes are extracted and applied to the resulting expression.
122
- * Pass `null` as the second argument to emit a `/pattern/flags` literal instead.
69
+ * @example PascalCase the final segment
70
+ * `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
123
71
  *
124
- * @example
125
- * toRegExpString('^(?im)foo') // 'new RegExp("foo", "im")'
126
- * toRegExpString('^(?im)foo', null) // → '/foo/im'
72
+ * @example Suffix applied to the final segment only
73
+ * `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
127
74
  */
128
- function toRegExpString(text, func = "RegExp") {
129
- const raw = trimQuotes(text);
130
- const match = raw.match(/^\^(\(\?([igmsuy]+)\))/i);
131
- const replacementTarget = match?.[1] ?? "";
132
- const matchedFlags = match?.[2];
133
- const cleaned = raw.replace(/^\\?\//, "").replace(/\\?\/$/, "").replace(replacementTarget, "");
134
- const { source, flags } = new RegExp(cleaned, matchedFlags);
135
- if (func === null) return `/${source}/${flags}`;
136
- return `new ${func}(${JSON.stringify(source)}${flags ? `, ${JSON.stringify(flags)}` : ""})`;
75
+ function toFilePath(name, caseLast = camelCase) {
76
+ const parts = name.split(/\.(?=[a-zA-Z])/);
77
+ return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
137
78
  }
138
79
  //#endregion
139
80
  //#region ../../internals/utils/src/reserved.ts
@@ -315,26 +256,24 @@ function resolveContentTypeVariants(entries, baseName) {
315
256
  * shared default naming so every plugin groups output consistently:
316
257
  *
317
258
  * - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
318
- * - other groups use `${camelCase(group)}${suffix}` (e.g. `petController`).
259
+ * - other groups use the camelCased group (`pet store``petStore`).
319
260
  *
320
261
  * A user-provided `group.name` always wins over the default namer, so callers stay in
321
262
  * control of their output folders. Returns `null` when grouping is disabled, matching the
322
263
  * per-plugin convention.
323
264
  *
324
265
  * @param group - The user-supplied group option, or `undefined` to disable grouping.
325
- * @param options.suffix - Appended to non-`path` group names, e.g. `'Controller'` or `'Requests'`.
326
266
  *
327
267
  * @example
328
268
  * ```ts
329
- * createGroupConfig(group, { suffix: 'Controller' }) // plugin-ts, plugin-client, …
330
- * createGroupConfig(group, { suffix: 'Requests' }) // plugin-cypress, plugin-mcp
269
+ * createGroupConfig(group) // shared across every plugin
331
270
  * ```
332
271
  */
333
- function createGroupConfig(group, options) {
272
+ function createGroupConfig(group) {
334
273
  if (!group) return null;
335
274
  const defaultName = (ctx) => {
336
275
  if (group.type === "path") return `${ctx.group.split("/")[1]}`;
337
- return `${camelCase(ctx.group)}${options.suffix}`;
276
+ return camelCase(ctx.group);
338
277
  };
339
278
  return {
340
279
  ...group,
@@ -399,7 +338,7 @@ function Operations({ name, operations }) {
399
338
  export: true,
400
339
  name,
401
340
  asConst: true,
402
- children: `{${stringifyObject(operationsJSON)}}`
341
+ children: `{${(0, _kubb_ast_utils.stringifyObject)(operationsJSON)}}`
403
342
  })
404
343
  }),
405
344
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
@@ -410,7 +349,7 @@ function Operations({ name, operations }) {
410
349
  export: true,
411
350
  name: "paths",
412
351
  asConst: true,
413
- children: `{${stringifyObject(pathsJSON)}}`
352
+ children: `{${(0, _kubb_ast_utils.stringifyObject)(pathsJSON)}}`
414
353
  })
415
354
  })
416
355
  ] });
@@ -497,12 +436,12 @@ function containsCodec(node, seen = /* @__PURE__ */ new Set()) {
497
436
  if (hasCodec(node)) return true;
498
437
  if (node.type === "ref") {
499
438
  if (!node.ref) return false;
500
- const refName = _kubb_core.ast.extractRefName(node.ref);
439
+ const refName = (0, _kubb_ast_utils.extractRefName)(node.ref);
501
440
  if (refName) {
502
441
  if (seen.has(refName)) return false;
503
442
  seen.add(refName);
504
443
  }
505
- const resolved = _kubb_core.ast.syncSchemaRef(node);
444
+ const resolved = (0, _kubb_ast_utils.syncSchemaRef)(node);
506
445
  if (resolved.type === "ref") return false;
507
446
  return containsCodec(resolved, seen);
508
447
  }
@@ -514,6 +453,13 @@ function containsCodec(node, seen = /* @__PURE__ */ new Set()) {
514
453
  return children.some((child) => containsCodec(child, seen));
515
454
  }
516
455
  /**
456
+ * Collects the names of `$ref` schemas that transitively contain a codec, so the generator can route
457
+ * them to their input (encode) variant.
458
+ */
459
+ function collectCodecRefNames(node) {
460
+ return _kubb_core.ast.collect(node, { schema: (n) => n.type === "ref" && n.ref && containsCodec(n) ? (0, _kubb_ast_utils.extractRefName)(n.ref) ?? void 0 : void 0 });
461
+ }
462
+ /**
517
463
  * Collects all resolved schema names for an operation's parameters and responses
518
464
  * into a single lookup object, useful for building imports and type references.
519
465
  */
@@ -548,7 +494,7 @@ function buildSchemaNames(node, { params, resolver }) {
548
494
  * Objects become `{}`, primitives become their string representation, strings are quoted.
549
495
  */
550
496
  function formatDefault(value) {
551
- if (typeof value === "string") return stringify(value);
497
+ if (typeof value === "string") return (0, _kubb_ast_utils.stringify)(value);
552
498
  if (typeof value === "object" && value !== null) return "{}";
553
499
  return String(value ?? "");
554
500
  }
@@ -557,7 +503,7 @@ function formatDefault(value) {
557
503
  * Strings are quoted; numbers and booleans are emitted raw.
558
504
  */
559
505
  function formatLiteral(v) {
560
- if (typeof v === "string") return stringify(v);
506
+ if (typeof v === "string") return (0, _kubb_ast_utils.stringify)(v);
561
507
  return String(v);
562
508
  }
563
509
  /**
@@ -581,7 +527,7 @@ function lengthConstraints({ min, max, pattern }) {
581
527
  return [
582
528
  min !== void 0 ? `.min(${min})` : "",
583
529
  max !== void 0 ? `.max(${max})` : "",
584
- pattern !== void 0 ? `.regex(${toRegExpString(pattern, null)})` : ""
530
+ pattern !== void 0 ? `.regex(${(0, _kubb_ast_utils.toRegExpString)(pattern, null)})` : ""
585
531
  ].join("");
586
532
  }
587
533
  /**
@@ -603,7 +549,7 @@ function lengthChecksMini({ min, max, pattern }) {
603
549
  const checks = [];
604
550
  if (min !== void 0) checks.push(`z.minLength(${min})`);
605
551
  if (max !== void 0) checks.push(`z.maxLength(${max})`);
606
- if (pattern !== void 0) checks.push(`z.regex(${toRegExpString(pattern, null)})`);
552
+ if (pattern !== void 0) checks.push(`z.regex(${(0, _kubb_ast_utils.toRegExpString)(pattern, null)})`);
607
553
  return checks.length ? `.check(${checks.join(", ")})` : "";
608
554
  }
609
555
  /**
@@ -618,7 +564,7 @@ function applyModifiers({ value, nullable, optional, nullish, defaultValue, desc
618
564
  return value;
619
565
  })();
620
566
  const withDefault = defaultValue !== void 0 ? `${withModifier}.default(${formatDefault(defaultValue)})` : withModifier;
621
- return description ? `${withDefault}.describe(${stringify(description)})` : withDefault;
567
+ return description ? `${withDefault}.describe(${(0, _kubb_ast_utils.stringify)(description)})` : withDefault;
622
568
  }
623
569
  /**
624
570
  * Apply nullable / optional / nullish modifiers using the functional `zod/mini` API
@@ -638,7 +584,7 @@ function strictOneOfMember$1(member, node) {
638
584
  if (node.type === "object" && node.additionalProperties === void 0) return `${member}.strict()`;
639
585
  if (node.type === "ref") {
640
586
  if (member.startsWith("z.lazy(")) return member;
641
- const schema = _kubb_core.ast.syncSchemaRef(node);
587
+ const schema = (0, _kubb_ast_utils.syncSchemaRef)(node);
642
588
  if (schema.type === "object" && (schema.additionalProperties === void 0 || schema.additionalProperties === false)) return `${member}.strict()`;
643
589
  }
644
590
  return member;
@@ -723,24 +669,24 @@ const printerZod = _kubb_core.ast.definePrinter((options) => {
723
669
  },
724
670
  ref(node) {
725
671
  if (!node.name) return null;
726
- const refName = node.ref ? _kubb_core.ast.extractRefName(node.ref) ?? node.name : node.name;
672
+ const refName = node.ref ? (0, _kubb_ast_utils.extractRefName)(node.ref) ?? node.name : node.name;
727
673
  const useInputVariant = node.ref != null && this.options.direction === "input" && containsCodec(node);
728
674
  const resolvedName = node.ref ? useInputVariant ? this.options.resolver?.resolveInputSchemaName(refName) ?? refName : this.options.resolver?.default(refName, "function") ?? refName : node.name;
729
675
  if (node.ref && this.options.cyclicSchemas?.has(refName)) return `z.lazy(() => ${resolvedName})`;
730
676
  return resolvedName;
731
677
  },
732
678
  object(node) {
733
- const objectBase = `z.object({\n ${node.properties.map((prop) => {
734
- const { name: propName, schema } = prop;
735
- const meta = _kubb_core.ast.syncSchemaRef(schema);
736
- const isNullable = meta.nullable;
737
- const isOptional = schema.optional;
738
- const isNullish = schema.nullish;
739
- const hasSelfRef = this.options.cyclicSchemas != null && _kubb_core.ast.containsCircularRef(schema, { circularSchemas: this.options.cyclicSchemas });
679
+ const isCyclic = (schema) => this.options.cyclicSchemas != null && (0, _kubb_ast_utils.containsCircularRef)(schema, { circularSchemas: this.options.cyclicSchemas });
680
+ const objectBase = `z.object(${(0, _kubb_ast_utils.buildObject)((0, _kubb_ast_utils.mapSchemaProperties)(node, (schema) => {
681
+ const hasSelfRef = isCyclic(schema);
740
682
  const savedCyclicSchemas = this.options.cyclicSchemas;
741
683
  if (hasSelfRef) this.options.cyclicSchemas = void 0;
742
- const baseOutput = this.transform(schema) ?? this.transform(_kubb_core.ast.createSchema({ type: "unknown" }));
684
+ const baseOutput = this.transform(schema) ?? this.transform(_kubb_core.ast.factory.createSchema({ type: "unknown" }));
743
685
  if (hasSelfRef) this.options.cyclicSchemas = savedCyclicSchemas;
686
+ return baseOutput;
687
+ }).map(({ name: propName, property, output: baseOutput }) => {
688
+ const { schema } = property;
689
+ const meta = (0, _kubb_ast_utils.syncSchemaRef)(schema);
744
690
  const wrappedOutput = this.options.wrapOutput ? this.options.wrapOutput({
745
691
  output: baseOutput,
746
692
  schema
@@ -748,42 +694,41 @@ const printerZod = _kubb_core.ast.definePrinter((options) => {
748
694
  const descriptionToApply = schema.type !== "ref" && meta.type === "ref" ? void 0 : meta.description;
749
695
  const value = applyModifiers({
750
696
  value: wrappedOutput,
751
- nullable: isNullable,
752
- optional: isOptional,
753
- nullish: isNullish,
697
+ nullable: meta.nullable,
698
+ optional: schema.optional || property.required === false,
699
+ nullish: schema.nullish,
754
700
  defaultValue: meta.default,
755
701
  description: descriptionToApply
756
702
  });
757
- if (hasSelfRef) return `get "${propName}"() { return ${value} }`;
758
- return `"${propName}": ${value}`;
759
- }).join(",\n ")}\n })`;
703
+ return isCyclic(schema) ? (0, _kubb_ast_utils.lazyGetter)({
704
+ name: propName,
705
+ body: value
706
+ }) : `${(0, _kubb_ast_utils.objectKey)(propName)}: ${value}`;
707
+ }))})`;
760
708
  return (() => {
761
709
  if (node.additionalProperties && node.additionalProperties !== true) {
762
710
  const catchallType = this.transform(node.additionalProperties);
763
711
  return catchallType ? `${objectBase}.catchall(${catchallType})` : objectBase;
764
712
  }
765
- if (node.additionalProperties === true) return `${objectBase}.catchall(${this.transform(_kubb_core.ast.createSchema({ type: "unknown" }))})`;
713
+ if (node.additionalProperties === true) return `${objectBase}.catchall(${this.transform(_kubb_core.ast.factory.createSchema({ type: "unknown" }))})`;
766
714
  if (node.additionalProperties === false) return `${objectBase}.strict()`;
767
715
  return objectBase;
768
716
  })();
769
717
  },
770
718
  array(node) {
771
- const base = `z.array(${(node.items ?? []).map((item) => this.transform(item)).filter(Boolean).join(", ") || this.transform(_kubb_core.ast.createSchema({ type: "unknown" }))})${lengthConstraints(node)}`;
719
+ const base = `z.array(${(0, _kubb_ast_utils.mapSchemaItems)(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean).join(", ") || this.transform(_kubb_core.ast.factory.createSchema({ type: "unknown" }))})${lengthConstraints(node)}`;
772
720
  return node.unique ? `${base}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })` : base;
773
721
  },
774
722
  tuple(node) {
775
- return `z.tuple([${(node.items ?? []).map((item) => this.transform(item)).filter(Boolean).join(", ")}])`;
723
+ return `z.tuple(${(0, _kubb_ast_utils.buildList)((0, _kubb_ast_utils.mapSchemaItems)(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean))})`;
776
724
  },
777
725
  union(node) {
778
726
  const nodeMembers = node.members ?? [];
779
- const members = nodeMembers.map((memberNode) => {
780
- const member = this.transform(memberNode);
781
- return member && node.strategy === "one" ? strictOneOfMember$1(member, memberNode) : member;
782
- }).filter(Boolean);
727
+ const members = (0, _kubb_ast_utils.mapSchemaMembers)(node, (memberNode) => this.transform(memberNode)).map(({ schema, output }) => output && node.strategy === "one" ? strictOneOfMember$1(output, schema) : output).filter(Boolean);
783
728
  if (members.length === 0) return "";
784
729
  if (members.length === 1) return members[0];
785
- if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === "intersection")) return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, [${members.join(", ")}])`;
786
- return `z.union([${members.join(", ")}])`;
730
+ if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === "intersection")) return `z.discriminatedUnion(${(0, _kubb_ast_utils.stringify)(node.discriminatorPropertyName)}, ${(0, _kubb_ast_utils.buildList)(members)})`;
731
+ return `z.union(${(0, _kubb_ast_utils.buildList)(members)})`;
787
732
  },
788
733
  intersection(node) {
789
734
  const members = node.members ?? [];
@@ -805,7 +750,7 @@ const printerZod = _kubb_core.ast.definePrinter((options) => {
805
750
  const { keysToOmit } = this.options;
806
751
  const transformed = this.transform(node);
807
752
  if (!transformed) return null;
808
- const meta = _kubb_core.ast.syncSchemaRef(node);
753
+ const meta = (0, _kubb_ast_utils.syncSchemaRef)(node);
809
754
  return applyModifiers({
810
755
  value: (() => {
811
756
  if (!keysToOmit?.length || meta.primitive !== "object" || meta.type === "union" && meta.discriminatorPropertyName) return transformed;
@@ -902,54 +847,53 @@ const printerZodMini = _kubb_core.ast.definePrinter((options) => {
902
847
  },
903
848
  ref(node) {
904
849
  if (!node.name) return null;
905
- const refName = node.ref ? _kubb_core.ast.extractRefName(node.ref) ?? node.name : node.name;
850
+ const refName = node.ref ? (0, _kubb_ast_utils.extractRefName)(node.ref) ?? node.name : node.name;
906
851
  const resolvedName = node.ref ? this.options.resolver?.default(refName, "function") ?? refName : node.name;
907
852
  if (node.ref && this.options.cyclicSchemas?.has(refName)) return `z.lazy(() => ${resolvedName})`;
908
853
  return resolvedName;
909
854
  },
910
855
  object(node) {
911
- return `z.object({\n ${node.properties.map((prop) => {
912
- const { name: propName, schema } = prop;
913
- const meta = _kubb_core.ast.syncSchemaRef(schema);
914
- const isNullable = meta.nullable;
915
- const isOptional = schema.optional;
916
- const isNullish = schema.nullish;
917
- const hasSelfRef = this.options.cyclicSchemas != null && _kubb_core.ast.containsCircularRef(schema, { circularSchemas: this.options.cyclicSchemas });
856
+ const isCyclic = (schema) => this.options.cyclicSchemas != null && (0, _kubb_ast_utils.containsCircularRef)(schema, { circularSchemas: this.options.cyclicSchemas });
857
+ return `z.object(${(0, _kubb_ast_utils.buildObject)((0, _kubb_ast_utils.mapSchemaProperties)(node, (schema) => {
858
+ const hasSelfRef = isCyclic(schema);
918
859
  const savedCyclicSchemas = this.options.cyclicSchemas;
919
860
  if (hasSelfRef) this.options.cyclicSchemas = void 0;
920
- const baseOutput = this.transform(schema) ?? this.transform(_kubb_core.ast.createSchema({ type: "unknown" }));
861
+ const baseOutput = this.transform(schema) ?? this.transform(_kubb_core.ast.factory.createSchema({ type: "unknown" }));
921
862
  if (hasSelfRef) this.options.cyclicSchemas = savedCyclicSchemas;
863
+ return baseOutput;
864
+ }).map(({ name: propName, property, output: baseOutput }) => {
865
+ const { schema } = property;
866
+ const meta = (0, _kubb_ast_utils.syncSchemaRef)(schema);
922
867
  const value = applyMiniModifiers({
923
868
  value: this.options.wrapOutput ? this.options.wrapOutput({
924
869
  output: baseOutput,
925
870
  schema
926
871
  }) || baseOutput : baseOutput,
927
- nullable: isNullable,
928
- optional: isOptional,
929
- nullish: isNullish,
872
+ nullable: meta.nullable,
873
+ optional: schema.optional || property.required === false,
874
+ nullish: schema.nullish,
930
875
  defaultValue: meta.default
931
876
  });
932
- if (hasSelfRef) return `get "${propName}"() { return ${value} }`;
933
- return `"${propName}": ${value}`;
934
- }).join(",\n ")}\n })`;
877
+ return isCyclic(schema) ? (0, _kubb_ast_utils.lazyGetter)({
878
+ name: propName,
879
+ body: value
880
+ }) : `${(0, _kubb_ast_utils.objectKey)(propName)}: ${value}`;
881
+ }))})`;
935
882
  },
936
883
  array(node) {
937
- const base = `z.array(${(node.items ?? []).map((item) => this.transform(item)).filter(Boolean).join(", ") || this.transform(_kubb_core.ast.createSchema({ type: "unknown" }))})${lengthChecksMini(node)}`;
884
+ const base = `z.array(${(0, _kubb_ast_utils.mapSchemaItems)(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean).join(", ") || this.transform(_kubb_core.ast.factory.createSchema({ type: "unknown" }))})${lengthChecksMini(node)}`;
938
885
  return node.unique ? `${base}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })` : base;
939
886
  },
940
887
  tuple(node) {
941
- return `z.tuple([${(node.items ?? []).map((item) => this.transform(item)).filter(Boolean).join(", ")}])`;
888
+ return `z.tuple(${(0, _kubb_ast_utils.buildList)((0, _kubb_ast_utils.mapSchemaItems)(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean))})`;
942
889
  },
943
890
  union(node) {
944
891
  const nodeMembers = node.members ?? [];
945
- const members = nodeMembers.map((memberNode) => {
946
- const member = this.transform(memberNode);
947
- return member && node.strategy === "one" ? strictOneOfMember(member, memberNode) : member;
948
- }).filter(Boolean);
892
+ const members = (0, _kubb_ast_utils.mapSchemaMembers)(node, (memberNode) => this.transform(memberNode)).map(({ schema, output }) => output && node.strategy === "one" ? strictOneOfMember(output, schema) : output).filter(Boolean);
949
893
  if (members.length === 0) return "";
950
894
  if (members.length === 1) return members[0];
951
- if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === "intersection")) return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, [${members.join(", ")}])`;
952
- return `z.union([${members.join(", ")}])`;
895
+ if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === "intersection")) return `z.discriminatedUnion(${(0, _kubb_ast_utils.stringify)(node.discriminatorPropertyName)}, ${(0, _kubb_ast_utils.buildList)(members)})`;
896
+ return `z.union(${(0, _kubb_ast_utils.buildList)(members)})`;
953
897
  },
954
898
  intersection(node) {
955
899
  const members = node.members ?? [];
@@ -971,7 +915,7 @@ const printerZodMini = _kubb_core.ast.definePrinter((options) => {
971
915
  const { keysToOmit } = this.options;
972
916
  const transformed = this.transform(node);
973
917
  if (!transformed) return null;
974
- const meta = _kubb_core.ast.syncSchemaRef(node);
918
+ const meta = (0, _kubb_ast_utils.syncSchemaRef)(node);
975
919
  return applyMiniModifiers({
976
920
  value: (() => {
977
921
  if (!keysToOmit?.length || meta.primitive !== "object" || meta.type === "union" && meta.discriminatorPropertyName) return transformed;
@@ -1047,17 +991,16 @@ function getMiniPrinter(resolver, params) {
1047
991
  */
1048
992
  const zodGenerator = (0, _kubb_core.defineGenerator)({
1049
993
  name: "zod",
1050
- renderer: _kubb_renderer_jsx.jsxRendererSync,
994
+ renderer: _kubb_renderer_jsx.jsxRenderer,
1051
995
  schema(node, ctx) {
1052
996
  const { adapter, config, resolver, root } = ctx;
1053
997
  const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, printer } = ctx.options;
1054
998
  const dateType = adapter.options.dateType;
1055
999
  if (!node.name) return;
1056
- const mode = ctx.getMode(output);
1057
1000
  const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath);
1058
1001
  const cyclicSchemas = new Set(ctx.meta.circularNames);
1059
1002
  const hasCodec = !mini && containsCodec(node);
1060
- const codecRefNames = new Set(hasCodec ? _kubb_core.ast.collect(node, { schema: (n) => n.type === "ref" && n.ref && containsCodec(n) ? _kubb_core.ast.extractRefName(n.ref) ?? void 0 : void 0 }) : []);
1003
+ const codecRefNames = new Set(hasCodec ? collectCodecRefNames(node) : []);
1061
1004
  const importEntries = adapter.getImports(node, (schemaName) => ({
1062
1005
  name: resolver.resolveSchemaName(schemaName),
1063
1006
  path: resolver.resolveFile({
@@ -1070,7 +1013,7 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
1070
1013
  }).path
1071
1014
  }));
1072
1015
  const inputImportEntries = hasCodec ? [...codecRefNames].map((schemaName) => ({
1073
- name: resolver.resolveInputSchemaName(schemaName),
1016
+ name: [resolver.resolveInputSchemaName(schemaName)],
1074
1017
  path: resolver.resolveFile({
1075
1018
  name: schemaName,
1076
1019
  extname: ".ts"
@@ -1139,7 +1082,7 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
1139
1082
  path: importPath,
1140
1083
  isNameSpace: isZodImport
1141
1084
  }),
1142
- mode === "split" && imports.map((imp) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1085
+ imports.map((imp) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1143
1086
  root: meta.file.path,
1144
1087
  path: imp.path,
1145
1088
  name: imp.name
@@ -1168,9 +1111,8 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
1168
1111
  const { adapter, config, resolver, root } = ctx;
1169
1112
  const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, paramsCasing, printer } = ctx.options;
1170
1113
  const dateType = adapter.options.dateType;
1171
- const mode = ctx.getMode(output);
1172
1114
  const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath);
1173
- const params = _kubb_core.ast.caseParams(node.parameters, paramsCasing);
1115
+ const params = (0, _kubb_ast_utils.caseParams)(node.parameters, paramsCasing);
1174
1116
  const meta = { file: resolver.resolveFile({
1175
1117
  name: node.operationId,
1176
1118
  extname: ".ts",
@@ -1185,7 +1127,7 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
1185
1127
  function renderSchemaEntry({ schema, name, keysToOmit, direction = "output" }) {
1186
1128
  if (!schema) return null;
1187
1129
  const inferTypeName = inferred ? resolver.resolveTypeName(name) : null;
1188
- const codecRefNames = direction === "input" && !mini ? new Set(_kubb_core.ast.collect(schema, { schema: (n) => n.type === "ref" && n.ref && containsCodec(n) ? _kubb_core.ast.extractRefName(n.ref) ?? void 0 : void 0 })) : null;
1130
+ const codecRefNames = direction === "input" && !mini ? new Set(collectCodecRefNames(schema)) : null;
1189
1131
  const imports = adapter.getImports(schema, (schemaName) => ({
1190
1132
  name: codecRefNames?.has(schemaName) ? resolver.resolveInputSchemaName(schemaName) : resolver.resolveSchemaName(schemaName),
1191
1133
  path: resolver.resolveFile({
@@ -1227,7 +1169,7 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
1227
1169
  cyclicSchemas,
1228
1170
  nodes: printer?.nodes
1229
1171
  })[direction];
1230
- return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [mode === "split" && imports.map((imp) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1172
+ return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [imports.map((imp) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1231
1173
  root: meta.file.path,
1232
1174
  path: imp.path,
1233
1175
  name: imp.name
@@ -1244,9 +1186,9 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
1244
1186
  }
1245
1187
  function buildContentTypeVariants(entries, baseName, decorate, direction) {
1246
1188
  const variants = resolveContentTypeVariants(entries, baseName);
1247
- const unionSchema = _kubb_core.ast.createSchema({
1189
+ const unionSchema = _kubb_core.ast.factory.createSchema({
1248
1190
  type: "union",
1249
- members: variants.map((variant) => _kubb_core.ast.createSchema({
1191
+ members: variants.map((variant) => _kubb_core.ast.factory.createSchema({
1250
1192
  type: "ref",
1251
1193
  name: variant.name
1252
1194
  }))
@@ -1284,12 +1226,12 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
1284
1226
  name: resolver.resolveSchemaName(schemaName),
1285
1227
  path: ""
1286
1228
  })).flatMap((imp) => Array.isArray(imp.name) ? imp.name : [imp.name]) : []))).has(responseUnionName)) return null;
1287
- const members = responsesWithSchema.map((res) => _kubb_core.ast.createSchema({
1229
+ const members = responsesWithSchema.map((res) => _kubb_core.ast.factory.createSchema({
1288
1230
  type: "ref",
1289
1231
  name: resolver.resolveResponseStatusName(node, res.statusCode)
1290
1232
  }));
1291
1233
  return renderSchemaEntry({
1292
- schema: members.length === 1 ? members[0] : _kubb_core.ast.createSchema({
1234
+ schema: members.length === 1 ? members[0] : _kubb_core.ast.factory.createSchema({
1293
1235
  type: "union",
1294
1236
  members
1295
1237
  }),
@@ -1367,7 +1309,7 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
1367
1309
  return {
1368
1310
  node,
1369
1311
  data: buildSchemaNames(node, {
1370
- params: _kubb_core.ast.caseParams(node.parameters, paramsCasing),
1312
+ params: (0, _kubb_ast_utils.caseParams)(node.parameters, paramsCasing),
1371
1313
  resolver
1372
1314
  })
1373
1315
  };
@@ -1435,14 +1377,16 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
1435
1377
  /**
1436
1378
  * Default resolver used by `@kubb/plugin-zod`. Decides the names and file
1437
1379
  * paths for every generated Zod schema. Schemas use camelCase with a
1438
- * `Schema` suffix (`listPetsSchema`); their inferred types use PascalCase.
1380
+ * `Schema` suffix (`listPetsSchema`); their inferred types use PascalCase
1381
+ * with a `SchemaType` suffix (`PetSchemaType`), so the value and the type
1382
+ * never share an identifier even when the schema name is all-uppercase.
1439
1383
  *
1440
1384
  * @example Resolve schema and type names
1441
1385
  * ```ts
1442
1386
  * import { resolverZod } from '@kubb/plugin-zod'
1443
1387
  *
1444
1388
  * resolverZod.default('list pets', 'function') // 'listPetsSchema'
1445
- * resolverZod.resolveSchemaTypeName('pet') // 'PetSchema'
1389
+ * resolverZod.resolveSchemaTypeName('pet') // 'PetSchemaType'
1446
1390
  * ```
1447
1391
  */
1448
1392
  const resolverZod = (0, _kubb_core.defineResolver)(() => {
@@ -1450,17 +1394,14 @@ const resolverZod = (0, _kubb_core.defineResolver)(() => {
1450
1394
  name: "default",
1451
1395
  pluginName: "plugin-zod",
1452
1396
  default(name, type) {
1453
- const resolved = camelCase(name, {
1454
- isFile: type === "file",
1455
- suffix: type ? "schema" : void 0
1456
- });
1457
- return type === "file" ? resolved : ensureValidVarName(resolved);
1397
+ if (type === "file") return toFilePath(name, (part) => camelCase(part, { suffix: "schema" }));
1398
+ return ensureValidVarName(camelCase(name, { suffix: type ? "schema" : void 0 }));
1458
1399
  },
1459
1400
  resolveSchemaName(name) {
1460
1401
  return ensureValidVarName(camelCase(name, { suffix: "schema" }));
1461
1402
  },
1462
1403
  resolveSchemaTypeName(name) {
1463
- return ensureValidVarName(pascalCase(name, { suffix: "schema" }));
1404
+ return ensureValidVarName(pascalCase(name, { suffix: "schema type" }));
1464
1405
  },
1465
1406
  resolveInputSchemaName(name) {
1466
1407
  return this.resolveSchemaName(`${name} input`);
@@ -1469,7 +1410,7 @@ const resolverZod = (0, _kubb_core.defineResolver)(() => {
1469
1410
  return this.resolveSchemaTypeName(`${name} input`);
1470
1411
  },
1471
1412
  resolveTypeName(name) {
1472
- return ensureValidVarName(pascalCase(name));
1413
+ return ensureValidVarName(pascalCase(name, { suffix: "type" }));
1473
1414
  },
1474
1415
  resolvePathName(name, type) {
1475
1416
  return this.default(name, type);
@@ -1535,9 +1476,9 @@ const pluginZodName = "plugin-zod";
1535
1476
  const pluginZod = (0, _kubb_core.definePlugin)((options) => {
1536
1477
  const { output = {
1537
1478
  path: "zod",
1538
- barrelType: "named"
1539
- }, 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;
1540
- const groupConfig = createGroupConfig(group, { suffix: "Controller" });
1479
+ barrel: { type: "named" }
1480
+ }, 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, macros: userMacros, generators: userGenerators = [] } = options;
1481
+ const groupConfig = createGroupConfig(group);
1541
1482
  return {
1542
1483
  name: pluginZodName,
1543
1484
  options,
@@ -1563,7 +1504,7 @@ const pluginZod = (0, _kubb_core.definePlugin)((options) => {
1563
1504
  ...resolverZod,
1564
1505
  ...userResolver
1565
1506
  } : resolverZod);
1566
- if (userTransformer) ctx.setTransformer(userTransformer);
1507
+ if (userMacros?.length) ctx.setMacros(userMacros);
1567
1508
  ctx.addGenerator(zodGenerator);
1568
1509
  for (const gen of userGenerators) ctx.addGenerator(gen);
1569
1510
  } }