├── .gitignore ├── .jshintrc ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Gruntfile.js ├── LICENSE ├── README.md ├── bower.json ├── circle.yml ├── dist ├── js-data-schema.js ├── js-data-schema.min.js └── js-data-schema.min.map ├── karma.conf.js ├── karma.start.js ├── lib ├── dataTypes.js ├── index.js ├── rules.js ├── schema.js └── utils.js ├── mocha.start.js ├── package.json └── test ├── Schema.spec.js ├── dataTypes.spec.js ├── rules.spec.js └── schemas.spec.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Commenting this out is preferred by some people, see 24 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 25 | node_modules 26 | 27 | # Users Environment Variables 28 | .lock-wscript 29 | 30 | .idea/ 31 | *.iml 32 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": false, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": false, 7 | "curly": true, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "indent": 2, 11 | "latedef": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "quotmark": "single", 15 | "regexp": true, 16 | "undef": true, 17 | "unused": true, 18 | "expr": true, 19 | "strict": false, 20 | "trailing": true, 21 | "smarttabs": true, 22 | "loopfunc": true, 23 | "validthis": true, 24 | "predef": [ 25 | "inject", 26 | "describe", 27 | "describe", 28 | "schemator", 29 | "fail", 30 | "TYPES_EXCEPT_OBJECT", 31 | "TYPES_EXCEPT_STRING", 32 | "it", 33 | "xit", 34 | "before", 35 | "after", 36 | "beforeEach", 37 | "afterEach", 38 | "expect", 39 | "spyOn", 40 | "process", 41 | "module", 42 | "assert" 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ##### 1.2.5 - 03 June 2015 2 | 3 | ###### Backwards compatible bug fixes 4 | - #18 - IE8 support 5 | - #19 - return when no schemaRules are set (ie8 compat) Thanks @willrstern 6 | 7 | ##### 1.2.4 - 08 April 2015 8 | 9 | ###### Backwards compatible bug fixes 10 | - #15 - Rule names conflict with schema-attribute names 11 | 12 | ##### 1.2.3 - 07 April 2015 13 | 14 | ###### Backwards compatible bug fixes 15 | - One tiny piece missing from the fix for #14 16 | 17 | ##### 1.2.2 - 07 April 2015 18 | 19 | ###### Backwards compatible bug fixes 20 | - #13 - Check for instance wherel rule is undefined - thanks to @facultymatt for the PR! 21 | - #14 - Errors are not properly collected for nested properties 22 | 23 | ##### 1.2.1 - 03 April 2015 24 | 25 | ###### Backwards compatible bug fixes 26 | - #12 - Custom dataType Not Working 27 | 28 | ##### 1.2.0 - 31 March 2015 29 | 30 | ###### Backwards compatible API changes 31 | - #5 - feature: supporting "composite rules" 32 | 33 | ##### 1.1.1 - 27 March 2015 34 | 35 | ###### Backwards compatible bug fixes 36 | - #9 - Custom defined rule is not called on multiple attributes - thanks to @gtarcea for the PR! 37 | 38 | ##### 1.1.0 - 27 March 2015 39 | 40 | ###### Other 41 | - #8 - Convert to ES6 42 | 43 | ##### 1.0.0 - 03 February 2015 44 | 45 | Stable Version 1.0.0 46 | 47 | ##### 1.0.0-beta.1 - 12 January 2015 48 | 49 | Now in beta. 50 | 51 | ##### 1.0.0-alpha.1 - 01 November 2014 52 | 53 | Stable Version 1.0.0-alpha.1 54 | 55 | ##### 0.1.0 - 15 September 2014 56 | 57 | Initial Release 58 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guide 2 | 3 | First, support is handled via the [Mailing List](https://groups.io/org/groupsio/jsdata). Ask your questions there. 4 | 5 | When submitting issues on GitHub, please include as much detail as possible to make debugging quick and easy. 6 | 7 | - good - Your versions of js-data, js-data-schema, etc, relevant console logs/error, code examples that revealed the issue 8 | - better - A [plnkr](http://plnkr.co/), [fiddle](http://jsfiddle.net/), or [bin](http://jsbin.com/?html,output) that demonstrates the issue 9 | - best - A Pull Request that fixes the issue, including test coverage for the issue and the fix 10 | 11 | [Github Issues](https://github.com/js-data/js-data-schema/issues). 12 | 13 | #### Pull Requests 14 | 15 | 1. Contribute to the issue that is the reason you'll be developing in the first place 16 | 1. Fork js-data-schema 17 | 1. `git clone https://github.com//js-data-schema.git` 18 | 1. `cd js-data-schema; npm install; bower install;` 19 | 1. `grunt go` (builds and starts a watch) 20 | 1. (in another terminal) `grunt karma:dev` (runs the tests) 21 | 1. Write your code, including relevant documentation and tests 22 | 1. Submit a PR and we'll review 23 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | 3 | require('jit-grunt')(grunt, { 4 | coveralls: 'grunt-karma-coveralls' 5 | }); 6 | require('time-grunt')(grunt); 7 | 8 | var config = { 9 | lib: 'lib', 10 | test: 'test' 11 | }; 12 | 13 | var webpack = require('webpack'); 14 | var pkg = grunt.file.readJSON('package.json'); 15 | var banner = 'js-data-schema\n' + 16 | '@version ' + pkg.version + ' - Homepage \n' + 17 | '@author Jason Dobry \n' + 18 | '@copyright (c) 2013-2015 Jason Dobry \n' + 19 | '@license MIT \n' + 20 | '\n' + 21 | '@overview Define and validate rules, datatypes and schemata in Node and in the browser.'; 22 | 23 | grunt.initConfig({ 24 | config: config, 25 | pkg: grunt.file.readJSON('package.json'), 26 | clean: { 27 | pre: ['coverage', 'dist/'] 28 | }, 29 | watch: { 30 | files: [ 31 | 'lib/**/*.js', 32 | 'test/**/*.js' 33 | ], 34 | tasks: ['build'] 35 | }, 36 | jshint: { 37 | options: { 38 | jshintrc: '.jshintrc', 39 | ignores: ['test/support/*.js'] 40 | }, 41 | src: [ 42 | 'Gruntfile.js', 43 | '<%= config.lib %>/{,*/}*.js' 44 | ], 45 | test: [ 46 | '<%= config.test %>/{,*/}*.js' 47 | ] 48 | }, 49 | 50 | uglify: { 51 | second: { 52 | options: { 53 | sourceMap: true, 54 | sourceMapName: 'dist/js-data-schema.min.map', 55 | banner: '/*!\n' + 56 | '* js-data-schema\n' + 57 | '* @version <%= pkg.version %> - Homepage \n' + 58 | '* @author Jason Dobry \n' + 59 | '* @copyright (c) 2013-2014 Jason Dobry \n' + 60 | '* @license MIT \n' + 61 | '*\n' + 62 | '* @overview Define and validate rules, datatypes and schemata in Node and in the browser.\n' + 63 | '*/\n' 64 | }, 65 | files: { 66 | 'dist/js-data-schema.min.js': ['dist/js-data-schema.js'] 67 | } 68 | } 69 | }, 70 | 71 | mochaTest: { 72 | dist: { 73 | options: { 74 | reporter: 'spec' 75 | }, 76 | src: ['mocha.start.js', 'test/**/*.js'] 77 | } 78 | }, 79 | webpack: { 80 | dist: { 81 | entry: './lib/index.js', 82 | output: { 83 | filename: './dist/js-data-schema.js', 84 | libraryTarget: 'umd', 85 | library: 'Schemator' 86 | }, 87 | module: { 88 | loaders: [ 89 | { test: /(lib)(.+)\.js$/, exclude: /node_modules/, loader: 'babel-loader?blacklist=useStrict' } 90 | ], 91 | preLoaders: [ 92 | { 93 | test: /(lib)(.+)\.js$|(test)(.+)\.js$/, // include .js files 94 | exclude: /node_modules/, // exclude any and all files in the node_modules folder 95 | loader: "jshint-loader?failOnHint=true" 96 | } 97 | ] 98 | }, 99 | plugins: [ 100 | new webpack.BannerPlugin(banner) 101 | ] 102 | } 103 | }, 104 | karma: { 105 | options: { 106 | configFile: './karma.conf.js' 107 | }, 108 | dist: {}, 109 | dev: { 110 | browsers: ['Chrome'], 111 | autoWatch: true, 112 | singleRun: false, 113 | reporters: ['spec'], 114 | preprocessors: {} 115 | } 116 | }, 117 | coveralls: { 118 | options: { 119 | coverage_dir: 'coverage' 120 | } 121 | } 122 | }); 123 | 124 | grunt.registerTask('test', [ 125 | 'build', 126 | 'mochaTest', 127 | 'karma:dist' 128 | ]); 129 | grunt.registerTask('build', [ 130 | 'clean', 131 | 'webpack', 132 | 'uglify' 133 | ]); 134 | grunt.registerTask('go', ['build', 'watch']); 135 | 136 | grunt.registerTask('default', ['build']); 137 | }; 138 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2015 Jason Dobry 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | js-data logo 2 | 3 | ## js-data-schema [![bower version](https://img.shields.io/bower/v/js-data-schema.svg?style=flat-square)](https://www.npmjs.org/package/js-data-schema) [![npm version](https://img.shields.io/npm/v/js-data-schema.svg?style=flat-square)](https://www.npmjs.org/package/js-data-schema) [![Circle CI](https://img.shields.io/circleci/project/js-data/js-data-schema/master.svg?style=flat-square)](https://circleci.com/gh/js-data/js-data-schema/tree/master) [![npm downloads](https://img.shields.io/npm/dm/js-data-schema.svg?style=flat-square)](https://www.npmjs.org/package/js-data-schema) [![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](https://github.com/js-data/js-data-schema/blob/master/LICENSE) 4 | 5 | __Define and validate rules, datatypes and schemata in Node and in the browser.__ 6 | 7 | ### API Documentation 8 | [Schemator API](http://www.js-data.io/docs/js-data-schema) 9 | 10 | ### Install 11 | 12 | ##### Node 13 | `npm install --save js-data-schema` 14 | 15 | ```js 16 | var Schemator = require('js-data-schema'); 17 | ``` 18 | 19 | ##### Browser 20 | `npm install --save js-data-schema` or `bower install --save js-data-schema` 21 | 22 | Load `js-data-schema/dist/js-data-schema.js` into your browser. 23 | 24 | ```js 25 | // global constructor if you're not using AMD or CommonJS 26 | window.Schemator; 27 | 28 | // AMD 29 | define(['js-data-schema'], function (Schemator) { ... }) 30 | 31 | // CommonJS 32 | var Schemator = require('js-data-schema'); 33 | ``` 34 | 35 | ### Getting Started 36 | 37 | ```js 38 | var schemator = new Schemator(); 39 | 40 | schemator.defineSchema('Person', { 41 | name: 'string' 42 | }); 43 | 44 | var errors = schemator.validateSync('Person', { name: 'John' }); 45 | 46 | errors; // null 47 | 48 | errors = schemator.validateSync('Person', { name: 50043 }); 49 | 50 | errors; // { 51 | // name: [{ 52 | // rule: 'type', 53 | // actual: 'number', 54 | // expected: 'string' 55 | // }] 56 | // } 57 | ``` 58 | 59 | ### Project Status 60 | 61 | __Latest Release:__ [![Latest Release](https://img.shields.io/github/release/js-data/js-data-schema.svg?style=flat-square)](https://github.com/js-data/js-data-schema/releases) 62 | 63 | __Status:__ 64 | 65 | [![Dependency Status](https://img.shields.io/gemnasium/js-data/js-data-schema.svg?style=flat-square)](https://gemnasium.com/js-data/js-data-schema) [![Coverage Status](https://img.shields.io/coveralls/js-data/js-data-schema/master.svg?style=flat-square)](https://coveralls.io/r/js-data/js-data-schema?branch=master) [![Codacity](https://img.shields.io/codacy/e35036d4d2ea4c618a5711f3dd4ba659.svg?style=flat-square)](https://www.codacy.com/public/jasondobry/js-data-schema/dashboard) 66 | 67 | __Supported Platforms:__ 68 | 69 | [![browsers](https://img.shields.io/badge/Browser-Chrome%2CFirefox%2CSafari%2COpera%2CIE%209%2B%2CiOS%20Safari%207.1%2B%2CAndroid%20Browser%202.3%2B-green.svg?style=flat-square)](https://github.com/js-data/js-data) 70 | 71 | ### API 72 | 73 | [js-data-schema api](http://www.js-data.io/docs/js-data-schema) 74 | 75 | ### Contributing 76 | 77 | First, support is handled via the [Mailing List](https://groups.io/org/groupsio/jsdata). Ask your questions there. 78 | 79 | When submitting issues on GitHub, please include as much detail as possible to make debugging quick and easy. 80 | 81 | - good - Your versions of js-data, js-data-schema, etc, relevant console logs/error, code examples that revealed the issue 82 | - better - A [plnkr](http://plnkr.co/), [fiddle](http://jsfiddle.net/), or [bin](http://jsbin.com/?html,output) that demonstrates the issue 83 | - best - A Pull Request that fixes the issue, including test coverage for the issue and the fix 84 | 85 | [Github Issues](https://github.com/js-data/js-data-schema/issues). 86 | 87 | #### Pull Requests 88 | 89 | 1. Contribute to the issue that is the reason you'll be developing in the first place 90 | 1. Fork js-data-schema 91 | 1. `git clone https://github.com//js-data-schema.git` 92 | 1. `cd js-data-schema; npm install; bower install;` 93 | 1. `grunt go` (builds and starts a watch) 94 | 1. (in another terminal) `grunt karma:dev` (runs the tests) 95 | 1. Write your code, including relevant documentation and tests 96 | 1. Submit a PR and we'll review 97 | 98 | ### License 99 | [MIT License](https://github.com/js-data/js-data-schema/blob/master/LICENSE) 100 | 101 | Copyright © 2013-2015 Jason Dobry 102 | 103 | Permission is hereby granted, free of charge, to any person obtaining a copy of 104 | this software and associated documentation files (the "Software"), to deal in 105 | the Software without restriction, including without limitation the rights to 106 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 107 | of the Software, and to permit persons to whom the Software is furnished to do 108 | so, subject to the following conditions: 109 | 110 | The above copyright notice and this permission notice shall be included in all 111 | copies or substantial portions of the Software. 112 | 113 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 114 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 115 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 116 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 117 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 118 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 119 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Jason Dobry", 3 | "name": "js-data-schema", 4 | "description": "Define and validate rules, datatypes and schemata in Node and in the browser.", 5 | "homepage": "http://www.js-data.io/docs/js-data-schema", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/js-data/js-data-schema.git" 9 | }, 10 | "main": "./dist/js-data-schema.js", 11 | "ignore": [ 12 | ".idea/", 13 | "*.iml", 14 | "lib/", 15 | "Gruntfile.js", 16 | "CONTRIBUTING.md", 17 | "node_modules/", 18 | "coverage/", 19 | "test/", 20 | ".*", 21 | "karma.conf", 22 | "package.json" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | pre: 3 | - bower install 4 | cache_directories: 5 | - "bower_components" 6 | test: 7 | post: 8 | - grunt coveralls || true 9 | -------------------------------------------------------------------------------- /dist/js-data-schema.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * js-data-schema 3 | * @version 1.2.5 - Homepage 4 | * @author Jason Dobry 5 | * @copyright (c) 2013-2015 Jason Dobry 6 | * @license MIT 7 | * 8 | * @overview Define and validate rules, datatypes and schemata in Node and in the browser. 9 | */ 10 | (function webpackUniversalModuleDefinition(root, factory) { 11 | if(typeof exports === 'object' && typeof module === 'object') 12 | module.exports = factory(); 13 | else if(typeof define === 'function' && define.amd) 14 | define(factory); 15 | else if(typeof exports === 'object') 16 | exports["Schemator"] = factory(); 17 | else 18 | root["Schemator"] = factory(); 19 | })(this, function() { 20 | return /******/ (function(modules) { // webpackBootstrap 21 | /******/ // The module cache 22 | /******/ var installedModules = {}; 23 | 24 | /******/ // The require function 25 | /******/ function __webpack_require__(moduleId) { 26 | 27 | /******/ // Check if module is in cache 28 | /******/ if(installedModules[moduleId]) 29 | /******/ return installedModules[moduleId].exports; 30 | 31 | /******/ // Create a new module (and put it into the cache) 32 | /******/ var module = installedModules[moduleId] = { 33 | /******/ exports: {}, 34 | /******/ id: moduleId, 35 | /******/ loaded: false 36 | /******/ }; 37 | 38 | /******/ // Execute the module function 39 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 40 | 41 | /******/ // Flag the module as loaded 42 | /******/ module.loaded = true; 43 | 44 | /******/ // Return the exports of the module 45 | /******/ return module.exports; 46 | /******/ } 47 | 48 | 49 | /******/ // expose the modules object (__webpack_modules__) 50 | /******/ __webpack_require__.m = modules; 51 | 52 | /******/ // expose the module cache 53 | /******/ __webpack_require__.c = installedModules; 54 | 55 | /******/ // __webpack_public_path__ 56 | /******/ __webpack_require__.p = ""; 57 | 58 | /******/ // Load entry module and return exports 59 | /******/ return __webpack_require__(0); 60 | /******/ }) 61 | /************************************************************************/ 62 | /******/ ([ 63 | /* 0 */ 64 | /***/ function(module, exports, __webpack_require__) { 65 | 66 | Object.defineProperty(exports, '__esModule', { 67 | value: true 68 | }); 69 | 70 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 71 | 72 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 73 | 74 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 75 | 76 | var _utils = __webpack_require__(1); 77 | 78 | var _utils2 = _interopRequireDefault(_utils); 79 | 80 | var _dataTypes = __webpack_require__(47); 81 | 82 | var _dataTypes2 = _interopRequireDefault(_dataTypes); 83 | 84 | var _rules = __webpack_require__(48); 85 | 86 | var _rules2 = _interopRequireDefault(_rules); 87 | 88 | var _schema = __webpack_require__(49); 89 | 90 | var _schema2 = _interopRequireDefault(_schema); 91 | 92 | var id = 1; 93 | 94 | var Schemator = (function () { 95 | function Schemator() { 96 | _classCallCheck(this, Schemator); 97 | 98 | this.dataTypes = {}; 99 | this.rules = {}; 100 | this.schemata = {}; 101 | this.id = id++; 102 | } 103 | 104 | _createClass(Schemator, [{ 105 | key: 'availableDataTypes', 106 | value: function availableDataTypes() { 107 | return _utils2['default'].unique(_utils2['default'].keys(this.dataTypes).concat(_utils2['default'].keys(_dataTypes2['default']))); 108 | } 109 | }, { 110 | key: 'availableRules', 111 | value: function availableRules() { 112 | return _utils2['default'].unique(_utils2['default'].keys(this.rules).concat(_utils2['default'].keys(_rules2['default']))); 113 | } 114 | }, { 115 | key: 'availableSchemata', 116 | value: function availableSchemata() { 117 | return _utils2['default'].keys(this.schemata); 118 | } 119 | }, { 120 | key: 'defineDataType', 121 | value: function defineDataType(name, typeDefinition) { 122 | if (!_utils2['default'].isString(name)) { 123 | throw new Error('"name" must be a string!'); 124 | } else if (!_utils2['default'].isFunction(typeDefinition)) { 125 | throw new Error('"typeDefinition" must be a function!'); 126 | } else if (this.dataTypes[name]) { 127 | throw new Error('dataType already registered!'); 128 | } 129 | this.dataTypes[name] = typeDefinition; 130 | } 131 | }, { 132 | key: 'defineRule', 133 | value: function defineRule(name, ruleFunc, async) { 134 | if (!_utils2['default'].isString(name)) { 135 | throw new Error('"name" must be a string!'); 136 | } else if (!_utils2['default'].isFunction(ruleFunc)) { 137 | throw new Error('"ruleFunc" must be a function!'); 138 | } else if (this.rules[name]) { 139 | throw new Error('rule already registered!'); 140 | } 141 | this.rules[name] = ruleFunc; 142 | this.rules[name].async = !!async; 143 | } 144 | }, { 145 | key: 'defineSchema', 146 | value: function defineSchema(name, schema) { 147 | if (this.schemata[name]) { 148 | throw new Error('schema already registered!'); 149 | } else if (schema instanceof _schema2['default']) { 150 | throw new Error('schema registered elsewhere!'); 151 | } 152 | this.schemata[name] = new _schema2['default'](name, schema, this); 153 | this.schemata[name].parent = this; 154 | return this.schemata[name]; 155 | } 156 | }, { 157 | key: 'getDataType', 158 | value: function getDataType(name) { 159 | return this.dataTypes[name] || _dataTypes2['default'][name]; 160 | } 161 | }, { 162 | key: 'getRule', 163 | value: function getRule(name) { 164 | return this.rules[name] || _rules2['default'][name]; 165 | } 166 | }, { 167 | key: 'getSchema', 168 | value: function getSchema(name) { 169 | return this.schemata[name]; 170 | } 171 | }, { 172 | key: 'removeDataType', 173 | value: function removeDataType(name) { 174 | delete this.dataTypes[name]; 175 | } 176 | }, { 177 | key: 'removeRule', 178 | value: function removeRule(name) { 179 | delete this.rules[name]; 180 | } 181 | }, { 182 | key: 'removeSchema', 183 | value: function removeSchema(name) { 184 | delete this.schemata[name]; 185 | } 186 | }, { 187 | key: 'schemaCheck', 188 | value: function schemaCheck(name) { 189 | if (!this.schemata[name]) { 190 | throw new Error('schema is not registered!'); 191 | } 192 | } 193 | }, { 194 | key: 'validateSync', 195 | value: function validateSync(name, attrs, options) { 196 | this.schemaCheck(name); 197 | return this.schemata[name].validateSync(attrs, options); 198 | } 199 | }, { 200 | key: 'validate', 201 | value: function validate(name, attrs, options, cb) { 202 | this.schemaCheck(name); 203 | return this.schemata[name].validate(attrs, options, cb); 204 | } 205 | }, { 206 | key: 'addDefaultsToTarget', 207 | value: function addDefaultsToTarget(name, target, overwrite) { 208 | this.schemaCheck(name); 209 | return this.schemata[name].addDefaultsToTarget(target, overwrite); 210 | } 211 | }, { 212 | key: 'setDefaults', 213 | value: function setDefaults(name, attrs) { 214 | this.schemaCheck(name); 215 | return this.schemata[name].setDefaults(attrs); 216 | } 217 | }, { 218 | key: 'getDefaults', 219 | value: function getDefaults(name) { 220 | this.schemaCheck(name); 221 | return this.schemata[name].getDefaults(); 222 | } 223 | }, { 224 | key: 'stripNonSchemaAttrs', 225 | value: function stripNonSchemaAttrs(name, attrs) { 226 | this.schemaCheck(name); 227 | return this.schemata[name].stripNonSchemaAttrs(attrs); 228 | } 229 | }]); 230 | 231 | return Schemator; 232 | })(); 233 | 234 | exports['default'] = Schemator; 235 | module.exports = exports['default']; 236 | 237 | /***/ }, 238 | /* 1 */ 239 | /***/ function(module, exports, __webpack_require__) { 240 | 241 | Object.defineProperty(exports, '__esModule', { 242 | value: true 243 | }); 244 | 245 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 246 | 247 | var _moutLangIsString = __webpack_require__(7); 248 | 249 | var _moutLangIsString2 = _interopRequireDefault(_moutLangIsString); 250 | 251 | var _moutLangIsBoolean = __webpack_require__(10); 252 | 253 | var _moutLangIsBoolean2 = _interopRequireDefault(_moutLangIsBoolean); 254 | 255 | var _moutLangIsNumber = __webpack_require__(11); 256 | 257 | var _moutLangIsNumber2 = _interopRequireDefault(_moutLangIsNumber); 258 | 259 | var _moutLangIsObject = __webpack_require__(12); 260 | 261 | var _moutLangIsObject2 = _interopRequireDefault(_moutLangIsObject); 262 | 263 | var _moutLangIsDate = __webpack_require__(13); 264 | 265 | var _moutLangIsDate2 = _interopRequireDefault(_moutLangIsDate); 266 | 267 | var _moutLangIsFunction = __webpack_require__(14); 268 | 269 | var _moutLangIsFunction2 = _interopRequireDefault(_moutLangIsFunction); 270 | 271 | var _moutLangIsUndefined = __webpack_require__(15); 272 | 273 | var _moutLangIsUndefined2 = _interopRequireDefault(_moutLangIsUndefined); 274 | 275 | var _moutLangIsArray = __webpack_require__(16); 276 | 277 | var _moutLangIsArray2 = _interopRequireDefault(_moutLangIsArray); 278 | 279 | var _moutLangIsEmpty = __webpack_require__(17); 280 | 281 | var _moutLangIsEmpty2 = _interopRequireDefault(_moutLangIsEmpty); 282 | 283 | var _moutLangToString = __webpack_require__(18); 284 | 285 | var _moutLangToString2 = _interopRequireDefault(_moutLangToString); 286 | 287 | var _moutLangToNumber = __webpack_require__(19); 288 | 289 | var _moutLangToNumber2 = _interopRequireDefault(_moutLangToNumber); 290 | 291 | var _moutObjectGet = __webpack_require__(20); 292 | 293 | var _moutObjectGet2 = _interopRequireDefault(_moutObjectGet); 294 | 295 | var _moutObjectDeepMixIn = __webpack_require__(22); 296 | 297 | var _moutObjectDeepMixIn2 = _interopRequireDefault(_moutObjectDeepMixIn); 298 | 299 | var _moutObjectDeepFillIn = __webpack_require__(2); 300 | 301 | var _moutObjectDeepFillIn2 = _interopRequireDefault(_moutObjectDeepFillIn); 302 | 303 | var _moutObjectForOwn = __webpack_require__(3); 304 | 305 | var _moutObjectForOwn2 = _interopRequireDefault(_moutObjectForOwn); 306 | 307 | var _moutObjectKeys = __webpack_require__(23); 308 | 309 | var _moutObjectKeys2 = _interopRequireDefault(_moutObjectKeys); 310 | 311 | var _moutObjectPick = __webpack_require__(24); 312 | 313 | var _moutObjectPick2 = _interopRequireDefault(_moutObjectPick); 314 | 315 | var _moutObjectFilter = __webpack_require__(26); 316 | 317 | var _moutObjectFilter2 = _interopRequireDefault(_moutObjectFilter); 318 | 319 | var _moutObjectMap = __webpack_require__(31); 320 | 321 | var _moutObjectMap2 = _interopRequireDefault(_moutObjectMap); 322 | 323 | var _moutObjectMerge = __webpack_require__(32); 324 | 325 | var _moutObjectMerge2 = _interopRequireDefault(_moutObjectMerge); 326 | 327 | var _moutObjectUnset = __webpack_require__(36); 328 | 329 | var _moutObjectUnset2 = _interopRequireDefault(_moutObjectUnset); 330 | 331 | var _moutArrayContains = __webpack_require__(38); 332 | 333 | var _moutArrayContains2 = _interopRequireDefault(_moutArrayContains); 334 | 335 | var _moutArrayIntersection = __webpack_require__(40); 336 | 337 | var _moutArrayIntersection2 = _interopRequireDefault(_moutArrayIntersection); 338 | 339 | var _moutArrayDifference = __webpack_require__(44); 340 | 341 | var _moutArrayDifference2 = _interopRequireDefault(_moutArrayDifference); 342 | 343 | var _moutArrayUnique = __webpack_require__(41); 344 | 345 | var _moutArrayUnique2 = _interopRequireDefault(_moutArrayUnique); 346 | 347 | var _moutNumberToInt = __webpack_require__(46); 348 | 349 | var _moutNumberToInt2 = _interopRequireDefault(_moutNumberToInt); 350 | 351 | exports['default'] = { 352 | isString: _moutLangIsString2['default'], 353 | isBoolean: _moutLangIsBoolean2['default'], 354 | isNumber: _moutLangIsNumber2['default'], 355 | isObject: _moutLangIsObject2['default'], 356 | isDate: _moutLangIsDate2['default'], 357 | isFunction: _moutLangIsFunction2['default'], 358 | isUndefined: _moutLangIsUndefined2['default'], 359 | isArray: _moutLangIsArray2['default'], 360 | isEmpty: _moutLangIsEmpty2['default'], 361 | toString: _moutLangToString2['default'], 362 | toNumber: _moutLangToNumber2['default'], 363 | 364 | 'get': _moutObjectGet2['default'], 365 | deepMixIn: _moutObjectDeepMixIn2['default'], 366 | deepFillIn: _moutObjectDeepFillIn2['default'], 367 | forOwn: _moutObjectForOwn2['default'], 368 | keys: _moutObjectKeys2['default'], 369 | pick: _moutObjectPick2['default'], 370 | filter: _moutObjectFilter2['default'], 371 | map: _moutObjectMap2['default'], 372 | merge: _moutObjectMerge2['default'], 373 | unset: _moutObjectUnset2['default'], 374 | 375 | contains: _moutArrayContains2['default'], 376 | intersection: _moutArrayIntersection2['default'], 377 | difference: _moutArrayDifference2['default'], 378 | unique: _moutArrayUnique2['default'], 379 | 380 | toInt: _moutNumberToInt2['default'], 381 | 382 | errMsg: function errMsg(rule, actual, expected) { 383 | return { 384 | rule: rule, 385 | actual: actual, 386 | expected: expected 387 | }; 388 | }, 389 | 390 | parallel: function parallel(tasks, cb) { 391 | var results = {}; 392 | var completed = 0; 393 | var length = 0; 394 | 395 | (0, _moutObjectForOwn2['default'])(tasks, function () { 396 | length += 1; 397 | }); 398 | 399 | (0, _moutObjectForOwn2['default'])(tasks, function (task, key) { 400 | task(function (err) { 401 | var args = Array.prototype.slice.call(arguments, 1); 402 | if (args.length <= 1) { 403 | args = args[0]; 404 | } 405 | results[key] = args; 406 | done(err); 407 | }); 408 | }); 409 | 410 | function done(err) { 411 | completed += 1; 412 | if (err || completed >= length) { 413 | cb(err, results); 414 | } 415 | } 416 | } 417 | }; 418 | module.exports = exports['default']; 419 | 420 | /***/ }, 421 | /* 2 */ 422 | /***/ function(module, exports, __webpack_require__) { 423 | 424 | var forOwn = __webpack_require__(3); 425 | var isPlainObject = __webpack_require__(6); 426 | 427 | /** 428 | * Deeply copy missing properties in the target from the defaults. 429 | */ 430 | function deepFillIn(target, defaults){ 431 | var i = 0, 432 | n = arguments.length, 433 | obj; 434 | 435 | while(++i < n) { 436 | obj = arguments[i]; 437 | if (obj) { 438 | // jshint loopfunc: true 439 | forOwn(obj, function(newValue, key) { 440 | var curValue = target[key]; 441 | if (curValue == null) { 442 | target[key] = newValue; 443 | } else if (isPlainObject(curValue) && 444 | isPlainObject(newValue)) { 445 | deepFillIn(curValue, newValue); 446 | } 447 | }); 448 | } 449 | } 450 | 451 | return target; 452 | } 453 | 454 | module.exports = deepFillIn; 455 | 456 | 457 | 458 | 459 | /***/ }, 460 | /* 3 */ 461 | /***/ function(module, exports, __webpack_require__) { 462 | 463 | var hasOwn = __webpack_require__(4); 464 | var forIn = __webpack_require__(5); 465 | 466 | /** 467 | * Similar to Array/forEach but works over object properties and fixes Don't 468 | * Enum bug on IE. 469 | * based on: http://whattheheadsaid.com/2010/10/a-safer-object-keys-compatibility-implementation 470 | */ 471 | function forOwn(obj, fn, thisObj){ 472 | forIn(obj, function(val, key){ 473 | if (hasOwn(obj, key)) { 474 | return fn.call(thisObj, obj[key], key, obj); 475 | } 476 | }); 477 | } 478 | 479 | module.exports = forOwn; 480 | 481 | 482 | 483 | 484 | /***/ }, 485 | /* 4 */ 486 | /***/ function(module, exports, __webpack_require__) { 487 | 488 | 489 | 490 | /** 491 | * Safer Object.hasOwnProperty 492 | */ 493 | function hasOwn(obj, prop){ 494 | return Object.prototype.hasOwnProperty.call(obj, prop); 495 | } 496 | 497 | module.exports = hasOwn; 498 | 499 | 500 | 501 | 502 | /***/ }, 503 | /* 5 */ 504 | /***/ function(module, exports, __webpack_require__) { 505 | 506 | var hasOwn = __webpack_require__(4); 507 | 508 | var _hasDontEnumBug, 509 | _dontEnums; 510 | 511 | function checkDontEnum(){ 512 | _dontEnums = [ 513 | 'toString', 514 | 'toLocaleString', 515 | 'valueOf', 516 | 'hasOwnProperty', 517 | 'isPrototypeOf', 518 | 'propertyIsEnumerable', 519 | 'constructor' 520 | ]; 521 | 522 | _hasDontEnumBug = true; 523 | 524 | for (var key in {'toString': null}) { 525 | _hasDontEnumBug = false; 526 | } 527 | } 528 | 529 | /** 530 | * Similar to Array/forEach but works over object properties and fixes Don't 531 | * Enum bug on IE. 532 | * based on: http://whattheheadsaid.com/2010/10/a-safer-object-keys-compatibility-implementation 533 | */ 534 | function forIn(obj, fn, thisObj){ 535 | var key, i = 0; 536 | // no need to check if argument is a real object that way we can use 537 | // it for arrays, functions, date, etc. 538 | 539 | //post-pone check till needed 540 | if (_hasDontEnumBug == null) checkDontEnum(); 541 | 542 | for (key in obj) { 543 | if (exec(fn, obj, key, thisObj) === false) { 544 | break; 545 | } 546 | } 547 | 548 | 549 | if (_hasDontEnumBug) { 550 | var ctor = obj.constructor, 551 | isProto = !!ctor && obj === ctor.prototype; 552 | 553 | while (key = _dontEnums[i++]) { 554 | // For constructor, if it is a prototype object the constructor 555 | // is always non-enumerable unless defined otherwise (and 556 | // enumerated above). For non-prototype objects, it will have 557 | // to be defined on this object, since it cannot be defined on 558 | // any prototype objects. 559 | // 560 | // For other [[DontEnum]] properties, check if the value is 561 | // different than Object prototype value. 562 | if ( 563 | (key !== 'constructor' || 564 | (!isProto && hasOwn(obj, key))) && 565 | obj[key] !== Object.prototype[key] 566 | ) { 567 | if (exec(fn, obj, key, thisObj) === false) { 568 | break; 569 | } 570 | } 571 | } 572 | } 573 | } 574 | 575 | function exec(fn, obj, key, thisObj){ 576 | return fn.call(thisObj, obj[key], key, obj); 577 | } 578 | 579 | module.exports = forIn; 580 | 581 | 582 | 583 | 584 | /***/ }, 585 | /* 6 */ 586 | /***/ function(module, exports, __webpack_require__) { 587 | 588 | 589 | 590 | /** 591 | * Checks if the value is created by the `Object` constructor. 592 | */ 593 | function isPlainObject(value) { 594 | return (!!value && typeof value === 'object' && 595 | value.constructor === Object); 596 | } 597 | 598 | module.exports = isPlainObject; 599 | 600 | 601 | 602 | 603 | /***/ }, 604 | /* 7 */ 605 | /***/ function(module, exports, __webpack_require__) { 606 | 607 | var isKind = __webpack_require__(8); 608 | /** 609 | */ 610 | function isString(val) { 611 | return isKind(val, 'String'); 612 | } 613 | module.exports = isString; 614 | 615 | 616 | 617 | /***/ }, 618 | /* 8 */ 619 | /***/ function(module, exports, __webpack_require__) { 620 | 621 | var kindOf = __webpack_require__(9); 622 | /** 623 | * Check if value is from a specific "kind". 624 | */ 625 | function isKind(val, kind){ 626 | return kindOf(val) === kind; 627 | } 628 | module.exports = isKind; 629 | 630 | 631 | 632 | /***/ }, 633 | /* 9 */ 634 | /***/ function(module, exports, __webpack_require__) { 635 | 636 | 637 | 638 | var _rKind = /^\[object (.*)\]$/, 639 | _toString = Object.prototype.toString, 640 | UNDEF; 641 | 642 | /** 643 | * Gets the "kind" of value. (e.g. "String", "Number", etc) 644 | */ 645 | function kindOf(val) { 646 | if (val === null) { 647 | return 'Null'; 648 | } else if (val === UNDEF) { 649 | return 'Undefined'; 650 | } else { 651 | return _rKind.exec( _toString.call(val) )[1]; 652 | } 653 | } 654 | module.exports = kindOf; 655 | 656 | 657 | 658 | /***/ }, 659 | /* 10 */ 660 | /***/ function(module, exports, __webpack_require__) { 661 | 662 | var isKind = __webpack_require__(8); 663 | /** 664 | */ 665 | function isBoolean(val) { 666 | return isKind(val, 'Boolean'); 667 | } 668 | module.exports = isBoolean; 669 | 670 | 671 | 672 | /***/ }, 673 | /* 11 */ 674 | /***/ function(module, exports, __webpack_require__) { 675 | 676 | var isKind = __webpack_require__(8); 677 | /** 678 | */ 679 | function isNumber(val) { 680 | return isKind(val, 'Number'); 681 | } 682 | module.exports = isNumber; 683 | 684 | 685 | 686 | /***/ }, 687 | /* 12 */ 688 | /***/ function(module, exports, __webpack_require__) { 689 | 690 | var isKind = __webpack_require__(8); 691 | /** 692 | */ 693 | function isObject(val) { 694 | return isKind(val, 'Object'); 695 | } 696 | module.exports = isObject; 697 | 698 | 699 | 700 | /***/ }, 701 | /* 13 */ 702 | /***/ function(module, exports, __webpack_require__) { 703 | 704 | var isKind = __webpack_require__(8); 705 | /** 706 | */ 707 | function isDate(val) { 708 | return isKind(val, 'Date'); 709 | } 710 | module.exports = isDate; 711 | 712 | 713 | 714 | /***/ }, 715 | /* 14 */ 716 | /***/ function(module, exports, __webpack_require__) { 717 | 718 | var isKind = __webpack_require__(8); 719 | /** 720 | */ 721 | function isFunction(val) { 722 | return isKind(val, 'Function'); 723 | } 724 | module.exports = isFunction; 725 | 726 | 727 | 728 | /***/ }, 729 | /* 15 */ 730 | /***/ function(module, exports, __webpack_require__) { 731 | 732 | 733 | var UNDEF; 734 | 735 | /** 736 | */ 737 | function isUndef(val){ 738 | return val === UNDEF; 739 | } 740 | module.exports = isUndef; 741 | 742 | 743 | 744 | /***/ }, 745 | /* 16 */ 746 | /***/ function(module, exports, __webpack_require__) { 747 | 748 | var isKind = __webpack_require__(8); 749 | /** 750 | */ 751 | var isArray = Array.isArray || function (val) { 752 | return isKind(val, 'Array'); 753 | }; 754 | module.exports = isArray; 755 | 756 | 757 | 758 | /***/ }, 759 | /* 17 */ 760 | /***/ function(module, exports, __webpack_require__) { 761 | 762 | var forOwn = __webpack_require__(3); 763 | var isArray = __webpack_require__(16); 764 | 765 | function isEmpty(val){ 766 | if (val == null) { 767 | // typeof null == 'object' so we check it first 768 | return true; 769 | } else if ( typeof val === 'string' || isArray(val) ) { 770 | return !val.length; 771 | } else if ( typeof val === 'object' ) { 772 | var result = true; 773 | forOwn(val, function(){ 774 | result = false; 775 | return false; // break loop 776 | }); 777 | return result; 778 | } else { 779 | return true; 780 | } 781 | } 782 | 783 | module.exports = isEmpty; 784 | 785 | 786 | 787 | 788 | /***/ }, 789 | /* 18 */ 790 | /***/ function(module, exports, __webpack_require__) { 791 | 792 | 793 | 794 | /** 795 | * Typecast a value to a String, using an empty string value for null or 796 | * undefined. 797 | */ 798 | function toString(val){ 799 | return val == null ? '' : val.toString(); 800 | } 801 | 802 | module.exports = toString; 803 | 804 | 805 | 806 | 807 | /***/ }, 808 | /* 19 */ 809 | /***/ function(module, exports, __webpack_require__) { 810 | 811 | var isArray = __webpack_require__(16); 812 | 813 | /** 814 | * covert value into number if numeric 815 | */ 816 | function toNumber(val){ 817 | // numberic values should come first because of -0 818 | if (typeof val === 'number') return val; 819 | // we want all falsy values (besides -0) to return zero to avoid 820 | // headaches 821 | if (!val) return 0; 822 | if (typeof val === 'string') return parseFloat(val); 823 | // arrays are edge cases. `Number([4]) === 4` 824 | if (isArray(val)) return NaN; 825 | return Number(val); 826 | } 827 | 828 | module.exports = toNumber; 829 | 830 | 831 | 832 | 833 | /***/ }, 834 | /* 20 */ 835 | /***/ function(module, exports, __webpack_require__) { 836 | 837 | var isPrimitive = __webpack_require__(21); 838 | 839 | /** 840 | * get "nested" object property 841 | */ 842 | function get(obj, prop){ 843 | var parts = prop.split('.'), 844 | last = parts.pop(); 845 | 846 | while (prop = parts.shift()) { 847 | obj = obj[prop]; 848 | if (obj == null) return; 849 | } 850 | 851 | return obj[last]; 852 | } 853 | 854 | module.exports = get; 855 | 856 | 857 | 858 | 859 | /***/ }, 860 | /* 21 */ 861 | /***/ function(module, exports, __webpack_require__) { 862 | 863 | 864 | 865 | /** 866 | * Checks if the object is a primitive 867 | */ 868 | function isPrimitive(value) { 869 | // Using switch fallthrough because it's simple to read and is 870 | // generally fast: http://jsperf.com/testing-value-is-primitive/5 871 | switch (typeof value) { 872 | case "string": 873 | case "number": 874 | case "boolean": 875 | return true; 876 | } 877 | 878 | return value == null; 879 | } 880 | 881 | module.exports = isPrimitive; 882 | 883 | 884 | 885 | 886 | /***/ }, 887 | /* 22 */ 888 | /***/ function(module, exports, __webpack_require__) { 889 | 890 | var forOwn = __webpack_require__(3); 891 | var isPlainObject = __webpack_require__(6); 892 | 893 | /** 894 | * Mixes objects into the target object, recursively mixing existing child 895 | * objects. 896 | */ 897 | function deepMixIn(target, objects) { 898 | var i = 0, 899 | n = arguments.length, 900 | obj; 901 | 902 | while(++i < n){ 903 | obj = arguments[i]; 904 | if (obj) { 905 | forOwn(obj, copyProp, target); 906 | } 907 | } 908 | 909 | return target; 910 | } 911 | 912 | function copyProp(val, key) { 913 | var existing = this[key]; 914 | if (isPlainObject(val) && isPlainObject(existing)) { 915 | deepMixIn(existing, val); 916 | } else { 917 | this[key] = val; 918 | } 919 | } 920 | 921 | module.exports = deepMixIn; 922 | 923 | 924 | 925 | 926 | /***/ }, 927 | /* 23 */ 928 | /***/ function(module, exports, __webpack_require__) { 929 | 930 | var forOwn = __webpack_require__(3); 931 | 932 | /** 933 | * Get object keys 934 | */ 935 | var keys = Object.keys || function (obj) { 936 | var keys = []; 937 | forOwn(obj, function(val, key){ 938 | keys.push(key); 939 | }); 940 | return keys; 941 | }; 942 | 943 | module.exports = keys; 944 | 945 | 946 | 947 | 948 | /***/ }, 949 | /* 24 */ 950 | /***/ function(module, exports, __webpack_require__) { 951 | 952 | var slice = __webpack_require__(25); 953 | 954 | /** 955 | * Return a copy of the object, filtered to only have values for the whitelisted keys. 956 | */ 957 | function pick(obj, var_keys){ 958 | var keys = typeof arguments[1] !== 'string'? arguments[1] : slice(arguments, 1), 959 | out = {}, 960 | i = 0, key; 961 | while (key = keys[i++]) { 962 | out[key] = obj[key]; 963 | } 964 | return out; 965 | } 966 | 967 | module.exports = pick; 968 | 969 | 970 | 971 | 972 | /***/ }, 973 | /* 25 */ 974 | /***/ function(module, exports, __webpack_require__) { 975 | 976 | 977 | 978 | /** 979 | * Create slice of source array or array-like object 980 | */ 981 | function slice(arr, start, end){ 982 | var len = arr.length; 983 | 984 | if (start == null) { 985 | start = 0; 986 | } else if (start < 0) { 987 | start = Math.max(len + start, 0); 988 | } else { 989 | start = Math.min(start, len); 990 | } 991 | 992 | if (end == null) { 993 | end = len; 994 | } else if (end < 0) { 995 | end = Math.max(len + end, 0); 996 | } else { 997 | end = Math.min(end, len); 998 | } 999 | 1000 | var result = []; 1001 | while (start < end) { 1002 | result.push(arr[start++]); 1003 | } 1004 | 1005 | return result; 1006 | } 1007 | 1008 | module.exports = slice; 1009 | 1010 | 1011 | 1012 | 1013 | /***/ }, 1014 | /* 26 */ 1015 | /***/ function(module, exports, __webpack_require__) { 1016 | 1017 | var forOwn = __webpack_require__(3); 1018 | var makeIterator = __webpack_require__(27); 1019 | 1020 | /** 1021 | * Creates a new object with all the properties where the callback returns 1022 | * true. 1023 | */ 1024 | function filterValues(obj, callback, thisObj) { 1025 | callback = makeIterator(callback, thisObj); 1026 | var output = {}; 1027 | forOwn(obj, function(value, key, obj) { 1028 | if (callback(value, key, obj)) { 1029 | output[key] = value; 1030 | } 1031 | }); 1032 | 1033 | return output; 1034 | } 1035 | module.exports = filterValues; 1036 | 1037 | 1038 | 1039 | /***/ }, 1040 | /* 27 */ 1041 | /***/ function(module, exports, __webpack_require__) { 1042 | 1043 | var identity = __webpack_require__(28); 1044 | var prop = __webpack_require__(29); 1045 | var deepMatches = __webpack_require__(30); 1046 | 1047 | /** 1048 | * Converts argument into a valid iterator. 1049 | * Used internally on most array/object/collection methods that receives a 1050 | * callback/iterator providing a shortcut syntax. 1051 | */ 1052 | function makeIterator(src, thisObj){ 1053 | if (src == null) { 1054 | return identity; 1055 | } 1056 | switch(typeof src) { 1057 | case 'function': 1058 | // function is the first to improve perf (most common case) 1059 | // also avoid using `Function#call` if not needed, which boosts 1060 | // perf a lot in some cases 1061 | return (typeof thisObj !== 'undefined')? function(val, i, arr){ 1062 | return src.call(thisObj, val, i, arr); 1063 | } : src; 1064 | case 'object': 1065 | return function(val){ 1066 | return deepMatches(val, src); 1067 | }; 1068 | case 'string': 1069 | case 'number': 1070 | return prop(src); 1071 | } 1072 | } 1073 | 1074 | module.exports = makeIterator; 1075 | 1076 | 1077 | 1078 | 1079 | /***/ }, 1080 | /* 28 */ 1081 | /***/ function(module, exports, __webpack_require__) { 1082 | 1083 | 1084 | 1085 | /** 1086 | * Returns the first argument provided to it. 1087 | */ 1088 | function identity(val){ 1089 | return val; 1090 | } 1091 | 1092 | module.exports = identity; 1093 | 1094 | 1095 | 1096 | 1097 | /***/ }, 1098 | /* 29 */ 1099 | /***/ function(module, exports, __webpack_require__) { 1100 | 1101 | 1102 | 1103 | /** 1104 | * Returns a function that gets a property of the passed object 1105 | */ 1106 | function prop(name){ 1107 | return function(obj){ 1108 | return obj[name]; 1109 | }; 1110 | } 1111 | 1112 | module.exports = prop; 1113 | 1114 | 1115 | 1116 | 1117 | /***/ }, 1118 | /* 30 */ 1119 | /***/ function(module, exports, __webpack_require__) { 1120 | 1121 | var forOwn = __webpack_require__(3); 1122 | var isArray = __webpack_require__(16); 1123 | 1124 | function containsMatch(array, pattern) { 1125 | var i = -1, length = array.length; 1126 | while (++i < length) { 1127 | if (deepMatches(array[i], pattern)) { 1128 | return true; 1129 | } 1130 | } 1131 | 1132 | return false; 1133 | } 1134 | 1135 | function matchArray(target, pattern) { 1136 | var i = -1, patternLength = pattern.length; 1137 | while (++i < patternLength) { 1138 | if (!containsMatch(target, pattern[i])) { 1139 | return false; 1140 | } 1141 | } 1142 | 1143 | return true; 1144 | } 1145 | 1146 | function matchObject(target, pattern) { 1147 | var result = true; 1148 | forOwn(pattern, function(val, key) { 1149 | if (!deepMatches(target[key], val)) { 1150 | // Return false to break out of forOwn early 1151 | return (result = false); 1152 | } 1153 | }); 1154 | 1155 | return result; 1156 | } 1157 | 1158 | /** 1159 | * Recursively check if the objects match. 1160 | */ 1161 | function deepMatches(target, pattern){ 1162 | if (target && typeof target === 'object') { 1163 | if (isArray(target) && isArray(pattern)) { 1164 | return matchArray(target, pattern); 1165 | } else { 1166 | return matchObject(target, pattern); 1167 | } 1168 | } else { 1169 | return target === pattern; 1170 | } 1171 | } 1172 | 1173 | module.exports = deepMatches; 1174 | 1175 | 1176 | 1177 | 1178 | /***/ }, 1179 | /* 31 */ 1180 | /***/ function(module, exports, __webpack_require__) { 1181 | 1182 | var forOwn = __webpack_require__(3); 1183 | var makeIterator = __webpack_require__(27); 1184 | 1185 | /** 1186 | * Creates a new object where all the values are the result of calling 1187 | * `callback`. 1188 | */ 1189 | function mapValues(obj, callback, thisObj) { 1190 | callback = makeIterator(callback, thisObj); 1191 | var output = {}; 1192 | forOwn(obj, function(val, key, obj) { 1193 | output[key] = callback(val, key, obj); 1194 | }); 1195 | 1196 | return output; 1197 | } 1198 | module.exports = mapValues; 1199 | 1200 | 1201 | 1202 | /***/ }, 1203 | /* 32 */ 1204 | /***/ function(module, exports, __webpack_require__) { 1205 | 1206 | var hasOwn = __webpack_require__(4); 1207 | var deepClone = __webpack_require__(33); 1208 | var isObject = __webpack_require__(12); 1209 | 1210 | /** 1211 | * Deep merge objects. 1212 | */ 1213 | function merge() { 1214 | var i = 1, 1215 | key, val, obj, target; 1216 | 1217 | // make sure we don't modify source element and it's properties 1218 | // objects are passed by reference 1219 | target = deepClone( arguments[0] ); 1220 | 1221 | while (obj = arguments[i++]) { 1222 | for (key in obj) { 1223 | if ( ! hasOwn(obj, key) ) { 1224 | continue; 1225 | } 1226 | 1227 | val = obj[key]; 1228 | 1229 | if ( isObject(val) && isObject(target[key]) ){ 1230 | // inception, deep merge objects 1231 | target[key] = merge(target[key], val); 1232 | } else { 1233 | // make sure arrays, regexp, date, objects are cloned 1234 | target[key] = deepClone(val); 1235 | } 1236 | 1237 | } 1238 | } 1239 | 1240 | return target; 1241 | } 1242 | 1243 | module.exports = merge; 1244 | 1245 | 1246 | 1247 | 1248 | /***/ }, 1249 | /* 33 */ 1250 | /***/ function(module, exports, __webpack_require__) { 1251 | 1252 | var clone = __webpack_require__(34); 1253 | var forOwn = __webpack_require__(3); 1254 | var kindOf = __webpack_require__(9); 1255 | var isPlainObject = __webpack_require__(6); 1256 | 1257 | /** 1258 | * Recursively clone native types. 1259 | */ 1260 | function deepClone(val, instanceClone) { 1261 | switch ( kindOf(val) ) { 1262 | case 'Object': 1263 | return cloneObject(val, instanceClone); 1264 | case 'Array': 1265 | return cloneArray(val, instanceClone); 1266 | default: 1267 | return clone(val); 1268 | } 1269 | } 1270 | 1271 | function cloneObject(source, instanceClone) { 1272 | if (isPlainObject(source)) { 1273 | var out = {}; 1274 | forOwn(source, function(val, key) { 1275 | this[key] = deepClone(val, instanceClone); 1276 | }, out); 1277 | return out; 1278 | } else if (instanceClone) { 1279 | return instanceClone(source); 1280 | } else { 1281 | return source; 1282 | } 1283 | } 1284 | 1285 | function cloneArray(arr, instanceClone) { 1286 | var out = [], 1287 | i = -1, 1288 | n = arr.length, 1289 | val; 1290 | while (++i < n) { 1291 | out[i] = deepClone(arr[i], instanceClone); 1292 | } 1293 | return out; 1294 | } 1295 | 1296 | module.exports = deepClone; 1297 | 1298 | 1299 | 1300 | 1301 | 1302 | /***/ }, 1303 | /* 34 */ 1304 | /***/ function(module, exports, __webpack_require__) { 1305 | 1306 | var kindOf = __webpack_require__(9); 1307 | var isPlainObject = __webpack_require__(6); 1308 | var mixIn = __webpack_require__(35); 1309 | 1310 | /** 1311 | * Clone native types. 1312 | */ 1313 | function clone(val){ 1314 | switch (kindOf(val)) { 1315 | case 'Object': 1316 | return cloneObject(val); 1317 | case 'Array': 1318 | return cloneArray(val); 1319 | case 'RegExp': 1320 | return cloneRegExp(val); 1321 | case 'Date': 1322 | return cloneDate(val); 1323 | default: 1324 | return val; 1325 | } 1326 | } 1327 | 1328 | function cloneObject(source) { 1329 | if (isPlainObject(source)) { 1330 | return mixIn({}, source); 1331 | } else { 1332 | return source; 1333 | } 1334 | } 1335 | 1336 | function cloneRegExp(r) { 1337 | var flags = ''; 1338 | flags += r.multiline ? 'm' : ''; 1339 | flags += r.global ? 'g' : ''; 1340 | flags += r.ignoreCase ? 'i' : ''; 1341 | return new RegExp(r.source, flags); 1342 | } 1343 | 1344 | function cloneDate(date) { 1345 | return new Date(+date); 1346 | } 1347 | 1348 | function cloneArray(arr) { 1349 | return arr.slice(); 1350 | } 1351 | 1352 | module.exports = clone; 1353 | 1354 | 1355 | 1356 | 1357 | /***/ }, 1358 | /* 35 */ 1359 | /***/ function(module, exports, __webpack_require__) { 1360 | 1361 | var forOwn = __webpack_require__(3); 1362 | 1363 | /** 1364 | * Combine properties from all the objects into first one. 1365 | * - This method affects target object in place, if you want to create a new Object pass an empty object as first param. 1366 | * @param {object} target Target Object 1367 | * @param {...object} objects Objects to be combined (0...n objects). 1368 | * @return {object} Target Object. 1369 | */ 1370 | function mixIn(target, objects){ 1371 | var i = 0, 1372 | n = arguments.length, 1373 | obj; 1374 | while(++i < n){ 1375 | obj = arguments[i]; 1376 | if (obj != null) { 1377 | forOwn(obj, copyProp, target); 1378 | } 1379 | } 1380 | return target; 1381 | } 1382 | 1383 | function copyProp(val, key){ 1384 | this[key] = val; 1385 | } 1386 | 1387 | module.exports = mixIn; 1388 | 1389 | 1390 | 1391 | /***/ }, 1392 | /* 36 */ 1393 | /***/ function(module, exports, __webpack_require__) { 1394 | 1395 | var has = __webpack_require__(37); 1396 | 1397 | /** 1398 | * Unset object property. 1399 | */ 1400 | function unset(obj, prop){ 1401 | if (has(obj, prop)) { 1402 | var parts = prop.split('.'), 1403 | last = parts.pop(); 1404 | while (prop = parts.shift()) { 1405 | obj = obj[prop]; 1406 | } 1407 | return (delete obj[last]); 1408 | 1409 | } else { 1410 | // if property doesn't exist treat as deleted 1411 | return true; 1412 | } 1413 | } 1414 | 1415 | module.exports = unset; 1416 | 1417 | 1418 | 1419 | 1420 | /***/ }, 1421 | /* 37 */ 1422 | /***/ function(module, exports, __webpack_require__) { 1423 | 1424 | var get = __webpack_require__(20); 1425 | 1426 | var UNDEF; 1427 | 1428 | /** 1429 | * Check if object has nested property. 1430 | */ 1431 | function has(obj, prop){ 1432 | return get(obj, prop) !== UNDEF; 1433 | } 1434 | 1435 | module.exports = has; 1436 | 1437 | 1438 | 1439 | 1440 | 1441 | /***/ }, 1442 | /* 38 */ 1443 | /***/ function(module, exports, __webpack_require__) { 1444 | 1445 | var indexOf = __webpack_require__(39); 1446 | 1447 | /** 1448 | * If array contains values. 1449 | */ 1450 | function contains(arr, val) { 1451 | return indexOf(arr, val) !== -1; 1452 | } 1453 | module.exports = contains; 1454 | 1455 | 1456 | 1457 | /***/ }, 1458 | /* 39 */ 1459 | /***/ function(module, exports, __webpack_require__) { 1460 | 1461 | 1462 | 1463 | /** 1464 | * Array.indexOf 1465 | */ 1466 | function indexOf(arr, item, fromIndex) { 1467 | fromIndex = fromIndex || 0; 1468 | if (arr == null) { 1469 | return -1; 1470 | } 1471 | 1472 | var len = arr.length, 1473 | i = fromIndex < 0 ? len + fromIndex : fromIndex; 1474 | while (i < len) { 1475 | // we iterate over sparse items since there is no way to make it 1476 | // work properly on IE 7-8. see #64 1477 | if (arr[i] === item) { 1478 | return i; 1479 | } 1480 | 1481 | i++; 1482 | } 1483 | 1484 | return -1; 1485 | } 1486 | 1487 | module.exports = indexOf; 1488 | 1489 | 1490 | 1491 | /***/ }, 1492 | /* 40 */ 1493 | /***/ function(module, exports, __webpack_require__) { 1494 | 1495 | var unique = __webpack_require__(41); 1496 | var filter = __webpack_require__(42); 1497 | var every = __webpack_require__(43); 1498 | var contains = __webpack_require__(38); 1499 | var slice = __webpack_require__(25); 1500 | 1501 | 1502 | /** 1503 | * Return a new Array with elements common to all Arrays. 1504 | * - based on underscore.js implementation 1505 | */ 1506 | function intersection(arr) { 1507 | var arrs = slice(arguments, 1), 1508 | result = filter(unique(arr), function(needle){ 1509 | return every(arrs, function(haystack){ 1510 | return contains(haystack, needle); 1511 | }); 1512 | }); 1513 | return result; 1514 | } 1515 | 1516 | module.exports = intersection; 1517 | 1518 | 1519 | 1520 | 1521 | /***/ }, 1522 | /* 41 */ 1523 | /***/ function(module, exports, __webpack_require__) { 1524 | 1525 | var filter = __webpack_require__(42); 1526 | 1527 | /** 1528 | * @return {array} Array of unique items 1529 | */ 1530 | function unique(arr, compare){ 1531 | compare = compare || isEqual; 1532 | return filter(arr, function(item, i, arr){ 1533 | var n = arr.length; 1534 | while (++i < n) { 1535 | if ( compare(item, arr[i]) ) { 1536 | return false; 1537 | } 1538 | } 1539 | return true; 1540 | }); 1541 | } 1542 | 1543 | function isEqual(a, b){ 1544 | return a === b; 1545 | } 1546 | 1547 | module.exports = unique; 1548 | 1549 | 1550 | 1551 | 1552 | /***/ }, 1553 | /* 42 */ 1554 | /***/ function(module, exports, __webpack_require__) { 1555 | 1556 | var makeIterator = __webpack_require__(27); 1557 | 1558 | /** 1559 | * Array filter 1560 | */ 1561 | function filter(arr, callback, thisObj) { 1562 | callback = makeIterator(callback, thisObj); 1563 | var results = []; 1564 | if (arr == null) { 1565 | return results; 1566 | } 1567 | 1568 | var i = -1, len = arr.length, value; 1569 | while (++i < len) { 1570 | value = arr[i]; 1571 | if (callback(value, i, arr)) { 1572 | results.push(value); 1573 | } 1574 | } 1575 | 1576 | return results; 1577 | } 1578 | 1579 | module.exports = filter; 1580 | 1581 | 1582 | 1583 | 1584 | /***/ }, 1585 | /* 43 */ 1586 | /***/ function(module, exports, __webpack_require__) { 1587 | 1588 | var makeIterator = __webpack_require__(27); 1589 | 1590 | /** 1591 | * Array every 1592 | */ 1593 | function every(arr, callback, thisObj) { 1594 | callback = makeIterator(callback, thisObj); 1595 | var result = true; 1596 | if (arr == null) { 1597 | return result; 1598 | } 1599 | 1600 | var i = -1, len = arr.length; 1601 | while (++i < len) { 1602 | // we iterate over sparse items since there is no way to make it 1603 | // work properly on IE 7-8. see #64 1604 | if (!callback(arr[i], i, arr) ) { 1605 | result = false; 1606 | break; 1607 | } 1608 | } 1609 | 1610 | return result; 1611 | } 1612 | 1613 | module.exports = every; 1614 | 1615 | 1616 | 1617 | /***/ }, 1618 | /* 44 */ 1619 | /***/ function(module, exports, __webpack_require__) { 1620 | 1621 | var unique = __webpack_require__(41); 1622 | var filter = __webpack_require__(42); 1623 | var some = __webpack_require__(45); 1624 | var contains = __webpack_require__(38); 1625 | var slice = __webpack_require__(25); 1626 | 1627 | 1628 | /** 1629 | * Return a new Array with elements that aren't present in the other Arrays. 1630 | */ 1631 | function difference(arr) { 1632 | var arrs = slice(arguments, 1), 1633 | result = filter(unique(arr), function(needle){ 1634 | return !some(arrs, function(haystack){ 1635 | return contains(haystack, needle); 1636 | }); 1637 | }); 1638 | return result; 1639 | } 1640 | 1641 | module.exports = difference; 1642 | 1643 | 1644 | 1645 | 1646 | /***/ }, 1647 | /* 45 */ 1648 | /***/ function(module, exports, __webpack_require__) { 1649 | 1650 | var makeIterator = __webpack_require__(27); 1651 | 1652 | /** 1653 | * Array some 1654 | */ 1655 | function some(arr, callback, thisObj) { 1656 | callback = makeIterator(callback, thisObj); 1657 | var result = false; 1658 | if (arr == null) { 1659 | return result; 1660 | } 1661 | 1662 | var i = -1, len = arr.length; 1663 | while (++i < len) { 1664 | // we iterate over sparse items since there is no way to make it 1665 | // work properly on IE 7-8. see #64 1666 | if ( callback(arr[i], i, arr) ) { 1667 | result = true; 1668 | break; 1669 | } 1670 | } 1671 | 1672 | return result; 1673 | } 1674 | 1675 | module.exports = some; 1676 | 1677 | 1678 | 1679 | /***/ }, 1680 | /* 46 */ 1681 | /***/ function(module, exports, __webpack_require__) { 1682 | 1683 | 1684 | 1685 | /** 1686 | * "Convert" value into an 32-bit integer. 1687 | * Works like `Math.floor` if val > 0 and `Math.ceil` if val < 0. 1688 | * IMPORTANT: val will wrap at 2^31 and -2^31. 1689 | * Perf tests: http://jsperf.com/vs-vs-parseint-bitwise-operators/7 1690 | */ 1691 | function toInt(val){ 1692 | // we do not use lang/toNumber because of perf and also because it 1693 | // doesn't break the functionality 1694 | return ~~val; 1695 | } 1696 | 1697 | module.exports = toInt; 1698 | 1699 | 1700 | 1701 | 1702 | /***/ }, 1703 | /* 47 */ 1704 | /***/ function(module, exports, __webpack_require__) { 1705 | 1706 | Object.defineProperty(exports, '__esModule', { 1707 | value: true 1708 | }); 1709 | 1710 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 1711 | 1712 | var _utils = __webpack_require__(1); 1713 | 1714 | var _utils2 = _interopRequireDefault(_utils); 1715 | 1716 | exports['default'] = { 1717 | string: function string(x) { 1718 | return _utils2['default'].isString(x) ? null : _utils2['default'].errMsg('type', typeof x, 'string'); 1719 | }, 1720 | number: function number(x) { 1721 | return _utils2['default'].isNumber(x) ? null : _utils2['default'].errMsg('type', typeof x, 'number'); 1722 | }, 1723 | integer: function integer(x) { 1724 | if (!_utils2['default'].isNumber(x)) { 1725 | return _utils2['default'].errMsg('type', typeof x, 'integer'); 1726 | } else if (Math.abs(x) - Math.abs(_utils2['default'].toInt(x)) !== 0) { 1727 | return _utils2['default'].errMsg('type', 'real', 'integer'); 1728 | } else { 1729 | return null; 1730 | } 1731 | }, 1732 | float: function float(x) { 1733 | return _utils2['default'].isNumber(x) ? null : _utils2['default'].errMsg('type', typeof x, 'float'); 1734 | }, 1735 | array: function array(x) { 1736 | return _utils2['default'].isArray(x) ? null : _utils2['default'].errMsg('type', typeof x, 'array'); 1737 | }, 1738 | object: function object(x) { 1739 | return _utils2['default'].isObject(x) ? null : _utils2['default'].errMsg('type', typeof x, 'object'); 1740 | }, 1741 | boolean: function boolean(x) { 1742 | return _utils2['default'].isBoolean(x) ? null : _utils2['default'].errMsg('type', typeof x, 'boolean'); 1743 | }, 1744 | date: function date(x) { 1745 | return _utils2['default'].isDate(x) ? null : _utils2['default'].errMsg('type', typeof x, 'date'); 1746 | } 1747 | }; 1748 | module.exports = exports['default']; 1749 | 1750 | /***/ }, 1751 | /* 48 */ 1752 | /***/ function(module, exports, __webpack_require__) { 1753 | 1754 | Object.defineProperty(exports, '__esModule', { 1755 | value: true 1756 | }); 1757 | 1758 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 1759 | 1760 | var _utils = __webpack_require__(1); 1761 | 1762 | var _utils2 = _interopRequireDefault(_utils); 1763 | 1764 | var _dataTypes = __webpack_require__(47); 1765 | 1766 | var _dataTypes2 = _interopRequireDefault(_dataTypes); 1767 | 1768 | exports['default'] = { 1769 | nullable: function nullable(x, _nullable) { 1770 | return (x === null || x === undefined) && !_nullable ? _utils2['default'].errMsg('nullable', 'x === ' + x, 'x !== null && x !== undefined') : null; 1771 | }, 1772 | max: function max(x, _max) { 1773 | return _utils2['default'].isNumber(x) && _utils2['default'].isNumber(_max) && x > _max ? _utils2['default'].errMsg('max', '' + x + ' > ' + _max, '' + x + ' <= ' + _max) : null; 1774 | }, 1775 | min: function min(x, _min) { 1776 | return _utils2['default'].isNumber(x) && _utils2['default'].isNumber(_min) && x < _min ? _utils2['default'].errMsg('min', '' + x + ' < ' + _min, '' + x + ' >= ' + _min) : null; 1777 | }, 1778 | maxLength: function maxLength(x, _maxLength) { 1779 | return (_utils2['default'].isString(x) || _utils2['default'].isArray(x)) && _utils2['default'].isNumber(_maxLength) && x.length > _maxLength ? _utils2['default'].errMsg('maxLength', '' + x.length + ' > ' + _maxLength, '' + x.length + ' <= ' + _maxLength) : null; 1780 | }, 1781 | minLength: function minLength(x, _minLength) { 1782 | return (_utils2['default'].isString(x) || _utils2['default'].isArray(x)) && _utils2['default'].isNumber(_minLength) && x.length < _minLength ? _utils2['default'].errMsg('minLength', '' + x.length + ' < ' + _minLength, '' + x.length + ' >= ' + _minLength) : null; 1783 | }, 1784 | type: function type(x, _type, customType, parent) { 1785 | return customType ? customType(x) : parent.dataTypes[_type] ? parent.dataTypes[_type](x) : _dataTypes2['default'][_type] ? _dataTypes2['default'][_type](x) : null; 1786 | } 1787 | }; 1788 | module.exports = exports['default']; 1789 | 1790 | /***/ }, 1791 | /* 49 */ 1792 | /***/ function(module, exports, __webpack_require__) { 1793 | 1794 | Object.defineProperty(exports, '__esModule', { 1795 | value: true 1796 | }); 1797 | 1798 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 1799 | 1800 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 1801 | 1802 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 1803 | 1804 | /*jshint latedef:false*/ 1805 | 1806 | var _utils = __webpack_require__(1); 1807 | 1808 | var _utils2 = _interopRequireDefault(_utils); 1809 | 1810 | var _rules = __webpack_require__(48); 1811 | 1812 | var _rules2 = _interopRequireDefault(_rules); 1813 | 1814 | var hasObject = function hasObject(v) { 1815 | var has = false; 1816 | _utils2['default'].forOwn(v, function (_v) { 1817 | if (_utils2['default'].isObject(_v)) { 1818 | has = true; 1819 | return false; 1820 | } 1821 | }); 1822 | return has; 1823 | }; 1824 | 1825 | function _executeRulesSync(targetKey, options, errors, value, key) { 1826 | var _this = this; 1827 | 1828 | var nestedKey = targetKey + (targetKey.length ? '.' : '') + key; 1829 | var schemaRules = _utils2['default'].get(this.schema, nestedKey); 1830 | 1831 | if (!schemaRules) { 1832 | return; 1833 | } else if (_utils2['default'].isObject(value) || hasObject(schemaRules)) { 1834 | var err = _validateSync.apply(this, [nestedKey, value || {}, options]); 1835 | if (err) { 1836 | errors[key] = err; 1837 | } 1838 | } else { 1839 | if (!_utils2['default'].isObject(schemaRules)) { 1840 | return; 1841 | } else if (schemaRules.nullable === true) { 1842 | var nullable = this.parent.rules.nullable || _rules2['default'].nullable; 1843 | var nErr = nullable.call(options.ctx, value, true, undefined, this.parent); 1844 | 1845 | if (nErr === null) { 1846 | return; 1847 | } 1848 | } 1849 | _utils2['default'].forOwn(schemaRules, function (ruleValue, ruleKey) { 1850 | var rule = _this.parent.rules[ruleKey] || _rules2['default'][ruleKey]; 1851 | if (rule && !rule.async) { 1852 | var err = rule.call(options.ctx, value, ruleValue, undefined, _this.parent); 1853 | if (err) { 1854 | if (!errors[key]) { 1855 | errors[key] = { 1856 | errors: [] 1857 | }; 1858 | } 1859 | errors[key].errors.push(err); 1860 | } 1861 | } 1862 | }); 1863 | } 1864 | } 1865 | 1866 | /** 1867 | * @see Schema#validateSync 1868 | */ 1869 | function _validateSync(targetKey, attrs, options) { 1870 | var _this2 = this; 1871 | 1872 | var errors = {}; 1873 | 1874 | try { 1875 | // Validate present attributes 1876 | _utils2['default'].forOwn(attrs, function (value, key) { 1877 | _executeRulesSync.call(_this2, targetKey, options, errors, value, key); 1878 | }); 1879 | // Validate missing attributes 1880 | if (!options.ignoreMissing) { 1881 | var schema = targetKey ? _utils2['default'].get(this.schema, targetKey) || {} : this.schema; 1882 | var missing = _utils2['default'].difference(_utils2['default'].keys(schema), _utils2['default'].keys(attrs)); 1883 | missing = _utils2['default'].pick(this.schema, missing); 1884 | missing = _utils2['default'].map(missing, function () { 1885 | return undefined; 1886 | }); 1887 | _utils2['default'].forOwn(missing, function (value, key) { 1888 | _executeRulesSync.call(_this2, targetKey, options, errors, value, key); 1889 | }); 1890 | } 1891 | if (!_utils2['default'].isEmpty(errors)) { 1892 | return errors; 1893 | } else { 1894 | return null; 1895 | } 1896 | } catch (err) { 1897 | return err; 1898 | } 1899 | } 1900 | 1901 | function _executeRules(options, value, key, prefix, errors, deepQueue, ruleQueue) { 1902 | var _this3 = this; 1903 | 1904 | var nestedKey = prefix + key; 1905 | var schemaRules = _utils2['default'].get(this.schema, nestedKey); 1906 | 1907 | if (!schemaRules) { 1908 | return; 1909 | } else if (_utils2['default'].isObject(value) || hasObject(schemaRules)) { 1910 | // Recurse down into nested attributes 1911 | deepQueue[key] = (function (nK, val) { 1912 | return function (next) { 1913 | _validate.apply(_this3, [nK, val || {}, options, next]); 1914 | }; 1915 | })(nestedKey, value); 1916 | } else { 1917 | if (!_utils2['default'].isObject(schemaRules)) { 1918 | return; 1919 | } else if (schemaRules.nullable === true) { 1920 | var nullable = this.parent.rules.nullable || _rules2['default'].nullable; 1921 | var nErr = nullable.call(options.ctx, value, true, undefined, this.parent); 1922 | 1923 | if (nErr === null) { 1924 | return; 1925 | } 1926 | } 1927 | _utils2['default'].forOwn(schemaRules, function (ruleValue, ruleKey) { 1928 | var rule = _this3.parent.rules[ruleKey] || _rules2['default'][ruleKey]; 1929 | // Asynchronous rules get added to the queue 1930 | if (rule && rule.async) { 1931 | ruleQueue['' + ruleKey + '_' + ruleValue] = (function (r, key, val, rVal) { 1932 | return function (next) { 1933 | r.call(options.ctx, val, rVal, function (err) { 1934 | next(null, { err: err, key: key }); 1935 | }); 1936 | }; 1937 | })(rule, key, value, ruleValue); 1938 | } else { 1939 | // Get results of synchronous rules immediately 1940 | var err = rule.call(options.ctx, value, ruleValue, undefined, _this3.parent); 1941 | if (err) { 1942 | if (!errors[key]) { 1943 | errors[key] = { 1944 | errors: [] 1945 | }; 1946 | } 1947 | errors[key].errors.push(err); 1948 | } 1949 | } 1950 | }); 1951 | } 1952 | } 1953 | 1954 | /** 1955 | * @see Schema#validate 1956 | */ 1957 | function _validate(targetKey, attrs, options, cb) { 1958 | var _this4 = this; 1959 | 1960 | var errors = {}; 1961 | var prefix = targetKey + (targetKey.length ? '.' : ''); 1962 | var deepQueue = {}; 1963 | var ruleQueue = {}; 1964 | var first = options.first; 1965 | 1966 | delete options.first; 1967 | 1968 | _utils2['default'].forOwn(attrs, function (value, key) { 1969 | _executeRules.call(_this4, options, value, key, prefix, errors, deepQueue, ruleQueue); 1970 | }); 1971 | 1972 | // Validate missing attributes 1973 | if (!options.ignoreMissing) { 1974 | var schema = targetKey ? _utils2['default'].get(this.schema, targetKey) || {} : this.schema; 1975 | var missing = _utils2['default'].difference(_utils2['default'].keys(schema), _utils2['default'].keys(attrs)); 1976 | 1977 | missing = _utils2['default'].pick(this.schema, missing); 1978 | missing = _utils2['default'].map(missing, function () { 1979 | return undefined; 1980 | }); 1981 | 1982 | _utils2['default'].forOwn(missing, function (value, key) { 1983 | _executeRules.call(_this4, options, value, key, prefix, errors, deepQueue, ruleQueue); 1984 | }); 1985 | } 1986 | 1987 | var finalQueue = {}; 1988 | 1989 | if (!_utils2['default'].isEmpty(deepQueue)) { 1990 | finalQueue.deepQueue = function (next) { 1991 | _utils2['default'].parallel(deepQueue, next); 1992 | }; 1993 | } 1994 | if (!_utils2['default'].isEmpty(ruleQueue)) { 1995 | finalQueue.ruleQueue = function (next) { 1996 | _utils2['default'].parallel(ruleQueue, next); 1997 | }; 1998 | } 1999 | 2000 | if (!_utils2['default'].isEmpty(finalQueue)) { 2001 | _utils2['default'].parallel(finalQueue, function (err, results) { 2002 | 2003 | // Merge results of recursion 2004 | if (results.deepQueue) { 2005 | results.deepQueue = _utils2['default'].filter(results.deepQueue, function (x) { 2006 | return x !== undefined && x !== null; 2007 | }); 2008 | _utils2['default'].deepMixIn(errors, results.deepQueue); 2009 | } 2010 | 2011 | // Merge results of asynchronous rules 2012 | if (results.ruleQueue) { 2013 | if (results.ruleQueue) { 2014 | results.ruleQueue = _utils2['default'].filter(results.ruleQueue, function (x) { 2015 | return x.err !== undefined && x.err !== null; 2016 | }); 2017 | } 2018 | _utils2['default'].forOwn(results.ruleQueue, function (value) { 2019 | if (!errors[value.key]) { 2020 | errors[value.key] = { 2021 | errors: [] 2022 | }; 2023 | } 2024 | errors[value.key].errors.push(value.err); 2025 | }); 2026 | } 2027 | 2028 | if (!_utils2['default'].isEmpty(errors)) { 2029 | first ? cb(errors) : cb(null, errors); 2030 | } else { 2031 | cb(null); 2032 | } 2033 | }); 2034 | } else { 2035 | if (!_utils2['default'].isEmpty(errors)) { 2036 | first ? cb(errors) : cb(null, errors); 2037 | } else { 2038 | cb(null); 2039 | } 2040 | } 2041 | } 2042 | 2043 | function _validateSchema(attrs, rules) { 2044 | rules = rules || []; 2045 | var keys = _utils2['default'].keys(attrs); 2046 | var noRules = _utils2['default'].intersection(keys, rules).length === 0; 2047 | 2048 | _utils2['default'].forOwn(attrs, function (value, key) { 2049 | if (noRules && _utils2['default'].isString(value)) { 2050 | attrs[key] = { 2051 | type: value 2052 | }; 2053 | } else if (_utils2['default'].isObject(value)) { 2054 | _validateSchema(value, rules); 2055 | } 2056 | }); 2057 | } 2058 | 2059 | var errors = { 2060 | a: 'Schema#validateSync(attrs[, options]): ', 2061 | b: 'Schema#validate(attrs[, options], cb): ' 2062 | }; 2063 | 2064 | var Schema = (function () { 2065 | function Schema(name, schema, parent) { 2066 | _classCallCheck(this, Schema); 2067 | 2068 | if (!_utils2['default'].isString(name)) { 2069 | throw new Error('"name" must be a string!'); 2070 | } else if (!_utils2['default'].isObject(schema)) { 2071 | throw new Error('"schema" must be an object!'); 2072 | } 2073 | this.name = name; 2074 | _validateSchema(schema, parent ? parent.availableRules() : _rules2['default']); 2075 | this.schema = schema; 2076 | } 2077 | 2078 | _createClass(Schema, [{ 2079 | key: 'validateSync', 2080 | value: function validateSync(attrs, options) { 2081 | options = options ? options === true ? { ignoreMissing: true } : options : {}; 2082 | if (!_utils2['default'].isObject(attrs)) { 2083 | throw new Error('' + errors.a + 'attrs: Must be an object!'); 2084 | } else if (!_utils2['default'].isObject(options)) { 2085 | throw new Error('' + errors.a + 'options: Must be an object!'); 2086 | } 2087 | options.ctx = attrs; 2088 | return _validateSync.call(this, '', attrs, options); 2089 | } 2090 | }, { 2091 | key: 'validate', 2092 | value: function validate(attrs, options, cb) { 2093 | options = options ? options === true ? { ignoreMissing: true } : options : {}; 2094 | if (_utils2['default'].isFunction(options)) { 2095 | cb = options; 2096 | options = {}; 2097 | } 2098 | if (!_utils2['default'].isFunction(cb)) { 2099 | throw new Error('' + errors.b + 'cb: Must be a function!'); 2100 | } else if (!_utils2['default'].isObject(attrs)) { 2101 | return cb(new Error('' + errors.b + 'attrs: Must be an object!')); 2102 | } else if (!_utils2['default'].isObject(options)) { 2103 | return cb(new Error('' + errors.b + 'options: Must be an object!')); 2104 | } 2105 | options.first = true; 2106 | options.ctx = attrs; 2107 | _validate.call(this, '', attrs, options, cb); 2108 | } 2109 | }, { 2110 | key: 'addDefaultsToTarget', 2111 | value: function addDefaultsToTarget(target, overwrite) { 2112 | if (!_utils2['default'].isObject(target)) { 2113 | throw new Error('"target" must be an object!'); 2114 | } else if (!this.defaults) { 2115 | throw new Error('No defaults have been set!'); 2116 | } else if (overwrite) { 2117 | _utils2['default'].deepMixIn(target, this.defaults); 2118 | } else { 2119 | _utils2['default'].deepFillIn(target, this.defaults); 2120 | } 2121 | } 2122 | }, { 2123 | key: 'setDefaults', 2124 | value: function setDefaults(attrs) { 2125 | if (!_utils2['default'].isObject(attrs)) { 2126 | throw new Error('Schema#defaults(attrs): attrs: Must be an object!'); 2127 | } else { 2128 | this.defaults = _utils2['default'].merge({}, attrs); 2129 | } 2130 | return this; 2131 | } 2132 | }, { 2133 | key: 'getDefaults', 2134 | value: function getDefaults() { 2135 | return _utils2['default'].merge({}, this.defaults); 2136 | } 2137 | }, { 2138 | key: 'stripNonSchemaAttrs', 2139 | value: function stripNonSchemaAttrs(attrs) { 2140 | _stripNonSchemaAttrs(attrs, this.schema); 2141 | return attrs; 2142 | } 2143 | }]); 2144 | 2145 | return Schema; 2146 | })(); 2147 | 2148 | function _stripNonSchemaAttrs(attrs, schemaLevel) { 2149 | _utils2['default'].forOwn(attrs, function (value, key) { 2150 | if (schemaLevel[key]) { 2151 | if (_utils2['default'].isObject(value) && _utils2['default'].isObject(schemaLevel[key])) { 2152 | _stripNonSchemaAttrs(value, schemaLevel[key]); 2153 | } 2154 | } else { 2155 | _utils2['default'].unset(attrs, key); 2156 | } 2157 | }); 2158 | } 2159 | 2160 | exports['default'] = Schema; 2161 | module.exports = exports['default']; 2162 | 2163 | /***/ } 2164 | /******/ ]) 2165 | }); 2166 | ; -------------------------------------------------------------------------------- /dist/js-data-schema.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * js-data-schema 3 | * @version 1.2.5 - Homepage 4 | * @author Jason Dobry 5 | * @copyright (c) 2013-2014 Jason Dobry 6 | * @license MIT 7 | * 8 | * @overview Define and validate rules, datatypes and schemata in Node and in the browser. 9 | */ 10 | 11 | !function(a,b){"object"==typeof exports&&"object"==typeof module?module.exports=b():"function"==typeof define&&define.amd?define(b):"object"==typeof exports?exports.Schemator=b():a.Schemator=b()}(this,function(){return function(a){function b(d){if(c[d])return c[d].exports;var e=c[d]={exports:{},id:d,loaded:!1};return a[d].call(e.exports,e,e.exports,b),e.loaded=!0,e.exports}var c={};return b.m=a,b.c=c,b.p="",b(0)}([function(a,b,c){function d(a){return a&&a.__esModule?a:{"default":a}}function e(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(b,"__esModule",{value:!0});var f=function(){function a(a,b){for(var c=0;c=f)&&b(a,d)}var d={},e=0,f=0;H["default"](a,function(){f+=1}),H["default"](a,function(a,b){a(function(a){var e=Array.prototype.slice.call(arguments,1);e.length<=1&&(e=e[0]),d[b]=e,c(a)})})}},a.exports=b["default"]},function(a,b,c){function d(a,b){for(var c,g=0,h=arguments.length;++gb?Math.max(d+b,0):Math.min(b,d),c=null==c?d:0>c?Math.max(d+c,0):Math.min(c,d);for(var e=[];c>b;)e.push(a[b++]);return e}a.exports=d},function(a,b,c){function d(a,b,c){b=f(b,c);var d={};return e(a,function(a,c,e){b(a,c,e)&&(d[c]=a)}),d}var e=c(3),f=c(27);a.exports=d},function(a,b,c){function d(a,b){if(null==a)return e;switch(typeof a){case"function":return"undefined"!=typeof b?function(c,d,e){return a.call(b,c,d,e)}:a;case"object":return function(b){return g(b,a)};case"string":case"number":return f(a)}}var e=c(28),f=c(29),g=c(30);a.exports=d},function(a,b,c){function d(a){return a}a.exports=d},function(a,b,c){function d(a){return function(b){return b[a]}}a.exports=d},function(a,b,c){function d(a,b){for(var c=-1,d=a.length;++cc?d+c:c;d>e;){if(a[e]===b)return e;e++}return-1}a.exports=d},function(a,b,c){function d(a){var b=i(arguments,1),c=f(e(a),function(a){return g(b,function(b){return h(b,a)})});return c}var e=c(41),f=c(42),g=c(43),h=c(38),i=c(25);a.exports=d},function(a,b,c){function d(a,b){return b=b||e,f(a,function(a,c,d){for(var e=d.length;++cb?f["default"].errMsg("max",""+a+" > "+b,""+a+" <= "+b):null},min:function(a,b){return f["default"].isNumber(a)&&f["default"].isNumber(b)&&b>a?f["default"].errMsg("min",""+a+" < "+b,""+a+" >= "+b):null},maxLength:function(a,b){return(f["default"].isString(a)||f["default"].isArray(a))&&f["default"].isNumber(b)&&a.length>b?f["default"].errMsg("maxLength",""+a.length+" > "+b,""+a.length+" <= "+b):null},minLength:function(a,b){return(f["default"].isString(a)||f["default"].isArray(a))&&f["default"].isNumber(b)&&a.length= "+b):null},type:function(a,b,c,d){return c?c(a):d.dataTypes[b]?d.dataTypes[b](a):h["default"][b]?h["default"][b](a):null}},a.exports=b["default"]},function(a,b,c){function d(a){return a&&a.__esModule?a:{"default":a}}function e(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}function f(a,b,c,d,e){var f=this,h=a+(a.length?".":"")+e,i=n["default"].get(this.schema,h);if(i)if(n["default"].isObject(d)||q(i)){var j=g.apply(this,[h,d||{},b]);j&&(c[e]=j)}else{if(!n["default"].isObject(i))return;if(i.nullable===!0){var k=this.parent.rules.nullable||p["default"].nullable,l=k.call(b.ctx,d,!0,void 0,this.parent);if(null===l)return}n["default"].forOwn(i,function(a,g){var h=f.parent.rules[g]||p["default"][g];if(h&&!h.async){var i=h.call(b.ctx,d,a,void 0,f.parent);i&&(c[e]||(c[e]={errors:[]}),c[e].errors.push(i))}})}}function g(a,b,c){var d=this,e={};try{if(n["default"].forOwn(b,function(b,g){f.call(d,a,c,e,b,g)}),!c.ignoreMissing){var g=a?n["default"].get(this.schema,a)||{}:this.schema,h=n["default"].difference(n["default"].keys(g),n["default"].keys(b));h=n["default"].pick(this.schema,h),h=n["default"].map(h,function(){return void 0}),n["default"].forOwn(h,function(b,g){f.call(d,a,c,e,b,g)})}return n["default"].isEmpty(e)?null:e}catch(i){return i}}function h(a,b,c,d,e,f,g){var h=this,j=d+c,k=n["default"].get(this.schema,j);if(k)if(n["default"].isObject(b)||q(k))f[c]=function(b,c){return function(d){i.apply(h,[b,c||{},a,d])}}(j,b);else{if(!n["default"].isObject(k))return;if(k.nullable===!0){var l=this.parent.rules.nullable||p["default"].nullable,m=l.call(a.ctx,b,!0,void 0,this.parent);if(null===m)return}n["default"].forOwn(k,function(d,f){var i=h.parent.rules[f]||p["default"][f];if(i&&i.async)g[""+f+"_"+d]=function(b,c,d,e){return function(f){b.call(a.ctx,d,e,function(a){f(null,{err:a,key:c})})}}(i,c,b,d);else{var j=i.call(a.ctx,b,d,void 0,h.parent);j&&(e[c]||(e[c]={errors:[]}),e[c].errors.push(j))}})}}function i(a,b,c,d){var e=this,f={},g=a+(a.length?".":""),i={},j={},k=c.first;if(delete c.first,n["default"].forOwn(b,function(a,b){h.call(e,c,a,b,g,f,i,j)}),!c.ignoreMissing){var l=a?n["default"].get(this.schema,a)||{}:this.schema,m=n["default"].difference(n["default"].keys(l),n["default"].keys(b));m=n["default"].pick(this.schema,m),m=n["default"].map(m,function(){return void 0}),n["default"].forOwn(m,function(a,b){h.call(e,c,a,b,g,f,i,j)})}var o={};n["default"].isEmpty(i)||(o.deepQueue=function(a){n["default"].parallel(i,a)}),n["default"].isEmpty(j)||(o.ruleQueue=function(a){n["default"].parallel(j,a)}),n["default"].isEmpty(o)?n["default"].isEmpty(f)?d(null):k?d(f):d(null,f):n["default"].parallel(o,function(a,b){b.deepQueue&&(b.deepQueue=n["default"].filter(b.deepQueue,function(a){return void 0!==a&&null!==a}),n["default"].deepMixIn(f,b.deepQueue)),b.ruleQueue&&(b.ruleQueue&&(b.ruleQueue=n["default"].filter(b.ruleQueue,function(a){return void 0!==a.err&&null!==a.err})),n["default"].forOwn(b.ruleQueue,function(a){f[a.key]||(f[a.key]={errors:[]}),f[a.key].errors.push(a.err)})),n["default"].isEmpty(f)?d(null):k?d(f):d(null,f)})}function j(a,b){b=b||[];var c=n["default"].keys(a),d=0===n["default"].intersection(c,b).length;n["default"].forOwn(a,function(c,e){d&&n["default"].isString(c)?a[e]={type:c}:n["default"].isObject(c)&&j(c,b)})}function k(a,b){n["default"].forOwn(a,function(c,d){b[d]?n["default"].isObject(c)&&n["default"].isObject(b[d])&&k(c,b[d]):n["default"].unset(a,d)})}Object.defineProperty(b,"__esModule",{value:!0});var l=function(){function a(a,b){for(var c=0;c max ? utils.errMsg('max', `${x} > ${max}`, `${x} <= ${max}`) : null; 10 | }, 11 | min(x, min) { 12 | return utils.isNumber(x) && utils.isNumber(min) && x < min ? utils.errMsg('min', `${x} < ${min}`, `${x} >= ${min}`) : null; 13 | }, 14 | maxLength(x, maxLength) { 15 | return (utils.isString(x) || utils.isArray(x)) && utils.isNumber(maxLength) && x.length > maxLength ? utils.errMsg('maxLength', `${x.length} > ${maxLength}`, `${x.length} <= ${maxLength}`) : null; 16 | }, 17 | minLength(x, minLength) { 18 | return (utils.isString(x) || utils.isArray(x)) && utils.isNumber(minLength) && x.length < minLength ? utils.errMsg('minLength', `${x.length} < ${minLength}`, `${x.length} >= ${minLength}`) : null; 19 | }, 20 | type(x, type, customType, parent) { 21 | return customType ? customType(x) : parent.dataTypes[type] ? parent.dataTypes[type](x) : defaultDataTypes[type] ? defaultDataTypes[type](x) : null; 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /lib/schema.js: -------------------------------------------------------------------------------- 1 | /*jshint latedef:false*/ 2 | import utils from './utils'; 3 | import defaultRules from './rules'; 4 | 5 | let hasObject = v => { 6 | var has = false; 7 | utils.forOwn(v, _v => { 8 | if (utils.isObject(_v)) { 9 | has = true; 10 | return false; 11 | } 12 | }); 13 | return has; 14 | }; 15 | 16 | function _executeRulesSync(targetKey, options, errors, value, key) { 17 | let nestedKey = targetKey + (targetKey.length ? '.' : '') + key; 18 | let schemaRules = utils.get(this.schema, nestedKey); 19 | 20 | if (!schemaRules) { 21 | return; 22 | } else if (utils.isObject(value) || hasObject(schemaRules)) { 23 | let err = _validateSync.apply(this, [nestedKey, value || {}, options]); 24 | if (err) { 25 | errors[key] = err; 26 | } 27 | } else { 28 | if (!utils.isObject(schemaRules)) { 29 | return; 30 | } else if (schemaRules.nullable === true) { 31 | let nullable = this.parent.rules.nullable || defaultRules.nullable; 32 | let nErr = nullable.call(options.ctx, value, true, undefined, this.parent); 33 | 34 | if (nErr === null) { 35 | return; 36 | } 37 | } 38 | utils.forOwn(schemaRules, (ruleValue, ruleKey) => { 39 | let rule = this.parent.rules[ruleKey] || defaultRules[ruleKey]; 40 | if (rule && !rule.async) { 41 | let err = rule.call(options.ctx, value, ruleValue, undefined, this.parent); 42 | if (err) { 43 | if (!errors[key]) { 44 | errors[key] = { 45 | errors: [] 46 | }; 47 | } 48 | errors[key].errors.push(err); 49 | } 50 | } 51 | }); 52 | } 53 | } 54 | 55 | /** 56 | * @see Schema#validateSync 57 | */ 58 | function _validateSync(targetKey, attrs, options) { 59 | let errors = {}; 60 | 61 | try { 62 | // Validate present attributes 63 | utils.forOwn(attrs, (value, key) => { 64 | _executeRulesSync.call(this, targetKey, options, errors, value, key); 65 | }); 66 | // Validate missing attributes 67 | if (!options.ignoreMissing) { 68 | let schema = targetKey ? utils.get(this.schema, targetKey) || {} : this.schema; 69 | let missing = utils.difference(utils.keys(schema), utils.keys(attrs)); 70 | missing = utils.pick(this.schema, missing); 71 | missing = utils.map(missing, () => undefined); 72 | utils.forOwn(missing, (value, key) => { 73 | _executeRulesSync.call(this, targetKey, options, errors, value, key); 74 | }); 75 | } 76 | if (!utils.isEmpty(errors)) { 77 | return errors; 78 | } else { 79 | return null; 80 | } 81 | } catch (err) { 82 | return err; 83 | } 84 | } 85 | 86 | function _executeRules(options, value, key, prefix, errors, deepQueue, ruleQueue) { 87 | let nestedKey = prefix + key; 88 | let schemaRules = utils.get(this.schema, nestedKey); 89 | 90 | if (!schemaRules) { 91 | return; 92 | } else if (utils.isObject(value) || hasObject(schemaRules)) { 93 | // Recurse down into nested attributes 94 | deepQueue[key] = ((nK, val) => { 95 | return next => { 96 | _validate.apply(this, [nK, val || {}, options, next]); 97 | }; 98 | })(nestedKey, value); 99 | } else { 100 | if (!utils.isObject(schemaRules)) { 101 | return; 102 | } else if (schemaRules.nullable === true) { 103 | let nullable = this.parent.rules.nullable || defaultRules.nullable; 104 | let nErr = nullable.call(options.ctx, value, true, undefined, this.parent); 105 | 106 | if (nErr === null) { 107 | return; 108 | } 109 | } 110 | utils.forOwn(schemaRules, (ruleValue, ruleKey) => { 111 | let rule = this.parent.rules[ruleKey] || defaultRules[ruleKey]; 112 | // Asynchronous rules get added to the queue 113 | if (rule && rule.async) { 114 | ruleQueue[`${ruleKey}_${ruleValue}`] = ((r, key, val, rVal) => { 115 | return next => { 116 | r.call(options.ctx, val, rVal, err => { 117 | next(null, { err: err, key: key }); 118 | }); 119 | }; 120 | })(rule, key, value, ruleValue); 121 | } else { 122 | // Get results of synchronous rules immediately 123 | let err = rule.call(options.ctx, value, ruleValue, undefined, this.parent); 124 | if (err) { 125 | if (!errors[key]) { 126 | errors[key] = { 127 | errors: [] 128 | }; 129 | } 130 | errors[key].errors.push(err); 131 | } 132 | } 133 | }); 134 | } 135 | } 136 | 137 | /** 138 | * @see Schema#validate 139 | */ 140 | function _validate(targetKey, attrs, options, cb) { 141 | let errors = {}; 142 | let prefix = targetKey + (targetKey.length ? '.' : ''); 143 | let deepQueue = {}; 144 | let ruleQueue = {}; 145 | let first = options.first; 146 | 147 | delete options.first; 148 | 149 | utils.forOwn(attrs, (value, key) => { 150 | _executeRules.call(this, options, value, key, prefix, errors, deepQueue, ruleQueue); 151 | }); 152 | 153 | // Validate missing attributes 154 | if (!options.ignoreMissing) { 155 | let schema = targetKey ? utils.get(this.schema, targetKey) || {} : this.schema; 156 | let missing = utils.difference(utils.keys(schema), utils.keys(attrs)); 157 | 158 | missing = utils.pick(this.schema, missing); 159 | missing = utils.map(missing, () => undefined); 160 | 161 | utils.forOwn(missing, (value, key) => { 162 | _executeRules.call(this, options, value, key, prefix, errors, deepQueue, ruleQueue); 163 | }); 164 | } 165 | 166 | var finalQueue = {}; 167 | 168 | if (!utils.isEmpty(deepQueue)) { 169 | finalQueue.deepQueue = next => { 170 | utils.parallel(deepQueue, next); 171 | }; 172 | } 173 | if (!utils.isEmpty(ruleQueue)) { 174 | finalQueue.ruleQueue = next => { 175 | utils.parallel(ruleQueue, next); 176 | }; 177 | } 178 | 179 | if (!utils.isEmpty(finalQueue)) { 180 | utils.parallel(finalQueue, (err, results) => { 181 | 182 | // Merge results of recursion 183 | if (results.deepQueue) { 184 | results.deepQueue = utils.filter(results.deepQueue, x => x !== undefined && x !== null); 185 | utils.deepMixIn(errors, results.deepQueue); 186 | } 187 | 188 | // Merge results of asynchronous rules 189 | if (results.ruleQueue) { 190 | if (results.ruleQueue) { 191 | results.ruleQueue = utils.filter(results.ruleQueue, x => x.err !== undefined && x.err !== null); 192 | } 193 | utils.forOwn(results.ruleQueue, value => { 194 | if (!errors[value.key]) { 195 | errors[value.key] = { 196 | errors: [] 197 | }; 198 | } 199 | errors[value.key].errors.push(value.err); 200 | }); 201 | } 202 | 203 | if (!utils.isEmpty(errors)) { 204 | first ? cb(errors) : cb(null, errors); 205 | } else { 206 | cb(null); 207 | } 208 | }); 209 | } else { 210 | if (!utils.isEmpty(errors)) { 211 | first ? cb(errors) : cb(null, errors); 212 | } else { 213 | cb(null); 214 | } 215 | } 216 | } 217 | 218 | function _validateSchema(attrs, rules) { 219 | rules = rules || []; 220 | let keys = utils.keys(attrs); 221 | let noRules = utils.intersection(keys, rules).length === 0; 222 | 223 | utils.forOwn(attrs, (value, key) => { 224 | if (noRules && utils.isString(value)) { 225 | attrs[key] = { 226 | type: value 227 | }; 228 | } else if (utils.isObject(value)) { 229 | _validateSchema(value, rules); 230 | } 231 | }); 232 | } 233 | 234 | let errors = { 235 | a: 'Schema#validateSync(attrs[, options]): ', 236 | b: 'Schema#validate(attrs[, options], cb): ' 237 | }; 238 | 239 | class Schema { 240 | constructor(name, schema, parent) { 241 | if (!utils.isString(name)) { 242 | throw new Error('"name" must be a string!'); 243 | } else if (!utils.isObject(schema)) { 244 | throw new Error('"schema" must be an object!'); 245 | } 246 | this.name = name; 247 | _validateSchema(schema, parent ? parent.availableRules() : defaultRules); 248 | this.schema = schema; 249 | } 250 | 251 | validateSync(attrs, options) { 252 | options = options ? (options === true ? { ignoreMissing: true } : options) : {}; 253 | if (!utils.isObject(attrs)) { 254 | throw new Error(`${errors.a}attrs: Must be an object!`); 255 | } else if (!utils.isObject(options)) { 256 | throw new Error(`${errors.a}options: Must be an object!`); 257 | } 258 | options.ctx = attrs; 259 | return _validateSync.call(this, '', attrs, options); 260 | } 261 | 262 | validate(attrs, options, cb) { 263 | options = options ? (options === true ? { ignoreMissing: true } : options) : {}; 264 | if (utils.isFunction(options)) { 265 | cb = options; 266 | options = {}; 267 | } 268 | if (!utils.isFunction(cb)) { 269 | throw new Error(`${errors.b}cb: Must be a function!`); 270 | } else if (!utils.isObject(attrs)) { 271 | return cb(new Error(`${errors.b}attrs: Must be an object!`)); 272 | } else if (!utils.isObject(options)) { 273 | return cb(new Error(`${errors.b}options: Must be an object!`)); 274 | } 275 | options.first = true; 276 | options.ctx = attrs; 277 | _validate.call(this, '', attrs, options, cb); 278 | } 279 | 280 | addDefaultsToTarget(target, overwrite) { 281 | if (!utils.isObject(target)) { 282 | throw new Error('"target" must be an object!'); 283 | } else if (!this.defaults) { 284 | throw new Error('No defaults have been set!'); 285 | } else if (overwrite) { 286 | utils.deepMixIn(target, this.defaults); 287 | } else { 288 | utils.deepFillIn(target, this.defaults); 289 | } 290 | } 291 | 292 | setDefaults(attrs) { 293 | if (!utils.isObject(attrs)) { 294 | throw new Error('Schema#defaults(attrs): attrs: Must be an object!'); 295 | } else { 296 | this.defaults = utils.merge({}, attrs); 297 | } 298 | return this; 299 | } 300 | 301 | getDefaults() { 302 | return utils.merge({}, this.defaults); 303 | } 304 | 305 | stripNonSchemaAttrs(attrs) { 306 | _stripNonSchemaAttrs(attrs, this.schema); 307 | return attrs; 308 | } 309 | } 310 | 311 | function _stripNonSchemaAttrs(attrs, schemaLevel) { 312 | utils.forOwn(attrs, (value, key) => { 313 | if (schemaLevel[key]) { 314 | if (utils.isObject(value) && utils.isObject(schemaLevel[key])) { 315 | _stripNonSchemaAttrs(value, schemaLevel[key]); 316 | } 317 | } else { 318 | utils.unset(attrs, key); 319 | } 320 | }); 321 | } 322 | 323 | export default Schema; 324 | -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | import isString from 'mout/lang/isString'; 2 | import isBoolean from 'mout/lang/isBoolean'; 3 | import isNumber from 'mout/lang/isNumber'; 4 | import isObject from 'mout/lang/isObject'; 5 | import isDate from 'mout/lang/isDate'; 6 | import isFunction from 'mout/lang/isFunction'; 7 | import isUndefined from 'mout/lang/isUndefined'; 8 | import isArray from 'mout/lang/isArray'; 9 | import isEmpty from 'mout/lang/isEmpty'; 10 | import toString from 'mout/lang/toString'; 11 | import toNumber from 'mout/lang/toNumber'; 12 | 13 | import _get from 'mout/object/get'; 14 | import deepMixIn from 'mout/object/deepMixIn'; 15 | import deepFillIn from 'mout/object/deepFillIn'; 16 | import forOwn from 'mout/object/forOwn'; 17 | import keys from 'mout/object/keys'; 18 | import pick from 'mout/object/pick'; 19 | import filter from 'mout/object/filter'; 20 | import map from 'mout/object/map'; 21 | import merge from 'mout/object/merge'; 22 | import unset from 'mout/object/unset'; 23 | 24 | import contains from 'mout/array/contains'; 25 | import intersection from 'mout/array/intersection'; 26 | import difference from 'mout/array/difference'; 27 | import unique from 'mout/array/unique'; 28 | 29 | import toInt from 'mout/number/toInt'; 30 | 31 | export default { 32 | isString, 33 | isBoolean, 34 | isNumber, 35 | isObject, 36 | isDate, 37 | isFunction, 38 | isUndefined, 39 | isArray, 40 | isEmpty, 41 | toString, 42 | toNumber, 43 | 44 | 'get': _get, 45 | deepMixIn, 46 | deepFillIn, 47 | forOwn, 48 | keys, 49 | pick, 50 | filter, 51 | map, 52 | merge, 53 | unset, 54 | 55 | contains, 56 | intersection, 57 | difference, 58 | unique, 59 | 60 | toInt, 61 | 62 | errMsg(rule, actual, expected) { 63 | return { 64 | rule, 65 | actual, 66 | expected 67 | }; 68 | }, 69 | 70 | parallel(tasks, cb) { 71 | var results = {}; 72 | var completed = 0; 73 | var length = 0; 74 | 75 | forOwn(tasks, () => { 76 | length += 1; 77 | }); 78 | 79 | 80 | forOwn(tasks, function (task, key) { 81 | task(function (err) { 82 | var args = Array.prototype.slice.call(arguments, 1); 83 | if (args.length <= 1) { 84 | args = args[0]; 85 | } 86 | results[key] = args; 87 | done(err); 88 | }); 89 | }); 90 | 91 | function done(err) { 92 | completed += 1; 93 | if (err || completed >= length) { 94 | cb(err, results); 95 | } 96 | } 97 | } 98 | }; 99 | -------------------------------------------------------------------------------- /mocha.start.js: -------------------------------------------------------------------------------- 1 | /*global assert:true */ 2 | 'use strict'; 3 | 4 | var assert = require('chai').assert; 5 | var mocha = require('mocha'); 6 | var sinon = require('sinon'); 7 | var Schemator = require('./'); 8 | 9 | var globals = module.exports = { 10 | fail: function (msg) { 11 | assert.equal('should not reach this!: ' + msg, 'failure'); 12 | }, 13 | TYPES_EXCEPT_STRING: [123, 123.123, null, undefined, {}, [], true, false, function () { 14 | }], 15 | TYPES_EXCEPT_STRING_OR_ARRAY: [123, 123.123, null, undefined, {}, true, false, function () { 16 | }], 17 | TYPES_EXCEPT_STRING_OR_NUMBER: [null, undefined, {}, [], true, false, function () { 18 | }], 19 | TYPES_EXCEPT_STRING_OR_ARRAY_OR_NUMBER: [null, undefined, {}, true, false, function () { 20 | }], 21 | TYPES_EXCEPT_NUMBER: ['string', null, undefined, {}, [], true, false, function () { 22 | }], 23 | TYPES_EXCEPT_OBJECT: ['string', 123, 123.123, null, undefined, true, false, function () { 24 | }], 25 | TYPES_EXCEPT_BOOLEAN: ['string', 123, 123.123, null, undefined, {}, [], function () { 26 | }], 27 | TYPES_EXCEPT_FUNCTION: ['string', 123, 123.123, null, undefined, {}, [], true, false], 28 | assert: assert, 29 | sinon: sinon, 30 | schemator: undefined 31 | }; 32 | 33 | var test = new mocha(); 34 | 35 | var testGlobals = []; 36 | 37 | for (var key in globals) { 38 | global[key] = globals[key]; 39 | testGlobals.push(globals[key]); 40 | } 41 | test.globals(testGlobals); 42 | 43 | beforeEach(function () { 44 | globals.schemator = new Schemator(); 45 | global.schemator = globals.schemator; 46 | }); 47 | 48 | afterEach(function () { 49 | globals.schemator = null; 50 | global.schemator = null; 51 | }); 52 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-data-schema", 3 | "description": "Define and validate rules, datatypes and schemata in Node and in the browser.", 4 | "version": "1.2.5", 5 | "homepage": "http://www.js-data.io/docs/js-data-schema", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/js-data/js-data-schema.git" 9 | }, 10 | "author": { 11 | "name": "Jason Dobry", 12 | "url": "http://www.pseudobry.com", 13 | "email": "jason.dobry@gmail.com" 14 | }, 15 | "main": "./dist/js-data-schema.js", 16 | "directories": { 17 | "lib": "./lib", 18 | "test": "./test" 19 | }, 20 | "keywords": [ 21 | "validate", 22 | "validator", 23 | "datatype", 24 | "model", 25 | "schema", 26 | "schemata", 27 | "rule", 28 | "object" 29 | ], 30 | "licenses": [ 31 | { 32 | "type": "MIT", 33 | "url": "https://github.com/js-data/js-data-schema/blob/master/LICENSE" 34 | } 35 | ], 36 | "readmeFilename": "README.md", 37 | "dependencies": { 38 | "mout": "0.11.0" 39 | }, 40 | "devDependencies": { 41 | "babel-core": "5.4.7", 42 | "babel-loader": "5.1.3", 43 | "chai": "2.3.0", 44 | "grunt": "0.4.5", 45 | "grunt-contrib-clean": "0.6.0", 46 | "grunt-contrib-uglify": "0.9.1", 47 | "grunt-contrib-watch": "0.6.1", 48 | "grunt-karma": "0.11.0", 49 | "grunt-karma-coveralls": "2.5.3", 50 | "grunt-mocha-test": "0.12.7", 51 | "grunt-webpack": "1.0.8", 52 | "jit-grunt": "0.9.1", 53 | "jshint": "2.8.0", 54 | "jshint-loader": "0.8.3", 55 | "karma": "0.12.35", 56 | "karma-chai": "0.1.0", 57 | "karma-chrome-launcher": "0.1.12", 58 | "karma-coverage": "0.3.1", 59 | "karma-firefox-launcher": "0.1.6", 60 | "karma-mocha": "0.1.10", 61 | "karma-phantomjs-launcher": "0.2.0", 62 | "karma-script-launcher": "0.1.0", 63 | "karma-sinon": "1.0.4", 64 | "karma-spec-reporter": "0.0.19", 65 | "mocha": "2.2.5", 66 | "sinon": "1.14.1", 67 | "time-grunt": "1.2.1", 68 | "webpack": "1.9.10", 69 | "webpack-dev-server": "1.9.0" 70 | }, 71 | "bugs": { 72 | "url": "https://github.com/js-data/js-data-schema/issues", 73 | "email": "jason.dobry@gmail.com" 74 | }, 75 | "scripts": { 76 | "test": "grunt test" 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /test/Schema.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Schema', function () { 4 | 5 | describe('Schema constructor', function () { 6 | it('should support shorthand for data type only attributes', function () { 7 | var schema = schemator.defineSchema('name', { 8 | name: 'string', 9 | age: 'number', 10 | address: { 11 | state: { 12 | type: 'string', 13 | maxLength: 45 14 | }, 15 | city: 'string' 16 | } 17 | }); 18 | 19 | assert.deepEqual(schema.schema, { 20 | name: { 21 | type: 'string' 22 | }, 23 | age: { 24 | type: 'number' 25 | }, 26 | address: { 27 | state: { 28 | type: 'string', 29 | maxLength: 45 30 | }, 31 | city: { 32 | type: 'string' 33 | } 34 | } 35 | }); 36 | }); 37 | it('should validate name', function () { 38 | var schema; 39 | for (var i = 0; i < TYPES_EXCEPT_STRING.length; i++) { 40 | try { 41 | schema = schemator.defineSchema(TYPES_EXCEPT_STRING[i], {}); 42 | fail('should fail on ' + TYPES_EXCEPT_STRING[i]); 43 | } catch (err) { 44 | assert.equal(err.message, '"name" must be a string!'); 45 | } 46 | assert.isUndefined(schema); 47 | } 48 | try { 49 | schema = schemator.defineSchema('a string', {}); 50 | assert.equal(schema.name, 'a string', 'Should set name correctly.'); 51 | } catch (err) { 52 | fail('should not fail on a string.'); 53 | } 54 | assert.isDefined(schema); 55 | }); 56 | it('validate schema', function () { 57 | var schema; 58 | for (var i = 0; i < TYPES_EXCEPT_OBJECT.length; i++) { 59 | try { 60 | schema = schemator.defineSchema('name', TYPES_EXCEPT_OBJECT[i]); 61 | fail('should fail on ' + TYPES_EXCEPT_OBJECT[i]); 62 | } catch (err) { 63 | assert.equal(err.message, '"schema" must be an object!'); 64 | } 65 | assert.isUndefined(schema); 66 | } 67 | try { 68 | schema = schemator.defineSchema('name', {}); 69 | assert.deepEqual(schema.schema, {}, 'Should set schema correctly.'); 70 | } catch (err) { 71 | fail('should not fail on an object.'); 72 | } 73 | assert.isDefined(schema); 74 | }); 75 | }); 76 | 77 | describe('Schema#validate', function () { 78 | it('should execute applicable validation rules', function (done) { 79 | var schema = schemator.defineSchema('test', { 80 | shouldSucceed: { 81 | type: 'string' 82 | }, 83 | shouldFail: { 84 | type: 'string' 85 | }, 86 | shouldFail2: { 87 | nullable: false 88 | }, 89 | shouldFail3: { 90 | nullable: false 91 | }, 92 | shouldFail4: { 93 | nullable: false 94 | } 95 | }); 96 | 97 | schema.validate({ 98 | shouldSucceed: 'isastring', 99 | shouldFail: true, 100 | shouldFail2: null, 101 | shouldFail3: undefined 102 | }, function (errors) { 103 | assert.deepEqual(errors, { 104 | shouldFail: { 105 | errors: [ 106 | { 107 | rule: 'type', 108 | actual: typeof true, 109 | expected: 'string' 110 | } 111 | ] 112 | }, 113 | shouldFail2: { 114 | errors: [ 115 | { 116 | rule: 'nullable', 117 | actual: 'x === null', 118 | expected: 'x !== null && x !== undefined' 119 | } 120 | ] 121 | }, 122 | shouldFail3: { 123 | errors: [ 124 | { 125 | rule: 'nullable', 126 | actual: 'x === undefined', 127 | expected: 'x !== null && x !== undefined' 128 | } 129 | ] 130 | }, 131 | shouldFail4: { 132 | errors: [ 133 | { 134 | rule: 'nullable', 135 | actual: 'x === undefined', 136 | expected: 'x !== null && x !== undefined' 137 | } 138 | ] 139 | } 140 | }, 'errors should be defined when the test fails'); 141 | done(); 142 | }); 143 | }); 144 | it('should execute multiple async validation rules', function (done) { 145 | schemator.defineRule('asyncString', function (x, options, done) { 146 | setTimeout(function () { 147 | if (typeof x !== 'string') { 148 | done({ 149 | rule: 'asyncString', 150 | actual: typeof x, 151 | expected: 'string' 152 | }); 153 | } else { 154 | done(); 155 | } 156 | }, 200); 157 | }, true); 158 | schemator.defineRule('asyncMaxLength', function (x, maxLength, done) { 159 | setTimeout(function () { 160 | if (x && (typeof x.length === 'number') && x.length > maxLength) { 161 | done({ 162 | rule: 'asyncMaxLength', 163 | actual: 'x.length > maxLength', 164 | expected: 'x.length <= maxLength' 165 | }); 166 | } else { 167 | done(); 168 | } 169 | }, 200); 170 | }, true); 171 | schemator.defineRule('asyncNumber', function (x, options, done) { 172 | setTimeout(function () { 173 | if (typeof x !== 'number') { 174 | done({ 175 | rule: 'asyncNumber', 176 | actual: typeof x, 177 | expected: 'number' 178 | }); 179 | } else { 180 | done(); 181 | } 182 | }, 200); 183 | }, true); 184 | 185 | var schema = schemator.defineSchema('test', { 186 | asyncString: { 187 | asyncString: true, 188 | asyncMaxLength: 2 189 | }, 190 | nested: { 191 | shouldFail: { 192 | asyncNumber: true 193 | } 194 | } 195 | }); 196 | 197 | schema.validate({ 198 | asyncString: ['shouldbestring', 'andistoolong', 3], 199 | nested: { 200 | shouldFail: 'shouldbeanumber' 201 | } 202 | }, function (errors) { 203 | try { 204 | assert.deepEqual(errors, { 205 | nested: { 206 | shouldFail: { 207 | errors: [ 208 | { 209 | rule: 'asyncNumber', 210 | actual: 'string', 211 | expected: 'number' 212 | } 213 | ] 214 | } 215 | }, 216 | asyncString: { 217 | errors: [ 218 | { 219 | rule: 'asyncString', 220 | actual: 'object', 221 | expected: 'string' 222 | }, 223 | { 224 | rule: 'asyncMaxLength', 225 | actual: 'x.length > maxLength', 226 | expected: 'x.length <= maxLength' 227 | } 228 | ] 229 | } 230 | }, 'errors should be defined when the test fails'); 231 | done(); 232 | } catch (err) { 233 | done(err); 234 | } 235 | }); 236 | }); 237 | 238 | it('should execute a mix of rules', function (done) { 239 | schemator.defineRule('asyncString2', function (x, options, done) { 240 | setTimeout(function () { 241 | if (typeof x !== 'string') { 242 | done({ 243 | rule: 'asyncString', 244 | actual: typeof x, 245 | expected: 'string' 246 | }); 247 | } else { 248 | done(); 249 | } 250 | }, 200); 251 | }, true); 252 | schemator.defineRule('asyncMaxLength2', function (x, maxLength, done) { 253 | setTimeout(function () { 254 | if (x && (typeof x.length === 'number') && x.length > maxLength) { 255 | done({ 256 | rule: 'asyncMaxLength', 257 | actual: 'x.length > maxLength', 258 | expected: 'x.length <= maxLength' 259 | }); 260 | } else { 261 | done(); 262 | } 263 | }, 200); 264 | }, true); 265 | schemator.defineRule('asyncNumber2', function (x, options, done) { 266 | setTimeout(function () { 267 | if (typeof x !== 'number') { 268 | done({ 269 | rule: 'asyncNumber', 270 | actual: typeof x, 271 | expected: 'number' 272 | }); 273 | } else { 274 | done(); 275 | } 276 | }, 200); 277 | }, true); 278 | 279 | var schema = schemator.defineSchema('test', { 280 | shouldFail: { 281 | asyncString2: true, 282 | type: 'string', 283 | maxLength: 1, 284 | asyncMaxLength2: 2 285 | }, 286 | nested: { 287 | shouldFail: { 288 | asyncNumber2: true, 289 | type: 'number' 290 | }, 291 | shouldSucceed: { 292 | type: 'number', 293 | nullable: true 294 | } 295 | }, 296 | willFail: { 297 | type: 'boolean' 298 | } 299 | }); 300 | 301 | schema.validate({ 302 | shouldFail: ['shouldbestring', 'andistoolong', 3], 303 | nested: { 304 | shouldFail: 'shouldbeanumber' 305 | }, 306 | willFail: 'shouldBeABoolean' 307 | }, function (errors) { 308 | assert.deepEqual(errors, { 309 | shouldFail: { 310 | errors: [ 311 | { 312 | rule: 'type', 313 | actual: 'object', 314 | expected: 'string' 315 | }, 316 | { 317 | rule: 'maxLength', 318 | actual: '3 > 1', 319 | expected: '3 <= 1' 320 | }, 321 | { 322 | rule: 'asyncString', 323 | actual: 'object', 324 | expected: 'string' 325 | }, 326 | { 327 | rule: 'asyncMaxLength', 328 | actual: 'x.length > maxLength', 329 | expected: 'x.length <= maxLength' 330 | } 331 | ] 332 | }, 333 | willFail: { 334 | errors: [ 335 | { 336 | rule: 'type', 337 | actual: 'string', 338 | expected: 'boolean' 339 | } 340 | ] 341 | }, 342 | nested: { 343 | shouldFail: { 344 | errors: [ 345 | { 346 | rule: 'type', 347 | actual: 'string', 348 | expected: 'number' 349 | }, 350 | { 351 | rule: 'asyncNumber', 352 | actual: 'string', 353 | expected: 'number' 354 | } 355 | ] 356 | } 357 | } 358 | }, 'errors should be defined when the test fails'); 359 | done(); 360 | }); 361 | }); 362 | 363 | it('should execute applicable validation rules', function (done) { 364 | var schema = schemator.defineSchema('test', { 365 | shouldSucceed: { 366 | type: 'string' 367 | } 368 | }); 369 | 370 | schema.validate({ 371 | shouldSucceed: 'isastring' 372 | }, function (errors) { 373 | assert.isNull(errors, 'errors should be undefined when the test succeeds'); 374 | done(); 375 | }); 376 | }); 377 | 378 | it('should work with custom data types', function () { 379 | schemator.defineDataType('foo', function (x) { 380 | if (typeof x !== 'number') { 381 | return 'arg!'; 382 | } 383 | return null 384 | }); 385 | var schema = schemator.defineSchema('test', { 386 | shouldFail: { 387 | type: 'foo' 388 | } 389 | }); 390 | 391 | var errors = schema.validateSync({ 392 | shouldFail: 'isastring' 393 | }); 394 | assert.deepEqual({ 395 | shouldFail: { 396 | errors: ['arg!'] 397 | } 398 | }, errors); 399 | }); 400 | 401 | it('should execute applicable validation rules', function (done) { 402 | var schema = schemator.defineSchema('test', { 403 | shouldFail: { 404 | type: 'string' 405 | } 406 | }); 407 | 408 | schema.validate({ 409 | shouldFail: true 410 | }, function (errors) { 411 | assert.deepEqual(errors, { 412 | shouldFail: { 413 | errors: [ 414 | { 415 | rule: 'type', 416 | actual: typeof true, 417 | expected: 'string' 418 | } 419 | ] 420 | } 421 | }, 'err should be defined when the test fails'); 422 | done(); 423 | }); 424 | }); 425 | 426 | it('should execute applicable nested validation rules', function (done) { 427 | var schema = schemator.defineSchema('test', { 428 | nested: { 429 | doubleNested: { 430 | shouldFail: { 431 | type: 'string' 432 | } 433 | } 434 | }, 435 | shouldFailAlso: { 436 | type: 'string' 437 | } 438 | }); 439 | 440 | schema.validate({ 441 | nested: { 442 | doubleNested: { 443 | shouldFail: true 444 | } 445 | }, 446 | shouldFailAlso: false 447 | }, function (errors) { 448 | assert.deepEqual(errors, { 449 | nested: { 450 | doubleNested: { 451 | shouldFail: { 452 | errors: [ 453 | { 454 | rule: 'type', 455 | actual: typeof true, 456 | expected: 'string' 457 | } 458 | ] 459 | } 460 | } 461 | }, 462 | shouldFailAlso: { 463 | errors: [ 464 | { 465 | rule: 'type', 466 | actual: typeof true, 467 | expected: 'string' 468 | } 469 | ] 470 | } 471 | }, 'err should be defined when the test fails'); 472 | done(); 473 | }); 474 | }); 475 | 476 | it('should execute applicable nested validation rules', function (done) { 477 | var schema = schemator.defineSchema('test', { 478 | nested: { 479 | doubleNested: { 480 | shouldSucceed: { 481 | type: 'string' 482 | } 483 | } 484 | }, 485 | shouldSucceedAlso: { 486 | type: 'string' 487 | } 488 | }); 489 | 490 | schema.validate({ 491 | nested: { 492 | doubleNested: { 493 | shouldSucceed: 'isastring' 494 | } 495 | }, 496 | shouldSucceedAlso: 'isastring' 497 | }, function (errors) { 498 | assert.deepEqual(errors, null, 'err should be null when the test succeeds'); 499 | done(); 500 | }); 501 | }); 502 | it('should execute applicable nested validation rules', function (done) { 503 | var schema = schemator.defineSchema('test', { 504 | nested: { 505 | doubleNested: { 506 | shouldFail: { 507 | type: 'string' 508 | }, 509 | shouldFail2: { 510 | nullable: false 511 | }, 512 | shouldSucceed: { 513 | nullable: true, 514 | type: 'string' 515 | } 516 | }, 517 | doubleNested2: { 518 | shouldFail: { 519 | max: 45 520 | } 521 | } 522 | }, 523 | shouldFailAlso: { 524 | maxLength: 4 525 | } 526 | }); 527 | 528 | schema.validate({ 529 | nested: { 530 | doubleNested: { 531 | shouldFail: 2 532 | }, 533 | doubleNested2: { 534 | shouldFail: 50 535 | } 536 | }, 537 | shouldFailAlso: 'isastring' 538 | }, function (errors) { 539 | assert.deepEqual(errors, { 540 | nested: { 541 | doubleNested: { 542 | shouldFail: { 543 | errors: [ 544 | { 545 | rule: 'type', 546 | actual: 'number', 547 | expected: 'string' 548 | } 549 | ] 550 | }, 551 | shouldFail2: { 552 | errors: [ 553 | { 554 | rule: 'nullable', 555 | actual: 'x === undefined', 556 | expected: 'x !== null && x !== undefined' 557 | } 558 | ] 559 | } 560 | }, 561 | doubleNested2: { 562 | shouldFail: { 563 | errors: [ 564 | { 565 | rule: 'max', 566 | actual: '50 > 45', 567 | expected: '50 <= 45' 568 | } 569 | ] 570 | } 571 | } 572 | }, 573 | shouldFailAlso: { 574 | errors: [ 575 | { 576 | rule: 'maxLength', 577 | actual: '9 > 4', 578 | expected: '9 <= 4' 579 | } 580 | ] 581 | } 582 | }, 'err should exist when the test fails'); 583 | done(); 584 | }); 585 | }); 586 | 587 | it('should throw an error if no callback is provided', function (done) { 588 | var schema = schemator.defineSchema('test', { 589 | shouldSucceed: { 590 | type: 'string' 591 | } 592 | }); 593 | 594 | try { 595 | schema.validate({ 596 | shouldSucceed: 'isastring', 597 | shouldFail: true 598 | }); 599 | fail('should have failed without a callback!'); 600 | } catch (err) { 601 | assert.isNotNull(err); 602 | } 603 | 604 | done(); 605 | }); 606 | 607 | it('should return an error if attrs is not an object', function (done) { 608 | var schema = schemator.defineSchema('test', { 609 | shouldSucceed: { 610 | type: 'string' 611 | } 612 | }); 613 | 614 | for (var i = 0; i < TYPES_EXCEPT_OBJECT.length; i++) { 615 | schema.validate(TYPES_EXCEPT_OBJECT[i], function (err) { 616 | assert.equal(err.message, 'Schema#validate(attrs[, options], cb): attrs: Must be an object!'); 617 | }); 618 | } 619 | 620 | done(); 621 | }); 622 | 623 | it('should return an error if options is provided and is not an object', function (done) { 624 | var schema = schemator.defineSchema('test', { 625 | shouldSucceed: { 626 | type: 'string' 627 | } 628 | }); 629 | 630 | for (var i = 0; i < TYPES_EXCEPT_OBJECT.length; i++) { 631 | if (!TYPES_EXCEPT_OBJECT[i] || TYPES_EXCEPT_OBJECT[i] === true) { 632 | continue; 633 | } 634 | schema.validate({ 635 | shouldSucceed: 'isastring' 636 | }, TYPES_EXCEPT_OBJECT[i], function (err) { 637 | assert.equal(err.message, 'Schema#validate(attrs[, options], cb): options: Must be an object!'); 638 | }); 639 | } 640 | 641 | done(); 642 | }); 643 | }); 644 | 645 | describe('Schema#addDefaults', function () { 646 | it('should add one-level deep defaults', function () { 647 | var schema = schemator.defineSchema('test', { 648 | defaultString: { 649 | type: 'string', 650 | default: 'defaultString' 651 | }, 652 | defaultNumber: { 653 | type: 'number' 654 | } 655 | }).setDefaults({ 656 | defaultString: 'defaultString', 657 | defaultNumber: 5 658 | }); 659 | 660 | var target = {}; 661 | 662 | schema.addDefaultsToTarget(target); 663 | 664 | assert.deepEqual(target, { 665 | defaultString: 'defaultString', 666 | defaultNumber: 5 667 | }, 'should add defaults'); 668 | }); 669 | it('should add one-level deep defaults', function () { 670 | var schema = schemator.defineSchema('test', { 671 | defaultString: { 672 | type: 'string', 673 | default: 'defaultString' 674 | }, 675 | defaultNumber: { 676 | type: 'number' 677 | } 678 | }).setDefaults({ 679 | defaultString: 'defaultString', 680 | defaultNumber: 5 681 | }); 682 | 683 | var target = { 684 | defaultString: 'shouldBeOverwritten' 685 | }; 686 | 687 | schema.addDefaultsToTarget(target, true); 688 | 689 | assert.deepEqual(target, { 690 | defaultString: 'defaultString', 691 | defaultNumber: 5 692 | }, 'should add defaults'); 693 | }); 694 | 695 | it('should add nested defaults', function () { 696 | var schema = schemator.defineSchema('test', { 697 | defaultString: { 698 | type: 'string' 699 | }, 700 | defaultNumber: { 701 | type: 'number' 702 | }, 703 | nested: { 704 | doubleNested: { 705 | defaultString3: { 706 | type: 'string' 707 | }, 708 | defaultNumber3: { 709 | type: 'number' 710 | } 711 | }, 712 | defaultString2: { 713 | type: 'string' 714 | }, 715 | defaultNumber2: { 716 | type: 'number' 717 | } 718 | } 719 | }).setDefaults({ 720 | defaultString: 'defaultString', 721 | defaultNumber: 5, 722 | nested: { 723 | doubleNested: { 724 | defaultString3: 'defaultString3', 725 | defaultNumber3: 15 726 | }, 727 | defaultString2: 'defaultString2', 728 | defaultNumber2: 10 729 | } 730 | }); 731 | 732 | var target = {}; 733 | 734 | schema.addDefaultsToTarget(target); 735 | 736 | assert.deepEqual(target, { 737 | defaultString: 'defaultString', 738 | defaultNumber: 5, 739 | nested: { 740 | doubleNested: { 741 | defaultString3: 'defaultString3', 742 | defaultNumber3: 15 743 | }, 744 | defaultString2: 'defaultString2', 745 | defaultNumber2: 10 746 | } 747 | }, 'should add nested defaults'); 748 | }); 749 | }); 750 | 751 | describe('Schema.validateSync(attrs, cb)', function () { 752 | it('should execute applicable validation rules', function () { 753 | var schema = schemator.defineSchema('test', { 754 | shouldSucceed: { 755 | type: 'string' 756 | }, 757 | shouldSucceed2: { 758 | type: 'string', 759 | nullable: true 760 | }, 761 | shouldSucceed3: { 762 | type: 'string', 763 | nullable: true 764 | }, 765 | shouldSucceed4: { 766 | type: 'string', 767 | nullable: true 768 | }, 769 | shouldFail: { 770 | type: 'string' 771 | }, 772 | shouldFail2: { 773 | nullable: false 774 | }, 775 | shouldFail3: { 776 | nullable: false 777 | }, 778 | shouldFail4: { 779 | nullable: false 780 | } 781 | }); 782 | 783 | var errors = schema.validateSync({ 784 | shouldSucceed: 'isastring', 785 | shouldSucceed2: null, 786 | shouldSucceed3: undefined, 787 | shouldFail: true, 788 | shouldFail2: null, 789 | shouldFail3: undefined 790 | }); 791 | assert.deepEqual(errors, { 792 | shouldFail: { 793 | errors: [ 794 | { 795 | rule: 'type', 796 | actual: typeof true, 797 | expected: 'string' 798 | } 799 | ] 800 | }, 801 | shouldFail2: { 802 | errors: [ 803 | { 804 | rule: 'nullable', 805 | actual: 'x === null', 806 | expected: 'x !== null && x !== undefined' 807 | } 808 | ] 809 | }, 810 | shouldFail3: { 811 | errors: [ 812 | { 813 | rule: 'nullable', 814 | actual: 'x === undefined', 815 | expected: 'x !== null && x !== undefined' 816 | } 817 | ] 818 | }, 819 | shouldFail4: { 820 | errors: [ 821 | { 822 | rule: 'nullable', 823 | actual: 'x === undefined', 824 | expected: 'x !== null && x !== undefined' 825 | } 826 | ] 827 | } 828 | }, 'errors should be defined when the test fails'); 829 | }); 830 | 831 | it('should execute applicable validation rules', function () { 832 | var schema = schemator.defineSchema('test', { 833 | shouldSucceed: { 834 | type: 'string' 835 | } 836 | }); 837 | 838 | var errors = schema.validateSync({ 839 | shouldSucceed: 'isastring' 840 | }); 841 | assert.isNull(errors, 'errors should be undefined when the test succeeds'); 842 | }); 843 | 844 | it('should execute applicable validation rules', function () { 845 | var schema = schemator.defineSchema('test', { 846 | shouldFail: { 847 | type: 'string' 848 | }, 849 | shouldFail2: { 850 | nullable: false 851 | } 852 | }); 853 | 854 | var errors = schema.validateSync({ 855 | shouldFail: true 856 | }); 857 | assert.deepEqual(errors, { 858 | shouldFail: { 859 | errors: [ 860 | { 861 | rule: 'type', 862 | actual: typeof true, 863 | expected: 'string' 864 | } 865 | ] 866 | }, 867 | shouldFail2: { 868 | errors: [ 869 | { 870 | rule: 'nullable', 871 | actual: 'x === undefined', 872 | expected: 'x !== null && x !== undefined' 873 | } 874 | ] 875 | } 876 | }, 'err should be defined when the test fails'); 877 | }); 878 | 879 | it('should execute applicable nested validation rules', function () { 880 | var schema = schemator.defineSchema('test', { 881 | nested: { 882 | doubleNested: { 883 | shouldFail: { 884 | type: 'string' 885 | }, 886 | shouldFail2: { 887 | nullable: false 888 | } 889 | } 890 | }, 891 | shouldFailAlso: { 892 | type: 'string' 893 | } 894 | }); 895 | 896 | var errors = schema.validateSync({ 897 | nested: { 898 | doubleNested: { 899 | shouldFail: true 900 | } 901 | }, 902 | shouldFailAlso: true 903 | }); 904 | assert.deepEqual(errors, { 905 | nested: { 906 | doubleNested: { 907 | shouldFail: { 908 | errors: [ 909 | { 910 | rule: 'type', 911 | actual: typeof true, 912 | expected: 'string' 913 | } 914 | ] 915 | }, 916 | shouldFail2: { 917 | errors: [ 918 | { 919 | rule: 'nullable', 920 | actual: 'x === undefined', 921 | expected: 'x !== null && x !== undefined' 922 | } 923 | ] 924 | } 925 | } 926 | }, 927 | shouldFailAlso: { 928 | errors: [ 929 | { 930 | rule: 'type', 931 | actual: typeof true, 932 | expected: 'string' 933 | } 934 | ] 935 | } 936 | }, 'err should be defined when the test fails'); 937 | }); 938 | 939 | it('should execute applicable nested validation rules', function () { 940 | var schema = schemator.defineSchema('test', { 941 | nested: { 942 | doubleNested: { 943 | shouldSucceed: { 944 | type: 'string' 945 | } 946 | } 947 | }, 948 | shouldSucceedAlso: { 949 | type: 'string' 950 | } 951 | }); 952 | 953 | var errors = schema.validateSync({ 954 | nested: { 955 | doubleNested: { 956 | shouldSucceed: 'isastring' 957 | } 958 | }, 959 | shouldSucceedAlso: 'isastring' 960 | }); 961 | assert.deepEqual(errors, null, 'err should be null when test succeeds'); 962 | }); 963 | 964 | it('should return an error if attrs is not an object', function () { 965 | var schema = schemator.defineSchema('test', { 966 | shouldSucceed: { 967 | type: 'string' 968 | } 969 | }); 970 | 971 | for (var i = 0; i < TYPES_EXCEPT_OBJECT.length; i++) { 972 | try { 973 | schema.validateSync(TYPES_EXCEPT_OBJECT[i]); 974 | } catch (err) { 975 | assert.equal(err.message, 'Schema#validateSync(attrs[, options]): attrs: Must be an object!'); 976 | } 977 | } 978 | }); 979 | 980 | it('should return an error if options is provided and is not an object', function () { 981 | var schema = schemator.defineSchema('test', { 982 | shouldSucceed: { 983 | type: 'string' 984 | } 985 | }); 986 | 987 | for (var i = 0; i < TYPES_EXCEPT_OBJECT.length; i++) { 988 | if (!TYPES_EXCEPT_OBJECT[i] || TYPES_EXCEPT_OBJECT[i] === true) { 989 | continue; 990 | } 991 | try { 992 | schema.validateSync({ 993 | shouldSucceed: 'isastring' 994 | }, TYPES_EXCEPT_OBJECT[i]); 995 | } catch (err) { 996 | assert.equal(err.message, 'Schema#validateSync(attrs[, options]): options: Must be an object!'); 997 | } 998 | } 999 | }); 1000 | }); 1001 | }); 1002 | -------------------------------------------------------------------------------- /test/dataTypes.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Schemator dataType methods', function () { 4 | 5 | describe('Schemator#availableDataTypes()', function () { 6 | it('not implemented'); 7 | }); 8 | 9 | describe('Schemator#defineDataType(name, typeDefinition)', function () { 10 | it('not implemented'); 11 | }); 12 | 13 | describe('Schemator#removeDataType(name)', function () { 14 | it('not implemented'); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /test/rules.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Schemator rule methods', function () { 4 | 5 | describe('Schemator#availableRules()', function () { 6 | it('not implemented'); 7 | }); 8 | 9 | describe('Schemator#defineRule(name, ruleFunc, async)', function () { 10 | it('not implemented'); 11 | }); 12 | 13 | describe('Schemator#removeRule(name)', function () { 14 | it('not implemented'); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /test/schemas.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Schemator schema methods', function () { 4 | 5 | describe('Schemator#availableSchemata()', function () { 6 | it('not implemented'); 7 | }); 8 | 9 | describe('Schemator#defineSchema(name, schema)', function () { 10 | it('not implemented'); 11 | }); 12 | 13 | describe('Schemator#removeSchema(name)', function () { 14 | it('not implemented'); 15 | }); 16 | 17 | describe('Schemator#getSchema(name)', function () { 18 | it('should return "undefined" if the schema is not registered', function () { 19 | assert.isUndefined(schemator.getSchema('PersonSchema')); 20 | }); 21 | it('should return the correct schema', function () { 22 | var PersonSchema = schemator.defineSchema('PersonSchema', { 23 | name: 'string' 24 | }); 25 | 26 | assert.isTrue(schemator.getSchema('PersonSchema') === PersonSchema); 27 | }); 28 | }); 29 | 30 | describe('Schemator#setDefaults(name, attrs)', function () { 31 | it('should throw an error if the schema is not registered', function () { 32 | assert.throws(function () { 33 | schemator.setDefaults('PersonSchema', { 34 | first: '' 35 | }); 36 | }, Error, 'schema is not registered!'); 37 | }); 38 | it('should set the defaults for the schema', function () { 39 | schemator.defineSchema('PersonSchema', { 40 | name: 'string' 41 | }); 42 | 43 | schemator.setDefaults('PersonSchema', { 44 | first: '' 45 | }); 46 | 47 | assert.deepEqual(schemator.getDefaults('PersonSchema'), { 48 | first: '' 49 | }); 50 | }); 51 | }); 52 | 53 | describe('Schemator#getDefaults(name)', function () { 54 | it('should throw an error if the schema is not registered', function () { 55 | assert.throws(function () { 56 | schemator.getDefaults('PersonSchema'); 57 | }, Error, 'schema is not registered!'); 58 | }); 59 | it('should get the defaults for the schema', function () { 60 | schemator.defineSchema('PersonSchema', { 61 | name: 'string' 62 | }); 63 | 64 | schemator.setDefaults('PersonSchema', { 65 | first: '' 66 | }); 67 | 68 | assert.deepEqual(schemator.getDefaults('PersonSchema'), { 69 | first: '' 70 | }); 71 | }); 72 | }); 73 | 74 | describe('Schemator#addDefaultsToTarget(name, attrs[, overwrite])', function () { 75 | it('should throw an error if the schema is not registered', function () { 76 | assert.throws(function () { 77 | schemator.addDefaultsToTarget('PersonSchema', {}); 78 | }, Error, 'schema is not registered!'); 79 | }); 80 | it('should add defaults to the target', function () { 81 | schemator.defineSchema('PersonSchema', { 82 | name: 'string' 83 | }); 84 | 85 | schemator.setDefaults('PersonSchema', { 86 | first: '', 87 | last: '', 88 | plan: 'free' 89 | }); 90 | 91 | var person = { 92 | first: 'John', 93 | plan: 'premium' 94 | }; 95 | 96 | schemator.addDefaultsToTarget('PersonSchema', person); 97 | 98 | assert.deepEqual(person, { 99 | first: 'John', 100 | last: '', 101 | plan: 'premium' 102 | }); 103 | 104 | schemator.addDefaultsToTarget('PersonSchema', person, true); 105 | 106 | assert.deepEqual(person, { 107 | first: '', 108 | last: '', 109 | plan: 'free' 110 | }); 111 | }); 112 | }); 113 | 114 | describe('Schemator#stripNonSchemaAttrs(name, attrs[, overwrite])', function () { 115 | it('should throw an error if the schema is not registered', function () { 116 | assert.throws(function () { 117 | schemator.stripNonSchemaAttrs('PersonSchema', {}); 118 | }, Error, 'schema is not registered!'); 119 | }); 120 | it('should strip non-schema attributes from the target', function () { 121 | schemator.defineSchema('PersonSchema', { 122 | name: 'string' 123 | }); 124 | 125 | var person = { 126 | name: 'John', 127 | random: 'value' 128 | }; 129 | 130 | schemator.stripNonSchemaAttrs('PersonSchema', person); 131 | 132 | assert.deepEqual(person, { 133 | name: 'John' 134 | }); 135 | }); 136 | }); 137 | }); 138 | --------------------------------------------------------------------------------