├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── bench ├── README.md ├── bench-lib.js ├── bench-nolib.js ├── bench-other-libs.js ├── bench.js └── package.json ├── build-browser.js ├── docs ├── API.md ├── CHANGELOG.md └── vademecum.md ├── lib ├── array.js ├── boolean.js ├── common.js ├── date.js ├── error.js ├── number.js ├── object.js └── string.js ├── package.json ├── test ├── array.js ├── boolean.js ├── date.js ├── error.js ├── number.js ├── object.js ├── string.js ├── test-runner.sh └── test.js ├── tyval.js └── tyval.min.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | .nyc_output 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # node-waf configuration 22 | .lock-wscript 23 | 24 | # Compiled binary addons (http://nodejs.org/api/addons.html) 25 | build/Release 26 | 27 | # Dependency directory 28 | node_modules 29 | 30 | # Optional npm cache directory 31 | .npm 32 | 33 | # Optional REPL history 34 | .node_repl_history 35 | 36 | # mac files 37 | .DS_Store 38 | 39 | # vim swap files 40 | *.swp 41 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test 2 | bench 3 | node_modules 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '6' 4 | - '5' 5 | - '4' 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Tomas Della Vedova 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 | # Tyval 2 | [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](http://standardjs.com/) [![Build Status](https://travis-ci.org/delvedor/Tyval.svg?branch=master)](https://travis-ci.org/delvedor/Tyval) [![NPM version](https://img.shields.io/npm/v/tyval.svg?style=flat)](https://www.npmjs.com/package/tyval) 3 | 4 | > Programs should be written for people to read, and only incidentally for machines to execute. 5 | > *[Abelson and Sussman]* 6 | 7 | **Tyval** is a validator for JavaScript, focused on **performances** and **extensibility**. 8 | 9 | The API is highly inspired from [Joi](https://github.com/hapijs/joi), but the implementation is very different. Tyval uses [code generation](https://github.com/delvedor/Tyval/blob/master/docs/vademecum.md) to achieve maximum speed when evaluating a variable. 10 | Tyval is designed to validate single values in a synchronous way and has not an error management, it always returns a boolean, *true* if all the validations has passed, *false* if at least one has failed, the design of the API forces to write atomic test, in this way the result of a single test does not influence the others. 11 | 12 | 13 | **Needs Node.js ≥ 4.0.0** 14 | 15 | [Benchmark](https://github.com/delvedor/Tyval/blob/master/bench/bench-other-libs.js) comparisons with other libraries: 16 | ```bash 17 | tyval (num) x 78,669,467 ops/sec ±1.75% (82 runs sampled) 18 | joi (num) x 37,540 ops/sec ±0.91% (89 runs sampled) 19 | validate.js (num) x 83,675 ops/sec ±1.60% (89 runs sampled) 20 | is-my-json-valid (num) x 61,898,685 ops/sec ±1.46% (88 runs sampled) 21 | 22 | tyval (str) x 81,093,089 ops/sec ±1.56% (85 runs sampled) 23 | joi (str) x 22,927 ops/sec ±1.40% (91 runs sampled) 24 | validate.js (str) x 96,270 ops/sec ±1.14% (91 runs sampled) 25 | is-my-json-valid (str) x 12,099,361 ops/sec ±1.13% (85 runs sampled) 26 | ``` 27 | 28 | ## Install 29 | ``` 30 | npm install tyval --save 31 | ``` 32 | 33 | ## Usage 34 | Easily require it, compose a function with the chainable API and then use it. 35 | ```javascript 36 | const tyval = require('tyval') 37 | 38 | const stringValidation = tyval.string().max(10).min(1).alphanum() 39 | const numberLimits = tyval.or(tyval.number().max(1), tyval.number().min(10)) 40 | 41 | function getSomeData (str, num, callback) { 42 | if (!stringValidation(str) || !numberLimits(num)) { 43 | return callback(new Error('Parameters not as expected!'), null) 44 | } 45 | // . . . 46 | } 47 | ``` 48 | Were you saying composability? :) 49 | ```javascript 50 | const tyval = require('tyval') 51 | const arr = tyval.array() 52 | 53 | const arrMin = arr.min(5) 54 | const arrMax = arr.max(20) 55 | const arrRange = tyval.or(arrMin, arrMax) 56 | 57 | const arrContain = arr.contains('string') 58 | const arrContainMin = arrContain.min(5) 59 | // Needless to say that the composability 60 | // works only with validations of the same type. 61 | ``` 62 | You can use it for your unit test as well! 63 | ```javascript 64 | const { test } = require('tap') 65 | const tyval = require('tyval') 66 | const generateString = require('../genStr') 67 | 68 | const stringValidation = tyval.string().max(10).min(1).alphanum() 69 | 70 | test('genStr', (t) => { 71 | t.plan(1) 72 | const result = generateString() 73 | // Here we are testing that generateString function returns 74 | // an alphanumeric string with a length between 1 and 10 characters 75 | t.true(stringValidation(result)) 76 | }) 77 | ``` 78 | 79 | 80 | ### Browser version 81 | If you need to use Tyval inside the browser use [`tyval.min.js`](https://github.com/delvedor/Tyval/blob/master/tyval.min.js), that is generated via *browserify* and *uglify*. 82 | ```html 83 | 84 | ``` 85 | 86 | 87 | ## API 88 | - tyval.string() 89 | * tyval.string().alphanum() 90 | * tyval.string().regex() 91 | * tyval.string().max() 92 | * tyval.string().min() 93 | * tyval.string().length() 94 | * tyval.string().mail() 95 | * tyval.string().ipv4() 96 | * tyval.string().ipv6() 97 | * tyval.string().base64() 98 | * tyval.string().JSON() 99 | * tyval.string().uuid() 100 | * tyval.string().MAC() 101 | * tyval.string().md5() 102 | * tyval.string().card() 103 | 104 | 105 | - tyval.number() 106 | * tyval.number().max() 107 | * tyval.number().min() 108 | * tyval.number().positive() 109 | * tyval.number().negative() 110 | * tyval.number().integer() 111 | * tyval.number().float() 112 | * tyval.number().safeInteger() 113 | * tyval.number().finite() 114 | * tyval.number().multiple() 115 | * tyval.number().notNaN() 116 | * tyval.number().port() 117 | 118 | - tyval.array() 119 | * tyval.array().max() 120 | * tyval.array().min() 121 | * tyval.array().length() 122 | * tyval.array().contains() 123 | * tyval.array().items() 124 | 125 | - tyval.date() 126 | * tyval.date().lower() 127 | * tyval.date().higher() 128 | 129 | - tyval.boolean() 130 | 131 | - tyval.object() 132 | * tyval.object().empty() 133 | * tyval.object().notNull() 134 | * tyval.object().notArray() 135 | * tyval.object().notDate() 136 | * tyval.object().notRegExp() 137 | * tyval.object().has() 138 | * tyval.object().hasNot() 139 | 140 | - tyval.error() 141 | * tyval.error().RangeError() 142 | * tyval.error().ReferenceError() 143 | * tyval.error().SyntaxError() 144 | * tyval.error().TypeError() 145 | * tyval.error().message() 146 | 147 | - tyval.or() 148 | 149 | - tyval._______.extend() 150 | 151 | ## TODO 152 | - [x] Rewrite API to improve performances 153 | - [x] Implement tyval.array() 154 | - [x] Implement max/min for array.length 155 | - [x] Refactor of the tyval object, divide functions by field (string, number, array, object...) for a better maintainability 156 | - [x] Add `Date` validator 157 | - [x] Split test in multiple files 158 | - [x] New string validation functions 159 | - [x] Browser version 160 | - [x] Improve lib code readability 161 | - [x] In toFunction, move function parameters inside function blocks to avoid naming conflicts 162 | - [x] Improve generated code readability 163 | - [x] Add `.or`functionality 164 | - [x] Remove `.toFunction()` 165 | - [ ] Add `Any` type 166 | - [ ] Make compatible extend/getArgs with es6 167 | - [ ] Add `.not`functionality eg: `tyval.not.string()` 168 | 169 | ## Contributing 170 | If you feel you can help in any way, be it with examples, extra testing, or new features please open a pull request or open an issue. 171 | 172 | Do you want to know more how this library is built? 173 | Have a look [here](https://github.com/delvedor/Tyval/blob/master/docs/vademecum.md)! 174 | 175 | I would make a special thanks to [@mcollina](https://github.com/mcollina) for helping me to improving the code. 176 | 177 | The code follows the Standard code style. 178 | [![js-standard-style](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard) 179 | 180 | ## License 181 | **[MIT](https://github.com/delvedor/Tyval/blob/master/LICENSE)** 182 | 183 | *The software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and non infringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the software.* 184 | 185 | Copyright © 2016 Tomas Della Vedova 186 | -------------------------------------------------------------------------------- /bench/README.md: -------------------------------------------------------------------------------- 1 | # Tyval 2 | 3 | ## Benchmark 4 | 5 | The benchmarks are divided in two separated bench, `nolib` and `lib`. 6 | As you can imagine the `nolib` is the bench without tyval, `lib` is the bench with tyval. 7 | 8 | These benchmarks are used to improve the overall performances and find perf issues. 9 | PR are welcome. 10 | ___ 11 | These benchmarks where taken via [benchmark.js](https://github.com/bestiejs/benchmark.js/) on Node v6.2.0, on a MacBook Pro Retina Late 2013 (i7, 16GB of RAM). 12 | ```bash 13 | Benchmarking nolib 14 | numTest x 27,342,295 ops/sec ±1.00% (87 runs sampled) 15 | numTest-false x 76,810,306 ops/sec ±1.62% (85 runs sampled) 16 | strTest x 14,052,643 ops/sec ±1.14% (84 runs sampled) 17 | strTest-false x 10,178,614 ops/sec ±1.01% (86 runs sampled) 18 | arrayTest x 32,832,476 ops/sec ±1.18% (85 runs sampled) 19 | arrayTest-false x 33,072,660 ops/sec ±1.06% (90 runs sampled) 20 | objTest x 25,616,040 ops/sec ±1.05% (88 runs sampled) 21 | objTest-false x 32,293,965 ops/sec ±1.14% (87 runs sampled) 22 | objHas x 6,456,075 ops/sec ±1.02% (89 runs sampled) 23 | objHas-false x 6,468,464 ops/sec ±1.01% (88 runs sampled) 24 | 25 | Benchmarking lib 26 | numTest x 27,805,074 ops/sec ±3.56% (86 runs sampled) 27 | numTest-false x 80,597,703 ops/sec ±1.55% (88 runs sampled) 28 | strTest x 14,342,357 ops/sec ±1.16% (87 runs sampled) 29 | strTest-false x 10,069,206 ops/sec ±1.03% (88 runs sampled) 30 | arrayTest x 32,771,117 ops/sec ±1.09% (88 runs sampled) 31 | arrayTest-false x 33,026,228 ops/sec ±1.20% (91 runs sampled) 32 | objTest x 25,807,471 ops/sec ±0.94% (89 runs sampled) 33 | objTest-false x 32,314,996 ops/sec ±1.06% (86 runs sampled) 34 | objHas x 6,273,742 ops/sec ±1.17% (88 runs sampled) 35 | objHas-false x 6,510,533 ops/sec ±0.80% (89 runs sampled) 36 | objHas(fast) x 58,913,622 ops/sec ±1.58% (87 runs sampled) 37 | objHas(fast)-false x 25,290,817 ops/sec ±0.87% (86 runs sampled) 38 | 39 | Done! 40 | ``` 41 | [Benchmark](https://github.com/delvedor/Tyval/blob/master/bench/bench-other-libs.js) comparisons with other libraries: 42 | ```bash 43 | tyval (num) x 78,669,467 ops/sec ±1.75% (82 runs sampled) 44 | joi (num) x 37,540 ops/sec ±0.91% (89 runs sampled) 45 | validate.js (num) x 83,675 ops/sec ±1.60% (89 runs sampled) 46 | is-my-json-valid (num) x 61,898,685 ops/sec ±1.46% (88 runs sampled) 47 | 48 | tyval (str) x 81,093,089 ops/sec ±1.56% (85 runs sampled) 49 | joi (str) x 22,927 ops/sec ±1.40% (91 runs sampled) 50 | validate.js (str) x 96,270 ops/sec ±1.14% (91 runs sampled) 51 | is-my-json-valid (str) x 12,099,361 ops/sec ±1.13% (85 runs sampled) 52 | ``` 53 | -------------------------------------------------------------------------------- /bench/bench-lib.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Benchmark = require('benchmark') 4 | const suite = Benchmark.Suite() 5 | const tyval = require('../tyval') 6 | 7 | const numTest = tyval.number().min(-5).max(10).integer().finite().safeInteger() 8 | const strTest = tyval.string().min(5).max(10).alphanum() 9 | const arrayTest = tyval.array().max(10).min(2) 10 | const objTest = tyval.object().notNull().notArray().notDate().notRegExp() 11 | const objHas = tyval.object().has('test').has('bench').hasNot('nope') 12 | const objHasFast = tyval.object().has('test', { fast: true }).has('bench', { fast: true }).hasNot('nope', { fast: true }) 13 | 14 | let objToTest = { 15 | test: 'test42', 16 | bench: 5 17 | } 18 | 19 | suite 20 | .add('numTest', function () { 21 | numTest(5) 22 | }) 23 | .add('numTest-false', function () { 24 | numTest(-50) 25 | }) 26 | .add('strTest', function () { 27 | strTest('abc123') 28 | }) 29 | .add('strTest-false', function () { 30 | strTest('abcdef') 31 | }) 32 | .add('arrayTest', function () { 33 | arrayTest([1, 2, 3]) 34 | }) 35 | .add('arrayTest-false', function () { 36 | arrayTest([1]) 37 | }) 38 | .add('objTest', function () { 39 | objTest({}) 40 | }) 41 | .add('objTest-false', function () { 42 | objTest([]) 43 | }) 44 | .add('objHas', function () { 45 | objHas({test: 1, bench: 2}) 46 | }) 47 | .add('objHas-false', function () { 48 | objHas({test: 1, bench: 2, nope: 3}) 49 | }) 50 | .add('objHas(fast)', function () { 51 | objHasFast({test: 1, bench: 2}) 52 | }) 53 | .add('objHas(fast)-false', function () { 54 | objHasFast({test: 1, bench: 2, nope: 3}) 55 | }) 56 | .add('combined obj and str', function () { 57 | objHas(objToTest) && strTest(objToTest.test) && numTest(objToTest.bench) 58 | }) 59 | .add('combined obj and str - fast', function () { 60 | objHasFast(objToTest) && strTest(objToTest.test) && numTest(objToTest.bench) 61 | }) 62 | .on('cycle', function (event) { 63 | console.log(String(event.target)) 64 | }) 65 | .on('complete', function () {}) 66 | .run() 67 | -------------------------------------------------------------------------------- /bench/bench-nolib.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Benchmark = require('benchmark') 4 | const suite = Benchmark.Suite() 5 | 6 | // Emulates the number test 7 | // const numTest = tyval.number().min(-5).max(10).integer().finite().safeInteger().toFunction() 8 | const numTest = function (number) { 9 | let bool = true 10 | bool = bool && typeof number === 'number' 11 | bool = bool && number >= -5 12 | bool = bool && number <= 10 13 | bool = bool && Number.isInteger(number) 14 | bool = bool && Number.isFinite(number) 15 | bool = bool && Number.isSafeInteger(number) 16 | return bool 17 | } 18 | 19 | // Emulates the string test 20 | // const strTest = tyval.string().min(5).max(10).alphanum().toFunction() 21 | const strTest = function (string) { 22 | let bool = true 23 | bool = bool && typeof string === 'string' 24 | bool = bool && string.length >= 5 25 | bool = bool && string.length <= 10 26 | let reg = /((^[0-9]+[a-z]+)|(^[a-z]+[0-9]+))+[0-9a-z]+$/i 27 | bool = bool && reg.test(string) 28 | return bool 29 | } 30 | 31 | // Emulates the array test 32 | // const arrayTest = tyval.array().max(10).min(2).toFunction() 33 | const arrayTest = function (array) { 34 | let bool = true 35 | bool = bool && Array.isArray(array) 36 | bool = bool && array.length <= 10 37 | bool = bool && array.length >= 2 38 | return bool 39 | } 40 | 41 | // Emulates the object test 42 | // const objTest = tyval.object().notNull().notArray().notDate().notRegExp().toFunction() 43 | const objTest = function (obj) { 44 | let bool = true 45 | bool = bool && typeof obj === 'object' 46 | bool = bool && obj !== null 47 | bool = bool && !Array.isArray(obj) 48 | bool = bool && !(obj instanceof Date) 49 | bool = bool && !(obj instanceof RegExp) 50 | return bool 51 | } 52 | 53 | const objHas = function (obj) { 54 | let bool = true 55 | bool = bool && typeof obj === 'object' 56 | bool = bool && obj.hasOwnProperty('test') 57 | bool = bool && obj.hasOwnProperty('bench') 58 | bool = bool && !obj.hasOwnProperty('nope') 59 | return bool 60 | } 61 | 62 | let objToTest = { 63 | test: 'test42', 64 | bench: 5 65 | } 66 | 67 | suite 68 | .add('numTest', function () { 69 | numTest(5) 70 | }) 71 | .add('numTest-false', function () { 72 | numTest(-50) 73 | }) 74 | .add('strTest', function () { 75 | strTest('abc123') 76 | }) 77 | .add('strTest-false', function () { 78 | strTest('abcdef') 79 | }) 80 | .add('arrayTest', function () { 81 | arrayTest([1, 2, 3]) 82 | }) 83 | .add('arrayTest-false', function () { 84 | arrayTest([1]) 85 | }) 86 | .add('objTest', function () { 87 | objTest({}) 88 | }) 89 | .add('objTest-false', function () { 90 | objTest([]) 91 | }) 92 | .add('objHas', function () { 93 | objHas({test: 1, bench: 2}) 94 | }) 95 | .add('objHas-false', function () { 96 | objHas({test: 1, bench: 2, nope: 3}) 97 | }) 98 | .add('combined obj and str', function () { 99 | objHas(objToTest) && strTest(objToTest.test) && numTest(objToTest.bench) 100 | }) 101 | .on('cycle', function (event) { 102 | console.log(String(event.target)) 103 | }) 104 | .on('complete', function () {}) 105 | .run() 106 | -------------------------------------------------------------------------------- /bench/bench-other-libs.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Benchmark = require('benchmark') 4 | const suite = Benchmark.Suite() 5 | 6 | // Libraries 7 | const tyval = require('../tyval') 8 | const Joi = require('joi') 9 | const validatejs = require('validate.js') 10 | const jsonValid = require('is-my-json-valid') 11 | 12 | // Number validations 13 | const tyvalNum = tyval.number().min(-5).max(10).integer() 14 | const joiNum = Joi.number().min(-5).max(10).integer() 15 | const validatejsNum = { numericality: { greaterThanOrEqualTo: -5, lessThanOrEqualTo: 10, onlyInteger: true } } 16 | const jsonValidNum = jsonValid({ 17 | type: 'integer', 18 | minimum: -5, 19 | maximum: 10 20 | }) 21 | let num = Math.floor(Math.random() * 15) - 5 22 | 23 | // String validations 24 | const tyvalStr = tyval.string().min(1).max(20) 25 | const joiStr = Joi.string().min(1).max(20) 26 | const validatejsStr = { length: { minimum: 1, maximum: 20 } } 27 | const jsonValidStr = jsonValid({ 28 | type: 'string', 29 | minimum: 1, 30 | maximum: 20 31 | }) 32 | let str = 'Benchmarks!' 33 | 34 | const tyvalItems = tyval.array().items(tyvalStr) 35 | const joiItems = Joi.array().items(joiStr) 36 | let arr = ['a', 'bb', 'ccc', 'dddd', 'eeeee', 'ffffff', 'ggggggg', 'hhhhhhhh', 'iiiiiiiii', 'llllllllll'] 37 | 38 | suite 39 | .add('tyval (num)', function () { 40 | tyvalNum(num) 41 | }) 42 | .add('joi (num)', function () { 43 | Joi.validate(num, joiNum) 44 | }) 45 | .add('validate.js (num)', function () { 46 | validatejs.single(num, validatejsNum) 47 | }) 48 | .add('is-my-json-valid (num)', function () { 49 | jsonValidNum(num) 50 | }) 51 | .add('tyval (str)', function () { 52 | tyvalStr(str) 53 | }) 54 | .add('joi (str)', function () { 55 | Joi.validate(str, joiStr) 56 | }) 57 | .add('validate.js (str)', function () { 58 | validatejs.single(str, validatejsStr) 59 | }) 60 | .add('is-my-json-valid (str)', function () { 61 | jsonValidStr(str) 62 | }) 63 | .add('tyval (array)', function () { 64 | tyvalItems(arr) 65 | }) 66 | .add('joi (array)', function () { 67 | Joi.validate(arr, joiItems) 68 | }) 69 | .on('cycle', function (event) { 70 | console.log(String(event.target)) 71 | }) 72 | .on('complete', function () {}) 73 | .run() 74 | -------------------------------------------------------------------------------- /bench/bench.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const chalk = require('chalk') 4 | 5 | console.log(chalk.yellow('Benchmarking nolib')) 6 | require('./bench-nolib') 7 | 8 | console.log(chalk.yellow('\nBenchmarking lib')) 9 | require('./bench-lib') 10 | 11 | console.log(chalk.yellow('\nBenchmarking other-libs')) 12 | require('./bench-other-libs') 13 | 14 | console.log(chalk.yellow('\nDone!')) 15 | -------------------------------------------------------------------------------- /bench/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bench", 3 | "version": "1.0.0", 4 | "description": "## Benchmark", 5 | "main": "bench.js", 6 | "private": true, 7 | "license": "MIT", 8 | "devDependencies": { 9 | "is-my-json-valid": "^2.13.1", 10 | "joi": "^8.4.2", 11 | "validate.js": "^0.10.0" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /build-browser.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fs = require('fs') 4 | const pump = require('pump') 5 | const browserify = require('browserify') 6 | const { minify } = require('uglify-js') 7 | const replaceStream = require('replacestream') 8 | const selfStream = require('self-stream') 9 | 10 | // Streams 11 | const brBabel = browserify('./tyval.js').transform('babelify', { presets: ['es2015'] }).bundle() 12 | const ws = fs.createWriteStream('./bundle.js') 13 | const rs = replaceStream('[function(require,module,exports){"use strict";module.exports=', '[function(require,module,exports){"use strict";window.tyval=') 14 | 15 | browserifyCode() 16 | 17 | function browserifyCode () { 18 | console.log('> Start browserify') 19 | console.log('> Start babelify') 20 | pump(brBabel, ws, (err) => { 21 | if (err) return err 22 | console.log('< End babelify') 23 | console.log('< End browserify') 24 | uglifyCode() 25 | }) 26 | } 27 | 28 | function uglifyCode () { 29 | console.log('> Start uglify') 30 | const { code } = minify('./bundle.js', { mangle: false }) 31 | fs.writeFile('./tyval.min.js', code, 'utf8', (err) => { 32 | if (err) return console.log(err) 33 | console.log('< End uglify') 34 | replaceExports() 35 | }) 36 | } 37 | 38 | function replaceExports () { 39 | console.log('> Start replace') 40 | selfStream('./tyval.min.js', rs, (err) => { 41 | if (err) return console.log(err) 42 | console.log('< End replace') 43 | fs.unlink('./bundle.js', (err) => { 44 | if (err) return console.log(err) 45 | console.log('Done!') 46 | }) 47 | }) 48 | } 49 | -------------------------------------------------------------------------------- /docs/API.md: -------------------------------------------------------------------------------- 1 | # Tyval [![NPM version](https://img.shields.io/npm/v/tyval.svg?style=flat)](https://www.npmjs.com/package/tyval) 2 | ## API 3 | - tyval.string() 4 | * tyval.string().alphanum() 5 | * tyval.string().regex() 6 | * tyval.string().max() 7 | * tyval.string().min() 8 | * tyval.string().length() 9 | * tyval.string().mail() 10 | * tyval.string().ipv4() 11 | * tyval.string().ipv6() 12 | * tyval.string().base64() 13 | * tyval.string().JSON() 14 | * tyval.string().uuid() 15 | * tyval.string().MAC() 16 | * tyval.string().md5() 17 | * tyval.card().card() 18 | 19 | 20 | - tyval.number() 21 | * tyval.number().max() 22 | * tyval.number().min() 23 | * tyval.number().positive() 24 | * tyval.number().negative() 25 | * tyval.number().integer() 26 | * tyval.number().float() 27 | * tyval.number().safeInteger() 28 | * tyval.number().finite() 29 | * tyval.number().multiple() 30 | * tyval.number().notNaN() 31 | * tyval.number().port() 32 | 33 | - tyval.array() 34 | * tyval.array().max() 35 | * tyval.array().min() 36 | * tyval.array().length() 37 | * tyval.array().contains() 38 | * tyval.array().items() 39 | 40 | - tyval.date() 41 | * tyval.date().lower() 42 | * tyval.date().higher() 43 | 44 | - tyval.boolean() 45 | 46 | - tyval.object() 47 | * tyval.object().empty() 48 | * tyval.object().notNull() 49 | * tyval.object().notArray() 50 | * tyval.object().notDate() 51 | * tyval.object().notRegExp() 52 | * tyval.object().has() 53 | * tyval.object().hasNot() 54 | 55 | - tyval.error() 56 | * tyval.error().RangeError() 57 | * tyval.error().ReferenceError() 58 | * tyval.error().SyntaxError() 59 | * tyval.error().TypeError() 60 | * tyval.error().message() 61 | 62 | - tyval.or() 63 | 64 | - tyval._______.extend() 65 | 66 | ___ 67 | 68 | 69 | ### tyval.string() 70 | Checks if the `value` is a string. 71 | 72 | 73 | #### .string().alphanum() 74 | Checks if the `value` is alphanumerical. 75 | 76 | 77 | #### .string().regex(regex) 78 | Test the regex passed as input on the `value`. 79 | `regex` is the regex code. 80 | 81 | 82 | #### .string().max(number) 83 | Checks if the `value.length` is lower than the passed max value. 84 | `number` is the number value to check. 85 | 86 | 87 | #### .string().min(number) 88 | Checks if the `value.length` is higher than the passed min value. 89 | `number` is the number value to check. 90 | 91 | 92 | #### .string().length(number) 93 | Checks if the `value.length` is equal than the passed value. 94 | `number` is the number value to check. 95 | 96 | 97 | #### .string().mail() 98 | Checks if the `value` is a valid mail string. 99 | 100 | 101 | #### .string().ipv4() 102 | Checks if the `value` is a valid ipv4 string. 103 | 104 | 105 | #### .string().ipv6() 106 | Checks if the `value` is a valid ipv6 string. 107 | 108 | 109 | #### .string().base64() 110 | Checks if the `value` is a valid base64 string. 111 | 112 | 113 | #### .string().JSON() 114 | Checks if the `value` is a valid JSON. 115 | 116 | 117 | #### .string().uuid() 118 | Checks if the `value` is a valid uuid string. 119 | 120 | 121 | #### .string().MAC() 122 | Checks if the `value` is a valid MAC address. 123 | 124 | 125 | #### .string().md5() 126 | Checks if the `value` is a valid md5 string. 127 | 128 | 129 | #### .string().card(cardType) 130 | Checks if the `value` is a valid card code string. 131 | Accepted cards are: *jcb*, *visa*, *discover*, *dinersclub*, *mastercard*, *americanexpress* 132 | 133 | ___ 134 | 135 | ### tyval.number() 136 | Checks if the `value` is a number. 137 | 138 | 139 | #### .number().max(number) 140 | Checks if the `value` is lower than the passed max value. 141 | `number` is the number value to check. 142 | 143 | 144 | #### .number().min(number) 145 | Checks if the `value` is higher than the passed min value. 146 | `number` is the number value to check. 147 | 148 | 149 | #### .number().positive() 150 | Checks if the `value` is positive. 151 | 152 | 153 | #### .number().negative() 154 | Checks if the `value` is negative. 155 | 156 | 157 | #### .number().integer() 158 | Checks if the `value` is an integer. 159 | good 160 | 161 | #### .number().float() 162 | Checks if the `value` is a float. 163 | 164 | 165 | #### .number().safeInteger() 166 | Checks if the `value` is a safeInteger. 167 | 168 | 169 | #### .number().finite() 170 | Checks if the `value` is finite. 171 | 172 | 173 | #### .number().multiple(number) 174 | Checks if the `value` is a multiple of the passed value. 175 | `number` is the multiple number value to check. 176 | 177 | 178 | #### .number().notNaN() 179 | Checks if the `value` is not a NaN. 180 | 181 | 182 | #### .number().port(options) 183 | Checks if the `value` is a valid network port number. 184 | If `reserved` is equal to true, the test returns false if the port number is lower than 1024, default to *false*. Usage: `.number().port(3000, { reserved: true })`. 185 | 186 | ___ 187 | 188 | ### tyval.array() 189 | Checks if the `value` is an array. 190 | 191 | 192 | #### .array().max(number) 193 | Checks if the `value.length` is lower than the passed max value. 194 | `number` is the number value to check. 195 | 196 | 197 | #### .array().min(number) 198 | Checks if the `value.length` is higher than the passed min value. 199 | `number` is the number value to check. 200 | 201 | 202 | #### .array().length(number) 203 | Checks if the `value.length` is the same as the passed value. 204 | `number` is the length number value to check. 205 | 206 | 207 | #### .array().contains(value) 208 | Checks if the array `value` contains the passed value 209 | 210 | 211 | #### .array().items(function) 212 | Checks if every array item is valid using the function passed as parameter. 213 | Example: 214 | ```javascript 215 | const str = tyval.string().min(1).max(10) 216 | const items = tyval.array().items(str) 217 | ``` 218 | 219 | ___ 220 | 221 | ### tyval.date() 222 | Checks if the `value` is a date. 223 | 224 | 225 | #### .date().lower(date) 226 | Checks if the `value.getTime()` is lower than the passed value. 227 | `date` is the date object to compare 228 | 229 | 230 | #### .date().higher(date) 231 | Checks if the `value.getTime()` is higher than the passed value. 232 | `date` is the date object to compare 233 | 234 | ___ 235 | 236 | ### tyval.boolean() 237 | Checks if the `value` is a boolean. 238 | 239 | ___ 240 | 241 | ### tyval.object() 242 | Checks if the `value` is an object. 243 | 244 | 245 | #### .object().empty() 246 | Checks if the `value` object is empty. 247 | 248 | 249 | #### .object().notNull() 250 | Checks if the `value` object is not null. 251 | This because `typeof null = 'object'` 252 | 253 | 254 | #### .object().notArray() 255 | Checks if the `value` object is not an array. 256 | This because `typeof [] = 'object'` 257 | 258 | 259 | #### .object().notDate() 260 | Checks if the `value` object is not a date. 261 | This because `typeof new Date() = 'object'` 262 | 263 | 264 | #### .object().notRegExp() 265 | Checks if the `value` object is not a RegExp. 266 | This because `typeof new RegExp() = 'object'` 267 | 268 | 269 | #### .object().has(key, options) 270 | Checks if the `value` object has the key passed as string. 271 | If `fast` is *true* the overall performances gets ~10x speed, but the test fails if the key value exist and is equal to *undefined*, default to *false*. Usage: `.object().has('key', { fast: true })` 272 | 273 | 274 | 275 | #### .object().hasNot(key, options) 276 | Checks if the `value` object has not the key passed as string. 277 | If `fast` is *true* the overall performances gets ~4x speed, but the test fails if the key value exist and is equal to *undefined*, default to *false*. Usage: `.object().hasNot('key', { fast: true })` 278 | 279 | ___ 280 | 281 | ### tyval.error() 282 | Checks if the `value` is an instance of `Error` 283 | 284 | 285 | #### .error().RangeError() 286 | Checks if the `value` is an instance of `RangeError` 287 | 288 | 289 | #### .error().ReferenceError() 290 | Checks if the `value` is an instance of `ReferenceError` 291 | 292 | 293 | #### .error().SyntaxError() 294 | Checks if the `value` is an instance of `SyntaxError` 295 | 296 | 297 | #### .error().TypeError() 298 | Checks if the `value` is an instance of `TypeError` 299 | 300 | 301 | #### .error().message(msg) 302 | Checks if the error message is the same as the given parameter. 303 | `msg` must be a string. 304 | 305 | ___ 306 | 307 | ### tyval.or(...) 308 | With this function you can compose a new validator by combining other tyval validators. 309 | The parameters *must be* functions. 310 | Example: 311 | ```javascript 312 | const numMax = tyval.number().max(1) 313 | const numMin = tyval.number().min(10) 314 | // represents n < 1 || n > 10 315 | const numberLimits = tyval.or(numMax, numMin) 316 | 317 | console.log(numberLimits(-3)) // true 318 | console.log(numberLimits(20)) // true 319 | console.log(numberLimits(5)) // false 320 | ``` 321 | 322 | ___ 323 | 324 | ### tyval._______.extend(function) 325 | Adds a new validator to tyval. 326 | **Inside the `_______` field you must put the type of validator you need to extend.** 327 | You can access the value to validate via `value` 328 | Use `if (condition) { return false }` to elaborate your validation. 329 | Usage: 330 | ```javascript 331 | tyval./*type you need to extend*/.extend(function someName () { 332 | // your validation code 333 | if (/* your boolean validator */) { 334 | return false 335 | } 336 | }) 337 | ``` 338 | Example: 339 | ```javascript 340 | tyval.number.extend(function isZero () { 341 | if (value !== 0) { 342 | return false 343 | } 344 | }) 345 | // let's use the extended function 346 | const zero = tyval.number().isZero() 347 | if (zero(0)) { 348 | console.log('is equal to zero :D') 349 | } 350 | ``` 351 | If you need to pass some `parameter` to the function: 352 | ```javascript 353 | tyval./*type you need to extend*/.extend(function someName (param) { 354 | // your validation code 355 | console.log($param$) 356 | if (/* your boolean validator */) { 357 | return false 358 | } 359 | }) 360 | ``` 361 | As you can imagine, `value` is a reserved name of **Tyval**, see [here](https://github.com/delvedor/Tyval/blob/master/docs/vademecum.md) why. 362 | If you are passing some variable in your function, write it with `'$'` inside your code, as you can see in the example, see [here](https://github.com/delvedor/Tyval/blob/master/docs/vademecum.md#whydollar) why. 363 | ```javascript 364 | // usage example with a parameter 365 | tyval.number.extend(function lessThan (num) { 366 | if (value > $num$) { 367 | return false 368 | } 369 | }) 370 | // let's use the extended function with a parameter 371 | const ltf = tyval.number().lessThan(50) 372 | if (ltf(10)) { 373 | console.log('is less than 50!') 374 | } 375 | ``` 376 | *Did you made a cool validator? Open a pull request! ;)* 377 | -------------------------------------------------------------------------------- /docs/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v4.0.0 4 | - Removed `.toFunction()` 5 | - Moved from `errors++` to `return false` 6 | - Refactor of library internal code 7 | - Composability! :) 8 | - Changed *fast* argument in object.has and .object.hasNot 9 | - Changed *reserved* argument in number.port 10 | - Added more type checks 11 | 12 | ## v3.4.0 13 | - Improved generated code readability 14 | - Added `or` function 15 | 16 | ## v3.3.0 17 | - Now *.toFunction()* supports `function.toString()` 18 | - Added `error` type 19 | - Added *error.RangeError*, *error.ReferenceError*, *error.SyntaxError*, *error.TypeError*, *error.message* 20 | - Added *string.card* 21 | - Added *array.items* 22 | 23 | ## v3.2.0 24 | - Now all validators use `if (condition) { errors++ }` statement instead of `state = state && condition` 25 | - Rewrited *.toFunction()* method 26 | - Updated *.extend()* method 27 | - Updated *.integer()*, *.float()* and *.safeInteger()* 28 | - Added *.npmignore* 29 | 30 | ## v3.1.0 31 | - Added *string.base64*, *string.JSON*, *string.uuid*, *string.MAC*, *string.md5* 32 | 33 | ## v3.0.0 34 | - New `validator` array 35 | - Removed `parameters` object 36 | - Rewrite of the API according the new validator array 37 | - Now all the parameters are block scoped 38 | - Renamed `check` to `state` 39 | - Renamed `variable` to `value` 40 | - Moved `extend` function to `common` utility 41 | - Rewrited `extend` function 42 | - Now `extend` function is easy to use 43 | - Rewrited `toFunction` function 44 | - Added blocking errors in `common` functions 45 | - Fixed tests 46 | - Updated dev deps 47 | - Updated browser script 48 | 49 | ## v2.6.2 50 | - Fix typo in build-browser 51 | - Regenerated the broswerify version 52 | 53 | ## v2.6.1 54 | - Updated ipv6 RegExp 55 | 56 | ## v2.6.0 57 | - Added Browser version 58 | 59 | ## v2.5.0 60 | - Added *string.mail*, *string.ipv4*, *string.ipv6* 61 | - Added *number.port* 62 | - Refactored toFunction 63 | - Removed esprima/estraverse/escodegen dependencies 64 | 65 | ## v2.4.0 66 | - Added *object.notNull*, *object.notArray*, *object.notDate*, *object.notRegExp*, *object.has* and *object.hasNot* 67 | - Added *array.contains* 68 | - More testing 69 | - Moved from ava to tap for testing 70 | - More benchmark 71 | 72 | ## v2.3.0 73 | - Added *date.lower* and *date.higher* validators 74 | - Added *object.empty* validator 75 | - Added *string.length* validator 76 | - Added *number.notNaN* validator 77 | - Splitted test in multiple files 78 | 79 | ## v2.2.0 80 | - Splitted code by type inside lib/ folder 81 | - Changed *extend* implementation 82 | - Added *Date* method 83 | - Renamed API 84 | - Dramatically improved performance 85 | - Updated *toFunction* method 86 | 87 | ## v2.1.0 88 | - Added toArray, maxArray and minArray 89 | - Updated test 90 | 91 | ## v2.0.0 92 | - Complete rewrite of the API 93 | - Rewrited test 94 | - Added benchmarks 95 | 96 | ## v1.0.0 97 | - Initial implementation. 98 | -------------------------------------------------------------------------------- /docs/vademecum.md: -------------------------------------------------------------------------------- 1 | # Tyval [![NPM version](https://img.shields.io/npm/v/tyval.svg?style=flat)](https://www.npmjs.com/package/tyval) 2 | 3 | ## How works this library? 4 | **Tyval** has two main goals: 5 | - **Speed** 6 | - **Extensibility** 7 | 8 | To achieve this two goals the lib adopted few decisions and technical implementations. 9 | Below you will find all the main parts of the code and their explanation. 10 | 11 | ### toFunction method 12 | This method is the core of the library, its purpose is to take all the validation functions and merge them into a single function via code generation. To achieve the *composability* all the type-validator-specific methods are added to the generated function. 13 | ```javascript 14 | toFunction: function (validatorObject) { 15 | return (context, validatorFunction, validatorParameters) => { 16 | // Returns an array with every line of the function 17 | // Here we remove the function declaration, 18 | // this works ONLY if there are not destructuring assignment as parameters. 19 | // Because of the lib design the validator function has never parameters, 20 | // so we can be "safe" about the following implementation. 21 | const linesOfCode = func => { 22 | func = func.toString() 23 | return func.substring(func.indexOf('{') + 1, func.lastIndexOf('}')) 24 | .split('\n') 25 | .filter(line => line.trim() !== '') // Removes empty lines 26 | } 27 | 28 | let code = '' 29 | // if the validator has already been defined 30 | if (context !== null) { 31 | let lines = linesOfCode(context) 32 | // Gets the number of spaces between 33 | // the beginning of the string and the first character 34 | let spaces = lines[0].search(/\S/) 35 | // Formats the indentation 36 | lines.forEach((line, index) => { 37 | if (index === lines.length - 1) return 38 | if (line.trim() === '{') spaces = line.search(/\S/) 39 | code += ` 40 | ${line.slice(spaces)}` 41 | }) 42 | // if is a new validator 43 | } else { 44 | // Function code 45 | code = ` 46 | "use strict" 47 | ` 48 | } 49 | 50 | code += ` 51 | {` 52 | const lines = linesOfCode(validatorFunction) 53 | const spaces = lines[0].search(/\S/) 54 | 55 | lines.forEach(line => { 56 | for (let val in validatorParameters) { 57 | let value 58 | if (typeof validatorParameters[val] === 'string') { 59 | value = `'${validatorParameters[val]}'` 60 | 61 | // If the variable is an object (but not an instance of RegExp) we stringify it 62 | } else if (typeof validatorParameters[val] === 'object' && !(validatorParameters[val] instanceof RegExp)) { 63 | value = JSON.stringify(validatorParameters[val]) 64 | 65 | // If the variable is a function 66 | } else if (typeof validatorParameters[val] === 'function') { 67 | value = validatorParameters[val].toString() 68 | 69 | // In all the other cases we add it 'as is' to the code 70 | } else { 71 | value = validatorParameters[val] 72 | } 73 | // Replace $varname$ with its value 74 | line = line.replace(new RegExp('\\' + val.substring(0, val.length - 1) + '\\$', 'g'), value) 75 | } 76 | code += ` 77 | ${line.slice(spaces)}` 78 | }) 79 | // Ends the block scope 80 | code += ` 81 | };` 82 | 83 | code += ` 84 | return true` 85 | 86 | // Appends all the validator functions to the newly generated function 87 | const func = new Function('value', code) 88 | Object.keys(validatorObject).forEach(val => { 89 | func[val] = validatorObject[val] 90 | }) 91 | 92 | // Fix for .length property 93 | Object.defineProperty(func, 'length', { 94 | writable: true 95 | }) 96 | if (typeof validatorObject.length === 'function') { 97 | func.length = validatorObject.length 98 | } 99 | 100 | return func 101 | } 102 | } 103 | ``` 104 | See the [full code](https://github.com/delvedor/Tyval/blob/master/lib/common.js)! 105 | 106 | **Real world example:** 107 | This following code 108 | ```javascript 109 | const strTest = tyval.string().min(5).max(10).alphanum() 110 | ``` 111 | generates: 112 | ```javascript 113 | function (value) { 114 | "use strict" 115 | { 116 | if (typeof value !== 'string') { 117 | return false 118 | } 119 | };{ 120 | if (value < 5) { 121 | return false 122 | } 123 | };{ 124 | if (value > 10) { 125 | return false 126 | } 127 | };{ 128 | const reg = /((^[0-9]+[a-z]+)|(^[a-z]+[0-9]+))+[0-9a-z]+$/i; 129 | if (!reg.test(value)) { 130 | return false 131 | } 132 | }; 133 | return true 134 | } 135 | ``` 136 | Before version 3.2.0, the validator was checked by a boolean assign, `state = state && condition`. 137 | From version 4.0.0, Tyval uses the following implementation, `if (condition) { return false }`, because has better performances. 138 | 139 | 140 | ### Why $varname$ 141 | Inside the parameters object every key is saved as `$varname$ : value`, the `'$'` are used during the code generation to replace the `$varname$` with his value to improve performances. 142 | Example: 143 | ```javascript 144 | if (value > $max$) { ... } 145 | // assuming $max$ is equal to 5, 146 | // after code generation becomes: 147 | if (value > 5) { ... } 148 | ``` 149 | 150 | ### extend method 151 | Extensibility is one of the core concept of Tyval, with the version 3.0.0 the `extend` function has been rewrited and simplified its use. 152 | ```javascript 153 | extend: function (tyvalValidator, func, toFunction) { 154 | // Thanks to: https://davidwalsh.name/javascript-arguments 155 | // gets the name of the arguments of a function 156 | const getArgs = func => { 157 | // First match everything inside the function argument parens. 158 | let args = func.toString().match(/function\s.*?\(([^)]*)\)/)[1] 159 | // Split the arguments string into an array comma delimited. 160 | return args.split(',').map(arg => { 161 | // Ensure no inline comments are parsed and trim the whitespace. 162 | return arg.replace(/\/\*.*\*\//, '').trim() 163 | }).filter(arg => { 164 | // Ensure no undefined values are added. 165 | return arg 166 | }) 167 | } 168 | 169 | // Gets the parameters name 170 | const parametersName = getArgs(func) 171 | 172 | // Extends the passed tyval validator with a new function 173 | tyvalValidator[func.name] = function () { 174 | // gets the parameters passed as arguments 175 | const parametersValue = Array.prototype.slice.call(arguments) 176 | if (parametersName.length !== parametersValue.length) { 177 | throw new Error('The length of parametersName and parametersValue do not coincide') 178 | } 179 | 180 | // Instantiate the parameters object 181 | const parameters = {} 182 | for (let i = 0; i < parametersName.length; i++) { 183 | // Adds the parameters name: value 184 | parameters['$' + parametersName[i] + '$'] = parametersValue[i] 185 | } 186 | 187 | return toFunction(this, func, parameters) 188 | } 189 | } 190 | ``` 191 | See the [full code](https://github.com/delvedor/Tyval/blob/master/lib/common.js)! 192 | -------------------------------------------------------------------------------- /lib/array.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /* globals value */ 4 | /* eslint-disable no-undef */ 5 | 6 | const buildToFunction = require('./common').toFunction 7 | const extend = require('./common').extend 8 | 9 | // checks if the value is an array 10 | const array = function () { 11 | return toFunction(null, function () { 12 | if (!Array.isArray(value)) { 13 | return false 14 | } 15 | }) 16 | } 17 | 18 | // checks if the array.length is higher than the passed value 19 | array.max = function (max) { 20 | if (typeof max !== 'number') { 21 | throw new TypeError('array.max - max is not a number') 22 | } 23 | return toFunction(this, function () { 24 | if (value.length > $max$) { 25 | return false 26 | } 27 | }, { $max$: max }) 28 | } 29 | 30 | // checks if the array.length is lower than the passed value 31 | array.min = function (min) { 32 | if (typeof min !== 'number') { 33 | throw new TypeError('array.min - min is not a number') 34 | } 35 | return toFunction(this, function () { 36 | if (value.length < $min$) { 37 | return false 38 | } 39 | }, { $min$: min }) 40 | } 41 | 42 | // Makes writable the length object property 43 | Object.defineProperty(array, 'length', { 44 | writable: true 45 | }) 46 | // checks if the array.length is the same if the passed value 47 | array.length = function (len) { 48 | if (typeof len !== 'number') { 49 | throw new TypeError('array.length - len is not a number') 50 | } 51 | return toFunction(this, function () { 52 | if (value.length !== $length$) { 53 | return false 54 | } 55 | }, { $length$: len }) 56 | } 57 | 58 | // checks if the array value contains the passed value 59 | array.contains = function (containValue) { 60 | return toFunction(this, function () { 61 | if (value.indexOf($containValue$) === -1) { 62 | return false 63 | } 64 | }, { $containValue$: containValue }) 65 | } 66 | 67 | // checks if every array item is valid using the function passed as parameter 68 | array.items = function (func) { 69 | if (typeof func !== 'function') { 70 | throw new TypeError('array.items parameter must be a function') 71 | } 72 | return toFunction(this, function () { 73 | const validate = $func$ 74 | for (let i = 0, len = value.length; i < len; i++) { 75 | if (!validate(value[i])) { 76 | return false 77 | } 78 | } 79 | }, { $func$: func }) 80 | } 81 | 82 | // Extends an array function 83 | array.extend = function (func) { 84 | return extend(this, func, toFunction) 85 | } 86 | 87 | const toFunction = buildToFunction(array) 88 | module.exports = array 89 | -------------------------------------------------------------------------------- /lib/boolean.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /* globals value */ 4 | 5 | const buildToFunction = require('./common').toFunction 6 | const extend = require('./common').extend 7 | 8 | const boolean = function () { 9 | return toFunction(null, function () { 10 | if (typeof value !== 'boolean') { 11 | return false 12 | } 13 | }) 14 | } 15 | 16 | // Extends a boolean function 17 | boolean.extend = function (func) { 18 | return extend(this, func, toFunction) 19 | } 20 | 21 | const toFunction = buildToFunction(boolean) 22 | module.exports = boolean 23 | -------------------------------------------------------------------------------- /lib/common.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /* eslint-disable no-new-func */ 4 | 5 | const common = { 6 | toFunction: function (validatorObject) { 7 | if (typeof validatorObject !== 'function') { 8 | throw new TypeError('validatorObject is not a function') 9 | } 10 | return (context, validatorFunction, validatorParameters) => { 11 | // Error checking 12 | if (typeof context !== 'function' && context !== null) { 13 | throw new TypeError('context is not a function or null') 14 | } 15 | if (typeof validatorFunction !== 'function') { 16 | throw new TypeError('validatorFunction must be a function') 17 | } 18 | if (validatorParameters && typeof validatorParameters !== 'object') { 19 | throw new TypeError('validatorParameters must be an object') 20 | } 21 | 22 | // Returns an array with every line of the function 23 | // Here we remove the function declaration, 24 | // this works ONLY if there are not destructuring assignment as parameters. 25 | // Because of the lib design the validator function has never parameters, 26 | // so we can be "safe" about the following implementation. 27 | const linesOfCode = func => { 28 | func = func.toString() 29 | return func.substring(func.indexOf('{') + 1, func.lastIndexOf('}')) 30 | .split('\n') 31 | .filter(line => line.trim() !== '') // Removes empty lines 32 | } 33 | 34 | let code = '' 35 | // if the validator has already been defined 36 | if (context !== null) { 37 | let lines = linesOfCode(context) 38 | // Gets the number of spaces between 39 | // the beginning of the string and the first character 40 | let spaces = lines[0].search(/\S/) 41 | // Formats the indentation 42 | lines.forEach((line, index) => { 43 | if (index === lines.length - 1) return 44 | if (line.trim() === '{') spaces = line.search(/\S/) 45 | code += ` 46 | ${line.slice(spaces)}` 47 | }) 48 | // if is a new validator 49 | } else { 50 | // Function code 51 | code = ` 52 | "use strict" 53 | ` 54 | } 55 | 56 | code += ` 57 | {` 58 | const lines = linesOfCode(validatorFunction) 59 | const spaces = lines[0].search(/\S/) 60 | 61 | lines.forEach(line => { 62 | for (let val in validatorParameters) { 63 | let value 64 | if (typeof validatorParameters[val] === 'string') { 65 | value = `'${validatorParameters[val]}'` 66 | 67 | // If the variable is an object (but not an instance of RegExp) we stringify it 68 | } else if (typeof validatorParameters[val] === 'object' && !(validatorParameters[val] instanceof RegExp)) { 69 | value = JSON.stringify(validatorParameters[val]) 70 | 71 | // If the variable is a function 72 | } else if (typeof validatorParameters[val] === 'function') { 73 | value = validatorParameters[val].toString() 74 | 75 | // In all the other cases we add it 'as is' to the code 76 | } else { 77 | value = validatorParameters[val] 78 | } 79 | // Replace $varname$ with its value 80 | line = line.replace(new RegExp('\\' + val.substring(0, val.length - 1) + '\\$', 'g'), value) 81 | } 82 | code += ` 83 | ${line.slice(spaces)}` 84 | }) 85 | // Ends the block scope 86 | code += ` 87 | };` 88 | 89 | code += ` 90 | return true` 91 | 92 | // Appends all the validator functions to the newly generated function 93 | const func = new Function('value', code) 94 | Object.keys(validatorObject).forEach(val => { 95 | func[val] = validatorObject[val] 96 | }) 97 | 98 | // Fix for .length property 99 | Object.defineProperty(func, 'length', { 100 | writable: true 101 | }) 102 | if (typeof validatorObject.length === 'function') { 103 | func.length = validatorObject.length 104 | } 105 | 106 | return func 107 | } 108 | }, 109 | 110 | or: function () { 111 | const functions = Array.prototype.slice.call(arguments) 112 | let code = ` "use strict" 113 | ` 114 | let returnCode = ` 115 | return` 116 | 117 | functions.forEach((func, index) => { 118 | if (typeof func !== 'function') { 119 | throw new Error('tyval.or, parameters must be functions') 120 | } 121 | // Stringify every validator function in the code 122 | code += ` 123 | const validate${index} = ${func.toString()}` 124 | // Call every declared function 125 | returnCode += ` validate${index}(orValue)` 126 | if (index < functions.length - 1) { 127 | returnCode += ' ||' 128 | } 129 | }) 130 | 131 | // Declares all the validator functions 132 | // and use them with closures 133 | code += ` 134 | function orify (orValue) {${returnCode} 135 | } 136 | return orify` 137 | return new Function(code)() 138 | }, 139 | 140 | extend: function (tyvalValidator, func, toFunction) { 141 | // Error checking 142 | if (typeof tyvalValidator !== 'function') { 143 | throw new TypeError('tyvalValidator is not a function') 144 | } 145 | if (typeof func !== 'function') { 146 | throw new TypeError('func is not a function') 147 | } 148 | if (typeof toFunction !== 'function') { 149 | throw new TypeError('toFunction is not a function') 150 | } 151 | 152 | // Thanks to: https://davidwalsh.name/javascript-arguments 153 | // gets the name of the arguments of a function 154 | const getArgs = func => { 155 | // First match everything inside the function argument parens. 156 | let args = func.toString().match(/function\s.*?\(([^)]*)\)/)[1] 157 | // Split the arguments string into an array comma delimited. 158 | return args.split(',').map(arg => { 159 | // Ensure no inline comments are parsed and trim the whitespace. 160 | return arg.replace(/\/\*.*\*\//, '').trim() 161 | }).filter(arg => { 162 | // Ensure no undefined values are added. 163 | return arg 164 | }) 165 | } 166 | 167 | // Gets the parameters name 168 | const parametersName = getArgs(func) 169 | 170 | // Extends the passed tyval validator with a new function 171 | tyvalValidator[func.name] = function () { 172 | // gets the parameters passed as arguments 173 | const parametersValue = Array.prototype.slice.call(arguments) 174 | if (parametersName.length !== parametersValue.length) { 175 | throw new Error('The length of parametersName and parametersValue do not coincide') 176 | } 177 | 178 | // Instantiate the parameters object 179 | const parameters = {} 180 | for (let i = 0; i < parametersName.length; i++) { 181 | // Adds the parameters name: value 182 | parameters['$' + parametersName[i] + '$'] = parametersValue[i] 183 | } 184 | 185 | return toFunction(this, func, parameters) 186 | } 187 | } 188 | } 189 | 190 | module.exports = common 191 | -------------------------------------------------------------------------------- /lib/date.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /* globals value, $newDate$ */ 4 | 5 | const buildToFunction = require('./common').toFunction 6 | const extend = require('./common').extend 7 | 8 | const date = function () { 9 | return toFunction(null, function () { 10 | if (!(value instanceof Date)) { 11 | return false 12 | } 13 | }) 14 | } 15 | 16 | // checks if the date value is lower than the passed date 17 | date.lower = function (d) { 18 | return toFunction(this, function () { 19 | if (value.getTime() >= $newDate$) { 20 | return false 21 | } 22 | }, { $newDate$: d.getTime() }) 23 | } 24 | 25 | // checks if the date value is higher than the passed date 26 | date.higher = function (d) { 27 | return toFunction(this, function () { 28 | if (value.getTime() <= $newDate$) { 29 | return false 30 | } 31 | }, { $newDate$: d.getTime() }) 32 | } 33 | 34 | // Extends a date function 35 | date.extend = function (func) { 36 | return extend(this, func, toFunction) 37 | } 38 | 39 | const toFunction = buildToFunction(date) 40 | module.exports = date 41 | -------------------------------------------------------------------------------- /lib/error.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /* globals value, $message$ */ 4 | 5 | const buildToFunction = require('./common').toFunction 6 | const extend = require('./common').extend 7 | 8 | // checks if the value is an instance of Error 9 | const error = function () { 10 | return toFunction(null, function () { 11 | if (!(value instanceof Error)) { 12 | return false 13 | } 14 | }) 15 | } 16 | 17 | // checks if the value is an instance of RangeError 18 | error.RangeError = function () { 19 | return toFunction(this, function () { 20 | if (!(value instanceof RangeError)) { 21 | return false 22 | } 23 | }) 24 | } 25 | 26 | // checks if the value is an instance of ReferenceError 27 | error.ReferenceError = function () { 28 | return toFunction(this, function () { 29 | if (!(value instanceof ReferenceError)) { 30 | return false 31 | } 32 | }) 33 | } 34 | 35 | // checks if the value is an instance of SyntaxError 36 | error.SyntaxError = function () { 37 | return toFunction(this, function () { 38 | if (!(value instanceof SyntaxError)) { 39 | return false 40 | } 41 | }) 42 | } 43 | 44 | // checks if the value is an instance of TypeError 45 | error.TypeError = function () { 46 | return toFunction(this, function () { 47 | if (!(value instanceof TypeError)) { 48 | return false 49 | } 50 | }) 51 | } 52 | 53 | // checks if the error message is the same as the given parameter 54 | error.message = function (message) { 55 | if (typeof message !== 'string') { 56 | throw new Error('error message must be a string') 57 | } 58 | return toFunction(this, function () { 59 | if (value.message !== $message$) { 60 | return false 61 | } 62 | }, { $message$: message }) 63 | } 64 | 65 | // Extends an object function 66 | error.extend = function (func) { 67 | return extend(this, func, toFunction) 68 | } 69 | 70 | const toFunction = buildToFunction(error) 71 | module.exports = error 72 | -------------------------------------------------------------------------------- /lib/number.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /* globals value */ 4 | /* eslint-disable no-undef */ 5 | 6 | const buildToFunction = require('./common').toFunction 7 | const extend = require('./common').extend 8 | 9 | // checks if the value is a number 10 | const number = function () { 11 | return toFunction(null, function () { 12 | if (typeof value !== 'number') { 13 | return false 14 | } 15 | }) 16 | } 17 | 18 | // checks if the number is lower than the passed value 19 | number.max = function (max) { 20 | if (typeof max !== 'number') { 21 | throw new TypeError('number.max - max is not a number') 22 | } 23 | return toFunction(this, function () { 24 | if (value > $max$) { 25 | return false 26 | } 27 | }, { $max$: max }) 28 | } 29 | 30 | // checks if the number is higher than the passed value 31 | number.min = function (min) { 32 | if (typeof min !== 'number') { 33 | throw new TypeError('number.min - min is not a number') 34 | } 35 | return toFunction(this, function () { 36 | if (value < $min$) { 37 | return false 38 | } 39 | }, { $min$: min }) 40 | } 41 | 42 | // checks if the value is a positive number 43 | number.positive = function () { 44 | return toFunction(this, function () { 45 | if (value < 0) { 46 | return false 47 | } 48 | }) 49 | } 50 | 51 | // checks if the value is a negative number 52 | number.negative = function () { 53 | return toFunction(this, function () { 54 | if (value > 0) { 55 | return false 56 | } 57 | }) 58 | } 59 | 60 | // checks if the value is an Integer 61 | number.integer = function () { 62 | // Thanks to https://github.com/mafintosh/is-my-json-valid 63 | return toFunction(this, function () { 64 | if (!(Math.floor(value) === value || value > 9007199254740992 || value < -9007199254740992)) { 65 | return false 66 | } 67 | }) 68 | } 69 | 70 | // checks if the value is a Float 71 | number.float = function () { 72 | // Thanks to https://github.com/mafintosh/is-my-json-valid 73 | return toFunction(this, function () { 74 | if (Math.floor(value) === value || value > 9007199254740992 || value < -9007199254740992) { 75 | return false 76 | } 77 | }) 78 | } 79 | 80 | // checks if the value is a safe integer 81 | number.safeInteger = function () { 82 | return toFunction(this, function () { 83 | if (value > 9007199254740991 || value < -9007199254740991) { 84 | return false 85 | } 86 | }) 87 | } 88 | 89 | // checks if the value is a finite number 90 | number.finite = function () { 91 | return toFunction(this, function () { 92 | if (!Number.isFinite(value)) { 93 | return false 94 | } 95 | }) 96 | } 97 | 98 | // checks if the value if a multiple of the passed value 99 | number.multiple = function (mult) { 100 | if (typeof mult !== 'number') { 101 | throw new TypeError('number.multiple - mult is not a number') 102 | } 103 | return toFunction(this, function () { 104 | if (value % $multiple$ !== 0) { 105 | return false 106 | } 107 | }, { $multiple$: mult }) 108 | } 109 | 110 | // checks if the value is not NaN 111 | number.notNaN = function () { 112 | return toFunction(this, function () { 113 | if (Number.isNaN(value)) { 114 | return false 115 | } 116 | }) 117 | } 118 | 119 | // checks if the number is a valid network port number 120 | number.port = function (options) { 121 | options = options || {} 122 | return toFunction(this, function () { 123 | if (value > 65535 || value < ($reserved$ ? 1024 : 0)) { 124 | return false 125 | } 126 | }, { $reserved$: options.reserved || false }) 127 | } 128 | 129 | // Extends a number function 130 | number.extend = function (func) { 131 | return extend(this, func, toFunction) 132 | } 133 | 134 | const toFunction = buildToFunction(number) 135 | module.exports = number 136 | -------------------------------------------------------------------------------- /lib/object.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /* globals value */ 4 | /* eslint-disable no-undef */ 5 | 6 | const buildToFunction = require('./common').toFunction 7 | const extend = require('./common').extend 8 | 9 | const object = function () { 10 | return toFunction(null, function () { 11 | if (typeof value !== 'object') { 12 | return false 13 | } 14 | }) 15 | } 16 | 17 | // checks if the value object is empty 18 | object.empty = function () { 19 | return toFunction(this, function () { 20 | if (Object.keys(value).length > 0) { 21 | return false 22 | } 23 | }) 24 | } 25 | 26 | // checks if the value is not null (because typeof null = 'object') 27 | object.notNull = function () { 28 | return toFunction(this, function () { 29 | if (value === null) { 30 | return false 31 | } 32 | }) 33 | } 34 | 35 | // checks if the value is not an array (because typeof [] = 'object') 36 | object.notArray = function notArray () { 37 | return toFunction(this, function () { 38 | if (Array.isArray(value)) { 39 | return false 40 | } 41 | }) 42 | } 43 | 44 | // checks if the value is not a Date (because of typeof new Date() = 'object') 45 | object.notDate = function notDate () { 46 | return toFunction(this, function () { 47 | if (value instanceof Date) { 48 | return false 49 | } 50 | }) 51 | } 52 | 53 | // checks if the value is not a RegExp (becaus of typeof new RegExp() = 'object') 54 | object.notRegExp = function notRegExp () { 55 | return toFunction(this, function () { 56 | if (value instanceof RegExp) { 57 | return false 58 | } 59 | }) 60 | } 61 | 62 | // checks if the value has the passed key 63 | // if fast is true the perf gets a ~10x speed 64 | object.has = function has (haskey, options) { 65 | if (typeof haskey !== 'string') { 66 | throw new TypeError('object.has - haskey is not a string') 67 | } 68 | options = options || {} 69 | if (options.fast) { 70 | return toFunction(this, function () { 71 | if (value[$haskey$] === undefined) { 72 | return false 73 | } 74 | }, { $haskey$: haskey }) 75 | } else { 76 | return toFunction(this, function () { 77 | if (!(value.hasOwnProperty($haskey$))) { 78 | return false 79 | } 80 | }, { $haskey$: haskey }) 81 | } 82 | } 83 | 84 | // checks if the value has not the passed key 85 | // if fast is true the perf gets a ~4x speed 86 | object.hasNot = function (hasnotkey, options) { 87 | if (typeof hasnotkey !== 'string') { 88 | throw new TypeError('object.hasNot - hasnotkey is not a string') 89 | } 90 | options = options || {} 91 | if (options.fast) { 92 | return toFunction(this, function () { 93 | if (value[$hasnotkey$] !== undefined) { 94 | return false 95 | } 96 | }, { $hasnotkey$: hasnotkey }) 97 | } else { 98 | return toFunction(this, function () { 99 | if (value.hasOwnProperty($hasnotkey$)) { 100 | return false 101 | } 102 | }, { $hasnotkey$: hasnotkey }) 103 | } 104 | } 105 | 106 | // Extends an object function 107 | object.extend = function (func) { 108 | return extend(this, func, toFunction) 109 | } 110 | 111 | const toFunction = buildToFunction(object) 112 | module.exports = object 113 | -------------------------------------------------------------------------------- /lib/string.js: -------------------------------------------------------------------------------- 1 | 'use string' 2 | 3 | /* globals value */ 4 | /* eslint-disable no-native-reassign, no-undef */ 5 | 6 | const buildToFunction = require('./common').toFunction 7 | const extend = require('./common').extend 8 | 9 | // checks if the value is a string 10 | const string = function () { 11 | return toFunction(null, function () { 12 | if (typeof value !== 'string') { 13 | return false 14 | } 15 | }) 16 | } 17 | 18 | // checks if the string is alphanumberic 19 | string.alphanum = function () { 20 | return toFunction(this, function () { 21 | const reg = /((^[0-9]+[a-z]+)|(^[a-z]+[0-9]+))+[0-9a-z]+$/i 22 | if (!(reg.test(value))) { 23 | return false 24 | } 25 | }) 26 | } 27 | 28 | // Tests the regex passed as input 29 | string.regex = function (reg) { 30 | if (!(reg instanceof RegExp)) { 31 | throw new TypeError('string.regex - reg is not a RegExp') 32 | } 33 | return toFunction(this, function () { 34 | const reg = $reg$ 35 | if (!(reg.test(value))) { 36 | return false 37 | } 38 | }, { $reg$: reg }) 39 | } 40 | 41 | // checks if the string.length is lower than the passed max value 42 | string.max = function (max) { 43 | if (typeof max !== 'number') { 44 | throw new TypeError('string.max - max is not a number') 45 | } 46 | return toFunction(this, function () { 47 | if (value.length > $max$) { 48 | return false 49 | } 50 | }, { $max$: max }) 51 | } 52 | 53 | // checks if the string.length is higher than the passed value 54 | string.min = function (min) { 55 | if (typeof min !== 'number') { 56 | throw new TypeError('string.min - min is not a number') 57 | } 58 | return toFunction(this, function () { 59 | if (value.length < $min$) { 60 | return false 61 | } 62 | }, { $min$: min }) 63 | } 64 | 65 | // Makes writable the length object property 66 | Object.defineProperty(string, 'length', { 67 | writable: true 68 | }) 69 | // checks if the string.length is the same than the passed value 70 | string.length = function (len) { 71 | if (typeof len !== 'number') { 72 | throw new TypeError('string.length - len is not a number') 73 | } 74 | return toFunction(this, function () { 75 | if (value.length !== $length$) { 76 | return false 77 | } 78 | }, { $length$: len }) 79 | } 80 | 81 | // checks if the value is a valid mail string 82 | string.mail = function () { 83 | // Thanks to: https://stackoverflow.com/questions/46155/validate-email-address-in-javascript 84 | return toFunction(this, function () { 85 | const mailReg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ 86 | if (!(mailReg.test(value))) { 87 | return false 88 | } 89 | }) 90 | } 91 | 92 | // checks if the value is a valid ipv4 string 93 | string.ipv4 = function () { 94 | return toFunction(this, function () { 95 | const ipReg = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/ 96 | if (!(ipReg.test(value))) { 97 | return false 98 | } 99 | }) 100 | } 101 | 102 | // checks if the value is a valid ipv6 string 103 | string.ipv6 = function () { 104 | // Thanks to: https://community.helpsystems.com/forums/intermapper/miscellaneous-topics/5acc4fcf-fa83-e511-80cf-0050568460e4 105 | return toFunction(this, function () { 106 | const ipReg = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/ 107 | if (!(ipReg.test(value))) { 108 | return false 109 | } 110 | }) 111 | } 112 | 113 | // checks if the value is a valid base64 string 114 | string.base64 = function () { 115 | return toFunction(this, function () { 116 | const base64Reg = /^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$/ 117 | if (!(base64Reg.test(value))) { 118 | return false 119 | } 120 | }) 121 | } 122 | 123 | // checks if the value is a valid JSON 124 | string.JSON = function () { 125 | return toFunction(this, function () { 126 | try { 127 | JSON.parse(value) 128 | } catch (e) { 129 | return false 130 | } 131 | }) 132 | } 133 | 134 | // checks if the value is a valid uuid string 135 | string.uuid = function () { 136 | return toFunction(this, function () { 137 | const uuidReg = /[a-f0-9]{8}-?[a-f0-9]{4}-?[1-5][a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}/i 138 | if (!(uuidReg.test(value))) { 139 | return false 140 | } 141 | }) 142 | } 143 | 144 | // checks if the value is a valid MAC address 145 | string.MAC = function () { 146 | return toFunction(this, function () { 147 | const macReg = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/ 148 | if (!(macReg.test(value))) { 149 | return false 150 | } 151 | }) 152 | } 153 | 154 | // checks if the value is a valid md5 string 155 | string.md5 = function () { 156 | return toFunction(this, function () { 157 | const md5Reg = /^[a-f0-9]{32}$/ 158 | if (!(md5Reg.test(value))) { 159 | return false 160 | } 161 | }) 162 | } 163 | 164 | // checks if the value corresponds to the card code selected 165 | string.card = function (cardType) { 166 | if (typeof cardType !== 'string') { 167 | throw new Error('cardType must be a string') 168 | } 169 | // Thanks to: https://github.com/DylanPiercey/the 170 | const cards = { 171 | jcb: /^(?:2131|1800|35\d{3})\d{11}$/, 172 | visa: /^4\d{12}(?:\d{3})?$/, 173 | discover: /^6(?:011|5\d{2})\d{12}$/, 174 | dinersclub: /^3(?:0[0-5]|[68]\d)\d{11}$/, 175 | mastercard: /^5[1-5]\d{14}$/, 176 | americanexpress: /^3[47]\d{13}$/ 177 | } 178 | if (!cards[cardType]) { 179 | throw new Error(cardType + ' card is not supported') 180 | } 181 | return toFunction(this, function () { 182 | if (!($reg$.test(value))) { 183 | return false 184 | } 185 | }, { $reg$: cards[cardType] }) 186 | } 187 | 188 | // Extends a string function 189 | string.extend = function (func) { 190 | return extend(this, func, toFunction) 191 | } 192 | 193 | const toFunction = buildToFunction(string) 194 | module.exports = string 195 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tyval", 3 | "version": "4.0.0", 4 | "description": "Fast and extensible validator for JavaScript", 5 | "main": "tyval.js", 6 | "scripts": { 7 | "bench": "npm run pretest && node bench/bench.js", 8 | "pretest": "standard", 9 | "test": "tap test/*.js", 10 | "test:all": "./test/test-runner.sh", 11 | "browser": "node build-browser.js" 12 | }, 13 | "keywords": [ 14 | "fast", 15 | "validator", 16 | "validation", 17 | "extensible", 18 | "composable", 19 | "type", 20 | "variable", 21 | "check", 22 | "value" 23 | ], 24 | "author": "Tomas Della Vedova - @delvedor (http://delved.org)", 25 | "license": "MIT", 26 | "repository": { 27 | "type": "git", 28 | "url": "git+https://github.com/delvedor/Tyval.git" 29 | }, 30 | "bugs": { 31 | "url": "https://github.com/delvedor/Tyval/issues" 32 | }, 33 | "homepage": "https://github.com/delvedor/Tyval#readme", 34 | "engines": { 35 | "node": ">=4.0.0" 36 | }, 37 | "devDependencies": { 38 | "babel-preset-es2015": "^6.16.0", 39 | "babelify": "^7.3.0", 40 | "benchmark": "^2.1.1", 41 | "browserify": "^13.1.0", 42 | "chalk": "^1.1.3", 43 | "is-my-json-valid": "^2.15.0", 44 | "joi": "^9.1.1", 45 | "replacestream": "^4.0.2", 46 | "self-stream": "^1.1.1", 47 | "standard": "^8.3.0", 48 | "tap": "^7.1.2", 49 | "uglify-js": "^2.7.3", 50 | "validate.js": "^0.10.0" 51 | }, 52 | "dependencies": {} 53 | } 54 | -------------------------------------------------------------------------------- /test/array.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const tap = require('tap') 4 | const test = tap.test 5 | const tyval = require('../tyval') 6 | 7 | test('array', (t) => { 8 | t.plan(4) 9 | t.is(typeof tyval.array, 'function') 10 | let arr = tyval.array() 11 | t.is(typeof arr, 'function') 12 | t.true(arr([])) 13 | t.false(arr('test')) 14 | }) 15 | 16 | test('array.max', (t) => { 17 | t.plan(4) 18 | t.is(typeof tyval.array.max, 'function') 19 | let max = tyval.array().max(10) 20 | t.is(typeof max, 'function') 21 | t.true(max([1, 2, 3])) 22 | t.false(max([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])) 23 | }) 24 | 25 | test('array.min', (t) => { 26 | t.plan(4) 27 | t.is(typeof tyval.array.min, 'function') 28 | let min = tyval.array().min(3) 29 | t.is(typeof min, 'function') 30 | t.true(min([1, 2, 3, 4])) 31 | t.false(min([])) 32 | }) 33 | 34 | test('array.length', (t) => { 35 | t.plan(4) 36 | t.is(typeof tyval.array.length, 'function') 37 | let len = tyval.array().length(3) 38 | t.is(typeof len, 'function') 39 | t.true(len([1, 2, 3])) 40 | t.false(len([1, 2])) 41 | }) 42 | 43 | test('arrayTest', (t) => { 44 | t.plan(4) 45 | let arrayTest = tyval.array().max(10).min(2) 46 | t.is(typeof arrayTest, 'function') 47 | t.true(arrayTest([1, 2, 3])) 48 | t.false(arrayTest([1])) 49 | t.false(arrayTest({})) 50 | }) 51 | 52 | /* eslint-disable no-undef */ 53 | test('array.extend', (t) => { 54 | t.plan(4) 55 | tyval.array.extend(function empty () { 56 | if (value.length !== 0) { 57 | return false 58 | } 59 | }) 60 | t.is(typeof tyval.array.empty, 'function') 61 | let empty = tyval.array().empty() 62 | t.is(typeof empty, 'function') 63 | t.true(empty([])) 64 | t.false(empty([1])) 65 | }) 66 | /* eslint-disable no-undef */ 67 | 68 | test('array.contains', (t) => { 69 | t.plan(4) 70 | t.is(typeof tyval.array.contains, 'function') 71 | let contains = tyval.array().contains(3) 72 | t.is(typeof contains, 'function') 73 | t.true(contains([1, 2, 3])) 74 | t.false(contains([1, 2])) 75 | }) 76 | 77 | test('array.items', (t) => { 78 | t.plan(4) 79 | t.is(typeof tyval.array.items, 'function') 80 | let item = tyval.string() 81 | let items = tyval.array().items(item) 82 | t.is(typeof items, 'function') 83 | t.true(items(['a', 'b', 'c'])) 84 | t.false(items([1, 'a', null])) 85 | }) 86 | -------------------------------------------------------------------------------- /test/boolean.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const tap = require('tap') 4 | const test = tap.test 5 | const tyval = require('../tyval') 6 | 7 | test('boolean', (t) => { 8 | t.plan(4) 9 | t.is(typeof tyval.boolean, 'function') 10 | let bool = tyval.boolean() 11 | t.is(typeof bool, 'function') 12 | t.true(bool(true)) 13 | t.false(bool('test')) 14 | }) 15 | 16 | -------------------------------------------------------------------------------- /test/date.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const tap = require('tap') 4 | const test = tap.test 5 | const tyval = require('../tyval') 6 | 7 | test('date', (t) => { 8 | t.plan(4) 9 | t.is(typeof tyval.date, 'function') 10 | let isDate = tyval.date() 11 | t.is(typeof isDate, 'function') 12 | t.true(isDate(new Date())) 13 | t.false(isDate('2016-06-03T13:45:53.225Z')) 14 | }) 15 | 16 | test('date.lower', (t) => { 17 | t.plan(4) 18 | t.is(typeof tyval.date.lower, 'function') 19 | let l = new Date() 20 | while (l.getTime() === new Date().getTime()) {} 21 | let h = new Date() 22 | let date = tyval.date().lower(h) 23 | t.is(typeof date, 'function') 24 | t.true(date(l)) 25 | t.false(date(h)) 26 | }) 27 | 28 | test('date.higher', (t) => { 29 | t.plan(4) 30 | t.is(typeof tyval.date.lower, 'function') 31 | let l = new Date() 32 | while (l.getTime() === new Date().getTime()) {} 33 | let h = new Date() 34 | let date = tyval.date().higher(l) 35 | t.is(typeof date, 'function') 36 | t.true(date(h)) 37 | t.false(date(l)) 38 | }) 39 | -------------------------------------------------------------------------------- /test/error.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const tap = require('tap') 4 | const test = tap.test 5 | const tyval = require('../tyval') 6 | 7 | test('error', (t) => { 8 | t.plan(4) 9 | t.is(typeof tyval.error, 'function') 10 | let err = tyval.error() 11 | t.is(typeof err, 'function') 12 | t.true(err(new Error())) 13 | t.false(err([])) 14 | }) 15 | 16 | test('error.RangeError', (t) => { 17 | t.plan(4) 18 | t.is(typeof tyval.error.RangeError, 'function') 19 | let err = tyval.error().RangeError() 20 | t.is(typeof err, 'function') 21 | t.true(err(new RangeError())) 22 | t.false(err(new Error())) 23 | }) 24 | 25 | test('error.ReferenceError', (t) => { 26 | t.plan(4) 27 | t.is(typeof tyval.error.ReferenceError, 'function') 28 | let err = tyval.error().ReferenceError() 29 | t.is(typeof err, 'function') 30 | t.true(err(new ReferenceError())) 31 | t.false(err(new Error())) 32 | }) 33 | 34 | test('error.SyntaxError', (t) => { 35 | t.plan(4) 36 | t.is(typeof tyval.error.SyntaxError, 'function') 37 | let err = tyval.error().SyntaxError() 38 | t.is(typeof err, 'function') 39 | t.true(err(new SyntaxError())) 40 | t.false(err(new Error())) 41 | }) 42 | 43 | test('error.TypeError', (t) => { 44 | t.plan(4) 45 | t.is(typeof tyval.error.TypeError, 'function') 46 | let err = tyval.error().TypeError() 47 | t.is(typeof err, 'function') 48 | t.true(err(new TypeError())) 49 | t.false(err(new Error())) 50 | }) 51 | 52 | test('error.message', (t) => { 53 | t.plan(4) 54 | t.is(typeof tyval.error.message, 'function') 55 | let err = tyval.error().message('some error') 56 | t.is(typeof err, 'function') 57 | t.true(err(new Error('some error'))) 58 | t.false(err(new Error('other error'))) 59 | }) 60 | -------------------------------------------------------------------------------- /test/number.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const tap = require('tap') 4 | const test = tap.test 5 | const tyval = require('../tyval') 6 | 7 | test('number', (t) => { 8 | t.plan(4) 9 | t.is(typeof tyval.number, 'function') 10 | let num = tyval.number() 11 | t.is(typeof num, 'function') 12 | t.true(num(1)) 13 | t.false(num('test')) 14 | }) 15 | 16 | test('number.max', (t) => { 17 | t.plan(4) 18 | t.is(typeof tyval.number.max, 'function') 19 | let max = tyval.number().max(10) 20 | t.is(typeof max, 'function') 21 | t.true(max(5)) 22 | t.false(max(15)) 23 | }) 24 | 25 | test('number.min', (t) => { 26 | t.plan(4) 27 | t.is(typeof tyval.number.min, 'function') 28 | let min = tyval.number().min(4) 29 | t.is(typeof min, 'function') 30 | t.true(min(5)) 31 | t.false(min(3)) 32 | }) 33 | 34 | test('number.positive', (t) => { 35 | t.plan(4) 36 | t.is(typeof tyval.number.positive, 'function') 37 | let pos = tyval.number().positive() 38 | t.is(typeof pos, 'function') 39 | t.true(pos(5)) 40 | t.false(pos(-5)) 41 | }) 42 | 43 | test('number.negative', (t) => { 44 | t.plan(4) 45 | t.is(typeof tyval.number.negative, 'function') 46 | let neg = tyval.number().negative() 47 | t.is(typeof neg, 'function') 48 | t.true(neg(-5)) 49 | t.false(neg(5)) 50 | }) 51 | 52 | test('number.integer', (t) => { 53 | t.plan(4) 54 | t.is(typeof tyval.number.integer, 'function') 55 | let int = tyval.number().integer() 56 | t.is(typeof int, 'function') 57 | t.true(int(1)) 58 | t.false(int(1.1)) 59 | }) 60 | 61 | test('number.float', (t) => { 62 | t.plan(4) 63 | t.is(typeof tyval.number.float, 'function') 64 | let float = tyval.number().float() 65 | t.is(typeof float, 'function') 66 | t.true(float(1.1)) 67 | t.false(float(1)) 68 | }) 69 | 70 | test('number.safeInteger', (t) => { 71 | t.plan(4) 72 | t.is(typeof tyval.number.safeInteger, 'function') 73 | let safe = tyval.number().safeInteger() 74 | t.is(typeof safe, 'function') 75 | t.true(safe(1)) 76 | t.false(safe(Number.MAX_SAFE_INTEGER + 1)) 77 | }) 78 | 79 | test('number.finite', (t) => { 80 | t.plan(4) 81 | t.is(typeof tyval.number.finite, 'function') 82 | let fin = tyval.number().finite() 83 | t.is(typeof fin, 'function') 84 | t.true(fin(1)) 85 | t.false(fin(Number.POSITIVE_INFINITY)) 86 | }) 87 | 88 | test('number.multiple', (t) => { 89 | t.plan(4) 90 | t.is(typeof tyval.number.multiple, 'function') 91 | let mul2 = tyval.number().multiple(2) 92 | t.is(typeof mul2, 'function') 93 | t.true(mul2(10)) 94 | t.false(mul2(3)) 95 | }) 96 | 97 | test('numTest', (t) => { 98 | t.plan(5) 99 | let numTest = tyval.number().min(-5).max(10).integer().finite().safeInteger() 100 | t.is(typeof numTest, 'function') 101 | t.true(numTest(5)) 102 | t.false(numTest(15)) 103 | t.false(numTest('15')) 104 | t.false(numTest(NaN)) 105 | }) 106 | 107 | /* eslint-disable no-undef */ 108 | test('number.extend', (t) => { 109 | t.plan(4) 110 | tyval.number.extend(function isZero () { 111 | if (value !== 0) { 112 | return false 113 | } 114 | }) 115 | t.is(typeof tyval.number.isZero, 'function') 116 | let zero = tyval.number().isZero() 117 | t.is(typeof zero, 'function') 118 | t.true(zero(0)) 119 | t.false(zero(1)) 120 | }) 121 | /* eslint-disable no-undef */ 122 | 123 | test('number.notNaN', (t) => { 124 | t.plan(4) 125 | t.is(typeof tyval.number.notNaN, 'function') 126 | let n = tyval.number().notNaN() 127 | t.is(typeof n, 'function') 128 | t.true(n(1)) 129 | t.false(n(NaN)) 130 | }) 131 | 132 | test('number.port', (t) => { 133 | t.plan(8) 134 | t.is(typeof tyval.number.port, 'function') 135 | let port = tyval.number().port() 136 | let portReserved = tyval.number().port({ reserved: true }) 137 | t.is(typeof port, 'function') 138 | t.is(typeof portReserved, 'function') 139 | t.true(port(1024)) 140 | t.false(port(-10)) 141 | t.false(port(70000)) 142 | t.true(portReserved(1025)) 143 | t.false(portReserved(1000)) 144 | }) 145 | -------------------------------------------------------------------------------- /test/object.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const tap = require('tap') 4 | const test = tap.test 5 | const tyval = require('../tyval') 6 | 7 | test('object', (t) => { 8 | t.plan(4) 9 | t.is(typeof tyval.object, 'function') 10 | let obj = tyval.object() 11 | t.is(typeof obj, 'function') 12 | t.true(obj({})) 13 | t.false(obj('test')) 14 | }) 15 | 16 | test('object.empty', (t) => { 17 | t.plan(4) 18 | t.is(typeof tyval.object.empty, 'function') 19 | let empty = tyval.object().empty() 20 | t.is(typeof empty, 'function') 21 | t.true(empty({})) 22 | t.false(empty({key: 'value'})) 23 | }) 24 | 25 | test('object.notNull', (t) => { 26 | t.plan(4) 27 | t.is(typeof tyval.object.notNull, 'function') 28 | let n = tyval.object().notNull() 29 | t.is(typeof n, 'function') 30 | t.true(n({})) 31 | t.false(n(null)) 32 | }) 33 | 34 | test('object.notArray', (t) => { 35 | t.plan(4) 36 | t.is(typeof tyval.object.notArray, 'function') 37 | let a = tyval.object().notArray() 38 | t.is(typeof a, 'function') 39 | t.true(a({})) 40 | t.false(a([])) 41 | }) 42 | 43 | test('object.notDate', (t) => { 44 | t.plan(4) 45 | t.is(typeof tyval.object.notDate, 'function') 46 | let n = tyval.object().notDate() 47 | t.is(typeof n, 'function') 48 | t.true(n({})) 49 | t.false(n(new Date())) 50 | }) 51 | 52 | test('object.notRegExp', (t) => { 53 | t.plan(4) 54 | t.is(typeof tyval.object.notRegExp, 'function') 55 | let n = tyval.object().notRegExp() 56 | t.is(typeof n, 'function') 57 | t.true(n({})) 58 | t.false(n(new RegExp())) 59 | }) 60 | 61 | test('object.has', (t) => { 62 | t.plan(4) 63 | t.is(typeof tyval.object.has, 'function') 64 | let has = tyval.object().has('test') 65 | t.is(typeof has, 'function') 66 | t.true(has({test: ''})) 67 | t.false(has({nope: ''})) 68 | }) 69 | 70 | test('object.has fast', (t) => { 71 | t.plan(5) 72 | t.is(typeof tyval.object.has, 'function') 73 | let has = tyval.object().has('test', { fast: true }) 74 | t.is(typeof has, 'function') 75 | t.true(has({test: ''})) 76 | t.false(has({nope: ''})) 77 | t.false(has({test: undefined})) 78 | }) 79 | 80 | test('object.hasNot', (t) => { 81 | t.plan(4) 82 | t.is(typeof tyval.object.hasNot, 'function') 83 | let hasNot = tyval.object().hasNot('test') 84 | t.is(typeof hasNot, 'function') 85 | t.true(hasNot({nope: ''})) 86 | t.false(hasNot({test: ''})) 87 | }) 88 | 89 | test('object.hasNot fast', (t) => { 90 | t.plan(5) 91 | t.is(typeof tyval.object.hasNot, 'function') 92 | let hasNot = tyval.object().hasNot('test', { fast: true }) 93 | t.is(typeof hasNot, 'function') 94 | t.true(hasNot({nope: ''})) 95 | t.false(hasNot({test: ''})) 96 | t.true(hasNot({test: undefined})) 97 | }) 98 | 99 | test('object.has - multiple', (t) => { 100 | t.plan(4) 101 | t.is(typeof tyval.object.has, 'function') 102 | let has = tyval.object().has('test').has('key') 103 | t.is(typeof has, 'function') 104 | t.true(has({test: 1, key: 2})) 105 | t.false(has({test: 1, nope: 2})) 106 | }) 107 | 108 | test('object.hasNot - multiple', (t) => { 109 | t.plan(4) 110 | t.is(typeof tyval.object.hasNot, 'function') 111 | let hasNot = tyval.object().hasNot('test').hasNot('key') 112 | t.is(typeof hasNot, 'function') 113 | t.true(hasNot({nil: 1, nope: 2})) 114 | t.false(hasNot({test: 1, key: 2})) 115 | }) 116 | 117 | test('object.has - object.hasNot', (t) => { 118 | t.plan(4) 119 | t.is(typeof tyval.object.hasNot, 'function') 120 | let key = tyval.object().has('test').hasNot('key') 121 | t.is(typeof key, 'function') 122 | t.true(key({test: 1, nope: 2})) 123 | t.false(key({test: 1, key: 2})) 124 | }) 125 | -------------------------------------------------------------------------------- /test/string.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const tap = require('tap') 4 | const test = tap.test 5 | const tyval = require('../tyval') 6 | 7 | test('string', (t) => { 8 | t.plan(4) 9 | t.is(typeof tyval.string, 'function') 10 | let str = tyval.string() 11 | t.is(typeof str, 'function') 12 | t.true(str('test')) 13 | t.false(str(1)) 14 | }) 15 | 16 | test('string.alphanum', (t) => { 17 | t.plan(5) 18 | t.is(typeof tyval.string.alphanum, 'function') 19 | let alp = tyval.string().alphanum() 20 | t.is(typeof alp, 'function') 21 | t.true(alp('abcd1234')) 22 | t.false(alp('abcd')) 23 | t.false(alp('123')) 24 | }) 25 | 26 | test('string.regex', (t) => { 27 | t.plan(5) 28 | t.is(typeof tyval.string.regex, 'function') 29 | let regex = tyval.string().regex(/((^[0-9]+[a-z]+)|(^[a-z]+[0-9]+))+[0-9a-z]+$/i) 30 | t.is(typeof regex, 'function') 31 | t.true(regex('abcd1234')) 32 | t.false(regex('abcd')) 33 | t.false(regex('123')) 34 | }) 35 | 36 | test('string.max', (t) => { 37 | t.plan(4) 38 | t.is(typeof tyval.string.max, 'function') 39 | let max = tyval.string().max(10) 40 | t.is(typeof max, 'function') 41 | t.true(max('test')) 42 | t.false(max('testtesttest')) 43 | }) 44 | 45 | test('string.min', (t) => { 46 | t.plan(4) 47 | t.is(typeof tyval.string.min, 'function') 48 | let min = tyval.string().min(3) 49 | t.is(typeof min, 'function') 50 | t.true(min('test')) 51 | t.false(min('no')) 52 | }) 53 | 54 | test('string.length', (t) => { 55 | t.plan(4) 56 | t.is(typeof tyval.string.length, 'function') 57 | let len = tyval.string().length(4) 58 | t.is(typeof len, 'function') 59 | t.true(len('test')) 60 | t.false(len('!test')) 61 | }) 62 | 63 | test('strTest', (t) => { 64 | t.plan(5) 65 | let strTest = tyval.string().min(5).max(10).alphanum() 66 | t.is(typeof strTest, 'function') 67 | t.true(strTest('abc123')) 68 | t.false(strTest('abc')) 69 | t.false(strTest('123')) 70 | t.false(strTest(123)) 71 | }) 72 | 73 | test('string.mail', (t) => { 74 | t.plan(9) 75 | t.is(typeof tyval.string.mail, 'function') 76 | let mail = tyval.string().mail() 77 | t.is(typeof mail, 'function') 78 | t.true(mail('test@gmail.com')) 79 | t.true(mail('make.test@sub.domain.com')) 80 | t.true(mail('the.answer.is.42@sub.sub.domain.eu')) 81 | t.false(mail('test@gmail.')) 82 | t.false(mail('test.gmail@.it')) 83 | t.false(mail('t.e.s.t.@g.e')) 84 | t.false(mail('t@t.t')) 85 | }) 86 | 87 | test('string.ipv4', (t) => { 88 | t.plan(9) 89 | t.is(typeof tyval.string.ipv4, 'function') 90 | let ip = tyval.string().ipv4() 91 | t.is(typeof ip, 'function') 92 | t.true(ip('192.168.20.20')) 93 | t.true(ip('0.255.0.255')) 94 | t.true(ip('0.0.0.0')) 95 | t.false(ip('192.168')) 96 | t.false(ip('192.168.20.256')) 97 | t.false(ip('::1')) 98 | t.false(ip('2001:0db8::1428:57ab')) 99 | }) 100 | 101 | test('string.ipv6', (t) => { 102 | t.plan(13) 103 | t.is(typeof tyval.string.ipv6, 'function') 104 | let ip = tyval.string().ipv6() 105 | t.is(typeof ip, 'function') 106 | t.true(ip('2001:0db8:0000:0000:0000:0000:1428:57ab')) 107 | t.true(ip('2001:0db8:0000:0000::1428:57ab')) 108 | t.true(ip('2001:0db8:0:0:0:0:1428:57ab')) 109 | t.true(ip('2001:0db8:0::0:1428:57ab')) 110 | t.true(ip('2001:0db8::1428:57ab')) 111 | t.false(ip('2001:0db8::25de::cade')) 112 | t.false(ip('1111:2222:3333:4444::5555:')) 113 | t.false(ip('2001:db8:85a3::8a2e:37023:7334')) 114 | t.false(ip('::ffff:257.1.2.3')) 115 | t.false(ip('1.2.3.4::')) 116 | t.false(ip('2001:db8:85a3::8a2e:370k:7334')) 117 | }) 118 | 119 | test('string.base64', (t) => { 120 | t.plan(6) 121 | t.is(typeof tyval.string.base64, 'function') 122 | let b64 = tyval.string().base64() 123 | t.is(typeof b64, 'function') 124 | t.true(b64('SSdtIGEgYmFzZTY0IHN0cmluZw==')) 125 | t.true(b64('MSsxPTU1')) 126 | t.false(b64('I\'m not a base64 string')) 127 | t.false(b64('Hello1World==')) 128 | }) 129 | 130 | test('string.JSON', (t) => { 131 | t.plan(4) 132 | t.is(typeof tyval.string.JSON, 'function') 133 | let isJSON = tyval.string().JSON() 134 | t.is(typeof isJSON, 'function') 135 | t.true(isJSON('{"valid":true}')) 136 | t.false(isJSON('{"valid:false}')) 137 | }) 138 | 139 | test('string.uuid', (t) => { 140 | t.plan(6) 141 | t.is(typeof tyval.string.uuid, 'function') 142 | let uuid = tyval.string().uuid() 143 | t.is(typeof uuid, 'function') 144 | t.true(uuid('6fa459ea-ee8a-3ca4-894e-db77e160355e')) 145 | t.true(uuid('16fd2706-8baf-433b-82eb-8c7fada847da')) 146 | t.true(uuid('886313e1-3b8a-5372-9b90-0c9aee199e5d')) 147 | t.false(uuid('what?')) 148 | }) 149 | 150 | test('string.MAC', (t) => { 151 | t.plan(4) 152 | t.is(typeof tyval.string.MAC, 'function') 153 | let isMac = tyval.string().MAC() 154 | t.is(typeof isMac, 'function') 155 | t.true(isMac('48-2C-6A-1E-59-3D')) 156 | t.false(isMac('nope!')) 157 | }) 158 | 159 | test('string.md5', (t) => { 160 | t.plan(4) 161 | t.is(typeof tyval.string.md5, 'function') 162 | let isMd5 = tyval.string().md5() 163 | t.is(typeof isMd5, 'function') 164 | t.true(isMd5('b4dd7f0b0ca6c25dd46cc096e45158eb')) 165 | t.false(isMd5('maybe but not')) 166 | }) 167 | 168 | /* eslint-disable no-undef */ 169 | test('string.extend', (t) => { 170 | t.plan(4) 171 | tyval.string.extend(function empty () { 172 | if (value.length > 0) { 173 | return false 174 | } 175 | }) 176 | t.is(typeof tyval.string.empty, 'function') 177 | let empty = tyval.string().empty() 178 | t.is(typeof empty, 'function') 179 | t.true(empty('')) 180 | t.false(empty('.')) 181 | }) 182 | /* eslint-disable no-undef */ 183 | 184 | test('string.card', (t) => { 185 | t.plan(15) 186 | t.is(typeof tyval.string.card, 'function') 187 | let card = tyval.string().card('jcb') 188 | t.true(card('3530111333300000')) 189 | t.false(card('378282246310005')) 190 | 191 | card = tyval.string().card('visa') 192 | t.true(card('4012888888881881')) 193 | t.false(card('3566002020360505')) 194 | 195 | card = tyval.string().card('discover') 196 | t.true(card('6011000990139424')) 197 | t.false(card('4111111111111111')) 198 | 199 | card = tyval.string().card('dinersclub') 200 | t.true(card('38520000023237')) 201 | t.false(card('6011111111111117')) 202 | 203 | card = tyval.string().card('mastercard') 204 | t.true(card('5105105105105100')) 205 | t.false(card('38520000023237')) 206 | 207 | card = tyval.string().card('americanexpress') 208 | t.true(card('371449635398431')) 209 | t.false(card('5555555555554444')) 210 | 211 | try { 212 | card = tyval.string().card('lannister') 213 | t.fail() 214 | } catch (e) { 215 | t.is(e.message, 'lannister card is not supported') 216 | t.pass() 217 | } 218 | }) 219 | -------------------------------------------------------------------------------- /test/test-runner.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | 3 | # This script run test and bench under Node v4, v5 and v6. 4 | # By default it runs only the test, if you pass '--bench' it will run the benchmark as well. 5 | 6 | set -e 7 | 8 | . ~/.nvm/nvm.sh 9 | 10 | bench=${1:-default} 11 | 12 | echo "\n-------------------------" 13 | echo "| Test under Node.js v4 |" 14 | echo "-------------------------" 15 | nvm use "4.5.0" 16 | npm test 17 | if [ $bench == "--bench" ] 18 | then 19 | npm run bench 20 | fi 21 | 22 | echo "\n-------------------------" 23 | echo "| Test under Node.js v5 |" 24 | echo "-------------------------" 25 | nvm use "5.11.1" 26 | npm test 27 | if [ $bench == "--bench" ] 28 | then 29 | npm run bench 30 | fi 31 | 32 | echo "\n-------------------------" 33 | echo "| Test under Node.js v6 |" 34 | echo "-------------------------" 35 | nvm use "6.5.0" 36 | npm test 37 | if [ $bench == "--bench" ] 38 | then 39 | npm run bench 40 | fi 41 | 42 | echo "\n--------------------" 43 | echo "| All test passed! |" 44 | echo "--------------------" 45 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const tap = require('tap') 4 | const test = tap.test 5 | const tyval = require('../tyval') 6 | 7 | test('tyval', (t) => { 8 | t.plan(1) 9 | t.is(typeof tyval, 'object') 10 | }) 11 | 12 | /* eslint-disable no-undef, no-unused-vars */ 13 | test('common.toFunction', (t) => { 14 | const common = require('../lib/common') 15 | t.plan(2) 16 | t.is(typeof common.toFunction, 'function') 17 | t.is(typeof common.toFunction(() => {}), 'function') 18 | }) 19 | 20 | test('common.toFunction - well formatted', (t) => { 21 | const common = require('../lib/common') 22 | t.plan(3) 23 | const num = function () { 24 | return toFunction(null, function () { 25 | if (typeof value !== 'number') { 26 | return false 27 | } 28 | }) 29 | } 30 | const toFunction = common.toFunction(num) 31 | t.is(typeof num(), 'function') 32 | t.true(num()(5)) 33 | t.false(num()('5')) 34 | }) 35 | 36 | test('common.toFunction - bad formatted', (t) => { 37 | const common = require('../lib/common') 38 | t.plan(3) 39 | const num = function () { 40 | return toFunction(null, function () {if(typeof value !=='number' ){return false } }) // eslint-disable-line 41 | } 42 | const toFunction = common.toFunction(num) 43 | t.is(typeof num(), 'function') 44 | t.true(num()(5)) 45 | t.false(num()('5')) 46 | }) 47 | 48 | test('common.toFunction - arrow function', (t) => { 49 | const common = require('../lib/common') 50 | t.plan(3) 51 | const num = function () { 52 | return toFunction(null, () => { 53 | if (typeof value !== 'number') { 54 | return false 55 | } 56 | }) 57 | } 58 | const toFunction = common.toFunction(num) 59 | t.is(typeof num(), 'function') 60 | t.true(num()(5)) 61 | t.false(num()('5')) 62 | }) 63 | 64 | test('common.toFunction - parameters', (t) => { 65 | const common = require('../lib/common') 66 | t.plan(3) 67 | const num = function (max) { 68 | return toFunction(null, function () { 69 | if (value !== $max$) { 70 | return false 71 | } 72 | }, { $max$: max }) 73 | } 74 | const toFunction = common.toFunction(num) 75 | t.is(typeof num(), 'function') 76 | t.true(num(5)(5)) 77 | t.false(num(5)('5')) 78 | }) 79 | 80 | test('common.toFunction - throw', (t) => { 81 | const common = require('../lib/common') 82 | t.plan(4) 83 | try { 84 | common.toFunction(null) 85 | t.fail() 86 | } catch (e) { 87 | t.pass() 88 | } 89 | 90 | const toFunction = common.toFunction(() => {}) 91 | try { 92 | toFunction(5) 93 | t.fail() 94 | } catch (e) { 95 | t.pass() 96 | } 97 | try { 98 | toFunction(null, 5) 99 | t.fail() 100 | } catch (e) { 101 | t.pass() 102 | } 103 | try { 104 | toFunction(null, () => {}, 5) 105 | t.fail() 106 | } catch (e) { 107 | t.pass() 108 | } 109 | }) 110 | 111 | test('or internal function declaration', (t) => { 112 | t.plan(6) 113 | t.is(typeof tyval.or, 'function') 114 | let validation = tyval.or(tyval.number().min(1).max(10), tyval.string().min(1).max(10)) 115 | t.is(typeof validation, 'function') 116 | t.true(validation(5)) 117 | t.true(validation('test')) 118 | t.false(validation(50)) 119 | t.false(validation('I will fail')) 120 | }) 121 | 122 | test('or external function declaration', (t) => { 123 | t.plan(6) 124 | t.is(typeof tyval.or, 'function') 125 | const num = tyval.number().min(1).max(10) 126 | const str = tyval.string().min(1).max(10) 127 | let validation = tyval.or(num, str) 128 | t.is(typeof validation, 'function') 129 | t.true(validation(5)) 130 | t.true(validation('test')) 131 | t.false(validation(50)) 132 | t.false(validation('I will fail')) 133 | }) 134 | 135 | test('or mixed function declaration', (t) => { 136 | t.plan(6) 137 | t.is(typeof tyval.or, 'function') 138 | const num = tyval.number().min(1).max(10) 139 | let validation = tyval.or(num, tyval.string().min(1).max(10)) 140 | t.is(typeof validation, 'function') 141 | t.true(validation(5)) 142 | t.true(validation('test')) 143 | t.false(validation(50)) 144 | t.false(validation('I will fail')) 145 | }) 146 | 147 | test('or multiple', (t) => { 148 | t.plan(8) 149 | t.is(typeof tyval.or, 'function') 150 | const num = tyval.number().min(1).max(10) 151 | const str = tyval.string().min(1).max(10) 152 | const obj = tyval.object().empty() 153 | let validation = tyval.or(num, str, obj) 154 | t.is(typeof validation, 'function') 155 | t.true(validation(5)) 156 | t.true(validation('test')) 157 | t.true(validation({})) 158 | t.false(validation(50)) 159 | t.false(validation('I will fail')) 160 | t.false(validation({ a: '1' })) 161 | }) 162 | 163 | test('or number range', (t) => { 164 | t.plan(3) 165 | const num1 = tyval.number().max(1) 166 | const num2 = tyval.number().min(10) 167 | // represents n < 1 || n > 10 168 | const or = tyval.or(num1, num2) 169 | t.true(or(20)) 170 | t.true(or(-3)) 171 | t.false(or(5)) 172 | }) 173 | -------------------------------------------------------------------------------- /tyval.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Tyval 3 | * Version: 4.0.0 4 | * Author: delvedor 5 | * Twitter: @delvedor 6 | * License: MIT 7 | * GitHub: https://github.com/delvedor/Tyval 8 | */ 9 | 10 | 'use strict' 11 | 12 | module.exports = { 13 | number: require('./lib/number'), 14 | string: require('./lib/string'), 15 | array: require('./lib/array'), 16 | date: require('./lib/date'), 17 | boolean: require('./lib/boolean'), 18 | object: require('./lib/object'), 19 | error: require('./lib/error'), 20 | or: require('./lib/common').or 21 | } 22 | -------------------------------------------------------------------------------- /tyval.min.js: -------------------------------------------------------------------------------- 1 | !function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a="function"==typeof require&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}for(var i="function"==typeof require&&require,o=0;o$max$)return!1},{$max$:max})},array.min=function(min){if("number"!=typeof min)throw new TypeError("array.min - min is not a number");return toFunction(this,function(){if(value.length<$min$)return!1},{$min$:min})},Object.defineProperty(array,"length",{writable:!0}),array.length=function(len){if("number"!=typeof len)throw new TypeError("array.length - len is not a number");return toFunction(this,function(){if(value.length!==$length$)return!1},{$length$:len})},array.contains=function(containValue){return toFunction(this,function(){if(value.indexOf($containValue$)===-1)return!1},{$containValue$:containValue})},array.items=function(func){if("function"!=typeof func)throw new TypeError("array.items parameter must be a function");return toFunction(this,function(){for(var validate=$func$,i=0,len=value.length;i=$newDate$)return!1},{$newDate$:d.getTime()})},date.higher=function(d){return toFunction(this,function(){if(value.getTime()<=$newDate$)return!1},{$newDate$:d.getTime()})},date.extend=function(func){return extend(this,func,toFunction)};var toFunction=buildToFunction(date);module.exports=date},{"./common":3}],5:[function(require,module,exports){"use strict";var buildToFunction=require("./common").toFunction,extend=require("./common").extend,error=function(){return toFunction(null,function(){if(!(value instanceof Error))return!1})};error.RangeError=function(){return toFunction(this,function(){if(!(value instanceof RangeError))return!1})},error.ReferenceError=function(){return toFunction(this,function(){if(!(value instanceof ReferenceError))return!1})},error.SyntaxError=function(){return toFunction(this,function(){if(!(value instanceof SyntaxError))return!1})},error.TypeError=function(){return toFunction(this,function(){if(!(value instanceof TypeError))return!1})},error.message=function(message){if("string"!=typeof message)throw new Error("error message must be a string");return toFunction(this,function(){if(value.message!==$message$)return!1},{$message$:message})},error.extend=function(func){return extend(this,func,toFunction)};var toFunction=buildToFunction(error);module.exports=error},{"./common":3}],6:[function(require,module,exports){"use strict";var buildToFunction=require("./common").toFunction,extend=require("./common").extend,number=function(){return toFunction(null,function(){if("number"!=typeof value)return!1})};number.max=function(max){if("number"!=typeof max)throw new TypeError("number.max - max is not a number");return toFunction(this,function(){if(value>$max$)return!1},{$max$:max})},number.min=function(min){if("number"!=typeof min)throw new TypeError("number.min - min is not a number");return toFunction(this,function(){if(value<$min$)return!1},{$min$:min})},number.positive=function(){return toFunction(this,function(){if(value<0)return!1})},number.negative=function(){return toFunction(this,function(){if(value>0)return!1})},number.integer=function(){return toFunction(this,function(){if(!(Math.floor(value)===value||value>9007199254740992||value<-9007199254740992))return!1})},number.float=function(){return toFunction(this,function(){if(Math.floor(value)===value||value>9007199254740992||value<-9007199254740992)return!1})},number.safeInteger=function(){return toFunction(this,function(){if(value>9007199254740991||value<-9007199254740991)return!1})},number.finite=function(){return toFunction(this,function(){if(!Number.isFinite(value))return!1})},number.multiple=function(mult){if("number"!=typeof mult)throw new TypeError("number.multiple - mult is not a number");return toFunction(this,function(){if(value%$multiple$!==0)return!1},{$multiple$:mult})},number.notNaN=function(){return toFunction(this,function(){if(Number.isNaN(value))return!1})},number.port=function(options){return options=options||{},toFunction(this,function(){if(value>65535||value<($reserved$?1024:0))return!1},{$reserved$:options.reserved||!1})},number.extend=function(func){return extend(this,func,toFunction)};var toFunction=buildToFunction(number);module.exports=number},{"./common":3}],7:[function(require,module,exports){"use strict";var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(obj){return typeof obj}:function(obj){return obj&&"function"==typeof Symbol&&obj.constructor===Symbol&&obj!==Symbol.prototype?"symbol":typeof obj},buildToFunction=require("./common").toFunction,extend=require("./common").extend,object=function(){return toFunction(null,function(){if("object"!==("undefined"==typeof value?"undefined":_typeof(value)))return!1})};object.empty=function(){return toFunction(this,function(){if(Object.keys(value).length>0)return!1})},object.notNull=function(){return toFunction(this,function(){if(null===value)return!1})},object.notArray=function(){return toFunction(this,function(){if(Array.isArray(value))return!1})},object.notDate=function(){return toFunction(this,function(){if(value instanceof Date)return!1})},object.notRegExp=function(){return toFunction(this,function(){if(value instanceof RegExp)return!1})},object.has=function(haskey,options){if("string"!=typeof haskey)throw new TypeError("object.has - haskey is not a string");return options=options||{},options.fast?toFunction(this,function(){if(void 0===value[$haskey$])return!1},{$haskey$:haskey}):toFunction(this,function(){if(!value.hasOwnProperty($haskey$))return!1},{$haskey$:haskey})},object.hasNot=function(hasnotkey,options){if("string"!=typeof hasnotkey)throw new TypeError("object.hasNot - hasnotkey is not a string");return options=options||{},options.fast?toFunction(this,function(){if(void 0!==value[$hasnotkey$])return!1},{$hasnotkey$:hasnotkey}):toFunction(this,function(){if(value.hasOwnProperty($hasnotkey$))return!1},{$hasnotkey$:hasnotkey})},object.extend=function(func){return extend(this,func,toFunction)};var toFunction=buildToFunction(object);module.exports=object},{"./common":3}],8:[function(require,module,exports){"use strict";"use string";var buildToFunction=require("./common").toFunction,extend=require("./common").extend,string=function(){return toFunction(null,function(){if("string"!=typeof value)return!1})};string.alphanum=function(){return toFunction(this,function(){var reg=/((^[0-9]+[a-z]+)|(^[a-z]+[0-9]+))+[0-9a-z]+$/i;if(!reg.test(value))return!1})},string.regex=function(reg){if(!(reg instanceof RegExp))throw new TypeError("string.regex - reg is not a RegExp");return toFunction(this,function(){var reg=$reg$;if(!reg.test(value))return!1},{$reg$:reg})},string.max=function(max){if("number"!=typeof max)throw new TypeError("string.max - max is not a number");return toFunction(this,function(){if(value.length>$max$)return!1},{$max$:max})},string.min=function(min){if("number"!=typeof min)throw new TypeError("string.min - min is not a number");return toFunction(this,function(){if(value.length<$min$)return!1},{$min$:min})},Object.defineProperty(string,"length",{writable:!0}),string.length=function(len){if("number"!=typeof len)throw new TypeError("string.length - len is not a number");return toFunction(this,function(){if(value.length!==$length$)return!1},{$length$:len})},string.mail=function(){return toFunction(this,function(){var mailReg=/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;if(!mailReg.test(value))return!1})},string.ipv4=function(){return toFunction(this,function(){var ipReg=/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;if(!ipReg.test(value))return!1})},string.ipv6=function(){return toFunction(this,function(){var ipReg=/^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/;if(!ipReg.test(value))return!1})},string.base64=function(){return toFunction(this,function(){var base64Reg=/^([A-Za-z0-9+\/]{4})*([A-Za-z0-9+\/]{4}|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{2}==)$/;if(!base64Reg.test(value))return!1})},string.JSON=function(){return toFunction(this,function(){try{JSON.parse(value)}catch(e){return!1}})},string.uuid=function(){return toFunction(this,function(){var uuidReg=/[a-f0-9]{8}-?[a-f0-9]{4}-?[1-5][a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}/i;if(!uuidReg.test(value))return!1})},string.MAC=function(){return toFunction(this,function(){var macReg=/^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/;if(!macReg.test(value))return!1})},string.md5=function(){return toFunction(this,function(){var md5Reg=/^[a-f0-9]{32}$/;if(!md5Reg.test(value))return!1})},string.card=function(cardType){if("string"!=typeof cardType)throw new Error("cardType must be a string");var cards={jcb:/^(?:2131|1800|35\d{3})\d{11}$/,visa:/^4\d{12}(?:\d{3})?$/,discover:/^6(?:011|5\d{2})\d{12}$/,dinersclub:/^3(?:0[0-5]|[68]\d)\d{11}$/,mastercard:/^5[1-5]\d{14}$/,americanexpress:/^3[47]\d{13}$/};if(!cards[cardType])throw new Error(cardType+" card is not supported");return toFunction(this,function(){if(!$reg$.test(value))return!1},{$reg$:cards[cardType]})},string.extend=function(func){return extend(this,func,toFunction)};var toFunction=buildToFunction(string);module.exports=string},{"./common":3}],9:[function(require,module,exports){"use strict";window.tyval={number:require("./lib/number"),string:require("./lib/string"),array:require("./lib/array"),date:require("./lib/date"),boolean:require("./lib/boolean"),object:require("./lib/object"),error:require("./lib/error"),or:require("./lib/common").or}},{"./lib/array":1,"./lib/boolean":2,"./lib/common":3,"./lib/date":4,"./lib/error":5,"./lib/number":6,"./lib/object":7,"./lib/string":8}]},{},[9]); --------------------------------------------------------------------------------