schema-shield 1.0.4 → 1.1.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
@@ -26,7 +26,21 @@ __export(lib_exports, {
26
26
  module.exports = __toCommonJS(lib_exports);
27
27
 
28
28
  // lib/utils/main-utils.ts
29
+ var hasOwnPropertyIntrinsic = Object.prototype.hasOwnProperty;
30
+ var hasOwnPropertyCall = Function.prototype.call.bind(
31
+ hasOwnPropertyIntrinsic
32
+ );
33
+ function definePropertyOrThrow(target, key, descriptor) {
34
+ if (!Reflect.defineProperty(target, key, descriptor)) {
35
+ throw new TypeError(`Cannot define property "${String(key)}"`);
36
+ }
37
+ return target;
38
+ }
39
+ function hasOwn(target, key) {
40
+ return hasOwnPropertyCall(target, key);
41
+ }
29
42
  var ValidationError = class extends Error {
43
+ code;
30
44
  message;
31
45
  item;
32
46
  keyword;
@@ -35,42 +49,61 @@ var ValidationError = class extends Error {
35
49
  instancePath = "";
36
50
  data;
37
51
  schema;
38
- _getCause(pointer = "#", instancePointer = "#") {
39
- let schemaPath = `${pointer}/${this.keyword}`;
40
- let instancePath = `${instancePointer}`;
41
- if (typeof this.item !== "undefined") {
42
- if (typeof this.item === "string" && this.item in this.schema) {
43
- schemaPath += `/${this.item}`;
44
- }
45
- instancePath += `/${this.item}`;
46
- }
47
- this.instancePath = instancePath;
48
- this.schemaPath = schemaPath;
49
- if (!this.cause || !(this.cause instanceof ValidationError)) {
50
- return this;
51
- }
52
- return this.cause._getCause(schemaPath, instancePath);
52
+ constructor(message) {
53
+ super(message);
54
+ this.message = message;
53
55
  }
54
56
  getCause() {
55
- return this._getCause();
56
- }
57
- _getTree() {
58
- const tree = {
59
- message: this.message,
60
- keyword: this.keyword,
61
- item: this.item,
62
- schemaPath: this.schemaPath,
63
- instancePath: this.instancePath,
64
- data: this.data
65
- };
66
- if (this.cause) {
67
- tree.cause = this.cause._getTree();
57
+ let current = this;
58
+ let schemaPointer = "#";
59
+ let instancePointer = "#";
60
+ const seen = /* @__PURE__ */ new Set();
61
+ while (!seen.has(current)) {
62
+ seen.add(current);
63
+ let schemaPath = `${schemaPointer}/${current.keyword}`;
64
+ let instancePath = instancePointer;
65
+ if (typeof current.item !== "undefined") {
66
+ if (typeof current.item === "string" && current.schema && typeof current.schema === "object" && current.item in current.schema) {
67
+ schemaPath += `/${escapeJsonPointerToken(current.item)}`;
68
+ }
69
+ instancePath += `/${escapeJsonPointerToken(current.item)}`;
70
+ }
71
+ current.schemaPath = schemaPath;
72
+ current.instancePath = instancePath;
73
+ if (!(current.cause instanceof ValidationError) || seen.has(current.cause)) {
74
+ return current;
75
+ }
76
+ schemaPointer = schemaPath;
77
+ instancePointer = instancePath;
78
+ current = current.cause;
68
79
  }
69
- return tree;
80
+ return current;
70
81
  }
71
82
  getTree() {
72
83
  this.getCause();
73
- return this._getTree();
84
+ let current = this;
85
+ let root;
86
+ let target;
87
+ const seen = /* @__PURE__ */ new Set();
88
+ while (current && !seen.has(current)) {
89
+ seen.add(current);
90
+ const node = {
91
+ message: current.message,
92
+ keyword: current.keyword,
93
+ item: current.item,
94
+ schemaPath: current.schemaPath,
95
+ instancePath: current.instancePath,
96
+ data: current.data
97
+ };
98
+ if (!root) {
99
+ root = node;
100
+ } else if (target) {
101
+ target.cause = node;
102
+ }
103
+ target = node;
104
+ current = current.cause instanceof ValidationError ? current.cause : void 0;
105
+ }
106
+ return root;
74
107
  }
75
108
  getPath() {
76
109
  const cause = this.getCause();
@@ -90,8 +123,11 @@ function getDefinedErrorFunctionForKey(key, schema, failFast) {
90
123
  KeywordError.schema = schema;
91
124
  const defineError = (message, options = {}) => {
92
125
  KeywordError.message = message;
126
+ KeywordError.code = options.code;
93
127
  KeywordError.item = options.item;
94
- KeywordError.cause = options.cause && options.cause !== true ? options.cause : void 0;
128
+ if (options.cause !== KeywordError) {
129
+ KeywordError.cause = options.cause && options.cause !== true ? options.cause : void 0;
130
+ }
95
131
  KeywordError.data = options.data;
96
132
  return KeywordError;
97
133
  };
@@ -100,11 +136,14 @@ function getDefinedErrorFunctionForKey(key, schema, failFast) {
100
136
  defineError
101
137
  );
102
138
  }
139
+ function escapeJsonPointerToken(value) {
140
+ return String(value).replace(/~/g, "~0").replace(/\//g, "~1");
141
+ }
103
142
  function isCompiledSchema(subSchema) {
104
143
  return !!subSchema && typeof subSchema === "object" && !Array.isArray(subSchema) && "$validate" in subSchema;
105
144
  }
106
145
  function getNamedFunction(name, fn) {
107
- return Object.defineProperty(fn, "name", { value: name });
146
+ return definePropertyOrThrow(fn, "name", { value: name });
108
147
  }
109
148
  function resolvePath(root, path) {
110
149
  if (!path || path === "#") {
@@ -149,7 +188,6 @@ var DURATION_REGEX = /^P(?!$)((\d+Y)?(\d+M)?(\d+W)?(\d+D)?)(T(?=\d)(\d+H)?(\d+M)
149
188
  var URI_REGEX = /^[a-zA-Z][a-zA-Z0-9+\-.]*:[^\s]*$/;
150
189
  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;
151
190
  var HOSTNAME_REGEX = /^[a-z0-9][a-z0-9-]{0,62}(?:\.[a-z0-9][a-z0-9-]{0,62})*[a-z0-9]$/i;
152
- var DATE_REGEX = /^(\d{4})-(\d{2})-(\d{2})$/;
153
191
  var TIME_REGEX = /^([01]\d|2[0-3]):([0-5]\d):([0-5]\d)(\.\d+)?(Z|([+-])([01]\d|2[0-3]):([0-5]\d))$/;
154
192
  var URI_REFERENCE_REGEX = /^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#((?![^#]*\\)[^#]*))?/i;
155
193
  var IRI_REGEX = /^[a-zA-Z][a-zA-Z0-9+\-.]*:[^\s]*$/;
@@ -214,6 +252,22 @@ function isValidIpv4(data) {
214
252
  function isHexCharCode(code) {
215
253
  return code >= 48 && code <= 57 || code >= 65 && code <= 70 || code >= 97 && code <= 102;
216
254
  }
255
+ function hasValidPercentEncoding(data) {
256
+ for (let index = 0; index < data.length; index++) {
257
+ const code = data.charCodeAt(index);
258
+ if (code === 92) {
259
+ return false;
260
+ }
261
+ if (code !== 37) {
262
+ continue;
263
+ }
264
+ if (index + 2 >= data.length || !isHexCharCode(data.charCodeAt(index + 1)) || !isHexCharCode(data.charCodeAt(index + 2))) {
265
+ return false;
266
+ }
267
+ index += 2;
268
+ }
269
+ return true;
270
+ }
217
271
  function isValidIpv6(data) {
218
272
  const length = data.length;
219
273
  if (length === 0) {
@@ -318,7 +372,7 @@ function isValidJsonPointer(data) {
318
372
  }
319
373
  function isValidRelativeJsonPointer(data) {
320
374
  if (data.length === 0) {
321
- return true;
375
+ return false;
322
376
  }
323
377
  let i = 0;
324
378
  while (i < data.length) {
@@ -331,6 +385,9 @@ function isValidRelativeJsonPointer(data) {
331
385
  if (i === 0) {
332
386
  return false;
333
387
  }
388
+ if (i > 1 && data.charCodeAt(0) === 48) {
389
+ return false;
390
+ }
334
391
  if (i === data.length) {
335
392
  return true;
336
393
  }
@@ -457,7 +514,7 @@ var Formats = {
457
514
  return true;
458
515
  },
459
516
  uri(data) {
460
- return URI_REGEX.test(data);
517
+ return URI_REGEX.test(data) && hasValidPercentEncoding(data);
461
518
  },
462
519
  email(data) {
463
520
  return EMAIL_REGEX.test(data);
@@ -472,15 +529,13 @@ var Formats = {
472
529
  return HOSTNAME_REGEX.test(data);
473
530
  },
474
531
  date(data) {
475
- const match = DATE_REGEX.exec(data);
476
- if (!match) {
532
+ if (data.length !== 10 || data.charCodeAt(4) !== 45 || data.charCodeAt(7) !== 45) {
477
533
  return false;
478
534
  }
479
- const [, yearStr, monthStr, dayStr] = match;
480
- const year = Number(yearStr);
481
- const month = Number(monthStr);
482
- const day = Number(dayStr);
483
- if (month < 1 || month > 12) {
535
+ const year = parseFourDigits(data, 0);
536
+ const month = parseTwoDigits(data, 5);
537
+ const day = parseTwoDigits(data, 8);
538
+ if (year < 0 || month < 1 || month > 12) {
484
539
  return false;
485
540
  }
486
541
  if (day < 1) {
@@ -552,10 +607,10 @@ var Types = {
552
607
  return typeof data === "string";
553
608
  },
554
609
  number(data) {
555
- return typeof data === "number";
610
+ return typeof data === "number" && Number.isFinite(data);
556
611
  },
557
612
  integer(data) {
558
- return typeof data === "number" && data % 1 === 0;
613
+ return typeof data === "number" && Number.isFinite(data) && data % 1 === 0;
559
614
  },
560
615
  boolean(data) {
561
616
  return typeof data === "boolean";
@@ -780,7 +835,7 @@ var ArrayKeywords = {
780
835
  let tupleLength = schema._tupleItemsLength;
781
836
  if (tupleLength === void 0) {
782
837
  tupleLength = schema.items.length;
783
- Object.defineProperty(schema, "_tupleItemsLength", {
838
+ definePropertyOrThrow(schema, "_tupleItemsLength", {
784
839
  value: tupleLength,
785
840
  enumerable: false,
786
841
  configurable: false,
@@ -837,13 +892,23 @@ var ArrayKeywords = {
837
892
  }
838
893
  return;
839
894
  }
840
- const primitiveSeen = /* @__PURE__ */ new Set();
895
+ let hasFirstPrimitive = false;
896
+ let firstPrimitive;
897
+ let primitiveSeen;
841
898
  let primitiveArraySignatures;
842
899
  let arrayBuckets;
843
900
  let objectBuckets;
844
901
  for (let i = 0; i < len; i++) {
845
902
  const item = data[i];
846
903
  if (isUniquePrimitive(item)) {
904
+ if (!hasFirstPrimitive) {
905
+ hasFirstPrimitive = true;
906
+ firstPrimitive = item;
907
+ continue;
908
+ }
909
+ if (!primitiveSeen) {
910
+ primitiveSeen = /* @__PURE__ */ new Set([firstPrimitive]);
911
+ }
847
912
  if (primitiveSeen.has(item)) {
848
913
  return defineError("Array items are not unique", { data: item });
849
914
  }
@@ -929,7 +994,7 @@ function isPlainObject(value) {
929
994
  if (!value || typeof value !== "object") {
930
995
  return false;
931
996
  }
932
- const proto = Object.getPrototypeOf(value);
997
+ const proto = Reflect.getPrototypeOf(value);
933
998
  return proto === Object.prototype || proto === null;
934
999
  }
935
1000
  function canUseStructuredClone(value) {
@@ -1024,7 +1089,7 @@ function deepCloneUnfreeze(obj, cloneClassInstances = false, seen = /* @__PURE__
1024
1089
  seen.set(source, clone);
1025
1090
  return clone;
1026
1091
  }
1027
- clone = Object.create(Object.getPrototypeOf(source));
1092
+ clone = Object.create(Reflect.getPrototypeOf(source));
1028
1093
  seen.set(source, clone);
1029
1094
  break;
1030
1095
  }
@@ -1053,7 +1118,7 @@ function deepCloneUnfreeze(obj, cloneClassInstances = false, seen = /* @__PURE__
1053
1118
  seen
1054
1119
  );
1055
1120
  }
1056
- Object.defineProperty(clone, key, descriptor);
1121
+ definePropertyOrThrow(clone, key, descriptor);
1057
1122
  }
1058
1123
  return clone;
1059
1124
  }
@@ -1064,6 +1129,9 @@ var NumberKeywords = {
1064
1129
  if (typeof data !== "number") {
1065
1130
  return;
1066
1131
  }
1132
+ if (!Number.isFinite(data)) {
1133
+ return defineError("Value must be finite", { data });
1134
+ }
1067
1135
  let min = schema.minimum;
1068
1136
  if (typeof schema.exclusiveMinimum === "number") {
1069
1137
  min = schema.exclusiveMinimum + 1e-15;
@@ -1079,6 +1147,9 @@ var NumberKeywords = {
1079
1147
  if (typeof data !== "number") {
1080
1148
  return;
1081
1149
  }
1150
+ if (!Number.isFinite(data)) {
1151
+ return defineError("Value must be finite", { data });
1152
+ }
1082
1153
  let max = schema.maximum;
1083
1154
  if (typeof schema.exclusiveMaximum === "number") {
1084
1155
  max = schema.exclusiveMaximum - 1e-15;
@@ -1094,11 +1165,14 @@ var NumberKeywords = {
1094
1165
  if (typeof data !== "number") {
1095
1166
  return;
1096
1167
  }
1097
- const quotient = data / schema.multipleOf;
1098
- if (!isFinite(quotient)) {
1099
- return;
1168
+ if (!Number.isFinite(data) || !Number.isFinite(schema.multipleOf) || schema.multipleOf <= 0) {
1169
+ return defineError("Value must use a finite positive multipleOf", {
1170
+ data
1171
+ });
1100
1172
  }
1101
- if (!areCloseEnough(quotient, Math.round(quotient))) {
1173
+ const quotient = data / schema.multipleOf;
1174
+ const valid = Number.isFinite(quotient) ? areCloseEnough(quotient, Math.round(quotient)) : data % schema.multipleOf === 0;
1175
+ if (!valid) {
1102
1176
  return defineError("Value is not a multiple of the multipleOf", { data });
1103
1177
  }
1104
1178
  return;
@@ -1187,6 +1261,29 @@ function compilePatternMatcher(pattern) {
1187
1261
 
1188
1262
  // lib/keywords/object-keywords.ts
1189
1263
  var PATTERN_KEY_CACHE_LIMIT = 512;
1264
+ function createApplyPropertyDefaults(replaceEmpty) {
1265
+ return function applyPropertyDefaults2(schema, data, instance) {
1266
+ if (!data || typeof data !== "object" || Array.isArray(data)) {
1267
+ return;
1268
+ }
1269
+ const defaultKeys = schema._defaultKeys;
1270
+ for (let i = 0; i < defaultKeys.length; i++) {
1271
+ const key = defaultKeys[i];
1272
+ const hasOwnValue = hasOwn(data, key);
1273
+ const value = hasOwnValue ? data[key] : void 0;
1274
+ if (hasOwnValue && value !== void 0 && (!replaceEmpty || value !== null && value !== "")) {
1275
+ continue;
1276
+ }
1277
+ instance.setDefault(
1278
+ data,
1279
+ key,
1280
+ deepCloneUnfreeze(schema.properties[key].default)
1281
+ );
1282
+ }
1283
+ };
1284
+ }
1285
+ var applyPropertyDefaults = createApplyPropertyDefaults(false);
1286
+ var applyEmptyPropertyDefaults = createApplyPropertyDefaults(true);
1190
1287
  function getPatternPropertyEntries(schema) {
1191
1288
  let entries = schema._patternPropertyEntries;
1192
1289
  if (entries) {
@@ -1206,7 +1303,7 @@ function getPatternPropertyEntries(schema) {
1206
1303
  match
1207
1304
  };
1208
1305
  }
1209
- Object.defineProperty(schema, "_patternPropertyEntries", {
1306
+ definePropertyOrThrow(schema, "_patternPropertyEntries", {
1210
1307
  value: entries,
1211
1308
  enumerable: false,
1212
1309
  configurable: false,
@@ -1223,7 +1320,7 @@ function getPatternKeyMatchIndexes(schema, key, entries) {
1223
1320
  }
1224
1321
  } else {
1225
1322
  cache = /* @__PURE__ */ new Map();
1226
- Object.defineProperty(schema, "_patternKeyMatchIndexCache", {
1323
+ definePropertyOrThrow(schema, "_patternKeyMatchIndexCache", {
1227
1324
  value: cache,
1228
1325
  enumerable: false,
1229
1326
  configurable: false,
@@ -1248,7 +1345,7 @@ var ObjectKeywords = {
1248
1345
  }
1249
1346
  for (let i = 0; i < schema.required.length; i++) {
1250
1347
  const key = schema.required[i];
1251
- if (!Object.prototype.hasOwnProperty.call(data, key)) {
1348
+ if (!hasOwn(data, key)) {
1252
1349
  return defineError("Required property is missing", {
1253
1350
  item: key,
1254
1351
  data: data[key]
@@ -1257,45 +1354,15 @@ var ObjectKeywords = {
1257
1354
  }
1258
1355
  return;
1259
1356
  },
1260
- properties(schema, data, defineError) {
1357
+ properties(schema, data, defineError, instance) {
1261
1358
  if (!data || typeof data !== "object" || Array.isArray(data)) {
1262
1359
  return;
1263
1360
  }
1264
- let propKeys = schema._propKeys;
1265
- if (!propKeys) {
1266
- propKeys = Object.keys(schema.properties || {});
1267
- Object.defineProperty(schema, "_propKeys", {
1268
- value: propKeys,
1269
- enumerable: false,
1270
- configurable: false,
1271
- writable: false
1272
- });
1273
- }
1274
- let requiredSet = schema._requiredSet;
1275
- if (requiredSet === void 0) {
1276
- requiredSet = Array.isArray(schema.required) ? new Set(schema.required) : null;
1277
- Object.defineProperty(schema, "_requiredSet", {
1278
- value: requiredSet,
1279
- enumerable: false,
1280
- configurable: false,
1281
- writable: false
1282
- });
1283
- }
1361
+ const propKeys = schema._propKeys;
1284
1362
  for (let i = 0; i < propKeys.length; i++) {
1285
1363
  const key = propKeys[i];
1286
1364
  const schemaProp = schema.properties[key];
1287
- if (!Object.prototype.hasOwnProperty.call(data, key)) {
1288
- if (requiredSet && requiredSet.has(key) && schemaProp && typeof schemaProp === "object" && !Array.isArray(schemaProp) && "default" in schemaProp) {
1289
- const error = schemaProp.$validate(schemaProp.default);
1290
- if (error) {
1291
- return defineError("Default property is invalid", {
1292
- item: key,
1293
- cause: error,
1294
- data: schemaProp.default
1295
- });
1296
- }
1297
- data[key] = deepCloneUnfreeze(schemaProp.default);
1298
- }
1365
+ if (!hasOwn(data, key)) {
1299
1366
  continue;
1300
1367
  }
1301
1368
  if (typeof schemaProp === "boolean") {
@@ -1330,7 +1397,7 @@ var ObjectKeywords = {
1330
1397
  return;
1331
1398
  }
1332
1399
  for (const key in data) {
1333
- if (!Object.prototype.hasOwnProperty.call(data, key)) {
1400
+ if (!hasOwn(data, key)) {
1334
1401
  continue;
1335
1402
  }
1336
1403
  const error = validate(data[key]);
@@ -1349,7 +1416,7 @@ var ObjectKeywords = {
1349
1416
  }
1350
1417
  let count = 0;
1351
1418
  for (const key in data) {
1352
- if (!Object.prototype.hasOwnProperty.call(data, key)) {
1419
+ if (!hasOwn(data, key)) {
1353
1420
  continue;
1354
1421
  }
1355
1422
  count++;
@@ -1365,7 +1432,7 @@ var ObjectKeywords = {
1365
1432
  }
1366
1433
  let count = 0;
1367
1434
  for (const key in data) {
1368
- if (!Object.prototype.hasOwnProperty.call(data, key)) {
1435
+ if (!hasOwn(data, key)) {
1369
1436
  continue;
1370
1437
  }
1371
1438
  count++;
@@ -1382,7 +1449,7 @@ var ObjectKeywords = {
1382
1449
  let apValidate = schema._apValidate;
1383
1450
  if (apValidate === void 0) {
1384
1451
  apValidate = isCompiledSchema(schema.additionalProperties) ? schema.additionalProperties.$validate : null;
1385
- Object.defineProperty(schema, "_apValidate", {
1452
+ definePropertyOrThrow(schema, "_apValidate", {
1386
1453
  value: apValidate,
1387
1454
  enumerable: false,
1388
1455
  configurable: false,
@@ -1391,10 +1458,10 @@ var ObjectKeywords = {
1391
1458
  }
1392
1459
  const patternEntries = getPatternPropertyEntries(schema);
1393
1460
  for (const key in data) {
1394
- if (!Object.prototype.hasOwnProperty.call(data, key)) {
1461
+ if (!hasOwn(data, key)) {
1395
1462
  continue;
1396
1463
  }
1397
- if (schema.properties && Object.prototype.hasOwnProperty.call(schema.properties, key)) {
1464
+ if (schema.properties && hasOwn(schema.properties, key)) {
1398
1465
  continue;
1399
1466
  }
1400
1467
  if (patternEntries && patternEntries.length) {
@@ -1430,12 +1497,12 @@ var ObjectKeywords = {
1430
1497
  return;
1431
1498
  }
1432
1499
  for (const key in data) {
1433
- if (!Object.prototype.hasOwnProperty.call(data, key)) {
1500
+ if (!hasOwn(data, key)) {
1434
1501
  continue;
1435
1502
  }
1436
1503
  const matchingIndexes = getPatternKeyMatchIndexes(schema, key, patternEntries);
1437
1504
  if (matchingIndexes.length === 0) {
1438
- if (schema.additionalProperties === false && !(schema.properties && Object.prototype.hasOwnProperty.call(schema.properties, key))) {
1505
+ if (schema.additionalProperties === false && !(schema.properties && hasOwn(schema.properties, key))) {
1439
1506
  return defineError("Additional properties are not allowed", {
1440
1507
  item: key,
1441
1508
  data: data[key]
@@ -1476,7 +1543,7 @@ var ObjectKeywords = {
1476
1543
  if (typeof pn === "boolean") {
1477
1544
  if (pn === false) {
1478
1545
  for (const key in data) {
1479
- if (Object.prototype.hasOwnProperty.call(data, key)) {
1546
+ if (hasOwn(data, key)) {
1480
1547
  return defineError("Properties are not allowed", { data });
1481
1548
  }
1482
1549
  }
@@ -1488,7 +1555,7 @@ var ObjectKeywords = {
1488
1555
  return;
1489
1556
  }
1490
1557
  for (const key in data) {
1491
- if (!Object.prototype.hasOwnProperty.call(data, key)) {
1558
+ if (!hasOwn(data, key)) {
1492
1559
  continue;
1493
1560
  }
1494
1561
  const error = validate(key);
@@ -1587,7 +1654,7 @@ function getBranchEntries(schema, key) {
1587
1654
  for (let i = 0; i < source.length; i++) {
1588
1655
  entries.push(toBranchEntry(source[i]));
1589
1656
  }
1590
- Object.defineProperty(schema, cacheKey, {
1657
+ definePropertyOrThrow(schema, cacheKey, {
1591
1658
  value: entries,
1592
1659
  enumerable: false,
1593
1660
  configurable: false,
@@ -1595,6 +1662,147 @@ function getBranchEntries(schema, key) {
1595
1662
  });
1596
1663
  return entries;
1597
1664
  }
1665
+ function evaluateAllOf(branches, data, defineError) {
1666
+ for (let i = 0; i < branches.length; i++) {
1667
+ const branch = branches[i];
1668
+ if (branch.kind === "validate") {
1669
+ const error = branch.validate(data);
1670
+ if (error) {
1671
+ return defineError("Value is not valid", { cause: error, data });
1672
+ }
1673
+ continue;
1674
+ }
1675
+ if (branch.kind === "alwaysValid") {
1676
+ continue;
1677
+ }
1678
+ if (branch.kind === "alwaysInvalid" || data !== branch.value) {
1679
+ return defineError("Value is not valid", { data });
1680
+ }
1681
+ }
1682
+ }
1683
+ function evaluateAnyOf(branches, data, defineError) {
1684
+ for (let i = 0; i < branches.length; i++) {
1685
+ const branch = branches[i];
1686
+ if (branch.kind === "validate") {
1687
+ if (!branch.validate(data)) {
1688
+ return;
1689
+ }
1690
+ continue;
1691
+ }
1692
+ if (branch.kind === "alwaysValid") {
1693
+ return;
1694
+ }
1695
+ if (branch.kind === "literal" && data === branch.value) {
1696
+ return;
1697
+ }
1698
+ }
1699
+ return defineError("Value is not valid", { data });
1700
+ }
1701
+ function evaluateOneOf(branches, data, defineError) {
1702
+ let validCount = 0;
1703
+ let winnerIndex = -1;
1704
+ for (let i = 0; i < branches.length; i++) {
1705
+ const branch = branches[i];
1706
+ let isValid = false;
1707
+ if (branch.kind === "validate") {
1708
+ isValid = !branch.validate(data);
1709
+ } else if (branch.kind === "alwaysValid") {
1710
+ isValid = true;
1711
+ } else if (branch.kind === "literal") {
1712
+ isValid = data === branch.value;
1713
+ }
1714
+ if (isValid) {
1715
+ validCount++;
1716
+ winnerIndex = i;
1717
+ if (validCount > 1) {
1718
+ return {
1719
+ error: defineError("Value is not valid", { data }),
1720
+ winnerIndex: -1
1721
+ };
1722
+ }
1723
+ }
1724
+ }
1725
+ return {
1726
+ error: validCount === 1 ? void 0 : defineError("Value is not valid", { data }),
1727
+ winnerIndex: validCount === 1 ? winnerIndex : -1
1728
+ };
1729
+ }
1730
+ function createCombinatorValidator(key, schema, defineError, validateSubschema, transactions) {
1731
+ const sourceBranches = getBranchEntries(schema, key);
1732
+ const branches = validateSubschema ? sourceBranches.map(
1733
+ (branch, index) => branch.kind === "validate" ? {
1734
+ kind: "validate",
1735
+ validate: (data) => validateSubschema(schema[key][index], data)
1736
+ } : branch
1737
+ ) : sourceBranches;
1738
+ if (!transactions) {
1739
+ if (key === "allOf") {
1740
+ return (data) => evaluateAllOf(branches, data, defineError);
1741
+ }
1742
+ if (key === "anyOf") {
1743
+ return (data) => evaluateAnyOf(branches, data, defineError);
1744
+ }
1745
+ return (data) => evaluateOneOf(branches, data, defineError).error;
1746
+ }
1747
+ if (key === "allOf") {
1748
+ return (data) => {
1749
+ const savepoint = transactions.savepoint();
1750
+ try {
1751
+ const error = evaluateAllOf(branches, data, defineError);
1752
+ if (error) {
1753
+ transactions.rollback(savepoint);
1754
+ }
1755
+ return error;
1756
+ } catch (error) {
1757
+ transactions.rollback(savepoint);
1758
+ throw error;
1759
+ }
1760
+ };
1761
+ }
1762
+ if (key === "anyOf") {
1763
+ return (data) => evaluateAnyOf(branches, data, defineError);
1764
+ }
1765
+ return (data) => {
1766
+ const savepoint = transactions.savepoint();
1767
+ const defaultsByBranch = [];
1768
+ const isolatedBranches = branches.map(
1769
+ (branch, index) => branch.kind === "validate" ? {
1770
+ kind: "validate",
1771
+ validate: (value) => {
1772
+ const branchSavepoint = transactions.savepoint();
1773
+ const error = branch.validate(value);
1774
+ if (!error) {
1775
+ defaultsByBranch[index] = transactions.capture(branchSavepoint);
1776
+ }
1777
+ return error;
1778
+ }
1779
+ } : branch
1780
+ );
1781
+ try {
1782
+ const result = evaluateOneOf(isolatedBranches, data, defineError);
1783
+ if (result.error) {
1784
+ transactions.rollback(savepoint);
1785
+ return result.error;
1786
+ }
1787
+ transactions.restore(defaultsByBranch[result.winnerIndex] || []);
1788
+ return;
1789
+ } catch (error) {
1790
+ transactions.rollback(savepoint);
1791
+ throw error;
1792
+ }
1793
+ };
1794
+ }
1795
+ function prepareCombinatorEntries(schema) {
1796
+ if (Array.isArray(schema.allOf)) {
1797
+ getBranchEntries(schema, "allOf");
1798
+ }
1799
+ if (Array.isArray(schema.anyOf)) {
1800
+ getBranchEntries(schema, "anyOf");
1801
+ }
1802
+ if (Array.isArray(schema.oneOf)) {
1803
+ getBranchEntries(schema, "oneOf");
1804
+ }
1805
+ }
1598
1806
  var OtherKeywords = {
1599
1807
  enum(schema, data, defineError) {
1600
1808
  let enumCache = schema._enumCache;
@@ -1611,7 +1819,7 @@ var OtherKeywords = {
1611
1819
  }
1612
1820
  }
1613
1821
  enumCache = { primitiveSet, objectValues };
1614
- Object.defineProperty(schema, "_enumCache", {
1822
+ definePropertyOrThrow(schema, "_enumCache", {
1615
1823
  value: enumCache,
1616
1824
  enumerable: false,
1617
1825
  configurable: false,
@@ -1631,137 +1839,13 @@ var OtherKeywords = {
1631
1839
  return defineError("Value is not one of the allowed values", { data });
1632
1840
  },
1633
1841
  allOf(schema, data, defineError) {
1634
- const branches = getBranchEntries(schema, "allOf");
1635
- if (branches.length === 1) {
1636
- const onlyBranch = branches[0];
1637
- if (onlyBranch.kind === "validate") {
1638
- const error = onlyBranch.validate(data);
1639
- if (error) {
1640
- return defineError("Value is not valid", { cause: error, data });
1641
- }
1642
- return;
1643
- }
1644
- if (onlyBranch.kind === "alwaysValid") {
1645
- return;
1646
- }
1647
- if (onlyBranch.kind === "alwaysInvalid") {
1648
- return defineError("Value is not valid", { data });
1649
- }
1650
- if (data !== onlyBranch.value) {
1651
- return defineError("Value is not valid", { data });
1652
- }
1653
- return;
1654
- }
1655
- for (let i = 0; i < branches.length; i++) {
1656
- const branch = branches[i];
1657
- if (branch.kind === "validate") {
1658
- const error = branch.validate(data);
1659
- if (error) {
1660
- return defineError("Value is not valid", { cause: error, data });
1661
- }
1662
- continue;
1663
- }
1664
- if (branch.kind === "alwaysValid") {
1665
- continue;
1666
- }
1667
- if (branch.kind === "alwaysInvalid") {
1668
- return defineError("Value is not valid", { data });
1669
- }
1670
- if (data !== branch.value) {
1671
- return defineError("Value is not valid", { data });
1672
- }
1673
- }
1674
- return;
1842
+ return createCombinatorValidator("allOf", schema, defineError)(data);
1675
1843
  },
1676
1844
  anyOf(schema, data, defineError) {
1677
- const branches = getBranchEntries(schema, "anyOf");
1678
- if (branches.length === 1) {
1679
- const onlyBranch = branches[0];
1680
- if (onlyBranch.kind === "validate") {
1681
- const error = onlyBranch.validate(data);
1682
- if (!error) {
1683
- return;
1684
- }
1685
- return defineError("Value is not valid", { data });
1686
- }
1687
- if (onlyBranch.kind === "alwaysValid") {
1688
- return;
1689
- }
1690
- if (onlyBranch.kind === "alwaysInvalid") {
1691
- return defineError("Value is not valid", { data });
1692
- }
1693
- if (data === onlyBranch.value) {
1694
- return;
1695
- }
1696
- return defineError("Value is not valid", { data });
1697
- }
1698
- for (let i = 0; i < branches.length; i++) {
1699
- const branch = branches[i];
1700
- if (branch.kind === "validate") {
1701
- const error = branch.validate(data);
1702
- if (!error) {
1703
- return;
1704
- }
1705
- continue;
1706
- }
1707
- if (branch.kind === "alwaysValid") {
1708
- return;
1709
- }
1710
- if (branch.kind === "alwaysInvalid") {
1711
- continue;
1712
- }
1713
- if (data === branch.value) {
1714
- return;
1715
- }
1716
- }
1717
- return defineError("Value is not valid", { data });
1845
+ return createCombinatorValidator("anyOf", schema, defineError)(data);
1718
1846
  },
1719
1847
  oneOf(schema, data, defineError) {
1720
- const branches = getBranchEntries(schema, "oneOf");
1721
- if (branches.length === 1) {
1722
- const onlyBranch = branches[0];
1723
- if (onlyBranch.kind === "validate") {
1724
- const error = onlyBranch.validate(data);
1725
- if (!error) {
1726
- return;
1727
- }
1728
- return defineError("Value is not valid", { data });
1729
- }
1730
- if (onlyBranch.kind === "alwaysValid") {
1731
- return;
1732
- }
1733
- if (onlyBranch.kind === "alwaysInvalid") {
1734
- return defineError("Value is not valid", { data });
1735
- }
1736
- if (data === onlyBranch.value) {
1737
- return;
1738
- }
1739
- return defineError("Value is not valid", { data });
1740
- }
1741
- let validCount = 0;
1742
- for (let i = 0; i < branches.length; i++) {
1743
- const branch = branches[i];
1744
- let isValid = false;
1745
- if (branch.kind === "validate") {
1746
- isValid = !branch.validate(data);
1747
- } else if (branch.kind === "alwaysValid") {
1748
- isValid = true;
1749
- } else if (branch.kind === "alwaysInvalid") {
1750
- isValid = false;
1751
- } else {
1752
- isValid = data === branch.value;
1753
- }
1754
- if (isValid) {
1755
- validCount++;
1756
- if (validCount > 1) {
1757
- return defineError("Value is not valid", { data });
1758
- }
1759
- }
1760
- }
1761
- if (validCount === 1) {
1762
- return;
1763
- }
1764
- return defineError("Value is not valid", { data });
1848
+ return createCombinatorValidator("oneOf", schema, defineError)(data);
1765
1849
  },
1766
1850
  const(schema, data, defineError) {
1767
1851
  if (data === schema.const) {
@@ -1821,45 +1905,62 @@ var OtherKeywords = {
1821
1905
  }
1822
1906
  return defineError("Value is not valid", { data });
1823
1907
  },
1824
- $ref(schema, data, defineError, instance) {
1825
- if (schema._resolvedRef) {
1826
- if (schema.$validate !== schema._resolvedRef) {
1827
- schema.$validate = schema._resolvedRef;
1828
- }
1908
+ $ref(schema, data, defineError) {
1909
+ if (typeof schema._resolvedRef === "function") {
1829
1910
  return schema._resolvedRef(data);
1830
1911
  }
1831
- const refPath = schema.$ref;
1832
- let targetSchema = instance.getSchemaRef(refPath);
1833
- if (!targetSchema) {
1834
- targetSchema = instance.getSchemaById(refPath);
1835
- }
1836
- if (!targetSchema) {
1837
- return defineError(`Missing reference: ${refPath}`);
1838
- }
1839
- if (!targetSchema.$validate) {
1840
- return;
1841
- }
1842
- schema._resolvedRef = targetSchema.$validate;
1843
- schema.$validate = schema._resolvedRef;
1844
- return schema._resolvedRef(data);
1912
+ return defineError(`Missing reference: ${schema.$ref}`);
1845
1913
  }
1846
1914
  };
1847
1915
 
1848
1916
  // lib/keywords/string-keywords.ts
1849
1917
  var PATTERN_MATCH_CACHE_LIMIT = 512;
1850
1918
  var FORMAT_RESULT_CACHE_LIMIT = 512;
1919
+ function hasAtLeastCodePoints(value, limit) {
1920
+ let count = 0;
1921
+ for (let index = 0; index < value.length; index++) {
1922
+ const unit = value.charCodeAt(index);
1923
+ if (unit >= 55296 && unit <= 56319 && index + 1 < value.length) {
1924
+ const nextUnit = value.charCodeAt(index + 1);
1925
+ if (nextUnit >= 56320 && nextUnit <= 57343) {
1926
+ index++;
1927
+ }
1928
+ }
1929
+ count++;
1930
+ if (count >= limit) {
1931
+ return true;
1932
+ }
1933
+ }
1934
+ return count >= limit;
1935
+ }
1851
1936
  var StringKeywords = {
1852
1937
  minLength(schema, data, defineError) {
1853
- if (typeof data !== "string" || data.length >= schema.minLength) {
1938
+ if (typeof data !== "string") {
1939
+ return;
1940
+ }
1941
+ const units = data.length;
1942
+ const limit = schema.minLength;
1943
+ if (units < limit) {
1944
+ return defineError("Value is shorter than the minimum length", { data });
1945
+ }
1946
+ if (units - limit >= limit || hasAtLeastCodePoints(data, limit)) {
1854
1947
  return;
1855
1948
  }
1856
1949
  return defineError("Value is shorter than the minimum length", { data });
1857
1950
  },
1858
1951
  maxLength(schema, data, defineError) {
1859
- if (typeof data !== "string" || data.length <= schema.maxLength) {
1952
+ if (typeof data !== "string") {
1953
+ return;
1954
+ }
1955
+ const units = data.length;
1956
+ const limit = schema.maxLength;
1957
+ if (units <= limit) {
1860
1958
  return;
1861
1959
  }
1862
- return defineError("Value is longer than the maximum length", { data });
1960
+ if (units - limit > limit || hasAtLeastCodePoints(data, limit + 1)) {
1961
+ return defineError("Value is longer than the maximum length", { data });
1962
+ }
1963
+ return;
1863
1964
  },
1864
1965
  pattern(schema, data, defineError) {
1865
1966
  if (typeof data !== "string") {
@@ -1871,7 +1972,7 @@ var StringKeywords = {
1871
1972
  try {
1872
1973
  const compiled = compilePatternMatcher(schema.pattern);
1873
1974
  patternMatch = compiled instanceof RegExp ? (value) => compiled.test(value) : compiled;
1874
- Object.defineProperty(schema, "_patternMatch", {
1975
+ definePropertyOrThrow(schema, "_patternMatch", {
1875
1976
  value: patternMatch,
1876
1977
  enumerable: false,
1877
1978
  configurable: false,
@@ -1886,7 +1987,7 @@ var StringKeywords = {
1886
1987
  }
1887
1988
  if (!patternMatchCache) {
1888
1989
  patternMatchCache = /* @__PURE__ */ new Map();
1889
- Object.defineProperty(schema, "_patternMatchCache", {
1990
+ definePropertyOrThrow(schema, "_patternMatchCache", {
1890
1991
  value: patternMatchCache,
1891
1992
  enumerable: false,
1892
1993
  configurable: false,
@@ -1918,7 +2019,7 @@ var StringKeywords = {
1918
2019
  let formatResultCache = schema._formatResultCache;
1919
2020
  if (formatValidate === void 0) {
1920
2021
  formatValidate = instance.getFormat(schema.format);
1921
- Object.defineProperty(schema, "_formatValidate", {
2022
+ definePropertyOrThrow(schema, "_formatValidate", {
1922
2023
  value: formatValidate,
1923
2024
  enumerable: false,
1924
2025
  configurable: false,
@@ -1933,7 +2034,7 @@ var StringKeywords = {
1933
2034
  schema.format,
1934
2035
  formatValidate
1935
2036
  );
1936
- Object.defineProperty(schema, "_formatResultCacheEnabled", {
2037
+ definePropertyOrThrow(schema, "_formatResultCacheEnabled", {
1937
2038
  value: formatResultCacheEnabled,
1938
2039
  enumerable: false,
1939
2040
  configurable: false,
@@ -1948,7 +2049,7 @@ var StringKeywords = {
1948
2049
  }
1949
2050
  if (!formatResultCache) {
1950
2051
  formatResultCache = /* @__PURE__ */ new Map();
1951
- Object.defineProperty(schema, "_formatResultCache", {
2052
+ definePropertyOrThrow(schema, "_formatResultCache", {
1952
2053
  value: formatResultCache,
1953
2054
  enumerable: false,
1954
2055
  configurable: false,
@@ -1981,20 +2082,61 @@ var keywords = {
1981
2082
  };
1982
2083
 
1983
2084
  // lib/index.ts
2085
+ var MAX_COMPILE_DEPTH = 128;
2086
+ var LOCAL_SCHEMA_BASE = "schema-shield://local/root";
2087
+ var FAIL_FAST_TYPE_VALIDATORS = {
2088
+ object: (data) => data !== null && typeof data === "object" && !Array.isArray(data) ? void 0 : true,
2089
+ array: (data) => Array.isArray(data) ? void 0 : true,
2090
+ string: (data) => typeof data === "string" ? void 0 : true,
2091
+ number: (data) => typeof data === "number" && Number.isFinite(data) ? void 0 : true,
2092
+ integer: (data) => typeof data === "number" && Number.isFinite(data) && Number.isInteger(data) ? void 0 : true,
2093
+ boolean: (data) => typeof data === "boolean" ? void 0 : true,
2094
+ null: (data) => data === null ? void 0 : true
2095
+ };
2096
+ function createBuiltinTypeValidator(_type, defineError, fallback) {
2097
+ return (data) => {
2098
+ if (!fallback(data)) {
2099
+ return defineError("Invalid type", { data });
2100
+ }
2101
+ };
2102
+ }
1984
2103
  var SchemaShield = class {
1985
2104
  types = {};
1986
2105
  formats = {};
1987
2106
  keywords = {};
1988
2107
  immutable = false;
2108
+ useDefaults = false;
1989
2109
  rootSchema = null;
1990
- idRegistry = /* @__PURE__ */ new Map();
1991
2110
  failFast = true;
2111
+ maxDepth;
2112
+ validationContexts = [];
2113
+ compileCache = /* @__PURE__ */ new WeakMap();
2114
+ compilingRequiresContext = false;
2115
+ compilingMutableSchemas = /* @__PURE__ */ new WeakSet();
1992
2116
  constructor({
1993
2117
  immutable = false,
1994
- failFast = true
2118
+ failFast = true,
2119
+ maxDepth = 128,
2120
+ useDefaults = false
1995
2121
  } = {}) {
2122
+ if (!Number.isInteger(maxDepth) || maxDepth < 1 || maxDepth > 256) {
2123
+ const error = new ValidationError("maxDepth must be an integer from 1 to 256");
2124
+ error.code = "INVALID_MAX_DEPTH";
2125
+ error.keyword = "maxDepth";
2126
+ throw error;
2127
+ }
2128
+ if (useDefaults !== false && useDefaults !== true && useDefaults !== "empty") {
2129
+ const error = new ValidationError(
2130
+ 'useDefaults must be false, true, or "empty"'
2131
+ );
2132
+ error.code = "INVALID_USE_DEFAULTS";
2133
+ error.keyword = "useDefaults";
2134
+ throw error;
2135
+ }
1996
2136
  this.immutable = immutable;
1997
2137
  this.failFast = failFast;
2138
+ this.maxDepth = maxDepth;
2139
+ this.useDefaults = useDefaults;
1998
2140
  for (const [type, validator] of Object.entries(Types)) {
1999
2141
  if (validator) {
2000
2142
  this.addType(type, validator);
@@ -2009,6 +2151,50 @@ var SchemaShield = class {
2009
2151
  }
2010
2152
  }
2011
2153
  }
2154
+ setDefault(target, key, value) {
2155
+ const context = this.validationContexts[this.validationContexts.length - 1];
2156
+ if (context) {
2157
+ context.defaults.push({
2158
+ target,
2159
+ key,
2160
+ descriptor: Reflect.getOwnPropertyDescriptor(target, key)
2161
+ });
2162
+ }
2163
+ definePropertyOrThrow(target, key, {
2164
+ value,
2165
+ enumerable: true,
2166
+ configurable: true,
2167
+ writable: true
2168
+ });
2169
+ }
2170
+ #defaultSavepoint() {
2171
+ const context = this.validationContexts[this.validationContexts.length - 1];
2172
+ return context ? context.defaults.length : 0;
2173
+ }
2174
+ #rollbackDefaultSavepoint(savepoint) {
2175
+ const context = this.validationContexts[this.validationContexts.length - 1];
2176
+ if (context) {
2177
+ this.rollbackDefaults(context, savepoint);
2178
+ }
2179
+ }
2180
+ #captureDefaultSavepoint(savepoint) {
2181
+ const context = this.validationContexts[this.validationContexts.length - 1];
2182
+ if (!context || context.defaults.length === savepoint) {
2183
+ return [];
2184
+ }
2185
+ const mutations = context.defaults.slice(savepoint).map((entry) => ({
2186
+ ...entry,
2187
+ value: entry.target[entry.key]
2188
+ }));
2189
+ this.rollbackDefaults(context, savepoint);
2190
+ return mutations;
2191
+ }
2192
+ #restoreDefaults(mutations) {
2193
+ for (let index = 0; index < mutations.length; index++) {
2194
+ const mutation = mutations[index];
2195
+ this.setDefault(mutation.target, mutation.key, mutation.value);
2196
+ }
2197
+ }
2012
2198
  addType(name, validator, overwrite = false) {
2013
2199
  if (this.types[name] && !overwrite) {
2014
2200
  throw new ValidationError(`Type "${name}" already exists`);
@@ -2046,14 +2232,465 @@ var SchemaShield = class {
2046
2232
  return resolvePath(this.rootSchema, path);
2047
2233
  }
2048
2234
  getSchemaById(id) {
2049
- return this.idRegistry.get(id);
2235
+ if (!this.rootSchema) {
2236
+ return;
2237
+ }
2238
+ const stack = [this.rootSchema];
2239
+ const seen = /* @__PURE__ */ new WeakSet();
2240
+ while (stack.length > 0) {
2241
+ const node = stack.pop();
2242
+ if (seen.has(node)) {
2243
+ continue;
2244
+ }
2245
+ seen.add(node);
2246
+ if (node.$id === id) {
2247
+ return node;
2248
+ }
2249
+ const children = this.schemaChildren(node);
2250
+ for (let index = 0; index < children.length; index++) {
2251
+ stack.push(children[index]);
2252
+ }
2253
+ }
2254
+ return;
2255
+ }
2256
+ depthError(message = "Maximum schema depth exceeded") {
2257
+ if (this.failFast) {
2258
+ return true;
2259
+ }
2260
+ const error = new ValidationError(message);
2261
+ error.code = "MAX_DEPTH_EXCEEDED";
2262
+ error.keyword = "maxDepth";
2263
+ return error;
2264
+ }
2265
+ schemaChildEntries(schema) {
2266
+ const children = [];
2267
+ const mapKeys = [
2268
+ "properties",
2269
+ "patternProperties",
2270
+ "definitions",
2271
+ "$defs",
2272
+ "dependencies"
2273
+ ];
2274
+ const arrayKeys = ["allOf", "anyOf", "oneOf", "items"];
2275
+ const singleKeys = [
2276
+ "items",
2277
+ "additionalItems",
2278
+ "additionalProperties",
2279
+ "contains",
2280
+ "propertyNames",
2281
+ "values",
2282
+ "elements",
2283
+ "not",
2284
+ "if",
2285
+ "then",
2286
+ "else"
2287
+ ];
2288
+ for (const key of mapKeys) {
2289
+ const value = schema[key];
2290
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
2291
+ continue;
2292
+ }
2293
+ for (const childKey of Object.keys(value)) {
2294
+ const child = value[childKey];
2295
+ if (child && typeof child === "object") {
2296
+ children.push({
2297
+ value: child,
2298
+ pointer: `/${this.escapePointerToken(key)}/${this.escapePointerToken(
2299
+ childKey
2300
+ )}`
2301
+ });
2302
+ }
2303
+ }
2304
+ }
2305
+ for (const key of arrayKeys) {
2306
+ const value = schema[key];
2307
+ if (!Array.isArray(value)) {
2308
+ continue;
2309
+ }
2310
+ for (let index = 0; index < value.length; index++) {
2311
+ const child = value[index];
2312
+ if (child && typeof child === "object") {
2313
+ children.push({
2314
+ value: child,
2315
+ pointer: `/${this.escapePointerToken(key)}/${index}`
2316
+ });
2317
+ }
2318
+ }
2319
+ }
2320
+ for (const key of singleKeys) {
2321
+ const value = schema[key];
2322
+ if (value && typeof value === "object" && !Array.isArray(value)) {
2323
+ children.push({
2324
+ value,
2325
+ pointer: `/${this.escapePointerToken(key)}`
2326
+ });
2327
+ }
2328
+ }
2329
+ for (const key of Object.keys(schema)) {
2330
+ if (key === "enum" || key === "const" || key === "default" || key === "examples" || mapKeys.includes(key) || arrayKeys.includes(key) || singleKeys.includes(key)) {
2331
+ continue;
2332
+ }
2333
+ const keyword = this.getKeyword(key);
2334
+ const value = schema[key];
2335
+ if (keyword && keyword !== keywords[key] && value && typeof value === "object") {
2336
+ children.push({
2337
+ value,
2338
+ pointer: `/${this.escapePointerToken(key)}`
2339
+ });
2340
+ }
2341
+ }
2342
+ return children;
2343
+ }
2344
+ schemaChildren(schema) {
2345
+ return this.schemaChildEntries(schema).map((entry) => entry.value);
2346
+ }
2347
+ registrySubschemaEntries(schema) {
2348
+ const children = [];
2349
+ const mapKeys = ["properties", "patternProperties", "definitions"];
2350
+ const arrayKeys = ["allOf", "anyOf", "oneOf", "items"];
2351
+ const singleKeys = [
2352
+ "items",
2353
+ "additionalItems",
2354
+ "additionalProperties",
2355
+ "contains",
2356
+ "propertyNames",
2357
+ "not",
2358
+ "if",
2359
+ "then",
2360
+ "else"
2361
+ ];
2362
+ for (const key of mapKeys) {
2363
+ const value = schema[key];
2364
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
2365
+ continue;
2366
+ }
2367
+ for (const childKey of Object.keys(value)) {
2368
+ const child = value[childKey];
2369
+ if (child && typeof child === "object" && !Array.isArray(child)) {
2370
+ children.push({
2371
+ value: child,
2372
+ pointer: `/${this.escapePointerToken(key)}/${this.escapePointerToken(
2373
+ childKey
2374
+ )}`
2375
+ });
2376
+ }
2377
+ }
2378
+ }
2379
+ const dependencies = schema.dependencies;
2380
+ if (dependencies && typeof dependencies === "object" && !Array.isArray(dependencies)) {
2381
+ for (const dependencyKey of Object.keys(dependencies)) {
2382
+ const child = dependencies[dependencyKey];
2383
+ if (child && typeof child === "object" && !Array.isArray(child)) {
2384
+ children.push({
2385
+ value: child,
2386
+ pointer: `/dependencies/${this.escapePointerToken(dependencyKey)}`
2387
+ });
2388
+ }
2389
+ }
2390
+ }
2391
+ for (const key of arrayKeys) {
2392
+ const value = schema[key];
2393
+ if (!Array.isArray(value)) {
2394
+ continue;
2395
+ }
2396
+ for (let index = 0; index < value.length; index++) {
2397
+ const child = value[index];
2398
+ if (child && typeof child === "object" && !Array.isArray(child)) {
2399
+ children.push({
2400
+ value: child,
2401
+ pointer: `/${this.escapePointerToken(key)}/${index}`
2402
+ });
2403
+ }
2404
+ }
2405
+ }
2406
+ for (const key of singleKeys) {
2407
+ const value = schema[key];
2408
+ if (value && typeof value === "object" && !Array.isArray(value)) {
2409
+ children.push({
2410
+ value,
2411
+ pointer: `/${this.escapePointerToken(key)}`
2412
+ });
2413
+ }
2414
+ }
2415
+ return children;
2416
+ }
2417
+ escapePointerToken(value) {
2418
+ return value.replace(/~/g, "~0").replace(/\//g, "~1");
2419
+ }
2420
+ resolveUri(reference, baseUri, keyword) {
2421
+ try {
2422
+ return new URL(reference, baseUri).href;
2423
+ } catch {
2424
+ const error = new ValidationError(`Invalid ${keyword} URI: ${reference}`);
2425
+ error.code = keyword === "$id" ? "INVALID_SCHEMA_ID" : "REFERENCE_NOT_FOUND";
2426
+ error.keyword = keyword;
2427
+ throw error;
2428
+ }
2429
+ }
2430
+ resourceUri(uri) {
2431
+ const hashIndex = uri.indexOf("#");
2432
+ return hashIndex === -1 ? uri : uri.slice(0, hashIndex);
2433
+ }
2434
+ buildReferenceRegistry(schema) {
2435
+ const aliases = /* @__PURE__ */ new Map();
2436
+ const positions = [];
2437
+ const positionsByNode = /* @__PURE__ */ new WeakMap();
2438
+ if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
2439
+ return Object.freeze({
2440
+ aliases,
2441
+ positions: Object.freeze(positions),
2442
+ positionsByNode
2443
+ });
2444
+ }
2445
+ const register = (uri, node) => {
2446
+ const existing = aliases.get(uri);
2447
+ if (existing && existing !== node) {
2448
+ const error = new ValidationError(`Duplicate schema identity: ${uri}`);
2449
+ error.code = "DUPLICATE_SCHEMA_ID";
2450
+ error.keyword = "$id";
2451
+ throw error;
2452
+ }
2453
+ aliases.set(uri, node);
2454
+ };
2455
+ register(LOCAL_SCHEMA_BASE, schema);
2456
+ const visited = /* @__PURE__ */ new WeakSet();
2457
+ const stack = [
2458
+ {
2459
+ node: schema,
2460
+ inheritedBase: LOCAL_SCHEMA_BASE,
2461
+ resourceRoot: schema,
2462
+ pointer: "#"
2463
+ }
2464
+ ];
2465
+ while (stack.length > 0) {
2466
+ const entry = stack.pop();
2467
+ if (visited.has(entry.node)) {
2468
+ continue;
2469
+ }
2470
+ visited.add(entry.node);
2471
+ let baseUri = entry.inheritedBase;
2472
+ let resourceRoot = entry.resourceRoot;
2473
+ if (typeof entry.node.$id === "string" && !("$ref" in entry.node)) {
2474
+ baseUri = this.resolveUri(entry.node.$id, entry.inheritedBase, "$id");
2475
+ register(baseUri, entry.node);
2476
+ if (baseUri.indexOf("#") === -1 || baseUri.endsWith("#")) {
2477
+ resourceRoot = entry.node;
2478
+ register(this.resourceUri(baseUri), entry.node);
2479
+ }
2480
+ }
2481
+ const position = {
2482
+ source: entry.node,
2483
+ baseUri,
2484
+ resourceRoot,
2485
+ pointer: entry.pointer
2486
+ };
2487
+ positions.push(position);
2488
+ positionsByNode.set(entry.node, position);
2489
+ const children = this.registrySubschemaEntries(entry.node);
2490
+ for (let index = children.length - 1; index >= 0; index--) {
2491
+ const child = children[index];
2492
+ if (Array.isArray(child.value)) {
2493
+ continue;
2494
+ }
2495
+ stack.push({
2496
+ node: child.value,
2497
+ inheritedBase: baseUri,
2498
+ resourceRoot,
2499
+ pointer: `${entry.pointer}${child.pointer}`
2500
+ });
2501
+ }
2502
+ }
2503
+ return Object.freeze({
2504
+ aliases,
2505
+ positions: Object.freeze(positions),
2506
+ positionsByNode
2507
+ });
2508
+ }
2509
+ resolveReferenceSource(ref, position, registry) {
2510
+ const resolvedUri = this.resolveUri(ref, position.baseUri, "$ref");
2511
+ const exactTarget = registry.aliases.get(resolvedUri);
2512
+ if (exactTarget) {
2513
+ return exactTarget;
2514
+ }
2515
+ const resourceRoot = registry.aliases.get(this.resourceUri(resolvedUri));
2516
+ if (!resourceRoot) {
2517
+ return;
2518
+ }
2519
+ const hashIndex = resolvedUri.indexOf("#");
2520
+ const fragment = hashIndex === -1 ? "" : resolvedUri.slice(hashIndex + 1);
2521
+ if (fragment.length === 0) {
2522
+ return resourceRoot;
2523
+ }
2524
+ if (fragment.startsWith("/")) {
2525
+ return resolvePath(resourceRoot, `#${fragment}`);
2526
+ }
2527
+ return;
2528
+ }
2529
+ analyzeSchema(schema, registry) {
2530
+ if (!schema || typeof schema !== "object") {
2531
+ return {
2532
+ requiresDepthGuard: false,
2533
+ requiresMutationJournal: false,
2534
+ mutableSchemas: /* @__PURE__ */ new WeakSet()
2535
+ };
2536
+ }
2537
+ const visiting = /* @__PURE__ */ new WeakSet();
2538
+ const visited = /* @__PURE__ */ new WeakSet();
2539
+ const stack = [{ value: schema, depth: 0, exit: false }];
2540
+ let requiresDepthGuard = false;
2541
+ let requiresMutationJournal = false;
2542
+ while (stack.length > 0) {
2543
+ const entry = stack.pop();
2544
+ if (entry.exit) {
2545
+ visiting.delete(entry.value);
2546
+ visited.add(entry.value);
2547
+ continue;
2548
+ }
2549
+ if (visited.has(entry.value)) {
2550
+ continue;
2551
+ }
2552
+ if (visiting.has(entry.value)) {
2553
+ const error = new ValidationError("Cyclic schema graph is not supported");
2554
+ error.code = "CYCLIC_SCHEMA_GRAPH";
2555
+ error.keyword = "compile";
2556
+ throw error;
2557
+ }
2558
+ if (entry.depth > MAX_COMPILE_DEPTH) {
2559
+ const error = new ValidationError("Maximum compile depth exceeded");
2560
+ error.code = "MAX_COMPILE_DEPTH_EXCEEDED";
2561
+ error.keyword = "compile";
2562
+ throw error;
2563
+ }
2564
+ if (entry.depth > this.maxDepth) {
2565
+ requiresDepthGuard = true;
2566
+ }
2567
+ visiting.add(entry.value);
2568
+ stack.push({ ...entry, exit: true });
2569
+ if (this.useDefaults !== false && this.hasPropertyDefaults(entry.value)) {
2570
+ requiresMutationJournal = true;
2571
+ }
2572
+ for (const key of Object.keys(entry.value)) {
2573
+ const keyword = this.getKeyword(key);
2574
+ if (keyword && keyword !== keywords[key]) {
2575
+ requiresDepthGuard = true;
2576
+ requiresMutationJournal = true;
2577
+ }
2578
+ }
2579
+ const children = this.schemaChildren(entry.value);
2580
+ for (let index = children.length - 1; index >= 0; index--) {
2581
+ stack.push({
2582
+ value: children[index],
2583
+ depth: entry.depth + 1,
2584
+ exit: false
2585
+ });
2586
+ }
2587
+ }
2588
+ const semanticState = /* @__PURE__ */ new WeakMap();
2589
+ const semanticStack = [{ value: schema, exit: false }];
2590
+ while (semanticStack.length > 0 && !requiresDepthGuard) {
2591
+ const entry = semanticStack.pop();
2592
+ if (entry.exit) {
2593
+ semanticState.set(entry.value, 2);
2594
+ continue;
2595
+ }
2596
+ const state = semanticState.get(entry.value);
2597
+ if (state === 1) {
2598
+ requiresDepthGuard = true;
2599
+ break;
2600
+ }
2601
+ if (state === 2) {
2602
+ continue;
2603
+ }
2604
+ semanticState.set(entry.value, 1);
2605
+ semanticStack.push({ value: entry.value, exit: true });
2606
+ const children = this.schemaChildren(entry.value);
2607
+ if (typeof entry.value.$ref === "string" && this.getKeyword("$ref") === keywords.$ref) {
2608
+ const position = registry.positionsByNode.get(entry.value);
2609
+ const target = position ? this.resolveReferenceSource(entry.value.$ref, position, registry) : void 0;
2610
+ if (target && typeof target === "object") {
2611
+ children.push(target);
2612
+ }
2613
+ }
2614
+ for (let index = children.length - 1; index >= 0; index--) {
2615
+ semanticStack.push({
2616
+ value: children[index],
2617
+ exit: false
2618
+ });
2619
+ }
2620
+ }
2621
+ const mutableSchemas = /* @__PURE__ */ new WeakSet();
2622
+ if (requiresMutationJournal) {
2623
+ const mutationStack = [{ value: schema, exit: false }];
2624
+ const mutationVisited = /* @__PURE__ */ new WeakSet();
2625
+ while (mutationStack.length > 0) {
2626
+ const entry = mutationStack.pop();
2627
+ if (entry.exit) {
2628
+ const children2 = this.schemaChildren(entry.value);
2629
+ const hasCustomKeyword = Object.keys(entry.value).some((key) => {
2630
+ const keyword = this.getKeyword(key);
2631
+ return !!keyword && keyword !== keywords[key];
2632
+ });
2633
+ if (this.useDefaults !== false && this.hasPropertyDefaults(entry.value) || hasCustomKeyword || typeof entry.value.$ref === "string" || children2.some((child) => mutableSchemas.has(child))) {
2634
+ mutableSchemas.add(entry.value);
2635
+ }
2636
+ continue;
2637
+ }
2638
+ if (mutationVisited.has(entry.value)) {
2639
+ continue;
2640
+ }
2641
+ mutationVisited.add(entry.value);
2642
+ mutationStack.push({ value: entry.value, exit: true });
2643
+ const children = this.schemaChildren(entry.value);
2644
+ for (let index = children.length - 1; index >= 0; index--) {
2645
+ mutationStack.push({
2646
+ value: children[index],
2647
+ exit: false
2648
+ });
2649
+ }
2650
+ }
2651
+ }
2652
+ return { requiresDepthGuard, requiresMutationJournal, mutableSchemas };
2050
2653
  }
2051
2654
  compile(schema) {
2052
- this.idRegistry.clear();
2655
+ const prepared = this.prepareSchema(schema);
2656
+ const compiledSchema = prepared.compiledSchema;
2657
+ if (!prepared.requiresDepthGuard && !prepared.requiresMutationJournal) {
2658
+ const directValidate = compiledSchema.$validate;
2659
+ const validate = this.immutable ? (data) => {
2660
+ const clonedData = deepCloneUnfreeze(data);
2661
+ const error = directValidate(clonedData);
2662
+ return error ? { data: clonedData, error, valid: false } : { data: clonedData, error: null, valid: true };
2663
+ } : (data) => {
2664
+ const error = directValidate(data);
2665
+ return error ? { data, error, valid: false } : { data, error: null, valid: true };
2666
+ };
2667
+ validate.compiledSchema = compiledSchema;
2668
+ return validate;
2669
+ }
2670
+ return this.createGuardedValidator(compiledSchema, prepared.depthGuardState);
2671
+ }
2672
+ prepareSchema(schema) {
2673
+ const referenceRegistry = this.buildReferenceRegistry(schema);
2674
+ const analysis = this.analyzeSchema(schema, referenceRegistry);
2675
+ this.compileCache = /* @__PURE__ */ new WeakMap();
2676
+ this.compilingRequiresContext = analysis.requiresDepthGuard || analysis.requiresMutationJournal;
2677
+ this.compilingMutableSchemas = analysis.mutableSchemas;
2053
2678
  const compiledSchema = this.compileSchema(schema);
2054
2679
  this.rootSchema = compiledSchema;
2680
+ let depthGuardState = null;
2681
+ if (analysis.requiresDepthGuard) {
2682
+ depthGuardState = this.installDepthGuards(compiledSchema);
2683
+ definePropertyOrThrow(compiledSchema, "_requiresDepthGuard", {
2684
+ value: true,
2685
+ enumerable: false,
2686
+ configurable: false,
2687
+ writable: false
2688
+ });
2689
+ } else if (analysis.requiresMutationJournal) {
2690
+ depthGuardState = { context: null };
2691
+ }
2055
2692
  if (compiledSchema._hasRef === true) {
2056
- this.linkReferences(compiledSchema);
2693
+ this.linkReferences(referenceRegistry);
2057
2694
  }
2058
2695
  if (!compiledSchema.$validate) {
2059
2696
  if (schema === false) {
@@ -2082,14 +2719,53 @@ var SchemaShield = class {
2082
2719
  );
2083
2720
  }
2084
2721
  }
2722
+ return {
2723
+ compiledSchema,
2724
+ requiresDepthGuard: analysis.requiresDepthGuard,
2725
+ requiresMutationJournal: analysis.requiresMutationJournal,
2726
+ depthGuardState
2727
+ };
2728
+ }
2729
+ createGuardedValidator(compiledSchema, depthGuardState) {
2730
+ const reusableContext = {
2731
+ active: false,
2732
+ depth: -1,
2733
+ depthExceeded: false,
2734
+ defaults: []
2735
+ };
2085
2736
  const validate = (data) => {
2086
2737
  this.rootSchema = compiledSchema;
2087
- const clonedData = this.immutable ? deepCloneUnfreeze(data) : data;
2088
- const res = compiledSchema.$validate(clonedData);
2089
- if (res) {
2090
- return { data: clonedData, error: res, valid: false };
2738
+ const context = reusableContext.active ? {
2739
+ active: false,
2740
+ depth: -1,
2741
+ depthExceeded: false,
2742
+ defaults: []
2743
+ } : reusableContext;
2744
+ context.active = true;
2745
+ context.depth = -1;
2746
+ context.depthExceeded = false;
2747
+ delete context.depthError;
2748
+ context.defaults.length = 0;
2749
+ this.validationContexts.push(context);
2750
+ const priorContext = depthGuardState.context;
2751
+ depthGuardState.context = context;
2752
+ let clonedData = data;
2753
+ try {
2754
+ clonedData = this.immutable ? deepCloneUnfreeze(data) : data;
2755
+ let error = compiledSchema.$validate(clonedData);
2756
+ if (this.isDepthError(error)) {
2757
+ this.rollbackDefaults(context, 0);
2758
+ error = context.depthError || this.depthError();
2759
+ }
2760
+ return error ? { data: clonedData, error, valid: false } : { data: clonedData, error: null, valid: true };
2761
+ } catch (error) {
2762
+ this.rollbackDefaults(context, 0);
2763
+ throw error;
2764
+ } finally {
2765
+ depthGuardState.context = priorContext;
2766
+ this.validationContexts.pop();
2767
+ context.active = false;
2091
2768
  }
2092
- return { data: clonedData, error: null, valid: true };
2093
2769
  };
2094
2770
  validate.compiledSchema = compiledSchema;
2095
2771
  return validate;
@@ -2192,7 +2868,7 @@ var SchemaShield = class {
2192
2868
  if (schema._hasRef === true) {
2193
2869
  return;
2194
2870
  }
2195
- Object.defineProperty(schema, "_hasRef", {
2871
+ definePropertyOrThrow(schema, "_hasRef", {
2196
2872
  value: true,
2197
2873
  enumerable: false,
2198
2874
  configurable: false,
@@ -2250,15 +2926,15 @@ var SchemaShield = class {
2250
2926
  return false;
2251
2927
  }
2252
2928
  }
2253
- hasRequiredDefaults(schema) {
2929
+ hasPropertyDefaults(schema) {
2254
2930
  const properties = schema.properties;
2255
2931
  if (!this.isPlainObject(properties)) {
2256
2932
  return false;
2257
2933
  }
2258
- const keys = Object.keys(properties);
2259
- for (let i = 0; i < keys.length; i++) {
2260
- const subSchema = properties[keys[i]];
2261
- if (this.isPlainObject(subSchema) && "default" in subSchema) {
2934
+ const propertyKeys = Object.keys(properties);
2935
+ for (let i = 0; i < propertyKeys.length; i++) {
2936
+ const subSchema = properties[propertyKeys[i]];
2937
+ if (this.isPlainObject(subSchema) && hasOwn(subSchema, "default")) {
2262
2938
  return true;
2263
2939
  }
2264
2940
  }
@@ -2267,24 +2943,134 @@ var SchemaShield = class {
2267
2943
  isDefaultTypeValidator(type, validator) {
2268
2944
  return Types[type] === validator;
2269
2945
  }
2270
- compileSchema(schema) {
2271
- if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
2272
- if (schema === true) {
2273
- schema = { anyOf: [{}] };
2274
- } else if (schema === false) {
2275
- schema = { oneOf: [] };
2946
+ rollbackDefaults(context, start) {
2947
+ for (let index = context.defaults.length - 1; index >= start; index--) {
2948
+ const entry = context.defaults[index];
2949
+ if (entry.descriptor) {
2950
+ definePropertyOrThrow(entry.target, entry.key, entry.descriptor);
2276
2951
  } else {
2277
- schema = { oneOf: [schema] };
2952
+ delete entry.target[entry.key];
2953
+ }
2954
+ }
2955
+ context.defaults.length = start;
2956
+ }
2957
+ isDepthError(error) {
2958
+ const context = this.validationContexts[this.validationContexts.length - 1];
2959
+ if (context?.depthExceeded) {
2960
+ return true;
2961
+ }
2962
+ if (!(error instanceof ValidationError)) {
2963
+ return false;
2964
+ }
2965
+ return error.getCause().code === "MAX_DEPTH_EXCEEDED";
2966
+ }
2967
+ validateSubschema(schema, data) {
2968
+ if (!schema || typeof schema.$validate !== "function") {
2969
+ return;
2970
+ }
2971
+ const context = this.validationContexts[this.validationContexts.length - 1];
2972
+ const savepoint = context?.defaults.length || 0;
2973
+ try {
2974
+ const error = schema.$validate(data);
2975
+ if (error && context) {
2976
+ this.rollbackDefaults(context, savepoint);
2977
+ }
2978
+ return error;
2979
+ } catch (error) {
2980
+ if (context) {
2981
+ this.rollbackDefaults(context, savepoint);
2982
+ }
2983
+ throw error;
2984
+ }
2985
+ }
2986
+ installDepthGuards(root) {
2987
+ const state = { context: null };
2988
+ const stack = [root];
2989
+ const seen = /* @__PURE__ */ new WeakSet();
2990
+ while (stack.length > 0) {
2991
+ const schema = stack.pop();
2992
+ if (!schema || typeof schema !== "object" || seen.has(schema)) {
2993
+ continue;
2994
+ }
2995
+ seen.add(schema);
2996
+ if (typeof schema.$validate === "function") {
2997
+ const directValidate = schema.$validate;
2998
+ schema.$validate = getNamedFunction(directValidate.name, (data) => {
2999
+ const context = state.context;
3000
+ if (!context) {
3001
+ return directValidate(data);
3002
+ }
3003
+ const nextDepth = context.depth + 1;
3004
+ if (nextDepth > this.maxDepth) {
3005
+ context.depthExceeded = true;
3006
+ if (!context.depthError) {
3007
+ context.depthError = this.depthError();
3008
+ }
3009
+ return context.depthError;
3010
+ }
3011
+ context.depth = nextDepth;
3012
+ try {
3013
+ return directValidate(data);
3014
+ } finally {
3015
+ context.depth--;
3016
+ }
3017
+ });
3018
+ }
3019
+ const children = this.schemaChildren(schema);
3020
+ for (const child of children) {
3021
+ stack.push(child);
2278
3022
  }
2279
3023
  }
3024
+ return state;
3025
+ }
3026
+ compileSchema(schema) {
3027
+ if (schema === true) {
3028
+ return {
3029
+ $validate: getNamedFunction("Validate_True", () => {
3030
+ })
3031
+ };
3032
+ }
3033
+ if (schema === false) {
3034
+ const compiledFalse = {};
3035
+ const defineError = getDefinedErrorFunctionForKey(
3036
+ "oneOf",
3037
+ compiledFalse,
3038
+ this.failFast
3039
+ );
3040
+ compiledFalse.$validate = getNamedFunction(
3041
+ "Validate_False",
3042
+ (data) => defineError("Value is not valid", { data })
3043
+ );
3044
+ return compiledFalse;
3045
+ }
3046
+ const sourceSchema = schema && typeof schema === "object" && !Array.isArray(schema) ? schema : null;
3047
+ const schemaCanApplyDefaults = sourceSchema !== null && this.compilingMutableSchemas.has(sourceSchema);
3048
+ if (sourceSchema) {
3049
+ const cached = this.compileCache.get(sourceSchema);
3050
+ if (cached) {
3051
+ return cached;
3052
+ }
3053
+ }
3054
+ if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
3055
+ schema = { oneOf: [schema] };
3056
+ }
2280
3057
  schema = this.normalizeSchemaForCompile(schema);
2281
3058
  const compiledSchema = deepCloneUnfreeze(
2282
3059
  schema
2283
3060
  );
2284
- let schemaHasRef = false;
2285
- if (typeof schema.$id === "string") {
2286
- this.idRegistry.set(schema.$id, compiledSchema);
3061
+ if (sourceSchema) {
3062
+ this.compileCache.set(sourceSchema, compiledSchema);
2287
3063
  }
3064
+ if (schemaCanApplyDefaults) {
3065
+ definePropertyOrThrow(compiledSchema, "_canApplyDefaults", {
3066
+ value: true,
3067
+ enumerable: false,
3068
+ configurable: false,
3069
+ writable: false
3070
+ });
3071
+ }
3072
+ const validateSubschema = this.compilingRequiresContext ? this.validateSubschema.bind(this) : void 0;
3073
+ let schemaHasRef = false;
2288
3074
  if ("$ref" in schema) {
2289
3075
  schemaHasRef = true;
2290
3076
  const refValidator = this.getKeyword("$ref");
@@ -2294,21 +3080,53 @@ var SchemaShield = class {
2294
3080
  schema["$ref"],
2295
3081
  this.failFast
2296
3082
  );
3083
+ const isBuiltinRef = refValidator === keywords.$ref;
2297
3084
  compiledSchema.$validate = getNamedFunction(
2298
- "Validate_Reference",
3085
+ isBuiltinRef ? "Validate_Reference" : refValidator.name || "$ref",
2299
3086
  (data) => refValidator(
2300
3087
  compiledSchema,
2301
3088
  data,
2302
3089
  defineError,
2303
- this
3090
+ this,
3091
+ validateSubschema
2304
3092
  )
2305
3093
  );
3094
+ if (!isBuiltinRef) {
3095
+ schemaHasRef = false;
3096
+ }
3097
+ }
3098
+ for (const key of ["definitions", "$defs"]) {
3099
+ const definitions = schema[key];
3100
+ if (!definitions || typeof definitions !== "object") {
3101
+ continue;
3102
+ }
3103
+ const compiledDefinitions = {};
3104
+ for (const definitionKey of Object.keys(definitions)) {
3105
+ compiledDefinitions[definitionKey] = this.compileSchema(
3106
+ definitions[definitionKey]
3107
+ );
3108
+ }
3109
+ compiledSchema[key] = compiledDefinitions;
3110
+ }
3111
+ if (schemaHasRef) {
3112
+ this.markSchemaHasRef(compiledSchema);
2306
3113
  }
2307
- this.markSchemaHasRef(compiledSchema);
2308
3114
  return compiledSchema;
2309
3115
  }
2310
3116
  const validators = [];
2311
3117
  const activeNames = [];
3118
+ const pendingCombinators = [];
3119
+ if (this.useDefaults !== false && this.getKeyword("properties") === keywords.properties && this.hasPropertyDefaults(schema)) {
3120
+ const applyDefaults = this.useDefaults === "empty" ? applyEmptyPropertyDefaults : applyPropertyDefaults;
3121
+ validators.push({
3122
+ name: applyDefaults.name,
3123
+ validate: getNamedFunction(
3124
+ applyDefaults.name,
3125
+ (data) => applyDefaults(compiledSchema, data, this)
3126
+ )
3127
+ });
3128
+ activeNames.push(applyDefaults.name);
3129
+ }
2312
3130
  if ("type" in schema) {
2313
3131
  const defineTypeError = getDefinedErrorFunctionForKey(
2314
3132
  "type",
@@ -2344,65 +3162,11 @@ var SchemaShield = class {
2344
3162
  if (typeFunctions.length === 1 && allTypesDefault) {
2345
3163
  const singleTypeName = defaultTypeNames[0];
2346
3164
  typeMethodName = singleTypeName;
2347
- switch (singleTypeName) {
2348
- case "object":
2349
- combinedTypeValidator = (data) => {
2350
- if (data === null || typeof data !== "object" || Array.isArray(data)) {
2351
- return defineTypeError("Invalid type", { data });
2352
- }
2353
- };
2354
- break;
2355
- case "array":
2356
- combinedTypeValidator = (data) => {
2357
- if (!Array.isArray(data)) {
2358
- return defineTypeError("Invalid type", { data });
2359
- }
2360
- };
2361
- break;
2362
- case "string":
2363
- combinedTypeValidator = (data) => {
2364
- if (typeof data !== "string") {
2365
- return defineTypeError("Invalid type", { data });
2366
- }
2367
- };
2368
- break;
2369
- case "number":
2370
- combinedTypeValidator = (data) => {
2371
- if (typeof data !== "number") {
2372
- return defineTypeError("Invalid type", { data });
2373
- }
2374
- };
2375
- break;
2376
- case "integer":
2377
- combinedTypeValidator = (data) => {
2378
- if (typeof data !== "number" || !Number.isInteger(data)) {
2379
- return defineTypeError("Invalid type", { data });
2380
- }
2381
- };
2382
- break;
2383
- case "boolean":
2384
- combinedTypeValidator = (data) => {
2385
- if (typeof data !== "boolean") {
2386
- return defineTypeError("Invalid type", { data });
2387
- }
2388
- };
2389
- break;
2390
- case "null":
2391
- combinedTypeValidator = (data) => {
2392
- if (data !== null) {
2393
- return defineTypeError("Invalid type", { data });
2394
- }
2395
- };
2396
- break;
2397
- default: {
2398
- const singleTypeFn = typeFunctions[0];
2399
- combinedTypeValidator = (data) => {
2400
- if (!singleTypeFn(data)) {
2401
- return defineTypeError("Invalid type", { data });
2402
- }
2403
- };
2404
- }
2405
- }
3165
+ combinedTypeValidator = this.failFast && FAIL_FAST_TYPE_VALIDATORS[singleTypeName] ? FAIL_FAST_TYPE_VALIDATORS[singleTypeName] : createBuiltinTypeValidator(
3166
+ singleTypeName,
3167
+ defineTypeError,
3168
+ typeFunctions[0]
3169
+ );
2406
3170
  } else if (typeFunctions.length > 1 && allTypesDefault) {
2407
3171
  typeMethodName = defaultTypeNames.join("_OR_");
2408
3172
  const allowsObject = defaultTypeNames.includes("object");
@@ -2415,6 +3179,9 @@ var SchemaShield = class {
2415
3179
  combinedTypeValidator = (data) => {
2416
3180
  const dataType = typeof data;
2417
3181
  if (dataType === "number") {
3182
+ if (!Number.isFinite(data)) {
3183
+ return defineTypeError("Invalid type", { data });
3184
+ }
2418
3185
  if (allowsNumber || allowsInteger && Number.isInteger(data)) {
2419
3186
  return;
2420
3187
  }
@@ -2478,7 +3245,8 @@ var SchemaShield = class {
2478
3245
  activeNames.push(typeMethodName);
2479
3246
  }
2480
3247
  const { type, $id, $ref, $validate, required, ...otherKeys } = schema;
2481
- const keyOrder = required ? this.hasRequiredDefaults(schema) ? [...Object.keys(otherKeys), "required"] : ["required", ...Object.keys(otherKeys)] : Object.keys(otherKeys);
3248
+ const otherKeyNames = Object.keys(otherKeys);
3249
+ const keyOrder = required ? ["required", ...otherKeyNames] : otherKeyNames;
2482
3250
  for (const key of keyOrder) {
2483
3251
  const keywordFn = this.getKeyword(key);
2484
3252
  if (!keywordFn) {
@@ -2493,12 +3261,33 @@ var SchemaShield = class {
2493
3261
  this.failFast
2494
3262
  );
2495
3263
  const fnName = keywordFn.name || key;
3264
+ if ((key === "allOf" || key === "anyOf" || key === "oneOf") && keywordFn === keywords[key]) {
3265
+ const item = {
3266
+ name: fnName,
3267
+ validate: () => {
3268
+ throw new ValidationError("Combinator validator was not prepared");
3269
+ }
3270
+ };
3271
+ validators.push(item);
3272
+ pendingCombinators.push({ item, key, defineError });
3273
+ activeNames.push(fnName);
3274
+ continue;
3275
+ }
3276
+ const keywordValidate = validateSubschema ? (data) => keywordFn(
3277
+ compiledSchema,
3278
+ data,
3279
+ defineError,
3280
+ this,
3281
+ validateSubschema
3282
+ ) : (data) => keywordFn(
3283
+ compiledSchema,
3284
+ data,
3285
+ defineError,
3286
+ this
3287
+ );
2496
3288
  validators.push({
2497
3289
  name: fnName,
2498
- validate: getNamedFunction(
2499
- fnName,
2500
- (data) => keywordFn(compiledSchema, data, defineError, this)
2501
- )
3290
+ validate: getNamedFunction(fnName, keywordValidate)
2502
3291
  });
2503
3292
  activeNames.push(fnName);
2504
3293
  }
@@ -2540,6 +3329,50 @@ var SchemaShield = class {
2540
3329
  continue;
2541
3330
  }
2542
3331
  }
3332
+ if (this.isPlainObject(schema.properties)) {
3333
+ definePropertyOrThrow(compiledSchema, "_propKeys", {
3334
+ value: Object.keys(schema.properties),
3335
+ enumerable: false,
3336
+ configurable: false,
3337
+ writable: false
3338
+ });
3339
+ }
3340
+ if (this.useDefaults !== false && this.isPlainObject(schema.properties) && this.hasPropertyDefaults(schema)) {
3341
+ const defaultKeys = Object.keys(schema.properties).filter(
3342
+ (key) => {
3343
+ const property = schema.properties[key];
3344
+ return property && typeof property === "object" && !Array.isArray(property) && hasOwn(property, "default");
3345
+ }
3346
+ );
3347
+ if (defaultKeys.length > 0) {
3348
+ definePropertyOrThrow(compiledSchema, "_defaultKeys", {
3349
+ value: defaultKeys,
3350
+ enumerable: false,
3351
+ configurable: false,
3352
+ writable: false
3353
+ });
3354
+ }
3355
+ }
3356
+ prepareCombinatorEntries(compiledSchema);
3357
+ for (let index = 0; index < pendingCombinators.length; index++) {
3358
+ const pending = pendingCombinators[index];
3359
+ const transactions = compiledSchema._canApplyDefaults === true ? {
3360
+ savepoint: () => this.#defaultSavepoint(),
3361
+ rollback: (savepoint) => this.#rollbackDefaultSavepoint(savepoint),
3362
+ capture: (savepoint) => this.#captureDefaultSavepoint(savepoint),
3363
+ restore: (mutations) => this.#restoreDefaults(mutations)
3364
+ } : void 0;
3365
+ pending.item.validate = getNamedFunction(
3366
+ pending.item.name,
3367
+ createCombinatorValidator(
3368
+ pending.key,
3369
+ compiledSchema,
3370
+ pending.defineError,
3371
+ validateSubschema,
3372
+ transactions
3373
+ )
3374
+ );
3375
+ }
2543
3376
  if (schemaHasRef) {
2544
3377
  this.markSchemaHasRef(compiledSchema);
2545
3378
  }
@@ -2581,54 +3414,80 @@ var SchemaShield = class {
2581
3414
  }
2582
3415
  return false;
2583
3416
  }
2584
- linkReferences(root) {
2585
- const stack = [root];
2586
- while (stack.length > 0) {
2587
- const node = stack.pop();
2588
- if (!node || typeof node !== "object")
3417
+ getCompiledReferenceTarget(ref, position, registry) {
3418
+ const resolvedUri = this.resolveUri(ref, position.baseUri, "$ref");
3419
+ const exactTarget = registry.aliases.get(resolvedUri);
3420
+ if (exactTarget) {
3421
+ return this.compileCache.get(exactTarget);
3422
+ }
3423
+ const resourceRoot = registry.aliases.get(this.resourceUri(resolvedUri));
3424
+ if (!resourceRoot) {
3425
+ return;
3426
+ }
3427
+ const compiledResource = this.compileCache.get(resourceRoot);
3428
+ if (!compiledResource) {
3429
+ return;
3430
+ }
3431
+ const hashIndex = resolvedUri.indexOf("#");
3432
+ const fragment = hashIndex === -1 ? "" : resolvedUri.slice(hashIndex + 1);
3433
+ if (fragment.length === 0) {
3434
+ return compiledResource;
3435
+ }
3436
+ if (fragment.startsWith("/")) {
3437
+ return resolvePath(compiledResource, `#${fragment}`);
3438
+ }
3439
+ return;
3440
+ }
3441
+ linkReferences(registry) {
3442
+ for (let index = 0; index < registry.positions.length; index++) {
3443
+ const position = registry.positions[index];
3444
+ if (typeof position.source.$ref !== "string" || this.getKeyword("$ref") !== keywords.$ref) {
2589
3445
  continue;
2590
- if (typeof node.$ref === "string" && typeof node.$validate === "function" && node.$validate.name === "Validate_Reference") {
2591
- const refPath = node.$ref;
2592
- let target = this.getSchemaRef(refPath);
2593
- if (typeof target === "undefined") {
2594
- target = this.getSchemaById(refPath);
2595
- }
2596
- if (typeof target === "boolean") {
2597
- if (target === true) {
2598
- node.$validate = getNamedFunction("Validate_Ref_True", () => {
2599
- });
2600
- } else {
2601
- const defineError = getDefinedErrorFunctionForKey(
2602
- "$ref",
2603
- node,
2604
- this.failFast
2605
- );
2606
- node.$validate = getNamedFunction(
2607
- "Validate_Ref_False",
2608
- (_data) => defineError("Value is not valid")
2609
- );
2610
- }
2611
- continue;
2612
- }
2613
- if (target && typeof target.$validate === "function") {
2614
- node.$validate = target.$validate;
2615
- }
2616
3446
  }
2617
- for (const key in node) {
2618
- const value = node[key];
2619
- if (!value)
2620
- continue;
2621
- if (Array.isArray(value)) {
2622
- for (let i = 0; i < value.length; i++) {
2623
- const v = value[i];
2624
- if (v && typeof v === "object") {
2625
- stack.push(v);
3447
+ const node = this.compileCache.get(position.source);
3448
+ const target = this.getCompiledReferenceTarget(
3449
+ position.source.$ref,
3450
+ position,
3451
+ registry
3452
+ );
3453
+ if (!node || typeof target === "undefined") {
3454
+ const error = new ValidationError(
3455
+ `Reference not found: ${position.source.$ref}`
3456
+ );
3457
+ error.code = "REFERENCE_NOT_FOUND";
3458
+ error.keyword = "$ref";
3459
+ throw error;
3460
+ }
3461
+ let targetValidate;
3462
+ if (target === true) {
3463
+ targetValidate = getNamedFunction("Validate_Ref_True", () => {
3464
+ });
3465
+ } else if (target === false) {
3466
+ const defineError = getDefinedErrorFunctionForKey(
3467
+ "$ref",
3468
+ node,
3469
+ this.failFast
3470
+ );
3471
+ targetValidate = getNamedFunction(
3472
+ "Validate_Ref_False",
3473
+ (data) => defineError("Value is not valid", { data })
3474
+ );
3475
+ } else {
3476
+ if (typeof target.$validate !== "function") {
3477
+ target.$validate = getNamedFunction(
3478
+ "Validate_Ref_Any",
3479
+ () => {
2626
3480
  }
2627
- }
2628
- } else if (typeof value === "object") {
2629
- stack.push(value);
3481
+ );
2630
3482
  }
3483
+ targetValidate = target.$validate;
2631
3484
  }
3485
+ definePropertyOrThrow(node, "_resolvedRef", {
3486
+ value: targetValidate,
3487
+ enumerable: false,
3488
+ configurable: false,
3489
+ writable: false
3490
+ });
2632
3491
  }
2633
3492
  }
2634
3493
  };