toolcraft-openapi 0.0.122 → 0.0.124
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/composition.json +6 -1
- package/node_modules/toolcraft-schema/LICENSE +21 -0
- package/node_modules/toolcraft-schema/README.md +89 -0
- package/node_modules/toolcraft-schema/dist/index.compile-check.d.ts +1 -0
- package/node_modules/toolcraft-schema/dist/index.compile-check.js +17 -0
- package/node_modules/toolcraft-schema/dist/index.d.ts +184 -0
- package/node_modules/toolcraft-schema/dist/index.js +295 -0
- package/node_modules/toolcraft-schema/dist/json-schema/compiler.d.ts +11 -0
- package/node_modules/toolcraft-schema/dist/json-schema/compiler.js +390 -0
- package/node_modules/toolcraft-schema/dist/json-schema/evaluate.d.ts +3 -0
- package/node_modules/toolcraft-schema/dist/json-schema/evaluate.js +442 -0
- package/node_modules/toolcraft-schema/dist/json-schema/index.d.ts +5 -0
- package/node_modules/toolcraft-schema/dist/json-schema/index.js +19 -0
- package/node_modules/toolcraft-schema/dist/json-schema/types.d.ts +33 -0
- package/node_modules/toolcraft-schema/dist/json-schema/types.js +1 -0
- package/node_modules/toolcraft-schema/dist/json-schema/utils.d.ts +19 -0
- package/node_modules/toolcraft-schema/dist/json-schema/utils.js +171 -0
- package/node_modules/toolcraft-schema/dist/json-schema-document.d.ts +14 -0
- package/node_modules/toolcraft-schema/dist/json-schema-document.js +17 -0
- package/node_modules/toolcraft-schema/dist/json.compile-check.d.ts +1 -0
- package/node_modules/toolcraft-schema/dist/json.compile-check.js +2 -0
- package/node_modules/toolcraft-schema/dist/json.d.ts +10 -0
- package/node_modules/toolcraft-schema/dist/json.js +5 -0
- package/node_modules/toolcraft-schema/dist/oneof.compile-check.d.ts +1 -0
- package/node_modules/toolcraft-schema/dist/oneof.compile-check.js +12 -0
- package/node_modules/toolcraft-schema/dist/oneof.d.ts +15 -0
- package/node_modules/toolcraft-schema/dist/oneof.js +18 -0
- package/node_modules/toolcraft-schema/dist/record.compile-check.d.ts +1 -0
- package/node_modules/toolcraft-schema/dist/record.compile-check.js +2 -0
- package/node_modules/toolcraft-schema/dist/record.d.ts +5 -0
- package/node_modules/toolcraft-schema/dist/record.js +6 -0
- package/node_modules/toolcraft-schema/dist/union.compile-check.d.ts +1 -0
- package/node_modules/toolcraft-schema/dist/union.compile-check.js +9 -0
- package/node_modules/toolcraft-schema/dist/union.d.ts +8 -0
- package/node_modules/toolcraft-schema/dist/union.js +45 -0
- package/node_modules/toolcraft-schema/dist/validate.d.ts +17 -0
- package/node_modules/toolcraft-schema/dist/validate.js +379 -0
- package/node_modules/toolcraft-schema/package.json +33 -0
- package/package.json +6 -4
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
import { deepEqual, invalidResult, isMultipleOf, isObject, issue, mergeResults, typeMatches, unicodeLength, validResult } from "./utils.js";
|
|
2
|
+
export function evaluateSchema(graph, value) {
|
|
3
|
+
return evaluateNode(graph, graph.root, value, {
|
|
4
|
+
instancePath: [],
|
|
5
|
+
dynamicScope: [],
|
|
6
|
+
activePairs: new Map()
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
function evaluateNode(graph, node, value, context) {
|
|
10
|
+
if (node.schema === true) {
|
|
11
|
+
return validResult();
|
|
12
|
+
}
|
|
13
|
+
if (node.schema === false) {
|
|
14
|
+
return invalidResult(issue(context.instancePath, "valid schema", value, "must NOT be valid"));
|
|
15
|
+
}
|
|
16
|
+
const activeValues = context.activePairs.get(node);
|
|
17
|
+
if (activeValues?.has(value) === true) {
|
|
18
|
+
return validResult();
|
|
19
|
+
}
|
|
20
|
+
const nextActivePairs = new Map(context.activePairs);
|
|
21
|
+
const nextActiveValues = new Set(activeValues ?? []);
|
|
22
|
+
nextActiveValues.add(value);
|
|
23
|
+
nextActivePairs.set(node, nextActiveValues);
|
|
24
|
+
const lastScope = context.dynamicScope.at(-1);
|
|
25
|
+
const dynamicScope = lastScope?.resourceUri === node.resourceRoot.resourceUri
|
|
26
|
+
? context.dynamicScope
|
|
27
|
+
: [...context.dynamicScope, node.resourceRoot];
|
|
28
|
+
const nextContext = { ...context, dynamicScope, activePairs: nextActivePairs };
|
|
29
|
+
const schema = node.schema;
|
|
30
|
+
const referenceResult = evaluateReferences(graph, node, schema, value, nextContext);
|
|
31
|
+
if (node.dialect === "draft7" && typeof schema.$ref === "string") {
|
|
32
|
+
return referenceResult ?? validResult();
|
|
33
|
+
}
|
|
34
|
+
const results = [];
|
|
35
|
+
if (referenceResult !== undefined) {
|
|
36
|
+
results.push(referenceResult);
|
|
37
|
+
}
|
|
38
|
+
results.push(...evaluateApplicators(graph, node, schema, value, nextContext));
|
|
39
|
+
if (node.validationVocabulary) {
|
|
40
|
+
results.push(...evaluateValidationKeywords(node, schema, value, nextContext.instancePath));
|
|
41
|
+
}
|
|
42
|
+
const merged = mergeResults(results);
|
|
43
|
+
if (isObject(value)) {
|
|
44
|
+
evaluateUnevaluatedProperties(graph, node, schema, value, nextContext, merged);
|
|
45
|
+
}
|
|
46
|
+
if (Array.isArray(value)) {
|
|
47
|
+
evaluateUnevaluatedItems(graph, node, schema, value, nextContext, merged);
|
|
48
|
+
}
|
|
49
|
+
return merged;
|
|
50
|
+
}
|
|
51
|
+
function evaluateReferences(graph, node, schema, value, context) {
|
|
52
|
+
const results = [];
|
|
53
|
+
if (typeof schema.$ref === "string") {
|
|
54
|
+
results.push(evaluateNode(graph, graph.resolve(node, schema.$ref), value, context));
|
|
55
|
+
}
|
|
56
|
+
if (typeof schema.$dynamicRef === "string") {
|
|
57
|
+
const staticTarget = graph.resolve(node, schema.$dynamicRef);
|
|
58
|
+
const fragment = new URL(schema.$dynamicRef, node.baseUri).hash.slice(1);
|
|
59
|
+
let target = staticTarget;
|
|
60
|
+
if (fragment !== "" &&
|
|
61
|
+
!fragment.startsWith("/") &&
|
|
62
|
+
isObject(staticTarget.schema) &&
|
|
63
|
+
staticTarget.schema.$dynamicAnchor === fragment) {
|
|
64
|
+
for (const scope of context.dynamicScope) {
|
|
65
|
+
const candidate = graph.dynamicAnchor(scope, fragment);
|
|
66
|
+
if (candidate !== undefined) {
|
|
67
|
+
target = candidate;
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
results.push(evaluateNode(graph, target, value, context));
|
|
73
|
+
}
|
|
74
|
+
if (typeof schema.$recursiveRef === "string") {
|
|
75
|
+
let target = graph.resolve(node, schema.$recursiveRef);
|
|
76
|
+
if (schema.$recursiveRef === "#") {
|
|
77
|
+
for (const scope of context.dynamicScope) {
|
|
78
|
+
if (isObject(scope.schema) && scope.schema.$recursiveAnchor === true) {
|
|
79
|
+
target = scope;
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
results.push(evaluateNode(graph, target, value, context));
|
|
85
|
+
}
|
|
86
|
+
return results.length === 0 ? undefined : mergeResults(results);
|
|
87
|
+
}
|
|
88
|
+
function evaluateApplicators(graph, node, schema, value, context) {
|
|
89
|
+
const results = [];
|
|
90
|
+
for (const keyword of ["allOf", "anyOf", "oneOf"]) {
|
|
91
|
+
const schemas = schema[keyword];
|
|
92
|
+
if (!Array.isArray(schemas)) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const branchResults = schemas.map((_, index) => evaluateChild(graph, node, `${keyword}/${index}`, value, context));
|
|
96
|
+
if (keyword === "allOf") {
|
|
97
|
+
results.push(mergeResults(branchResults));
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
const successful = branchResults.filter((result) => result.valid);
|
|
101
|
+
const valid = keyword === "anyOf" ? successful.length > 0 : successful.length === 1;
|
|
102
|
+
if (valid) {
|
|
103
|
+
results.push(mergeResults(successful));
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
results.push(invalidResult(issue(context.instancePath, keyword, value, keyword === "anyOf"
|
|
107
|
+
? "must match a schema in anyOf"
|
|
108
|
+
: "must match exactly one schema in oneOf")));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (schema.not !== undefined) {
|
|
113
|
+
const result = evaluateChild(graph, node, "not", value, context);
|
|
114
|
+
if (result.valid) {
|
|
115
|
+
results.push(invalidResult(issue(context.instancePath, "not", value, "must NOT be valid")));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (schema.if !== undefined) {
|
|
119
|
+
const condition = evaluateChild(graph, node, "if", value, context);
|
|
120
|
+
const selected = condition.valid ? "then" : "else";
|
|
121
|
+
if (condition.valid) {
|
|
122
|
+
results.push(condition);
|
|
123
|
+
}
|
|
124
|
+
if (schema[selected] !== undefined) {
|
|
125
|
+
results.push(evaluateChild(graph, node, selected, value, context));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (isObject(value)) {
|
|
129
|
+
results.push(...evaluateObjectApplicators(graph, node, schema, value, context));
|
|
130
|
+
}
|
|
131
|
+
if (Array.isArray(value)) {
|
|
132
|
+
results.push(...evaluateArrayApplicators(graph, node, schema, value, context));
|
|
133
|
+
}
|
|
134
|
+
return results;
|
|
135
|
+
}
|
|
136
|
+
function evaluateObjectApplicators(graph, node, schema, value, context) {
|
|
137
|
+
const results = [];
|
|
138
|
+
const evaluated = new Set();
|
|
139
|
+
const properties = isObject(schema.properties) ? schema.properties : {};
|
|
140
|
+
for (const key of Object.keys(properties)) {
|
|
141
|
+
if (Object.prototype.hasOwnProperty.call(value, key)) {
|
|
142
|
+
const result = evaluateChild(graph, node, `properties/${key}`, value[key], childContext(context, key));
|
|
143
|
+
evaluated.add(key);
|
|
144
|
+
results.push(markProperty(atChildLocation(result), key));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const patternProperties = isObject(schema.patternProperties) ? schema.patternProperties : {};
|
|
148
|
+
for (const [pattern] of Object.entries(patternProperties)) {
|
|
149
|
+
const expression = new RegExp(pattern, "u");
|
|
150
|
+
for (const [key, propertyValue] of Object.entries(value)) {
|
|
151
|
+
if (expression.test(key)) {
|
|
152
|
+
const result = evaluateChild(graph, node, `patternProperties/${pattern}`, propertyValue, childContext(context, key));
|
|
153
|
+
evaluated.add(key);
|
|
154
|
+
results.push(markProperty(atChildLocation(result), key));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (schema.additionalProperties !== undefined) {
|
|
159
|
+
for (const [key, propertyValue] of Object.entries(value)) {
|
|
160
|
+
if (!evaluated.has(key)) {
|
|
161
|
+
const result = evaluateChild(graph, node, "additionalProperties", propertyValue, childContext(context, key));
|
|
162
|
+
results.push(markProperty(atChildLocation(result), key));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (schema.propertyNames !== undefined) {
|
|
167
|
+
for (const key of Object.keys(value)) {
|
|
168
|
+
results.push(atChildLocation(evaluateChild(graph, node, "propertyNames", key, childContext(context, key))));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
const dependentSchemas = isObject(schema.dependentSchemas) ? schema.dependentSchemas : {};
|
|
172
|
+
for (const key of Object.keys(dependentSchemas)) {
|
|
173
|
+
if (Object.prototype.hasOwnProperty.call(value, key)) {
|
|
174
|
+
results.push(evaluateChild(graph, node, `dependentSchemas/${key}`, value, context));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (node.dialect === "draft7" && isObject(schema.dependencies)) {
|
|
178
|
+
for (const [key, dependency] of Object.entries(schema.dependencies)) {
|
|
179
|
+
if (Object.prototype.hasOwnProperty.call(value, key) && !Array.isArray(dependency)) {
|
|
180
|
+
results.push(evaluateInline(graph, node, dependency, value, context, `dependencies/${key}`));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return results;
|
|
185
|
+
}
|
|
186
|
+
function evaluateArrayApplicators(graph, node, schema, value, context) {
|
|
187
|
+
const results = [];
|
|
188
|
+
if (node.dialect === "draft2020-12") {
|
|
189
|
+
const prefixItems = Array.isArray(schema.prefixItems) ? schema.prefixItems : [];
|
|
190
|
+
prefixItems.forEach((_, index) => {
|
|
191
|
+
if (index < value.length) {
|
|
192
|
+
const result = evaluateChild(graph, node, `prefixItems/${index}`, value[index], childContext(context, String(index)));
|
|
193
|
+
results.push(markItem(atChildLocation(result), index));
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
if (schema.items !== undefined && !Array.isArray(schema.items)) {
|
|
197
|
+
for (let index = prefixItems.length; index < value.length; index += 1) {
|
|
198
|
+
const result = evaluateChild(graph, node, "items", value[index], childContext(context, String(index)));
|
|
199
|
+
results.push(markItem(atChildLocation(result), index));
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
else if (Array.isArray(schema.items)) {
|
|
204
|
+
schema.items.forEach((_, index) => {
|
|
205
|
+
if (index < value.length) {
|
|
206
|
+
const result = evaluateChild(graph, node, `items/${index}`, value[index], childContext(context, String(index)));
|
|
207
|
+
results.push(markItem(atChildLocation(result), index));
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
if (schema.additionalItems !== undefined) {
|
|
211
|
+
for (let index = schema.items.length; index < value.length; index += 1) {
|
|
212
|
+
const result = evaluateChild(graph, node, "additionalItems", value[index], childContext(context, String(index)));
|
|
213
|
+
results.push(markItem(atChildLocation(result), index));
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
else if (schema.items !== undefined) {
|
|
218
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
219
|
+
const result = evaluateChild(graph, node, "items", value[index], childContext(context, String(index)));
|
|
220
|
+
results.push(markItem(atChildLocation(result), index));
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (schema.contains !== undefined) {
|
|
224
|
+
const containsResults = value.map((item, index) => evaluateChild(graph, node, "contains", item, childContext(context, String(index))));
|
|
225
|
+
const matching = containsResults
|
|
226
|
+
.map((result, index) => ({ result, index }))
|
|
227
|
+
.filter(({ result }) => result.valid);
|
|
228
|
+
const minimum = node.dialect === "draft2020-12" && typeof schema.minContains === "number"
|
|
229
|
+
? schema.minContains
|
|
230
|
+
: 1;
|
|
231
|
+
const maximum = node.dialect === "draft2020-12" && typeof schema.maxContains === "number"
|
|
232
|
+
? schema.maxContains
|
|
233
|
+
: Number.POSITIVE_INFINITY;
|
|
234
|
+
if (matching.length < minimum || matching.length > maximum) {
|
|
235
|
+
results.push(invalidResult(issue(context.instancePath, "contains", value, "must contain required matching items")));
|
|
236
|
+
}
|
|
237
|
+
else if (node.dialect === "draft2020-12") {
|
|
238
|
+
const result = validResult();
|
|
239
|
+
for (const match of matching) {
|
|
240
|
+
result.evaluatedItems.add(match.index);
|
|
241
|
+
}
|
|
242
|
+
results.push(result);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return results;
|
|
246
|
+
}
|
|
247
|
+
function evaluateValidationKeywords(node, schema, value, path) {
|
|
248
|
+
const results = [];
|
|
249
|
+
if (schema.nullable === true && value === null) {
|
|
250
|
+
return results;
|
|
251
|
+
}
|
|
252
|
+
const types = typeof schema.type === "string" ? [schema.type] : Array.isArray(schema.type) ? schema.type : [];
|
|
253
|
+
if (types.length > 0 &&
|
|
254
|
+
!types.some((type) => typeof type === "string" && typeMatches(type, value))) {
|
|
255
|
+
results.push(invalidResult(issue(path, types.join(","), value, `must be ${types.join(",")}`)));
|
|
256
|
+
return results;
|
|
257
|
+
}
|
|
258
|
+
if (schema.const !== undefined && !deepEqual(schema.const, value)) {
|
|
259
|
+
results.push(invalidResult(issue(path, "const", value, "must be equal to constant")));
|
|
260
|
+
}
|
|
261
|
+
if (Array.isArray(schema.enum) && !schema.enum.some((entry) => deepEqual(entry, value))) {
|
|
262
|
+
results.push(invalidResult(issue(path, "enum", value, "must be equal to one of the allowed values")));
|
|
263
|
+
}
|
|
264
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
265
|
+
evaluateNumberKeywords(schema, value, path, results);
|
|
266
|
+
}
|
|
267
|
+
if (typeof value === "string") {
|
|
268
|
+
evaluateStringKeywords(schema, value, path, results);
|
|
269
|
+
}
|
|
270
|
+
if (Array.isArray(value)) {
|
|
271
|
+
evaluateArrayKeywords(schema, value, path, results);
|
|
272
|
+
}
|
|
273
|
+
if (isObject(value)) {
|
|
274
|
+
evaluateObjectKeywords(node, schema, value, path, results);
|
|
275
|
+
}
|
|
276
|
+
return results;
|
|
277
|
+
}
|
|
278
|
+
function evaluateNumberKeywords(schema, value, path, results) {
|
|
279
|
+
if (typeof schema.multipleOf === "number" && !isMultipleOf(value, schema.multipleOf)) {
|
|
280
|
+
results.push(invalidResult(issue(path, `multiple of ${schema.multipleOf}`, value, `must be multiple of ${schema.multipleOf}`)));
|
|
281
|
+
}
|
|
282
|
+
if (typeof schema.maximum === "number" && value > schema.maximum) {
|
|
283
|
+
results.push(invalidResult(issue(path, `<= ${schema.maximum}`, value, `must be <= ${schema.maximum}`)));
|
|
284
|
+
}
|
|
285
|
+
if (typeof schema.minimum === "number" && value < schema.minimum) {
|
|
286
|
+
results.push(invalidResult(issue(path, `>= ${schema.minimum}`, value, `must be >= ${schema.minimum}`)));
|
|
287
|
+
}
|
|
288
|
+
if (typeof schema.exclusiveMaximum === "number" && value >= schema.exclusiveMaximum) {
|
|
289
|
+
results.push(invalidResult(issue(path, `< ${schema.exclusiveMaximum}`, value, `must be < ${schema.exclusiveMaximum}`)));
|
|
290
|
+
}
|
|
291
|
+
if (typeof schema.exclusiveMinimum === "number" && value <= schema.exclusiveMinimum) {
|
|
292
|
+
results.push(invalidResult(issue(path, `> ${schema.exclusiveMinimum}`, value, `must be > ${schema.exclusiveMinimum}`)));
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
function evaluateStringKeywords(schema, value, path, results) {
|
|
296
|
+
const length = unicodeLength(value);
|
|
297
|
+
if (typeof schema.maxLength === "number" && length > schema.maxLength) {
|
|
298
|
+
results.push(invalidResult(issue(path, `length <= ${schema.maxLength}`, value, `must NOT have more than ${schema.maxLength} characters`)));
|
|
299
|
+
}
|
|
300
|
+
if (typeof schema.minLength === "number" && length < schema.minLength) {
|
|
301
|
+
results.push(invalidResult(issue(path, `length >= ${schema.minLength}`, value, `must NOT have fewer than ${schema.minLength} characters`)));
|
|
302
|
+
}
|
|
303
|
+
if (typeof schema.pattern === "string" && !new RegExp(schema.pattern, "u").test(value)) {
|
|
304
|
+
results.push(invalidResult(issue(path, `pattern ${schema.pattern}`, value, `must match pattern ${schema.pattern}`)));
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
function evaluateArrayKeywords(schema, value, path, results) {
|
|
308
|
+
if (typeof schema.maxItems === "number" && value.length > schema.maxItems) {
|
|
309
|
+
results.push(invalidResult(issue(path, `items <= ${schema.maxItems}`, value, `must NOT have more than ${schema.maxItems} items`)));
|
|
310
|
+
}
|
|
311
|
+
if (typeof schema.minItems === "number" && value.length < schema.minItems) {
|
|
312
|
+
results.push(invalidResult(issue(path, `items >= ${schema.minItems}`, value, `must NOT have fewer than ${schema.minItems} items`)));
|
|
313
|
+
}
|
|
314
|
+
if (schema.uniqueItems === true) {
|
|
315
|
+
for (let left = 0; left < value.length; left += 1) {
|
|
316
|
+
for (let right = left + 1; right < value.length; right += 1) {
|
|
317
|
+
if (deepEqual(value[left], value[right])) {
|
|
318
|
+
results.push(invalidResult(issue(path, "unique items", value, "must NOT have duplicate items")));
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
function evaluateObjectKeywords(node, schema, value, path, results) {
|
|
326
|
+
const keys = Object.keys(value);
|
|
327
|
+
if (typeof schema.maxProperties === "number" && keys.length > schema.maxProperties) {
|
|
328
|
+
results.push(invalidResult(issue(path, `properties <= ${schema.maxProperties}`, value, `must NOT have more than ${schema.maxProperties} properties`)));
|
|
329
|
+
}
|
|
330
|
+
if (typeof schema.minProperties === "number" && keys.length < schema.minProperties) {
|
|
331
|
+
results.push(invalidResult(issue(path, `properties >= ${schema.minProperties}`, value, `must NOT have fewer than ${schema.minProperties} properties`)));
|
|
332
|
+
}
|
|
333
|
+
if (Array.isArray(schema.required)) {
|
|
334
|
+
for (const key of schema.required) {
|
|
335
|
+
if (typeof key === "string" && !Object.prototype.hasOwnProperty.call(value, key)) {
|
|
336
|
+
results.push(invalidResult(issue([...path, key], "required", undefined, `must have required property '${key}'`)));
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
const dependentRequired = isObject(schema.dependentRequired) ? schema.dependentRequired : {};
|
|
341
|
+
for (const [key, dependencies] of Object.entries(dependentRequired)) {
|
|
342
|
+
if (Object.prototype.hasOwnProperty.call(value, key) && Array.isArray(dependencies)) {
|
|
343
|
+
addMissingDependencies(value, dependencies, path, results);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
if (node.dialect === "draft7" && isObject(schema.dependencies)) {
|
|
347
|
+
for (const [key, dependencies] of Object.entries(schema.dependencies)) {
|
|
348
|
+
if (Object.prototype.hasOwnProperty.call(value, key) && Array.isArray(dependencies)) {
|
|
349
|
+
addMissingDependencies(value, dependencies, path, results);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
function addMissingDependencies(value, dependencies, path, results) {
|
|
355
|
+
for (const dependency of dependencies) {
|
|
356
|
+
if (typeof dependency === "string" &&
|
|
357
|
+
!Object.prototype.hasOwnProperty.call(value, dependency)) {
|
|
358
|
+
results.push(invalidResult(issue([...path, dependency], "dependency", undefined, `must have property '${dependency}'`)));
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
function evaluateUnevaluatedProperties(graph, node, schema, value, context, result) {
|
|
363
|
+
if (node.dialect !== "draft2020-12" || schema.unevaluatedProperties === undefined) {
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
for (const [key, propertyValue] of Object.entries(value)) {
|
|
367
|
+
if (!result.evaluatedProperties.has(key)) {
|
|
368
|
+
const propertyResult = evaluateChild(graph, node, "unevaluatedProperties", propertyValue, childContext(context, key));
|
|
369
|
+
propertyResult.evaluatedProperties.add(key);
|
|
370
|
+
mergeInto(result, propertyResult);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
function evaluateUnevaluatedItems(graph, node, schema, value, context, result) {
|
|
375
|
+
if (node.dialect !== "draft2020-12" || schema.unevaluatedItems === undefined) {
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
379
|
+
if (!result.evaluatedItems.has(index)) {
|
|
380
|
+
const itemResult = evaluateChild(graph, node, "unevaluatedItems", value[index], childContext(context, String(index)));
|
|
381
|
+
itemResult.evaluatedItems.add(index);
|
|
382
|
+
mergeInto(result, itemResult);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
function evaluateChild(graph, node, key, value, context) {
|
|
387
|
+
const child = node.children.get(key);
|
|
388
|
+
if (child === undefined) {
|
|
389
|
+
return validResult();
|
|
390
|
+
}
|
|
391
|
+
return evaluateNode(graph, child, value, context);
|
|
392
|
+
}
|
|
393
|
+
function evaluateInline(graph, node, schema, value, context, key) {
|
|
394
|
+
const child = node.children.get(key);
|
|
395
|
+
if (child !== undefined) {
|
|
396
|
+
return evaluateNode(graph, child, value, context);
|
|
397
|
+
}
|
|
398
|
+
if (typeof schema !== "boolean" && !isObject(schema)) {
|
|
399
|
+
return validResult();
|
|
400
|
+
}
|
|
401
|
+
const inlineNode = {
|
|
402
|
+
schema: schema,
|
|
403
|
+
documentId: node.documentId,
|
|
404
|
+
dialect: node.dialect,
|
|
405
|
+
baseUri: node.baseUri,
|
|
406
|
+
resourceUri: node.resourceUri,
|
|
407
|
+
resourceRoot: node.resourceRoot,
|
|
408
|
+
pointer: node.pointer,
|
|
409
|
+
validationVocabulary: node.validationVocabulary,
|
|
410
|
+
children: new Map()
|
|
411
|
+
};
|
|
412
|
+
return evaluateNode(graph, inlineNode, value, context);
|
|
413
|
+
}
|
|
414
|
+
function childContext(context, segment) {
|
|
415
|
+
return { ...context, instancePath: [...context.instancePath, segment] };
|
|
416
|
+
}
|
|
417
|
+
function mergeInto(target, source) {
|
|
418
|
+
target.valid &&= source.valid;
|
|
419
|
+
target.issues.push(...source.issues);
|
|
420
|
+
for (const key of source.evaluatedProperties) {
|
|
421
|
+
target.evaluatedProperties.add(key);
|
|
422
|
+
}
|
|
423
|
+
for (const index of source.evaluatedItems) {
|
|
424
|
+
target.evaluatedItems.add(index);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
function atChildLocation(result) {
|
|
428
|
+
return {
|
|
429
|
+
valid: result.valid,
|
|
430
|
+
issues: result.issues,
|
|
431
|
+
evaluatedProperties: new Set(),
|
|
432
|
+
evaluatedItems: new Set()
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
function markProperty(result, key) {
|
|
436
|
+
result.evaluatedProperties.add(key);
|
|
437
|
+
return result;
|
|
438
|
+
}
|
|
439
|
+
function markItem(result, index) {
|
|
440
|
+
result.evaluatedItems.add(index);
|
|
441
|
+
return result;
|
|
442
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ValidationIssue } from "../validate.js";
|
|
2
|
+
import type { CompileJsonSchemaOptions, CompiledJsonSchema } from "./types.js";
|
|
3
|
+
export type { CompileJsonSchemaOptions, CompiledJsonSchema } from "./types.js";
|
|
4
|
+
export declare function compileJsonSchema(schema: unknown, options?: CompileJsonSchemaOptions): CompiledJsonSchema;
|
|
5
|
+
export declare function formatIssues(issues: readonly ValidationIssue[]): string;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { compileGraph } from "./compiler.js";
|
|
2
|
+
import { evaluateSchema } from "./evaluate.js";
|
|
3
|
+
export function compileJsonSchema(schema, options = {}) {
|
|
4
|
+
const graph = compileGraph(schema, options);
|
|
5
|
+
return {
|
|
6
|
+
validate(value) {
|
|
7
|
+
const result = evaluateSchema(graph, value);
|
|
8
|
+
return result.valid ? { ok: true, value } : { ok: false, issues: result.issues };
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export function formatIssues(issues) {
|
|
13
|
+
return issues
|
|
14
|
+
.map((current) => {
|
|
15
|
+
const path = current.path.length === 0 ? "data" : `data/${current.path.join("/")}`;
|
|
16
|
+
return `${path} ${current.message}`;
|
|
17
|
+
})
|
|
18
|
+
.join(", ");
|
|
19
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ValidationIssue, ValidationResult } from "../validate.js";
|
|
2
|
+
export type JsonSchemaRegistry = Record<string, unknown>;
|
|
3
|
+
export interface CompileJsonSchemaOptions {
|
|
4
|
+
registry?: JsonSchemaRegistry;
|
|
5
|
+
}
|
|
6
|
+
export interface CompiledJsonSchema {
|
|
7
|
+
validate(value: unknown): ValidationResult<unknown>;
|
|
8
|
+
}
|
|
9
|
+
export type Dialect = "draft7" | "draft2020-12";
|
|
10
|
+
export type SchemaObject = Record<string, unknown>;
|
|
11
|
+
export type JsonSchema = boolean | SchemaObject;
|
|
12
|
+
export interface SchemaNode {
|
|
13
|
+
schema: JsonSchema;
|
|
14
|
+
documentId: string;
|
|
15
|
+
dialect: Dialect;
|
|
16
|
+
baseUri: string;
|
|
17
|
+
resourceUri: string;
|
|
18
|
+
resourceRoot: SchemaNode;
|
|
19
|
+
pointer: string;
|
|
20
|
+
validationVocabulary: boolean;
|
|
21
|
+
children: Map<string, SchemaNode>;
|
|
22
|
+
}
|
|
23
|
+
export interface EvaluationResult {
|
|
24
|
+
valid: boolean;
|
|
25
|
+
issues: ValidationIssue[];
|
|
26
|
+
evaluatedProperties: Set<string>;
|
|
27
|
+
evaluatedItems: Set<number>;
|
|
28
|
+
}
|
|
29
|
+
export interface EvaluationContext {
|
|
30
|
+
instancePath: readonly string[];
|
|
31
|
+
dynamicScope: readonly SchemaNode[];
|
|
32
|
+
activePairs: Map<SchemaNode, Set<unknown>>;
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ValidationIssue } from "../validate.js";
|
|
2
|
+
import type { Dialect, EvaluationResult, JsonSchema, SchemaObject } from "./types.js";
|
|
3
|
+
export declare function isSchema(value: unknown): value is JsonSchema;
|
|
4
|
+
export declare function isObject(value: unknown): value is SchemaObject;
|
|
5
|
+
export declare function dialectFor(schema: SchemaObject, inherited: Dialect): Dialect;
|
|
6
|
+
export declare function resolveUri(reference: string, baseUri: string): string;
|
|
7
|
+
export declare function withoutFragment(uri: string): string;
|
|
8
|
+
export declare function fragmentOf(uri: string): string;
|
|
9
|
+
export declare function escapePointer(value: string): string;
|
|
10
|
+
export declare function decodePointer(value: string): string;
|
|
11
|
+
export declare function deepEqual(left: unknown, right: unknown): boolean;
|
|
12
|
+
export declare function receivedType(value: unknown): string;
|
|
13
|
+
export declare function issue(path: readonly string[], expected: string, value: unknown, message: string, keyword?: string): ValidationIssue;
|
|
14
|
+
export declare function validResult(): EvaluationResult;
|
|
15
|
+
export declare function invalidResult(problem: ValidationIssue): EvaluationResult;
|
|
16
|
+
export declare function mergeResults(results: readonly EvaluationResult[]): EvaluationResult;
|
|
17
|
+
export declare function typeMatches(type: string, value: unknown): boolean;
|
|
18
|
+
export declare function unicodeLength(value: string): number;
|
|
19
|
+
export declare function isMultipleOf(value: number, divisor: number): boolean;
|