@kubb/plugin-zod 5.0.0-beta.98 → 5.0.0

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
@@ -12,6 +12,179 @@ var __name = (target, value) => __defProp(target, "name", {
12
12
  let kubb_kit = require("kubb/kit");
13
13
  let kubb_jsx = require("kubb/jsx");
14
14
  let kubb_jsx_jsx_runtime = require("kubb/jsx/jsx-runtime");
15
+ //#region ../../internals/shared/src/params.ts
16
+ /**
17
+ * Drops parameters that share the same name, keeping the first.
18
+ *
19
+ * A malformed spec can declare the same parameter name twice within one `in` location. Both would
20
+ * resolve to the same output property, so emitting both would yield an object type with a duplicate
21
+ * member, which TypeScript rejects. This is a defensive guard against that case, not a casing guard:
22
+ * parameter names flow through unchanged, so no two distinct names ever collide here anymore.
23
+ */
24
+ function dedupeParams(params) {
25
+ const seen = /* @__PURE__ */ new Set();
26
+ return params.filter((param) => {
27
+ if (seen.has(param.name)) return false;
28
+ seen.add(param.name);
29
+ return true;
30
+ });
31
+ }
32
+ //#endregion
33
+ //#region ../../internals/shared/src/operation.ts
34
+ /**
35
+ * Maps a content type to the PascalCase suffix used to name per-content-type variants
36
+ * (e.g. `application/json` → `Json`, `application/xml` → `Xml`, `multipart/form-data` → `FormData`).
37
+ */
38
+ function getContentTypeSuffix(contentType) {
39
+ const baseType = contentType.split(";")[0].trim();
40
+ if (baseType === "application/json") return "Json";
41
+ if (baseType === "multipart/form-data") return "FormData";
42
+ if (baseType === "application/x-www-form-urlencoded") return "FormUrlEncoded";
43
+ const parts = (baseType.split("/").pop() ?? baseType).split(/[^a-zA-Z0-9]+/).filter(Boolean);
44
+ if (parts.length === 0) return "Unknown";
45
+ return parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
46
+ }
47
+ /**
48
+ * Appends a content-type suffix to a base name, keeping a trailing `Data` segment last
49
+ * (e.g. `AddPetData` + `Json` → `AddPetJsonData`, `AddPetStatus200` + `Xml` → `AddPetStatus200Xml`).
50
+ */
51
+ function getPerContentTypeName(baseName, suffix) {
52
+ if (baseName.endsWith("Data")) return suffix.endsWith("Data") ? baseName.slice(0, -4) + suffix : `${baseName.slice(0, -4)}${suffix}Data`;
53
+ return baseName + suffix;
54
+ }
55
+ /**
56
+ * Resolves per-content-type variant names for a set of content entries, deduplicating suffix
57
+ * collisions with a numeric counter. Entries without a schema are skipped. The returned `suffix` is
58
+ * the final (possibly counter-augmented) value, so callers can derive parallel names in another
59
+ * namespace (e.g. plugin-faker deriving the matching plugin-ts type name).
60
+ */
61
+ function resolveContentTypeVariants(entries, baseName) {
62
+ const usedNames = /* @__PURE__ */ new Set();
63
+ return entries.filter((entry) => entry.schema).map((entry) => {
64
+ const baseSuffix = getContentTypeSuffix(entry.contentType);
65
+ let suffix = baseSuffix;
66
+ let name = getPerContentTypeName(baseName, suffix);
67
+ let counter = 2;
68
+ while (usedNames.has(name)) {
69
+ suffix = `${baseSuffix}${counter++}`;
70
+ name = getPerContentTypeName(baseName, suffix);
71
+ }
72
+ usedNames.add(name);
73
+ return {
74
+ name,
75
+ suffix,
76
+ schema: entry.schema,
77
+ keysToOmit: entry.keysToOmit,
78
+ contentType: entry.contentType
79
+ };
80
+ });
81
+ }
82
+ const operationParameterGroupsByNode = /* @__PURE__ */ new WeakMap();
83
+ /**
84
+ * Groups an operation's parameters by location (`path`/`query`/`header`/`cookie`), deduping each
85
+ * group by name. Every plugin generator visiting the same `OperationNode` shares one AST instance
86
+ * (see `KubbDriver`), so the result is cached per node to avoid re-filtering and re-deduping the
87
+ * same parameters once per plugin.
88
+ */
89
+ function getOperationParameters(node) {
90
+ const cached = operationParameterGroupsByNode.get(node);
91
+ if (cached) return cached;
92
+ const groups = {
93
+ path: dedupeParams(node.parameters.filter((param) => param.in === "path")),
94
+ query: dedupeParams(node.parameters.filter((param) => param.in === "query")),
95
+ header: dedupeParams(node.parameters.filter((param) => param.in === "header")),
96
+ cookie: dedupeParams(node.parameters.filter((param) => param.in === "cookie"))
97
+ };
98
+ operationParameterGroupsByNode.set(node, groups);
99
+ return groups;
100
+ }
101
+ /**
102
+ * Builds the combined `{ body, path, query, headers }` options object schema for an operation,
103
+ * referencing the already-resolved body and grouped param names. Shared by `@kubb/plugin-ts`'s
104
+ * `Options` type and `@kubb/plugin-zod`'s inferred options schema, so both printers emit the same
105
+ * shape from the same inputs. `primitive: 'object'` is a no-op for the TS printer and tells the Zod
106
+ * printer to emit `z.object(…)` rather than a record.
107
+ */
108
+ function buildOptionsSchema(node, resolver) {
109
+ const { path, query, header } = getOperationParameters(node);
110
+ const hasBody = Boolean(node.requestBody?.content?.[0]?.schema);
111
+ const createNever = () => kubb_kit.ast.factory.createSchema({
112
+ type: "never",
113
+ primitive: void 0,
114
+ optional: true
115
+ });
116
+ const groups = [
117
+ {
118
+ name: "path",
119
+ params: path,
120
+ resolve: resolver.param.path
121
+ },
122
+ {
123
+ name: "query",
124
+ params: query,
125
+ resolve: resolver.param.query
126
+ },
127
+ {
128
+ name: "headers",
129
+ params: header,
130
+ resolve: resolver.param.headers
131
+ }
132
+ ];
133
+ return kubb_kit.ast.factory.createSchema({
134
+ type: "object",
135
+ primitive: "object",
136
+ deprecated: node.deprecated,
137
+ properties: [kubb_kit.ast.factory.createProperty({
138
+ name: "body",
139
+ required: hasBody,
140
+ schema: hasBody ? kubb_kit.ast.factory.createSchema({
141
+ type: "ref",
142
+ name: resolver.response.body(node)
143
+ }) : createNever()
144
+ }), ...groups.map(({ name, params, resolve }) => {
145
+ const required = params.some((param) => param.required);
146
+ return kubb_kit.ast.factory.createProperty({
147
+ name,
148
+ required,
149
+ schema: params.length > 0 ? kubb_kit.ast.factory.createSchema({
150
+ type: "ref",
151
+ name: resolve.call(resolver.param, node, params[0]),
152
+ optional: !required
153
+ }) : createNever()
154
+ });
155
+ })]
156
+ });
157
+ }
158
+ function getStatusCodeNumber(statusCode) {
159
+ const code = Number(statusCode);
160
+ return Number.isNaN(code) ? null : code;
161
+ }
162
+ function isSuccessStatusCode(statusCode) {
163
+ const code = getStatusCodeNumber(statusCode);
164
+ return code !== null && code >= 200 && code < 300;
165
+ }
166
+ function getSuccessResponses(responses) {
167
+ return responses.filter((response) => isSuccessStatusCode(response.statusCode));
168
+ }
169
+ //#endregion
170
+ //#region ../../internals/shared/src/adapter.ts
171
+ /**
172
+ * Narrows the generic `Adapter` from a generator context to the OpenAPI adapter,
173
+ * so OAS-only options (`dateType`, `enums`) and the parsed `document` are typed.
174
+ *
175
+ * Throws when a non-OAS adapter is configured, turning a silently wrong cast into a
176
+ * clear, actionable error at the point of use.
177
+ *
178
+ * @example
179
+ * ```ts
180
+ * const { dateType } = getOasAdapter(ctx.adapter).options
181
+ * ```
182
+ */
183
+ function getOasAdapter(adapter) {
184
+ if (adapter.name !== "oas") throw new Error(`Expected the OpenAPI adapter (adapterOas), but received "${adapter.name}". Configure \`adapter: adapterOas()\` in your Kubb config.`);
185
+ return adapter;
186
+ }
187
+ //#endregion
15
188
  //#region ../../internals/utils/src/casing.ts
16
189
  /**
17
190
  * Shared implementation for camelCase and PascalCase conversion.
@@ -356,106 +529,6 @@ function toFilePath(name, caseLast = camelCase) {
356
529
  return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
357
530
  }
358
531
  //#endregion
359
- //#region ../../internals/shared/src/params.ts
360
- const caseParamsCache = /* @__PURE__ */ new WeakMap();
361
- /**
362
- * Applies camelCase to parameter names and returns a new array without mutating the input.
363
- *
364
- * Run it before handing parameters to schema builders so output property keys get the right casing
365
- * while `OperationNode.parameters` stays intact for other consumers. When `casing` is unset, the
366
- * original array is returned unchanged. Results are cached per input array.
367
- */
368
- function caseParams(params, casing) {
369
- if (!casing) return params;
370
- const cached = caseParamsCache.get(params);
371
- if (cached) return cached;
372
- const result = params.map((param) => ({
373
- ...param,
374
- name: camelCase(param.name)
375
- }));
376
- caseParamsCache.set(params, result);
377
- return result;
378
- }
379
- //#endregion
380
- //#region ../../internals/shared/src/operation.ts
381
- /**
382
- * Maps a content type to the PascalCase suffix used to name per-content-type variants
383
- * (e.g. `application/json` → `Json`, `application/xml` → `Xml`, `multipart/form-data` → `FormData`).
384
- */
385
- function getContentTypeSuffix(contentType) {
386
- const baseType = contentType.split(";")[0].trim();
387
- if (baseType === "application/json") return "Json";
388
- if (baseType === "multipart/form-data") return "FormData";
389
- if (baseType === "application/x-www-form-urlencoded") return "FormUrlEncoded";
390
- const parts = (baseType.split("/").pop() ?? baseType).split(/[^a-zA-Z0-9]+/).filter(Boolean);
391
- if (parts.length === 0) return "Unknown";
392
- return parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
393
- }
394
- /**
395
- * Appends a content-type suffix to a base name, keeping a trailing `Data` segment last
396
- * (e.g. `AddPetData` + `Json` → `AddPetJsonData`, `AddPetStatus200` + `Xml` → `AddPetStatus200Xml`).
397
- */
398
- function getPerContentTypeName(baseName, suffix) {
399
- if (baseName.endsWith("Data")) return suffix.endsWith("Data") ? baseName.slice(0, -4) + suffix : `${baseName.slice(0, -4)}${suffix}Data`;
400
- return baseName + suffix;
401
- }
402
- /**
403
- * Resolves per-content-type variant names for a set of content entries, deduplicating suffix
404
- * collisions with a numeric counter. Entries without a schema are skipped. The returned `suffix` is
405
- * the final (possibly counter-augmented) value, so callers can derive parallel names in another
406
- * namespace (e.g. plugin-faker deriving the matching plugin-ts type name).
407
- */
408
- function resolveContentTypeVariants(entries, baseName) {
409
- const usedNames = /* @__PURE__ */ new Set();
410
- return entries.filter((entry) => entry.schema).map((entry) => {
411
- const baseSuffix = getContentTypeSuffix(entry.contentType);
412
- let suffix = baseSuffix;
413
- let name = getPerContentTypeName(baseName, suffix);
414
- let counter = 2;
415
- while (usedNames.has(name)) {
416
- suffix = `${baseSuffix}${counter++}`;
417
- name = getPerContentTypeName(baseName, suffix);
418
- }
419
- usedNames.add(name);
420
- return {
421
- name,
422
- suffix,
423
- schema: entry.schema,
424
- keysToOmit: entry.keysToOmit,
425
- contentType: entry.contentType
426
- };
427
- });
428
- }
429
- function getStatusCodeNumber(statusCode) {
430
- const code = Number(statusCode);
431
- return Number.isNaN(code) ? null : code;
432
- }
433
- function isSuccessStatusCode(statusCode) {
434
- const code = getStatusCodeNumber(statusCode);
435
- return code !== null && code >= 200 && code < 300;
436
- }
437
- function getSuccessResponses(responses) {
438
- return responses.filter((response) => isSuccessStatusCode(response.statusCode));
439
- }
440
- //#endregion
441
- //#region ../../internals/shared/src/adapter.ts
442
- /**
443
- * Narrows the generic `Adapter` from a generator context to the OpenAPI adapter,
444
- * so OAS-only options (`dateType`, `enums`) and the parsed `document` are typed.
445
- *
446
- * Throws when a non-OAS adapter is configured, turning a silently wrong cast into a
447
- * clear, actionable error at the point of use.
448
- *
449
- * @example
450
- * ```ts
451
- * const { dateType } = getOasAdapter(ctx.adapter).options
452
- * ```
453
- */
454
- function getOasAdapter(adapter) {
455
- if (adapter.name !== "oas") throw new Error(`Expected the OpenAPI adapter (adapterOas), but received "${adapter.name}". Configure \`adapter: adapterOas()\` in your Kubb config.`);
456
- return adapter;
457
- }
458
- //#endregion
459
532
  //#region ../../internals/shared/src/resolver.ts
460
533
  /**
461
534
  * Resolves a single operation parameter name with the
@@ -468,6 +541,29 @@ function operationParamName(node, param) {
468
541
  return this.name(`${node.operationId} ${param.in} ${param.name}`);
469
542
  }
470
543
  /**
544
+ * Builds the shared `param` namespace. Spread the result into `createResolver`
545
+ * and override individual methods next to it when a plugin deviates.
546
+ *
547
+ * @example
548
+ * ```ts
549
+ * createResolver<PluginTs>({ param: createOperationParamResolver(), ... })
550
+ * ```
551
+ */
552
+ function createOperationParamResolver() {
553
+ return {
554
+ name: operationParamName,
555
+ path(node) {
556
+ return this.name(`${node.operationId} Path`);
557
+ },
558
+ query(node) {
559
+ return this.name(`${node.operationId} Query`);
560
+ },
561
+ headers(node) {
562
+ return this.name(`${node.operationId} Headers`);
563
+ }
564
+ };
565
+ }
566
+ /**
471
567
  * Builds the shared `response` namespace. Spread the result into
472
568
  * `createResolver` and add plugin-specific methods (`options`, `error`) next
473
569
  * to it.
@@ -515,7 +611,7 @@ function createCasedFile(caseLast) {
515
611
  * `resolver.imports` would resolve file paths that are then discarded.
516
612
  */
517
613
  function collectRefNames(schema) {
518
- return kubb_kit.ast.collect(schema, { schema: (node) => {
614
+ return kubb_kit.ast.collectSync(schema, { schema: (node) => {
519
615
  const refNode = kubb_kit.ast.narrowSchema(node, "ref");
520
616
  if (!refNode?.ref) return null;
521
617
  return kubb_kit.ast.resolveRefName(refNode);
@@ -553,6 +649,45 @@ function createGroupConfig(group) {
553
649
  };
554
650
  }
555
651
  //#endregion
652
+ //#region ../../internals/shared/src/schemaTraversal.ts
653
+ /**
654
+ * Maps each property of an object schema to its transformed output. Pairs every result with the
655
+ * original property so the printer keeps full control over modifiers, getters, and key syntax.
656
+ *
657
+ * @example
658
+ * ```ts
659
+ * const entries = mapSchemaProperties(node, (schema) => this.transform(schema))
660
+ * // entries: [{ name: 'id', property, output: 'z.number()' }, ...]
661
+ * ```
662
+ */
663
+ function mapSchemaProperties(node, transform) {
664
+ return node.properties.map((property) => ({
665
+ name: property.name,
666
+ property,
667
+ output: transform(property.schema)
668
+ }));
669
+ }
670
+ /**
671
+ * Maps each member of a union or intersection schema to its transformed output, pairing every
672
+ * result with the original member.
673
+ */
674
+ function mapSchemaMembers(node, transform) {
675
+ return (node.members ?? []).map((schema) => ({
676
+ schema,
677
+ output: transform(schema)
678
+ }));
679
+ }
680
+ /**
681
+ * Maps each item of an array or tuple schema to its transformed output, pairing every result with
682
+ * the original item.
683
+ */
684
+ function mapSchemaItems(node, transform) {
685
+ return (node.items ?? []).map((schema) => ({
686
+ schema,
687
+ output: transform(schema)
688
+ }));
689
+ }
690
+ //#endregion
556
691
  //#region src/components/Zod.tsx
557
692
  function Zod({ name, node, printer, inferTypeName, cyclic }) {
558
693
  const output = printer.print(node);
@@ -636,12 +771,12 @@ function containsCodec(node, seen = /* @__PURE__ */ new Set()) {
636
771
  if (hasCodec(node)) return true;
637
772
  if (node.type === "ref") {
638
773
  if (!node.ref) return false;
639
- const refName = kubb_kit.ast.extractRefName(node.ref);
774
+ const refName = (0, kubb_kit.extractRefName)(node.ref);
640
775
  if (refName) {
641
776
  if (seen.has(refName)) return false;
642
777
  seen.add(refName);
643
778
  }
644
- const resolved = kubb_kit.ast.syncSchemaRef(node);
779
+ const resolved = (0, kubb_kit.syncSchemaRef)(node);
645
780
  if (resolved.type === "ref") return false;
646
781
  return containsCodec(resolved, seen);
647
782
  }
@@ -657,7 +792,7 @@ function containsCodec(node, seen = /* @__PURE__ */ new Set()) {
657
792
  * them to their input (encode) variant.
658
793
  */
659
794
  function collectCodecRefNames(node) {
660
- return kubb_kit.ast.collect(node, { schema: (n) => n.type === "ref" && n.ref && containsCodec(n) ? kubb_kit.ast.resolveRefName(n) ?? void 0 : void 0 });
795
+ return kubb_kit.ast.collectSync(node, { schema: (n) => n.type === "ref" && n.ref && containsCodec(n) ? kubb_kit.ast.resolveRefName(n) ?? void 0 : void 0 });
661
796
  }
662
797
  /**
663
798
  * Whether the node is a plain inline object whose shape can be lifted into an `.extend({ … })`
@@ -680,7 +815,7 @@ function isObjectSchemaNode(node, cyclicSchemas) {
680
815
  if (node.type === "ref") {
681
816
  const refName = kubb_kit.ast.resolveRefName(node);
682
817
  if (refName && cyclicSchemas?.has(refName)) return false;
683
- const resolved = kubb_kit.ast.syncSchemaRef(node);
818
+ const resolved = (0, kubb_kit.syncSchemaRef)(node);
684
819
  return resolved.type === "ref" || isObjectSchemaNode(resolved, cyclicSchemas);
685
820
  }
686
821
  if (node.type === "union") {
@@ -756,6 +891,20 @@ function buildEnum(values) {
756
891
  return `z.union([${literals.join(", ")}])`;
757
892
  }
758
893
  /**
894
+ * Digit pattern for a `type: 'string'` schema that carries an integer `format`, the way ProtoJSON
895
+ * encodes 64-bit integers. Returns `undefined` for every other format, and a `pattern` from the
896
+ * spec takes precedence over this fallback.
897
+ *
898
+ * @example
899
+ * ```ts
900
+ * integerFormatPattern('uint64') // '^\\d+$'
901
+ * ```
902
+ */
903
+ function integerFormatPattern(format) {
904
+ if (format === "int32" || format === "int64") return "^-?\\d+$";
905
+ if (format === "uint64") return "^\\d+$";
906
+ }
907
+ /**
759
908
  * Map a `regexType` to the `func` argument of `toRegExpString`: `'constructor'` emits
760
909
  * `new RegExp(...)`, while `'literal'` (the default) emits a regex literal.
761
910
  */
@@ -880,6 +1029,22 @@ function applyMiniModifiers({ value, schema, nullable, optional, nullish, defaul
880
1029
  const literal = defaultValue !== void 0 ? defaultLiteral(schema, defaultValue) : null;
881
1030
  return literal !== null ? `z._default(${withModifier}, ${literal})` : withModifier;
882
1031
  }
1032
+ /**
1033
+ * Builds an `object` schema node grouping the given parameter nodes.
1034
+ * The `primitive: 'object'` marker ensures the Zod printer emits `z.object(…)` rather than a record.
1035
+ */
1036
+ function buildGroupedParamsSchema({ params, optional }) {
1037
+ return kubb_kit.ast.factory.createSchema({
1038
+ type: "object",
1039
+ optional,
1040
+ primitive: "object",
1041
+ properties: params.map((param) => kubb_kit.ast.factory.createProperty({
1042
+ name: param.name,
1043
+ required: param.required,
1044
+ schema: param.schema
1045
+ }))
1046
+ });
1047
+ }
883
1048
  //#endregion
884
1049
  //#region src/printers/printerZod.ts
885
1050
  function strictOneOfMember$1(member, node, cyclicSchemas) {
@@ -888,7 +1053,7 @@ function strictOneOfMember$1(member, node, cyclicSchemas) {
888
1053
  if (member.startsWith("z.lazy(")) return member;
889
1054
  const refName = kubb_kit.ast.resolveRefName(node);
890
1055
  if (refName && cyclicSchemas?.has(refName)) return member;
891
- const schema = kubb_kit.ast.syncSchemaRef(node);
1056
+ const schema = (0, kubb_kit.syncSchemaRef)(node);
892
1057
  if (schema.nullable || schema.optional || node.nullable || node.optional) return member;
893
1058
  if (schema.type === "object" && (schema.additionalProperties === void 0 || schema.additionalProperties === false)) return `${member}.strict()`;
894
1059
  }
@@ -913,8 +1078,8 @@ function getMemberConstraint({ member, regexType }) {
913
1078
  function buildZodObjectShape(ctx, node) {
914
1079
  const objectNode = kubb_kit.ast.narrowSchema(node, "object");
915
1080
  if (!objectNode) return "{}";
916
- const isCyclic = (schema) => ctx.options.cyclicSchemas != null && kubb_kit.ast.containsCircularRef(schema, { circularSchemas: ctx.options.cyclicSchemas });
917
- return buildObject(kubb_kit.ast.mapSchemaProperties(objectNode, (schema) => {
1081
+ const isCyclic = (schema) => ctx.options.cyclicSchemas != null && (0, kubb_kit.containsCircularRef)(schema, { circularSchemas: ctx.options.cyclicSchemas });
1082
+ return buildObject(mapSchemaProperties(objectNode, (schema) => {
918
1083
  const hasSelfRef = isCyclic(schema);
919
1084
  const savedCyclicSchemas = ctx.options.cyclicSchemas;
920
1085
  if (hasSelfRef) ctx.options.cyclicSchemas = void 0;
@@ -923,7 +1088,7 @@ function buildZodObjectShape(ctx, node) {
923
1088
  return baseOutput;
924
1089
  }).map(({ name: propName, property, output: baseOutput }) => {
925
1090
  const { schema } = property;
926
- const meta = kubb_kit.ast.syncSchemaRef(schema);
1091
+ const meta = (0, kubb_kit.syncSchemaRef)(schema);
927
1092
  const descriptionToApply = schema.type !== "ref" && meta.type === "ref" ? void 0 : meta.description;
928
1093
  const value = applyModifiers({
929
1094
  value: baseOutput,
@@ -966,8 +1131,11 @@ const printerZod = kubb_kit.ast.createPrinter((options) => {
966
1131
  boolean: () => "z.boolean()",
967
1132
  null: () => "z.null()",
968
1133
  string(node) {
969
- return `${shouldCoerce(this.options.coercion, "strings") ? "z.coerce.string()" : "z.string()"}${lengthConstraints({
1134
+ const base = shouldCoerce(this.options.coercion, "strings") ? "z.coerce.string()" : "z.string()";
1135
+ const pattern = node.pattern ?? integerFormatPattern(node.format);
1136
+ return `${base}${lengthConstraints({
970
1137
  ...node,
1138
+ pattern,
971
1139
  regexType: this.options.regexType
972
1140
  })}`;
973
1141
  },
@@ -1066,18 +1234,18 @@ const printerZod = kubb_kit.ast.createPrinter((options) => {
1066
1234
  })();
1067
1235
  },
1068
1236
  array(node) {
1069
- const base = `z.array(${kubb_kit.ast.mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean).join(", ") || this.transform(kubb_kit.ast.factory.createSchema({ type: "unknown" }))})${lengthConstraints({
1237
+ 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({
1070
1238
  ...node,
1071
1239
  regexType: this.options.regexType
1072
1240
  })}`;
1073
1241
  return node.unique ? `${base}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })` : base;
1074
1242
  },
1075
1243
  tuple(node) {
1076
- return `z.tuple(${buildList(kubb_kit.ast.mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean))})`;
1244
+ return `z.tuple(${buildList(mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean))})`;
1077
1245
  },
1078
1246
  union(node) {
1079
1247
  const nodeMembers = node.members ?? [];
1080
- const members = kubb_kit.ast.mapSchemaMembers(node, (memberNode) => this.transform(memberNode)).map(({ schema, output }) => output && node.strategy === "one" ? strictOneOfMember$1(output, schema, cyclicSchemaNames) : output).filter(Boolean);
1248
+ const members = mapSchemaMembers(node, (memberNode) => this.transform(memberNode)).map(({ schema, output }) => output && node.strategy === "one" ? strictOneOfMember$1(output, schema, cyclicSchemaNames) : output).filter(Boolean);
1081
1249
  if (members.length === 0) return "";
1082
1250
  if (members.length === 1) return members[0];
1083
1251
  const allDiscriminable = nodeMembers.every((m) => isObjectSchemaNode(m, cyclicSchemaNames));
@@ -1108,7 +1276,7 @@ const printerZod = kubb_kit.ast.createPrinter((options) => {
1108
1276
  const { keysToOmit } = this.options;
1109
1277
  const transformed = this.transform(node);
1110
1278
  if (!transformed) return null;
1111
- const meta = kubb_kit.ast.syncSchemaRef(node);
1279
+ const meta = (0, kubb_kit.syncSchemaRef)(node);
1112
1280
  return applyModifiers({
1113
1281
  value: (() => {
1114
1282
  if (!keysToOmit?.length || meta.primitive !== "object" || meta.type === "union" && meta.discriminatorPropertyName) return transformed;
@@ -1153,8 +1321,8 @@ function getMemberConstraintMini({ member, regexType }) {
1153
1321
  function buildZodMiniObjectShape(ctx, node) {
1154
1322
  const objectNode = kubb_kit.ast.narrowSchema(node, "object");
1155
1323
  if (!objectNode) return "{}";
1156
- const isCyclic = (schema) => ctx.options.cyclicSchemas != null && kubb_kit.ast.containsCircularRef(schema, { circularSchemas: ctx.options.cyclicSchemas });
1157
- return buildObject(kubb_kit.ast.mapSchemaProperties(objectNode, (schema) => {
1324
+ const isCyclic = (schema) => ctx.options.cyclicSchemas != null && (0, kubb_kit.containsCircularRef)(schema, { circularSchemas: ctx.options.cyclicSchemas });
1325
+ return buildObject(mapSchemaProperties(objectNode, (schema) => {
1158
1326
  const hasSelfRef = isCyclic(schema);
1159
1327
  const savedCyclicSchemas = ctx.options.cyclicSchemas;
1160
1328
  if (hasSelfRef) ctx.options.cyclicSchemas = void 0;
@@ -1163,7 +1331,7 @@ function buildZodMiniObjectShape(ctx, node) {
1163
1331
  return baseOutput;
1164
1332
  }).map(({ name: propName, property, output: baseOutput }) => {
1165
1333
  const { schema } = property;
1166
- const meta = kubb_kit.ast.syncSchemaRef(schema);
1334
+ const meta = (0, kubb_kit.syncSchemaRef)(schema);
1167
1335
  const value = applyMiniModifiers({
1168
1336
  value: baseOutput,
1169
1337
  schema,
@@ -1203,8 +1371,10 @@ const printerZodMini = kubb_kit.ast.createPrinter((options) => {
1203
1371
  boolean: () => "z.boolean()",
1204
1372
  null: () => "z.null()",
1205
1373
  string(node) {
1374
+ const pattern = node.pattern ?? integerFormatPattern(node.format);
1206
1375
  return `z.string()${lengthChecksMini({
1207
1376
  ...node,
1377
+ pattern,
1208
1378
  regexType: this.options.regexType
1209
1379
  })}`;
1210
1380
  },
@@ -1292,18 +1462,18 @@ const printerZodMini = kubb_kit.ast.createPrinter((options) => {
1292
1462
  return objectBase;
1293
1463
  },
1294
1464
  array(node) {
1295
- const base = `z.array(${kubb_kit.ast.mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean).join(", ") || this.transform(kubb_kit.ast.factory.createSchema({ type: "unknown" }))})${lengthChecksMini({
1465
+ 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({
1296
1466
  ...node,
1297
1467
  regexType: this.options.regexType
1298
1468
  })}`;
1299
1469
  return node.unique ? `${base}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })` : base;
1300
1470
  },
1301
1471
  tuple(node) {
1302
- return `z.tuple(${buildList(kubb_kit.ast.mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean))})`;
1472
+ return `z.tuple(${buildList(mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean))})`;
1303
1473
  },
1304
1474
  union(node) {
1305
1475
  const nodeMembers = node.members ?? [];
1306
- const members = kubb_kit.ast.mapSchemaMembers(node, (memberNode) => this.transform(memberNode)).map(({ schema, output }) => output && node.strategy === "one" ? strictOneOfMember(output, schema) : output).filter(Boolean);
1476
+ const members = mapSchemaMembers(node, (memberNode) => this.transform(memberNode)).map(({ schema, output }) => output && node.strategy === "one" ? strictOneOfMember(output, schema) : output).filter(Boolean);
1307
1477
  if (members.length === 0) return "";
1308
1478
  if (members.length === 1) return members[0];
1309
1479
  const allDiscriminable = nodeMembers.every((m) => isObjectSchemaNode(m, cyclicSchemaNames));
@@ -1334,7 +1504,7 @@ const printerZodMini = kubb_kit.ast.createPrinter((options) => {
1334
1504
  const { keysToOmit } = this.options;
1335
1505
  const transformed = this.transform(node);
1336
1506
  if (!transformed) return null;
1337
- const meta = kubb_kit.ast.syncSchemaRef(node);
1507
+ const meta = (0, kubb_kit.syncSchemaRef)(node);
1338
1508
  return applyMiniModifiers({
1339
1509
  value: (() => {
1340
1510
  if (!keysToOmit?.length || meta.primitive !== "object" || meta.type === "union" && meta.discriminatorPropertyName) return transformed;
@@ -1531,7 +1701,6 @@ const zodGenerator = (0, kubb_kit.defineGenerator)({
1531
1701
  const { output, coercion, guidType, regexType, mini, inferred, importPath, group, printer } = ctx.options;
1532
1702
  const dateType = getOasAdapter(adapter).options.dateType;
1533
1703
  const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath);
1534
- const params = caseParams(node.parameters, "camelcase");
1535
1704
  const meta = { file: resolver.file({
1536
1705
  name: node.operationId,
1537
1706
  extname: ".ts",
@@ -1637,7 +1806,7 @@ const zodGenerator = (0, kubb_kit.defineGenerator)({
1637
1806
  name
1638
1807
  });
1639
1808
  }
1640
- const paramSchemas = params.map((param) => renderSchemaEntry({
1809
+ const paramSchemas = node.parameters.map((param) => renderSchemaEntry({
1641
1810
  schema: param.schema,
1642
1811
  name: resolver.param.name(node, param),
1643
1812
  direction: "input"
@@ -1686,6 +1855,30 @@ const zodGenerator = (0, kubb_kit.defineGenerator)({
1686
1855
  description: node.requestBody.description ?? schema.description
1687
1856
  }), "input");
1688
1857
  })();
1858
+ const { path, query, header } = getOperationParameters(node);
1859
+ const paramGroupSchemas = inferred ? [
1860
+ {
1861
+ kind: "path",
1862
+ params: path
1863
+ },
1864
+ {
1865
+ kind: "query",
1866
+ params: query
1867
+ },
1868
+ {
1869
+ kind: "headers",
1870
+ params: header
1871
+ }
1872
+ ].filter(({ params }) => params.length > 0).map(({ kind, params }) => renderSchemaEntry({
1873
+ schema: buildGroupedParamsSchema({ params }),
1874
+ name: resolver.param[kind](node, params[0]),
1875
+ direction: "input"
1876
+ })) : [];
1877
+ const optionsSchema = inferred ? renderSchemaEntry({
1878
+ schema: buildOptionsSchema(node, resolver),
1879
+ name: resolver.name(`${node.operationId} Options`),
1880
+ direction: "input"
1881
+ }) : null;
1689
1882
  return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx.File, {
1690
1883
  baseName: meta.file.baseName,
1691
1884
  path: meta.file.path,
@@ -1716,7 +1909,9 @@ const zodGenerator = (0, kubb_kit.defineGenerator)({
1716
1909
  responseSchemas,
1717
1910
  responseUnionSchema,
1718
1911
  errorUnionSchema,
1719
- requestSchema
1912
+ requestSchema,
1913
+ paramGroupSchemas,
1914
+ optionsSchema
1720
1915
  ]
1721
1916
  });
1722
1917
  }
@@ -1758,22 +1953,14 @@ const resolverZod = (0, kubb_kit.createResolver)({
1758
1953
  return this.schema.typeName(`${name} input`);
1759
1954
  }
1760
1955
  },
1761
- param: {
1762
- name: operationParamName,
1763
- path(node, param) {
1764
- return this.param.name(node, param);
1765
- },
1766
- query(node, param) {
1767
- return this.param.name(node, param);
1768
- },
1769
- headers(node, param) {
1770
- return this.param.name(node, param);
1771
- }
1772
- },
1956
+ param: createOperationParamResolver(),
1773
1957
  response: {
1774
1958
  ...createOperationResponseResolver(),
1775
1959
  error(node) {
1776
1960
  return this.name(`${node.operationId} Error`);
1961
+ },
1962
+ options(node) {
1963
+ return this.schema.type(this.name(`${node.operationId} Options`));
1777
1964
  }
1778
1965
  }
1779
1966
  });