z-schema 12.3.1 → 12.4.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/bin/z-schema CHANGED
File without changes
package/cjs/index.cjs CHANGED
@@ -80,7 +80,7 @@ function isObject(value) {
80
80
  function isInteger(value) {
81
81
  return typeof value === "number" && Number.isFinite(value) && value % 1 === 0;
82
82
  }
83
- const NON_SCHEMA_KEYWORDS_SET = new Set([
83
+ const NON_SCHEMA_KEYWORDS_SET = /* @__PURE__ */ new Set([
84
84
  "enum",
85
85
  "const",
86
86
  "default",
@@ -255,7 +255,8 @@ const areEqual = (json1, json2, options, _depth = 0) => {
255
255
  }
256
256
  if (isObject(json1) && isObject(json2)) {
257
257
  const keys1 = sortedKeys(json1);
258
- if (!areEqual(keys1, sortedKeys(json2), options, _depth + 1)) return false;
258
+ const keys2 = sortedKeys(json2);
259
+ if (!areEqual(keys1, keys2, options, _depth + 1)) return false;
259
260
  len = keys1.length;
260
261
  for (i = 0; i < len; i++) if (!areEqual(json1[keys1[i]], json2[keys1[i]], options, _depth + 1)) return false;
261
262
  return true;
@@ -1680,6 +1681,94 @@ const isValidRfc3339Date = (year, month, day) => {
1680
1681
  //#region src/utils/hostname.ts
1681
1682
  const IDN_SEPARATOR_REGEX = /[\u3002\uFF0E\uFF61]/g;
1682
1683
  const IDN_SEPARATOR_TEST_REGEX = /[\u3002\uFF0E\uFF61]/;
1684
+ const XN_LABEL_REGEX = /^xn--/i;
1685
+ const DISALLOWED_CATEGORY_REGEX = /[\p{P}\p{S}\p{Z}\p{C}]/u;
1686
+ const DISALLOWED_CATEGORY_WHITELIST = /* @__PURE__ */ new Set([
1687
+ "-",
1688
+ "·",
1689
+ "͵",
1690
+ "׳",
1691
+ "״",
1692
+ "・",
1693
+ "۽",
1694
+ "۾",
1695
+ "་",
1696
+ "‌",
1697
+ "‍"
1698
+ ]);
1699
+ const isDisallowedCodePoint = (char) => DISALLOWED_CATEGORY_REGEX.test(char) && !DISALLOWED_CATEGORY_WHITELIST.has(char);
1700
+ const TRANSPARENT_MARK_REGEX = /\p{Mn}/u;
1701
+ const ARABIC_SCRIPT_REGEX = /\p{Script=Arabic}/u;
1702
+ const ARABIC_INDIC_DIGITS_REGEX = /[\u0660-\u0669\u06F0-\u06F9]/;
1703
+ const isArabicJoiningLetter = (char) => char !== void 0 && ARABIC_SCRIPT_REGEX.test(char) && !ARABIC_INDIC_DIGITS_REGEX.test(char);
1704
+ const hasZwnjJoiningContext = (label, idx) => {
1705
+ let before = idx - 1;
1706
+ while (before >= 0 && TRANSPARENT_MARK_REGEX.test(label[before])) before--;
1707
+ let after = idx + 1;
1708
+ while (after < label.length && TRANSPARENT_MARK_REGEX.test(label[after])) after++;
1709
+ return isArabicJoiningLetter(label[before]) && isArabicJoiningLetter(label[after]);
1710
+ };
1711
+ const BIDI_NSM_REGEX = /[\p{Mn}\p{Me}]/u;
1712
+ const BIDI_ARABIC_INDIC_DIGIT_REGEX = /[\u0660-\u0669]/;
1713
+ const BIDI_EXTENDED_ARABIC_INDIC_DIGIT_REGEX = /[\u06F0-\u06F9]/;
1714
+ const BIDI_HEBREW_SCRIPT_REGEX = /\p{Script=Hebrew}/u;
1715
+ const BIDI_AL_SCRIPT_REGEX = /[\p{Script=Arabic}\p{Script=Syriac}\p{Script=Thaana}]/u;
1716
+ const BIDI_ASCII_DIGIT_REGEX = /[0-9]/;
1717
+ const BIDI_LETTER_REGEX = /\p{L}/u;
1718
+ const classifyBidi = (char) => {
1719
+ if (BIDI_NSM_REGEX.test(char)) return "NSM";
1720
+ if (BIDI_ARABIC_INDIC_DIGIT_REGEX.test(char)) return "AN";
1721
+ if (BIDI_EXTENDED_ARABIC_INDIC_DIGIT_REGEX.test(char)) return "EN";
1722
+ if (BIDI_HEBREW_SCRIPT_REGEX.test(char)) return "R";
1723
+ if (BIDI_AL_SCRIPT_REGEX.test(char)) return "AL";
1724
+ if (BIDI_ASCII_DIGIT_REGEX.test(char)) return "EN";
1725
+ if (BIDI_LETTER_REGEX.test(char)) return "L";
1726
+ return "ON";
1727
+ };
1728
+ const isLabelBidiTriggering = (label) => {
1729
+ for (const char of label) {
1730
+ const bidiClass = classifyBidi(char);
1731
+ if (bidiClass === "R" || bidiClass === "AL" || bidiClass === "AN") return true;
1732
+ }
1733
+ return false;
1734
+ };
1735
+ const isLabelBidiRuleValid = (label) => {
1736
+ const classes = [];
1737
+ for (const char of label) classes.push(classifyBidi(char));
1738
+ if (classes.length === 0) return false;
1739
+ const first = classes[0];
1740
+ if (first !== "L" && first !== "R" && first !== "AL") return false;
1741
+ let lastIdx = classes.length - 1;
1742
+ while (lastIdx > 0 && classes[lastIdx] === "NSM") lastIdx--;
1743
+ const last = classes[lastIdx];
1744
+ if (first === "R" || first === "AL") {
1745
+ let hasEN = false;
1746
+ let hasAN = false;
1747
+ for (let i = 0; i < classes.length; i++) {
1748
+ const c = classes[i];
1749
+ if (c !== "R" && c !== "AL" && c !== "AN" && c !== "EN" && c !== "NSM" && c !== "ON") return false;
1750
+ if (c === "EN") hasEN = true;
1751
+ else if (c === "AN") hasAN = true;
1752
+ }
1753
+ if (hasEN && hasAN) return false;
1754
+ return last === "R" || last === "AL" || last === "EN" || last === "AN";
1755
+ }
1756
+ for (let i = 0; i < classes.length; i++) {
1757
+ const c = classes[i];
1758
+ if (c !== "L" && c !== "EN" && c !== "NSM" && c !== "ON") return false;
1759
+ }
1760
+ return last === "L" || last === "EN";
1761
+ };
1762
+ const isDomainBidiValid = (unicodeLabels) => {
1763
+ let isBidiDomain = false;
1764
+ for (let i = 0; i < unicodeLabels.length; i++) if (isLabelBidiTriggering(unicodeLabels[i])) {
1765
+ isBidiDomain = true;
1766
+ break;
1767
+ }
1768
+ if (!isBidiDomain) return true;
1769
+ for (let i = 0; i < unicodeLabels.length; i++) if (!isLabelBidiRuleValid(unicodeLabels[i])) return false;
1770
+ return true;
1771
+ };
1683
1772
  const splitHostnameLabels = (hostname) => {
1684
1773
  if (hostname.length === 0 || hostname.length > 255) return null;
1685
1774
  if (hostname.startsWith(".") || hostname.endsWith(".")) return null;
@@ -1695,7 +1784,7 @@ const isGreek = (char) => /\p{Script=Greek}/u.test(char);
1695
1784
  const isHebrew = (char) => /\p{Script=Hebrew}/u.test(char);
1696
1785
  const hasCjkKanaOrHan = (input) => /[\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Han}]/u.test(input);
1697
1786
  const toUnicodeLabel = (label) => {
1698
- if (!/^xn--/i.test(label)) return label;
1787
+ if (!XN_LABEL_REGEX.test(label)) return label;
1699
1788
  try {
1700
1789
  return punycode_punycode_js.default.toUnicode(label.toLowerCase());
1701
1790
  } catch {
@@ -1704,7 +1793,7 @@ const toUnicodeLabel = (label) => {
1704
1793
  };
1705
1794
  const isValidIdnUnicodeLabel = (label) => {
1706
1795
  if (label.startsWith("-") || label.endsWith("-")) return false;
1707
- if (label.length >= 4 && label[2] === "-" && label[3] === "-" && !/^xn--/i.test(label)) return false;
1796
+ if (label.length >= 4 && label[2] === "-" && label[3] === "-" && !XN_LABEL_REGEX.test(label)) return false;
1708
1797
  if (/^\p{M}/u.test(label)) return false;
1709
1798
  if (/[\u302E\u302F\u0640\u07FA]/u.test(label)) return false;
1710
1799
  for (let idx = 0; idx < label.length; idx++) {
@@ -1713,7 +1802,9 @@ const isValidIdnUnicodeLabel = (label) => {
1713
1802
  if (char === "͵" && (idx === label.length - 1 || !isGreek(label[idx + 1]))) return false;
1714
1803
  if ((char === "׳" || char === "״") && (idx === 0 || !isHebrew(label[idx - 1]))) return false;
1715
1804
  if (char === "‍" && (idx === 0 || label[idx - 1] !== "्")) return false;
1805
+ if (char === "‌" && (idx === 0 || label[idx - 1] !== "्" && !hasZwnjJoiningContext(label, idx))) return false;
1716
1806
  }
1807
+ for (const char of label) if (isDisallowedCodePoint(char)) return false;
1717
1808
  if (label.includes("・") && !hasCjkKanaOrHan(label.replaceAll("・", ""))) return false;
1718
1809
  const hasArabicIndic = /[\u0660-\u0669]/.test(label);
1719
1810
  const hasExtendedArabicIndic = /[\u06F0-\u06F9]/.test(label);
@@ -1728,7 +1819,7 @@ const isValidHostname = (hostname) => {
1728
1819
  for (let i = 0; i < labels.length; i++) {
1729
1820
  const label = labels[i];
1730
1821
  if (!isAsciiHostnameLabel(label)) return false;
1731
- if (/^xn--/i.test(label)) {
1822
+ if (XN_LABEL_REGEX.test(label)) {
1732
1823
  const unicodeLabel = toUnicodeLabel(label);
1733
1824
  if (unicodeLabel === null || !isValidIdnUnicodeLabel(unicodeLabel)) return false;
1734
1825
  }
@@ -1736,14 +1827,28 @@ const isValidHostname = (hostname) => {
1736
1827
  return true;
1737
1828
  };
1738
1829
  const isValidIdnHostname = (hostname) => {
1739
- const labels = splitHostnameLabels(hostname.replace(IDN_SEPARATOR_REGEX, "."));
1830
+ const normalizedHostname = hostname.replace(IDN_SEPARATOR_REGEX, ".");
1831
+ const labels = splitHostnameLabels(normalizedHostname);
1740
1832
  if (labels === null) return false;
1833
+ const unicodeLabels = [];
1834
+ let totalLength = labels.length - 1;
1741
1835
  for (let i = 0; i < labels.length; i++) {
1742
1836
  const label = labels[i];
1743
1837
  const unicodeLabel = toUnicodeLabel(label);
1744
1838
  if (unicodeLabel === null || !isValidIdnUnicodeLabel(unicodeLabel)) return false;
1839
+ let aLabel;
1840
+ try {
1841
+ aLabel = punycode_punycode_js.default.toASCII(unicodeLabel);
1842
+ } catch {
1843
+ return false;
1844
+ }
1845
+ if (XN_LABEL_REGEX.test(label) && aLabel.toLowerCase() !== label.toLowerCase()) return false;
1846
+ if (aLabel.length > 63) return false;
1847
+ totalLength += aLabel.length;
1848
+ unicodeLabels.push(unicodeLabel);
1745
1849
  }
1746
- return true;
1850
+ if (totalLength > 253) return false;
1851
+ return isDomainBidiValid(unicodeLabels);
1747
1852
  };
1748
1853
  //#endregion
1749
1854
  //#region src/utils/time.ts
@@ -1756,7 +1861,7 @@ const toUtcTime = (hour, minute, offsetSign, offsetHour, offsetMinute) => {
1756
1861
  minute: normalizedUtcTotalMinutes % 60
1757
1862
  };
1758
1863
  };
1759
- const RFC3339_TIME_REGEX = /^([0-9]{2}):([0-9]{2}):([0-9]{2})(\.[0-9]+)?(z|([+-][0-9]{2}:[0-9]{2}))$/i;
1864
+ const RFC3339_TIME_REGEX = /^(?<hour>[0-9]{2}):(?<minute>[0-9]{2}):(?<second>[0-9]{2})(?<fraction>\.[0-9]+)?(?<offset>z|(?<offsetValue>[+-][0-9]{2}:[0-9]{2}))$/i;
1760
1865
  const parseRfc3339Time = (time) => {
1761
1866
  const matches = RFC3339_TIME_REGEX.exec(time);
1762
1867
  if (matches === null) return null;
@@ -1767,7 +1872,7 @@ const parseRfc3339Time = (time) => {
1767
1872
  let utcHour = hour;
1768
1873
  let utcMinute = minute;
1769
1874
  if (matches[5].toLowerCase() !== "z") {
1770
- const offsetMatches = /^([+-])([0-9]{2}):([0-9]{2})$/.exec(matches[5]);
1875
+ const offsetMatches = /^(?<sign>[+-])(?<hour>[0-9]{2}):(?<minute>[0-9]{2})$/.exec(matches[5]);
1771
1876
  if (offsetMatches === null) return null;
1772
1877
  const offsetSign = offsetMatches[1];
1773
1878
  const offsetHour = Number.parseInt(offsetMatches[2], 10);
@@ -1790,7 +1895,7 @@ const parseRfc3339Time = (time) => {
1790
1895
  //#region src/format-validators.ts
1791
1896
  const dateValidator = (date) => {
1792
1897
  if (typeof date !== "string") return true;
1793
- const matches = /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.exec(date);
1898
+ const matches = /^(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})$/.exec(date);
1794
1899
  if (matches === null) return false;
1795
1900
  return isValidRfc3339Date(Number.parseInt(matches[1], 10), Number.parseInt(matches[2], 10), Number.parseInt(matches[3], 10));
1796
1901
  };
@@ -1801,7 +1906,7 @@ const dateTimeValidator = (dateTime) => {
1801
1906
  if (tIdx === -1) return false;
1802
1907
  const datePart = dateTime.slice(0, tIdx);
1803
1908
  const timePart = dateTime.slice(tIdx + 1);
1804
- const dateMatches = /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.exec(datePart);
1909
+ const dateMatches = /^(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})$/.exec(datePart);
1805
1910
  if (dateMatches === null) return false;
1806
1911
  if (!isValidRfc3339Date(Number.parseInt(dateMatches[1], 10), Number.parseInt(dateMatches[2], 10), Number.parseInt(dateMatches[3], 10))) return false;
1807
1912
  return parseRfc3339Time(timePart) !== null;
@@ -1812,7 +1917,7 @@ const emailValidator = (email) => {
1812
1917
  require_tld: true,
1813
1918
  allow_ip_domain: true
1814
1919
  })) return true;
1815
- const ipv6Literal = /^(.+)@\[IPv6:([^\]]+)\]$/i.exec(email);
1920
+ const ipv6Literal = /^(?<localPart>.+)@\[IPv6:(?<address>[^\]]+)\]$/i.exec(email);
1816
1921
  if (!ipv6Literal) return false;
1817
1922
  const localPart = ipv6Literal[1];
1818
1923
  const addressPart = ipv6Literal[2];
@@ -1832,7 +1937,7 @@ const ipv6Validator = (ipv6) => {
1832
1937
  if (ipv6.includes("%")) return false;
1833
1938
  return validator_lib_isIP_js.default.default(ipv6, 6);
1834
1939
  };
1835
- const INVALID_REGEX_ESCAPES = new Set(["a"]);
1940
+ const INVALID_REGEX_ESCAPES = /* @__PURE__ */ new Set(["a"]);
1836
1941
  const regexValidator = (input) => {
1837
1942
  if (typeof input !== "string") return true;
1838
1943
  for (let idx = 0; idx < input.length; idx++) {
@@ -1885,7 +1990,7 @@ const uriValidator = (uri) => {
1885
1990
  if (typeof uri !== "string") return true;
1886
1991
  if (/[^\u0000-\u007F]/.test(uri)) return false;
1887
1992
  if (!hasValidPercentEncoding(uri)) return false;
1888
- const match = /^([a-zA-Z][a-zA-Z0-9+.-]*):\/\/([^/?#]*)/.exec(uri);
1993
+ const match = /^(?<scheme>[a-zA-Z][a-zA-Z0-9+.-]*):\/\/(?<authority>[^/?#]*)/.exec(uri);
1889
1994
  if (match) {
1890
1995
  const authority = match[2];
1891
1996
  const atIndex = authority.indexOf("@");
@@ -1909,11 +2014,11 @@ const uriValidator = (uri) => {
1909
2014
  const uriReferenceValidator = (uri) => {
1910
2015
  if (typeof uri !== "string") return true;
1911
2016
  if (/[^\u0000-\u007F]/.test(uri)) return false;
1912
- return /^([a-zA-Z][a-zA-Z0-9+.-]*:)?[^"\\<>^{}^`| ]*$/.test(uri);
2017
+ return /^(?:[a-zA-Z][a-zA-Z0-9+.-]*:)?[^"\\<>^{}^`| ]*$/.test(uri);
1913
2018
  };
1914
2019
  const uriTemplateValidator = (uri) => {
1915
2020
  if (typeof uri !== "string") return true;
1916
- if (!/^([a-zA-Z][a-zA-Z0-9+.-]*:)?[^"\\<>^`| ]*$/.test(uri)) return false;
2021
+ if (!/^(?:[a-zA-Z][a-zA-Z0-9+.-]*:)?[^"\\<>^`| ]*$/.test(uri)) return false;
1917
2022
  let inExpression = false;
1918
2023
  for (let idx = 0; idx < uri.length; idx++) {
1919
2024
  const ch = uri[idx];
@@ -1945,7 +2050,7 @@ const jsonPointerValidator = (pointer) => {
1945
2050
  };
1946
2051
  const relativeJsonPointerValidator = (pointer) => {
1947
2052
  if (typeof pointer !== "string") return true;
1948
- const match = /^(0|[1-9]\d*)(.*)$/.exec(pointer);
2053
+ const match = /^(?<int>0|[1-9]\d*)(?<suffix>.*)$/.exec(pointer);
1949
2054
  if (!match) return false;
1950
2055
  const suffix = match[2];
1951
2056
  if (suffix === "" || suffix === "#") return true;
@@ -1959,9 +2064,19 @@ const timeValidator = (time) => {
1959
2064
  if (typeof time !== "string") return true;
1960
2065
  return parseRfc3339Time(time) !== null;
1961
2066
  };
2067
+ const LONE_SURROGATE_REGEX = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:^|[^\uD800-\uDBFF])[\uDC00-\uDFFF]/;
2068
+ const QUOTED_LOCAL_PART_REGEX = /^"(?:[^"\\]|\\.)*"$/u;
2069
+ const UNQUOTED_LOCAL_PART_REGEX = /^[^\s@]+$/u;
2070
+ const isValidIdnEmailLocalPart = (localPart) => {
2071
+ if (localPart.length === 0 || LONE_SURROGATE_REGEX.test(localPart)) return false;
2072
+ if (localPart.length >= 2 && localPart.startsWith("\"") && localPart.endsWith("\"")) return QUOTED_LOCAL_PART_REGEX.test(localPart);
2073
+ return UNQUOTED_LOCAL_PART_REGEX.test(localPart);
2074
+ };
1962
2075
  const idnEmailValidator = (email) => {
1963
2076
  if (typeof email !== "string") return true;
1964
- return /^[^\s@]+@[^\s@]+$/.test(email);
2077
+ const atIdx = email.lastIndexOf("@");
2078
+ if (atIdx <= 0 || atIdx === email.length - 1) return false;
2079
+ return isValidIdnEmailLocalPart(email.slice(0, atIdx)) && isValidIdnHostname(email.slice(atIdx + 1));
1965
2080
  };
1966
2081
  const idnHostnameValidator = (hostname) => {
1967
2082
  if (typeof hostname !== "string") return true;
@@ -1979,7 +2094,7 @@ const iriValidator = (iri) => {
1979
2094
  };
1980
2095
  const iriReferenceValidator = (iriReference) => {
1981
2096
  if (typeof iriReference !== "string") return true;
1982
- return /^([a-zA-Z][a-zA-Z0-9+.-]*:)?[^"\\<>^{}^`| ]*$/u.test(iriReference);
2097
+ return /^(?:[a-zA-Z][a-zA-Z0-9+.-]*:)?[^"\\<>^{}^`| ]*$/u.test(iriReference);
1983
2098
  };
1984
2099
  const inbuiltValidators = {
1985
2100
  date: dateValidator,
@@ -2156,7 +2271,7 @@ const VOCAB_VALIDATION_2019_09 = "https://json-schema.org/draft/2019-09/vocab/va
2156
2271
  const VOCAB_VALIDATION_2020_12 = "https://json-schema.org/draft/2020-12/vocab/validation";
2157
2272
  const VOCAB_FORMAT_2019_09 = "https://json-schema.org/draft/2019-09/vocab/format";
2158
2273
  const VOCAB_FORMAT_ASSERTION_2020_12 = "https://json-schema.org/draft/2020-12/vocab/format-assertion";
2159
- const VALIDATION_VOCAB_KEYWORDS = new Set([
2274
+ const VALIDATION_VOCAB_KEYWORDS = /* @__PURE__ */ new Set([
2160
2275
  "type",
2161
2276
  "multipleOf",
2162
2277
  "maximum",
@@ -2187,7 +2302,9 @@ const isValidationVocabularyEnabled = (schema, report, version) => {
2187
2302
  const metaSchema = currentSchemaMeta || rootSchemaMeta;
2188
2303
  if (!metaSchema || typeof metaSchema !== "object" || !isObject(metaSchema.$vocabulary)) return true;
2189
2304
  const vocabulary = metaSchema.$vocabulary;
2190
- if (Object.hasOwn(vocabulary, VOCAB_VALIDATION_2019_09) || Object.hasOwn(vocabulary, VOCAB_VALIDATION_2020_12)) return vocabulary[VOCAB_VALIDATION_2019_09] || vocabulary[VOCAB_VALIDATION_2020_12];
2305
+ const has2019 = Object.hasOwn(vocabulary, VOCAB_VALIDATION_2019_09);
2306
+ const has2020 = Object.hasOwn(vocabulary, VOCAB_VALIDATION_2020_12);
2307
+ if (has2019 || has2020) return vocabulary[VOCAB_VALIDATION_2019_09] || vocabulary[VOCAB_VALIDATION_2020_12];
2191
2308
  return false;
2192
2309
  };
2193
2310
  /**
@@ -2623,11 +2740,11 @@ function formatValidator(ctx, report, schema, json) {
2623
2740
  if (result instanceof Promise) {
2624
2741
  const promiseResult = result;
2625
2742
  report.addAsyncTaskWithPath(async (callback) => {
2743
+ let resolved = false;
2626
2744
  try {
2627
- callback(await promiseResult);
2628
- } catch {
2629
- callback(false);
2630
- }
2745
+ resolved = await promiseResult;
2746
+ } catch {}
2747
+ callback(resolved);
2631
2748
  }, [], (resolvedResult) => {
2632
2749
  if (resolvedResult !== true) report.addError("INVALID_FORMAT", [schema.format, JSON.stringify(json)], void 0, schema, "format");
2633
2750
  });
@@ -3120,7 +3237,8 @@ function validate(ctx, report, schema, json) {
3120
3237
  }
3121
3238
  }
3122
3239
  if (deferredUnevaluatedKeys.length > 0 && !(report.errors.length > 0 && ctx.options.breakOnFirstError)) for (let i = 0; i < deferredUnevaluatedKeys.length; i++) {
3123
- const validator = JsonValidators[deferredUnevaluatedKeys[i]];
3240
+ const key = deferredUnevaluatedKeys[i];
3241
+ const validator = JsonValidators[key];
3124
3242
  if (validator) {
3125
3243
  validator(ctx, report, schema, json);
3126
3244
  if (report.errors.length && ctx.options.breakOnFirstError) break;
@@ -3147,7 +3265,7 @@ function setSchemaReader(schemaReader) {
3147
3265
  }
3148
3266
  //#endregion
3149
3267
  //#region src/schema-compiler.ts
3150
- const UNSAFE_TARGETS = new Set([
3268
+ const UNSAFE_TARGETS = /* @__PURE__ */ new Set([
3151
3269
  Object.prototype,
3152
3270
  Function.prototype,
3153
3271
  Array.prototype
@@ -3229,7 +3347,10 @@ const collectIds = (obj, maxDepth = 100) => {
3229
3347
  }
3230
3348
  }
3231
3349
  id.absoluteParent = absoluteParent;
3232
- if (id.absoluteParent) id.absoluteUri = resolveSchemaScopeId(id.absoluteParent.absoluteUri || id.absoluteParent.id, schemaNode, id.id);
3350
+ if (id.absoluteParent) {
3351
+ const parentUri = id.absoluteParent.absoluteUri || id.absoluteParent.id;
3352
+ id.absoluteUri = resolveSchemaScopeId(parentUri, schemaNode, id.id);
3353
+ }
3233
3354
  }
3234
3355
  ids.push(id);
3235
3356
  scope.push(id);
@@ -3313,7 +3434,8 @@ var SchemaCompiler = class {
3313
3434
  for (const item of ids) if (item.absoluteUri) {
3314
3435
  this.validator.scache.cacheSchemaByUri(item.absoluteUri, item.obj);
3315
3436
  if (item.type === "relative" && item.absoluteParent && isSimpleIdentifier(item.id)) {
3316
- const altAbsoluteUri = resolveReference(item.absoluteParent.absoluteUri || item.absoluteParent.id, item.id);
3437
+ const parentUri = item.absoluteParent.absoluteUri || item.absoluteParent.id;
3438
+ const altAbsoluteUri = resolveReference(parentUri, item.id);
3317
3439
  this.validator.scache.cacheSchemaByUri(altAbsoluteUri, item.obj);
3318
3440
  }
3319
3441
  } else if (item.type === "root") this.validator.scache.cacheSchemaByUri(item.id, item.obj);
@@ -8,7 +8,7 @@ import isURLModule from "validator/lib/isURL.js";
8
8
  //#region src/format-validators.ts
9
9
  const dateValidator = (date) => {
10
10
  if (typeof date !== "string") return true;
11
- const matches = /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.exec(date);
11
+ const matches = /^(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})$/.exec(date);
12
12
  if (matches === null) return false;
13
13
  return isValidRfc3339Date(Number.parseInt(matches[1], 10), Number.parseInt(matches[2], 10), Number.parseInt(matches[3], 10));
14
14
  };
@@ -19,7 +19,7 @@ const dateTimeValidator = (dateTime) => {
19
19
  if (tIdx === -1) return false;
20
20
  const datePart = dateTime.slice(0, tIdx);
21
21
  const timePart = dateTime.slice(tIdx + 1);
22
- const dateMatches = /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.exec(datePart);
22
+ const dateMatches = /^(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})$/.exec(datePart);
23
23
  if (dateMatches === null) return false;
24
24
  if (!isValidRfc3339Date(Number.parseInt(dateMatches[1], 10), Number.parseInt(dateMatches[2], 10), Number.parseInt(dateMatches[3], 10))) return false;
25
25
  return parseRfc3339Time(timePart) !== null;
@@ -30,7 +30,7 @@ const emailValidator = (email) => {
30
30
  require_tld: true,
31
31
  allow_ip_domain: true
32
32
  })) return true;
33
- const ipv6Literal = /^(.+)@\[IPv6:([^\]]+)\]$/i.exec(email);
33
+ const ipv6Literal = /^(?<localPart>.+)@\[IPv6:(?<address>[^\]]+)\]$/i.exec(email);
34
34
  if (!ipv6Literal) return false;
35
35
  const localPart = ipv6Literal[1];
36
36
  const addressPart = ipv6Literal[2];
@@ -50,7 +50,7 @@ const ipv6Validator = (ipv6) => {
50
50
  if (ipv6.includes("%")) return false;
51
51
  return isIPModule.default(ipv6, 6);
52
52
  };
53
- const INVALID_REGEX_ESCAPES = new Set(["a"]);
53
+ const INVALID_REGEX_ESCAPES = /* @__PURE__ */ new Set(["a"]);
54
54
  const regexValidator = (input) => {
55
55
  if (typeof input !== "string") return true;
56
56
  for (let idx = 0; idx < input.length; idx++) {
@@ -103,7 +103,7 @@ const uriValidator = (uri) => {
103
103
  if (typeof uri !== "string") return true;
104
104
  if (/[^\u0000-\u007F]/.test(uri)) return false;
105
105
  if (!hasValidPercentEncoding(uri)) return false;
106
- const match = /^([a-zA-Z][a-zA-Z0-9+.-]*):\/\/([^/?#]*)/.exec(uri);
106
+ const match = /^(?<scheme>[a-zA-Z][a-zA-Z0-9+.-]*):\/\/(?<authority>[^/?#]*)/.exec(uri);
107
107
  if (match) {
108
108
  const authority = match[2];
109
109
  const atIndex = authority.indexOf("@");
@@ -127,11 +127,11 @@ const uriValidator = (uri) => {
127
127
  const uriReferenceValidator = (uri) => {
128
128
  if (typeof uri !== "string") return true;
129
129
  if (/[^\u0000-\u007F]/.test(uri)) return false;
130
- return /^([a-zA-Z][a-zA-Z0-9+.-]*:)?[^"\\<>^{}^`| ]*$/.test(uri);
130
+ return /^(?:[a-zA-Z][a-zA-Z0-9+.-]*:)?[^"\\<>^{}^`| ]*$/.test(uri);
131
131
  };
132
132
  const uriTemplateValidator = (uri) => {
133
133
  if (typeof uri !== "string") return true;
134
- if (!/^([a-zA-Z][a-zA-Z0-9+.-]*:)?[^"\\<>^`| ]*$/.test(uri)) return false;
134
+ if (!/^(?:[a-zA-Z][a-zA-Z0-9+.-]*:)?[^"\\<>^`| ]*$/.test(uri)) return false;
135
135
  let inExpression = false;
136
136
  for (let idx = 0; idx < uri.length; idx++) {
137
137
  const ch = uri[idx];
@@ -163,7 +163,7 @@ const jsonPointerValidator = (pointer) => {
163
163
  };
164
164
  const relativeJsonPointerValidator = (pointer) => {
165
165
  if (typeof pointer !== "string") return true;
166
- const match = /^(0|[1-9]\d*)(.*)$/.exec(pointer);
166
+ const match = /^(?<int>0|[1-9]\d*)(?<suffix>.*)$/.exec(pointer);
167
167
  if (!match) return false;
168
168
  const suffix = match[2];
169
169
  if (suffix === "" || suffix === "#") return true;
@@ -177,9 +177,19 @@ const timeValidator = (time) => {
177
177
  if (typeof time !== "string") return true;
178
178
  return parseRfc3339Time(time) !== null;
179
179
  };
180
+ const LONE_SURROGATE_REGEX = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:^|[^\uD800-\uDBFF])[\uDC00-\uDFFF]/;
181
+ const QUOTED_LOCAL_PART_REGEX = /^"(?:[^"\\]|\\.)*"$/u;
182
+ const UNQUOTED_LOCAL_PART_REGEX = /^[^\s@]+$/u;
183
+ const isValidIdnEmailLocalPart = (localPart) => {
184
+ if (localPart.length === 0 || LONE_SURROGATE_REGEX.test(localPart)) return false;
185
+ if (localPart.length >= 2 && localPart.startsWith("\"") && localPart.endsWith("\"")) return QUOTED_LOCAL_PART_REGEX.test(localPart);
186
+ return UNQUOTED_LOCAL_PART_REGEX.test(localPart);
187
+ };
180
188
  const idnEmailValidator = (email) => {
181
189
  if (typeof email !== "string") return true;
182
- return /^[^\s@]+@[^\s@]+$/.test(email);
190
+ const atIdx = email.lastIndexOf("@");
191
+ if (atIdx <= 0 || atIdx === email.length - 1) return false;
192
+ return isValidIdnEmailLocalPart(email.slice(0, atIdx)) && isValidIdnHostname(email.slice(atIdx + 1));
183
193
  };
184
194
  const idnHostnameValidator = (hostname) => {
185
195
  if (typeof hostname !== "string") return true;
@@ -197,7 +207,7 @@ const iriValidator = (iri) => {
197
207
  };
198
208
  const iriReferenceValidator = (iriReference) => {
199
209
  if (typeof iriReference !== "string") return true;
200
- return /^([a-zA-Z][a-zA-Z0-9+.-]*:)?[^"\\<>^{}^`| ]*$/u.test(iriReference);
210
+ return /^(?:[a-zA-Z][a-zA-Z0-9+.-]*:)?[^"\\<>^{}^`| ]*$/u.test(iriReference);
201
211
  };
202
212
  const inbuiltValidators = {
203
213
  date: dateValidator,
@@ -1,11 +1,17 @@
1
1
  import { getRemotePath, isAbsoluteUri } from "./utils/uri.js";
2
2
  import { isObject } from "./utils/what-is.js";
3
- const NON_SCHEMA_KEYWORDS_SET = new Set([
3
+ //#region src/json-schema.ts
4
+ /**
5
+ * Keywords whose values are not JSON Schema sub-schemas and must not be
6
+ * traversed during schema walking (id collection, reference collection, etc.).
7
+ */
8
+ const NON_SCHEMA_KEYWORDS = [
4
9
  "enum",
5
10
  "const",
6
11
  "default",
7
12
  "examples"
8
- ]);
13
+ ];
14
+ const NON_SCHEMA_KEYWORDS_SET = new Set(NON_SCHEMA_KEYWORDS);
9
15
  /** Returns true if the key is an internal z-schema property (prefixed with `__$`). */
10
16
  const isInternalKey = (key) => key.startsWith("__$");
11
17
  const getId = (schema) => {
@@ -45,4 +51,4 @@ const findId = (schema, id, targetBaseUri, currentBaseUri, maxDepth = 100, _dept
45
51
  }
46
52
  };
47
53
  //#endregion
48
- export { NON_SCHEMA_KEYWORDS_SET, findId, getId, isInternalKey };
54
+ export { NON_SCHEMA_KEYWORDS, NON_SCHEMA_KEYWORDS_SET, findId, getId, isInternalKey };
@@ -437,7 +437,8 @@ function validate(ctx, report, schema, json) {
437
437
  }
438
438
  }
439
439
  if (deferredUnevaluatedKeys.length > 0 && !(report.errors.length > 0 && ctx.options.breakOnFirstError)) for (let i = 0; i < deferredUnevaluatedKeys.length; i++) {
440
- const validator = JsonValidators[deferredUnevaluatedKeys[i]];
440
+ const key = deferredUnevaluatedKeys[i];
441
+ const validator = JsonValidators[key];
441
442
  if (validator) {
442
443
  validator(ctx, report, schema, json);
443
444
  if (report.errors.length && ctx.options.breakOnFirstError) break;
@@ -454,4 +455,4 @@ function validate(ctx, report, schema, json) {
454
455
  return report.errors.length === 0;
455
456
  }
456
457
  //#endregion
457
- export { validate };
458
+ export { JsonValidators, validate };
package/dist/report.d.ts CHANGED
@@ -93,4 +93,4 @@ declare class Report {
93
93
  addCustomError(errorCode: ErrorCode, errorMessage: string, params?: ErrorParam[], subReports?: Report | Report[], schema?: JsonSchema | boolean, keyword?: keyof JsonSchemaAll): void;
94
94
  }
95
95
  //#endregion
96
- export { Report, SchemaErrorDetail };
96
+ export { Report, ReportOptions, SchemaErrorDetail };
@@ -20,4 +20,4 @@ declare class SchemaCache {
20
20
  getSchemaByUri(report: Report, uri: string, root?: JsonSchemaInternal): JsonSchemaInternal | undefined;
21
21
  }
22
22
  //#endregion
23
- export { SchemaCache };
23
+ export { SchemaCache, SchemaCacheStorage };
@@ -3,7 +3,7 @@ import { NON_SCHEMA_KEYWORDS_SET, getId, isInternalKey } from "./json-schema.js"
3
3
  import { Report } from "./report.js";
4
4
  import { getSchemaReader } from "./z-schema-reader.js";
5
5
  //#region src/schema-compiler.ts
6
- const UNSAFE_TARGETS = new Set([
6
+ const UNSAFE_TARGETS = /* @__PURE__ */ new Set([
7
7
  Object.prototype,
8
8
  Function.prototype,
9
9
  Array.prototype
@@ -85,7 +85,10 @@ const collectIds = (obj, maxDepth = 100) => {
85
85
  }
86
86
  }
87
87
  id.absoluteParent = absoluteParent;
88
- if (id.absoluteParent) id.absoluteUri = resolveSchemaScopeId(id.absoluteParent.absoluteUri || id.absoluteParent.id, schemaNode, id.id);
88
+ if (id.absoluteParent) {
89
+ const parentUri = id.absoluteParent.absoluteUri || id.absoluteParent.id;
90
+ id.absoluteUri = resolveSchemaScopeId(parentUri, schemaNode, id.id);
91
+ }
89
92
  }
90
93
  ids.push(id);
91
94
  scope.push(id);
@@ -169,7 +172,8 @@ var SchemaCompiler = class {
169
172
  for (const item of ids) if (item.absoluteUri) {
170
173
  this.validator.scache.cacheSchemaByUri(item.absoluteUri, item.obj);
171
174
  if (item.type === "relative" && item.absoluteParent && isSimpleIdentifier(item.id)) {
172
- const altAbsoluteUri = resolveReference(item.absoluteParent.absoluteUri || item.absoluteParent.id, item.id);
175
+ const parentUri = item.absoluteParent.absoluteUri || item.absoluteParent.id;
176
+ const altAbsoluteUri = resolveReference(parentUri, item.id);
173
177
  this.validator.scache.cacheSchemaByUri(altAbsoluteUri, item.obj);
174
178
  }
175
179
  } else if (item.type === "root") this.validator.scache.cacheSchemaByUri(item.id, item.obj);
@@ -280,4 +284,4 @@ var SchemaCompiler = class {
280
284
  }
281
285
  };
282
286
  //#endregion
283
- export { SchemaCompiler };
287
+ export { SchemaCompiler, collectIds, collectReferences };