zod-openapi
Version:
Convert Zod Schemas to OpenAPI v3.x documentation
1,671 lines (1,669 loc) • 74.8 kB
JavaScript
import { previousSymbol, currentSymbol } from "./extendZodSymbols.chunk.mjs";
const isZodType = (zodType, typeName) => {
var _a;
return ((_a = zodType == null ? void 0 : zodType._def) == null ? void 0 : _a.typeName) === typeName;
};
const isAnyZodType = (zodType) => {
var _a;
return Boolean(
(_a = zodType == null ? void 0 : zodType._def) == null ? void 0 : _a.typeName
);
};
const openApiVersions = [
"3.0.0",
"3.0.1",
"3.0.2",
"3.0.3",
"3.1.0"
];
const satisfiesVersion = (test, against) => openApiVersions.indexOf(test) >= openApiVersions.indexOf(against);
const createDescriptionMetadata = (schema, description, state) => {
if (satisfiesVersion(state.components.openapi, "3.1.0")) {
return {
type: "ref",
schema: {
$ref: schema.schema.$ref,
description
},
zodType: schema.zodType,
effects: schema.effects,
schemaObject: schema.schemaObject
};
}
return {
type: "schema",
schema: {
description,
allOf: [schema.schema]
},
effects: schema.effects
};
};
const isValueEqual = (value, previous) => {
if (typeof value !== typeof previous) {
return false;
}
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
return value === previous;
}
if (Array.isArray(value) && Array.isArray(previous)) {
const sorted = [...value].sort();
const previousSorted = [...previous].sort();
return sorted.every((v, i) => isValueEqual(v, previousSorted[i]));
}
if (value === null || previous === null) {
return value === previous;
}
if (typeof value === "object" && typeof previous === "object") {
const keys = Object.keys(value);
return keys.every(
(key) => isValueEqual(
value[key],
previous[key]
)
);
}
return value === previous;
};
const enhanceWithMetadata = (schema, metadata, state, previous) => {
const values = Object.entries(metadata).reduce(
(acc, [key, value]) => {
if (value === void 0) {
return acc;
}
acc[key] = value;
return acc;
},
{}
);
const length = Object.values(values).length;
if (schema.type === "ref") {
if (length === 0) {
return schema;
}
if (length === 1 && metadata.description) {
return createDescriptionMetadata(schema, metadata.description, state);
}
return {
type: "schema",
schema: {
allOf: [schema.schema],
...metadata
},
effects: schema.effects
};
}
if (previous && schema.schema.type !== "object") {
const diff = Object.entries({ ...schema.schema, ...values }).reduce(
(acc, [key, value]) => {
if (previous.schemaObject && isValueEqual(
previous.schemaObject[key],
value
)) {
return acc;
}
acc[key] = value;
return acc;
},
{}
);
const diffLength = Object.values(diff).length;
if (diffLength === 0) {
return {
type: "ref",
schema: {
$ref: previous.schema.$ref
},
effects: schema.effects,
schemaObject: previous.schemaObject,
zodType: previous.zodType
};
}
if (diffLength === 1 && typeof diff.description === "string") {
return createDescriptionMetadata(previous, diff.description, state);
}
return {
type: "schema",
schema: { allOf: [previous.schema], ...diff },
effects: schema.effects
};
}
return {
type: "schema",
schema: {
...schema.schema,
...metadata
},
effects: schema.effects
};
};
const createArraySchema = (zodArray, state) => {
var _a, _b, _c, _d;
const zodType = zodArray._def.type;
const minItems = ((_a = zodArray._def.exactLength) == null ? void 0 : _a.value) ?? ((_b = zodArray._def.minLength) == null ? void 0 : _b.value);
const maxItems = ((_c = zodArray._def.exactLength) == null ? void 0 : _c.value) ?? ((_d = zodArray._def.maxLength) == null ? void 0 : _d.value);
const items = createSchemaObject(zodType, state, ["array items"]);
return {
type: "schema",
schema: {
type: "array",
items: items.schema,
...minItems !== void 0 && { minItems },
...maxItems !== void 0 && { maxItems }
},
effects: items.effects
};
};
const createBigIntSchema = (_zodBigInt) => ({
type: "schema",
schema: {
type: "integer",
format: "int64"
}
});
const createBooleanSchema = (_zodBoolean) => ({
type: "schema",
schema: {
type: "boolean"
}
});
const createBrandedSchema = (zodBranded, state) => createSchemaObject(zodBranded._def.type, state, ["brand"]);
const createCatchSchema = (zodCatch, state, previous) => {
const schemaObject = createSchemaObject(zodCatch._def.innerType, state, [
"default"
]);
const catchResult = zodCatch.safeParse(void 0);
const maybeDefaultValue = catchResult.success ? {
default: catchResult.data
} : {};
return enhanceWithMetadata(schemaObject, maybeDefaultValue, state, previous);
};
const createDateSchema = (_zodDate, state) => {
var _a;
return {
type: "schema",
schema: ((_a = state.documentOptions) == null ? void 0 : _a.defaultDateSchema) ?? {
type: "string"
}
};
};
const createDefaultSchema = (zodDefault, state, previous) => {
const schemaObject = createSchemaObject(zodDefault._def.innerType, state, [
"default"
]);
return enhanceWithMetadata(
schemaObject,
{
default: zodDefault._def.defaultValue()
},
state,
previous
);
};
const createNativeEnumSchema = (zodEnum, state) => {
const enumValues = getValidEnumValues(zodEnum._def.values);
const { numbers, strings } = sortStringsAndNumbers(enumValues);
if (strings.length && numbers.length) {
if (satisfiesVersion(state.components.openapi, "3.1.0")) {
return {
type: "schema",
schema: {
type: ["string", "number"],
enum: [...strings, ...numbers]
}
};
}
return {
type: "schema",
schema: {
oneOf: [
{ type: "string", enum: strings },
{ type: "number", enum: numbers }
]
}
};
}
if (strings.length) {
return {
type: "schema",
schema: {
type: "string",
enum: strings
}
};
}
return {
type: "schema",
schema: {
type: "number",
enum: numbers
}
};
};
const getValidEnumValues = (enumValues) => {
const keys = Object.keys(enumValues).filter(
(key) => typeof enumValues[enumValues[key]] !== "number"
);
return keys.map((key) => enumValues[key]);
};
const sortStringsAndNumbers = (values) => ({
strings: values.filter((value) => typeof value === "string"),
numbers: values.filter((value) => typeof value === "number")
});
const createTransformSchema = (zodTransform, state) => {
var _a, _b, _c, _d, _e, _f;
if (((_b = (_a = zodTransform._def.zodOpenApi) == null ? void 0 : _a.openapi) == null ? void 0 : _b.effectType) === "output") {
return {
type: "schema",
schema: createManualOutputTransformSchema(zodTransform, state)
};
}
if (((_d = (_c = zodTransform._def.zodOpenApi) == null ? void 0 : _c.openapi) == null ? void 0 : _d.effectType) === "input" || ((_f = (_e = zodTransform._def.zodOpenApi) == null ? void 0 : _e.openapi) == null ? void 0 : _f.effectType) === "same") {
return createSchemaObject(zodTransform._def.schema, state, [
"transform input"
]);
}
if (state.type === "output") {
return {
type: "schema",
schema: createManualOutputTransformSchema(zodTransform, state)
};
}
const schema = createSchemaObject(zodTransform._def.schema, state, [
"transform input"
]);
return {
...schema,
effects: flattenEffects([
[
{
type: "schema",
creationType: "input",
zodType: zodTransform,
path: [...state.path]
}
],
schema.effects
])
};
};
const createManualOutputTransformSchema = (zodTransform, state) => {
var _a, _b, _c;
if (!((_b = (_a = zodTransform._def.zodOpenApi) == null ? void 0 : _a.openapi) == null ? void 0 : _b.type)) {
const zodType = zodTransform.constructor.name;
const schemaName = `${zodType} - ${zodTransform._def.effect.type}`;
throw new Error(
`Failed to determine a type for ${schemaName} at ${state.path.join(
" > "
)}. Please change the 'effectType' to 'same' or 'input', wrap it in a ZodPipeline or assign it a manual 'type'.`
);
}
return {
type: (_c = zodTransform._def.zodOpenApi) == null ? void 0 : _c.openapi.type
};
};
const getZodTypeName = (zodType) => {
if (isZodType(zodType, "ZodEffects")) {
return `${zodType._def.typeName} - ${zodType._def.effect.type}`;
}
return zodType._def.typeName;
};
const throwTransformError = (effect) => {
const typeName = getZodTypeName(effect.zodType);
const input = effect.creationType;
const opposite = input === "input" ? "output" : "input";
throw new Error(
`The ${typeName} at ${effect.path.join(
" > "
)} is used within a registered compoment schema${effect.component ? ` (${effect.component.ref})` : ""} and contains an ${input} transformation${effect.component ? ` (${getZodTypeName(
effect.component.zodType
)}) defined at ${effect.component.path.join(" > ")}` : ""} which is also used in an ${opposite} schema.
This may cause the schema to render incorrectly and is most likely a mistake. You can resolve this by:
1. Setting an \`effectType\` on one of the transformations to \`same\` (Not applicable for ZodDefault), \`input\` or \`output\` eg. \`.openapi({type: 'same'})\`
2. Wrapping the transformation in a ZodPipeline
3. Assigning a manual type to the transformation eg. \`.openapi({type: 'string'})\`
4. Removing the transformation
5. Deregister the component containing the transformation`
);
};
const resolveSingleEffect = (effect, state) => {
if (effect.type === "schema") {
return {
creationType: effect.creationType,
path: effect.path,
zodType: effect.zodType
};
}
if (effect.type === "component") {
if (state.visited.has(effect.zodType)) {
return;
}
const component = state.components.schemas.get(effect.zodType);
if ((component == null ? void 0 : component.type) !== "complete") {
throw new Error("Something went wrong, component schema is not complete");
}
if (component.resolvedEffect) {
return {
creationType: component.resolvedEffect.creationType,
path: effect.path,
zodType: effect.zodType,
component: {
ref: component.ref,
zodType: component.resolvedEffect.zodType,
path: component.resolvedEffect.path
}
};
}
if (!component.effects) {
return void 0;
}
state.visited.add(effect.zodType);
const resolved = resolveEffect(component.effects, state);
state.visited.delete(effect.zodType);
if (!resolved) {
return void 0;
}
component.resolvedEffect = resolved;
return resolved;
}
return void 0;
};
const resolveEffect = (effects, state) => {
const { input, output } = effects.reduce(
(acc, effect) => {
const resolvedSchemaEffect = resolveSingleEffect(effect, state);
if ((resolvedSchemaEffect == null ? void 0 : resolvedSchemaEffect.creationType) === "input") {
acc.input.push(resolvedSchemaEffect);
}
if ((resolvedSchemaEffect == null ? void 0 : resolvedSchemaEffect.creationType) === "output") {
acc.output.push(resolvedSchemaEffect);
}
if (resolvedSchemaEffect && acc.input.length > 1 && acc.output.length > 1) {
throwTransformError(resolvedSchemaEffect);
}
return acc;
},
{ input: [], output: [] }
);
if (input.length > 0) {
return input[0];
}
if (output.length > 0) {
return output[0];
}
return void 0;
};
const verifyEffects = (effects, state) => {
const resolved = resolveEffect(effects, state);
if ((resolved == null ? void 0 : resolved.creationType) && resolved.creationType !== state.type) {
throwTransformError(resolved);
}
};
const flattenEffects = (effects) => {
const allEffects = effects.reduce((acc, effect) => {
if (effect) {
return acc.concat(effect);
}
return acc;
}, []);
return allEffects.length ? allEffects : void 0;
};
const createDiscriminatedUnionSchema = (zodDiscriminatedUnion, state) => {
const options = zodDiscriminatedUnion.options;
const schemas = options.map(
(option, index) => createSchemaObject(option, state, [`discriminated union option ${index}`])
);
const schemaObjects = schemas.map((schema) => schema.schema);
const discriminator = mapDiscriminator(
schemaObjects,
options,
zodDiscriminatedUnion.discriminator,
state
);
return {
type: "schema",
schema: {
oneOf: schemaObjects,
...discriminator && { discriminator }
},
effects: flattenEffects(schemas.map((schema) => schema.effects))
};
};
const unwrapLiterals = (zodType, state) => {
if (isZodType(zodType, "ZodLiteral")) {
if (typeof zodType._def.value !== "string") {
return void 0;
}
return [zodType._def.value];
}
if (isZodType(zodType, "ZodNativeEnum")) {
const schema = createNativeEnumSchema(zodType, state);
if (schema.type === "schema" && schema.schema.type === "string") {
return schema.schema.enum;
}
}
if (isZodType(zodType, "ZodEnum")) {
return zodType._def.values;
}
if (isZodType(zodType, "ZodBranded")) {
return unwrapLiterals(zodType._def.type, state);
}
if (isZodType(zodType, "ZodReadonly")) {
return unwrapLiterals(zodType._def.innerType, state);
}
if (isZodType(zodType, "ZodCatch")) {
return unwrapLiterals(zodType._def.innerType, state);
}
return void 0;
};
const mapDiscriminator = (schemas, zodObjects, discriminator, state) => {
var _a;
if (typeof discriminator !== "string") {
return void 0;
}
const mapping = {};
for (const [index, zodObject] of zodObjects.entries()) {
const schema = schemas[index];
const componentSchemaRef = "$ref" in schema ? schema == null ? void 0 : schema.$ref : void 0;
if (!componentSchemaRef) {
if ((_a = state.documentOptions) == null ? void 0 : _a.enforceDiscriminatedUnionComponents) {
throw new Error(
`Discriminated Union member ${index} at ${state.path.join(" > ")} is not registered as a component`
);
}
return void 0;
}
const value = zodObject.shape[discriminator];
const literals = unwrapLiterals(value, state);
if (!literals) {
return void 0;
}
for (const enumValue of literals) {
mapping[enumValue] = componentSchemaRef;
}
}
return {
propertyName: discriminator,
mapping
};
};
const createEnumSchema = (zodEnum) => ({
type: "schema",
schema: {
type: "string",
enum: zodEnum._def.values
}
});
const createIntersectionSchema = (zodIntersection, state) => {
const schemas = flattenIntersection(zodIntersection);
const allOfs = schemas.map(
(schema, index) => createSchemaObject(schema, state, [`intersection ${index}`])
);
return {
type: "schema",
schema: {
allOf: allOfs.map((schema) => schema.schema)
},
effects: flattenEffects(allOfs.map((schema) => schema.effects))
};
};
const flattenIntersection = (zodType) => {
if (!isZodType(zodType, "ZodIntersection")) {
return [zodType];
}
const leftSchemas = flattenIntersection(zodType._def.left);
const rightSchemas = flattenIntersection(zodType._def.right);
return [...leftSchemas, ...rightSchemas];
};
const createLazySchema = (zodLazy, state) => {
const innerSchema = zodLazy._def.getter();
return createSchemaObject(innerSchema, state, ["lazy schema"]);
};
const createNullSchema = () => ({
type: "schema",
schema: {
type: "null"
}
});
const createLiteralSchema = (zodLiteral, state) => {
if (zodLiteral.value === null) {
return createNullSchema();
}
if (satisfiesVersion(state.components.openapi, "3.1.0")) {
return {
type: "schema",
schema: {
type: typeof zodLiteral.value,
const: zodLiteral.value
}
};
}
return {
type: "schema",
schema: {
type: typeof zodLiteral.value,
enum: [zodLiteral.value]
}
};
};
const createManualTypeSchema = (zodSchema, state) => {
var _a, _b, _c;
if (!((_b = (_a = zodSchema._def.zodOpenApi) == null ? void 0 : _a.openapi) == null ? void 0 : _b.type)) {
const schemaName = zodSchema.constructor.name;
throw new Error(
`Unknown schema ${schemaName} at ${state.path.join(
" > "
)}. Please assign it a manual 'type'.`
);
}
return {
type: "schema",
schema: {
type: (_c = zodSchema._def.zodOpenApi) == null ? void 0 : _c.openapi.type
}
};
};
const createNullableSchema = (zodNullable, state) => {
const schemaObject = createSchemaObject(zodNullable.unwrap(), state, [
"nullable"
]);
if (satisfiesVersion(state.components.openapi, "3.1.0")) {
if (schemaObject.type === "ref" || schemaObject.schema.allOf) {
return {
type: "schema",
schema: {
oneOf: mapNullOf([schemaObject.schema], state.components.openapi)
},
effects: schemaObject.effects
};
}
if (schemaObject.schema.oneOf) {
const { oneOf, ...schema3 } = schemaObject.schema;
return {
type: "schema",
schema: {
oneOf: mapNullOf(oneOf, state.components.openapi),
...schema3
},
effects: schemaObject.effects
};
}
if (schemaObject.schema.anyOf) {
const { anyOf, ...schema3 } = schemaObject.schema;
return {
type: "schema",
schema: {
anyOf: mapNullOf(anyOf, state.components.openapi),
...schema3
},
effects: schemaObject.effects
};
}
const { type: type2, const: schemaConst, ...schema2 } = schemaObject.schema;
if (schemaConst) {
return {
type: "schema",
schema: {
type: mapNullType(type2),
enum: [schemaConst, null],
...schema2
},
effects: schemaObject.effects
};
}
return {
type: "schema",
schema: {
type: mapNullType(type2),
...schema2,
// https://github.com/json-schema-org/json-schema-spec/issues/258
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
...schema2.enum && { enum: [...schema2.enum, null] }
},
effects: schemaObject.effects
};
}
if (schemaObject.type === "ref") {
return {
type: "schema",
schema: {
allOf: [schemaObject.schema],
nullable: true
},
effects: schemaObject.effects
};
}
const { type, ...schema } = schemaObject.schema;
return {
type: "schema",
schema: {
...type && { type },
nullable: true,
...schema,
// https://github.com/OAI/OpenAPI-Specification/blob/main/proposals/2019-10-31-Clarify-Nullable.md#if-a-schema-specifies-nullable-true-and-enum-1-2-3-does-that-schema-allow-null-values-see-1900
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
...schema.enum && { enum: [...schema.enum, null] }
},
effects: schemaObject.effects
};
};
const mapNullType = (type) => {
if (!type) {
return "null";
}
if (Array.isArray(type)) {
return [...type, "null"];
}
return [type, "null"];
};
const mapNullOf = (ofSchema, openapi) => {
if (satisfiesVersion(openapi, "3.1.0")) {
return [...ofSchema, { type: "null" }];
}
return [...ofSchema, { nullable: true }];
};
const createNumberSchema = (zodNumber, state) => {
const zodNumberChecks = getZodNumberChecks(zodNumber);
const minimum = mapMinimum(zodNumberChecks, state.components.openapi);
const maximum = mapMaximum(zodNumberChecks, state.components.openapi);
const multipleOf = mapMultipleOf(zodNumberChecks);
return {
type: "schema",
schema: {
type: mapNumberType(zodNumberChecks),
...multipleOf && multipleOf,
...minimum && minimum,
// Union types are not easy to tame
...maximum && maximum
}
};
};
const mapMultipleOf = (zodNumberCheck) => zodNumberCheck.multipleOf ? { multipleOf: zodNumberCheck.multipleOf.value } : void 0;
const mapMaximum = (zodNumberCheck, openapi) => {
if (!zodNumberCheck.max) {
return void 0;
}
const maximum = zodNumberCheck.max.value;
if (zodNumberCheck.max.inclusive) {
return { ...maximum !== void 0 && { maximum } };
}
if (satisfiesVersion(openapi, "3.1.0")) {
return { exclusiveMaximum: maximum };
}
return { maximum, exclusiveMaximum: true };
};
const mapMinimum = (zodNumberCheck, openapi) => {
if (!zodNumberCheck.min) {
return void 0;
}
const minimum = zodNumberCheck.min.value;
if (zodNumberCheck.min.inclusive) {
return { ...minimum !== void 0 && { minimum } };
}
if (satisfiesVersion(openapi, "3.1.0")) {
return { exclusiveMinimum: minimum };
}
return { minimum, exclusiveMinimum: true };
};
const getZodNumberChecks = (zodNumber) => zodNumber._def.checks.reduce((acc, check) => {
acc[check.kind] = check;
return acc;
}, {});
const mapNumberType = (zodNumberChecks) => zodNumberChecks.int ? "integer" : "number";
const createOptionalSchema = (zodOptional, state) => createSchemaObject(zodOptional.unwrap(), state, ["optional"]);
const isOptionalObjectKey = (zodSchema) => isZodType(zodSchema, "ZodNever") || isZodType(zodSchema, "ZodUndefined") || isZodType(zodSchema, "ZodLiteral") && zodSchema._def.value === void 0;
const createObjectSchema = (zodObject, previous, state) => {
const extendedSchema = createExtendedSchema(
zodObject,
previous == null ? void 0 : previous.zodType,
state
);
if (extendedSchema) {
return extendedSchema;
}
return createObjectSchemaFromShape(
zodObject.shape,
{
unknownKeys: zodObject._def.unknownKeys,
catchAll: zodObject._def.catchall
},
state
);
};
const createExtendedSchema = (zodObject, baseZodObject, state) => {
var _a, _b, _c, _d, _e;
if (!baseZodObject) {
return void 0;
}
const component = state.components.schemas.get(baseZodObject);
if (component ?? ((_b = (_a = baseZodObject._def.zodOpenApi) == null ? void 0 : _a.openapi) == null ? void 0 : _b.ref)) {
createSchemaObject(baseZodObject, state, ["extended schema"]);
}
const completeComponent = state.components.schemas.get(baseZodObject);
if (!completeComponent) {
return void 0;
}
const diffOpts = createDiffOpts(
{
unknownKeys: baseZodObject._def.unknownKeys,
catchAll: baseZodObject._def.catchall
},
{
unknownKeys: zodObject._def.unknownKeys,
catchAll: zodObject._def.catchall
}
);
if (!diffOpts) {
return void 0;
}
const diffShape = createShapeDiff(
baseZodObject._def.shape(),
zodObject._def.shape()
);
if (!diffShape) {
return void 0;
}
const extendedSchema = createObjectSchemaFromShape(
diffShape,
diffOpts,
state,
true
);
const schemaLength = Object.keys(extendedSchema.schema).length;
const effects = flattenEffects([
completeComponent.type === "complete" ? completeComponent.effects : [],
completeComponent.type === "in-progress" ? [
{
type: "component",
zodType: zodObject,
path: [...state.path]
}
] : [],
extendedSchema.effects
]);
if (schemaLength === 0) {
return {
type: "ref",
schema: {
$ref: createComponentSchemaRef(
completeComponent.ref,
(_c = state.documentOptions) == null ? void 0 : _c.componentRefPath
)
},
schemaObject: completeComponent.type === "complete" ? completeComponent.schemaObject : void 0,
zodType: zodObject,
effects
};
}
if (schemaLength === 1 && extendedSchema.schema.description) {
return createDescriptionMetadata(
{
type: "ref",
schema: {
$ref: createComponentSchemaRef(
completeComponent.ref,
(_d = state.documentOptions) == null ? void 0 : _d.componentRefPath
)
},
schemaObject: completeComponent.type === "complete" ? completeComponent.schemaObject : void 0,
zodType: zodObject,
effects
},
extendedSchema.schema.description,
state
);
}
return {
type: "schema",
schema: {
allOf: [
{
$ref: createComponentSchemaRef(
completeComponent.ref,
(_e = state.documentOptions) == null ? void 0 : _e.componentRefPath
)
}
],
...extendedSchema.schema
},
effects: flattenEffects([
completeComponent.type === "complete" ? completeComponent.effects : [],
completeComponent.type === "in-progress" ? [
{
type: "component",
zodType: zodObject,
path: [...state.path]
}
] : [],
extendedSchema.effects
])
};
};
const createDiffOpts = (baseOpts, extendedOpts) => {
if (baseOpts.unknownKeys === "strict" || !isZodType(baseOpts.catchAll, "ZodNever")) {
return void 0;
}
return {
catchAll: extendedOpts.catchAll,
unknownKeys: extendedOpts.unknownKeys
};
};
const createShapeDiff = (baseObj, extendedObj) => {
const acc = {};
for (const [key, val] of Object.entries(extendedObj)) {
const baseValue = baseObj[key];
if (val === baseValue) {
continue;
}
if (baseValue === void 0) {
acc[key] = extendedObj[key];
continue;
}
return null;
}
return acc;
};
const mapAdditionalProperties = ({ unknownKeys, catchAll }, state) => {
if (!isZodType(catchAll, "ZodNever")) {
return createSchemaObject(catchAll, state, ["additional properties"]);
}
if (unknownKeys === "strict") {
return false;
}
if (unknownKeys === "passthrough") {
return true;
}
return void 0;
};
const createObjectSchemaFromShape = (shape, { unknownKeys, catchAll }, state, omitType) => {
const properties = mapProperties(shape, state);
const required = mapRequired(properties, shape, state);
const additionalProperties = mapAdditionalProperties(
{ catchAll, unknownKeys },
state
);
return {
type: "schema",
schema: {
...!omitType && { type: "object" },
...properties && { properties: properties.properties },
...(required == null ? void 0 : required.required.length) && { required: required.required },
...additionalProperties !== void 0 && {
additionalProperties: typeof additionalProperties === "object" ? additionalProperties.schema : additionalProperties
}
},
effects: flattenEffects([
...(properties == null ? void 0 : properties.effects) ?? [],
typeof additionalProperties === "object" && (additionalProperties == null ? void 0 : additionalProperties.effects),
required == null ? void 0 : required.effects
])
};
};
const mapRequired = (properties, shape, state) => {
if (!properties) {
return void 0;
}
const { required, effects } = Object.entries(properties.schemas).reduce(
(acc, [key, schemaOrRef]) => {
const zodSchema = shape[key];
if (!zodSchema) {
throw new Error("Property somehow doesn't exist in shape");
}
const result = zodSchema.safeParse(void 0);
if (!result.success) {
acc.required.push(key);
return acc;
}
if (result.data !== void 0) {
const baseEffect = {
zodType: zodSchema,
path: [...state.path, `property: ${key}`]
};
const effect = schemaOrRef.type === "ref" ? {
...baseEffect,
type: "component"
} : {
...baseEffect,
type: "schema",
creationType: state.type
};
acc.effects.push(effect);
if (state.type === "output") {
acc.required.push(key);
}
}
return acc;
},
{
required: [],
effects: []
}
);
return { required, effects };
};
const mapProperties = (shape, state) => {
const shapeEntries = Object.entries(shape);
if (!shapeEntries.length) {
return void 0;
}
return shapeEntries.reduce(
(acc, [key, zodSchema]) => {
if (isOptionalObjectKey(zodSchema)) {
return acc;
}
const schema = createSchemaObject(zodSchema, state, [`property: ${key}`]);
acc.schemas[key] = schema;
acc.properties[key] = schema.schema;
acc.effects.push(schema.effects);
return acc;
},
{
schemas: {},
properties: {},
effects: []
}
);
};
const createPipelineSchema = (zodPipeline, state) => {
var _a, _b, _c, _d, _e, _f;
if (((_b = (_a = zodPipeline._def.zodOpenApi) == null ? void 0 : _a.openapi) == null ? void 0 : _b.effectType) === "input" || ((_d = (_c = zodPipeline._def.zodOpenApi) == null ? void 0 : _c.openapi) == null ? void 0 : _d.effectType) === "same") {
return createSchemaObject(zodPipeline._def.in, state, ["pipeline input"]);
}
if (((_f = (_e = zodPipeline._def.zodOpenApi) == null ? void 0 : _e.openapi) == null ? void 0 : _f.effectType) === "output") {
return createSchemaObject(zodPipeline._def.out, state, ["pipeline output"]);
}
if (state.type === "input") {
const schema2 = createSchemaObject(zodPipeline._def.in, state, [
"pipeline input"
]);
return {
...schema2,
effects: flattenEffects([
[
{
type: "schema",
creationType: "input",
path: [...state.path],
zodType: zodPipeline
}
],
schema2.effects
])
};
}
const schema = createSchemaObject(zodPipeline._def.out, state, [
"pipeline output"
]);
return {
...schema,
effects: flattenEffects([
[
{
type: "schema",
creationType: "output",
path: [...state.path],
zodType: zodPipeline
}
],
schema.effects
])
};
};
const createPreprocessSchema = (zodPreprocess, state) => createSchemaObject(zodPreprocess._def.schema, state, ["preprocess schema"]);
const createReadonlySchema = (zodReadonly, state) => (
// Readonly doesn't change OpenAPI schema
createSchemaObject(zodReadonly._def.innerType, state, ["readonly"])
);
const createRecordSchema = (zodRecord, state) => {
const additionalProperties = createSchemaObject(
zodRecord.valueSchema,
state,
["record value"]
);
const keySchema = createSchemaObject(zodRecord.keySchema, state, [
"record key"
]);
const maybeComponent = state.components.schemas.get(zodRecord.keySchema);
const isComplete = maybeComponent && maybeComponent.type === "complete";
const maybeSchema = isComplete && maybeComponent.schemaObject;
const maybeEffects = isComplete && maybeComponent.effects || void 0;
const renderedKeySchema = maybeSchema || keySchema.schema;
if ("enum" in renderedKeySchema && renderedKeySchema.enum) {
return {
type: "schema",
schema: {
type: "object",
properties: renderedKeySchema.enum.reduce((acc, key) => {
acc[key] = additionalProperties.schema;
return acc;
}, {}),
additionalProperties: false
},
effects: flattenEffects([
keySchema.effects,
additionalProperties.effects,
maybeEffects
])
};
}
if (satisfiesVersion(state.components.openapi, "3.1.0") && "type" in renderedKeySchema && renderedKeySchema.type === "string" && Object.keys(renderedKeySchema).length > 1) {
return {
type: "schema",
schema: {
type: "object",
propertyNames: keySchema.schema,
additionalProperties: additionalProperties.schema
},
effects: flattenEffects([
keySchema.effects,
additionalProperties.effects
])
};
}
return {
type: "schema",
schema: {
type: "object",
additionalProperties: additionalProperties.schema
},
effects: additionalProperties.effects
};
};
const createRefineSchema = (zodRefine, state) => createSchemaObject(zodRefine._def.schema, state, ["refine schema"]);
const createSetSchema = (zodSet, state) => {
var _a, _b;
const schema = zodSet._def.valueType;
const minItems = (_a = zodSet._def.minSize) == null ? void 0 : _a.value;
const maxItems = (_b = zodSet._def.maxSize) == null ? void 0 : _b.value;
const itemSchema = createSchemaObject(schema, state, ["set items"]);
return {
type: "schema",
schema: {
type: "array",
items: itemSchema.schema,
uniqueItems: true,
...minItems !== void 0 && { minItems },
...maxItems !== void 0 && { maxItems }
},
effects: itemSchema.effects
};
};
const createStringSchema = (zodString, state) => {
var _a, _b, _c, _d, _e, _f, _g, _h;
const zodStringChecks = getZodStringChecks(zodString);
const format = mapStringFormat(zodStringChecks);
const patterns = mapPatterns(zodStringChecks);
const minLength = ((_b = (_a = zodStringChecks.length) == null ? void 0 : _a[0]) == null ? void 0 : _b.value) ?? ((_d = (_c = zodStringChecks.min) == null ? void 0 : _c[0]) == null ? void 0 : _d.value);
const maxLength = ((_f = (_e = zodStringChecks.length) == null ? void 0 : _e[0]) == null ? void 0 : _f.value) ?? ((_h = (_g = zodStringChecks.max) == null ? void 0 : _g[0]) == null ? void 0 : _h.value);
const contentEncoding = satisfiesVersion(state.components.openapi, "3.1.0") ? mapContentEncoding(zodStringChecks) : void 0;
if (patterns.length <= 1) {
return {
type: "schema",
schema: {
type: "string",
...format && { format },
...patterns[0] && { pattern: patterns[0] },
...minLength !== void 0 && { minLength },
...maxLength !== void 0 && { maxLength },
...contentEncoding && { contentEncoding }
}
};
}
return {
type: "schema",
schema: {
allOf: [
{
type: "string",
...format && { format },
...patterns[0] && { pattern: patterns[0] },
...minLength !== void 0 && { minLength },
...maxLength !== void 0 && { maxLength },
...contentEncoding && { contentEncoding }
},
...patterns.slice(1).map(
(pattern) => ({
type: "string",
pattern
})
)
]
}
};
};
const getZodStringChecks = (zodString) => zodString._def.checks.reduce(
(acc, check) => {
const mapping = acc[check.kind];
if (mapping) {
mapping.push(check);
return acc;
}
acc[check.kind] = [check];
return acc;
},
{}
);
const mapPatterns = (zodStringChecks) => {
const startsWith = mapStartsWith(zodStringChecks);
const endsWith = mapEndsWith(zodStringChecks);
const regex = mapRegex(zodStringChecks);
const includes = mapIncludes(zodStringChecks);
const patterns = [
...regex ?? [],
...startsWith ? [startsWith] : [],
...endsWith ? [endsWith] : [],
...includes ?? []
];
return patterns;
};
const mapStartsWith = (zodStringChecks) => {
var _a, _b;
if ((_b = (_a = zodStringChecks.startsWith) == null ? void 0 : _a[0]) == null ? void 0 : _b.value) {
return `^${zodStringChecks.startsWith[0].value}`;
}
return void 0;
};
const mapEndsWith = (zodStringChecks) => {
var _a, _b;
if ((_b = (_a = zodStringChecks.endsWith) == null ? void 0 : _a[0]) == null ? void 0 : _b.value) {
return `${zodStringChecks.endsWith[0].value}$`;
}
return void 0;
};
const mapRegex = (zodStringChecks) => {
var _a;
return (_a = zodStringChecks.regex) == null ? void 0 : _a.map((regexCheck) => regexCheck.regex.source);
};
const mapIncludes = (zodStringChecks) => {
var _a;
return (_a = zodStringChecks.includes) == null ? void 0 : _a.map((includeCheck) => {
if (includeCheck.position === 0) {
return `^${includeCheck.value}`;
}
if (includeCheck.position) {
return `^.{${includeCheck.position}}${includeCheck.value}`;
}
return includeCheck.value;
});
};
const mapStringFormat = (zodStringChecks) => {
var _a, _b, _c, _d;
if (zodStringChecks.uuid) {
return "uuid";
}
if (zodStringChecks.datetime) {
return "date-time";
}
if (zodStringChecks.date) {
return "date";
}
if (zodStringChecks.time) {
return "time";
}
if (zodStringChecks.duration) {
return "duration";
}
if (zodStringChecks.email) {
return "email";
}
if (zodStringChecks.url) {
return "uri";
}
if ((_a = zodStringChecks.ip) == null ? void 0 : _a.every((ip) => ip.version === "v4")) {
return "ipv4";
}
if ((_b = zodStringChecks.ip) == null ? void 0 : _b.every((ip) => ip.version === "v6")) {
return "ipv6";
}
if ((_c = zodStringChecks.cidr) == null ? void 0 : _c.every((ip) => ip.version === "v4")) {
return "ipv4";
}
if ((_d = zodStringChecks.cidr) == null ? void 0 : _d.every((ip) => ip.version === "v6")) {
return "ipv6";
}
return void 0;
};
const mapContentEncoding = (zodStringChecks) => {
if (zodStringChecks.base64) {
return "base64";
}
return void 0;
};
const createTupleSchema = (zodTuple, state) => {
const items = zodTuple.items;
const rest = zodTuple._def.rest;
const prefixItems = mapPrefixItems(items, state);
if (satisfiesVersion(state.components.openapi, "3.1.0")) {
if (!rest) {
return {
type: "schema",
schema: {
type: "array",
maxItems: items.length,
minItems: items.length,
...prefixItems && {
prefixItems: prefixItems.schemas.map((item) => item.schema)
}
},
effects: prefixItems == null ? void 0 : prefixItems.effects
};
}
const itemSchema = createSchemaObject(rest, state, ["tuple items"]);
return {
type: "schema",
schema: {
type: "array",
items: itemSchema.schema,
...prefixItems && {
prefixItems: prefixItems.schemas.map((item) => item.schema)
}
},
effects: flattenEffects([prefixItems == null ? void 0 : prefixItems.effects, itemSchema.effects])
};
}
if (!rest) {
return {
type: "schema",
schema: {
type: "array",
maxItems: items.length,
minItems: items.length,
...prefixItems && {
items: { oneOf: prefixItems.schemas.map((item) => item.schema) }
}
},
effects: prefixItems == null ? void 0 : prefixItems.effects
};
}
if (prefixItems) {
const restSchema = createSchemaObject(rest, state, ["tuple items"]);
return {
type: "schema",
schema: {
type: "array",
items: {
oneOf: [
...prefixItems.schemas.map((item) => item.schema),
restSchema.schema
]
}
},
effects: flattenEffects([restSchema.effects, prefixItems.effects])
};
}
return {
type: "schema",
schema: {
type: "array"
}
};
};
const mapPrefixItems = (items, state) => {
if (items.length) {
const schemas = items.map(
(item, index) => createSchemaObject(item, state, [`tuple item ${index}`])
);
return {
effects: flattenEffects(schemas.map((s) => s.effects)),
schemas
};
}
return void 0;
};
const createUnionSchema = (zodUnion, state) => {
var _a, _b, _c;
const schemas = zodUnion.options.reduce((acc, option, index) => {
if (!isOptionalObjectKey(option)) {
acc.push(createSchemaObject(option, state, [`union option ${index}`]));
}
return acc;
}, []);
if (((_b = (_a = zodUnion._def.zodOpenApi) == null ? void 0 : _a.openapi) == null ? void 0 : _b.unionOneOf) ?? ((_c = state.documentOptions) == null ? void 0 : _c.unionOneOf)) {
return {
type: "schema",
schema: {
oneOf: schemas.map((s) => s.schema)
},
effects: flattenEffects(schemas.map((s) => s.effects))
};
}
return {
type: "schema",
schema: {
anyOf: schemas.map((s) => s.schema)
},
effects: flattenEffects(schemas.map((s) => s.effects))
};
};
const createUnknownSchema = (_zodUnknown) => ({
type: "schema",
schema: {}
});
const createSchemaSwitch = (zodSchema, previous, state) => {
var _a, _b;
if ((_b = (_a = zodSchema._def.zodOpenApi) == null ? void 0 : _a.openapi) == null ? void 0 : _b.type) {
return createManualTypeSchema(zodSchema, state);
}
if (isZodType(zodSchema, "ZodString")) {
return createStringSchema(zodSchema, state);
}
if (isZodType(zodSchema, "ZodNumber")) {
return createNumberSchema(zodSchema, state);
}
if (isZodType(zodSchema, "ZodBoolean")) {
return createBooleanSchema();
}
if (isZodType(zodSchema, "ZodEnum")) {
return createEnumSchema(zodSchema);
}
if (isZodType(zodSchema, "ZodLiteral")) {
return createLiteralSchema(zodSchema, state);
}
if (isZodType(zodSchema, "ZodNativeEnum")) {
return createNativeEnumSchema(zodSchema, state);
}
if (isZodType(zodSchema, "ZodArray")) {
return createArraySchema(zodSchema, state);
}
if (isZodType(zodSchema, "ZodObject")) {
return createObjectSchema(zodSchema, previous, state);
}
if (isZodType(zodSchema, "ZodUnion")) {
return createUnionSchema(zodSchema, state);
}
if (isZodType(zodSchema, "ZodDiscriminatedUnion")) {
return createDiscriminatedUnionSchema(zodSchema, state);
}
if (isZodType(zodSchema, "ZodNull")) {
return createNullSchema();
}
if (isZodType(zodSchema, "ZodNullable")) {
return createNullableSchema(zodSchema, state);
}
if (isZodType(zodSchema, "ZodOptional")) {
return createOptionalSchema(zodSchema, state);
}
if (isZodType(zodSchema, "ZodReadonly")) {
return createReadonlySchema(zodSchema, state);
}
if (isZodType(zodSchema, "ZodDefault")) {
return createDefaultSchema(zodSchema, state, previous);
}
if (isZodType(zodSchema, "ZodRecord")) {
return createRecordSchema(zodSchema, state);
}
if (isZodType(zodSchema, "ZodTuple")) {
return createTupleSchema(zodSchema, state);
}
if (isZodType(zodSchema, "ZodDate")) {
return createDateSchema(zodSchema, state);
}
if (isZodType(zodSchema, "ZodPipeline")) {
return createPipelineSchema(zodSchema, state);
}
if (isZodType(zodSchema, "ZodEffects") && zodSchema._def.effect.type === "transform") {
return createTransformSchema(zodSchema, state);
}
if (isZodType(zodSchema, "ZodEffects") && zodSchema._def.effect.type === "preprocess") {
return createPreprocessSchema(zodSchema, state);
}
if (isZodType(zodSchema, "ZodEffects") && zodSchema._def.effect.type === "refinement") {
return createRefineSchema(zodSchema, state);
}
if (isZodType(zodSchema, "ZodNativeEnum")) {
return createNativeEnumSchema(zodSchema, state);
}
if (isZodType(zodSchema, "ZodIntersection")) {
return createIntersectionSchema(zodSchema, state);
}
if (isZodType(zodSchema, "ZodCatch")) {
return createCatchSchema(zodSchema, state, previous);
}
if (isZodType(zodSchema, "ZodUnknown") || isZodType(zodSchema, "ZodAny")) {
return createUnknownSchema();
}
if (isZodType(zodSchema, "ZodLazy")) {
return createLazySchema(zodSchema, state);
}
if (isZodType(zodSchema, "ZodBranded")) {
return createBrandedSchema(zodSchema, state);
}
if (isZodType(zodSchema, "ZodSet")) {
return createSetSchema(zodSchema, state);
}
if (isZodType(zodSchema, "ZodBigInt")) {
return createBigIntSchema();
}
return createManualTypeSchema(zodSchema, state);
};
const createNewSchema = ({
zodSchema,
previous,
state
}) => {
var _a;
if (state.visited.has(zodSchema)) {
throw new Error(
`The schema at ${state.path.join(
" > "
)} needs to be registered because it's circularly referenced`
);
}
state.visited.add(zodSchema);
const {
effectType,
param,
header,
ref,
refType,
unionOneOf,
...additionalMetadata
} = ((_a = zodSchema._def.zodOpenApi) == null ? void 0 : _a.openapi) ?? {};
const schema = createSchemaSwitch(zodSchema, previous, state);
const schemaWithMetadata = enhanceWithMetadata(
schema,
additionalMetadata,
state,
previous
);
state.visited.delete(zodSchema);
return schemaWithMetadata;
};
const createNewRef = ({
previous,
ref,
zodSchema,
state
}) => {
var _a;
state.components.schemas.set(zodSchema, {
type: "in-progress",
ref
});
const newSchema = createNewSchema({
zodSchema,
previous,
state: {
...state,
visited: /* @__PURE__ */ new Set()
}
});
state.components.schemas.set(zodSchema, {
type: "complete",
ref,
schemaObject: newSchema.schema,
effects: newSchema.effects
});
return {
type: "ref",
schema: {
$ref: createComponentSchemaRef(
ref,
(_a = state.documentOptions) == null ? void 0 : _a.componentRefPath
)
},
schemaObject: newSchema.schema,
effects: newSchema.effects ? [
{
type: "component",
zodType: zodSchema,
path: [...state.path]
}
] : void 0,
zodType: zodSchema
};
};
const createExistingRef = (zodSchema, component, state) => {
var _a, _b;
if (component && component.type === "complete") {
return {
type: "ref",
schema: {
$ref: createComponentSchemaRef(
component.ref,
(_a = state.documentOptions) == null ? void 0 : _a.componentRefPath
)
},
schemaObject: component.schemaObject,
effects: component.effects ? [
{
type: "component",
zodType: zodSchema,
path: [...state.path]
}
] : void 0,
zodType: zodSchema
};
}
if (component && component.type === "in-progress") {
return {
type: "ref",
schema: {
$ref: createComponentSchemaRef(
component.ref,
(_b = state.documentOptions) == null ? void 0 : _b.componentRefPath
)
},
schemaObject: void 0,
effects: [
{
type: "component",
zodType: zodSchema,
path: [...state.path]
}
],
zodType: zodSchema
};
}
return;
};
const createSchemaOrRef = (zodSchema, state, onlyRef) => {
var _a, _b, _c, _d;
const component = state.components.schemas.get(zodSchema);
const existingRef = createExistingRef(zodSchema, component, state);
if (existingRef) {
return existingRef;
}
const previous = ((_a = zodSchema._def.zodOpenApi) == null ? void 0 : _a[previousSymbol]) ? createSchemaOrRef(
zodSchema._def.zodOpenApi[previousSymbol],
state,
true
) : void 0;
const current = ((_b = zodSchema._def.zodOpenApi) == null ? void 0 : _b[currentSymbol]) && zodSchema._def.zodOpenApi[currentSymbol] !== zodSchema ? createSchemaOrRef(
zodSchema._def.zodOpenApi[currentSymbol],
state,
true
) : void 0;
const ref = ((_d = (_c = zodSchema._def.zodOpenApi) == null ? void 0 : _c.openapi) == null ? void 0 : _d.ref) ?? (component == null ? void 0 : component.ref);
if (ref) {
return current ? createNewSchema({ zodSchema, previous: current, state }) : createNewRef({ ref, zodSchema, previous, state });
}
if (onlyRef) {
return previous ?? current;
}
return createNewSchema({ zodSchema, previous: previous ?? current, state });
};
const createSchemaObject = (zodSchema, state, subpath) => {
state.path.push(...subpath);
const schema = createSchemaOrRef(zodSchema, state);
if (!schema) {
throw new Error("Schema does not exist");
}
state.path.pop();
return schema;
};
const createSchema = (zodSchema, state, subpath) => {
const schema = createSchemaObject(zodSchema, state, subpath);
if (schema.effects) {
verifyEffects(schema.effects, state);
}
return schema.schema;
};
const createMediaTypeSchema = (schemaObject, components, type, subpath, documentOptions) => {
if (!schemaObject) {
return void 0;
}
if (!isAnyZodType(schemaObject)) {
return schemaObject;
}
return createSchema(
schemaObject,
{
components,
type,
path: [],
visited: /* @__PURE__ */ new Set(),
documentOptions
},
subpath
);
};
const createMediaTypeObject = (mediaTypeObject, components, type, subpath, documentOptions) => {
if (!mediaTypeObject) {
return void 0;
}
return {
...mediaTypeObject,
schema: createMediaTypeSchema(
mediaTypeObject.schema,
components,
type,
[...subpath, "schema"],
documentOptions
)
};
};
const createContent = (contentObject, components, type, subpath, documentOptions) => Object.entries(contentObject).reduce(
(acc, [mediaType, zodOpenApiMediaTypeObject]) => {
const mediaTypeObject = createMediaTypeObject(
zodOpenApiMediaTypeObject,
components,
type,
[...subpath, mediaType],
documentOptions
);
if (mediaTypeObject) {
acc[mediaType] = mediaTypeObject;
}
return acc;
},
{}
);
const createComponentParamRef = (ref) => `#/components/parameters/${ref}`;
const createBaseParameter = (schema, components, subpath, documentOptions) => {
var _a, _b, _c, _d;
const { ref, ...rest } = ((_b = (_a = schema._def.zodOpenApi) == null ? void 0 : _a.openapi) == null ? void 0 : _b.param) ?? {};
const state = {
components,
type: "input",
path: [],
visited: /* @__PURE__ */ new Set(),
documentOptions
};
const schemaObject = createSchema(schema, state, [...subpath, "schema"]);
const required = !schema.isOptional();
const description = ((_d = (_c = schema._def.zodOpenApi) == null ? void 0 : _c.openapi) == null ? void 0 : _d.description) ?? schema._def.description;
return {
...description && { description },
...rest,
...sc