├── .github └── workflows │ └── npm-publish.yml ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── esbuild.js ├── index.d.ts ├── package-lock.json ├── package.json ├── src ├── builtin_validators │ ├── index.js │ ├── is.js │ ├── length.js │ ├── match.js │ ├── max.js │ ├── maxlength.js │ ├── min.js │ ├── minlength.js │ ├── oneof.js │ ├── required.js │ └── type.js └── index.js └── tests ├── 01_simple_tests.js ├── 02_valid_output.js ├── 03_invalid_output.js ├── 04_custom_messages.js ├── 05_chaining_test.js ├── 06_async_test.js ├── 07_custom_validator.js └── 08_not_operator.js /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish on NPM 2 | 3 | on: 4 | push: 5 | paths: 6 | - '.github/workflows/npm-publish.yml' 7 | - 'package.json' 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-18.04 12 | steps: 13 | - uses: actions/checkout@v1 14 | - name: Setup Node 15 | uses: actions/setup-node@v1 16 | with: 17 | node-version: 12 18 | - name: Installing NPM deps 19 | run: npm install 20 | - name: Testing 21 | run: npm run test 22 | 23 | publish-npm: 24 | needs: test 25 | runs-on: ubuntu-18.04 26 | steps: 27 | - uses: actions/checkout@v1 28 | - name: Setup Node 29 | uses: actions/setup-node@v1 30 | with: 31 | node-version: 12 32 | registry-url: https://registry.npmjs.org/ 33 | - name: Installing NPM deps 34 | run: npm install 35 | - name: Build modules 36 | run: npm run build 37 | - name: Publishing on NPM 38 | run: npm publish 39 | env: 40 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | tests 3 | node_modules 4 | .github 5 | rollup.config.js 6 | .gitignore -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Alexey Schebelev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aovi 2 | 3 | AOVI is a lightweight dependency free library to validate values of the simple object's properties in declarative way. May be used in Node or in browser as well. 4 | 5 | ## Usage 6 | 7 | 8 | ### Node 9 | ```js 10 | const {aovi} = require('aovi'); 11 | 12 | const test_object = {name:'John'}; 13 | 14 | const result = aovi(test_object) 15 | .check('name') 16 | .required() 17 | .match(/^[a-z]+$/i) 18 | 19 | console.log(result.valid); // true 20 | ``` 21 | 22 | ### Browser 23 | ```html 24 | 25 | ... 26 | 27 | 28 | 42 | ``` 43 | 44 | 45 | ## Example 46 | 47 | ```js 48 | const {aovi} = require('aovi'); 49 | 50 | const test_object = { 51 | user: 'John', 52 | sex: 'male', 53 | email: 'no', 54 | age: 16 55 | } 56 | 57 | const result = aovi(test_object) 58 | .check('user') // the property name 59 | .required() // check something 60 | .type('string') // check more 61 | .check('sex') // and once again with another property 62 | .required() 63 | .oneof(['male','female']) 64 | .check('email') 65 | .match(/[^@]+@[^\.]+\..+/,'E-Mail is invalid') 66 | .minLength(6) // skipped, because match already failed 67 | .check('age') 68 | .min(18) 69 | .check('name') 70 | .type('string') // skipped, because name is undefined and not required 71 | 72 | console.log(result.valid); // false 73 | console.log(result.text()); // E-Mail is invalid. age must be greater than 18. 74 | console.log(result.json()); // [{"name": "email", "error":"E-Mail is invalid"},{"name": "age", "error":"age must be greater than 18"}] 75 | ``` 76 | 77 | 78 | ### Asynchronus example 79 | 80 | ```js 81 | // http-request with body_parser.json middleware example 82 | async function(req,res){ 83 | 84 | // asynchronously request db and check password 85 | const check_password = async function(userid,password) { 86 | const user = await db.getuser(userid); 87 | return user.password===password; 88 | } 89 | 90 | const data = req.body; 91 | 92 | const result = aovi(data) 93 | .check('password') 94 | .required() 95 | .is(async (password) => { 96 | return await check_password( data.userid, password ) 97 | },'Wrong password') 98 | .check('userid') 99 | .required() 100 | .type('number') 101 | 102 | // don't miss await keyword here 103 | console.log(await result.valid); 104 | console.log(await result.text()); 105 | console.log(await result.json()); 106 | } 107 | 108 | ``` 109 | 110 | ## Custom validators 111 | 112 | You may add your own reusable validators to the `aovi` object. Custom validator is the function which must return object containing properties below: 113 | 114 | * `name` - name for validator method 115 | 116 | * `test` - test function must return `true` or `false`. When `true` value of the property is valid. Function gets the value of the property as its parameter. 117 | 118 | * `message` - default validation error message. Name of the property or its label will be added at the start of this text. 119 | 120 | * `notMessage` - default validation error message if used `.not` operator before. Name of the property or its label will be added at the start of this text. If ommited, `.not` operator will not be allowing to use with this custom validator. 121 | 122 | 123 | ### Custom validator example 124 | 125 | ```js 126 | // a,b - params, which should be be passed to the validator by user 127 | const my_custom_validator = (a,b) => { 128 | return { 129 | // name of the validator method 130 | name: 'between', 131 | 132 | // test function gets value, must return true or false 133 | test: (v)=>(v>=a && v<=b), 134 | 135 | // error message, when test returns false 136 | message: `%Label% must be between ${a} and ${b}` 137 | 138 | // error message, when used .not and test returns true 139 | notMessage: `%Label% must not be between ${a} and ${b}` 140 | } 141 | } 142 | 143 | const test_object={number1:5, number2:42, number3:100}; 144 | 145 | const result = aovi(test_object) 146 | .use(my_custom_validator) 147 | .check('number1') 148 | .between(10,50) 149 | .check('number2') 150 | .not.between(0,100) 151 | .check('number3') 152 | .between(1,10,'The value is out of range') 153 | 154 | console.log(result.text()); // number1 must be between 10 and 50. number2 must not be between 0 and 100. The value is out of range. 155 | ``` 156 | 157 | ## Meassages variables 158 | There is a set ov placeholders, which you may use in validators custom messages or in the messages of custom validators. They will be replaced by their values in result. 159 | 160 | * `%label%` - label (or name, if label not specified) of the tested property. 161 | * `%Label%` - same, but with capitalized first letter. 162 | * `%name%` - name of the tested property. 163 | * `%Name%` - same, but with capitalized first letter. 164 | * `%value%` - provided property value. 165 | 166 | ## API 167 | 168 | ### `aovi(test_object)` 169 | 170 | Detrmine object for validation 171 | 172 | ### `.use(validator_function)` 173 | 174 | Add custom validator to the `aovi` object. See [Custom validators](#custom-validators) for more info. 175 | 176 | ### `.check(property_name,[label])` 177 | 178 | Set which property will be validate by next functions. `label` is the name for the property which will be shown in default error messages 179 | 180 | ### `.not` 181 | 182 | Invert result of the next validator. Can be used before `type`,`match`,`is`,`oneof` and `length` validators. Example `.not.length(5)`. 183 | 184 | ### `.required([custom_message])` 185 | 186 | Check if the propertyis defined in object and its value is not equal empty string. 187 | 188 | ### `.type(type,[custom_message])` 189 | 190 | Check if the property's value is of specified `type`, like 'string','number' etc. 191 | 192 | ### `.match(regular_expression,[custom_message])` 193 | 194 | Check if the property's value is match `regular_expression`. 195 | 196 | ### `.is(function,[custom_message])` 197 | 198 | Pass if the `function` returns `true`. This function accept one parameter which is a value of the current property. If `function` is asynchronous, you must call result functions (`.text()`, `.json()`, `.array()` and `.valid`) asynchronously too. 199 | 200 | ### `.oneof(array_of_variants,[custom_message])` 201 | 202 | Check if the property's value is in the `array_of_variants` array. 203 | 204 | ### `.length(needed_length,[custom_message])` 205 | 206 | Check if the property's value length is exact `needed_length`. 207 | 208 | ### `.minLength(min_length,[custom_message])` 209 | 210 | Check if the property's value length is greater than or equal `min_length`. 211 | 212 | ### `.maxLength(max_length,[custom_message])` 213 | 214 | Check if the property's value length is less than or equal `max_length`. 215 | 216 | ### `.min(minimum,[custom_message])` 217 | 218 | Check if the property's valueis greater than or equal `minimum`. 219 | 220 | ### `.max(maximum,[custom_message])` 221 | 222 | Check if the property's value is less than or equal `maximum`. 223 | 224 | ### `.valid` 225 | 226 | Is `true` when no errors where occured during validation chain. Will be `false` in other case. 227 | 228 | ### `.text()` 229 | 230 | Return errors as string. Will return `''` if no errors. 231 | 232 | ### `.json()` 233 | 234 | Return errors as JSON-string. Will return `'[]'` if no errors. 235 | 236 | ### `.array()` 237 | 238 | Return errors as array. Will return `[]` if no errors. -------------------------------------------------------------------------------- /esbuild.js: -------------------------------------------------------------------------------- 1 | const { build } = require('esbuild'); 2 | const pkg = require('./package.json'); 3 | 4 | const DEV = process.argv.includes('--dev'); 5 | 6 | // Node-module 7 | build({ 8 | entryPoints: ['./src/index.js'], 9 | format: "cjs", 10 | outfile: pkg.main, 11 | minify: !DEV, 12 | sourcemap: DEV && 'inline', 13 | bundle: true, 14 | }).catch((e) => { 15 | process.exit(1); 16 | }) 17 | 18 | if(!DEV){ 19 | // ES-module 20 | build({ 21 | entryPoints: ['./src/index.js'], 22 | platform: 'node', 23 | format: "esm", 24 | outfile: pkg.module, 25 | minify: !DEV, 26 | bundle: true, 27 | }).catch((e) => { 28 | process.exit(1); 29 | }) 30 | 31 | // Browser 32 | build({ 33 | entryPoints: ['./src/index.js'], 34 | format: "iife", 35 | outfile: pkg.cdn, 36 | minify: !DEV, 37 | bundle: true, 38 | globalName: 'Aovi', 39 | }).catch((e) => { 40 | process.exit(1); 41 | }) 42 | } 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | export declare type AoviTestObject = Record 2 | export declare type ValidatorCreator = (...params:[any]) => Validator 3 | export declare type TestFunction = (value:any) => Boolean | Promise 4 | 5 | export declare type ValidationError = { 6 | /** Property name or label */ 7 | name:string, 8 | /** Validation error message */ 9 | error: string 10 | } 11 | 12 | export declare interface Validator { 13 | /** Name of the validator method */ 14 | name: string, 15 | /** Function which gets value for testing, 16 | * must return true or false 17 | * */ 18 | test: (value:any)=>boolean, 19 | /** Message, when test returns false */ 20 | message: string, 21 | /** Message, when used .not and test returns true */ 22 | notMessage?: string 23 | } 24 | 25 | export declare type Aovi = { 26 | /** Add custom validator to the aovi object. 27 | * @param custom_validator Your custom validator function. 28 | * @see https://www.npmjs.com/package/aovi#custom-validators 29 | */ 30 | use: (custom_validator: ValidatorCreator) => Aovi 31 | 32 | /** Set which property will be validate by next functions. 33 | * @param prop Property name from testing object 34 | * @param label Name for the property which will be shown in default error messages 35 | * */ 36 | check: (prop:string,label?:string) => Aovi 37 | 38 | /** Invert result of the next validator. 39 | * Can be used before type,match,is,oneof and length validators. 40 | * */ 41 | not: Aovi, 42 | 43 | /** Check if the property is defined in object and its value is not equal empty string. 44 | * @param message Custom message when test fails 45 | */ 46 | required: (message?:string) => Aovi 47 | 48 | /** Check if the property's value is of specified type, like 'string','number' etc. 49 | * @param type Needed type of the value 50 | * @param message Custom message when test fails 51 | */ 52 | type: (type:string,message?:string) => Aovi 53 | 54 | /** Check if the property's value is match regular expression. 55 | * @param regex Regular expression to test value 56 | * @param message Custom message when test fails 57 | */ 58 | match: (regex:RegExp,message?:string) => Aovi 59 | 60 | /** Pass if the function returns true. This function accept one parameter which is a value of the current property. 61 | * If function is asynchronous, you must call result 62 | * functions (.text(), .json(), .array() and .valid) asynchronously too. 63 | * @param func Function to test the value. Must return true or false. 64 | * @param message Custom message when test fails 65 | */ 66 | is: (func:TestFunction,message?:string) => Aovi 67 | 68 | /** Check if the property's value is in the array of variants. 69 | * @param variants Array of possible variants 70 | * @param message Custom message when test fails 71 | */ 72 | oneof: (variants:[any],message?:string) => Aovi 73 | 74 | /** Check if the property's value length is exact needed length. 75 | * @param length Needed length 76 | * @param message Custom message when test fails 77 | */ 78 | length: (length:number,message?:string) => Aovi 79 | 80 | /** Check if the property's value length is greater than or equal provided number. 81 | * @param length Minimum length 82 | * @param message Custom message when test fails 83 | */ 84 | minLength: (length:number,message?:string) => Aovi 85 | 86 | /** Check if the property's value length is less than or equal provided number. 87 | * @param length Maximum length 88 | * @param message Custom message when test fails 89 | */ 90 | maxLength: (length:number,message?:string) => Aovi 91 | 92 | /** Check if the property's valueis greater than or equal minimum value. 93 | * @param value Minimum value 94 | * @param message Custom message when test fails 95 | */ 96 | min: (value:number,message?:string) => Aovi 97 | 98 | /** Check if the property's value is less than or equal maximum value. 99 | * @param value Maximum value 100 | * @param message Custom message when test fails 101 | */ 102 | max: (value:number,message?:string) => Aovi 103 | 104 | /** Is true when no errors where occured during validation chain. 105 | * Will be false in other case. 106 | * */ 107 | valid: Boolean | Promise 108 | 109 | /** Return errors as string. 110 | * Will return '' if no errors. 111 | * */ 112 | text: () => string | Promise 113 | 114 | /** Return errors as a JSON-string. 115 | * Will return '[]' if no errors. 116 | * */ 117 | json: () => string | Promise 118 | 119 | /** Return errors as array. 120 | * Will return [] if no errors. 121 | * */ 122 | array: () => [ValidationError] | Promise<[ValidationError]> 123 | } & { 124 | [key: string]: (...params:[any]) => boolean 125 | } 126 | 127 | /** Run validation of the test object 128 | * @param object Object which properties will be validated 129 | */ 130 | export function aovi(object: AoviTestObject): Aovi -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aovi", 3 | "version": "2.0.5", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "aovi", 9 | "version": "2.0.5", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "esbuild": "^0.13.10", 13 | "uvu": "^0.5.2" 14 | } 15 | }, 16 | "node_modules/dequal": { 17 | "version": "2.0.2", 18 | "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.2.tgz", 19 | "integrity": "sha512-q9K8BlJVxK7hQYqa6XISGmBZbtQQWVXSrRrWreHC94rMt1QL/Impruc+7p2CYSYuVIUr+YCt6hjrs1kkdJRTug==", 20 | "dev": true, 21 | "engines": { 22 | "node": ">=6" 23 | } 24 | }, 25 | "node_modules/esbuild": { 26 | "version": "0.13.10", 27 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.10.tgz", 28 | "integrity": "sha512-0NfCsnAh5XatHIx6Cu93wpR2v6opPoOMxONYhaAoZKzGYqAE+INcDeX2wqMdcndvPQdWCuuCmvlnsh0zmbHcSQ==", 29 | "dev": true, 30 | "hasInstallScript": true, 31 | "bin": { 32 | "esbuild": "bin/esbuild" 33 | }, 34 | "optionalDependencies": { 35 | "esbuild-android-arm64": "0.13.10", 36 | "esbuild-darwin-64": "0.13.10", 37 | "esbuild-darwin-arm64": "0.13.10", 38 | "esbuild-freebsd-64": "0.13.10", 39 | "esbuild-freebsd-arm64": "0.13.10", 40 | "esbuild-linux-32": "0.13.10", 41 | "esbuild-linux-64": "0.13.10", 42 | "esbuild-linux-arm": "0.13.10", 43 | "esbuild-linux-arm64": "0.13.10", 44 | "esbuild-linux-mips64le": "0.13.10", 45 | "esbuild-linux-ppc64le": "0.13.10", 46 | "esbuild-netbsd-64": "0.13.10", 47 | "esbuild-openbsd-64": "0.13.10", 48 | "esbuild-sunos-64": "0.13.10", 49 | "esbuild-windows-32": "0.13.10", 50 | "esbuild-windows-64": "0.13.10", 51 | "esbuild-windows-arm64": "0.13.10" 52 | } 53 | }, 54 | "node_modules/esbuild-android-arm64": { 55 | "version": "0.13.10", 56 | "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.13.10.tgz", 57 | "integrity": "sha512-1sCdVAq64yMp2Uhlu+97/enFxpmrj31QHtThz7K+/QGjbHa7JZdBdBsZCzWJuntKHZ+EU178tHYkvjaI9z5sGg==", 58 | "cpu": [ 59 | "arm64" 60 | ], 61 | "dev": true, 62 | "optional": true, 63 | "os": [ 64 | "android" 65 | ] 66 | }, 67 | "node_modules/esbuild-darwin-64": { 68 | "version": "0.13.10", 69 | "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.13.10.tgz", 70 | "integrity": "sha512-XlL+BYZ2h9cz3opHfFgSHGA+iy/mljBFIRU9q++f9SiBXEZTb4gTW/IENAD1l9oKH0FdO9rUpyAfV+lM4uAxrg==", 71 | "cpu": [ 72 | "x64" 73 | ], 74 | "dev": true, 75 | "optional": true, 76 | "os": [ 77 | "darwin" 78 | ] 79 | }, 80 | "node_modules/esbuild-darwin-arm64": { 81 | "version": "0.13.10", 82 | "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.10.tgz", 83 | "integrity": "sha512-RZMMqMTyActMrXKkW71IQO8B0tyQm0Bm+ZJQWNaHJchL5LlqazJi7rriwSocP+sKLszHhsyTEBBh6qPdw5g5yQ==", 84 | "cpu": [ 85 | "arm64" 86 | ], 87 | "dev": true, 88 | "optional": true, 89 | "os": [ 90 | "darwin" 91 | ] 92 | }, 93 | "node_modules/esbuild-freebsd-64": { 94 | "version": "0.13.10", 95 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.10.tgz", 96 | "integrity": "sha512-pf4BEN9reF3jvZEZdxljVgOv5JS4kuYFCI78xk+2HWustbLvTP0b9XXfWI/OD0ZLWbyLYZYIA+VbVe4tdAklig==", 97 | "cpu": [ 98 | "x64" 99 | ], 100 | "dev": true, 101 | "optional": true, 102 | "os": [ 103 | "freebsd" 104 | ] 105 | }, 106 | "node_modules/esbuild-freebsd-arm64": { 107 | "version": "0.13.10", 108 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.10.tgz", 109 | "integrity": "sha512-j9PUcuNWmlxr4/ry4dK/s6zKh42Jhh/N5qnAAj7tx3gMbkIHW0JBoVSbbgp97p88X9xgKbXx4lG2sJDhDWmsYQ==", 110 | "cpu": [ 111 | "arm64" 112 | ], 113 | "dev": true, 114 | "optional": true, 115 | "os": [ 116 | "freebsd" 117 | ] 118 | }, 119 | "node_modules/esbuild-linux-32": { 120 | "version": "0.13.10", 121 | "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.13.10.tgz", 122 | "integrity": "sha512-imtdHG5ru0xUUXuc2ofdtyw0fWlHYXV7JjF7oZHgmn0b+B4o4Nr6ZON3xxoo1IP8wIekW+7b9exIf/MYq0QV7w==", 123 | "cpu": [ 124 | "ia32" 125 | ], 126 | "dev": true, 127 | "optional": true, 128 | "os": [ 129 | "linux" 130 | ] 131 | }, 132 | "node_modules/esbuild-linux-64": { 133 | "version": "0.13.10", 134 | "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.10.tgz", 135 | "integrity": "sha512-O7fzQIH2e7GC98dvoTH0rad5BVLm9yU3cRWfEmryCEIFTwbNEWCEWOfsePuoGOHRtSwoVY1hPc21CJE4/9rWxQ==", 136 | "cpu": [ 137 | "x64" 138 | ], 139 | "dev": true, 140 | "optional": true, 141 | "os": [ 142 | "linux" 143 | ] 144 | }, 145 | "node_modules/esbuild-linux-arm": { 146 | "version": "0.13.10", 147 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.13.10.tgz", 148 | "integrity": "sha512-R2Jij4A0K8BcmBehvQeUteQEcf24Y2YZ6mizlNFuJOBPxe3vZNmkZ4mCE7Pf1tbcqA65qZx8J3WSHeGJl9EsJA==", 149 | "cpu": [ 150 | "arm" 151 | ], 152 | "dev": true, 153 | "optional": true, 154 | "os": [ 155 | "linux" 156 | ] 157 | }, 158 | "node_modules/esbuild-linux-arm64": { 159 | "version": "0.13.10", 160 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.10.tgz", 161 | "integrity": "sha512-bkGxN67S2n0PF4zhh87/92kBTsH2xXLuH6T5omReKhpXdJZF5SVDSk5XU/nngARzE+e6QK6isK060Dr5uobzNw==", 162 | "cpu": [ 163 | "arm64" 164 | ], 165 | "dev": true, 166 | "optional": true, 167 | "os": [ 168 | "linux" 169 | ] 170 | }, 171 | "node_modules/esbuild-linux-mips64le": { 172 | "version": "0.13.10", 173 | "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.10.tgz", 174 | "integrity": "sha512-UDNO5snJYOLWrA2uOUxM/PVbzzh2TR7Zf2i8zCCuFlYgvAb/81XO+Tasp3YAElDpp4VGqqcpBXLtofa9nrnJGA==", 175 | "cpu": [ 176 | "mips64el" 177 | ], 178 | "dev": true, 179 | "optional": true, 180 | "os": [ 181 | "linux" 182 | ] 183 | }, 184 | "node_modules/esbuild-linux-ppc64le": { 185 | "version": "0.13.10", 186 | "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.10.tgz", 187 | "integrity": "sha512-xu6J9rMWu1TcEGuEmoc8gsTrJCEPsf+QtxK4IiUZNde9r4Q4nlRVah4JVZP3hJapZgZJcxsse0XiKXh1UFdOeA==", 188 | "cpu": [ 189 | "ppc64" 190 | ], 191 | "dev": true, 192 | "optional": true, 193 | "os": [ 194 | "linux" 195 | ] 196 | }, 197 | "node_modules/esbuild-netbsd-64": { 198 | "version": "0.13.10", 199 | "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.10.tgz", 200 | "integrity": "sha512-d+Gr0ScMC2J83Bfx/ZvJHK0UAEMncctwgjRth9d4zppYGLk/xMfFKxv5z1ib8yZpQThafq8aPm8AqmFIJrEesw==", 201 | "cpu": [ 202 | "x64" 203 | ], 204 | "dev": true, 205 | "optional": true, 206 | "os": [ 207 | "netbsd" 208 | ] 209 | }, 210 | "node_modules/esbuild-openbsd-64": { 211 | "version": "0.13.10", 212 | "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.10.tgz", 213 | "integrity": "sha512-OuCYc+bNKumBvxflga+nFzZvxsgmWQW+z4rMGIjM5XIW0nNbGgRc5p/0PSDv0rTdxAmwCpV69fezal0xjrDaaA==", 214 | "cpu": [ 215 | "x64" 216 | ], 217 | "dev": true, 218 | "optional": true, 219 | "os": [ 220 | "openbsd" 221 | ] 222 | }, 223 | "node_modules/esbuild-sunos-64": { 224 | "version": "0.13.10", 225 | "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.13.10.tgz", 226 | "integrity": "sha512-gUkgivZK11bD56wDoLsnYrsOHD/zHzzLSdqKcIl3wRMulfHpRBpoX8gL0dbWr+8N9c+1HDdbNdvxSRmZ4RCVwg==", 227 | "cpu": [ 228 | "x64" 229 | ], 230 | "dev": true, 231 | "optional": true, 232 | "os": [ 233 | "sunos" 234 | ] 235 | }, 236 | "node_modules/esbuild-windows-32": { 237 | "version": "0.13.10", 238 | "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.13.10.tgz", 239 | "integrity": "sha512-C1xJ54E56dGWRaYcTnRy7amVZ9n1/D/D2/qVw7e5EtS7p+Fv/yZxxgqyb1hMGKXgtFYX4jMpU5eWBF/AsYrn+A==", 240 | "cpu": [ 241 | "ia32" 242 | ], 243 | "dev": true, 244 | "optional": true, 245 | "os": [ 246 | "win32" 247 | ] 248 | }, 249 | "node_modules/esbuild-windows-64": { 250 | "version": "0.13.10", 251 | "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.10.tgz", 252 | "integrity": "sha512-6+EXEXopEs3SvPFAHcps2Krp/FvqXXsOQV33cInmyilb0ZBEQew4MIoZtMIyB3YXoV6//dl3i6YbPrFZaWEinQ==", 253 | "cpu": [ 254 | "x64" 255 | ], 256 | "dev": true, 257 | "optional": true, 258 | "os": [ 259 | "win32" 260 | ] 261 | }, 262 | "node_modules/esbuild-windows-arm64": { 263 | "version": "0.13.10", 264 | "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.10.tgz", 265 | "integrity": "sha512-xTqM/XKhORo6u9S5I0dNJWEdWoemFjogLUTVLkQMVyUV3ZuMChahVA+bCqKHdyX55pCFxD/8v2fm3/sfFMWN+g==", 266 | "cpu": [ 267 | "arm64" 268 | ], 269 | "dev": true, 270 | "optional": true, 271 | "os": [ 272 | "win32" 273 | ] 274 | }, 275 | "node_modules/kleur": { 276 | "version": "4.1.4", 277 | "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.4.tgz", 278 | "integrity": "sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==", 279 | "dev": true, 280 | "engines": { 281 | "node": ">=6" 282 | } 283 | }, 284 | "node_modules/mri": { 285 | "version": "1.1.6", 286 | "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.6.tgz", 287 | "integrity": "sha512-oi1b3MfbyGa7FJMP9GmLTttni5JoICpYBRlq+x5V16fZbLsnL9N3wFqqIm/nIG43FjUFkFh9Epzp/kzUGUnJxQ==", 288 | "dev": true, 289 | "engines": { 290 | "node": ">=4" 291 | } 292 | }, 293 | "node_modules/sade": { 294 | "version": "1.7.4", 295 | "resolved": "https://registry.npmjs.org/sade/-/sade-1.7.4.tgz", 296 | "integrity": "sha512-y5yauMD93rX840MwUJr7C1ysLFBgMspsdTo4UVrDg3fXDvtwOyIqykhVAAm6fk/3au77773itJStObgK+LKaiA==", 297 | "dev": true, 298 | "dependencies": { 299 | "mri": "^1.1.0" 300 | }, 301 | "engines": { 302 | "node": ">= 6" 303 | } 304 | }, 305 | "node_modules/totalist": { 306 | "version": "2.0.0", 307 | "resolved": "https://registry.npmjs.org/totalist/-/totalist-2.0.0.tgz", 308 | "integrity": "sha512-+Y17F0YzxfACxTyjfhnJQEe7afPA0GSpYlFkl2VFMxYP7jshQf9gXV7cH47EfToBumFThfKBvfAcoUn6fdNeRQ==", 309 | "dev": true, 310 | "engines": { 311 | "node": ">=6" 312 | } 313 | }, 314 | "node_modules/uvu": { 315 | "version": "0.5.2", 316 | "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.2.tgz", 317 | "integrity": "sha512-m2hLe7I2eROhh+tm3WE5cTo/Cv3WQA7Oc9f7JB6uWv+/zVKvfAm53bMyOoGOSZeQ7Ov2Fu9pLhFr7p07bnT20w==", 318 | "dev": true, 319 | "dependencies": { 320 | "dequal": "^2.0.0", 321 | "diff": "^5.0.0", 322 | "kleur": "^4.0.3", 323 | "sade": "^1.7.3", 324 | "totalist": "^2.0.0" 325 | }, 326 | "bin": { 327 | "uvu": "bin.js" 328 | }, 329 | "engines": { 330 | "node": ">=8" 331 | } 332 | }, 333 | "node_modules/uvu/node_modules/diff": { 334 | "version": "5.0.0", 335 | "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", 336 | "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", 337 | "dev": true, 338 | "engines": { 339 | "node": ">=0.3.1" 340 | } 341 | } 342 | }, 343 | "dependencies": { 344 | "dequal": { 345 | "version": "2.0.2", 346 | "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.2.tgz", 347 | "integrity": "sha512-q9K8BlJVxK7hQYqa6XISGmBZbtQQWVXSrRrWreHC94rMt1QL/Impruc+7p2CYSYuVIUr+YCt6hjrs1kkdJRTug==", 348 | "dev": true 349 | }, 350 | "esbuild": { 351 | "version": "0.13.10", 352 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.10.tgz", 353 | "integrity": "sha512-0NfCsnAh5XatHIx6Cu93wpR2v6opPoOMxONYhaAoZKzGYqAE+INcDeX2wqMdcndvPQdWCuuCmvlnsh0zmbHcSQ==", 354 | "dev": true, 355 | "requires": { 356 | "esbuild-android-arm64": "0.13.10", 357 | "esbuild-darwin-64": "0.13.10", 358 | "esbuild-darwin-arm64": "0.13.10", 359 | "esbuild-freebsd-64": "0.13.10", 360 | "esbuild-freebsd-arm64": "0.13.10", 361 | "esbuild-linux-32": "0.13.10", 362 | "esbuild-linux-64": "0.13.10", 363 | "esbuild-linux-arm": "0.13.10", 364 | "esbuild-linux-arm64": "0.13.10", 365 | "esbuild-linux-mips64le": "0.13.10", 366 | "esbuild-linux-ppc64le": "0.13.10", 367 | "esbuild-netbsd-64": "0.13.10", 368 | "esbuild-openbsd-64": "0.13.10", 369 | "esbuild-sunos-64": "0.13.10", 370 | "esbuild-windows-32": "0.13.10", 371 | "esbuild-windows-64": "0.13.10", 372 | "esbuild-windows-arm64": "0.13.10" 373 | } 374 | }, 375 | "esbuild-android-arm64": { 376 | "version": "0.13.10", 377 | "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.13.10.tgz", 378 | "integrity": "sha512-1sCdVAq64yMp2Uhlu+97/enFxpmrj31QHtThz7K+/QGjbHa7JZdBdBsZCzWJuntKHZ+EU178tHYkvjaI9z5sGg==", 379 | "dev": true, 380 | "optional": true 381 | }, 382 | "esbuild-darwin-64": { 383 | "version": "0.13.10", 384 | "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.13.10.tgz", 385 | "integrity": "sha512-XlL+BYZ2h9cz3opHfFgSHGA+iy/mljBFIRU9q++f9SiBXEZTb4gTW/IENAD1l9oKH0FdO9rUpyAfV+lM4uAxrg==", 386 | "dev": true, 387 | "optional": true 388 | }, 389 | "esbuild-darwin-arm64": { 390 | "version": "0.13.10", 391 | "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.10.tgz", 392 | "integrity": "sha512-RZMMqMTyActMrXKkW71IQO8B0tyQm0Bm+ZJQWNaHJchL5LlqazJi7rriwSocP+sKLszHhsyTEBBh6qPdw5g5yQ==", 393 | "dev": true, 394 | "optional": true 395 | }, 396 | "esbuild-freebsd-64": { 397 | "version": "0.13.10", 398 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.10.tgz", 399 | "integrity": "sha512-pf4BEN9reF3jvZEZdxljVgOv5JS4kuYFCI78xk+2HWustbLvTP0b9XXfWI/OD0ZLWbyLYZYIA+VbVe4tdAklig==", 400 | "dev": true, 401 | "optional": true 402 | }, 403 | "esbuild-freebsd-arm64": { 404 | "version": "0.13.10", 405 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.10.tgz", 406 | "integrity": "sha512-j9PUcuNWmlxr4/ry4dK/s6zKh42Jhh/N5qnAAj7tx3gMbkIHW0JBoVSbbgp97p88X9xgKbXx4lG2sJDhDWmsYQ==", 407 | "dev": true, 408 | "optional": true 409 | }, 410 | "esbuild-linux-32": { 411 | "version": "0.13.10", 412 | "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.13.10.tgz", 413 | "integrity": "sha512-imtdHG5ru0xUUXuc2ofdtyw0fWlHYXV7JjF7oZHgmn0b+B4o4Nr6ZON3xxoo1IP8wIekW+7b9exIf/MYq0QV7w==", 414 | "dev": true, 415 | "optional": true 416 | }, 417 | "esbuild-linux-64": { 418 | "version": "0.13.10", 419 | "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.10.tgz", 420 | "integrity": "sha512-O7fzQIH2e7GC98dvoTH0rad5BVLm9yU3cRWfEmryCEIFTwbNEWCEWOfsePuoGOHRtSwoVY1hPc21CJE4/9rWxQ==", 421 | "dev": true, 422 | "optional": true 423 | }, 424 | "esbuild-linux-arm": { 425 | "version": "0.13.10", 426 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.13.10.tgz", 427 | "integrity": "sha512-R2Jij4A0K8BcmBehvQeUteQEcf24Y2YZ6mizlNFuJOBPxe3vZNmkZ4mCE7Pf1tbcqA65qZx8J3WSHeGJl9EsJA==", 428 | "dev": true, 429 | "optional": true 430 | }, 431 | "esbuild-linux-arm64": { 432 | "version": "0.13.10", 433 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.10.tgz", 434 | "integrity": "sha512-bkGxN67S2n0PF4zhh87/92kBTsH2xXLuH6T5omReKhpXdJZF5SVDSk5XU/nngARzE+e6QK6isK060Dr5uobzNw==", 435 | "dev": true, 436 | "optional": true 437 | }, 438 | "esbuild-linux-mips64le": { 439 | "version": "0.13.10", 440 | "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.10.tgz", 441 | "integrity": "sha512-UDNO5snJYOLWrA2uOUxM/PVbzzh2TR7Zf2i8zCCuFlYgvAb/81XO+Tasp3YAElDpp4VGqqcpBXLtofa9nrnJGA==", 442 | "dev": true, 443 | "optional": true 444 | }, 445 | "esbuild-linux-ppc64le": { 446 | "version": "0.13.10", 447 | "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.10.tgz", 448 | "integrity": "sha512-xu6J9rMWu1TcEGuEmoc8gsTrJCEPsf+QtxK4IiUZNde9r4Q4nlRVah4JVZP3hJapZgZJcxsse0XiKXh1UFdOeA==", 449 | "dev": true, 450 | "optional": true 451 | }, 452 | "esbuild-netbsd-64": { 453 | "version": "0.13.10", 454 | "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.10.tgz", 455 | "integrity": "sha512-d+Gr0ScMC2J83Bfx/ZvJHK0UAEMncctwgjRth9d4zppYGLk/xMfFKxv5z1ib8yZpQThafq8aPm8AqmFIJrEesw==", 456 | "dev": true, 457 | "optional": true 458 | }, 459 | "esbuild-openbsd-64": { 460 | "version": "0.13.10", 461 | "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.10.tgz", 462 | "integrity": "sha512-OuCYc+bNKumBvxflga+nFzZvxsgmWQW+z4rMGIjM5XIW0nNbGgRc5p/0PSDv0rTdxAmwCpV69fezal0xjrDaaA==", 463 | "dev": true, 464 | "optional": true 465 | }, 466 | "esbuild-sunos-64": { 467 | "version": "0.13.10", 468 | "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.13.10.tgz", 469 | "integrity": "sha512-gUkgivZK11bD56wDoLsnYrsOHD/zHzzLSdqKcIl3wRMulfHpRBpoX8gL0dbWr+8N9c+1HDdbNdvxSRmZ4RCVwg==", 470 | "dev": true, 471 | "optional": true 472 | }, 473 | "esbuild-windows-32": { 474 | "version": "0.13.10", 475 | "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.13.10.tgz", 476 | "integrity": "sha512-C1xJ54E56dGWRaYcTnRy7amVZ9n1/D/D2/qVw7e5EtS7p+Fv/yZxxgqyb1hMGKXgtFYX4jMpU5eWBF/AsYrn+A==", 477 | "dev": true, 478 | "optional": true 479 | }, 480 | "esbuild-windows-64": { 481 | "version": "0.13.10", 482 | "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.10.tgz", 483 | "integrity": "sha512-6+EXEXopEs3SvPFAHcps2Krp/FvqXXsOQV33cInmyilb0ZBEQew4MIoZtMIyB3YXoV6//dl3i6YbPrFZaWEinQ==", 484 | "dev": true, 485 | "optional": true 486 | }, 487 | "esbuild-windows-arm64": { 488 | "version": "0.13.10", 489 | "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.10.tgz", 490 | "integrity": "sha512-xTqM/XKhORo6u9S5I0dNJWEdWoemFjogLUTVLkQMVyUV3ZuMChahVA+bCqKHdyX55pCFxD/8v2fm3/sfFMWN+g==", 491 | "dev": true, 492 | "optional": true 493 | }, 494 | "kleur": { 495 | "version": "4.1.4", 496 | "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.4.tgz", 497 | "integrity": "sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==", 498 | "dev": true 499 | }, 500 | "mri": { 501 | "version": "1.1.6", 502 | "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.6.tgz", 503 | "integrity": "sha512-oi1b3MfbyGa7FJMP9GmLTttni5JoICpYBRlq+x5V16fZbLsnL9N3wFqqIm/nIG43FjUFkFh9Epzp/kzUGUnJxQ==", 504 | "dev": true 505 | }, 506 | "sade": { 507 | "version": "1.7.4", 508 | "resolved": "https://registry.npmjs.org/sade/-/sade-1.7.4.tgz", 509 | "integrity": "sha512-y5yauMD93rX840MwUJr7C1ysLFBgMspsdTo4UVrDg3fXDvtwOyIqykhVAAm6fk/3au77773itJStObgK+LKaiA==", 510 | "dev": true, 511 | "requires": { 512 | "mri": "^1.1.0" 513 | } 514 | }, 515 | "totalist": { 516 | "version": "2.0.0", 517 | "resolved": "https://registry.npmjs.org/totalist/-/totalist-2.0.0.tgz", 518 | "integrity": "sha512-+Y17F0YzxfACxTyjfhnJQEe7afPA0GSpYlFkl2VFMxYP7jshQf9gXV7cH47EfToBumFThfKBvfAcoUn6fdNeRQ==", 519 | "dev": true 520 | }, 521 | "uvu": { 522 | "version": "0.5.2", 523 | "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.2.tgz", 524 | "integrity": "sha512-m2hLe7I2eROhh+tm3WE5cTo/Cv3WQA7Oc9f7JB6uWv+/zVKvfAm53bMyOoGOSZeQ7Ov2Fu9pLhFr7p07bnT20w==", 525 | "dev": true, 526 | "requires": { 527 | "dequal": "^2.0.0", 528 | "diff": "^5.0.0", 529 | "kleur": "^4.0.3", 530 | "sade": "^1.7.3", 531 | "totalist": "^2.0.0" 532 | }, 533 | "dependencies": { 534 | "diff": { 535 | "version": "5.0.0", 536 | "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", 537 | "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", 538 | "dev": true 539 | } 540 | } 541 | } 542 | } 543 | } 544 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aovi", 3 | "version": "2.0.5", 4 | "description": "AOVI is a tiny dependency free library to validate values in declarative way.", 5 | "main": "dist/aovi.js", 6 | "module": "dist/aovi.mjs", 7 | "cdn": "dist/aovi.min.js", 8 | "unpkg": "dist/aovi.min.js", 9 | "types": "index.d.ts", 10 | "scripts": { 11 | "build": "node esbuild", 12 | "pretest": "node esbuild --dev", 13 | "test": "export NODE_OPTIONS='--enable-source-maps' && uvu tests" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/AlexxNB/aovi.git" 18 | }, 19 | "keywords": [ 20 | "validator", 21 | "validte", 22 | "validation", 23 | "object validator", 24 | "form" 25 | ], 26 | "author": "Alexey Schebelev", 27 | "license": "MIT", 28 | "bugs": { 29 | "url": "https://github.com/AlexxNB/aovi/issues" 30 | }, 31 | "homepage": "https://github.com/AlexxNB/aovi#readme", 32 | "devDependencies": { 33 | "esbuild": "^0.13.10", 34 | "uvu": "^0.5.2" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/builtin_validators/index.js: -------------------------------------------------------------------------------- 1 | import required from './required'; 2 | import is from './is'; 3 | import type from './type'; 4 | import match from './match'; 5 | import length from './length'; 6 | import minLength from './minlength'; 7 | import maxLength from './maxlength'; 8 | import min from './min'; 9 | import max from './max'; 10 | import oneof from './oneof'; 11 | 12 | const list = { 13 | required, 14 | is, 15 | type, 16 | match, 17 | length, 18 | minLength, 19 | maxLength, 20 | min, 21 | max, 22 | oneof 23 | } 24 | 25 | export default function(methods){ 26 | for(let name in list){ 27 | methods.use(list[name],name); 28 | } 29 | } -------------------------------------------------------------------------------- /src/builtin_validators/is.js: -------------------------------------------------------------------------------- 1 | export default (cond) => { 2 | return { 3 | test: cond, 4 | message: `%Label% is not valid`, 5 | notMessage: `%Label% is truly` 6 | } 7 | } -------------------------------------------------------------------------------- /src/builtin_validators/length.js: -------------------------------------------------------------------------------- 1 | export default (length) => { 2 | return { 3 | test: v => v.length === length, 4 | message: `%Label% must have a length of ${length}`, 5 | notMessage: `%Label% must not have a length of ${length}` 6 | } 7 | } -------------------------------------------------------------------------------- /src/builtin_validators/match.js: -------------------------------------------------------------------------------- 1 | export default (regex) => { 2 | return { 3 | test: v => regex.test(v), 4 | message: `%Label% must match ${regex.toString()}`, 5 | notMessage: `%Label% must not match ${regex.toString()}` 6 | } 7 | } -------------------------------------------------------------------------------- /src/builtin_validators/max.js: -------------------------------------------------------------------------------- 1 | export default (max) => { 2 | return { 3 | test: v => v <= max, 4 | message: `%Label% must be less than ${max}`, 5 | } 6 | } -------------------------------------------------------------------------------- /src/builtin_validators/maxlength.js: -------------------------------------------------------------------------------- 1 | export default (max) => { 2 | return { 3 | test: v => v.length <= max, 4 | message: `%Label% must have a maximum length of ${max}` 5 | } 6 | } -------------------------------------------------------------------------------- /src/builtin_validators/min.js: -------------------------------------------------------------------------------- 1 | export default (min) => { 2 | return { 3 | test: v => v >= min, 4 | message: `%Label% must be greater than ${min}` 5 | } 6 | } -------------------------------------------------------------------------------- /src/builtin_validators/minlength.js: -------------------------------------------------------------------------------- 1 | export default (min) => { 2 | return { 3 | test: v => v.length >= min, 4 | message: `%Label% must have a minimum length of ${min}` 5 | } 6 | } -------------------------------------------------------------------------------- /src/builtin_validators/oneof.js: -------------------------------------------------------------------------------- 1 | export default (list) => { 2 | return { 3 | test: v => list.includes(v), 4 | message: `%Label% must be either ${list.slice(0,-1).join(', ')} or ${list[list.length-1]}`, 5 | notMessage: `%Label% must be neither ${list.slice(0,-1).join(', ')} or ${list[list.length-1]}` 6 | } 7 | } -------------------------------------------------------------------------------- /src/builtin_validators/required.js: -------------------------------------------------------------------------------- 1 | export default () => { 2 | return { 3 | test: (_,exists)=>exists, 4 | message: `%Label% is required`, 5 | required: true 6 | } 7 | } -------------------------------------------------------------------------------- /src/builtin_validators/type.js: -------------------------------------------------------------------------------- 1 | export default (type) => { 2 | return { 3 | test: v => typeof v === type, 4 | message: `%Label% must be of type ${type}`, 5 | notMessage: `%Label% must not be of type ${type}` 6 | } 7 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import injectBuiltinValidators from './builtin_validators'; 2 | 3 | export function aovi(input) { 4 | 5 | const validator = { 6 | errors: null, 7 | queue: [], 8 | current: null, 9 | isnot: false, 10 | async: null, 11 | 12 | add: (name,label) => { 13 | validator.current = { 14 | name, 15 | label: label||name, 16 | value: input[name], 17 | exists: (input[name] !== undefined && input[name] !== ''), 18 | checks:[] 19 | }; 20 | validator.queue.push(validator.current); 21 | return methods; 22 | }, 23 | 24 | test: (condition,message,notMessage,customMessage,required)=>{ 25 | 26 | if(!notMessage && validator.isnot) throw new Error('Using .not with unsupported validator'); 27 | 28 | const conditionFn = typeof condition === 'function' ? condition : ()=>condition; 29 | 30 | validator.current.checks.push({ 31 | fn: validator.isnot ? (v,e) => !conditionFn(v,e) : conditionFn, 32 | message: customMessage||`${validator.isnot ? notMessage : message}`, 33 | required: required||false 34 | }); 35 | 36 | validator.isnot=false; 37 | 38 | return methods 39 | }, 40 | 41 | proc: () => { 42 | if(validator.errors || validator.async) return validator.async; 43 | 44 | validator.errors = []; 45 | validator.async = null; 46 | 47 | let promises = []; 48 | 49 | for(let prop of validator.queue) 50 | for(let check of prop.checks){ 51 | if(!check.required && !prop.exists) continue; 52 | 53 | const addError = (valid)=>!valid && validator.errors.push({ 54 | name: prop.name, 55 | error: injectVars(check.message,prop) 56 | }); 57 | 58 | const checkResult = check.fn(prop.value,prop.exists); 59 | 60 | if(checkResult && checkResult.then) { 61 | promises.push(checkResult); 62 | checkResult.then(result => { 63 | addError(result); 64 | }) 65 | }else addError(checkResult); 66 | } 67 | 68 | if(promises.length) validator.await = Promise.all(promises); 69 | return validator.await; 70 | } 71 | } 72 | 73 | const preserved = ['check','use','not','valid','text','json','array']; 74 | 75 | const methods = { 76 | check: validator.add, 77 | use: (customValidator,name) => { 78 | name = name || customValidator().name; 79 | 80 | if(preserved.includes(name)) throw new Error('Invalid validator name:'+name); 81 | 82 | methods[name] = function (){ 83 | const params=Array.from(arguments); 84 | const customMessage = params.length > customValidator.length ? params.pop() : undefined; 85 | const custom = customValidator.apply(null,params); 86 | return validator.test(custom.test, custom.message, custom.notMessage||false, customMessage, custom.required); 87 | } 88 | return methods; 89 | }, 90 | get not(){ 91 | validator.isnot = true; 92 | return methods 93 | }, 94 | get valid(){ return wait(validator.proc, () => validator.errors.length === 0 )}, 95 | text: () => wait(validator.proc, () => validator.errors.map(e=>e.error+'.').join(' ')), 96 | json: () => wait(validator.proc, () => JSON.stringify(validator.errors) ), 97 | array: () => wait(validator.proc, () => validator.errors ) 98 | } 99 | 100 | injectBuiltinValidators(methods); 101 | 102 | return methods; 103 | } 104 | 105 | function wait(fn,callback){ 106 | const result = fn(); 107 | if(result && result.then){ 108 | return new Promise( resolve => result.then( res => resolve( callback(res) ) ) ); 109 | }else return callback(result); 110 | } 111 | 112 | function injectVars(str,prop){ 113 | return str 114 | .replace(new RegExp(`%label%`,'g'),prop.label) 115 | .replace(new RegExp(`%Label%`,'g'),capitilizeFL(prop.label)) 116 | .replace(new RegExp(`%name%`,'g'),prop.name) 117 | .replace(new RegExp(`%Name%`,'g'),capitilizeFL(prop.name)) 118 | .replace(new RegExp(`%value%`,'g'),String(prop.value)); 119 | } 120 | 121 | function capitilizeFL(str){ 122 | return str.charAt(0).toUpperCase() + str.slice(1) 123 | } -------------------------------------------------------------------------------- /tests/01_simple_tests.js: -------------------------------------------------------------------------------- 1 | const { test } = require('uvu'); 2 | const {equal} = require('uvu/assert'); 3 | const {aovi} = require('../dist/aovi.js'); 4 | 5 | test('Simple tests', () => { 6 | let result; 7 | 8 | result = aovi({name:''}) 9 | .check('name','Your name') 10 | .required() 11 | equal(result.text(),'Your name is required.',"Custom label for message"); 12 | 13 | result = aovi({name:'John',lastname:''}) 14 | .check('unexistent') 15 | .required() 16 | .check('name') 17 | .required() 18 | .check('lastname') 19 | .required() 20 | equal(result.text(),'Unexistent is required. Lastname is required.',"Required test"); 21 | 22 | result = aovi({name:123}) 23 | .check('name') 24 | .type('string') 25 | .check('name') 26 | .type('number') 27 | equal(result.text(),'Name must be of type string.',"Type test"); 28 | 29 | result = aovi({name:'john'}) 30 | .check('name') 31 | .match(/^[a-z]+$/) 32 | .check('name') 33 | .match(/^[0-9]+$/) 34 | equal(result.text(),'Name must match /^[0-9]+$/.',"Match test"); 35 | 36 | result = aovi({name:'john',list:[1,2,3,4,5]}) 37 | .check('name') 38 | .length(4) 39 | .check('name') 40 | .length(10) 41 | .check('list') 42 | .length(5) 43 | .check('list') 44 | .length(10) 45 | equal(result.text(),'Name must have a length of 10. List must have a length of 10.',"Length test"); 46 | 47 | result = aovi({name:'john',list:[1,2,3,4,5]}) 48 | .check('name') 49 | .minLength(3) 50 | .check('name') 51 | .minLength(10) 52 | .check('list') 53 | .minLength(3) 54 | .check('list') 55 | .minLength(10) 56 | equal(result.text(),'Name must have a minimum length of 10. List must have a minimum length of 10.',"minLength test"); 57 | 58 | result = aovi({name:'john',list:[1,2,3,4,5]}) 59 | .check('name') 60 | .maxLength(3) 61 | .check('name') 62 | .maxLength(10) 63 | .check('list') 64 | .maxLength(3) 65 | .check('list') 66 | .maxLength(10) 67 | equal(result.text(),'Name must have a maximum length of 3. List must have a maximum length of 3.',"maxLength test"); 68 | 69 | result = aovi({number:42}) 70 | .check('number') 71 | .min(3) 72 | .check('number') 73 | .min(50) 74 | equal(result.text(),'Number must be greater than 50.',"Min test"); 75 | 76 | result = aovi({number:42}) 77 | .check('number') 78 | .max(3) 79 | .check('number') 80 | .max(50) 81 | equal(result.text(),'Number must be less than 3.',"Max test"); 82 | 83 | const test_obj = {name:'john'}; 84 | result = aovi(test_obj) 85 | .check('name') 86 | .is(test_obj.name === 'john') 87 | .check('name') 88 | .is(()=>test_obj.name === 'bill') 89 | equal(result.text(),'Name is not valid.',"Is test"); 90 | 91 | result = aovi({name:'john'}) 92 | .check('name') 93 | .oneof(['bill','john','alex']) 94 | .check('name') 95 | .oneof(['bill','boris','alex']) 96 | equal(result.text(),'Name must be either bill, boris or alex.',"Oneof test"); 97 | }); 98 | 99 | test.run(); -------------------------------------------------------------------------------- /tests/02_valid_output.js: -------------------------------------------------------------------------------- 1 | const { test } = require('uvu'); 2 | const {equal} = require('uvu/assert'); 3 | const {aovi} = require('../dist/aovi.js'); 4 | 5 | test('Valid output', () => { 6 | let result; 7 | 8 | result = aovi({name:'john'}) 9 | .check('name') 10 | .required() 11 | 12 | equal(result.valid,true,"Valid"); 13 | equal(result.text(),'',"Text"); 14 | equal(result.json(),'[]',"JSON"); 15 | equal(result.array(),[],"Array"); 16 | }); 17 | 18 | test.run(); -------------------------------------------------------------------------------- /tests/03_invalid_output.js: -------------------------------------------------------------------------------- 1 | const { test } = require('uvu'); 2 | const {equal} = require('uvu/assert'); 3 | const {aovi} = require('../dist/aovi.js'); 4 | 5 | test('Invalid output', () => { 6 | let result; 7 | 8 | result = aovi({name:'john',lastname:''}) 9 | .check('name') 10 | .oneof(['bill','boris','alex']) 11 | .check('lastname') 12 | .required() 13 | 14 | equal(result.valid,false,"Valid"); 15 | equal(result.text(),'Name must be either bill, boris or alex. Lastname is required.',"Text"); 16 | equal(result.json(),'[{"name":"name","error":"Name must be either bill, boris or alex"},{"name":"lastname","error":"Lastname is required"}]',"JSON"); 17 | equal(result.array(),[ { name: 'name', error: 'Name must be either bill, boris or alex' }, { name: 'lastname', error: 'Lastname is required' } ],"Array"); 18 | }); 19 | 20 | test.run(); -------------------------------------------------------------------------------- /tests/04_custom_messages.js: -------------------------------------------------------------------------------- 1 | const { test } = require('uvu'); 2 | const {equal} = require('uvu/assert'); 3 | const {aovi} = require('../dist/aovi.js'); 4 | 5 | test('Custom messages', () => { 6 | let result,msg; 7 | 8 | msg = 'You must provide the lastname'; 9 | result = aovi({name:'john'}) 10 | .check('lastname') 11 | .required(msg) 12 | 13 | equal(result.text(),`${msg}.`,"Required custom message"); 14 | 15 | msg = 'Wrong type for name'; 16 | result = aovi({name:'john'}) 17 | .check('name') 18 | .type('number',msg) 19 | 20 | equal(result.text(),`${msg}.`,"Type custom message"); 21 | 22 | msg = 'Wrong name'; 23 | result = aovi({name:'john'}) 24 | .check('name') 25 | .match(/^[0-9]+$/,msg) 26 | 27 | equal(result.text(),`${msg}.`,"Match custom message"); 28 | 29 | msg = 'Wrong name'; 30 | result = aovi({name:'john'}) 31 | .check('name') 32 | .match(/^[0-9]+$/,msg) 33 | 34 | equal(result.text(),`${msg}.`,"Match custom message"); 35 | 36 | msg = 'Wrong length'; 37 | result = aovi({name:'john'}) 38 | .check('name') 39 | .length(5,msg) 40 | .check('name') 41 | .minLength(5,msg) 42 | .check('name') 43 | .maxLength(3,msg) 44 | 45 | equal(result.text(),`${msg}. ${msg}. ${msg}.`,"Length,minLength,maxLength custom messages"); 46 | 47 | msg = 'Wrong value'; 48 | result = aovi({number:42}) 49 | .check('number') 50 | .min(50,msg) 51 | .check('number') 52 | .max(5,msg) 53 | 54 | equal(result.text(),`${msg}. ${msg}.`,"Min,Max custom messages"); 55 | 56 | msg = 'Name is bad'; 57 | result = aovi({name:'john'}) 58 | .check('name') 59 | .is(false,msg) 60 | 61 | equal(result.text(),`${msg}.`,"Is custom message"); 62 | 63 | msg = 'Name should be one of the list'; 64 | result = aovi({name:'john'}) 65 | .check('name') 66 | .oneof(['one','two','three'],msg) 67 | 68 | equal(result.text(),`${msg}.`,"Oneof custom message"); 69 | 70 | }); 71 | 72 | test.run(); -------------------------------------------------------------------------------- /tests/05_chaining_test.js: -------------------------------------------------------------------------------- 1 | const { test } = require('uvu'); 2 | const {equal} = require('uvu/assert'); 3 | const {aovi} = require('../dist/aovi.js'); 4 | 5 | test('Chaining test', () => { 6 | let result; 7 | 8 | result = aovi({name:'john',lastname:''}) 9 | .check('name') 10 | .required() 11 | .length(10) 12 | .check('lastname') 13 | .length(10) 14 | 15 | equal(result.text(),'Name must have a length of 10.',"Skip checks if not required"); 16 | }); 17 | 18 | test.run(); -------------------------------------------------------------------------------- /tests/06_async_test.js: -------------------------------------------------------------------------------- 1 | const { test } = require('uvu'); 2 | const {equal} = require('uvu/assert'); 3 | const {aovi} = require('../dist/aovi.js'); 4 | 5 | test('Asynchronus test', async () => { 6 | let result,test_object={}; 7 | 8 | let dbrequest = (name)=>new Promise((resolve, reject) => setTimeout( _ => {resolve(name==="john");}, 100)); 9 | 10 | test_object={name:'john',name2:'bob'}; 11 | 12 | result = aovi(test_object) 13 | .check('name') 14 | .is( async v => dbrequest(v) ) 15 | .length(3) 16 | .check('name2') 17 | .is( async v => dbrequest(v) ) 18 | 19 | equal(await result.text(),'Name must have a length of 3. Name2 is not valid.',"Asynchronus text"); 20 | equal(await result.json(),'[{"name":"name","error":"Name must have a length of 3"},{"name":"name2","error":"Name2 is not valid"}]',"Asynchronus json"); 21 | }); 22 | 23 | test.run(); -------------------------------------------------------------------------------- /tests/07_custom_validator.js: -------------------------------------------------------------------------------- 1 | const { test } = require('uvu'); 2 | const {equal} = require('uvu/assert'); 3 | const {aovi} = require('../dist/aovi.js'); 4 | 5 | test('Custom validator', () => { 6 | let result,test_object={}; 7 | 8 | const validator = (a,b) => { 9 | return { 10 | name: 'between', 11 | test: (v)=>(v>=a && v<=b), 12 | message: `%Label% must be between ${a} and ${b}`, 13 | notMessage: `%Label% must not be between ${a} and ${b}` 14 | } 15 | } 16 | 17 | test_object={number:42,number2:100}; 18 | 19 | result = aovi(test_object) 20 | .use(validator) 21 | .check('number') 22 | .between(10,100) 23 | .check('number') 24 | .between(1,10) 25 | .check('number') 26 | .not.between(10,50) 27 | .check('number2') 28 | .between(10,50,'Value is out of range') 29 | 30 | equal(result.text(),'Number must be between 1 and 10. Number must not be between 10 and 50. Value is out of range.',"Test custom validator"); 31 | }); 32 | 33 | test.run(); -------------------------------------------------------------------------------- /tests/08_not_operator.js: -------------------------------------------------------------------------------- 1 | const { test } = require('uvu'); 2 | const {equal} = require('uvu/assert'); 3 | const {aovi} = require('../dist/aovi.js'); 4 | 5 | test('Not operator', () => { 6 | let result; 7 | 8 | result = aovi({name:'john'}) 9 | .check('name') 10 | .not.length(4) 11 | 12 | equal(result.text(),'Name must not have a length of 4.',"Test not operator"); 13 | }); 14 | 15 | test.run(); --------------------------------------------------------------------------------