@orval/zod 8.18.0 → 8.20.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.d.mts CHANGED
@@ -1,10 +1,30 @@
1
- import { ClientBuilder, ClientGeneratorsBuilder, ContextSpec, GeneratorDependency, GeneratorMutator, OpenApiParameterObject, OpenApiReferenceObject, OpenApiSchemaObject, PackageJson, ZodCoerceType } from "@orval/core";
1
+ import { ClientBuilder, ClientDependenciesBuilder, ClientGeneratorsBuilder, ContextSpec, GeneratorMutator, OpenApiParameterObject, OpenApiReferenceObject, OpenApiSchemaObject, PackageJson, ZodCoerceType, ZodVariantOption, ZodVersionOption } from "@orval/core";
2
2
 
3
3
  //#region src/compatible-v4.d.ts
4
4
  declare const isZodVersionV4: (packageJson: PackageJson) => boolean;
5
+ /**
6
+ * Resolves whether to emit Zod 4-style output.
7
+ *
8
+ * An explicit `override.zod.version` of `3` or `4` always wins; `'auto'` (the
9
+ * default) falls back to inferring from the output project's resolved `zod`
10
+ * version via {@link isZodVersionV4}. When no package metadata is available,
11
+ * `'auto'` now defaults to Zod 4 so fresh or partially-installed workspaces
12
+ * still get the modern baseline. This keeps generation deterministic when a
13
+ * target is pinned while preserving package-detection for `'auto'`.
14
+ */
15
+ declare const resolveIsZodV4: (version: ZodVersionOption | undefined, packageJson: PackageJson | undefined) => boolean;
16
+ declare const assertZodTarget: ({
17
+ variant,
18
+ isZodV4
19
+ }: {
20
+ variant: ZodVariantOption | undefined;
21
+ isZodV4: boolean;
22
+ }) => void;
23
+ declare const getZodImportSource: (variant: ZodVariantOption | undefined) => "zod/mini" | "zod";
24
+ declare const getZodTypeName: (variant: ZodVariantOption | undefined) => "ZodMiniType" | "ZodType";
5
25
  //#endregion
6
26
  //#region src/index.d.ts
7
- declare const getZodDependencies: () => GeneratorDependency[];
27
+ declare const getZodDependencies: ClientDependenciesBuilder;
8
28
  declare const predefinedZodFormats: Set<string>;
9
29
  interface ZodValidationSchemaDefinition {
10
30
  functions: [string, unknown][];
@@ -47,6 +67,15 @@ declare const generateZodValidationSchemaDefinition: (schema: OpenApiSchemaObjec
47
67
  * placeholder instead of being inlined.
48
68
  */
49
69
  useReusableSchemas?: boolean;
70
+ /**
71
+ * When true, suppress File/Blob coercion for binary string fields anywhere
72
+ * in the tree (`format: binary` / `contentMediaType: application/octet-stream`),
73
+ * keeping them as `string`. Set for `application/x-www-form-urlencoded` bodies,
74
+ * which serialize via URLSearchParams (string-only) — mirrors core's
75
+ * `formDataContext.urlEncoded` handling in getScalar (#1624). Threaded into
76
+ * every recursive call so nested/array/composed fields are covered too.
77
+ */
78
+ urlEncoded?: boolean;
50
79
  /**
51
80
  * When true (and `isZodV4`), the top-level (named component) schema emits a
52
81
  * `.meta({ id, description?, deprecated? })` instead of `.describe(...)`.
@@ -75,7 +104,7 @@ interface ZodParamsContext {
75
104
  interface ZodParamsInjection extends Pick<ZodParamsContext, 'operationId' | 'location' | 'schemaName'> {
76
105
  mutator: GeneratorMutator;
77
106
  }
78
- declare const parseZodValidationSchemaDefinition: (input: ZodValidationSchemaDefinition, context: ContextSpec, coerceTypes: boolean | ZodCoerceType[] | undefined, strict: boolean, isZodV4: boolean, preprocess?: GeneratorMutator, paramsInjection?: ZodParamsInjection) => {
107
+ declare const parseZodValidationSchemaDefinition: (input: ZodValidationSchemaDefinition, context: ContextSpec, coerceTypes: boolean | ZodCoerceType[] | undefined, strict: boolean, isZodV4: boolean, preprocess?: GeneratorMutator, paramsInjection?: ZodParamsInjection, variant?: ZodVariantOption) => {
79
108
  zod: string;
80
109
  consts: string;
81
110
  usedRefs: Set<string>;
@@ -139,5 +168,5 @@ declare const parseParameters: ({
139
168
  declare const generateZod: ClientBuilder;
140
169
  declare const builder: () => () => ClientGeneratorsBuilder;
141
170
  //#endregion
142
- export { ZodParamsContext, ZodParamsInjection, ZodValidationSchemaDefinition, builder, builder as default, dereference, generateFormDataZodSchema, generateZod, generateZodValidationSchemaDefinition, getZodDependencies, isZodVersionV4, parseParameters, parseZodValidationSchemaDefinition, predefinedZodFormats };
171
+ export { ZodParamsContext, ZodParamsInjection, ZodValidationSchemaDefinition, assertZodTarget, builder, builder as default, dereference, generateFormDataZodSchema, generateZod, generateZodValidationSchemaDefinition, getZodDependencies, getZodImportSource, getZodTypeName, isZodVersionV4, parseParameters, parseZodValidationSchemaDefinition, predefinedZodFormats, resolveIsZodV4 };
143
172
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { buildDynamicScope, camel, compareVersions, generateMutator, getDynamicAnchorName, getFormDataFieldFileType, getNumberWord, getRefInfo, isBoolean, isDynamicReference, isNumber, isObject, isString, jsStringEscape, jsStringLiteralEscape, logVerbose, pascal, resolveDynamicRef, resolveRef, stringify } from "@orval/core";
1
+ import { buildDynamicScope, buildInlineDynamicScope, camel, compareVersions, generateMutator, getDynamicAnchorName, getFormDataFieldFileType, getNumberWord, getRefInfo, isBoolean, isDynamicReference, isNumber, isObject, isString, jsStringEscape, jsStringLiteralEscape, logVerbose, pascal, resolveDynamicRef, resolveRef, stringify } from "@orval/core";
2
2
  import { unique } from "remeda";
3
3
  //#region src/compatible-v4.ts
4
4
  const getZodPackageVersion = (packageJson) => {
@@ -10,6 +10,27 @@ const isZodVersionV4 = (packageJson) => {
10
10
  const withoutRc = version.split("-")[0];
11
11
  return compareVersions(withoutRc, "4.0.0");
12
12
  };
13
+ /**
14
+ * Resolves whether to emit Zod 4-style output.
15
+ *
16
+ * An explicit `override.zod.version` of `3` or `4` always wins; `'auto'` (the
17
+ * default) falls back to inferring from the output project's resolved `zod`
18
+ * version via {@link isZodVersionV4}. When no package metadata is available,
19
+ * `'auto'` now defaults to Zod 4 so fresh or partially-installed workspaces
20
+ * still get the modern baseline. This keeps generation deterministic when a
21
+ * target is pinned while preserving package-detection for `'auto'`.
22
+ */
23
+ const resolveIsZodV4 = (version, packageJson) => {
24
+ if (version === 4) return true;
25
+ if (version === 3) return false;
26
+ if (!packageJson || !getZodPackageVersion(packageJson)) return true;
27
+ return isZodVersionV4(packageJson);
28
+ };
29
+ const assertZodTarget = ({ variant, isZodV4 }) => {
30
+ if (variant === "mini" && !isZodV4) throw new Error("Zod Mini requires Zod 4 output. Use override.zod.version: 4 or install zod@^4 when override.zod.version is 'auto'.");
31
+ };
32
+ const getZodImportSource = (variant) => variant === "mini" ? "zod/mini" : "zod";
33
+ const getZodTypeName = (variant) => variant === "mini" ? "ZodMiniType" : "ZodType";
13
34
  const getZodDateFormat = (isZodV4) => {
14
35
  return isZodV4 ? "iso.date" : "date";
15
36
  };
@@ -38,7 +59,7 @@ const getLooseObjectFunctionName = (isZodV4) => {
38
59
  };
39
60
  //#endregion
40
61
  //#region src/index.ts
41
- const ZOD_DEPENDENCIES = [{
62
+ const getZodDependencies = (_hasGlobalMutator, _hasParamsSerializerOptions, _packageJson, _httpClient, _hasTagsMutator, override) => [{
42
63
  exports: [{
43
64
  default: false,
44
65
  name: "zod",
@@ -46,9 +67,8 @@ const ZOD_DEPENDENCIES = [{
46
67
  namespaceImport: true,
47
68
  values: true
48
69
  }],
49
- dependency: "zod"
70
+ dependency: getZodImportSource(override?.zod.variant)
50
71
  }];
51
- const getZodDependencies = () => ZOD_DEPENDENCIES;
52
72
  /**
53
73
  * values that may appear in "type". Equals SchemaObjectType
54
74
  */
@@ -94,6 +114,9 @@ const COERCIBLE_TYPES = new Set([
94
114
  "bigint",
95
115
  "date"
96
116
  ]);
117
+ const PURE_COMMENT = "/*#__PURE__*/ ";
118
+ const zodMiniCall = (fn, args = "") => `${PURE_COMMENT}zod.${fn}(${args})`;
119
+ const zodMiniCoerceCall = (fn, args = "") => `${PURE_COMMENT}zod.coerce.${fn}(${args})`;
97
120
  const minAndMaxTypes = new Set([
98
121
  "number",
99
122
  "string",
@@ -190,6 +213,7 @@ const generateZodValidationSchemaDefinition = (schema, context, name, strict, is
190
213
  schema = dereference(schema, context);
191
214
  }
192
215
  const useReusableSchemas = rules?.useReusableSchemas ?? false;
216
+ const urlEncoded = rules?.urlEncoded ?? false;
193
217
  const consts = [];
194
218
  const constNameRegistry = rules?.constNameRegistry ?? {};
195
219
  const constsCounter = isNumber(constNameRegistry[name]) ? constNameRegistry[name] + 1 : 0;
@@ -231,7 +255,8 @@ const generateZodValidationSchemaDefinition = (schema, context, name, strict, is
231
255
  required: true,
232
256
  additionalRequired: allOfRequired,
233
257
  constNameRegistry,
234
- useReusableSchemas
258
+ useReusableSchemas,
259
+ urlEncoded
235
260
  }));
236
261
  if ((schema.allOf || schema.oneOf || schema.anyOf) && schema.properties) {
237
262
  const additionalPropertiesSchema = {
@@ -245,7 +270,8 @@ const generateZodValidationSchemaDefinition = (schema, context, name, strict, is
245
270
  required: true,
246
271
  additionalRequired: allOfRequired,
247
272
  constNameRegistry,
248
- useReusableSchemas
273
+ useReusableSchemas,
274
+ urlEncoded
249
275
  });
250
276
  if (schema.oneOf || schema.anyOf) functions.push(["allOf", [{
251
277
  functions: [[separator, baseSchemas]],
@@ -288,7 +314,8 @@ const generateZodValidationSchemaDefinition = (schema, context, name, strict, is
288
314
  }, context, name, strict, isZodV4, {
289
315
  required: true,
290
316
  constNameRegistry,
291
- useReusableSchemas
317
+ useReusableSchemas,
318
+ urlEncoded
292
319
  }))]);
293
320
  if (!required && nullable) functions.push(["nullish", void 0]);
294
321
  else if (nullable) functions.push(["nullable", void 0]);
@@ -323,12 +350,14 @@ const generateZodValidationSchemaDefinition = (schema, context, name, strict, is
323
350
  functions.push(["tuple", prefixItems.map((item, idx) => generateZodValidationSchemaDefinition(dereference(item, context), context, camel(`${name}-${idx}-item`), isZodV4, strict, {
324
351
  required: true,
325
352
  constNameRegistry,
326
- useReusableSchemas
353
+ useReusableSchemas,
354
+ urlEncoded
327
355
  }))]);
328
356
  if (schema.items && (max ?? Number.POSITIVE_INFINITY) > prefixItems.length) functions.push(["rest", generateZodValidationSchemaDefinition(schema.items, context, camel(`${name}-item`), strict, isZodV4, {
329
357
  required: true,
330
358
  constNameRegistry,
331
- useReusableSchemas
359
+ useReusableSchemas,
360
+ urlEncoded
332
361
  })]);
333
362
  }
334
363
  }
@@ -337,7 +366,8 @@ const generateZodValidationSchemaDefinition = (schema, context, name, strict, is
337
366
  functions.push(["array", generateZodValidationSchemaDefinition(schema.items, context, camel(`${name}-item`), strict, isZodV4, {
338
367
  required: true,
339
368
  constNameRegistry,
340
- useReusableSchemas
369
+ useReusableSchemas,
370
+ urlEncoded
341
371
  })]);
342
372
  break;
343
373
  case "string":
@@ -346,11 +376,11 @@ const generateZodValidationSchemaDefinition = (schema, context, name, strict, is
346
376
  functions.push(["date", void 0]);
347
377
  break;
348
378
  }
349
- if (schema.format === "binary") {
379
+ if (!urlEncoded && schema.format === "binary") {
350
380
  functions.push(["instanceof", "File"]);
351
381
  break;
352
382
  }
353
- if (schema.contentMediaType === "application/octet-stream" && !schema.contentEncoding) {
383
+ if (!urlEncoded && schema.contentMediaType === "application/octet-stream" && !schema.contentEncoding) {
354
384
  functions.push(["instanceof", "File"]);
355
385
  break;
356
386
  }
@@ -415,7 +445,8 @@ const generateZodValidationSchemaDefinition = (schema, context, name, strict, is
415
445
  functions.push([objectType, Object.keys(properties).map((key) => ({ [key]: rules?.propertyOverrides?.[key] ?? generateZodValidationSchemaDefinition(properties[key], context, camel(`${name}-${key}`), strict, isZodV4, {
416
446
  required: requiredKeys.has(key),
417
447
  constNameRegistry,
418
- useReusableSchemas
448
+ useReusableSchemas,
449
+ urlEncoded
419
450
  }) })).reduce((acc, curr) => ({
420
451
  ...acc,
421
452
  ...curr
@@ -433,7 +464,8 @@ const generateZodValidationSchemaDefinition = (schema, context, name, strict, is
433
464
  functions.push(["additionalProperties", generateZodValidationSchemaDefinition(isBoolean(schema.additionalProperties) ? {} : schema.additionalProperties, context, name, strict, isZodV4, {
434
465
  required: true,
435
466
  constNameRegistry,
436
- useReusableSchemas
467
+ useReusableSchemas,
468
+ urlEncoded
437
469
  })]);
438
470
  break;
439
471
  }
@@ -512,7 +544,7 @@ const PARAMS_MERGE_INTO_OPTIONS_VALIDATORS = new Set([
512
544
  "iso.datetime",
513
545
  "iso.time"
514
546
  ]);
515
- const parseZodValidationSchemaDefinition = (input, context, coerceTypes = false, strict, isZodV4, preprocess, paramsInjection) => {
547
+ const parseZodValidationSchemaDefinition = (input, context, coerceTypes = false, strict, isZodV4, preprocess, paramsInjection, variant = "classic") => {
516
548
  if (input.functions.length === 0) return {
517
549
  zod: "",
518
550
  consts: "",
@@ -546,6 +578,196 @@ const parseZodValidationSchemaDefinition = (input, context, coerceTypes = false,
546
578
  };
547
579
  return `${paramsInjection.mutator.name}(${JSON.stringify(ctx)})`;
548
580
  };
581
+ const shouldCoerce = (fn) => coerceTypes && (Array.isArray(coerceTypes) ? coerceTypes.includes(fn) : COERCIBLE_TYPES.has(fn));
582
+ const buildCombinedArgs = (fn, args, fieldPath) => {
583
+ const formattedArgs = formatFunctionArgs(args);
584
+ const paramsArg = buildParamsArg(fn, fieldPath);
585
+ if (paramsArg && formattedArgs && PARAMS_MERGE_INTO_OPTIONS_VALIDATORS.has(fn)) return `{ ...${formattedArgs}, ...${paramsArg} }`;
586
+ if (paramsArg) return formattedArgs ? `${formattedArgs}, ${paramsArg}` : paramsArg;
587
+ return formattedArgs;
588
+ };
589
+ const renderMiniDefinition = (definition, fieldPath = []) => {
590
+ let current;
591
+ const requireCurrent = (fn) => {
592
+ if (!current) throw new Error(`Cannot render zod mini ${fn} without a base schema`);
593
+ return current;
594
+ };
595
+ const renderObject = (objectArgs, objectType) => ({
596
+ kind: "object",
597
+ expr: `${zodMiniCall(objectType, `{
598
+ ${Object.entries(objectArgs).map(([key, schema]) => {
599
+ const rendered = renderMiniDefinition(schema, [...fieldPath, key]);
600
+ appendConstsChunk(schema.consts.join("\n"));
601
+ if (Array.isArray(coerceTypes) && coerceTypes.includes("array") && schema.functions.some(([fn]) => fn === "array")) return ` "${key}": ${zodMiniCall("pipe", `${zodMiniCall("transform", "(value) => value === undefined || Array.isArray(value) ? value : [value]")}, ${rendered.expr}`)}`;
602
+ return ` "${key}": ${rendered.expr}`;
603
+ }).join(",\n")}
604
+ }`)}`
605
+ });
606
+ for (let index = 0; index < definition.functions.length; index++) {
607
+ const [fn, args = ""] = definition.functions[index];
608
+ if (fn === "namedRef") {
609
+ const refArgs = args;
610
+ usedRefs.add(refArgs.name);
611
+ current = {
612
+ expr: `__REF_${refArgs.name}__`,
613
+ kind: "ref"
614
+ };
615
+ continue;
616
+ }
617
+ if (fn === "fileOrString") {
618
+ current = {
619
+ expr: zodMiniCall("union", `[${zodMiniCall("instanceof", "File")}, ${zodMiniCall("string")}]`),
620
+ kind: "union"
621
+ };
622
+ continue;
623
+ }
624
+ if (fn === "allOf") {
625
+ const allOfArgs = args;
626
+ if (strict && allOfArgs.length > 0 && allOfArgs.every((partSchema) => {
627
+ if (partSchema.functions.length === 0) return false;
628
+ const firstFn = partSchema.functions[0][0];
629
+ return firstFn === "object" || firstFn === "strictObject";
630
+ })) {
631
+ const mergedProperties = {};
632
+ const allConsts = [];
633
+ for (const partSchema of allOfArgs) {
634
+ if (partSchema.consts.length > 0) allConsts.push(partSchema.consts.join("\n"));
635
+ const objectFunctionIndex = partSchema.functions.findIndex(([fnName]) => fnName === "object" || fnName === "strictObject");
636
+ if (objectFunctionIndex !== -1) {
637
+ const objectArgs = partSchema.functions[objectFunctionIndex][1];
638
+ if (isObject(objectArgs)) Object.assign(mergedProperties, objectArgs);
639
+ }
640
+ }
641
+ appendConstsChunk(allConsts.join("\n"));
642
+ current = renderObject(mergedProperties, getObjectFunctionName(true, strict));
643
+ continue;
644
+ }
645
+ const rendered = allOfArgs.map((partSchema) => {
646
+ appendConstsChunk(partSchema.consts.join("\n"));
647
+ return renderMiniDefinition(partSchema, fieldPath).expr;
648
+ });
649
+ if (rendered.length === 0) {
650
+ current = { expr: "" };
651
+ continue;
652
+ }
653
+ current = {
654
+ expr: rendered.reduce((acc, value) => acc ? zodMiniCall("intersection", `${acc}, ${value}`) : value),
655
+ kind: "intersection"
656
+ };
657
+ continue;
658
+ }
659
+ if (fn === "oneOf" || fn === "anyOf") {
660
+ const unionArgs = args;
661
+ if (unionArgs.length === 1) {
662
+ appendConstsChunk(unionArgs[0].consts.join("\n"));
663
+ current = renderMiniDefinition(unionArgs[0], fieldPath);
664
+ continue;
665
+ }
666
+ current = {
667
+ expr: zodMiniCall("union", `[${unionArgs.map((arg) => {
668
+ appendConstsChunk(arg.consts.join("\n"));
669
+ return renderMiniDefinition(arg, fieldPath).expr;
670
+ }).join(",")}]`),
671
+ kind: "union"
672
+ };
673
+ continue;
674
+ }
675
+ if (fn === "additionalProperties") {
676
+ const additionalPropertiesArgs = args;
677
+ const rendered = renderMiniDefinition(additionalPropertiesArgs, fieldPath);
678
+ if (Array.isArray(additionalPropertiesArgs.consts)) appendConstsChunk(additionalPropertiesArgs.consts.join("\n"));
679
+ current = {
680
+ expr: zodMiniCall("record", `${zodMiniCall("string")}, ${rendered.expr}`),
681
+ kind: "object"
682
+ };
683
+ continue;
684
+ }
685
+ if (fn === "object" || fn === "strictObject" || fn === "looseObject") {
686
+ current = renderObject(args, fn === "looseObject" ? "looseObject" : getObjectFunctionName(true, strict));
687
+ continue;
688
+ }
689
+ if (fn === "passthrough" || fn === "strict") continue;
690
+ if (fn === "array") {
691
+ const arrayArgs = args;
692
+ const rendered = renderMiniDefinition(arrayArgs, fieldPath);
693
+ if (isString(arrayArgs.consts)) appendConstsChunk(arrayArgs.consts);
694
+ else if (Array.isArray(arrayArgs.consts)) appendConstsChunk(arrayArgs.consts.join("\n"));
695
+ current = {
696
+ expr: zodMiniCall("array", rendered.expr),
697
+ kind: "array"
698
+ };
699
+ continue;
700
+ }
701
+ if (fn === "tuple") {
702
+ const tupleItems = args.map((x) => {
703
+ const rendered = renderMiniDefinition(x, fieldPath);
704
+ appendConstsChunk(x.consts.join("\n"));
705
+ return rendered.expr;
706
+ }).join(",\n");
707
+ const next = definition.functions[index + 1];
708
+ if (next?.[0] === "rest") {
709
+ const restDefinition = next[1];
710
+ const rest = renderMiniDefinition(restDefinition, fieldPath).expr;
711
+ appendConstsChunk(restDefinition.consts.join("\n"));
712
+ current = {
713
+ expr: zodMiniCall("tuple", `[${tupleItems}], ${rest}`),
714
+ kind: "tuple"
715
+ };
716
+ index++;
717
+ } else current = {
718
+ expr: zodMiniCall("tuple", `[${tupleItems}]`),
719
+ kind: "tuple"
720
+ };
721
+ continue;
722
+ }
723
+ const combinedArgs = buildCombinedArgs(fn, args, fieldPath);
724
+ if (fn === "optional" || fn === "nullable" || fn === "nullish") {
725
+ const value = requireCurrent(fn);
726
+ current = {
727
+ expr: zodMiniCall(fn, value.expr),
728
+ kind: value.kind
729
+ };
730
+ continue;
731
+ }
732
+ if (fn === "default") {
733
+ const value = requireCurrent(fn);
734
+ current = {
735
+ expr: zodMiniCall("_default", `${value.expr}, ${combinedArgs}`),
736
+ kind: value.kind
737
+ };
738
+ continue;
739
+ }
740
+ if (fn === "describe" || fn === "meta") {
741
+ const value = requireCurrent(fn);
742
+ current = {
743
+ expr: `${value.expr}.check(${zodMiniCall(fn, combinedArgs)})`,
744
+ kind: value.kind
745
+ };
746
+ continue;
747
+ }
748
+ if (fn === "min" || fn === "max" || fn === "gt" || fn === "lt" || fn === "multipleOf" || fn === "regex" || fn === "length") {
749
+ const value = requireCurrent(fn);
750
+ const checkName = fn === "min" ? value.kind === "number" ? "gte" : "minLength" : fn === "max" ? value.kind === "number" ? "lte" : "maxLength" : fn;
751
+ current = {
752
+ expr: `${value.expr}.check(${zodMiniCall(checkName, combinedArgs)})`,
753
+ kind: value.kind
754
+ };
755
+ continue;
756
+ }
757
+ if (fn !== "date" && shouldCoerce(fn) || fn === "date" && shouldCoerce(fn) && context.output.override.useDates) {
758
+ current = {
759
+ expr: zodMiniCoerceCall(fn, combinedArgs),
760
+ kind: fn
761
+ };
762
+ continue;
763
+ }
764
+ current = {
765
+ expr: zodMiniCall(fn, combinedArgs),
766
+ kind: fn === "enum" || fn === "literal" || fn === "stringFormat" ? "string" : fn.split(".")[0]
767
+ };
768
+ }
769
+ return current ?? { expr: "" };
770
+ };
549
771
  const parseProperty = (property, fieldPath = []) => {
550
772
  const [fn, args = ""] = property;
551
773
  if (fn === "namedRef") {
@@ -658,6 +880,16 @@ ${Object.entries(objectArgs).map(([key, schema]) => {
658
880
  return `.${fn}(${combinedArgs})`;
659
881
  };
660
882
  appendConstsChunk(input.consts.join("\n"));
883
+ if (variant === "mini") {
884
+ const rendered = renderMiniDefinition(input);
885
+ const value = preprocess ? zodMiniCall("pipe", `${zodMiniCall("transform", preprocess.name)}, ${rendered.expr}`) : rendered.expr;
886
+ if (consts.includes(",export")) consts = consts.replaceAll(",export", "\nexport");
887
+ return {
888
+ zod: value,
889
+ consts,
890
+ usedRefs
891
+ };
892
+ }
661
893
  const schema = input.functions.map((prop) => parseProperty(prop)).join("");
662
894
  const value = preprocess ? `.preprocess(${preprocess.name}, ${schema.startsWith(".") ? "zod" : ""}${schema})` : schema;
663
895
  const zod = `${value.startsWith(".") ? "zod" : ""}${value}`;
@@ -740,17 +972,27 @@ function dereferenceProperties(schema, context) {
740
972
  }, {});
741
973
  }
742
974
  function buildScopedContext(childContext, refName, resolvedSchema) {
743
- if (!refName) return childContext;
744
- const schemaName = extractSchemaNameFromRef(refName);
745
- if (!schemaName) return childContext;
746
- const schemaRecord = resolvedSchema;
747
- const hasDynamicAnchor = typeof schemaRecord.$dynamicAnchor === "string";
748
- const defs = schemaRecord.$defs;
749
- const hasDefsAnchors = defs && typeof defs === "object" && Object.values(defs).some((d) => d && typeof d === "object" && "$dynamicAnchor" in d);
750
- if (!hasDynamicAnchor && !hasDefsAnchors) return childContext;
975
+ if (refName) {
976
+ const schemaName = extractSchemaNameFromRef(refName);
977
+ if (!schemaName) return childContext;
978
+ const schemaRecord = resolvedSchema;
979
+ const hasDynamicAnchor = typeof schemaRecord.$dynamicAnchor === "string";
980
+ const defs = schemaRecord.$defs;
981
+ const hasDefsAnchors = defs && typeof defs === "object" && Object.values(defs).some((d) => d && typeof d === "object" && "$dynamicAnchor" in d);
982
+ if (!hasDynamicAnchor && !hasDefsAnchors) return childContext;
983
+ return {
984
+ ...childContext,
985
+ dynamicScope: buildDynamicScope(schemaName, resolvedSchema, childContext)
986
+ };
987
+ }
988
+ const inlineScope = buildInlineDynamicScope(resolvedSchema);
989
+ if (Object.keys(inlineScope).length === 0) return childContext;
751
990
  return {
752
991
  ...childContext,
753
- dynamicScope: buildDynamicScope(schemaName, resolvedSchema, childContext)
992
+ dynamicScope: {
993
+ ...childContext.dynamicScope,
994
+ ...inlineScope
995
+ }
754
996
  };
755
997
  }
756
998
  function dereferenceDynamicRef(schema, context) {
@@ -801,6 +1043,15 @@ const generateFormDataZodSchema = (schema, context, name, strict, isZodV4, encod
801
1043
  useReusableSchemas
802
1044
  });
803
1045
  };
1046
+ /**
1047
+ * Parse a request body or response into a zod validation schema definition.
1048
+ *
1049
+ * Selects the relevant content type — JSON (and `+json` vendor types),
1050
+ * `multipart/form-data`, or `application/x-www-form-urlencoded`, plus
1051
+ * `text/plain` for responses — and generates the matching schema, handling
1052
+ * array roots. The url-encoded string-contract rationale lives where the flag
1053
+ * is derived (see `isFormUrlEncoded` below).
1054
+ */
804
1055
  const parseBodyAndResponse = ({ data, context, name, strict, generate, isZodV4, parseType, useReusableSchemas }) => {
805
1056
  if (!data || !generate) return {
806
1057
  input: {
@@ -813,7 +1064,9 @@ const parseBodyAndResponse = ({ data, context, name, strict, generate, isZodV4,
813
1064
  const contentEntries = Object.entries(resolvedRef.content ?? {});
814
1065
  const jsonContent = contentEntries.find(isMediaType(String.raw`^application\/([^/;]+\+)?json$`));
815
1066
  const formDataContent = contentEntries.find(isMediaType(String.raw`^multipart\/form-data$`));
816
- const [contentType, mediaType] = jsonContent ? ["application/json", jsonContent[1]] : formDataContent ? ["multipart/form-data", formDataContent[1]] : [void 0, void 0];
1067
+ const formUrlEncodedContent = contentEntries.find(isMediaType(String.raw`^application\/x-www-form-urlencoded$`));
1068
+ const [contentType, mediaType] = jsonContent ? ["application/json", jsonContent[1]] : formDataContent ? ["multipart/form-data", formDataContent[1]] : formUrlEncodedContent ? ["application/x-www-form-urlencoded", formUrlEncodedContent[1]] : [void 0, void 0];
1069
+ const isFormUrlEncoded = contentType === "application/x-www-form-urlencoded";
817
1070
  const schema = mediaType?.schema;
818
1071
  if (!schema) {
819
1072
  if (parseType === "response") {
@@ -844,7 +1097,8 @@ const parseBodyAndResponse = ({ data, context, name, strict, generate, isZodV4,
844
1097
  return {
845
1098
  input: generateZodValidationSchemaDefinition(parseType === "body" ? removeReadOnlyProperties(rawItems) : rawItems, context, name, strict, isZodV4, {
846
1099
  required: true,
847
- useReusableSchemas
1100
+ useReusableSchemas,
1101
+ urlEncoded: isFormUrlEncoded
848
1102
  }),
849
1103
  isArray: true,
850
1104
  rules: {
@@ -857,7 +1111,8 @@ const parseBodyAndResponse = ({ data, context, name, strict, generate, isZodV4,
857
1111
  return {
858
1112
  input: contentType === "multipart/form-data" ? generateFormDataZodSchema(effectiveSchema, context, name, strict, isZodV4, encoding, useReusableSchemas) : generateZodValidationSchemaDefinition(effectiveSchema, context, name, strict, isZodV4, {
859
1113
  required: true,
860
- useReusableSchemas
1114
+ useReusableSchemas,
1115
+ urlEncoded: isFormUrlEncoded
861
1116
  }),
862
1117
  isArray: false
863
1118
  };
@@ -965,7 +1220,12 @@ const parseParameters = ({ data, context, operationName, isZodV4, strict, genera
965
1220
  };
966
1221
  };
967
1222
  const generateZodRoute = async ({ operationId, operationName, verb, override }, { pathRoute, context, output }) => {
968
- const isZodV4 = !!context.output.packageJson && isZodVersionV4(context.output.packageJson);
1223
+ const zodVariant = context.output.override.zod.variant;
1224
+ const isZodV4 = resolveIsZodV4(context.output.override.zod.version, context.output.packageJson);
1225
+ assertZodTarget({
1226
+ variant: zodVariant,
1227
+ isZodV4
1228
+ });
969
1229
  const useReusableSchemas = context.output.override.zod.generateReusableSchemas;
970
1230
  const spec = context.spec.paths?.[pathRoute];
971
1231
  if (spec == void 0) throw new Error(`No such path ${pathRoute} in ${context.projectName}`);
@@ -1021,7 +1281,7 @@ const generateZodRoute = async ({ operationId, operationName, verb, override },
1021
1281
  location,
1022
1282
  schemaName: `${pascalOperationName}${schemaSuffix}`
1023
1283
  } : void 0;
1024
- let inputParams = parseZodValidationSchemaDefinition(parsedParameters.params, context, override.zod.coerce.param, override.zod.strict.param, isZodV4, preprocessParams, makeParamsInjection("param", "Params"));
1284
+ let inputParams = parseZodValidationSchemaDefinition(parsedParameters.params, context, override.zod.coerce.param, override.zod.strict.param, isZodV4, preprocessParams, makeParamsInjection("param", "Params"), zodVariant);
1025
1285
  const preprocessQueryParams = override.zod.preprocess?.query ? await generateMutator({
1026
1286
  output,
1027
1287
  mutator: override.zod.preprocess.query,
@@ -1029,7 +1289,7 @@ const generateZodRoute = async ({ operationId, operationName, verb, override },
1029
1289
  workspace: context.workspace,
1030
1290
  tsconfig: context.output.tsconfig
1031
1291
  }) : void 0;
1032
- let inputQueryParams = parseZodValidationSchemaDefinition(parsedParameters.queryParams, context, override.zod.coerce.query, override.zod.strict.query, isZodV4, preprocessQueryParams, makeParamsInjection("query", "QueryParams"));
1292
+ let inputQueryParams = parseZodValidationSchemaDefinition(parsedParameters.queryParams, context, override.zod.coerce.query, override.zod.strict.query, isZodV4, preprocessQueryParams, makeParamsInjection("query", "QueryParams"), zodVariant);
1033
1293
  const preprocessHeader = override.zod.preprocess?.header ? await generateMutator({
1034
1294
  output,
1035
1295
  mutator: override.zod.preprocess.header,
@@ -1037,7 +1297,7 @@ const generateZodRoute = async ({ operationId, operationName, verb, override },
1037
1297
  workspace: context.workspace,
1038
1298
  tsconfig: context.output.tsconfig
1039
1299
  }) : void 0;
1040
- let inputHeaders = parseZodValidationSchemaDefinition(parsedParameters.headers, context, override.zod.coerce.header, override.zod.strict.header, isZodV4, preprocessHeader, makeParamsInjection("header", "Header"));
1300
+ let inputHeaders = parseZodValidationSchemaDefinition(parsedParameters.headers, context, override.zod.coerce.header, override.zod.strict.header, isZodV4, preprocessHeader, makeParamsInjection("header", "Header"), zodVariant);
1041
1301
  const preprocessBody = override.zod.preprocess?.body ? await generateMutator({
1042
1302
  output,
1043
1303
  mutator: override.zod.preprocess.body,
@@ -1045,7 +1305,7 @@ const generateZodRoute = async ({ operationId, operationName, verb, override },
1045
1305
  workspace: context.workspace,
1046
1306
  tsconfig: context.output.tsconfig
1047
1307
  }) : void 0;
1048
- let inputBody = parseZodValidationSchemaDefinition(parsedBody.input, context, override.zod.coerce.body, override.zod.strict.body, isZodV4, preprocessBody, makeParamsInjection("body", "Body"));
1308
+ let inputBody = parseZodValidationSchemaDefinition(parsedBody.input, context, override.zod.coerce.body, override.zod.strict.body, isZodV4, preprocessBody, makeParamsInjection("body", "Body"), zodVariant);
1049
1309
  const preprocessResponse = override.zod.preprocess?.response ? await generateMutator({
1050
1310
  output,
1051
1311
  mutator: override.zod.preprocess.response,
@@ -1053,7 +1313,7 @@ const generateZodRoute = async ({ operationId, operationName, verb, override },
1053
1313
  workspace: context.workspace,
1054
1314
  tsconfig: context.output.tsconfig
1055
1315
  }) : void 0;
1056
- const inputResponses = parsedResponses.map((parsedResponse, index) => parseZodValidationSchemaDefinition(parsedResponse.input, context, override.zod.coerce.response, override.zod.strict.response, isZodV4, preprocessResponse, makeParamsInjection("response", responses[index][0] ? `${responses[index][0]}Response` : "Response")));
1316
+ const inputResponses = parsedResponses.map((parsedResponse, index) => parseZodValidationSchemaDefinition(parsedResponse.input, context, override.zod.coerce.response, override.zod.strict.response, isZodV4, preprocessResponse, makeParamsInjection("response", responses[index][0] ? `${responses[index][0]}Response` : "Response"), zodVariant));
1057
1317
  const SENTINEL_PATTERN = /__REF_([A-Za-z_$][A-Za-z0-9_$]*)__/g;
1058
1318
  const rewriteSentinels = (s) => s.replaceAll(SENTINEL_PATTERN, (_m, name) => name);
1059
1319
  const allUsedRefs = new Set([
@@ -1092,6 +1352,11 @@ const generateZodRoute = async ({ operationId, operationName, verb, override },
1092
1352
  };
1093
1353
  const useBrandedTypes = override.zod.useBrandedTypes;
1094
1354
  const brand = (name) => useBrandedTypes ? isZodV4 ? `.brand("${name}")` : `.brand<"${name}">()` : "";
1355
+ const zodArrayWithBounds = (itemName, rules) => {
1356
+ const checks = [...rules?.min ? [zodMiniCall("minLength", `${rules.min}`)] : [], ...rules?.max ? [zodMiniCall("maxLength", `${rules.max}`)] : []];
1357
+ if (zodVariant === "mini") return `${zodMiniCall("array", itemName)}${checks.map((check) => `.check(${check})`).join("")}`;
1358
+ return `zod.array(${itemName})${rules?.min ? `.min(${rules.min})` : ""}${rules?.max ? `.max(${rules.max})` : ""}`;
1359
+ };
1095
1360
  const localTaken = new Set(allUsedRefs);
1096
1361
  const allocateExportName = (baseName, hasItem) => {
1097
1362
  const collides = (name) => localTaken.has(name) || hasItem && localTaken.has(`${name}Item`);
@@ -1126,7 +1391,7 @@ const generateZodRoute = async ({ operationId, operationName, verb, override },
1126
1391
  ...inputHeaders.zod ? [`export const ${headerName} = ${inputHeaders.zod}${brand(headerName)}`] : [],
1127
1392
  ...inputBody.consts ? [inputBody.consts] : [],
1128
1393
  ...inputBody.zod ? [parsedBody.isArray ? `export const ${bodyName}Item = ${inputBody.zod}
1129
- export const ${bodyName} = zod.array(${bodyName}Item)${parsedBody.rules?.min ? `.min(${parsedBody.rules.min})` : ""}${parsedBody.rules?.max ? `.max(${parsedBody.rules.max})` : ""}${brand(bodyName)}` : `export const ${bodyName} = ${inputBody.zod}${brand(bodyName)}`] : [],
1394
+ export const ${bodyName} = ${zodArrayWithBounds(bodyName + "Item", parsedBody.rules)}${brand(bodyName)}` : `export const ${bodyName} = ${inputBody.zod}${brand(bodyName)}`] : [],
1130
1395
  ...inputResponses.flatMap((inputResponse, index) => {
1131
1396
  const operationResponse = allocateExportName(pascal(`${operationName}-${responses[index][0]}-response`), parsedResponses[index].isArray);
1132
1397
  if (!inputResponse.zod) {
@@ -1140,10 +1405,10 @@ export const ${bodyName} = zod.array(${bodyName}Item)${parsedBody.rules?.min ? `
1140
1405
  const specResponseKeys = new Set(Object.keys(spec[verb]?.responses ?? {}));
1141
1406
  isNoContent = !(specResponseKeys.has("200") || specResponseKeys.has("2XX") || specResponseKeys.has("2xx"));
1142
1407
  }
1143
- return [`export const ${operationResponse} = ${isNoContent ? "zod.void()" : "zod.unknown()"}${brand(operationResponse)}`];
1408
+ return [`export const ${operationResponse} = ${isNoContent ? zodVariant === "mini" ? zodMiniCall("void") : "zod.void()" : zodVariant === "mini" ? zodMiniCall("unknown") : "zod.unknown()"}${brand(operationResponse)}`];
1144
1409
  }
1145
1410
  return [...inputResponse.consts ? [inputResponse.consts] : [], parsedResponses[index].isArray ? `export const ${operationResponse}Item = ${inputResponse.zod}
1146
- export const ${operationResponse} = zod.array(${operationResponse}Item)${parsedResponses[index].rules?.min ? `.min(${parsedResponses[index].rules.min})` : ""}${parsedResponses[index].rules?.max ? `.max(${parsedResponses[index].rules.max})` : ""}${brand(operationResponse)}` : `export const ${operationResponse} = ${inputResponse.zod}${brand(operationResponse)}`];
1411
+ export const ${operationResponse} = ${zodArrayWithBounds(`${operationResponse}Item`, parsedResponses[index].rules)}${brand(operationResponse)}` : `export const ${operationResponse} = ${inputResponse.zod}${brand(operationResponse)}`];
1147
1412
  })
1148
1413
  ].join("\n\n"),
1149
1414
  mutators: [
@@ -1175,6 +1440,6 @@ const zodClientBuilder = {
1175
1440
  };
1176
1441
  const builder = () => () => zodClientBuilder;
1177
1442
  //#endregion
1178
- export { builder, builder as default, dereference, generateFormDataZodSchema, generateZod, generateZodValidationSchemaDefinition, getZodDependencies, isZodVersionV4, parseParameters, parseZodValidationSchemaDefinition, predefinedZodFormats };
1443
+ export { assertZodTarget, builder, builder as default, dereference, generateFormDataZodSchema, generateZod, generateZodValidationSchemaDefinition, getZodDependencies, getZodImportSource, getZodTypeName, isZodVersionV4, parseParameters, parseZodValidationSchemaDefinition, predefinedZodFormats, resolveIsZodV4 };
1179
1444
 
1180
1445
  //# sourceMappingURL=index.mjs.map