UNPKG

oas

Version:

Comprehensive tooling for working with OpenAPI definitions

1,401 lines (1,390 loc) 55.1 kB
import { PARAMETER_ORDERING, getExtension } from "./chunk-S27IGTVG.js"; import { isOpenAPI30, isRef, isSchema } from "./chunk-PSNTODZL.js"; // src/lib/matches-mimetype.ts function matchesMediaType(types2, mediaType) { return types2.some((type) => { return mediaType.indexOf(type) > -1; }); } var matches_mimetype_default = { formUrlEncoded: (mimeType) => { return matchesMediaType(["application/x-www-form-urlencoded"], mimeType); }, json: (contentType) => { return matchesMediaType( ["application/json", "application/x-json", "text/json", "text/x-json", "+json"], contentType ); }, multipart: (contentType) => { return matchesMediaType( ["multipart/mixed", "multipart/related", "multipart/form-data", "multipart/alternative"], contentType ); }, wildcard: (contentType) => { return contentType === "*/*"; }, xml: (contentType) => { return matchesMediaType( [ "application/xml", "application/xml-external-parsed-entity", "application/xml-dtd", "text/xml", "text/xml-external-parsed-entity", "+xml" ], contentType ); } }; // src/lib/get-parameter-content-type.ts function getParameterContentType(contentKeys) { if (contentKeys.length === 0) { return void 0; } if (contentKeys.length === 1) { return contentKeys[0]; } const jsonLikeContentTypes = contentKeys.filter((k) => matches_mimetype_default.json(k)); if (jsonLikeContentTypes.length) { return jsonLikeContentTypes[0]; } return contentKeys[0]; } // src/lib/refs.ts import jsonpointer from "jsonpointer"; // src/lib/helpers.ts function hasSchemaType(schema, discriminator) { if (Array.isArray(schema.type)) { return schema.type.includes(discriminator); } return schema.type === discriminator; } function isObject(val) { return typeof val === "object" && val !== null && !Array.isArray(val); } function isPrimitive(val) { return typeof val === "string" || typeof val === "number" || typeof val === "boolean"; } // src/lib/refs.ts function decorateComponentSchemasWithRefName(api) { if (!api?.components?.schemas || typeof api.components.schemas !== "object") { return; } Object.keys(api.components.schemas).forEach((schemaName) => { if (isPrimitive(api.components?.schemas?.[schemaName]) || Array.isArray(api.components?.schemas?.[schemaName]) || api.components?.schemas?.[schemaName] === null) { return; } (api.components?.schemas?.[schemaName])["x-readme-ref-name"] = schemaName; }); } function encodePointer(str) { return str.replaceAll("~", "~0").replaceAll("/", "~1"); } function decodePointer(str) { return str.replace(/~([01])/g, (_, digit) => digit === "0" ? "~" : "/"); } function findRef($ref, definition) { let currRef = $ref.trim(); if (currRef === "") { return false; } if (currRef.startsWith("#")) { currRef = decodeURIComponent(currRef.substring(1)); } else { throw new Error(`Could not find a definition for ${$ref}.`); } const current = jsonpointer.get(definition, currRef); if (current === void 0) { throw new Error(`Could not find a definition for ${$ref}.`); } return current; } function dereferenceRef(value, definition, seenRefs = /* @__PURE__ */ new Set()) { if (value === void 0) { return void 0; } if (isRef(value)) { if (!definition) { return value; } const ref = value.$ref; if (seenRefs.has(ref)) { return value; } seenRefs.add(ref); try { const dereferenced = findRef(ref, definition); if (isRef(dereferenced)) { return dereferenceRef(dereferenced, definition, seenRefs); } return { ...dereferenced }; } catch { return value; } } return value; } function dereferenceRefDeep(value, definition, seenRefs = /* @__PURE__ */ new Set()) { if (value === null || value === void 0) return value; if (typeof value !== "object") return value; if (isRef(value)) { if (!definition) return value; const resolved = dereferenceRef(value, definition, seenRefs); if (isRef(resolved)) return resolved; return dereferenceRefDeep(resolved, definition, seenRefs); } if (Array.isArray(value)) { return value.map((entry) => dereferenceRefDeep(entry, definition, seenRefs)); } const out = {}; for (const key of Object.keys(value)) { out[key] = dereferenceRefDeep(value[key], definition, seenRefs); } return out; } function getDereferencingOptions(circularRefs) { return { resolve: { // We shouldn't be resolving external pointers at this point so just ignore them. external: false }, dereference: { // If circular `$refs` are ignored they'll remain in the schema as `$ref: String`, otherwise // `$ref` just won't exist. This, in tandem with `onCircular`, allows us to do easy and // accumulate a list of circular references. circular: "ignore", onCircular: (path) => { circularRefs.add(`#${path.split("#")[1]}`); } } }; } function collectRefsInSchema(schema) { const refs = /* @__PURE__ */ new Set(); if (!schema || typeof schema !== "object") return refs; const obj = schema; if (isRef(obj)) { refs.add(obj.$ref); } for (const value of Object.values(obj)) { if (Array.isArray(value)) { for (const item of value) { for (const r of collectRefsInSchema(item)) refs.add(r); } } else if (value && typeof value === "object") { for (const r of collectRefsInSchema(value)) refs.add(r); } } return refs; } function filterRequiredRefsToReferenced(requiredRefs, usedSchemas) { const referenced = new Set(requiredRefs); let prevSize = 0; while (referenced.size > prevSize) { prevSize = referenced.size; for (const ref of referenced) { const schema = usedSchemas.get(ref); if (schema) { for (const r of collectRefsInSchema(schema)) { referenced.add(r); } } } } const filtered = /* @__PURE__ */ new Map(); for (const ref of referenced) { const s = usedSchemas.get(ref); if (s !== void 0) { filtered.set(ref, s); } } return filtered; } function getRefPathSegments(ref) { if (!ref.startsWith("#/")) return null; const path = ref.slice(2).split("/").map((seg) => { return decodeURIComponent(decodePointer(seg)); }); if (path.length < 2) { return null; } return path; } function isArrayIndexSegment(seg) { return /^\d+$/.test(seg); } function childShouldBeSchemaArray(parentKey, childSeg) { if (!childSeg || !isArrayIndexSegment(childSeg)) { return false; } return parentKey === "allOf" || parentKey === "anyOf" || parentKey === "oneOf"; } function mergeReferencedSchemasIntoRoot(root, refToSchema) { const sortedEntries = [...refToSchema.entries()].toSorted((a, b) => { const aDepth = a[0].split("/").length; const bDepth = b[0].split("/").length; return aDepth - bDepth; }); for (const [ref, schema] of sortedEntries) { const segments = getRefPathSegments(ref); if (!segments || segments.length === 0) { continue; } let current = root; let pathInvalid = false; for (let i = 0; i < segments.length - 1; i++) { const seg = segments[i]; const nextSeg = segments[i + 1]; if (Array.isArray(current)) { const idx = Number(seg); if (!Number.isInteger(idx) || idx < 0) { pathInvalid = true; break; } const slot = current[idx]; if (slot === void 0 || slot === null || typeof slot !== "object" || Array.isArray(slot)) { const nextObj = {}; current[idx] = nextObj; current = nextObj; } else { current = slot; } continue; } const cur = current; const existing = cur[seg]; if (childShouldBeSchemaArray(seg, nextSeg)) { if (!Array.isArray(existing)) { cur[seg] = []; } current = cur[seg]; continue; } let next; if (existing !== void 0 && existing !== null && typeof existing === "object" && !Array.isArray(existing)) { next = existing; } else { next = {}; cur[seg] = next; } current = next; } if (pathInvalid) { continue; } const lastSeg = segments[segments.length - 1]; if (Array.isArray(current)) { const idx = Number(lastSeg); if (!Number.isInteger(idx) || idx < 0) { continue; } current[idx] = schema; } else { current[lastSeg] = schema; } } } // src/lib/clone-object.ts function cloneObject(obj) { if (typeof obj === "undefined") { return void 0; } return JSON.parse(JSON.stringify(obj)); } // src/lib/build-discriminator-one-of.ts function hasDiscriminatorWithoutPolymorphism(schema) { if (!schema || typeof schema !== "object") return false; if (!("discriminator" in schema)) return false; if ("oneOf" in schema || "anyOf" in schema) return false; return true; } function allOfReferencesSchema(schema, targetSchemaName) { if (!schema || typeof schema !== "object") return false; if (!("allOf" in schema) || !Array.isArray(schema.allOf)) return false; return schema.allOf.some((item) => { if (isRef(item)) { const refParts = item.$ref.split("/"); const refSchemaName = refParts[refParts.length - 1]; return refSchemaName === targetSchemaName; } return false; }); } function findDiscriminatorChildren(definition) { const childrenMap = /* @__PURE__ */ new Map(); const childrenRefMap = /* @__PURE__ */ new Map(); if (!definition?.components?.schemas || typeof definition.components.schemas !== "object") { return { children: childrenMap, refs: childrenRefMap }; } const schemas = definition.components.schemas; const schemaNames = Object.keys(schemas); const discriminatorSchemas = schemaNames.filter((name) => { return hasDiscriminatorWithoutPolymorphism(schemas[name]); }); for (const baseName of discriminatorSchemas) { const baseSchema = schemas[baseName]; const discriminator = baseSchema.discriminator; let childSchemaNames = []; if (discriminator.mapping && typeof discriminator.mapping === "object") { const mappingRefs = Object.values(discriminator.mapping); if (mappingRefs.length > 0) { childSchemaNames = mappingRefs.map((ref) => ref.split("/").pop()).filter((name) => { if (!name) return false; const childSchema = schemas[name]; return !!childSchema && allOfReferencesSchema(childSchema, baseName); }); } } if (!childSchemaNames.length) { childSchemaNames = schemaNames.filter((name) => { if (name === baseName) return false; return allOfReferencesSchema(schemas[name], baseName); }); } if (childSchemaNames.length) { for (const childName of childSchemaNames) { childrenRefMap.set(childName, `#/components/schemas/${childName}`); } childrenMap.set(baseName, childSchemaNames); childrenRefMap.set(baseName, `#/components/schemas/${baseName}`); } } return { children: childrenMap, refs: childrenRefMap }; } function applyDiscriminatorOneOfToUsedSchemas(definition, usedSchemas, getOrAddSchema) { const { children: childrenMap, refs: childrenRefMap } = findDiscriminatorChildren(definition); if (!childrenMap.size) return; for (const [baseName, childNames] of childrenMap) { const baseRef = childrenRefMap.get(baseName); if (!baseRef) continue; const baseSchema = usedSchemas.get(baseRef); if (!baseSchema || typeof baseSchema !== "object") continue; const oneOf = []; for (const childName of childNames) { const childRef = childrenRefMap.get(childName); if (!childRef) continue; const childSchema = getOrAddSchema(childRef); if (childSchema) { oneOf.push({ $ref: childRef }); } } if (oneOf.length > 0) { baseSchema.oneOf = oneOf; } } for (const [parentSchemaName, childNames] of childrenMap) { for (const childName of childNames) { const childRef = childrenRefMap.get(childName); if (!childRef) continue; const childSchema = usedSchemas.get(childRef); if (!childSchema || !("allOf" in childSchema) || !Array.isArray(childSchema.allOf)) { continue; } for (let i = 0; i < childSchema.allOf.length; i++) { const item = childSchema.allOf[i]; if (item && typeof item === "object" && "x-readme-ref-name" in item && item["x-readme-ref-name"] === parentSchemaName && "oneOf" in item) { const clonedItem = cloneObject(item); delete clonedItem.oneOf; childSchema.allOf[i] = clonedItem; } } } } } // src/lib/openapi-to-json-schema.ts import mergeJSONSchemaAllOf from "json-schema-merge-allof"; import jsonpointer2 from "jsonpointer"; import removeUndefinedObjects from "remove-undefined-objects"; var UNSUPPORTED_SCHEMA_PROPS = [ "example", // OpenAPI supports `example` but we're mapping it to `examples` in this library. "externalDocs", "xml" ]; var METADATA_SIBLING_KEYS = /* @__PURE__ */ new Set(["description", "summary", "title"]); var mergeAllOfSchemasOptions = { ignoreAdditionalProperties: true, resolvers: { // `merge-json-schema-allof` by default takes the first `description` when you're merging an // `allOf` but because generally when you're merging two schemas together with an `allOf` you // want data in the subsequent schemas to be applied to the first and `description` should be a // part of that. description: (obj) => { return obj.slice(-1)[0]; }, // `merge-json-schema-allof` doesn't support merging enum arrays but since that's a safe and // simple operation as enums always contain primitives we can handle it ourselves with a custom // resolver. We intersect the arrays so that child schemas can narrow a parent's broad enum // (e.g. [1,2,20,50] ∩ [1] = [1]). // // We unfortunately need to cast our return value as `any[]` because the internal types of // `merge-json-schema-allof`'s `enum` resolver are not portable. enum: (obj) => { const arrays = obj; const intersection = arrays.reduce((acc, e) => acc.filter((v) => e.includes(v))); return intersection.length > 0 ? intersection : arrays.reduce((acc, e) => acc.concat(e), []); }, // For any unknown keywords (e.g., `example`, `format`, `x-readme-ref-name`), we fallback to // using the `title` resolver (which uses the first value found). // https://github.com/mokkabonna/json-schema-merge-allof/blob/ea2e48ee34415022de5a50c236eb4793a943ad11/src/index.js#L292 // https://github.com/mokkabonna/json-schema-merge-allof/blob/ea2e48ee34415022de5a50c236eb4793a943ad11/README.md?plain=1#L147 defaultResolver: mergeJSONSchemaAllOf.options.resolvers.title } }; var PENDING_SCHEMA = { __pending: true }; function isPendingSchema(s) { return isObject(s) && "__pending" in s && s.__pending === true; } function getSchemaVersionString(schema, api) { if (isOpenAPI30(api)) { return "http://json-schema.org/draft-04/schema#"; } if (schema.$schema) { return schema.$schema; } if (api.jsonSchemaDialect) { return api.jsonSchemaDialect; } return "https://json-schema.org/draft/2020-12/schema#"; } function isPolymorphicSchema(schema) { return "allOf" in schema || "anyOf" in schema || "oneOf" in schema; } function shouldFoldParentItemsIntoPolymorphBranch(item) { if (!isObject(item)) return false; if (isRef(item)) return true; const branch = item; if (!("type" in branch) || branch.type === void 0) return true; if (!hasSchemaType(branch, "array")) return false; return !("items" in branch) || branch.items === void 0; } function isEmptyPolymorphicSchema(list) { if (!Array.isArray(list)) return false; if (!list.length) return true; return list.every((branch) => { if (branch === null || branch === void 0) return true; if (typeof branch !== "object" || Array.isArray(branch)) return false; return Array.isArray(branch) ? !branch.length : !Object.keys(branch).length; }); } function inlinePropertyRefsForMerge(schema, usedSchemas, refLogger, conflictPaths, currentPath) { const out = structuredClone(schema); if (!("properties" in out) || typeof out.properties !== "object" || out.properties === null) { return out; } for (const key of Object.keys(out.properties)) { const path = currentPath ? `${currentPath}.${key}` : key; const val = out.properties[key]; if (isRef(val)) { if (val.$ref.startsWith("#/paths/")) { refLogger(val.$ref, "ref"); continue; } const resolved = usedSchemas.get(val.$ref); if (resolved !== void 0 && !isPendingSchema(resolved)) { const isConflictPath = conflictPaths?.has(path) ?? false; const inlined = isConflictPath ? inlinePropertyRefsForMerge(structuredClone(resolved), usedSchemas, refLogger, conflictPaths, path) : structuredClone(resolved); const siblings = {}; for (const siblingKey of Object.keys(val)) { if (METADATA_SIBLING_KEYS.has(siblingKey) || siblingKey.startsWith("x-")) { siblings[siblingKey] = val[siblingKey]; } } out.properties[key] = { ...inlined, ...siblings }; } } else if (val && typeof val === "object" && !Array.isArray(val)) { if ("properties" in val) { out.properties[key] = inlinePropertyRefsForMerge( val, usedSchemas, refLogger, conflictPaths, path ); } inlineAllOfRefsDeep(out.properties[key] ?? val, usedSchemas); } } return out; } function collectConflictPaths(allOfBranches, usedSchemas) { const pathBranchCount = /* @__PURE__ */ new Map(); const pathsSeenInThisBranch = /* @__PURE__ */ new Set(); const refsCurrentlyResolving = /* @__PURE__ */ new Set(); function recordPath(path) { if (pathsSeenInThisBranch.has(path)) return; pathsSeenInThisBranch.add(path); pathBranchCount.set(path, (pathBranchCount.get(path) ?? 0) + 1); } function walk(schema, path) { if (!isObject(schema)) return; if (typeof schema.$ref === "string") { const ref = schema.$ref; const resolved = usedSchemas.get(ref); if (refsCurrentlyResolving.has(ref) || !resolved || isPendingSchema(resolved)) return; refsCurrentlyResolving.add(ref); walk(resolved, path); refsCurrentlyResolving.delete(ref); return; } if (!isObject(schema.properties)) return; for (const [propertyName, childSchema] of Object.entries(schema.properties)) { const childPath = path ? `${path}.${propertyName}` : propertyName; recordPath(childPath); walk(childSchema, childPath); } } for (const branch of allOfBranches) { pathsSeenInThisBranch.clear(); refsCurrentlyResolving.clear(); walk(branch); } const conflictPaths = /* @__PURE__ */ new Set(); for (const [path, branchCount] of pathBranchCount) { if (branchCount > 1) conflictPaths.add(path); } return conflictPaths; } function inlineAllOfRefsDeep(schema, usedSchemas) { const allOfKey = "/allOf"; for (const keyword of ["oneOf", "anyOf"]) { if (!Array.isArray(schema[keyword])) continue; for (let i = 0; i < schema[keyword].length; i++) { const option = schema[keyword][i]; if (!option || typeof option !== "object") continue; if (isRef(option) && option.$ref.includes(allOfKey)) { const resolved = usedSchemas.get(option.$ref); if (resolved !== void 0 && !isPendingSchema(resolved)) { schema[keyword][i] = structuredClone(resolved); } } else if (!isRef(option)) { inlineAllOfRefsDeep(option, usedSchemas); } } } if ("properties" in schema && typeof schema.properties === "object" && schema.properties !== null) { for (const key of Object.keys(schema.properties)) { const val = schema.properties[key]; if (isRef(val) && val.$ref.includes(allOfKey)) { const resolved = usedSchemas.get(val.$ref); if (resolved !== void 0 && !isPendingSchema(resolved)) { schema.properties[key] = structuredClone(resolved); } } else if (val && typeof val === "object" && !Array.isArray(val) && !isRef(val)) { inlineAllOfRefsDeep(val, usedSchemas); } } } } function resolveAndCacheRefSchema({ schema, definition, usedSchemas, seenRefs, conversionOptions, returnMode, refLogger }) { const ref = schema.$ref; const refsEmittedAsStub = conversionOptions.refsEmittedAsStub; const existing = usedSchemas.get(ref); if (existing !== void 0 && !isPendingSchema(existing)) { if (returnMode === "converted") { return existing; } if (refsEmittedAsStub?.has(ref)) { return { $ref: ref }; } if (!isRef(existing)) { return structuredClone(existing); } return { $ref: ref }; } if (existing !== void 0 && isPendingSchema(existing)) { refsEmittedAsStub?.add(ref); return { $ref: ref }; } usedSchemas.set(ref, PENDING_SCHEMA); if (returnMode === "ref") { let resolved; try { const dereferenced = dereferenceRef(schema, definition, seenRefs); if (isRef(dereferenced)) { refLogger(dereferenced.$ref, "ref"); let converted2; try { const pointer = ref.startsWith("#") ? decodeURIComponent(ref.substring(1)) : ref; const rawSchema = jsonpointer2.get(definition, pointer); if (rawSchema && typeof rawSchema === "object") { converted2 = toJSONSchema(structuredClone(rawSchema), { ...conversionOptions, seenRefs }); } else { converted2 = { $ref: ref }; } } catch { converted2 = { $ref: ref }; } usedSchemas.set(ref, converted2); refLogger(ref, "ref"); refsEmittedAsStub?.add(ref); return { $ref: ref }; } resolved = dereferenced; } catch { refLogger(ref, "ref"); usedSchemas.set(ref, { $ref: ref }); refsEmittedAsStub?.add(ref); return { $ref: ref }; } const converted = toJSONSchema(structuredClone(resolved), { ...conversionOptions, seenRefs }); usedSchemas.set(ref, converted); refLogger(ref, "ref"); refsEmittedAsStub?.add(ref); return { $ref: ref }; } try { const dereferenced = dereferenceRef(schema, definition, seenRefs); if (isRef(dereferenced)) { let converted2; try { const pointer = ref.startsWith("#") ? decodeURIComponent(ref.substring(1)) : ref; const rawSchema = jsonpointer2.get(definition, pointer); if (rawSchema && typeof rawSchema === "object") { converted2 = toJSONSchema(structuredClone(rawSchema), { ...conversionOptions, seenRefs }); } else { converted2 = { $ref: ref }; } } catch { converted2 = { $ref: ref }; } usedSchemas.set(ref, converted2); return converted2; } const converted = toJSONSchema(structuredClone(dereferenced), { ...conversionOptions, seenRefs }); usedSchemas.set(ref, converted); return converted; } catch { usedSchemas.set(ref, { $ref: ref }); return { $ref: ref }; } } function isRequestBodySchema(schema) { return "content" in schema; } function searchForValueByPropAndPointer(property, pointer, schemas = []) { if (!schemas.length || !pointer.length) { return; } const locSplit = pointer.split("/").filter(Boolean).toReversed(); const pointers = []; let point = ""; for (let i = 0; i < locSplit.length; i += 1) { point = `/${locSplit[i]}${point}`; pointers.push(point); } let foundValue; const rev = [...schemas].toReversed(); for (let i = 0; i < pointers.length; i += 1) { for (let ii = 0; ii < rev.length; ii += 1) { let schema = rev[ii]; if (property === "example") { if ("example" in schema) { schema = schema.example; } else { if (!Array.isArray(schema.examples) || !schema.examples.length) { continue; } schema = [...schema.examples].shift(); } } else { schema = schema.default; } try { foundValue = jsonpointer2.get(schema, pointers[i]); } catch { } if (foundValue !== void 0) { break; } } if (foundValue !== void 0) { break; } } return foundValue; } function toJSONSchema(data, opts) { let schema = data === true ? {} : { ...data }; const schemaAdditionalProperties = isSchema(schema) ? schema.additionalProperties : null; const { addEnumsToDescriptions, currentLocation, definition, globalDefaults, hideReadOnlyProperties, hideWriteOnlyProperties, isPolymorphicAllOfChild, prevDefaultSchemas = [], prevExampleSchemas = [], refLogger, seenRefs, usedSchemas, refsEmittedAsStub = /* @__PURE__ */ new Set() } = { addEnumsToDescriptions: false, currentLocation: "", globalDefaults: {}, hideReadOnlyProperties: false, hideWriteOnlyProperties: false, isPolymorphicAllOfChild: false, prevDefaultSchemas: [], prevExampleSchemas: [], refLogger: () => true, seenRefs: /* @__PURE__ */ new Set(), usedSchemas: /* @__PURE__ */ new Map(), refsEmittedAsStub: /* @__PURE__ */ new Set(), ...opts }; const polyOptions = { addEnumsToDescriptions, currentLocation, definition, globalDefaults, hideReadOnlyProperties, hideWriteOnlyProperties, isPolymorphicAllOfChild: false, prevDefaultSchemas, prevExampleSchemas, refLogger, seenRefs, usedSchemas, refsEmittedAsStub }; if (isRef(schema)) { if (definition && usedSchemas) { const resolved = resolveAndCacheRefSchema({ schema, definition, usedSchemas, seenRefs, conversionOptions: polyOptions, returnMode: "ref", refLogger }); const { $ref: _$ref, properties: _propertiesWithRef, ...siblings } = schema; if (Object.keys(siblings).length > 0) { return { ...resolved, ...siblings }; } return resolved; } refLogger(schema.$ref, "ref"); return schema; } if (isSchema(schema, isPolymorphicAllOfChild)) { if ("allOf" in schema && Array.isArray(schema.allOf)) { if ("properties" in schema && schema.properties !== void 0 && typeof schema.properties === "object" && schema.properties !== null && !Array.isArray(schema.properties)) { const preprocessed = {}; for (const prop of Object.keys(schema.properties)) { const val = schema.properties[prop]; if (Array.isArray(val) || typeof val === "object" && val !== null) { const converted = toJSONSchema(val, { ...polyOptions, currentLocation: `${currentLocation}/${encodePointer(prop)}`, prevDefaultSchemas, prevExampleSchemas }); if (hideReadOnlyProperties || hideWriteOnlyProperties) { let resolvedRefIsEmpty = false; if (isRef(converted) && usedSchemas) { const cached = usedSchemas.get(converted.$ref); if (cached && !isRef(cached) && !isPendingSchema(cached) && Object.keys(cached).length === 0) { resolvedRefIsEmpty = true; } } const originallyNonEmpty = Object.keys(val).length > 0; if (originallyNonEmpty && (!Object.keys(converted).length || resolvedRefIsEmpty)) { continue; } } preprocessed[prop] = converted; } else { preprocessed[prop] = val; } } schema = { ...schema, properties: preprocessed }; } let allOfSchemas = schema.allOf; if (definition && usedSchemas) { const allOfOptions = allOfSchemas.length > 1 ? { ...polyOptions, refLogger: () => { } } : polyOptions; allOfSchemas = allOfSchemas.map((item) => { if (isRef(item)) { if (Object.keys(item).length === 1) { return resolveAndCacheRefSchema({ schema: item, definition, usedSchemas, seenRefs, conversionOptions: allOfOptions, returnMode: "converted", refLogger }); } const { $ref, ...siblings } = item; const resolved = resolveAndCacheRefSchema({ schema: { $ref }, definition, usedSchemas, seenRefs, conversionOptions: allOfOptions, returnMode: "converted", refLogger }); if (!Object.keys(siblings).length) { return resolved; } const siblingSchema = toJSONSchema(siblings, allOfOptions); try { return mergeJSONSchemaAllOf( { allOf: [resolved, siblingSchema] }, mergeAllOfSchemasOptions ); } catch { return resolved; } } return toJSONSchema(item, allOfOptions); }); const conflictPaths = collectConflictPaths(allOfSchemas, usedSchemas); schema = { ...schema, allOf: allOfSchemas.map((s) => inlinePropertyRefsForMerge(s, usedSchemas, refLogger, conflictPaths)) }; } try { schema = mergeJSONSchemaAllOf(schema, mergeAllOfSchemasOptions); } catch { const { ...schemaWithoutAllOf } = schema; schema = schemaWithoutAllOf; delete schema.allOf; } collectRefsInSchema(schema).forEach((ref) => { refLogger(ref, "ref"); }); if (isRef(schema)) { refLogger(schema.$ref, "ref"); return schema; } } ["anyOf", "oneOf"].forEach((polyType) => { if (polyType in schema && Array.isArray(schema[polyType])) { const discriminatorPropertyName = "discriminator" in schema && schema.discriminator && isObject(schema.discriminator) ? schema.discriminator.propertyName : void 0; schema[polyType].forEach((item, idx) => { if (!schema[polyType]?.[idx]) { return; } const itemOptions = { ...polyOptions, currentLocation: `${currentLocation}/${idx}` }; if ("properties" in schema) { schema[polyType][idx] = toJSONSchema( { required: schema.required, allOf: [item, { properties: schema.properties }] }, itemOptions ); } else if ("items" in schema) { if (shouldFoldParentItemsIntoPolymorphBranch(item)) { schema[polyType][idx] = toJSONSchema( { allOf: [item, { items: schema.items }] }, itemOptions ); } else { schema[polyType][idx] = toJSONSchema(item, itemOptions); } } else { schema[polyType][idx] = toJSONSchema(item, itemOptions); } if ("required" in schema[polyType][idx]) { if (Array.isArray(schema[polyType][idx].required) && schema[polyType][idx].required.length === 0) { delete schema[polyType][idx].required; } else if (isObject(schema[polyType][idx]) && typeof schema[polyType][idx].required === "boolean") { delete schema[polyType][idx].required; } } if (discriminatorPropertyName) { const childSchema = schema[polyType][idx]; if (isObject(childSchema)) { if ("discriminator" in childSchema) { delete childSchema.discriminator; } if ("oneOf" in childSchema) { delete childSchema.oneOf; } if ("anyOf" in childSchema) { delete childSchema.anyOf; } } if (definition && usedSchemas && isRef(childSchema)) { const resolved = usedSchemas.get(childSchema.$ref); if (resolved && typeof resolved === "object" && !isPendingSchema(resolved)) { if ("discriminator" in resolved) { delete resolved.discriminator; } if ("oneOf" in resolved) { delete resolved.oneOf; } if ("anyOf" in resolved) { delete resolved.anyOf; } usedSchemas.set(childSchema.$ref, resolved); } } } }); } }); if (schema?.discriminator?.mapping && typeof schema.discriminator.mapping === "object") { const mapping = schema.discriminator.mapping; Object.keys(mapping).forEach((k) => { refLogger(mapping[k], "discriminator"); }); } } if (!("type" in schema) && !isPolymorphicSchema(schema) && !isRequestBodySchema(schema)) { if ("properties" in schema) { schema.type = "object"; } else if ("items" in schema) { schema.type = "array"; } else { } } if ("type" in schema && schema.type !== void 0) { if ("nullable" in schema) { if (schema.nullable) { if (Array.isArray(schema.type)) { schema.type.push("null"); } else if (schema.type !== null && schema.type !== "null") { schema.type = [schema.type, "null"]; } } delete schema.nullable; } if (schema.type === null) { schema.type = "null"; } else if (Array.isArray(schema.type)) { if (schema.type.includes(null)) { schema.type[schema.type.indexOf(null)] = "null"; } schema.type = Array.from(new Set(schema.type)); if (schema.type.length === 1) { schema.type = schema.type.shift(); } else if (schema.type.includes("array") || schema.type.includes("boolean") || schema.type.includes("object")) { const isNullable = schema.type.includes("null"); if (schema.type.length === 2 && isNullable) { } else { const nonPrimitives = []; Object.entries({ // https://json-schema.org/understanding-json-schema/reference/array.html array: [ "additionalItems", "contains", "items", "maxContains", "maxItems", "minContains", "minItems", "prefixItems", "uniqueItems" ], // https://json-schema.org/understanding-json-schema/reference/boolean.html boolean: [ // Booleans don't have any boolean-specific properties. ], // https://json-schema.org/understanding-json-schema/reference/object.html object: [ "additionalProperties", "maxProperties", "minProperties", "nullable", "patternProperties", "properties", "propertyNames", "required" ] }).forEach(([typeKey, keywords]) => { if (!schema.type?.includes(typeKey)) { return; } const reducedSchema = removeUndefinedObjects({ type: isNullable ? [typeKey, "null"] : typeKey, allowEmptyValue: schema.allowEmptyValue ?? void 0, deprecated: schema.deprecated ?? void 0, description: schema.description ?? void 0, readOnly: schema.readOnly ?? void 0, title: schema.title ?? void 0, writeOnly: schema.writeOnly ?? void 0 }); keywords.forEach((keyword) => { if (keyword in schema) { reducedSchema[keyword] = schema[keyword]; delete schema[keyword]; } }); nonPrimitives.push(reducedSchema); }); schema.type = schema.type.filter((t) => t !== "array" && t !== "boolean" && t !== "object"); if (schema.type.length === 1) { schema.type = schema.type.shift(); } if (schema.type && schema.type.length > 1) { schema = { oneOf: [schema, ...nonPrimitives] }; } else { schema = { oneOf: nonPrimitives }; } } } } } if (isSchema(schema, isPolymorphicAllOfChild)) { if ("default" in schema && isObject(schema.default)) { prevDefaultSchemas.push({ default: schema.default }); } if ("example" in schema) { if (isPrimitive(schema.example)) { schema.examples = [schema.example]; } else if (Array.isArray(schema.example)) { schema.examples = schema.example.filter((example) => isPrimitive(example)); if (!schema.examples.length) { delete schema.examples; } } else { prevExampleSchemas.push({ example: schema.example }); } delete schema.example; } else if ("examples" in schema) { let reshapedExamples = false; if (typeof schema.examples === "object" && schema.examples !== null && !Array.isArray(schema.examples)) { const examples = []; Object.entries(schema.examples).forEach(([name, example]) => { let currentExample = example; if (name === "$ref") { currentExample = dereferenceRef({ $ref: currentExample }, definition, seenRefs); if (!currentExample || isRef(currentExample)) { refLogger(currentExample.$ref, "ref"); return; } } if ("value" in currentExample) { if (isPrimitive(currentExample.value)) { examples.push(currentExample.value); reshapedExamples = true; } else if (Array.isArray(currentExample.value) && isPrimitive(currentExample.value[0])) { examples.push(currentExample.value[0]); reshapedExamples = true; } else { prevExampleSchemas.push({ example: currentExample.value }); } } }); if (examples.length) { reshapedExamples = true; schema.examples = examples; } } else if (Array.isArray(schema.examples) && isPrimitive(schema.examples[0])) { reshapedExamples = true; } if (!reshapedExamples) { delete schema.examples; } } if (!hasSchemaType(schema, "array") && !hasSchemaType(schema, "object") && !schema.examples) { const foundExample = searchForValueByPropAndPointer("example", currentLocation, prevExampleSchemas); if (foundExample) { if (isPrimitive(foundExample) || Array.isArray(foundExample) && isPrimitive(foundExample[0])) { schema.examples = [foundExample]; } } } if (hasSchemaType(schema, "array")) { if ("items" in schema && schema.items !== void 0) { if (!(definition && usedSchemas) && !Array.isArray(schema.items) && Object.keys(schema.items).length === 1 && isRef(schema.items)) { refLogger(schema.items.$ref, "ref"); } else if (schema.items !== true) { schema.items = toJSONSchema(schema.items, { ...polyOptions, currentLocation: `${currentLocation}/0`, prevDefaultSchemas: [], prevExampleSchemas }); if ("required" in schema.items) { if (Array.isArray(schema.items.required) && schema.items.required.length === 0) { delete schema.items.required; } else if (isObject(schema.items) && !Array.isArray(schema.items.required)) { delete schema.items.required; } } } } else if ("properties" in schema || "additionalProperties" in schema) { schema.type = "object"; } else { schema.items = {}; } } else if (hasSchemaType(schema, "object")) { if ("properties" in schema && schema.properties !== void 0) { Object.keys(schema.properties).forEach((prop) => { if (Array.isArray(schema.properties?.[prop]) || typeof schema.properties?.[prop] === "object" && schema.properties?.[prop] !== null) { const newPropSchema = toJSONSchema(schema.properties[prop], { ...polyOptions, currentLocation: `${currentLocation}/${encodePointer(prop)}`, // Keep parent defaults visible to this child, but isolate mutations that happen while // walking its descendants. Otherwise a default pushed by one sibling can be found by // later siblings with the same nested property name. prevDefaultSchemas: [...prevDefaultSchemas], prevExampleSchemas }); let propShouldBeUpdated = true; if (hideReadOnlyProperties || hideWriteOnlyProperties) { let resolvedRefIsEmpty = false; if (isRef(newPropSchema) && usedSchemas) { const cached = usedSchemas.get(newPropSchema.$ref); if (cached && !isRef(cached) && !isPendingSchema(cached) && Object.keys(cached).length === 0) { resolvedRefIsEmpty = true; } } if (!Object.keys(newPropSchema).length || resolvedRefIsEmpty) { if (Object.keys(schema.properties[prop]).length > 0) { delete schema.properties[prop]; propShouldBeUpdated = false; } } } if (propShouldBeUpdated) { schema.properties[prop] = newPropSchema; if (isObject(newPropSchema) && "required" in newPropSchema && typeof newPropSchema.required === "boolean" && newPropSchema.required === true) { if ("required" in schema && Array.isArray(schema.required)) { schema.required.push(prop); } else { schema.required = [prop]; } delete schema.properties[prop].required; } } } }); if (hideReadOnlyProperties || hideWriteOnlyProperties) { if (!Object.keys(schema.properties).length) { return {}; } } } if (typeof schemaAdditionalProperties === "object" && schemaAdditionalProperties !== null) { if (!("type" in schemaAdditionalProperties) && !("$ref" in schemaAdditionalProperties) && // We know it will be a schema object because it's dereferenced !isPolymorphicSchema(schemaAdditionalProperties)) { schema.additionalProperties = true; } else { schema.additionalProperties = toJSONSchema(schemaAdditionalProperties, { ...polyOptions, currentLocation, prevDefaultSchemas, prevExampleSchemas }); } } if (!isPolymorphicSchema(schema) && !("properties" in schema) && !("additionalProperties" in schema)) { schema.additionalProperties = true; } } } if (isSchema(schema, isPolymorphicAllOfChild) && globalDefaults && Object.keys(globalDefaults).length > 0 && currentLocation) { try { const userJwtDefault = jsonpointer2.get(globalDefaults, currentLocation); if (userJwtDefault) { schema.default = userJwtDefault; } } catch { } } if ("default" in schema && typeof schema.default !== "undefined") { if (hasSchemaType(schema, "object")) { delete schema.default; } else if ("allowEmptyValue" in schema && schema.allowEmptyValue && schema.default === "" || schema.default !== "") { } else { delete schema.default; } } else if (prevDefaultSchemas.length) { const foundDefault = searchForValueByPropAndPointer("default", currentLocation, prevDefaultSchemas); if (isPrimitive(foundDefault) || foundDefault === null || Array.isArray(foundDefault) && hasSchemaType(schema, "array")) { schema.default = foundDefault; } } if (isSchema(schema, isPolymorphicAllOfChild) && "enum" in schema && Array.isArray(schema.enum)) { schema.enum = Array.from(new Set(schema.enum)); if (addEnumsToDescriptions) { const enums = schema.enum.filter((v) => v !== void 0 && (typeof v !== "string" || v.trim() !== "")).map((str) => `\`${str}\``).join(" "); if (enums.length) { const currentDescription = "description" in schema && typeof schema.description === "string" ? schema.description : ""; if (!currentDescription) { schema.description = enums; } else { const paragraphs = currentDescription.split(/\n\n+/).map((p) => p.trim()); const enumParagraphCount = paragraphs.filter((p) => p === enums).length; if (enumParagraphCount > 1) { const withoutEnum = paragraphs.filter((p) => p !== enums); schema.description = withoutEnum.length > 0 ? `${withoutEnum.join("\n\n")} ${enums}` : enums; } else if (paragraphs.some((p) => p === enums)) { } else { schema.description = `${currentDescription} ${enums}`; } } } } } if ("anyOf" in schema || "oneOf" in schema) { for (const key of ["anyOf", "oneOf"]) { if (key in schema && isEmptyPolymorphicSchema(schema[key])) { delete schema[key]; } } if ("anyOf" in schema || "oneOf" in schema) { if ("properties" in schema) { delete schema.properties; } if ("items" in schema) { delete schema.items; if ("type" in schema && schema.type === "array") { delete schema.type; } } } } for (let i = 0; i < UNSUPPORTED_SCHEMA_PROPS.length; i += 1) { delete schema[UNSUPPORTED_SCHEMA_PROPS[i]]; } if (hideReadOnlyProperties && "readOnly" in schema && schema.readOnly === true) { return {}; } else if (hideWriteOnlyProperties && "writeOnly" in schema && schema.writeOnly === true) { return {}; } return schema; } // src/operation/transformers/get-parameters-as-json-schema.ts var types = { path: "Path Params", query: "Query Params", body: "Body Params", cookie: "Cookie Params", formData: "Form Data", header: "Headers", metadata: "Metadata" // This a special type reserved for https://npm.im/api }; function getParametersAsJSONSchema(operation, api, opts) { const seenRefs = /* @__PURE__ */ new Set(); const refsByGroup = /* @__PURE__ */ new Map(); const usedSchemasByGroup = /* @__PURE__ */ new Map(); function refLoggerForSchemaGroup(group) { let set = refsByGroup.get(group); if (!set) { set = /* @__PURE__ */ new Set(); refsByGroup.set(group, set); } return set; } function usedSchemasForSchemaGroup(group) { let map = usedSchemasByGroup.get(group); if (!map) { map = /* @__PURE__ */ new Map(); usedSchemasByGroup.set(group, map); } return map; } const baseSchemaOptions = { definition: api, globalDefaults: opts?.globalDefaults, hideReadOnlyProperties: opts?.hideReadOnlyProperties, hideWriteOnlyProperties: opts?.hideWriteOnlyProperties, seenRefs }; function transformRequestBody() { const requestBody = operation.getRequestBody(opts?.contentType); if (!requestBody) return null; const { mediaType, mediaTypeObject, description } = requestBody; const type = mediaType === "application/x-www-form-urlencoded" ? "formData" : "body"; if (!mediaTypeObject.schema || !Object.keys(mediaTypeObject.schema).length) { return null; } const prevExampleSchemas = []; if ("example" in mediaTypeObject) { prevExampleSchemas.push({ example: mediaTypeObject.example }); } else if ("examples" in mediaTypeObject) { prevExampleSchemas.push({ examples: Object.values(mediaTypeObject.examples || {}).map((ex) => { let example = ex; if (!example) return; if (isRef(example)) { example = dereferenceRef(example, operation.api); if (!example || isRef(example)) return; } return example.value; }).filter((item) => item !== void 0) }); } const requestSchema = cloneObject(mediaTypeObject.schema); const cleanedSchema = toJSONSchema(requestSchema, { ...baseSchemaOptions, usedSchemas: usedSchemasForSchemaGroup(type), prevExampleSchemas, refLogger: (ref) => refLoggerForSchemaGroup(type).add(ref) }); if (!Object.keys(cleanedSchema).length) { return null; } return { type, label: types[type], schema: isPrimitive(cleanedSchema) ? cleanedSchema : { ...cleanedSchema, $schema: getSchemaVersionString(cleanedSchema, api) }, ...description ? { description } : {} }; } function transformParameters() { const operationParams = operation.getParameters(); const transformed = Object.keys(types).map((type) => { const required = []; const parameters = operationParams.filter((param) => param.in === type); if (parameters.length === 0) { return null; } const properties = parameters.reduce((prev, current) => { let schema2 = {}; if ("schema" in current) { const currentSchema = current.schema ? cloneObject(current.schema) : {}; if (current.example) { currentSchema.example = current.exampl