Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@asteasolutions/zod-to-openapi

Package Overview
Dependencies
Maintainers
3
Versions
61
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@asteasolutions/zod-to-openapi - npm Package Compare versions

Comparing version 2.3.0 to 3.0.0

dist/openapi-metadata.d.ts

3

dist/index.d.ts

@@ -1,4 +0,5 @@

export * from './zod-extensions';
export { ZodOpenAPIMetadata, extendZodWithOpenApi } from './zod-extensions';
export * from './openapi-metadata';
export { OpenAPIGenerator } from './openapi-generator';
export { OpenAPIRegistry, RouteConfig, ResponseConfig, } from './openapi-registry';
export * as OpenAPI from 'openapi3-ts';

@@ -29,4 +29,6 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.OpenAPI = exports.OpenAPIRegistry = exports.OpenAPIGenerator = void 0;
__exportStar(require("./zod-extensions"), exports);
exports.OpenAPI = exports.OpenAPIRegistry = exports.OpenAPIGenerator = exports.extendZodWithOpenApi = void 0;
var zod_extensions_1 = require("./zod-extensions");
Object.defineProperty(exports, "extendZodWithOpenApi", { enumerable: true, get: function () { return zod_extensions_1.extendZodWithOpenApi; } });
__exportStar(require("./openapi-metadata"), exports);
var openapi_generator_1 = require("./openapi-generator");

@@ -33,0 +35,0 @@ Object.defineProperty(exports, "OpenAPIGenerator", { enumerable: true, get: function () { return openapi_generator_1.OpenAPIGenerator; } });

@@ -9,4 +9,4 @@ export declare function isUndefined<T>(value: any): value is undefined;

[K in keyof T]: T[keyof T];
}>>(object: T, predicate: (val: T[keyof T]) => boolean): Result;
}>>(object: T, predicate: (val: T[keyof T], key: keyof T) => boolean): Result;
export declare function compact<T extends any>(arr: (T | null | undefined)[]): T[];
export declare function objectEquals(x: any, y: any): boolean;

@@ -33,3 +33,3 @@ "use strict";

Object.entries(object).forEach(([key, value]) => {
if (!predicate(value)) {
if (!predicate(value, key)) {
result[key] = value;

@@ -36,0 +36,0 @@ }

@@ -25,2 +25,3 @@ import type { z } from 'zod';

ZodVoid: z.ZodVoid;
ZodDate: z.ZodDate;
};

@@ -27,0 +28,0 @@ export declare function isZodType<TypeName extends keyof ZodTypes>(schema: object, typeName: TypeName): schema is ZodTypes[TypeName];

@@ -1,14 +0,9 @@

import { OpenAPIObject, InfoObject, ServerObject, SecurityRequirementObject, TagObject, ExternalDocumentationObject, ComponentsObject } from 'openapi3-ts';
import { OpenAPIObject } from 'openapi3-ts';
import { OpenAPIDefinitions } from './openapi-registry';
interface OpenAPIObjectConfig {
openapi: string;
info: InfoObject;
servers?: ServerObject[];
security?: SecurityRequirementObject[];
tags?: TagObject[];
externalDocs?: ExternalDocumentationObject;
[key: string]: unknown;
}
declare const openApiVersions: readonly ["3.0.0", "3.0.1", "3.0.2", "3.0.3", "3.1.0"];
export declare type OpenApiVersion = typeof openApiVersions[number];
export declare type OpenAPIObjectConfig = Omit<OpenAPIObject, 'paths' | 'components' | 'webhooks' | 'openapi'>;
export declare class OpenAPIGenerator {
private definitions;
private openAPIVersion;
private schemaRefs;

@@ -19,5 +14,5 @@ private paramRefs;

private rawComponents;
constructor(definitions: OpenAPIDefinitions[]);
constructor(definitions: OpenAPIDefinitions[], openAPIVersion: OpenApiVersion);
generateDocument(config: OpenAPIObjectConfig): OpenAPIObject;
generateComponents(): ComponentsObject;
generateComponents(): Pick<OpenAPIObject, 'components'>;
private buildComponents;

@@ -49,4 +44,10 @@ private sortDefinitions;

private mapStringFormat;
private mapDiscriminator;
private openApiVersionSatisfies;
private mapNullableOfArray;
private mapNullableType;
private toOpenAPISchema;
private isOptionalSchema;
private getDefaultValue;
private requiredKeysOf;
private toOpenAPIObjectSchema;

@@ -56,7 +57,12 @@ private flattenUnionTypes;

private unwrapChained;
/**
* A method that omits all custom keys added to the regular OpenAPI
* metadata properties
*/
private buildSchemaMetadata;
private buildParameterMetadata;
private getMetadata;
private getInternalMetadata;
private applySchemaMetadata;
}
export {};

@@ -19,5 +19,8 @@ "use strict";

const enum_info_1 = require("./lib/enum-info");
// List of Open API Versions. Please make sure these are in ascending order
const openApiVersions = ['3.0.0', '3.0.1', '3.0.2', '3.0.3', '3.1.0'];
class OpenAPIGenerator {
constructor(definitions) {
constructor(definitions, openAPIVersion) {
this.definitions = definitions;
this.openAPIVersion = openAPIVersion;
this.schemaRefs = {};

@@ -28,2 +31,4 @@ this.paramRefs = {};

this.rawComponents = [];
this.openApiVersionSatisfies = (inputVersion, comparison) => openApiVersions.indexOf(inputVersion) >=
openApiVersions.indexOf(comparison);
this.sortDefinitions();

@@ -33,3 +38,3 @@ }

this.definitions.forEach(definition => this.generateSingle(definition));
return Object.assign(Object.assign(Object.assign({}, config), { components: this.buildComponents(), paths: this.pathRefs }), (Object.keys(this.webhookRefs).length && {
return Object.assign(Object.assign(Object.assign({}, config), { openapi: this.openAPIVersion, components: this.buildComponents(), paths: this.pathRefs }), (Object.keys(this.webhookRefs).length && {
webhooks: this.webhookRefs,

@@ -87,3 +92,3 @@ }));

generateParameterDefinition(zodSchema) {
const metadata = this.getMetadata(zodSchema);
const metadata = this.getInternalMetadata(zodSchema);
const result = this.generateParameter(zodSchema);

@@ -96,7 +101,8 @@ if (metadata === null || metadata === void 0 ? void 0 : metadata.refId) {

getParameterRef(schemaMetadata, external) {
const parameterMetadata = schemaMetadata === null || schemaMetadata === void 0 ? void 0 : schemaMetadata.param;
const existingRef = (schemaMetadata === null || schemaMetadata === void 0 ? void 0 : schemaMetadata.refId)
? this.paramRefs[schemaMetadata.refId]
var _a, _b, _c, _d, _e;
const parameterMetadata = (_a = schemaMetadata === null || schemaMetadata === void 0 ? void 0 : schemaMetadata.metadata) === null || _a === void 0 ? void 0 : _a.param;
const existingRef = ((_b = schemaMetadata === null || schemaMetadata === void 0 ? void 0 : schemaMetadata._internal) === null || _b === void 0 ? void 0 : _b.refId)
? this.paramRefs[(_c = schemaMetadata._internal) === null || _c === void 0 ? void 0 : _c.refId]
: undefined;
if (!(schemaMetadata === null || schemaMetadata === void 0 ? void 0 : schemaMetadata.refId) || !existingRef) {
if (!((_d = schemaMetadata === null || schemaMetadata === void 0 ? void 0 : schemaMetadata._internal) === null || _d === void 0 ? void 0 : _d.refId) || !existingRef) {
return undefined;

@@ -127,8 +133,9 @@ }

return {
$ref: `#/components/parameters/${schemaMetadata.refId}`,
$ref: `#/components/parameters/${(_e = schemaMetadata._internal) === null || _e === void 0 ? void 0 : _e.refId}`,
};
}
generateInlineParameters(zodSchema, location) {
var _a;
const metadata = this.getMetadata(zodSchema);
const parameterMetadata = metadata === null || metadata === void 0 ? void 0 : metadata.param;
const parameterMetadata = (_a = metadata === null || metadata === void 0 ? void 0 : metadata.metadata) === null || _a === void 0 ? void 0 : _a.param;
const referencedSchema = this.getParameterRef(metadata, { in: location });

@@ -141,3 +148,3 @@ if (referencedSchema) {

const parameters = Object.entries(propTypes).map(([key, schema]) => {
var _a;
var _a, _b;
const innerMetadata = this.getMetadata(schema);

@@ -151,3 +158,3 @@ const referencedSchema = this.getParameterRef(innerMetadata, {

}
const innerParameterMetadata = innerMetadata === null || innerMetadata === void 0 ? void 0 : innerMetadata.param;
const innerParameterMetadata = (_a = innerMetadata === null || innerMetadata === void 0 ? void 0 : innerMetadata.metadata) === null || _a === void 0 ? void 0 : _a.param;
if ((innerParameterMetadata === null || innerParameterMetadata === void 0 ? void 0 : innerParameterMetadata.name) &&

@@ -162,3 +169,3 @@ innerParameterMetadata.name !== key) {

innerParameterMetadata.in !== location) {
throw new errors_1.ConflictError(`Conflicting location for parameter ${(_a = innerParameterMetadata.name) !== null && _a !== void 0 ? _a : key}`, {
throw new errors_1.ConflictError(`Conflicting location for parameter ${(_b = innerParameterMetadata.name) !== null && _b !== void 0 ? _b : key}`, {
key: 'in',

@@ -183,4 +190,5 @@ values: [location, innerParameterMetadata.in],

generateParameter(zodSchema) {
var _a;
const metadata = this.getMetadata(zodSchema);
const paramMetadata = metadata === null || metadata === void 0 ? void 0 : metadata.param;
const paramMetadata = (_a = metadata === null || metadata === void 0 ? void 0 : metadata.metadata) === null || _a === void 0 ? void 0 : _a.param;
const paramName = paramMetadata === null || paramMetadata === void 0 ? void 0 : paramMetadata.name;

@@ -197,3 +205,3 @@ const paramLocation = paramMetadata === null || paramMetadata === void 0 ? void 0 : paramMetadata.in;

}
const required = !zodSchema.isOptional() && !zodSchema.isNullable();
const required = !this.isOptionalSchema(zodSchema) && !zodSchema.isNullable();
const schema = this.generateSimpleSchema(zodSchema);

@@ -207,13 +215,24 @@ return Object.assign({ in: paramLocation, name: paramName, schema,

generateSimpleSchema(zodSchema) {
var _a, _b, _c, _d;
const innerSchema = this.unwrapChained(zodSchema);
const metadata = zodSchema._def.openapi
? zodSchema._def.openapi
: innerSchema._def.openapi;
const refId = metadata === null || metadata === void 0 ? void 0 : metadata.refId;
const metadata = (_a = zodSchema._def.openapi) !== null && _a !== void 0 ? _a : innerSchema._def.openapi;
const defaultValue = this.getDefaultValue(zodSchema);
const refId = (_b = metadata === null || metadata === void 0 ? void 0 : metadata._internal) === null || _b === void 0 ? void 0 : _b.refId;
if (refId && this.schemaRefs[refId]) {
const schemaRef = this.schemaRefs[refId];
const referenceObject = {
$ref: `#/components/schemas/${refId}`,
};
const nullableMetadata = zodSchema.isNullable() ? { nullable: true } : {};
const appliedMetadata = this.applySchemaMetadata(nullableMetadata, metadata);
// New metadata from .openapi()
const newMetadata = (0, lodash_1.omitBy)(
// We do not want to check our "custom" metadata fields. We only want
// the plain metadata for a SchemaObject.
this.buildSchemaMetadata((_c = metadata === null || metadata === void 0 ? void 0 : metadata.metadata) !== null && _c !== void 0 ? _c : {}), (value, key) => value === undefined || (0, lodash_1.objectEquals)(value, schemaRef[key]));
// New metadata from ZodSchema properties.
// Do not calculate schema metadata overrides if type is provided in .openapi
// https://github.com/asteasolutions/zod-to-openapi/pull/52/files/8ff707fe06e222bc573ed46cf654af8ee0b0786d#r996430801
const newSchemaMetadata = !newMetadata.type
? (0, lodash_1.omitBy)(this.toOpenAPISchema(innerSchema, zodSchema.isNullable(), defaultValue), (value, key) => value === undefined || (0, lodash_1.objectEquals)(value, schemaRef[key]))
: {};
const appliedMetadata = this.applySchemaMetadata(newSchemaMetadata, newMetadata);
if (Object.keys(appliedMetadata).length > 0) {

@@ -226,9 +245,9 @@ return {

}
const result = (metadata === null || metadata === void 0 ? void 0 : metadata.type)
const result = ((_d = metadata === null || metadata === void 0 ? void 0 : metadata.metadata) === null || _d === void 0 ? void 0 : _d.type)
? {
type: metadata === null || metadata === void 0 ? void 0 : metadata.type,
type: metadata === null || metadata === void 0 ? void 0 : metadata.metadata.type,
}
: this.toOpenAPISchema(innerSchema, zodSchema.isNullable());
return metadata
? this.applySchemaMetadata(result, metadata)
: this.toOpenAPISchema(innerSchema, zodSchema.isNullable(), defaultValue);
return (metadata === null || metadata === void 0 ? void 0 : metadata.metadata)
? this.applySchemaMetadata(result, metadata.metadata)
: (0, lodash_1.omitBy)(result, lodash_1.isNil);

@@ -238,3 +257,3 @@ }

const simpleSchema = this.generateSimpleSchema(zodSchema);
if (simpleSchema.$ref) {
if ('$ref' in simpleSchema && simpleSchema.$ref) {
return simpleSchema;

@@ -247,7 +266,8 @@ }

generateSchemaDefinition(zodSchema) {
var _a;
const metadata = this.getMetadata(zodSchema);
const refId = metadata === null || metadata === void 0 ? void 0 : metadata.refId;
const refId = (_a = metadata === null || metadata === void 0 ? void 0 : metadata._internal) === null || _a === void 0 ? void 0 : _a.refId;
const simpleSchema = this.generateSimpleSchema(zodSchema);
const result = metadata
? this.applySchemaMetadata(simpleSchema, metadata)
const result = (metadata === null || metadata === void 0 ? void 0 : metadata.metadata)
? this.applySchemaMetadata(simpleSchema, metadata.metadata)
: simpleSchema;

@@ -340,4 +360,46 @@ if (refId) {

}
toOpenAPISchema(zodSchema, isNullable) {
var _a, _b, _c, _d, _e;
mapDiscriminator(zodObjects, discriminator) {
// All schemas must be registered to use a discriminator
if (zodObjects.some(obj => { var _a, _b; return ((_b = (_a = obj._def.openapi) === null || _a === void 0 ? void 0 : _a._internal) === null || _b === void 0 ? void 0 : _b.refId) === undefined; })) {
return undefined;
}
const mapping = {};
zodObjects.forEach(obj => {
var _a, _b, _c, _d;
const value = (_b = (_a = obj.shape) === null || _a === void 0 ? void 0 : _a[discriminator]) === null || _b === void 0 ? void 0 : _b._def.value;
// This should never happen because Zod checks the disciminator type but to keep the types happy
if (typeof value !== 'string') {
throw new Error(`Discriminator ${discriminator} could not be found in one of the values of a discriminated union`);
}
mapping[value] = `#/components/schemas/${(_d = (_c = obj._def.openapi) === null || _c === void 0 ? void 0 : _c._internal) === null || _d === void 0 ? void 0 : _d.refId}`;
});
return {
propertyName: discriminator,
mapping,
};
}
mapNullableOfArray(objects, isNullable) {
if (isNullable) {
if (this.openApiVersionSatisfies(this.openAPIVersion, '3.1.0')) {
return [...objects, { type: 'null' }];
}
return [...objects, { nullable: true }];
}
return objects;
}
mapNullableType(type, isNullable) {
// Open API 3.1.0 made the `nullable` key invalid and instead you use type arrays
if (isNullable &&
this.openApiVersionSatisfies(this.openAPIVersion, '3.1.0')) {
return {
type: Array.isArray(type) ? [...type, 'null'] : [type, 'null'],
};
}
return {
type,
nullable: isNullable ? true : undefined,
};
}
toOpenAPISchema(zodSchema, isNullable, defaultValue) {
var _a, _b, _c, _d, _e, _f, _g, _h;
if ((0, zod_is_type_1.isZodType)(zodSchema, 'ZodNull')) {

@@ -348,27 +410,16 @@ return { type: 'null' };

const regexCheck = this.getZodStringCheck(zodSchema, 'regex');
return {
type: 'string',
nullable: isNullable ? true : undefined,
format: this.mapStringFormat(zodSchema),
pattern: regexCheck === null || regexCheck === void 0 ? void 0 : regexCheck.regex.source,
};
return Object.assign(Object.assign({}, this.mapNullableType('string', isNullable)), {
// FIXME: https://github.com/colinhacks/zod/commit/d78047e9f44596a96d637abb0ce209cd2732d88c
minLength: Number.isFinite(zodSchema.minLength)
? (_a = zodSchema.minLength) !== null && _a !== void 0 ? _a : undefined
: undefined, maxLength: Number.isFinite(zodSchema.maxLength)
? (_b = zodSchema.maxLength) !== null && _b !== void 0 ? _b : undefined
: undefined, format: this.mapStringFormat(zodSchema), pattern: regexCheck === null || regexCheck === void 0 ? void 0 : regexCheck.regex.source, default: defaultValue });
}
if ((0, zod_is_type_1.isZodType)(zodSchema, 'ZodNumber')) {
return {
type: zodSchema.isInt ? 'integer' : 'number',
minimum: (_a = zodSchema.minValue) !== null && _a !== void 0 ? _a : undefined,
maximum: (_b = zodSchema.maxValue) !== null && _b !== void 0 ? _b : undefined,
nullable: isNullable ? true : undefined,
};
return Object.assign(Object.assign({}, this.mapNullableType(zodSchema.isInt ? 'integer' : 'number', isNullable)), { minimum: (_c = zodSchema.minValue) !== null && _c !== void 0 ? _c : undefined, maximum: (_d = zodSchema.maxValue) !== null && _d !== void 0 ? _d : undefined, default: defaultValue });
}
if ((0, zod_is_type_1.isZodType)(zodSchema, 'ZodBoolean')) {
return {
type: 'boolean',
nullable: isNullable ? true : undefined,
};
return Object.assign(Object.assign({}, this.mapNullableType('boolean', isNullable)), { default: defaultValue });
}
if ((0, zod_is_type_1.isZodType)(zodSchema, 'ZodDefault')) {
const innerSchema = zodSchema._def.innerType;
return this.generateInnerSchema(innerSchema);
}
if ((0, zod_is_type_1.isZodType)(zodSchema, 'ZodEffects') &&

@@ -381,15 +432,7 @@ (zodSchema._def.effect.type === 'refinement' ||

if ((0, zod_is_type_1.isZodType)(zodSchema, 'ZodLiteral')) {
return {
type: typeof zodSchema._def.value,
nullable: isNullable ? true : undefined,
enum: [zodSchema._def.value],
};
return Object.assign(Object.assign({}, this.mapNullableType(typeof zodSchema._def.value, isNullable)), { enum: [zodSchema._def.value], default: defaultValue });
}
if ((0, zod_is_type_1.isZodType)(zodSchema, 'ZodEnum')) {
// ZodEnum only accepts strings
return {
type: 'string',
nullable: isNullable ? true : undefined,
enum: zodSchema._def.values,
};
return Object.assign(Object.assign({}, this.mapNullableType('string', isNullable)), { enum: zodSchema._def.values, default: defaultValue });
}

@@ -409,19 +452,10 @@ if ((0, zod_is_type_1.isZodType)(zodSchema, 'ZodNativeEnum')) {

}
return {
type: type === 'numeric' ? 'number' : 'string',
nullable: isNullable ? true : undefined,
enum: values,
};
return Object.assign(Object.assign({}, this.mapNullableType(type === 'numeric' ? 'number' : 'string', isNullable)), { enum: values, default: defaultValue });
}
if ((0, zod_is_type_1.isZodType)(zodSchema, 'ZodObject')) {
return this.toOpenAPIObjectSchema(zodSchema, isNullable);
return this.toOpenAPIObjectSchema(zodSchema, isNullable, defaultValue);
}
if ((0, zod_is_type_1.isZodType)(zodSchema, 'ZodArray')) {
const itemType = zodSchema._def.type;
return {
type: 'array',
items: this.generateInnerSchema(itemType),
minItems: (_c = zodSchema._def.minLength) === null || _c === void 0 ? void 0 : _c.value,
maxItems: (_d = zodSchema._def.maxLength) === null || _d === void 0 ? void 0 : _d.value,
};
return Object.assign(Object.assign({}, this.mapNullableType('array', isNullable)), { items: this.generateInnerSchema(itemType), minItems: (_e = zodSchema._def.minLength) === null || _e === void 0 ? void 0 : _e.value, maxItems: (_f = zodSchema._def.maxLength) === null || _f === void 0 ? void 0 : _f.value, default: defaultValue });
}

@@ -431,3 +465,4 @@ if ((0, zod_is_type_1.isZodType)(zodSchema, 'ZodUnion')) {

return {
anyOf: options.map(schema => this.generateInnerSchema(schema)),
anyOf: this.mapNullableOfArray(options.map(schema => this.generateInnerSchema(schema)), isNullable),
default: defaultValue,
};

@@ -437,4 +472,13 @@ }

const options = [...zodSchema.options.values()];
const optionSchema = options.map(schema => this.generateInnerSchema(schema));
if (isNullable) {
return {
oneOf: this.mapNullableOfArray(optionSchema, isNullable),
default: defaultValue,
};
}
return {
anyOf: options.map(schema => this.generateInnerSchema(schema)),
oneOf: optionSchema,
discriminator: this.mapDiscriminator(options, zodSchema.discriminator),
default: defaultValue,
};

@@ -444,12 +488,16 @@ }

const subtypes = this.flattenIntersectionTypes(zodSchema);
return {
const allOfSchema = {
allOf: subtypes.map(schema => this.generateInnerSchema(schema)),
};
if (isNullable) {
return {
anyOf: this.mapNullableOfArray([allOfSchema], isNullable),
default: defaultValue,
};
}
return Object.assign(Object.assign({}, allOfSchema), { default: defaultValue });
}
if ((0, zod_is_type_1.isZodType)(zodSchema, 'ZodRecord')) {
const propertiesType = zodSchema._def.valueType;
return {
type: 'object',
additionalProperties: this.generateInnerSchema(propertiesType),
};
return Object.assign(Object.assign({}, this.mapNullableType('object', isNullable)), { additionalProperties: this.generateInnerSchema(propertiesType), default: defaultValue });
}

@@ -459,3 +507,6 @@ if ((0, zod_is_type_1.isZodType)(zodSchema, 'ZodUnknown')) {

}
const refId = (_e = this.getMetadata(zodSchema)) === null || _e === void 0 ? void 0 : _e.refId;
if ((0, zod_is_type_1.isZodType)(zodSchema, 'ZodDate')) {
return Object.assign(Object.assign({}, this.mapNullableType('string', isNullable)), { default: defaultValue });
}
const refId = (_h = (_g = this.getMetadata(zodSchema)) === null || _g === void 0 ? void 0 : _g._internal) === null || _h === void 0 ? void 0 : _h.refId;
throw new errors_1.UnknownZodTypeError({

@@ -475,27 +526,39 @@ currentSchema: zodSchema._def,

}
toOpenAPIObjectSchema(zodSchema, isNullable) {
var _a, _b, _c;
const extendedFrom = (_a = zodSchema._def.openapi) === null || _a === void 0 ? void 0 : _a.extendedFrom;
const propTypes = zodSchema._def.shape();
const unknownKeysOption = zodSchema._unknownKeys;
const requiredProperties = Object.entries(propTypes)
getDefaultValue(zodSchema) {
if ((0, zod_is_type_1.isZodType)(zodSchema, 'ZodOptional') ||
(0, zod_is_type_1.isZodType)(zodSchema, 'ZodNullable')) {
return this.getDefaultValue(zodSchema.unwrap());
}
if ((0, zod_is_type_1.isZodType)(zodSchema, 'ZodEffects')) {
return this.getDefaultValue(zodSchema._def.schema);
}
if ((0, zod_is_type_1.isZodType)(zodSchema, 'ZodDefault')) {
return zodSchema._def.defaultValue();
}
return undefined;
}
requiredKeysOf(objectSchema) {
return Object.entries(objectSchema._def.shape())
.filter(([_key, type]) => !this.isOptionalSchema(type))
.map(([key, _type]) => key);
const schemaProperties = (0, lodash_1.mapValues)(propTypes, propSchema => this.generateInnerSchema(propSchema));
let alreadyRegistered = [];
let alreadyRequired = [];
if (extendedFrom) {
const registeredSchema = this.schemaRefs[extendedFrom];
if (!registeredSchema) {
throw new Error(`Attempt to extend an unregistered schema with id ${extendedFrom}.`);
}
const registeredProperties = (_b = registeredSchema.properties) !== null && _b !== void 0 ? _b : {};
alreadyRegistered = Object.keys(registeredProperties).filter(propKey => {
return (0, lodash_1.objectEquals)(schemaProperties[propKey], registeredProperties[propKey]);
});
alreadyRequired = (_c = registeredSchema.required) !== null && _c !== void 0 ? _c : [];
}
const properties = (0, lodash_1.omit)(schemaProperties, alreadyRegistered);
const additionallyRequired = requiredProperties.filter(prop => !alreadyRequired.includes(prop));
const objectData = Object.assign(Object.assign(Object.assign({ type: 'object', properties }, (isNullable ? { nullable: true } : {})), (additionallyRequired.length > 0
}
toOpenAPIObjectSchema(zodSchema, isNullable, defaultValue) {
var _a, _b;
const extendedFrom = (_b = (_a = zodSchema._def.openapi) === null || _a === void 0 ? void 0 : _a._internal) === null || _b === void 0 ? void 0 : _b.extendedFrom;
const parentShape = extendedFrom === null || extendedFrom === void 0 ? void 0 : extendedFrom.schema._def.shape();
const childShape = zodSchema._def.shape();
const keysRequiredByParent = extendedFrom
? this.requiredKeysOf(extendedFrom.schema)
: [];
const keysRequiredByChild = this.requiredKeysOf(zodSchema);
const propsOfParent = parentShape
? (0, lodash_1.mapValues)(parentShape, _ => this.generateInnerSchema(_))
: {};
const propsOfChild = (0, lodash_1.mapValues)(childShape, _ => this.generateInnerSchema(_));
const properties = Object.fromEntries(Object.entries(propsOfChild).filter(([key, type]) => {
return !(0, lodash_1.objectEquals)(propsOfParent[key], type);
}));
const additionallyRequired = keysRequiredByChild.filter(prop => !keysRequiredByParent.includes(prop));
const unknownKeysOption = zodSchema._unknownKeys;
const objectData = Object.assign(Object.assign(Object.assign(Object.assign({}, this.mapNullableType('object', isNullable)), { default: defaultValue, properties }), (additionallyRequired.length > 0
? { required: additionallyRequired }

@@ -507,3 +570,6 @@ : {})), (unknownKeysOption === 'passthrough'

return {
allOf: [{ $ref: `#/components/schemas/${extendedFrom}` }, objectData],
allOf: [
{ $ref: `#/components/schemas/${extendedFrom.refId}` },
objectData,
],
};

@@ -541,5 +607,8 @@ }

}
/**
* A method that omits all custom keys added to the regular OpenAPI
* metadata properties
*/
buildSchemaMetadata(metadata) {
// A place to omit all custom keys added to the openapi
return (0, lodash_1.omitBy)((0, lodash_1.omit)(metadata, ['param', 'refId', 'extendedFrom']), lodash_1.isNil);
return (0, lodash_1.omitBy)((0, lodash_1.omit)(metadata, ['param']), lodash_1.isNil);
}

@@ -556,2 +625,9 @@ buildParameterMetadata(metadata) {

}
getInternalMetadata(zodSchema) {
const innerSchema = this.unwrapChained(zodSchema);
const openapi = zodSchema._def.openapi
? zodSchema._def.openapi
: innerSchema._def.openapi;
return openapi === null || openapi === void 0 ? void 0 : openapi._internal;
}
applySchemaMetadata(initialData, metadata) {

@@ -558,0 +634,0 @@ return (0, lodash_1.omitBy)(Object.assign(Object.assign({}, initialData), this.buildSchemaMetadata(metadata)), lodash_1.isNil);

@@ -93,3 +93,4 @@ import { CallbackObject, ComponentsObject, EncodingObject, ExampleObject, ExamplesObject, HeaderObject, HeadersObject, ISpecificationExtension, LinkObject, LinksObject, OperationObject, ParameterObject, ReferenceObject, RequestBodyObject, ResponseObject, SchemaObject, SecuritySchemeObject } from 'openapi3-ts';

};
private schemaWithRefId;
}
export {};

@@ -18,6 +18,5 @@ "use strict";

register(refId, zodSchema) {
const currentMetadata = zodSchema._def.openapi;
const schemaWithMetadata = zodSchema.openapi(Object.assign(Object.assign({}, currentMetadata), { refId }));
this._definitions.push({ type: 'schema', schema: schemaWithMetadata });
return schemaWithMetadata;
const schemaWithRefId = this.schemaWithRefId(refId, zodSchema);
this._definitions.push({ type: 'schema', schema: schemaWithRefId });
return schemaWithRefId;
}

@@ -28,5 +27,6 @@ /**

registerParameter(refId, zodSchema) {
var _a, _b;
const currentMetadata = zodSchema._def.openapi;
const schemaWithMetadata = zodSchema.openapi(Object.assign(Object.assign({}, currentMetadata), { param: Object.assign(Object.assign({}, currentMetadata === null || currentMetadata === void 0 ? void 0 : currentMetadata.param), { name: (_b = (_a = currentMetadata === null || currentMetadata === void 0 ? void 0 : currentMetadata.param) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : refId }), refId }));
var _a, _b, _c;
const schemaWithRefId = this.schemaWithRefId(refId, zodSchema);
const currentMetadata = (_a = schemaWithRefId._def.openapi) === null || _a === void 0 ? void 0 : _a.metadata;
const schemaWithMetadata = zodSchema.openapi(Object.assign(Object.assign({}, currentMetadata), { param: Object.assign(Object.assign({}, currentMetadata === null || currentMetadata === void 0 ? void 0 : currentMetadata.param), { name: (_c = (_b = currentMetadata === null || currentMetadata === void 0 ? void 0 : currentMetadata.param) === null || _b === void 0 ? void 0 : _b.name) !== null && _c !== void 0 ? _c : refId }) }));
this._definitions.push({

@@ -76,3 +76,6 @@ type: 'parameter',

}
schemaWithRefId(refId, zodSchema) {
return zodSchema.internal_openapi({ refId });
}
}
exports.OpenAPIRegistry = OpenAPIRegistry;
import { ParameterObject, SchemaObject } from 'openapi3-ts';
import type { z } from 'zod';
export interface ZodOpenAPIMetadata<T = any> extends SchemaObject {
refId?: string;
extendedFrom?: string;
import type { z, ZodObject, ZodRawShape } from 'zod';
declare type ExampleValue<T> = T extends Date ? string : T;
export interface ZodOpenAPIMetadata<T = any, E = ExampleValue<T>> extends SchemaObject {
param?: Partial<ParameterObject> & {
example?: T;
example?: E;
};
example?: T;
example?: E;
examples?: E[];
default?: T;
}
export interface ZodOpenAPIInternalMetadata {
refId?: string;
extendedFrom?: {
refId: string;
schema: ZodObject<ZodRawShape>;
};
}
export interface ZodOpenApiFullMetadata<T = any> {
_internal?: ZodOpenAPIInternalMetadata;
metadata?: ZodOpenAPIMetadata<T>;
}
declare module 'zod' {
interface ZodTypeDef {
openapi?: ZodOpenAPIMetadata;
openapi?: ZodOpenApiFullMetadata;
}
abstract class ZodSchema<Output, Def extends ZodTypeDef, Input = Output> {
openapi<T extends ZodSchema<any>>(this: T, metadata: Partial<ZodOpenAPIMetadata<z.infer<T>>>): T;
/**
* This method should NOT be used outside of @astesolution/zod-to-openapi code!
* Any usage of it can lead to unexpected consequences when generating the
* OpenApi schemas!
*
* @deprecated
*/
internal_openapi<T extends ZodSchema<any>>(this: T, metadata: Partial<ZodOpenAPIInternalMetadata>): T;
}
}
export declare function extendZodWithOpenApi(zod: typeof z): void;
export {};

@@ -24,12 +24,37 @@ "use strict";

zod.ZodSchema.prototype.openapi = function (openapi) {
var _a;
const _b = openapi !== null && openapi !== void 0 ? openapi : {}, { param } = _b, restOfOpenApi = __rest(_b, ["param"]);
const result = new this.constructor(Object.assign(Object.assign({}, this._def), { openapi: Object.assign(Object.assign(Object.assign({}, this._def.openapi), restOfOpenApi), { param: Object.assign(Object.assign({}, (_a = this._def.openapi) === null || _a === void 0 ? void 0 : _a.param), param) }) }));
var _a, _b, _c, _d, _e, _f;
const _g = openapi !== null && openapi !== void 0 ? openapi : {}, { param } = _g, restOfOpenApi = __rest(_g, ["param"]);
const result = new this.constructor(Object.assign(Object.assign({}, this._def), { openapi: {
_internal: (_a = this._def.openapi) === null || _a === void 0 ? void 0 : _a._internal,
metadata: Object.assign(Object.assign(Object.assign({}, (_b = this._def.openapi) === null || _b === void 0 ? void 0 : _b.metadata), restOfOpenApi), { param: ((_d = (_c = this._def.openapi) === null || _c === void 0 ? void 0 : _c.metadata) === null || _d === void 0 ? void 0 : _d.param) || param
? Object.assign(Object.assign({}, (_f = (_e = this._def.openapi) === null || _e === void 0 ? void 0 : _e.metadata) === null || _f === void 0 ? void 0 : _f.param), param) : undefined }),
} }));
/**
* We want to preserve the behavior created by `internal_openapi`
* for extended objects. Applying metadata does not change
* the parent's `refId` to be used for `extendedFrom`
*/
if ((0, zod_is_type_1.isZodType)(this, 'ZodObject')) {
const initialExtend = this.extend;
result.extend = this.extend;
}
return result;
};
zod.ZodSchema.prototype.internal_openapi = function (openapi) {
var _a, _b;
const result = new this.constructor(Object.assign(Object.assign({}, this._def), { openapi: {
_internal: Object.assign(Object.assign({}, (_a = this._def.openapi) === null || _a === void 0 ? void 0 : _a._internal), openapi),
metadata: (_b = this._def.openapi) === null || _b === void 0 ? void 0 : _b.metadata,
} }));
if ((0, zod_is_type_1.isZodType)(this, 'ZodObject')) {
const originalExtend = this.extend;
result.extend = function (...args) {
var _a;
const extendedResult = initialExtend.apply(result, args);
var _a, _b, _c, _d, _e;
const extendedResult = originalExtend.apply(this, args);
extendedResult._def.openapi = {
extendedFrom: (_a = result._def.openapi) === null || _a === void 0 ? void 0 : _a.refId,
_internal: {
extendedFrom: ((_b = (_a = this._def.openapi) === null || _a === void 0 ? void 0 : _a._internal) === null || _b === void 0 ? void 0 : _b.refId)
? { refId: (_d = (_c = this._def.openapi) === null || _c === void 0 ? void 0 : _c._internal) === null || _d === void 0 ? void 0 : _d.refId, schema: this }
: (_e = this._def.openapi) === null || _e === void 0 ? void 0 : _e._internal.extendedFrom,
},
metadata: {},
};

@@ -53,2 +78,8 @@ return extendedResult;

};
const zodDefault = zod.ZodSchema.prototype.default;
zod.ZodSchema.prototype.default = function (...args) {
const result = zodDefault.apply(this, args);
result._def.openapi = this._def.openapi;
return result;
};
const zodPick = zod.ZodObject.prototype.pick;

@@ -55,0 +86,0 @@ zod.ZodObject.prototype.pick = function (...args) {

{
"name": "@asteasolutions/zod-to-openapi",
"version": "2.3.0",
"version": "3.0.0",
"description": "Builds OpenAPI schemas from Zod schemas",

@@ -33,3 +33,3 @@ "main": "dist/index.js",

"dependencies": {
"openapi3-ts": "^2.0.2"
"openapi3-ts": "^3.1.1"
},

@@ -36,0 +36,0 @@ "peerDependencies": {

# Zod to OpenAPI
[![npm version](https://img.shields.io/npm/v/@asteasolutions/zod-to-openapi)](https://www.npmjs.com/package/@asteasolutions/zod-to-openapi)
[![npm downloads](https://img.shields.io/npm/dm/@asteasolutions/zod-to-openapi)](https://www.npmjs.com/package/@asteasolutions/zod-to-openapi)
A library that uses [zod schemas](https://github.com/colinhacks/zod) to generate an Open API Swagger documentation.

@@ -10,7 +13,8 @@

3. [The Registry](#the-registry)
4. [Defining schemas](#defining-schemas)
5. [Defining routes](#defining-routes)
6. [Defining custom components](#defining-custom-components)
6. [A full example](#a-full-example)
7. [Adding it as part of your build](#adding-it-as-part-of-your-build)
4. [The Generator](#the-generator)
5. [Defining schemas](#defining-schemas)
6. [Defining routes & webhooks](#defining-routes--webhooks)
7. [Defining custom components](#defining-custom-components)
8. [A full example](#a-full-example)
9. [Adding it as part of your build](#adding-it-as-part-of-your-build)
3. [Zod schema types](#zod-schema-types)

@@ -142,3 +146,3 @@ 1. [Supported types](#supported-types)

const generator = new OpenAPIGenerator(registry.definitions);
const generator = new OpenAPIGenerator(registry.definitions, '3.0.0');

@@ -148,4 +152,30 @@ return generator.generateComponents();

### The Generator
The generator constructor takes 2 arguments. An array of definitions from the registry and an Open API version.
The Open API version affects how some components are generated. For example: changing the version to `3.1.0` from `3.0.0` will result in following differences:
```ts
z.string().nullable().openapi(refId: 'name');
```
```yml
# 3.1.0
# nullable is invalid in 3.1.0 but type arrays are invalid in previous versions
name:
type:
- 'string'
- 'null'
# 3.0.0
name:
type: 'string'
nullable: true
```
`generateComponents` will generate only the `/components` section of an OpenAPI document (e.g. only `schemas` and `parameters`), not generating actual routes.
`generateDocument` will generate the whole OpenAPI document.
### Defining schemas

@@ -195,7 +225,7 @@

### Defining routes
### Defining routes & webhooks
#### Registering a path
#### Registering a path or webhook
An OpenAPI path is registered using the `registerPath` method of an `OpenAPIRegistry` instance.
An OpenAPI path is registered using the `registerPath` method of an `OpenAPIRegistry` instance. An OpenAPI webhook is registered using the `registerWebhook` method and takes the same parameters as `registerPath`.

@@ -324,3 +354,2 @@ ```ts

return generator.generateDocument({
openapi: '3.0.0',
info: {

@@ -373,3 +402,5 @@ version: '1.0.0',

- `ZodDefault`
- `ZodEffects` - only for `.refine()`
- `ZodNullable`
- `ZodOptional`
- `ZodEffects` - only for `.refine()`, `.preprocess()`
- `ZodLiteral`

@@ -380,2 +411,4 @@ - `ZodEnum`

- `ZodArray`
- `ZodDiscriminatedUnion`
- including `discriminator` mapping when all Zod objects in the union are registered with `.register()` or contain a `refId`.
- `ZodUnion`

@@ -385,2 +418,3 @@ - `ZodIntersection`

- `ZodUnknown`
- `ZodDate`

@@ -387,0 +421,0 @@ Extending an instance of `ZodObject` is also supported and results in an OpenApi definition with `allOf`

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc