ascertain 2.1.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +252 -114
- package/build/index.cjs +616 -183
- package/build/index.cjs.map +1 -1
- package/build/index.d.ts +215 -186
- package/build/index.js +577 -183
- package/build/index.js.map +1 -1
- package/package.json +15 -13
- package/src/index.ts +777 -380
package/build/index.cjs
CHANGED
|
@@ -30,54 +30,166 @@ _export(exports, {
|
|
|
30
30
|
get ascertain () {
|
|
31
31
|
return ascertain;
|
|
32
32
|
},
|
|
33
|
+
get check () {
|
|
34
|
+
return check;
|
|
35
|
+
},
|
|
33
36
|
get compile () {
|
|
34
37
|
return compile;
|
|
35
38
|
},
|
|
36
39
|
get createValidator () {
|
|
37
40
|
return createValidator;
|
|
38
41
|
},
|
|
42
|
+
get discriminated () {
|
|
43
|
+
return discriminated;
|
|
44
|
+
},
|
|
45
|
+
get format () {
|
|
46
|
+
return format;
|
|
47
|
+
},
|
|
39
48
|
get fromBase64 () {
|
|
40
49
|
return fromBase64;
|
|
41
50
|
},
|
|
51
|
+
get gt () {
|
|
52
|
+
return gt;
|
|
53
|
+
},
|
|
54
|
+
get integer () {
|
|
55
|
+
return integer;
|
|
56
|
+
},
|
|
57
|
+
get lt () {
|
|
58
|
+
return lt;
|
|
59
|
+
},
|
|
60
|
+
get max () {
|
|
61
|
+
return max;
|
|
62
|
+
},
|
|
63
|
+
get maxLength () {
|
|
64
|
+
return maxLength;
|
|
65
|
+
},
|
|
66
|
+
get min () {
|
|
67
|
+
return min;
|
|
68
|
+
},
|
|
69
|
+
get minLength () {
|
|
70
|
+
return minLength;
|
|
71
|
+
},
|
|
72
|
+
get multipleOf () {
|
|
73
|
+
return multipleOf;
|
|
74
|
+
},
|
|
42
75
|
get optional () {
|
|
43
76
|
return optional;
|
|
44
77
|
},
|
|
45
78
|
get or () {
|
|
46
79
|
return or;
|
|
47
80
|
},
|
|
81
|
+
get standardSchema () {
|
|
82
|
+
return standardSchema;
|
|
83
|
+
},
|
|
48
84
|
get tuple () {
|
|
49
85
|
return tuple;
|
|
86
|
+
},
|
|
87
|
+
get uniqueItems () {
|
|
88
|
+
return uniqueItems;
|
|
50
89
|
}
|
|
51
90
|
});
|
|
52
|
-
class Operator {
|
|
53
|
-
schemas;
|
|
54
|
-
constructor(schemas){
|
|
55
|
-
this.schemas = schemas;
|
|
56
|
-
if (schemas.length === 0) {
|
|
57
|
-
throw new TypeError(`Operation schema ${this.constructor.name} must have at least one element`);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
91
|
const $keys = Symbol.for('@@keys');
|
|
62
92
|
const $values = Symbol.for('@@values');
|
|
63
93
|
const $strict = Symbol.for('@@strict');
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
94
|
+
const $op = Symbol.for('@@op');
|
|
95
|
+
const OR = Symbol.for('@@or');
|
|
96
|
+
const AND = Symbol.for('@@and');
|
|
97
|
+
const OPTIONAL = Symbol.for('@@optional');
|
|
98
|
+
const TUPLE = Symbol.for('@@tuple');
|
|
99
|
+
const DISCRIMINATED = Symbol.for('@@discriminated');
|
|
100
|
+
const CHECK = Symbol.for('@@check');
|
|
101
|
+
const OrCtor = function(schemas) {
|
|
102
|
+
this.schemas = schemas;
|
|
103
|
+
};
|
|
104
|
+
OrCtor.prototype[$op] = OR;
|
|
105
|
+
const AndCtor = function(schemas) {
|
|
106
|
+
this.schemas = schemas;
|
|
107
|
+
};
|
|
108
|
+
AndCtor.prototype[$op] = AND;
|
|
109
|
+
const OptionalCtor = function(schema) {
|
|
110
|
+
this.schemas = [
|
|
111
|
+
schema
|
|
112
|
+
];
|
|
113
|
+
};
|
|
114
|
+
OptionalCtor.prototype[$op] = OPTIONAL;
|
|
115
|
+
const TupleCtor = function(schemas) {
|
|
116
|
+
this.schemas = schemas;
|
|
117
|
+
};
|
|
118
|
+
TupleCtor.prototype[$op] = TUPLE;
|
|
119
|
+
const DiscriminatedCtor = function(schemas, key) {
|
|
120
|
+
this.schemas = schemas;
|
|
121
|
+
this.key = key;
|
|
122
|
+
};
|
|
123
|
+
DiscriminatedCtor.prototype[$op] = DISCRIMINATED;
|
|
124
|
+
const CheckCtor = function(compileFn) {
|
|
125
|
+
this.compile = compileFn;
|
|
126
|
+
};
|
|
127
|
+
CheckCtor.prototype[$op] = CHECK;
|
|
128
|
+
const or = (...schemas)=>{
|
|
129
|
+
if (schemas.length === 0) throw new TypeError('Operator requires at least one schema');
|
|
130
|
+
return new OrCtor(schemas);
|
|
131
|
+
};
|
|
132
|
+
const and = (...schemas)=>{
|
|
133
|
+
if (schemas.length === 0) throw new TypeError('Operator requires at least one schema');
|
|
134
|
+
return new AndCtor(schemas);
|
|
135
|
+
};
|
|
136
|
+
const optional = (schema)=>new OptionalCtor(schema);
|
|
137
|
+
const tuple = (...schemas)=>{
|
|
138
|
+
if (schemas.length === 0) throw new TypeError('Operator requires at least one schema');
|
|
139
|
+
return new TupleCtor(schemas);
|
|
140
|
+
};
|
|
141
|
+
const discriminated = (schemas, key)=>{
|
|
142
|
+
if (schemas.length === 0) throw new TypeError('discriminated requires at least one schema');
|
|
143
|
+
return new DiscriminatedCtor(schemas, key);
|
|
144
|
+
};
|
|
145
|
+
const check = (fnOrOpts, message)=>{
|
|
146
|
+
if (typeof fnOrOpts === 'function') {
|
|
147
|
+
return new CheckCtor((v, ctx)=>{
|
|
148
|
+
const fnRef = ctx.ref(fnOrOpts);
|
|
149
|
+
return {
|
|
150
|
+
check: `!${fnRef}(${v})`,
|
|
151
|
+
message: message ? JSON.stringify(message) : `\`check failed for value \${${v}}\``
|
|
152
|
+
};
|
|
153
|
+
});
|
|
75
154
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
|
|
155
|
+
return new CheckCtor(fnOrOpts.compile);
|
|
156
|
+
};
|
|
157
|
+
const min = (n, message)=>new CheckCtor((v)=>({
|
|
158
|
+
check: `${v} < ${n}`,
|
|
159
|
+
message: message ? JSON.stringify(message) : `\`must be >= ${n}, got \${${v}}\``
|
|
160
|
+
}));
|
|
161
|
+
const max = (n, message)=>new CheckCtor((v)=>({
|
|
162
|
+
check: `${v} > ${n}`,
|
|
163
|
+
message: message ? JSON.stringify(message) : `\`must be <= ${n}, got \${${v}}\``
|
|
164
|
+
}));
|
|
165
|
+
const integer = (message)=>new CheckCtor((v)=>({
|
|
166
|
+
check: `!Number.isInteger(${v})`,
|
|
167
|
+
message: message ? JSON.stringify(message) : `\`must be an integer, got \${${v}}\``
|
|
168
|
+
}));
|
|
169
|
+
const minLength = (n, message)=>new CheckCtor((v)=>({
|
|
170
|
+
check: `${v}.length < ${n}`,
|
|
171
|
+
message: message ? JSON.stringify(message) : `\`length must be >= ${n}, got \${${v}.length}\``
|
|
172
|
+
}));
|
|
173
|
+
const maxLength = (n, message)=>new CheckCtor((v)=>({
|
|
174
|
+
check: `${v}.length > ${n}`,
|
|
175
|
+
message: message ? JSON.stringify(message) : `\`length must be <= ${n}, got \${${v}.length}\``
|
|
176
|
+
}));
|
|
177
|
+
const gt = (n, message)=>new CheckCtor((v)=>({
|
|
178
|
+
check: `${v} <= ${n}`,
|
|
179
|
+
message: message ? JSON.stringify(message) : `\`must be > ${n}, got \${${v}}\``
|
|
180
|
+
}));
|
|
181
|
+
const lt = (n, message)=>new CheckCtor((v)=>({
|
|
182
|
+
check: `${v} >= ${n}`,
|
|
183
|
+
message: message ? JSON.stringify(message) : `\`must be < ${n}, got \${${v}}\``
|
|
184
|
+
}));
|
|
185
|
+
const multipleOf = (n, message)=>new CheckCtor((v)=>({
|
|
186
|
+
check: `${v} % ${n} !== 0`,
|
|
187
|
+
message: message ? JSON.stringify(message) : `\`must be a multiple of ${n}, got \${${v}}\``
|
|
188
|
+
}));
|
|
189
|
+
const uniqueItems = (message)=>new CheckCtor((v)=>({
|
|
190
|
+
check: `new Set(${v}).size !== ${v}.length`,
|
|
191
|
+
message: message ? JSON.stringify(message) : `\`must have unique items\``
|
|
192
|
+
}));
|
|
81
193
|
const fromBase64 = typeof Buffer === 'undefined' ? (value)=>atob(value) : (value)=>Buffer.from(value, 'base64').toString('utf-8');
|
|
82
194
|
const MULTIPLIERS = {
|
|
83
195
|
ms: 1,
|
|
@@ -105,7 +217,7 @@ const as = {
|
|
|
105
217
|
if (Number.isNaN(result)) return asError(`Invalid value ${value}, expected a valid number`);
|
|
106
218
|
return value[0] === '-' ? -result : result;
|
|
107
219
|
}
|
|
108
|
-
const result = value.
|
|
220
|
+
const result = value.trim() ? Number(value) : NaN;
|
|
109
221
|
return Number.isNaN(result) ? asError(`Invalid value ${value}, expected a valid number`) : result;
|
|
110
222
|
},
|
|
111
223
|
date: (value)=>{
|
|
@@ -142,6 +254,86 @@ const as = {
|
|
|
142
254
|
}
|
|
143
255
|
}
|
|
144
256
|
};
|
|
257
|
+
const DATETIME_RE = /^\d{4}-[01]\d-[0-3]\d[t\s](?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d{2}(?::?\d{2})?)$/i;
|
|
258
|
+
const TIME_FMT_RE = /^(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d{2}(?::?\d{2})?)$/i;
|
|
259
|
+
const DURATION_RE = /^P(?!$)(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+S)?)?$/;
|
|
260
|
+
const EMAIL_RE = /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i;
|
|
261
|
+
const IDN_EMAIL_RE = /^[a-z0-9!#$%&'*+/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF-]+)*@(?:[a-z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF](?:[a-z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF-]*[a-z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])?\.)+[a-z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF](?:[a-z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF-]*[a-z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])?$/i;
|
|
262
|
+
const HOSTNAME_RE = /^(?=.{1,253}\.?$)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*\.?$/i;
|
|
263
|
+
const IDN_HOSTNAME_RE = /^(?=.{1,253}\.?$)[a-z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF](?:[a-z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF-]{0,61}[a-z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])?(?:\.[a-z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF](?:[a-z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF-]{0,61}[a-z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])?)*\.?$/i;
|
|
264
|
+
const IPV4_RE = /^(?:(?: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)$/;
|
|
265
|
+
const IPV6_RE = /^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?$/i;
|
|
266
|
+
const URI_RE = /^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[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,1}[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|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\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})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[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;
|
|
267
|
+
const isUriRef = (s)=>URI_RE.test(s) || /^[a-z0-9\-._~:/?#\[\]@!$&'()*+,;=%]*$/i.test(s);
|
|
268
|
+
const IRI_RE = /^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[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,1}[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|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\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})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'()*+,;=\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:@\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'()*+,;=:@\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|%[0-9a-f]{2})*)*)(?:\?(?:[a-z0-9\-._~!$&'()*+,;=:@/?\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'()*+,;=:@/?\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|%[0-9a-f]{2})*)?$/i;
|
|
269
|
+
const isIriRef = (s)=>IRI_RE.test(s) || /^[a-z0-9\-._~:/?#\[\]@!$&'()*+,;=\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF%]*$/i.test(s);
|
|
270
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
271
|
+
const URI_TEMPLATE_RE = /^(?:(?:[^\x00-\x20"'<>%\\^`{|}]|%[0-9a-f]{2})|\{[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?)*\})*$/i;
|
|
272
|
+
const JSON_POINTER_RE = /^(?:\/(?:[^~/]|~0|~1)*)*$/;
|
|
273
|
+
const REL_JSON_POINTER_RE = /^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/;
|
|
274
|
+
const DAYS = [
|
|
275
|
+
0,
|
|
276
|
+
31,
|
|
277
|
+
28,
|
|
278
|
+
31,
|
|
279
|
+
30,
|
|
280
|
+
31,
|
|
281
|
+
30,
|
|
282
|
+
31,
|
|
283
|
+
31,
|
|
284
|
+
30,
|
|
285
|
+
31,
|
|
286
|
+
30,
|
|
287
|
+
31
|
|
288
|
+
];
|
|
289
|
+
const isValidDate = (s)=>{
|
|
290
|
+
const m = /^\d{4}-(\d{2})-(\d{2})$/.exec(s);
|
|
291
|
+
if (!m) return false;
|
|
292
|
+
const month = +m[1], day = +m[2];
|
|
293
|
+
if (month < 1 || month > 12 || day < 1) return false;
|
|
294
|
+
if (month === 2) {
|
|
295
|
+
const y = +s.slice(0, 4);
|
|
296
|
+
return day <= (y % 4 === 0 && (y % 100 !== 0 || y % 400 === 0) ? 29 : 28);
|
|
297
|
+
}
|
|
298
|
+
return day <= DAYS[month];
|
|
299
|
+
};
|
|
300
|
+
const isValidRegex = (s)=>{
|
|
301
|
+
try {
|
|
302
|
+
new RegExp(s);
|
|
303
|
+
return true;
|
|
304
|
+
} catch {
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
const regexFormat = (re, name, message)=>new CheckCtor((v, ctx)=>({
|
|
309
|
+
check: `!${ctx.ref(re)}.test(${v})`,
|
|
310
|
+
message: message ? JSON.stringify(message) : `\`must be a valid ${name}, got \${${v}}\``
|
|
311
|
+
}));
|
|
312
|
+
const fnFormat = (fn, name, message)=>new CheckCtor((v, ctx)=>({
|
|
313
|
+
check: `!${ctx.ref(fn)}(${v})`,
|
|
314
|
+
message: message ? JSON.stringify(message) : `\`must be a valid ${name}, got \${${v}}\``
|
|
315
|
+
}));
|
|
316
|
+
const format = {
|
|
317
|
+
dateTime: (message)=>regexFormat(DATETIME_RE, 'date-time', message),
|
|
318
|
+
date: (message)=>fnFormat(isValidDate, 'date', message),
|
|
319
|
+
time: (message)=>regexFormat(TIME_FMT_RE, 'time', message),
|
|
320
|
+
duration: (message)=>regexFormat(DURATION_RE, 'duration', message),
|
|
321
|
+
email: (message)=>regexFormat(EMAIL_RE, 'email', message),
|
|
322
|
+
idnEmail: (message)=>regexFormat(IDN_EMAIL_RE, 'idn-email', message),
|
|
323
|
+
hostname: (message)=>regexFormat(HOSTNAME_RE, 'hostname', message),
|
|
324
|
+
idnHostname: (message)=>regexFormat(IDN_HOSTNAME_RE, 'idn-hostname', message),
|
|
325
|
+
ipv4: (message)=>regexFormat(IPV4_RE, 'ipv4', message),
|
|
326
|
+
ipv6: (message)=>regexFormat(IPV6_RE, 'ipv6', message),
|
|
327
|
+
uri: (message)=>regexFormat(URI_RE, 'uri', message),
|
|
328
|
+
uriReference: (message)=>fnFormat(isUriRef, 'uri-reference', message),
|
|
329
|
+
iri: (message)=>regexFormat(IRI_RE, 'iri', message),
|
|
330
|
+
iriReference: (message)=>fnFormat(isIriRef, 'iri-reference', message),
|
|
331
|
+
uuid: (message)=>regexFormat(UUID_RE, 'uuid', message),
|
|
332
|
+
uriTemplate: (message)=>regexFormat(URI_TEMPLATE_RE, 'uri-template', message),
|
|
333
|
+
jsonPointer: (message)=>regexFormat(JSON_POINTER_RE, 'json-pointer', message),
|
|
334
|
+
relativeJsonPointer: (message)=>regexFormat(REL_JSON_POINTER_RE, 'relative-json-pointer', message),
|
|
335
|
+
regex: (message)=>fnFormat(isValidRegex, 'regex', message)
|
|
336
|
+
};
|
|
145
337
|
class Context {
|
|
146
338
|
registry = [];
|
|
147
339
|
lookupMap = new Map();
|
|
@@ -161,193 +353,434 @@ class Context {
|
|
|
161
353
|
return `${prefix}$$${this.varIndex++}`;
|
|
162
354
|
}
|
|
163
355
|
}
|
|
164
|
-
const
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
356
|
+
const isTagged = (schema)=>schema?.[$op] !== undefined;
|
|
357
|
+
const childMode = (mode, key)=>{
|
|
358
|
+
if (typeof key === 'object' && 'dynamic' in key) {
|
|
359
|
+
return {
|
|
360
|
+
fast: false,
|
|
361
|
+
firstError: mode.firstError,
|
|
362
|
+
issues: mode.issues,
|
|
363
|
+
path: mode.path,
|
|
364
|
+
pathExpr: `[${mode.path.map((k)=>JSON.stringify(k)).join(',')}${mode.path.length ? ',' : ''}${key.dynamic}]`
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
const newPath = [
|
|
368
|
+
...mode.path,
|
|
369
|
+
key
|
|
370
|
+
];
|
|
371
|
+
return {
|
|
372
|
+
fast: false,
|
|
373
|
+
firstError: mode.firstError,
|
|
374
|
+
issues: mode.issues,
|
|
375
|
+
path: newPath,
|
|
376
|
+
pathExpr: JSON.stringify(newPath)
|
|
377
|
+
};
|
|
378
|
+
};
|
|
379
|
+
const toLiteral = (value)=>typeof value === 'bigint' ? `${value}n` : JSON.stringify(value);
|
|
380
|
+
const codeGen = (schema, context, valuePath, mode)=>{
|
|
381
|
+
const emit = mode.fast ? null : mode.firstError ? (msg)=>`${mode.issues} = [{ message: ${msg}, path: ${mode.pathExpr} }]; return ${mode.issues};` : (msg)=>`(${mode.issues} || (${mode.issues} = [])).push({ message: ${msg}, path: ${mode.pathExpr} });`;
|
|
382
|
+
const fail = mode.fast ? mode.onFail ?? 'return false;' : '';
|
|
383
|
+
if (isTagged(schema)) {
|
|
384
|
+
const tag = schema[$op];
|
|
385
|
+
if (tag === AND) {
|
|
386
|
+
const valueAlias = context.unique('v');
|
|
387
|
+
const code = schema.schemas.map((s)=>codeGen(s, context, valueAlias, mode)).join('\n');
|
|
388
|
+
return `const ${valueAlias} = ${valuePath};\n${code}`;
|
|
389
|
+
} else if (tag === OR) {
|
|
390
|
+
const valueAlias = context.unique('v');
|
|
391
|
+
const foundValid = context.unique('valid');
|
|
392
|
+
if (mode.fast) {
|
|
393
|
+
const branches = schema.schemas.map((s)=>{
|
|
394
|
+
const branchValid = context.unique('valid');
|
|
395
|
+
const branchCode = codeGen(s, context, valueAlias, {
|
|
396
|
+
...mode,
|
|
397
|
+
onFail: `${branchValid} = false;`
|
|
398
|
+
});
|
|
399
|
+
return `if (!${foundValid}) { let ${branchValid} = true; ${branchCode} if (${branchValid}) { ${foundValid} = true; } }`;
|
|
400
|
+
});
|
|
401
|
+
return `const ${valueAlias} = ${valuePath};\nlet ${foundValid} = false;\n${branches.join('\n')}\nif (!${foundValid}) { ${fail} }`;
|
|
402
|
+
} else if (mode.firstError) {
|
|
403
|
+
const firstBranchIssues = context.unique('iss');
|
|
404
|
+
const branches = schema.schemas.map((s, idx)=>{
|
|
405
|
+
const branchIssues = context.unique('iss');
|
|
406
|
+
const branchCode = codeGen(s, context, valueAlias, {
|
|
407
|
+
fast: false,
|
|
408
|
+
firstError: true,
|
|
409
|
+
issues: branchIssues,
|
|
410
|
+
path: mode.path,
|
|
411
|
+
pathExpr: mode.pathExpr
|
|
412
|
+
}).replace(new RegExp(`; return ${branchIssues};`, 'g'), ';');
|
|
413
|
+
if (idx === 0) {
|
|
414
|
+
return `if (!${foundValid}) { let ${branchIssues}; ${branchCode} if (!${branchIssues}) { ${foundValid} = true; } else { ${firstBranchIssues} = ${branchIssues}; } }`;
|
|
415
|
+
}
|
|
416
|
+
return `if (!${foundValid}) { let ${branchIssues}; ${branchCode} if (!${branchIssues}) { ${foundValid} = true; } }`;
|
|
417
|
+
});
|
|
418
|
+
return `const ${valueAlias} = ${valuePath};\nlet ${firstBranchIssues};\nlet ${foundValid} = false;\n${branches.join('\n')}\nif (!${foundValid}) { return ${firstBranchIssues}; }`;
|
|
419
|
+
} else {
|
|
420
|
+
const localIssues = context.unique('iss');
|
|
421
|
+
const branches = schema.schemas.map((s)=>{
|
|
422
|
+
const branchIssues = context.unique('iss');
|
|
423
|
+
const branchCode = codeGen(s, context, valueAlias, {
|
|
424
|
+
fast: false,
|
|
425
|
+
firstError: false,
|
|
426
|
+
issues: branchIssues,
|
|
427
|
+
path: mode.path,
|
|
428
|
+
pathExpr: mode.pathExpr
|
|
429
|
+
});
|
|
430
|
+
return `if (!${foundValid}) { let ${branchIssues}; ${branchCode} if (!${branchIssues}) { ${foundValid} = true; } else { ${localIssues}.push(...${branchIssues}); } }`;
|
|
431
|
+
});
|
|
432
|
+
return `const ${valueAlias} = ${valuePath};\nconst ${localIssues} = [];\nlet ${foundValid} = false;\n${branches.join('\n')}\nif (!${foundValid}) { (${mode.issues} || (${mode.issues} = [])).push(...${localIssues}); }`;
|
|
433
|
+
}
|
|
434
|
+
} else if (tag === OPTIONAL) {
|
|
435
|
+
const valueAlias = context.unique('v');
|
|
436
|
+
return `const ${valueAlias} = ${valuePath};\nif (${valueAlias} !== undefined && ${valueAlias} !== null) { ${codeGen(schema.schemas[0], context, valueAlias, mode)} }`;
|
|
437
|
+
} else if (tag === TUPLE) {
|
|
438
|
+
const valueAlias = context.unique('v');
|
|
439
|
+
if (mode.fast) {
|
|
440
|
+
return `const ${valueAlias} = ${valuePath};\nif (${valueAlias} === null || typeof ${valueAlias} !== 'object' || !Array.isArray(${valueAlias}) || ${valueAlias}.length !== ${schema.schemas.length}) { ${fail} }\n${schema.schemas.map((s, idx)=>codeGen(s, context, `${valueAlias}[${idx}]`, mode)).join('\n')}`;
|
|
441
|
+
} else {
|
|
442
|
+
return [
|
|
443
|
+
`const ${valueAlias} = ${valuePath};`,
|
|
444
|
+
`if (${valueAlias} === null || ${valueAlias} === undefined) { ${emit(`\`Invalid value \${${valueAlias}}, expected non-nullable\``)} }`,
|
|
445
|
+
`else if (typeof ${valueAlias} !== 'object') { ${emit(`\`Invalid type \${typeof ${valueAlias}}, expected an instance of Array\``)} }`,
|
|
446
|
+
`else if (!Array.isArray(${valueAlias})) { ${emit(`\`Invalid instance of \${${valueAlias}.constructor?.name}, expected an instance of Array\``)} }`,
|
|
447
|
+
`else if (${valueAlias}.length !== ${schema.schemas.length}) { ${emit(`\`Invalid tuple length \${${valueAlias}.length}, expected ${schema.schemas.length}\``)} }`,
|
|
448
|
+
`else { ${schema.schemas.map((s, idx)=>codeGen(s, context, `${valueAlias}[${idx}]`, childMode(mode, idx))).join('\n')} }`
|
|
449
|
+
].join('\n');
|
|
450
|
+
}
|
|
451
|
+
} else if (tag === CHECK) {
|
|
452
|
+
const valueAlias = context.unique('v');
|
|
453
|
+
const ref = (v)=>`ctx.registry[${context.register(v)}]`;
|
|
454
|
+
const { check: cond, message } = schema.compile(valueAlias, {
|
|
455
|
+
ref
|
|
456
|
+
});
|
|
457
|
+
if (mode.fast) {
|
|
458
|
+
return `const ${valueAlias} = ${valuePath};\nif (${cond}) { ${fail} }`;
|
|
459
|
+
}
|
|
460
|
+
return `const ${valueAlias} = ${valuePath};\nif (${cond}) { ${emit(message)} }`;
|
|
461
|
+
} else {
|
|
462
|
+
const { key, schemas } = schema;
|
|
463
|
+
const valueAlias = context.unique('v');
|
|
464
|
+
const discriminantAlias = context.unique('d');
|
|
465
|
+
const keyStr = JSON.stringify(key);
|
|
466
|
+
const variants = [];
|
|
467
|
+
for (const s of schemas){
|
|
468
|
+
if (typeof s !== 'object' || s === null || !(key in s)) {
|
|
469
|
+
throw new TypeError(`discriminated: each schema must have the discriminant key "${key}"`);
|
|
470
|
+
}
|
|
471
|
+
const discriminantValue = s[key];
|
|
472
|
+
if (typeof discriminantValue !== 'string' && typeof discriminantValue !== 'number' && typeof discriminantValue !== 'boolean') {
|
|
473
|
+
throw new TypeError(`discriminated: discriminant value must be a string, number, or boolean literal`);
|
|
474
|
+
}
|
|
475
|
+
variants.push({
|
|
476
|
+
value: discriminantValue,
|
|
477
|
+
schema: s
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
if (mode.fast) {
|
|
481
|
+
const branches = variants.map(({ value, schema: s })=>{
|
|
482
|
+
const branchCode = codeGen(s, context, valueAlias, mode);
|
|
483
|
+
return `if (${discriminantAlias} === ${JSON.stringify(value)}) { ${branchCode} }`;
|
|
484
|
+
});
|
|
485
|
+
return [
|
|
486
|
+
`const ${valueAlias} = ${valuePath};`,
|
|
487
|
+
`if (${valueAlias} === null || ${valueAlias} === undefined || typeof ${valueAlias} !== 'object' || ${valueAlias} instanceof Error) { ${fail} }`,
|
|
488
|
+
`const ${discriminantAlias} = ${valueAlias}[${keyStr}];`,
|
|
489
|
+
branches.join(' else ') + ` else { ${fail} }`
|
|
490
|
+
].join('\n');
|
|
491
|
+
} else {
|
|
492
|
+
const validValues = variants.map((v)=>JSON.stringify(v.value)).join(', ');
|
|
493
|
+
const branches = variants.map(({ value, schema: s })=>{
|
|
494
|
+
const branchCode = codeGen(s, context, valueAlias, mode);
|
|
495
|
+
return `if (${discriminantAlias} === ${JSON.stringify(value)}) { ${branchCode} }`;
|
|
496
|
+
});
|
|
497
|
+
return [
|
|
498
|
+
`const ${valueAlias} = ${valuePath};`,
|
|
499
|
+
`if (${valueAlias} === null || ${valueAlias} === undefined) { ${emit(`\`Invalid value \${${valueAlias}}, expected non-nullable\``)} }`,
|
|
500
|
+
`else if (typeof ${valueAlias} !== 'object') { ${emit(`\`Invalid type \${typeof ${valueAlias}}, expected an object\``)} }`,
|
|
501
|
+
`else if (${valueAlias} instanceof Error) { ${emit(`\`\${${valueAlias}.message}\``)} }`,
|
|
502
|
+
`else {`,
|
|
503
|
+
` const ${discriminantAlias} = ${valueAlias}[${keyStr}];`,
|
|
504
|
+
` ${branches.join(' else ')} else { ${emit(`\`Invalid discriminant value \${JSON.stringify(${discriminantAlias})}, expected one of: ${validValues}\``)} }`,
|
|
505
|
+
`}`
|
|
506
|
+
].join('\n');
|
|
507
|
+
}
|
|
219
508
|
}
|
|
509
|
+
}
|
|
510
|
+
if (typeof schema === 'function') {
|
|
511
|
+
const valueAlias = context.unique('v');
|
|
220
512
|
const name = schema?.name;
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if (primitiveType
|
|
225
|
-
|
|
513
|
+
const s = schema;
|
|
514
|
+
const primitiveType = s === String ? 'string' : s === Number ? 'number' : s === Boolean ? 'boolean' : s === BigInt ? 'bigint' : s === Symbol ? 'symbol' : null;
|
|
515
|
+
if (mode.fast) {
|
|
516
|
+
if (primitiveType) {
|
|
517
|
+
const checks = [
|
|
518
|
+
`typeof ${valueAlias} !== '${primitiveType}'`
|
|
519
|
+
];
|
|
520
|
+
if (primitiveType === 'number') checks.push(`Number.isNaN(${valueAlias})`);
|
|
521
|
+
return `const ${valueAlias} = ${valuePath};\nif (${checks.join(' || ')}) { ${fail} }`;
|
|
522
|
+
} else if (name === 'Function') {
|
|
523
|
+
return `const ${valueAlias} = ${valuePath};\nif (typeof ${valueAlias} !== 'function') { ${fail} }`;
|
|
524
|
+
} else {
|
|
525
|
+
const isError = schema === Error || schema?.prototype instanceof Error;
|
|
526
|
+
const index = context.register(schema);
|
|
527
|
+
const registryAlias = context.unique('r');
|
|
528
|
+
return `const ${valueAlias} = ${valuePath};\nconst ${registryAlias} = ctx.registry[${index}];\nif (${valueAlias} === null || ${valueAlias} === undefined${isError ? '' : ` || ${valueAlias} instanceof Error`} || (typeof ${valueAlias} === 'object' && !(${valueAlias} instanceof ${registryAlias})) || (typeof ${valueAlias} !== 'object' && ${valueAlias}?.constructor !== ${registryAlias}) || Number.isNaN(${valueAlias}?.valueOf?.())) { ${fail} }`;
|
|
226
529
|
}
|
|
227
|
-
} else if (name === 'Function') {
|
|
228
|
-
code.push(`if (typeof ${valueAlias} !== 'function') { throw new TypeError(\`Invalid type \${typeof ${valueAlias}} for path "${path}", expected type Function\`); }`);
|
|
229
530
|
} else {
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
531
|
+
const code = [
|
|
532
|
+
`const ${valueAlias} = ${valuePath};`
|
|
533
|
+
];
|
|
534
|
+
if (primitiveType) {
|
|
535
|
+
code.push(`if (${valueAlias} === null || ${valueAlias} === undefined) { ${emit(`\`Invalid value \${${valueAlias}}, expected non-nullable\``)} }`);
|
|
536
|
+
code.push(`else if (${valueAlias} instanceof Error) { ${emit(`\`\${${valueAlias}.message}\``)} }`);
|
|
537
|
+
code.push(`else if (typeof ${valueAlias} !== '${primitiveType}') { ${emit(`\`Invalid type \${typeof ${valueAlias}}, expected type ${name}\``)} }`);
|
|
538
|
+
if (primitiveType === 'number') code.push(`else if (Number.isNaN(${valueAlias})) { ${emit(`\`Invalid value \${${valueAlias}}, expected a valid ${name}\``)} }`);
|
|
539
|
+
} else if (name === 'Function') {
|
|
540
|
+
code.push(`if (${valueAlias} === null || ${valueAlias} === undefined) { ${emit(`\`Invalid value \${${valueAlias}}, expected non-nullable\``)} }`);
|
|
541
|
+
code.push(`else if (${valueAlias} instanceof Error) { ${emit(`\`\${${valueAlias}.message}\``)} }`);
|
|
542
|
+
code.push(`else if (typeof ${valueAlias} !== 'function') { ${emit(`\`Invalid type \${typeof ${valueAlias}}, expected type Function\``)} }`);
|
|
543
|
+
} else {
|
|
544
|
+
const isError = schema === Error || schema?.prototype instanceof Error;
|
|
545
|
+
const index = context.register(schema);
|
|
546
|
+
const registryAlias = context.unique('r');
|
|
547
|
+
code.push(`const ${registryAlias} = ctx.registry[${index}];`);
|
|
548
|
+
code.push(`if (${valueAlias} === null || ${valueAlias} === undefined) { ${emit(`\`Invalid value \${${valueAlias}}, expected non-nullable\``)} }`);
|
|
549
|
+
if (!isError) code.push(`else if (${valueAlias} instanceof Error) { ${emit(`\`\${${valueAlias}.message}\``)} }`);
|
|
550
|
+
code.push(`else if (typeof ${valueAlias} === 'object' && !(${valueAlias} instanceof ${registryAlias})) { ${emit(`\`Invalid instance of \${${valueAlias}?.constructor?.name}, expected an instance of ${name}\``)} }`);
|
|
551
|
+
code.push(`else if (typeof ${valueAlias} !== 'object' && ${valueAlias}?.constructor !== ${registryAlias}) { ${emit(`\`Invalid type \${${valueAlias}?.constructor?.name}, expected type ${name}\``)} }`);
|
|
552
|
+
code.push(`else if (Number.isNaN(${valueAlias}?.valueOf?.())) { ${emit(`\`Invalid value \${${valueAlias}}, expected a valid ${name}\``)} }`);
|
|
553
|
+
}
|
|
554
|
+
return code.join('\n');
|
|
233
555
|
}
|
|
234
|
-
|
|
235
|
-
|
|
556
|
+
}
|
|
557
|
+
if (Array.isArray(schema)) {
|
|
236
558
|
const valueAlias = context.unique('v');
|
|
237
|
-
|
|
238
|
-
`const ${valueAlias} = ${valuePath}
|
|
239
|
-
codeGenExpectNonNullable(valueAlias, path),
|
|
240
|
-
codeGenExpectNonError(valueAlias, path),
|
|
241
|
-
codeGenExpectObject(valueAlias, path, 'Array'),
|
|
242
|
-
codeGenExpectArray(valueAlias, path)
|
|
243
|
-
];
|
|
244
|
-
if (schema.length > 0) {
|
|
245
|
-
const value = context.unique('val');
|
|
246
|
-
const key = context.unique('key');
|
|
247
|
-
const errorsAlias = context.unique('err');
|
|
248
|
-
code.push(`const ${errorsAlias} = [];`);
|
|
559
|
+
if (mode.fast) {
|
|
560
|
+
let code = `const ${valueAlias} = ${valuePath};\nif (!Array.isArray(${valueAlias})) { ${fail} }`;
|
|
249
561
|
if (schema.length === 1) {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
code
|
|
253
|
-
|
|
562
|
+
const value = context.unique('val');
|
|
563
|
+
const key = context.unique('key');
|
|
564
|
+
code += `\nfor (let ${key} = 0; ${key} < ${valueAlias}.length; ${key}++) { const ${value} = ${valueAlias}[${key}]; ${codeGen(schema[0], context, value, mode)} }`;
|
|
565
|
+
} else if (schema.length > 1) {
|
|
566
|
+
code += `\nif (${valueAlias}.length > ${schema.length}) { ${fail} }`;
|
|
567
|
+
code += '\n' + schema.map((s, idx)=>codeGen(s, context, `${valueAlias}[${idx}]`, mode)).join('\n');
|
|
254
568
|
}
|
|
255
|
-
code
|
|
256
|
-
}
|
|
257
|
-
return code.join('\n');
|
|
258
|
-
} else if (typeof schema === 'object' && schema !== null) {
|
|
259
|
-
if (schema instanceof RegExp) {
|
|
260
|
-
const valueAlias = context.unique('v');
|
|
261
|
-
return `
|
|
262
|
-
const ${valueAlias} = ${valuePath};
|
|
263
|
-
${codeGenExpectNonNullable(valueAlias, path)}
|
|
264
|
-
${codeGenExpectNonError(valueAlias, path)}
|
|
265
|
-
if (!${schema.toString()}.test(String(${valueAlias}))) { throw new TypeError(\`Invalid value \${${valueAlias}} for path "${path}", expected to match ${schema.toString()}\`); }
|
|
266
|
-
`;
|
|
569
|
+
return code;
|
|
267
570
|
} else {
|
|
268
|
-
const valueAlias = context.unique('v');
|
|
269
571
|
const code = [
|
|
270
572
|
`const ${valueAlias} = ${valuePath};`,
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
573
|
+
`if (${valueAlias} === null || ${valueAlias} === undefined) { ${emit(`\`Invalid value \${${valueAlias}}, expected non-nullable\``)} }`,
|
|
574
|
+
`else if (${valueAlias} instanceof Error) { ${emit(`\`\${${valueAlias}.message}\``)} }`,
|
|
575
|
+
`else if (typeof ${valueAlias} !== 'object') { ${emit(`\`Invalid type \${typeof ${valueAlias}}, expected an instance of Array\``)} }`,
|
|
576
|
+
`else if (!Array.isArray(${valueAlias})) { ${emit(`\`Invalid instance of \${${valueAlias}.constructor?.name}, expected an instance of Array\``)} }`
|
|
274
577
|
];
|
|
275
|
-
if (
|
|
276
|
-
const
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
${
|
|
284
|
-
`);
|
|
578
|
+
if (schema.length > 0) {
|
|
579
|
+
const value = context.unique('val');
|
|
580
|
+
const key = context.unique('key');
|
|
581
|
+
if (schema.length === 1) {
|
|
582
|
+
code.push(`else { for (let ${key} = 0; ${key} < ${valueAlias}.length; ${key}++) { const ${value} = ${valueAlias}[${key}]; ${codeGen(schema[0], context, value, childMode(mode, {
|
|
583
|
+
dynamic: key
|
|
584
|
+
}))} } }`);
|
|
585
|
+
} else {
|
|
586
|
+
code.push(`else if (${valueAlias}.length > ${schema.length}) { ${emit(`\`Invalid tuple length \${${valueAlias}.length}, expected ${schema.length}\``)} }`);
|
|
587
|
+
code.push(`else { ${schema.map((s, idx)=>codeGen(s, context, `${valueAlias}[${idx}]`, childMode(mode, idx))).join('\n')} }`);
|
|
588
|
+
}
|
|
285
589
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
const ${
|
|
294
|
-
|
|
295
|
-
${
|
|
296
|
-
`);
|
|
590
|
+
return code.join('\n');
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
if (typeof schema === 'object' && schema !== null) {
|
|
594
|
+
if (schema instanceof RegExp) {
|
|
595
|
+
const valueAlias = context.unique('v');
|
|
596
|
+
if (mode.fast) {
|
|
597
|
+
return `const ${valueAlias} = ${valuePath};\nif (${valueAlias} === null || ${valueAlias} === undefined || ${valueAlias} instanceof Error || !${schema.toString()}.test(String(${valueAlias}))) { ${fail} }`;
|
|
598
|
+
} else {
|
|
599
|
+
return `const ${valueAlias} = ${valuePath};\nif (${valueAlias} === null || ${valueAlias} === undefined) { ${emit(`\`Invalid value \${${valueAlias}}, expected non-nullable\``)} }\nelse if (${valueAlias} instanceof Error) { ${emit(`\`\${${valueAlias}.message}\``)} }\nelse if (!${schema.toString()}.test(String(${valueAlias}))) { ${emit(`\`Invalid value \${${valueAlias}}, expected to match ${schema.toString()}\``)} }`;
|
|
297
600
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
601
|
+
} else {
|
|
602
|
+
const valueAlias = context.unique('v');
|
|
603
|
+
if (mode.fast) {
|
|
604
|
+
let code = `const ${valueAlias} = ${valuePath};\nif (${valueAlias} === null || typeof ${valueAlias} !== 'object' || ${valueAlias} instanceof Error) { ${fail} }`;
|
|
605
|
+
if ($keys in schema) {
|
|
606
|
+
const keysAlias = context.unique('k');
|
|
607
|
+
const kAlias = context.unique('k');
|
|
608
|
+
code += `\nconst ${keysAlias} = Object.keys(${valueAlias});\nfor (const ${kAlias} of ${keysAlias}) { ${codeGen(schema[$keys], context, kAlias, mode)} }`;
|
|
609
|
+
}
|
|
610
|
+
if ($values in schema) {
|
|
611
|
+
const vAlias = context.unique('val');
|
|
612
|
+
const kAlias = context.unique('k');
|
|
613
|
+
const entriesAlias = context.unique('en');
|
|
614
|
+
code += `\nconst ${entriesAlias} = Object.entries(${valueAlias});\nfor (const [${kAlias}, ${vAlias}] of ${entriesAlias}) { ${codeGen(schema[$values], context, vAlias, mode)} }`;
|
|
615
|
+
}
|
|
616
|
+
if ($strict in schema && schema[$strict]) {
|
|
617
|
+
const keysAlias = context.unique('k');
|
|
618
|
+
const kAlias = context.unique('k');
|
|
619
|
+
const extraAlias = context.unique('ex');
|
|
620
|
+
code += `\nconst ${keysAlias} = new Set(${JSON.stringify(Object.keys(schema))});\nconst ${extraAlias} = Object.keys(${valueAlias}).filter(${kAlias} => !${keysAlias}.has(${kAlias}));\nif (${extraAlias}.length !== 0) { ${fail} }`;
|
|
621
|
+
}
|
|
622
|
+
code += '\n' + Object.entries(schema).map(([key, s])=>codeGen(s, context, `${valueAlias}[${JSON.stringify(key)}]`, mode)).join('\n');
|
|
623
|
+
return code;
|
|
624
|
+
} else {
|
|
625
|
+
const code = [
|
|
626
|
+
`const ${valueAlias} = ${valuePath};`,
|
|
627
|
+
`if (${valueAlias} === null || ${valueAlias} === undefined) { ${emit(`\`Invalid value \${${valueAlias}}, expected non-nullable\``)} }`,
|
|
628
|
+
`else if (typeof ${valueAlias} !== 'object') { ${emit(`\`Invalid type \${typeof ${valueAlias}}, expected an instance of Object\``)} }`,
|
|
629
|
+
`else if (${valueAlias} instanceof Error) { ${emit(`\`\${${valueAlias}.message}\``)} }`,
|
|
630
|
+
'else {'
|
|
631
|
+
];
|
|
632
|
+
const innerCode = [];
|
|
633
|
+
if ($keys in schema) {
|
|
634
|
+
const keysAlias = context.unique('k');
|
|
635
|
+
const kAlias = context.unique('k');
|
|
636
|
+
innerCode.push(`const ${keysAlias} = Object.keys(${valueAlias});`);
|
|
637
|
+
innerCode.push(`for (const ${kAlias} of ${keysAlias}) { ${codeGen(schema[$keys], context, kAlias, childMode(mode, {
|
|
638
|
+
dynamic: kAlias
|
|
639
|
+
}))} }`);
|
|
640
|
+
}
|
|
641
|
+
if ($values in schema) {
|
|
642
|
+
const vAlias = context.unique('val');
|
|
643
|
+
const kAlias = context.unique('k');
|
|
644
|
+
const entriesAlias = context.unique('en');
|
|
645
|
+
innerCode.push(`const ${entriesAlias} = Object.entries(${valueAlias});`);
|
|
646
|
+
innerCode.push(`for (const [${kAlias}, ${vAlias}] of ${entriesAlias}) { ${codeGen(schema[$values], context, vAlias, childMode(mode, {
|
|
647
|
+
dynamic: kAlias
|
|
648
|
+
}))} }`);
|
|
649
|
+
}
|
|
650
|
+
if ($strict in schema && schema[$strict]) {
|
|
651
|
+
const keysAlias = context.unique('k');
|
|
652
|
+
const kAlias = context.unique('k');
|
|
653
|
+
const extraAlias = context.unique('ex');
|
|
654
|
+
innerCode.push(`const ${keysAlias} = new Set(${JSON.stringify(Object.keys(schema))});`);
|
|
655
|
+
innerCode.push(`const ${extraAlias} = Object.keys(${valueAlias}).filter(${kAlias} => !${keysAlias}.has(${kAlias}));`);
|
|
656
|
+
innerCode.push(`if (${extraAlias}.length !== 0) { ${emit(`\`Extra properties: \${${extraAlias}}, are not allowed\``)} }`);
|
|
657
|
+
}
|
|
658
|
+
innerCode.push(...Object.entries(schema).map(([key, s])=>codeGen(s, context, `${valueAlias}[${JSON.stringify(key)}]`, childMode(mode, key))));
|
|
659
|
+
code.push(innerCode.join('\n'), '}');
|
|
660
|
+
return code.join('\n');
|
|
305
661
|
}
|
|
306
|
-
code.push(...Object.entries(schema).map(([key, s])=>codeGen(s, context, `${valueAlias}[${JSON.stringify(key)}]`, `${path}.${key}`)));
|
|
307
|
-
return `${code.join('\n')}`;
|
|
308
662
|
}
|
|
309
|
-
}
|
|
663
|
+
}
|
|
664
|
+
if (typeof schema === 'symbol') {
|
|
310
665
|
const index = context.register(schema);
|
|
311
666
|
const valueAlias = context.unique('v');
|
|
312
667
|
const registryAlias = context.unique('r');
|
|
313
|
-
|
|
314
|
-
const ${valueAlias} = ${valuePath}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
668
|
+
if (mode.fast) {
|
|
669
|
+
return `const ${valueAlias} = ${valuePath};\nconst ${registryAlias} = ctx.registry[${index}];\nif (typeof ${valueAlias} !== 'symbol' || ${valueAlias} !== ${registryAlias}) { ${fail} }`;
|
|
670
|
+
} else {
|
|
671
|
+
return `const ${valueAlias} = ${valuePath};\nconst ${registryAlias} = ctx.registry[${index}];\nif (typeof ${valueAlias} !== 'symbol') { ${emit(`\`Invalid type \${typeof ${valueAlias}}, expected symbol\``)} }\nelse if (${valueAlias} !== ${registryAlias}) { ${emit(`\`Invalid value \${${valueAlias}.toString()}, expected ${schema.toString()}\``)} }`;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
if (schema === null || schema === undefined) {
|
|
320
675
|
const valueAlias = context.unique('v');
|
|
321
|
-
|
|
322
|
-
const ${valueAlias} = ${valuePath}
|
|
323
|
-
|
|
324
|
-
|
|
676
|
+
if (mode.fast) {
|
|
677
|
+
return `const ${valueAlias} = ${valuePath};\nif (${valueAlias} !== null && ${valueAlias} !== undefined) { ${fail} }`;
|
|
678
|
+
} else {
|
|
679
|
+
return `const ${valueAlias} = ${valuePath};\nif (${valueAlias} !== null && ${valueAlias} !== undefined) { ${emit(`\`Invalid value \${String(${valueAlias})}, expected nullable\``)} }`;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
const valueAlias = context.unique('v');
|
|
683
|
+
if (mode.fast) {
|
|
684
|
+
return `const ${valueAlias} = ${valuePath};\nif (typeof ${valueAlias} !== '${typeof schema}' || ${valueAlias} !== ${toLiteral(schema)}) { ${fail} }`;
|
|
325
685
|
} else {
|
|
326
|
-
const valueAlias = context.unique('v');
|
|
327
686
|
const value = context.unique('val');
|
|
328
|
-
return `
|
|
329
|
-
const ${valueAlias} = ${valuePath};
|
|
330
|
-
const ${value} = ${JSON.stringify(schema)};
|
|
331
|
-
${codeGenExpectNonError(valueAlias, path)}
|
|
332
|
-
if (typeof ${valueAlias} !== '${typeof schema}') { throw new TypeError(\`Invalid type \${typeof ${valueAlias}} for path "${path}", expected ${typeof schema}\`); }
|
|
333
|
-
if (${valueAlias} !== ${value}) { throw new TypeError(\`Invalid value \${JSON.stringify(${valueAlias})} for path "${path}", expected ${JSON.stringify(schema)}\`); }
|
|
334
|
-
`;
|
|
687
|
+
return `const ${valueAlias} = ${valuePath};\nconst ${value} = ${toLiteral(schema)};\nif (${valueAlias} instanceof Error) { ${emit(`\`\${${valueAlias}.message}\``)} }\nelse if (typeof ${valueAlias} !== '${typeof schema}') { ${emit(`\`Invalid type \${typeof ${valueAlias}}, expected ${typeof schema}\``)} }\nelse if (${valueAlias} !== ${value}) { ${emit(`\`Invalid value \${String(${valueAlias})}, expected ${toLiteral(schema)}\``)} }`;
|
|
335
688
|
}
|
|
336
689
|
};
|
|
337
|
-
const
|
|
690
|
+
const emptyIssues = [];
|
|
691
|
+
const compile = (schema, options)=>{
|
|
692
|
+
const allErrors = options?.allErrors ?? false;
|
|
693
|
+
if (allErrors) {
|
|
694
|
+
const fastContext = new Context();
|
|
695
|
+
const fastCode = `${codeGen(schema, fastContext, 'data', {
|
|
696
|
+
fast: true
|
|
697
|
+
})}\nreturn true;`;
|
|
698
|
+
const fastValidator = new Function('ctx', `return (data) => {\n${fastCode}\n};`)(fastContext);
|
|
699
|
+
const issueContext = new Context();
|
|
700
|
+
const issueCode = `let issues;\n${codeGen(schema, issueContext, 'data', {
|
|
701
|
+
fast: false,
|
|
702
|
+
firstError: false,
|
|
703
|
+
issues: 'issues',
|
|
704
|
+
path: [],
|
|
705
|
+
pathExpr: '[]'
|
|
706
|
+
})}\nreturn issues || [];`;
|
|
707
|
+
const issueValidator = new Function('ctx', `return (data) => {\n${issueCode}\n};`)(issueContext);
|
|
708
|
+
const validator = (data)=>{
|
|
709
|
+
if (fastValidator(data)) {
|
|
710
|
+
return true;
|
|
711
|
+
}
|
|
712
|
+
validator.issues = issueValidator(data);
|
|
713
|
+
return false;
|
|
714
|
+
};
|
|
715
|
+
validator.issues = emptyIssues;
|
|
716
|
+
return validator;
|
|
717
|
+
}
|
|
718
|
+
const fastContext = new Context();
|
|
719
|
+
const fastCode = `${codeGen(schema, fastContext, 'data', {
|
|
720
|
+
fast: true
|
|
721
|
+
})}\nreturn true;`;
|
|
722
|
+
const fastValidator = new Function('ctx', `return (data) => {\n${fastCode}\n};`)(fastContext);
|
|
338
723
|
const context = new Context();
|
|
339
|
-
const code = codeGen(schema, context, 'data',
|
|
340
|
-
|
|
341
|
-
|
|
724
|
+
const code = codeGen(schema, context, 'data', {
|
|
725
|
+
fast: false,
|
|
726
|
+
firstError: true,
|
|
727
|
+
issues: 'issues',
|
|
728
|
+
path: [],
|
|
729
|
+
pathExpr: '[]'
|
|
730
|
+
});
|
|
731
|
+
const firstErrorValidator = new Function('ctx', `return (data) => {\nlet issues;\n${code}\nreturn issues;\n};`)(context);
|
|
732
|
+
const validator = (data)=>{
|
|
733
|
+
if (fastValidator(data)) {
|
|
734
|
+
return true;
|
|
735
|
+
}
|
|
736
|
+
validator.issues = firstErrorValidator(data);
|
|
737
|
+
return false;
|
|
738
|
+
};
|
|
739
|
+
validator.issues = emptyIssues;
|
|
740
|
+
return validator;
|
|
342
741
|
};
|
|
343
|
-
const ascertain = (schema, data
|
|
344
|
-
compile(schema
|
|
742
|
+
const ascertain = (schema, data)=>{
|
|
743
|
+
const validator = compile(schema);
|
|
744
|
+
if (!validator(data)) {
|
|
745
|
+
throw new TypeError(validator.issues[0].message, {
|
|
746
|
+
cause: {
|
|
747
|
+
issues: validator.issues
|
|
748
|
+
}
|
|
749
|
+
});
|
|
750
|
+
}
|
|
345
751
|
};
|
|
346
|
-
const createValidator = (config
|
|
752
|
+
const createValidator = (config)=>{
|
|
347
753
|
return (schema)=>{
|
|
348
|
-
ascertain(schema, config
|
|
754
|
+
ascertain(schema, config);
|
|
349
755
|
return config;
|
|
350
756
|
};
|
|
351
757
|
};
|
|
758
|
+
const standardSchema = (schema)=>{
|
|
759
|
+
const validator = compile(schema);
|
|
760
|
+
const fn = (data)=>{
|
|
761
|
+
if (!validator(data)) {
|
|
762
|
+
throw new TypeError(validator.issues[0].message, {
|
|
763
|
+
cause: {
|
|
764
|
+
issues: validator.issues
|
|
765
|
+
}
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
fn['~standard'] = {
|
|
770
|
+
version: 1,
|
|
771
|
+
vendor: 'ascertain',
|
|
772
|
+
validate: (value)=>{
|
|
773
|
+
if (validator(value)) {
|
|
774
|
+
return {
|
|
775
|
+
value: value
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
return {
|
|
779
|
+
issues: validator.issues
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
return fn;
|
|
784
|
+
};
|
|
352
785
|
|
|
353
786
|
//# sourceMappingURL=index.cjs.map
|