schema-shield 0.0.5 → 1.0.0

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/dist/index.mjs CHANGED
@@ -53,14 +53,18 @@ var ValidationError = class extends Error {
53
53
  };
54
54
  }
55
55
  };
56
- function getDefinedErrorFunctionForKey(key, schema) {
56
+ var FAIL_FAST_DEFINE_ERROR = () => true;
57
+ function getDefinedErrorFunctionForKey(key, schema, failFast) {
58
+ if (failFast) {
59
+ return FAIL_FAST_DEFINE_ERROR;
60
+ }
57
61
  const KeywordError = new ValidationError(`Invalid ${key}`);
58
62
  KeywordError.keyword = key;
59
63
  KeywordError.schema = schema;
60
64
  const defineError = (message, options = {}) => {
61
65
  KeywordError.message = message;
62
66
  KeywordError.item = options.item;
63
- KeywordError.cause = options.cause;
67
+ KeywordError.cause = options.cause && options.cause !== true ? options.cause : void 0;
64
68
  KeywordError.data = options.data;
65
69
  return KeywordError;
66
70
  };
@@ -69,34 +73,38 @@ function getDefinedErrorFunctionForKey(key, schema) {
69
73
  defineError
70
74
  );
71
75
  }
72
- function deepEqual(obj, other) {
73
- if (Array.isArray(obj) && Array.isArray(other)) {
74
- if (obj.length !== other.length) {
75
- return false;
76
+ function hasChanged(prev, current) {
77
+ if (Array.isArray(prev)) {
78
+ if (Array.isArray(current) === false) {
79
+ return true;
76
80
  }
77
- for (let i = 0; i < obj.length; i++) {
78
- if (!deepEqual(obj[i], other[i])) {
79
- return false;
81
+ if (prev.length !== current.length) {
82
+ return true;
83
+ }
84
+ for (let i = 0; i < current.length; i++) {
85
+ if (hasChanged(prev[i], current[i])) {
86
+ return true;
80
87
  }
81
88
  }
82
- return true;
89
+ return false;
83
90
  }
84
- if (typeof obj === "object" && typeof other === "object") {
85
- if (obj === null || other === null) {
86
- return obj === other;
91
+ if (typeof prev === "object" && prev !== null) {
92
+ if (typeof current !== "object" || current === null) {
93
+ return true;
87
94
  }
88
- const keys = Object.keys(obj);
89
- if (keys.length !== Object.keys(other).length) {
90
- return false;
95
+ for (const key in current) {
96
+ if (hasChanged(prev[key], current[key])) {
97
+ return true;
98
+ }
91
99
  }
92
- for (const key of keys) {
93
- if (!deepEqual(obj[key], other[key])) {
94
- return false;
100
+ for (const key in prev) {
101
+ if (hasChanged(prev[key], current[key])) {
102
+ return true;
95
103
  }
96
104
  }
97
- return true;
105
+ return false;
98
106
  }
99
- return obj === other;
107
+ return Object.is(prev, current) === false;
100
108
  }
101
109
  function isObject(data) {
102
110
  return typeof data === "object" && data !== null && !Array.isArray(data);
@@ -104,27 +112,116 @@ function isObject(data) {
104
112
  function areCloseEnough(a, b, epsilon = 1e-15) {
105
113
  return Math.abs(a - b) <= epsilon * Math.max(Math.abs(a), Math.abs(b));
106
114
  }
107
- function deepClone(obj) {
108
- if (Array.isArray(obj)) {
109
- const result = [];
110
- for (let i = 0; i < obj.length; i++) {
111
- result[i] = deepClone(obj[i]);
112
- }
113
- return result;
114
- }
115
- if (obj && obj.constructor && obj.constructor.name !== "Object") {
115
+ function deepClone(obj, cloneClassInstances = false, seen = /* @__PURE__ */ new WeakMap()) {
116
+ if (typeof obj === "undefined" || obj === null || typeof obj !== "object") {
116
117
  return obj;
117
118
  }
118
- if (isObject(obj)) {
119
- const result = {
120
- ...obj
121
- };
122
- for (const key in obj) {
123
- result[key] = deepClone(obj[key]);
119
+ if (seen.has(obj)) {
120
+ return seen.get(obj);
121
+ }
122
+ let clone;
123
+ if (typeof structuredClone === "function") {
124
+ clone = structuredClone(obj);
125
+ seen.set(obj, clone);
126
+ return clone;
127
+ }
128
+ switch (true) {
129
+ case Array.isArray(obj): {
130
+ clone = [];
131
+ seen.set(obj, clone);
132
+ for (let i = 0, l = obj.length; i < l; i++) {
133
+ clone[i] = deepClone(obj[i], cloneClassInstances, seen);
134
+ }
135
+ return clone;
136
+ }
137
+ case obj instanceof Date: {
138
+ clone = new Date(obj.getTime());
139
+ seen.set(obj, clone);
140
+ return clone;
141
+ }
142
+ case obj instanceof RegExp: {
143
+ clone = new RegExp(obj.source, obj.flags);
144
+ seen.set(obj, clone);
145
+ return clone;
146
+ }
147
+ case obj instanceof Map: {
148
+ clone = /* @__PURE__ */ new Map();
149
+ seen.set(obj, clone);
150
+ for (const [key, value] of obj.entries()) {
151
+ clone.set(
152
+ deepClone(key, cloneClassInstances, seen),
153
+ deepClone(value, cloneClassInstances, seen)
154
+ );
155
+ }
156
+ return clone;
157
+ }
158
+ case obj instanceof Set: {
159
+ clone = /* @__PURE__ */ new Set();
160
+ seen.set(obj, clone);
161
+ for (const value of obj.values()) {
162
+ clone.add(deepClone(value, cloneClassInstances, seen));
163
+ }
164
+ return clone;
165
+ }
166
+ case obj instanceof ArrayBuffer: {
167
+ clone = obj.slice(0);
168
+ seen.set(obj, clone);
169
+ return clone;
170
+ }
171
+ case ArrayBuffer.isView(obj): {
172
+ clone = new obj.constructor(obj.buffer.slice(0));
173
+ seen.set(obj, clone);
174
+ return clone;
175
+ }
176
+ case (typeof Buffer !== "undefined" && obj instanceof Buffer): {
177
+ clone = Buffer.from(obj);
178
+ seen.set(obj, clone);
179
+ return clone;
180
+ }
181
+ case obj instanceof Error: {
182
+ clone = new obj.constructor(obj.message);
183
+ seen.set(obj, clone);
184
+ break;
185
+ }
186
+ case (obj instanceof Promise || obj instanceof WeakMap || obj instanceof WeakSet): {
187
+ clone = obj;
188
+ seen.set(obj, clone);
189
+ return clone;
190
+ }
191
+ case (obj.constructor && obj.constructor !== Object): {
192
+ if (!cloneClassInstances) {
193
+ clone = obj;
194
+ seen.set(obj, clone);
195
+ return clone;
196
+ }
197
+ clone = Object.create(Object.getPrototypeOf(obj));
198
+ seen.set(obj, clone);
199
+ break;
200
+ }
201
+ default: {
202
+ clone = {};
203
+ seen.set(obj, clone);
204
+ const keys = Reflect.ownKeys(obj);
205
+ for (let i = 0, l = keys.length; i < l; i++) {
206
+ const key = keys[i];
207
+ clone[key] = deepClone(
208
+ obj[key],
209
+ cloneClassInstances,
210
+ seen
211
+ );
212
+ }
213
+ return clone;
124
214
  }
125
- return result;
126
215
  }
127
- return obj;
216
+ const descriptors = Object.getOwnPropertyDescriptors(obj);
217
+ for (const key of Reflect.ownKeys(descriptors)) {
218
+ const descriptor = descriptors[key];
219
+ if ("value" in descriptor) {
220
+ descriptor.value = deepClone(descriptor.value, cloneClassInstances, seen);
221
+ }
222
+ Object.defineProperty(clone, key, descriptor);
223
+ }
224
+ return clone;
128
225
  }
129
226
  function isCompiledSchema(subSchema) {
130
227
  return isObject(subSchema) && "$validate" in subSchema;
@@ -132,82 +229,86 @@ function isCompiledSchema(subSchema) {
132
229
  function getNamedFunction(name, fn) {
133
230
  return Object.defineProperty(fn, "name", { value: name });
134
231
  }
232
+ function resolvePath(root, path) {
233
+ if (!path || path === "#") {
234
+ return root;
235
+ }
236
+ if (path.startsWith("#/")) {
237
+ const parts = path.split("/").slice(1);
238
+ let current = root;
239
+ for (const part of parts) {
240
+ const decodedUriPart = decodeURIComponent(part);
241
+ const key = decodedUriPart.replace(/~1/g, "/").replace(/~0/g, "~");
242
+ if (current && typeof current === "object" && key in current) {
243
+ current = current[key];
244
+ } else {
245
+ return;
246
+ }
247
+ }
248
+ return current;
249
+ }
250
+ if (!path.includes("#")) {
251
+ if (root.definitions && root.definitions[path]) {
252
+ return root.definitions[path];
253
+ }
254
+ if (root.defs && root.defs[path]) {
255
+ return root.defs[path];
256
+ }
257
+ if (root.$id && typeof root.$id === "string") {
258
+ if (root.$id === path || root.$id.endsWith("/" + path)) {
259
+ return root;
260
+ }
261
+ }
262
+ }
263
+ return;
264
+ }
135
265
 
136
266
  // lib/formats.ts
267
+ var UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
268
+ var DURATION_REGEX = /^P(?!$)((\d+Y)?(\d+M)?(\d+W)?(\d+D)?)(T(?=\d)(\d+H)?(\d+M)?(\d+S)?)?$/;
269
+ var DATE_TIME_REGEX = /^(\d{4})-(0[0-9]|1[0-2])-(\d{2})T(0[0-9]|1\d|2[0-3]):([0-5]\d):((?:[0-5]\d|60))(?:.\d+)?(?:([+-])(0[0-9]|1\d|2[0-3]):([0-5]\d)|Z)?$/i;
270
+ var URI_REGEX = /^[a-zA-Z][a-zA-Z0-9+\-.]*:[^\s]*$/;
271
+ var EMAIL_REGEX = /^(?!\.)(?!.*\.$)[a-z0-9!#$%&'*+/=?^_`{|}~-]{1,20}(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]{1,21}){0,2}@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,60}[a-z0-9])?){0,3}$/i;
272
+ var IPV4_REGEX = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])$/;
273
+ var IPV6_REGEX = /(?:\s+|:::+|^\w{5,}|\w{5}$|^:{1}\w|\w:{1}$)/;
274
+ var IPV6_SHORT_REGEX = /^[0-9a-fA-F:.]*$/;
275
+ var IPV6_FULL_REGEX = /^(?:(?:[0-9a-fA-F]{1,4}:){7}(?:[0-9a-fA-F]{1,4}|:))$/;
276
+ var IPV6_INVALID_CHAR_REGEX = /(?:[0-9a-fA-F]{5,}|\D[0-9a-fA-F]{3}:)/;
277
+ var IPV6_FAST_FAIL_REGEX = /^(?:(?:(?:[0-9a-fA-F]{1,4}(?::|$)){1,6}))|(?:::(?:[0-9a-fA-F]{1,4})){0,5}$/;
278
+ var HOSTNAME_REGEX = /^[a-z0-9][a-z0-9-]{0,62}(?:\.[a-z0-9][a-z0-9-]{0,62})*[a-z0-9]$/i;
279
+ var DATE_REGEX = /^(\d{4})-(\d{2})-(\d{2})$/;
280
+ var JSON_POINTER_REGEX = /^\/(?:[^~]|~0|~1)*$/;
281
+ var RELATIVE_JSON_POINTER_REGEX = /^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/;
282
+ var TIME_REGEX = /^([01]\d|2[0-3]):([0-5]\d):([0-5]\d)(\.\d+)?(Z|([+-])([01]\d|2[0-3]):([0-5]\d))$/;
283
+ var URI_REFERENCE_REGEX = /^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#((?![^#]*\\)[^#]*))?/i;
284
+ var URI_TEMPLATE_REGEX = /^(?:[^{}]|\{[^}]+\})*$/;
285
+ var IRI_REGEX = /^[a-zA-Z][a-zA-Z0-9+\-.]*:[^\s]*$/;
286
+ var IRI_REFERENCE_REGEX = /^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#((?![^#]*\\)[^#]*))?/i;
287
+ var IDN_EMAIL_REGEX = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
288
+ var IDN_HOSTNAME_REGEX = /^[^\s!@#$%^&*()_+\=\[\]{};':"\\|,<>\/?]+$/;
289
+ var BACK_SLASH_REGEX = /\\/;
290
+ var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
137
291
  var Formats = {
138
292
  ["date-time"](data) {
139
- const match = data.match(
140
- /^(\d{4})-(0[0-9]|1[0-2])-(\d{2})T(0[0-9]|1\d|2[0-3]):([0-5]\d):((?:[0-5]\d|60))(?:.\d+)?(?:([+-])(0[0-9]|1\d|2[0-3]):([0-5]\d)|Z)?$/i
141
- );
293
+ const match = data.match(DATE_TIME_REGEX);
142
294
  if (!match) {
143
295
  return false;
144
296
  }
145
- let day = Number(match[3]);
146
- if (match[2] === "02" && day > 29) {
297
+ const [, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr] = match;
298
+ const year = Number(yearStr);
299
+ const month = Number(monthStr);
300
+ const day = Number(dayStr);
301
+ const hour = Number(hourStr);
302
+ const minute = Number(minuteStr);
303
+ const second = Number(secondStr);
304
+ if (month < 1 || month > 12) {
147
305
  return false;
148
306
  }
149
- const [
150
- ,
151
- yearStr,
152
- monthStr,
153
- ,
154
- hourStr,
155
- minuteStr,
156
- secondStr,
157
- timezoneSign,
158
- timezoneHourStr,
159
- timezoneMinuteStr
160
- ] = match;
161
- let year = Number(yearStr);
162
- let month = Number(monthStr);
163
- let hour = Number(hourStr);
164
- let minute = Number(minuteStr);
165
- let second = Number(secondStr);
166
- if (timezoneSign === "-" || timezoneSign === "+") {
167
- const timezoneHour = Number(timezoneHourStr);
168
- const timezoneMinute = Number(timezoneMinuteStr);
169
- if (timezoneSign === "-") {
170
- hour += timezoneHour;
171
- minute += timezoneMinute;
172
- } else if (timezoneSign === "+") {
173
- hour -= timezoneHour;
174
- minute -= timezoneMinute;
175
- }
176
- if (minute > 59) {
177
- hour += 1;
178
- minute -= 60;
179
- } else if (minute < 0) {
180
- hour -= 1;
181
- minute += 60;
182
- }
183
- if (hour > 23) {
184
- day += 1;
185
- hour -= 24;
186
- } else if (hour < 0) {
187
- day -= 1;
188
- hour += 24;
189
- }
190
- if (day > 31) {
191
- month += 1;
192
- day -= 31;
193
- } else if (day < 1) {
194
- month -= 1;
195
- day += 31;
196
- }
197
- if (month > 12) {
198
- year += 1;
199
- month -= 12;
200
- } else if (month < 1) {
201
- year -= 1;
202
- month += 12;
203
- }
204
- if (year < 0) {
205
- return false;
206
- }
307
+ if (day < 1) {
308
+ return false;
207
309
  }
208
- const daysInMonth = [31, , 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
209
- const maxDays = month === 2 ? year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) ? 29 : 28 : daysInMonth[month - 1];
210
- if (day > maxDays) {
310
+ const maxDays = month === 2 ? year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) ? 29 : 28 : DAYS_IN_MONTH[month - 1];
311
+ if (!maxDays || day > maxDays) {
211
312
  return false;
212
313
  }
213
314
  if (second === 60 && (minute !== 59 || hour !== 23)) {
@@ -216,24 +317,20 @@ var Formats = {
216
317
  return true;
217
318
  },
218
319
  uri(data) {
219
- return /^[a-zA-Z][a-zA-Z0-9+\-.]*:[^\s]*$/.test(data);
320
+ return URI_REGEX.test(data);
220
321
  },
221
322
  email(data) {
222
- return /^(?!\.)(?!.*\.$)[a-z0-9!#$%&'*+/=?^_`{|}~-]{1,20}(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]{1,21}){0,2}@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,60}[a-z0-9])?){0,3}$/i.test(
223
- data
224
- );
323
+ return EMAIL_REGEX.test(data);
225
324
  },
226
325
  ipv4(data) {
227
- return /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])$/.test(
228
- data
229
- );
326
+ return IPV4_REGEX.test(data);
230
327
  },
231
328
  // ipv6: isMyIpValid({ version: 6 }),
232
329
  ipv6(data) {
233
330
  if (data === "::") {
234
331
  return true;
235
332
  }
236
- if (data.indexOf(":") === -1 || /(?:\s+|:::+|^\w{5,}|\w{5}$|^:{1}\w|\w:{1}$)/.test(data)) {
333
+ if (data.indexOf(":") === -1 || IPV6_REGEX.test(data)) {
237
334
  return false;
238
335
  }
239
336
  const hasIpv4 = data.indexOf(".") !== -1;
@@ -241,9 +338,7 @@ var Formats = {
241
338
  if (hasIpv4) {
242
339
  addressParts = data.split(":");
243
340
  const ipv4Part = addressParts.pop();
244
- if (!/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])$/.test(
245
- ipv4Part
246
- )) {
341
+ if (!IPV4_REGEX.test(ipv4Part)) {
247
342
  return false;
248
343
  }
249
344
  }
@@ -253,32 +348,38 @@ var Formats = {
253
348
  if (ipv6Part.split("::").length - 1 > 1) {
254
349
  return false;
255
350
  }
256
- if (!/^[0-9a-fA-F:.]*$/.test(ipv6Part)) {
351
+ if (!IPV6_SHORT_REGEX.test(ipv6Part)) {
257
352
  return false;
258
353
  }
259
- return /^(?:(?:(?:[0-9a-fA-F]{1,4}(?::|$)){1,6}))|(?:::(?:[0-9a-fA-F]{1,4})){0,5}$/.test(
260
- ipv6Part
261
- );
354
+ return IPV6_FAST_FAIL_REGEX.test(ipv6Part);
262
355
  }
263
- const isIpv6Valid = /^(?:(?:[0-9a-fA-F]{1,4}:){7}(?:[0-9a-fA-F]{1,4}|:))$/.test(ipv6Part);
264
- const hasInvalidChar = /(?:[0-9a-fA-F]{5,}|\D[0-9a-fA-F]{3}:)/.test(
265
- ipv6Part
266
- );
356
+ const isIpv6Valid = IPV6_FULL_REGEX.test(ipv6Part);
357
+ const hasInvalidChar = IPV6_INVALID_CHAR_REGEX.test(ipv6Part);
267
358
  if (hasIpv4) {
268
359
  return isIpv6Valid || !hasInvalidChar;
269
360
  }
270
361
  return isIpv6Valid && !hasInvalidChar;
271
362
  },
272
363
  hostname(data) {
273
- return /^[a-z0-9][a-z0-9-]{0,62}(?:\.[a-z0-9][a-z0-9-]{0,62})*[a-z0-9]$/i.test(
274
- data
275
- );
364
+ return HOSTNAME_REGEX.test(data);
276
365
  },
277
366
  date(data) {
278
- if (/^(\d{4})-(\d{2})-(\d{2})$/.test(data) === false) {
367
+ const match = DATE_REGEX.exec(data);
368
+ if (!match) {
279
369
  return false;
280
370
  }
281
- return !isNaN(new Date(data).getTime());
371
+ const [, yearStr, monthStr, dayStr] = match;
372
+ const year = Number(yearStr);
373
+ const month = Number(monthStr);
374
+ const day = Number(dayStr);
375
+ if (month < 1 || month > 12) {
376
+ return false;
377
+ }
378
+ if (day < 1) {
379
+ return false;
380
+ }
381
+ const maxDays = month === 2 ? year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) ? 29 : 28 : DAYS_IN_MONTH[month - 1];
382
+ return !!maxDays && day <= maxDays;
282
383
  },
283
384
  regex(data) {
284
385
  try {
@@ -292,39 +393,49 @@ var Formats = {
292
393
  if (data === "") {
293
394
  return true;
294
395
  }
295
- return /^\/(?:[^~]|~0|~1)*$/.test(data);
396
+ return JSON_POINTER_REGEX.test(data);
296
397
  },
297
398
  "relative-json-pointer"(data) {
298
399
  if (data === "") {
299
400
  return true;
300
401
  }
301
- return /^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/.test(data);
402
+ return RELATIVE_JSON_POINTER_REGEX.test(data);
302
403
  },
303
404
  time(data) {
304
- return /^(\d{2}):(\d{2}):(\d{2})(\.\d+)?(Z|([+-])(\d{2}):(\d{2}))$/.test(
305
- data
306
- );
405
+ return TIME_REGEX.test(data);
307
406
  },
308
407
  "uri-reference"(data) {
309
- if (/\\/.test(data)) {
408
+ if (BACK_SLASH_REGEX.test(data)) {
310
409
  return false;
311
410
  }
312
- return /^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#((?![^#]*\\)[^#]*))?/i.test(
313
- data
314
- );
411
+ return URI_REFERENCE_REGEX.test(data);
315
412
  },
316
413
  "uri-template"(data) {
317
- return /^(?:(?:https?:\/\/[\w.-]+)?\/?)?[\w- ;,.\/?%&=]*(?:\{[\w-]+(?::\d+)?\}[\w- ;,.\/?%&=]*)*\/?$/.test(
318
- data
319
- );
414
+ return URI_TEMPLATE_REGEX.test(data);
415
+ },
416
+ duration(data) {
417
+ return DURATION_REGEX.test(data);
418
+ },
419
+ uuid(data) {
420
+ return UUID_REGEX.test(data);
421
+ },
422
+ // IRI is like URI but allows Unicode. We reuse a permissive logic.
423
+ iri(data) {
424
+ return IRI_REGEX.test(data);
320
425
  },
321
- // Not supported yet
322
- duration: false,
323
- uuid: false,
324
- "idn-email": false,
325
- "idn-hostname": false,
326
- iri: false,
327
- "iri-reference": false
426
+ "iri-reference"(data) {
427
+ if (BACK_SLASH_REGEX.test(data)) {
428
+ return false;
429
+ }
430
+ return IRI_REFERENCE_REGEX.test(data);
431
+ },
432
+ // Best-effort structural validation for IDN (no punycode/tables)
433
+ "idn-email"(data) {
434
+ return IDN_EMAIL_REGEX.test(data);
435
+ },
436
+ "idn-hostname"(data) {
437
+ return IDN_HOSTNAME_REGEX.test(data);
438
+ }
328
439
  };
329
440
 
330
441
  // lib/types.ts
@@ -333,10 +444,7 @@ var Types = {
333
444
  return isObject(data);
334
445
  },
335
446
  array(data) {
336
- if (Array.isArray(data)) {
337
- return true;
338
- }
339
- return typeof data === "object" && data !== null && "length" in data && "0" in data && Object.keys(data).length - 1 === data.length;
447
+ return Array.isArray(data);
340
448
  },
341
449
  string(data) {
342
450
  return typeof data === "string";
@@ -367,6 +475,7 @@ var Types = {
367
475
 
368
476
  // lib/keywords/array-keywords.ts
369
477
  var ArrayKeywords = {
478
+ // lib/keywords/array-keywords.ts
370
479
  items(schema, data, defineError) {
371
480
  if (!Array.isArray(data)) {
372
481
  return;
@@ -381,11 +490,11 @@ var ArrayKeywords = {
381
490
  }
382
491
  if (Array.isArray(schemaItems)) {
383
492
  const schemaItemsLength = schemaItems.length;
384
- const itemsLength = Math.min(schemaItemsLength, dataLength);
493
+ const itemsLength = schemaItemsLength < dataLength ? schemaItemsLength : dataLength;
385
494
  for (let i = 0; i < itemsLength; i++) {
386
495
  const schemaItem = schemaItems[i];
387
496
  if (typeof schemaItem === "boolean") {
388
- if (schemaItem === false && typeof data[i] !== "undefined") {
497
+ if (schemaItem === false && data[i] !== void 0) {
389
498
  return defineError("Array item is not allowed", {
390
499
  item: i,
391
500
  data: data[i]
@@ -393,8 +502,9 @@ var ArrayKeywords = {
393
502
  }
394
503
  continue;
395
504
  }
396
- if (isCompiledSchema(schemaItem)) {
397
- const error = schemaItem.$validate(data[i]);
505
+ const validate2 = schemaItem && schemaItem.$validate;
506
+ if (typeof validate2 === "function") {
507
+ const error = validate2(data[i]);
398
508
  if (error) {
399
509
  return defineError("Array item is invalid", {
400
510
  item: i,
@@ -406,26 +516,32 @@ var ArrayKeywords = {
406
516
  }
407
517
  return;
408
518
  }
409
- if (isCompiledSchema(schemaItems)) {
410
- for (let i = 0; i < dataLength; i++) {
411
- const error = schemaItems.$validate(data[i]);
412
- if (error) {
413
- return defineError("Array item is invalid", {
414
- item: i,
415
- cause: error,
416
- data: data[i]
417
- });
418
- }
519
+ const validate = schemaItems && schemaItems.$validate;
520
+ if (typeof validate !== "function") {
521
+ return;
522
+ }
523
+ for (let i = 0; i < dataLength; i++) {
524
+ const error = validate(data[i]);
525
+ if (error) {
526
+ return defineError("Array item is invalid", {
527
+ item: i,
528
+ cause: error,
529
+ data: data[i]
530
+ });
419
531
  }
420
532
  }
421
- return;
422
533
  },
423
534
  elements(schema, data, defineError) {
424
- if (!Array.isArray(data) || !isCompiledSchema(schema.elements)) {
535
+ if (!Array.isArray(data)) {
536
+ return;
537
+ }
538
+ const elementsSchema = schema.elements;
539
+ const validate = elementsSchema && elementsSchema.$validate;
540
+ if (typeof validate !== "function") {
425
541
  return;
426
542
  }
427
543
  for (let i = 0; i < data.length; i++) {
428
- const error = schema.elements.$validate(data[i]);
544
+ const error = validate(data[i]);
429
545
  if (error) {
430
546
  return defineError("Array item is invalid", {
431
547
  item: i,
@@ -434,7 +550,6 @@ var ArrayKeywords = {
434
550
  });
435
551
  }
436
552
  }
437
- return;
438
553
  },
439
554
  minItems(schema, data, defineError) {
440
555
  if (!Array.isArray(data) || data.length >= schema.minItems) {
@@ -480,28 +595,30 @@ var ArrayKeywords = {
480
595
  if (!Array.isArray(data) || !schema.uniqueItems) {
481
596
  return;
482
597
  }
483
- const unique = /* @__PURE__ */ new Set();
484
- for (const item of data) {
485
- let itemStr;
486
- if (typeof item === "string") {
487
- itemStr = `s:${item}`;
488
- } else if (isObject(item)) {
489
- itemStr = `o:${JSON.stringify(
490
- Object.fromEntries(
491
- Object.entries(item).sort(([a], [b]) => a.localeCompare(b))
492
- )
493
- )}`;
494
- } else if (Array.isArray(item)) {
495
- itemStr = JSON.stringify(item);
496
- } else {
497
- itemStr = String(item);
598
+ const len = data.length;
599
+ if (len <= 1) {
600
+ return;
601
+ }
602
+ const primitiveSeen = /* @__PURE__ */ new Set();
603
+ for (let i = 0; i < len; i++) {
604
+ const item = data[i];
605
+ const type = typeof item;
606
+ if (item === null || type === "string" || type === "number" || type === "boolean") {
607
+ if (primitiveSeen.has(item)) {
608
+ return defineError("Array items are not unique", { data: item });
609
+ }
610
+ primitiveSeen.add(item);
611
+ continue;
498
612
  }
499
- if (unique.has(itemStr)) {
500
- return defineError("Array items are not unique", { data: item });
613
+ if (item && typeof item === "object") {
614
+ for (let j = 0; j < i; j++) {
615
+ const prev = data[j];
616
+ if (prev && typeof prev === "object" && !hasChanged(prev, item)) {
617
+ return defineError("Array items are not unique", { data: item });
618
+ }
619
+ }
501
620
  }
502
- unique.add(itemStr);
503
621
  }
504
- return;
505
622
  },
506
623
  contains(schema, data, defineError) {
507
624
  if (!Array.isArray(data)) {
@@ -597,7 +714,6 @@ var NumberKeywords = {
597
714
 
598
715
  // lib/keywords/object-keywords.ts
599
716
  var ObjectKeywords = {
600
- // Object
601
717
  required(schema, data, defineError) {
602
718
  if (!isObject(data)) {
603
719
  return;
@@ -617,16 +733,46 @@ var ObjectKeywords = {
617
733
  if (!isObject(data)) {
618
734
  return;
619
735
  }
620
- for (const key of Object.keys(schema.properties)) {
621
- if (!data.hasOwnProperty(key)) {
622
- const schemaProp = schema.properties[key];
623
- if (isObject(schemaProp) && "default" in schemaProp) {
624
- data[key] = schemaProp.default;
736
+ let propKeys = schema._propKeys;
737
+ if (!propKeys) {
738
+ propKeys = Object.keys(schema.properties || {});
739
+ Object.defineProperty(schema, "_propKeys", {
740
+ value: propKeys,
741
+ enumerable: false,
742
+ configurable: false,
743
+ writable: false
744
+ });
745
+ }
746
+ let requiredKeys = schema._requiredKeys;
747
+ if (requiredKeys === void 0) {
748
+ requiredKeys = Array.isArray(schema.required) ? schema.required : null;
749
+ Object.defineProperty(schema, "_requiredKeys", {
750
+ value: requiredKeys,
751
+ enumerable: false,
752
+ configurable: false,
753
+ writable: false
754
+ });
755
+ }
756
+ const required = requiredKeys || [];
757
+ for (let i = 0; i < propKeys.length; i++) {
758
+ const key = propKeys[i];
759
+ const schemaProp = schema.properties[key];
760
+ if (!Object.prototype.hasOwnProperty.call(data, key)) {
761
+ if (required.length && required.indexOf(key) !== -1 && isObject(schemaProp) && "default" in schemaProp) {
762
+ const error = schemaProp.$validate(schemaProp.default);
763
+ if (error) {
764
+ return defineError("Default property is invalid", {
765
+ item: key,
766
+ cause: error,
767
+ data: schemaProp.default
768
+ });
769
+ }
770
+ data[key] = deepClone(schemaProp.default);
625
771
  }
626
772
  continue;
627
773
  }
628
- if (typeof schema.properties[key] === "boolean") {
629
- if (schema.properties[key] === false) {
774
+ if (typeof schemaProp === "boolean") {
775
+ if (schemaProp === false) {
630
776
  return defineError("Property is not allowed", {
631
777
  item: key,
632
778
  data: data[key]
@@ -634,8 +780,8 @@ var ObjectKeywords = {
634
780
  }
635
781
  continue;
636
782
  }
637
- if ("$validate" in schema.properties[key]) {
638
- const error = schema.properties[key].$validate(data[key]);
783
+ if (schemaProp && "$validate" in schemaProp) {
784
+ const error = schemaProp.$validate(data[key]);
639
785
  if (error) {
640
786
  return defineError("Property is invalid", {
641
787
  item: key,
@@ -648,12 +794,18 @@ var ObjectKeywords = {
648
794
  return;
649
795
  },
650
796
  values(schema, data, defineError) {
651
- if (!isObject(data) || !isCompiledSchema(schema.values)) {
797
+ if (!isObject(data)) {
798
+ return;
799
+ }
800
+ const valueSchema = schema.values;
801
+ const validate = valueSchema && valueSchema.$validate;
802
+ if (typeof validate !== "function") {
652
803
  return;
653
804
  }
654
805
  const keys = Object.keys(data);
655
- for (const key of keys) {
656
- const error = schema.values.$validate(data[key]);
806
+ for (let i = 0; i < keys.length; i++) {
807
+ const key = keys[i];
808
+ const error = validate(data[key]);
657
809
  if (error) {
658
810
  return defineError("Property is invalid", {
659
811
  item: key,
@@ -662,7 +814,6 @@ var ObjectKeywords = {
662
814
  });
663
815
  }
664
816
  }
665
- return;
666
817
  },
667
818
  maxProperties(schema, data, defineError) {
668
819
  if (!isObject(data) || Object.keys(data).length <= schema.maxProperties) {
@@ -681,15 +832,37 @@ var ObjectKeywords = {
681
832
  return;
682
833
  }
683
834
  const keys = Object.keys(data);
684
- const isCompiled = isCompiledSchema(schema.additionalProperties);
685
- for (const key of keys) {
835
+ let apIsCompiled = schema._apIsCompiled;
836
+ if (apIsCompiled === void 0) {
837
+ apIsCompiled = isCompiledSchema(schema.additionalProperties);
838
+ Object.defineProperty(schema, "_apIsCompiled", {
839
+ value: apIsCompiled,
840
+ enumerable: false
841
+ });
842
+ }
843
+ let patternList = schema._patternPropertiesList;
844
+ if (schema.patternProperties && !patternList) {
845
+ patternList = [];
846
+ for (const pattern in schema.patternProperties) {
847
+ patternList.push({
848
+ regex: new RegExp(pattern, "u"),
849
+ key: pattern
850
+ });
851
+ }
852
+ Object.defineProperty(schema, "_patternPropertiesList", {
853
+ value: patternList,
854
+ enumerable: false
855
+ });
856
+ }
857
+ for (let i = 0; i < keys.length; i++) {
858
+ const key = keys[i];
686
859
  if (schema.properties && schema.properties.hasOwnProperty(key)) {
687
860
  continue;
688
861
  }
689
- if (schema.patternProperties) {
862
+ if (patternList && patternList.length) {
690
863
  let match = false;
691
- for (const pattern in schema.patternProperties) {
692
- if (new RegExp(pattern, "u").test(key)) {
864
+ for (let j = 0; j < patternList.length; j++) {
865
+ if (patternList[j].regex.test(key)) {
693
866
  match = true;
694
867
  break;
695
868
  }
@@ -704,7 +877,7 @@ var ObjectKeywords = {
704
877
  data: data[key]
705
878
  });
706
879
  }
707
- if (isCompiled) {
880
+ if (apIsCompiled && isCompiledSchema(schema.additionalProperties)) {
708
881
  const error = schema.additionalProperties.$validate(data[key]);
709
882
  if (error) {
710
883
  return defineError("Additional properties are invalid", {
@@ -721,12 +894,30 @@ var ObjectKeywords = {
721
894
  if (!isObject(data)) {
722
895
  return;
723
896
  }
724
- const patterns = Object.keys(schema.patternProperties);
725
- for (const pattern of patterns) {
726
- const regex = new RegExp(pattern, "u");
727
- if (typeof schema.patternProperties[pattern] === "boolean") {
728
- if (schema.patternProperties[pattern] === false) {
729
- for (const key in data) {
897
+ let patternList = schema._patternPropertiesList;
898
+ if (!patternList) {
899
+ patternList = [];
900
+ const patterns = Object.keys(schema.patternProperties || {});
901
+ for (let i = 0; i < patterns.length; i++) {
902
+ const pattern = patterns[i];
903
+ patternList.push({
904
+ regex: new RegExp(pattern, "u"),
905
+ key: pattern
906
+ });
907
+ }
908
+ Object.defineProperty(schema, "_patternPropertiesList", {
909
+ value: patternList,
910
+ enumerable: false
911
+ });
912
+ }
913
+ const dataKeys = Object.keys(data);
914
+ for (let p = 0; p < patternList.length; p++) {
915
+ const { regex, key: patternKey } = patternList[p];
916
+ const schemaProp = schema.patternProperties[patternKey];
917
+ if (typeof schemaProp === "boolean") {
918
+ if (schemaProp === false) {
919
+ for (let i = 0; i < dataKeys.length; i++) {
920
+ const key = dataKeys[i];
730
921
  if (regex.test(key)) {
731
922
  return defineError("Property is not allowed", {
732
923
  item: key,
@@ -737,13 +928,11 @@ var ObjectKeywords = {
737
928
  }
738
929
  continue;
739
930
  }
740
- const keys = Object.keys(data);
741
- for (const key of keys) {
742
- if (regex.test(key)) {
743
- if ("$validate" in schema.patternProperties[pattern]) {
744
- const error = schema.patternProperties[pattern].$validate(
745
- data[key]
746
- );
931
+ if ("$validate" in schemaProp) {
932
+ for (let i = 0; i < dataKeys.length; i++) {
933
+ const key = dataKeys[i];
934
+ if (regex.test(key)) {
935
+ const error = schemaProp.$validate(data[key]);
747
936
  if (error) {
748
937
  return defineError("Property is invalid", {
749
938
  item: key,
@@ -761,24 +950,30 @@ var ObjectKeywords = {
761
950
  if (!isObject(data)) {
762
951
  return;
763
952
  }
764
- if (typeof schema.propertyNames === "boolean") {
765
- if (schema.propertyNames === false && Object.keys(data).length > 0) {
953
+ const pn = schema.propertyNames;
954
+ if (typeof pn === "boolean") {
955
+ if (pn === false && Object.keys(data).length > 0) {
766
956
  return defineError("Properties are not allowed", { data });
767
957
  }
958
+ return;
768
959
  }
769
- if (isCompiledSchema(schema.propertyNames)) {
770
- for (let key in data) {
771
- const error = schema.propertyNames.$validate(key);
772
- if (error) {
773
- return defineError("Property name is invalid", {
774
- item: key,
775
- cause: error,
776
- data: data[key]
777
- });
778
- }
960
+ const validate = pn && pn.$validate;
961
+ if (typeof validate !== "function") {
962
+ return;
963
+ }
964
+ for (const key in data) {
965
+ if (!Object.prototype.hasOwnProperty.call(data, key)) {
966
+ continue;
967
+ }
968
+ const error = validate(key);
969
+ if (error) {
970
+ return defineError("Property name is invalid", {
971
+ item: key,
972
+ cause: error,
973
+ data: data[key]
974
+ });
779
975
  }
780
976
  }
781
- return;
782
977
  },
783
978
  dependencies(schema, data, defineError) {
784
979
  if (!isObject(data)) {
@@ -827,7 +1022,6 @@ var ObjectKeywords = {
827
1022
  else: false,
828
1023
  default: false,
829
1024
  // Not implemented yet
830
- $ref: false,
831
1025
  definitions: false,
832
1026
  $id: false,
833
1027
  $schema: false,
@@ -846,17 +1040,14 @@ var ObjectKeywords = {
846
1040
  // lib/keywords/other-keywords.ts
847
1041
  var OtherKeywords = {
848
1042
  enum(schema, data, defineError) {
849
- const isArray = Array.isArray(data);
850
- const isObject2 = typeof data === "object" && data !== null;
851
- for (let i = 0; i < schema.enum.length; i++) {
852
- const enumItem = schema.enum[i];
1043
+ const list = schema.enum;
1044
+ for (let i = 0; i < list.length; i++) {
1045
+ const enumItem = list[i];
853
1046
  if (enumItem === data) {
854
1047
  return;
855
1048
  }
856
- if (isArray && Array.isArray(enumItem) || isObject2 && typeof enumItem === "object" && enumItem !== null) {
857
- if (deepEqual(enumItem, data)) {
858
- return;
859
- }
1049
+ if (enumItem !== null && data !== null && typeof enumItem === "object" && typeof data === "object" && !hasChanged(enumItem, data)) {
1050
+ return;
860
1051
  }
861
1052
  }
862
1053
  return defineError("Value is not one of the allowed values", { data });
@@ -909,27 +1100,40 @@ var OtherKeywords = {
909
1100
  return defineError("Value is not valid", { data });
910
1101
  },
911
1102
  oneOf(schema, data, defineError) {
1103
+ const list = schema.oneOf;
912
1104
  let validCount = 0;
913
- for (let i = 0; i < schema.oneOf.length; i++) {
914
- if (isObject(schema.oneOf[i])) {
915
- if ("$validate" in schema.oneOf[i]) {
916
- const error = schema.oneOf[i].$validate(data);
1105
+ for (let i = 0; i < list.length; i++) {
1106
+ const sub = list[i];
1107
+ if (isObject(sub)) {
1108
+ if ("$validate" in sub) {
1109
+ const error = sub.$validate(data);
917
1110
  if (!error) {
918
1111
  validCount++;
1112
+ if (validCount > 1) {
1113
+ return defineError("Value is not valid", { data });
1114
+ }
919
1115
  }
920
1116
  continue;
921
1117
  }
922
1118
  validCount++;
1119
+ if (validCount > 1) {
1120
+ return defineError("Value is not valid", { data });
1121
+ }
923
1122
  continue;
924
- } else {
925
- if (typeof schema.oneOf[i] === "boolean") {
926
- if (Boolean(data) === schema.oneOf[i]) {
927
- validCount++;
1123
+ }
1124
+ if (typeof sub === "boolean") {
1125
+ if (Boolean(data) === sub) {
1126
+ validCount++;
1127
+ if (validCount > 1) {
1128
+ return defineError("Value is not valid", { data });
928
1129
  }
929
- continue;
930
1130
  }
931
- if (data === schema.oneOf[i]) {
932
- validCount++;
1131
+ continue;
1132
+ }
1133
+ if (data === sub) {
1134
+ validCount++;
1135
+ if (validCount > 1) {
1136
+ return defineError("Value is not valid", { data });
933
1137
  }
934
1138
  }
935
1139
  }
@@ -939,12 +1143,15 @@ var OtherKeywords = {
939
1143
  return defineError("Value is not valid", { data });
940
1144
  },
941
1145
  const(schema, data, defineError) {
942
- if (data === schema.const || isObject(data) && isObject(schema.const) && deepEqual(data, schema.const) || Array.isArray(data) && Array.isArray(schema.const) && deepEqual(data, schema.const)) {
1146
+ if (data === schema.const) {
1147
+ return;
1148
+ }
1149
+ if (isObject(data) && isObject(schema.const) && !hasChanged(data, schema.const) || Array.isArray(data) && Array.isArray(schema.const) && !hasChanged(data, schema.const)) {
943
1150
  return;
944
1151
  }
945
1152
  return defineError("Value is not valid", { data });
946
1153
  },
947
- if(schema, data, defineError) {
1154
+ if(schema, data) {
948
1155
  if ("then" in schema === false && "else" in schema === false) {
949
1156
  return;
950
1157
  }
@@ -992,6 +1199,24 @@ var OtherKeywords = {
992
1199
  return defineError("Value is not valid", { data });
993
1200
  }
994
1201
  return defineError("Value is not valid", { data });
1202
+ },
1203
+ $ref(schema, data, defineError, instance) {
1204
+ if (schema._resolvedRef) {
1205
+ return schema._resolvedRef(data);
1206
+ }
1207
+ const refPath = schema.$ref;
1208
+ let targetSchema = instance.getSchemaRef(refPath);
1209
+ if (!targetSchema) {
1210
+ targetSchema = instance.getSchemaById(refPath);
1211
+ }
1212
+ if (!targetSchema) {
1213
+ return defineError(`Missing reference: ${refPath}`);
1214
+ }
1215
+ if (!targetSchema.$validate) {
1216
+ return;
1217
+ }
1218
+ schema._resolvedRef = targetSchema.$validate;
1219
+ return schema._resolvedRef(data);
995
1220
  }
996
1221
  };
997
1222
 
@@ -1013,9 +1238,22 @@ var StringKeywords = {
1013
1238
  if (typeof data !== "string") {
1014
1239
  return;
1015
1240
  }
1016
- const patternRegexp = new RegExp(schema.pattern, "u");
1017
- if (patternRegexp instanceof RegExp === false) {
1018
- return defineError("Invalid regular expression", { data });
1241
+ let patternRegexp = schema._patternRegexp;
1242
+ if (!patternRegexp) {
1243
+ try {
1244
+ patternRegexp = new RegExp(schema.pattern, "u");
1245
+ Object.defineProperty(schema, "_patternRegexp", {
1246
+ value: patternRegexp,
1247
+ enumerable: false,
1248
+ configurable: false,
1249
+ writable: false
1250
+ });
1251
+ } catch (error) {
1252
+ return defineError("Invalid regular expression", {
1253
+ data,
1254
+ cause: error
1255
+ });
1256
+ }
1019
1257
  }
1020
1258
  if (patternRegexp.test(data)) {
1021
1259
  return;
@@ -1028,7 +1266,16 @@ var StringKeywords = {
1028
1266
  if (typeof data !== "string") {
1029
1267
  return;
1030
1268
  }
1031
- const formatValidate = instance.getFormat(schema.format);
1269
+ let formatValidate = schema._formatValidate;
1270
+ if (formatValidate === void 0) {
1271
+ formatValidate = instance.getFormat(schema.format);
1272
+ Object.defineProperty(schema, "_formatValidate", {
1273
+ value: formatValidate,
1274
+ enumerable: false,
1275
+ configurable: false,
1276
+ writable: false
1277
+ });
1278
+ }
1032
1279
  if (!formatValidate || formatValidate(data)) {
1033
1280
  return;
1034
1281
  }
@@ -1051,10 +1298,15 @@ var SchemaShield = class {
1051
1298
  formats = {};
1052
1299
  keywords = {};
1053
1300
  immutable = false;
1301
+ rootSchema = null;
1302
+ idRegistry = /* @__PURE__ */ new Map();
1303
+ failFast = true;
1054
1304
  constructor({
1055
- immutable = false
1305
+ immutable = false,
1306
+ failFast = true
1056
1307
  } = {}) {
1057
1308
  this.immutable = immutable;
1309
+ this.failFast = failFast;
1058
1310
  for (const [type, validator] of Object.entries(Types)) {
1059
1311
  if (validator) {
1060
1312
  this.addType(type, validator);
@@ -1096,26 +1348,38 @@ var SchemaShield = class {
1096
1348
  getKeyword(keyword) {
1097
1349
  return this.keywords[keyword];
1098
1350
  }
1351
+ getSchemaRef(path) {
1352
+ if (!this.rootSchema) {
1353
+ return;
1354
+ }
1355
+ return resolvePath(this.rootSchema, path);
1356
+ }
1357
+ getSchemaById(id) {
1358
+ return this.idRegistry.get(id);
1359
+ }
1099
1360
  compile(schema) {
1361
+ this.idRegistry.clear();
1100
1362
  const compiledSchema = this.compileSchema(schema);
1363
+ this.rootSchema = compiledSchema;
1364
+ this.linkReferences(compiledSchema);
1101
1365
  if (!compiledSchema.$validate) {
1102
1366
  if (this.isSchemaLike(schema) === false) {
1103
1367
  throw new ValidationError("Invalid schema");
1104
1368
  }
1105
1369
  compiledSchema.$validate = getNamedFunction(
1106
- "any",
1370
+ "Validate_Any",
1107
1371
  () => {
1108
1372
  }
1109
1373
  );
1110
1374
  }
1111
1375
  const validate = (data) => {
1376
+ this.rootSchema = compiledSchema;
1112
1377
  const clonedData = this.immutable ? deepClone(data) : data;
1113
- const error = compiledSchema.$validate(clonedData);
1114
- return {
1115
- data: clonedData,
1116
- error: error ? error : null,
1117
- valid: !error
1118
- };
1378
+ const res = compiledSchema.$validate(clonedData);
1379
+ if (res) {
1380
+ return { data: clonedData, error: res, valid: false };
1381
+ }
1382
+ return { data: clonedData, error: null, valid: true };
1119
1383
  };
1120
1384
  validate.compiledSchema = compiledSchema;
1121
1385
  return validate;
@@ -1123,99 +1387,113 @@ var SchemaShield = class {
1123
1387
  compileSchema(schema) {
1124
1388
  if (!isObject(schema)) {
1125
1389
  if (schema === true) {
1126
- schema = {
1127
- anyOf: [{}]
1128
- };
1390
+ schema = { anyOf: [{}] };
1129
1391
  } else if (schema === false) {
1130
- schema = {
1131
- oneOf: []
1132
- };
1392
+ schema = { oneOf: [] };
1133
1393
  } else {
1134
- schema = {
1135
- oneOf: [schema]
1136
- };
1394
+ schema = { oneOf: [schema] };
1137
1395
  }
1138
1396
  }
1139
1397
  const compiledSchema = deepClone(schema);
1140
- const defineTypeError = getDefinedErrorFunctionForKey("type", schema);
1141
- const typeValidations = [];
1142
- let methodName = "";
1398
+ if (typeof schema.$id === "string") {
1399
+ this.idRegistry.set(schema.$id, compiledSchema);
1400
+ }
1401
+ if ("$ref" in schema) {
1402
+ const refValidator = this.getKeyword("$ref");
1403
+ if (refValidator) {
1404
+ const defineError = getDefinedErrorFunctionForKey(
1405
+ "$ref",
1406
+ schema["$ref"],
1407
+ this.failFast
1408
+ );
1409
+ compiledSchema.$validate = getNamedFunction(
1410
+ "Validate_Reference",
1411
+ (data) => refValidator(
1412
+ compiledSchema,
1413
+ data,
1414
+ defineError,
1415
+ this
1416
+ )
1417
+ );
1418
+ }
1419
+ return compiledSchema;
1420
+ }
1421
+ const validators = [];
1422
+ const activeNames = [];
1143
1423
  if ("type" in schema) {
1424
+ const defineTypeError = getDefinedErrorFunctionForKey(
1425
+ "type",
1426
+ schema,
1427
+ this.failFast
1428
+ );
1144
1429
  const types = Array.isArray(schema.type) ? schema.type : schema.type.split(",").map((t) => t.trim());
1145
- for (const type of types) {
1146
- const validator = this.getType(type);
1430
+ const typeFunctions = [];
1431
+ const typeNames = [];
1432
+ for (const type2 of types) {
1433
+ const validator = this.getType(type2);
1147
1434
  if (validator) {
1148
- typeValidations.push(validator);
1149
- methodName += (methodName ? "_OR_" : "") + validator.name;
1435
+ typeFunctions.push(validator);
1436
+ typeNames.push(validator.name);
1150
1437
  }
1151
1438
  }
1152
- const typeValidationsLength = typeValidations.length;
1153
- if (typeValidationsLength === 0) {
1154
- throw defineTypeError("Invalid type for schema", { data: schema.type });
1155
- }
1156
- if (typeValidationsLength === 1) {
1157
- const typeValidation = typeValidations[0];
1158
- compiledSchema.$validate = getNamedFunction(
1159
- methodName,
1160
- (data) => {
1161
- if (!typeValidation(data)) {
1162
- return defineTypeError("Invalid type", { data });
1163
- }
1439
+ if (typeFunctions.length === 0) {
1440
+ throw getDefinedErrorFunctionForKey(
1441
+ "type",
1442
+ schema,
1443
+ this.failFast
1444
+ )("Invalid type for schema", { data: schema.type });
1445
+ }
1446
+ let combinedTypeValidator;
1447
+ let typeMethodName = "";
1448
+ if (typeFunctions.length === 1) {
1449
+ typeMethodName = typeNames[0];
1450
+ const singleTypeFn = typeFunctions[0];
1451
+ combinedTypeValidator = (data) => {
1452
+ if (!singleTypeFn(data)) {
1453
+ return defineTypeError("Invalid type", { data });
1164
1454
  }
1165
- );
1166
- } else if (typeValidationsLength > 1) {
1167
- compiledSchema.$validate = getNamedFunction(
1168
- methodName,
1169
- (data) => {
1170
- for (let i = 0; i < typeValidationsLength; i++) {
1171
- if (typeValidations[i](data)) {
1172
- return;
1173
- }
1455
+ };
1456
+ } else {
1457
+ typeMethodName = typeNames.join("_OR_");
1458
+ combinedTypeValidator = (data) => {
1459
+ for (let i = 0; i < typeFunctions.length; i++) {
1460
+ if (typeFunctions[i](data)) {
1461
+ return;
1174
1462
  }
1175
- return defineTypeError("Invalid type", { data });
1176
1463
  }
1464
+ return defineTypeError("Invalid type", { data });
1465
+ };
1466
+ }
1467
+ const typeAdapter = (_s, data) => combinedTypeValidator(data);
1468
+ validators.push({
1469
+ fn: getNamedFunction(typeMethodName, typeAdapter),
1470
+ defineError: defineTypeError
1471
+ });
1472
+ activeNames.push(typeMethodName);
1473
+ }
1474
+ const { type, $id, $ref, $validate, required, ...otherKeys } = schema;
1475
+ const keyOrder = required ? [...Object.keys(otherKeys), "required"] : Object.keys(otherKeys);
1476
+ for (const key of keyOrder) {
1477
+ const keywordFn = this.getKeyword(key);
1478
+ if (keywordFn) {
1479
+ const defineError = getDefinedErrorFunctionForKey(
1480
+ key,
1481
+ schema[key],
1482
+ this.failFast
1177
1483
  );
1484
+ const fnName = keywordFn.name || key;
1485
+ validators.push({
1486
+ fn: keywordFn,
1487
+ defineError
1488
+ });
1489
+ activeNames.push(fnName);
1178
1490
  }
1179
1491
  }
1180
- for (const key of Object.keys(schema)) {
1181
- if (key === "type") {
1182
- compiledSchema.type = schema.type;
1492
+ const literalKeywords = ["enum", "const", "default", "examples"];
1493
+ for (const key of keyOrder) {
1494
+ if (literalKeywords.includes(key)) {
1183
1495
  continue;
1184
1496
  }
1185
- const keywordValidator = this.getKeyword(key);
1186
- if (keywordValidator) {
1187
- const defineError = getDefinedErrorFunctionForKey(key, schema[key]);
1188
- if (compiledSchema.$validate) {
1189
- const prevValidator = compiledSchema.$validate;
1190
- methodName += `_AND_${keywordValidator.name}`;
1191
- compiledSchema.$validate = getNamedFunction(
1192
- methodName,
1193
- (data) => {
1194
- const error = prevValidator(data);
1195
- if (error) {
1196
- return error;
1197
- }
1198
- return keywordValidator(
1199
- compiledSchema,
1200
- data,
1201
- defineError,
1202
- this
1203
- );
1204
- }
1205
- );
1206
- } else {
1207
- methodName = keywordValidator.name;
1208
- compiledSchema.$validate = getNamedFunction(
1209
- methodName,
1210
- (data) => keywordValidator(
1211
- compiledSchema,
1212
- data,
1213
- defineError,
1214
- this
1215
- )
1216
- );
1217
- }
1218
- }
1219
1497
  if (isObject(schema[key])) {
1220
1498
  if (key === "properties") {
1221
1499
  for (const subKey of Object.keys(schema[key])) {
@@ -1229,12 +1507,39 @@ var SchemaShield = class {
1229
1507
  continue;
1230
1508
  }
1231
1509
  if (Array.isArray(schema[key])) {
1232
- compiledSchema[key] = schema[key].map(
1233
- (subSchema, index) => this.isSchemaLike(subSchema) ? this.compileSchema(subSchema) : subSchema
1234
- );
1510
+ for (let i = 0; i < schema[key].length; i++) {
1511
+ if (this.isSchemaLike(schema[key][i])) {
1512
+ compiledSchema[key][i] = this.compileSchema(schema[key][i]);
1513
+ }
1514
+ }
1235
1515
  continue;
1236
1516
  }
1237
- compiledSchema[key] = schema[key];
1517
+ }
1518
+ if (validators.length === 0) {
1519
+ return compiledSchema;
1520
+ }
1521
+ if (validators.length === 1) {
1522
+ const v = validators[0];
1523
+ compiledSchema.$validate = getNamedFunction(
1524
+ activeNames[0],
1525
+ (data) => v.fn(compiledSchema, data, v.defineError, this)
1526
+ );
1527
+ } else {
1528
+ const compositeName = "Validate_" + activeNames.join("_AND_");
1529
+ const masterValidator = (data) => {
1530
+ for (let i = 0; i < validators.length; i++) {
1531
+ const v = validators[i];
1532
+ const error = v.fn(compiledSchema, data, v.defineError, this);
1533
+ if (error) {
1534
+ return error;
1535
+ }
1536
+ }
1537
+ return;
1538
+ };
1539
+ compiledSchema.$validate = getNamedFunction(
1540
+ compositeName,
1541
+ masterValidator
1542
+ );
1238
1543
  }
1239
1544
  return compiledSchema;
1240
1545
  }
@@ -1251,7 +1556,59 @@ var SchemaShield = class {
1251
1556
  }
1252
1557
  return false;
1253
1558
  }
1559
+ linkReferences(root) {
1560
+ const stack = [root];
1561
+ while (stack.length > 0) {
1562
+ const node = stack.pop();
1563
+ if (!node || typeof node !== "object")
1564
+ continue;
1565
+ if (typeof node.$ref === "string" && typeof node.$validate === "function" && node.$validate.name === "Validate_Reference") {
1566
+ const refPath = node.$ref;
1567
+ let target = this.getSchemaRef(refPath);
1568
+ if (typeof target === "undefined") {
1569
+ target = this.getSchemaById(refPath);
1570
+ }
1571
+ if (typeof target === "boolean") {
1572
+ if (target === true) {
1573
+ node.$validate = getNamedFunction("Validate_Ref_True", () => {
1574
+ });
1575
+ } else {
1576
+ const defineError = getDefinedErrorFunctionForKey(
1577
+ "$ref",
1578
+ node,
1579
+ this.failFast
1580
+ );
1581
+ node.$validate = getNamedFunction(
1582
+ "Validate_Ref_False",
1583
+ (_data) => defineError("Value is not valid")
1584
+ );
1585
+ }
1586
+ continue;
1587
+ }
1588
+ if (target && typeof target.$validate === "function") {
1589
+ node.$validate = target.$validate;
1590
+ }
1591
+ }
1592
+ for (const key in node) {
1593
+ const value = node[key];
1594
+ if (!value)
1595
+ continue;
1596
+ if (Array.isArray(value)) {
1597
+ for (let i = 0; i < value.length; i++) {
1598
+ const v = value[i];
1599
+ if (v && typeof v === "object") {
1600
+ stack.push(v);
1601
+ }
1602
+ }
1603
+ } else if (typeof value === "object") {
1604
+ stack.push(value);
1605
+ }
1606
+ }
1607
+ }
1608
+ }
1254
1609
  };
1255
1610
  export {
1256
- SchemaShield
1611
+ SchemaShield,
1612
+ ValidationError,
1613
+ deepClone
1257
1614
  };