-
-
Notifications
You must be signed in to change notification settings - Fork 19
Expand file tree
/
Copy pathprocess-schema.js
More file actions
88 lines (78 loc) · 3.1 KB
/
process-schema.js
File metadata and controls
88 lines (78 loc) · 3.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/**
* @typedef {Object} JSONSchema
* @property {string} [$id]
* @property {string} [$schema]
* @property {string} [$ref]
* @property {string} [title]
* @property {string} [description]
* @property {string | string[]} [type]
* @property {boolean} [nullable]
* @property {JSONSchema | boolean} [items]
* @property {Record<string, JSONSchema | boolean>} [properties]
* @property {JSONSchema | boolean} [additionalProperties]
* @property {JSONSchema[]} [oneOf]
* @property {JSONSchema[]} [anyOf]
* @property {JSONSchema[]} [allOf]
* @property {Record<string, JSONSchema | boolean>} [definitions]
* @property {unknown} [enum]
* @property {unknown} [const]
* @property {unknown} [default]
* @property {Record<string, unknown>} [examples]
* @property {Record<string, unknown>} [other] - Any additional schema fields.
*/
/**
* @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, unknown>} [meta] - Arbitrary metadata shared across recursion.
*/
/**
* @typedef {Object} SchemaVisitor
* @property {(schema: JSONSchema | boolean, context?: ProcessContext) => JSONSchema | boolean | void} [schema]
* @property {(obj: JSONSchema | boolean, context?: ProcessContext) => JSONSchema | boolean | void} [object]
* @property {(arr: (JSONSchema | boolean)[], 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 | boolean} json - JSON Schema to process.
* @param {ProcessContext} [context] - Optional shared context passed through recursion.
* @returns {JSONSchema | boolean} - The processed JSON Schema.
*/
const processSchema = (visitor, json, context) => {
if (!json || typeof json !== "object") return json; // safety check
json = { ...json };
if (typeof visitor?.schema === "function") {
json = visitor.schema(json, context) || json;
}
for (const name of NESTED_WITH_NAME) {
if (json[name] && typeof json[name] === "object" && !Array.isArray(json[name])) {
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 (json[name] && typeof json[name] === "object" && !Array.isArray(json[name])) {
json[name] = processSchema(visitor, json[name], context);
}
}
for (const name of NESTED_ARRAY) {
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);
}
}
}
return json;
};
module.exports = processSchema;