@orval/zod 8.32.0 → 8.34.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,6 +1,6 @@
1
- import { ClientBuilder, ClientDependenciesBuilder, ClientGeneratorsBuilder, ContextSpec, GeneratorMutator, OpenApiParameterObject, OpenApiReferenceObject, OpenApiSchemaObject, PackageJson, ZodCoerceType, ZodVariantOption, ZodVersionOption } from "@orval/core";
1
+ import { ClientBuilder, ClientDependenciesBuilder, ClientGeneratorsBuilder, ContextSpec, GeneratorMutator, GeneratorVerbOptions, OpenApiParameterObject, OpenApiReferenceObject, OpenApiSchemaObject, PackageJson, ZodCoerceType, ZodVariantOption, ZodVersionOption } from "@orval/core";
2
2
  //#region src/compatible-v4.d.ts
3
- declare const isZodVersionV4: (packageJson: PackageJson) => boolean;
3
+ export declare const isZodVersionV4: (packageJson: PackageJson) => boolean;
4
4
  /**
5
5
  * Resolves whether to emit Zod 4-style output.
6
6
  *
@@ -11,13 +11,13 @@ declare const isZodVersionV4: (packageJson: PackageJson) => boolean;
11
11
  * still get the modern baseline. This keeps generation deterministic when a
12
12
  * target is pinned while preserving package-detection for `'auto'`.
13
13
  */
14
- declare const resolveIsZodV4: (version: ZodVersionOption | undefined, packageJson: PackageJson | undefined) => boolean;
15
- declare const assertZodTarget: ({ variant, isZodV4 }: {
14
+ export declare const resolveIsZodV4: (version: ZodVersionOption | undefined, packageJson: PackageJson | undefined) => boolean;
15
+ export declare const assertZodTarget: ({ variant, isZodV4 }: {
16
16
  variant: ZodVariantOption | undefined;
17
17
  isZodV4: boolean;
18
18
  }) => void;
19
- declare const getZodImportSource: (variant: ZodVariantOption | undefined) => "zod/mini" | "zod";
20
- declare const getZodTypeName: (variant: ZodVariantOption | undefined) => "ZodMiniType" | "ZodType";
19
+ export declare const getZodImportSource: (variant: ZodVariantOption | undefined) => "zod/mini" | "zod";
20
+ export declare const getZodTypeName: (variant: ZodVariantOption | undefined) => "ZodMiniType" | "ZodType";
21
21
  //#endregion
22
22
  //#region src/export-emitter.d.ts
23
23
  /**
@@ -70,12 +70,12 @@ interface ZodExportBlock {
70
70
  * or a recursive `zod.ZodType<X>` pin. See {@link ZodExportBlock} for what
71
71
  * this deliberately leaves to the caller.
72
72
  */
73
- declare const renderZodExport: (block: ZodExportBlock) => string;
73
+ export declare const renderZodExport: (block: ZodExportBlock) => string;
74
74
  //#endregion
75
75
  //#region src/index.d.ts
76
- declare const getZodDependencies: ClientDependenciesBuilder;
77
- declare const predefinedZodFormats: Set<string>;
78
- interface ZodValidationSchemaDefinition {
76
+ export declare const getZodDependencies: ClientDependenciesBuilder;
77
+ export declare const predefinedZodFormats: Set<string>;
78
+ export interface ZodValidationSchemaDefinition {
79
79
  functions: [string, unknown][];
80
80
  consts: string[];
81
81
  }
@@ -87,7 +87,7 @@ interface DateTimeOptions {
87
87
  interface TimeOptions {
88
88
  precision?: -1 | 0 | 1 | 2 | 3;
89
89
  }
90
- declare const generateZodValidationSchemaDefinition: (schema: OpenApiSchemaObject | OpenApiReferenceObject | undefined, context: ContextSpec, name: string, strict: boolean, isZodV4: boolean, rules?: {
90
+ export declare const generateZodValidationSchemaDefinition: (schema: OpenApiSchemaObject | OpenApiReferenceObject | undefined, context: ContextSpec, name: string, strict: boolean, isZodV4: boolean, rules?: {
91
91
  required?: boolean;
92
92
  /**
93
93
  * Required keys inherited from sibling `allOf` members. Per JSON Schema /
@@ -138,7 +138,7 @@ declare const generateZodValidationSchemaDefinition: (schema: OpenApiSchemaObjec
138
138
  * every emitted validator. Exported so consumers can type their function with
139
139
  * `import type { ZodParamsContext } from 'orval'` instead of hand-writing it.
140
140
  */
141
- interface ZodParamsContext {
141
+ export interface ZodParamsContext {
142
142
  /** The OpenAPI `operationId`, or `''` for shared component schemas. */
143
143
  operationId: string;
144
144
  /** `'schema'` is used for shared component schemas with no owning operation. */
@@ -150,16 +150,16 @@ interface ZodParamsContext {
150
150
  /** The Zod method being emitted, e.g. `'string'`, `'min'`, `'email'`. */
151
151
  validator: string;
152
152
  }
153
- interface ZodParamsInjection extends Pick<ZodParamsContext, 'operationId' | 'location' | 'schemaName'> {
153
+ export interface ZodParamsInjection extends Pick<ZodParamsContext, 'operationId' | 'location' | 'schemaName'> {
154
154
  mutator: GeneratorMutator;
155
155
  }
156
- interface ZodMetaArgs {
156
+ export interface ZodMetaArgs {
157
157
  id?: string;
158
158
  title?: string;
159
159
  description?: string;
160
160
  deprecated?: boolean;
161
161
  }
162
- declare const parseZodValidationSchemaDefinition: (input: ZodValidationSchemaDefinition, context: ContextSpec, coerceTypes: boolean | ZodCoerceType[] | undefined, strict: boolean, isZodV4: boolean, preprocess?: GeneratorMutator, paramsInjection?: ZodParamsInjection, variant?: ZodVariantOption, exactOptional?: boolean) => {
162
+ export declare const parseZodValidationSchemaDefinition: (input: ZodValidationSchemaDefinition, context: ContextSpec, coerceTypes: boolean | ZodCoerceType[] | undefined, strict: boolean, isZodV4: boolean, preprocess?: GeneratorMutator, paramsInjection?: ZodParamsInjection, variant?: ZodVariantOption, exactOptional?: boolean) => {
163
163
  zod: string;
164
164
  consts: string;
165
165
  usedRefs: Set<string>;
@@ -178,16 +178,23 @@ declare const parseZodValidationSchemaDefinition: (input: ZodValidationSchemaDef
178
178
  * 3. If resolved to a concrete schema, inline it (same as `$ref`).
179
179
  * 4. If unresolved, external, or a generic parameter → return `{}`.
180
180
  */
181
- declare const dereference: (schema: OpenApiSchemaObject | OpenApiReferenceObject, context: ContextSpec) => OpenApiSchemaObject;
181
+ export declare const dereference: (schema: OpenApiSchemaObject | OpenApiReferenceObject, context: ContextSpec) => OpenApiSchemaObject;
182
182
  /**
183
183
  * Generate zod schema for form-data request body.
184
184
  * Handles file type detection for top-level properties based on encoding.contentType
185
185
  * and contentMediaType. Mirrors type gen's resolveFormDataRootObject.
186
186
  */
187
- declare const generateFormDataZodSchema: (schema: OpenApiSchemaObject, context: ContextSpec, name: string, strict: boolean, isZodV4: boolean, encoding?: Record<string, {
187
+ export declare const generateFormDataZodSchema: (schema: OpenApiSchemaObject, context: ContextSpec, name: string, strict: boolean, isZodV4: boolean, encoding?: Record<string, {
188
188
  contentType?: string;
189
189
  }>, useReusableSchemas?: boolean) => ZodValidationSchemaDefinition;
190
- declare const parseParameters: ({ data, context, operationName, isZodV4, strict, generate, useReusableSchemas }: {
190
+ /** The success response is emitted as a plain `zod.object(...)`. */
191
+ export declare const isObjectResponseSchema: (verbOptions: Pick<GeneratorVerbOptions, "verb" | "pathRoute" | "override">, context: ContextSpec) => boolean;
192
+ /**
193
+ * A `<Operation>Response` schema that can be converted to JSON Schema is
194
+ * emitted for the success response.
195
+ */
196
+ export declare const hasResponseSchema: (verbOptions: Pick<GeneratorVerbOptions, "verb" | "pathRoute" | "override">, context: ContextSpec) => boolean;
197
+ export declare const parseParameters: ({ data, context, operationName, isZodV4, strict, generate, useReusableSchemas }: {
191
198
  data: (OpenApiParameterObject | OpenApiReferenceObject)[] | undefined;
192
199
  context: ContextSpec;
193
200
  operationName: string;
@@ -212,8 +219,8 @@ declare const parseParameters: ({ data, context, operationName, isZodV4, strict,
212
219
  queryParams: ZodValidationSchemaDefinition;
213
220
  params: ZodValidationSchemaDefinition;
214
221
  };
215
- declare const generateZod: ClientBuilder;
216
- declare const builder: () => () => ClientGeneratorsBuilder;
222
+ export declare const generateZod: ClientBuilder;
223
+ export declare const builder: () => () => ClientGeneratorsBuilder;
217
224
  //#endregion
218
- export { type ZodExportBlock, ZodMetaArgs, ZodParamsContext, ZodParamsInjection, ZodValidationSchemaDefinition, assertZodTarget, builder, builder as default, dereference, generateFormDataZodSchema, generateZod, generateZodValidationSchemaDefinition, getZodDependencies, getZodImportSource, getZodTypeName, isZodVersionV4, parseParameters, parseZodValidationSchemaDefinition, predefinedZodFormats, renderZodExport, resolveIsZodV4 };
225
+ export { type ZodExportBlock, builder as default };
219
226
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -65,8 +65,37 @@ const getLooseObjectFunctionName = (isZodV4) => {
65
65
  //#region src/export-emitter.ts
66
66
  /** Marks a call as side-effect-free for bundlers' tree-shaking passes. */
67
67
  const PURE_COMMENT = "/*#__PURE__*/ ";
68
+ /**
69
+ * A dotted path of JavaScript identifiers — the only shape a Zod method name
70
+ * may take. The dots are for zod v4's namespaced constructors (`iso.datetime`,
71
+ * `iso.date`), which the emitters reach through this same path.
72
+ */
73
+ const ZOD_METHOD_NAME_PATTERN = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*$/;
74
+ /**
75
+ * Refuses a Zod method name that is not a plain (optionally dotted) identifier.
76
+ *
77
+ * Method names are interpolated unquoted into generated source (`zod.<fn>()`,
78
+ * `.<fn>(...)`), so anything else there is executable code rather than a call.
79
+ * Every name reaching an emitter is generator-chosen, so this only fires when a
80
+ * spec-derived string has leaked into a code position — which is exactly how a
81
+ * schema's scalar `type` became import-time RCE.
82
+ *
83
+ * Called at each interpolation site rather than once up front: a definition
84
+ * tuple's name is not always a method name. The discriminator path smuggles
85
+ * `oneOf::discriminator::<property>` through the same field, and the branches
86
+ * that consume those decode them instead of emitting them.
87
+ *
88
+ * @see GHSA-v263-cp2v-vrrx
89
+ */
90
+ const assertZodMethodName = (fn) => {
91
+ if (ZOD_METHOD_NAME_PATTERN.test(fn)) return;
92
+ throw new Error(`orval: refusing to generate a Zod schema with "${fn}" as a method name — it is not a plain identifier. This value would otherwise be emitted verbatim into generated source.`);
93
+ };
68
94
  /** Renders a Zod Mini functional call, e.g. `zod.minLength(1)`, with the pure-call comment. */
69
- const zodMiniCall = (fn, args = "") => `${PURE_COMMENT}zod.${fn}(${args})`;
95
+ const zodMiniCall = (fn, args = "") => {
96
+ assertZodMethodName(fn);
97
+ return `${PURE_COMMENT}zod.${fn}(${args})`;
98
+ };
70
99
  const renderArrayWithBounds = (itemName, variant, rules) => {
71
100
  if (variant === "mini") {
72
101
  const checks = [...rules?.min ? [zodMiniCall("minLength", `${rules.min}`)] : [], ...rules?.max ? [zodMiniCall("maxLength", `${rules.max}`)] : []];
@@ -145,7 +174,7 @@ const resolveZodType = (schema) => {
145
174
  if (type === "array" && "prefixItems" in schema) return "tuple";
146
175
  return type;
147
176
  }
148
- const type = isString(schemaTypeValue) ? schemaTypeValue : void 0;
177
+ const type = isString(schemaTypeValue) && possibleSchemaTypes.has(schemaTypeValue) ? schemaTypeValue : void 0;
149
178
  if (schema.type === "array" && "prefixItems" in schema) return "tuple";
150
179
  if (!type && "const" in schema) {
151
180
  const constValue = schema.const;
@@ -163,7 +192,10 @@ const COERCIBLE_TYPES = /* @__PURE__ */ new Set([
163
192
  "bigint",
164
193
  "date"
165
194
  ]);
166
- const zodMiniCoerceCall = (fn, args = "") => `${PURE_COMMENT}zod.coerce.${fn}(${args})`;
195
+ const zodMiniCoerceCall = (fn, args = "") => {
196
+ assertZodMethodName(fn);
197
+ return `${PURE_COMMENT}zod.coerce.${fn}(${args})`;
198
+ };
167
199
  const hasUnicodePropertyEscape = (pattern) => /(^|[^\\])(?:\\\\)*\\[pP]\{/.test(pattern);
168
200
  const buildRegExpLiteral = (pattern) => {
169
201
  const innerPattern = pattern.slice(pattern.startsWith("/") ? 1 : 0, pattern.endsWith("/") ? -1 : void 0);
@@ -1251,8 +1283,12 @@ ${Object.entries(objectArgs).map(([key, schema]) => {
1251
1283
  if (shouldCoerce("number")) return `.coerce.number(${numberArgs}).int(${combinedArgs})`;
1252
1284
  if (!isZodV4) return `.number(${numberArgs}).int(${combinedArgs})`;
1253
1285
  }
1254
- if (fn !== "date" && shouldCoerceType || fn === "date" && shouldCoerceType && context.output.override.useDates) return `.coerce.${fn}(${combinedArgs})`;
1286
+ if (fn !== "date" && shouldCoerceType || fn === "date" && shouldCoerceType && context.output.override.useDates) {
1287
+ assertZodMethodName(fn);
1288
+ return `.coerce.${fn}(${combinedArgs})`;
1289
+ }
1255
1290
  if (exactOptional && isZodV4 && fn === "optional") return ".exactOptional()";
1291
+ assertZodMethodName(fn);
1256
1292
  return `.${fn}(${combinedArgs})`;
1257
1293
  };
1258
1294
  appendConstsChunk(input.consts.join("\n"));
@@ -1501,6 +1537,64 @@ const getSingleResponse = (responses) => {
1501
1537
  const otherSuccess = Object.entries(responses).find(([code]) => code.startsWith("2") && code !== "204" && code !== "205")?.[1];
1502
1538
  return responses["200"] ?? responses["2XX"] ?? responses["2xx"] ?? otherSuccess ?? responses["204"] ?? responses["205"];
1503
1539
  };
1540
+ const parseResponseSchema = ({ verb, pathRoute, override }, context) => {
1541
+ const isZodV4 = resolveIsZodV4(context.output.override.zod.version, context.output.packageJson);
1542
+ return {
1543
+ isZodV4,
1544
+ ...parseBodyAndResponse({
1545
+ data: getSingleResponse(context.spec.paths?.[pathRoute]?.[verb]?.responses),
1546
+ context,
1547
+ name: "response",
1548
+ strict: override.zod.strict.response,
1549
+ generate: override.zod.generate.response,
1550
+ isZodV4,
1551
+ parseType: "response"
1552
+ })
1553
+ };
1554
+ };
1555
+ /** The success response is emitted as a plain `zod.object(...)`. */
1556
+ const isObjectResponseSchema = (verbOptions, context) => {
1557
+ if (context.output.override.zod.generateEachHttpStatus) return false;
1558
+ const { input, isArray, isZodV4 } = parseResponseSchema(verbOptions, context);
1559
+ const [root, ...modifiers] = input.functions;
1560
+ if (isArray || root === void 0) return false;
1561
+ if (verbOptions.override.zod.preprocess?.response || verbOptions.override.zod.useBrandedTypes && !isZodV4) return false;
1562
+ const objectRoots = /* @__PURE__ */ new Set([
1563
+ "object",
1564
+ "looseObject",
1565
+ "strictObject"
1566
+ ]);
1567
+ const objectModifiers = /* @__PURE__ */ new Set([
1568
+ "strict",
1569
+ "passthrough",
1570
+ "describe"
1571
+ ]);
1572
+ return objectRoots.has(root[0]) && modifiers.every(([fn]) => objectModifiers.has(fn));
1573
+ };
1574
+ /**
1575
+ * A `<Operation>Response` schema that can be converted to JSON Schema is
1576
+ * emitted for the success response.
1577
+ */
1578
+ const hasResponseSchema = (verbOptions, context) => {
1579
+ if (context.output.override.zod.generateEachHttpStatus) return false;
1580
+ const { input, isArray } = parseResponseSchema(verbOptions, context);
1581
+ if (!isArray && input.functions.length === 0) return false;
1582
+ const unsupportedTypes = /* @__PURE__ */ new Set([
1583
+ "allOf",
1584
+ "date",
1585
+ "instanceof"
1586
+ ]);
1587
+ const containsUnsupported = (value) => {
1588
+ if (Array.isArray(value)) return value.some(containsUnsupported);
1589
+ if (!isObject(value)) return false;
1590
+ if (Array.isArray(value.functions)) {
1591
+ const functions = value.functions;
1592
+ return functions[0] !== void 0 && unsupportedTypes.has(functions[0][0]) || functions.some(([, args]) => containsUnsupported(args));
1593
+ }
1594
+ return Object.values(value).some(containsUnsupported);
1595
+ };
1596
+ return !containsUnsupported(input);
1597
+ };
1504
1598
  const parseParameters = ({ data, context, operationName, isZodV4, strict, generate, useReusableSchemas }) => {
1505
1599
  if (!data) return {
1506
1600
  headers: {
@@ -1824,6 +1918,6 @@ const zodClientBuilder = {
1824
1918
  };
1825
1919
  const builder = () => () => zodClientBuilder;
1826
1920
  //#endregion
1827
- export { assertZodTarget, builder, builder as default, dereference, generateFormDataZodSchema, generateZod, generateZodValidationSchemaDefinition, getZodDependencies, getZodImportSource, getZodTypeName, isZodVersionV4, parseParameters, parseZodValidationSchemaDefinition, predefinedZodFormats, renderZodExport, resolveIsZodV4 };
1921
+ export { assertZodTarget, builder, builder as default, dereference, generateFormDataZodSchema, generateZod, generateZodValidationSchemaDefinition, getZodDependencies, getZodImportSource, getZodTypeName, hasResponseSchema, isObjectResponseSchema, isZodVersionV4, parseParameters, parseZodValidationSchemaDefinition, predefinedZodFormats, renderZodExport, resolveIsZodV4 };
1828
1922
 
1829
1923
  //# sourceMappingURL=index.mjs.map