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.js CHANGED
@@ -19,7 +19,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
19
19
  // lib/index.ts
20
20
  var lib_exports = {};
21
21
  __export(lib_exports, {
22
- SchemaShield: () => SchemaShield
22
+ SchemaShield: () => SchemaShield,
23
+ ValidationError: () => ValidationError,
24
+ deepClone: () => deepClone
23
25
  });
24
26
  module.exports = __toCommonJS(lib_exports);
25
27
 
@@ -78,14 +80,18 @@ var ValidationError = class extends Error {
78
80
  };
79
81
  }
80
82
  };
81
- function getDefinedErrorFunctionForKey(key, schema) {
83
+ var FAIL_FAST_DEFINE_ERROR = () => true;
84
+ function getDefinedErrorFunctionForKey(key, schema, failFast) {
85
+ if (failFast) {
86
+ return FAIL_FAST_DEFINE_ERROR;
87
+ }
82
88
  const KeywordError = new ValidationError(`Invalid ${key}`);
83
89
  KeywordError.keyword = key;
84
90
  KeywordError.schema = schema;
85
91
  const defineError = (message, options = {}) => {
86
92
  KeywordError.message = message;
87
93
  KeywordError.item = options.item;
88
- KeywordError.cause = options.cause;
94
+ KeywordError.cause = options.cause && options.cause !== true ? options.cause : void 0;
89
95
  KeywordError.data = options.data;
90
96
  return KeywordError;
91
97
  };
@@ -94,34 +100,38 @@ function getDefinedErrorFunctionForKey(key, schema) {
94
100
  defineError
95
101
  );
96
102
  }
97
- function deepEqual(obj, other) {
98
- if (Array.isArray(obj) && Array.isArray(other)) {
99
- if (obj.length !== other.length) {
100
- return false;
103
+ function hasChanged(prev, current) {
104
+ if (Array.isArray(prev)) {
105
+ if (Array.isArray(current) === false) {
106
+ return true;
101
107
  }
102
- for (let i = 0; i < obj.length; i++) {
103
- if (!deepEqual(obj[i], other[i])) {
104
- return false;
108
+ if (prev.length !== current.length) {
109
+ return true;
110
+ }
111
+ for (let i = 0; i < current.length; i++) {
112
+ if (hasChanged(prev[i], current[i])) {
113
+ return true;
105
114
  }
106
115
  }
107
- return true;
116
+ return false;
108
117
  }
109
- if (typeof obj === "object" && typeof other === "object") {
110
- if (obj === null || other === null) {
111
- return obj === other;
118
+ if (typeof prev === "object" && prev !== null) {
119
+ if (typeof current !== "object" || current === null) {
120
+ return true;
112
121
  }
113
- const keys = Object.keys(obj);
114
- if (keys.length !== Object.keys(other).length) {
115
- return false;
122
+ for (const key in current) {
123
+ if (hasChanged(prev[key], current[key])) {
124
+ return true;
125
+ }
116
126
  }
117
- for (const key of keys) {
118
- if (!deepEqual(obj[key], other[key])) {
119
- return false;
127
+ for (const key in prev) {
128
+ if (hasChanged(prev[key], current[key])) {
129
+ return true;
120
130
  }
121
131
  }
122
- return true;
132
+ return false;
123
133
  }
124
- return obj === other;
134
+ return Object.is(prev, current) === false;
125
135
  }
126
136
  function isObject(data) {
127
137
  return typeof data === "object" && data !== null && !Array.isArray(data);
@@ -129,27 +139,116 @@ function isObject(data) {
129
139
  function areCloseEnough(a, b, epsilon = 1e-15) {
130
140
  return Math.abs(a - b) <= epsilon * Math.max(Math.abs(a), Math.abs(b));
131
141
  }
132
- function deepClone(obj) {
133
- if (Array.isArray(obj)) {
134
- const result = [];
135
- for (let i = 0; i < obj.length; i++) {
136
- result[i] = deepClone(obj[i]);
137
- }
138
- return result;
139
- }
140
- if (obj && obj.constructor && obj.constructor.name !== "Object") {
142
+ function deepClone(obj, cloneClassInstances = false, seen = /* @__PURE__ */ new WeakMap()) {
143
+ if (typeof obj === "undefined" || obj === null || typeof obj !== "object") {
141
144
  return obj;
142
145
  }
143
- if (isObject(obj)) {
144
- const result = {
145
- ...obj
146
- };
147
- for (const key in obj) {
148
- result[key] = deepClone(obj[key]);
146
+ if (seen.has(obj)) {
147
+ return seen.get(obj);
148
+ }
149
+ let clone;
150
+ if (typeof structuredClone === "function") {
151
+ clone = structuredClone(obj);
152
+ seen.set(obj, clone);
153
+ return clone;
154
+ }
155
+ switch (true) {
156
+ case Array.isArray(obj): {
157
+ clone = [];
158
+ seen.set(obj, clone);
159
+ for (let i = 0, l = obj.length; i < l; i++) {
160
+ clone[i] = deepClone(obj[i], cloneClassInstances, seen);
161
+ }
162
+ return clone;
163
+ }
164
+ case obj instanceof Date: {
165
+ clone = new Date(obj.getTime());
166
+ seen.set(obj, clone);
167
+ return clone;
168
+ }
169
+ case obj instanceof RegExp: {
170
+ clone = new RegExp(obj.source, obj.flags);
171
+ seen.set(obj, clone);
172
+ return clone;
173
+ }
174
+ case obj instanceof Map: {
175
+ clone = /* @__PURE__ */ new Map();
176
+ seen.set(obj, clone);
177
+ for (const [key, value] of obj.entries()) {
178
+ clone.set(
179
+ deepClone(key, cloneClassInstances, seen),
180
+ deepClone(value, cloneClassInstances, seen)
181
+ );
182
+ }
183
+ return clone;
184
+ }
185
+ case obj instanceof Set: {
186
+ clone = /* @__PURE__ */ new Set();
187
+ seen.set(obj, clone);
188
+ for (const value of obj.values()) {
189
+ clone.add(deepClone(value, cloneClassInstances, seen));
190
+ }
191
+ return clone;
192
+ }
193
+ case obj instanceof ArrayBuffer: {
194
+ clone = obj.slice(0);
195
+ seen.set(obj, clone);
196
+ return clone;
197
+ }
198
+ case ArrayBuffer.isView(obj): {
199
+ clone = new obj.constructor(obj.buffer.slice(0));
200
+ seen.set(obj, clone);
201
+ return clone;
202
+ }
203
+ case (typeof Buffer !== "undefined" && obj instanceof Buffer): {
204
+ clone = Buffer.from(obj);
205
+ seen.set(obj, clone);
206
+ return clone;
207
+ }
208
+ case obj instanceof Error: {
209
+ clone = new obj.constructor(obj.message);
210
+ seen.set(obj, clone);
211
+ break;
212
+ }
213
+ case (obj instanceof Promise || obj instanceof WeakMap || obj instanceof WeakSet): {
214
+ clone = obj;
215
+ seen.set(obj, clone);
216
+ return clone;
217
+ }
218
+ case (obj.constructor && obj.constructor !== Object): {
219
+ if (!cloneClassInstances) {
220
+ clone = obj;
221
+ seen.set(obj, clone);
222
+ return clone;
223
+ }
224
+ clone = Object.create(Object.getPrototypeOf(obj));
225
+ seen.set(obj, clone);
226
+ break;
227
+ }
228
+ default: {
229
+ clone = {};
230
+ seen.set(obj, clone);
231
+ const keys = Reflect.ownKeys(obj);
232
+ for (let i = 0, l = keys.length; i < l; i++) {
233
+ const key = keys[i];
234
+ clone[key] = deepClone(
235
+ obj[key],
236
+ cloneClassInstances,
237
+ seen
238
+ );
239
+ }
240
+ return clone;
149
241
  }
150
- return result;
151
242
  }
152
- return obj;
243
+ const descriptors = Object.getOwnPropertyDescriptors(obj);
244
+ for (const key of Reflect.ownKeys(descriptors)) {
245
+ const descriptor = descriptors[key];
246
+ if ("value" in descriptor) {
247
+ descriptor.value = deepClone(descriptor.value, cloneClassInstances, seen);
248
+ }
249
+ Object.defineProperty(clone, key, descriptor);
250
+ }
251
+ return clone;
153
252
  }
154
253
  function isCompiledSchema(subSchema) {
155
254
  return isObject(subSchema) && "$validate" in subSchema;
@@ -157,82 +256,86 @@ function isCompiledSchema(subSchema) {
157
256
  function getNamedFunction(name, fn) {
158
257
  return Object.defineProperty(fn, "name", { value: name });
159
258
  }
259
+ function resolvePath(root, path) {
260
+ if (!path || path === "#") {
261
+ return root;
262
+ }
263
+ if (path.startsWith("#/")) {
264
+ const parts = path.split("/").slice(1);
265
+ let current = root;
266
+ for (const part of parts) {
267
+ const decodedUriPart = decodeURIComponent(part);
268
+ const key = decodedUriPart.replace(/~1/g, "/").replace(/~0/g, "~");
269
+ if (current && typeof current === "object" && key in current) {
270
+ current = current[key];
271
+ } else {
272
+ return;
273
+ }
274
+ }
275
+ return current;
276
+ }
277
+ if (!path.includes("#")) {
278
+ if (root.definitions && root.definitions[path]) {
279
+ return root.definitions[path];
280
+ }
281
+ if (root.defs && root.defs[path]) {
282
+ return root.defs[path];
283
+ }
284
+ if (root.$id && typeof root.$id === "string") {
285
+ if (root.$id === path || root.$id.endsWith("/" + path)) {
286
+ return root;
287
+ }
288
+ }
289
+ }
290
+ return;
291
+ }
160
292
 
161
293
  // lib/formats.ts
294
+ var UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
295
+ var DURATION_REGEX = /^P(?!$)((\d+Y)?(\d+M)?(\d+W)?(\d+D)?)(T(?=\d)(\d+H)?(\d+M)?(\d+S)?)?$/;
296
+ 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;
297
+ var URI_REGEX = /^[a-zA-Z][a-zA-Z0-9+\-.]*:[^\s]*$/;
298
+ 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;
299
+ 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])$/;
300
+ var IPV6_REGEX = /(?:\s+|:::+|^\w{5,}|\w{5}$|^:{1}\w|\w:{1}$)/;
301
+ var IPV6_SHORT_REGEX = /^[0-9a-fA-F:.]*$/;
302
+ var IPV6_FULL_REGEX = /^(?:(?:[0-9a-fA-F]{1,4}:){7}(?:[0-9a-fA-F]{1,4}|:))$/;
303
+ var IPV6_INVALID_CHAR_REGEX = /(?:[0-9a-fA-F]{5,}|\D[0-9a-fA-F]{3}:)/;
304
+ var IPV6_FAST_FAIL_REGEX = /^(?:(?:(?:[0-9a-fA-F]{1,4}(?::|$)){1,6}))|(?:::(?:[0-9a-fA-F]{1,4})){0,5}$/;
305
+ var HOSTNAME_REGEX = /^[a-z0-9][a-z0-9-]{0,62}(?:\.[a-z0-9][a-z0-9-]{0,62})*[a-z0-9]$/i;
306
+ var DATE_REGEX = /^(\d{4})-(\d{2})-(\d{2})$/;
307
+ var JSON_POINTER_REGEX = /^\/(?:[^~]|~0|~1)*$/;
308
+ var RELATIVE_JSON_POINTER_REGEX = /^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/;
309
+ var TIME_REGEX = /^([01]\d|2[0-3]):([0-5]\d):([0-5]\d)(\.\d+)?(Z|([+-])([01]\d|2[0-3]):([0-5]\d))$/;
310
+ var URI_REFERENCE_REGEX = /^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#((?![^#]*\\)[^#]*))?/i;
311
+ var URI_TEMPLATE_REGEX = /^(?:[^{}]|\{[^}]+\})*$/;
312
+ var IRI_REGEX = /^[a-zA-Z][a-zA-Z0-9+\-.]*:[^\s]*$/;
313
+ var IRI_REFERENCE_REGEX = /^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#((?![^#]*\\)[^#]*))?/i;
314
+ var IDN_EMAIL_REGEX = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
315
+ var IDN_HOSTNAME_REGEX = /^[^\s!@#$%^&*()_+\=\[\]{};':"\\|,<>\/?]+$/;
316
+ var BACK_SLASH_REGEX = /\\/;
317
+ var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
162
318
  var Formats = {
163
319
  ["date-time"](data) {
164
- const match = data.match(
165
- /^(\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
166
- );
320
+ const match = data.match(DATE_TIME_REGEX);
167
321
  if (!match) {
168
322
  return false;
169
323
  }
170
- let day = Number(match[3]);
171
- if (match[2] === "02" && day > 29) {
324
+ const [, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr] = match;
325
+ const year = Number(yearStr);
326
+ const month = Number(monthStr);
327
+ const day = Number(dayStr);
328
+ const hour = Number(hourStr);
329
+ const minute = Number(minuteStr);
330
+ const second = Number(secondStr);
331
+ if (month < 1 || month > 12) {
172
332
  return false;
173
333
  }
174
- const [
175
- ,
176
- yearStr,
177
- monthStr,
178
- ,
179
- hourStr,
180
- minuteStr,
181
- secondStr,
182
- timezoneSign,
183
- timezoneHourStr,
184
- timezoneMinuteStr
185
- ] = match;
186
- let year = Number(yearStr);
187
- let month = Number(monthStr);
188
- let hour = Number(hourStr);
189
- let minute = Number(minuteStr);
190
- let second = Number(secondStr);
191
- if (timezoneSign === "-" || timezoneSign === "+") {
192
- const timezoneHour = Number(timezoneHourStr);
193
- const timezoneMinute = Number(timezoneMinuteStr);
194
- if (timezoneSign === "-") {
195
- hour += timezoneHour;
196
- minute += timezoneMinute;
197
- } else if (timezoneSign === "+") {
198
- hour -= timezoneHour;
199
- minute -= timezoneMinute;
200
- }
201
- if (minute > 59) {
202
- hour += 1;
203
- minute -= 60;
204
- } else if (minute < 0) {
205
- hour -= 1;
206
- minute += 60;
207
- }
208
- if (hour > 23) {
209
- day += 1;
210
- hour -= 24;
211
- } else if (hour < 0) {
212
- day -= 1;
213
- hour += 24;
214
- }
215
- if (day > 31) {
216
- month += 1;
217
- day -= 31;
218
- } else if (day < 1) {
219
- month -= 1;
220
- day += 31;
221
- }
222
- if (month > 12) {
223
- year += 1;
224
- month -= 12;
225
- } else if (month < 1) {
226
- year -= 1;
227
- month += 12;
228
- }
229
- if (year < 0) {
230
- return false;
231
- }
334
+ if (day < 1) {
335
+ return false;
232
336
  }
233
- const daysInMonth = [31, , 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
234
- const maxDays = month === 2 ? year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) ? 29 : 28 : daysInMonth[month - 1];
235
- if (day > maxDays) {
337
+ const maxDays = month === 2 ? year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) ? 29 : 28 : DAYS_IN_MONTH[month - 1];
338
+ if (!maxDays || day > maxDays) {
236
339
  return false;
237
340
  }
238
341
  if (second === 60 && (minute !== 59 || hour !== 23)) {
@@ -241,24 +344,20 @@ var Formats = {
241
344
  return true;
242
345
  },
243
346
  uri(data) {
244
- return /^[a-zA-Z][a-zA-Z0-9+\-.]*:[^\s]*$/.test(data);
347
+ return URI_REGEX.test(data);
245
348
  },
246
349
  email(data) {
247
- 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(
248
- data
249
- );
350
+ return EMAIL_REGEX.test(data);
250
351
  },
251
352
  ipv4(data) {
252
- 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(
253
- data
254
- );
353
+ return IPV4_REGEX.test(data);
255
354
  },
256
355
  // ipv6: isMyIpValid({ version: 6 }),
257
356
  ipv6(data) {
258
357
  if (data === "::") {
259
358
  return true;
260
359
  }
261
- if (data.indexOf(":") === -1 || /(?:\s+|:::+|^\w{5,}|\w{5}$|^:{1}\w|\w:{1}$)/.test(data)) {
360
+ if (data.indexOf(":") === -1 || IPV6_REGEX.test(data)) {
262
361
  return false;
263
362
  }
264
363
  const hasIpv4 = data.indexOf(".") !== -1;
@@ -266,9 +365,7 @@ var Formats = {
266
365
  if (hasIpv4) {
267
366
  addressParts = data.split(":");
268
367
  const ipv4Part = addressParts.pop();
269
- 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(
270
- ipv4Part
271
- )) {
368
+ if (!IPV4_REGEX.test(ipv4Part)) {
272
369
  return false;
273
370
  }
274
371
  }
@@ -278,32 +375,38 @@ var Formats = {
278
375
  if (ipv6Part.split("::").length - 1 > 1) {
279
376
  return false;
280
377
  }
281
- if (!/^[0-9a-fA-F:.]*$/.test(ipv6Part)) {
378
+ if (!IPV6_SHORT_REGEX.test(ipv6Part)) {
282
379
  return false;
283
380
  }
284
- return /^(?:(?:(?:[0-9a-fA-F]{1,4}(?::|$)){1,6}))|(?:::(?:[0-9a-fA-F]{1,4})){0,5}$/.test(
285
- ipv6Part
286
- );
381
+ return IPV6_FAST_FAIL_REGEX.test(ipv6Part);
287
382
  }
288
- const isIpv6Valid = /^(?:(?:[0-9a-fA-F]{1,4}:){7}(?:[0-9a-fA-F]{1,4}|:))$/.test(ipv6Part);
289
- const hasInvalidChar = /(?:[0-9a-fA-F]{5,}|\D[0-9a-fA-F]{3}:)/.test(
290
- ipv6Part
291
- );
383
+ const isIpv6Valid = IPV6_FULL_REGEX.test(ipv6Part);
384
+ const hasInvalidChar = IPV6_INVALID_CHAR_REGEX.test(ipv6Part);
292
385
  if (hasIpv4) {
293
386
  return isIpv6Valid || !hasInvalidChar;
294
387
  }
295
388
  return isIpv6Valid && !hasInvalidChar;
296
389
  },
297
390
  hostname(data) {
298
- return /^[a-z0-9][a-z0-9-]{0,62}(?:\.[a-z0-9][a-z0-9-]{0,62})*[a-z0-9]$/i.test(
299
- data
300
- );
391
+ return HOSTNAME_REGEX.test(data);
301
392
  },
302
393
  date(data) {
303
- if (/^(\d{4})-(\d{2})-(\d{2})$/.test(data) === false) {
394
+ const match = DATE_REGEX.exec(data);
395
+ if (!match) {
304
396
  return false;
305
397
  }
306
- return !isNaN(new Date(data).getTime());
398
+ const [, yearStr, monthStr, dayStr] = match;
399
+ const year = Number(yearStr);
400
+ const month = Number(monthStr);
401
+ const day = Number(dayStr);
402
+ if (month < 1 || month > 12) {
403
+ return false;
404
+ }
405
+ if (day < 1) {
406
+ return false;
407
+ }
408
+ const maxDays = month === 2 ? year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) ? 29 : 28 : DAYS_IN_MONTH[month - 1];
409
+ return !!maxDays && day <= maxDays;
307
410
  },
308
411
  regex(data) {
309
412
  try {
@@ -317,39 +420,49 @@ var Formats = {
317
420
  if (data === "") {
318
421
  return true;
319
422
  }
320
- return /^\/(?:[^~]|~0|~1)*$/.test(data);
423
+ return JSON_POINTER_REGEX.test(data);
321
424
  },
322
425
  "relative-json-pointer"(data) {
323
426
  if (data === "") {
324
427
  return true;
325
428
  }
326
- return /^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/.test(data);
429
+ return RELATIVE_JSON_POINTER_REGEX.test(data);
327
430
  },
328
431
  time(data) {
329
- return /^(\d{2}):(\d{2}):(\d{2})(\.\d+)?(Z|([+-])(\d{2}):(\d{2}))$/.test(
330
- data
331
- );
432
+ return TIME_REGEX.test(data);
332
433
  },
333
434
  "uri-reference"(data) {
334
- if (/\\/.test(data)) {
435
+ if (BACK_SLASH_REGEX.test(data)) {
335
436
  return false;
336
437
  }
337
- return /^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#((?![^#]*\\)[^#]*))?/i.test(
338
- data
339
- );
438
+ return URI_REFERENCE_REGEX.test(data);
340
439
  },
341
440
  "uri-template"(data) {
342
- return /^(?:(?:https?:\/\/[\w.-]+)?\/?)?[\w- ;,.\/?%&=]*(?:\{[\w-]+(?::\d+)?\}[\w- ;,.\/?%&=]*)*\/?$/.test(
343
- data
344
- );
441
+ return URI_TEMPLATE_REGEX.test(data);
442
+ },
443
+ duration(data) {
444
+ return DURATION_REGEX.test(data);
445
+ },
446
+ uuid(data) {
447
+ return UUID_REGEX.test(data);
448
+ },
449
+ // IRI is like URI but allows Unicode. We reuse a permissive logic.
450
+ iri(data) {
451
+ return IRI_REGEX.test(data);
345
452
  },
346
- // Not supported yet
347
- duration: false,
348
- uuid: false,
349
- "idn-email": false,
350
- "idn-hostname": false,
351
- iri: false,
352
- "iri-reference": false
453
+ "iri-reference"(data) {
454
+ if (BACK_SLASH_REGEX.test(data)) {
455
+ return false;
456
+ }
457
+ return IRI_REFERENCE_REGEX.test(data);
458
+ },
459
+ // Best-effort structural validation for IDN (no punycode/tables)
460
+ "idn-email"(data) {
461
+ return IDN_EMAIL_REGEX.test(data);
462
+ },
463
+ "idn-hostname"(data) {
464
+ return IDN_HOSTNAME_REGEX.test(data);
465
+ }
353
466
  };
354
467
 
355
468
  // lib/types.ts
@@ -358,10 +471,7 @@ var Types = {
358
471
  return isObject(data);
359
472
  },
360
473
  array(data) {
361
- if (Array.isArray(data)) {
362
- return true;
363
- }
364
- return typeof data === "object" && data !== null && "length" in data && "0" in data && Object.keys(data).length - 1 === data.length;
474
+ return Array.isArray(data);
365
475
  },
366
476
  string(data) {
367
477
  return typeof data === "string";
@@ -392,6 +502,7 @@ var Types = {
392
502
 
393
503
  // lib/keywords/array-keywords.ts
394
504
  var ArrayKeywords = {
505
+ // lib/keywords/array-keywords.ts
395
506
  items(schema, data, defineError) {
396
507
  if (!Array.isArray(data)) {
397
508
  return;
@@ -406,11 +517,11 @@ var ArrayKeywords = {
406
517
  }
407
518
  if (Array.isArray(schemaItems)) {
408
519
  const schemaItemsLength = schemaItems.length;
409
- const itemsLength = Math.min(schemaItemsLength, dataLength);
520
+ const itemsLength = schemaItemsLength < dataLength ? schemaItemsLength : dataLength;
410
521
  for (let i = 0; i < itemsLength; i++) {
411
522
  const schemaItem = schemaItems[i];
412
523
  if (typeof schemaItem === "boolean") {
413
- if (schemaItem === false && typeof data[i] !== "undefined") {
524
+ if (schemaItem === false && data[i] !== void 0) {
414
525
  return defineError("Array item is not allowed", {
415
526
  item: i,
416
527
  data: data[i]
@@ -418,8 +529,9 @@ var ArrayKeywords = {
418
529
  }
419
530
  continue;
420
531
  }
421
- if (isCompiledSchema(schemaItem)) {
422
- const error = schemaItem.$validate(data[i]);
532
+ const validate2 = schemaItem && schemaItem.$validate;
533
+ if (typeof validate2 === "function") {
534
+ const error = validate2(data[i]);
423
535
  if (error) {
424
536
  return defineError("Array item is invalid", {
425
537
  item: i,
@@ -431,26 +543,32 @@ var ArrayKeywords = {
431
543
  }
432
544
  return;
433
545
  }
434
- if (isCompiledSchema(schemaItems)) {
435
- for (let i = 0; i < dataLength; i++) {
436
- const error = schemaItems.$validate(data[i]);
437
- if (error) {
438
- return defineError("Array item is invalid", {
439
- item: i,
440
- cause: error,
441
- data: data[i]
442
- });
443
- }
546
+ const validate = schemaItems && schemaItems.$validate;
547
+ if (typeof validate !== "function") {
548
+ return;
549
+ }
550
+ for (let i = 0; i < dataLength; i++) {
551
+ const error = validate(data[i]);
552
+ if (error) {
553
+ return defineError("Array item is invalid", {
554
+ item: i,
555
+ cause: error,
556
+ data: data[i]
557
+ });
444
558
  }
445
559
  }
446
- return;
447
560
  },
448
561
  elements(schema, data, defineError) {
449
- if (!Array.isArray(data) || !isCompiledSchema(schema.elements)) {
562
+ if (!Array.isArray(data)) {
563
+ return;
564
+ }
565
+ const elementsSchema = schema.elements;
566
+ const validate = elementsSchema && elementsSchema.$validate;
567
+ if (typeof validate !== "function") {
450
568
  return;
451
569
  }
452
570
  for (let i = 0; i < data.length; i++) {
453
- const error = schema.elements.$validate(data[i]);
571
+ const error = validate(data[i]);
454
572
  if (error) {
455
573
  return defineError("Array item is invalid", {
456
574
  item: i,
@@ -459,7 +577,6 @@ var ArrayKeywords = {
459
577
  });
460
578
  }
461
579
  }
462
- return;
463
580
  },
464
581
  minItems(schema, data, defineError) {
465
582
  if (!Array.isArray(data) || data.length >= schema.minItems) {
@@ -505,28 +622,30 @@ var ArrayKeywords = {
505
622
  if (!Array.isArray(data) || !schema.uniqueItems) {
506
623
  return;
507
624
  }
508
- const unique = /* @__PURE__ */ new Set();
509
- for (const item of data) {
510
- let itemStr;
511
- if (typeof item === "string") {
512
- itemStr = `s:${item}`;
513
- } else if (isObject(item)) {
514
- itemStr = `o:${JSON.stringify(
515
- Object.fromEntries(
516
- Object.entries(item).sort(([a], [b]) => a.localeCompare(b))
517
- )
518
- )}`;
519
- } else if (Array.isArray(item)) {
520
- itemStr = JSON.stringify(item);
521
- } else {
522
- itemStr = String(item);
625
+ const len = data.length;
626
+ if (len <= 1) {
627
+ return;
628
+ }
629
+ const primitiveSeen = /* @__PURE__ */ new Set();
630
+ for (let i = 0; i < len; i++) {
631
+ const item = data[i];
632
+ const type = typeof item;
633
+ if (item === null || type === "string" || type === "number" || type === "boolean") {
634
+ if (primitiveSeen.has(item)) {
635
+ return defineError("Array items are not unique", { data: item });
636
+ }
637
+ primitiveSeen.add(item);
638
+ continue;
523
639
  }
524
- if (unique.has(itemStr)) {
525
- return defineError("Array items are not unique", { data: item });
640
+ if (item && typeof item === "object") {
641
+ for (let j = 0; j < i; j++) {
642
+ const prev = data[j];
643
+ if (prev && typeof prev === "object" && !hasChanged(prev, item)) {
644
+ return defineError("Array items are not unique", { data: item });
645
+ }
646
+ }
526
647
  }
527
- unique.add(itemStr);
528
648
  }
529
- return;
530
649
  },
531
650
  contains(schema, data, defineError) {
532
651
  if (!Array.isArray(data)) {
@@ -622,7 +741,6 @@ var NumberKeywords = {
622
741
 
623
742
  // lib/keywords/object-keywords.ts
624
743
  var ObjectKeywords = {
625
- // Object
626
744
  required(schema, data, defineError) {
627
745
  if (!isObject(data)) {
628
746
  return;
@@ -642,16 +760,46 @@ var ObjectKeywords = {
642
760
  if (!isObject(data)) {
643
761
  return;
644
762
  }
645
- for (const key of Object.keys(schema.properties)) {
646
- if (!data.hasOwnProperty(key)) {
647
- const schemaProp = schema.properties[key];
648
- if (isObject(schemaProp) && "default" in schemaProp) {
649
- data[key] = schemaProp.default;
763
+ let propKeys = schema._propKeys;
764
+ if (!propKeys) {
765
+ propKeys = Object.keys(schema.properties || {});
766
+ Object.defineProperty(schema, "_propKeys", {
767
+ value: propKeys,
768
+ enumerable: false,
769
+ configurable: false,
770
+ writable: false
771
+ });
772
+ }
773
+ let requiredKeys = schema._requiredKeys;
774
+ if (requiredKeys === void 0) {
775
+ requiredKeys = Array.isArray(schema.required) ? schema.required : null;
776
+ Object.defineProperty(schema, "_requiredKeys", {
777
+ value: requiredKeys,
778
+ enumerable: false,
779
+ configurable: false,
780
+ writable: false
781
+ });
782
+ }
783
+ const required = requiredKeys || [];
784
+ for (let i = 0; i < propKeys.length; i++) {
785
+ const key = propKeys[i];
786
+ const schemaProp = schema.properties[key];
787
+ if (!Object.prototype.hasOwnProperty.call(data, key)) {
788
+ if (required.length && required.indexOf(key) !== -1 && isObject(schemaProp) && "default" in schemaProp) {
789
+ const error = schemaProp.$validate(schemaProp.default);
790
+ if (error) {
791
+ return defineError("Default property is invalid", {
792
+ item: key,
793
+ cause: error,
794
+ data: schemaProp.default
795
+ });
796
+ }
797
+ data[key] = deepClone(schemaProp.default);
650
798
  }
651
799
  continue;
652
800
  }
653
- if (typeof schema.properties[key] === "boolean") {
654
- if (schema.properties[key] === false) {
801
+ if (typeof schemaProp === "boolean") {
802
+ if (schemaProp === false) {
655
803
  return defineError("Property is not allowed", {
656
804
  item: key,
657
805
  data: data[key]
@@ -659,8 +807,8 @@ var ObjectKeywords = {
659
807
  }
660
808
  continue;
661
809
  }
662
- if ("$validate" in schema.properties[key]) {
663
- const error = schema.properties[key].$validate(data[key]);
810
+ if (schemaProp && "$validate" in schemaProp) {
811
+ const error = schemaProp.$validate(data[key]);
664
812
  if (error) {
665
813
  return defineError("Property is invalid", {
666
814
  item: key,
@@ -673,12 +821,18 @@ var ObjectKeywords = {
673
821
  return;
674
822
  },
675
823
  values(schema, data, defineError) {
676
- if (!isObject(data) || !isCompiledSchema(schema.values)) {
824
+ if (!isObject(data)) {
825
+ return;
826
+ }
827
+ const valueSchema = schema.values;
828
+ const validate = valueSchema && valueSchema.$validate;
829
+ if (typeof validate !== "function") {
677
830
  return;
678
831
  }
679
832
  const keys = Object.keys(data);
680
- for (const key of keys) {
681
- const error = schema.values.$validate(data[key]);
833
+ for (let i = 0; i < keys.length; i++) {
834
+ const key = keys[i];
835
+ const error = validate(data[key]);
682
836
  if (error) {
683
837
  return defineError("Property is invalid", {
684
838
  item: key,
@@ -687,7 +841,6 @@ var ObjectKeywords = {
687
841
  });
688
842
  }
689
843
  }
690
- return;
691
844
  },
692
845
  maxProperties(schema, data, defineError) {
693
846
  if (!isObject(data) || Object.keys(data).length <= schema.maxProperties) {
@@ -706,15 +859,37 @@ var ObjectKeywords = {
706
859
  return;
707
860
  }
708
861
  const keys = Object.keys(data);
709
- const isCompiled = isCompiledSchema(schema.additionalProperties);
710
- for (const key of keys) {
862
+ let apIsCompiled = schema._apIsCompiled;
863
+ if (apIsCompiled === void 0) {
864
+ apIsCompiled = isCompiledSchema(schema.additionalProperties);
865
+ Object.defineProperty(schema, "_apIsCompiled", {
866
+ value: apIsCompiled,
867
+ enumerable: false
868
+ });
869
+ }
870
+ let patternList = schema._patternPropertiesList;
871
+ if (schema.patternProperties && !patternList) {
872
+ patternList = [];
873
+ for (const pattern in schema.patternProperties) {
874
+ patternList.push({
875
+ regex: new RegExp(pattern, "u"),
876
+ key: pattern
877
+ });
878
+ }
879
+ Object.defineProperty(schema, "_patternPropertiesList", {
880
+ value: patternList,
881
+ enumerable: false
882
+ });
883
+ }
884
+ for (let i = 0; i < keys.length; i++) {
885
+ const key = keys[i];
711
886
  if (schema.properties && schema.properties.hasOwnProperty(key)) {
712
887
  continue;
713
888
  }
714
- if (schema.patternProperties) {
889
+ if (patternList && patternList.length) {
715
890
  let match = false;
716
- for (const pattern in schema.patternProperties) {
717
- if (new RegExp(pattern, "u").test(key)) {
891
+ for (let j = 0; j < patternList.length; j++) {
892
+ if (patternList[j].regex.test(key)) {
718
893
  match = true;
719
894
  break;
720
895
  }
@@ -729,7 +904,7 @@ var ObjectKeywords = {
729
904
  data: data[key]
730
905
  });
731
906
  }
732
- if (isCompiled) {
907
+ if (apIsCompiled && isCompiledSchema(schema.additionalProperties)) {
733
908
  const error = schema.additionalProperties.$validate(data[key]);
734
909
  if (error) {
735
910
  return defineError("Additional properties are invalid", {
@@ -746,12 +921,30 @@ var ObjectKeywords = {
746
921
  if (!isObject(data)) {
747
922
  return;
748
923
  }
749
- const patterns = Object.keys(schema.patternProperties);
750
- for (const pattern of patterns) {
751
- const regex = new RegExp(pattern, "u");
752
- if (typeof schema.patternProperties[pattern] === "boolean") {
753
- if (schema.patternProperties[pattern] === false) {
754
- for (const key in data) {
924
+ let patternList = schema._patternPropertiesList;
925
+ if (!patternList) {
926
+ patternList = [];
927
+ const patterns = Object.keys(schema.patternProperties || {});
928
+ for (let i = 0; i < patterns.length; i++) {
929
+ const pattern = patterns[i];
930
+ patternList.push({
931
+ regex: new RegExp(pattern, "u"),
932
+ key: pattern
933
+ });
934
+ }
935
+ Object.defineProperty(schema, "_patternPropertiesList", {
936
+ value: patternList,
937
+ enumerable: false
938
+ });
939
+ }
940
+ const dataKeys = Object.keys(data);
941
+ for (let p = 0; p < patternList.length; p++) {
942
+ const { regex, key: patternKey } = patternList[p];
943
+ const schemaProp = schema.patternProperties[patternKey];
944
+ if (typeof schemaProp === "boolean") {
945
+ if (schemaProp === false) {
946
+ for (let i = 0; i < dataKeys.length; i++) {
947
+ const key = dataKeys[i];
755
948
  if (regex.test(key)) {
756
949
  return defineError("Property is not allowed", {
757
950
  item: key,
@@ -762,13 +955,11 @@ var ObjectKeywords = {
762
955
  }
763
956
  continue;
764
957
  }
765
- const keys = Object.keys(data);
766
- for (const key of keys) {
767
- if (regex.test(key)) {
768
- if ("$validate" in schema.patternProperties[pattern]) {
769
- const error = schema.patternProperties[pattern].$validate(
770
- data[key]
771
- );
958
+ if ("$validate" in schemaProp) {
959
+ for (let i = 0; i < dataKeys.length; i++) {
960
+ const key = dataKeys[i];
961
+ if (regex.test(key)) {
962
+ const error = schemaProp.$validate(data[key]);
772
963
  if (error) {
773
964
  return defineError("Property is invalid", {
774
965
  item: key,
@@ -786,24 +977,30 @@ var ObjectKeywords = {
786
977
  if (!isObject(data)) {
787
978
  return;
788
979
  }
789
- if (typeof schema.propertyNames === "boolean") {
790
- if (schema.propertyNames === false && Object.keys(data).length > 0) {
980
+ const pn = schema.propertyNames;
981
+ if (typeof pn === "boolean") {
982
+ if (pn === false && Object.keys(data).length > 0) {
791
983
  return defineError("Properties are not allowed", { data });
792
984
  }
985
+ return;
793
986
  }
794
- if (isCompiledSchema(schema.propertyNames)) {
795
- for (let key in data) {
796
- const error = schema.propertyNames.$validate(key);
797
- if (error) {
798
- return defineError("Property name is invalid", {
799
- item: key,
800
- cause: error,
801
- data: data[key]
802
- });
803
- }
987
+ const validate = pn && pn.$validate;
988
+ if (typeof validate !== "function") {
989
+ return;
990
+ }
991
+ for (const key in data) {
992
+ if (!Object.prototype.hasOwnProperty.call(data, key)) {
993
+ continue;
994
+ }
995
+ const error = validate(key);
996
+ if (error) {
997
+ return defineError("Property name is invalid", {
998
+ item: key,
999
+ cause: error,
1000
+ data: data[key]
1001
+ });
804
1002
  }
805
1003
  }
806
- return;
807
1004
  },
808
1005
  dependencies(schema, data, defineError) {
809
1006
  if (!isObject(data)) {
@@ -852,7 +1049,6 @@ var ObjectKeywords = {
852
1049
  else: false,
853
1050
  default: false,
854
1051
  // Not implemented yet
855
- $ref: false,
856
1052
  definitions: false,
857
1053
  $id: false,
858
1054
  $schema: false,
@@ -871,17 +1067,14 @@ var ObjectKeywords = {
871
1067
  // lib/keywords/other-keywords.ts
872
1068
  var OtherKeywords = {
873
1069
  enum(schema, data, defineError) {
874
- const isArray = Array.isArray(data);
875
- const isObject2 = typeof data === "object" && data !== null;
876
- for (let i = 0; i < schema.enum.length; i++) {
877
- const enumItem = schema.enum[i];
1070
+ const list = schema.enum;
1071
+ for (let i = 0; i < list.length; i++) {
1072
+ const enumItem = list[i];
878
1073
  if (enumItem === data) {
879
1074
  return;
880
1075
  }
881
- if (isArray && Array.isArray(enumItem) || isObject2 && typeof enumItem === "object" && enumItem !== null) {
882
- if (deepEqual(enumItem, data)) {
883
- return;
884
- }
1076
+ if (enumItem !== null && data !== null && typeof enumItem === "object" && typeof data === "object" && !hasChanged(enumItem, data)) {
1077
+ return;
885
1078
  }
886
1079
  }
887
1080
  return defineError("Value is not one of the allowed values", { data });
@@ -934,27 +1127,40 @@ var OtherKeywords = {
934
1127
  return defineError("Value is not valid", { data });
935
1128
  },
936
1129
  oneOf(schema, data, defineError) {
1130
+ const list = schema.oneOf;
937
1131
  let validCount = 0;
938
- for (let i = 0; i < schema.oneOf.length; i++) {
939
- if (isObject(schema.oneOf[i])) {
940
- if ("$validate" in schema.oneOf[i]) {
941
- const error = schema.oneOf[i].$validate(data);
1132
+ for (let i = 0; i < list.length; i++) {
1133
+ const sub = list[i];
1134
+ if (isObject(sub)) {
1135
+ if ("$validate" in sub) {
1136
+ const error = sub.$validate(data);
942
1137
  if (!error) {
943
1138
  validCount++;
1139
+ if (validCount > 1) {
1140
+ return defineError("Value is not valid", { data });
1141
+ }
944
1142
  }
945
1143
  continue;
946
1144
  }
947
1145
  validCount++;
1146
+ if (validCount > 1) {
1147
+ return defineError("Value is not valid", { data });
1148
+ }
948
1149
  continue;
949
- } else {
950
- if (typeof schema.oneOf[i] === "boolean") {
951
- if (Boolean(data) === schema.oneOf[i]) {
952
- validCount++;
1150
+ }
1151
+ if (typeof sub === "boolean") {
1152
+ if (Boolean(data) === sub) {
1153
+ validCount++;
1154
+ if (validCount > 1) {
1155
+ return defineError("Value is not valid", { data });
953
1156
  }
954
- continue;
955
1157
  }
956
- if (data === schema.oneOf[i]) {
957
- validCount++;
1158
+ continue;
1159
+ }
1160
+ if (data === sub) {
1161
+ validCount++;
1162
+ if (validCount > 1) {
1163
+ return defineError("Value is not valid", { data });
958
1164
  }
959
1165
  }
960
1166
  }
@@ -964,12 +1170,15 @@ var OtherKeywords = {
964
1170
  return defineError("Value is not valid", { data });
965
1171
  },
966
1172
  const(schema, data, defineError) {
967
- if (data === schema.const || isObject(data) && isObject(schema.const) && deepEqual(data, schema.const) || Array.isArray(data) && Array.isArray(schema.const) && deepEqual(data, schema.const)) {
1173
+ if (data === schema.const) {
1174
+ return;
1175
+ }
1176
+ if (isObject(data) && isObject(schema.const) && !hasChanged(data, schema.const) || Array.isArray(data) && Array.isArray(schema.const) && !hasChanged(data, schema.const)) {
968
1177
  return;
969
1178
  }
970
1179
  return defineError("Value is not valid", { data });
971
1180
  },
972
- if(schema, data, defineError) {
1181
+ if(schema, data) {
973
1182
  if ("then" in schema === false && "else" in schema === false) {
974
1183
  return;
975
1184
  }
@@ -1017,6 +1226,24 @@ var OtherKeywords = {
1017
1226
  return defineError("Value is not valid", { data });
1018
1227
  }
1019
1228
  return defineError("Value is not valid", { data });
1229
+ },
1230
+ $ref(schema, data, defineError, instance) {
1231
+ if (schema._resolvedRef) {
1232
+ return schema._resolvedRef(data);
1233
+ }
1234
+ const refPath = schema.$ref;
1235
+ let targetSchema = instance.getSchemaRef(refPath);
1236
+ if (!targetSchema) {
1237
+ targetSchema = instance.getSchemaById(refPath);
1238
+ }
1239
+ if (!targetSchema) {
1240
+ return defineError(`Missing reference: ${refPath}`);
1241
+ }
1242
+ if (!targetSchema.$validate) {
1243
+ return;
1244
+ }
1245
+ schema._resolvedRef = targetSchema.$validate;
1246
+ return schema._resolvedRef(data);
1020
1247
  }
1021
1248
  };
1022
1249
 
@@ -1038,9 +1265,22 @@ var StringKeywords = {
1038
1265
  if (typeof data !== "string") {
1039
1266
  return;
1040
1267
  }
1041
- const patternRegexp = new RegExp(schema.pattern, "u");
1042
- if (patternRegexp instanceof RegExp === false) {
1043
- return defineError("Invalid regular expression", { data });
1268
+ let patternRegexp = schema._patternRegexp;
1269
+ if (!patternRegexp) {
1270
+ try {
1271
+ patternRegexp = new RegExp(schema.pattern, "u");
1272
+ Object.defineProperty(schema, "_patternRegexp", {
1273
+ value: patternRegexp,
1274
+ enumerable: false,
1275
+ configurable: false,
1276
+ writable: false
1277
+ });
1278
+ } catch (error) {
1279
+ return defineError("Invalid regular expression", {
1280
+ data,
1281
+ cause: error
1282
+ });
1283
+ }
1044
1284
  }
1045
1285
  if (patternRegexp.test(data)) {
1046
1286
  return;
@@ -1053,7 +1293,16 @@ var StringKeywords = {
1053
1293
  if (typeof data !== "string") {
1054
1294
  return;
1055
1295
  }
1056
- const formatValidate = instance.getFormat(schema.format);
1296
+ let formatValidate = schema._formatValidate;
1297
+ if (formatValidate === void 0) {
1298
+ formatValidate = instance.getFormat(schema.format);
1299
+ Object.defineProperty(schema, "_formatValidate", {
1300
+ value: formatValidate,
1301
+ enumerable: false,
1302
+ configurable: false,
1303
+ writable: false
1304
+ });
1305
+ }
1057
1306
  if (!formatValidate || formatValidate(data)) {
1058
1307
  return;
1059
1308
  }
@@ -1076,10 +1325,15 @@ var SchemaShield = class {
1076
1325
  formats = {};
1077
1326
  keywords = {};
1078
1327
  immutable = false;
1328
+ rootSchema = null;
1329
+ idRegistry = /* @__PURE__ */ new Map();
1330
+ failFast = true;
1079
1331
  constructor({
1080
- immutable = false
1332
+ immutable = false,
1333
+ failFast = true
1081
1334
  } = {}) {
1082
1335
  this.immutable = immutable;
1336
+ this.failFast = failFast;
1083
1337
  for (const [type, validator] of Object.entries(Types)) {
1084
1338
  if (validator) {
1085
1339
  this.addType(type, validator);
@@ -1121,26 +1375,38 @@ var SchemaShield = class {
1121
1375
  getKeyword(keyword) {
1122
1376
  return this.keywords[keyword];
1123
1377
  }
1378
+ getSchemaRef(path) {
1379
+ if (!this.rootSchema) {
1380
+ return;
1381
+ }
1382
+ return resolvePath(this.rootSchema, path);
1383
+ }
1384
+ getSchemaById(id) {
1385
+ return this.idRegistry.get(id);
1386
+ }
1124
1387
  compile(schema) {
1388
+ this.idRegistry.clear();
1125
1389
  const compiledSchema = this.compileSchema(schema);
1390
+ this.rootSchema = compiledSchema;
1391
+ this.linkReferences(compiledSchema);
1126
1392
  if (!compiledSchema.$validate) {
1127
1393
  if (this.isSchemaLike(schema) === false) {
1128
1394
  throw new ValidationError("Invalid schema");
1129
1395
  }
1130
1396
  compiledSchema.$validate = getNamedFunction(
1131
- "any",
1397
+ "Validate_Any",
1132
1398
  () => {
1133
1399
  }
1134
1400
  );
1135
1401
  }
1136
1402
  const validate = (data) => {
1403
+ this.rootSchema = compiledSchema;
1137
1404
  const clonedData = this.immutable ? deepClone(data) : data;
1138
- const error = compiledSchema.$validate(clonedData);
1139
- return {
1140
- data: clonedData,
1141
- error: error ? error : null,
1142
- valid: !error
1143
- };
1405
+ const res = compiledSchema.$validate(clonedData);
1406
+ if (res) {
1407
+ return { data: clonedData, error: res, valid: false };
1408
+ }
1409
+ return { data: clonedData, error: null, valid: true };
1144
1410
  };
1145
1411
  validate.compiledSchema = compiledSchema;
1146
1412
  return validate;
@@ -1148,99 +1414,113 @@ var SchemaShield = class {
1148
1414
  compileSchema(schema) {
1149
1415
  if (!isObject(schema)) {
1150
1416
  if (schema === true) {
1151
- schema = {
1152
- anyOf: [{}]
1153
- };
1417
+ schema = { anyOf: [{}] };
1154
1418
  } else if (schema === false) {
1155
- schema = {
1156
- oneOf: []
1157
- };
1419
+ schema = { oneOf: [] };
1158
1420
  } else {
1159
- schema = {
1160
- oneOf: [schema]
1161
- };
1421
+ schema = { oneOf: [schema] };
1162
1422
  }
1163
1423
  }
1164
1424
  const compiledSchema = deepClone(schema);
1165
- const defineTypeError = getDefinedErrorFunctionForKey("type", schema);
1166
- const typeValidations = [];
1167
- let methodName = "";
1425
+ if (typeof schema.$id === "string") {
1426
+ this.idRegistry.set(schema.$id, compiledSchema);
1427
+ }
1428
+ if ("$ref" in schema) {
1429
+ const refValidator = this.getKeyword("$ref");
1430
+ if (refValidator) {
1431
+ const defineError = getDefinedErrorFunctionForKey(
1432
+ "$ref",
1433
+ schema["$ref"],
1434
+ this.failFast
1435
+ );
1436
+ compiledSchema.$validate = getNamedFunction(
1437
+ "Validate_Reference",
1438
+ (data) => refValidator(
1439
+ compiledSchema,
1440
+ data,
1441
+ defineError,
1442
+ this
1443
+ )
1444
+ );
1445
+ }
1446
+ return compiledSchema;
1447
+ }
1448
+ const validators = [];
1449
+ const activeNames = [];
1168
1450
  if ("type" in schema) {
1451
+ const defineTypeError = getDefinedErrorFunctionForKey(
1452
+ "type",
1453
+ schema,
1454
+ this.failFast
1455
+ );
1169
1456
  const types = Array.isArray(schema.type) ? schema.type : schema.type.split(",").map((t) => t.trim());
1170
- for (const type of types) {
1171
- const validator = this.getType(type);
1457
+ const typeFunctions = [];
1458
+ const typeNames = [];
1459
+ for (const type2 of types) {
1460
+ const validator = this.getType(type2);
1172
1461
  if (validator) {
1173
- typeValidations.push(validator);
1174
- methodName += (methodName ? "_OR_" : "") + validator.name;
1462
+ typeFunctions.push(validator);
1463
+ typeNames.push(validator.name);
1175
1464
  }
1176
1465
  }
1177
- const typeValidationsLength = typeValidations.length;
1178
- if (typeValidationsLength === 0) {
1179
- throw defineTypeError("Invalid type for schema", { data: schema.type });
1180
- }
1181
- if (typeValidationsLength === 1) {
1182
- const typeValidation = typeValidations[0];
1183
- compiledSchema.$validate = getNamedFunction(
1184
- methodName,
1185
- (data) => {
1186
- if (!typeValidation(data)) {
1187
- return defineTypeError("Invalid type", { data });
1188
- }
1466
+ if (typeFunctions.length === 0) {
1467
+ throw getDefinedErrorFunctionForKey(
1468
+ "type",
1469
+ schema,
1470
+ this.failFast
1471
+ )("Invalid type for schema", { data: schema.type });
1472
+ }
1473
+ let combinedTypeValidator;
1474
+ let typeMethodName = "";
1475
+ if (typeFunctions.length === 1) {
1476
+ typeMethodName = typeNames[0];
1477
+ const singleTypeFn = typeFunctions[0];
1478
+ combinedTypeValidator = (data) => {
1479
+ if (!singleTypeFn(data)) {
1480
+ return defineTypeError("Invalid type", { data });
1189
1481
  }
1190
- );
1191
- } else if (typeValidationsLength > 1) {
1192
- compiledSchema.$validate = getNamedFunction(
1193
- methodName,
1194
- (data) => {
1195
- for (let i = 0; i < typeValidationsLength; i++) {
1196
- if (typeValidations[i](data)) {
1197
- return;
1198
- }
1482
+ };
1483
+ } else {
1484
+ typeMethodName = typeNames.join("_OR_");
1485
+ combinedTypeValidator = (data) => {
1486
+ for (let i = 0; i < typeFunctions.length; i++) {
1487
+ if (typeFunctions[i](data)) {
1488
+ return;
1199
1489
  }
1200
- return defineTypeError("Invalid type", { data });
1201
1490
  }
1491
+ return defineTypeError("Invalid type", { data });
1492
+ };
1493
+ }
1494
+ const typeAdapter = (_s, data) => combinedTypeValidator(data);
1495
+ validators.push({
1496
+ fn: getNamedFunction(typeMethodName, typeAdapter),
1497
+ defineError: defineTypeError
1498
+ });
1499
+ activeNames.push(typeMethodName);
1500
+ }
1501
+ const { type, $id, $ref, $validate, required, ...otherKeys } = schema;
1502
+ const keyOrder = required ? [...Object.keys(otherKeys), "required"] : Object.keys(otherKeys);
1503
+ for (const key of keyOrder) {
1504
+ const keywordFn = this.getKeyword(key);
1505
+ if (keywordFn) {
1506
+ const defineError = getDefinedErrorFunctionForKey(
1507
+ key,
1508
+ schema[key],
1509
+ this.failFast
1202
1510
  );
1511
+ const fnName = keywordFn.name || key;
1512
+ validators.push({
1513
+ fn: keywordFn,
1514
+ defineError
1515
+ });
1516
+ activeNames.push(fnName);
1203
1517
  }
1204
1518
  }
1205
- for (const key of Object.keys(schema)) {
1206
- if (key === "type") {
1207
- compiledSchema.type = schema.type;
1519
+ const literalKeywords = ["enum", "const", "default", "examples"];
1520
+ for (const key of keyOrder) {
1521
+ if (literalKeywords.includes(key)) {
1208
1522
  continue;
1209
1523
  }
1210
- const keywordValidator = this.getKeyword(key);
1211
- if (keywordValidator) {
1212
- const defineError = getDefinedErrorFunctionForKey(key, schema[key]);
1213
- if (compiledSchema.$validate) {
1214
- const prevValidator = compiledSchema.$validate;
1215
- methodName += `_AND_${keywordValidator.name}`;
1216
- compiledSchema.$validate = getNamedFunction(
1217
- methodName,
1218
- (data) => {
1219
- const error = prevValidator(data);
1220
- if (error) {
1221
- return error;
1222
- }
1223
- return keywordValidator(
1224
- compiledSchema,
1225
- data,
1226
- defineError,
1227
- this
1228
- );
1229
- }
1230
- );
1231
- } else {
1232
- methodName = keywordValidator.name;
1233
- compiledSchema.$validate = getNamedFunction(
1234
- methodName,
1235
- (data) => keywordValidator(
1236
- compiledSchema,
1237
- data,
1238
- defineError,
1239
- this
1240
- )
1241
- );
1242
- }
1243
- }
1244
1524
  if (isObject(schema[key])) {
1245
1525
  if (key === "properties") {
1246
1526
  for (const subKey of Object.keys(schema[key])) {
@@ -1254,12 +1534,39 @@ var SchemaShield = class {
1254
1534
  continue;
1255
1535
  }
1256
1536
  if (Array.isArray(schema[key])) {
1257
- compiledSchema[key] = schema[key].map(
1258
- (subSchema, index) => this.isSchemaLike(subSchema) ? this.compileSchema(subSchema) : subSchema
1259
- );
1537
+ for (let i = 0; i < schema[key].length; i++) {
1538
+ if (this.isSchemaLike(schema[key][i])) {
1539
+ compiledSchema[key][i] = this.compileSchema(schema[key][i]);
1540
+ }
1541
+ }
1260
1542
  continue;
1261
1543
  }
1262
- compiledSchema[key] = schema[key];
1544
+ }
1545
+ if (validators.length === 0) {
1546
+ return compiledSchema;
1547
+ }
1548
+ if (validators.length === 1) {
1549
+ const v = validators[0];
1550
+ compiledSchema.$validate = getNamedFunction(
1551
+ activeNames[0],
1552
+ (data) => v.fn(compiledSchema, data, v.defineError, this)
1553
+ );
1554
+ } else {
1555
+ const compositeName = "Validate_" + activeNames.join("_AND_");
1556
+ const masterValidator = (data) => {
1557
+ for (let i = 0; i < validators.length; i++) {
1558
+ const v = validators[i];
1559
+ const error = v.fn(compiledSchema, data, v.defineError, this);
1560
+ if (error) {
1561
+ return error;
1562
+ }
1563
+ }
1564
+ return;
1565
+ };
1566
+ compiledSchema.$validate = getNamedFunction(
1567
+ compositeName,
1568
+ masterValidator
1569
+ );
1263
1570
  }
1264
1571
  return compiledSchema;
1265
1572
  }
@@ -1276,4 +1583,54 @@ var SchemaShield = class {
1276
1583
  }
1277
1584
  return false;
1278
1585
  }
1586
+ linkReferences(root) {
1587
+ const stack = [root];
1588
+ while (stack.length > 0) {
1589
+ const node = stack.pop();
1590
+ if (!node || typeof node !== "object")
1591
+ continue;
1592
+ if (typeof node.$ref === "string" && typeof node.$validate === "function" && node.$validate.name === "Validate_Reference") {
1593
+ const refPath = node.$ref;
1594
+ let target = this.getSchemaRef(refPath);
1595
+ if (typeof target === "undefined") {
1596
+ target = this.getSchemaById(refPath);
1597
+ }
1598
+ if (typeof target === "boolean") {
1599
+ if (target === true) {
1600
+ node.$validate = getNamedFunction("Validate_Ref_True", () => {
1601
+ });
1602
+ } else {
1603
+ const defineError = getDefinedErrorFunctionForKey(
1604
+ "$ref",
1605
+ node,
1606
+ this.failFast
1607
+ );
1608
+ node.$validate = getNamedFunction(
1609
+ "Validate_Ref_False",
1610
+ (_data) => defineError("Value is not valid")
1611
+ );
1612
+ }
1613
+ continue;
1614
+ }
1615
+ if (target && typeof target.$validate === "function") {
1616
+ node.$validate = target.$validate;
1617
+ }
1618
+ }
1619
+ for (const key in node) {
1620
+ const value = node[key];
1621
+ if (!value)
1622
+ continue;
1623
+ if (Array.isArray(value)) {
1624
+ for (let i = 0; i < value.length; i++) {
1625
+ const v = value[i];
1626
+ if (v && typeof v === "object") {
1627
+ stack.push(v);
1628
+ }
1629
+ }
1630
+ } else if (typeof value === "object") {
1631
+ stack.push(value);
1632
+ }
1633
+ }
1634
+ }
1635
+ }
1279
1636
  };