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.
- package/README.md +7 -11
- package/bin/z-schema +0 -0
- package/cjs/index.d.ts +192 -117
- package/cjs/index.js +949 -998
- 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 +10 -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} +66 -77
- package/package.json +8 -4
- package/src/{Errors.ts → errors.ts} +4 -0
- package/src/format-validators.ts +191 -0
- package/src/index.ts +12 -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} +108 -113
- package/umd/ZSchema.js +949 -998
- package/umd/ZSchema.min.js +1 -1
- package/dist/FormatValidators.js +0 -136
- package/dist/SchemaCache.js +0 -173
- 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 -97
- package/src/SchemaCache.ts +0 -189
- 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,68 +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
|
-
/**
|
|
147
|
-
* @param schema - JSON object representing schema
|
|
148
|
-
* @returns {boolean} true if schema is valid.
|
|
149
|
-
*/
|
|
150
134
|
validateSchema(schema) {
|
|
151
135
|
if (Array.isArray(schema) && schema.length === 0) {
|
|
152
136
|
throw new Error('.validateSchema was called with an empty array');
|
|
153
137
|
}
|
|
154
138
|
const report = new Report(this.options);
|
|
155
|
-
schema =
|
|
156
|
-
const compiled =
|
|
139
|
+
schema = this.scache.getSchema(report, schema);
|
|
140
|
+
const compiled = this.sc.compileSchema(report, schema);
|
|
157
141
|
if (compiled) {
|
|
158
|
-
|
|
142
|
+
this.sv.validateSchema(report, schema);
|
|
159
143
|
}
|
|
160
144
|
this.lastReport = report;
|
|
161
145
|
return report.isValid();
|
|
@@ -169,9 +153,9 @@ export class ZSchema {
|
|
|
169
153
|
options = {};
|
|
170
154
|
}
|
|
171
155
|
this.validateOptions = options;
|
|
172
|
-
const
|
|
173
|
-
if (
|
|
174
|
-
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!');
|
|
175
159
|
if (callback) {
|
|
176
160
|
setTimeout(function () {
|
|
177
161
|
callback(e, false);
|
|
@@ -185,17 +169,18 @@ export class ZSchema {
|
|
|
185
169
|
report.json = json;
|
|
186
170
|
if (typeof schema === 'string') {
|
|
187
171
|
const schemaName = schema;
|
|
188
|
-
|
|
189
|
-
if (!
|
|
172
|
+
const _schema = this.scache.getSchema(report, schemaName);
|
|
173
|
+
if (!_schema) {
|
|
190
174
|
throw new Error("Schema with id '" + schemaName + "' wasn't found in the validator cache!");
|
|
191
175
|
}
|
|
176
|
+
schema = _schema;
|
|
192
177
|
}
|
|
193
178
|
else {
|
|
194
|
-
schema =
|
|
179
|
+
schema = this.scache.getSchema(report, schema);
|
|
195
180
|
}
|
|
196
181
|
let compiled = false;
|
|
197
182
|
if (!foundError) {
|
|
198
|
-
compiled =
|
|
183
|
+
compiled = this.sc.compileSchema(report, schema);
|
|
199
184
|
}
|
|
200
185
|
if (!compiled) {
|
|
201
186
|
this.lastReport = report;
|
|
@@ -203,7 +188,7 @@ export class ZSchema {
|
|
|
203
188
|
}
|
|
204
189
|
let validated = false;
|
|
205
190
|
if (!foundError) {
|
|
206
|
-
validated =
|
|
191
|
+
validated = this.sv.validateSchema(report, schema);
|
|
207
192
|
}
|
|
208
193
|
if (!validated) {
|
|
209
194
|
this.lastReport = report;
|
|
@@ -217,7 +202,7 @@ export class ZSchema {
|
|
|
217
202
|
}
|
|
218
203
|
}
|
|
219
204
|
if (!foundError) {
|
|
220
|
-
|
|
205
|
+
validateJson.call(this, report, schema, json);
|
|
221
206
|
}
|
|
222
207
|
if (callback) {
|
|
223
208
|
report.processAsyncTasks(this.options.asyncTimeout, callback);
|
|
@@ -234,6 +219,9 @@ export class ZSchema {
|
|
|
234
219
|
* Returns an Error object for the most recent failed validation, or null if the validation was successful.
|
|
235
220
|
*/
|
|
236
221
|
getLastError() {
|
|
222
|
+
if (!this.lastReport) {
|
|
223
|
+
throw new Error(`getLastError() called before doing any validation!`);
|
|
224
|
+
}
|
|
237
225
|
if (this.lastReport.errors.length === 0) {
|
|
238
226
|
return null;
|
|
239
227
|
}
|
|
@@ -251,27 +239,29 @@ export class ZSchema {
|
|
|
251
239
|
return this.lastReport && this.lastReport.errors.length > 0 ? this.lastReport.errors : null;
|
|
252
240
|
}
|
|
253
241
|
setRemoteReference(uri, schema, validationOptions) {
|
|
242
|
+
let _schema;
|
|
254
243
|
if (typeof schema === 'string') {
|
|
255
|
-
|
|
244
|
+
_schema = JSON.parse(schema);
|
|
256
245
|
}
|
|
257
246
|
else {
|
|
258
|
-
|
|
247
|
+
_schema = deepClone(schema);
|
|
259
248
|
}
|
|
260
249
|
if (validationOptions) {
|
|
261
|
-
|
|
250
|
+
_schema.__$validationOptions = normalizeOptions(validationOptions);
|
|
262
251
|
}
|
|
263
|
-
|
|
252
|
+
this.scache.cacheSchemaByUri(uri, _schema);
|
|
264
253
|
}
|
|
265
254
|
compileSchema(schema) {
|
|
266
255
|
const report = new Report(this.options);
|
|
267
|
-
schema =
|
|
268
|
-
|
|
256
|
+
schema = this.scache.getSchema(report, schema);
|
|
257
|
+
this.sc.compileSchema(report, schema);
|
|
269
258
|
this.lastReport = report;
|
|
270
259
|
return report.isValid();
|
|
271
260
|
}
|
|
272
261
|
getMissingReferences(arr) {
|
|
273
|
-
arr = arr || this.lastReport
|
|
274
|
-
let res = []
|
|
262
|
+
arr = arr || this.lastReport?.errors || [];
|
|
263
|
+
let res = [];
|
|
264
|
+
let idx = arr.length;
|
|
275
265
|
while (idx--) {
|
|
276
266
|
const error = arr[idx];
|
|
277
267
|
if (error.code === 'UNRESOLVABLE_REFERENCE') {
|
|
@@ -291,7 +281,7 @@ export class ZSchema {
|
|
|
291
281
|
const missingRemoteReferences = [];
|
|
292
282
|
let idx = missingReferences.length;
|
|
293
283
|
while (idx--) {
|
|
294
|
-
const remoteReference =
|
|
284
|
+
const remoteReference = getRemotePath(missingReferences[idx]);
|
|
295
285
|
if (remoteReference && missingRemoteReferences.indexOf(remoteReference) === -1) {
|
|
296
286
|
missingRemoteReferences.push(remoteReference);
|
|
297
287
|
}
|
|
@@ -300,14 +290,13 @@ export class ZSchema {
|
|
|
300
290
|
}
|
|
301
291
|
getResolvedSchema(schema) {
|
|
302
292
|
const report = new Report(this.options);
|
|
303
|
-
schema =
|
|
293
|
+
schema = this.scache.getSchema(report, schema);
|
|
304
294
|
// clone before making any modifications
|
|
305
|
-
schema =
|
|
295
|
+
schema = deepClone(schema);
|
|
306
296
|
const visited = [];
|
|
307
|
-
// clean-up the schema and resolve references
|
|
308
297
|
const cleanup = function (schema) {
|
|
309
298
|
let key;
|
|
310
|
-
const typeOf =
|
|
299
|
+
const typeOf = whatIs(schema);
|
|
311
300
|
if (typeOf !== 'object' && typeOf !== 'array') {
|
|
312
301
|
return;
|
|
313
302
|
}
|
|
@@ -360,6 +349,6 @@ export class ZSchema {
|
|
|
360
349
|
static setSchemaReader(schemaReader) {
|
|
361
350
|
ZSchema.schemaReader = schemaReader;
|
|
362
351
|
}
|
|
363
|
-
static schemaSymbol =
|
|
364
|
-
static jsonSymbol =
|
|
352
|
+
static schemaSymbol = schemaSymbol;
|
|
353
|
+
static jsonSymbol = jsonSymbol;
|
|
365
354
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "z-schema",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.6",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=22.0.0"
|
|
6
6
|
},
|
|
@@ -61,13 +61,15 @@
|
|
|
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
|
-
"test:sample": "npx vitest run --silent=false --hideSkippedTests --project node -t \"invalid definition\""
|
|
71
|
+
"test:sample": "npx vitest run --silent=false --hideSkippedTests --project node -t \"invalid definition\"",
|
|
72
|
+
"release:commit": "node -e \"const v=process.argv[1]; if(!v) throw new Error('Usage: npm run release:empty-commit -- <version>'); require('child_process').execSync(`git commit --allow-empty -m \\\"chore: release ${v}\\\" -m \\\"Release-As: ${v}\\\"`, {stdio:'inherit'});\" --"
|
|
71
73
|
},
|
|
72
74
|
"dependencies": {
|
|
73
75
|
"lodash.get": "^4.4.2",
|
|
@@ -87,7 +89,9 @@
|
|
|
87
89
|
"@rollup/plugin-terser": "^0.4.4",
|
|
88
90
|
"@rollup/plugin-typescript": "^12.3.0",
|
|
89
91
|
"@types/lodash.get": "^4.4.9",
|
|
92
|
+
"@types/lodash.isequal": "^4.5.8",
|
|
90
93
|
"@types/node": "^25.1.0",
|
|
94
|
+
"@types/validator": "^13.15.10",
|
|
91
95
|
"@vitest/browser-playwright": "^4.0.18",
|
|
92
96
|
"@vitest/eslint-plugin": "^1.6.6",
|
|
93
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,14 @@
|
|
|
1
|
-
import { ZSchema } from './
|
|
1
|
+
import { ZSchema } from './z-schema.js';
|
|
2
|
+
|
|
3
|
+
// Export types and interfaces from relevant files
|
|
4
|
+
export type * from './errors.js';
|
|
5
|
+
export type * from './format-validators.js';
|
|
6
|
+
export type * from './json-schema.js';
|
|
7
|
+
export type * from './json-validation.js';
|
|
8
|
+
export type * from './report.js';
|
|
9
|
+
export type * from './schema-cache.js';
|
|
10
|
+
export type * from './schema-compiler.js';
|
|
11
|
+
export type * from './schema-validator.js';
|
|
12
|
+
export type * from './z-schema.js';
|
|
2
13
|
|
|
3
14
|
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
|
+
};
|