z-schema 7.0.5 → 7.0.7
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 +0 -9
- package/cjs/index.d.ts +179 -119
- package/cjs/index.js +1048 -1101
- package/{src/FormatValidators.ts → dist/format-validators.js} +97 -65
- package/dist/index.js +1 -1
- package/dist/json-schema.js +40 -0
- package/dist/{JsonValidation.js → json-validation.js} +75 -69
- package/dist/{Report.js → report.js} +35 -45
- package/dist/schema-cache.js +109 -0
- package/dist/schema-compiler.js +255 -0
- package/dist/{SchemaValidation.js → schema-validator.js} +153 -149
- package/dist/types/{Errors.d.ts → errors.d.ts} +2 -0
- package/dist/types/format-validators.d.ts +10 -0
- package/dist/types/index.d.ts +6 -1
- package/dist/types/json-schema.d.ts +50 -0
- package/dist/types/json-validation.d.ts +7 -0
- package/dist/types/{Report.d.ts → report.d.ts} +22 -23
- package/dist/types/schema-cache.d.ts +17 -0
- package/dist/types/schema-compiler.d.ts +16 -0
- package/dist/types/schema-validator.d.ts +10 -0
- package/dist/types/utils/array.d.ts +2 -0
- package/dist/types/utils/clone.d.ts +2 -0
- package/dist/types/utils/json.d.ts +7 -0
- package/dist/types/utils/symbols.d.ts +2 -0
- package/dist/types/utils/unicode.d.ts +14 -0
- package/dist/types/utils/uri.d.ts +4 -0
- package/dist/types/utils/what-is.d.ts +3 -0
- package/dist/types/z-schema.d.ts +75 -0
- package/dist/utils/array.js +27 -0
- package/dist/utils/clone.js +61 -0
- package/dist/utils/json.js +59 -0
- package/dist/utils/symbols.js +2 -0
- package/dist/utils/unicode.js +45 -0
- package/dist/utils/uri.js +15 -0
- package/dist/utils/what-is.js +29 -0
- package/dist/{ZSchema.js → z-schema.js} +69 -84
- package/package.json +6 -3
- package/src/{Errors.ts → errors.ts} +4 -0
- package/src/format-validators.ts +191 -0
- package/src/index.ts +8 -1
- package/src/json-schema.ts +97 -0
- package/src/{JsonValidation.ts → json-validation.ts} +137 -127
- package/src/{Report.ts → report.ts} +60 -70
- package/src/schema-cache.ts +122 -0
- package/src/schema-compiler.ts +300 -0
- package/src/{SchemaValidation.ts → schema-validator.ts} +213 -215
- package/src/utils/array.ts +29 -0
- package/src/utils/clone.ts +63 -0
- package/src/utils/json.ts +74 -0
- package/src/utils/symbols.ts +3 -0
- package/src/utils/unicode.ts +43 -0
- package/src/utils/uri.ts +18 -0
- package/src/utils/what-is.ts +46 -0
- package/src/{ZSchema.ts → z-schema.ts} +112 -121
- package/umd/ZSchema.js +1048 -1101
- package/umd/ZSchema.min.js +1 -1
- package/dist/FormatValidators.js +0 -136
- package/dist/SchemaCache.js +0 -172
- package/dist/SchemaCompilation.js +0 -259
- package/dist/Utils.js +0 -266
- package/dist/types/FormatValidators.d.ts +0 -12
- package/dist/types/JsonValidation.d.ts +0 -37
- package/dist/types/SchemaCache.d.ts +0 -26
- package/dist/types/SchemaCompilation.d.ts +0 -1
- package/dist/types/SchemaValidation.d.ts +0 -6
- package/dist/types/Utils.d.ts +0 -64
- package/dist/types/ZSchema.d.ts +0 -99
- package/src/SchemaCache.ts +0 -188
- package/src/SchemaCompilation.ts +0 -293
- package/src/Utils.ts +0 -286
- /package/dist/{Errors.js → errors.js} +0 -0
- /package/dist/schemas/{hyper-schema.json → draft-04-hyper-schema.json} +0 -0
- /package/dist/schemas/{schema.json → draft-04-schema.json} +0 -0
- /package/src/schemas/{hyper-schema.json → draft-04-hyper-schema.json} +0 -0
- /package/src/schemas/{schema.json → draft-04-schema.json} +0 -0
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import get from 'lodash.get';
|
|
2
|
-
import { Report } from './
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
2
|
+
import { Report } from './report.js';
|
|
3
|
+
import { getRegisteredFormats, registerFormat, unregisterFormat } from './format-validators.js';
|
|
4
|
+
import { validate as validateJson } from './json-validation.js';
|
|
5
|
+
import { SchemaCache } from './schema-cache.js';
|
|
6
|
+
import { SchemaCompiler } from './schema-compiler.js';
|
|
7
|
+
import { SchemaValidator } from './schema-validator.js';
|
|
8
|
+
import { shallowClone, deepClone } from './utils/clone.js';
|
|
9
|
+
import { whatIs } from './utils/what-is.js';
|
|
10
|
+
import { schemaSymbol, jsonSymbol } from './utils/symbols.js';
|
|
11
|
+
import { getRemotePath } from './utils/uri.js';
|
|
12
|
+
import _Draft4Schema from './schemas/draft-04-schema.json' with { type: 'json' };
|
|
13
|
+
import _Draft4HyperSchema from './schemas/draft-04-hyper-schema.json' with { type: 'json' };
|
|
14
|
+
const Draft4Schema = _Draft4Schema;
|
|
15
|
+
const Draft4HyperSchema = _Draft4HyperSchema;
|
|
11
16
|
/**
|
|
12
17
|
* default options
|
|
13
18
|
*/
|
|
@@ -57,11 +62,13 @@ const defaultOptions = {
|
|
|
57
62
|
// function to be called on every schema
|
|
58
63
|
customValidator: null,
|
|
59
64
|
};
|
|
60
|
-
|
|
65
|
+
const normalizeOptions = (options) => {
|
|
61
66
|
let normalized;
|
|
62
67
|
// options
|
|
63
68
|
if (typeof options === 'object') {
|
|
64
|
-
let keys = Object.keys(options)
|
|
69
|
+
let keys = Object.keys(options);
|
|
70
|
+
let idx = keys.length;
|
|
71
|
+
let key;
|
|
65
72
|
// check that the options are correctly configured
|
|
66
73
|
while (idx--) {
|
|
67
74
|
key = keys[idx];
|
|
@@ -75,13 +82,13 @@ function normalizeOptions(options) {
|
|
|
75
82
|
while (idx--) {
|
|
76
83
|
key = keys[idx];
|
|
77
84
|
if (options[key] === undefined) {
|
|
78
|
-
options[key] =
|
|
85
|
+
options[key] = shallowClone(defaultOptions[key]);
|
|
79
86
|
}
|
|
80
87
|
}
|
|
81
88
|
normalized = options;
|
|
82
89
|
}
|
|
83
90
|
else {
|
|
84
|
-
normalized =
|
|
91
|
+
normalized = shallowClone(defaultOptions);
|
|
85
92
|
}
|
|
86
93
|
if (normalized.strictMode === true) {
|
|
87
94
|
normalized.forceAdditional = true;
|
|
@@ -94,72 +101,45 @@ function normalizeOptions(options) {
|
|
|
94
101
|
normalized.noEmptyArrays = true;
|
|
95
102
|
}
|
|
96
103
|
return normalized;
|
|
97
|
-
}
|
|
104
|
+
};
|
|
98
105
|
export class ZSchema {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
* Register a custom format.
|
|
102
|
-
*
|
|
103
|
-
* @param name - name of the custom format
|
|
104
|
-
* @param validatorFunction - custom format validator function.
|
|
105
|
-
* Returns `true` if `value` matches the custom format.
|
|
106
|
-
*/
|
|
107
|
-
static registerFormat(formatName, validatorFunction) {
|
|
108
|
-
FormatValidators[formatName] = validatorFunction;
|
|
106
|
+
static registerFormat(name, validatorFunction) {
|
|
107
|
+
return registerFormat(name, validatorFunction);
|
|
109
108
|
}
|
|
110
|
-
/**
|
|
111
|
-
* Unregister a format.
|
|
112
|
-
*
|
|
113
|
-
* @param name - name of the custom format
|
|
114
|
-
*/
|
|
115
109
|
static unregisterFormat(name) {
|
|
116
|
-
|
|
110
|
+
return unregisterFormat(name);
|
|
117
111
|
}
|
|
118
|
-
/**
|
|
119
|
-
* Get the list of all registered formats.
|
|
120
|
-
*
|
|
121
|
-
* Both the names of the burned-in formats and the custom format names are
|
|
122
|
-
* returned by this function.
|
|
123
|
-
*
|
|
124
|
-
* @returns {string[]} the list of all registered format names.
|
|
125
|
-
*/
|
|
126
112
|
static getRegisteredFormats() {
|
|
127
|
-
return
|
|
113
|
+
return getRegisteredFormats();
|
|
128
114
|
}
|
|
129
115
|
static getDefaultOptions() {
|
|
130
|
-
return
|
|
116
|
+
return deepClone(defaultOptions);
|
|
131
117
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
118
|
+
lastReport;
|
|
119
|
+
scache;
|
|
120
|
+
sc;
|
|
121
|
+
sv;
|
|
122
|
+
validateOptions = {};
|
|
135
123
|
options;
|
|
136
124
|
constructor(options) {
|
|
137
|
-
this.
|
|
138
|
-
this.
|
|
139
|
-
this.
|
|
125
|
+
this.scache = new SchemaCache(this);
|
|
126
|
+
this.sc = new SchemaCompiler(this);
|
|
127
|
+
this.sv = new SchemaValidator(this);
|
|
140
128
|
this.options = normalizeOptions(options);
|
|
141
129
|
// Disable strict validation for the built-in schemas
|
|
142
130
|
const metaschemaOptions = normalizeOptions({});
|
|
143
131
|
this.setRemoteReference('http://json-schema.org/draft-04/schema', Draft4Schema, metaschemaOptions);
|
|
144
132
|
this.setRemoteReference('http://json-schema.org/draft-04/hyper-schema', Draft4HyperSchema, metaschemaOptions);
|
|
145
133
|
}
|
|
146
|
-
/** Used by SchemaCache to break circular dependency with SchemaCompilation */
|
|
147
|
-
_compileSchema(report, schema) {
|
|
148
|
-
return SchemaCompilation.compileSchema.call(this, report, schema);
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* @param schema - JSON object representing schema
|
|
152
|
-
* @returns {boolean} true if schema is valid.
|
|
153
|
-
*/
|
|
154
134
|
validateSchema(schema) {
|
|
155
135
|
if (Array.isArray(schema) && schema.length === 0) {
|
|
156
136
|
throw new Error('.validateSchema was called with an empty array');
|
|
157
137
|
}
|
|
158
138
|
const report = new Report(this.options);
|
|
159
|
-
schema =
|
|
160
|
-
const compiled =
|
|
139
|
+
schema = this.scache.getSchema(report, schema);
|
|
140
|
+
const compiled = this.sc.compileSchema(report, schema);
|
|
161
141
|
if (compiled) {
|
|
162
|
-
|
|
142
|
+
this.sv.validateSchema(report, schema);
|
|
163
143
|
}
|
|
164
144
|
this.lastReport = report;
|
|
165
145
|
return report.isValid();
|
|
@@ -173,9 +153,9 @@ export class ZSchema {
|
|
|
173
153
|
options = {};
|
|
174
154
|
}
|
|
175
155
|
this.validateOptions = options;
|
|
176
|
-
const
|
|
177
|
-
if (
|
|
178
|
-
const e = new Error('Invalid .validate call - schema must be a string or object but ' +
|
|
156
|
+
const schemaType = whatIs(schema);
|
|
157
|
+
if (schemaType !== 'string' && schemaType !== 'object') {
|
|
158
|
+
const e = new Error('Invalid .validate call - schema must be a string or object but ' + schemaType + ' was passed!');
|
|
179
159
|
if (callback) {
|
|
180
160
|
setTimeout(function () {
|
|
181
161
|
callback(e, false);
|
|
@@ -187,19 +167,20 @@ export class ZSchema {
|
|
|
187
167
|
let foundError = false;
|
|
188
168
|
const report = new Report(this.options);
|
|
189
169
|
report.json = json;
|
|
170
|
+
let _schema;
|
|
190
171
|
if (typeof schema === 'string') {
|
|
191
172
|
const schemaName = schema;
|
|
192
|
-
|
|
193
|
-
if (!
|
|
173
|
+
_schema = this.scache.getSchema(report, schemaName);
|
|
174
|
+
if (!_schema) {
|
|
194
175
|
throw new Error("Schema with id '" + schemaName + "' wasn't found in the validator cache!");
|
|
195
176
|
}
|
|
196
177
|
}
|
|
197
178
|
else {
|
|
198
|
-
|
|
179
|
+
_schema = this.scache.getSchema(report, schema);
|
|
199
180
|
}
|
|
200
181
|
let compiled = false;
|
|
201
182
|
if (!foundError) {
|
|
202
|
-
compiled =
|
|
183
|
+
compiled = this.sc.compileSchema(report, _schema);
|
|
203
184
|
}
|
|
204
185
|
if (!compiled) {
|
|
205
186
|
this.lastReport = report;
|
|
@@ -207,21 +188,21 @@ export class ZSchema {
|
|
|
207
188
|
}
|
|
208
189
|
let validated = false;
|
|
209
190
|
if (!foundError) {
|
|
210
|
-
validated =
|
|
191
|
+
validated = this.sv.validateSchema(report, _schema);
|
|
211
192
|
}
|
|
212
193
|
if (!validated) {
|
|
213
194
|
this.lastReport = report;
|
|
214
195
|
foundError = true;
|
|
215
196
|
}
|
|
216
197
|
if (options.schemaPath) {
|
|
217
|
-
report.rootSchema =
|
|
218
|
-
|
|
219
|
-
if (!
|
|
198
|
+
report.rootSchema = _schema;
|
|
199
|
+
_schema = get(_schema, options.schemaPath);
|
|
200
|
+
if (!_schema) {
|
|
220
201
|
throw new Error("Schema path '" + options.schemaPath + "' wasn't found in the schema!");
|
|
221
202
|
}
|
|
222
203
|
}
|
|
223
204
|
if (!foundError) {
|
|
224
|
-
|
|
205
|
+
validateJson.call(this, report, _schema, json);
|
|
225
206
|
}
|
|
226
207
|
if (callback) {
|
|
227
208
|
report.processAsyncTasks(this.options.asyncTimeout, callback);
|
|
@@ -238,6 +219,9 @@ export class ZSchema {
|
|
|
238
219
|
* Returns an Error object for the most recent failed validation, or null if the validation was successful.
|
|
239
220
|
*/
|
|
240
221
|
getLastError() {
|
|
222
|
+
if (!this.lastReport) {
|
|
223
|
+
throw new Error(`getLastError() called before doing any validation!`);
|
|
224
|
+
}
|
|
241
225
|
if (this.lastReport.errors.length === 0) {
|
|
242
226
|
return null;
|
|
243
227
|
}
|
|
@@ -255,27 +239,29 @@ export class ZSchema {
|
|
|
255
239
|
return this.lastReport && this.lastReport.errors.length > 0 ? this.lastReport.errors : null;
|
|
256
240
|
}
|
|
257
241
|
setRemoteReference(uri, schema, validationOptions) {
|
|
242
|
+
let _schema;
|
|
258
243
|
if (typeof schema === 'string') {
|
|
259
|
-
|
|
244
|
+
_schema = JSON.parse(schema);
|
|
260
245
|
}
|
|
261
246
|
else {
|
|
262
|
-
|
|
247
|
+
_schema = deepClone(schema);
|
|
263
248
|
}
|
|
264
249
|
if (validationOptions) {
|
|
265
|
-
|
|
250
|
+
_schema.__$validationOptions = normalizeOptions(validationOptions);
|
|
266
251
|
}
|
|
267
|
-
|
|
252
|
+
this.scache.cacheSchemaByUri(uri, _schema);
|
|
268
253
|
}
|
|
269
254
|
compileSchema(schema) {
|
|
270
255
|
const report = new Report(this.options);
|
|
271
|
-
schema =
|
|
272
|
-
|
|
256
|
+
schema = this.scache.getSchema(report, schema);
|
|
257
|
+
this.sc.compileSchema(report, schema);
|
|
273
258
|
this.lastReport = report;
|
|
274
259
|
return report.isValid();
|
|
275
260
|
}
|
|
276
261
|
getMissingReferences(arr) {
|
|
277
|
-
arr = arr || this.lastReport
|
|
278
|
-
let res = []
|
|
262
|
+
arr = arr || this.lastReport?.errors || [];
|
|
263
|
+
let res = [];
|
|
264
|
+
let idx = arr.length;
|
|
279
265
|
while (idx--) {
|
|
280
266
|
const error = arr[idx];
|
|
281
267
|
if (error.code === 'UNRESOLVABLE_REFERENCE') {
|
|
@@ -295,7 +281,7 @@ export class ZSchema {
|
|
|
295
281
|
const missingRemoteReferences = [];
|
|
296
282
|
let idx = missingReferences.length;
|
|
297
283
|
while (idx--) {
|
|
298
|
-
const remoteReference =
|
|
284
|
+
const remoteReference = getRemotePath(missingReferences[idx]);
|
|
299
285
|
if (remoteReference && missingRemoteReferences.indexOf(remoteReference) === -1) {
|
|
300
286
|
missingRemoteReferences.push(remoteReference);
|
|
301
287
|
}
|
|
@@ -304,14 +290,13 @@ export class ZSchema {
|
|
|
304
290
|
}
|
|
305
291
|
getResolvedSchema(schema) {
|
|
306
292
|
const report = new Report(this.options);
|
|
307
|
-
schema =
|
|
293
|
+
schema = this.scache.getSchema(report, schema);
|
|
308
294
|
// clone before making any modifications
|
|
309
|
-
schema =
|
|
295
|
+
schema = deepClone(schema);
|
|
310
296
|
const visited = [];
|
|
311
|
-
// clean-up the schema and resolve references
|
|
312
297
|
const cleanup = function (schema) {
|
|
313
298
|
let key;
|
|
314
|
-
const typeOf =
|
|
299
|
+
const typeOf = whatIs(schema);
|
|
315
300
|
if (typeOf !== 'object' && typeOf !== 'array') {
|
|
316
301
|
return;
|
|
317
302
|
}
|
|
@@ -364,6 +349,6 @@ export class ZSchema {
|
|
|
364
349
|
static setSchemaReader(schemaReader) {
|
|
365
350
|
ZSchema.schemaReader = schemaReader;
|
|
366
351
|
}
|
|
367
|
-
static schemaSymbol =
|
|
368
|
-
static jsonSymbol =
|
|
352
|
+
static schemaSymbol = schemaSymbol;
|
|
353
|
+
static jsonSymbol = jsonSymbol;
|
|
369
354
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "z-schema",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.7",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=22.0.0"
|
|
6
6
|
},
|
|
@@ -61,10 +61,11 @@
|
|
|
61
61
|
"build:watch": "rollup -c -w",
|
|
62
62
|
"copy:module-json": "node -e \"fs.copyFileSync('./src/package.json', './dist/package.json')\"",
|
|
63
63
|
"copy:schemas": "npm run copy:schema && npm run copy:hyper-schema",
|
|
64
|
-
"copy:schema": "node -e \"fs.copyFileSync('./json-schema/draft-04/schema', './src/schemas/schema.json')\"",
|
|
65
|
-
"copy:hyper-schema": "node -e \"fs.copyFileSync('./json-schema/draft-04/hyper-schema', './src/schemas/hyper-schema.json')\"",
|
|
64
|
+
"copy:schema": "node -e \"fs.copyFileSync('./json-schema/draft-04/schema', './src/schemas/draft-04-schema.json')\"",
|
|
65
|
+
"copy:hyper-schema": "node -e \"fs.copyFileSync('./json-schema/draft-04/hyper-schema', './src/schemas/draft-04-hyper-schema.json')\"",
|
|
66
66
|
"prepublishOnly": "npm run clean && npm run build && npm test",
|
|
67
67
|
"test": "vitest run",
|
|
68
|
+
"test:quick": "npm run build && npm run test:node",
|
|
68
69
|
"test:browsers": "vitest run --project browsers --silent=true",
|
|
69
70
|
"test:node": "vitest run --project node --silent=true",
|
|
70
71
|
"test:sample": "npx vitest run --silent=false --hideSkippedTests --project node -t \"invalid definition\"",
|
|
@@ -88,7 +89,9 @@
|
|
|
88
89
|
"@rollup/plugin-terser": "^0.4.4",
|
|
89
90
|
"@rollup/plugin-typescript": "^12.3.0",
|
|
90
91
|
"@types/lodash.get": "^4.4.9",
|
|
92
|
+
"@types/lodash.isequal": "^4.5.8",
|
|
91
93
|
"@types/node": "^25.1.0",
|
|
94
|
+
"@types/validator": "^13.15.10",
|
|
92
95
|
"@vitest/browser-playwright": "^4.0.18",
|
|
93
96
|
"@vitest/eslint-plugin": "^1.6.6",
|
|
94
97
|
"browserify": "^17.0.0",
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
export type ErrorCode = keyof typeof Errors;
|
|
2
|
+
|
|
3
|
+
export type ErrorParam = string | number | Array<string | number>;
|
|
4
|
+
|
|
1
5
|
export const Errors = {
|
|
2
6
|
INVALID_TYPE: 'Expected type {0} but found type {1}',
|
|
3
7
|
INVALID_FORMAT: "Object didn't pass validation for format {0}: {1}",
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import validator from 'validator';
|
|
2
|
+
import { sortedKeys } from './utils/json.js';
|
|
3
|
+
|
|
4
|
+
const { isEmail, isIP, isURL } = validator;
|
|
5
|
+
|
|
6
|
+
export type FormatValidatorFn = (input: unknown) => boolean;
|
|
7
|
+
|
|
8
|
+
const dateValidator: FormatValidatorFn = (date: unknown) => {
|
|
9
|
+
if (typeof date !== 'string') {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
// full-date from http://tools.ietf.org/html/rfc3339#section-5.6
|
|
13
|
+
const matches = /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.exec(date);
|
|
14
|
+
if (matches === null) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
// var year = matches[1];
|
|
18
|
+
// var month = matches[2];
|
|
19
|
+
// var day = matches[3];
|
|
20
|
+
if (matches[2] < '01' || matches[2] > '12' || matches[3] < '01' || matches[3] > '31') {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
return true;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const dateTimeValidator: FormatValidatorFn = (dateTime: unknown) => {
|
|
27
|
+
if (typeof dateTime !== 'string') {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
// date-time from http://tools.ietf.org/html/rfc3339#section-5.6
|
|
31
|
+
const s = dateTime.toLowerCase().split('t');
|
|
32
|
+
if (!dateValidator(s[0])) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
const matches = /^([0-9]{2}):([0-9]{2}):([0-9]{2})(.[0-9]+)?(z|([+-][0-9]{2}:[0-9]{2}))$/.exec(s[1]);
|
|
36
|
+
if (matches === null) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
// var hour = matches[1];
|
|
40
|
+
// var minute = matches[2];
|
|
41
|
+
// var second = matches[3];
|
|
42
|
+
// var fraction = matches[4];
|
|
43
|
+
// var timezone = matches[5];
|
|
44
|
+
if (matches[1] > '23' || matches[2] > '59' || matches[3] > '59') {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
return true;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const emailValidator: FormatValidatorFn = (email: unknown) => {
|
|
51
|
+
if (typeof email !== 'string') {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
return isEmail(email, { require_tld: true });
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const hostnameValidator: FormatValidatorFn = (hostname: unknown) => {
|
|
58
|
+
if (typeof hostname !== 'string') {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
/*
|
|
62
|
+
http://json-schema.org/latest/json-schema-validation.html#anchor114
|
|
63
|
+
A string instance is valid against this attribute if it is a valid
|
|
64
|
+
representation for an Internet host name, as defined by RFC 1034, section 3.1 [RFC1034].
|
|
65
|
+
|
|
66
|
+
http://tools.ietf.org/html/rfc1034#section-3.5
|
|
67
|
+
|
|
68
|
+
<digit> ::= any one of the ten digits 0 through 9
|
|
69
|
+
var digit = /[0-9]/;
|
|
70
|
+
|
|
71
|
+
<letter> ::= any one of the 52 alphabetic characters A through Z in upper case and a through z in lower case
|
|
72
|
+
var letter = /[a-zA-Z]/;
|
|
73
|
+
|
|
74
|
+
<let-dig> ::= <letter> | <digit>
|
|
75
|
+
var letDig = /[0-9a-zA-Z]/;
|
|
76
|
+
|
|
77
|
+
<let-dig-hyp> ::= <let-dig> | "-"
|
|
78
|
+
var letDigHyp = /[-0-9a-zA-Z]/;
|
|
79
|
+
|
|
80
|
+
<ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
|
|
81
|
+
var ldhStr = /[-0-9a-zA-Z]+/;
|
|
82
|
+
|
|
83
|
+
<label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
|
|
84
|
+
var label = /[a-zA-Z](([-0-9a-zA-Z]+)?[0-9a-zA-Z])?/;
|
|
85
|
+
|
|
86
|
+
<subdomain> ::= <label> | <subdomain> "." <label>
|
|
87
|
+
var subdomain = /^[a-zA-Z](([-0-9a-zA-Z]+)?[0-9a-zA-Z])?(\.[a-zA-Z](([-0-9a-zA-Z]+)?[0-9a-zA-Z])?)*$/;
|
|
88
|
+
|
|
89
|
+
<domain> ::= <subdomain> | " "
|
|
90
|
+
var domain = null;
|
|
91
|
+
*/
|
|
92
|
+
const valid = /^[a-zA-Z](([-0-9a-zA-Z]+)?[0-9a-zA-Z])?(\.[a-zA-Z](([-0-9a-zA-Z]+)?[0-9a-zA-Z])?)*$/.test(hostname);
|
|
93
|
+
if (valid) {
|
|
94
|
+
// the sum of all label octets and label lengths is limited to 255.
|
|
95
|
+
if (hostname.length > 255) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
// Each node has a label, which is zero to 63 octets in length
|
|
99
|
+
const labels = hostname.split('.');
|
|
100
|
+
for (let i = 0; i < labels.length; i++) {
|
|
101
|
+
if (labels[i].length > 63) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return valid;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const ipv4Validator: FormatValidatorFn = (ipv4: unknown) => {
|
|
110
|
+
if (typeof ipv4 !== 'string') {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
return isIP(ipv4, 4);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const ipv6Validator: FormatValidatorFn = (ipv6: unknown) => {
|
|
117
|
+
if (typeof ipv6 !== 'string') {
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
return isIP(ipv6, 6);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const regexValidator: FormatValidatorFn = (input: unknown) => {
|
|
124
|
+
if (typeof input !== 'string') {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
RegExp(input);
|
|
129
|
+
return true;
|
|
130
|
+
} catch (_e) {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const strictUriValidator: FormatValidatorFn = (uri: unknown) => typeof uri !== 'string' || isURL(uri);
|
|
136
|
+
|
|
137
|
+
const uriValidator: FormatValidatorFn = function (uri: unknown) {
|
|
138
|
+
// https://github.com/zaggino/z-schema/issues/18
|
|
139
|
+
// RegExp from http://tools.ietf.org/html/rfc3986#appendix-B
|
|
140
|
+
return typeof uri !== 'string' || RegExp('^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?').test(uri);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export interface FormatValidatorsOptions {
|
|
144
|
+
strictUris?: boolean;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const inbuiltValidators: Record<string, FormatValidatorFn> = {
|
|
148
|
+
date: dateValidator,
|
|
149
|
+
'date-time': dateTimeValidator,
|
|
150
|
+
email: emailValidator,
|
|
151
|
+
hostname: hostnameValidator,
|
|
152
|
+
'host-name': hostnameValidator,
|
|
153
|
+
ipv4: ipv4Validator,
|
|
154
|
+
ipv6: ipv6Validator,
|
|
155
|
+
regex: regexValidator,
|
|
156
|
+
uri: uriValidator,
|
|
157
|
+
'strict-uri': strictUriValidator,
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const customValidators: Record<string, FormatValidatorFn> = {};
|
|
161
|
+
|
|
162
|
+
export function getFormatValidators(options?: FormatValidatorsOptions): Record<string, FormatValidatorFn> {
|
|
163
|
+
return {
|
|
164
|
+
...inbuiltValidators,
|
|
165
|
+
...(options?.strictUris ? { uri: strictUriValidator } : {}),
|
|
166
|
+
...customValidators,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function registerFormat(name: string, validatorFunction: FormatValidatorFn) {
|
|
171
|
+
customValidators[name] = validatorFunction;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function unregisterFormat(name: string) {
|
|
175
|
+
delete customValidators[name];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function getSupportedFormats() {
|
|
179
|
+
return sortedKeys({
|
|
180
|
+
...inbuiltValidators,
|
|
181
|
+
...customValidators,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function isFormatSupported(name: string): boolean {
|
|
186
|
+
return inbuiltValidators[name] != null || customValidators[name] != null;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function getRegisteredFormats() {
|
|
190
|
+
return sortedKeys(customValidators);
|
|
191
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
import { ZSchema } from './
|
|
1
|
+
import { ZSchema } from './z-schema.js';
|
|
2
|
+
|
|
3
|
+
// Export types and interfaces from relevant files
|
|
4
|
+
export type { FormatValidatorFn } from './format-validators.js';
|
|
5
|
+
export type { ErrorCode, ErrorParam, Errors } from './errors.js';
|
|
6
|
+
export type { JsonSchema, JsonSchemaType } from './json-schema.js';
|
|
7
|
+
export type { Report, SchemaError, SchemaErrorDetail } from './report.js';
|
|
8
|
+
export type { ZSchemaOptions, SchemaReader, ValidateOptions, ValidateCallback } from './z-schema.js';
|
|
2
9
|
|
|
3
10
|
export default ZSchema;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Reference } from './schema-compiler.js';
|
|
2
|
+
import { isObject } from './utils/what-is.js';
|
|
3
|
+
|
|
4
|
+
export interface JsonSchema {
|
|
5
|
+
$ref?: string;
|
|
6
|
+
id?: string;
|
|
7
|
+
$schema?: string;
|
|
8
|
+
title?: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
default?: unknown;
|
|
11
|
+
definitions?: Record<string, JsonSchema>;
|
|
12
|
+
type?: string | string[];
|
|
13
|
+
properties?: Record<string, JsonSchema>;
|
|
14
|
+
patternProperties?: Record<string, JsonSchema>;
|
|
15
|
+
dependencies?: Record<string, string[]>;
|
|
16
|
+
// properties
|
|
17
|
+
multipleOf?: number;
|
|
18
|
+
minimum?: number;
|
|
19
|
+
exclusiveMinimum?: boolean;
|
|
20
|
+
maximum?: number;
|
|
21
|
+
exclusiveMaximum?: boolean;
|
|
22
|
+
minLength?: number;
|
|
23
|
+
maxLength?: number;
|
|
24
|
+
pattern?: string;
|
|
25
|
+
additionalItems?: boolean | JsonSchema;
|
|
26
|
+
items?: JsonSchema | JsonSchema[];
|
|
27
|
+
minItems?: number;
|
|
28
|
+
maxItems?: number;
|
|
29
|
+
uniqueItems?: boolean;
|
|
30
|
+
minProperties?: number;
|
|
31
|
+
maxProperties?: number;
|
|
32
|
+
required?: string[];
|
|
33
|
+
additionalProperties?: boolean | JsonSchema;
|
|
34
|
+
enum?: Array<unknown>;
|
|
35
|
+
format?: string;
|
|
36
|
+
allOf?: JsonSchema[];
|
|
37
|
+
anyOf?: JsonSchema[];
|
|
38
|
+
oneOf?: JsonSchema[];
|
|
39
|
+
not?: JsonSchema;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export type JsonSchemaType = 'array' | 'boolean' | 'integer' | 'null' | 'number' | 'object' | 'string';
|
|
43
|
+
|
|
44
|
+
export interface ZSchemaInternalProperties {
|
|
45
|
+
__$compiled?: unknown;
|
|
46
|
+
__$missingReferences?: Reference[];
|
|
47
|
+
__$refResolved?: JsonSchema;
|
|
48
|
+
__$schemaResolved?: unknown;
|
|
49
|
+
__$validated?: boolean;
|
|
50
|
+
__$validationOptions?: unknown;
|
|
51
|
+
__$visited?: boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface JsonSchemaInternal extends JsonSchema, ZSchemaInternalProperties {}
|
|
55
|
+
|
|
56
|
+
export const findId = (schema: JsonSchemaInternal, id: string): JsonSchemaInternal | undefined => {
|
|
57
|
+
// process only arrays and objects
|
|
58
|
+
if (typeof schema !== 'object' || schema === null) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// no id means root so return itself
|
|
63
|
+
if (!id) {
|
|
64
|
+
return schema;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (schema.id) {
|
|
68
|
+
if (schema.id === id || (schema.id[0] === '#' && schema.id.substring(1) === id)) {
|
|
69
|
+
return schema;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
let idx, result;
|
|
74
|
+
if (Array.isArray(schema)) {
|
|
75
|
+
idx = schema.length;
|
|
76
|
+
while (idx--) {
|
|
77
|
+
result = findId(schema[idx], id);
|
|
78
|
+
if (result) {
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (isObject(schema)) {
|
|
84
|
+
const keys = Object.keys(schema) as Array<keyof JsonSchemaInternal>;
|
|
85
|
+
idx = keys.length;
|
|
86
|
+
while (idx--) {
|
|
87
|
+
const k = keys[idx];
|
|
88
|
+
if (k.indexOf('__$') === 0) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
result = findId(schema[k] as JsonSchemaInternal, id);
|
|
92
|
+
if (result) {
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
};
|