z-schema 6.0.2 → 7.0.0-beta.2
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/README.md +154 -134
- package/bin/z-schema +128 -124
- package/cjs/ZSchema.d.ts +227 -0
- package/cjs/ZSchema.js +13785 -0
- package/dist/Errors.js +50 -0
- package/dist/FormatValidators.js +136 -0
- package/{src → dist}/JsonValidation.js +184 -213
- package/dist/Report.js +220 -0
- package/{src → dist}/SchemaCache.js +67 -82
- package/{src → dist}/SchemaCompilation.js +89 -129
- package/dist/SchemaValidation.js +631 -0
- package/{src → dist}/Utils.js +96 -104
- package/dist/ZSchema.js +365 -0
- package/dist/index.js +2 -0
- package/dist/schemas/hyper-schema.json +156 -0
- package/dist/schemas/schema.json +151 -0
- package/dist/types/Errors.d.ts +44 -0
- package/dist/types/FormatValidators.d.ts +12 -0
- package/dist/types/JsonValidation.d.ts +37 -0
- package/dist/types/Report.d.ts +87 -0
- package/dist/types/SchemaCache.d.ts +26 -0
- package/dist/types/SchemaCompilation.d.ts +1 -0
- package/dist/types/SchemaValidation.d.ts +6 -0
- package/dist/types/Utils.d.ts +64 -0
- package/dist/types/ZSchema.d.ts +97 -0
- package/dist/types/index.d.ts +2 -0
- package/package.json +59 -45
- package/src/Errors.ts +56 -0
- package/src/FormatValidators.ts +136 -0
- package/src/JsonValidation.ts +624 -0
- package/src/Report.ts +337 -0
- package/src/SchemaCache.ts +189 -0
- package/src/SchemaCompilation.ts +293 -0
- package/src/SchemaValidation.ts +629 -0
- package/src/Utils.ts +286 -0
- package/src/ZSchema.ts +467 -0
- package/src/index.ts +3 -0
- package/src/schemas/_ +0 -0
- package/umd/ZSchema.js +13791 -0
- package/umd/ZSchema.min.js +1 -0
- package/dist/ZSchema-browser-min.js +0 -2
- package/dist/ZSchema-browser-min.js.map +0 -1
- package/dist/ZSchema-browser-test.js +0 -32247
- package/dist/ZSchema-browser.js +0 -12745
- package/index.d.ts +0 -175
- package/src/Errors.js +0 -60
- package/src/FormatValidators.js +0 -129
- package/src/Polyfills.js +0 -16
- package/src/Report.js +0 -299
- package/src/SchemaValidation.js +0 -619
- package/src/ZSchema.js +0 -409
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { Report } from './Report.js';
|
|
2
|
+
import { getSchemaByUri, checkCacheForUri, cacheSchemaByUri, removeFromCacheByUri } from './SchemaCache.js';
|
|
3
|
+
import * as Utils from './Utils.js';
|
|
4
|
+
|
|
5
|
+
function mergeReference(scope, ref) {
|
|
6
|
+
if (Utils.isAbsoluteUri(ref)) {
|
|
7
|
+
return ref;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
let joinedScope = scope.join('');
|
|
11
|
+
const isScopeAbsolute = Utils.isAbsoluteUri(joinedScope);
|
|
12
|
+
const isScopeRelative = Utils.isRelativeUri(joinedScope);
|
|
13
|
+
const isRefRelative = Utils.isRelativeUri(ref);
|
|
14
|
+
let toRemove;
|
|
15
|
+
|
|
16
|
+
if (isScopeAbsolute && isRefRelative) {
|
|
17
|
+
toRemove = joinedScope.match(/\/[^/]*$/);
|
|
18
|
+
if (toRemove) {
|
|
19
|
+
joinedScope = joinedScope.slice(0, toRemove.index + 1);
|
|
20
|
+
}
|
|
21
|
+
} else if (isScopeRelative && isRefRelative) {
|
|
22
|
+
joinedScope = '';
|
|
23
|
+
} else {
|
|
24
|
+
toRemove = joinedScope.match(/[^#/]+$/);
|
|
25
|
+
if (toRemove) {
|
|
26
|
+
joinedScope = joinedScope.slice(0, toRemove.index);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let res = joinedScope + ref;
|
|
31
|
+
res = res.replace(/##/, '#');
|
|
32
|
+
return res;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function collectReferences(obj, results, scope, path) {
|
|
36
|
+
results = results || [];
|
|
37
|
+
scope = scope || [];
|
|
38
|
+
path = path || [];
|
|
39
|
+
|
|
40
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
41
|
+
return results;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (typeof obj.id === 'string') {
|
|
45
|
+
scope.push(obj.id);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (typeof obj.$ref === 'string' && typeof obj.__$refResolved === 'undefined') {
|
|
49
|
+
results.push({
|
|
50
|
+
ref: mergeReference(scope, obj.$ref),
|
|
51
|
+
key: '$ref',
|
|
52
|
+
obj: obj,
|
|
53
|
+
path: path.slice(0),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
if (typeof obj.$schema === 'string' && typeof obj.__$schemaResolved === 'undefined') {
|
|
57
|
+
results.push({
|
|
58
|
+
ref: mergeReference(scope, obj.$schema),
|
|
59
|
+
key: '$schema',
|
|
60
|
+
obj: obj,
|
|
61
|
+
path: path.slice(0),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let idx;
|
|
66
|
+
if (Array.isArray(obj)) {
|
|
67
|
+
idx = obj.length;
|
|
68
|
+
while (idx--) {
|
|
69
|
+
path.push(idx.toString());
|
|
70
|
+
collectReferences(obj[idx], results, scope, path);
|
|
71
|
+
path.pop();
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
const keys = Object.keys(obj);
|
|
75
|
+
idx = keys.length;
|
|
76
|
+
while (idx--) {
|
|
77
|
+
// do not recurse through resolved references and other z-schema props
|
|
78
|
+
if (keys[idx].indexOf('__$') === 0) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
path.push(keys[idx]);
|
|
82
|
+
collectReferences(obj[keys[idx]], results, scope, path);
|
|
83
|
+
path.pop();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (typeof obj.id === 'string') {
|
|
88
|
+
scope.pop();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return results;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const compileArrayOfSchemasLoop = function (mainReport, arr) {
|
|
95
|
+
let idx = arr.length,
|
|
96
|
+
compiledCount = 0;
|
|
97
|
+
|
|
98
|
+
while (idx--) {
|
|
99
|
+
// try to compile each schema separately
|
|
100
|
+
const report = new Report(mainReport);
|
|
101
|
+
const isValid = compileSchema.call(this, report, arr[idx]);
|
|
102
|
+
if (isValid) {
|
|
103
|
+
compiledCount++;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// copy errors to report
|
|
107
|
+
mainReport.errors = mainReport.errors.concat(report.errors);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return compiledCount;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
function findId(arr, id) {
|
|
114
|
+
let idx = arr.length;
|
|
115
|
+
while (idx--) {
|
|
116
|
+
if (arr[idx].id === id) {
|
|
117
|
+
return arr[idx];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const compileArrayOfSchemas = function (report, arr) {
|
|
124
|
+
let compiled = 0,
|
|
125
|
+
lastLoopCompiled;
|
|
126
|
+
|
|
127
|
+
do {
|
|
128
|
+
// remove all UNRESOLVABLE_REFERENCE errors before compiling array again
|
|
129
|
+
let idx = report.errors.length;
|
|
130
|
+
while (idx--) {
|
|
131
|
+
if (report.errors[idx].code === 'UNRESOLVABLE_REFERENCE') {
|
|
132
|
+
report.errors.splice(idx, 1);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// remember how many were compiled in the last loop
|
|
137
|
+
lastLoopCompiled = compiled;
|
|
138
|
+
|
|
139
|
+
// count how many are compiled now
|
|
140
|
+
compiled = compileArrayOfSchemasLoop.call(this, report, arr);
|
|
141
|
+
|
|
142
|
+
// fix __$missingReferences if possible
|
|
143
|
+
idx = arr.length;
|
|
144
|
+
while (idx--) {
|
|
145
|
+
const sch = arr[idx];
|
|
146
|
+
if (sch.__$missingReferences) {
|
|
147
|
+
let idx2 = sch.__$missingReferences.length;
|
|
148
|
+
while (idx2--) {
|
|
149
|
+
const refObj = sch.__$missingReferences[idx2];
|
|
150
|
+
const response = findId(arr, refObj.ref);
|
|
151
|
+
if (response) {
|
|
152
|
+
// this might create circular references
|
|
153
|
+
refObj.obj['__' + refObj.key + 'Resolved'] = response;
|
|
154
|
+
// it's resolved now so delete it
|
|
155
|
+
sch.__$missingReferences.splice(idx2, 1);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (sch.__$missingReferences.length === 0) {
|
|
159
|
+
delete sch.__$missingReferences;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// keep repeating if not all compiled and at least one more was compiled in the last loop
|
|
165
|
+
} while (compiled !== arr.length && compiled !== lastLoopCompiled);
|
|
166
|
+
|
|
167
|
+
return report.isValid();
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
export function compileSchema(report, schema) {
|
|
171
|
+
report.commonErrorMessage = 'SCHEMA_COMPILATION_FAILED';
|
|
172
|
+
|
|
173
|
+
// if schema is a string, assume it's a uri
|
|
174
|
+
if (typeof schema === 'string') {
|
|
175
|
+
const loadedSchema = getSchemaByUri.call(this, report, schema);
|
|
176
|
+
if (!loadedSchema) {
|
|
177
|
+
report.addError('SCHEMA_NOT_REACHABLE', [schema]);
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
schema = loadedSchema;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// if schema is an array, assume it's an array of schemas
|
|
184
|
+
if (Array.isArray(schema)) {
|
|
185
|
+
return compileArrayOfSchemas.call(this, report, schema);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// if we have an id than it should be cached already (if this instance has compiled it)
|
|
189
|
+
if (schema.__$compiled && schema.id && checkCacheForUri.call(this, schema.id) === false) {
|
|
190
|
+
schema.__$compiled = undefined;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// do not re-compile schemas
|
|
194
|
+
if (schema.__$compiled) {
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (schema.id && typeof schema.id === 'string') {
|
|
199
|
+
// add this to our schemaCache (before compilation in case we have references including id)
|
|
200
|
+
cacheSchemaByUri.call(this, schema.id, schema);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// this method can be called recursively, so we need to remember our root
|
|
204
|
+
let isRoot = false;
|
|
205
|
+
if (!report.rootSchema) {
|
|
206
|
+
report.rootSchema = schema;
|
|
207
|
+
isRoot = true;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// delete all __$missingReferences from previous compilation attempts
|
|
211
|
+
const isValidExceptReferences = report.isValid();
|
|
212
|
+
delete schema.__$missingReferences;
|
|
213
|
+
|
|
214
|
+
// collect all references that need to be resolved - $ref and $schema
|
|
215
|
+
const refs = collectReferences.call(this, schema);
|
|
216
|
+
let idx = refs.length;
|
|
217
|
+
while (idx--) {
|
|
218
|
+
// resolve all the collected references into __xxxResolved pointer
|
|
219
|
+
const refObj = refs[idx];
|
|
220
|
+
let response = getSchemaByUri.call(this, report, refObj.ref, schema);
|
|
221
|
+
|
|
222
|
+
// we can try to use custom schemaReader if available
|
|
223
|
+
if (!response) {
|
|
224
|
+
const schemaReader = this.getSchemaReader();
|
|
225
|
+
if (schemaReader) {
|
|
226
|
+
// it's supposed to return a valid schema
|
|
227
|
+
const s = schemaReader(refObj.ref);
|
|
228
|
+
if (s) {
|
|
229
|
+
// it needs to have the id
|
|
230
|
+
s.id = refObj.ref;
|
|
231
|
+
// try to compile the schema
|
|
232
|
+
const subreport = new Report(report);
|
|
233
|
+
if (!compileSchema.call(this, subreport, s)) {
|
|
234
|
+
// copy errors to report
|
|
235
|
+
report.errors = report.errors.concat(subreport.errors);
|
|
236
|
+
} else {
|
|
237
|
+
response = getSchemaByUri.call(this, report, refObj.ref, schema);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (!response) {
|
|
244
|
+
const hasNotValid = report.hasError('REMOTE_NOT_VALID', [refObj.ref]);
|
|
245
|
+
const isAbsolute = Utils.isAbsoluteUri(refObj.ref);
|
|
246
|
+
let isDownloaded = false;
|
|
247
|
+
const ignoreUnresolvableRemotes = this.options.ignoreUnresolvableReferences === true;
|
|
248
|
+
|
|
249
|
+
if (isAbsolute) {
|
|
250
|
+
// we shouldn't add UNRESOLVABLE_REFERENCE for schemas we already have downloaded
|
|
251
|
+
// and set through setRemoteReference method
|
|
252
|
+
isDownloaded = checkCacheForUri.call(this, refObj.ref);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (hasNotValid) {
|
|
256
|
+
// already has REMOTE_NOT_VALID error for this one
|
|
257
|
+
} else if (ignoreUnresolvableRemotes && isAbsolute) {
|
|
258
|
+
// ignoreUnresolvableRemotes is on and remote isAbsolute
|
|
259
|
+
} else if (isDownloaded) {
|
|
260
|
+
// remote is downloaded, so no UNRESOLVABLE_REFERENCE
|
|
261
|
+
} else {
|
|
262
|
+
Array.prototype.push.apply(report.path, refObj.path);
|
|
263
|
+
report.addError('UNRESOLVABLE_REFERENCE', [refObj.ref]);
|
|
264
|
+
report.path = report.path.slice(0, -refObj.path.length);
|
|
265
|
+
|
|
266
|
+
// pusblish unresolved references out
|
|
267
|
+
if (isValidExceptReferences) {
|
|
268
|
+
schema.__$missingReferences = schema.__$missingReferences || [];
|
|
269
|
+
schema.__$missingReferences.push(refObj);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
// this might create circular references
|
|
274
|
+
refObj.obj['__' + refObj.key + 'Resolved'] = response;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const isValid = report.isValid();
|
|
278
|
+
if (isValid) {
|
|
279
|
+
schema.__$compiled = true;
|
|
280
|
+
} else {
|
|
281
|
+
if (schema.id && typeof schema.id === 'string') {
|
|
282
|
+
// remove this schema from schemaCache because it failed to compile
|
|
283
|
+
removeFromCacheByUri.call(this, schema.id);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// we don't need the root pointer anymore
|
|
288
|
+
if (isRoot) {
|
|
289
|
+
report.rootSchema = undefined;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return isValid;
|
|
293
|
+
}
|