@scalar/api-reference
Version:
Generate beautiful API references from OpenAPI documents
175 lines (174 loc) • 6.96 kB
JavaScript
import { getResolvedRef } from "@scalar/workspace-store/helpers/get-resolved-ref";
import { resolve } from "@scalar/workspace-store/resolve";
import { isArraySchema } from "@scalar/workspace-store/schemas/v3.1/strict/type-guards";
import { objectKeys } from "@scalar/helpers/object/object-keys";
//#region src/components/Content/Schema/helpers/merge-all-of-schemas.ts
/**
* Merges multiple OpenAPI schema objects into a single schema object.
* Handles nested allOf compositions and merges properties recursively.
*
* @param schemas - Array of OpenAPI schema objects to merge
* @param rootSchema - Optional root schema to merge with the result
* @returns Merged schema object
*/
var mergeAllOfSchemas = (schemas, rootSchema) => {
if (!schemas?.allOf?.length || !Array.isArray(schemas.allOf)) return rootSchema || {};
const result = {};
const { allOf: _, ...baseSchema } = schemas;
for (const _schema of schemas.allOf) {
if (!_schema || typeof _schema !== "object") continue;
const schema = resolve.schema(_schema);
if (schema.allOf) {
mergeSchemaIntoResult(result, mergeAllOfSchemas(schema));
continue;
}
mergeSchemaIntoResult(result, schema);
}
if (Object.keys(baseSchema).length > 0) mergeSchemaIntoResult(result, baseSchema, true);
if (rootSchema && typeof rootSchema === "object") if (rootSchema.allOf) mergeSchemaIntoResult(result, mergeAllOfSchemas(rootSchema), true);
else mergeSchemaIntoResult(result, rootSchema, true);
return result;
};
/**
* Efficiently merges a source schema into a target result object.
* Handles all schema merging logic in a single optimized function.
*
* @param result - The target schema object to merge into
* @param schema - The source schema object to merge from
* @param override - Whether to override existing properties (default: false)
*/
var mergeSchemaIntoResult = (result, schema, override = false) => {
const schemaKeys = objectKeys(schema);
if (schemaKeys.length === 0) return;
for (const key of schemaKeys) {
const value = getResolvedRef(schema[key]);
if (value === void 0) continue;
if (key === "required") {
if (Array.isArray(value) && value.length > 0) if (result.required?.length) result.required = [...new Set([...result.required, ...value])];
else result.required = value.slice();
} else if (key === "properties") {
if (value && typeof value === "object") {
if (!result.properties) result.properties = {};
mergePropertiesIntoResult(result.properties, value);
}
} else if (key === "items") {
const items = resolve.schema(value);
if (items) {
if (isArraySchema(schema)) {
if (!result.items) result.items = {};
if (items.allOf) {
const mergedItems = mergeAllOfSchemas(items);
Object.assign(result.items, mergedItems);
} else mergeItemsIntoResult(getResolvedRef(result.items), items);
} else if (items.allOf) {
const mergedItems = mergeAllOfSchemas(items);
if ("properties" in mergedItems) {
if (!("properties" in result)) result.properties = {};
"properties" in result && mergePropertiesIntoResult(result.properties, mergedItems.properties);
}
} else if (!("items" in result)) result.items = items;
}
} else if (key === "enum") {
if (Array.isArray(value) && value.length > 0) result.enum = [...new Set([...result.enum || [], ...value])];
} else if (key === "oneOf" || key === "anyOf") {
if (Array.isArray(value)) {
if (!("properties" in result)) result.properties = {};
for (const _option of value) {
const option = resolve.schema(_option);
if (option && "properties" in option && "properties" in result) mergePropertiesIntoResult(result.properties, option.properties);
}
}
} else if (key === "allOf") continue;
else if (override || result[key] === void 0) result[key] = value;
}
};
/**
* Efficiently merges properties into a result object without creating new objects.
*/
var mergePropertiesIntoResult = (result, properties) => {
const propertyKeys = Object.keys(properties ?? {});
if (!properties || !result || propertyKeys.length === 0) return;
for (const key of propertyKeys) {
const schema = resolve.schema(properties[key]);
if (!schema) {
delete result[key];
continue;
}
if (typeof schema !== "object") {
result[key] = schema;
continue;
}
if (!result[key]) {
if (schema.allOf) result[key] = mergeAllOfSchemas(schema);
else if (isArraySchema(schema) && resolve.schema(schema.items)?.allOf) result[key] = {
...schema,
items: mergeAllOfSchemas(resolve.schema(schema.items))
};
else if (properties[key]) result[key] = properties[key];
continue;
}
const existing = resolve.schema(result[key]);
if (schema.allOf) result[key] = mergeAllOfSchemas({ allOf: [existing, ...schema.allOf] });
else if (isArraySchema(schema) && isArraySchema(existing) && schema.items) {
const existingItems = resolve.schema(existing.items);
result[key] = {
...existing,
type: "array",
items: existingItems ? mergeItems(existingItems, resolve.schema(schema.items)) : resolve.schema(schema.items)
};
} else if ("properties" in existing && "properties" in schema) {
const merged = {
...existing,
...schema
};
merged.properties = { ...existing.properties };
mergePropertiesIntoResult(merged.properties, schema.properties);
result[key] = merged;
} else result[key] = {
...schema,
...existing
};
}
};
/**
* Efficiently merges array items into a result object.
*/
var mergeItemsIntoResult = (result, items) => {
if (items.allOf || result.allOf) {
const allOfSchemas = [];
if (result.allOf) for (const schema of result.allOf) allOfSchemas.push(resolve.schema(schema));
else allOfSchemas.push(result);
if (items.allOf) for (const schema of items.allOf) allOfSchemas.push(resolve.schema(schema));
else allOfSchemas.push(items);
const merged = mergeAllOfSchemas({ allOf: allOfSchemas });
Object.assign(result, merged);
return;
}
Object.assign(result, items);
if ("properties" in result && "properties" in items) mergePropertiesIntoResult(result.properties, items.properties);
};
/**
* Helper function for merging items that returns a new object.
*/
var mergeItems = (existing, incoming) => {
if (existing.allOf || incoming.allOf) {
const allOfSchemas = [];
if (existing.allOf) for (const schema of existing.allOf) allOfSchemas.push(resolve.schema(schema));
else allOfSchemas.push(existing);
if (incoming.allOf) for (const schema of incoming.allOf) allOfSchemas.push(resolve.schema(schema));
else allOfSchemas.push(incoming);
return mergeAllOfSchemas({ allOf: allOfSchemas });
}
const merged = {
...existing,
...incoming
};
if ("properties" in existing && "properties" in incoming) {
merged.properties = { ...existing.properties };
mergePropertiesIntoResult(merged.properties, incoming.properties);
}
return merged;
};
//#endregion
export { mergeAllOfSchemas };
//# sourceMappingURL=merge-all-of-schemas.js.map