@orval/zod 8.36.0 → 8.38.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,4 +1,4 @@
1
- import { ClientBuilder, ClientDependenciesBuilder, ClientGeneratorsBuilder, ContextSpec, GeneratorMutator, GeneratorVerbOptions, OpenApiParameterObject, OpenApiReferenceObject, OpenApiSchemaObject, PackageJson, ZodCoerceType, ZodVariantOption, ZodVersionOption } from "@orval/core";
1
+ import { ClientBuilder, ClientDependenciesBuilder, ClientGeneratorsBuilder, ContextSpec, GeneratorMutator, GeneratorVerbOptions, OpenApiNonBooleanSchemaObject, OpenApiParameterObject, OpenApiReferenceObject, OpenApiSchemaObject, PackageJson, ZodCoerceType, ZodVariantOption, ZodVersionOption } from "@orval/core";
2
2
  //#region src/compatible-v4.d.ts
3
3
  export declare const isZodVersionV4: (packageJson: PackageJson) => boolean;
4
4
  /**
@@ -87,7 +87,7 @@ interface DateTimeOptions {
87
87
  interface TimeOptions {
88
88
  precision?: -1 | 0 | 1 | 2 | 3;
89
89
  }
90
- export declare const generateZodValidationSchemaDefinition: (schema: OpenApiSchemaObject | OpenApiReferenceObject | undefined, context: ContextSpec, name: string, strict: boolean, isZodV4: boolean, rules?: {
90
+ export declare const generateZodValidationSchemaDefinition: (schemaInput: 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 /
@@ -178,7 +178,7 @@ export declare const parseZodValidationSchemaDefinition: (input: ZodValidationSc
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
- export declare const dereference: (schema: OpenApiSchemaObject | OpenApiReferenceObject, context: ContextSpec) => OpenApiSchemaObject;
181
+ export declare const dereference: (schema: OpenApiSchemaObject | OpenApiReferenceObject, context: ContextSpec) => OpenApiNonBooleanSchemaObject;
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
package/dist/index.mjs CHANGED
@@ -1,9 +1,5 @@
1
- import { EnumGeneration, assertSafeNumericConstraint, buildDynamicScope, buildInlineDynamicScope, camel, compareVersions, createLogger, generateMutator, getDynamicAnchorName, getEnumImplementation, getEnumMembers, getEnumValueInfo, getFormDataFieldFileType, getNumberWord, getRefInfo, getRequiredKeys, hasEnumMetadata, isBoolean, isDynamicReference, isNumber, isObject, isSchemaNullable, isString, jsStringEscape, jsStringLiteralEscape, pascal, resolveDynamicRef, resolveRef, safeNumericConstraint, stringify } from "@orval/core";
1
+ import { EnumGeneration, assertSafeNumericConstraint, buildDynamicScope, buildInlineDynamicScope, camel, compareVersions, createLogger, generateMutator, getDynamicAnchorName, getEnumImplementation, getEnumMembers, getEnumValueInfo, getFormDataFieldFileType, getNumberWord, getRefInfo, getRequiredKeys, hasEnumMetadata, isBoolean, isDynamicReference, isNumber, isObject, isSchemaNullable, isString, jsStringEscape, jsStringLiteralEscape, pascal, resolveDynamicRef, resolveRef, safeNumericConstraint, stringify, toObjectSchema, unique } from "@orval/core";
2
2
  import jsesc from "jsesc";
3
- import { unique } from "remeda";
4
- //#region src/logger.ts
5
- const logger = createLogger("@orval/zod");
6
- //#endregion
7
3
  //#region src/compatible-v4.ts
8
4
  const getZodPackageVersion = (packageJson) => {
9
5
  return packageJson.resolvedVersions?.zod ?? packageJson.dependencies?.zod ?? packageJson.devDependencies?.zod ?? packageJson.peerDependencies?.zod;
@@ -131,6 +127,9 @@ const renderZodExport = (block) => {
131
127
  return `${constLine}\n\n${companionLines(block.name)}`;
132
128
  };
133
129
  //#endregion
130
+ //#region src/logger.ts
131
+ const logger = createLogger("@orval/zod");
132
+ //#endregion
134
133
  //#region src/index.ts
135
134
  const getZodDependencies = (_hasGlobalMutator, _hasParamsSerializerOptions, _packageJson, _httpClient, _hasTagsMutator, override) => [{
136
135
  exports: [{
@@ -166,9 +165,10 @@ const predefinedZodFormats = /* @__PURE__ */ new Set([
166
165
  "uuid"
167
166
  ]);
168
167
  const resolveZodType = (schema) => {
168
+ if (typeof schema !== "object") return schema ? "unknown" : "never";
169
169
  const schemaTypeValue = schema.type;
170
170
  if (Array.isArray(schemaTypeValue)) {
171
- const nonNullTypes = schemaTypeValue.filter((t) => isString(t)).filter((t) => t !== "null" && possibleSchemaTypes.has(t));
171
+ const nonNullTypes = schemaTypeValue.filter((t) => t !== "null" && possibleSchemaTypes.has(t));
172
172
  if (nonNullTypes.length > 1) return { multiType: nonNullTypes };
173
173
  const type = nonNullTypes[0];
174
174
  if (type === "array" && "prefixItems" in schema) return "tuple";
@@ -252,6 +252,7 @@ function needsInlineDefault(schema, seen = /* @__PURE__ */ new Set()) {
252
252
  return false;
253
253
  }
254
254
  const removeReadOnlyProperties = (schema) => {
255
+ if (typeof schema !== "object") return toObjectSchema(schema);
255
256
  if (schema.properties && isObject(schema.properties)) {
256
257
  const filteredProperties = {};
257
258
  for (const [key, value] of Object.entries(schema.properties)) {
@@ -282,11 +283,16 @@ const decodeDiscriminatorSeparator = (fn) => {
282
283
  };
283
284
  };
284
285
  const resolveUnionMemberSchema = (member, context) => {
285
- if (member && "$ref" in member && typeof member.$ref === "string") return tryResolveRefSchema(member.$ref, context);
286
+ if (typeof member !== "object") return toObjectSchema(member);
287
+ if ("$ref" in member && typeof member.$ref === "string") {
288
+ const resolved = tryResolveRefSchema(member.$ref, context);
289
+ return resolved ? toObjectSchema(resolved) : void 0;
290
+ }
286
291
  return member;
287
292
  };
288
293
  const isPlainObjectSchema = (schema) => {
289
- if (!schema || schema.oneOf || schema.anyOf || schema.allOf) return false;
294
+ if (!schema || typeof schema !== "object") return false;
295
+ if (schema.oneOf || schema.anyOf || schema.allOf) return false;
290
296
  return schema.type === "object" || isObject(schema.properties) && Object.keys(schema.properties).length > 0;
291
297
  };
292
298
  const SHAPELESS_MEMBER_KEYS = /* @__PURE__ */ new Set([
@@ -296,13 +302,13 @@ const SHAPELESS_MEMBER_KEYS = /* @__PURE__ */ new Set([
296
302
  "not"
297
303
  ]);
298
304
  const isConstraintOnlyMember = (member) => {
299
- if ("$ref" in member) return false;
305
+ if (typeof member !== "object" || "$ref" in member) return false;
300
306
  const schema = member;
301
307
  if (!Array.isArray(schema.required) || schema.required.length === 0) return false;
302
308
  return Object.keys(schema).every((key) => SHAPELESS_MEMBER_KEYS.has(key));
303
309
  };
304
310
  const getForbiddenKeys = (member) => {
305
- if (!isObject(member.not)) return void 0;
311
+ if (typeof member !== "object" || typeof member.not !== "object") return;
306
312
  const not = member.not;
307
313
  const branches = Array.isArray(not.anyOf) ? not.anyOf : Array.isArray(not.required) ? [not] : [];
308
314
  const forbidden = /* @__PURE__ */ new Set();
@@ -310,14 +316,14 @@ const getForbiddenKeys = (member) => {
310
316
  return forbidden.size > 0 ? [...forbidden] : void 0;
311
317
  };
312
318
  const hasLiteralDiscriminator = (schema, property) => {
313
- if (!schema || !isObject(schema.properties)) return false;
319
+ if (!schema || typeof schema !== "object" || !isObject(schema.properties)) return false;
314
320
  const propertySchema = schema.properties[property];
315
321
  if (!isObject(propertySchema)) return false;
316
322
  return propertySchema.const !== void 0 || Array.isArray(propertySchema.enum);
317
323
  };
318
324
  const collectDiscriminatorValues = (member, property, context) => {
319
325
  const readValues = (schema) => {
320
- if (!schema || !isObject(schema.properties)) return null;
326
+ if (!schema || typeof schema !== "object" || !isObject(schema.properties)) return null;
321
327
  const propertySchema = schema.properties[property];
322
328
  if (!isObject(propertySchema)) return null;
323
329
  const constValue = propertySchema.const;
@@ -351,11 +357,12 @@ const isDiscriminatableMember = (member, property, useReusableSchemas, context)
351
357
  if (!isPlainObjectSchema(resolved)) return false;
352
358
  return hasLiteralDiscriminator(resolved, property);
353
359
  };
354
- const generateZodValidationSchemaDefinition = (schema, context, name, strict, isZodV4, rules) => {
355
- if (!schema) return {
356
- functions: [],
360
+ const generateZodValidationSchemaDefinition = (schemaInput, context, name, strict, isZodV4, rules) => {
361
+ if (schemaInput == null || typeof schemaInput !== "object") return {
362
+ functions: schemaInput === true ? [["unknown", void 0]] : schemaInput === false ? [["never", void 0]] : [],
357
363
  consts: []
358
364
  };
365
+ let schema = schemaInput;
359
366
  const CHAINABLE_SIBLINGS = /* @__PURE__ */ new Set(["default", "description"]);
360
367
  const isChainable = (k) => CHAINABLE_SIBLINGS.has(k);
361
368
  const applyChainableSiblings = (functions, consts, siblingSchema) => {
@@ -457,8 +464,9 @@ const generateZodValidationSchemaDefinition = (schema, context, name, strict, is
457
464
  const separator = schema.allOf ? "allOf" : schema.oneOf ? "oneOf" : "anyOf";
458
465
  const schemas = schema.allOf ?? schema.oneOf ?? schema.anyOf;
459
466
  const discriminatorProperty = (() => {
460
- if (!context.output?.override?.zod?.generateDiscriminatedUnion || !(schema.oneOf || schema.anyOf) || !schema.discriminator?.propertyName || schemas.length <= 1) return;
461
- const property = schema.discriminator.propertyName;
467
+ const propertyName = schema.discriminator?.propertyName;
468
+ if (!context.output?.override?.zod?.generateDiscriminatedUnion || !(schema.oneOf || schema.anyOf) || typeof propertyName !== "string" || schemas.length <= 1) return;
469
+ const property = propertyName;
462
470
  if (!schemas.every((member) => isDiscriminatableMember(member, property, useReusableSchemas, context))) return;
463
471
  const seenValues = /* @__PURE__ */ new Set();
464
472
  for (const member of schemas) {
@@ -474,7 +482,7 @@ const generateZodValidationSchemaDefinition = (schema, context, name, strict, is
474
482
  })();
475
483
  const unionSeparator = discriminatorProperty ? encodeDiscriminatorSeparator(separator, discriminatorProperty) : separator;
476
484
  const allOfRequired = schema.allOf ? [.../* @__PURE__ */ new Set([...getRequiredKeys(schema, name), ...schemas.flatMap((member, index) => {
477
- const isRef = "$ref" in member && typeof member.$ref === "string";
485
+ const isRef = typeof member === "object" && member !== null && "$ref" in member && typeof member.$ref === "string";
478
486
  const resolved = isRef ? tryResolveRefSchema(member.$ref, context) : member;
479
487
  if (!resolved) return [];
480
488
  return getRequiredKeys(resolved, isRef ? member.$ref : `${name}.allOf[${index}]`);
@@ -482,8 +490,9 @@ const generateZodValidationSchemaDefinition = (schema, context, name, strict, is
482
490
  const withSiblingProperties = (member) => {
483
491
  const properties = schema.properties;
484
492
  if (!(schema.oneOf || schema.anyOf) || !isObject(properties) || Object.keys(properties).length === 0 || !isConstraintOnlyMember(member)) return member;
493
+ if (typeof member !== "object") return member;
485
494
  const required = member.required;
486
- if (!required.every((key) => Object.hasOwn(properties, key))) return member;
495
+ if (!required?.every((key) => Object.hasOwn(properties, key))) return member;
487
496
  const forbidden = getForbiddenKeys(member);
488
497
  let propertyOverrides;
489
498
  if (forbidden) {
@@ -500,7 +509,7 @@ const generateZodValidationSchemaDefinition = (schema, context, name, strict, is
500
509
  type: "object",
501
510
  properties,
502
511
  required,
503
- description: member.description,
512
+ description: typeof member === "object" ? member.description : void 0,
504
513
  "x-orval-property-overrides": propertyOverrides
505
514
  };
506
515
  };
@@ -587,7 +596,7 @@ const generateZodValidationSchemaDefinition = (schema, context, name, strict, is
587
596
  } else {
588
597
  defaultValue = formatDefaultValue(schema.default);
589
598
  const hasEnumOrObjectItems = schema.items && typeof schema.items === "object" && ("enum" in schema.items || schema.items.type === "object" || "$ref" in schema.items || needsInlineDefault(schema.items));
590
- const isArrayWithEnumItems = Array.isArray(schema.default) && type === "array" && schema.items && "enum" in schema.items && schema.default.length > 0;
599
+ const isArrayWithEnumItems = Array.isArray(schema.default) && type === "array" && schema.items && typeof schema.items === "object" && "enum" in schema.items && schema.default.length > 0;
591
600
  const isArrayWithObjectItems = Array.isArray(schema.default) && type === "array" && hasEnumOrObjectItems && schema.default.length > 0;
592
601
  if (isArrayWithEnumItems || isArrayWithObjectItems) {
593
602
  defaultVarName = defaultValue;
@@ -637,9 +646,8 @@ const generateZodValidationSchemaDefinition = (schema, context, name, strict, is
637
646
  * >
638
647
  * > Omitting this keyword has the same assertion behavior as an empty array.
639
648
  */
640
- if ("prefixItems" in schema) {
641
- const schema31 = schema;
642
- const prefixItems = Array.isArray(schema31.prefixItems) ? schema31.prefixItems : [];
649
+ if (schema.prefixItems) {
650
+ const prefixItems = Array.isArray(schema.prefixItems) ? schema.prefixItems : [];
643
651
  if (prefixItems.length > 0) {
644
652
  functions.push(["tuple", prefixItems.map((item, idx) => generateZodValidationSchemaDefinition(dereference(item, context), context, camel(`${name}-${idx}-item`), strict, isZodV4, {
645
653
  required: true,
@@ -1343,26 +1351,28 @@ function extractSchemaNameFromRef($ref) {
1343
1351
  * 4. If unresolved, external, or a generic parameter → return `{}`.
1344
1352
  */
1345
1353
  const dereference = (schema, context) => {
1346
- const refName = "$ref" in schema ? schema.$ref : void 0;
1354
+ if (typeof schema !== "object") return toObjectSchema(schema);
1355
+ const refName = typeof schema.$ref === "string" ? schema.$ref : void 0;
1347
1356
  if (refName && context.parents?.includes(refName)) return {};
1348
- if (isDynamicReference(schema)) return dereferenceDynamicRef(schema, context);
1357
+ if (isDynamicReference(schema)) return toObjectSchema(dereferenceDynamicRef(schema, context));
1349
1358
  const childContext = {
1350
1359
  ...context,
1351
1360
  ...refName ? { parents: [...context.parents ?? [], refName] } : void 0
1352
1361
  };
1353
- const resolvedSchema = "$ref" in schema ? (() => {
1362
+ const resolvedSchema = typeof schema.$ref === "string" ? (() => {
1354
1363
  const referencedSchema = tryResolveRefSchema(schema.$ref, context);
1355
- if (!referencedSchema || !isObject(referencedSchema)) return;
1364
+ if (!referencedSchema) return;
1365
+ if (typeof referencedSchema !== "object") return toObjectSchema(referencedSchema);
1356
1366
  const siblingProperties = Object.fromEntries(Object.entries(schema).filter(([key]) => key !== "$ref"));
1357
1367
  return {
1358
1368
  ...referencedSchema,
1359
1369
  ...siblingProperties
1360
1370
  };
1361
1371
  })() : schema;
1362
- if (!resolvedSchema) return {};
1372
+ if (!resolvedSchema || typeof resolvedSchema !== "object") return typeof resolvedSchema === "boolean" ? toObjectSchema(resolvedSchema) : {};
1363
1373
  const resolvedContext = buildScopedContext(childContext, refName, resolvedSchema);
1364
- if (isDynamicReference(resolvedSchema)) return dereferenceDynamicRef(resolvedSchema, resolvedContext);
1365
- return dereferenceProperties(resolvedSchema, resolvedContext);
1374
+ if (isDynamicReference(resolvedSchema)) return toObjectSchema(dereferenceDynamicRef(resolvedSchema, resolvedContext));
1375
+ return toObjectSchema(dereferenceProperties(resolvedSchema, resolvedContext));
1366
1376
  };
1367
1377
  function dereferenceProperties(schema, context) {
1368
1378
  return Object.entries(schema).reduce((acc, [key, value]) => {
@@ -1426,6 +1436,10 @@ function encodeSegment(segment) {
1426
1436
  * and contentMediaType. Mirrors type gen's resolveFormDataRootObject.
1427
1437
  */
1428
1438
  const generateFormDataZodSchema = (schema, context, name, strict, isZodV4, encoding, useReusableSchemas) => {
1439
+ if (typeof schema !== "object") return generateZodValidationSchemaDefinition(schema, context, name, strict, isZodV4, {
1440
+ required: true,
1441
+ useReusableSchemas
1442
+ });
1429
1443
  const propertyOverrides = {};
1430
1444
  if (schema.properties) for (const key of Object.keys(schema.properties)) {
1431
1445
  const propSchema = schema.properties[key];
@@ -1501,7 +1515,7 @@ const parseBodyAndResponse = ({ data, context, name, strict, generate, isZodV4,
1501
1515
  const min = safeNumericConstraint(rawMin, "minimum/minLength/minItems");
1502
1516
  const max = safeNumericConstraint(rawMax, "maximum/maxLength/maxItems");
1503
1517
  const rawItems = useReusableSchemas ? (() => {
1504
- return resolveRef(schema, context).schema.items ?? resolvedJsonSchema.items;
1518
+ return toObjectSchema(typeof schema === "boolean" ? schema : resolveRef(schema, context).schema).items ?? resolvedJsonSchema.items;
1505
1519
  })() : resolvedJsonSchema.items;
1506
1520
  return {
1507
1521
  input: generateZodValidationSchemaDefinition(parseType === "body" ? removeReadOnlyProperties(rawItems) : rawItems, context, name, strict, isZodV4, {
@@ -1537,7 +1551,7 @@ const parseResponseSchema = ({ verb, pathRoute, override }, context) => {
1537
1551
  return {
1538
1552
  isZodV4,
1539
1553
  ...parseBodyAndResponse({
1540
- data: getSingleResponse(context.spec.paths?.[pathRoute]?.[verb]?.responses),
1554
+ data: getSingleResponse((verb === "query" ? void 0 : context.spec.paths?.[pathRoute]?.[verb])?.responses),
1541
1555
  context,
1542
1556
  name: "response",
1543
1557
  strict: override.zod.strict.response,
@@ -1613,10 +1627,10 @@ const parseParameters = ({ data, context, operationName, isZodV4, strict, genera
1613
1627
  const constNameRegistry = {};
1614
1628
  const defintionsByParameters = data.reduce((acc, val) => {
1615
1629
  const { schema: parameter } = resolveRef(val, context);
1616
- if (!parameter.schema) return acc;
1617
- if (!parameter.in || !parameter.name) return acc;
1618
- const schemaForGen = useReusableSchemas ? parameter.description ? Object.assign({}, parameter.schema, { description: parameter.description }) : parameter.schema : (() => {
1619
- const s = dereference(parameter.schema, context);
1630
+ if (!("schema" in parameter) || !parameter.in || !parameter.name) return acc;
1631
+ const parameterSchema = typeof parameter.schema === "boolean" ? toObjectSchema(parameter.schema) : parameter.schema;
1632
+ const schemaForGen = useReusableSchemas ? parameter.description ? Object.assign({}, parameterSchema, { description: parameter.description }) : parameterSchema : (() => {
1633
+ const s = dereference(parameterSchema, context);
1620
1634
  s.description = parameter.description;
1621
1635
  return s;
1622
1636
  })();
@@ -1699,7 +1713,8 @@ const generateZodRoute = async ({ operationId, operationName, typeName, verb, ov
1699
1713
  const useReusableSchemas = context.output.override.zod.generateReusableSchemas;
1700
1714
  const spec = context.spec.paths?.[pathRoute];
1701
1715
  if (spec == void 0) throw new Error(`No such path ${pathRoute} in ${context.projectName}`);
1702
- const parameters = [...spec.parameters ?? [], ...spec[verb]?.parameters ?? []];
1716
+ const operation = verb === "query" ? void 0 : spec[verb];
1717
+ const parameters = [...spec.parameters ?? [], ...operation?.parameters ?? []];
1703
1718
  const parsedParameters = parseParameters({
1704
1719
  data: parameters,
1705
1720
  context,
@@ -1709,7 +1724,7 @@ const generateZodRoute = async ({ operationId, operationName, typeName, verb, ov
1709
1724
  generate: override.zod.generate,
1710
1725
  useReusableSchemas
1711
1726
  });
1712
- const requestBody = spec[verb]?.requestBody;
1727
+ const requestBody = operation?.requestBody;
1713
1728
  const parsedBody = parseBodyAndResponse({
1714
1729
  data: requestBody,
1715
1730
  context,
@@ -1720,7 +1735,7 @@ const generateZodRoute = async ({ operationId, operationName, typeName, verb, ov
1720
1735
  parseType: "body",
1721
1736
  useReusableSchemas
1722
1737
  });
1723
- const responses = context.output.override.zod.generateEachHttpStatus ? Object.entries(spec[verb]?.responses ?? {}) : [["", getSingleResponse(spec[verb]?.responses)]];
1738
+ const responses = context.output.override.zod.generateEachHttpStatus ? Object.entries(operation?.responses ?? {}) : [["", getSingleResponse(operation?.responses)]];
1724
1739
  const parsedResponses = responses.map(([code, response]) => parseBodyAndResponse({
1725
1740
  data: response,
1726
1741
  context,
@@ -1875,7 +1890,7 @@ const generateZodRoute = async ({ operationId, operationName, typeName, verb, ov
1875
1890
  let isNoContent;
1876
1891
  if (isEachHttpStatusMode) isNoContent = noContentStatusCodes.has(statusCode);
1877
1892
  else {
1878
- const specResponseKeys = new Set(Object.keys(spec[verb]?.responses ?? {}));
1893
+ const specResponseKeys = new Set(Object.keys(operation?.responses ?? {}));
1879
1894
  isNoContent = !(specResponseKeys.has("200") || specResponseKeys.has("2XX") || specResponseKeys.has("2xx"));
1880
1895
  }
1881
1896
  const noContentSchema = isNoContent ? zodVariant === "mini" ? zodMiniCall("void") : "zod.void()" : zodVariant === "mini" ? zodMiniCall("unknown") : "zod.unknown()";