@mintlify/validation
Version:
Validates mint.json files
195 lines (194 loc) • 7.29 kB
JavaScript
import hash from 'object-hash';
import { v4 as uuidv4 } from 'uuid';
export const replaceSchemaRefs = ({ schema, refUuidMap, uuidObjectHashMap, hashedNodeMap, schemaToUuidMap = new WeakMap(), }) => {
if ('$ref' in schema) {
const refId = schema.$ref;
const uuid = refUuidMap[refId] || uuidv4();
schema.$ref = uuid;
return;
}
if (schemaToUuidMap.has(schema)) {
const value = schemaToUuidMap.get(schema);
if (value === '') {
// circular reference found at the top level without processing children
return;
}
else {
return;
}
}
schemaToUuidMap.set(schema, '');
if ('properties' in schema && schema.properties && typeof schema.properties === 'object') {
Object.entries(schema.properties).forEach(([propName, subschema]) => {
var _a;
if (typeof subschema !== 'object' || subschema === null) {
return;
}
const childUuid = processChildSchema({
childSchema: subschema,
refUuidMap,
uuidObjectHashMap,
hashedNodeMap,
schemaToUuidMap,
});
if (childUuid && ((_a = schema.properties) === null || _a === void 0 ? void 0 : _a[propName])) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
schema.properties[propName] = childUuid;
}
});
}
if ('items' in schema && schema.items && typeof schema.items === 'object') {
const childUuid = processChildSchema({
childSchema: schema.items,
refUuidMap,
uuidObjectHashMap,
hashedNodeMap,
schemaToUuidMap,
});
if (childUuid) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
schema.items = childUuid;
}
}
if ('additionalProperties' in schema && typeof schema.additionalProperties === 'object') {
const childUuid = processChildSchema({
childSchema: schema.additionalProperties,
refUuidMap,
uuidObjectHashMap,
hashedNodeMap,
schemaToUuidMap,
});
if (childUuid) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
schema.additionalProperties = childUuid;
}
}
if ('oneOf' in schema && schema.oneOf && Array.isArray(schema.oneOf)) {
schema.oneOf.forEach((subschema, index) => {
var _a;
if (typeof subschema !== 'object') {
return;
}
const childUuid = processChildSchema({
childSchema: subschema,
refUuidMap,
uuidObjectHashMap,
hashedNodeMap,
schemaToUuidMap,
});
if (childUuid && ((_a = schema.oneOf) === null || _a === void 0 ? void 0 : _a[index])) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
schema.oneOf[index] = childUuid;
}
});
}
if ('anyOf' in schema && schema.anyOf && Array.isArray(schema.anyOf)) {
schema.anyOf.forEach((subschema, index) => {
var _a;
if (typeof subschema !== 'object') {
return;
}
const childUuid = processChildSchema({
childSchema: subschema,
refUuidMap,
uuidObjectHashMap,
hashedNodeMap,
schemaToUuidMap,
});
if (childUuid && ((_a = schema.anyOf) === null || _a === void 0 ? void 0 : _a[index])) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
schema.anyOf[index] = childUuid;
}
});
}
if ('allOf' in schema && schema.allOf && Array.isArray(schema.allOf)) {
schema.allOf.forEach((subschema, index) => {
var _a;
if (typeof subschema !== 'object') {
return;
}
const childUuid = processChildSchema({
childSchema: subschema,
refUuidMap,
uuidObjectHashMap,
hashedNodeMap,
schemaToUuidMap,
});
if (childUuid && ((_a = schema.allOf) === null || _a === void 0 ? void 0 : _a[index])) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
schema.allOf[index] = childUuid;
}
});
}
if ('not' in schema && schema.not && typeof schema.not === 'object') {
const childUuid = processChildSchema({
childSchema: schema.not,
refUuidMap,
uuidObjectHashMap,
hashedNodeMap,
schemaToUuidMap,
});
if (childUuid) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
schema.not = childUuid;
}
}
};
const processChildSchema = ({ childSchema, refUuidMap, uuidObjectHashMap, hashedNodeMap, schemaToUuidMap, }) => {
if (typeof childSchema !== 'object') {
return;
}
// preserve sibling properties when resolving $ref
if ('$ref' in childSchema) {
const refId = childSchema.$ref;
const refUuid = refUuidMap[refId] || uuidv4();
// if no siblings, safe to return ref uuid directly
const siblingKeys = Object.keys(childSchema).filter((key) => key !== '$ref');
if (siblingKeys.length === 0) {
return refUuid;
}
// otherwise make new schema node with $ref and adjacent properties
const childUuid = uuidv4();
childSchema.$ref = refUuid;
const objectHash = hash(childSchema);
if (objectHash in hashedNodeMap) {
uuidObjectHashMap[childUuid] = objectHash;
schemaToUuidMap.set(childSchema, childUuid);
return childUuid;
}
uuidObjectHashMap[childUuid] = objectHash;
hashedNodeMap[objectHash] = childSchema;
schemaToUuidMap.set(childSchema, childUuid);
return childUuid;
}
if (schemaToUuidMap.has(childSchema)) {
// circular reference!! return existing uuid
const existingUuid = schemaToUuidMap.get(childSchema);
if (existingUuid) {
return existingUuid;
}
}
// assign new uuid for child
const childUuid = uuidv4();
const objectHash = hash(childSchema);
// check if this exact schema already exists in the graph
if (objectHash in hashedNodeMap) {
// schema already exists, just map the uuid to it
uuidObjectHashMap[childUuid] = objectHash;
schemaToUuidMap.set(childSchema, childUuid);
return childUuid;
}
// add to the graph maps
uuidObjectHashMap[childUuid] = objectHash;
// recursively process the child schema before adding it to the graph
replaceSchemaRefs({
schema: childSchema,
refUuidMap,
uuidObjectHashMap,
hashedNodeMap,
schemaToUuidMap,
});
schemaToUuidMap.set(childSchema, childUuid);
hashedNodeMap[objectHash] = childSchema;
return childUuid;
};