UNPKG

dilswer

Version:

Blazingly fast data validation library with TypeScript integration.

854 lines (852 loc) 28.4 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); // src/validation-algorithms/compile-fast-validator.ts var compile_fast_validator_exports = {}; __export(compile_fast_validator_exports, { compileFastValidator: () => compileFastValidator }); module.exports = __toCommonJS(compile_fast_validator_exports); var KNOWN_GLOBAL_CLASSES = /* @__PURE__ */ new Map([ [Map, "Map"], [Set, "Set"], [RegExp, "RegExp"], [Date, "Date"], [Error, "Error"], [EvalError, "EvalError"], [RangeError, "RangeError"], [ReferenceError, "ReferenceError"], [SyntaxError, "SyntaxError"], [TypeError, "TypeError"], [URIError, "URIError"], [AggregateError, "AggregateError"], [Array, "Array"], [Object, "Object"], [WeakMap, "WeakMap"], [WeakSet, "WeakSet"], [Function, "Function"], [String, "String"], [Number, "Number"], [Boolean, "Boolean"], [Int8Array, "Int8Array"], [Uint8Array, "Uint8Array"], [Uint8ClampedArray, "Uint8ClampedArray"], [Int16Array, "Int16Array"], [Uint16Array, "Uint16Array"], [Int32Array, "Int32Array"], [Uint32Array, "Uint32Array"], [Float32Array, "Float32Array"], [Float64Array, "Float64Array"], [BigInt64Array, "BigInt64Array"], [BigUint64Array, "BigUint64Array"], [ArrayBuffer, "ArrayBuffer"], [DataView, "DataView"], [Promise, "Promise"] ]); if (typeof SharedArrayBuffer !== "undefined") { KNOWN_GLOBAL_CLASSES.set(SharedArrayBuffer, "SharedArrayBuffer"); } var propertyAccessor = (propertyName) => { if (propertyName.match(/^[a-zA-Z_$][a-zA-Z_$0-9]*$/)) { return `.${propertyName}`; } return `[${JSON.stringify(propertyName)}]`; }; var ConditionBuilder = class { constructor(type) { this.type = type; __publicField(this, "conditions", []); } add(condition) { this.conditions.push(condition); return this; } build() { if (this.conditions.length === 0) { return "true"; } if (this.conditions.length === 1) { const c = this.conditions[0]; return typeof c === "string" ? c : c.build(); } const separator = this.type === "||" ? " || " : " && "; return "(" + this.conditions.map((c) => typeof c === "string" ? c : c.build()).join(separator) + ")"; } }; var $dependency = (depName) => `_$getDependency(${JSON.stringify(depName)})`; var $condition = (type) => new ConditionBuilder(type); var $type = (varname, type) => { return `typeof ${varname} === "${type}"`; }; var _serialize = (value) => { return JSON.stringify(value); }; var $defineFn = (fnName, argName, body) => { return { declaration: `const ${fnName} = (${argName}) => ${body};`, $invokeWith(varname) { return `${fnName}(${varname})`; } }; }; var $defineRegexp = (name, regexp) => { return { declaration: `const ${name} = new RegExp(${JSON.stringify( regexp.source )}, ${JSON.stringify(regexp.flags)});`, $test(varname) { return `${name}.test(${varname})`; } }; }; var $lte = (name, value) => { return `${name} <= ${value}`; }; var $gte = (name, value) => { return `${name} >= ${value}`; }; var $equal = (varname, value) => { return `${varname} === ${_serialize(value)}`; }; var $notEqual = (varname, value) => { return `${varname} !== ${_serialize(value)}`; }; var $laxNotEqual = (varname, value) => { return `${varname} != ${_serialize(value)}`; }; var $isArray = (varname) => { return `Array.isArray(${varname})`; }; var $isSet = (varname) => { return $equal(`${varname}[Symbol.toStringTag]`, "Set"); }; var $isInteger = (varname) => { return `Number.isInteger(${varname})`; }; var $notNaN = (varname) => { return `!Number.isNaN(${varname})`; }; var $has = (varname, key) => { return `${_serialize(key)} in ${varname}`; }; var $ternary = (condition) => { return { then(thenValue) { return { else(elseValue) { return `(${typeof condition === "string" ? condition : condition.build()} ? ${thenValue} : ${elseValue})`; } }; } }; }; var $length = (varname, is, than) => { switch (is) { case "==": return `${varname}.length === ${than}`; case ">": return `${varname}.length > ${than}`; case "<": return `${varname}.length < ${than}`; case ">=": return `${varname}.length >= ${than}`; case "<=": return `${varname}.length <= ${than}`; } }; var $every = (generator, varname, predicate, startIndex) => { generator.includes.every = true; const elemName = generator.getUniqueVarName(); const indexName = generator.getUniqueVarName(); if (startIndex != null) { return `_$every(${varname}, (${elemName}, ${indexName}) => ${predicate(elemName, indexName)}, ${startIndex})`; } return `_$every(${varname}, (${elemName}, ${indexName}) => ${predicate(elemName, indexName)})`; }; var $everySome = (generator, varname, everyPredicate, somePredicate, startIdx) => { generator.includes.everySome = true; const elemName1 = generator.getUniqueVarName(); const indexName1 = generator.getUniqueVarName(); const elemName2 = generator.getUniqueVarName(); const indexName2 = generator.getUniqueVarName(); if (startIdx != null) { return `_$everySome(${varname}, (${elemName1}, ${indexName1}) => ${everyPredicate(elemName1, indexName1)}, (${elemName2}, ${indexName2}) => ${somePredicate(elemName2, indexName2)}, ${startIdx})`; } return `_$everySome(${varname}, (${elemName1}, ${indexName1}) => ${everyPredicate(elemName1, indexName1)}, (${elemName2}, ${indexName2}) => ${somePredicate(elemName2, indexName2)})`; }; var $everyObjectValue = (generator, varname, predicate) => { const elemName = generator.getUniqueVarName(); return `_$everyObjectValue(${varname}, (${elemName}) => ${predicate(elemName)})`; }; var $everyInSet = (generator, varname, predicate) => { const elemName = generator.getUniqueVarName(); return `_$everyInSet(${varname}, (${elemName}) => ${predicate(elemName)})`; }; var $charCode = (varname, is) => { if (Array.isArray(is)) { return `${varname}.charCodeAt(0) >= ${is[0]} && ${varname}.charCodeAt(0) <= ${is[1]}`; } return `${varname}.charCodeAt(0) === ${is}`; }; var $charCount = (generator, varname, char, is, expected) => { generator.includes.charCount = true; return `_$countChar(${varname}, ${JSON.stringify(char)}) ${is} ${expected}`; }; var $instanceof = (generator, varname, constructor) => { if (KNOWN_GLOBAL_CLASSES.has(constructor)) { return `(${varname} instanceof ${KNOWN_GLOBAL_CLASSES.get(constructor)})`; } const name = generator.getUniqueVarName(); generator.addDependency(name, constructor); return `(${varname} instanceof ${$dependency(name)})`; }; var ValidateGenerator = class { constructor(get$validate) { this.get$validate = get$validate; } $validate(varname) { const validate = this.get$validate(varname, this); if (typeof validate === "string") { return validate; } return validate.build(); } }; var DataTypeValidatorVisitor = class { constructor() { __publicField(this, "includes", { every: false, everySome: false, charCount: false, stringNumeral: false, stringInteger: false, recursive: false, set: false, dict: false, array: false, custom: false, instanceof: false }); __publicField(this, "circValidationFnNames", /* @__PURE__ */ new Map()); __publicField(this, "_counter1", 0); __publicField(this, "_counter2", 0); __publicField(this, "_counter3", 0); __publicField(this, "knownTypes", /* @__PURE__ */ new Map()); __publicField(this, "outerDeclarations", []); __publicField(this, "innerDeclarations", []); __publicField(this, "dependencies", []); } addDeclaration(type, inlined) { if (type === "inner") { this.innerDeclarations.push(inlined); } else { this.outerDeclarations.push(inlined); } return this; } addDependency(name, value) { this.dependencies.push([name, value]); return this; } getUniqueStringForType(type) { if (this.knownTypes.has(type)) { return this.knownTypes.get(type); } const name = `_$t${++this._counter3}`; this.knownTypes.set(type, name); return name; } getUniqueVarName() { return `_$a${++this._counter1}`; } getUniqueFnName() { return `_$v${++this._counter2}`; } visitPrimitive(type) { switch (type.simpleType) { case "boolean": return new ValidateGenerator( (varname) => $type(varname, "boolean") ); case "integer": return new ValidateGenerator( (varname) => { const cond = $condition("&&").add($type(varname, "number")).add($isInteger(varname)); if (type.options.max != null) { cond.add($lte(varname, type.options.max)); } if (type.options.min != null) { cond.add($gte(varname, type.options.min)); } return cond; } ); case "null": return new ValidateGenerator( (varname) => $equal(varname, null) ); case "number": return new ValidateGenerator( (varname) => { let cond = $condition("&&").add($type(varname, "number")).add($notNaN(varname)); if (type.options.max != null) { cond = cond.add($lte(varname, type.options.max)); } if (type.options.min != null) { cond = cond.add($gte(varname, type.options.min)); } return cond; } ); case "string": return new ValidateGenerator( (varname) => { const cond = $condition("&&").add($type(varname, "string")); if (type.options.max != null) { cond.add($length(varname, "<=", type.options.max)); } if (type.options.min != null) { cond.add($length(varname, ">=", type.options.min)); } return cond; } ); case "stringinteger": this.includes.stringInteger = true; if (type.options.negative === false && type.options.positive === false) { return new ValidateGenerator( (varname) => $condition("&&").add($type(varname, "string")).add($length(varname, ">", 0)).add( $ternary($charCode(varname, 45 /* Minus */)).then($every( this, varname, (char) => $charCode(char, 48 /* Zero */), 1 // start from the second char )).else($every( this, varname, (char) => $charCode(char, 48 /* Zero */) )) ) ); } if (type.options.negative === false) { return new ValidateGenerator( (varname) => $condition("&&").add($type(varname, "string")).add($length(varname, ">", 0)).add( $everySome( this, varname, (char) => $charCode(char, [48 /* Zero */, 57 /* Nine */]), // each char must be a digit (char) => $charCode(char, [49 /* One */, 57 /* Nine */]) // at least one char must be a non-zero digit ) ) ); } if (type.options.positive === false) { return new ValidateGenerator( (varname) => $condition("&&").add($type(varname, "string")).add($length(varname, ">", 0)).add($charCode(varname, 45 /* Minus */)).add( $everySome( this, varname, (char) => $charCode(char, [48 /* Zero */, 57 /* Nine */]), // each char must be a digit (char) => $charCode(char, [49 /* One */, 57 /* Nine */]), // at least one char must be a non-zero digit 1 // start from the second char ) ) ); } return new ValidateGenerator( (varname) => $condition("&&").add($type(varname, "string")).add($length(varname, ">", 0)).add( $ternary($charCode(varname, 45 /* Minus */)).then($every( this, varname, (char) => $charCode(char, [48 /* Zero */, 57 /* Nine */]), 1 // start from the second char )).else($every( this, varname, (char) => $charCode(char, [48 /* Zero */, 57 /* Nine */]) )) ) ); case "stringnumeral": this.includes.stringNumeral = true; if (type.options.negative === false && type.options.positive === false) { return new ValidateGenerator( (varname) => $condition("&&").add($type(varname, "string")).add($length(varname, ">", 0)).add( $ternary($charCode(varname, 45 /* Minus */)).then( $every( this, varname, (char) => $condition("||").add($charCode(char, 48 /* Zero */)).add($charCode(char, 46 /* Dot */)).build(), 1 // start from the second char ) ).else( $every( this, varname, (char) => $condition("||").add($charCode(char, 48 /* Zero */)).add($charCode(char, 46 /* Dot */)).build() ) ) ).add($charCount(this, varname, ".", "<", 2)) ); } if (type.options.negative === false) { return new ValidateGenerator( (varname) => $condition("&&").add($type(varname, "string")).add($length(varname, ">", 0)).add( $everySome( this, varname, (char) => $condition("||").add($charCode(char, [48 /* Zero */, 57 /* Nine */])).add($charCode(char, 46 /* Dot */)).build(), (char) => $charCode(char, [49 /* One */, 57 /* Nine */]) ) ).add($charCount(this, varname, ".", "<", 2)) ); } if (type.options.positive === false) { return new ValidateGenerator( (varname) => $condition("&&").add($type(varname, "string")).add($length(varname, ">", 0)).add($charCode(varname, 45 /* Minus */)).add( $everySome( this, varname, (char) => $condition("||").add($charCode(char, [48 /* Zero */, 57 /* Nine */])).add($charCode(char, 46 /* Dot */)).build(), (char) => $charCode(char, [49 /* One */, 57 /* Nine */]), 1 ) ).add($charCount(this, varname, ".", "<", 2)) ); } return new ValidateGenerator( (varname) => $condition("&&").add($type(varname, "string")).add($length(varname, ">", 0)).add( $ternary($charCode(varname, 45 /* Minus */)).then($every( this, varname, (char) => $condition("||").add($charCode(char, [48 /* Zero */, 57 /* Nine */])).add($charCode(char, 46 /* Dot */)).build(), 1 )).else($every( this, varname, (char) => $condition("||").add($charCode(char, [48 /* Zero */, 57 /* Nine */])).add($charCode(char, 46 /* Dot */)).build() )) ).add($charCount(this, varname, ".", "<", 2)) ); case "function": return new ValidateGenerator( (varname) => $type(varname, "function") ); case "symbol": return new ValidateGenerator( (varname) => $type(varname, "symbol") ); case "undefined": return new ValidateGenerator( (varname) => $type(varname, "undefined") ); case "unknown": return new ValidateGenerator(() => "true"); } } visitArrayOf(type, children) { this.includes.array = true; if (children && children.length > 0) { const oneof = this.visitOneOf(type["union"], children); return new ValidateGenerator( (varname) => $condition("&&").add($isArray(varname)).add($every(this, varname, (elem) => oneof.$validate(elem))) ); } return new ValidateGenerator( (varname) => $isArray(varname) ); } visitTuple(type, children) { return new ValidateGenerator((varname) => { const cond = $condition("&&").add($isArray(varname)).add($length(varname, "==", type.tuple.length)); if (children && children.length > 0) { for (const [index, c] of children.entries()) { cond.add(c.$validate(`${varname}[${index}]`)); } } return cond; }); } visitRecordOf(type, children = []) { const getRecordConditions = (varName) => { const cond = $condition("&&").add($type(varName, "object")).add($laxNotEqual(varName, null)); if (children && children.length > 0) { for (const c of children) { const accessor = propertyAccessor(c.propertyName); const nextName = `${varName}${accessor}`; if (c.required !== true) { cond.add( $ternary( $condition("&&").add($has(varName, c.propertyName)).add($notEqual(nextName, void 0)) ).then(c.child.$validate(nextName)).else("true") ); } else { cond.add($has(varName, c.propertyName)); cond.add(c.child.$validate(nextName)); } } } return cond; }; return new ValidateGenerator((varname) => { if (varname.split(/\[|\./).length > 2) { const fnName = this.getUniqueFnName(); const argName = this.getUniqueVarName(); const validateFn = $defineFn( fnName, argName, getRecordConditions(argName).build() ); this.addDeclaration("inner", validateFn.declaration); return validateFn.$invokeWith(varname); } else { return getRecordConditions(varname); } }); } visitDict(type, children) { this.includes.dict = true; const isDictConditions = (varName) => { return $condition("&&").add($type(varName, "object")).add($laxNotEqual(varName, null)); }; if (children && children.length > 0) { const oneof = this.visitOneOf(type["union"], children); return new ValidateGenerator( (varname) => isDictConditions(varname).add( $everyObjectValue(this, varname, (elem) => oneof.$validate(elem)) ) ); } else { return new ValidateGenerator( (varname) => isDictConditions(varname) ); } } visitSetOf(type, children = []) { this.includes.set = true; const getBaseSetConditions = (varName) => { return $condition("&&").add($type(varName, "object")).add($laxNotEqual(varName, null)).add($isSet(varName)); }; if (children && children.length > 0) { const oneof = this.visitOneOf(type["union"], children); return new ValidateGenerator( (varname) => getBaseSetConditions(varname).add( $everyInSet(this, varname, (elem) => oneof.$validate(elem)) ) ); } return new ValidateGenerator( (varname) => getBaseSetConditions(varname) ); } visitOneOf(type, children) { return new ValidateGenerator((varname) => { const cond = $condition("||"); for (const c of children) { cond.add(c.$validate(varname)); } return cond; }); } visitAllOf(type, children) { return new ValidateGenerator((varname) => { const cond = $condition("&&"); for (const c of children) { cond.add(c.$validate(varname)); } return cond; }); } visitLiteral(type) { return new ValidateGenerator( (varname) => $equal(varname, type.literal) ); } visitEnum(type) { const enumKeys = Object.keys(type.enumInstance).filter( (key) => Number.isNaN(Number(key)) ); return new ValidateGenerator((varname) => { const cond = $condition("||"); for (const key of enumKeys) { const member = type.enumInstance[key]; cond.add($equal(varname, member)); } return cond; }); } visitEnumMember(type) { return new ValidateGenerator( (varname) => $equal(varname, type.enumMember) ); } visitInstanceOf(type) { if (!KNOWN_GLOBAL_CLASSES.has(type.instanceOf)) { this.includes.instanceof = true; } return new ValidateGenerator( (varname) => $condition("&&").add($instanceof(this, varname, type.instanceOf)) ); } visitCustom(type) { this.includes.custom = true; const customDepName = this.getUniqueVarName(); this.addDependency(customDepName, type.custom); return new ValidateGenerator( (varname) => `${$dependency(customDepName)}(${varname})` ); } visitStringMatching(type) { const regexp = $defineRegexp(this.getUniqueVarName(), type.pattern); this.addDeclaration("outer", regexp.declaration); return new ValidateGenerator( (varname) => $condition("&&").add($type(varname, "string")).add(regexp.$test(varname)) ); } visitRecursive(circular, children) { const [childSchema] = children; const child = circular.type; if (this.circValidationFnNames.has(child)) { const validatorFnName = this.circValidationFnNames.get(child); const argName = this.getUniqueVarName(); const typeUniqueName = this.getUniqueStringForType(child); const validateFn = $defineFn( validatorFnName, argName, $ternary( `_$wasRecursivelyValidated(${_serialize(typeUniqueName)}, ${argName})` ).then("true").else(childSchema.$validate(argName)) ); this.includes.recursive = true; this.addDeclaration("inner", validateFn.declaration); return new ValidateGenerator( (varname) => validateFn.$invokeWith(varname) ); } return childSchema; } visitRecursiveRef(type) { const referencedType = type._getReferencedType(); if (this.circValidationFnNames.has(referencedType)) { const validatorFnName2 = this.circValidationFnNames.get(referencedType); return new ValidateGenerator( (varname) => `${validatorFnName2}(${varname})` ); } const validatorFnName = this.getUniqueFnName(); this.circValidationFnNames.set(referencedType, validatorFnName); return new ValidateGenerator( (varname) => `${validatorFnName}(${varname})` ); } visit(type, children) { switch (type.kind) { case "simple": return this.visitPrimitive(type); case "array": return this.visitArrayOf(type, children); case "tuple": return this.visitTuple(type, children); case "record": return this.visitRecordOf(type, children); case "dictionary": return this.visitDict(type, children); case "set": return this.visitSetOf(type, children); case "union": return this.visitOneOf(type, children); case "intersection": return this.visitAllOf(type, children); case "literal": return this.visitLiteral(type); case "enumUnion": return this.visitEnum(type); case "enumMember": return this.visitEnumMember(type); case "instanceOf": return this.visitInstanceOf(type); case "custom": return this.visitCustom(type); case "stringMatching": return this.visitStringMatching(type); case "circular": return this.visitRecursive(type, children); case "circularRef": return this.visitRecursiveRef(type); } } }; var e = eval; var every = ( /* js */ ` function _$every(_$arr, _$predicate, _$start = 0) { for (let _$i = _$start; _$i < _$arr.length; _$i++) { if (!_$predicate(_$arr[_$i], _$i)) return false; } return true; }; `.trim() ); var everySome = ( /* js */ ` function _$everySome(_$arr, _$everyPredicate, _$somePredicate, _$start = 0) { let _$someSatisfied = false; for (let _$i = _$start; _$i < _$arr.length; _$i++) { if (!_$everyPredicate(_$arr[_$i], _$i)) return false; if (!_$someSatisfied) { _$someSatisfied = _$somePredicate(_$arr[_$i], _$i); } } return _$someSatisfied; }; `.trim() ); var everyInSet = ( /* js */ ` function _$everyInSet(_$set, _$predicate) { for (let _$item of _$set) { if (!_$predicate(_$item)) return false; } return true; }; `.trim() ); var everyObjectValue = ( /* js */ ` function _$everyObjectValue(_$obj, _$predicate) { for (let _$key in _$obj) { if (!_$predicate(_$obj[_$key])) return false; } return true; }; `.trim() ); var recursiveTracker = ( /* js */ ` const _$validatedRecursiveValues = new Map(); function _$wasRecursivelyValidated(_$tn, _$d) { let _$set = _$validatedRecursiveValues.get(_$tn); if (!_$set) { _$set = new Set([_$d]); _$validatedRecursiveValues.set(_$tn, _$set); return false; } if (_$set.has(_$d)) { return true; } _$set.add(_$d); return false; }; `.trim() ); var countChar = ( /* js */ ` function _$countChar(_$str, _$char) { let _$count = 0; for (let _$i = 0; _$i < _$str.length; _$i++) { if (_$str[_$i] === _$char) _$count++; } return _$count; }; `.trim() ); var compileFastValidator = (dataType) => { const visitor = new DataTypeValidatorVisitor(); const generator = dataType._acceptVisitor(visitor); const validation = generator.$validate("data"); const outerDeclarations = []; const innerDeclarations = []; if (visitor.includes.every) { outerDeclarations.push(every); } if (visitor.includes.everySome) { outerDeclarations.push(everySome); } if (visitor.includes.set) { outerDeclarations.push(everyInSet); } if (visitor.includes.dict) { outerDeclarations.push(everyObjectValue); } if (visitor.includes.charCount) { outerDeclarations.push(countChar); } if (visitor.outerDeclarations) { outerDeclarations.push(...visitor.outerDeclarations); } if (visitor.includes.recursive) { innerDeclarations.push(recursiveTracker); } if (visitor.innerDeclarations) { innerDeclarations.push(...visitor.innerDeclarations); } const validatorStr = `(_$getDependency) => { ${outerDeclarations.join("\n ")} return function validate(data) { ${innerDeclarations.join("\n ")} return ${validation}; } }`; const deps = new Map(visitor.dependencies); const _$getDependency = (name) => { return deps.get(name); }; const evaluatedCode = e(validatorStr); const validator = evaluatedCode(_$getDependency); const { includes } = visitor; validator.asString = (name = "validate") => { if (includes.custom) { throw new Error( "Validators with Custom type validation cannot be compiled to standalone code" ); } if (includes.instanceof) { throw new Error( "Validators with InstanceOf type validation cannot be compiled to standalone code" ); } return `${outerDeclarations.join("\n")} function ${name}(data) { ${innerDeclarations.join("\n ")} return ${validation} }`; }; return validator; };