typebox 1.3.11 → 1.3.12
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/build/format/date.d.mts +1 -1
- package/build/format/date.mjs +1 -1
- package/build/format/date_time.d.mts +2 -2
- package/build/format/date_time.mjs +3 -3
- package/build/format/email.d.mts +1 -1
- package/build/format/email.mjs +2 -2
- package/build/format/hostname.mjs +2 -10
- package/build/format/idn_email.d.mts +1 -1
- package/build/format/idn_email.mjs +3 -3
- package/build/format/idn_hostname.d.mts +1 -0
- package/build/format/idn_hostname.mjs +3 -11
- package/build/format/idna/format/bidi.d.mts +3 -0
- package/build/format/idna/format/bidi.mjs +98 -0
- package/build/format/idna/format/puny.d.mts +3 -0
- package/build/format/idna/format/puny.mjs +144 -0
- package/build/format/idna/hostname.d.mts +1 -0
- package/build/format/idna/hostname.mjs +16 -0
- package/build/format/idna/idn-hostname.d.mts +1 -0
- package/build/format/idna/idn-hostname.mjs +23 -0
- package/build/format/idna/index.d.mts +2 -0
- package/build/format/idna/index.mjs +2 -0
- package/build/format/idna/label/ascii.d.mts +1 -0
- package/build/format/idna/label/ascii.mjs +9 -0
- package/build/format/idna/label/puny.d.mts +1 -0
- package/build/format/idna/label/puny.mjs +28 -0
- package/build/format/idna/label/unicode.d.mts +1 -0
- package/build/format/idna/label/unicode.mjs +106 -0
- package/build/format/idna/pattern/pattern.d.mts +30 -0
- package/build/format/idna/pattern/pattern.mjs +70 -0
- package/build/format/ipv4.d.mts +0 -1
- package/build/format/ipv4.mjs +2 -32
- package/build/format/ipv6.mjs +2 -61
- package/build/format/iri.d.mts +1 -1
- package/build/format/iri.mjs +2 -8
- package/build/format/iri_reference.d.mts +2 -2
- package/build/format/iri_reference.mjs +9 -55
- package/build/format/regex.d.mts +1 -2
- package/build/format/regex.mjs +2 -6
- package/build/format/time.d.mts +2 -2
- package/build/format/time.mjs +19 -14
- package/build/format/uri.mjs +2 -129
- package/build/format/uri_template.d.mts +0 -1
- package/build/format/uri_template.mjs +1 -2
- package/build/format/url.d.mts +2 -2
- package/build/format/url.mjs +3 -4
- package/build/guard/guard.mjs +2 -2
- package/build/guard/string.d.mts +2 -2
- package/build/guard/string.mjs +6 -6
- package/build/schema/engine/_regexp.d.mts +2 -0
- package/build/schema/engine/_regexp.mjs +4 -0
- package/build/schema/engine/_stack.mjs +1 -1
- package/build/schema/engine/additionalProperties.mjs +4 -3
- package/build/schema/engine/pattern.mjs +3 -2
- package/build/schema/engine/patternProperties.mjs +4 -3
- package/build/schema/engine/schema.mjs +13 -4
- package/build/schema/resolve/ref.d.mts +1 -0
- package/build/schema/resolve/ref.mjs +13 -3
- package/build/schema/types/index.d.mts +0 -2
- package/build/schema/types/index.mjs +0 -2
- package/package.json +1 -1
- package/build/format/_idna.d.mts +0 -2
- package/build/format/_idna.mjs +0 -246
- package/build/format/_puny.d.mts +0 -1
- package/build/format/_puny.mjs +0 -77
- package/build/schema/types/contentEncoding.d.mts +0 -9
- package/build/schema/types/contentEncoding.mjs +0 -13
- package/build/schema/types/contentMediaType.d.mts +0 -9
- package/build/schema/types/contentMediaType.mjs +0 -13
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// deno-fmt-ignore-file
|
|
2
|
+
// -------------------------------------------------------------------
|
|
3
|
+
// Validation Rules
|
|
4
|
+
// -------------------------------------------------------------------
|
|
5
|
+
export const RE_RULE_HYPHEN_PLACEMENT = /^(?!-).*(?<!-)$/;
|
|
6
|
+
export const RE_RULE_NOT_RESERVED_ACE = /^(?!..--)/;
|
|
7
|
+
export const RE_ASCII_LDH = /^[a-zA-Z0-9-]*$/;
|
|
8
|
+
// -------------------------------------------------------------------
|
|
9
|
+
// ASCII & Encoding
|
|
10
|
+
// -------------------------------------------------------------------
|
|
11
|
+
export const RE_ASCII = /^\p{ASCII}*$/u;
|
|
12
|
+
export const RE_NON_ASCII = /[^\p{ASCII}]/u;
|
|
13
|
+
// -------------------------------------------------------------------
|
|
14
|
+
// Structural & Unicode Controls
|
|
15
|
+
// -------------------------------------------------------------------
|
|
16
|
+
export const RE_HIGH_SURROGATE = /[\u{d800}-\u{dbff}]/u;
|
|
17
|
+
export const RE_REGIONAL_INDICATOR = /[\u{1f1e6}-\u{1f1ff}]/u;
|
|
18
|
+
export const RE_VARIATION_SELECTOR = /[\u{fe00}-\u{fe0f}]/u;
|
|
19
|
+
export const RE_ZERO_WIDTH_JOINER = /\u{200d}/u;
|
|
20
|
+
// -------------------------------------------------------------------
|
|
21
|
+
// Numbers & Digits
|
|
22
|
+
// -------------------------------------------------------------------
|
|
23
|
+
export const RE_ASCII_DIGIT = /[0-9]/;
|
|
24
|
+
export const RE_ARABIC_INDIC_DIGIT = /[\u{0660}-\u{0669}]/u;
|
|
25
|
+
export const RE_EXT_ARABIC_INDIC_DIGIT = /[\u{06f0}-\u{06f9}]/u;
|
|
26
|
+
// -------------------------------------------------------------------
|
|
27
|
+
// Separators
|
|
28
|
+
// -------------------------------------------------------------------
|
|
29
|
+
export const RE_COMMON_SEPARATOR = /[\u{002e}\u{002c}\u{003a}\u{002f}]/u;
|
|
30
|
+
export const RE_EUROPEAN_SEPARATOR = /[\u{002d}\u{002b}]/u;
|
|
31
|
+
// -------------------------------------------------------------------
|
|
32
|
+
// General Categories
|
|
33
|
+
// -------------------------------------------------------------------
|
|
34
|
+
export const RE_MARK_NONSPACING = /\p{Mn}/u;
|
|
35
|
+
export const RE_MARK_SPACING_COMBINING = /\p{Mc}/u;
|
|
36
|
+
export const RE_MARK_ENCLOSING = /\p{Me}/u;
|
|
37
|
+
export const RE_COMBINING_MARK = /[\p{Mn}\p{Mc}\p{Me}]/u;
|
|
38
|
+
export const RE_LETTER = /\p{L}/u;
|
|
39
|
+
export const RE_NUMBER_DECIMAL = /\p{Nd}/u;
|
|
40
|
+
// -------------------------------------------------------------------
|
|
41
|
+
// Scripts
|
|
42
|
+
// -------------------------------------------------------------------
|
|
43
|
+
export const RE_SCRIPT_GREEK = /\p{Script=Greek}/u;
|
|
44
|
+
export const RE_SCRIPT_HEBREW = /\p{Script=Hebrew}/u;
|
|
45
|
+
export const RE_SCRIPT_JAPANESE = /[\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Han}]/u;
|
|
46
|
+
export const RE_SCRIPT_ARABIC_LETTER = /[\p{Script=Arabic}\p{Script=Syriac}\p{Script=Thaana}\p{Script=Mandaic}]/u;
|
|
47
|
+
// -------------------------------------------------------------------
|
|
48
|
+
// RFC Specifications & Context Exceptions
|
|
49
|
+
// -------------------------------------------------------------------
|
|
50
|
+
export const RE_VIRAMA = /[\u{094d}\u{09cd}\u{0a4d}\u{0acd}\u{0b4d}\u{0bcd}\u{0c4d}\u{0ccd}\u{0d3b}\u{0d3c}\u{0d4d}\u{0dca}\u{1b44}\u{1baa}\u{1bab}\u{a9c0}\u{11046}\u{1107f}\u{110b9}\u{11133}\u{11134}\u{111c0}\u{11235}\u{1134d}\u{11442}\u{114c2}\u{115bf}\u{1163f}\u{116b6}\u{11c3f}\u{11d44}\u{11d45}]/u;
|
|
51
|
+
export const RE_RFC5892_DISALLOWED = /[\u{0640}\u{07fa}\u{302e}\u{302f}\u{3031}\u{3032}\u{3033}\u{3034}\u{3035}\u{303b}]/u;
|
|
52
|
+
export const RE_CONTEXTO_EXCEPTIONS = /[\u{00b7}\u{0375}\u{05f3}\u{05f4}\u{200c}\u{200d}\u{30fb}]/u;
|
|
53
|
+
export const RE_PVALID_EXCEPTIONS = /[\u{00df}\u{03c2}\u{06fd}\u{06fe}\u{0f0b}\u{3007}]/u;
|
|
54
|
+
// -------------------------------------------------------------------
|
|
55
|
+
// Composite Patterns
|
|
56
|
+
// -------------------------------------------------------------------
|
|
57
|
+
export const RE_EUROPEAN_NUMBER = new RegExp([
|
|
58
|
+
RE_ASCII_DIGIT,
|
|
59
|
+
RE_EXT_ARABIC_INDIC_DIGIT
|
|
60
|
+
].map((regexp) => regexp.source).join('|'), 'u');
|
|
61
|
+
export const RE_PERMITTED_CATEGORY = new RegExp([
|
|
62
|
+
RE_LETTER,
|
|
63
|
+
RE_EUROPEAN_SEPARATOR,
|
|
64
|
+
RE_COMMON_SEPARATOR,
|
|
65
|
+
RE_NUMBER_DECIMAL,
|
|
66
|
+
RE_MARK_NONSPACING,
|
|
67
|
+
RE_MARK_SPACING_COMBINING,
|
|
68
|
+
RE_CONTEXTO_EXCEPTIONS,
|
|
69
|
+
RE_PVALID_EXCEPTIONS
|
|
70
|
+
].map((regexp) => regexp.source).join('|'), 'u');
|
package/build/format/ipv4.d.mts
CHANGED
package/build/format/ipv4.mjs
CHANGED
|
@@ -1,38 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
// Ranged Fast Path
|
|
3
|
-
// ------------------------------------------------------------------
|
|
4
|
-
/* Returns true if the value is a IPV4 address from index range offsets */
|
|
5
|
-
export function IsIPv4Internal(value, start, end) {
|
|
6
|
-
let dots = 0;
|
|
7
|
-
let num = 0;
|
|
8
|
-
let digits = 0;
|
|
9
|
-
let leading = 0;
|
|
10
|
-
for (let i = start; i < end; i++) {
|
|
11
|
-
const ch = value.charCodeAt(i);
|
|
12
|
-
if (ch === 46) { // '.'
|
|
13
|
-
if (digits === 0 || num > 255 || (leading === 48 && digits > 1))
|
|
14
|
-
return false;
|
|
15
|
-
dots++;
|
|
16
|
-
num = 0;
|
|
17
|
-
digits = 0;
|
|
18
|
-
leading = 0;
|
|
19
|
-
}
|
|
20
|
-
else if (ch >= 48 && ch <= 57) { // '0'-'9'
|
|
21
|
-
if (digits === 0)
|
|
22
|
-
leading = ch;
|
|
23
|
-
num = num * 10 + (ch - 48);
|
|
24
|
-
digits++;
|
|
25
|
-
}
|
|
26
|
-
else {
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return dots === 3 && digits > 0 && num <= 255 && !(leading === 48 && digits > 1);
|
|
31
|
-
}
|
|
1
|
+
const IPv4 = /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/;
|
|
32
2
|
/**
|
|
33
3
|
* Returns true if the value is a IPV4 address
|
|
34
4
|
* @specification http://tools.ietf.org/html/rfc2673#section-3.2
|
|
35
5
|
*/
|
|
36
6
|
export function IsIPv4(value) {
|
|
37
|
-
return
|
|
7
|
+
return IPv4.test(value);
|
|
38
8
|
}
|
package/build/format/ipv6.mjs
CHANGED
|
@@ -1,67 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
function InRange(ch) {
|
|
3
|
-
return (ch >= 48 && ch <= 57) || // 0-9
|
|
4
|
-
(ch >= 65 && ch <= 70) || // A-F
|
|
5
|
-
(ch >= 97 && ch <= 102); // a-f
|
|
6
|
-
}
|
|
1
|
+
const IPv6 = /^(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:)?[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)$/i;
|
|
7
2
|
/**
|
|
8
3
|
* Returns true if the value is an IPv6 address
|
|
9
4
|
* @specification http://tools.ietf.org/html/rfc2373#section-2.2
|
|
10
5
|
*/
|
|
11
6
|
export function IsIPv6(value) {
|
|
12
|
-
|
|
13
|
-
if (length === 0)
|
|
14
|
-
return false;
|
|
15
|
-
let groups = 0;
|
|
16
|
-
let compressed = false;
|
|
17
|
-
let i = 0;
|
|
18
|
-
// handle leading '::'
|
|
19
|
-
if (value.charCodeAt(0) === 58 && value.charCodeAt(1) === 58) {
|
|
20
|
-
if (length === 2)
|
|
21
|
-
return true; // '::' is valid
|
|
22
|
-
compressed = true;
|
|
23
|
-
i = 2;
|
|
24
|
-
}
|
|
25
|
-
while (i < length) {
|
|
26
|
-
// read hex digits
|
|
27
|
-
let digits = 0;
|
|
28
|
-
const start = i;
|
|
29
|
-
while (i < length && InRange(value.charCodeAt(i))) {
|
|
30
|
-
i++;
|
|
31
|
-
digits++;
|
|
32
|
-
}
|
|
33
|
-
if (digits === 0)
|
|
34
|
-
return false;
|
|
35
|
-
const next = value.charCodeAt(i);
|
|
36
|
-
// check for embedded IPv4 at the end
|
|
37
|
-
if (next === 46) { // '.'
|
|
38
|
-
if (!IsIPv4Internal(value, start, length))
|
|
39
|
-
return false;
|
|
40
|
-
groups += 2;
|
|
41
|
-
i = length;
|
|
42
|
-
break;
|
|
43
|
-
}
|
|
44
|
-
if (digits > 4)
|
|
45
|
-
return false;
|
|
46
|
-
groups++;
|
|
47
|
-
if (i === length)
|
|
48
|
-
break;
|
|
49
|
-
// expect ':' separator
|
|
50
|
-
if (next !== 58)
|
|
51
|
-
return false;
|
|
52
|
-
i++;
|
|
53
|
-
// check for '::' compression
|
|
54
|
-
if (value.charCodeAt(i) === 58) {
|
|
55
|
-
if (compressed)
|
|
56
|
-
return false; // only one '::' allowed
|
|
57
|
-
// check for ':::'
|
|
58
|
-
if (value.charCodeAt(i + 1) === 58)
|
|
59
|
-
return false;
|
|
60
|
-
compressed = true;
|
|
61
|
-
i++;
|
|
62
|
-
if (i === length)
|
|
63
|
-
break; // trailing '::'
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return compressed ? groups <= 7 : groups === 8;
|
|
7
|
+
return IPv6.test(value);
|
|
67
8
|
}
|
package/build/format/iri.d.mts
CHANGED
package/build/format/iri.mjs
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Returns true if the value is a Iri
|
|
3
|
-
* @specification
|
|
3
|
+
* @specification https://datatracker.ietf.org/doc/html/rfc3987
|
|
4
4
|
*/
|
|
5
5
|
export function IsIri(value) {
|
|
6
|
-
|
|
7
|
-
new URL(value);
|
|
8
|
-
return true;
|
|
9
|
-
}
|
|
10
|
-
catch {
|
|
11
|
-
return false;
|
|
12
|
-
}
|
|
6
|
+
return URL.canParse(value);
|
|
13
7
|
}
|
|
@@ -1,60 +1,14 @@
|
|
|
1
1
|
// deno-lint-ignore-file no-control-regex
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
return true;
|
|
7
|
-
}
|
|
8
|
-
catch {
|
|
9
|
-
return false;
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
// deno-coverage-ignore-stop
|
|
2
|
+
// Rejects unencoded spaces (0x20), backslashes, control characters (0x00-0x1F, 0x7F), or malformed percent-encodings
|
|
3
|
+
const InvalidIriChars = /[\x00-\x20\x7F\\]|%(?![0-9a-fA-F]{2})/;
|
|
4
|
+
// Detects malformed scheme/authority patterns lacking a colon (e.g. "httpx//example.com")
|
|
5
|
+
const MalformedScheme = /^[a-zA-Z][a-zA-Z0-9+\-.]*\/\//;
|
|
13
6
|
/**
|
|
14
|
-
* Returns true if the value is
|
|
15
|
-
* @specification
|
|
7
|
+
* Returns true if the value is an IRI reference
|
|
8
|
+
* @specification https://tools.ietf.org/html/rfc3987
|
|
16
9
|
*/
|
|
17
10
|
export function IsIriReference(value) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
if (value.includes('\\')) { // Backslash (U+005C)
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
// ASCII control characters (U+0000-U+001F and U+007F)
|
|
27
|
-
if (/[\x00-\x1F\x7F]/.test(value)) {
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
// 2. Check for invalid percent-encoding
|
|
31
|
-
// A percent sign '%' must always be followed by exactly two hexadecimal characters.
|
|
32
|
-
// This regex finds a '%' that is NOT followed by two hexadecimal characters.
|
|
33
|
-
// - `%` matches a literal percent sign.
|
|
34
|
-
// - `(?!...)` is a negative lookahead, asserting that what follows is NOT.
|
|
35
|
-
// - `[0-9a-fA-F]{2}` matches exactly two hexadecimal digits.
|
|
36
|
-
// So, if a '%' is found that is not immediately followed by two hex digits, this regex will match.
|
|
37
|
-
if (/%(?![0-9a-fA-F]{2})/.test(value)) {
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
// 3. Handle empty string (valid relative reference to the current document)
|
|
41
|
-
if (value === '') {
|
|
42
|
-
return true;
|
|
43
|
-
}
|
|
44
|
-
// 4. Determine if it's attempting to be an absolute IRI or a relative IRI, and parse.
|
|
45
|
-
const colonIndex = value.indexOf(':');
|
|
46
|
-
const hasValidSchemePrefix = colonIndex > 0 && // Colon must not be at the very beginning (e.g., ":foo")
|
|
47
|
-
/^[a-zA-Z][a-zA-Z0-9+\-.]*$/.test(value.substring(0, colonIndex));
|
|
48
|
-
if (hasValidSchemePrefix) {
|
|
49
|
-
return TryUrl(value);
|
|
50
|
-
}
|
|
51
|
-
else {
|
|
52
|
-
// This handles cases like `httpx//example.com/path` which look like malformed absolute URIs.
|
|
53
|
-
const looksLikeMalformedSchemeAndAuthority = value.match(/^([a-zA-Z][a-zA-Z0-9+\-.]*)(\/\/)/);
|
|
54
|
-
if (looksLikeMalformedSchemeAndAuthority && colonIndex === -1) {
|
|
55
|
-
return false;
|
|
56
|
-
}
|
|
57
|
-
// Otherwise, it's a relative IRI reference.
|
|
58
|
-
return TryUrl(value);
|
|
59
|
-
}
|
|
11
|
+
return (!InvalidIriChars.test(value) &&
|
|
12
|
+
!MalformedScheme.test(value) &&
|
|
13
|
+
URL.canParse(value, 'http://example.com'));
|
|
60
14
|
}
|
package/build/format/regex.d.mts
CHANGED
package/build/format/regex.mjs
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Returns true if the value is a regular expression string pattern
|
|
3
|
-
* @specification
|
|
4
|
-
* @source ajv-formats
|
|
3
|
+
* @specification https://ecma-international.org/ecma-262
|
|
5
4
|
*/
|
|
6
5
|
export function IsRegex(value) {
|
|
7
|
-
if (value.length === 0) {
|
|
8
|
-
return false;
|
|
9
|
-
}
|
|
10
6
|
try {
|
|
11
|
-
new RegExp(value);
|
|
7
|
+
new RegExp(value, 'u');
|
|
12
8
|
return true;
|
|
13
9
|
}
|
|
14
10
|
catch {
|
package/build/format/time.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Returns true if the value is
|
|
3
|
-
* @specification
|
|
2
|
+
* Returns true if the value is an ISO time string
|
|
3
|
+
* @specification https://datatracker.ietf.org/doc/html/rfc3339
|
|
4
4
|
*/
|
|
5
5
|
export declare function IsTime(value: string, strictTimeZone?: boolean): boolean;
|
package/build/format/time.mjs
CHANGED
|
@@ -1,27 +1,32 @@
|
|
|
1
|
-
const TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?
|
|
1
|
+
const TIME = /^(\d\d):(\d\d):(\d\d)(?:\.\d+)?(?:([Zz])|([+-])(\d\d):(\d\d))?$/;
|
|
2
2
|
/**
|
|
3
|
-
* Returns true if the value is
|
|
4
|
-
* @specification
|
|
3
|
+
* Returns true if the value is an ISO time string
|
|
4
|
+
* @specification https://datatracker.ietf.org/doc/html/rfc3339
|
|
5
5
|
*/
|
|
6
6
|
export function IsTime(value, strictTimeZone = true) {
|
|
7
7
|
const matches = TIME.exec(value);
|
|
8
8
|
if (!matches)
|
|
9
9
|
return false;
|
|
10
|
+
// Require timezone offset or 'Z'/'z' when strictTimeZone is true
|
|
11
|
+
if (strictTimeZone && !matches[4] && !matches[5])
|
|
12
|
+
return false;
|
|
10
13
|
const hr = +matches[1];
|
|
11
14
|
const min = +matches[2];
|
|
12
15
|
const sec = +matches[3];
|
|
13
|
-
|
|
14
|
-
const tzH = +(matches[5] || 0); // tzH is now matches[5]
|
|
15
|
-
const tzM = +(matches[6] || 0); // tzM is now matches[6]
|
|
16
|
-
if (tzH > 23 || tzM > 59)
|
|
17
|
-
return false; // Check for valid hour/minute range in offset
|
|
18
|
-
if (strictTimeZone && !matches[4] && value.toLowerCase().indexOf('z') === -1) {
|
|
19
|
-
// If strictTimeZone is true, and neither 'Z' nor a '+/-' offset was found
|
|
16
|
+
if (hr > 23 || min > 59 || sec > 60)
|
|
20
17
|
return false;
|
|
18
|
+
if (matches[5]) {
|
|
19
|
+
const tzH = +matches[6];
|
|
20
|
+
const tzM = +matches[7];
|
|
21
|
+
if (tzH > 23 || tzM > 59)
|
|
22
|
+
return false;
|
|
21
23
|
}
|
|
22
|
-
if (
|
|
24
|
+
if (sec < 60)
|
|
23
25
|
return true;
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
26
|
+
// Leap second handling: must normalize to 23:59:60 UTC (1439 total UTC minutes)
|
|
27
|
+
const tzSign = matches[5] === '-' ? -1 : 1;
|
|
28
|
+
const tzH = +(matches[6] || 0);
|
|
29
|
+
const tzM = +(matches[7] || 0);
|
|
30
|
+
const totalUtcMin = (hr * 60 + min) - tzSign * (tzH * 60 + tzM);
|
|
31
|
+
return (totalUtcMin % 1440 + 1440) % 1440 === 1439;
|
|
27
32
|
}
|
package/build/format/uri.mjs
CHANGED
|
@@ -1,135 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
return (ch >= 97 && ch <= 122) || (ch >= 65 && ch <= 90); // a-z, A-Z
|
|
3
|
-
}
|
|
4
|
-
function IsAlphaNumeric(ch) {
|
|
5
|
-
return IsAlpha(ch) || (ch >= 48 && ch <= 57); // a-z, A-Z, 0-9
|
|
6
|
-
}
|
|
7
|
-
function IsHex(ch) {
|
|
8
|
-
return (ch >= 48 && ch <= 57) || // 0-9
|
|
9
|
-
(ch >= 65 && ch <= 70) || // A-F
|
|
10
|
-
(ch >= 97 && ch <= 102); // a-f
|
|
11
|
-
}
|
|
12
|
-
function IsSchemeChar(ch) {
|
|
13
|
-
return IsAlphaNumeric(ch) ||
|
|
14
|
-
ch === 43 || ch === 45 || ch === 46; // '+', '-', '.'
|
|
15
|
-
}
|
|
16
|
-
function IsUnreserved(ch) {
|
|
17
|
-
return IsAlphaNumeric(ch) ||
|
|
18
|
-
ch === 45 || ch === 46 || // '-', '.'
|
|
19
|
-
ch === 95 || ch === 126; // '_', '~'
|
|
20
|
-
}
|
|
21
|
-
function IsSubDelim(ch) {
|
|
22
|
-
return ch === 33 || ch === 36 || ch === 38 || ch === 39 ||
|
|
23
|
-
ch === 40 || ch === 41 || ch === 42 || ch === 43 ||
|
|
24
|
-
ch === 44 || ch === 59 || ch === 61; // ! $ & ' ( ) * + , ; =
|
|
25
|
-
}
|
|
26
|
-
function IsPchar(ch) {
|
|
27
|
-
return IsUnreserved(ch) || IsSubDelim(ch) || ch === 58 || ch === 64; // ':', '@'
|
|
28
|
-
}
|
|
1
|
+
const Uri = /^[a-z][a-z0-9+\-.]*:(?:\/\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:[a-f0-9:.]|v[0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)+\]|(?:[a-z0-9\-._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?(?:\?(?:[a-z0-9\-._~!$&'()*+,;=:@\/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'()*+,;=:@\/?]|%[0-9a-f]{2})*)?$/i;
|
|
29
2
|
/**
|
|
30
3
|
* Returns true if the value matches RFC 3986 URI syntax.
|
|
31
4
|
* @specification https://tools.ietf.org/html/rfc3986
|
|
32
5
|
*/
|
|
33
6
|
export function IsUri(value) {
|
|
34
|
-
|
|
35
|
-
if (length === 0)
|
|
36
|
-
return false;
|
|
37
|
-
// Scheme: must start with a letter
|
|
38
|
-
if (!IsAlpha(value.charCodeAt(0)))
|
|
39
|
-
return false;
|
|
40
|
-
// Scheme: continues until ':'
|
|
41
|
-
let i = 1;
|
|
42
|
-
while (i < length) {
|
|
43
|
-
const ch = value.charCodeAt(i);
|
|
44
|
-
if (ch === 58)
|
|
45
|
-
break; // ':'
|
|
46
|
-
if (!IsSchemeChar(ch))
|
|
47
|
-
return false;
|
|
48
|
-
i++;
|
|
49
|
-
}
|
|
50
|
-
// Ensure scheme is terminated by ':'
|
|
51
|
-
if (value.charCodeAt(i) !== 58)
|
|
52
|
-
return false;
|
|
53
|
-
i++;
|
|
54
|
-
// Authority: optional '//'
|
|
55
|
-
if (value.charCodeAt(i) === 47 && value.charCodeAt(i + 1) === 47) {
|
|
56
|
-
i += 2;
|
|
57
|
-
// Userinfo: check for '@' before path/query/fragment delimiters
|
|
58
|
-
const authorityStart = i;
|
|
59
|
-
let atPos = -1;
|
|
60
|
-
for (let j = i; j < length; j++) {
|
|
61
|
-
const ch = value.charCodeAt(j);
|
|
62
|
-
if (ch === 64) {
|
|
63
|
-
atPos = j;
|
|
64
|
-
break;
|
|
65
|
-
} // '@'
|
|
66
|
-
if (ch === 47 || ch === 63 || ch === 35)
|
|
67
|
-
break; // '/', '?', '#'
|
|
68
|
-
}
|
|
69
|
-
if (atPos !== -1) {
|
|
70
|
-
// Userinfo: validate allowed chars and percent-encoding
|
|
71
|
-
for (let j = authorityStart; j < atPos; j++) {
|
|
72
|
-
const ch = value.charCodeAt(j);
|
|
73
|
-
if (ch === 91 || ch === 93)
|
|
74
|
-
return false; // No '[' or ']' in userinfo
|
|
75
|
-
if (ch === 37) { // '%' percent-encoding
|
|
76
|
-
if (j + 2 >= atPos || !IsHex(value.charCodeAt(j + 1)) || !IsHex(value.charCodeAt(j + 2)))
|
|
77
|
-
return false;
|
|
78
|
-
j += 2;
|
|
79
|
-
}
|
|
80
|
-
else if (!IsUnreserved(ch) && !IsSubDelim(ch) && ch !== 58)
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
i = atPos + 1;
|
|
84
|
-
}
|
|
85
|
-
// Host: IP-literal [addr] or reg-name
|
|
86
|
-
if (value.charCodeAt(i) === 91) { // '['
|
|
87
|
-
i++;
|
|
88
|
-
while (i < length && value.charCodeAt(i) !== 93)
|
|
89
|
-
i++;
|
|
90
|
-
if (value.charCodeAt(i) !== 93)
|
|
91
|
-
return false; // ']'
|
|
92
|
-
i++;
|
|
93
|
-
}
|
|
94
|
-
else {
|
|
95
|
-
// Host: validate ASCII; skip validation for non-ASCII (Unicode hosts)
|
|
96
|
-
while (i < length) {
|
|
97
|
-
const ch = value.charCodeAt(i);
|
|
98
|
-
if (ch === 47 || ch === 63 || ch === 35 || ch === 58)
|
|
99
|
-
break;
|
|
100
|
-
if (ch < 128 && !IsUnreserved(ch) && !IsSubDelim(ch))
|
|
101
|
-
return false;
|
|
102
|
-
i++;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
// Port: optional digits after ':'
|
|
106
|
-
if (value.charCodeAt(i) === 58) { // ':'
|
|
107
|
-
i++;
|
|
108
|
-
while (i < length) {
|
|
109
|
-
const ch = value.charCodeAt(i);
|
|
110
|
-
if (ch === 47 || ch === 63 || ch === 35)
|
|
111
|
-
break;
|
|
112
|
-
if (ch < 48 || ch > 57)
|
|
113
|
-
return false; // 0-9
|
|
114
|
-
i++;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
// Path, Query, and Fragment
|
|
119
|
-
while (i < length) {
|
|
120
|
-
const ch = value.charCodeAt(i);
|
|
121
|
-
if (ch === 37) { // '%' percent-encoding
|
|
122
|
-
if (i + 2 >= length || !IsHex(value.charCodeAt(i + 1)) || !IsHex(value.charCodeAt(i + 2)))
|
|
123
|
-
return false;
|
|
124
|
-
i += 2;
|
|
125
|
-
}
|
|
126
|
-
else if (ch > 127) {
|
|
127
|
-
return false; // URI is ASCII-only
|
|
128
|
-
}
|
|
129
|
-
else if (!(IsPchar(ch) || ch === 47 || ch === 63 || ch === 35)) {
|
|
130
|
-
return false; // '/', '?', '#'
|
|
131
|
-
}
|
|
132
|
-
i++;
|
|
133
|
-
}
|
|
134
|
-
return true;
|
|
7
|
+
return Uri.test(value);
|
|
135
8
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
// deno-lint-ignore-file
|
|
2
|
-
const UriTemplate = /^(?:(?:[^\x00-\x20"
|
|
2
|
+
const UriTemplate = /^(?:(?:[^\x00-\x20"<>%\\^`{|}\x7f]|%[0-9a-f]{2})|\{[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+(?:\.(?:[a-z0-9_]|%[0-9a-f]{2})+)*(?::[1-9][0-9]{0,3}|\*)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+(?:\.(?:[a-z0-9_]|%[0-9a-f]{2})+)*(?::[1-9][0-9]{0,3}|\*)?)*\})*$/i;
|
|
3
3
|
/**
|
|
4
4
|
* Returns true if the value is a uri template
|
|
5
5
|
* @specification
|
|
6
|
-
* @source ajv-formats
|
|
7
6
|
*/
|
|
8
7
|
export function IsUriTemplate(value) {
|
|
9
8
|
return UriTemplate.test(value);
|
package/build/format/url.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Returns true if the value is a Url
|
|
3
|
-
* @specification
|
|
4
|
-
* @
|
|
3
|
+
* @specification https://datatracker.ietf.org/doc/html/rfc3986
|
|
4
|
+
* @specification https://datatracker.ietf.org/doc/html/rfc3987
|
|
5
5
|
*/
|
|
6
6
|
export declare function IsUrl(value: string): boolean;
|
package/build/format/url.mjs
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
const Url = /^(?:https?|ftp):\/\/(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u{00a1}-\u{ffff}]+-)*[a-z0-9\u{00a1}-\u{ffff}]+)(?:\.(?:[a-z0-9\u{00a1}-\u{ffff}]+-)*[a-z0-9\u{00a1}-\u{ffff}]+)*(?:\.(?:[a-z\u{00a1}-\u{ffff}]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/iu;
|
|
2
1
|
/**
|
|
3
2
|
* Returns true if the value is a Url
|
|
4
|
-
* @specification
|
|
5
|
-
* @
|
|
3
|
+
* @specification https://datatracker.ietf.org/doc/html/rfc3986
|
|
4
|
+
* @specification https://datatracker.ietf.org/doc/html/rfc3987
|
|
6
5
|
*/
|
|
7
6
|
export function IsUrl(value) {
|
|
8
|
-
return
|
|
7
|
+
return URL.canParse(value);
|
|
9
8
|
}
|
package/build/guard/guard.mjs
CHANGED
|
@@ -129,11 +129,11 @@ export function GraphemeCount(value) {
|
|
|
129
129
|
}
|
|
130
130
|
/** Returns true if the string has at most the given number of graphemes */
|
|
131
131
|
export function IsMaxLength(value, length) {
|
|
132
|
-
return String.
|
|
132
|
+
return String.IsMaxLength(value, length);
|
|
133
133
|
}
|
|
134
134
|
/** Returns true if the string has at least the given number of graphemes */
|
|
135
135
|
export function IsMinLength(value, length) {
|
|
136
|
-
return String.
|
|
136
|
+
return String.IsMinLength(value, length);
|
|
137
137
|
}
|
|
138
138
|
// --------------------------------------------------------------------------
|
|
139
139
|
// Array
|
package/build/guard/string.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Returns the number of grapheme clusters in a string */
|
|
2
2
|
export declare function GraphemeCount(value: string): number;
|
|
3
3
|
/** Fast check for minimum grapheme length, falls back to full check if needed */
|
|
4
|
-
export declare function
|
|
4
|
+
export declare function IsMinLength(value: string, minLength: number): boolean;
|
|
5
5
|
/** Fast check for maximum grapheme length, falls back to full check if needed */
|
|
6
|
-
export declare function
|
|
6
|
+
export declare function IsMaxLength(value: string, maxLength: number): boolean;
|
package/build/guard/string.mjs
CHANGED
|
@@ -106,7 +106,7 @@ export function GraphemeCount(value) {
|
|
|
106
106
|
// IsMinLength
|
|
107
107
|
// --------------------------------------------------------------------------
|
|
108
108
|
/** Checks if a string has at least a minimum number of grapheme clusters */
|
|
109
|
-
function
|
|
109
|
+
function IsMinLengthSegmented(value, minLength) {
|
|
110
110
|
// ----------------------------------------------------------------
|
|
111
111
|
// Inaccessible via public interface (review)
|
|
112
112
|
//
|
|
@@ -129,7 +129,7 @@ function IsMinLength(value, minLength) {
|
|
|
129
129
|
// IsMaxLength
|
|
130
130
|
// --------------------------------------------------------------------------
|
|
131
131
|
/** Checks if a string has at most a maximum number of grapheme clusters */
|
|
132
|
-
function
|
|
132
|
+
function IsMaxLengthSegmented(value, maxLength) {
|
|
133
133
|
let count = 0;
|
|
134
134
|
let index = 0;
|
|
135
135
|
while (index < value.length) {
|
|
@@ -144,13 +144,13 @@ function IsMaxLength(value, maxLength) {
|
|
|
144
144
|
// IsMinLengthFast
|
|
145
145
|
// --------------------------------------------------------------------------
|
|
146
146
|
/** Fast check for minimum grapheme length, falls back to full check if needed */
|
|
147
|
-
export function
|
|
147
|
+
export function IsMinLength(value, minLength) {
|
|
148
148
|
if (minLength === 0)
|
|
149
149
|
return true; // 0-length
|
|
150
150
|
let index = 0;
|
|
151
151
|
while (index < value.length) {
|
|
152
152
|
if (IsGraphemeCodePoint(value.charCodeAt(index))) {
|
|
153
|
-
return
|
|
153
|
+
return IsMinLengthSegmented(value, minLength);
|
|
154
154
|
}
|
|
155
155
|
index++;
|
|
156
156
|
if (index >= minLength)
|
|
@@ -162,11 +162,11 @@ export function IsMinLengthFast(value, minLength) {
|
|
|
162
162
|
// IsMaxLengthFast
|
|
163
163
|
// --------------------------------------------------------------------------
|
|
164
164
|
/** Fast check for maximum grapheme length, falls back to full check if needed */
|
|
165
|
-
export function
|
|
165
|
+
export function IsMaxLength(value, maxLength) {
|
|
166
166
|
let index = 0;
|
|
167
167
|
while (index < value.length) {
|
|
168
168
|
if (IsGraphemeCodePoint(value.charCodeAt(index))) {
|
|
169
|
-
return
|
|
169
|
+
return IsMaxLengthSegmented(value, maxLength);
|
|
170
170
|
}
|
|
171
171
|
index++;
|
|
172
172
|
if (index > maxLength)
|
|
@@ -22,7 +22,7 @@ export class Stack {
|
|
|
22
22
|
// Base
|
|
23
23
|
// ----------------------------------------------------------------
|
|
24
24
|
BaseURL() {
|
|
25
|
-
return this.ids.reduce((result, schema) => new URL(schema.$id, result),
|
|
25
|
+
return this.ids.reduce((result, schema) => new URL(schema.$id, result), Resolve.DefaultBase);
|
|
26
26
|
}
|
|
27
27
|
Base() {
|
|
28
28
|
return this.ids[this.ids.length - 1] ?? this.schema;
|