zod-openapi 3.2.0 → 3.3.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/README.md CHANGED
@@ -469,7 +469,7 @@ createDocument({
469
469
 
470
470
  ##### Zod Effects
471
471
 
472
- `.transform()`, `.default()` and `.pipe()` are complicated because they technically comprise of two types (input & output). This means that we need to understand which type you are creating. In particular with transform it is very difficult to infer the output type. This library will automatically select which _type_ to use by checking how the schema is used based on the following rules:
472
+ `.transform()`, `.catch()`, `.default()` and `.pipe()` are complicated because they technically comprise of two types (input & output). This means that we need to understand which type you are creating. In particular with transform it is very difficult to infer the output type. This library will automatically select which _type_ to use by checking how the schema is used based on the following rules:
473
473
 
474
474
  _Input_: Request Bodies, Request Parameters, Headers
475
475
 
@@ -694,6 +694,8 @@ For example in `z.string().nullable()` will be rendered differently
694
694
  - ZodBoolean
695
695
  - ZodBranded
696
696
  - ZodCatch
697
+ - Treated as ZodDefault
698
+ - ZodCustom
697
699
  - ZodDate
698
700
  - `type` is mapped as `string` by default
699
701
  - ZodDefault
@@ -55,7 +55,18 @@ const createBooleanSchema = (_zodBoolean) => ({
55
55
  }
56
56
  });
57
57
  const createBrandedSchema = (zodBranded, state) => createSchemaObject(zodBranded._def.type, state, ["brand"]);
58
- const createCatchSchema = (zodCatch, state) => createSchemaObject(zodCatch._def.innerType, state, ["catch"]);
58
+ const createCatchSchema = (zodCatch, state) => {
59
+ const schemaObject = createSchemaObject(zodCatch._def.innerType, state, [
60
+ "default"
61
+ ]);
62
+ const catchResult = zodCatch.safeParse(void 0);
63
+ const maybeDefaultValue = catchResult.success ? {
64
+ default: catchResult.data
65
+ } : void 0;
66
+ return enhanceWithMetadata(schemaObject, {
67
+ ...maybeDefaultValue
68
+ });
69
+ };
59
70
  const createDateSchema = (_zodDate, state) => {
60
71
  var _a;
61
72
  return {
@@ -565,74 +576,7 @@ const getZodNumberChecks = (zodNumber) => zodNumber._def.checks.reduce((acc, che
565
576
  }, {});
566
577
  const mapNumberType = (zodNumberChecks) => zodNumberChecks.int ? "integer" : "number";
567
578
  const createOptionalSchema = (zodOptional, state) => createSchemaObject(zodOptional.unwrap(), state, ["optional"]);
568
- const isOptionalSchema = (zodSchema, state) => {
569
- var _a, _b, _c;
570
- if (isZodType(zodSchema, "ZodOptional") || isZodType(zodSchema, "ZodNever") || isZodType(zodSchema, "ZodUndefined") || isZodType(zodSchema, "ZodLiteral") && zodSchema._def.value === void 0) {
571
- return { optional: true };
572
- }
573
- if (isZodType(zodSchema, "ZodDefault")) {
574
- if (((_a = zodSchema._def.openapi) == null ? void 0 : _a.effectType) === "input") {
575
- return { optional: true };
576
- }
577
- if (((_b = zodSchema._def.openapi) == null ? void 0 : _b.effectType) === "output") {
578
- return { optional: false };
579
- }
580
- return {
581
- optional: state.type === "input",
582
- effects: [
583
- {
584
- type: "schema",
585
- creationType: state.type,
586
- zodType: zodSchema,
587
- path: [...state.path]
588
- }
589
- ]
590
- };
591
- }
592
- if (isZodType(zodSchema, "ZodNullable") || isZodType(zodSchema, "ZodCatch")) {
593
- return isOptionalSchema(zodSchema._def.innerType, state);
594
- }
595
- if (isZodType(zodSchema, "ZodEffects")) {
596
- return isOptionalSchema(zodSchema._def.schema, state);
597
- }
598
- if (isZodType(zodSchema, "ZodUnion") || isZodType(zodSchema, "ZodDiscriminatedUnion")) {
599
- const results = zodSchema._def.options.map(
600
- (schema) => isOptionalSchema(schema, state)
601
- );
602
- return results.reduce(
603
- (acc, result) => ({
604
- optional: acc.optional || result.optional,
605
- effects: flattenEffects([acc.effects, result.effects])
606
- }),
607
- { optional: false }
608
- );
609
- }
610
- if (isZodType(zodSchema, "ZodIntersection")) {
611
- const results = [zodSchema._def.left, zodSchema._def.right].map(
612
- (schema) => isOptionalSchema(schema, state)
613
- );
614
- return results.reduce(
615
- (acc, result) => ({
616
- optional: acc.optional || result.optional,
617
- effects: flattenEffects([acc.effects, result.effects])
618
- }),
619
- { optional: false }
620
- );
621
- }
622
- if (isZodType(zodSchema, "ZodPipeline")) {
623
- const type = ((_c = zodSchema._def.openapi) == null ? void 0 : _c.effectType) ?? state.type;
624
- if (type === "input") {
625
- return isOptionalSchema(zodSchema._def.in, state);
626
- }
627
- if (type === "output") {
628
- return isOptionalSchema(zodSchema._def.out, state);
629
- }
630
- }
631
- if (isZodType(zodSchema, "ZodLazy")) {
632
- return isOptionalSchema(zodSchema._def.getter(), state);
633
- }
634
- return { optional: zodSchema.isOptional() };
635
- };
579
+ const isOptionalObjectKey = (zodSchema) => isZodType(zodSchema, "ZodNever") || isZodType(zodSchema, "ZodUndefined") || isZodType(zodSchema, "ZodLiteral") && zodSchema._def.value === void 0;
636
580
  const createObjectSchema = (zodObject, state) => {
637
581
  var _a;
638
582
  const extendedSchema = createExtendedSchema(
@@ -742,7 +686,7 @@ const createShapeDiff = (baseObj, extendedObj) => {
742
686
  };
743
687
  const createObjectSchemaFromShape = (shape, { unknownKeys, catchAll }, state) => {
744
688
  const properties = mapProperties(shape, state);
745
- const required = mapRequired(shape, state);
689
+ const required = mapRequired(properties, shape, state);
746
690
  const additionalProperties = !isZodType(catchAll, "ZodNever") ? createSchemaObject(catchAll, state, ["additional properties"]) : void 0;
747
691
  return {
748
692
  type: "schema",
@@ -762,17 +706,38 @@ const createObjectSchemaFromShape = (shape, { unknownKeys, catchAll }, state) =>
762
706
  ])
763
707
  };
764
708
  };
765
- const mapRequired = (shape, state) => {
766
- const { required, effects: allEffects } = Object.entries(shape).reduce(
767
- (acc, [key, zodSchema]) => {
768
- state.path.push(`property: ${key}`);
769
- const { optional, effects } = isOptionalSchema(zodSchema, state);
770
- state.path.pop();
771
- if (!optional) {
709
+ const mapRequired = (properties, shape, state) => {
710
+ if (!properties) {
711
+ return void 0;
712
+ }
713
+ const { required, effects } = Object.entries(properties.schemas).reduce(
714
+ (acc, [key, schemaOrRef]) => {
715
+ const zodSchema = shape[key];
716
+ if (!zodSchema) {
717
+ throw new Error("Property somehow doesn't exist in shape");
718
+ }
719
+ const result = zodSchema.safeParse(void 0);
720
+ if (!result.success) {
772
721
  acc.required.push(key);
722
+ return acc;
773
723
  }
774
- if (effects) {
775
- acc.effects.push(effects);
724
+ if (result.data !== void 0) {
725
+ const baseEffect = {
726
+ zodType: zodSchema,
727
+ path: [...state.path, `property: ${key}`]
728
+ };
729
+ const effect = schemaOrRef.type === "ref" ? {
730
+ ...baseEffect,
731
+ type: "component"
732
+ } : {
733
+ ...baseEffect,
734
+ type: "schema",
735
+ creationType: state.type
736
+ };
737
+ acc.effects.push(effect);
738
+ if (state.type === "output") {
739
+ acc.required.push(key);
740
+ }
776
741
  }
777
742
  return acc;
778
743
  },
@@ -781,7 +746,7 @@ const mapRequired = (shape, state) => {
781
746
  effects: []
782
747
  }
783
748
  );
784
- return { required, effects: flattenEffects(allEffects) };
749
+ return { required, effects };
785
750
  };
786
751
  const mapProperties = (shape, state) => {
787
752
  const shapeEntries = Object.entries(shape);
@@ -790,17 +755,17 @@ const mapProperties = (shape, state) => {
790
755
  }
791
756
  return shapeEntries.reduce(
792
757
  (acc, [key, zodSchema]) => {
793
- if (isZodType(zodSchema, "ZodNever") || isZodType(zodSchema, "ZodUndefined")) {
758
+ if (isOptionalObjectKey(zodSchema)) {
794
759
  return acc;
795
760
  }
796
- const property = createSchemaObject(zodSchema, state, [
797
- `property: ${key}`
798
- ]);
799
- acc.properties[key] = property.schema;
800
- acc.effects.push(property.effects);
761
+ const schema = createSchemaObject(zodSchema, state, [`property: ${key}`]);
762
+ acc.schemas[key] = schema;
763
+ acc.properties[key] = schema.schema;
764
+ acc.effects.push(schema.effects);
801
765
  return acc;
802
766
  },
803
767
  {
768
+ schemas: {},
804
769
  properties: {},
805
770
  effects: []
806
771
  }
@@ -1141,9 +1106,12 @@ const mapPrefixItems = (items, state) => {
1141
1106
  };
1142
1107
  const createUnionSchema = (zodUnion, state) => {
1143
1108
  var _a, _b;
1144
- const schemas = zodUnion.options.map(
1145
- (option, index) => createSchemaObject(option, state, [`union option ${index}`])
1146
- );
1109
+ const schemas = zodUnion.options.reduce((acc, option, index) => {
1110
+ if (!isOptionalObjectKey(option)) {
1111
+ acc.push(createSchemaObject(option, state, [`union option ${index}`]));
1112
+ }
1113
+ return acc;
1114
+ }, []);
1147
1115
  if (((_a = zodUnion._def.openapi) == null ? void 0 : _a.unionOneOf) ?? ((_b = state.documentOptions) == null ? void 0 : _b.unionOneOf)) {
1148
1116
  return {
1149
1117
  type: "schema",
@@ -1441,7 +1409,7 @@ const createContent = (contentObject, components, type, subpath, documentOptions
1441
1409
  );
1442
1410
  const createComponentParamRef = (ref) => `#/components/parameters/${ref}`;
1443
1411
  const createBaseParameter = (schema, components, subpath, documentOptions) => {
1444
- var _a, _b, _c;
1412
+ var _a, _b;
1445
1413
  const { ref, ...rest } = ((_a = schema._def.openapi) == null ? void 0 : _a.param) ?? {};
1446
1414
  const state = {
1447
1415
  components,
@@ -1451,8 +1419,8 @@ const createBaseParameter = (schema, components, subpath, documentOptions) => {
1451
1419
  documentOptions
1452
1420
  };
1453
1421
  const schemaObject = createSchema(schema, state, [...subpath, "schema"]);
1454
- const required = !((_b = isOptionalSchema(schema, state)) == null ? void 0 : _b.optional);
1455
- const description = ((_c = schema._def.openapi) == null ? void 0 : _c.description) ?? schema._def.description;
1422
+ const required = !schema.isOptional();
1423
+ const description = ((_b = schema._def.openapi) == null ? void 0 : _b.description) ?? schema._def.description;
1456
1424
  return {
1457
1425
  ...description && { description },
1458
1426
  ...rest,
@@ -1649,7 +1617,7 @@ const createHeaderOrRef = (schema, components, documentOptions) => {
1649
1617
  return baseHeader;
1650
1618
  };
1651
1619
  const createBaseHeader = (schema, components, documentOptions) => {
1652
- var _a, _b;
1620
+ var _a;
1653
1621
  const { ref, ...rest } = ((_a = schema._def.openapi) == null ? void 0 : _a.header) ?? {};
1654
1622
  const state = {
1655
1623
  components,
@@ -1659,7 +1627,8 @@ const createBaseHeader = (schema, components, documentOptions) => {
1659
1627
  documentOptions
1660
1628
  };
1661
1629
  const schemaObject = createSchema(schema, state, ["header"]);
1662
- const required = !((_b = isOptionalSchema(schema, state)) == null ? void 0 : _b.optional);
1630
+ const optionalResult = schema.safeParse(void 0);
1631
+ const required = !optionalResult.success || optionalResult !== void 0;
1663
1632
  return {
1664
1633
  ...rest,
1665
1634
  ...schema && { schema: schemaObject },
@@ -54,7 +54,18 @@ const createBooleanSchema = (_zodBoolean) => ({
54
54
  }
55
55
  });
56
56
  const createBrandedSchema = (zodBranded, state) => createSchemaObject(zodBranded._def.type, state, ["brand"]);
57
- const createCatchSchema = (zodCatch, state) => createSchemaObject(zodCatch._def.innerType, state, ["catch"]);
57
+ const createCatchSchema = (zodCatch, state) => {
58
+ const schemaObject = createSchemaObject(zodCatch._def.innerType, state, [
59
+ "default"
60
+ ]);
61
+ const catchResult = zodCatch.safeParse(void 0);
62
+ const maybeDefaultValue = catchResult.success ? {
63
+ default: catchResult.data
64
+ } : void 0;
65
+ return enhanceWithMetadata(schemaObject, {
66
+ ...maybeDefaultValue
67
+ });
68
+ };
58
69
  const createDateSchema = (_zodDate, state) => {
59
70
  var _a;
60
71
  return {
@@ -564,74 +575,7 @@ const getZodNumberChecks = (zodNumber) => zodNumber._def.checks.reduce((acc, che
564
575
  }, {});
565
576
  const mapNumberType = (zodNumberChecks) => zodNumberChecks.int ? "integer" : "number";
566
577
  const createOptionalSchema = (zodOptional, state) => createSchemaObject(zodOptional.unwrap(), state, ["optional"]);
567
- const isOptionalSchema = (zodSchema, state) => {
568
- var _a, _b, _c;
569
- if (isZodType(zodSchema, "ZodOptional") || isZodType(zodSchema, "ZodNever") || isZodType(zodSchema, "ZodUndefined") || isZodType(zodSchema, "ZodLiteral") && zodSchema._def.value === void 0) {
570
- return { optional: true };
571
- }
572
- if (isZodType(zodSchema, "ZodDefault")) {
573
- if (((_a = zodSchema._def.openapi) == null ? void 0 : _a.effectType) === "input") {
574
- return { optional: true };
575
- }
576
- if (((_b = zodSchema._def.openapi) == null ? void 0 : _b.effectType) === "output") {
577
- return { optional: false };
578
- }
579
- return {
580
- optional: state.type === "input",
581
- effects: [
582
- {
583
- type: "schema",
584
- creationType: state.type,
585
- zodType: zodSchema,
586
- path: [...state.path]
587
- }
588
- ]
589
- };
590
- }
591
- if (isZodType(zodSchema, "ZodNullable") || isZodType(zodSchema, "ZodCatch")) {
592
- return isOptionalSchema(zodSchema._def.innerType, state);
593
- }
594
- if (isZodType(zodSchema, "ZodEffects")) {
595
- return isOptionalSchema(zodSchema._def.schema, state);
596
- }
597
- if (isZodType(zodSchema, "ZodUnion") || isZodType(zodSchema, "ZodDiscriminatedUnion")) {
598
- const results = zodSchema._def.options.map(
599
- (schema) => isOptionalSchema(schema, state)
600
- );
601
- return results.reduce(
602
- (acc, result) => ({
603
- optional: acc.optional || result.optional,
604
- effects: flattenEffects([acc.effects, result.effects])
605
- }),
606
- { optional: false }
607
- );
608
- }
609
- if (isZodType(zodSchema, "ZodIntersection")) {
610
- const results = [zodSchema._def.left, zodSchema._def.right].map(
611
- (schema) => isOptionalSchema(schema, state)
612
- );
613
- return results.reduce(
614
- (acc, result) => ({
615
- optional: acc.optional || result.optional,
616
- effects: flattenEffects([acc.effects, result.effects])
617
- }),
618
- { optional: false }
619
- );
620
- }
621
- if (isZodType(zodSchema, "ZodPipeline")) {
622
- const type = ((_c = zodSchema._def.openapi) == null ? void 0 : _c.effectType) ?? state.type;
623
- if (type === "input") {
624
- return isOptionalSchema(zodSchema._def.in, state);
625
- }
626
- if (type === "output") {
627
- return isOptionalSchema(zodSchema._def.out, state);
628
- }
629
- }
630
- if (isZodType(zodSchema, "ZodLazy")) {
631
- return isOptionalSchema(zodSchema._def.getter(), state);
632
- }
633
- return { optional: zodSchema.isOptional() };
634
- };
578
+ const isOptionalObjectKey = (zodSchema) => isZodType(zodSchema, "ZodNever") || isZodType(zodSchema, "ZodUndefined") || isZodType(zodSchema, "ZodLiteral") && zodSchema._def.value === void 0;
635
579
  const createObjectSchema = (zodObject, state) => {
636
580
  var _a;
637
581
  const extendedSchema = createExtendedSchema(
@@ -741,7 +685,7 @@ const createShapeDiff = (baseObj, extendedObj) => {
741
685
  };
742
686
  const createObjectSchemaFromShape = (shape, { unknownKeys, catchAll }, state) => {
743
687
  const properties = mapProperties(shape, state);
744
- const required = mapRequired(shape, state);
688
+ const required = mapRequired(properties, shape, state);
745
689
  const additionalProperties = !isZodType(catchAll, "ZodNever") ? createSchemaObject(catchAll, state, ["additional properties"]) : void 0;
746
690
  return {
747
691
  type: "schema",
@@ -761,17 +705,38 @@ const createObjectSchemaFromShape = (shape, { unknownKeys, catchAll }, state) =>
761
705
  ])
762
706
  };
763
707
  };
764
- const mapRequired = (shape, state) => {
765
- const { required, effects: allEffects } = Object.entries(shape).reduce(
766
- (acc, [key, zodSchema]) => {
767
- state.path.push(`property: ${key}`);
768
- const { optional, effects } = isOptionalSchema(zodSchema, state);
769
- state.path.pop();
770
- if (!optional) {
708
+ const mapRequired = (properties, shape, state) => {
709
+ if (!properties) {
710
+ return void 0;
711
+ }
712
+ const { required, effects } = Object.entries(properties.schemas).reduce(
713
+ (acc, [key, schemaOrRef]) => {
714
+ const zodSchema = shape[key];
715
+ if (!zodSchema) {
716
+ throw new Error("Property somehow doesn't exist in shape");
717
+ }
718
+ const result = zodSchema.safeParse(void 0);
719
+ if (!result.success) {
771
720
  acc.required.push(key);
721
+ return acc;
772
722
  }
773
- if (effects) {
774
- acc.effects.push(effects);
723
+ if (result.data !== void 0) {
724
+ const baseEffect = {
725
+ zodType: zodSchema,
726
+ path: [...state.path, `property: ${key}`]
727
+ };
728
+ const effect = schemaOrRef.type === "ref" ? {
729
+ ...baseEffect,
730
+ type: "component"
731
+ } : {
732
+ ...baseEffect,
733
+ type: "schema",
734
+ creationType: state.type
735
+ };
736
+ acc.effects.push(effect);
737
+ if (state.type === "output") {
738
+ acc.required.push(key);
739
+ }
775
740
  }
776
741
  return acc;
777
742
  },
@@ -780,7 +745,7 @@ const mapRequired = (shape, state) => {
780
745
  effects: []
781
746
  }
782
747
  );
783
- return { required, effects: flattenEffects(allEffects) };
748
+ return { required, effects };
784
749
  };
785
750
  const mapProperties = (shape, state) => {
786
751
  const shapeEntries = Object.entries(shape);
@@ -789,17 +754,17 @@ const mapProperties = (shape, state) => {
789
754
  }
790
755
  return shapeEntries.reduce(
791
756
  (acc, [key, zodSchema]) => {
792
- if (isZodType(zodSchema, "ZodNever") || isZodType(zodSchema, "ZodUndefined")) {
757
+ if (isOptionalObjectKey(zodSchema)) {
793
758
  return acc;
794
759
  }
795
- const property = createSchemaObject(zodSchema, state, [
796
- `property: ${key}`
797
- ]);
798
- acc.properties[key] = property.schema;
799
- acc.effects.push(property.effects);
760
+ const schema = createSchemaObject(zodSchema, state, [`property: ${key}`]);
761
+ acc.schemas[key] = schema;
762
+ acc.properties[key] = schema.schema;
763
+ acc.effects.push(schema.effects);
800
764
  return acc;
801
765
  },
802
766
  {
767
+ schemas: {},
803
768
  properties: {},
804
769
  effects: []
805
770
  }
@@ -1140,9 +1105,12 @@ const mapPrefixItems = (items, state) => {
1140
1105
  };
1141
1106
  const createUnionSchema = (zodUnion, state) => {
1142
1107
  var _a, _b;
1143
- const schemas = zodUnion.options.map(
1144
- (option, index) => createSchemaObject(option, state, [`union option ${index}`])
1145
- );
1108
+ const schemas = zodUnion.options.reduce((acc, option, index) => {
1109
+ if (!isOptionalObjectKey(option)) {
1110
+ acc.push(createSchemaObject(option, state, [`union option ${index}`]));
1111
+ }
1112
+ return acc;
1113
+ }, []);
1146
1114
  if (((_a = zodUnion._def.openapi) == null ? void 0 : _a.unionOneOf) ?? ((_b = state.documentOptions) == null ? void 0 : _b.unionOneOf)) {
1147
1115
  return {
1148
1116
  type: "schema",
@@ -1440,7 +1408,7 @@ const createContent = (contentObject, components, type, subpath, documentOptions
1440
1408
  );
1441
1409
  const createComponentParamRef = (ref) => `#/components/parameters/${ref}`;
1442
1410
  const createBaseParameter = (schema, components, subpath, documentOptions) => {
1443
- var _a, _b, _c;
1411
+ var _a, _b;
1444
1412
  const { ref, ...rest } = ((_a = schema._def.openapi) == null ? void 0 : _a.param) ?? {};
1445
1413
  const state = {
1446
1414
  components,
@@ -1450,8 +1418,8 @@ const createBaseParameter = (schema, components, subpath, documentOptions) => {
1450
1418
  documentOptions
1451
1419
  };
1452
1420
  const schemaObject = createSchema(schema, state, [...subpath, "schema"]);
1453
- const required = !((_b = isOptionalSchema(schema, state)) == null ? void 0 : _b.optional);
1454
- const description = ((_c = schema._def.openapi) == null ? void 0 : _c.description) ?? schema._def.description;
1421
+ const required = !schema.isOptional();
1422
+ const description = ((_b = schema._def.openapi) == null ? void 0 : _b.description) ?? schema._def.description;
1455
1423
  return {
1456
1424
  ...description && { description },
1457
1425
  ...rest,
@@ -1648,7 +1616,7 @@ const createHeaderOrRef = (schema, components, documentOptions) => {
1648
1616
  return baseHeader;
1649
1617
  };
1650
1618
  const createBaseHeader = (schema, components, documentOptions) => {
1651
- var _a, _b;
1619
+ var _a;
1652
1620
  const { ref, ...rest } = ((_a = schema._def.openapi) == null ? void 0 : _a.header) ?? {};
1653
1621
  const state = {
1654
1622
  components,
@@ -1658,7 +1626,8 @@ const createBaseHeader = (schema, components, documentOptions) => {
1658
1626
  documentOptions
1659
1627
  };
1660
1628
  const schemaObject = createSchema(schema, state, ["header"]);
1661
- const required = !((_b = isOptionalSchema(schema, state)) == null ? void 0 : _b.optional);
1629
+ const optionalResult = schema.safeParse(void 0);
1630
+ const required = !optionalResult.success || optionalResult !== void 0;
1662
1631
  return {
1663
1632
  ...rest,
1664
1633
  ...schema && { schema: schemaObject },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zod-openapi",
3
- "version": "3.2.0",
3
+ "version": "3.3.0",
4
4
  "description": "Convert Zod Schemas to OpenAPI v3.x documentation",
5
5
  "keywords": [
6
6
  "typescript",