schema-shield 1.0.0 → 1.0.1

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.
Files changed (39) hide show
  1. package/README.md +38 -12
  2. package/dist/formats.d.ts.map +1 -1
  3. package/dist/index.d.ts +14 -3
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +1445 -447
  6. package/dist/index.min.js +1 -1
  7. package/dist/index.min.js.map +1 -1
  8. package/dist/index.mjs +1445 -447
  9. package/dist/keywords/array-keywords.d.ts.map +1 -1
  10. package/dist/keywords/object-keywords.d.ts.map +1 -1
  11. package/dist/keywords/other-keywords.d.ts.map +1 -1
  12. package/dist/keywords/string-keywords.d.ts.map +1 -1
  13. package/dist/types.d.ts.map +1 -1
  14. package/dist/utils/deep-freeze.d.ts +5 -0
  15. package/dist/utils/deep-freeze.d.ts.map +1 -0
  16. package/dist/utils/has-changed.d.ts +2 -0
  17. package/dist/utils/has-changed.d.ts.map +1 -0
  18. package/dist/utils/index.d.ts +5 -0
  19. package/dist/utils/index.d.ts.map +1 -0
  20. package/dist/{utils.d.ts → utils/main-utils.d.ts} +3 -6
  21. package/dist/utils/main-utils.d.ts.map +1 -0
  22. package/dist/utils/pattern-matcher.d.ts +3 -0
  23. package/dist/utils/pattern-matcher.d.ts.map +1 -0
  24. package/lib/formats.ts +402 -84
  25. package/lib/index.ts +494 -46
  26. package/lib/keywords/array-keywords.ts +215 -21
  27. package/lib/keywords/number-keywords.ts +1 -1
  28. package/lib/keywords/object-keywords.ts +218 -113
  29. package/lib/keywords/other-keywords.ts +229 -76
  30. package/lib/keywords/string-keywords.ts +97 -7
  31. package/lib/types.ts +4 -5
  32. package/lib/utils/deep-freeze.ts +208 -0
  33. package/lib/utils/has-changed.ts +51 -0
  34. package/lib/utils/index.ts +4 -0
  35. package/lib/utils/main-utils.ts +190 -0
  36. package/lib/utils/pattern-matcher.ts +66 -0
  37. package/package.json +1 -1
  38. package/dist/utils.d.ts.map +0 -1
  39. package/lib/utils.ts +0 -362
@@ -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,CA6MzD,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;AA+F3C,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAkTzD,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,CA8XlE,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;AA8F3C,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,GAAG,KAAK,CA2YlE,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,CAiOzD,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;AAkD3C,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CA0UzD,CAAC"}
@@ -1 +1 @@
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
+ {"version":3,"file":"string-keywords.d.ts","sourceRoot":"","sources":["../../lib/keywords/string-keywords.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,eAAe,EAAE,MAAM,UAAU,CAAC;AAM3D,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAiK1D,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,CAiCtD,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../lib/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,eAAO,MAAM,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,KAAK,CAiCtD,CAAC"}
@@ -0,0 +1,5 @@
1
+ export declare function deepFreeze(obj: any, freezeClassInstances?: boolean, seen?: WeakSet<object>): any;
2
+ declare function isPlainObject(value: any): boolean;
3
+ export { isPlainObject };
4
+ export declare function deepCloneUnfreeze<T>(obj: T, cloneClassInstances?: boolean, seen?: WeakMap<object, any>): T;
5
+ //# sourceMappingURL=deep-freeze.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deep-freeze.d.ts","sourceRoot":"","sources":["../../lib/utils/deep-freeze.ts"],"names":[],"mappings":"AAAA,wBAAgB,UAAU,CACxB,GAAG,EAAE,GAAG,EACR,oBAAoB,GAAE,OAAe,EACrC,IAAI,kBAAgB,GACnB,GAAG,CAkCL;AAED,iBAAS,aAAa,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAO1C;AAED,OAAO,EAAE,aAAa,EAAE,CAAC;AAuBzB,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,GAAG,EAAE,CAAC,EACN,mBAAmB,UAAQ,EAC3B,IAAI,uBAAgB,GACnB,CAAC,CAmIH"}
@@ -0,0 +1,2 @@
1
+ export declare function hasChanged(prev: any, current: any): boolean;
2
+ //# sourceMappingURL=has-changed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"has-changed.d.ts","sourceRoot":"","sources":["../../lib/utils/has-changed.ts"],"names":[],"mappings":"AAEA,wBAAgB,UAAU,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,WAgDjD"}
@@ -0,0 +1,5 @@
1
+ export * from "./main-utils";
2
+ export * from "./has-changed";
3
+ export * from "./deep-freeze";
4
+ export { deepCloneUnfreeze as deepClone } from "./deep-freeze";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,iBAAiB,IAAI,SAAS,EAAE,MAAM,eAAe,CAAC"}
@@ -1,4 +1,4 @@
1
- import { CompiledSchema } from "./index";
1
+ import { CompiledSchema } from "../index";
2
2
  interface ErrorTree {
3
3
  message: string;
4
4
  keyword: string;
@@ -35,13 +35,10 @@ export interface DefineErrorFunction {
35
35
  (message: string, options?: DefineErrorOptions): ValidationError | void | true;
36
36
  }
37
37
  export declare function getDefinedErrorFunctionForKey(key: string, schema: CompiledSchema, failFast: boolean): DefineErrorFunction;
38
- export declare function hasChanged(prev: any, current: any): boolean;
39
- export declare function isObject(data: any): boolean;
40
- export declare function areCloseEnough(a: any, b: any, epsilon?: number): boolean;
41
38
  export declare function getUTF16Length(str: any): number;
42
- export declare function deepClone<T>(obj: T, cloneClassInstances?: boolean, seen?: WeakMap<object, any>): T;
43
39
  export declare function isCompiledSchema(subSchema: any): subSchema is CompiledSchema;
44
40
  export declare function getNamedFunction<T>(name: string, fn: T): T;
45
41
  export declare function resolvePath(root: any, path: string): any;
42
+ export declare function areCloseEnough(a: number, b: number, epsilon?: number): boolean;
46
43
  export {};
47
- //# sourceMappingURL=utils.d.ts.map
44
+ //# sourceMappingURL=main-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main-utils.d.ts","sourceRoot":"","sources":["../../lib/utils/main-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE1C,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,CACE,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,kBAAkB,GAC3B,eAAe,GAAG,IAAI,GAAG,IAAI,CAAC;CAClC;AAGD,wBAAgB,6BAA6B,CAC3C,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,OAAO,uBAuBlB;AAED,wBAAgB,cAAc,CAAC,GAAG,KAAA,UAUjC;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,GAAG,GAAG,SAAS,IAAI,cAAc,CAO5E;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;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,SAAQ,GAAG,OAAO,CAE7E"}
@@ -0,0 +1,3 @@
1
+ export type CompiledPatternMatcher = RegExp | ((value: string) => boolean);
2
+ export declare function compilePatternMatcher(pattern: string): CompiledPatternMatcher;
3
+ //# sourceMappingURL=pattern-matcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pattern-matcher.d.ts","sourceRoot":"","sources":["../../lib/utils/pattern-matcher.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,sBAAsB,GAAG,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;AAE3E,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,sBAAsB,CAuD7E"}
package/lib/formats.ts CHANGED
@@ -6,53 +6,409 @@ const UUID_REGEX =
6
6
  // ISO 8601 Duration (P3Y6M4DT12H30M5S)
7
7
  const DURATION_REGEX =
8
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
9
  const URI_REGEX = /^[a-zA-Z][a-zA-Z0-9+\-.]*:[^\s]*$/;
12
10
  const EMAIL_REGEX =
13
11
  /^(?!\.)(?!.*\.$)[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
12
  const HOSTNAME_REGEX =
23
13
  /^[a-z0-9][a-z0-9-]{0,62}(?:\.[a-z0-9][a-z0-9-]{0,62})*[a-z0-9]$/i;
24
14
  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
15
  const TIME_REGEX =
28
16
  /^([01]\d|2[0-3]):([0-5]\d):([0-5]\d)(\.\d+)?(Z|([+-])([01]\d|2[0-3]):([0-5]\d))$/;
29
17
  const URI_REFERENCE_REGEX =
30
18
  /^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#((?![^#]*\\)[^#]*))?/i;
31
- const URI_TEMPLATE_REGEX = /^(?:[^{}]|\{[^}]+\})*$/;
32
19
  const IRI_REGEX = /^[a-zA-Z][a-zA-Z0-9+\-.]*:[^\s]*$/;
33
20
  const IRI_REFERENCE_REGEX =
34
21
  /^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#((?![^#]*\\)[^#]*))?/i;
35
22
  const IDN_EMAIL_REGEX = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
36
23
  const IDN_HOSTNAME_REGEX = /^[^\s!@#$%^&*()_+\=\[\]{};':"\\|,<>\/?]+$/;
37
- const BACK_SLASH_REGEX = /\\/;
38
24
 
39
25
  const DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
40
26
 
27
+ function isDigitCharCode(code: number) {
28
+ return code >= 48 && code <= 57;
29
+ }
30
+
31
+ function parseTwoDigits(data: string, index: number) {
32
+ const first = data.charCodeAt(index) - 48;
33
+ const second = data.charCodeAt(index + 1) - 48;
34
+
35
+ if (first < 0 || first > 9 || second < 0 || second > 9) {
36
+ return -1;
37
+ }
38
+
39
+ return first * 10 + second;
40
+ }
41
+
42
+ function parseFourDigits(data: string, index: number) {
43
+ const a = data.charCodeAt(index) - 48;
44
+ const b = data.charCodeAt(index + 1) - 48;
45
+ const c = data.charCodeAt(index + 2) - 48;
46
+ const d = data.charCodeAt(index + 3) - 48;
47
+
48
+ if (
49
+ a < 0 ||
50
+ a > 9 ||
51
+ b < 0 ||
52
+ b > 9 ||
53
+ c < 0 ||
54
+ c > 9 ||
55
+ d < 0 ||
56
+ d > 9
57
+ ) {
58
+ return -1;
59
+ }
60
+
61
+ return a * 1000 + b * 100 + c * 10 + d;
62
+ }
63
+
64
+ function isValidIpv4Range(data: string, start: number, end: number) {
65
+ let segmentCount = 0;
66
+ let segmentStart = start;
67
+
68
+ for (let i = start; i <= end; i++) {
69
+ if (i !== end && data.charCodeAt(i) !== 46) {
70
+ continue;
71
+ }
72
+
73
+ const segmentLength = i - segmentStart;
74
+ if (segmentLength < 1 || segmentLength > 3) {
75
+ return false;
76
+ }
77
+
78
+ if (segmentLength > 1 && data.charCodeAt(segmentStart) === 48) {
79
+ return false;
80
+ }
81
+
82
+ let value = 0;
83
+ for (let j = segmentStart; j < i; j++) {
84
+ const digit = data.charCodeAt(j) - 48;
85
+ if (digit < 0 || digit > 9) {
86
+ return false;
87
+ }
88
+
89
+ value = value * 10 + digit;
90
+ }
91
+
92
+ if (value > 255) {
93
+ return false;
94
+ }
95
+
96
+ segmentCount++;
97
+ segmentStart = i + 1;
98
+ }
99
+
100
+ return segmentCount === 4;
101
+ }
102
+
103
+ function isValidIpv4(data: string) {
104
+ return isValidIpv4Range(data, 0, data.length);
105
+ }
106
+
107
+ function isHexCharCode(code: number) {
108
+ return (
109
+ (code >= 48 && code <= 57) ||
110
+ (code >= 65 && code <= 70) ||
111
+ (code >= 97 && code <= 102)
112
+ );
113
+ }
114
+
115
+ function isValidIpv6(data: string) {
116
+ const length = data.length;
117
+ if (length === 0) {
118
+ return false;
119
+ }
120
+
121
+ let hasColon = false;
122
+ let hasDoubleColon = false;
123
+ let hextetCount = 0;
124
+ let i = 0;
125
+
126
+ while (i < length) {
127
+ if (data.charCodeAt(i) === 58) {
128
+ hasColon = true;
129
+
130
+ if (i + 1 < length && data.charCodeAt(i + 1) === 58) {
131
+ if (hasDoubleColon) {
132
+ return false;
133
+ }
134
+
135
+ hasDoubleColon = true;
136
+ i += 2;
137
+
138
+ if (i === length) {
139
+ break;
140
+ }
141
+
142
+ continue;
143
+ }
144
+
145
+ return false;
146
+ }
147
+
148
+ const segmentStart = i;
149
+ let segmentLength = 0;
150
+
151
+ while (i < length && isHexCharCode(data.charCodeAt(i))) {
152
+ segmentLength++;
153
+ if (segmentLength > 4) {
154
+ return false;
155
+ }
156
+
157
+ i++;
158
+ }
159
+
160
+ if (segmentLength === 0) {
161
+ return false;
162
+ }
163
+
164
+ if (i < length && data.charCodeAt(i) === 46) {
165
+ if (!hasColon) {
166
+ return false;
167
+ }
168
+
169
+ if (!isValidIpv4Range(data, segmentStart, length)) {
170
+ return false;
171
+ }
172
+
173
+ if (hasDoubleColon) {
174
+ return hextetCount < 6;
175
+ }
176
+
177
+ return hextetCount === 6;
178
+ }
179
+
180
+ hextetCount++;
181
+ if (hextetCount > 8) {
182
+ return false;
183
+ }
184
+
185
+ if (i === length) {
186
+ break;
187
+ }
188
+
189
+ if (data.charCodeAt(i) !== 58) {
190
+ return false;
191
+ }
192
+
193
+ hasColon = true;
194
+ i++;
195
+
196
+ if (i === length) {
197
+ return false;
198
+ }
199
+
200
+ if (data.charCodeAt(i) === 58) {
201
+ if (hasDoubleColon) {
202
+ return false;
203
+ }
204
+
205
+ hasDoubleColon = true;
206
+ i++;
207
+
208
+ if (i === length) {
209
+ break;
210
+ }
211
+ }
212
+ }
213
+
214
+ if (!hasColon) {
215
+ return false;
216
+ }
217
+
218
+ if (hasDoubleColon) {
219
+ return hextetCount < 8;
220
+ }
221
+
222
+ return hextetCount === 8;
223
+ }
224
+
225
+ function isValidJsonPointer(data: string) {
226
+ if (data === "") {
227
+ return true;
228
+ }
229
+
230
+ if (data.charCodeAt(0) !== 47) {
231
+ return false;
232
+ }
233
+
234
+ for (let i = 1; i < data.length; i++) {
235
+ if (data.charCodeAt(i) !== 126) {
236
+ continue;
237
+ }
238
+
239
+ const next = data.charCodeAt(i + 1);
240
+ if (next !== 48 && next !== 49) {
241
+ return false;
242
+ }
243
+
244
+ i++;
245
+ }
246
+
247
+ return true;
248
+ }
249
+
250
+ function isValidRelativeJsonPointer(data: string) {
251
+ if (data.length === 0) {
252
+ return true;
253
+ }
254
+
255
+ let i = 0;
256
+ while (i < data.length) {
257
+ const code = data.charCodeAt(i);
258
+ if (code < 48 || code > 57) {
259
+ break;
260
+ }
261
+ i++;
262
+ }
263
+
264
+ if (i === 0) {
265
+ return false;
266
+ }
267
+
268
+ if (i === data.length) {
269
+ return true;
270
+ }
271
+
272
+ if (data.charCodeAt(i) === 35) {
273
+ return i + 1 === data.length;
274
+ }
275
+
276
+ if (data.charCodeAt(i) !== 47) {
277
+ return false;
278
+ }
279
+
280
+ for (i = i + 1; i < data.length; i++) {
281
+ if (data.charCodeAt(i) !== 126) {
282
+ continue;
283
+ }
284
+
285
+ const next = data.charCodeAt(i + 1);
286
+ if (next !== 48 && next !== 49) {
287
+ return false;
288
+ }
289
+
290
+ i++;
291
+ }
292
+
293
+ return true;
294
+ }
295
+
296
+ function isValidUriTemplate(data: string) {
297
+ for (let i = 0; i < data.length; i++) {
298
+ const code = data.charCodeAt(i);
299
+
300
+ if (code === 125) {
301
+ return false;
302
+ }
303
+
304
+ if (code !== 123) {
305
+ continue;
306
+ }
307
+
308
+ const closeIndex = data.indexOf("}", i + 1);
309
+ if (closeIndex === -1 || closeIndex === i + 1) {
310
+ return false;
311
+ }
312
+
313
+ i = closeIndex;
314
+ }
315
+
316
+ return true;
317
+ }
318
+
41
319
  export const Formats: Record<string, FormatFunction | false> = {
42
320
  ["date-time"](data) {
43
- const match = data.match(DATE_TIME_REGEX);
44
- if (!match) {
321
+ const length = data.length;
322
+ if (length < 19) {
45
323
  return false;
46
324
  }
47
325
 
48
- const [, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr] = match;
326
+ if (
327
+ data.charCodeAt(4) !== 45 ||
328
+ data.charCodeAt(7) !== 45 ||
329
+ data.charCodeAt(13) !== 58 ||
330
+ data.charCodeAt(16) !== 58
331
+ ) {
332
+ return false;
333
+ }
49
334
 
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);
335
+ const tCode = data.charCodeAt(10);
336
+ if (tCode !== 84 && tCode !== 116) {
337
+ return false;
338
+ }
339
+
340
+ const year = parseFourDigits(data, 0);
341
+ const month = parseTwoDigits(data, 5);
342
+ const day = parseTwoDigits(data, 8);
343
+ const hour = parseTwoDigits(data, 11);
344
+ const minute = parseTwoDigits(data, 14);
345
+ const second = parseTwoDigits(data, 17);
346
+
347
+ if (
348
+ year < 0 ||
349
+ month < 0 ||
350
+ day < 0 ||
351
+ hour < 0 ||
352
+ minute < 0 ||
353
+ second < 0
354
+ ) {
355
+ return false;
356
+ }
357
+
358
+ if (hour > 23 || minute > 59 || second > 60) {
359
+ return false;
360
+ }
361
+
362
+ let cursor = 19;
363
+ let offsetSign: "+" | "-" | null = null;
364
+ let offsetHour = 0;
365
+ let offsetMinute = 0;
366
+
367
+ if (cursor < length && data.charCodeAt(cursor) === 46) {
368
+ cursor++;
369
+ const fracStart = cursor;
370
+ while (cursor < length && isDigitCharCode(data.charCodeAt(cursor))) {
371
+ cursor++;
372
+ }
373
+
374
+ if (cursor === fracStart) {
375
+ return false;
376
+ }
377
+ }
378
+
379
+ if (cursor < length) {
380
+ const tzCode = data.charCodeAt(cursor);
381
+
382
+ if (tzCode === 90 || tzCode === 122) {
383
+ cursor++;
384
+ } else if (tzCode === 43 || tzCode === 45) {
385
+ offsetSign = tzCode === 43 ? "+" : "-";
386
+
387
+ if (cursor + 6 > length || data.charCodeAt(cursor + 3) !== 58) {
388
+ return false;
389
+ }
390
+
391
+ offsetHour = parseTwoDigits(data, cursor + 1);
392
+ offsetMinute = parseTwoDigits(data, cursor + 4);
393
+
394
+ if (
395
+ offsetHour < 0 ||
396
+ offsetMinute < 0 ||
397
+ offsetHour > 23 ||
398
+ offsetMinute > 59
399
+ ) {
400
+ return false;
401
+ }
402
+
403
+ cursor += 6;
404
+ } else {
405
+ return false;
406
+ }
407
+ }
408
+
409
+ if (cursor !== length) {
410
+ return false;
411
+ }
56
412
 
57
413
  // Mes 1–12
58
414
  if (month < 1 || month > 12) {
@@ -74,9 +430,22 @@ export const Formats: Record<string, FormatFunction | false> = {
74
430
  return false;
75
431
  }
76
432
 
77
- // Leap seconds (si quieres seguir permitiendo 60)
78
- if (second === 60 && (minute !== 59 || hour !== 23)) {
79
- return false;
433
+ if (second === 60) {
434
+ let utcTotalMinutes = hour * 60 + minute;
435
+
436
+ if (offsetSign) {
437
+ const offsetTotalMinutes = offsetHour * 60 + offsetMinute;
438
+ utcTotalMinutes +=
439
+ offsetSign === "+" ? -offsetTotalMinutes : offsetTotalMinutes;
440
+ utcTotalMinutes %= 24 * 60;
441
+ if (utcTotalMinutes < 0) {
442
+ utcTotalMinutes += 24 * 60;
443
+ }
444
+ }
445
+
446
+ if (utcTotalMinutes !== 23 * 60 + 59) {
447
+ return false;
448
+ }
80
449
  }
81
450
 
82
451
  return true;
@@ -88,54 +457,11 @@ export const Formats: Record<string, FormatFunction | false> = {
88
457
  return EMAIL_REGEX.test(data);
89
458
  },
90
459
  ipv4(data) {
91
- // Matches a string formed by 4 numbers between 0 and 255 separated by dots without leading zeros
92
- return IPV4_REGEX.test(data);
460
+ return isValidIpv4(data);
93
461
  },
94
462
 
95
- // ipv6: isMyIpValid({ version: 6 }),
96
463
  ipv6(data) {
97
- if (data === "::") {
98
- return true;
99
- }
100
-
101
- if (data.indexOf(":") === -1 || IPV6_REGEX.test(data)) {
102
- return false;
103
- }
104
-
105
- const hasIpv4 = data.indexOf(".") !== -1;
106
- let addressParts = data;
107
-
108
- if (hasIpv4) {
109
- addressParts = data.split(":");
110
- const ipv4Part = addressParts.pop();
111
- if (!IPV4_REGEX.test(ipv4Part)) {
112
- return false;
113
- }
114
- }
115
-
116
- const isShortened = data.indexOf("::") !== -1;
117
- const ipv6Part = hasIpv4 ? addressParts.join(":") : data;
118
-
119
- if (isShortened) {
120
- if (ipv6Part.split("::").length - 1 > 1) {
121
- return false;
122
- }
123
-
124
- if (!IPV6_SHORT_REGEX.test(ipv6Part)) {
125
- return false;
126
- }
127
-
128
- return IPV6_FAST_FAIL_REGEX.test(ipv6Part);
129
- }
130
-
131
- const isIpv6Valid = IPV6_FULL_REGEX.test(ipv6Part);
132
- const hasInvalidChar = IPV6_INVALID_CHAR_REGEX.test(ipv6Part);
133
-
134
- if (hasIpv4) {
135
- return isIpv6Valid || !hasInvalidChar;
136
- }
137
-
138
- return isIpv6Valid && !hasInvalidChar;
464
+ return isValidIpv6(data);
139
465
  },
140
466
 
141
467
  hostname(data) {
@@ -177,24 +503,16 @@ export const Formats: Record<string, FormatFunction | false> = {
177
503
  }
178
504
  },
179
505
  "json-pointer"(data) {
180
- if (data === "") {
181
- return true;
182
- }
183
-
184
- return JSON_POINTER_REGEX.test(data);
506
+ return isValidJsonPointer(data);
185
507
  },
186
508
  "relative-json-pointer"(data) {
187
- if (data === "") {
188
- return true;
189
- }
190
-
191
- return RELATIVE_JSON_POINTER_REGEX.test(data);
509
+ return isValidRelativeJsonPointer(data);
192
510
  },
193
511
  time(data) {
194
512
  return TIME_REGEX.test(data);
195
513
  },
196
514
  "uri-reference"(data) {
197
- if (BACK_SLASH_REGEX.test(data)) {
515
+ if (data.includes("\\")) {
198
516
  return false;
199
517
  }
200
518
 
@@ -202,7 +520,7 @@ export const Formats: Record<string, FormatFunction | false> = {
202
520
  },
203
521
 
204
522
  "uri-template"(data) {
205
- return URI_TEMPLATE_REGEX.test(data);
523
+ return isValidUriTemplate(data);
206
524
  },
207
525
 
208
526
  duration(data) {
@@ -219,7 +537,7 @@ export const Formats: Record<string, FormatFunction | false> = {
219
537
  },
220
538
 
221
539
  "iri-reference"(data) {
222
- if (BACK_SLASH_REGEX.test(data)) {
540
+ if (data.includes("\\")) {
223
541
  return false;
224
542
  }
225
543
  return IRI_REFERENCE_REGEX.test(data);