z-schema 6.0.2 → 7.0.0-beta.1

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 (47) hide show
  1. package/README.md +154 -134
  2. package/bin/z-schema +128 -124
  3. package/dist/Errors.js +50 -0
  4. package/dist/FormatValidators.js +136 -0
  5. package/{src → dist}/JsonValidation.js +184 -213
  6. package/dist/Report.js +220 -0
  7. package/{src → dist}/SchemaCache.js +67 -82
  8. package/{src → dist}/SchemaCompilation.js +89 -129
  9. package/dist/SchemaValidation.js +631 -0
  10. package/{src → dist}/Utils.js +96 -104
  11. package/dist/ZSchema-umd-min.js +1 -0
  12. package/dist/ZSchema-umd.js +13791 -0
  13. package/dist/ZSchema.cjs +13785 -0
  14. package/dist/ZSchema.js +366 -0
  15. package/dist/schemas/hyper-schema.json +156 -0
  16. package/dist/schemas/schema.json +151 -0
  17. package/dist/types/Errors.d.ts +44 -0
  18. package/dist/types/FormatValidators.d.ts +12 -0
  19. package/dist/types/JsonValidation.d.ts +37 -0
  20. package/dist/types/Report.d.ts +87 -0
  21. package/dist/types/SchemaCache.d.ts +26 -0
  22. package/dist/types/SchemaCompilation.d.ts +1 -0
  23. package/dist/types/SchemaValidation.d.ts +6 -0
  24. package/dist/types/Utils.d.ts +64 -0
  25. package/dist/types/ZSchema.d.ts +97 -0
  26. package/package.json +54 -43
  27. package/src/Errors.ts +56 -0
  28. package/src/FormatValidators.ts +136 -0
  29. package/src/JsonValidation.ts +624 -0
  30. package/src/Report.ts +337 -0
  31. package/src/SchemaCache.ts +189 -0
  32. package/src/SchemaCompilation.ts +293 -0
  33. package/src/SchemaValidation.ts +629 -0
  34. package/src/Utils.ts +286 -0
  35. package/src/ZSchema.ts +469 -0
  36. package/src/schemas/_ +0 -0
  37. package/dist/ZSchema-browser-min.js +0 -2
  38. package/dist/ZSchema-browser-min.js.map +0 -1
  39. package/dist/ZSchema-browser-test.js +0 -32247
  40. package/dist/ZSchema-browser.js +0 -12745
  41. package/index.d.ts +0 -175
  42. package/src/Errors.js +0 -60
  43. package/src/FormatValidators.js +0 -129
  44. package/src/Polyfills.js +0 -16
  45. package/src/Report.js +0 -299
  46. package/src/SchemaValidation.js +0 -619
  47. package/src/ZSchema.js +0 -409
package/dist/Report.js ADDED
@@ -0,0 +1,220 @@
1
+ import get from 'lodash.get';
2
+ import { Errors } from './Errors.js';
3
+ import * as Utils from './Utils.js';
4
+ export class Report {
5
+ errors;
6
+ parentReport;
7
+ options;
8
+ reportOptions;
9
+ path;
10
+ asyncTasks;
11
+ rootSchema;
12
+ commonErrorMessage;
13
+ json;
14
+ constructor(parentOrOptions, reportOptions) {
15
+ this.parentReport = parentOrOptions instanceof Report ? parentOrOptions : undefined;
16
+ this.options = parentOrOptions instanceof Report ? parentOrOptions.options : parentOrOptions || {};
17
+ this.reportOptions = reportOptions || {};
18
+ this.errors = [];
19
+ /**
20
+ * @type {string[]}
21
+ */
22
+ this.path = [];
23
+ this.asyncTasks = [];
24
+ this.rootSchema = undefined;
25
+ this.commonErrorMessage = undefined;
26
+ this.json = undefined;
27
+ }
28
+ isValid() {
29
+ if (this.asyncTasks.length > 0) {
30
+ throw new Error("Async tasks pending, can't answer isValid");
31
+ }
32
+ return this.errors.length === 0;
33
+ }
34
+ addAsyncTask(fn, args, asyncTaskResultProcessFn) {
35
+ this.asyncTasks.push([fn, args, asyncTaskResultProcessFn]);
36
+ }
37
+ getAncestor(id) {
38
+ if (!this.parentReport) {
39
+ return undefined;
40
+ }
41
+ if (this.parentReport.getSchemaId() === id) {
42
+ return this.parentReport;
43
+ }
44
+ return this.parentReport.getAncestor(id);
45
+ }
46
+ processAsyncTasks(timeout, callback) {
47
+ const validationTimeout = timeout || 2000;
48
+ let tasksCount = this.asyncTasks.length;
49
+ let idx = tasksCount;
50
+ let timedOut = false;
51
+ const finish = () => {
52
+ setTimeout(() => {
53
+ const valid = this.errors.length === 0, err = valid ? null : this.errors;
54
+ callback(err, valid);
55
+ }, 0);
56
+ };
57
+ function respond(asyncTaskResultProcessFn) {
58
+ return function (asyncTaskResult) {
59
+ if (timedOut) {
60
+ return;
61
+ }
62
+ asyncTaskResultProcessFn(asyncTaskResult);
63
+ if (--tasksCount === 0) {
64
+ finish();
65
+ }
66
+ };
67
+ }
68
+ // finish if tasks are completed or there are any errors and breaking on first error was requested
69
+ if (tasksCount === 0 || (this.errors.length > 0 && this.options.breakOnFirstError)) {
70
+ finish();
71
+ return;
72
+ }
73
+ while (idx--) {
74
+ const [fn, fnArgs, processFn] = this.asyncTasks[idx];
75
+ const respondCallback = respond(processFn);
76
+ fn(...fnArgs, respondCallback);
77
+ }
78
+ setTimeout(() => {
79
+ if (tasksCount > 0) {
80
+ timedOut = true;
81
+ this.addError('ASYNC_TIMEOUT', [tasksCount, validationTimeout]);
82
+ callback(this.errors, false);
83
+ }
84
+ }, validationTimeout);
85
+ }
86
+ getPath(returnPathAsString) {
87
+ /**
88
+ * @type {string[]|string}
89
+ */
90
+ let path = [];
91
+ if (this.parentReport) {
92
+ path = path.concat(this.parentReport.path);
93
+ }
94
+ path = path.concat(this.path);
95
+ if (returnPathAsString !== true) {
96
+ // Sanitize the path segments (http://tools.ietf.org/html/rfc6901#section-4)
97
+ return ('#/' +
98
+ path
99
+ .map(function (segment) {
100
+ segment = segment.toString();
101
+ if (Utils.isAbsoluteUri(segment)) {
102
+ return 'uri(' + segment + ')';
103
+ }
104
+ return segment.replace(/~/g, '~0').replace(/\//g, '~1');
105
+ })
106
+ .join('/'));
107
+ }
108
+ return path;
109
+ }
110
+ getSchemaId() {
111
+ if (!this.rootSchema) {
112
+ return null;
113
+ }
114
+ // get the error path as an array
115
+ let path = [];
116
+ if (this.parentReport) {
117
+ path = path.concat(this.parentReport.path);
118
+ }
119
+ path = path.concat(this.path);
120
+ // try to find id in the error path
121
+ while (path.length > 0) {
122
+ const obj = get(this.rootSchema, path);
123
+ if (obj && obj.id) {
124
+ return obj.id;
125
+ }
126
+ path.pop();
127
+ }
128
+ // return id of the root
129
+ return this.rootSchema.id;
130
+ }
131
+ hasError(errorCode, params) {
132
+ let idx = this.errors.length;
133
+ while (idx--) {
134
+ if (this.errors[idx].code === errorCode) {
135
+ // assume match
136
+ let match = true;
137
+ // check the params too
138
+ let idx2 = this.errors[idx].params.length;
139
+ while (idx2--) {
140
+ if (this.errors[idx].params[idx2] !== params[idx2]) {
141
+ match = false;
142
+ }
143
+ }
144
+ // if match, return true
145
+ if (match) {
146
+ return match;
147
+ }
148
+ }
149
+ }
150
+ return false;
151
+ }
152
+ addError(errorCode, params, subReports, schema) {
153
+ if (!errorCode) {
154
+ throw new Error('No errorCode passed into addError()');
155
+ }
156
+ this.addCustomError(errorCode, Errors[errorCode], params, subReports, schema);
157
+ }
158
+ getJson() {
159
+ if (this.json) {
160
+ return this.json;
161
+ }
162
+ if (this.parentReport) {
163
+ return this.parentReport.getJson();
164
+ }
165
+ return undefined;
166
+ }
167
+ addCustomError(errorCode, errorMessage, params, subReports, schema) {
168
+ if (this.errors.length >= this.reportOptions.maxErrors) {
169
+ return;
170
+ }
171
+ if (!errorMessage) {
172
+ throw new Error('No errorMessage known for code ' + errorCode);
173
+ }
174
+ params = params || [];
175
+ let idx = params.length;
176
+ while (idx--) {
177
+ const whatIs = Utils.whatIs(params[idx]);
178
+ const param = whatIs === 'object' || whatIs === 'null' ? JSON.stringify(params[idx]) : params[idx];
179
+ errorMessage = errorMessage.replace('{' + idx + '}', param);
180
+ }
181
+ const err = {
182
+ code: errorCode,
183
+ params: params,
184
+ message: errorMessage,
185
+ path: this.getPath(this.options.reportPathAsArray),
186
+ schemaId: this.getSchemaId(),
187
+ };
188
+ err[Utils.schemaSymbol] = schema;
189
+ err[Utils.jsonSymbol] = this.getJson();
190
+ if (schema && typeof schema === 'string') {
191
+ err.description = schema;
192
+ }
193
+ else if (schema && typeof schema === 'object') {
194
+ if (schema.title) {
195
+ err.title = schema.title;
196
+ }
197
+ if (schema.description) {
198
+ err.description = schema.description;
199
+ }
200
+ }
201
+ if (subReports != null) {
202
+ if (!Array.isArray(subReports)) {
203
+ subReports = [subReports];
204
+ }
205
+ err.inner = [];
206
+ idx = subReports.length;
207
+ while (idx--) {
208
+ const subReport = subReports[idx];
209
+ let idx2 = subReport.errors.length;
210
+ while (idx2--) {
211
+ err.inner.push(subReport.errors[idx2]);
212
+ }
213
+ }
214
+ if (err.inner.length === 0) {
215
+ err.inner = undefined;
216
+ }
217
+ }
218
+ this.errors.push(err);
219
+ }
220
+ }
@@ -1,69 +1,64 @@
1
- "use strict";
2
-
3
- var isequal = require("lodash.isequal");
4
- var Report = require("./Report");
5
- var SchemaCompilation = require("./SchemaCompilation");
6
- var SchemaValidation = require("./SchemaValidation");
7
- var Utils = require("./Utils");
8
-
1
+ import isequal from 'lodash.isequal';
2
+ import { Report } from './Report.js';
3
+ import { compileSchema } from './SchemaCompilation.js';
4
+ import * as SchemaValidation from './SchemaValidation.js';
5
+ import * as Utils from './Utils.js';
9
6
  function decodeJSONPointer(str) {
10
7
  // http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07#section-3
11
8
  return decodeURIComponent(str).replace(/~[0-1]/g, function (x) {
12
- return x === "~1" ? "/" : "~";
9
+ return x === '~1' ? '/' : '~';
13
10
  });
14
11
  }
15
-
16
- function getRemotePath(uri) {
17
- var io = uri.indexOf("#");
12
+ export function getRemotePath(uri) {
13
+ const io = uri.indexOf('#');
18
14
  return io === -1 ? uri : uri.slice(0, io);
19
15
  }
20
-
21
16
  function getQueryPath(uri) {
22
- var io = uri.indexOf("#");
23
- var res = io === -1 ? undefined : uri.slice(io + 1);
17
+ const io = uri.indexOf('#');
18
+ const res = io === -1 ? undefined : uri.slice(io + 1);
24
19
  // WARN: do not slice slash, #/ means take root and go down from it
25
20
  // if (res && res[0] === "/") { res = res.slice(1); }
26
21
  return res;
27
22
  }
28
-
29
23
  function findId(schema, id) {
30
24
  // process only arrays and objects
31
- if (typeof schema !== "object" || schema === null) {
25
+ if (typeof schema !== 'object' || schema === null) {
32
26
  return;
33
27
  }
34
-
35
28
  // no id means root so return itself
36
29
  if (!id) {
37
30
  return schema;
38
31
  }
39
-
40
32
  if (schema.id) {
41
- if (schema.id === id || schema.id[0] === "#" && schema.id.substring(1) === id) {
33
+ if (schema.id === id || (schema.id[0] === '#' && schema.id.substring(1) === id)) {
42
34
  return schema;
43
35
  }
44
36
  }
45
-
46
- var idx, result;
37
+ let idx, result;
47
38
  if (Array.isArray(schema)) {
48
39
  idx = schema.length;
49
40
  while (idx--) {
50
41
  result = findId(schema[idx], id);
51
- if (result) { return result; }
42
+ if (result) {
43
+ return result;
44
+ }
52
45
  }
53
- } else {
54
- var keys = Object.keys(schema);
46
+ }
47
+ else {
48
+ const keys = Object.keys(schema);
55
49
  idx = keys.length;
56
50
  while (idx--) {
57
- var k = keys[idx];
58
- if (k.indexOf("__$") === 0) {
51
+ const k = keys[idx];
52
+ if (k.indexOf('__$') === 0) {
59
53
  continue;
60
54
  }
61
55
  result = findId(schema[k], id);
62
- if (result) { return result; }
56
+ if (result) {
57
+ return result;
58
+ }
63
59
  }
64
60
  }
65
61
  }
66
-
67
62
  /**
68
63
  *
69
64
  * @param {*} uri
@@ -71,118 +66,108 @@ function findId(schema, id) {
71
66
  *
72
67
  * @returns {void}
73
68
  */
74
- exports.cacheSchemaByUri = function (uri, schema) {
75
- var remotePath = getRemotePath(uri);
69
+ export function cacheSchemaByUri(uri, schema) {
70
+ const remotePath = getRemotePath(uri);
76
71
  if (remotePath) {
77
72
  this.cache[remotePath] = schema;
78
73
  }
79
- };
80
-
74
+ }
81
75
  /**
82
76
  *
83
77
  * @param {*} uri
84
78
  *
85
79
  * @returns {void}
86
80
  */
87
- exports.removeFromCacheByUri = function (uri) {
88
- var remotePath = getRemotePath(uri);
81
+ export function removeFromCacheByUri(uri) {
82
+ const remotePath = getRemotePath(uri);
89
83
  if (remotePath) {
90
84
  delete this.cache[remotePath];
91
85
  }
92
- };
93
-
86
+ }
94
87
  /**
95
88
  *
96
89
  * @param {*} uri
97
90
  *
98
91
  * @returns {boolean}
99
92
  */
100
- exports.checkCacheForUri = function (uri) {
101
- var remotePath = getRemotePath(uri);
93
+ export function checkCacheForUri(uri) {
94
+ const remotePath = getRemotePath(uri);
102
95
  return remotePath ? this.cache[remotePath] != null : false;
103
- };
104
-
105
- exports.getSchema = function (report, schema) {
106
- if (typeof schema === "object") {
107
- schema = exports.getSchemaByReference.call(this, report, schema);
96
+ }
97
+ export function getSchema(report, schema) {
98
+ if (typeof schema === 'object') {
99
+ schema = getSchemaByReference.call(this, report, schema);
108
100
  }
109
- if (typeof schema === "string") {
110
- schema = exports.getSchemaByUri.call(this, report, schema);
101
+ if (typeof schema === 'string') {
102
+ schema = getSchemaByUri.call(this, report, schema);
111
103
  }
112
104
  return schema;
113
- };
114
-
115
- exports.getSchemaByReference = function (report, key) {
116
- var i = this.referenceCache.length;
105
+ }
106
+ export function getSchemaByReference(report, key) {
107
+ let i = this.referenceCache.length;
117
108
  while (i--) {
118
109
  if (isequal(this.referenceCache[i][0], key)) {
119
110
  return this.referenceCache[i][1];
120
111
  }
121
112
  }
122
113
  // not found
123
- var schema = Utils.cloneDeep(key);
114
+ const schema = Utils.cloneDeep(key);
124
115
  this.referenceCache.push([key, schema]);
125
116
  return schema;
126
- };
127
-
128
- exports.getSchemaByUri = function (report, uri, root) {
129
- var remotePath = getRemotePath(uri),
130
- queryPath = getQueryPath(uri),
131
- result = remotePath ? this.cache[remotePath] : root;
132
-
117
+ }
118
+ export function getSchemaByUri(report, uri, root) {
119
+ const remotePath = getRemotePath(uri);
120
+ const queryPath = getQueryPath(uri);
121
+ let result = remotePath ? this.cache[remotePath] : root;
133
122
  if (result && remotePath) {
134
123
  // we need to avoid compiling schemas in a recursive loop
135
- var compileRemote = result !== root;
124
+ const compileRemote = result !== root;
136
125
  // now we need to compile and validate resolved schema (in case it's not already)
137
126
  if (compileRemote) {
138
-
139
127
  report.path.push(remotePath);
140
-
141
- var remoteReport;
142
-
143
- var anscestorReport = report.getAncestor(result.id);
128
+ let remoteReport;
129
+ const anscestorReport = report.getAncestor(result.id);
144
130
  if (anscestorReport) {
145
131
  remoteReport = anscestorReport;
146
- } else {
132
+ }
133
+ else {
147
134
  remoteReport = new Report(report);
148
- if (SchemaCompilation.compileSchema.call(this, remoteReport, result)) {
149
- var savedOptions = this.options;
135
+ if (compileSchema.call(this, remoteReport, result)) {
136
+ const savedOptions = this.options;
150
137
  try {
151
138
  // If custom validationOptions were provided to setRemoteReference(),
152
139
  // use them instead of the default options
153
140
  this.options = result.__$validationOptions || this.options;
154
141
  SchemaValidation.validateSchema.call(this, remoteReport, result);
155
- } finally {
142
+ }
143
+ finally {
156
144
  this.options = savedOptions;
157
145
  }
158
146
  }
159
147
  }
160
- var remoteReportIsValid = remoteReport.isValid();
148
+ const remoteReportIsValid = remoteReport.isValid();
161
149
  if (!remoteReportIsValid) {
162
- report.addError("REMOTE_NOT_VALID", [uri], remoteReport);
150
+ report.addError('REMOTE_NOT_VALID', [uri], remoteReport);
163
151
  }
164
-
165
152
  report.path.pop();
166
-
167
153
  if (!remoteReportIsValid) {
168
154
  return undefined;
169
155
  }
170
156
  }
171
157
  }
172
-
173
158
  if (result && queryPath) {
174
- var parts = queryPath.split("/");
175
- for (var idx = 0, lim = parts.length; result && idx < lim; idx++) {
176
- var key = decodeJSONPointer(parts[idx]);
177
- if (idx === 0) { // it's an id
159
+ const parts = queryPath.split('/');
160
+ for (let idx = 0, lim = parts.length; result && idx < lim; idx++) {
161
+ const key = decodeJSONPointer(parts[idx]);
162
+ if (idx === 0) {
163
+ // it's an id
178
164
  result = findId(result, key);
179
- } else { // it's a path behind id
165
+ }
166
+ else {
167
+ // it's a path behind id
180
168
  result = result[key];
181
169
  }
182
170
  }
183
171
  }
184
-
185
172
  return result;
186
- };
187
-
188
- exports.getRemotePath = getRemotePath;
173
+ }