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
@@ -1,13 +1,18 @@
1
1
  import get from 'lodash.get';
2
- import { Report } from './Report.js';
3
- import { FormatValidators } from './FormatValidators.js';
4
- import * as JsonValidation from './JsonValidation.js';
5
- import * as SchemaCache from './SchemaCache.js';
6
- import * as SchemaCompilation from './SchemaCompilation.js';
7
- import * as SchemaValidation from './SchemaValidation.js';
8
- import * as Utils from './Utils.js';
9
- import Draft4Schema from './schemas/schema.json' with { type: 'json' };
10
- import Draft4HyperSchema from './schemas/hyper-schema.json' with { type: 'json' };
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
- function normalizeOptions(options) {
65
+ const normalizeOptions = (options) => {
61
66
  let normalized;
62
67
  // options
63
68
  if (typeof options === 'object') {
64
- let keys = Object.keys(options), idx = keys.length, key;
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] = Utils.clone(defaultOptions[key]);
85
+ options[key] = shallowClone(defaultOptions[key]);
79
86
  }
80
87
  }
81
88
  normalized = options;
82
89
  }
83
90
  else {
84
- normalized = Utils.clone(defaultOptions);
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
- lastReport;
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
- delete FormatValidators[name];
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 Object.keys(FormatValidators);
113
+ return getRegisteredFormats();
128
114
  }
129
115
  static getDefaultOptions() {
130
- return Utils.cloneDeep(defaultOptions);
116
+ return deepClone(defaultOptions);
131
117
  }
132
- cache;
133
- referenceCache;
134
- validateOptions;
118
+ lastReport;
119
+ scache;
120
+ sc;
121
+ sv;
122
+ validateOptions = {};
135
123
  options;
136
124
  constructor(options) {
137
- this.cache = {};
138
- this.referenceCache = [];
139
- this.validateOptions = {};
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 = SchemaCache.getSchema.call(this, report, schema);
156
- const compiled = SchemaCompilation.compileSchema.call(this, report, schema);
139
+ schema = this.scache.getSchema(report, schema);
140
+ const compiled = this.sc.compileSchema(report, schema);
157
141
  if (compiled) {
158
- SchemaValidation.validateSchema.call(this, report, schema);
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 whatIs = Utils.whatIs(schema);
173
- if (whatIs !== 'string' && whatIs !== 'object') {
174
- const e = new Error('Invalid .validate call - schema must be a string or object but ' + whatIs + ' was passed!');
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
- schema = SchemaCache.getSchema.call(this, report, schemaName);
189
- if (!schema) {
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 = SchemaCache.getSchema.call(this, report, schema);
179
+ schema = this.scache.getSchema(report, schema);
195
180
  }
196
181
  let compiled = false;
197
182
  if (!foundError) {
198
- compiled = SchemaCompilation.compileSchema.call(this, report, schema);
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 = SchemaValidation.validateSchema.call(this, report, schema);
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
- JsonValidation.validate.call(this, report, schema, json);
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
- schema = JSON.parse(schema);
244
+ _schema = JSON.parse(schema);
256
245
  }
257
246
  else {
258
- schema = Utils.cloneDeep(schema);
247
+ _schema = deepClone(schema);
259
248
  }
260
249
  if (validationOptions) {
261
- schema.__$validationOptions = normalizeOptions(validationOptions);
250
+ _schema.__$validationOptions = normalizeOptions(validationOptions);
262
251
  }
263
- SchemaCache.cacheSchemaByUri.call(this, uri, schema);
252
+ this.scache.cacheSchemaByUri(uri, _schema);
264
253
  }
265
254
  compileSchema(schema) {
266
255
  const report = new Report(this.options);
267
- schema = SchemaCache.getSchema.call(this, report, schema);
268
- SchemaCompilation.compileSchema.call(this, report, schema);
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.errors;
274
- let res = [], idx = arr.length;
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 = SchemaCache.getRemotePath(missingReferences[idx]);
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 = SchemaCache.getSchema.call(this, report, schema);
293
+ schema = this.scache.getSchema(report, schema);
304
294
  // clone before making any modifications
305
- schema = Utils.cloneDeep(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 = Utils.whatIs(schema);
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 = Utils.schemaSymbol;
364
- static jsonSymbol = Utils.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.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 './ZSchema.js';
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
+ };