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.
Files changed (75) hide show
  1. package/README.md +0 -9
  2. package/cjs/index.d.ts +179 -119
  3. package/cjs/index.js +1048 -1101
  4. package/{src/FormatValidators.ts → dist/format-validators.js} +97 -65
  5. package/dist/index.js +1 -1
  6. package/dist/json-schema.js +40 -0
  7. package/dist/{JsonValidation.js → json-validation.js} +75 -69
  8. package/dist/{Report.js → report.js} +35 -45
  9. package/dist/schema-cache.js +109 -0
  10. package/dist/schema-compiler.js +255 -0
  11. package/dist/{SchemaValidation.js → schema-validator.js} +153 -149
  12. package/dist/types/{Errors.d.ts → errors.d.ts} +2 -0
  13. package/dist/types/format-validators.d.ts +10 -0
  14. package/dist/types/index.d.ts +6 -1
  15. package/dist/types/json-schema.d.ts +50 -0
  16. package/dist/types/json-validation.d.ts +7 -0
  17. package/dist/types/{Report.d.ts → report.d.ts} +22 -23
  18. package/dist/types/schema-cache.d.ts +17 -0
  19. package/dist/types/schema-compiler.d.ts +16 -0
  20. package/dist/types/schema-validator.d.ts +10 -0
  21. package/dist/types/utils/array.d.ts +2 -0
  22. package/dist/types/utils/clone.d.ts +2 -0
  23. package/dist/types/utils/json.d.ts +7 -0
  24. package/dist/types/utils/symbols.d.ts +2 -0
  25. package/dist/types/utils/unicode.d.ts +14 -0
  26. package/dist/types/utils/uri.d.ts +4 -0
  27. package/dist/types/utils/what-is.d.ts +3 -0
  28. package/dist/types/z-schema.d.ts +75 -0
  29. package/dist/utils/array.js +27 -0
  30. package/dist/utils/clone.js +61 -0
  31. package/dist/utils/json.js +59 -0
  32. package/dist/utils/symbols.js +2 -0
  33. package/dist/utils/unicode.js +45 -0
  34. package/dist/utils/uri.js +15 -0
  35. package/dist/utils/what-is.js +29 -0
  36. package/dist/{ZSchema.js → z-schema.js} +69 -84
  37. package/package.json +6 -3
  38. package/src/{Errors.ts → errors.ts} +4 -0
  39. package/src/format-validators.ts +191 -0
  40. package/src/index.ts +8 -1
  41. package/src/json-schema.ts +97 -0
  42. package/src/{JsonValidation.ts → json-validation.ts} +137 -127
  43. package/src/{Report.ts → report.ts} +60 -70
  44. package/src/schema-cache.ts +122 -0
  45. package/src/schema-compiler.ts +300 -0
  46. package/src/{SchemaValidation.ts → schema-validator.ts} +213 -215
  47. package/src/utils/array.ts +29 -0
  48. package/src/utils/clone.ts +63 -0
  49. package/src/utils/json.ts +74 -0
  50. package/src/utils/symbols.ts +3 -0
  51. package/src/utils/unicode.ts +43 -0
  52. package/src/utils/uri.ts +18 -0
  53. package/src/utils/what-is.ts +46 -0
  54. package/src/{ZSchema.ts → z-schema.ts} +112 -121
  55. package/umd/ZSchema.js +1048 -1101
  56. package/umd/ZSchema.min.js +1 -1
  57. package/dist/FormatValidators.js +0 -136
  58. package/dist/SchemaCache.js +0 -172
  59. package/dist/SchemaCompilation.js +0 -259
  60. package/dist/Utils.js +0 -266
  61. package/dist/types/FormatValidators.d.ts +0 -12
  62. package/dist/types/JsonValidation.d.ts +0 -37
  63. package/dist/types/SchemaCache.d.ts +0 -26
  64. package/dist/types/SchemaCompilation.d.ts +0 -1
  65. package/dist/types/SchemaValidation.d.ts +0 -6
  66. package/dist/types/Utils.d.ts +0 -64
  67. package/dist/types/ZSchema.d.ts +0 -99
  68. package/src/SchemaCache.ts +0 -188
  69. package/src/SchemaCompilation.ts +0 -293
  70. package/src/Utils.ts +0 -286
  71. /package/dist/{Errors.js → errors.js} +0 -0
  72. /package/dist/schemas/{hyper-schema.json → draft-04-hyper-schema.json} +0 -0
  73. /package/dist/schemas/{schema.json → draft-04-schema.json} +0 -0
  74. /package/src/schemas/{hyper-schema.json → draft-04-hyper-schema.json} +0 -0
  75. /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,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
- 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
- /** 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 = SchemaCache.getSchema.call(this, report, schema);
160
- const compiled = SchemaCompilation.compileSchema.call(this, report, schema);
139
+ schema = this.scache.getSchema(report, schema);
140
+ const compiled = this.sc.compileSchema(report, schema);
161
141
  if (compiled) {
162
- SchemaValidation.validateSchema.call(this, report, schema);
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 whatIs = Utils.whatIs(schema);
177
- if (whatIs !== 'string' && whatIs !== 'object') {
178
- 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!');
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
- schema = SchemaCache.getSchema.call(this, report, schemaName);
193
- if (!schema) {
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
- schema = SchemaCache.getSchema.call(this, report, schema);
179
+ _schema = this.scache.getSchema(report, schema);
199
180
  }
200
181
  let compiled = false;
201
182
  if (!foundError) {
202
- compiled = SchemaCompilation.compileSchema.call(this, report, schema);
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 = SchemaValidation.validateSchema.call(this, report, schema);
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 = schema;
218
- schema = get(schema, options.schemaPath);
219
- if (!schema) {
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
- JsonValidation.validate.call(this, report, schema, json);
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
- schema = JSON.parse(schema);
244
+ _schema = JSON.parse(schema);
260
245
  }
261
246
  else {
262
- schema = Utils.cloneDeep(schema);
247
+ _schema = deepClone(schema);
263
248
  }
264
249
  if (validationOptions) {
265
- schema.__$validationOptions = normalizeOptions(validationOptions);
250
+ _schema.__$validationOptions = normalizeOptions(validationOptions);
266
251
  }
267
- SchemaCache.cacheSchemaByUri.call(this, uri, schema);
252
+ this.scache.cacheSchemaByUri(uri, _schema);
268
253
  }
269
254
  compileSchema(schema) {
270
255
  const report = new Report(this.options);
271
- schema = SchemaCache.getSchema.call(this, report, schema);
272
- SchemaCompilation.compileSchema.call(this, report, schema);
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.errors;
278
- let res = [], idx = arr.length;
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 = SchemaCache.getRemotePath(missingReferences[idx]);
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 = SchemaCache.getSchema.call(this, report, schema);
293
+ schema = this.scache.getSchema(report, schema);
308
294
  // clone before making any modifications
309
- schema = Utils.cloneDeep(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 = Utils.whatIs(schema);
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 = Utils.schemaSymbol;
368
- static jsonSymbol = Utils.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.5",
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 './ZSchema.js';
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
+ };