├── .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();
--------------------------------------------------------------------------------