typebox 1.1.15 → 1.1.17
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/_idna.d.mts +2 -0
- package/build/format/_idna.mjs +216 -0
- package/build/format/_puny.d.mts +1 -0
- package/build/format/_puny.mjs +74 -0
- package/build/format/duration.d.mts +2 -2
- package/build/format/duration.mjs +3 -3
- package/build/format/hostname.d.mts +3 -1
- package/build/format/hostname.mjs +10 -26
- package/build/format/idn-hostname.d.mts +4 -2
- package/build/format/idn-hostname.mjs +9 -109
- package/build/format/uri-reference.d.mts +2 -3
- package/build/format/uri-reference.mjs +3 -4
- package/build/guard/string.d.mts +0 -4
- package/build/guard/string.mjs +8 -2
- package/build/schema/engine/_context.d.mts +5 -2
- package/build/schema/engine/_context.mjs +39 -8
- package/build/schema/engine/_externals.d.mts +2 -4
- package/build/schema/engine/_externals.mjs +9 -18
- package/build/schema/engine/additionalItems.mjs +4 -4
- package/build/schema/engine/additionalProperties.mjs +4 -4
- package/build/schema/engine/items.mjs +7 -7
- package/build/schema/engine/patternProperties.mjs +4 -4
- package/build/schema/engine/prefixItems.mjs +4 -4
- package/build/schema/engine/properties.mjs +4 -4
- package/build/schema/engine/schema.d.mts +3 -0
- package/build/schema/engine/schema.mjs +11 -0
- package/build/type/types/schema.d.mts +1 -24
- package/package.json +1 -1
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import * as Puny from './_puny.mjs';
|
|
2
|
+
// ------------------------------------------------------------------
|
|
3
|
+
// Unicode General Category Helper (RFC 5892)
|
|
4
|
+
// ------------------------------------------------------------------
|
|
5
|
+
function IsNonspacingMark(cp) {
|
|
6
|
+
return /\p{Mn}/u.test(String.fromCodePoint(cp));
|
|
7
|
+
}
|
|
8
|
+
function IsSpacingCombiningMark(cp) {
|
|
9
|
+
return /\p{Mc}/u.test(String.fromCodePoint(cp));
|
|
10
|
+
}
|
|
11
|
+
function IsEnclosingMark(cp) {
|
|
12
|
+
return /\p{Me}/u.test(String.fromCodePoint(cp));
|
|
13
|
+
}
|
|
14
|
+
function IsCombiningMark(cp) {
|
|
15
|
+
return IsNonspacingMark(cp) || IsSpacingCombiningMark(cp) || IsEnclosingMark(cp);
|
|
16
|
+
}
|
|
17
|
+
// ------------------------------------------------------------------
|
|
18
|
+
// RFC 5892 §2.6 DISALLOWED exceptions
|
|
19
|
+
//
|
|
20
|
+
// https://tools.ietf.org/html/rfc5892#section-2.6
|
|
21
|
+
// ------------------------------------------------------------------
|
|
22
|
+
const RFC5892_DISALLOWED = new Set([
|
|
23
|
+
0x0640, // ARABIC TATWEEL
|
|
24
|
+
0x07fa, // NKO LAJANYALAN
|
|
25
|
+
0x302e, // HANGUL SINGLE DOT TONE MARK
|
|
26
|
+
0x302f, // HANGUL DOUBLE DOT TONE MARK
|
|
27
|
+
0x3031, // VERTICAL KANA REPEAT MARK
|
|
28
|
+
0x3032, // VERTICAL KANA REPEAT WITH VOICED ITERATION MARK
|
|
29
|
+
0x3033, // VERTICAL KANA REPEAT MARK UPPER HALF
|
|
30
|
+
0x3034, // VERTICAL KANA REPEAT WITH VOICED ITERATION MARK UPPER HALF
|
|
31
|
+
0x3035, // VERTICAL KANA REPEAT MARK LOWER HALF
|
|
32
|
+
0x303b // VERTICAL IDEOGRAPHIC ITERATION MARK
|
|
33
|
+
]);
|
|
34
|
+
// ------------------------------------------------------------------
|
|
35
|
+
// A set of Virama (halant) code points used to validate CONTEXTJ
|
|
36
|
+
// rules (RFC 5892 Appendix A.1). These characters allow a subsequent
|
|
37
|
+
// Zero Width Joiner (U+200D) to be valid in a label.
|
|
38
|
+
// ------------------------------------------------------------------
|
|
39
|
+
const VIRAMA_CPS = new Set([
|
|
40
|
+
0x094d,
|
|
41
|
+
0x09cd,
|
|
42
|
+
0x0a4d,
|
|
43
|
+
0x0acd,
|
|
44
|
+
0x0b4d,
|
|
45
|
+
0x0bcd,
|
|
46
|
+
0x0c4d,
|
|
47
|
+
0x0ccd,
|
|
48
|
+
0x0d3b,
|
|
49
|
+
0x0d3c,
|
|
50
|
+
0x0d4d,
|
|
51
|
+
0x0dca,
|
|
52
|
+
0x1b44,
|
|
53
|
+
0x1baa,
|
|
54
|
+
0x1bab,
|
|
55
|
+
0xa9c0,
|
|
56
|
+
0x11046,
|
|
57
|
+
0x1107f,
|
|
58
|
+
0x110b9,
|
|
59
|
+
0x11133,
|
|
60
|
+
0x11134,
|
|
61
|
+
0x111c0,
|
|
62
|
+
0x11235,
|
|
63
|
+
0x1134d,
|
|
64
|
+
0x11442,
|
|
65
|
+
0x114c2,
|
|
66
|
+
0x115bf,
|
|
67
|
+
0x1163f,
|
|
68
|
+
0x116b6,
|
|
69
|
+
0x11c3f,
|
|
70
|
+
0x11d44,
|
|
71
|
+
0x11d45
|
|
72
|
+
]);
|
|
73
|
+
// ------------------------------------------------------------------
|
|
74
|
+
// Guards for CONTEXTO rules (RFC 5892 Appendix A)
|
|
75
|
+
// ------------------------------------------------------------------
|
|
76
|
+
function IsGreek(cp) {
|
|
77
|
+
return /\p{Script=Greek}/u.test(String.fromCodePoint(cp));
|
|
78
|
+
}
|
|
79
|
+
function IsHebrew(cp) {
|
|
80
|
+
return /\p{Script=Hebrew}/u.test(String.fromCodePoint(cp));
|
|
81
|
+
}
|
|
82
|
+
function IsHiragana(cp) {
|
|
83
|
+
return /\p{Script=Hiragana}/u.test(String.fromCodePoint(cp));
|
|
84
|
+
}
|
|
85
|
+
function IsKatakana(cp) {
|
|
86
|
+
return /\p{Script=Katakana}/u.test(String.fromCodePoint(cp));
|
|
87
|
+
}
|
|
88
|
+
function IsHan(cp) {
|
|
89
|
+
return /\p{Script=Han}/u.test(String.fromCodePoint(cp));
|
|
90
|
+
}
|
|
91
|
+
function IsArabicIndicDigit(cp) {
|
|
92
|
+
return cp >= 0x0660 && cp <= 0x0669;
|
|
93
|
+
}
|
|
94
|
+
function IsExtendedArabicIndicDigit(cp) {
|
|
95
|
+
return cp >= 0x06f0 && cp <= 0x06f9;
|
|
96
|
+
}
|
|
97
|
+
function IsVirama(cp) {
|
|
98
|
+
return VIRAMA_CPS.has(cp);
|
|
99
|
+
}
|
|
100
|
+
// ------------------------------------------------------------------
|
|
101
|
+
// IsUnicodeLabel
|
|
102
|
+
// ------------------------------------------------------------------
|
|
103
|
+
function IsUnicodeLabel(value) {
|
|
104
|
+
if (value.length === 0)
|
|
105
|
+
return false;
|
|
106
|
+
// Use spread to handle surrogate pairs and provide O(1) neighbor access
|
|
107
|
+
const cps = [...value].map((c) => c.codePointAt(0));
|
|
108
|
+
const len = cps.length;
|
|
109
|
+
// RFC 5891 §4.2.3.2: Hyphen rules
|
|
110
|
+
if (cps[0] === 0x2d || cps[len - 1] === 0x2d)
|
|
111
|
+
return false;
|
|
112
|
+
if (len >= 4 && cps[2] === 0x2d && cps[3] === 0x2d)
|
|
113
|
+
return false;
|
|
114
|
+
// RFC 5891 §4.2.3.2 - Must not begin with a combining mark
|
|
115
|
+
if (IsCombiningMark(cps[0]))
|
|
116
|
+
return false;
|
|
117
|
+
let hasJapanese = false;
|
|
118
|
+
let hasArabicIndic = false;
|
|
119
|
+
let hasExtendedArabicIndic = false;
|
|
120
|
+
for (let i = 0; i < len; i++) {
|
|
121
|
+
const cp = cps[i];
|
|
122
|
+
// 1. DISALLOWED exceptions
|
|
123
|
+
if (RFC5892_DISALLOWED.has(cp))
|
|
124
|
+
return false;
|
|
125
|
+
// 2. Collect Flags
|
|
126
|
+
if (IsHiragana(cp) || IsKatakana(cp) || IsHan(cp))
|
|
127
|
+
hasJapanese = true;
|
|
128
|
+
if (IsArabicIndicDigit(cp))
|
|
129
|
+
hasArabicIndic = true;
|
|
130
|
+
if (IsExtendedArabicIndicDigit(cp))
|
|
131
|
+
hasExtendedArabicIndic = true;
|
|
132
|
+
// 3. CONTEXTO / CONTEXTJ Neighbor Rules
|
|
133
|
+
const prev = cps[i - 1], next = cps[i + 1];
|
|
134
|
+
switch (cp) {
|
|
135
|
+
case 0x00b7:
|
|
136
|
+
if (prev !== 0x006c || next !== 0x006c)
|
|
137
|
+
return false;
|
|
138
|
+
break; // MIDDLE DOT (Catalan)
|
|
139
|
+
case 0x0375:
|
|
140
|
+
if (next === undefined || !IsGreek(next))
|
|
141
|
+
return false;
|
|
142
|
+
break; // Greek KERAIA
|
|
143
|
+
case 0x05f3:
|
|
144
|
+
case 0x05f4:
|
|
145
|
+
if (prev === undefined || !IsHebrew(prev))
|
|
146
|
+
return false;
|
|
147
|
+
break; // Hebrew GERESH
|
|
148
|
+
case 0x200d:
|
|
149
|
+
if (prev === undefined || !IsVirama(prev))
|
|
150
|
+
return false;
|
|
151
|
+
break; // ZWJ
|
|
152
|
+
case 0x30fb: /* Checked at end via hasJapanese */
|
|
153
|
+
break; // KATAKANA MIDDLE DOT
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// 4. Global Context Validations (Post-loop)
|
|
157
|
+
// RFC 5892 Appendix A.7 - Katakana Middle Dot requirement
|
|
158
|
+
if (value.includes('\u30fb') && !hasJapanese)
|
|
159
|
+
return false;
|
|
160
|
+
// RFC 5892 Appendix A.8/A.9 - Mixing Arabic Digits
|
|
161
|
+
if (hasArabicIndic && hasExtendedArabicIndic)
|
|
162
|
+
return false;
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
// ------------------------------------------------------------------
|
|
166
|
+
// IsAsciiLabel
|
|
167
|
+
// ------------------------------------------------------------------
|
|
168
|
+
function IsAsciiLabel(value) {
|
|
169
|
+
// Must not start or end with a hyphen
|
|
170
|
+
if (value.charCodeAt(0) === 45 || value.charCodeAt(value.length - 1) === 45)
|
|
171
|
+
return false;
|
|
172
|
+
// RFC 5891 §4.2.3.1 : "--" at positions 3-4 is reserved for A-labels only
|
|
173
|
+
if (value.length >= 4 && value.charCodeAt(2) === 45 && value.charCodeAt(3) === 45)
|
|
174
|
+
return false;
|
|
175
|
+
// All characters must be alphanumeric or hyphen
|
|
176
|
+
for (let i = 0; i < value.length; i++) {
|
|
177
|
+
const ch = value.charCodeAt(i);
|
|
178
|
+
if (!((ch >= 97 && ch <= 122) || // a-z
|
|
179
|
+
(ch >= 65 && ch <= 90) || // A-Z
|
|
180
|
+
(ch >= 48 && ch <= 57) || // 0-9
|
|
181
|
+
ch === 45 // '-'
|
|
182
|
+
))
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
// ------------------------------------------------------------------
|
|
188
|
+
// IsPunyLabel
|
|
189
|
+
// ------------------------------------------------------------------
|
|
190
|
+
function IsPuny(value) {
|
|
191
|
+
return value.toLowerCase().startsWith('xn--');
|
|
192
|
+
}
|
|
193
|
+
function IsPunyLabel(value) {
|
|
194
|
+
try {
|
|
195
|
+
return IsUnicodeLabel(Puny.Decode(value.slice(4)));
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return false; // invalid punycode encoding
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// ------------------------------------------------------------------
|
|
202
|
+
// IsIdnLabel
|
|
203
|
+
// ------------------------------------------------------------------
|
|
204
|
+
export function IsIdnLabel(value) {
|
|
205
|
+
if (value.length === 0 || value.length > 63)
|
|
206
|
+
return false;
|
|
207
|
+
return IsPuny(value) ? IsPunyLabel(value) : IsUnicodeLabel(value);
|
|
208
|
+
}
|
|
209
|
+
// ------------------------------------------------------------------
|
|
210
|
+
// IsLabel
|
|
211
|
+
// ------------------------------------------------------------------
|
|
212
|
+
export function IsLabel(value) {
|
|
213
|
+
if (value.length === 0 || value.length > 63)
|
|
214
|
+
return false;
|
|
215
|
+
return IsPuny(value) ? IsPunyLabel(value) : IsAsciiLabel(value);
|
|
216
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function Decode(value: string): string;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// ------------------------------------------------------------------
|
|
2
|
+
// PunyCode (RFC 3492)
|
|
3
|
+
// ------------------------------------------------------------------
|
|
4
|
+
const PUNYCODE_BASE = 36;
|
|
5
|
+
const PUNYCODE_TMIN = 1;
|
|
6
|
+
const PUNYCODE_TMAX = 26;
|
|
7
|
+
const PUNYCODE_SKEW = 38;
|
|
8
|
+
const PUNYCODE_DAMP = 700;
|
|
9
|
+
const PUNYCODE_INITIAL_BIAS = 72;
|
|
10
|
+
const PUNYCODE_INITIAL_N = 128;
|
|
11
|
+
// ------------------------------------------------------------------
|
|
12
|
+
// Adapt
|
|
13
|
+
// ------------------------------------------------------------------
|
|
14
|
+
function Adapt(delta, numPoints, firstTime) {
|
|
15
|
+
delta = firstTime ? Math.floor(delta / PUNYCODE_DAMP) : delta >> 1;
|
|
16
|
+
delta += Math.floor(delta / numPoints);
|
|
17
|
+
let k = 0;
|
|
18
|
+
while (delta > (((PUNYCODE_BASE - PUNYCODE_TMIN) * PUNYCODE_TMAX) >> 1)) {
|
|
19
|
+
delta = Math.floor(delta / (PUNYCODE_BASE - PUNYCODE_TMIN));
|
|
20
|
+
k += PUNYCODE_BASE;
|
|
21
|
+
}
|
|
22
|
+
return k + Math.floor(((PUNYCODE_BASE - PUNYCODE_TMIN + 1) * delta) / (delta + PUNYCODE_SKEW));
|
|
23
|
+
}
|
|
24
|
+
// ------------------------------------------------------------------
|
|
25
|
+
// Decode
|
|
26
|
+
// ------------------------------------------------------------------
|
|
27
|
+
export function Decode(value) {
|
|
28
|
+
const output = [];
|
|
29
|
+
let n = PUNYCODE_INITIAL_N;
|
|
30
|
+
let i = 0;
|
|
31
|
+
let bias = PUNYCODE_INITIAL_BIAS;
|
|
32
|
+
const delimIdx = value.lastIndexOf('-');
|
|
33
|
+
if (delimIdx > 0) {
|
|
34
|
+
for (let j = 0; j < delimIdx; j++) {
|
|
35
|
+
const cp = value.charCodeAt(j);
|
|
36
|
+
if (cp >= 128)
|
|
37
|
+
throw new Error('Invalid punycode: non-basic before delimiter');
|
|
38
|
+
output.push(cp);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
let inIdx = delimIdx < 0 ? 0 : delimIdx + 1;
|
|
42
|
+
while (inIdx < value.length) {
|
|
43
|
+
const oldi = i;
|
|
44
|
+
let w = 1;
|
|
45
|
+
let k = PUNYCODE_BASE;
|
|
46
|
+
while (true) {
|
|
47
|
+
if (inIdx >= value.length)
|
|
48
|
+
throw new Error('Invalid punycode: unexpected end of input');
|
|
49
|
+
const ch = value.charCodeAt(inIdx++);
|
|
50
|
+
let digit;
|
|
51
|
+
if (ch >= 0x61 && ch <= 0x7a)
|
|
52
|
+
digit = ch - 0x61; // a-z => 0-25
|
|
53
|
+
else if (ch >= 0x30 && ch <= 0x39)
|
|
54
|
+
digit = ch - 0x30 + 26; // 0-9 => 26-35
|
|
55
|
+
else if (ch >= 0x41 && ch <= 0x5a)
|
|
56
|
+
digit = ch - 0x41; // A-Z => 0-25
|
|
57
|
+
else
|
|
58
|
+
throw new Error('Invalid punycode: bad digit character');
|
|
59
|
+
i += digit * w;
|
|
60
|
+
const t = k <= bias ? PUNYCODE_TMIN : k >= bias + PUNYCODE_TMAX ? PUNYCODE_TMAX : k - bias;
|
|
61
|
+
if (digit < t)
|
|
62
|
+
break;
|
|
63
|
+
w *= PUNYCODE_BASE - t;
|
|
64
|
+
k += PUNYCODE_BASE;
|
|
65
|
+
}
|
|
66
|
+
const outLen = output.length + 1;
|
|
67
|
+
bias = Adapt(i - oldi, outLen, oldi === 0);
|
|
68
|
+
n += Math.floor(i / outLen);
|
|
69
|
+
i %= outLen;
|
|
70
|
+
output.splice(i, 0, n);
|
|
71
|
+
i++;
|
|
72
|
+
}
|
|
73
|
+
return globalThis.String.fromCodePoint(...output);
|
|
74
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
const Duration = /^P(
|
|
1
|
+
const Duration = /^P((\d+Y(\d+M(\d+D)?)?|\d+M(\d+D)?|\d+D)(T(\d+H(\d+M(\d+S)?)?|\d+M(\d+S)?|\d+S))?|T(\d+H(\d+M(\d+S)?)?|\d+M(\d+S)?|\d+S)|\d+W)$/;
|
|
2
2
|
/**
|
|
3
|
-
* Returns true if the value is a
|
|
4
|
-
* @
|
|
3
|
+
* Returns true if the value is a valid ISO-8601 duration.
|
|
4
|
+
* @specification https://tools.ietf.org/html/rfc3339
|
|
5
5
|
*/
|
|
6
6
|
export function IsDuration(value) {
|
|
7
7
|
return Duration.test(value);
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Returns true if the value
|
|
2
|
+
* Returns true if the value is a valid hostname.
|
|
3
3
|
* @specification https://tools.ietf.org/html/rfc1123
|
|
4
|
+
* @specification https://tools.ietf.org/html/rfc5891
|
|
5
|
+
* @specification https://tools.ietf.org/html/rfc5892
|
|
4
6
|
*/
|
|
5
7
|
export declare function IsHostname(value: string): boolean;
|
|
@@ -1,34 +1,18 @@
|
|
|
1
|
+
import * as Idna from './_idna.mjs';
|
|
1
2
|
/**
|
|
2
|
-
* Returns true if the value
|
|
3
|
+
* Returns true if the value is a valid hostname.
|
|
3
4
|
* @specification https://tools.ietf.org/html/rfc1123
|
|
5
|
+
* @specification https://tools.ietf.org/html/rfc5891
|
|
6
|
+
* @specification https://tools.ietf.org/html/rfc5892
|
|
4
7
|
*/
|
|
5
8
|
export function IsHostname(value) {
|
|
6
|
-
if (value.length
|
|
9
|
+
if (value.length === 0 || value.length > 253)
|
|
7
10
|
return false;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
for (
|
|
11
|
-
|
|
12
|
-
if (ch === 46) { // '.'
|
|
13
|
-
// trailing dot is valid e.g. "example.com." but not "."
|
|
14
|
-
if (i === value.length - 1 && start < i)
|
|
15
|
-
break;
|
|
16
|
-
const len = i - start;
|
|
17
|
-
if (len === 0 || len > 63 || value.charCodeAt(start) === 45 || prev === 45)
|
|
18
|
-
return false;
|
|
19
|
-
start = i + 1;
|
|
20
|
-
}
|
|
21
|
-
else if (!((ch >= 97 && ch <= 122) || // a-z
|
|
22
|
-
(ch >= 65 && ch <= 90) || // A-Z
|
|
23
|
-
(ch >= 48 && ch <= 57) || // 0-9
|
|
24
|
-
ch === 45 // '-'
|
|
25
|
-
)) {
|
|
11
|
+
if (value.charCodeAt(value.length - 1) === 46)
|
|
12
|
+
return false;
|
|
13
|
+
for (const label of value.split('.')) {
|
|
14
|
+
if (!Idna.IsLabel(label))
|
|
26
15
|
return false;
|
|
27
|
-
}
|
|
28
|
-
prev = ch;
|
|
29
16
|
}
|
|
30
|
-
|
|
31
|
-
const first = value.charCodeAt(start);
|
|
32
|
-
const last = value.charCodeAt(value.length - 1);
|
|
33
|
-
return length > 0 && length <= 63 && first !== 45 && last !== 45;
|
|
17
|
+
return true;
|
|
34
18
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Returns true if the value is
|
|
3
|
-
* @specification
|
|
2
|
+
* Returns true if the value is a valid internationalized (IDN) hostname.
|
|
3
|
+
* @specification https://tools.ietf.org/html/rfc3490
|
|
4
|
+
* @specification https://tools.ietf.org/html/rfc5891
|
|
5
|
+
* @specification https://tools.ietf.org/html/rfc5892
|
|
4
6
|
*/
|
|
5
7
|
export declare function IsIdnHostname(value: string): boolean;
|
|
@@ -1,118 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
return ((cp >= 0x3040 && cp <= 0x309F) || // Hiragana
|
|
3
|
-
(cp >= 0x30A0 && cp <= 0x30FF && cp !== 0x30FB) || // Katakana (excluding U+30FB)
|
|
4
|
-
(cp >= 0x4E00 && cp <= 0x9FFF) // Han (CJK Unified Ideographs)
|
|
5
|
-
);
|
|
6
|
-
}
|
|
1
|
+
import { IsIdnLabel } from './_idna.mjs';
|
|
7
2
|
/**
|
|
8
|
-
* Returns true if the value is
|
|
9
|
-
* @specification
|
|
3
|
+
* Returns true if the value is a valid internationalized (IDN) hostname.
|
|
4
|
+
* @specification https://tools.ietf.org/html/rfc3490
|
|
5
|
+
* @specification https://tools.ietf.org/html/rfc5891
|
|
6
|
+
* @specification https://tools.ietf.org/html/rfc5892
|
|
10
7
|
*/
|
|
11
8
|
export function IsIdnHostname(value) {
|
|
12
9
|
if (value.length === 0 || value.includes(' '))
|
|
13
10
|
return false;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const normalized = value.normalize('NFC').replace(/[\u002E\u3002\uFF0E\uFF61]/g, '.');
|
|
17
|
-
if (normalized.length > 253)
|
|
11
|
+
const canonical = value.normalize('NFC').replace(/[\u002E\u3002\uFF0E\uFF61]/g, '.');
|
|
12
|
+
if (canonical.length > 253)
|
|
18
13
|
return false;
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
if (label.length === 0 || label.length > 63)
|
|
22
|
-
return false;
|
|
23
|
-
// Labels must not begin or end with a hyphen
|
|
24
|
-
if (label.charCodeAt(0) === 45 || label.charCodeAt(label.length - 1) === 45)
|
|
25
|
-
return false;
|
|
26
|
-
// A-label (punycode) checks
|
|
27
|
-
if ((label.charCodeAt(0) === 120 || label.charCodeAt(0) === 88) && // 'x' or 'X'
|
|
28
|
-
(label.charCodeAt(1) === 110 || label.charCodeAt(1) === 78) && // 'n' or 'N'
|
|
29
|
-
label.charCodeAt(2) === 45 && // '-'
|
|
30
|
-
label.charCodeAt(3) === 45 // '-'
|
|
31
|
-
) {
|
|
32
|
-
const punycodePart = label.slice(4);
|
|
33
|
-
if (punycodePart.length < 2 || punycodePart.includes('---'))
|
|
34
|
-
return false;
|
|
35
|
-
continue;
|
|
36
|
-
}
|
|
37
|
-
// U-label checks
|
|
38
|
-
let hasArabicIndic = false;
|
|
39
|
-
let hasExtendedArabicIndic = false;
|
|
40
|
-
for (let i = 0; i < label.length; i++) {
|
|
41
|
-
// deno-coverage-ignore
|
|
42
|
-
const cp = label.codePointAt(i) ?? 0;
|
|
43
|
-
// Disallowed code points
|
|
44
|
-
if (cp === 0x302E || cp === 0x302F ||
|
|
45
|
-
cp === 0x3031 || cp === 0x3032 || cp === 0x3033 || cp === 0x3034 || cp === 0x3035 ||
|
|
46
|
-
cp === 0x303B || cp === 0x0640 || cp === 0x07FA)
|
|
47
|
-
return false;
|
|
48
|
-
// Disallow labels starting with certain combining marks
|
|
49
|
-
if (i === 0 && (cp === 0x0903 || cp === 0x0300 || cp === 0x0488))
|
|
50
|
-
return false;
|
|
51
|
-
// MIDDLE DOT (U+00B7) must be flanked by 'l' or 'L'
|
|
52
|
-
if (cp === 0x00B7) {
|
|
53
|
-
if (i === 0 || i === label.length - 1)
|
|
54
|
-
return false;
|
|
55
|
-
// deno-coverage-ignore
|
|
56
|
-
const prev = label.codePointAt(i - 1) ?? 0;
|
|
57
|
-
// deno-coverage-ignore
|
|
58
|
-
const next = label.codePointAt(i + 1) ?? 0;
|
|
59
|
-
if ((prev !== 108 && prev !== 76) || (next !== 108 && next !== 76))
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
// KATAKANA MIDDLE DOT (U+30FB) | U+30FB is below U+FFFF so stride is always 1
|
|
63
|
-
if (cp === 0x30FB) {
|
|
64
|
-
if (label.length === 1)
|
|
65
|
-
return false;
|
|
66
|
-
if (i === 0) {
|
|
67
|
-
// deno-coverage-ignore
|
|
68
|
-
const next = label.codePointAt(i + 1) ?? 0;
|
|
69
|
-
if (!IsValidAdjacentForKatakanaMiddleDot(next))
|
|
70
|
-
return false;
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
// deno-coverage-ignore
|
|
74
|
-
const prev = label.codePointAt(i - 1) ?? 0;
|
|
75
|
-
// deno-coverage-ignore
|
|
76
|
-
const next = label.codePointAt(i + 1) ?? 0;
|
|
77
|
-
if (!IsValidAdjacentForKatakanaMiddleDot(prev) || !IsValidAdjacentForKatakanaMiddleDot(next))
|
|
78
|
-
return false;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
// Greek KERAIA (U+0375) | U+0375 is below U+FFFF so stride is always 1
|
|
82
|
-
if (cp === 0x0375) {
|
|
83
|
-
if (i === label.length - 1)
|
|
84
|
-
return false;
|
|
85
|
-
// deno-coverage-ignore
|
|
86
|
-
const next = label.codePointAt(i + 1) ?? 0;
|
|
87
|
-
if (next < 0x0370 || next > 0x03FF)
|
|
88
|
-
return false;
|
|
89
|
-
}
|
|
90
|
-
// Hebrew GERESH (U+05F3) and GERSHAYIM (U+05F4)
|
|
91
|
-
if (cp === 0x05F3 || cp === 0x05F4) {
|
|
92
|
-
if (i === 0)
|
|
93
|
-
return false;
|
|
94
|
-
// deno-coverage-ignore
|
|
95
|
-
const prev = label.codePointAt(i - 1) ?? 0;
|
|
96
|
-
if (prev < 0x05D0 || prev > 0x05EA)
|
|
97
|
-
return false;
|
|
98
|
-
}
|
|
99
|
-
// ZERO WIDTH JOINER (U+200D)
|
|
100
|
-
if (cp === 0x200D) {
|
|
101
|
-
if (i === 0)
|
|
102
|
-
return false;
|
|
103
|
-
// deno-coverage-ignore
|
|
104
|
-
const prev = label.codePointAt(i - 1) ?? 0;
|
|
105
|
-
if (prev !== 0x094D)
|
|
106
|
-
return false;
|
|
107
|
-
}
|
|
108
|
-
// Arabic-Indic digits
|
|
109
|
-
if (cp >= 0x0660 && cp <= 0x0669)
|
|
110
|
-
hasArabicIndic = true;
|
|
111
|
-
// Extended Arabic-Indic digits
|
|
112
|
-
if (cp >= 0x06F0 && cp <= 0x06F9)
|
|
113
|
-
hasExtendedArabicIndic = true;
|
|
114
|
-
}
|
|
115
|
-
if (hasArabicIndic && hasExtendedArabicIndic)
|
|
14
|
+
for (const label of canonical.split('.')) {
|
|
15
|
+
if (!IsIdnLabel(label))
|
|
116
16
|
return false;
|
|
117
17
|
}
|
|
118
18
|
return true;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Returns true if the value is a
|
|
3
|
-
* @specification
|
|
4
|
-
* @source ajv-formats
|
|
2
|
+
* Returns true if the value is a valid URI Reference.
|
|
3
|
+
* @specification https://tools.ietf.org/html/rfc3986
|
|
5
4
|
*/
|
|
6
5
|
export declare function IsUriReference(value: string): boolean;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
const UriReference = /^(?:(?:[a-z][a-z0-9+\-.]*:)
|
|
1
|
+
const UriReference = /^(?!.*[^\x00-\x7F])(?!.*\\)(?:(?:[a-z][a-z0-9+\-.]*:)?(?:\/\/[^\s[\]{}<>^`|]*)?|[^\s[\]{}<>^`|]*)(?:\?[^\s[\]{}<>^`|]*)?(?:#[^\s[\]{}<>^`|]*)?$/i;
|
|
2
2
|
/**
|
|
3
|
-
* Returns true if the value is a
|
|
4
|
-
* @specification
|
|
5
|
-
* @source ajv-formats
|
|
3
|
+
* Returns true if the value is a valid URI Reference.
|
|
4
|
+
* @specification https://tools.ietf.org/html/rfc3986
|
|
6
5
|
*/
|
|
7
6
|
export function IsUriReference(value) {
|
|
8
7
|
return UriReference.test(value);
|
package/build/guard/string.d.mts
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
/** Returns the number of grapheme clusters in a string */
|
|
2
2
|
export declare function GraphemeCount(value: string): number;
|
|
3
|
-
/** Checks if a string has at least a minimum number of grapheme clusters */
|
|
4
|
-
export declare function IsMinLength(value: string, minLength: number): boolean;
|
|
5
|
-
/** Checks if a string has at most a maximum number of grapheme clusters */
|
|
6
|
-
export declare function IsMaxLength(value: string, maxLength: number): boolean;
|
|
7
3
|
/** Fast check for minimum grapheme length, falls back to full check if needed */
|
|
8
4
|
export declare function IsMinLengthFast(value: string, minLength: number): boolean;
|
|
9
5
|
/** Fast check for maximum grapheme length, falls back to full check if needed */
|
package/build/guard/string.mjs
CHANGED
|
@@ -94,9 +94,15 @@ export function GraphemeCount(value) {
|
|
|
94
94
|
// IsMinLength
|
|
95
95
|
// --------------------------------------------------------------------------
|
|
96
96
|
/** Checks if a string has at least a minimum number of grapheme clusters */
|
|
97
|
-
|
|
97
|
+
function IsMinLength(value, minLength) {
|
|
98
|
+
// ----------------------------------------------------------------
|
|
99
|
+
// Inaccessible via public interface (review)
|
|
100
|
+
//
|
|
101
|
+
// deno-coverage-ignore-start
|
|
102
|
+
// ----------------------------------------------------------------
|
|
98
103
|
if (minLength === 0)
|
|
99
104
|
return true; // 0-length
|
|
105
|
+
// deno-coverage-ignore-stop
|
|
100
106
|
let count = 0;
|
|
101
107
|
let index = 0;
|
|
102
108
|
while (index < value.length) {
|
|
@@ -111,7 +117,7 @@ export function IsMinLength(value, minLength) {
|
|
|
111
117
|
// IsMaxLength
|
|
112
118
|
// --------------------------------------------------------------------------
|
|
113
119
|
/** Checks if a string has at most a maximum number of grapheme clusters */
|
|
114
|
-
|
|
120
|
+
function IsMaxLength(value, maxLength) {
|
|
115
121
|
let count = 0;
|
|
116
122
|
let index = 0;
|
|
117
123
|
while (index < value.length) {
|
|
@@ -4,14 +4,17 @@ export declare class BuildContext {
|
|
|
4
4
|
private readonly hasUnevaluated;
|
|
5
5
|
constructor(hasUnevaluated: boolean);
|
|
6
6
|
UseUnevaluated(): boolean;
|
|
7
|
+
Push(): string;
|
|
8
|
+
Pop(): string;
|
|
7
9
|
AddIndex(index: string): string;
|
|
8
10
|
AddKey(key: string): string;
|
|
9
11
|
Merge(results: string): string;
|
|
10
12
|
}
|
|
11
13
|
export declare class CheckContext {
|
|
12
|
-
private readonly
|
|
13
|
-
private readonly keys;
|
|
14
|
+
private readonly stack;
|
|
14
15
|
constructor();
|
|
16
|
+
Push(): true;
|
|
17
|
+
Pop(): true;
|
|
15
18
|
AddIndex(index: number): true;
|
|
16
19
|
AddKey(key: string): true;
|
|
17
20
|
GetIndices(): Set<number>;
|
|
@@ -30,6 +30,18 @@ export class BuildContext {
|
|
|
30
30
|
UseUnevaluated() {
|
|
31
31
|
return this.hasUnevaluated;
|
|
32
32
|
}
|
|
33
|
+
// ----------------------------------------------------------------
|
|
34
|
+
// Stack
|
|
35
|
+
// ----------------------------------------------------------------
|
|
36
|
+
Push() {
|
|
37
|
+
return E.Call(E.Member('context', 'Push'), []);
|
|
38
|
+
}
|
|
39
|
+
Pop() {
|
|
40
|
+
return E.Call(E.Member('context', 'Pop'), []);
|
|
41
|
+
}
|
|
42
|
+
// ----------------------------------------------------------------
|
|
43
|
+
// Top
|
|
44
|
+
// ----------------------------------------------------------------
|
|
33
45
|
AddIndex(index) {
|
|
34
46
|
return E.Call(E.Member('context', 'AddIndex'), [index]);
|
|
35
47
|
}
|
|
@@ -45,27 +57,46 @@ export class BuildContext {
|
|
|
45
57
|
// ------------------------------------------------------------------
|
|
46
58
|
export class CheckContext {
|
|
47
59
|
constructor() {
|
|
48
|
-
|
|
49
|
-
|
|
60
|
+
const indices = new Set();
|
|
61
|
+
const keys = new Set();
|
|
62
|
+
this.stack = [{ indices, keys }];
|
|
63
|
+
}
|
|
64
|
+
// ----------------------------------------------------------------
|
|
65
|
+
// Stack
|
|
66
|
+
// ----------------------------------------------------------------
|
|
67
|
+
Push() {
|
|
68
|
+
const indices = new Set();
|
|
69
|
+
const keys = new Set();
|
|
70
|
+
this.stack.push({ indices, keys });
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
Pop() {
|
|
74
|
+
this.stack.pop();
|
|
75
|
+
return true;
|
|
50
76
|
}
|
|
77
|
+
// ----------------------------------------------------------------
|
|
78
|
+
// Top
|
|
79
|
+
// ----------------------------------------------------------------
|
|
51
80
|
AddIndex(index) {
|
|
52
|
-
this.
|
|
81
|
+
this.GetIndices().add(index);
|
|
53
82
|
return true;
|
|
54
83
|
}
|
|
55
84
|
AddKey(key) {
|
|
56
|
-
this.
|
|
85
|
+
this.GetKeys().add(key);
|
|
57
86
|
return true;
|
|
58
87
|
}
|
|
59
88
|
GetIndices() {
|
|
60
|
-
|
|
89
|
+
const top = this.stack[this.stack.length - 1];
|
|
90
|
+
return top.indices;
|
|
61
91
|
}
|
|
62
92
|
GetKeys() {
|
|
63
|
-
|
|
93
|
+
const top = this.stack[this.stack.length - 1];
|
|
94
|
+
return top.keys;
|
|
64
95
|
}
|
|
65
96
|
Merge(results) {
|
|
66
97
|
for (const context of results) {
|
|
67
|
-
context.
|
|
68
|
-
context.
|
|
98
|
+
context.GetIndices().forEach(value => this.GetIndices().add(value));
|
|
99
|
+
context.GetKeys().forEach(value => this.GetKeys().add(value));
|
|
69
100
|
}
|
|
70
101
|
return true;
|
|
71
102
|
}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
export interface TExternal {
|
|
2
|
-
|
|
3
|
-
identifier: string;
|
|
4
|
-
/** An array of external variables */
|
|
2
|
+
identifier: 'External';
|
|
5
3
|
variables: unknown[];
|
|
6
4
|
}
|
|
7
|
-
export declare function ResetExternal(): void;
|
|
8
5
|
export declare function CreateVariable(value: unknown): string;
|
|
6
|
+
export declare function ResetExternal(): void;
|
|
9
7
|
export declare function GetExternal(): TExternal;
|
|
@@ -1,34 +1,25 @@
|
|
|
1
1
|
// deno-fmt-ignore-file
|
|
2
|
-
const identifier = 'external_';
|
|
3
|
-
let resetCount = 0;
|
|
4
2
|
const state = {
|
|
5
|
-
identifier:
|
|
3
|
+
identifier: 'External',
|
|
6
4
|
variables: []
|
|
7
5
|
};
|
|
8
6
|
// ------------------------------------------------------------------
|
|
9
|
-
// ResetExternals
|
|
10
|
-
//
|
|
11
|
-
// Each reset results in a new external group identifier. This is done
|
|
12
|
-
// to prevent variable name overlap when generating and evaluating
|
|
13
|
-
// multiple types in the same scope.
|
|
14
|
-
//
|
|
15
|
-
// ------------------------------------------------------------------
|
|
16
|
-
export function ResetExternal() {
|
|
17
|
-
state.identifier = `${identifier}${resetCount}`;
|
|
18
|
-
state.variables = [];
|
|
19
|
-
resetCount += 1;
|
|
20
|
-
}
|
|
21
|
-
// ------------------------------------------------------------------
|
|
22
7
|
// CreateVariable
|
|
23
8
|
// ------------------------------------------------------------------
|
|
24
9
|
export function CreateVariable(value) {
|
|
25
|
-
const call =
|
|
10
|
+
const call = `External[${state.variables.length}]`;
|
|
26
11
|
state.variables.push(value);
|
|
27
12
|
return call;
|
|
28
13
|
}
|
|
29
14
|
// ------------------------------------------------------------------
|
|
15
|
+
// ResetExternal
|
|
16
|
+
// ------------------------------------------------------------------
|
|
17
|
+
export function ResetExternal() {
|
|
18
|
+
state.variables = [];
|
|
19
|
+
}
|
|
20
|
+
// ------------------------------------------------------------------
|
|
30
21
|
// GetExternals
|
|
31
22
|
// ------------------------------------------------------------------
|
|
32
23
|
export function GetExternal() {
|
|
33
|
-
return state;
|
|
24
|
+
return { ...state };
|
|
34
25
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import * as Schema from '../types/index.mjs';
|
|
3
3
|
import { Unique } from './_unique.mjs';
|
|
4
4
|
import { Guard as G, EmitGuard as E } from '../../guard/index.mjs';
|
|
5
|
-
import {
|
|
5
|
+
import { BuildSchemaPushStack, CheckSchemaPushStack, ErrorSchemaPushStack } from './schema.mjs';
|
|
6
6
|
// ------------------------------------------------------------------
|
|
7
7
|
// Valid
|
|
8
8
|
// ------------------------------------------------------------------
|
|
@@ -16,7 +16,7 @@ export function BuildAdditionalItems(stack, context, schema, value) {
|
|
|
16
16
|
if (!IsValid(schema))
|
|
17
17
|
return E.Constant(true);
|
|
18
18
|
const [item, index] = [Unique(), Unique()];
|
|
19
|
-
const isSchema =
|
|
19
|
+
const isSchema = BuildSchemaPushStack(stack, context, schema.additionalItems, item);
|
|
20
20
|
const isLength = E.IsLessThan(index, E.Constant(schema.items.length));
|
|
21
21
|
const addIndex = context.AddIndex(index);
|
|
22
22
|
const guarded = context.UseUnevaluated() ? E.Or(isLength, E.And(isSchema, addIndex)) : E.Or(isLength, isSchema);
|
|
@@ -30,7 +30,7 @@ export function CheckAdditionalItems(stack, context, schema, value) {
|
|
|
30
30
|
return true;
|
|
31
31
|
const isAdditionalItems = value.every((item, index) => {
|
|
32
32
|
return G.IsLessThan(index, schema.items.length)
|
|
33
|
-
|| (
|
|
33
|
+
|| (CheckSchemaPushStack(stack, context, schema.additionalItems, item) && context.AddIndex(index));
|
|
34
34
|
});
|
|
35
35
|
return isAdditionalItems;
|
|
36
36
|
}
|
|
@@ -44,7 +44,7 @@ export function ErrorAdditionalItems(stack, context, schemaPath, instancePath, s
|
|
|
44
44
|
const nextSchemaPath = `${schemaPath}/additionalItems`;
|
|
45
45
|
const nextInstancePath = `${instancePath}/${index}`;
|
|
46
46
|
return G.IsLessThan(index, schema.items.length) ||
|
|
47
|
-
(
|
|
47
|
+
(ErrorSchemaPushStack(stack, context, nextSchemaPath, nextInstancePath, schema.additionalItems, item) && context.AddIndex(index));
|
|
48
48
|
});
|
|
49
49
|
return isAdditionalItems;
|
|
50
50
|
}
|
|
@@ -4,7 +4,7 @@ import * as V from './_externals.mjs';
|
|
|
4
4
|
import { Unique } from './_unique.mjs';
|
|
5
5
|
import { AccumulatedErrorContext } from './_context.mjs';
|
|
6
6
|
import { EmitGuard as E, Guard as G } from '../../guard/index.mjs';
|
|
7
|
-
import {
|
|
7
|
+
import { BuildSchemaPushStack, CheckSchemaPushStack, ErrorSchemaPushStack } from './schema.mjs';
|
|
8
8
|
// ------------------------------------------------------------------
|
|
9
9
|
// Common: GetPropertiesPattern
|
|
10
10
|
//
|
|
@@ -59,7 +59,7 @@ export function BuildAdditionalPropertiesFast(context, schema, value) {
|
|
|
59
59
|
export function BuildAdditionalPropertiesStandard(stack, context, schema, value) {
|
|
60
60
|
const [key, _index] = [Unique(), Unique()];
|
|
61
61
|
const regexp = V.CreateVariable(new RegExp(GetPropertiesPattern(schema)));
|
|
62
|
-
const isSchema =
|
|
62
|
+
const isSchema = BuildSchemaPushStack(stack, context, schema.additionalProperties, `${value}[${key}]`);
|
|
63
63
|
const isKey = E.Call(E.Member(regexp, 'test'), [key]);
|
|
64
64
|
const addKey = context.AddKey(key);
|
|
65
65
|
const guarded = context.UseUnevaluated() ? E.Or(isKey, E.And(isSchema, addKey)) : E.Or(isKey, isSchema);
|
|
@@ -81,7 +81,7 @@ export function CheckAdditionalProperties(stack, context, schema, value) {
|
|
|
81
81
|
const regexp = new RegExp(GetPropertiesPattern(schema));
|
|
82
82
|
const isAdditionalProperties = G.Every(G.Keys(value), 0, (key, _index) => {
|
|
83
83
|
return regexp.test(key) ||
|
|
84
|
-
(
|
|
84
|
+
(CheckSchemaPushStack(stack, context, schema.additionalProperties, value[key]) && context.AddKey(key));
|
|
85
85
|
});
|
|
86
86
|
return isAdditionalProperties;
|
|
87
87
|
}
|
|
@@ -96,7 +96,7 @@ export function ErrorAdditionalProperties(stack, context, schemaPath, instancePa
|
|
|
96
96
|
const nextInstancePath = `${instancePath}/${key}`;
|
|
97
97
|
const nextContext = new AccumulatedErrorContext();
|
|
98
98
|
const isAdditionalProperty = regexp.test(key) ||
|
|
99
|
-
(
|
|
99
|
+
(ErrorSchemaPushStack(stack, nextContext, nextSchemaPath, nextInstancePath, schema.additionalProperties, value[key]) && context.AddKey(key));
|
|
100
100
|
if (!isAdditionalProperty)
|
|
101
101
|
additionalProperties.push(key);
|
|
102
102
|
return isAdditionalProperty;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// deno-fmt-ignore-file
|
|
2
2
|
import * as Schema from '../types/index.mjs';
|
|
3
3
|
import { Guard as G, EmitGuard as E } from '../../guard/index.mjs';
|
|
4
|
-
import {
|
|
4
|
+
import { BuildSchemaPushStack, CheckSchemaPushStack, ErrorSchemaPushStack } from './schema.mjs';
|
|
5
5
|
// ------------------------------------------------------------------
|
|
6
6
|
// ItemsSized
|
|
7
7
|
// ------------------------------------------------------------------
|
|
8
8
|
function BuildItemsSized(stack, context, schema, value) {
|
|
9
9
|
return E.ReduceAnd(schema.items.map((schema, index) => {
|
|
10
10
|
const isLength = E.IsLessEqualThan(E.Member(value, 'length'), E.Constant(index));
|
|
11
|
-
const isSchema =
|
|
11
|
+
const isSchema = BuildSchemaPushStack(stack, context, schema, `${value}[${index}]`);
|
|
12
12
|
const addIndex = context.AddIndex(E.Constant(index));
|
|
13
13
|
const guarded = context.UseUnevaluated() ? E.And(isSchema, addIndex) : isSchema;
|
|
14
14
|
return E.Or(isLength, guarded);
|
|
@@ -17,7 +17,7 @@ function BuildItemsSized(stack, context, schema, value) {
|
|
|
17
17
|
function CheckItemsSized(stack, context, schema, value) {
|
|
18
18
|
return G.Every(schema.items, 0, (schema, index) => {
|
|
19
19
|
return G.IsLessEqualThan(value.length, index)
|
|
20
|
-
|| (
|
|
20
|
+
|| (CheckSchemaPushStack(stack, context, schema, value[index]) && context.AddIndex(index));
|
|
21
21
|
});
|
|
22
22
|
}
|
|
23
23
|
function ErrorItemsSized(stack, context, schemaPath, instancePath, schema, value) {
|
|
@@ -25,7 +25,7 @@ function ErrorItemsSized(stack, context, schemaPath, instancePath, schema, value
|
|
|
25
25
|
const nextSchemaPath = `${schemaPath}/items/${index}`;
|
|
26
26
|
const nextInstancePath = `${instancePath}/${index}`;
|
|
27
27
|
return G.IsLessEqualThan(value.length, index)
|
|
28
|
-
|| (
|
|
28
|
+
|| (ErrorSchemaPushStack(stack, context, nextSchemaPath, nextInstancePath, schema, value[index]) && context.AddIndex(index));
|
|
29
29
|
});
|
|
30
30
|
}
|
|
31
31
|
// ------------------------------------------------------------------
|
|
@@ -33,7 +33,7 @@ function ErrorItemsSized(stack, context, schemaPath, instancePath, schema, value
|
|
|
33
33
|
// ------------------------------------------------------------------
|
|
34
34
|
function BuildItemsUnsized(stack, context, schema, value) {
|
|
35
35
|
const offset = Schema.IsPrefixItems(schema) ? schema.prefixItems.length : 0;
|
|
36
|
-
const isSchema =
|
|
36
|
+
const isSchema = BuildSchemaPushStack(stack, context, schema.items, 'element');
|
|
37
37
|
const addIndex = context.AddIndex('index');
|
|
38
38
|
const guarded = context.UseUnevaluated() ? E.And(isSchema, addIndex) : isSchema;
|
|
39
39
|
return E.Every(value, E.Constant(offset), ['element', 'index'], guarded);
|
|
@@ -41,7 +41,7 @@ function BuildItemsUnsized(stack, context, schema, value) {
|
|
|
41
41
|
function CheckItemsUnsized(stack, context, schema, value) {
|
|
42
42
|
const offset = Schema.IsPrefixItems(schema) ? schema.prefixItems.length : 0;
|
|
43
43
|
return G.Every(value, offset, (element, index) => {
|
|
44
|
-
return
|
|
44
|
+
return CheckSchemaPushStack(stack, context, schema.items, element)
|
|
45
45
|
&& context.AddIndex(index);
|
|
46
46
|
});
|
|
47
47
|
}
|
|
@@ -50,7 +50,7 @@ function ErrorItemsUnsized(stack, context, schemaPath, instancePath, schema, val
|
|
|
50
50
|
return G.EveryAll(value, offset, (element, index) => {
|
|
51
51
|
const nextSchemaPath = `${schemaPath}/items`;
|
|
52
52
|
const nextInstancePath = `${instancePath}/${index}`;
|
|
53
|
-
return
|
|
53
|
+
return ErrorSchemaPushStack(stack, context, nextSchemaPath, nextInstancePath, schema.items, element)
|
|
54
54
|
&& context.AddIndex(index);
|
|
55
55
|
});
|
|
56
56
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import * as Externals from './_externals.mjs';
|
|
3
3
|
import { Unique } from './_unique.mjs';
|
|
4
4
|
import { Guard as G, EmitGuard as E } from '../../guard/index.mjs';
|
|
5
|
-
import {
|
|
5
|
+
import { BuildSchemaPushStack, CheckSchemaPushStack, ErrorSchemaPushStack } from './schema.mjs';
|
|
6
6
|
// ------------------------------------------------------------------
|
|
7
7
|
// Build
|
|
8
8
|
// ------------------------------------------------------------------
|
|
@@ -11,7 +11,7 @@ export function BuildPatternProperties(stack, context, schema, value) {
|
|
|
11
11
|
const [key, prop] = [Unique(), Unique()];
|
|
12
12
|
const regexp = Externals.CreateVariable(new RegExp(pattern, 'u'));
|
|
13
13
|
const notKey = E.Not(E.Call(E.Member(regexp, 'test'), [key]));
|
|
14
|
-
const isSchema =
|
|
14
|
+
const isSchema = BuildSchemaPushStack(stack, context, schema, prop);
|
|
15
15
|
const addKey = context.AddKey(key);
|
|
16
16
|
const guarded = context.UseUnevaluated() ? E.Or(notKey, E.And(isSchema, addKey)) : E.Or(notKey, isSchema);
|
|
17
17
|
return E.Every(E.Entries(value), E.Constant(0), [`[${key}, ${prop}]`, '_'], guarded);
|
|
@@ -24,7 +24,7 @@ export function CheckPatternProperties(stack, context, schema, value) {
|
|
|
24
24
|
return G.Every(G.Entries(schema.patternProperties), 0, ([pattern, schema]) => {
|
|
25
25
|
const regexp = new RegExp(pattern, 'u');
|
|
26
26
|
return G.Every(G.Entries(value), 0, ([key, prop]) => {
|
|
27
|
-
return !regexp.test(key) ||
|
|
27
|
+
return !regexp.test(key) || CheckSchemaPushStack(stack, context, schema, prop) && context.AddKey(key);
|
|
28
28
|
});
|
|
29
29
|
});
|
|
30
30
|
}
|
|
@@ -38,7 +38,7 @@ export function ErrorPatternProperties(stack, context, schemaPath, instancePath,
|
|
|
38
38
|
return G.EveryAll(G.Entries(value), 0, ([key, value]) => {
|
|
39
39
|
const nextInstancePath = `${instancePath}/${key}`;
|
|
40
40
|
const notKey = !regexp.test(key);
|
|
41
|
-
return notKey ||
|
|
41
|
+
return notKey || ErrorSchemaPushStack(stack, context, nextSchemaPath, nextInstancePath, schema, value) && context.AddKey(key);
|
|
42
42
|
});
|
|
43
43
|
});
|
|
44
44
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// deno-fmt-ignore-file
|
|
2
2
|
import { Guard as G, EmitGuard as E } from '../../guard/index.mjs';
|
|
3
|
-
import {
|
|
3
|
+
import { BuildSchemaPushStack, CheckSchemaPushStack, ErrorSchemaPushStack } from './schema.mjs';
|
|
4
4
|
// ------------------------------------------------------------------
|
|
5
5
|
// Build
|
|
6
6
|
// ------------------------------------------------------------------
|
|
7
7
|
export function BuildPrefixItems(stack, context, schema, value) {
|
|
8
8
|
return E.ReduceAnd(schema.prefixItems.map((schema, index) => {
|
|
9
9
|
const isLength = E.IsLessEqualThan(E.Member(value, 'length'), E.Constant(index));
|
|
10
|
-
const isSchema =
|
|
10
|
+
const isSchema = BuildSchemaPushStack(stack, context, schema, `${value}[${index}]`);
|
|
11
11
|
const addIndex = context.AddIndex(E.Constant(index));
|
|
12
12
|
const guarded = context.UseUnevaluated() ? E.And(isSchema, addIndex) : isSchema;
|
|
13
13
|
return E.Or(isLength, guarded);
|
|
@@ -19,7 +19,7 @@ export function BuildPrefixItems(stack, context, schema, value) {
|
|
|
19
19
|
export function CheckPrefixItems(stack, context, schema, value) {
|
|
20
20
|
return G.IsEqual(value.length, 0) || G.Every(schema.prefixItems, 0, (schema, index) => {
|
|
21
21
|
return G.IsLessEqualThan(value.length, index)
|
|
22
|
-
|| (
|
|
22
|
+
|| (CheckSchemaPushStack(stack, context, schema, value[index]) && context.AddIndex(index));
|
|
23
23
|
});
|
|
24
24
|
}
|
|
25
25
|
// ------------------------------------------------------------------
|
|
@@ -30,6 +30,6 @@ export function ErrorPrefixItems(stack, context, schemaPath, instancePath, schem
|
|
|
30
30
|
const nextSchemaPath = `${schemaPath}/prefixItems/${index}`;
|
|
31
31
|
const nextInstancePath = `${instancePath}/${index}`;
|
|
32
32
|
return G.IsLessEqualThan(value.length, index)
|
|
33
|
-
|| (
|
|
33
|
+
|| (ErrorSchemaPushStack(stack, context, nextSchemaPath, nextInstancePath, schema, value[index]) && context.AddIndex(index));
|
|
34
34
|
});
|
|
35
35
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// deno-fmt-ignore-file
|
|
2
2
|
import * as Schema from '../types/index.mjs';
|
|
3
3
|
import { Guard as G, EmitGuard as E } from '../../guard/index.mjs';
|
|
4
|
-
import {
|
|
4
|
+
import { BuildSchemaPushStack, CheckSchemaPushStack, ErrorSchemaPushStack } from './schema.mjs';
|
|
5
5
|
import { InexactOptionalCheck, InexactOptionalBuild, IsExactOptional } from './_exact_optional.mjs';
|
|
6
6
|
// ------------------------------------------------------------------
|
|
7
7
|
// Build
|
|
@@ -10,7 +10,7 @@ export function BuildProperties(stack, context, schema, value) {
|
|
|
10
10
|
const required = Schema.IsRequired(schema) ? schema.required : [];
|
|
11
11
|
const everyKey = G.Entries(schema.properties).map(([key, schema]) => {
|
|
12
12
|
const notKey = E.Not(E.HasPropertyKey(value, E.Constant(key)));
|
|
13
|
-
const isSchema =
|
|
13
|
+
const isSchema = BuildSchemaPushStack(stack, context, schema, E.Member(value, key));
|
|
14
14
|
const addKey = context.AddKey(E.Constant(key));
|
|
15
15
|
const guarded = context.UseUnevaluated() ? E.And(isSchema, addKey) : isSchema;
|
|
16
16
|
// --------------------------------------------------------------
|
|
@@ -50,7 +50,7 @@ export function BuildProperties(stack, context, schema, value) {
|
|
|
50
50
|
export function CheckProperties(stack, context, schema, value) {
|
|
51
51
|
const required = Schema.IsRequired(schema) ? schema.required : [];
|
|
52
52
|
const isProperties = G.Every(G.Entries(schema.properties), 0, ([key, schema]) => {
|
|
53
|
-
const isProperty = !G.HasPropertyKey(value, key) || (
|
|
53
|
+
const isProperty = !G.HasPropertyKey(value, key) || (CheckSchemaPushStack(stack, context, schema, value[key]) && context.AddKey(key));
|
|
54
54
|
return IsExactOptional(required, key)
|
|
55
55
|
? isProperty
|
|
56
56
|
: InexactOptionalCheck(value, key) || isProperty;
|
|
@@ -66,7 +66,7 @@ export function ErrorProperties(stack, context, schemaPath, instancePath, schema
|
|
|
66
66
|
const nextSchemaPath = `${schemaPath}/properties/${key}`;
|
|
67
67
|
const nextInstancePath = `${instancePath}/${key}`;
|
|
68
68
|
// Defer error generation for IsExactOptional
|
|
69
|
-
const isProperty = () => (!G.HasPropertyKey(value, key) || (
|
|
69
|
+
const isProperty = () => (!G.HasPropertyKey(value, key) || (ErrorSchemaPushStack(stack, context, nextSchemaPath, nextInstancePath, schema, value[key]) && context.AddKey(key)));
|
|
70
70
|
return IsExactOptional(required, key)
|
|
71
71
|
? isProperty()
|
|
72
72
|
: InexactOptionalCheck(value, key) || isProperty();
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import * as Schema from '../types/index.mjs';
|
|
2
2
|
import { Stack } from './_stack.mjs';
|
|
3
3
|
import { BuildContext, CheckContext, ErrorContext } from './_context.mjs';
|
|
4
|
+
export declare function BuildSchemaPushStack(stack: Stack, context: BuildContext, schema: Schema.XSchema, value: string): string;
|
|
4
5
|
export declare function BuildSchema(stack: Stack, context: BuildContext, schema: Schema.XSchema, value: string): string;
|
|
6
|
+
export declare function CheckSchemaPushStack(stack: Stack, context: CheckContext, schema: Schema.XSchema, value: unknown): boolean;
|
|
5
7
|
export declare function CheckSchema(stack: Stack, context: CheckContext, schema: Schema.XSchema, value: unknown): boolean;
|
|
8
|
+
export declare function ErrorSchemaPushStack(stack: Stack, context: ErrorContext, schemaPath: string, instancePath: string, schema: Schema.XSchema, value: unknown): boolean;
|
|
6
9
|
export declare function ErrorSchema(stack: Stack, context: ErrorContext, schemaPath: string, instancePath: string, schema: Schema.XSchema, value: unknown): boolean;
|
|
@@ -117,6 +117,11 @@ function HasNumberKeywords(schema) {
|
|
|
117
117
|
// ----------------------------------------------------------------
|
|
118
118
|
// Build
|
|
119
119
|
// ----------------------------------------------------------------
|
|
120
|
+
export function BuildSchemaPushStack(stack, context, schema, value) {
|
|
121
|
+
return context.UseUnevaluated()
|
|
122
|
+
? E.And(E.And(context.Push(), BuildSchema(stack, context, schema, value)), context.Pop())
|
|
123
|
+
: BuildSchema(stack, context, schema, value);
|
|
124
|
+
}
|
|
120
125
|
export function BuildSchema(stack, context, schema, value) {
|
|
121
126
|
stack.Push(schema);
|
|
122
127
|
const conditions = [];
|
|
@@ -237,6 +242,9 @@ export function BuildSchema(stack, context, schema, value) {
|
|
|
237
242
|
// ----------------------------------------------------------------
|
|
238
243
|
// Check
|
|
239
244
|
// ----------------------------------------------------------------
|
|
245
|
+
export function CheckSchemaPushStack(stack, context, schema, value) {
|
|
246
|
+
return (context.Push() && CheckSchema(stack, context, schema, value)) && context.Pop();
|
|
247
|
+
}
|
|
240
248
|
export function CheckSchema(stack, context, schema, value) {
|
|
241
249
|
stack.Push(schema);
|
|
242
250
|
const result = Schema.IsBooleanSchema(schema) ? CheckBooleanSchema(stack, context, schema, value) : ((!Schema.IsType(schema) || CheckType(stack, context, schema, value)) &&
|
|
@@ -287,6 +295,9 @@ export function CheckSchema(stack, context, schema, value) {
|
|
|
287
295
|
// ----------------------------------------------------------------
|
|
288
296
|
// Error
|
|
289
297
|
// ----------------------------------------------------------------
|
|
298
|
+
export function ErrorSchemaPushStack(stack, context, schemaPath, instancePath, schema, value) {
|
|
299
|
+
return (context.Push() && ErrorSchema(stack, context, schemaPath, instancePath, schema, value)) && context.Pop();
|
|
300
|
+
}
|
|
290
301
|
export function ErrorSchema(stack, context, schemaPath, instancePath, schema, value) {
|
|
291
302
|
stack.Push(schema);
|
|
292
303
|
const result = (Schema.IsBooleanSchema(schema)) ? ErrorBooleanSchema(stack, context, schemaPath, instancePath, schema, value) : (!!(+(!Schema.IsType(schema) || ErrorType(stack, context, schemaPath, instancePath, schema, value)) &
|
|
@@ -155,30 +155,7 @@ export interface TNumberOptions extends TSchemaOptions {
|
|
|
155
155
|
export type TFormat = 'date-time' | 'date' | 'duration' | 'email' | 'hostname' | 'idn-email' | 'idn-hostname' | 'ipv4' | 'ipv6' | 'iri-reference' | 'iri' | 'json-pointer-uri-fragment' | 'json-pointer' | 'json-string' | 'regex' | 'relative-json-pointer' | 'time' | 'uri-reference' | 'uri-template' | 'uri' | 'url' | 'uuid' | ({} & string);
|
|
156
156
|
export interface TStringOptions extends TSchemaOptions {
|
|
157
157
|
/**
|
|
158
|
-
* Specifies the expected string format.
|
|
159
|
-
*
|
|
160
|
-
* Common values include:
|
|
161
|
-
* - `base64` – Base64-encoded string.
|
|
162
|
-
* - `date-time` – [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time format.
|
|
163
|
-
* - `date` – [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date (YYYY-MM-DD).
|
|
164
|
-
* - `duration` – [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) duration format.
|
|
165
|
-
* - `email` – RFC 5321/5322 compliant email address.
|
|
166
|
-
* - `hostname` – RFC 1034/1035 compliant host name.
|
|
167
|
-
* - `idn-email` – Internationalized email address.
|
|
168
|
-
* - `idn-hostname` – Internationalized host name.
|
|
169
|
-
* - `ipv4` – IPv4 address.
|
|
170
|
-
* - `ipv6` – IPv6 address.
|
|
171
|
-
* - `iri` / `iri-reference` – Internationalized Resource Identifier.
|
|
172
|
-
* - `json-pointer` / `json-pointer-uri-fragment` – JSON Pointer format.
|
|
173
|
-
* - `json-string` – String containing valid JSON.
|
|
174
|
-
* - `regex` – Regular expression syntax.
|
|
175
|
-
* - `relative-json-pointer` – Relative JSON Pointer format.
|
|
176
|
-
* - `time` – [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) time (HH:MM:SS).
|
|
177
|
-
* - `uri-reference` / `uri-template` – URI reference or template.
|
|
178
|
-
* - `url` – Web URL format.
|
|
179
|
-
* - `uuid` – RFC 4122 UUID string.
|
|
180
|
-
*
|
|
181
|
-
* May also be a custom format string.
|
|
158
|
+
* Specifies the expected string format. May also be a custom format string.
|
|
182
159
|
*/
|
|
183
160
|
format?: TFormat;
|
|
184
161
|
/**
|