@kubb/plugin-zod 5.7.2 → 5.7.4

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
@@ -2,11 +2,10 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- //#endregion
6
5
  let kubb_kit = require("kubb/kit");
7
6
  let kubb_jsx = require("kubb/jsx");
8
7
  let kubb_jsx_jsx_runtime = require("kubb/jsx/jsx-runtime");
9
- //#region ../../internals/shared/src/operation.ts
8
+ // ../../internals/shared/src/operation.ts
10
9
  /**
11
10
  * Maps a content type to the PascalCase suffix used to name per-content-type variants
12
11
  * (e.g. `application/json` → `Json`, `application/xml` → `Xml`, `multipart/form-data` → `FormData`).
@@ -89,7 +88,10 @@ function getOperationParameters(node) {
89
88
  * shape from the same inputs. `primitive: 'object'` is a no-op for the TS printer and tells the Zod
90
89
  * printer to emit `z.object(…)` rather than a record.
91
90
  */
92
- function buildOptionsSchema(node, resolver) {
91
+ function hasRequestContentTypeChoice(node) {
92
+ return (node.requestBody?.content ?? []).filter((entry) => entry.schema).length > 1;
93
+ }
94
+ function buildOptionsSchema(node, resolver, { pairContentTypes = false } = {}) {
93
95
  const { path, query, header } = getOperationParameters(node);
94
96
  const hasBody = Boolean(node.requestBody?.content?.[0]?.schema);
95
97
  const createNever = () => kubb_kit.ast.factory.createSchema({
@@ -114,29 +116,57 @@ function buildOptionsSchema(node, resolver) {
114
116
  resolve: resolver.param.headers
115
117
  }
116
118
  ];
117
- return kubb_kit.ast.factory.createSchema({
119
+ const createOptions = (body, extra = []) => kubb_kit.ast.factory.createSchema({
118
120
  type: "object",
119
121
  primitive: "object",
120
122
  deprecated: node.deprecated,
121
- properties: [kubb_kit.ast.factory.createProperty({
122
- name: "body",
123
- required: hasBody,
124
- schema: hasBody ? kubb_kit.ast.factory.createSchema({
125
- type: "ref",
126
- name: resolver.response.body(node)
127
- }) : createNever()
128
- }), ...groups.map(({ name, params, resolve }) => {
129
- const required = params.some((param) => param.required);
130
- return kubb_kit.ast.factory.createProperty({
131
- name,
132
- required,
133
- schema: params.length > 0 ? kubb_kit.ast.factory.createSchema({
134
- type: "ref",
135
- name: resolve.call(resolver.param, node, params[0]),
136
- optional: !required
137
- }) : createNever()
138
- });
139
- })]
123
+ properties: [
124
+ kubb_kit.ast.factory.createProperty({
125
+ name: "body",
126
+ required: hasBody,
127
+ schema: body
128
+ }),
129
+ ...groups.map(({ name, params, resolve }) => {
130
+ const required = params.some((param) => param.required);
131
+ return kubb_kit.ast.factory.createProperty({
132
+ name,
133
+ required,
134
+ schema: params.length > 0 ? kubb_kit.ast.factory.createSchema({
135
+ type: "ref",
136
+ name: resolve.call(resolver.param, node, params[0]),
137
+ optional: !required
138
+ }) : createNever()
139
+ });
140
+ }),
141
+ ...extra
142
+ ]
143
+ });
144
+ const variants = pairContentTypes && hasRequestContentTypeChoice(node) ? resolveContentTypeVariants(node.requestBody?.content ?? [], resolver.response.body(node)) : [];
145
+ if (variants.length < 2) return createOptions(hasBody ? kubb_kit.ast.factory.createSchema({
146
+ type: "ref",
147
+ name: resolver.response.body(node)
148
+ }) : createNever());
149
+ return kubb_kit.ast.factory.createSchema({
150
+ type: "union",
151
+ members: variants.map((variant, index) => createOptions(kubb_kit.ast.factory.createSchema({
152
+ type: "ref",
153
+ name: variant.name
154
+ }), [kubb_kit.ast.factory.createProperty({
155
+ name: "contentType",
156
+ required: index > 0,
157
+ schema: kubb_kit.ast.factory.createSchema({
158
+ type: "object",
159
+ primitive: "object",
160
+ properties: [kubb_kit.ast.factory.createProperty({
161
+ name: "request",
162
+ required: true,
163
+ schema: kubb_kit.ast.factory.createSchema({
164
+ type: "enum",
165
+ enumValues: [variant.contentType]
166
+ })
167
+ })]
168
+ })
169
+ })]))
140
170
  });
141
171
  }
142
172
  /**
@@ -206,8 +236,7 @@ function isSuccessStatusCode(statusCode) {
206
236
  function getSuccessResponses(responses) {
207
237
  return responses.filter((response) => isSuccessStatusCode(response.statusCode));
208
238
  }
209
- //#endregion
210
- //#region ../../internals/shared/src/adapter.ts
239
+ // ../../internals/shared/src/adapter.ts
211
240
  /**
212
241
  * Narrows the generic `Adapter` from a generator context to the OpenAPI adapter,
213
242
  * so OAS-only options (`dateType`, `enums`) and the parsed `document` are typed.
@@ -230,58 +259,7 @@ function getOasAdapter(adapter) {
230
259
  });
231
260
  return adapter;
232
261
  }
233
- //#endregion
234
- //#region ../../internals/utils/src/casing.ts
235
- /**
236
- * Shared implementation for camelCase and PascalCase conversion.
237
- * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
238
- * and capitalizes each word according to `pascal`.
239
- *
240
- * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
241
- */
242
- function convert(text, pascal) {
243
- 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) => {
244
- if (word.length > 1 && word === word.toUpperCase()) return word;
245
- return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
246
- }).join("").replace(/[^a-zA-Z0-9]/g, "");
247
- }
248
- const cache = /* @__PURE__ */ new Map();
249
- const cacheLimit = 1e4;
250
- function toCamelOrPascal(text, pascal) {
251
- const key = `${pascal ? "P" : "c"}${text}`;
252
- const cached = cache.get(key);
253
- if (cached !== void 0) return cached;
254
- const result = convert(text, pascal);
255
- if (cache.size >= cacheLimit) cache.clear();
256
- cache.set(key, result);
257
- return result;
258
- }
259
- /**
260
- * Converts `text` to camelCase.
261
- *
262
- * @example Word boundaries
263
- * `camelCase('hello-world') // 'helloWorld'`
264
- *
265
- * @example With a prefix
266
- * `camelCase('tag', { prefix: 'create' }) // 'createTag'`
267
- */
268
- function camelCase(text, { prefix = "", suffix = "" } = {}) {
269
- return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
270
- }
271
- /**
272
- * Converts `text` to PascalCase.
273
- *
274
- * @example Word boundaries
275
- * `pascalCase('hello-world') // 'HelloWorld'`
276
- *
277
- * @example With a suffix
278
- * `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`
279
- */
280
- function pascalCase(text, { prefix = "", suffix = "" } = {}) {
281
- return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
282
- }
283
- //#endregion
284
- //#region ../../internals/utils/src/reserved.ts
262
+ // ../../internals/utils/src/reserved.ts
285
263
  /**
286
264
  * JavaScript and Java reserved words.
287
265
  * @link https://github.com/jonschlinkert/reserved/blob/master/index.js
@@ -289,6 +267,7 @@ function pascalCase(text, { prefix = "", suffix = "" } = {}) {
289
267
  const reservedWords = /* @__PURE__ */ new Set([
290
268
  "abstract",
291
269
  "arguments",
270
+ "await",
292
271
  "boolean",
293
272
  "break",
294
273
  "byte",
@@ -419,8 +398,7 @@ function ensureValidVarName(name) {
419
398
  function isIdentifier(name) {
420
399
  return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
421
400
  }
422
- //#endregion
423
- //#region ../../internals/utils/src/strings.ts
401
+ // ../../internals/utils/src/strings.ts
424
402
  /**
425
403
  * Wraps a value in single quotes for emitting a single-quoted JavaScript string literal, escaping
426
404
  * any backslash or single quote in the content.
@@ -469,6 +447,34 @@ function stringify(value) {
469
447
  if (value === void 0 || value === null) return "''";
470
448
  return `'${JSON.stringify(trimQuotes(value.toString())).slice(1, -1).replace(/\\"/g, "\"").replace(/'/g, "\\'")}'`;
471
449
  }
450
+ const NEEDS_UNICODE = /(?<!\\)(?:\\\\)*\\[pPu]\{|[\u{10000}-\u{10FFFF}]/u;
451
+ const KEPT_ESCAPE = /[A-Za-z0-9^$\\.*+?()[\]{}|/]/;
452
+ /**
453
+ * Drops the backslash from escapes that the `u` flag rejects: an escaped punctuation character that is
454
+ * not a syntax character, and `\-` outside a class. Both match the same character without the backslash.
455
+ */
456
+ function dropIdentityEscapes(pattern) {
457
+ let inClass = false;
458
+ return pattern.replace(/\\(.)|[[\]]/gs, (match, escaped) => {
459
+ if (escaped === void 0) {
460
+ inClass = match === "[";
461
+ return match;
462
+ }
463
+ return KEPT_ESCAPE.test(escaped) || escaped === "-" && inClass ? match : escaped;
464
+ });
465
+ }
466
+ /**
467
+ * Builds the `RegExp` for a pattern, adding the `u` flag when it uses `\p{...}`, `\P{...}` or `\u{...}`, or
468
+ * holds a character outside the BMP. A pattern that is not valid under `u` keeps the flags it came with.
469
+ */
470
+ function toRegExp(pattern, flags) {
471
+ if (flags?.includes("u") || !NEEDS_UNICODE.test(pattern)) return new RegExp(pattern, flags);
472
+ try {
473
+ return new RegExp(dropIdentityEscapes(pattern), `${flags ?? ""}u`);
474
+ } catch {
475
+ return new RegExp(pattern, flags);
476
+ }
477
+ }
472
478
  /**
473
479
  * Converts a pattern string into a `new RegExp(...)` constructor call or a regex literal string.
474
480
  * Inline flags expressed as a `^(?im)` prefix are extracted and applied to the resulting expression.
@@ -485,13 +491,11 @@ function toRegExpString(text, func = "RegExp") {
485
491
  const match = raw.match(/^\^(\(\?([igmsuy]+)\))/i);
486
492
  const replacementTarget = match?.[1] ?? "";
487
493
  const matchedFlags = match?.[2];
488
- const cleaned = raw.replace(/^\\?\//, "").replace(/\\?\/$/, "").replace(replacementTarget, "");
489
- const { source, flags } = new RegExp(cleaned, matchedFlags);
494
+ const { source, flags } = toRegExp(raw.replace(/^\\?\//, "").replace(/\\?\/$/, "").replace(replacementTarget, ""), matchedFlags);
490
495
  if (func === null) return `/${source}/${flags}`;
491
496
  return `new ${func}(${JSON.stringify(source)}${flags ? `, ${JSON.stringify(flags)}` : ""})`;
492
497
  }
493
- //#endregion
494
- //#region ../../internals/utils/src/codegen.ts
498
+ // ../../internals/utils/src/codegen.ts
495
499
  const INDENT = " ";
496
500
  /**
497
501
  * Indents every non-empty line of `text` by one indent level, leaving blank lines empty.
@@ -560,8 +564,7 @@ function buildList(items, brackets = ["[", "]"]) {
560
564
  function lazyGetter({ name, body }) {
561
565
  return `get ${objectKey(name)}() { return ${body} }`;
562
566
  }
563
- //#endregion
564
- //#region ../../internals/utils/src/fs.ts
567
+ // ../../internals/utils/src/fs.ts
565
568
  /**
566
569
  * Builds a nested file path from a dotted name. Splits on dots that precede a letter
567
570
  * (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
@@ -581,21 +584,28 @@ function lazyGetter({ name, body }) {
581
584
  * @example Suffix applied to the final segment only
582
585
  * `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
583
586
  */
584
- function toFilePath(name, caseLast = camelCase) {
587
+ function toFilePath(name, caseLast = kubb_kit.camelCase) {
585
588
  const parts = name.split(/\.(?=[a-zA-Z])/);
586
- return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
589
+ return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : (0, kubb_kit.camelCase)(part)).filter(Boolean).join("/");
587
590
  }
588
- //#endregion
589
- //#region ../../internals/shared/src/resolver.ts
591
+ // ../../internals/shared/src/resolver.ts
590
592
  /**
591
- * Resolves a single operation parameter name with the
592
- * `<operationId> <in> <name>` template.
593
+ * Builds the allocation key for a declaration an operation owns. `scope` names which declaration
594
+ * it is, so two of them never share an allocation even when a custom resolver gives both the same
595
+ * name. Leave `scope` empty for the operation's own declaration.
593
596
  *
594
597
  * @example
595
- * `operationParamName.call(resolver, node, param) // → 'DeletePetPathPetId'`
598
+ * ```ts
599
+ * resolver.resolveName({ key: operationKey(node, 'response', 'status', 404), name })
600
+ * ```
596
601
  */
597
- function operationParamName(node, param) {
598
- return this.name(`${node.operationId} ${param.in} ${param.name}`);
602
+ function operationKey(node, ...scope) {
603
+ return [
604
+ "operation",
605
+ node.method,
606
+ node.path,
607
+ ...scope.map(String)
608
+ ];
599
609
  }
600
610
  /**
601
611
  * Builds the shared `param` namespace. Spread the result into `createResolver`
@@ -608,15 +618,33 @@ function operationParamName(node, param) {
608
618
  */
609
619
  function createOperationParamResolver() {
610
620
  return {
611
- name: operationParamName,
621
+ name(node, param) {
622
+ const name = this.name(`${node.operationId} ${param.in} ${param.name}`);
623
+ return this.resolveName({
624
+ key: operationKey(node, "param", "name", param.in, param.name),
625
+ name
626
+ });
627
+ },
612
628
  path(node) {
613
- return this.name(`${node.operationId} Path`);
629
+ const name = this.name(`${node.operationId} Path`);
630
+ return this.resolveName({
631
+ key: operationKey(node, "param", "path"),
632
+ name
633
+ });
614
634
  },
615
635
  query(node) {
616
- return this.name(`${node.operationId} Query`);
636
+ const name = this.name(`${node.operationId} Query`);
637
+ return this.resolveName({
638
+ key: operationKey(node, "param", "query"),
639
+ name
640
+ });
617
641
  },
618
642
  headers(node) {
619
- return this.name(`${node.operationId} Headers`);
643
+ const name = this.name(`${node.operationId} Headers`);
644
+ return this.resolveName({
645
+ key: operationKey(node, "param", "headers"),
646
+ name
647
+ });
620
648
  }
621
649
  };
622
650
  }
@@ -633,16 +661,32 @@ function createOperationParamResolver() {
633
661
  function createOperationResponseResolver() {
634
662
  return {
635
663
  status(node, statusCode) {
636
- return this.name(`${node.operationId} Status ${statusCode}`);
664
+ const name = this.name(`${node.operationId} Status ${statusCode}`);
665
+ return this.resolveName({
666
+ key: operationKey(node, "response", "status", statusCode),
667
+ name
668
+ });
637
669
  },
638
670
  body(node) {
639
- return this.name(`${node.operationId} Body`);
671
+ const name = this.name(`${node.operationId} Body`);
672
+ return this.resolveName({
673
+ key: operationKey(node, "response", "body"),
674
+ name
675
+ });
640
676
  },
641
677
  responses(node) {
642
- return this.name(`${node.operationId} Responses`);
678
+ const name = this.name(`${node.operationId} Responses`);
679
+ return this.resolveName({
680
+ key: operationKey(node, "response", "responses"),
681
+ name
682
+ });
643
683
  },
644
684
  response(node) {
645
- return this.name(`${node.operationId} Response`);
685
+ const name = this.name(`${node.operationId} Response`);
686
+ return this.resolveName({
687
+ key: operationKey(node, "response", "response"),
688
+ name
689
+ });
646
690
  }
647
691
  };
648
692
  }
@@ -657,11 +701,10 @@ function createOperationResponseResolver() {
657
701
  */
658
702
  function createCasedFile(caseLast) {
659
703
  return { baseName({ name, extname }) {
660
- return `${toFilePath(name, caseLast)}${extname}`;
704
+ return this.memo(`file:${name}\0${extname}`, () => `${toFilePath(name, caseLast)}${extname}`);
661
705
  } };
662
706
  }
663
- //#endregion
664
- //#region ../../internals/shared/src/group.ts
707
+ // ../../internals/shared/src/group.ts
665
708
  /**
666
709
  * Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
667
710
  * shared default naming so every plugin groups output consistently:
@@ -684,15 +727,14 @@ function createGroupConfig(group) {
684
727
  if (!group) return null;
685
728
  const defaultName = (ctx) => {
686
729
  if (group.type === "path") return `${ctx.group.split("/")[1]}`;
687
- return camelCase(ctx.group);
730
+ return (0, kubb_kit.camelCase)(ctx.group);
688
731
  };
689
732
  return {
690
733
  ...group,
691
734
  name: group.name ? group.name : defaultName
692
735
  };
693
736
  }
694
- //#endregion
695
- //#region ../../internals/shared/src/schemaTraversal.ts
737
+ // ../../internals/shared/src/schemaTraversal.ts
696
738
  /**
697
739
  * Maps each property of an object schema to its transformed output. Pairs every result with the
698
740
  * original property so the printer keeps full control over modifiers, getters, and key syntax.
@@ -730,8 +772,7 @@ function mapSchemaItems(node, transform) {
730
772
  output: transform(schema)
731
773
  }));
732
774
  }
733
- //#endregion
734
- //#region src/components/Zod.tsx
775
+ // src/components/Zod.tsx
735
776
  function Zod({ name, node, printer, inferTypeName, inferKind = "infer", typeGuards, isName, assertName, mini, cyclic, compile }) {
736
777
  const output = printer.print(node);
737
778
  const printerImports = printer.drainImports();
@@ -803,8 +844,7 @@ function Zod({ name, node, printer, inferTypeName, inferKind = "infer", typeGuar
803
844
  })
804
845
  ] });
805
846
  }
806
- //#endregion
807
- //#region src/utils.ts
847
+ // src/utils.ts
808
848
  /**
809
849
  * Returns `true` when the given coercion option enables coercion for the specified type.
810
850
  */
@@ -833,11 +873,12 @@ function isClosedKeySchema(node) {
833
873
  }
834
874
  /**
835
875
  * Whether the node is a plain inline object whose shape can be lifted into an `.extend({ … })`
836
- * argument. A catchall, `patternProperties`, or a nullable/optional wrapper cannot, so those stay
876
+ * argument. Property counts, a catchall, `patternProperties`, or a nullable/optional wrapper cannot, so those stay
837
877
  * on `.and(…)`.
838
878
  */
839
879
  function isPlainInlineObject(node) {
840
880
  if (node.type !== "object") return false;
881
+ if (node.minProperties !== void 0 || node.maxProperties !== void 0) return false;
841
882
  if (node.nullable || node.optional || node.nullish) return false;
842
883
  const hasAdditionalProperties = node.additionalProperties !== void 0;
843
884
  const hasPatternProperties = Boolean(node.patternProperties);
@@ -848,11 +889,12 @@ function isPlainInlineObject(node) {
848
889
  * `z.discriminatedUnion` option. Covers object nodes, `$ref`s that resolve to (or are still
849
890
  * unresolved) objects, single-member unions of an object, and object-composable `allOf`.
850
891
  *
851
- * `cyclicSchemas` bounds the recursion: a ref into a cycle renders as `z.lazy(…)`, not an object.
892
+ * Property counts render as a pipe. A ref into `cyclicSchemas` renders as `z.lazy(…)`.
852
893
  */
853
894
  function isObjectSchemaNode(node, cyclicSchemas) {
854
895
  if (node.nullable || node.optional || node.nullish) return false;
855
896
  if (node.type === "object") {
897
+ if (node.minProperties !== void 0 || node.maxProperties !== void 0) return false;
856
898
  const hasProperties = Boolean(node.properties?.length);
857
899
  const hasTypedAdditionalProperties = Boolean(node.additionalProperties && node.additionalProperties !== true);
858
900
  const hasPatternProperties = Boolean(node.patternProperties);
@@ -880,6 +922,49 @@ function isObjectComposableIntersection(node, cyclicSchemas) {
880
922
  const [first, ...rest] = node.members ?? [];
881
923
  return !!first && isObjectSchemaNode(first, cyclicSchemas) && rest.every(isPlainInlineObject);
882
924
  }
925
+ function preserveRequiredProperties(members) {
926
+ const hasRequiredProperty = ({ node, propertyName, seen = /* @__PURE__ */ new Set() }) => {
927
+ if (seen.has(node)) return false;
928
+ seen.add(node);
929
+ const resolved = (0, kubb_kit.syncSchemaRef)(node);
930
+ const composition = kubb_kit.ast.narrowSchema(resolved, kubb_kit.ast.schemaTypes.intersection) ?? kubb_kit.ast.narrowSchema(resolved, kubb_kit.ast.schemaTypes.union);
931
+ if (composition) {
932
+ if (composition.type === kubb_kit.ast.schemaTypes.union && composition.members?.length !== 1) return false;
933
+ return composition.members?.some((child) => hasRequiredProperty({
934
+ node: child,
935
+ propertyName,
936
+ seen
937
+ })) ?? false;
938
+ }
939
+ return kubb_kit.ast.resolveSchemaProperties({
940
+ node: resolved,
941
+ propertyName
942
+ }).some((property) => property.required !== false && !property.schema.optional && !property.schema.nullish);
943
+ };
944
+ return members.map((member, index) => {
945
+ const object = kubb_kit.ast.narrowSchema(member, kubb_kit.ast.schemaTypes.object);
946
+ if (!object || index === 0) return member;
947
+ return {
948
+ ...object,
949
+ properties: object.properties.map((property) => {
950
+ if (!members.slice(0, index).some((base) => hasRequiredProperty({
951
+ node: base,
952
+ propertyName: property.name
953
+ }))) return property;
954
+ return {
955
+ ...property,
956
+ required: true,
957
+ schema: {
958
+ ...property.schema,
959
+ optional: false,
960
+ nullish: false,
961
+ nullable: property.schema.nullable || property.schema.nullish
962
+ }
963
+ };
964
+ })
965
+ };
966
+ });
967
+ }
883
968
  /**
884
969
  * Format a default value as a code-level literal.
885
970
  * Arrays and objects keep their contents as a JSON literal, primitives become their string
@@ -926,11 +1011,20 @@ function defaultLiteral(node, value) {
926
1011
  */
927
1012
  function buildEnum(values) {
928
1013
  if (values.every((v) => typeof v === "string")) return `z.enum([${values.map((v) => formatDefault(v)).join(", ")}])`;
929
- const literals = values.map((v) => `z.literal(${formatDefault(v)})`);
1014
+ const literals = values.map((value) => buildLiteral(value));
930
1015
  if (literals.length === 1) return literals[0];
931
1016
  return `z.union([${literals.join(", ")}])`;
932
1017
  }
933
1018
  /**
1019
+ * Preserves the structure of JSON const values in both Zod APIs.
1020
+ */
1021
+ function buildLiteral(value) {
1022
+ if (value === null) return "z.null()";
1023
+ if (Array.isArray(value)) return `z.tuple([${value.map((item) => buildLiteral(item)).join(", ")}])`;
1024
+ if (typeof value === "object") return `z.strictObject({${Object.entries(value).map(([name, item]) => `${JSON.stringify(name)}: ${buildLiteral(item)}`).join(", ")}})`;
1025
+ return `z.literal(${formatDefault(value)})`;
1026
+ }
1027
+ /**
934
1028
  * Digit pattern for a `type: 'string'` schema that carries an integer `format`, the way ProtoJSON
935
1029
  * encodes 64-bit integers. Returns `undefined` for every other format, and a `pattern` from the
936
1030
  * spec takes precedence over this fallback.
@@ -1013,6 +1107,17 @@ function lengthChecksMini({ min, max, pattern, regexType }) {
1013
1107
  return checks.length ? `.check(${checks.join(", ")})` : "";
1014
1108
  }
1015
1109
  /**
1110
+ * Checks property counts before object parsing strips unknown keys or inserts defaults.
1111
+ */
1112
+ function objectConstraints({ value, node }) {
1113
+ if (node.type !== "object") return value;
1114
+ const checks = [];
1115
+ const skip = "typeof value !== \"object\" || value === null || Array.isArray(value)";
1116
+ if (node.minProperties !== void 0) checks.push(`z.refine(value => ${skip} || Object.keys(value).length >= ${node.minProperties}, { message: "Object must have at least ${node.minProperties} properties" })`);
1117
+ if (node.maxProperties !== void 0) checks.push(`z.refine(value => ${skip} || Object.keys(value).length <= ${node.maxProperties}, { message: "Object must have at most ${node.maxProperties} properties" })`);
1118
+ return checks.length ? `z.pipe(z.unknown().check(${checks.join(", ")}), ${value})` : value;
1119
+ }
1120
+ /**
1016
1121
  * Apply nullable / optional / nullish modifiers, an optional `.describe()` call, and an
1017
1122
  * optional `.meta({ examples })` call to a schema value string using the chainable Zod v4 API.
1018
1123
  */
@@ -1102,7 +1207,7 @@ function getMemberConstraint({ member, regexType, lengths, numbers }) {
1102
1207
  /**
1103
1208
  * Builds the `{ key: value, … }` shape shared by the `z.object(...)` and `.extend(...)` renderings.
1104
1209
  */
1105
- function buildObjectShape(ctx, node, modify) {
1210
+ function buildObjectShape(ctx, node, renderProperty) {
1106
1211
  const objectNode = kubb_kit.ast.narrowSchema(node, "object");
1107
1212
  if (!objectNode) return "{}";
1108
1213
  const isCyclic = (schema) => ctx.options.cyclicSchemas != null && (0, kubb_kit.containsCircularRef)(schema, { circularSchemas: ctx.options.cyclicSchemas });
@@ -1113,18 +1218,16 @@ function buildObjectShape(ctx, node, modify) {
1113
1218
  const baseOutput = ctx.transform(schema) ?? ctx.transform(kubb_kit.ast.factory.createSchema({ type: "unknown" }));
1114
1219
  if (hasSelfRef) ctx.options.cyclicSchemas = savedCyclicSchemas;
1115
1220
  return baseOutput;
1116
- }).map(({ name: propName, property, output: baseOutput }) => {
1221
+ }).map(({ name, property, output: value }) => {
1117
1222
  const { schema } = property;
1118
- const value = modify({
1119
- value: baseOutput,
1223
+ return renderProperty({
1224
+ name,
1225
+ value,
1120
1226
  schema,
1121
1227
  required: property.required,
1122
- meta: (0, kubb_kit.syncSchemaRef)(schema)
1228
+ meta: (0, kubb_kit.syncSchemaRef)(schema),
1229
+ cyclic: isCyclic(schema)
1123
1230
  });
1124
- return isCyclic(schema) ? lazyGetter({
1125
- name: propName,
1126
- body: value
1127
- }) : `${objectKey(propName)}: ${value}`;
1128
1231
  }));
1129
1232
  }
1130
1233
  /**
@@ -1139,22 +1242,28 @@ function printWithOmit(ctx, node, modify, mini = false) {
1139
1242
  if (!keysToOmit?.length || meta.primitive !== "object" || meta.type === "union" && meta.discriminatorPropertyName) return transformed;
1140
1243
  const unwrap = omitUnwrapChain(node, mini ? "._zod.def.innerType" : ".unwrap()");
1141
1244
  const mask = `{ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} }`;
1142
- const omit = (target) => mini ? `z.omit(${target}${unwrap}, ${mask})` : `${target}${unwrap}.omit(${mask})`;
1245
+ const omit = (target) => {
1246
+ const inner = `${target}${unwrap}`;
1247
+ const hasObjectConstraints = meta.type === "object" && (meta.minProperties !== void 0 || meta.maxProperties !== void 0);
1248
+ const pipe = mini ? `${inner}._zod.def` : inner;
1249
+ const object = hasObjectConstraints ? `${pipe}.out` : inner;
1250
+ const omitted = mini ? `z.omit(${object}, ${mask})` : `${object}.omit(${mask})`;
1251
+ return hasObjectConstraints ? `z.pipe(${pipe}.in, ${omitted})` : omitted;
1252
+ };
1143
1253
  const lazyMatch = transformed.match(/^z\.lazy\(\(\)\s*=>\s*(.+)\)$/);
1144
1254
  if (lazyMatch) return `z.lazy(() => ${omit(lazyMatch[1])})`;
1145
1255
  return omit(transformed);
1146
1256
  })(), meta);
1147
1257
  }
1148
- //#endregion
1149
- //#region src/printers/printerZod.ts
1258
+ // src/printers/printerZod.ts
1150
1259
  function strictOneOfMember(member, node, cyclicSchemas) {
1151
- if (node.type === "ref") {
1260
+ if (node.type === kubb_kit.ast.schemaTypes.ref) {
1152
1261
  if (member.startsWith("z.lazy(")) return member;
1153
1262
  const refName = kubb_kit.ast.resolveRefName(node);
1154
1263
  if (refName && cyclicSchemas?.has(refName)) return member;
1155
1264
  const schema = (0, kubb_kit.syncSchemaRef)(node);
1156
1265
  if (schema.nullable || schema.optional || node.nullable || node.optional) return member;
1157
- if (schema.type === "object" && (schema.additionalProperties === void 0 || schema.additionalProperties === false)) return `${member}.strict()`;
1266
+ if (schema.type === kubb_kit.ast.schemaTypes.object && (schema.additionalProperties === void 0 || schema.additionalProperties === false)) return `${member}.strict()`;
1158
1267
  }
1159
1268
  return member;
1160
1269
  }
@@ -1162,28 +1271,70 @@ function strictOneOfMember(member, node, cyclicSchemas) {
1162
1271
  * Builds the `{ key: value, … }` shape for an object node with the chainable modifiers.
1163
1272
  */
1164
1273
  function buildZodObjectShape(ctx, node) {
1165
- return buildObjectShape(ctx, node, ({ value, schema, required, meta }) => applyModifiers({
1166
- value,
1167
- schema,
1168
- nullable: meta.nullable,
1169
- optional: schema.optional || required === false,
1170
- nullish: schema.nullish,
1171
- defaultValue: meta.default,
1172
- description: schema.type !== "ref" && meta.type === "ref" ? void 0 : meta.description,
1173
- examples: meta.examples
1174
- }));
1274
+ return buildObjectShape(ctx, node, ({ name, value, schema, required, meta, cyclic }) => {
1275
+ const body = applyModifiers({
1276
+ value,
1277
+ schema,
1278
+ nullable: meta.nullable,
1279
+ optional: schema.optional || required === false,
1280
+ nullish: schema.nullish,
1281
+ defaultValue: meta.default,
1282
+ description: schema.type !== kubb_kit.ast.schemaTypes.ref && meta.type === kubb_kit.ast.schemaTypes.ref ? void 0 : meta.description,
1283
+ examples: meta.examples
1284
+ });
1285
+ if (!cyclic) return `${objectKey(name)}: ${body}`;
1286
+ const returnType = recursiveGetterType({
1287
+ ctx,
1288
+ schema,
1289
+ required
1290
+ });
1291
+ return returnType ? `get ${objectKey(name)}(): ${returnType} { return ${body} }` : lazyGetter({
1292
+ name,
1293
+ body
1294
+ });
1295
+ });
1296
+ }
1297
+ /**
1298
+ * Breaks getter inference cycles without widening recursive refs and arrays to `z.ZodType`.
1299
+ */
1300
+ function recursiveGetterType({ ctx, schema, required }) {
1301
+ function baseType(schema) {
1302
+ if (ctx.options.nodes?.[schema.type]) return void 0;
1303
+ if (schema.type === kubb_kit.ast.schemaTypes.ref) {
1304
+ const saved = ctx.options.cyclicSchemas;
1305
+ ctx.options.cyclicSchemas = void 0;
1306
+ const name = ctx.transform(schema);
1307
+ ctx.options.cyclicSchemas = saved;
1308
+ return name ? `typeof ${name}` : void 0;
1309
+ }
1310
+ if (schema.type === kubb_kit.ast.schemaTypes.array && schema.items?.length === 1) {
1311
+ const item = baseType(schema.items[0]);
1312
+ return item ? `z.ZodArray<${item}>` : void 0;
1313
+ }
1314
+ }
1315
+ const base = baseType(schema);
1316
+ if (!base) return void 0;
1317
+ const meta = (0, kubb_kit.syncSchemaRef)(schema);
1318
+ const optional = schema.optional || required === false;
1319
+ const wrapped = (() => {
1320
+ if (schema.nullish || meta.nullable && optional) return `z.ZodOptional<z.ZodNullable<${base}>>`;
1321
+ if (optional) return `z.ZodOptional<${base}>`;
1322
+ if (meta.nullable) return `z.ZodNullable<${base}>`;
1323
+ return base;
1324
+ })();
1325
+ return meta.default !== void 0 && defaultLiteral(schema, meta.default) !== null ? `z.ZodDefault<${wrapped}>` : wrapped;
1175
1326
  }
1176
1327
  /**
1177
1328
  * Types the direction probe skips. They delegate to their children rather than reading
1178
1329
  * `direction` themselves, so {@link containsDirectionalNode} walks into the children instead.
1179
1330
  */
1180
1331
  const CONTAINER_TYPES = /* @__PURE__ */ new Set([
1181
- "object",
1182
- "array",
1183
- "tuple",
1184
- "union",
1185
- "intersection",
1186
- "ref"
1332
+ kubb_kit.ast.schemaTypes.object,
1333
+ kubb_kit.ast.schemaTypes.array,
1334
+ kubb_kit.ast.schemaTypes.tuple,
1335
+ kubb_kit.ast.schemaTypes.union,
1336
+ kubb_kit.ast.schemaTypes.intersection,
1337
+ kubb_kit.ast.schemaTypes.ref
1187
1338
  ]);
1188
1339
  /**
1189
1340
  * Runs the node's effective handler (a `printer.nodes` override, else the built-in) once per
@@ -1215,7 +1366,7 @@ function variesByDirection({ node, printerOptions }) {
1215
1366
  */
1216
1367
  function containsDirectionalNode({ node, printerOptions, seen = /* @__PURE__ */ new Set() }) {
1217
1368
  if (!node) return false;
1218
- if (node.type === "ref") {
1369
+ if (node.type === kubb_kit.ast.schemaTypes.ref) {
1219
1370
  if (!node.ref) return false;
1220
1371
  const refName = (0, kubb_kit.extractRefName)(node.ref);
1221
1372
  if (refName) {
@@ -1223,7 +1374,7 @@ function containsDirectionalNode({ node, printerOptions, seen = /* @__PURE__ */
1223
1374
  seen.add(refName);
1224
1375
  }
1225
1376
  const resolved = (0, kubb_kit.syncSchemaRef)(node);
1226
- if (resolved.type === "ref") return false;
1377
+ if (resolved.type === kubb_kit.ast.schemaTypes.ref) return false;
1227
1378
  return containsDirectionalNode({
1228
1379
  node: resolved,
1229
1380
  printerOptions,
@@ -1239,7 +1390,7 @@ function containsDirectionalNode({ node, printerOptions, seen = /* @__PURE__ */
1239
1390
  if ("items" in node && node.items) children.push(...node.items);
1240
1391
  if ("members" in node && node.members) children.push(...node.members);
1241
1392
  if ("additionalProperties" in node && node.additionalProperties && node.additionalProperties !== true) children.push(node.additionalProperties);
1242
- const propertyNames = node.type === "object" ? node.propertyNames : void 0;
1393
+ const propertyNames = node.type === kubb_kit.ast.schemaTypes.object ? node.propertyNames : void 0;
1243
1394
  if (propertyNames) children.push(propertyNames);
1244
1395
  return children.some((child) => containsDirectionalNode({
1245
1396
  node: child,
@@ -1251,7 +1402,7 @@ function containsDirectionalNode({ node, printerOptions, seen = /* @__PURE__ */
1251
1402
  * Names of the `$ref` schemas the generator should route to their input (encode) variant.
1252
1403
  */
1253
1404
  function collectDirectionalRefNames({ node, printerOptions }) {
1254
- return kubb_kit.ast.collectSync(node, { schema: (n) => n.type === "ref" && n.ref && containsDirectionalNode({
1405
+ return kubb_kit.ast.collectSync(node, { schema: (n) => n.type === kubb_kit.ast.schemaTypes.ref && n.ref && containsDirectionalNode({
1255
1406
  node: n,
1256
1407
  printerOptions
1257
1408
  }) ? kubb_kit.ast.resolveRefName(n) ?? void 0 : void 0 });
@@ -1343,7 +1494,7 @@ const scalarNodes = {
1343
1494
  enum(node) {
1344
1495
  const nonNullValues = (node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? []).filter((v) => v !== null);
1345
1496
  if (node.namedEnumValues?.length) {
1346
- const literals = nonNullValues.map((v) => `z.literal(${formatDefault(v)})`);
1497
+ const literals = nonNullValues.map((v) => buildLiteral(v));
1347
1498
  if (literals.length === 1) return literals[0];
1348
1499
  return `z.union([${literals.join(", ")}])`;
1349
1500
  }
@@ -1364,6 +1515,7 @@ const scalarNodes = {
1364
1515
  */
1365
1516
  const printerZod = kubb_kit.ast.createPrinter((options) => {
1366
1517
  const cyclicSchemaNames = options.cyclicSchemas;
1518
+ const unionsWithSiblings = /* @__PURE__ */ new WeakSet();
1367
1519
  return {
1368
1520
  name: "zod",
1369
1521
  options,
@@ -1389,37 +1541,40 @@ const printerZod = kubb_kit.ast.createPrinter((options) => {
1389
1541
  const isStrict = node.additionalProperties === false && patterns.length === 0;
1390
1542
  const isLoose = node.additionalProperties === true;
1391
1543
  const objectBase = `${isStrict ? "z.strictObject" : isLoose ? "z.looseObject" : "z.object"}(${buildZodObjectShape(this, node)})`;
1392
- return (() => {
1393
- const recordFn = isClosedKeySchema(propertyNamesNode) ? "z.partialRecord" : "z.record";
1394
- if (node.additionalProperties && node.additionalProperties !== true) {
1395
- const catchallType = this.transform(node.additionalProperties);
1396
- if (entries.length === 0) return catchallType ? `${recordFn}(${propertyNamesKeySchema || "z.string()"}, ${catchallType})` : objectBase;
1397
- return catchallType ? `${objectBase}.catchall(${catchallType})` : objectBase;
1398
- }
1399
- if (entries.length === 0 && patterns.length === 0 && propertyNamesKeySchema && !isStrict) {
1400
- const unknownType = this.transform(kubb_kit.ast.factory.createSchema({ type: "unknown" }));
1401
- return `${recordFn}(${propertyNamesKeySchema}, ${unknownType})`;
1402
- }
1403
- if (node.additionalProperties === true) return objectBase;
1404
- if (isStrict) return objectBase;
1405
- if (patterns.length > 0) {
1406
- const values = patterns.map(([, valueSchema]) => {
1407
- const valueType = this.transform(valueSchema) ?? this.transform(kubb_kit.ast.factory.createSchema({ type: "unknown" }));
1408
- return valueSchema.nullable ? `${valueType}.nullable()` : valueType;
1409
- });
1410
- const distinct = [...new Set(values)];
1411
- const value = distinct.length === 1 ? distinct[0] : `z.union([${distinct.join(", ")}])`;
1412
- if (entries.length > 0) return `${objectBase}.catchall(${value})`;
1413
- return `z.record(${patternKeySchema({
1414
- patterns: patterns.map(([pattern]) => pattern),
1415
- regexType: this.options.regexType
1416
- })}, ${value})`;
1417
- }
1418
- return objectBase;
1419
- })();
1544
+ return objectConstraints({
1545
+ value: (() => {
1546
+ const recordFn = isClosedKeySchema(propertyNamesNode) ? "z.partialRecord" : "z.record";
1547
+ if (node.additionalProperties && node.additionalProperties !== true) {
1548
+ const catchallType = this.transform(node.additionalProperties);
1549
+ if (entries.length === 0) return catchallType ? `${recordFn}(${propertyNamesKeySchema || "z.string()"}, ${catchallType})` : objectBase;
1550
+ return catchallType ? `${objectBase}.catchall(${catchallType})` : objectBase;
1551
+ }
1552
+ if (entries.length === 0 && patterns.length === 0 && propertyNamesKeySchema && !isStrict) {
1553
+ const unknownType = this.transform(kubb_kit.ast.factory.createSchema({ type: kubb_kit.ast.schemaTypes.unknown }));
1554
+ return `${recordFn}(${propertyNamesKeySchema}, ${unknownType})`;
1555
+ }
1556
+ if (node.additionalProperties === true) return objectBase;
1557
+ if (isStrict) return objectBase;
1558
+ if (patterns.length > 0) {
1559
+ const values = patterns.map(([, valueSchema]) => {
1560
+ const valueType = this.transform(valueSchema) ?? this.transform(kubb_kit.ast.factory.createSchema({ type: kubb_kit.ast.schemaTypes.unknown }));
1561
+ return valueSchema.nullable ? `${valueType}.nullable()` : valueType;
1562
+ });
1563
+ const distinct = [...new Set(values)];
1564
+ const value = distinct.length === 1 ? distinct[0] : `z.union([${distinct.join(", ")}])`;
1565
+ if (entries.length > 0) return `${objectBase}.catchall(${value})`;
1566
+ return `z.record(${patternKeySchema({
1567
+ patterns: patterns.map(([pattern]) => pattern),
1568
+ regexType: this.options.regexType
1569
+ })}, ${value})`;
1570
+ }
1571
+ return objectBase;
1572
+ })(),
1573
+ node
1574
+ });
1420
1575
  },
1421
1576
  array(node) {
1422
- const base = `z.array(${mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean).join(", ") || this.transform(kubb_kit.ast.factory.createSchema({ type: "unknown" }))})${lengthConstraints({
1577
+ const base = `z.array(${mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean).join(", ") || this.transform(kubb_kit.ast.factory.createSchema({ type: kubb_kit.ast.schemaTypes.unknown }))})${lengthConstraints({
1423
1578
  ...node,
1424
1579
  regexType: this.options.regexType
1425
1580
  })}`;
@@ -1432,7 +1587,7 @@ const printerZod = kubb_kit.ast.createPrinter((options) => {
1432
1587
  const nodeMembers = node.members ?? [];
1433
1588
  const isDiscriminated = !!node.discriminatorPropertyName && nodeMembers.every((m) => isObjectSchemaNode(m, cyclicSchemaNames));
1434
1589
  const isXor = node.strategy === "one" && !isDiscriminated && this.options.oneOfType === "xor";
1435
- const isStrict = node.strategy === "one" && !isXor;
1590
+ const isStrict = node.strategy === "one" && !isXor && !unionsWithSiblings.has(node);
1436
1591
  const members = mapSchemaMembers(node, (memberNode) => this.transform(isStrict ? strictOneOfNode(memberNode) : memberNode)).map(({ schema, output }) => output && isStrict ? strictOneOfMember(output, schema, cyclicSchemaNames) : output).filter(Boolean);
1437
1592
  if (members.length === 0) return "";
1438
1593
  if (members.length === 1) return members[0];
@@ -1443,11 +1598,21 @@ const printerZod = kubb_kit.ast.createPrinter((options) => {
1443
1598
  intersection(node) {
1444
1599
  const members = node.members ?? [];
1445
1600
  if (members.length === 0) return "";
1601
+ const hasSiblingProperties = members.some((member) => member.type === kubb_kit.ast.schemaTypes.object && member.properties.length > 0);
1602
+ const transform = (member) => {
1603
+ if (!hasSiblingProperties) return this.transform(member);
1604
+ unionsWithSiblings.add(member);
1605
+ try {
1606
+ return this.transform(member);
1607
+ } finally {
1608
+ unionsWithSiblings.delete(member);
1609
+ }
1610
+ };
1446
1611
  const [first, ...rest] = members;
1447
1612
  if (!first) return "";
1448
- const firstBase = this.transform(first);
1613
+ const firstBase = transform(first);
1449
1614
  if (!firstBase) return "";
1450
- if (rest.length > 0 && isObjectComposableIntersection(node, cyclicSchemaNames)) return rest.reduce((acc, member) => `${acc}.extend(${buildZodObjectShape(this, member)})`, firstBase);
1615
+ if (rest.length > 0 && isObjectComposableIntersection(node, cyclicSchemaNames)) return preserveRequiredProperties(members).slice(1).reduce((acc, member) => `${acc}.extend(${buildZodObjectShape(this, member)})`, firstBase);
1451
1616
  return rest.reduce((acc, member) => {
1452
1617
  const constraint = getMemberConstraint({
1453
1618
  member,
@@ -1456,7 +1621,7 @@ const printerZod = kubb_kit.ast.createPrinter((options) => {
1456
1621
  numbers: numberConstraints
1457
1622
  });
1458
1623
  if (constraint) return acc + constraint;
1459
- const transformed = this.transform(member);
1624
+ const transformed = transform(member);
1460
1625
  return transformed ? `${acc}.and(${transformed})` : acc;
1461
1626
  }, firstBase);
1462
1627
  }
@@ -1476,20 +1641,25 @@ const printerZod = kubb_kit.ast.createPrinter((options) => {
1476
1641
  }
1477
1642
  };
1478
1643
  });
1479
- //#endregion
1480
- //#region src/printers/printerZodMini.ts
1644
+ // src/printers/printerZodMini.ts
1481
1645
  /**
1482
1646
  * Builds the `{ key: value, … }` shape for an object node with the functional `zod/mini` modifiers.
1483
1647
  */
1484
1648
  function buildZodMiniObjectShape(ctx, node) {
1485
- return buildObjectShape(ctx, node, ({ value, schema, required, meta }) => applyMiniModifiers({
1486
- value,
1487
- schema,
1488
- nullable: meta.nullable,
1489
- optional: schema.optional || required === false,
1490
- nullish: schema.nullish,
1491
- defaultValue: meta.default
1492
- }));
1649
+ return buildObjectShape(ctx, node, ({ name, value, schema, required, meta, cyclic }) => {
1650
+ const body = applyMiniModifiers({
1651
+ value,
1652
+ schema,
1653
+ nullable: meta.nullable,
1654
+ optional: schema.optional || required === false,
1655
+ nullish: schema.nullish,
1656
+ defaultValue: meta.default
1657
+ });
1658
+ return cyclic ? lazyGetter({
1659
+ name,
1660
+ body
1661
+ }) : `${objectKey(name)}: ${body}`;
1662
+ });
1493
1663
  }
1494
1664
  /**
1495
1665
  * Zod v4 Mini printer built with `definePrinter`.
@@ -1505,6 +1675,7 @@ function buildZodMiniObjectShape(ctx, node) {
1505
1675
  */
1506
1676
  const printerZodMini = kubb_kit.ast.createPrinter((options) => {
1507
1677
  const cyclicSchemaNames = options.cyclicSchemas;
1678
+ const unionsWithSiblings = /* @__PURE__ */ new WeakSet();
1508
1679
  return {
1509
1680
  name: "zod-mini",
1510
1681
  options,
@@ -1577,7 +1748,7 @@ const printerZodMini = kubb_kit.ast.createPrinter((options) => {
1577
1748
  enum(node) {
1578
1749
  const nonNullValues = (node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? []).filter((v) => v !== null);
1579
1750
  if (node.namedEnumValues?.length) {
1580
- const literals = nonNullValues.map((v) => `z.literal(${formatDefault(v)})`);
1751
+ const literals = nonNullValues.map((v) => buildLiteral(v));
1581
1752
  if (literals.length === 1) return literals[0];
1582
1753
  return `z.union([${literals.join(", ")}])`;
1583
1754
  }
@@ -1599,32 +1770,40 @@ const printerZodMini = kubb_kit.ast.createPrinter((options) => {
1599
1770
  const isStrict = node.additionalProperties === false && patterns.length === 0;
1600
1771
  const isLoose = node.additionalProperties === true;
1601
1772
  const objectBase = `${isStrict ? "z.strictObject" : isLoose ? "z.looseObject" : "z.object"}(${buildZodMiniObjectShape(this, node)})`;
1602
- const recordFn = isClosedKeySchema(propertyNamesNode) ? "z.partialRecord" : "z.record";
1603
- if (node.additionalProperties && node.additionalProperties !== true) {
1604
- const catchallType = this.transform(node.additionalProperties);
1605
- if (entries.length === 0) return catchallType ? `${recordFn}(${propertyNamesKeySchema || "z.string()"}, ${catchallType})` : objectBase;
1606
- return catchallType ? `z.catchall(${objectBase}, ${catchallType})` : objectBase;
1607
- }
1608
- if (entries.length === 0 && patterns.length === 0 && propertyNamesKeySchema && !isStrict) return `${recordFn}(${propertyNamesKeySchema}, ${this.transform(kubb_kit.ast.factory.createSchema({ type: "unknown" }))})`;
1609
- if (node.additionalProperties === true) return objectBase;
1610
- if (isStrict) return objectBase;
1611
- if (patterns.length > 0) {
1612
- const values = patterns.map(([, valueSchema]) => {
1613
- const valueType = this.transform(valueSchema) ?? this.transform(kubb_kit.ast.factory.createSchema({ type: "unknown" }));
1614
- return valueSchema.nullable ? `z.nullable(${valueType})` : valueType;
1615
- });
1616
- const distinct = [...new Set(values)];
1617
- const value = distinct.length === 1 ? distinct[0] : `z.union([${distinct.join(", ")}])`;
1618
- if (entries.length > 0) return `z.catchall(${objectBase}, ${value})`;
1619
- return `z.record(${patternKeySchemaMini({
1620
- patterns: patterns.map(([pattern]) => pattern),
1621
- regexType: this.options.regexType
1622
- })}, ${value})`;
1623
- }
1624
- return objectBase;
1773
+ return objectConstraints({
1774
+ value: (() => {
1775
+ const recordFn = isClosedKeySchema(propertyNamesNode) ? "z.partialRecord" : "z.record";
1776
+ if (node.additionalProperties && node.additionalProperties !== true) {
1777
+ const catchallType = this.transform(node.additionalProperties);
1778
+ if (entries.length === 0) return catchallType ? `${recordFn}(${propertyNamesKeySchema || "z.string()"}, ${catchallType})` : objectBase;
1779
+ return catchallType ? `z.catchall(${objectBase}, ${catchallType})` : objectBase;
1780
+ }
1781
+ if (entries.length === 0 && patterns.length === 0 && propertyNamesKeySchema && !isStrict) {
1782
+ const unknownType = this.transform(kubb_kit.ast.factory.createSchema({ type: kubb_kit.ast.schemaTypes.unknown }));
1783
+ return `${recordFn}(${propertyNamesKeySchema}, ${unknownType})`;
1784
+ }
1785
+ if (node.additionalProperties === true) return objectBase;
1786
+ if (isStrict) return objectBase;
1787
+ if (patterns.length > 0) {
1788
+ const values = patterns.map(([, valueSchema]) => {
1789
+ const valueType = this.transform(valueSchema) ?? this.transform(kubb_kit.ast.factory.createSchema({ type: kubb_kit.ast.schemaTypes.unknown }));
1790
+ return valueSchema.nullable ? `z.nullable(${valueType})` : valueType;
1791
+ });
1792
+ const distinct = [...new Set(values)];
1793
+ const value = distinct.length === 1 ? distinct[0] : `z.union([${distinct.join(", ")}])`;
1794
+ if (entries.length > 0) return `z.catchall(${objectBase}, ${value})`;
1795
+ return `z.record(${patternKeySchemaMini({
1796
+ patterns: patterns.map(([pattern]) => pattern),
1797
+ regexType: this.options.regexType
1798
+ })}, ${value})`;
1799
+ }
1800
+ return objectBase;
1801
+ })(),
1802
+ node
1803
+ });
1625
1804
  },
1626
1805
  array(node) {
1627
- const base = `z.array(${mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean).join(", ") || this.transform(kubb_kit.ast.factory.createSchema({ type: "unknown" }))})${lengthChecksMini({
1806
+ const base = `z.array(${mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean).join(", ") || this.transform(kubb_kit.ast.factory.createSchema({ type: kubb_kit.ast.schemaTypes.unknown }))})${lengthChecksMini({
1628
1807
  ...node,
1629
1808
  regexType: this.options.regexType
1630
1809
  })}`;
@@ -1637,7 +1816,7 @@ const printerZodMini = kubb_kit.ast.createPrinter((options) => {
1637
1816
  const nodeMembers = node.members ?? [];
1638
1817
  const isDiscriminated = !!node.discriminatorPropertyName && nodeMembers.every((m) => isObjectSchemaNode(m, cyclicSchemaNames));
1639
1818
  const isXor = node.strategy === "one" && !isDiscriminated && this.options.oneOfType === "xor";
1640
- const isStrict = node.strategy === "one" && !isXor;
1819
+ const isStrict = node.strategy === "one" && !isXor && !unionsWithSiblings.has(node);
1641
1820
  const members = mapSchemaMembers(node, (memberNode) => this.transform(isStrict ? strictOneOfNode(memberNode) : memberNode)).map(({ output }) => output).filter(Boolean);
1642
1821
  if (members.length === 0) return "";
1643
1822
  if (members.length === 1) return members[0];
@@ -1648,11 +1827,21 @@ const printerZodMini = kubb_kit.ast.createPrinter((options) => {
1648
1827
  intersection(node) {
1649
1828
  const members = node.members ?? [];
1650
1829
  if (members.length === 0) return "";
1830
+ const hasSiblingProperties = members.some((member) => member.type === kubb_kit.ast.schemaTypes.object && member.properties.length > 0);
1831
+ const transform = (member) => {
1832
+ if (!hasSiblingProperties) return this.transform(member);
1833
+ unionsWithSiblings.add(member);
1834
+ try {
1835
+ return this.transform(member);
1836
+ } finally {
1837
+ unionsWithSiblings.delete(member);
1838
+ }
1839
+ };
1651
1840
  const [first, ...rest] = members;
1652
1841
  if (!first) return "";
1653
- const firstBase = this.transform(first);
1842
+ const firstBase = transform(first);
1654
1843
  if (!firstBase) return "";
1655
- if (rest.length > 0 && isObjectComposableIntersection(node, cyclicSchemaNames)) return rest.reduce((acc, member) => `z.extend(${acc}, ${buildZodMiniObjectShape(this, member)})`, firstBase);
1844
+ if (rest.length > 0 && isObjectComposableIntersection(node, cyclicSchemaNames)) return preserveRequiredProperties(members).slice(1).reduce((acc, member) => `z.extend(${acc}, ${buildZodMiniObjectShape(this, member)})`, firstBase);
1656
1845
  return rest.reduce((acc, member) => {
1657
1846
  const constraint = getMemberConstraint({
1658
1847
  member,
@@ -1661,7 +1850,7 @@ const printerZodMini = kubb_kit.ast.createPrinter((options) => {
1661
1850
  numbers: numberChecksMini
1662
1851
  });
1663
1852
  if (constraint) return acc + constraint;
1664
- const transformed = this.transform(member);
1853
+ const transformed = transform(member);
1665
1854
  return transformed ? `z.intersection(${acc}, ${transformed})` : acc;
1666
1855
  }, firstBase);
1667
1856
  }
@@ -1679,8 +1868,7 @@ const printerZodMini = kubb_kit.ast.createPrinter((options) => {
1679
1868
  }
1680
1869
  };
1681
1870
  });
1682
- //#endregion
1683
- //#region src/generators/zodGenerator.tsx
1871
+ // src/generators/zodGenerator.tsx
1684
1872
  /**
1685
1873
  * The helper an inferred alias is printed with. `'codec'` reads a request (encode) schema from its
1686
1874
  * input and a response (decode) schema from its output; `true` keeps `z.infer` for both.
@@ -2141,17 +2329,14 @@ const zodGenerator = (0, kubb_kit.defineGenerator)({
2141
2329
  });
2142
2330
  }
2143
2331
  });
2144
- //#endregion
2145
- //#region kubb-extension.json
2332
+ // kubb-extension.json
2146
2333
  var compatibility = {
2147
- "kubb": ">=5.8.3",
2334
+ "kubb": ">=5.14.6",
2148
2335
  "node": ">=22.22"
2149
2336
  };
2150
- //#endregion
2151
- //#region package.json
2152
- var version = "5.7.2";
2153
- //#endregion
2154
- //#region src/resolvers/resolverZod.ts
2337
+ // package.json
2338
+ var version = "5.7.4";
2339
+ // src/resolvers/resolverZod.ts
2155
2340
  /**
2156
2341
  * Default resolver used by `@kubb/plugin-zod`. Decides the names and file
2157
2342
  * paths for every generated Zod schema. Schemas use camelCase with a
@@ -2170,15 +2355,15 @@ var version = "5.7.2";
2170
2355
  const resolverZod = (0, kubb_kit.createResolver)({
2171
2356
  pluginName: "plugin-zod",
2172
2357
  name(name) {
2173
- return ensureValidVarName(camelCase(name, { suffix: "schema" }));
2358
+ return this.memo(`name:${name}`, () => ensureValidVarName((0, kubb_kit.camelCase)(name, { suffix: "schema" })));
2174
2359
  },
2175
- file: createCasedFile((part) => camelCase(part, { suffix: "schema" })),
2360
+ file: createCasedFile((part) => (0, kubb_kit.camelCase)(part, { suffix: "schema" })),
2176
2361
  schema: {
2177
2362
  typeName(name) {
2178
- return ensureValidVarName(pascalCase(name, { suffix: "schema type" }));
2363
+ return this.memo(`typeName:${name}`, () => ensureValidVarName((0, kubb_kit.pascalCase)(name, { suffix: "schema type" })));
2179
2364
  },
2180
2365
  type(name) {
2181
- return ensureValidVarName(pascalCase(name, { suffix: "type" }));
2366
+ return this.memo(`type:${name}`, () => ensureValidVarName((0, kubb_kit.pascalCase)(name, { suffix: "type" })));
2182
2367
  },
2183
2368
  inputName(name) {
2184
2369
  return this.name(`${name} input`);
@@ -2187,25 +2372,32 @@ const resolverZod = (0, kubb_kit.createResolver)({
2187
2372
  return this.schema.typeName(`${name} input`);
2188
2373
  },
2189
2374
  isName(name) {
2190
- return ensureValidVarName(camelCase(name, { prefix: "is" }));
2375
+ return ensureValidVarName((0, kubb_kit.camelCase)(name, { prefix: "is" }));
2191
2376
  },
2192
2377
  assertName(name) {
2193
- return ensureValidVarName(camelCase(name, { prefix: "assert" }));
2378
+ return ensureValidVarName((0, kubb_kit.camelCase)(name, { prefix: "assert" }));
2194
2379
  }
2195
2380
  },
2196
2381
  param: createOperationParamResolver(),
2197
2382
  response: {
2198
2383
  ...createOperationResponseResolver(),
2199
2384
  error(node) {
2200
- return this.name(`${node.operationId} Error`);
2385
+ const name = this.name(`${node.operationId} Error`);
2386
+ return this.resolveName({
2387
+ key: operationKey(node, "response", "error"),
2388
+ name
2389
+ });
2201
2390
  },
2202
2391
  options(node) {
2203
- return this.schema.type(this.name(`${node.operationId} Options`));
2392
+ const name = this.schema.type(this.name(`${node.operationId} Options`));
2393
+ return this.resolveName({
2394
+ key: operationKey(node, "response", "options"),
2395
+ name
2396
+ });
2204
2397
  }
2205
2398
  }
2206
2399
  });
2207
- //#endregion
2208
- //#region src/plugin.ts
2400
+ // src/plugin.ts
2209
2401
  /**
2210
2402
  * Canonical plugin name for `@kubb/plugin-zod`. Used for driver lookups and
2211
2403
  * cross-plugin dependency references.
@@ -2271,7 +2463,6 @@ const pluginZod = (0, kubb_kit.defineExtension)({
2271
2463
  };
2272
2464
  }
2273
2465
  });
2274
- //#endregion
2275
2466
  exports.default = pluginZod;
2276
2467
  exports.pluginZod = pluginZod;
2277
2468
  exports.pluginZodName = pluginZodName;