ascertain 3.2.21 → 3.2.23
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 +49 -29
- package/build/index.cjs +439 -74
- package/build/index.cjs.map +1 -1
- package/build/index.d.ts +7 -8
- package/build/index.js +439 -74
- package/build/index.js.map +1 -1
- package/package.json +6 -4
- package/src/index.ts +439 -81
- package/build/__bench__/benchmark.d.ts +0 -1
package/build/index.cjs
CHANGED
|
@@ -101,6 +101,11 @@ const OPTIONAL = Symbol.for('@@optional');
|
|
|
101
101
|
const TUPLE = Symbol.for('@@tuple');
|
|
102
102
|
const DISCRIMINATED = Symbol.for('@@discriminated');
|
|
103
103
|
const CHECK = Symbol.for('@@check');
|
|
104
|
+
const ERR = Symbol.for('@@err');
|
|
105
|
+
function ErrCtor(message) {
|
|
106
|
+
this.message = message;
|
|
107
|
+
}
|
|
108
|
+
ErrCtor.prototype[ERR] = true;
|
|
104
109
|
const OrCtor = function(schemas) {
|
|
105
110
|
this.schemas = schemas;
|
|
106
111
|
};
|
|
@@ -202,7 +207,39 @@ const oneOf = (values, message)=>{
|
|
|
202
207
|
].map(toLiteral).join(', ')}], got \${${v}}\``
|
|
203
208
|
}));
|
|
204
209
|
};
|
|
205
|
-
const
|
|
210
|
+
const utf8Encoder = new TextEncoder();
|
|
211
|
+
const utf8Decoder = new TextDecoder('utf-8');
|
|
212
|
+
const fromBase64Bytes = (value)=>{
|
|
213
|
+
const bin = atob(value);
|
|
214
|
+
const out = new Uint8Array(bin.length);
|
|
215
|
+
for(let i = 0; i < bin.length; i++)out[i] = bin.charCodeAt(i);
|
|
216
|
+
return out;
|
|
217
|
+
};
|
|
218
|
+
const fromBase64 = (value)=>utf8Decoder.decode(fromBase64Bytes(value));
|
|
219
|
+
const HEX_LUT = (()=>{
|
|
220
|
+
const t = new Int8Array(256).fill(-1);
|
|
221
|
+
for(let i = 0; i < 10; i++)t[48 + i] = i;
|
|
222
|
+
for(let i = 0; i < 6; i++){
|
|
223
|
+
t[97 + i] = 10 + i;
|
|
224
|
+
t[65 + i] = 10 + i;
|
|
225
|
+
}
|
|
226
|
+
return t;
|
|
227
|
+
})();
|
|
228
|
+
const fromHex = (value)=>{
|
|
229
|
+
let start = 0;
|
|
230
|
+
if (value.length >= 2 && value.charCodeAt(0) === 48 && (value.charCodeAt(1) | 32) === 120) {
|
|
231
|
+
start = 2;
|
|
232
|
+
}
|
|
233
|
+
const digits = value.length - start;
|
|
234
|
+
if (digits === 0 || digits % 2 !== 0) throw new TypeError('invalid hex length');
|
|
235
|
+
const out = new Uint8Array(digits / 2);
|
|
236
|
+
for(let i = 0; i < out.length; i++){
|
|
237
|
+
const byte = HEX_LUT[value.charCodeAt(start + i * 2)] << 4 | HEX_LUT[value.charCodeAt(start + i * 2 + 1)];
|
|
238
|
+
if (byte < 0) throw new TypeError('invalid hex digit');
|
|
239
|
+
out[i] = byte;
|
|
240
|
+
}
|
|
241
|
+
return out;
|
|
242
|
+
};
|
|
206
243
|
const MULTIPLIERS = {
|
|
207
244
|
ms: 1,
|
|
208
245
|
s: 1000,
|
|
@@ -212,7 +249,7 @@ const MULTIPLIERS = {
|
|
|
212
249
|
w: 604800000
|
|
213
250
|
};
|
|
214
251
|
const TIME_REGEX = /^(\d*\.?\d*)(ms|s|m|h|d|w)?$/;
|
|
215
|
-
const asError = (message)=>new
|
|
252
|
+
const asError = (message)=>new ErrCtor(message);
|
|
216
253
|
const as = {
|
|
217
254
|
string: (value)=>{
|
|
218
255
|
return typeof value === 'string' ? value : asError(`Invalid value "${value}", expected a string`);
|
|
@@ -264,6 +301,16 @@ const as = {
|
|
|
264
301
|
} catch {
|
|
265
302
|
return asError(`Invalid value ${value}, expected a valid base64 string`);
|
|
266
303
|
}
|
|
304
|
+
},
|
|
305
|
+
data: (value, type = 'utf-8')=>{
|
|
306
|
+
if (typeof value !== 'string') return asError(`Invalid value ${value}, expected a string`);
|
|
307
|
+
try {
|
|
308
|
+
if (type === 'hex') return fromHex(value);
|
|
309
|
+
if (type === 'base64') return fromBase64Bytes(value);
|
|
310
|
+
return utf8Encoder.encode(value);
|
|
311
|
+
} catch {
|
|
312
|
+
return asError(`Invalid value ${value}, expected a valid ${type} string`);
|
|
313
|
+
}
|
|
267
314
|
}
|
|
268
315
|
};
|
|
269
316
|
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;
|
|
@@ -350,6 +397,7 @@ class Context {
|
|
|
350
397
|
registry = [];
|
|
351
398
|
lookupMap = new Map();
|
|
352
399
|
varIndex = 0;
|
|
400
|
+
pure = false;
|
|
353
401
|
register(value) {
|
|
354
402
|
const index = this.lookupMap.get(value);
|
|
355
403
|
if (index !== undefined) {
|
|
@@ -366,32 +414,128 @@ class Context {
|
|
|
366
414
|
}
|
|
367
415
|
}
|
|
368
416
|
const isTagged = (schema)=>schema?.[$op] !== undefined;
|
|
369
|
-
const
|
|
417
|
+
const buildDynamicPathExpr = (staticPath, dynamicParts)=>`[${[
|
|
418
|
+
...staticPath.map((k)=>JSON.stringify(k)),
|
|
419
|
+
...dynamicParts
|
|
420
|
+
].join(',')}]`;
|
|
421
|
+
const childMode = (mode, key, ctx)=>{
|
|
422
|
+
const carryReady = !mode.firstError && mode.issuesReady;
|
|
370
423
|
if (typeof key === 'object' && 'dynamic' in key) {
|
|
371
|
-
|
|
424
|
+
const dynamicParts = [
|
|
425
|
+
...mode.dynamicParts ?? [],
|
|
426
|
+
key.dynamic
|
|
427
|
+
];
|
|
428
|
+
const m = {
|
|
429
|
+
fast: false,
|
|
430
|
+
firstError: mode.firstError,
|
|
431
|
+
issues: mode.issues,
|
|
432
|
+
path: mode.path,
|
|
433
|
+
pathExpr: buildDynamicPathExpr(mode.path, dynamicParts),
|
|
434
|
+
dynamicParts
|
|
435
|
+
};
|
|
436
|
+
if (carryReady) m.issuesReady = true;
|
|
437
|
+
return m;
|
|
438
|
+
}
|
|
439
|
+
if (mode.dynamicParts) {
|
|
440
|
+
const dynamicParts = [
|
|
441
|
+
...mode.dynamicParts,
|
|
442
|
+
JSON.stringify(key)
|
|
443
|
+
];
|
|
444
|
+
const m = {
|
|
372
445
|
fast: false,
|
|
373
446
|
firstError: mode.firstError,
|
|
374
447
|
issues: mode.issues,
|
|
375
448
|
path: mode.path,
|
|
376
|
-
pathExpr:
|
|
449
|
+
pathExpr: buildDynamicPathExpr(mode.path, dynamicParts),
|
|
450
|
+
dynamicParts
|
|
377
451
|
};
|
|
452
|
+
if (carryReady) m.issuesReady = true;
|
|
453
|
+
return m;
|
|
378
454
|
}
|
|
379
455
|
const newPath = [
|
|
380
456
|
...mode.path,
|
|
381
457
|
key
|
|
382
458
|
];
|
|
383
|
-
|
|
459
|
+
const pathExpr = `reg[${ctx.register(Object.freeze(newPath))}]`;
|
|
460
|
+
const m = {
|
|
384
461
|
fast: false,
|
|
385
462
|
firstError: mode.firstError,
|
|
386
463
|
issues: mode.issues,
|
|
387
464
|
path: newPath,
|
|
388
|
-
pathExpr
|
|
465
|
+
pathExpr
|
|
389
466
|
};
|
|
467
|
+
if (carryReady) m.issuesReady = true;
|
|
468
|
+
return m;
|
|
390
469
|
};
|
|
391
470
|
const toLiteral = (value)=>typeof value === 'bigint' ? `${value}n` : JSON.stringify(value);
|
|
471
|
+
const collapsibleTypeOf = (s)=>s === String ? 'string' : s === Number ? 'number' : s === Boolean ? 'boolean' : s === BigInt ? 'bigint' : s === Symbol ? 'symbol' : typeof s === 'function' && s?.name === 'Function' ? 'function' : null;
|
|
472
|
+
const buildCollapsedOr = (schemas, v)=>{
|
|
473
|
+
let hasNull = false;
|
|
474
|
+
let hasUndefined = false;
|
|
475
|
+
const groups = new Map();
|
|
476
|
+
for (const s of schemas){
|
|
477
|
+
if (s === null) {
|
|
478
|
+
hasNull = true;
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
if (s === undefined) {
|
|
482
|
+
hasUndefined = true;
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
const ct = collapsibleTypeOf(s);
|
|
486
|
+
if (ct) {
|
|
487
|
+
let g = groups.get(ct);
|
|
488
|
+
if (!g) {
|
|
489
|
+
g = {
|
|
490
|
+
ctor: false,
|
|
491
|
+
literals: []
|
|
492
|
+
};
|
|
493
|
+
groups.set(ct, g);
|
|
494
|
+
}
|
|
495
|
+
g.ctor = true;
|
|
496
|
+
continue;
|
|
497
|
+
}
|
|
498
|
+
const t = typeof s;
|
|
499
|
+
let g = groups.get(t);
|
|
500
|
+
if (!g) {
|
|
501
|
+
g = {
|
|
502
|
+
ctor: false,
|
|
503
|
+
literals: []
|
|
504
|
+
};
|
|
505
|
+
groups.set(t, g);
|
|
506
|
+
}
|
|
507
|
+
g.literals.push(s);
|
|
508
|
+
}
|
|
509
|
+
const clauses = [];
|
|
510
|
+
if (hasNull) clauses.push(`${v} === null`);
|
|
511
|
+
if (hasUndefined) clauses.push(`${v} === undefined`);
|
|
512
|
+
for (const [type, { ctor, literals }] of groups){
|
|
513
|
+
if (ctor) {
|
|
514
|
+
clauses.push(type === 'number' ? `(typeof ${v} === 'number' && ${v} === ${v})` : `typeof ${v} === '${type}'`);
|
|
515
|
+
} else if (literals.length === 1) {
|
|
516
|
+
clauses.push(`${v} === ${toLiteral(literals[0])}`);
|
|
517
|
+
} else {
|
|
518
|
+
clauses.push(`(typeof ${v} === '${type}' && (${literals.map((l)=>`${v} === ${toLiteral(l)}`).join(' || ')}))`);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
return clauses.join(' || ');
|
|
522
|
+
};
|
|
523
|
+
const buildExpectedDesc = (schemas)=>{
|
|
524
|
+
const parts = [];
|
|
525
|
+
for (const s of schemas){
|
|
526
|
+
if (s === null) parts.push('null');
|
|
527
|
+
else if (s === undefined) parts.push('undefined');
|
|
528
|
+
else if (collapsibleTypeOf(s) !== null) parts.push(s.name);
|
|
529
|
+
else parts.push(toLiteral(s));
|
|
530
|
+
}
|
|
531
|
+
return parts.join(', ');
|
|
532
|
+
};
|
|
533
|
+
const isCollapsible = (s)=>s === null || s === undefined || typeof s !== 'object' && typeof s !== 'function' && typeof s !== 'symbol' || collapsibleTypeOf(s) !== null;
|
|
392
534
|
const codeGen = (schema, context, valuePath, mode)=>{
|
|
393
|
-
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} });`;
|
|
535
|
+
const emit = mode.fast ? null : mode.firstError ? (msg)=>`${mode.issues} = [{ message: ${msg}, path: ${mode.pathExpr} }]; return ${mode.issues};` : !mode.firstError && mode.issuesReady ? (msg)=>`${mode.issues}.push({ message: ${msg}, path: ${mode.pathExpr} });` : (msg)=>`(${mode.issues} || (${mode.issues} = [])).push({ message: ${msg}, path: ${mode.pathExpr} });`;
|
|
394
536
|
const fail = mode.fast ? mode.onFail ?? 'return false;' : '';
|
|
537
|
+
const errChk = (v)=>context.pure ? '' : ` || (typeof ${v} === 'object' && ${v} !== null && ${v}[err] === true)`;
|
|
538
|
+
const errBranch = (v)=>context.pure ? '' : `else if (typeof ${v} === 'object' && ${v} !== null && ${v}[err] === true) { ${emit(`\`\${${v}.message}\``)} }`;
|
|
395
539
|
if (isTagged(schema)) {
|
|
396
540
|
const tag = schema[$op];
|
|
397
541
|
if (tag === AND) {
|
|
@@ -402,6 +546,27 @@ const codeGen = (schema, context, valuePath, mode)=>{
|
|
|
402
546
|
const valueAlias = context.unique('v');
|
|
403
547
|
const foundValid = context.unique('valid');
|
|
404
548
|
if (mode.fast) {
|
|
549
|
+
const collapsible = [];
|
|
550
|
+
const complex = [];
|
|
551
|
+
for (const s of schema.schemas){
|
|
552
|
+
if (isCollapsible(s)) collapsible.push(s);
|
|
553
|
+
else complex.push(s);
|
|
554
|
+
}
|
|
555
|
+
if (collapsible.length > 0 && complex.length === 0) {
|
|
556
|
+
return `const ${valueAlias} = ${valuePath};\nif (!(${buildCollapsedOr(collapsible, valueAlias)})) { ${fail} }`;
|
|
557
|
+
}
|
|
558
|
+
if (collapsible.length > 0) {
|
|
559
|
+
const condition = buildCollapsedOr(collapsible, valueAlias);
|
|
560
|
+
const branches = complex.map((s)=>{
|
|
561
|
+
const branchValid = context.unique('valid');
|
|
562
|
+
const branchCode = codeGen(s, context, valueAlias, {
|
|
563
|
+
...mode,
|
|
564
|
+
onFail: `${branchValid} = false;`
|
|
565
|
+
});
|
|
566
|
+
return `if (!${foundValid}) { let ${branchValid} = true; ${branchCode} if (${branchValid}) { ${foundValid} = true; } }`;
|
|
567
|
+
});
|
|
568
|
+
return `const ${valueAlias} = ${valuePath};\nlet ${foundValid} = ${condition};\n${branches.join('\n')}\nif (!${foundValid}) { ${fail} }`;
|
|
569
|
+
}
|
|
405
570
|
const branches = schema.schemas.map((s)=>{
|
|
406
571
|
const branchValid = context.unique('valid');
|
|
407
572
|
const branchCode = codeGen(s, context, valueAlias, {
|
|
@@ -412,6 +577,11 @@ const codeGen = (schema, context, valuePath, mode)=>{
|
|
|
412
577
|
});
|
|
413
578
|
return `const ${valueAlias} = ${valuePath};\nlet ${foundValid} = false;\n${branches.join('\n')}\nif (!${foundValid}) { ${fail} }`;
|
|
414
579
|
} else if (mode.firstError) {
|
|
580
|
+
if (schema.schemas.every(isCollapsible)) {
|
|
581
|
+
const condition = buildCollapsedOr(schema.schemas, valueAlias);
|
|
582
|
+
const expected = buildExpectedDesc(schema.schemas);
|
|
583
|
+
return `const ${valueAlias} = ${valuePath};\nif (!(${condition})) { ${mode.issues} = [{ message: \`Invalid value \${${valueAlias}}, expected one of: ${expected}\`, path: ${mode.pathExpr} }]; return ${mode.issues}; }`;
|
|
584
|
+
}
|
|
415
585
|
const firstBranchIssues = context.unique('iss');
|
|
416
586
|
const branches = schema.schemas.map((s, idx)=>{
|
|
417
587
|
const branchIssues = context.unique('iss');
|
|
@@ -429,6 +599,12 @@ const codeGen = (schema, context, valuePath, mode)=>{
|
|
|
429
599
|
});
|
|
430
600
|
return `const ${valueAlias} = ${valuePath};\nlet ${firstBranchIssues};\nlet ${foundValid} = false;\n${branches.join('\n')}\nif (!${foundValid}) { return ${firstBranchIssues}; }`;
|
|
431
601
|
} else {
|
|
602
|
+
if (schema.schemas.every(isCollapsible)) {
|
|
603
|
+
const condition = buildCollapsedOr(schema.schemas, valueAlias);
|
|
604
|
+
const expected = buildExpectedDesc(schema.schemas);
|
|
605
|
+
const push = mode.issuesReady ? `${mode.issues}.push({ message: \`Invalid value \${${valueAlias}}, expected one of: ${expected}\`, path: ${mode.pathExpr} });` : `(${mode.issues} || (${mode.issues} = [])).push({ message: \`Invalid value \${${valueAlias}}, expected one of: ${expected}\`, path: ${mode.pathExpr} });`;
|
|
606
|
+
return `const ${valueAlias} = ${valuePath};\nif (!(${condition})) { ${push} }`;
|
|
607
|
+
}
|
|
432
608
|
const localIssues = context.unique('iss');
|
|
433
609
|
const branches = schema.schemas.map((s)=>{
|
|
434
610
|
const branchIssues = context.unique('iss');
|
|
@@ -441,11 +617,42 @@ const codeGen = (schema, context, valuePath, mode)=>{
|
|
|
441
617
|
});
|
|
442
618
|
return `if (!${foundValid}) { let ${branchIssues}; ${branchCode} if (!${branchIssues}) { ${foundValid} = true; } else { ${localIssues}.push(...${branchIssues}); } }`;
|
|
443
619
|
});
|
|
444
|
-
|
|
620
|
+
const pushExpr = !mode.fast && !mode.firstError && mode.issuesReady ? `${mode.issues}.push(...${localIssues})` : `(${mode.issues} || (${mode.issues} = [])).push(...${localIssues})`;
|
|
621
|
+
return `const ${valueAlias} = ${valuePath};\nconst ${localIssues} = [];\nlet ${foundValid} = false;\n${branches.join('\n')}\nif (!${foundValid}) { ${pushExpr}; }`;
|
|
445
622
|
}
|
|
446
623
|
} else if (tag === OPTIONAL) {
|
|
447
624
|
const valueAlias = context.unique('v');
|
|
448
|
-
|
|
625
|
+
const inner = schema.schemas[0];
|
|
626
|
+
if (!mode.fast && typeof inner === 'function') {
|
|
627
|
+
const iname = inner?.name;
|
|
628
|
+
const is = inner;
|
|
629
|
+
const pt = is === String ? 'string' : is === Number ? 'number' : is === Boolean ? 'boolean' : is === BigInt ? 'bigint' : is === Symbol ? 'symbol' : null;
|
|
630
|
+
if (pt) {
|
|
631
|
+
const typeMsgs = Object.fromEntries([
|
|
632
|
+
'string',
|
|
633
|
+
'number',
|
|
634
|
+
'boolean',
|
|
635
|
+
'bigint',
|
|
636
|
+
'symbol',
|
|
637
|
+
'undefined',
|
|
638
|
+
'object',
|
|
639
|
+
'function'
|
|
640
|
+
].map((t)=>[
|
|
641
|
+
t,
|
|
642
|
+
`Invalid type ${t}, expected type ${iname}`
|
|
643
|
+
]));
|
|
644
|
+
const typeMsgIdx = context.register(typeMsgs);
|
|
645
|
+
const lines = [
|
|
646
|
+
`const ${valueAlias} = ${valuePath};`,
|
|
647
|
+
`if (${valueAlias} !== undefined && ${valueAlias} !== null) {`
|
|
648
|
+
];
|
|
649
|
+
lines.push(`if (typeof ${valueAlias} !== '${pt}') { ${emit(`reg[${typeMsgIdx}][typeof ${valueAlias}]`)} }`);
|
|
650
|
+
if (pt === 'number') lines.push(`else if (${valueAlias} !== ${valueAlias}) { ${emit(`"Invalid value NaN, expected a valid ${iname}"`)} }`);
|
|
651
|
+
lines.push(`}`);
|
|
652
|
+
return lines.join('\n');
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
return `const ${valueAlias} = ${valuePath};\nif (${valueAlias} !== undefined && ${valueAlias} !== null) { ${codeGen(inner, context, valueAlias, mode)} }`;
|
|
449
656
|
} else if (tag === TUPLE) {
|
|
450
657
|
const valueAlias = context.unique('v');
|
|
451
658
|
if (mode.fast) {
|
|
@@ -457,12 +664,12 @@ const codeGen = (schema, context, valuePath, mode)=>{
|
|
|
457
664
|
`else if (typeof ${valueAlias} !== 'object') { ${emit(`\`Invalid type \${typeof ${valueAlias}}, expected an instance of Array\``)} }`,
|
|
458
665
|
`else if (!Array.isArray(${valueAlias})) { ${emit(`\`Invalid instance of \${${valueAlias}.constructor?.name}, expected an instance of Array\``)} }`,
|
|
459
666
|
`else if (${valueAlias}.length !== ${schema.schemas.length}) { ${emit(`\`Invalid tuple length \${${valueAlias}.length}, expected ${schema.schemas.length}\``)} }`,
|
|
460
|
-
`else { ${schema.schemas.map((s, idx)=>codeGen(s, context, `${valueAlias}[${idx}]`, childMode(mode, idx))).join('\n')} }`
|
|
667
|
+
`else { ${schema.schemas.map((s, idx)=>codeGen(s, context, `${valueAlias}[${idx}]`, childMode(mode, idx, context))).join('\n')} }`
|
|
461
668
|
].join('\n');
|
|
462
669
|
}
|
|
463
670
|
} else if (tag === CHECK) {
|
|
464
671
|
const valueAlias = context.unique('v');
|
|
465
|
-
const ref = (v)=>`
|
|
672
|
+
const ref = (v)=>`reg[${context.register(v)}]`;
|
|
466
673
|
const { check: cond, message } = schema.compile(valueAlias, {
|
|
467
674
|
ref
|
|
468
675
|
});
|
|
@@ -489,31 +696,33 @@ const codeGen = (schema, context, valuePath, mode)=>{
|
|
|
489
696
|
schema: s
|
|
490
697
|
});
|
|
491
698
|
}
|
|
699
|
+
const genVariantProps = (s, variantMode)=>{
|
|
700
|
+
const obj = s;
|
|
701
|
+
return Object.entries(obj).filter(([k])=>k !== key).map(([k, ps])=>codeGen(ps, context, `${valueAlias}[${JSON.stringify(k)}]`, variantMode.fast ? variantMode : childMode(variantMode, k, context))).join('\n');
|
|
702
|
+
};
|
|
492
703
|
if (mode.fast) {
|
|
493
704
|
const branches = variants.map(({ value, schema: s })=>{
|
|
494
|
-
|
|
495
|
-
return `if (${discriminantAlias} === ${JSON.stringify(value)}) { ${branchCode} }`;
|
|
705
|
+
return `if (${discriminantAlias} === ${JSON.stringify(value)}) { ${genVariantProps(s, mode)} }`;
|
|
496
706
|
});
|
|
497
707
|
return [
|
|
498
708
|
`const ${valueAlias} = ${valuePath};`,
|
|
499
|
-
`if (${valueAlias} === null || ${valueAlias} === undefined || typeof ${valueAlias} !== 'object'
|
|
709
|
+
`if (${valueAlias} === null || ${valueAlias} === undefined || typeof ${valueAlias} !== 'object'${errChk(valueAlias)}) { ${fail} }`,
|
|
500
710
|
`const ${discriminantAlias} = ${valueAlias}[${keyStr}];`,
|
|
501
711
|
branches.join(' else ') + ` else { ${fail} }`
|
|
502
712
|
].join('\n');
|
|
503
713
|
} else {
|
|
504
714
|
const validValues = variants.map((v)=>JSON.stringify(v.value)).join(', ');
|
|
505
715
|
const branches = variants.map(({ value, schema: s })=>{
|
|
506
|
-
|
|
507
|
-
return `if (${discriminantAlias} === ${JSON.stringify(value)}) { ${branchCode} }`;
|
|
716
|
+
return `if (${discriminantAlias} === ${JSON.stringify(value)}) { ${genVariantProps(s, mode)} }`;
|
|
508
717
|
});
|
|
509
718
|
return [
|
|
510
719
|
`const ${valueAlias} = ${valuePath};`,
|
|
511
720
|
`if (${valueAlias} === null || ${valueAlias} === undefined) { ${emit(`\`Invalid value \${${valueAlias}}, expected non-nullable\``)} }`,
|
|
512
721
|
`else if (typeof ${valueAlias} !== 'object') { ${emit(`\`Invalid type \${typeof ${valueAlias}}, expected an object\``)} }`,
|
|
513
|
-
|
|
722
|
+
`${errBranch(valueAlias)}`,
|
|
514
723
|
`else {`,
|
|
515
724
|
` const ${discriminantAlias} = ${valueAlias}[${keyStr}];`,
|
|
516
|
-
` ${branches.join(' else ')} else { ${emit(
|
|
725
|
+
` ${branches.join(' else ')} else { ${emit(`"Invalid discriminant value " + String(${discriminantAlias}) + ", expected one of: ${validValues.replace(/"/g, "'")}"`)} }`,
|
|
517
726
|
`}`
|
|
518
727
|
].join('\n');
|
|
519
728
|
}
|
|
@@ -529,7 +738,7 @@ const codeGen = (schema, context, valuePath, mode)=>{
|
|
|
529
738
|
const checks = [
|
|
530
739
|
`typeof ${valueAlias} !== '${primitiveType}'`
|
|
531
740
|
];
|
|
532
|
-
if (primitiveType === 'number') checks.push(
|
|
741
|
+
if (primitiveType === 'number') checks.push(`${valueAlias} !== ${valueAlias}`);
|
|
533
742
|
return `const ${valueAlias} = ${valuePath};\nif (${checks.join(' || ')}) { ${fail} }`;
|
|
534
743
|
} else if (name === 'Function') {
|
|
535
744
|
return `const ${valueAlias} = ${valuePath};\nif (typeof ${valueAlias} !== 'function') { ${fail} }`;
|
|
@@ -537,28 +746,42 @@ const codeGen = (schema, context, valuePath, mode)=>{
|
|
|
537
746
|
const isError = schema === Error || schema?.prototype instanceof Error;
|
|
538
747
|
const index = context.register(schema);
|
|
539
748
|
const registryAlias = context.unique('r');
|
|
540
|
-
return `const ${valueAlias} = ${valuePath};\nconst ${registryAlias} =
|
|
749
|
+
return `const ${valueAlias} = ${valuePath};\nconst ${registryAlias} = reg[${index}];\nif (${valueAlias} === null || ${valueAlias} === undefined${isError || context.pure ? '' : ` || (typeof ${valueAlias} === 'object' && ${valueAlias}[err] === true)`} || (typeof ${valueAlias} === 'object' && !(${valueAlias} instanceof ${registryAlias})) || (typeof ${valueAlias} !== 'object' && ${valueAlias}?.constructor !== ${registryAlias}) || Number.isNaN(${valueAlias}?.valueOf?.())) { ${fail} }`;
|
|
541
750
|
}
|
|
542
751
|
} else {
|
|
543
752
|
const code = [
|
|
544
753
|
`const ${valueAlias} = ${valuePath};`
|
|
545
754
|
];
|
|
546
755
|
if (primitiveType) {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
756
|
+
const typeMsgs = Object.fromEntries([
|
|
757
|
+
'string',
|
|
758
|
+
'number',
|
|
759
|
+
'boolean',
|
|
760
|
+
'bigint',
|
|
761
|
+
'symbol',
|
|
762
|
+
'undefined',
|
|
763
|
+
'object',
|
|
764
|
+
'function'
|
|
765
|
+
].map((t)=>[
|
|
766
|
+
t,
|
|
767
|
+
`Invalid type ${t}, expected type ${name}`
|
|
768
|
+
]));
|
|
769
|
+
const typeMsgIdx = context.register(typeMsgs);
|
|
770
|
+
code.push(`if (${valueAlias} === null || ${valueAlias} === undefined) { ${emit(`${valueAlias} === null ? "Invalid value null, expected non-nullable" : "Invalid value undefined, expected non-nullable"`)} }`);
|
|
771
|
+
code.push(`${errBranch(valueAlias)}`);
|
|
772
|
+
code.push(`else if (typeof ${valueAlias} !== '${primitiveType}') { ${emit(`reg[${typeMsgIdx}][typeof ${valueAlias}]`)} }`);
|
|
773
|
+
if (primitiveType === 'number') code.push(`else if (${valueAlias} !== ${valueAlias}) { ${emit(`"Invalid value NaN, expected a valid ${name}"`)} }`);
|
|
551
774
|
} else if (name === 'Function') {
|
|
552
775
|
code.push(`if (${valueAlias} === null || ${valueAlias} === undefined) { ${emit(`\`Invalid value \${${valueAlias}}, expected non-nullable\``)} }`);
|
|
553
|
-
code.push(
|
|
776
|
+
code.push(`${errBranch(valueAlias)}`);
|
|
554
777
|
code.push(`else if (typeof ${valueAlias} !== 'function') { ${emit(`\`Invalid type \${typeof ${valueAlias}}, expected type Function\``)} }`);
|
|
555
778
|
} else {
|
|
556
779
|
const isError = schema === Error || schema?.prototype instanceof Error;
|
|
557
780
|
const index = context.register(schema);
|
|
558
781
|
const registryAlias = context.unique('r');
|
|
559
|
-
code.push(`const ${registryAlias} =
|
|
782
|
+
code.push(`const ${registryAlias} = reg[${index}];`);
|
|
560
783
|
code.push(`if (${valueAlias} === null || ${valueAlias} === undefined) { ${emit(`\`Invalid value \${${valueAlias}}, expected non-nullable\``)} }`);
|
|
561
|
-
if (!isError) code.push(
|
|
784
|
+
if (!isError) code.push(`${errBranch(valueAlias)}`);
|
|
562
785
|
code.push(`else if (typeof ${valueAlias} === 'object' && !(${valueAlias} instanceof ${registryAlias})) { ${emit(`\`Invalid instance of \${${valueAlias}?.constructor?.name}, expected an instance of ${name}\``)} }`);
|
|
563
786
|
code.push(`else if (typeof ${valueAlias} !== 'object' && ${valueAlias}?.constructor !== ${registryAlias}) { ${emit(`\`Invalid type \${${valueAlias}?.constructor?.name}, expected type ${name}\``)} }`);
|
|
564
787
|
code.push(`else if (Number.isNaN(${valueAlias}?.valueOf?.())) { ${emit(`\`Invalid value \${${valueAlias}}, expected a valid ${name}\``)} }`);
|
|
@@ -580,11 +803,24 @@ const codeGen = (schema, context, valuePath, mode)=>{
|
|
|
580
803
|
}
|
|
581
804
|
return code;
|
|
582
805
|
} else {
|
|
806
|
+
const arrTypeMsgs = Object.fromEntries([
|
|
807
|
+
'string',
|
|
808
|
+
'number',
|
|
809
|
+
'boolean',
|
|
810
|
+
'bigint',
|
|
811
|
+
'symbol',
|
|
812
|
+
'undefined',
|
|
813
|
+
'function'
|
|
814
|
+
].map((t)=>[
|
|
815
|
+
t,
|
|
816
|
+
`Invalid type ${t}, expected an instance of Array`
|
|
817
|
+
]));
|
|
818
|
+
const arrTypeMsgIdx = context.register(arrTypeMsgs);
|
|
583
819
|
const code = [
|
|
584
820
|
`const ${valueAlias} = ${valuePath};`,
|
|
585
|
-
`if (${valueAlias} === null || ${valueAlias} === undefined) { ${emit(
|
|
586
|
-
|
|
587
|
-
`else if (typeof ${valueAlias} !== 'object') { ${emit(
|
|
821
|
+
`if (${valueAlias} === null || ${valueAlias} === undefined) { ${emit(`${valueAlias} === null ? "Invalid value null, expected non-nullable" : "Invalid value undefined, expected non-nullable"`)} }`,
|
|
822
|
+
`${errBranch(valueAlias)}`,
|
|
823
|
+
`else if (typeof ${valueAlias} !== 'object') { ${emit(`reg[${arrTypeMsgIdx}][typeof ${valueAlias}]`)} }`,
|
|
588
824
|
`else if (!Array.isArray(${valueAlias})) { ${emit(`\`Invalid instance of \${${valueAlias}.constructor?.name}, expected an instance of Array\``)} }`
|
|
589
825
|
];
|
|
590
826
|
if (schema.length > 0) {
|
|
@@ -593,10 +829,10 @@ const codeGen = (schema, context, valuePath, mode)=>{
|
|
|
593
829
|
if (schema.length === 1) {
|
|
594
830
|
code.push(`else { for (let ${key} = 0; ${key} < ${valueAlias}.length; ${key}++) { const ${value} = ${valueAlias}[${key}]; ${codeGen(schema[0], context, value, childMode(mode, {
|
|
595
831
|
dynamic: key
|
|
596
|
-
}))} } }`);
|
|
832
|
+
}, context))} } }`);
|
|
597
833
|
} else {
|
|
598
834
|
code.push(`else if (${valueAlias}.length > ${schema.length}) { ${emit(`\`Invalid tuple length \${${valueAlias}.length}, expected ${schema.length}\``)} }`);
|
|
599
|
-
code.push(`else { ${schema.map((s, idx)=>codeGen(s, context, `${valueAlias}[${idx}]`, childMode(mode, idx))).join('\n')} }`);
|
|
835
|
+
code.push(`else { ${schema.map((s, idx)=>codeGen(s, context, `${valueAlias}[${idx}]`, childMode(mode, idx, context))).join('\n')} }`);
|
|
600
836
|
}
|
|
601
837
|
}
|
|
602
838
|
return code.join('\n');
|
|
@@ -606,68 +842,127 @@ const codeGen = (schema, context, valuePath, mode)=>{
|
|
|
606
842
|
if (schema instanceof RegExp) {
|
|
607
843
|
const valueAlias = context.unique('v');
|
|
608
844
|
if (mode.fast) {
|
|
609
|
-
return `const ${valueAlias} = ${valuePath};\nif (${valueAlias} === null || ${valueAlias} === undefined
|
|
845
|
+
return `const ${valueAlias} = ${valuePath};\nif (${valueAlias} === null || ${valueAlias} === undefined${errChk(valueAlias)} || !${schema.toString()}.test(String(${valueAlias}))) { ${fail} }`;
|
|
610
846
|
} else {
|
|
611
|
-
return `const ${valueAlias} = ${valuePath};\nif (${valueAlias} === null || ${valueAlias} === undefined) { ${emit(`\`Invalid value \${${valueAlias}}, expected non-nullable\``)} }\
|
|
847
|
+
return `const ${valueAlias} = ${valuePath};\nif (${valueAlias} === null || ${valueAlias} === undefined) { ${emit(`\`Invalid value \${${valueAlias}}, expected non-nullable\``)} }\n${errBranch(valueAlias)}\n${context.pure ? '' : 'else '}if (!${schema.toString()}.test(String(${valueAlias}))) { ${emit(`\`Invalid value \${${valueAlias}}, expected to match ${schema.toString()}\``)} }`;
|
|
612
848
|
}
|
|
613
849
|
} else {
|
|
614
850
|
const valueAlias = context.unique('v');
|
|
615
851
|
if (mode.fast) {
|
|
616
|
-
|
|
852
|
+
const indexed = mode.indexed && !mode.onFail;
|
|
853
|
+
const rootFail = indexed ? 'return 0;' : fail;
|
|
854
|
+
let code = `const ${valueAlias} = ${valuePath};\nif (${valueAlias} === null || typeof ${valueAlias} !== 'object'${errChk(valueAlias)}) { ${rootFail} }`;
|
|
617
855
|
if ($keys in schema) {
|
|
618
|
-
const keysAlias = context.unique('k');
|
|
619
856
|
const kAlias = context.unique('k');
|
|
620
|
-
code += `\
|
|
857
|
+
code += `\nfor (const ${kAlias} in ${valueAlias}) { ${codeGen(schema[$keys], context, kAlias, indexed ? {
|
|
858
|
+
fast: true,
|
|
859
|
+
onFail: 'return 0;'
|
|
860
|
+
} : mode)} }`;
|
|
621
861
|
}
|
|
622
862
|
if ($values in schema) {
|
|
623
|
-
const vAlias = context.unique('val');
|
|
624
863
|
const kAlias = context.unique('k');
|
|
625
|
-
const
|
|
626
|
-
|
|
864
|
+
code += `\nfor (const ${kAlias} in ${valueAlias}) { ${codeGen(schema[$values], context, `${valueAlias}[${kAlias}]`, indexed ? {
|
|
865
|
+
fast: true,
|
|
866
|
+
onFail: 'return 0;'
|
|
867
|
+
} : mode)} }`;
|
|
627
868
|
}
|
|
628
869
|
if ($strict in schema && schema[$strict]) {
|
|
629
|
-
const
|
|
870
|
+
const allowedRef = `reg[${context.register(Object.fromEntries(Object.keys(schema).map((k)=>[
|
|
871
|
+
k,
|
|
872
|
+
1
|
|
873
|
+
])))}]`;
|
|
630
874
|
const kAlias = context.unique('k');
|
|
631
|
-
const
|
|
632
|
-
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} }`;
|
|
875
|
+
code += `\nfor (const ${kAlias} in ${valueAlias}) { if (!Object.hasOwn(${allowedRef}, ${kAlias})) { ${rootFail} } }`;
|
|
633
876
|
}
|
|
634
|
-
|
|
877
|
+
const entries = Object.entries(schema);
|
|
878
|
+
code += '\n' + entries.map(([key, s], idx)=>{
|
|
879
|
+
const propMode = indexed ? {
|
|
880
|
+
fast: true,
|
|
881
|
+
onFail: `return ${idx + 1};`
|
|
882
|
+
} : mode;
|
|
883
|
+
return codeGen(s, context, `${valueAlias}[${JSON.stringify(key)}]`, propMode);
|
|
884
|
+
}).join('\n');
|
|
885
|
+
if (indexed) code += `\nreturn -1;`;
|
|
635
886
|
return code;
|
|
636
887
|
} else {
|
|
888
|
+
const sv = !mode.fast && mode.startVar;
|
|
637
889
|
const code = [
|
|
638
|
-
`const ${valueAlias} = ${valuePath}
|
|
639
|
-
`if (${valueAlias} === null || ${valueAlias} === undefined) { ${emit(`\`Invalid value \${${valueAlias}}, expected non-nullable\``)} }`,
|
|
640
|
-
`else if (typeof ${valueAlias} !== 'object') { ${emit(`\`Invalid type \${typeof ${valueAlias}}, expected an instance of Object\``)} }`,
|
|
641
|
-
`else if (${valueAlias} instanceof Error) { ${emit(`\`\${${valueAlias}.message}\``)} }`,
|
|
642
|
-
'else {'
|
|
890
|
+
`const ${valueAlias} = ${valuePath};`
|
|
643
891
|
];
|
|
892
|
+
if (sv) {
|
|
893
|
+
const rootCase = [];
|
|
894
|
+
if (mode.firstError) {
|
|
895
|
+
rootCase.push(`if (${valueAlias} === null || ${valueAlias} === undefined) { ${emit(`\`Invalid value \${${valueAlias}}, expected non-nullable\``)} }`);
|
|
896
|
+
rootCase.push(`else if (typeof ${valueAlias} !== 'object') { ${emit(`\`Invalid type \${typeof ${valueAlias}}, expected an instance of Object\``)} }`);
|
|
897
|
+
rootCase.push(`${errBranch(valueAlias)}`);
|
|
898
|
+
} else {
|
|
899
|
+
rootCase.push(`if (${valueAlias} === null || ${valueAlias} === undefined) { ${emit(`\`Invalid value \${${valueAlias}}, expected non-nullable\``)} break; }`);
|
|
900
|
+
rootCase.push(`else if (typeof ${valueAlias} !== 'object') { ${emit(`\`Invalid type \${typeof ${valueAlias}}, expected an instance of Object\``)} break; }`);
|
|
901
|
+
if (!context.pure) rootCase.push(`else if (typeof ${valueAlias} === 'object' && ${valueAlias}[err] === true) { ${emit(`\`\${${valueAlias}.message}\``)} break; }`);
|
|
902
|
+
}
|
|
903
|
+
if ($keys in schema) {
|
|
904
|
+
const kAlias = context.unique('k');
|
|
905
|
+
rootCase.push(`for (const ${kAlias} in ${valueAlias}) { ${codeGen(schema[$keys], context, kAlias, childMode(mode, {
|
|
906
|
+
dynamic: kAlias
|
|
907
|
+
}, context))} }`);
|
|
908
|
+
}
|
|
909
|
+
if ($values in schema) {
|
|
910
|
+
const kAlias = context.unique('k');
|
|
911
|
+
rootCase.push(`for (const ${kAlias} in ${valueAlias}) { ${codeGen(schema[$values], context, `${valueAlias}[${kAlias}]`, childMode(mode, {
|
|
912
|
+
dynamic: kAlias
|
|
913
|
+
}, context))} }`);
|
|
914
|
+
}
|
|
915
|
+
if ($strict in schema && schema[$strict]) {
|
|
916
|
+
const allowedRef = `reg[${context.register(Object.fromEntries(Object.keys(schema).map((k)=>[
|
|
917
|
+
k,
|
|
918
|
+
1
|
|
919
|
+
])))}]`;
|
|
920
|
+
const kAlias = context.unique('k');
|
|
921
|
+
const extraAlias = context.unique('ex');
|
|
922
|
+
rootCase.push(`const ${extraAlias} = [];`);
|
|
923
|
+
rootCase.push(`for (const ${kAlias} in ${valueAlias}) { if (!Object.hasOwn(${allowedRef}, ${kAlias})) ${extraAlias}.push(${kAlias}); }`);
|
|
924
|
+
rootCase.push(`if (${extraAlias}.length !== 0) { ${emit(`\`Extra properties: \${${extraAlias}}, are not allowed\``)} }`);
|
|
925
|
+
}
|
|
926
|
+
code.push(`switch (${sv}) {`);
|
|
927
|
+
code.push(`case 0: { ${rootCase.join('\n')} }`);
|
|
928
|
+
const entries = Object.entries(schema);
|
|
929
|
+
entries.forEach(([key, s], idx)=>{
|
|
930
|
+
const propCode = codeGen(s, context, `${valueAlias}[${JSON.stringify(key)}]`, childMode(mode, key, context));
|
|
931
|
+
code.push(`case ${idx + 1}: { ${propCode} }`);
|
|
932
|
+
});
|
|
933
|
+
code.push(`}`);
|
|
934
|
+
return code.join('\n');
|
|
935
|
+
}
|
|
936
|
+
code.push(`if (${valueAlias} === null || ${valueAlias} === undefined) { ${emit(`\`Invalid value \${${valueAlias}}, expected non-nullable\``)} }`);
|
|
937
|
+
code.push(`else if (typeof ${valueAlias} !== 'object') { ${emit(`\`Invalid type \${typeof ${valueAlias}}, expected an instance of Object\``)} }`);
|
|
938
|
+
code.push(`${errBranch(valueAlias)}`);
|
|
939
|
+
code.push('else {');
|
|
644
940
|
const innerCode = [];
|
|
645
941
|
if ($keys in schema) {
|
|
646
|
-
const keysAlias = context.unique('k');
|
|
647
942
|
const kAlias = context.unique('k');
|
|
648
|
-
innerCode.push(`const ${
|
|
649
|
-
innerCode.push(`for (const ${kAlias} of ${keysAlias}) { ${codeGen(schema[$keys], context, kAlias, childMode(mode, {
|
|
943
|
+
innerCode.push(`for (const ${kAlias} in ${valueAlias}) { ${codeGen(schema[$keys], context, kAlias, childMode(mode, {
|
|
650
944
|
dynamic: kAlias
|
|
651
|
-
}))} }`);
|
|
945
|
+
}, context))} }`);
|
|
652
946
|
}
|
|
653
947
|
if ($values in schema) {
|
|
654
|
-
const vAlias = context.unique('val');
|
|
655
948
|
const kAlias = context.unique('k');
|
|
656
|
-
const
|
|
657
|
-
innerCode.push(`const ${entriesAlias} = Object.entries(${valueAlias});`);
|
|
658
|
-
innerCode.push(`for (const [${kAlias}, ${vAlias}] of ${entriesAlias}) { ${codeGen(schema[$values], context, vAlias, childMode(mode, {
|
|
949
|
+
innerCode.push(`for (const ${kAlias} in ${valueAlias}) { ${codeGen(schema[$values], context, `${valueAlias}[${kAlias}]`, childMode(mode, {
|
|
659
950
|
dynamic: kAlias
|
|
660
|
-
}))} }`);
|
|
951
|
+
}, context))} }`);
|
|
661
952
|
}
|
|
662
953
|
if ($strict in schema && schema[$strict]) {
|
|
663
|
-
const
|
|
954
|
+
const allowedRef = `reg[${context.register(Object.fromEntries(Object.keys(schema).map((k)=>[
|
|
955
|
+
k,
|
|
956
|
+
1
|
|
957
|
+
])))}]`;
|
|
664
958
|
const kAlias = context.unique('k');
|
|
665
959
|
const extraAlias = context.unique('ex');
|
|
666
|
-
innerCode.push(`const ${
|
|
667
|
-
innerCode.push(`const ${
|
|
960
|
+
innerCode.push(`const ${extraAlias} = [];`);
|
|
961
|
+
innerCode.push(`for (const ${kAlias} in ${valueAlias}) { if (!Object.hasOwn(${allowedRef}, ${kAlias})) ${extraAlias}.push(${kAlias}); }`);
|
|
668
962
|
innerCode.push(`if (${extraAlias}.length !== 0) { ${emit(`\`Extra properties: \${${extraAlias}}, are not allowed\``)} }`);
|
|
669
963
|
}
|
|
670
|
-
|
|
964
|
+
const entries = Object.entries(schema);
|
|
965
|
+
innerCode.push(...entries.map(([key, s])=>codeGen(s, context, `${valueAlias}[${JSON.stringify(key)}]`, childMode(mode, key, context))));
|
|
671
966
|
code.push(innerCode.join('\n'), '}');
|
|
672
967
|
return code.join('\n');
|
|
673
968
|
}
|
|
@@ -678,9 +973,9 @@ const codeGen = (schema, context, valuePath, mode)=>{
|
|
|
678
973
|
const valueAlias = context.unique('v');
|
|
679
974
|
const registryAlias = context.unique('r');
|
|
680
975
|
if (mode.fast) {
|
|
681
|
-
return `const ${valueAlias} = ${valuePath};\nconst ${registryAlias} =
|
|
976
|
+
return `const ${valueAlias} = ${valuePath};\nconst ${registryAlias} = reg[${index}];\nif (typeof ${valueAlias} !== 'symbol' || ${valueAlias} !== ${registryAlias}) { ${fail} }`;
|
|
682
977
|
} else {
|
|
683
|
-
return `const ${valueAlias} = ${valuePath};\nconst ${registryAlias} =
|
|
978
|
+
return `const ${valueAlias} = ${valuePath};\nconst ${registryAlias} = reg[${index}];\nif (typeof ${valueAlias} !== 'symbol') { ${emit(`\`Invalid type \${typeof ${valueAlias}}, expected symbol\``)} }\nelse if (${valueAlias} !== ${registryAlias}) { ${emit(`\`Invalid value \${${valueAlias}.toString()}, expected ${schema.toString()}\``)} }`;
|
|
684
979
|
}
|
|
685
980
|
}
|
|
686
981
|
if (schema === null || schema === undefined) {
|
|
@@ -696,27 +991,63 @@ const codeGen = (schema, context, valuePath, mode)=>{
|
|
|
696
991
|
return `const ${valueAlias} = ${valuePath};\nif (typeof ${valueAlias} !== '${typeof schema}' || ${valueAlias} !== ${toLiteral(schema)}) { ${fail} }`;
|
|
697
992
|
} else {
|
|
698
993
|
const value = context.unique('val');
|
|
699
|
-
return `const ${valueAlias} = ${valuePath};\nconst ${value} = ${toLiteral(schema)};\nif (${valueAlias}
|
|
994
|
+
return context.pure ? `const ${valueAlias} = ${valuePath};\nconst ${value} = ${toLiteral(schema)};\nif (typeof ${valueAlias} !== '${typeof schema}') { ${emit(`\`Invalid type \${typeof ${valueAlias}}, expected ${typeof schema}\``)} }\nelse if (${valueAlias} !== ${value}) { ${emit(`\`Invalid value \${String(${valueAlias})}, expected ${toLiteral(schema)}\``)} }` : `const ${valueAlias} = ${valuePath};\nconst ${value} = ${toLiteral(schema)};\nif (typeof ${valueAlias} === 'object' && ${valueAlias} !== null && ${valueAlias}[err] === true) { ${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)}\``)} }`;
|
|
700
995
|
}
|
|
701
996
|
};
|
|
702
997
|
const emptyIssues = [];
|
|
703
998
|
const compile = (schema, options)=>{
|
|
704
999
|
const allErrors = options?.allErrors ?? false;
|
|
1000
|
+
const pure = options?.pure ?? false;
|
|
1001
|
+
const canIndex = typeof schema === 'object' && schema !== null && !isTagged(schema) && !Array.isArray(schema) && !(schema instanceof RegExp);
|
|
705
1002
|
if (allErrors) {
|
|
1003
|
+
if (canIndex) {
|
|
1004
|
+
const fastContext = new Context();
|
|
1005
|
+
fastContext.pure = pure;
|
|
1006
|
+
const fastCode = codeGen(schema, fastContext, 'data', {
|
|
1007
|
+
fast: true,
|
|
1008
|
+
indexed: true
|
|
1009
|
+
});
|
|
1010
|
+
const indexedFast = new Function('reg', 'err', `return (data) => {\n${fastCode}\n};`)(fastContext.registry, ERR);
|
|
1011
|
+
const issueContext = new Context();
|
|
1012
|
+
issueContext.pure = pure;
|
|
1013
|
+
const rootPath = `reg[${issueContext.register(Object.freeze([]))}]`;
|
|
1014
|
+
const issueCode = `let issues = [];\n${codeGen(schema, issueContext, 'data', {
|
|
1015
|
+
fast: false,
|
|
1016
|
+
firstError: false,
|
|
1017
|
+
issues: 'issues',
|
|
1018
|
+
path: [],
|
|
1019
|
+
pathExpr: rootPath,
|
|
1020
|
+
startVar: 'start',
|
|
1021
|
+
issuesReady: true
|
|
1022
|
+
})}\nreturn issues;`;
|
|
1023
|
+
const issueValidator = new Function('reg', 'err', `return (data, start) => {\n${issueCode}\n};`)(issueContext.registry, ERR);
|
|
1024
|
+
const validator = (data)=>{
|
|
1025
|
+
const idx = indexedFast(data);
|
|
1026
|
+
if (idx === -1) return true;
|
|
1027
|
+
validator.issues = issueValidator(data, idx);
|
|
1028
|
+
return false;
|
|
1029
|
+
};
|
|
1030
|
+
validator.issues = emptyIssues;
|
|
1031
|
+
return validator;
|
|
1032
|
+
}
|
|
706
1033
|
const fastContext = new Context();
|
|
1034
|
+
fastContext.pure = pure;
|
|
707
1035
|
const fastCode = `${codeGen(schema, fastContext, 'data', {
|
|
708
1036
|
fast: true
|
|
709
1037
|
})}\nreturn true;`;
|
|
710
|
-
const fastValidator = new Function('
|
|
1038
|
+
const fastValidator = new Function('reg', 'err', `return (data) => {\n${fastCode}\n};`)(fastContext.registry, ERR);
|
|
711
1039
|
const issueContext = new Context();
|
|
712
|
-
|
|
1040
|
+
issueContext.pure = pure;
|
|
1041
|
+
const rootPath2 = `reg[${issueContext.register(Object.freeze([]))}]`;
|
|
1042
|
+
const issueCode = `let issues = [];\n${codeGen(schema, issueContext, 'data', {
|
|
713
1043
|
fast: false,
|
|
714
1044
|
firstError: false,
|
|
715
1045
|
issues: 'issues',
|
|
716
1046
|
path: [],
|
|
717
|
-
pathExpr:
|
|
718
|
-
|
|
719
|
-
|
|
1047
|
+
pathExpr: rootPath2,
|
|
1048
|
+
issuesReady: true
|
|
1049
|
+
})}\nreturn issues;`;
|
|
1050
|
+
const issueValidator = new Function('reg', 'err', `return (data) => {\n${issueCode}\n};`)(issueContext.registry, ERR);
|
|
720
1051
|
const validator = (data)=>{
|
|
721
1052
|
if (fastValidator(data)) {
|
|
722
1053
|
return true;
|
|
@@ -727,20 +1058,54 @@ const compile = (schema, options)=>{
|
|
|
727
1058
|
validator.issues = emptyIssues;
|
|
728
1059
|
return validator;
|
|
729
1060
|
}
|
|
1061
|
+
if (canIndex) {
|
|
1062
|
+
const fastContext = new Context();
|
|
1063
|
+
fastContext.pure = pure;
|
|
1064
|
+
const fastCode = codeGen(schema, fastContext, 'data', {
|
|
1065
|
+
fast: true,
|
|
1066
|
+
indexed: true
|
|
1067
|
+
});
|
|
1068
|
+
const indexedFast = new Function('reg', 'err', `return (data) => {\n${fastCode}\n};`)(fastContext.registry, ERR);
|
|
1069
|
+
const context = new Context();
|
|
1070
|
+
context.pure = pure;
|
|
1071
|
+
const rootPath = `reg[${context.register(Object.freeze([]))}]`;
|
|
1072
|
+
const code = codeGen(schema, context, 'data', {
|
|
1073
|
+
fast: false,
|
|
1074
|
+
firstError: true,
|
|
1075
|
+
issues: 'issues',
|
|
1076
|
+
path: [],
|
|
1077
|
+
pathExpr: rootPath,
|
|
1078
|
+
startVar: 'start'
|
|
1079
|
+
});
|
|
1080
|
+
const firstErrorValidator = new Function('reg', 'err', `return (data, start) => {\nlet issues;\n${code}\nreturn issues;\n};`)(context.registry, ERR);
|
|
1081
|
+
const validator = (data)=>{
|
|
1082
|
+
const idx = indexedFast(data);
|
|
1083
|
+
if (idx === -1) {
|
|
1084
|
+
return true;
|
|
1085
|
+
}
|
|
1086
|
+
validator.issues = firstErrorValidator(data, idx);
|
|
1087
|
+
return false;
|
|
1088
|
+
};
|
|
1089
|
+
validator.issues = emptyIssues;
|
|
1090
|
+
return validator;
|
|
1091
|
+
}
|
|
730
1092
|
const fastContext = new Context();
|
|
1093
|
+
fastContext.pure = pure;
|
|
731
1094
|
const fastCode = `${codeGen(schema, fastContext, 'data', {
|
|
732
1095
|
fast: true
|
|
733
1096
|
})}\nreturn true;`;
|
|
734
|
-
const fastValidator = new Function('
|
|
1097
|
+
const fastValidator = new Function('reg', 'err', `return (data) => {\n${fastCode}\n};`)(fastContext.registry, ERR);
|
|
735
1098
|
const context = new Context();
|
|
1099
|
+
context.pure = pure;
|
|
1100
|
+
const rootPath3 = `reg[${context.register(Object.freeze([]))}]`;
|
|
736
1101
|
const code = codeGen(schema, context, 'data', {
|
|
737
1102
|
fast: false,
|
|
738
1103
|
firstError: true,
|
|
739
1104
|
issues: 'issues',
|
|
740
1105
|
path: [],
|
|
741
|
-
pathExpr:
|
|
1106
|
+
pathExpr: rootPath3
|
|
742
1107
|
});
|
|
743
|
-
const firstErrorValidator = new Function('
|
|
1108
|
+
const firstErrorValidator = new Function('reg', 'err', `return (data) => {\nlet issues;\n${code}\nreturn issues;\n};`)(context.registry, ERR);
|
|
744
1109
|
const validator = (data)=>{
|
|
745
1110
|
if (fastValidator(data)) {
|
|
746
1111
|
return true;
|