schema-shield 0.0.6 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"array-keywords.d.ts","sourceRoot":"","sources":["../../lib/keywords/array-keywords.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CA+LzD,CAAC"}
1
+ {"version":3,"file":"array-keywords.d.ts","sourceRoot":"","sources":["../../lib/keywords/array-keywords.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CA6MzD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"object-keywords.d.ts","sourceRoot":"","sources":["../../lib/keywords/object-keywords.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,GAAG,KAAK,CAwRlE,CAAC"}
1
+ {"version":3,"file":"object-keywords.d.ts","sourceRoot":"","sources":["../../lib/keywords/object-keywords.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,GAAG,KAAK,CA8XlE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"other-keywords.d.ts","sourceRoot":"","sources":["../../lib/keywords/other-keywords.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAyLzD,CAAC"}
1
+ {"version":3,"file":"other-keywords.d.ts","sourceRoot":"","sources":["../../lib/keywords/other-keywords.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAiOzD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"string-keywords.d.ts","sourceRoot":"","sources":["../../lib/keywords/string-keywords.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAiD1D,CAAC"}
1
+ {"version":3,"file":"string-keywords.d.ts","sourceRoot":"","sources":["../../lib/keywords/string-keywords.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3D,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CA2E1D,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../lib/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAGvC,eAAO,MAAM,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,KAAK,CA6CtD,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../lib/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAGvC,eAAO,MAAM,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,KAAK,CAiCtD,CAAC"}
package/dist/utils.d.ts CHANGED
@@ -28,19 +28,20 @@ export declare class ValidationError extends Error {
28
28
  }
29
29
  export interface DefineErrorOptions {
30
30
  item?: any;
31
- cause?: ValidationError;
31
+ cause?: ValidationError | true;
32
32
  data?: any;
33
33
  }
34
34
  export interface DefineErrorFunction {
35
- (message: string, options?: DefineErrorOptions): ValidationError;
35
+ (message: string, options?: DefineErrorOptions): ValidationError | void | true;
36
36
  }
37
- export declare function getDefinedErrorFunctionForKey(key: string, schema: CompiledSchema): DefineErrorFunction;
38
- export declare function deepEqual(obj: Array<any> | Record<string, any>, other: Array<any> | Record<string, any>): boolean;
37
+ export declare function getDefinedErrorFunctionForKey(key: string, schema: CompiledSchema, failFast: boolean): DefineErrorFunction;
38
+ export declare function hasChanged(prev: any, current: any): boolean;
39
39
  export declare function isObject(data: any): boolean;
40
40
  export declare function areCloseEnough(a: any, b: any, epsilon?: number): boolean;
41
41
  export declare function getUTF16Length(str: any): number;
42
- export declare function deepClone(obj: any): any;
42
+ export declare function deepClone<T>(obj: T, cloneClassInstances?: boolean, seen?: WeakMap<object, any>): T;
43
43
  export declare function isCompiledSchema(subSchema: any): subSchema is CompiledSchema;
44
44
  export declare function getNamedFunction<T>(name: string, fn: T): T;
45
+ export declare function resolvePath(root: any, path: string): any;
45
46
  export {};
46
47
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../lib/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAEzC,UAAU,SAAS;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB;AAED,qBAAa,eAAgB,SAAQ,KAAK;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,UAAU,EAAE,MAAM,CAAM;IACxB,YAAY,EAAE,MAAM,CAAM;IAC1B,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,MAAM,CAAC,EAAE,cAAc,CAAC;IAExB,OAAO,CAAC,SAAS;IAqBjB,QAAQ,IAAI,eAAe;IAI3B,OAAO,CAAC,QAAQ;IAiBhB,OAAO,IAAI,SAAS;IAKpB,OAAO;;;;CAOR;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ;AAED,MAAM,WAAW,mBAAmB;IAClC,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,eAAe,CAAC;CAClE;AAED,wBAAgB,6BAA6B,CAC3C,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,cAAc,uBAkBvB;AAED,wBAAgB,SAAS,CACvB,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACrC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,WAoCxC;AAED,wBAAgB,QAAQ,CAAC,IAAI,KAAA,WAE5B;AAED,wBAAgB,cAAc,CAAC,CAAC,KAAA,EAAE,CAAC,KAAA,EAAE,OAAO,SAAQ,WAEnD;AAED,wBAAgB,cAAc,CAAC,GAAG,KAAA,UAUjC;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,CAyBvC;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,GAAG,GAAG,SAAS,IAAI,cAAc,CAE5E;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,CAE1D"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../lib/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAEzC,UAAU,SAAS;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB;AAED,qBAAa,eAAgB,SAAQ,KAAK;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,UAAU,EAAE,MAAM,CAAM;IACxB,YAAY,EAAE,MAAM,CAAM;IAC1B,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,MAAM,CAAC,EAAE,cAAc,CAAC;IAExB,OAAO,CAAC,SAAS;IAqBjB,QAAQ,IAAI,eAAe;IAI3B,OAAO,CAAC,QAAQ;IAiBhB,OAAO,IAAI,SAAS;IAKpB,OAAO;;;;CAOR;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,KAAK,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IAC/B,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ;AAED,MAAM,WAAW,mBAAmB;IAClC,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAC1C,eAAe,GACf,IAAI,GACJ,IAAI,CAAC;CACV;AAGD,wBAAgB,6BAA6B,CAC3C,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,OAAO,uBAuBlB;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,WAwCjD;AAED,wBAAgB,QAAQ,CAAC,IAAI,KAAA,WAE5B;AAED,wBAAgB,cAAc,CAAC,CAAC,KAAA,EAAE,CAAC,KAAA,EAAE,OAAO,SAAQ,WAEnD;AAED,wBAAgB,cAAc,CAAC,GAAG,KAAA,UAUjC;AAED,wBAAgB,SAAS,CAAC,CAAC,EACzB,GAAG,EAAE,CAAC,EACN,mBAAmB,UAAQ,EAC3B,IAAI,uBAAgB,GACnB,CAAC,CA6HH;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,GAAG,GAAG,SAAS,IAAI,cAAc,CAE5E;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,CAE1D;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,GAAG,CAwCxD"}
package/lib/formats.ts CHANGED
@@ -1,102 +1,80 @@
1
1
  import { FormatFunction } from "./index";
2
2
 
3
+ // Regex helpers
4
+ const UUID_REGEX =
5
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
6
+ // ISO 8601 Duration (P3Y6M4DT12H30M5S)
7
+ const DURATION_REGEX =
8
+ /^P(?!$)((\d+Y)?(\d+M)?(\d+W)?(\d+D)?)(T(?=\d)(\d+H)?(\d+M)?(\d+S)?)?$/;
9
+ const DATE_TIME_REGEX =
10
+ /^(\d{4})-(0[0-9]|1[0-2])-(\d{2})T(0[0-9]|1\d|2[0-3]):([0-5]\d):((?:[0-5]\d|60))(?:.\d+)?(?:([+-])(0[0-9]|1\d|2[0-3]):([0-5]\d)|Z)?$/i;
11
+ const URI_REGEX = /^[a-zA-Z][a-zA-Z0-9+\-.]*:[^\s]*$/;
12
+ const EMAIL_REGEX =
13
+ /^(?!\.)(?!.*\.$)[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;
14
+ const IPV4_REGEX =
15
+ /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])$/;
16
+ const IPV6_REGEX = /(?:\s+|:::+|^\w{5,}|\w{5}$|^:{1}\w|\w:{1}$)/;
17
+ const IPV6_SHORT_REGEX = /^[0-9a-fA-F:.]*$/;
18
+ const IPV6_FULL_REGEX = /^(?:(?:[0-9a-fA-F]{1,4}:){7}(?:[0-9a-fA-F]{1,4}|:))$/;
19
+ const IPV6_INVALID_CHAR_REGEX = /(?:[0-9a-fA-F]{5,}|\D[0-9a-fA-F]{3}:)/;
20
+ const IPV6_FAST_FAIL_REGEX =
21
+ /^(?:(?:(?:[0-9a-fA-F]{1,4}(?::|$)){1,6}))|(?:::(?:[0-9a-fA-F]{1,4})){0,5}$/;
22
+ const HOSTNAME_REGEX =
23
+ /^[a-z0-9][a-z0-9-]{0,62}(?:\.[a-z0-9][a-z0-9-]{0,62})*[a-z0-9]$/i;
24
+ const DATE_REGEX = /^(\d{4})-(\d{2})-(\d{2})$/;
25
+ const JSON_POINTER_REGEX = /^\/(?:[^~]|~0|~1)*$/;
26
+ const RELATIVE_JSON_POINTER_REGEX = /^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/;
27
+ const TIME_REGEX =
28
+ /^([01]\d|2[0-3]):([0-5]\d):([0-5]\d)(\.\d+)?(Z|([+-])([01]\d|2[0-3]):([0-5]\d))$/;
29
+ const URI_REFERENCE_REGEX =
30
+ /^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#((?![^#]*\\)[^#]*))?/i;
31
+ const URI_TEMPLATE_REGEX = /^(?:[^{}]|\{[^}]+\})*$/;
32
+ const IRI_REGEX = /^[a-zA-Z][a-zA-Z0-9+\-.]*:[^\s]*$/;
33
+ const IRI_REFERENCE_REGEX =
34
+ /^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#((?![^#]*\\)[^#]*))?/i;
35
+ const IDN_EMAIL_REGEX = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
36
+ const IDN_HOSTNAME_REGEX = /^[^\s!@#$%^&*()_+\=\[\]{};':"\\|,<>\/?]+$/;
37
+ const BACK_SLASH_REGEX = /\\/;
38
+
39
+ const DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
40
+
3
41
  export const Formats: Record<string, FormatFunction | false> = {
4
42
  ["date-time"](data) {
5
- const match = data.match(
6
- /^(\d{4})-(0[0-9]|1[0-2])-(\d{2})T(0[0-9]|1\d|2[0-3]):([0-5]\d):((?:[0-5]\d|60))(?:.\d+)?(?:([+-])(0[0-9]|1\d|2[0-3]):([0-5]\d)|Z)?$/i
7
- );
8
-
43
+ const match = data.match(DATE_TIME_REGEX);
9
44
  if (!match) {
10
45
  return false;
11
46
  }
12
47
 
13
- let day = Number(match[3]);
48
+ const [, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr] = match;
49
+
50
+ const year = Number(yearStr);
51
+ const month = Number(monthStr);
52
+ const day = Number(dayStr);
53
+ const hour = Number(hourStr);
54
+ const minute = Number(minuteStr);
55
+ const second = Number(secondStr);
14
56
 
15
- if (match[2] === "02" && day > 29) {
57
+ // Mes 1–12
58
+ if (month < 1 || month > 12) {
16
59
  return false;
17
60
  }
18
-
19
- const [
20
- ,
21
- yearStr,
22
- monthStr,
23
- ,
24
- hourStr,
25
- minuteStr,
26
- secondStr,
27
- timezoneSign,
28
- timezoneHourStr,
29
- timezoneMinuteStr
30
- ] = match;
31
-
32
- let year = Number(yearStr);
33
- let month = Number(monthStr);
34
- let hour = Number(hourStr);
35
- let minute = Number(minuteStr);
36
- let second = Number(secondStr);
37
-
38
- if (timezoneSign === "-" || timezoneSign === "+") {
39
- const timezoneHour = Number(timezoneHourStr);
40
- const timezoneMinute = Number(timezoneMinuteStr);
41
-
42
- if (timezoneSign === "-") {
43
- hour += timezoneHour;
44
- minute += timezoneMinute;
45
- } else if (timezoneSign === "+") {
46
- hour -= timezoneHour;
47
- minute -= timezoneMinute;
48
- }
49
-
50
- if (minute > 59) {
51
- hour += 1;
52
- minute -= 60;
53
- } else if (minute < 0) {
54
- hour -= 1;
55
- minute += 60;
56
- }
57
-
58
- if (hour > 23) {
59
- day += 1;
60
- hour -= 24;
61
- } else if (hour < 0) {
62
- day -= 1;
63
- hour += 24;
64
- }
65
-
66
- if (day > 31) {
67
- month += 1;
68
- day -= 31;
69
- } else if (day < 1) {
70
- month -= 1;
71
- day += 31;
72
- }
73
-
74
- if (month > 12) {
75
- year += 1;
76
- month -= 12;
77
- } else if (month < 1) {
78
- year -= 1;
79
- month += 12;
80
- }
81
-
82
- if (year < 0) {
83
- return false;
84
- }
61
+ // Día >= 1
62
+ if (day < 1) {
63
+ return false;
85
64
  }
86
65
 
87
- const daysInMonth = [31, , 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
88
66
  const maxDays =
89
67
  month === 2
90
68
  ? year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0)
91
69
  ? 29
92
70
  : 28
93
- : daysInMonth[month - 1];
71
+ : DAYS_IN_MONTH[month - 1];
94
72
 
95
- if (day > maxDays) {
73
+ if (!maxDays || day > maxDays) {
96
74
  return false;
97
75
  }
98
76
 
99
- // Leap seconds
77
+ // Leap seconds (si quieres seguir permitiendo 60)
100
78
  if (second === 60 && (minute !== 59 || hour !== 23)) {
101
79
  return false;
102
80
  }
@@ -104,19 +82,14 @@ export const Formats: Record<string, FormatFunction | false> = {
104
82
  return true;
105
83
  },
106
84
  uri(data) {
107
- return /^[a-zA-Z][a-zA-Z0-9+\-.]*:[^\s]*$/.test(data);
85
+ return URI_REGEX.test(data);
108
86
  },
109
87
  email(data) {
110
- return /^(?!\.)(?!.*\.$)[a-z0-9!#$%&'*+/=?^_`{|}~-]{1,20}(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]{1,21}){0,2}@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,60}[a-z0-9])?){0,3}$/i.test(
111
- data
112
- );
88
+ return EMAIL_REGEX.test(data);
113
89
  },
114
90
  ipv4(data) {
115
91
  // Matches a string formed by 4 numbers between 0 and 255 separated by dots without leading zeros
116
- // /^((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])$/
117
- return /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])$/.test(
118
- data
119
- );
92
+ return IPV4_REGEX.test(data);
120
93
  },
121
94
 
122
95
  // ipv6: isMyIpValid({ version: 6 }),
@@ -125,10 +98,7 @@ export const Formats: Record<string, FormatFunction | false> = {
125
98
  return true;
126
99
  }
127
100
 
128
- if (
129
- data.indexOf(":") === -1 ||
130
- /(?:\s+|:::+|^\w{5,}|\w{5}$|^:{1}\w|\w:{1}$)/.test(data)
131
- ) {
101
+ if (data.indexOf(":") === -1 || IPV6_REGEX.test(data)) {
132
102
  return false;
133
103
  }
134
104
 
@@ -138,11 +108,7 @@ export const Formats: Record<string, FormatFunction | false> = {
138
108
  if (hasIpv4) {
139
109
  addressParts = data.split(":");
140
110
  const ipv4Part = addressParts.pop();
141
- if (
142
- !/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])$/.test(
143
- ipv4Part
144
- )
145
- ) {
111
+ if (!IPV4_REGEX.test(ipv4Part)) {
146
112
  return false;
147
113
  }
148
114
  }
@@ -155,21 +121,15 @@ export const Formats: Record<string, FormatFunction | false> = {
155
121
  return false;
156
122
  }
157
123
 
158
- if (!/^[0-9a-fA-F:.]*$/.test(ipv6Part)) {
124
+ if (!IPV6_SHORT_REGEX.test(ipv6Part)) {
159
125
  return false;
160
126
  }
161
127
 
162
- return /^(?:(?:(?:[0-9a-fA-F]{1,4}(?::|$)){1,6}))|(?:::(?:[0-9a-fA-F]{1,4})){0,5}$/.test(
163
- ipv6Part
164
- );
128
+ return IPV6_FAST_FAIL_REGEX.test(ipv6Part);
165
129
  }
166
130
 
167
- const isIpv6Valid =
168
- /^(?:(?:[0-9a-fA-F]{1,4}:){7}(?:[0-9a-fA-F]{1,4}|:))$/.test(ipv6Part);
169
-
170
- const hasInvalidChar = /(?:[0-9a-fA-F]{5,}|\D[0-9a-fA-F]{3}:)/.test(
171
- ipv6Part
172
- );
131
+ const isIpv6Valid = IPV6_FULL_REGEX.test(ipv6Part);
132
+ const hasInvalidChar = IPV6_INVALID_CHAR_REGEX.test(ipv6Part);
173
133
 
174
134
  if (hasIpv4) {
175
135
  return isIpv6Valid || !hasInvalidChar;
@@ -179,16 +139,34 @@ export const Formats: Record<string, FormatFunction | false> = {
179
139
  },
180
140
 
181
141
  hostname(data) {
182
- return /^[a-z0-9][a-z0-9-]{0,62}(?:\.[a-z0-9][a-z0-9-]{0,62})*[a-z0-9]$/i.test(
183
- data
184
- );
142
+ return HOSTNAME_REGEX.test(data);
185
143
  },
186
144
  date(data) {
187
- if (/^(\d{4})-(\d{2})-(\d{2})$/.test(data) === false) {
145
+ const match = DATE_REGEX.exec(data);
146
+ if (!match) {
147
+ return false;
148
+ }
149
+
150
+ const [, yearStr, monthStr, dayStr] = match;
151
+ const year = Number(yearStr);
152
+ const month = Number(monthStr);
153
+ const day = Number(dayStr);
154
+
155
+ if (month < 1 || month > 12) {
188
156
  return false;
189
157
  }
158
+ if (day < 1) {
159
+ return false;
160
+ }
161
+
162
+ const maxDays =
163
+ month === 2
164
+ ? year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0)
165
+ ? 29
166
+ : 28
167
+ : DAYS_IN_MONTH[month - 1];
190
168
 
191
- return !isNaN(new Date(data).getTime());
169
+ return !!maxDays && day <= maxDays;
192
170
  },
193
171
  regex(data) {
194
172
  try {
@@ -203,40 +181,57 @@ export const Formats: Record<string, FormatFunction | false> = {
203
181
  return true;
204
182
  }
205
183
 
206
- return /^\/(?:[^~]|~0|~1)*$/.test(data);
184
+ return JSON_POINTER_REGEX.test(data);
207
185
  },
208
186
  "relative-json-pointer"(data) {
209
187
  if (data === "") {
210
188
  return true;
211
189
  }
212
190
 
213
- return /^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/.test(data);
191
+ return RELATIVE_JSON_POINTER_REGEX.test(data);
214
192
  },
215
193
  time(data) {
216
- return /^(\d{2}):(\d{2}):(\d{2})(\.\d+)?(Z|([+-])(\d{2}):(\d{2}))$/.test(
217
- data
218
- );
194
+ return TIME_REGEX.test(data);
219
195
  },
220
196
  "uri-reference"(data) {
221
- if (/\\/.test(data)) {
197
+ if (BACK_SLASH_REGEX.test(data)) {
222
198
  return false;
223
199
  }
224
200
 
225
- return /^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#((?![^#]*\\)[^#]*))?/i.test(
226
- data
227
- );
201
+ return URI_REFERENCE_REGEX.test(data);
228
202
  },
203
+
229
204
  "uri-template"(data) {
230
- return /^(?:(?:https?:\/\/[\w.-]+)?\/?)?[\w- ;,.\/?%&=]*(?:\{[\w-]+(?::\d+)?\}[\w- ;,.\/?%&=]*)*\/?$/.test(
231
- data
232
- );
233
- },
234
-
235
- // Not supported yet
236
- duration: false,
237
- uuid: false,
238
- "idn-email": false,
239
- "idn-hostname": false,
240
- iri: false,
241
- "iri-reference": false
205
+ return URI_TEMPLATE_REGEX.test(data);
206
+ },
207
+
208
+ duration(data) {
209
+ return DURATION_REGEX.test(data);
210
+ },
211
+
212
+ uuid(data) {
213
+ return UUID_REGEX.test(data);
214
+ },
215
+
216
+ // IRI is like URI but allows Unicode. We reuse a permissive logic.
217
+ iri(data) {
218
+ return IRI_REGEX.test(data);
219
+ },
220
+
221
+ "iri-reference"(data) {
222
+ if (BACK_SLASH_REGEX.test(data)) {
223
+ return false;
224
+ }
225
+ return IRI_REFERENCE_REGEX.test(data);
226
+ },
227
+
228
+ // Best-effort structural validation for IDN (no punycode/tables)
229
+ "idn-email"(data) {
230
+ return IDN_EMAIL_REGEX.test(data);
231
+ },
232
+
233
+ "idn-hostname"(data) {
234
+ // Allows unicode, forbids spaces and typical invalid URL chars
235
+ return IDN_HOSTNAME_REGEX.test(data);
236
+ }
242
237
  };