z-schema 9.0.1 → 10.0.0
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 +123 -191
- package/cjs/index.d.ts +33 -9
- package/cjs/index.js +4799 -3984
- package/dist/errors.js +5 -0
- package/dist/format-validators.js +65 -0
- package/dist/json-schema-versions.js +5 -0
- package/dist/json-schema.js +11 -4
- package/dist/json-validation.js +151 -10
- package/dist/report.js +2 -3
- package/dist/schema-cache.js +23 -2
- package/dist/schema-compiler.js +25 -10
- package/dist/schema-validator.js +66 -45
- package/dist/schemas/draft-06-hyper-schema.json +132 -0
- package/dist/schemas/draft-06-links.json +43 -0
- package/dist/schemas/draft-06-schema.json +155 -0
- package/dist/types/errors.d.ts +4 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/json-schema-versions.d.ts +23 -0
- package/dist/types/json-schema.d.ts +5 -9
- package/dist/types/json-validation.d.ts +3 -3
- package/dist/types/report.d.ts +3 -3
- package/dist/types/schema-cache.d.ts +1 -1
- package/dist/types/schema-compiler.d.ts +1 -1
- package/dist/types/schema-validator.d.ts +1 -1
- package/dist/types/utils/what-is.d.ts +1 -0
- package/dist/types/z-schema-base.d.ts +1 -1
- package/dist/types/z-schema-options.d.ts +1 -1
- package/dist/types/z-schema-reader.d.ts +1 -1
- package/dist/types/z-schema-versions.d.ts +1 -0
- package/dist/types/z-schema.d.ts +10 -1
- package/dist/utils/schema-regex.js +4 -3
- package/dist/utils/what-is.js +4 -1
- package/dist/z-schema-base.js +4 -5
- package/dist/z-schema-options.js +3 -1
- package/dist/z-schema-versions.js +27 -0
- package/dist/z-schema.js +21 -7
- package/package.json +2 -2
- package/src/errors.ts +6 -0
- package/src/format-validators.ts +65 -0
- package/src/index.ts +2 -1
- package/src/json-schema-versions.ts +34 -0
- package/src/json-schema.ts +22 -16
- package/src/json-validation.ts +183 -13
- package/src/report.ts +5 -6
- package/src/schema-cache.ts +25 -3
- package/src/schema-compiler.ts +25 -11
- package/src/schema-validator.ts +128 -62
- package/src/schemas/draft-06-hyper-schema.json +133 -0
- package/src/schemas/draft-06-links.json +43 -0
- package/src/schemas/draft-06-schema.json +155 -0
- package/src/utils/schema-regex.ts +5 -3
- package/src/utils/what-is.ts +5 -1
- package/src/z-schema-base.ts +5 -6
- package/src/z-schema-options.ts +3 -2
- package/src/z-schema-reader.ts +1 -1
- package/src/z-schema-versions.ts +38 -0
- package/src/z-schema.ts +27 -11
- package/umd/ZSchema.js +5100 -4285
- package/umd/ZSchema.min.js +1 -1
package/dist/errors.js
CHANGED
|
@@ -47,6 +47,11 @@ export const Errors = {
|
|
|
47
47
|
ASYNC_TIMEOUT: '{0} asynchronous task(s) have timed out after {1} ms',
|
|
48
48
|
PARENT_SCHEMA_VALIDATION_FAILED: 'Schema failed to validate against its parent schema, see inner errors for details.',
|
|
49
49
|
REMOTE_NOT_VALID: "Remote reference didn't compile successfully: {0}",
|
|
50
|
+
// Draft-06 errors
|
|
51
|
+
SCHEMA_IS_FALSE: 'Boolean schema "false" is always invalid.',
|
|
52
|
+
CONST: 'Value does not match const: {0}',
|
|
53
|
+
CONTAINS: 'Array does not contain an item matching the schema',
|
|
54
|
+
PROPERTY_NAMES: 'Property name {0} does not match the propertyNames schema',
|
|
50
55
|
};
|
|
51
56
|
export class ValidateError extends Error {
|
|
52
57
|
name;
|
|
@@ -191,6 +191,65 @@ const uriValidator = function (uri) {
|
|
|
191
191
|
}
|
|
192
192
|
return /^[a-zA-Z][a-zA-Z0-9+.-]*:[^"\\<>^{}^`| ]*$/.test(uri);
|
|
193
193
|
};
|
|
194
|
+
const uriReferenceValidator = (uri) => {
|
|
195
|
+
if (typeof uri !== 'string')
|
|
196
|
+
return true;
|
|
197
|
+
// eslint-disable-next-line no-control-regex
|
|
198
|
+
if (/[^\x00-\x7F]/.test(uri))
|
|
199
|
+
return false;
|
|
200
|
+
// URI-reference allows relative URIs
|
|
201
|
+
return /^([a-zA-Z][a-zA-Z0-9+.-]*:)?[^"\\<>^{}^`| ]*$/.test(uri);
|
|
202
|
+
};
|
|
203
|
+
const uriTemplateValidator = (uri) => {
|
|
204
|
+
if (typeof uri !== 'string')
|
|
205
|
+
return true;
|
|
206
|
+
// URI template allows braces for expressions.
|
|
207
|
+
if (!/^([a-zA-Z][a-zA-Z0-9+.-]*:)?[^"\\<>^`| ]*$/.test(uri)) {
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
let inExpression = false;
|
|
211
|
+
for (let idx = 0; idx < uri.length; idx++) {
|
|
212
|
+
const ch = uri[idx];
|
|
213
|
+
if (ch === '{') {
|
|
214
|
+
if (inExpression) {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
inExpression = true;
|
|
218
|
+
}
|
|
219
|
+
else if (ch === '}') {
|
|
220
|
+
if (!inExpression) {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
inExpression = false;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return !inExpression;
|
|
227
|
+
};
|
|
228
|
+
const jsonPointerValidator = (pointer) => {
|
|
229
|
+
if (typeof pointer !== 'string')
|
|
230
|
+
return true;
|
|
231
|
+
// JSON Pointer: empty, or a sequence of '/'-prefixed reference tokens.
|
|
232
|
+
// In each token, '~' must be escaped as '~0' or '~1'.
|
|
233
|
+
return pointer === '' || /^(?:\/(?:[^~]|~0|~1)*)+$/.test(pointer);
|
|
234
|
+
};
|
|
235
|
+
const relativeJsonPointerValidator = (pointer) => {
|
|
236
|
+
if (typeof pointer !== 'string')
|
|
237
|
+
return true;
|
|
238
|
+
// Relative JSON Pointer: number#path or empty
|
|
239
|
+
return /^\d+(#.*)?$/.test(pointer) || pointer === '';
|
|
240
|
+
};
|
|
241
|
+
const timeValidator = (time) => {
|
|
242
|
+
if (typeof time !== 'string')
|
|
243
|
+
return true;
|
|
244
|
+
// time: hh:mm:ss[.fraction]
|
|
245
|
+
return /^([01]\d|2[0-3]):([0-5]\d):([0-5]\d)(\.\d+)?$/.test(time);
|
|
246
|
+
};
|
|
247
|
+
const idnEmailValidator = (email) => {
|
|
248
|
+
if (typeof email !== 'string')
|
|
249
|
+
return true;
|
|
250
|
+
// Simple email check, allowing international chars
|
|
251
|
+
return /^[^\s@]+@[^\s@]+$/.test(email);
|
|
252
|
+
};
|
|
194
253
|
const inbuiltValidators = {
|
|
195
254
|
date: dateValidator,
|
|
196
255
|
'date-time': dateTimeValidator,
|
|
@@ -202,6 +261,12 @@ const inbuiltValidators = {
|
|
|
202
261
|
regex: regexValidator,
|
|
203
262
|
uri: uriValidator,
|
|
204
263
|
'strict-uri': strictUriValidator,
|
|
264
|
+
'uri-reference': uriReferenceValidator,
|
|
265
|
+
'uri-template': uriTemplateValidator,
|
|
266
|
+
'json-pointer': jsonPointerValidator,
|
|
267
|
+
'relative-json-pointer': relativeJsonPointerValidator,
|
|
268
|
+
time: timeValidator,
|
|
269
|
+
'idn-email': idnEmailValidator,
|
|
205
270
|
};
|
|
206
271
|
const customValidators = {};
|
|
207
272
|
export function getFormatValidators(options) {
|
package/dist/json-schema.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { isObject } from './utils/what-is.js';
|
|
2
|
-
export const
|
|
3
|
-
|
|
2
|
+
export const getId = (schema) => {
|
|
3
|
+
if (schema.$id) {
|
|
4
|
+
return schema.$id;
|
|
5
|
+
}
|
|
6
|
+
if (schema.id) {
|
|
7
|
+
return schema.id;
|
|
8
|
+
}
|
|
9
|
+
return undefined;
|
|
4
10
|
};
|
|
5
11
|
export const findId = (schema, id) => {
|
|
6
12
|
// process only arrays and objects
|
|
@@ -11,8 +17,9 @@ export const findId = (schema, id) => {
|
|
|
11
17
|
if (!id) {
|
|
12
18
|
return schema;
|
|
13
19
|
}
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
const schemaId = getId(schema);
|
|
21
|
+
if (schemaId) {
|
|
22
|
+
if (schemaId === id || (schemaId[0] === '#' && schemaId.substring(1) === id)) {
|
|
16
23
|
return schema;
|
|
17
24
|
}
|
|
18
25
|
}
|
package/dist/json-validation.js
CHANGED
|
@@ -54,8 +54,20 @@ export const JsonValidators = {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
},
|
|
57
|
-
exclusiveMaximum: function () {
|
|
58
|
-
//
|
|
57
|
+
exclusiveMaximum: function (report, schema, json) {
|
|
58
|
+
// In draft-06+, exclusiveMaximum is a standalone number
|
|
59
|
+
if (typeof schema.exclusiveMaximum === 'number') {
|
|
60
|
+
if (shouldSkipValidate(this.validateOptions, ['MAXIMUM_EXCLUSIVE'])) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (typeof json !== 'number') {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (json >= schema.exclusiveMaximum) {
|
|
67
|
+
report.addError('MAXIMUM_EXCLUSIVE', [json, schema.exclusiveMaximum], undefined, schema, 'exclusiveMaximum');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// In draft-04, exclusiveMaximum is a boolean handled inside the `maximum` validator
|
|
59
71
|
},
|
|
60
72
|
minimum: function (report, schema, json) {
|
|
61
73
|
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.1.3.2
|
|
@@ -76,8 +88,20 @@ export const JsonValidators = {
|
|
|
76
88
|
}
|
|
77
89
|
}
|
|
78
90
|
},
|
|
79
|
-
exclusiveMinimum: function () {
|
|
80
|
-
//
|
|
91
|
+
exclusiveMinimum: function (report, schema, json) {
|
|
92
|
+
// In draft-06+, exclusiveMinimum is a standalone number
|
|
93
|
+
if (typeof schema.exclusiveMinimum === 'number') {
|
|
94
|
+
if (shouldSkipValidate(this.validateOptions, ['MINIMUM_EXCLUSIVE'])) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (typeof json !== 'number') {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (json <= schema.exclusiveMinimum) {
|
|
101
|
+
report.addError('MINIMUM_EXCLUSIVE', [json, schema.exclusiveMinimum], undefined, schema, 'exclusiveMinimum');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// In draft-04, exclusiveMinimum is a boolean handled inside the `minimum` validator
|
|
81
105
|
},
|
|
82
106
|
maxLength: function (report, schema, json) {
|
|
83
107
|
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.2.1.2
|
|
@@ -256,7 +280,11 @@ export const JsonValidators = {
|
|
|
256
280
|
// for each regex in "pp", remove all elements of "s" which this regex matches.
|
|
257
281
|
let idx = pp.length;
|
|
258
282
|
while (idx--) {
|
|
259
|
-
const
|
|
283
|
+
const result = compileSchemaRegex(pp[idx]);
|
|
284
|
+
if (!result.ok) {
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
const regExp = result.value;
|
|
260
288
|
let idx2 = s.length;
|
|
261
289
|
while (idx2--) {
|
|
262
290
|
if (regExp.test(s[idx2]) === true) {
|
|
@@ -549,6 +577,112 @@ export const JsonValidators = {
|
|
|
549
577
|
report.addError('UNKNOWN_FORMAT', [schema.format], undefined, schema, 'format');
|
|
550
578
|
}
|
|
551
579
|
},
|
|
580
|
+
// draft-06 additions
|
|
581
|
+
$id: () => {
|
|
582
|
+
// TODO: implement
|
|
583
|
+
},
|
|
584
|
+
const: function (report, schema, json) {
|
|
585
|
+
const constValue = schema.const;
|
|
586
|
+
if (areEqual(json, constValue) === false) {
|
|
587
|
+
report.addError('CONST', [JSON.stringify(constValue)], undefined, schema, undefined);
|
|
588
|
+
}
|
|
589
|
+
},
|
|
590
|
+
contains: function (report, schema, json) {
|
|
591
|
+
if (shouldSkipValidate(this.validateOptions, ['CONTAINS'])) {
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
if (!Array.isArray(json)) {
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
const containsSchema = schema.contains;
|
|
598
|
+
if (containsSchema === undefined) {
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
const subReports = [];
|
|
602
|
+
let idx = json.length;
|
|
603
|
+
while (idx--) {
|
|
604
|
+
const subReport = new Report(report);
|
|
605
|
+
subReports.push(subReport);
|
|
606
|
+
validate.call(this, subReport, containsSchema, json[idx]);
|
|
607
|
+
}
|
|
608
|
+
const asyncTasksBefore = report.asyncTasks.length;
|
|
609
|
+
for (const subReport of subReports) {
|
|
610
|
+
report.asyncTasks.push(...subReport.asyncTasks);
|
|
611
|
+
}
|
|
612
|
+
const hasAsyncTasks = report.asyncTasks.length > asyncTasksBefore;
|
|
613
|
+
const addContainsErrorIfNeeded = () => {
|
|
614
|
+
let hasValidItem = false;
|
|
615
|
+
for (const subReport of subReports) {
|
|
616
|
+
if (subReport.errors.length === 0) {
|
|
617
|
+
hasValidItem = true;
|
|
618
|
+
break;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
if (!hasValidItem) {
|
|
622
|
+
report.addError('CONTAINS', undefined, subReports, schema, undefined);
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
if (hasAsyncTasks) {
|
|
626
|
+
const pathBeforeAsync = shallowClone(report.path);
|
|
627
|
+
report.addAsyncTask((callback) => {
|
|
628
|
+
setTimeout(() => callback(null), 0);
|
|
629
|
+
}, [], () => {
|
|
630
|
+
const backup = report.path;
|
|
631
|
+
report.path = pathBeforeAsync;
|
|
632
|
+
addContainsErrorIfNeeded();
|
|
633
|
+
report.path = backup;
|
|
634
|
+
});
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
addContainsErrorIfNeeded();
|
|
638
|
+
},
|
|
639
|
+
examples: () => {
|
|
640
|
+
// TODO: implement
|
|
641
|
+
},
|
|
642
|
+
propertyNames: function (report, schema, json) {
|
|
643
|
+
if (shouldSkipValidate(this.validateOptions, ['PROPERTY_NAMES'])) {
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
if (!isObject(json)) {
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
const propertyNamesSchema = schema.propertyNames;
|
|
650
|
+
if (propertyNamesSchema === undefined) {
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
const keys = Object.keys(json);
|
|
654
|
+
const subReports = [];
|
|
655
|
+
for (const key of keys) {
|
|
656
|
+
const subReport = new Report(report);
|
|
657
|
+
subReports.push(subReport);
|
|
658
|
+
validate.call(this, subReport, propertyNamesSchema, key);
|
|
659
|
+
}
|
|
660
|
+
const asyncTasksBefore = report.asyncTasks.length;
|
|
661
|
+
for (const subReport of subReports) {
|
|
662
|
+
report.asyncTasks.push(...subReport.asyncTasks);
|
|
663
|
+
}
|
|
664
|
+
const hasAsyncTasks = report.asyncTasks.length > asyncTasksBefore;
|
|
665
|
+
const addPropertyNameErrors = () => {
|
|
666
|
+
for (let idx = 0; idx < keys.length; idx++) {
|
|
667
|
+
if (subReports[idx].errors.length > 0) {
|
|
668
|
+
report.addError('PROPERTY_NAMES', [keys[idx]], subReports[idx], schema, undefined);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
};
|
|
672
|
+
if (hasAsyncTasks) {
|
|
673
|
+
const pathBeforeAsync = shallowClone(report.path);
|
|
674
|
+
report.addAsyncTask((callback) => {
|
|
675
|
+
setTimeout(() => callback(null), 0);
|
|
676
|
+
}, [], () => {
|
|
677
|
+
const backup = report.path;
|
|
678
|
+
report.path = pathBeforeAsync;
|
|
679
|
+
addPropertyNameErrors();
|
|
680
|
+
report.path = backup;
|
|
681
|
+
});
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
addPropertyNameErrors();
|
|
685
|
+
},
|
|
552
686
|
};
|
|
553
687
|
const recurseArray = function (report, schema, json) {
|
|
554
688
|
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.8.2
|
|
@@ -575,7 +709,7 @@ const recurseArray = function (report, schema, json) {
|
|
|
575
709
|
}
|
|
576
710
|
}
|
|
577
711
|
}
|
|
578
|
-
else if (typeof schema.items === 'object') {
|
|
712
|
+
else if (typeof schema.items === 'object' || typeof schema.items === 'boolean') {
|
|
579
713
|
// If items is a schema, then the child instance must be valid against this schema,
|
|
580
714
|
// regardless of its index, and regardless of the value of "additionalItems".
|
|
581
715
|
while (idx--) {
|
|
@@ -615,7 +749,8 @@ const recurseObject = function (report, schema, json) {
|
|
|
615
749
|
let idx2 = pp.length;
|
|
616
750
|
while (idx2--) {
|
|
617
751
|
const regexString = pp[idx2];
|
|
618
|
-
|
|
752
|
+
const result = compileSchemaRegex(regexString);
|
|
753
|
+
if (result.ok && result.value.test(m) === true) {
|
|
619
754
|
s.push(schema.patternProperties[regexString]);
|
|
620
755
|
}
|
|
621
756
|
}
|
|
@@ -651,10 +786,16 @@ const recurseObject = function (report, schema, json) {
|
|
|
651
786
|
};
|
|
652
787
|
export function validate(report, schema, json) {
|
|
653
788
|
report.commonErrorMessage = 'JSON_OBJECT_VALIDATION_FAILED';
|
|
789
|
+
if (schema === true) {
|
|
790
|
+
return true;
|
|
791
|
+
}
|
|
792
|
+
if (schema === false) {
|
|
793
|
+
report.addError('SCHEMA_IS_FALSE', [], undefined, schema);
|
|
794
|
+
return false;
|
|
795
|
+
}
|
|
654
796
|
// check if schema is an object
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
report.addError('SCHEMA_NOT_AN_OBJECT', [to], undefined, schema);
|
|
797
|
+
if (!isObject(schema)) {
|
|
798
|
+
report.addError('SCHEMA_NOT_AN_OBJECT', [whatIs(schema)], undefined, schema);
|
|
658
799
|
return false;
|
|
659
800
|
}
|
|
660
801
|
// check if schema is empty, everything is valid against empty schema
|
package/dist/report.js
CHANGED
|
@@ -2,7 +2,7 @@ import { Errors, getValidateError } from './errors.js';
|
|
|
2
2
|
import { get } from './utils/json.js';
|
|
3
3
|
import { jsonSymbol, schemaSymbol } from './utils/symbols.js';
|
|
4
4
|
import { isAbsoluteUri } from './utils/uri.js';
|
|
5
|
-
import {
|
|
5
|
+
import { isObject } from './utils/what-is.js';
|
|
6
6
|
export class Report {
|
|
7
7
|
asyncTasks = [];
|
|
8
8
|
commonErrorMessage;
|
|
@@ -183,8 +183,7 @@ export class Report {
|
|
|
183
183
|
params = params || [];
|
|
184
184
|
let idx = params.length;
|
|
185
185
|
while (idx--) {
|
|
186
|
-
const
|
|
187
|
-
const param = paramType === 'object' || paramType === 'null' ? JSON.stringify(params[idx]) : params[idx];
|
|
186
|
+
const param = params[idx] === null || isObject(params[idx]) ? JSON.stringify(params[idx]) : params[idx];
|
|
188
187
|
errorMessage = errorMessage.replace('{' + idx + '}', param.toString());
|
|
189
188
|
}
|
|
190
189
|
const err = {
|
package/dist/schema-cache.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { findId } from './json-schema.js';
|
|
1
|
+
import { findId, getId } from './json-schema.js';
|
|
2
2
|
import { Report } from './report.js';
|
|
3
3
|
import { deepClone } from './utils/clone.js';
|
|
4
4
|
import { decodeJSONPointer } from './utils/json.js';
|
|
@@ -49,7 +49,9 @@ export class SchemaCache {
|
|
|
49
49
|
return this.cache[path];
|
|
50
50
|
}
|
|
51
51
|
const asClone = (s) => {
|
|
52
|
-
s.id
|
|
52
|
+
if (!s.id || (!isAbsoluteUri(s.id) && isAbsoluteUri(path))) {
|
|
53
|
+
s.id = path;
|
|
54
|
+
}
|
|
53
55
|
return deepClone(s);
|
|
54
56
|
};
|
|
55
57
|
found = SchemaCache.global_cache[path];
|
|
@@ -59,9 +61,28 @@ export class SchemaCache {
|
|
|
59
61
|
return undefined;
|
|
60
62
|
}
|
|
61
63
|
getSchemaByUri(report, uri, root) {
|
|
64
|
+
if (root && !isAbsoluteUri(uri)) {
|
|
65
|
+
let rootId = getId(root);
|
|
66
|
+
if ((!rootId || !isAbsoluteUri(rootId)) && typeof root.id === 'string' && isAbsoluteUri(root.id)) {
|
|
67
|
+
rootId = root.id;
|
|
68
|
+
}
|
|
69
|
+
if (rootId && isAbsoluteUri(rootId)) {
|
|
70
|
+
const hashIndex = rootId.indexOf('#');
|
|
71
|
+
const rootBase = hashIndex === -1 ? rootId : rootId.slice(0, hashIndex);
|
|
72
|
+
try {
|
|
73
|
+
uri = new URL(uri, rootBase).toString();
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// keep original uri when URL construction fails
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
62
80
|
const remotePath = getRemotePath(uri);
|
|
63
81
|
const queryPath = getQueryPath(uri);
|
|
64
82
|
let result = remotePath ? this.fromCache(remotePath) : root;
|
|
83
|
+
if (result && remotePath && isAbsoluteUri(remotePath) && (!result.id || !isAbsoluteUri(result.id))) {
|
|
84
|
+
result.id = remotePath;
|
|
85
|
+
}
|
|
65
86
|
if (result && remotePath) {
|
|
66
87
|
// we need to avoid compiling schemas in a recursive loop
|
|
67
88
|
const compileRemote = result !== root;
|
package/dist/schema-compiler.js
CHANGED
|
@@ -1,31 +1,38 @@
|
|
|
1
|
+
import { getId } from './json-schema.js';
|
|
1
2
|
import { Report } from './report.js';
|
|
2
3
|
import { getRemotePath, isAbsoluteUri } from './utils/uri.js';
|
|
3
4
|
import { getSchemaReader } from './z-schema-reader.js';
|
|
4
5
|
export const collectIds = (obj) => {
|
|
5
6
|
const ids = [];
|
|
7
|
+
const doNotCollectIdsFrom = ['enum', 'const', 'default', 'examples'];
|
|
6
8
|
function walk(node, scope) {
|
|
7
9
|
if (typeof node !== 'object' || node == null)
|
|
8
10
|
return;
|
|
9
11
|
let addedScope = false;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
const nodeId = getId(node);
|
|
13
|
+
if (typeof nodeId === 'string') {
|
|
14
|
+
let type = isAbsoluteUri(nodeId) ? 'absolute' : 'relative';
|
|
12
15
|
if (scope.length === 0) {
|
|
13
16
|
type = 'root';
|
|
14
17
|
}
|
|
15
18
|
const id = {
|
|
16
|
-
id:
|
|
19
|
+
id: nodeId,
|
|
17
20
|
type,
|
|
18
21
|
obj: node,
|
|
19
22
|
};
|
|
20
|
-
if (type === 'absolute' || (type === 'root' && isAbsoluteUri(
|
|
21
|
-
id.absoluteUri =
|
|
23
|
+
if (type === 'absolute' || (type === 'root' && isAbsoluteUri(nodeId))) {
|
|
24
|
+
id.absoluteUri = nodeId;
|
|
25
|
+
}
|
|
26
|
+
else if (type === 'root' && typeof node.id === 'string' && isAbsoluteUri(node.id) && node.id !== nodeId) {
|
|
27
|
+
id.absoluteUri = resolveIdScope(node.id, nodeId);
|
|
22
28
|
}
|
|
23
29
|
else if (type === 'relative') {
|
|
24
30
|
id.absoluteParent = scope
|
|
25
31
|
.filter((x) => x.type === 'absolute' || (x.type === 'root' && x.absoluteUri))
|
|
26
32
|
.slice(-1)[0];
|
|
27
33
|
if (id.absoluteParent) {
|
|
28
|
-
|
|
34
|
+
const parentUri = id.absoluteParent.absoluteUri || id.absoluteParent.id;
|
|
35
|
+
id.absoluteUri = parentUri.split('/').slice(0, -1).concat(id.id).join('/');
|
|
29
36
|
}
|
|
30
37
|
}
|
|
31
38
|
ids.push(id);
|
|
@@ -39,7 +46,7 @@ export const collectIds = (obj) => {
|
|
|
39
46
|
}
|
|
40
47
|
else {
|
|
41
48
|
for (const key of Object.keys(node)) {
|
|
42
|
-
if (key.indexOf('__$') === 0)
|
|
49
|
+
if (key.indexOf('__$') === 0 || doNotCollectIdsFrom.includes(key))
|
|
43
50
|
continue;
|
|
44
51
|
walk(node[key], scope);
|
|
45
52
|
}
|
|
@@ -51,7 +58,7 @@ export const collectIds = (obj) => {
|
|
|
51
58
|
walk(obj, []);
|
|
52
59
|
return ids;
|
|
53
60
|
};
|
|
54
|
-
const doNotCollectReferencesFrom = ['enum'];
|
|
61
|
+
const doNotCollectReferencesFrom = ['enum', 'const', 'default', 'examples'];
|
|
55
62
|
export const collectReferences = (obj, results, scope, path) => {
|
|
56
63
|
results = results || [];
|
|
57
64
|
scope = scope || [];
|
|
@@ -62,9 +69,14 @@ export const collectReferences = (obj, results, scope, path) => {
|
|
|
62
69
|
const hasRef = typeof obj.$ref === 'string' && typeof obj.__$refResolved === 'undefined';
|
|
63
70
|
let addedScope = false;
|
|
64
71
|
const isRootScope = scope.length === 0;
|
|
65
|
-
|
|
72
|
+
const objId = getId(obj);
|
|
73
|
+
let scopeId = objId;
|
|
74
|
+
if (typeof obj.id === 'string' && isAbsoluteUri(obj.id) && (!scopeId || !isAbsoluteUri(scopeId))) {
|
|
75
|
+
scopeId = obj.id;
|
|
76
|
+
}
|
|
77
|
+
if (typeof scopeId === 'string' && (isRootScope || !hasRef)) {
|
|
66
78
|
const base = scope.length > 0 ? scope[scope.length - 1] : undefined;
|
|
67
|
-
scope.push(resolveIdScope(base,
|
|
79
|
+
scope.push(resolveIdScope(base, scopeId));
|
|
68
80
|
addedScope = true;
|
|
69
81
|
}
|
|
70
82
|
if (hasRef) {
|
|
@@ -186,6 +198,9 @@ export class SchemaCompiler {
|
|
|
186
198
|
}
|
|
187
199
|
return this.compileArrayOfSchemas(report, schema);
|
|
188
200
|
}
|
|
201
|
+
else if (typeof schema === 'boolean') {
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
189
204
|
else {
|
|
190
205
|
if (!options?.noCache) {
|
|
191
206
|
this.collectAndCacheIds(schema);
|