oas
Version:
Comprehensive tooling for working with OpenAPI definitions
1,401 lines (1,390 loc) • 55.1 kB
JavaScript
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