z-schema 7.0.0 → 7.0.6

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.
Files changed (76) hide show
  1. package/README.md +7 -11
  2. package/bin/z-schema +0 -0
  3. package/cjs/index.d.ts +192 -117
  4. package/cjs/index.js +949 -998
  5. package/{src/FormatValidators.ts → dist/format-validators.js} +97 -65
  6. package/dist/index.js +1 -1
  7. package/dist/json-schema.js +40 -0
  8. package/dist/{JsonValidation.js → json-validation.js} +75 -69
  9. package/dist/{Report.js → report.js} +35 -45
  10. package/dist/schema-cache.js +109 -0
  11. package/dist/schema-compiler.js +255 -0
  12. package/dist/{SchemaValidation.js → schema-validator.js} +153 -149
  13. package/dist/types/{Errors.d.ts → errors.d.ts} +2 -0
  14. package/dist/types/format-validators.d.ts +10 -0
  15. package/dist/types/index.d.ts +10 -1
  16. package/dist/types/json-schema.d.ts +50 -0
  17. package/dist/types/json-validation.d.ts +7 -0
  18. package/dist/types/{Report.d.ts → report.d.ts} +22 -23
  19. package/dist/types/schema-cache.d.ts +17 -0
  20. package/dist/types/schema-compiler.d.ts +16 -0
  21. package/dist/types/schema-validator.d.ts +10 -0
  22. package/dist/types/utils/array.d.ts +2 -0
  23. package/dist/types/utils/clone.d.ts +2 -0
  24. package/dist/types/utils/json.d.ts +7 -0
  25. package/dist/types/utils/symbols.d.ts +2 -0
  26. package/dist/types/utils/unicode.d.ts +14 -0
  27. package/dist/types/utils/uri.d.ts +4 -0
  28. package/dist/types/utils/what-is.d.ts +3 -0
  29. package/dist/types/z-schema.d.ts +75 -0
  30. package/dist/utils/array.js +27 -0
  31. package/dist/utils/clone.js +61 -0
  32. package/dist/utils/json.js +59 -0
  33. package/dist/utils/symbols.js +2 -0
  34. package/dist/utils/unicode.js +45 -0
  35. package/dist/utils/uri.js +15 -0
  36. package/dist/utils/what-is.js +29 -0
  37. package/dist/{ZSchema.js → z-schema.js} +66 -77
  38. package/package.json +8 -4
  39. package/src/{Errors.ts → errors.ts} +4 -0
  40. package/src/format-validators.ts +191 -0
  41. package/src/index.ts +12 -1
  42. package/src/json-schema.ts +97 -0
  43. package/src/{JsonValidation.ts → json-validation.ts} +137 -127
  44. package/src/{Report.ts → report.ts} +60 -70
  45. package/src/schema-cache.ts +122 -0
  46. package/src/schema-compiler.ts +300 -0
  47. package/src/{SchemaValidation.ts → schema-validator.ts} +213 -215
  48. package/src/utils/array.ts +29 -0
  49. package/src/utils/clone.ts +63 -0
  50. package/src/utils/json.ts +74 -0
  51. package/src/utils/symbols.ts +3 -0
  52. package/src/utils/unicode.ts +43 -0
  53. package/src/utils/uri.ts +18 -0
  54. package/src/utils/what-is.ts +46 -0
  55. package/src/{ZSchema.ts → z-schema.ts} +108 -113
  56. package/umd/ZSchema.js +949 -998
  57. package/umd/ZSchema.min.js +1 -1
  58. package/dist/FormatValidators.js +0 -136
  59. package/dist/SchemaCache.js +0 -173
  60. package/dist/SchemaCompilation.js +0 -259
  61. package/dist/Utils.js +0 -266
  62. package/dist/types/FormatValidators.d.ts +0 -12
  63. package/dist/types/JsonValidation.d.ts +0 -37
  64. package/dist/types/SchemaCache.d.ts +0 -26
  65. package/dist/types/SchemaCompilation.d.ts +0 -1
  66. package/dist/types/SchemaValidation.d.ts +0 -6
  67. package/dist/types/Utils.d.ts +0 -64
  68. package/dist/types/ZSchema.d.ts +0 -97
  69. package/src/SchemaCache.ts +0 -189
  70. package/src/SchemaCompilation.ts +0 -293
  71. package/src/Utils.ts +0 -286
  72. /package/dist/{Errors.js → errors.js} +0 -0
  73. /package/dist/schemas/{hyper-schema.json → draft-04-hyper-schema.json} +0 -0
  74. /package/dist/schemas/{schema.json → draft-04-schema.json} +0 -0
  75. /package/src/schemas/{hyper-schema.json → draft-04-hyper-schema.json} +0 -0
  76. /package/src/schemas/{schema.json → draft-04-schema.json} +0 -0
@@ -0,0 +1,109 @@
1
+ import isequal from 'lodash.isequal';
2
+ import { Report } from './report.js';
3
+ import { findId } from './json-schema.js';
4
+ import { getQueryPath, getRemotePath } from './utils/uri.js';
5
+ import { deepClone } from './utils/clone.js';
6
+ import { decodeJSONPointer } from './utils/json.js';
7
+ export class SchemaCache {
8
+ validator;
9
+ cache = {};
10
+ referenceCache = [];
11
+ constructor(validator) {
12
+ this.validator = validator;
13
+ }
14
+ cacheSchemaByUri(uri, schema) {
15
+ const remotePath = getRemotePath(uri);
16
+ if (remotePath) {
17
+ this.cache[remotePath] = schema;
18
+ }
19
+ }
20
+ removeFromCacheByUri(uri) {
21
+ const remotePath = getRemotePath(uri);
22
+ if (remotePath) {
23
+ delete this.cache[remotePath];
24
+ }
25
+ }
26
+ checkCacheForUri(uri) {
27
+ const remotePath = getRemotePath(uri);
28
+ return remotePath ? this.cache[remotePath] != null : false;
29
+ }
30
+ getSchema(report, refOrSchema) {
31
+ if (typeof refOrSchema === 'string') {
32
+ // ref input
33
+ return this.getSchemaByUri(report, refOrSchema);
34
+ }
35
+ if (typeof refOrSchema === 'object') {
36
+ // schema obj input
37
+ return this.getSchemaByReference(report, refOrSchema);
38
+ }
39
+ throw new Error(`unexpected code reached`);
40
+ }
41
+ getSchemaByUri(report, uri, root) {
42
+ const remotePath = getRemotePath(uri);
43
+ const queryPath = getQueryPath(uri);
44
+ let result = remotePath ? this.cache[remotePath] : root;
45
+ if (result && remotePath) {
46
+ // we need to avoid compiling schemas in a recursive loop
47
+ const compileRemote = result !== root;
48
+ // now we need to compile and validate resolved schema (in case it's not already)
49
+ if (compileRemote) {
50
+ report.path.push(remotePath);
51
+ let remoteReport;
52
+ const anscestorReport = result.id ? report.getAncestor(result.id) : undefined;
53
+ if (anscestorReport) {
54
+ remoteReport = anscestorReport;
55
+ }
56
+ else {
57
+ remoteReport = new Report(report);
58
+ if (this.validator.sc.compileSchema(remoteReport, result)) {
59
+ const savedOptions = this.validator.options;
60
+ try {
61
+ // If custom validationOptions were provided to setRemoteReference(),
62
+ // use them instead of the default options
63
+ this.validator.options = result.__$validationOptions || this.validator.options;
64
+ this.validator.sv.validateSchema(remoteReport, result);
65
+ }
66
+ finally {
67
+ this.validator.options = savedOptions;
68
+ }
69
+ }
70
+ }
71
+ const remoteReportIsValid = remoteReport.isValid();
72
+ if (!remoteReportIsValid) {
73
+ report.addError('REMOTE_NOT_VALID', [uri], remoteReport);
74
+ }
75
+ report.path.pop();
76
+ if (!remoteReportIsValid) {
77
+ return undefined;
78
+ }
79
+ }
80
+ }
81
+ if (result && queryPath) {
82
+ const parts = queryPath.split('/');
83
+ for (let idx = 0, lim = parts.length; result && idx < lim; idx++) {
84
+ const key = decodeJSONPointer(parts[idx]);
85
+ if (idx === 0) {
86
+ // it's an id
87
+ result = findId(result, key);
88
+ }
89
+ else {
90
+ // it's a path behind id
91
+ result = result[key];
92
+ }
93
+ }
94
+ }
95
+ return result;
96
+ }
97
+ getSchemaByReference(report, schema) {
98
+ let i = this.referenceCache.length;
99
+ while (i--) {
100
+ if (isequal(this.referenceCache[i][0], schema)) {
101
+ return this.referenceCache[i][1];
102
+ }
103
+ }
104
+ // not found
105
+ const schemaClone = deepClone(schema);
106
+ this.referenceCache.push([schema, schemaClone]);
107
+ return schemaClone;
108
+ }
109
+ }
@@ -0,0 +1,255 @@
1
+ import { Report } from './report.js';
2
+ import { isAbsoluteUri, isRelativeUri } from './utils/uri.js';
3
+ const collectReferences = (obj, results, scope, path) => {
4
+ results = results || [];
5
+ scope = scope || [];
6
+ path = path || [];
7
+ if (typeof obj !== 'object' || obj === null) {
8
+ return results;
9
+ }
10
+ if (typeof obj.id === 'string') {
11
+ scope.push(obj.id);
12
+ }
13
+ if (typeof obj.$ref === 'string' && typeof obj.__$refResolved === 'undefined') {
14
+ results.push({
15
+ ref: mergeReference(scope, obj.$ref),
16
+ key: '$ref',
17
+ obj: obj,
18
+ path: path.slice(0),
19
+ });
20
+ }
21
+ if (typeof obj.$schema === 'string' && typeof obj.__$schemaResolved === 'undefined') {
22
+ results.push({
23
+ ref: mergeReference(scope, obj.$schema),
24
+ key: '$schema',
25
+ obj: obj,
26
+ path: path.slice(0),
27
+ });
28
+ }
29
+ let idx;
30
+ if (Array.isArray(obj)) {
31
+ idx = obj.length;
32
+ while (idx--) {
33
+ path.push(idx);
34
+ collectReferences(obj[idx], results, scope, path);
35
+ path.pop();
36
+ }
37
+ }
38
+ else {
39
+ const keys = Object.keys(obj);
40
+ idx = keys.length;
41
+ while (idx--) {
42
+ // do not recurse through resolved references and other z-schema props
43
+ if (keys[idx].indexOf('__$') === 0) {
44
+ continue;
45
+ }
46
+ path.push(keys[idx]);
47
+ collectReferences(obj[keys[idx]], results, scope, path);
48
+ path.pop();
49
+ }
50
+ }
51
+ if (typeof obj.id === 'string') {
52
+ scope.pop();
53
+ }
54
+ return results;
55
+ };
56
+ const mergeReference = (scope, ref) => {
57
+ if (isAbsoluteUri(ref)) {
58
+ return ref;
59
+ }
60
+ let joinedScope = scope.join('');
61
+ const isScopeAbsolute = isAbsoluteUri(joinedScope);
62
+ const isScopeRelative = isRelativeUri(joinedScope);
63
+ const isRefRelative = isRelativeUri(ref);
64
+ let toRemove;
65
+ if (isScopeAbsolute && isRefRelative) {
66
+ toRemove = joinedScope.match(/\/[^/]*$/);
67
+ if (toRemove) {
68
+ joinedScope = joinedScope.slice(0, toRemove.index + 1);
69
+ }
70
+ }
71
+ else if (isScopeRelative && isRefRelative) {
72
+ joinedScope = '';
73
+ }
74
+ else {
75
+ toRemove = joinedScope.match(/[^#/]+$/);
76
+ if (toRemove) {
77
+ joinedScope = joinedScope.slice(0, toRemove.index);
78
+ }
79
+ }
80
+ let res = joinedScope + ref;
81
+ res = res.replace(/##/, '#');
82
+ return res;
83
+ };
84
+ export class SchemaCompiler {
85
+ validator;
86
+ constructor(validator) {
87
+ this.validator = validator;
88
+ }
89
+ compileSchema(report, schema) {
90
+ report.commonErrorMessage = 'SCHEMA_COMPILATION_FAILED';
91
+ // if schema is a string, assume it's a uri
92
+ if (typeof schema === 'string') {
93
+ const loadedSchema = this.validator.scache.getSchemaByUri(report, schema);
94
+ if (!loadedSchema) {
95
+ report.addError('SCHEMA_NOT_REACHABLE', [schema]);
96
+ return false;
97
+ }
98
+ schema = loadedSchema;
99
+ }
100
+ // if schema is an array, assume it's an array of schemas
101
+ if (Array.isArray(schema)) {
102
+ return this.compileArrayOfSchemas(report, schema);
103
+ }
104
+ // if we have an id than it should be cached already (if this instance has compiled it)
105
+ if (schema.__$compiled && schema.id && this.validator.scache.checkCacheForUri(schema.id) === false) {
106
+ schema.__$compiled = undefined;
107
+ }
108
+ // do not re-compile schemas
109
+ if (schema.__$compiled) {
110
+ return true;
111
+ }
112
+ if (schema.id && typeof schema.id === 'string') {
113
+ // add this to our schemaCache (before compilation in case we have references including id)
114
+ this.validator.scache.cacheSchemaByUri(schema.id, schema);
115
+ }
116
+ // this method can be called recursively, so we need to remember our root
117
+ let isRoot = false;
118
+ if (!report.rootSchema) {
119
+ report.rootSchema = schema;
120
+ isRoot = true;
121
+ }
122
+ // delete all __$missingReferences from previous compilation attempts
123
+ const isValidExceptReferences = report.isValid();
124
+ delete schema.__$missingReferences;
125
+ // collect all references that need to be resolved - $ref and $schema
126
+ const refs = collectReferences(schema);
127
+ let idx = refs.length;
128
+ while (idx--) {
129
+ // resolve all the collected references into __xxxResolved pointer
130
+ const refObj = refs[idx];
131
+ let response = this.validator.scache.getSchemaByUri(report, refObj.ref, schema);
132
+ // we can try to use custom schemaReader if available
133
+ if (!response) {
134
+ const schemaReader = this.validator.getSchemaReader();
135
+ if (schemaReader) {
136
+ // it's supposed to return a valid schema
137
+ const s = schemaReader(refObj.ref);
138
+ if (s) {
139
+ // it needs to have the id
140
+ s.id = refObj.ref;
141
+ // try to compile the schema
142
+ const subreport = new Report(report);
143
+ if (!this.compileSchema(subreport, s)) {
144
+ // copy errors to report
145
+ report.errors = report.errors.concat(subreport.errors);
146
+ }
147
+ else {
148
+ response = this.validator.scache.getSchemaByUri(report, refObj.ref, schema);
149
+ }
150
+ }
151
+ }
152
+ }
153
+ if (!response) {
154
+ const hasNotValid = report.hasError('REMOTE_NOT_VALID', [refObj.ref]);
155
+ const isAbsolute = isAbsoluteUri(refObj.ref);
156
+ let isDownloaded = false;
157
+ const ignoreUnresolvableRemotes = this.validator.options.ignoreUnresolvableReferences === true;
158
+ if (isAbsolute) {
159
+ // we shouldn't add UNRESOLVABLE_REFERENCE for schemas we already have downloaded
160
+ // and set through setRemoteReference method
161
+ isDownloaded = this.validator.scache.checkCacheForUri(refObj.ref);
162
+ }
163
+ if (hasNotValid) {
164
+ // already has REMOTE_NOT_VALID error for this one
165
+ }
166
+ else if (ignoreUnresolvableRemotes && isAbsolute) {
167
+ // ignoreUnresolvableRemotes is on and remote isAbsolute
168
+ }
169
+ else if (isDownloaded) {
170
+ // remote is downloaded, so no UNRESOLVABLE_REFERENCE
171
+ }
172
+ else {
173
+ report.path.push(...refObj.path);
174
+ report.addError('UNRESOLVABLE_REFERENCE', [refObj.ref]);
175
+ report.path = report.path.slice(0, -refObj.path.length);
176
+ // pusblish unresolved references out
177
+ if (isValidExceptReferences) {
178
+ schema.__$missingReferences = schema.__$missingReferences || [];
179
+ schema.__$missingReferences.push(refObj);
180
+ }
181
+ }
182
+ }
183
+ // this might create circular references
184
+ refObj.obj[`__${refObj.key}Resolved`] = response;
185
+ }
186
+ const isValid = report.isValid();
187
+ if (isValid) {
188
+ schema.__$compiled = true;
189
+ }
190
+ else {
191
+ if (schema.id && typeof schema.id === 'string') {
192
+ // remove this schema from schemaCache because it failed to compile
193
+ this.validator.scache.removeFromCacheByUri(schema.id);
194
+ }
195
+ }
196
+ // we don't need the root pointer anymore
197
+ if (isRoot) {
198
+ report.rootSchema = undefined;
199
+ }
200
+ return isValid;
201
+ }
202
+ compileArrayOfSchemas(report, arr) {
203
+ let compiled = 0, lastLoopCompiled;
204
+ do {
205
+ // remove all UNRESOLVABLE_REFERENCE errors before compiling array again
206
+ let idx = report.errors.length;
207
+ while (idx--) {
208
+ if (report.errors[idx].code === 'UNRESOLVABLE_REFERENCE') {
209
+ report.errors.splice(idx, 1);
210
+ }
211
+ }
212
+ // remember how many were compiled in the last loop
213
+ lastLoopCompiled = compiled;
214
+ // count how many are compiled now
215
+ compiled = this.compileArrayOfSchemasLoop(report, arr);
216
+ // fix __$missingReferences if possible
217
+ idx = arr.length;
218
+ while (idx--) {
219
+ const sch = arr[idx];
220
+ if (sch.__$missingReferences) {
221
+ let idx2 = sch.__$missingReferences.length;
222
+ while (idx2--) {
223
+ const refObj = sch.__$missingReferences[idx2];
224
+ const response = arr.find((x) => x.id === refObj.ref);
225
+ if (response) {
226
+ // this might create circular references
227
+ refObj.obj[`__${refObj.key}Resolved`] = response;
228
+ // it's resolved now so delete it
229
+ sch.__$missingReferences.splice(idx2, 1);
230
+ }
231
+ }
232
+ if (sch.__$missingReferences.length === 0) {
233
+ delete sch.__$missingReferences;
234
+ }
235
+ }
236
+ }
237
+ // keep repeating if not all compiled and at least one more was compiled in the last loop
238
+ } while (compiled !== arr.length && compiled !== lastLoopCompiled);
239
+ return report.isValid();
240
+ }
241
+ compileArrayOfSchemasLoop(mainReport, arr) {
242
+ let idx = arr.length, compiledCount = 0;
243
+ while (idx--) {
244
+ // try to compile each schema separately
245
+ const report = new Report(mainReport);
246
+ const isValid = this.compileSchema(report, arr[idx]);
247
+ if (isValid) {
248
+ compiledCount++;
249
+ }
250
+ // copy errors to report
251
+ mainReport.errors = mainReport.errors.concat(report.errors);
252
+ }
253
+ return compiledCount;
254
+ }
255
+ }