Skip to content
Open
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 46 additions & 12 deletions lib/process-schema.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,70 @@
const NESTED_WITH_NAME = ["definitions", "properties"];
/**
* @typedef {Record<string, any> | boolean} JSONSchema
Comment thread
Manas-Dikshit marked this conversation as resolved.
Outdated
* Represents a JSON Schema node — either an object schema or a boolean schema.
*/

const NESTED_DIRECT = ["items", "additionalProperties", "not"];
/**
* @typedef {Object} ProcessContext
* @property {JSONSchema} [rootSchema] - The root schema being processed.
* @property {string[]} [path] - Path segments from the root to the current node.
* @property {Record<string, any>} [meta] - Arbitrary metadata shared across recursion.
*/

/**
* @typedef {Object} SchemaVisitor
* @property {(schema: JSONSchema, context?: ProcessContext) => JSONSchema | void} [schema]
* @property {(obj: JSONSchema, context?: ProcessContext) => JSONSchema | void} [object]
* @property {(arr: JSONSchema[], context?: ProcessContext) => void} [array]
*/

// Constants defining nested schema traversal structure
const NESTED_WITH_NAME = ["definitions", "properties"];
const NESTED_DIRECT = ["items", "additionalProperties", "not"];
const NESTED_ARRAY = ["oneOf", "anyOf", "allOf"];

/**
* Recursively processes a JSON Schema using the visitor pattern.
*
* @param {SchemaVisitor} visitor - Visitor functions to apply.
* @param {JSONSchema} json - JSON Schema to process.
* @param {ProcessContext} [context] - Optional shared context passed through recursion.
* @returns {JSONSchema} - The processed JSON Schema.
*/
Comment thread
Manas-Dikshit marked this conversation as resolved.
const processSchema = (visitor, json, context) => {
Comment thread
Manas-Dikshit marked this conversation as resolved.
if (!json || typeof json !== "object") return json; // safety check

json = { ...json };
if (visitor.schema) json = visitor.schema(json, context);
if (typeof visitor?.schema === "function") {
json = visitor.schema(json, context) || json;
}

for (const name of NESTED_WITH_NAME) {
if (name in json && json[name] && typeof json[name] === "object") {
if (visitor.object) json[name] = visitor.object(json[name], context);
for (const key in json[name]) {
if (json[name] && typeof json[name] === "object" && !Array.isArray(json[name])) {
Comment thread
Manas-Dikshit marked this conversation as resolved.
Outdated
if (typeof visitor?.object === "function") {
json[name] = visitor.object(json[name], context) || json[name];
}
for (const key of Object.keys(json[name])) {
json[name][key] = processSchema(visitor, json[name][key], context);
}
}
}

for (const name of NESTED_DIRECT) {
if (name in json && json[name] && typeof json[name] === "object") {
if (json[name] && typeof json[name] === "object" && !Array.isArray(json[name])) {
json[name] = processSchema(visitor, json[name], context);
}
}

for (const name of NESTED_ARRAY) {
if (name in json && Array.isArray(json[name])) {
json[name] = json[name].slice();
for (let i = 0; i < json[name].length; i++) {
json[name][i] = processSchema(visitor, json[name][i], context);
if (Array.isArray(json[name])) {
json[name] = json[name].map((item) => processSchema(visitor, item, context));
if (typeof visitor?.array === "function") {
visitor.array(json[name], context);
}
if (visitor.array) visitor.array(json[name], context);
}
}

return json;
};

module.exports = processSchema;