UNPKG

zod-openapi

Version:

Convert Zod Schemas to OpenAPI v3.x documentation

931 lines (914 loc) 35.6 kB
import { globalRegistry } from "zod/v4/core"; import { globalRegistry as globalRegistry$1, object, registry, toJSONSchema as toJSONSchema$1 } from "zod/v4"; //#region src/zod.ts const isAnyZodType = (schema) => typeof schema === "object" && schema !== null && "_zod" in schema; //#endregion //#region src/create/examples.ts const createExamples = (examples, registry$1, path) => { if (!examples) return; const examplesObject = {}; for (const [name, example] of Object.entries(examples)) examplesObject[name] = registry$1.addExample(example, [...path, name]); return examplesObject; }; //#endregion //#region src/create/content.ts const createMediaTypeObject = (mediaType, ctx, path) => { const { schema, examples, ...rest } = mediaType; const mediaTypeObject = rest; if (isAnyZodType(schema)) mediaTypeObject.schema = ctx.registry.addSchema(schema, [...path, "schema"], { io: ctx.io, source: { type: "mediaType" } }); else mediaTypeObject.schema = schema; if (examples) mediaTypeObject.examples = createExamples(examples, ctx.registry, [...path, "examples"]); return mediaTypeObject; }; const createContent = (content, ctx, path) => { const contentObject = {}; for (const [mediaType, mediaTypeObject] of Object.entries(content)) if (mediaTypeObject) contentObject[mediaType] = createMediaTypeObject(mediaTypeObject, ctx, [...path, mediaType]); return contentObject; }; //#endregion //#region src/create/object.ts const unwrapZodObject = (zodType, io, path) => { const def = zodType._zod.def; switch (def.type) { case "object": return zodType; case "lazy": return unwrapZodObject(def.getter(), io, path); case "pipe": if (io === "input") return unwrapZodObject(def.in, io, path); return unwrapZodObject(def.out, io, path); } throw new Error(`Failed to unwrap ZodObject from type: ${zodType._zod.def.type} at ${path.join(" > ")}`); }; const isRequired = (zodType, io) => { if (io === "input") return zodType._zod.optin === void 0; return zodType._zod.optout === void 0; }; //#endregion //#region src/create/headers.ts const createHeaders = (headers, registry$1, path) => { if (!headers) return; if (isAnyZodType(headers)) { const zodObject = unwrapZodObject(headers, "output", path); const headersObject = {}; for (const [key, zodSchema] of Object.entries(zodObject._zod.def.shape)) headersObject[key] = registry$1.addHeader(zodSchema, [...path, key]); return headersObject; } return headers; }; //#endregion //#region src/create/links.ts const createLinks = (links, registry$1, path) => { if (!links) return; const linksObject = {}; for (const [name, link] of Object.entries(links)) linksObject[name] = registry$1.addLink(link, [...path, name]); return linksObject; }; //#endregion //#region src/create/parameters.ts const createManualParameters = (parameters, registry$1, path) => { if (!parameters) return; const parameterObjects = []; for (const parameter of parameters) { if (isAnyZodType(parameter)) { const paramObject = registry$1.addParameter(parameter, [...path, "parameters"]); parameterObjects.push(paramObject); continue; } parameterObjects.push(parameter); } return parameterObjects; }; const createParameters = (requestParams, registry$1, path) => { if (!requestParams) return; const parameterObjects = []; for (const [location, schema] of Object.entries(requestParams ?? {})) { const zodObject = unwrapZodObject(schema, "input", path); for (const [name, zodSchema] of Object.entries(zodObject._zod.def.shape)) { const paramObject = registry$1.addParameter(zodSchema, [ ...path, location, name ], { location: { in: location, name } }); parameterObjects.push(paramObject); } } return parameterObjects; }; //#endregion //#region src/create/specificationExtension.ts const isISpecificationExtension = (key) => key.startsWith("x-"); //#endregion //#region src/create/callbacks.ts const createCallbacks = (callbacks, registry$1, path) => { if (!callbacks) return; const callbacksObject = {}; for (const [name, value] of Object.entries(callbacks)) { if (isISpecificationExtension(name)) { callbacksObject[name] = value; continue; } callbacksObject[name] = registry$1.addCallback(value, [...path, name]); } return callbacksObject; }; //#endregion //#region src/create/responses.ts const createResponses = (responses, registry$1, path) => { if (!responses) return; const responsesObject = {}; for (const [statusCode, response] of Object.entries(responses)) { if (!response) continue; if (isISpecificationExtension(statusCode)) { responsesObject[statusCode] = response; continue; } if ("$ref" in response) { responsesObject[statusCode] = response; continue; } responsesObject[statusCode] = registry$1.addResponse(response, [...path, statusCode]); } return responsesObject; }; //#endregion //#region src/create/paths.ts const createOperation = (operation, registry$1, path) => { const { parameters, requestParams, requestBody, responses, callbacks, ...rest } = operation; const operationObject = rest; const maybeManualParameters = createManualParameters(parameters, registry$1, [...path, "parameters"]); const maybeRequestParams = createParameters(requestParams, registry$1, [...path, "requestParams"]); if (maybeRequestParams || maybeManualParameters) operationObject.parameters = [...maybeRequestParams ?? [], ...maybeManualParameters ?? []]; const maybeRequestBody = requestBody && registry$1.addRequestBody(requestBody, path); if (maybeRequestBody) operationObject.requestBody = maybeRequestBody; const maybeResponses = createResponses(responses, registry$1, [...path, "responses"]); if (maybeResponses) operationObject.responses = maybeResponses; const maybeCallbacks = createCallbacks(callbacks, registry$1, [...path, "callbacks"]); if (maybeCallbacks) operationObject.callbacks = maybeCallbacks; return operationObject; }; const createPaths = (paths, registry$1, path) => { if (!paths) return; const pathsObject = {}; for (const [singlePath, pathItemObject] of Object.entries(paths)) { if (isISpecificationExtension(singlePath)) { pathsObject[singlePath] = pathItemObject; continue; } pathsObject[singlePath] = registry$1.addPathItem(pathItemObject, [...path, singlePath]); } return pathsObject; }; //#endregion //#region src/openapi.ts const openApiVersions = [ "3.0.0", "3.0.1", "3.0.2", "3.0.3", "3.1.0", "3.1.1" ]; const satisfiesVersion = (test, against) => openApiVersions.indexOf(test) >= openApiVersions.indexOf(against); //#endregion //#region src/create/schema/override.ts const override = (ctx) => { const def = ctx.zodSchema._zod.def; switch (def.type) { case "bigint": ctx.jsonSchema.type = "integer"; ctx.jsonSchema.format = "int64"; break; case "union": { if ("discriminator" in def && typeof def.discriminator === "string") { ctx.jsonSchema.oneOf ??= ctx.jsonSchema.anyOf; delete ctx.jsonSchema.anyOf; ctx.jsonSchema.type = "object"; ctx.jsonSchema.discriminator = { propertyName: def.discriminator }; const mapping = {}; for (const [index, obj] of Object.entries(ctx.jsonSchema.oneOf)) { const ref = obj.$ref; if (!ref) { delete ctx.jsonSchema.discriminator; return; } const discriminatorValues = def.options[Number(index)]._zod.propValues?.[def.discriminator]; if (!discriminatorValues?.size) return; for (const value of [...discriminatorValues ?? []]) { if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") return; mapping[String(value)] = ref; } } ctx.jsonSchema.discriminator.mapping = mapping; } const meta = globalRegistry$1.get(ctx.zodSchema); if (typeof meta?.unionOneOf === "boolean") { if (meta.unionOneOf) { ctx.jsonSchema.oneOf = ctx.jsonSchema.anyOf; delete ctx.jsonSchema.anyOf; } delete ctx.jsonSchema.unionOneOf; } break; } case "date": ctx.jsonSchema.type = "string"; break; case "literal": if (def.values.includes(void 0)) break; break; case "undefined": ctx.jsonSchema.not = {}; break; } }; const validate = (ctx, opts, previousContext) => { if (previousContext.context && ctx.zodSchema._zod.parent !== previousContext.context.zodSchema) { if (previousContext.context.zodSchema._zod.def.type === "pipe") throw new Error(`Zod transform found at ${previousContext.context.path.join(" > ")} are not supported in output schemas. Please use \`.overwrite()\` or wrap the schema in a \`.pipe()\` or assign it manual metadata with \`.meta()\``); throw new Error(`Zod schema of type \`${previousContext.context.zodSchema._zod.def.type}\` at ${previousContext.context?.path.join(" > ")} cannot be represented in OpenAPI. Please assign it metadata with \`.meta()\``); } previousContext.context = void 0; if (Object.keys(ctx.jsonSchema).length) return; const def = ctx.zodSchema._zod.def; const allowEmptySchema = opts.allowEmptySchema?.[def.type]; if (allowEmptySchema === true || allowEmptySchema?.[ctx.io]) return; switch (def.type) { case "optional": validate({ ...ctx, zodSchema: def.innerType }, opts, previousContext); return; case "any": return; case "unknown": return; case "pipe": if (ctx.io === "output") { if (!ctx.zodSchema._zod.parent) { previousContext.context = ctx; return; } } return; case "transform": if (ctx.io === "output") return; break; case "literal": if (def.values.includes(void 0)) throw new Error(`Zod literal at ${ctx.path.join(" > ")} cannot include \`undefined\` as a value. Please use \`z.undefined()\` or \`.optional()\` instead.`); return; case "custom": if (!ctx.zodSchema._zod.parent) { previousContext.context = ctx; return; } } throw new Error(`Zod schema of type \`${def.type}\` at ${ctx.path.join(" > ")} cannot be represented in OpenAPI. Please assign it metadata with \`.meta()\``); }; //#endregion //#region src/create/schema/rename.ts const renameComponents = (components, outputIds, ctx, refPath) => { const componentsToRename = /* @__PURE__ */ new Map(); if (ctx.io === "input") return componentsToRename; const componentDependencies = /* @__PURE__ */ new Map(); const stringifiedComponents = /* @__PURE__ */ new Map(); for (const [key, value] of Object.entries(components)) { const stringified = JSON.stringify(value); const regex = new RegExp(`"${refPath}([^"]+)"`, "g"); const matches = stringified.matchAll(regex); const dependencies = /* @__PURE__ */ new Set(); for (const match of matches) { const dep = match[1]; if (dep !== key) dependencies.add(dep); } stringifiedComponents.set(key, stringified); componentDependencies.set(key, { dependencies }); } for (const [key] of stringifiedComponents) { if (!ctx.registry.components.schemas.ids.get(key)) continue; if (isDependencyPure(componentDependencies, stringifiedComponents, ctx.registry, key)) continue; const newName = outputIds.get(key) ?? `${key}${ctx.opts.outputIdSuffix ?? "Output"}`; componentsToRename.set(key, newName); components[newName] = components[key]; delete components[key]; } return componentsToRename; }; const isDependencyPure = (componentDependencies, stringifiedComponents, registry$1, key, visited = /* @__PURE__ */ new Set()) => { if (visited.has(key)) return true; const dependencies = componentDependencies.get(key); if (dependencies.pure !== void 0) return dependencies.pure; const stringified = stringifiedComponents.get(key); const component = registry$1.components.schemas.ids.get(key); if (component && stringified !== JSON.stringify(component)) { dependencies.pure = false; return false; } visited.add(key); const result = [...dependencies.dependencies].every((dep) => isDependencyPure(componentDependencies, stringifiedComponents, registry$1, dep, new Set(visited))); dependencies.pure = result; return result; }; //#endregion //#region src/create/schema/schema.ts const createSchema = (schema, ctx = {}) => { ctx.registry ??= createRegistry({ schemas: ctx.schemaComponents }); ctx.opts ??= {}; ctx.io ??= "output"; const registrySchemas = Object.fromEntries(ctx.registry.components.schemas[ctx.io]); const schemas = { zodOpenApiCreateSchema: { zodType: schema } }; Object.assign(schemas, registrySchemas); const jsonSchemas = createSchemas(schemas, { registry: ctx.registry, io: ctx.io, opts: { ...ctx.opts, schemaRefPath: ctx.schemaRefPath }, openapiVersion: ctx.openapiVersion }); return { schema: jsonSchemas.schemas.zodOpenApiCreateSchema, components: jsonSchemas.components }; }; const zodOpenApiMetadataFields = [ "param", "header", "unusedIO", "override", "outputId" ]; const deleteZodOpenApiMeta = (jsonSchema) => { zodOpenApiMetadataFields.forEach((field) => { delete jsonSchema[field]; }); }; const deleteInvalidJsonSchemaFields = (jsonSchema) => { delete jsonSchema.$schema; delete jsonSchema.id; delete jsonSchema.$id; }; const createSchemas = (schemas, ctx) => { const refPath = ctx.opts.schemaRefPath ?? "#/components/schemas/"; const entries = {}; for (const [name, { zodType }] of Object.entries(schemas)) entries[name] = zodType; const zodRegistry = registry(); zodRegistry.add(object(entries), { id: "zodOpenApiCreateSchema" }); for (const [id, { zodType }] of ctx.registry.components.schemas.manual) zodRegistry.add(zodType, { id }); const outputIds = /* @__PURE__ */ new Map(); const defsName = satisfiesVersion(ctx.openapiVersion ?? "3.1.0", "3.1.0") ? "$defs" : "definitions"; const previousContext = {}; const jsonSchema = toJSONSchema$1(zodRegistry, { override(context) { const meta = globalRegistry$1.get(context.zodSchema); if (meta?.outputId && meta?.id) outputIds.set(meta.id, meta.outputId); if (context.jsonSchema.$ref) return; const enrichedContext = { ...context, io: ctx.io }; override(enrichedContext); if (typeof ctx.opts.override === "function") ctx.opts.override(enrichedContext); if (typeof meta?.override === "function") { meta.override(enrichedContext); delete context.jsonSchema.override; } if (typeof meta?.override === "object" && meta.override !== null) { Object.assign(context.jsonSchema, meta.override); delete context.jsonSchema.override; } deleteInvalidJsonSchemaFields(context.jsonSchema); deleteZodOpenApiMeta(context.jsonSchema); validate(enrichedContext, ctx.opts, previousContext); }, io: ctx.io, unrepresentable: "any", reused: ctx.opts.reused, cycles: ctx.opts.cycles, target: satisfiesVersion(ctx.openapiVersion ?? "3.1.0", "3.1.0") ? void 0 : "openapi-3.0", uri: (id) => id === "__shared" ? `#ZOD_OPENAPI/${id}` : `#ZOD_OPENAPI/__shared#/${defsName}/${id}` }); const components = jsonSchema.schemas.__shared?.[defsName] ?? {}; jsonSchema.schemas.__shared ??= { [defsName]: components }; const dynamicComponents = /* @__PURE__ */ new Map(); for (const [key, value] of Object.entries(components)) { deleteInvalidJsonSchemaFields(value); if (/^schema\d+$/.test(key)) { const newName = `__schema${ctx.registry.components.schemas.dynamicSchemaCount++}`; dynamicComponents.set(key, `"${refPath}${newName}"`); if (newName !== key) { components[newName] = value; delete components[key]; } } } for (const [key] of ctx.registry.components.schemas.manual) { const manualComponent = jsonSchema.schemas[key]; if (!manualComponent) continue; deleteInvalidJsonSchemaFields(manualComponent); } const manualUsed = {}; const parsedJsonSchema = JSON.parse(JSON.stringify(jsonSchema).replace(/"#ZOD_OPENAPI\/__shared#\/(?:\$defs|definitions)\/([^"]+)"/g, (_, match) => { const dynamic = dynamicComponents.get(match); if (dynamic) return dynamic; if (ctx.registry.components.schemas.manual.get(match)) manualUsed[match] = true; return `"${refPath}${match}"`; })); const parsedComponents = parsedJsonSchema.schemas.__shared?.[defsName] ?? {}; parsedJsonSchema.schemas.__shared ??= { [defsName]: parsedComponents }; for (const [key] of ctx.registry.components.schemas.manual) { const manualComponent = parsedJsonSchema.schemas[key]; if (!manualComponent) continue; if (manualUsed[key]) { if (parsedComponents[key]) throw new Error(`Component "${key}" is already registered as a component in the registry`); parsedComponents[key] = manualComponent; } } const componentsToRename = renameComponents(parsedComponents, outputIds, ctx, refPath); if (!componentsToRename.size) { const parsedSchemas = parsedJsonSchema.schemas.zodOpenApiCreateSchema?.properties; delete parsedJsonSchema.schemas.zodOpenApiCreateSchema; delete parsedJsonSchema.schemas.__shared; return { schemas: parsedSchemas, components: parsedComponents, manual: parsedJsonSchema.schemas }; } const renamedJsonSchema = JSON.parse(JSON.stringify(parsedJsonSchema).replace(new RegExp(`"${refPath}([^"]+)"`, "g"), (_, match) => { const replacement = componentsToRename.get(match); if (replacement) return `"${refPath}${replacement}"`; return `"${refPath}${match}"`; })); const renamedSchemas = renamedJsonSchema.schemas.zodOpenApiCreateSchema?.properties; const renamedComponents = renamedJsonSchema.schemas.__shared?.[defsName] ?? {}; delete renamedJsonSchema.schemas.zodOpenApiCreateSchema; delete renamedJsonSchema.schemas.__shared; return { schemas: renamedSchemas, components: renamedComponents, manual: renamedJsonSchema.schemas }; }; //#endregion //#region src/create/components.ts const createRegistry = (components) => { const registry$1 = { components: { schemas: { dynamicSchemaCount: 0, input: /* @__PURE__ */ new Map(), output: /* @__PURE__ */ new Map(), ids: /* @__PURE__ */ new Map(), manual: /* @__PURE__ */ new Map() }, headers: { ids: /* @__PURE__ */ new Map(), seen: /* @__PURE__ */ new WeakMap() }, requestBodies: { ids: /* @__PURE__ */ new Map(), seen: /* @__PURE__ */ new WeakMap() }, responses: { ids: /* @__PURE__ */ new Map(), seen: /* @__PURE__ */ new WeakMap() }, parameters: { ids: /* @__PURE__ */ new Map(), seen: /* @__PURE__ */ new WeakMap() }, callbacks: { ids: /* @__PURE__ */ new Map(), seen: /* @__PURE__ */ new WeakMap() }, pathItems: { ids: /* @__PURE__ */ new Map(), seen: /* @__PURE__ */ new WeakMap() }, securitySchemes: { ids: /* @__PURE__ */ new Map(), seen: /* @__PURE__ */ new WeakMap() }, links: { ids: /* @__PURE__ */ new Map(), seen: /* @__PURE__ */ new WeakMap() }, examples: { ids: /* @__PURE__ */ new Map(), seen: /* @__PURE__ */ new WeakMap() } }, addSchema: (schema, path, opts) => { const schemaObject = {}; registry$1.components.schemas[opts.io].set(path.join(" > "), { schemaObject, zodType: schema, source: { path, ...opts?.source } }); return schemaObject; }, addParameter: (parameter, path, opts) => { const seenParameter = registry$1.components.parameters.seen.get(parameter); if (seenParameter) return seenParameter; const meta = globalRegistry.get(parameter); const name = opts?.location?.name ?? meta?.param?.name; const inLocation = opts?.location?.in ?? meta?.param?.in; if (opts?.location?.name && meta?.param?.name || opts?.location?.in && meta?.param?.in) throw new Error(`Parameter at ${path.join(" > ")} has both \`.meta({ param: { name, in } })\` and \`.meta({ param: { location: { in, name } } })\` information`); if (!name || !inLocation) throw new Error(`Parameter at ${path.join(" > ")} is missing \`.meta({ param: { name, in } })\` information`); const schemaObject = registry$1.addSchema(parameter, [ ...path, inLocation, name, "schema" ], { io: "input", source: { type: "parameter", location: { in: inLocation, name } } }); const { id: metaId, examples, ...rest } = meta?.param ?? {}; const parameterObject = { in: inLocation, name, schema: schemaObject, ...rest }; const examplesObject = createExamples(examples, registry$1, [ ...path, inLocation, name, "examples" ]); if (examplesObject) parameterObject.examples = examplesObject; if (isRequired(parameter, "input")) parameterObject.required = true; if (!parameterObject.description && meta?.description) parameterObject.description = meta.description; const id = metaId ?? opts?.manualId; if (id) { if (registry$1.components.parameters.ids.has(id)) throw new Error(`Schema "${id}" at ${path.join(" > ")} is already registered`); const ref = { $ref: `#/components/parameters/${id}` }; registry$1.components.parameters.seen.set(parameter, ref); registry$1.components.parameters.ids.set(id, parameterObject); if (opts?.manualId) return parameterObject; return ref; } if (opts?.location?.name || opts?.location?.in) return parameterObject; registry$1.components.parameters.seen.set(parameter, parameterObject); return parameterObject; }, addHeader: (header, path, opts) => { const seenHeader = registry$1.components.headers.seen.get(header); if (seenHeader) return seenHeader; const meta = globalRegistry.get(header); const { id: metaId, ...rest } = meta?.header ?? {}; const id = metaId ?? opts?.manualId; const headerObject = rest; if (isRequired(header, "output")) headerObject.required = true; if (!headerObject.description && meta?.description) headerObject.description = meta.description; headerObject.schema = registry$1.addSchema(header, [...path, "schema"], { io: "output", source: { type: "header" } }); if (id) { if (registry$1.components.schemas.ids.has(id)) throw new Error(`Schema "${id}" at ${path.join(" > ")} is already registered`); const ref = { $ref: `#/components/headers/${id}` }; registry$1.components.headers.ids.set(id, headerObject); registry$1.components.headers.seen.set(header, ref); if (opts?.manualId) return headerObject; return ref; } registry$1.components.headers.seen.set(header, headerObject); return headerObject; }, addRequestBody: (requestBody, path, opts) => { const seenRequestBody = registry$1.components.requestBodies.seen.get(requestBody); if (seenRequestBody) return seenRequestBody; const { content, id: metaId, ...rest } = requestBody; const requestBodyObject = { ...rest, content: createContent(content, { registry: registry$1, io: "input" }, [...path, "content"]) }; const id = metaId ?? opts?.manualId; if (id) { if (registry$1.components.requestBodies.ids.has(id)) throw new Error(`RequestBody "${id}" at ${path.join(" > ")} is already registered`); const ref = { $ref: `#/components/requestBodies/${id}` }; registry$1.components.requestBodies.ids.set(id, requestBodyObject); registry$1.components.requestBodies.seen.set(requestBody, ref); if (opts?.manualId) return requestBodyObject; return ref; } registry$1.components.requestBodies.seen.set(requestBody, requestBodyObject); return requestBodyObject; }, addPathItem: (pathItem, path, opts) => { const seenPathItem = registry$1.components.pathItems.seen.get(pathItem); if (seenPathItem) return seenPathItem; const pathItemObject = {}; const { id: metaId, ...rest } = pathItem; const id = metaId ?? opts?.manualId; for (const [key, value] of Object.entries(rest)) { if (isISpecificationExtension(key)) { pathItemObject[key] = value; continue; } if (key === "get" || key === "put" || key === "post" || key === "delete" || key === "options" || key === "head" || key === "patch" || key === "trace") { pathItemObject[key] = createOperation(value, registry$1, [...path, key]); continue; } if (key === "parameters") { pathItemObject[key] = createManualParameters(value, registry$1, [...path, key]); continue; } pathItemObject[key] = value; } if (id) { if (registry$1.components.pathItems.ids.has(id)) throw new Error(`PathItem "${id}" at ${path.join(" > ")} is already registered`); const ref = { $ref: `#/components/pathItems/${id}` }; registry$1.components.pathItems.ids.set(id, pathItemObject); registry$1.components.pathItems.seen.set(pathItem, ref); if (opts?.manualId) return pathItemObject; return ref; } registry$1.components.pathItems.seen.set(pathItem, pathItemObject); return pathItemObject; }, addResponse: (response, path, opts) => { const seenResponse = registry$1.components.responses.seen.get(response); if (seenResponse) return seenResponse; const { content, headers, links, id: metaId, ...rest } = response; const responseObject = rest; const maybeHeaders = createHeaders(headers, registry$1, [...path, "headers"]); if (maybeHeaders) responseObject.headers = maybeHeaders; if (content) responseObject.content = createContent(content, { registry: registry$1, io: "output" }, [...path, "content"]); if (links) responseObject.links = createLinks(links, registry$1, [...path, "links"]); const id = metaId ?? opts?.manualId; if (id) { if (registry$1.components.responses.ids.has(id)) throw new Error(`Response "${id}" at ${path.join(" > ")} is already registered`); const ref = { $ref: `#/components/responses/${id}` }; registry$1.components.responses.ids.set(id, responseObject); registry$1.components.responses.seen.set(response, ref); if (opts?.manualId) return responseObject; return ref; } registry$1.components.responses.seen.set(response, responseObject); return responseObject; }, addCallback: (callback, path, opts) => { const seenCallback = registry$1.components.callbacks.seen.get(callback); if (seenCallback) return seenCallback; const { id: metaId, ...rest } = callback; const callbackObject = {}; for (const [name, pathItem] of Object.entries(rest)) { if (isISpecificationExtension(name)) { callbackObject[name] = pathItem; continue; } callbackObject[name] = registry$1.addPathItem(pathItem, [...path, name]); } const id = metaId ?? opts?.manualId; if (id) { if (registry$1.components.callbacks.ids.has(id)) throw new Error(`Callback "${id}" at ${path.join(" > ")} is already registered`); const ref = { $ref: `#/components/callbacks/${id}` }; registry$1.components.callbacks.ids.set(id, callbackObject); registry$1.components.callbacks.seen.set(callback, ref); if (opts?.manualId) return callbackObject; return ref; } registry$1.components.callbacks.seen.set(callback, callbackObject); return callbackObject; }, addSecurityScheme: (securityScheme, path, opts) => { const seenSecurityScheme = registry$1.components.securitySchemes.seen.get(securityScheme); if (seenSecurityScheme) return seenSecurityScheme; const { id: metaId, ...rest } = securityScheme; const securitySchemeObject = rest; const id = metaId ?? opts?.manualId; if (id) { if (registry$1.components.securitySchemes.ids.has(id)) throw new Error(`SecurityScheme "${id}" at ${path.join(" > ")} is already registered`); const ref = { $ref: `#/components/securitySchemes/${id}` }; registry$1.components.securitySchemes.ids.set(id, securitySchemeObject); registry$1.components.securitySchemes.seen.set(securityScheme, ref); if (opts?.manualId) return securitySchemeObject; return ref; } registry$1.components.securitySchemes.seen.set(securityScheme, securitySchemeObject); return securitySchemeObject; }, addLink: (link, path, opts) => { const seenLink = registry$1.components.links.seen.get(link); if (seenLink) return seenLink; const { id: metaId, ...rest } = link; const linkObject = rest; const id = metaId ?? opts?.manualId; if (id) { if (registry$1.components.links.ids.has(id)) throw new Error(`Link "${id}" at ${path.join(" > ")} is already registered`); const ref = { $ref: `#/components/links/${id}` }; registry$1.components.links.ids.set(id, linkObject); registry$1.components.links.seen.set(link, ref); if (opts?.manualId) return linkObject; return ref; } registry$1.components.links.seen.set(link, linkObject); return linkObject; }, addExample: (example, path, opts) => { const seenExample = registry$1.components.examples.seen.get(example); if (seenExample) return seenExample; const { id: metaId, ...rest } = example; const exampleObject = rest; const id = metaId ?? opts?.manualId; if (id) { if (registry$1.components.examples.ids.has(id)) throw new Error(`Example "${id}" at ${path.join(" > ")} is already registered`); const ref = { $ref: `#/components/examples/${id}` }; registry$1.components.examples.ids.set(id, exampleObject); registry$1.components.examples.seen.set(example, ref); if (opts?.manualId) return exampleObject; return ref; } registry$1.components.examples.seen.set(example, exampleObject); return exampleObject; } }; registerSchemas(components?.schemas, registry$1); registerParameters(components?.parameters, registry$1); registerHeaders(components?.headers, registry$1); registerResponses(components?.responses, registry$1); registerPathItems(components?.pathItems, registry$1); registerRequestBodies(components?.requestBodies, registry$1); registerCallbacks(components?.callbacks, registry$1); registerSecuritySchemes(components?.securitySchemes, registry$1); registerLinks(components?.links, registry$1); registerExamples(components?.examples, registry$1); return registry$1; }; const registerSchemas = (schemas, registry$1) => { if (!schemas) return; for (const [key, schema] of Object.entries(schemas)) { if (isAnyZodType(schema)) { const id = globalRegistry.get(schema)?.id ?? key; registry$1.components.schemas.manual.set(id, { input: { schemaObject: {} }, output: { schemaObject: {} }, zodType: schema }); continue; } registry$1.components.schemas.ids.set(key, schema); } }; const registerParameters = (parameters, registry$1) => { if (!parameters) return; for (const [key, schema] of Object.entries(parameters)) { if (isAnyZodType(schema)) { const path = [ "components", "parameters", key ]; registry$1.addParameter(schema, path, { manualId: key }); continue; } registry$1.components.parameters.ids.set(key, schema); } }; const registerHeaders = (headers, registry$1) => { if (!headers) return; for (const [key, schema] of Object.entries(headers)) { if (isAnyZodType(schema)) { const path = [ "components", "headers", key ]; registry$1.addHeader(schema, path, { manualId: key }); continue; } registry$1.components.headers.ids.set(key, schema); } }; const registerResponses = (responses, registry$1) => { if (!responses) return; for (const [key, schema] of Object.entries(responses)) { const responseObject = registry$1.addResponse(schema, [ "components", "responses", key ], { manualId: key }); registry$1.components.responses.ids.set(key, responseObject); registry$1.components.responses.seen.set(schema, responseObject); } }; const registerRequestBodies = (requestBodies, registry$1) => { if (!requestBodies) return; for (const [key, schema] of Object.entries(requestBodies)) { if (isAnyZodType(schema)) { registry$1.addRequestBody(schema, [ "components", "requestBodies", key ], { manualId: key }); continue; } registry$1.components.requestBodies.ids.set(key, schema); } }; const registerCallbacks = (callbacks, registry$1) => { if (!callbacks) return; for (const [key, schema] of Object.entries(callbacks)) registry$1.addCallback(schema, [ "components", "callbacks", key ], { manualId: key }); }; const registerPathItems = (pathItems, registry$1) => { if (!pathItems) return; for (const [key, schema] of Object.entries(pathItems)) registry$1.addPathItem(schema, [ "components", "pathItems", key ], { manualId: key }); }; const registerSecuritySchemes = (securitySchemes, registry$1) => { if (!securitySchemes) return; for (const [key, schema] of Object.entries(securitySchemes)) registry$1.addSecurityScheme(schema, [ "components", "securitySchemes", key ], { manualId: key }); }; const registerLinks = (links, registry$1) => { if (!links) return; for (const [key, schema] of Object.entries(links)) registry$1.addLink(schema, [ "components", "links", key ], { manualId: key }); }; const registerExamples = (examples, registry$1) => { if (!examples) return; for (const [key, schema] of Object.entries(examples)) registry$1.components.examples.ids.set(key, schema); }; const createIOSchemas = (ctx) => { const { schemas, components, manual } = createSchemas(Object.fromEntries(ctx.registry.components.schemas[ctx.io]), ctx); for (const [key, schema] of Object.entries(components)) ctx.registry.components.schemas.ids.set(key, schema); for (const [key, schema] of Object.entries(schemas)) { const ioSchema = ctx.registry.components.schemas[ctx.io].get(key); if (ioSchema) Object.assign(ioSchema.schemaObject, schema); } for (const [key, value] of Object.entries(manual)) { const manualSchema = ctx.registry.components.schemas.manual.get(key); if (!manualSchema) continue; if (components[key]) manualSchema[ctx.io].used = true; Object.assign(manualSchema[ctx.io].schemaObject, value); } }; const createManualSchemas = (registry$1) => { for (const [key, value] of registry$1.components.schemas.manual) if (!value.input.used) { const schema = value[globalRegistry.get(value.zodType)?.unusedIO ?? "output"].schemaObject; registry$1.components.schemas.ids.set(key, schema); } }; const createComponents = (registry$1, opts, openapiVersion) => { createIOSchemas({ registry: registry$1, io: "input", opts, openapiVersion }); createIOSchemas({ registry: registry$1, io: "output", opts, openapiVersion }); createManualSchemas(registry$1); const components = {}; if (registry$1.components.schemas.ids.size > 0) components.schemas = Object.fromEntries(registry$1.components.schemas.ids); if (registry$1.components.headers.ids.size > 0) components.headers = Object.fromEntries(registry$1.components.headers.ids); if (registry$1.components.requestBodies.ids.size > 0) components.requestBodies = Object.fromEntries(registry$1.components.requestBodies.ids); if (registry$1.components.responses.ids.size > 0) components.responses = Object.fromEntries(registry$1.components.responses.ids); if (registry$1.components.parameters.ids.size > 0) components.parameters = Object.fromEntries(registry$1.components.parameters.ids); if (registry$1.components.callbacks.ids.size > 0) components.callbacks = Object.fromEntries(registry$1.components.callbacks.ids); if (registry$1.components.pathItems.ids.size > 0) components.pathItems = Object.fromEntries(registry$1.components.pathItems.ids); if (registry$1.components.securitySchemes.ids.size > 0) components.securitySchemes = Object.fromEntries(registry$1.components.securitySchemes.ids); if (registry$1.components.links.ids.size > 0) components.links = Object.fromEntries(registry$1.components.links.ids); if (registry$1.components.examples.ids.size > 0) components.examples = Object.fromEntries(registry$1.components.examples.ids); return components; }; //#endregion export { unwrapZodObject as a, createPaths as i, createRegistry as n, isAnyZodType as o, createSchema as r, createComponents as t };