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