Comparing version 0.5.1 to 0.6.0
'use strict'; | ||
var gulp = require('gulp') | ||
, del = require('del') | ||
, plumber = require('gulp-plumber') | ||
, mocha = require('gulp-mocha'); | ||
var es = require('event-stream'); | ||
var Buffer = require('buffer').Buffer; | ||
var jstransform = require('jstransform') | ||
, mocha = require('gulp-mocha') | ||
, rename = require('gulp-rename') | ||
, babelTransform = require('gulp-babel-helpers') | ||
gulp.task('test-runner', function(){ | ||
gulp.watch(['./lib/**/*.js'], ['jstransform']) | ||
gulp.watch(['./src/**/*.js'], ['compile']) | ||
gulp.watch('./test/**/*.js', function(e){ | ||
gulp.src(e.path) | ||
.pipe(plumber()) | ||
.pipe(transform()) | ||
.pipe(mocha({ reporter: 'spec' })) | ||
@@ -22,4 +20,35 @@ }) | ||
gulp.task('clean', function(cb){ | ||
del('./lib', cb); | ||
}) | ||
gulp.task('compile', [ 'clean' ], function(){ | ||
return gulp.src('./src/**/*.js') | ||
.pipe(plumber()) | ||
.pipe(babelTransform({ | ||
experimental: true, loose: ['all'], | ||
whitelist: [ | ||
'es6.classes', | ||
'es6.modules', | ||
'es6.spread', | ||
'es6.blockScoping', | ||
'es6.arrowFunctions', | ||
'es6.properties.computed', | ||
'es6.properties.shorthand', | ||
'es6.parameters.default', | ||
'es6.parameters.rest', | ||
'es6.templateLiterals', | ||
'es6.destructuring', | ||
'es7.objectRestSpread' | ||
] | ||
} | ||
, './util/babelHelpers.js' | ||
, './lib/util/babelHelpers.js')) | ||
.pipe(rename({ extname: '.js' })) | ||
.pipe(gulp.dest('./lib')); | ||
}) | ||
gulp.task('watch', function(){ | ||
gulp.watch(['./lib/**/*.js', './test/**/*.js'], ['jstransform']) | ||
gulp.watch(['./src/**/*.js', './test/**/*.js'], ['build']) | ||
}) | ||
@@ -30,47 +59,2 @@ | ||
.pipe(mocha({ reporter: 'spec' })); | ||
}); | ||
gulp.task('jstransform', function() { | ||
gulp.src('./lib/**/*.js') | ||
.pipe(plumber()) | ||
.pipe(transform()) | ||
.pipe(gulp.dest('./dist/')) | ||
}) | ||
var defaultVisitors = [ | ||
'jstransform/visitors/es6-arrow-function-visitors', | ||
'jstransform/visitors/es6-class-visitors', | ||
'jstransform/visitors/es6-destructuring-visitors', | ||
'jstransform/visitors/es6-object-concise-method-visitors', | ||
'jstransform/visitors/es6-object-short-notation-visitors', | ||
'jstransform/visitors/es6-rest-param-visitors', | ||
'jstransform/visitors/es6-template-visitors', | ||
'jstransform/visitors/es7-spread-property-visitors' | ||
]; | ||
function transform(opt) { | ||
function modifyFile(file){ | ||
if (file.isNull()) | ||
return this.emit('data', file); // pass along | ||
if (file.isStream()) | ||
return this.emit('error', new Error("gulp-jstransfrom: Streaming not supported")); | ||
// Influenced by https://github.com/stoyan/etc/master/es6r/es6r.js | ||
var str = file.contents.toString('utf8'); | ||
var visitors = []; | ||
defaultVisitors.forEach(function(visitor) { | ||
visitors = visitors.concat(require(visitor).visitorList); | ||
}); | ||
var converted = jstransform.transform(visitors, str); | ||
file.contents = new Buffer(converted.code); | ||
this.emit('data', file); | ||
} | ||
return es.through(modifyFile); | ||
} | ||
}) |
150
lib/array.js
@@ -1,111 +0,97 @@ | ||
'use strict'; | ||
var SchemaObject = require('./mixed') | ||
, Promise = require('es6-promise').Promise | ||
, locale = require('./locale.js').array | ||
, _ = require('lodash') | ||
"use strict"; | ||
var babelHelpers = require("./util/babelHelpers.js"); | ||
var MixedSchema = require("./mixed"), | ||
Promise = require("es6-promise").Promise, | ||
locale = require("./locale.js").array, | ||
inherits = require("./util/_").inherits; | ||
// hi | ||
module.exports = ArraySchema; | ||
var _Array = module.exports = SchemaObject.extend({ | ||
function ArraySchema() { | ||
if (!(this instanceof ArraySchema)) return new ArraySchema(); | ||
constructor: function(){ | ||
if ( !(this instanceof _Array)) return new _Array() | ||
SchemaObject.call(this) | ||
MixedSchema.call(this, { type: "array" }); | ||
this._type = 'array' | ||
}, | ||
this.transforms.push(function (values) { | ||
if (typeof values === "string") try { | ||
values = JSON.parse(values); | ||
} catch (err) { | ||
values = null; | ||
} | ||
isType: function(v) { | ||
if( this._nullable && v === null) return true | ||
return Array.isArray(v) | ||
}, | ||
if (Array.isArray(values)) return this._subType ? values.map(this._subType.cast, this._subType) : values; | ||
_coerce: function(values) { | ||
if (typeof values === 'string') { | ||
try { | ||
values = JSON.parse(values) | ||
} catch (err){ values = null } | ||
} | ||
return this.isType(values) ? values : null; | ||
}); | ||
} | ||
if(values === undefined ) | ||
return | ||
inherits(ArraySchema, MixedSchema, { | ||
if( this.isType(values) ) | ||
return this._subType | ||
? values.map(this._subType.cast, this._subType) | ||
: values | ||
return null | ||
_typeCheck: function (v) { | ||
return Array.isArray(v); | ||
}, | ||
_validate: function(_value, _opts, _state){ | ||
_validate: function (_value, _opts, _state) { | ||
var context, subType, schema; | ||
_state = _state || {} | ||
context = _state.parent || (_opts || {}).context | ||
schema = this._resolve(context) | ||
subType = schema._subType | ||
_state = _state || {}; | ||
context = _state.parent || (_opts || {}).context; | ||
schema = this._resolve(context); | ||
subType = schema._subType; | ||
return SchemaObject.prototype._validate.call(this, _value, _opts, _state) | ||
.then(function(value){ | ||
return MixedSchema.prototype._validate.call(this, _value, _opts, _state).then(function (value) { | ||
if ( !subType ) return value | ||
if (!subType || !Array.isArray(value)) return value; | ||
return Promise | ||
.all(value.map((item, key) => { | ||
var path = (_state.path || '') + '['+ key + ']' | ||
, state = _.defaults({ path, key, parent: value }, _state); | ||
return Promise.all(value.map(function (item, key) { | ||
var path = (_state.path || "") + "[" + key + "]", | ||
state = babelHelpers._extends({}, _state, { path: path, key: key, parent: value }); | ||
return subType._validate(item, _opts, state) | ||
})) | ||
.then(() => value) | ||
}) | ||
return subType._validate(item, _opts, state); | ||
})).then(function () { | ||
return value; | ||
}); | ||
}); | ||
}, | ||
of: function(schema){ | ||
var next = this.clone() | ||
next._subType = schema | ||
return next | ||
of: function (schema) { | ||
var next = this.clone(); | ||
next._subType = schema; | ||
return next; | ||
}, | ||
required: function(msg){ | ||
return this.validation( | ||
{ hashKey: 'required', message: msg || locale.required }, | ||
function(value){ | ||
return value && this.isType(value) && !!value.length | ||
}) | ||
required: function (msg) { | ||
var _this = this; | ||
return this.validation({ hashKey: "required", message: msg || locale.required }, function (value) { | ||
return _this.isType(value) && !!value.length; | ||
}); | ||
}, | ||
min: function(min, msg){ | ||
msg = msg || locale.min | ||
min: function (min, msg) { | ||
msg = msg || locale.min; | ||
return this.validation( | ||
{ message: msg, hashKey: 'min', params: { min: min } } | ||
, function(value){ | ||
return value && value.length >= min | ||
}) | ||
return this.validation({ message: msg, hashKey: "min", params: { min: min } }, function (value) { | ||
return value && value.length >= min; | ||
}); | ||
}, | ||
max: function(max, msg){ | ||
msg = msg || locale.max | ||
return this.validation( | ||
{ message: msg, hashKey: 'max', params: { max: max } } | ||
, function(value){ | ||
return value && value.length <= max | ||
}) | ||
max: function (max, msg) { | ||
msg = msg || locale.max; | ||
return this.validation({ message: msg, hashKey: "max", params: { max: max } }, function (value) { | ||
return value && value.length <= max; | ||
}); | ||
}, | ||
compact: function(rejector){ | ||
return this.transform(function(values){ | ||
return values.filter(rejector ? reject : compact) | ||
}) | ||
compact: function (rejector) { | ||
var reject = !rejector ? function (v) { | ||
return !!v; | ||
} : function (v, i, a) { | ||
return !rejector(v, i, a); | ||
}; | ||
function reject(v,i,a){ | ||
return !rejector.call(this, v, i, a) | ||
} | ||
return this.transform(function (values) { | ||
return values.filter(reject); | ||
}); | ||
} | ||
}) | ||
function compact(v){ | ||
return !!v | ||
} | ||
}); |
@@ -1,34 +0,36 @@ | ||
'use strict'; | ||
var SchemaObject = require('./mixed') | ||
, locale = require('./locale.js').boolean | ||
"use strict"; | ||
var MixedSchema = require("./mixed"), | ||
locale = require("./locale.js").boolean, | ||
inherits = require("./util/_").inherits; | ||
var _Boolean = module.exports = SchemaObject.extend({ | ||
var isBool = function (v) { | ||
return typeof v === "boolean"; | ||
}; | ||
constructor: function(){ | ||
if ( !(this instanceof _Boolean)) return new _Boolean() | ||
SchemaObject.call(this) | ||
module.exports = BooleanSchema; | ||
this._type = 'boolean' | ||
}, | ||
function BooleanSchema() { | ||
if (!(this instanceof BooleanSchema)) return new BooleanSchema(); | ||
isType: function(v) { | ||
if( this._nullable && v === null) return true | ||
return isBool(v) | ||
}, | ||
MixedSchema.call(this, { type: "boolean" }); | ||
_coerce: function(value) { | ||
if(value == null || this.isType(value)) return value | ||
return (/true|1/i).test(value) | ||
this.transforms.push(function (value) { | ||
if (this.isType(value)) return value; | ||
return /true|1/i.test(value); | ||
}); | ||
} | ||
inherits(BooleanSchema, MixedSchema, { | ||
_typeCheck: isBool, | ||
_coerce: function (value) { | ||
if (this.isType(value)) return value; | ||
return /true|1/i.test(value); | ||
}, | ||
required: function(msg){ | ||
return this.validation( | ||
{ hashKey: 'required', message: msg || locale.required }, | ||
isBool) | ||
required: function (msg) { | ||
return this.validation({ hashKey: "required", message: msg || locale.required }, isBool); | ||
} | ||
}) | ||
function isBool(v){ | ||
return typeof v === 'boolean' | ||
} | ||
}); |
@@ -1,66 +0,60 @@ | ||
'use strict'; | ||
var SchemaObject = require('./mixed') | ||
, isoParse = require('./util/isodate') | ||
, locale = require('./locale.js').date | ||
, _ = require('lodash') | ||
"use strict"; | ||
var MixedSchema = require("./mixed"); | ||
var isoParse = require("./util/isodate"); | ||
var locale = require("./locale.js").date; | ||
var _require = require("./util/_"); | ||
var _Date = module.exports = SchemaObject.extend({ | ||
var isDate = _require.isDate; | ||
var inherits = _require.inherits; | ||
constructor: function(){ | ||
if ( !(this instanceof _Date)) return new _Date() | ||
SchemaObject.call(this) | ||
var invalidDate = new Date(""); | ||
this._type = 'date' | ||
}, | ||
module.exports = DateSchema; | ||
isType: function(v) { | ||
if( this._nullable && v === null) return true | ||
return _.isDate(v) | ||
}, | ||
function DateSchema() { | ||
if (!(this instanceof DateSchema)) return new DateSchema(); | ||
_coerce: function(value) { | ||
if(value == null ) return value | ||
if(_.isDate(value) ) return new Date(value) | ||
MixedSchema.call(this, { type: "date" }); | ||
value = isoParse(value) | ||
return value ? new Date(value) : null | ||
this.transforms.push(function (value) { | ||
if (this.isType(value)) return isDate(value) ? new Date(value) : value; | ||
value = isoParse(value); | ||
return value ? new Date(value) : invalidDate; | ||
}); | ||
} | ||
inherits(DateSchema, MixedSchema, { | ||
_typeCheck: function (v) { | ||
return isDate(v) && !isNaN(v.getTime()); | ||
}, | ||
required: function(msg){ | ||
return this.validation( | ||
{ hashKey: 'required', message: msg || locale.required }, | ||
_.isDate) | ||
required: function (msg) { | ||
return this.validation({ hashKey: "required", message: msg || locale.required }, isDate); | ||
}, | ||
min: function(min, msg){ | ||
min: function (min, msg) { | ||
var limit = this.cast(min); | ||
msg = msg || locale.min | ||
msg = msg || locale.min; | ||
if(!this.isType(limit)) | ||
throw new TypeError('min must be a Date or a value that can be parsed to a Date') | ||
if (!this.isType(limit)) throw new TypeError("min must be a Date or a value that can be parsed to a Date"); | ||
return this.validation( | ||
{ message: msg, hashKey: 'min', params: { min: min } } | ||
, function(value){ | ||
var val = this.cast(value) | ||
return val && (val >= limit) | ||
}) | ||
return this.validation({ message: msg, hashKey: "min", params: { min: min } }, function (value) { | ||
return value && value >= limit; | ||
}); | ||
}, | ||
max: function(max, msg){ | ||
max: function (max, msg) { | ||
var limit = this.cast(max); | ||
msg = msg || locale.max | ||
if(!this.isType(limit)) | ||
throw new TypeError('max must be a Date or a value that can be parsed to a Date') | ||
if (!this.isType(limit)) throw new TypeError("max must be a Date or a value that can be parsed to a Date"); | ||
return this.validation( | ||
{ message: msg, hashKey: 'max', params: { max: max } } | ||
, function(value){ | ||
var val = this.cast(value) | ||
return val <= limit | ||
}) | ||
return this.validation({ hashKey: "max", message: msg || locale.max, params: { max: max } }, function (value) { | ||
return !value || value <= limit; | ||
}); | ||
} | ||
}) | ||
}); |
@@ -5,47 +5,46 @@ | ||
mixed: { | ||
required: '${path} is a required field', | ||
oneOf: '${path} must be one the following values: ${values}', | ||
notOneOf: '${path} must not be one the following values: ${values}' | ||
required: "${path} is a required field", | ||
oneOf: "${path} must be one the following values: ${values}", | ||
notOneOf: "${path} must not be one the following values: ${values}" | ||
}, | ||
string: { | ||
required: '${path} is a required field', | ||
min: '${path} must be at least ${min} characters', | ||
max: '${path} must be less than ${max} characters', | ||
matches: '${path} must match the following: "${regex}"', | ||
email: '${path} must be a valid email', | ||
url: '${path} must be a valid URL', | ||
trim: '${path} must be a trimmed string', | ||
lowercase: '${path} must be a lowercase string', | ||
uppercase: '${path} must be a uppercase string', | ||
required: "${path} is a required field", | ||
min: "${path} must be at least ${min} characters", | ||
max: "${path} must be less than ${max} characters", | ||
matches: "${path} must match the following: \"${regex}\"", | ||
email: "${path} must be a valid email", | ||
url: "${path} must be a valid URL", | ||
trim: "${path} must be a trimmed string", | ||
lowercase: "${path} must be a lowercase string", | ||
uppercase: "${path} must be a uppercase string" | ||
}, | ||
boolean: { | ||
required: '${path} is a required field' | ||
required: "${path} is a required field" | ||
}, | ||
number: { | ||
required: '${path} is a required field', | ||
min: '${path} must be at least ${min}', | ||
max: '${path} must be less or equal to than ${max}', | ||
positive: '${path} must be a positive number', | ||
negative: '${path} must be a negative number', | ||
integer: '${path} must be an integer' | ||
required: "${path} is a required field", | ||
min: "${path} must be at least ${min}", | ||
max: "${path} must be less than or equal to ${max}", | ||
positive: "${path} must be a positive number", | ||
negative: "${path} must be a negative number", | ||
integer: "${path} must be an integer" | ||
}, | ||
date: { | ||
required: '${path} is a required field', | ||
min: '${path} field must be later than ${min}', | ||
max: '${path} field must be at earlier than ${max}', | ||
required: "${path} is a required field", | ||
min: "${path} field must be later than ${min}", | ||
max: "${path} field must be at earlier than ${max}" | ||
}, | ||
object: { | ||
required: '${path} is a required field', | ||
required: "${path} is a required field" | ||
}, | ||
array: { | ||
required: '${path} is a required field', | ||
min: '${path} field must have at least ${min} items', | ||
max: '${path} field must have less than ${max} items', | ||
} | ||
} | ||
required: "${path} is a required field", | ||
min: "${path} field must have at least ${min} items", | ||
max: "${path} field must have less than ${max} items" } | ||
}; |
413
lib/mixed.js
@@ -1,34 +0,32 @@ | ||
'use strict'; | ||
var interpolate = require('./util/interpolate') | ||
, Promise = require('es6-promise').Promise | ||
, Condition = require('./util/condition') | ||
, ValidationError = require('./util/validation-error') | ||
, getter = require('property-expr').getter | ||
, locale = require('./locale.js').mixed | ||
, _ = require('lodash') | ||
"use strict"; | ||
var babelHelpers = require("./util/babelHelpers.js"); | ||
var interpolate = require("./util/interpolate"), | ||
Promise = require("es6-promise").Promise, | ||
Condition = require("./util/condition"), | ||
ValidationError = require("./util/validation-error"), | ||
getter = require("property-expr").getter, | ||
locale = require("./locale.js").mixed, | ||
_ = require("./util/_"), | ||
cloneDeep = require("./util/clone"), | ||
BadSet = require("./util/set"); | ||
var initProps; | ||
module.exports = SchemaType | ||
module.exports = SchemaType; | ||
function SchemaType(){ | ||
var props; | ||
function SchemaType() { | ||
var options = arguments[0] === undefined ? {} : arguments[0]; | ||
if ( !(this instanceof SchemaType)) | ||
return new SchemaType() | ||
if (!(this instanceof SchemaType)) return new SchemaType(); | ||
props = initProps; | ||
initProps = null; | ||
this._deps = []; | ||
this._conditions = []; | ||
this._options = {}; | ||
this._activeTests = {}; | ||
this._whitelist = new BadSet(); | ||
this._blacklist = new BadSet(); | ||
this.validations = []; | ||
this.transforms = []; | ||
this._deps = [] | ||
this._conditions = [] | ||
this._options = {} | ||
this._activeTests = {} | ||
this._whitelist = [] | ||
this._blacklist = [] | ||
this.validations = [] | ||
this.transforms = [] | ||
if (_.has(options, "default")) this._default = options.default; | ||
_.extend(this, props) | ||
this._type = 'mixed' | ||
this._type = options.type || "mixed"; | ||
} | ||
@@ -42,290 +40,231 @@ | ||
clone: function(){ | ||
return this.constructor.create( | ||
_.transform(this, function(obj, val, key){ | ||
obj[key] = val.clone | ||
? val.clone() | ||
: Array.isArray(val) | ||
? val.slice() | ||
: _.isPlainObject(val) | ||
? _.clone(val) | ||
: val | ||
}, {}) | ||
) | ||
clone: function () { | ||
return cloneDeep(this); | ||
}, | ||
concat: function(schema){ | ||
var next = this.clone() | ||
return _.merge(next, schema.clone(), function(a, b){ | ||
if(Array.isArray(a)) return a.concat(b) | ||
}) | ||
concat: function (schema) { | ||
return _.merge(this.clone(), schema.clone()); | ||
}, | ||
isType: function(value){ | ||
if ( this._nullable && value === null ) return true | ||
return value !== undefined | ||
isType: function (v) { | ||
if (this._nullable && v === null) return true; | ||
return !this._typeCheck || this._typeCheck(v); | ||
}, | ||
cast: function(_value, _opts){ | ||
var schema = this._resolve((_opts|| {}).context) | ||
return schema._cast(_value, _opts) | ||
cast: function (_value, _opts) { | ||
var schema = this._resolve((_opts || {}).context); | ||
return schema._cast(_value, _opts); | ||
}, | ||
_cast: function(_value, _opts){ | ||
var self = this | ||
, value = this._coerce ? this._coerce(_value) : _value | ||
_cast: function (_value) { | ||
var _this = this; | ||
if( value == null && _.has(self, '_default') ) | ||
value = self._nullable && value === null | ||
? value | ||
: self.default() | ||
var value = this._coerce && _value !== undefined ? this._coerce(_value) : _value; | ||
return self.transforms.reduce(function(value, transform){ | ||
return transform.call(self, value) | ||
}, value) | ||
if (value === undefined && _.has(this, "_default")) value = this.default(); | ||
return value === undefined ? value : this.transforms.reduce(function (value, transform) { | ||
return transform.call(_this, value, _value); | ||
}, value); | ||
}, | ||
_resolve: function(context){ | ||
var schema = this; | ||
return this._conditions.reduce(function(schema, match){ | ||
if(!context) throw new Error('missing the context necessary to cast this value') | ||
return match.resolve(schema, getter(match.key)(context)) | ||
}, schema) | ||
_resolve: function (context) { | ||
var schema = this; | ||
return this._conditions.reduce(function (schema, match) { | ||
if (!context) throw new Error("missing the context necessary to cast this value"); | ||
return match.resolve(schema, getter(match.key)(context)); | ||
}, schema); | ||
}, | ||
//-- validations | ||
_validate: function(value, _opts, _state) { | ||
var valids = this._whitelist | ||
, invalids = this._blacklist | ||
, context = (_opts || {}).context || _state.parent | ||
, schema, valid, state, isStrict; | ||
_validate: function (value, _opts, _state) { | ||
var valids = this._whitelist, | ||
invalids = this._blacklist, | ||
context = (_opts || {}).context || _state.parent, | ||
schema, | ||
valid, | ||
state, | ||
isStrict; | ||
state = _.defaults(_state || {}, { path: 'this' }) | ||
schema = this._resolve(context) | ||
isStrict = schema._option('strict', _opts) | ||
state = _state; | ||
schema = this._resolve(context); | ||
isStrict = schema._option("strict", _opts); | ||
!state.path && (state.path = "this"); | ||
var errors = []; | ||
if( !state.isCast && !isStrict ) | ||
value = schema._cast(value, _opts) | ||
if (!state.isCast && !isStrict) value = schema._cast(value, _opts); | ||
if ( value !== undefined && !schema.isType(value) ){ | ||
errors.push('value: ' + value + " is must be a " + schema._type + " type") | ||
return Promise.reject(new ValidationError(errors)) | ||
if (value !== undefined && !schema.isType(value)) { | ||
errors.push("value: " + value + " is must be a " + schema._type + " type"); | ||
return Promise.reject(new ValidationError(errors)); | ||
} | ||
if( valids.length ) { | ||
valid = has(valids, value) | ||
if (valids.length) { | ||
valid = valids.has(value); | ||
if( !valid ) | ||
return Promise.reject(new ValidationError(errors.concat( | ||
schema._whitelistError(_.extend({ values: valids.join(', ') }, state))) | ||
)) | ||
if (!valid) return Promise.reject(new ValidationError(errors.concat(schema._whitelistError(babelHelpers._extends({ values: valids.values().join(", ") }, state))))); | ||
} | ||
if( has(invalids, value) ){ | ||
return Promise.reject(new ValidationError(errors.concat( | ||
schema._blacklistError(_.extend({ values: invalids.join(', ') }, state )) | ||
))) | ||
if (invalids.has(value)) { | ||
return Promise.reject(new ValidationError(errors.concat(schema._blacklistError(babelHelpers._extends({ values: invalids.values().join(", ") }, state))))); | ||
} | ||
return Promise | ||
.all(schema.validations.map(fn => fn.call(schema, value, state))) | ||
.then(() => { | ||
if ( errors.length ) | ||
throw new ValidationError(errors) | ||
return Promise.all(schema.validations.map(function (fn) { | ||
return fn.call(schema, value, state); | ||
})).then(function () { | ||
if (errors.length) throw new ValidationError(errors); | ||
return value | ||
}); | ||
return value; | ||
}); | ||
}, | ||
validate(value, options, cb){ | ||
if (typeof options === 'function') | ||
cb = options, options = {} | ||
validate: function (value, options, cb) { | ||
if (typeof options === "function") cb = options, options = {}; | ||
return nodeify(this._validate(value, options, {}), cb) | ||
return nodeify(this._validate(value, options, {}), cb); | ||
}, | ||
isValid(value, options, cb){ | ||
if (typeof options === 'function') | ||
cb = options, options = {} | ||
isValid: function (value, options, cb) { | ||
if (typeof options === "function") cb = options, options = {}; | ||
return nodeify(this | ||
.validate(value, options) | ||
.then(() => true) | ||
.catch(err => { | ||
if ( err instanceof ValidationError) return false | ||
throw err | ||
}), cb) | ||
}, | ||
return nodeify(this.validate(value, options).then(function () { | ||
return true; | ||
}).catch(function (err) { | ||
if (err instanceof ValidationError) return false; | ||
throw err; | ||
}), cb); | ||
}, | ||
default: function(def){ | ||
if( arguments.length === 0) | ||
return _.result(this, '_default') | ||
default: function (def) { | ||
if (arguments.length === 0) { | ||
var dflt = this._default; | ||
return typeof dflt === "function" ? dflt.call(this) : cloneDeep(dflt); | ||
} | ||
var next = this.clone() | ||
next._default = def | ||
return next | ||
var next = this.clone(); | ||
next._default = def; | ||
return next; | ||
}, | ||
strict(){ | ||
var next = this.clone() | ||
next._options.strict = true | ||
return next | ||
strict: function () { | ||
var next = this.clone(); | ||
next._options.strict = true; | ||
return next; | ||
}, | ||
required(msg){ | ||
return this.validation( | ||
{ hashKey: 'required', message: msg || locale.required }, | ||
function(value, params){ | ||
return value !== undefined && this.isType(value) | ||
}) | ||
required: function (msg) { | ||
var _this = this; | ||
return this.validation({ hashKey: "required", message: msg || locale.required }, function (value) { | ||
return value !== undefined && _this.isType(value); | ||
}); | ||
}, | ||
nullable(value){ | ||
var next = this.clone() | ||
next._nullable = value === false ? false : true | ||
return next | ||
nullable: function (value) { | ||
var next = this.clone(); | ||
next._nullable = value === false ? false : true; | ||
return next; | ||
}, | ||
transform(fn){ | ||
transform: function (fn) { | ||
var next = this.clone(); | ||
next.transforms.push(fn) | ||
return next | ||
next.transforms.push(fn); | ||
return next; | ||
}, | ||
validation(msg, fn, passInDoneCallback){ | ||
var opts = msg | ||
, next = this.clone() | ||
, hashKey, errorMsg; | ||
validation: function (msg, validator, passInDoneCallback) { | ||
var opts = msg, | ||
next = this.clone(), | ||
hashKey, | ||
errorMsg; | ||
if(typeof msg === 'string') | ||
opts = { message: msg } | ||
if (typeof msg === "string") opts = { message: msg }; | ||
if( !!next._whitelist.length ) | ||
throw new TypeError('Cannot add validations when specific valid values are specified') | ||
if (next._whitelist.length) throw new TypeError("Cannot add validations when specific valid values are specified"); | ||
errorMsg = interpolate(opts.message) | ||
hashKey = opts.hashKey | ||
errorMsg = interpolate(opts.message); | ||
hashKey = opts.hashKey; | ||
if( !hashKey || !_.has(next._activeTests, hashKey) ){ | ||
if( hashKey ) next._activeTests[hashKey] = true | ||
next.validations.push(validate) | ||
if (!hashKey || !_.has(next._activeTests, hashKey)) { | ||
if (hashKey) next._activeTests[hashKey] = true; | ||
next.validations.push(validate); | ||
} | ||
return next | ||
return next; | ||
function validate(value, state) { | ||
var self = this, result; | ||
var _this = this; | ||
result = passInDoneCallback | ||
? new Promise((resolve, reject) => fn.call(self, value, createCallback(resolve, reject))) | ||
: Promise.resolve(fn.call(this, value)) | ||
var result = new Promise(function (resolve, reject) { | ||
!passInDoneCallback ? resolve(validator.call(_this, value)) : validator.call(_this, value, function (err, valid) { | ||
return err ? reject(err) : resolve(valid); | ||
}); | ||
}); | ||
return result | ||
.then(function(valid){ | ||
if (!valid) | ||
throw new ValidationError(errorMsg(_.extend({}, state, opts.params))) | ||
}) | ||
return result.then(function (valid) { | ||
if (!valid) throw new ValidationError(errorMsg(babelHelpers._extends({}, state, opts.params))); | ||
}); | ||
} | ||
}, | ||
when(key, options){ | ||
when: function (key, options) { | ||
var next = this.clone(); | ||
next._deps.push(key) | ||
next._conditions.push(new Condition(key, next, options)) | ||
return next | ||
next._deps.push(key); | ||
next._conditions.push(new Condition(key, next, options)); | ||
return next; | ||
}, | ||
oneOf(enums, msg) { | ||
var next = this.clone() | ||
oneOf: function (enums, msg) { | ||
var next = this.clone(); | ||
if( !!next.validations.length ) | ||
throw new TypeError('Cannot specify values when there are validation rules specified') | ||
if (next.validations.length) throw new TypeError("Cannot specify values when there are validation rules specified"); | ||
next._whitelistError = interpolate(msg || next._whitelistError || locale.oneOf) | ||
next._whitelistError = interpolate(msg || next._whitelistError || locale.oneOf); | ||
_.each(enums, function(val){ | ||
remove(next._blacklist, val) | ||
add(next._whitelist, val) | ||
}) | ||
return next | ||
}, | ||
enums.forEach(function (val) { | ||
next._blacklist.delete(val); | ||
next._whitelist.add(val); | ||
}); | ||
notOneOf: function(enums, msg) { | ||
var next = this.clone() | ||
next._blacklistError = interpolate(msg || next._blacklistError || locale.notOneOf) | ||
_.each(enums, function(val){ | ||
remove(next._whitelist, val) | ||
add(next._blacklist, val) | ||
}) | ||
return next | ||
return next; | ||
}, | ||
_option: function (key, overrides){ | ||
return _.has(overrides, key) | ||
? overrides[key] : this._options[key] | ||
} | ||
} | ||
notOneOf: function (enums, msg) { | ||
var next = this.clone(); | ||
SchemaType.create = function(spec){ | ||
var Klass = this | ||
initProps = spec | ||
return new Klass; | ||
} | ||
next._blacklistError = interpolate(msg || next._blacklistError || locale.notOneOf); | ||
SchemaType.extend = function(spec){ | ||
var proto = Object.create(this.prototype) | ||
, child = spec.constructor; | ||
enums.forEach(function (val) { | ||
next._whitelist.delete(val); | ||
next._blacklist.add(val); | ||
}); | ||
_.extend(child, this) | ||
_.extend(proto, spec) | ||
return next; | ||
}, | ||
child.prototype = proto | ||
child.prototype.constructor = child | ||
return child | ||
} | ||
_option: function (key, overrides) { | ||
return _.has(overrides, key) ? overrides[key] : this._options[key]; | ||
} | ||
}; | ||
var aliases = { | ||
oneOf: ["equals"] | ||
}; | ||
function add(arr, item){ | ||
if(typeof item === 'function' || (!Array.isArray(item) && _.isObject(item) )) | ||
throw new TypeError | ||
for (var method in aliases) if (_.has(aliases, method)) aliases[method].forEach(function (alias) { | ||
return SchemaType.prototype[alias] = SchemaType.prototype[method]; | ||
}); | ||
if(!has(arr, item)) arr.push(item) | ||
} | ||
function nodeify(promise, cb) { | ||
if (typeof cb !== "function") return promise; | ||
function remove(arr, item){ | ||
var idx = _.indexOf(arr, item) | ||
if( idx !== -1) arr.splice(idx, 1) | ||
} | ||
function has(arr, item){ | ||
return _.any(arr, function(val){ | ||
if (item === val) return true | ||
if( _.isDate(item) ) return +val === +item | ||
return false | ||
}) | ||
} | ||
function nodeify(promise, cb){ | ||
if(typeof cb !== 'function') | ||
return promise | ||
promise | ||
.then(val => cb(null, val)) | ||
.catch(err => cb(err)) | ||
} | ||
function createCallback(resolve, reject){ | ||
return function(err, valid){ | ||
if (err) return reject(err) | ||
resolve(valid) | ||
} | ||
promise.then(function (val) { | ||
return cb(null, val); | ||
}, function (err) { | ||
return cb(err); | ||
}); | ||
} |
@@ -1,93 +0,79 @@ | ||
'use strict'; | ||
var SchemaObject = require('./mixed') | ||
, locale = require('./locale.js').number | ||
, _ = require('lodash') | ||
"use strict"; | ||
var SchemaObject = require("./mixed"); | ||
var locale = require("./locale.js").number; | ||
var _require = require("./util/_"); | ||
var _Number = module.exports = SchemaObject.extend({ | ||
var isDate = _require.isDate; | ||
var inherits = _require.inherits; | ||
constructor: function(){ | ||
if ( !(this instanceof _Number)) return new _Number() | ||
SchemaObject.call(this) | ||
module.exports = NumberSchema; | ||
this._type = 'number' | ||
function NumberSchema() { | ||
if (!(this instanceof NumberSchema)) return new NumberSchema(); | ||
// if ( !_.has(this, '_default') ) | ||
// this._default = 0 | ||
}, | ||
SchemaObject.call(this, { type: "number" }); | ||
isType: function(v) { | ||
if( this._nullable && v === null) return true | ||
return _.isNumber(v) && !_.isNaN(v) | ||
}, | ||
this.transforms.push(function (value) { | ||
if (this.isType(value)) return value; | ||
if (typeof value === "boolean") return value ? 1 : 0; | ||
_coerce: function(value, nullable) { | ||
if ( value == null ) return value | ||
if ( this.isType(value) ) return value | ||
if ( _.isBoolean(value) ) return value ? 1 : 0 | ||
return isDate(value) ? +value : parseFloat(value); | ||
}); | ||
} | ||
return _.isDate(value) ? +value : parseFloat(value) | ||
}, | ||
inherits(NumberSchema, SchemaObject, { | ||
required: function(msg){ | ||
return this.validation( | ||
{ hashKey: 'required', message: msg || locale.required }, | ||
function(v){ | ||
return v != null && this.isType(v) | ||
}) | ||
_typeCheck: function (v) { | ||
return typeof v === "number" && !(v !== +v); | ||
}, | ||
min: function(min, msg){ | ||
msg = msg || locale.min | ||
required: function (msg) { | ||
var _this = this; | ||
return this.validation( | ||
{ message: msg, hashKey: 'min', params: { min: min } } | ||
, function(value){ | ||
return value >= min | ||
}) | ||
return this.validation({ hashKey: "required", message: msg || locale.required }, function (v) { | ||
return v != null && _this.isType(v); | ||
}); | ||
}, | ||
max: function(max, msg){ | ||
msg = msg || locale.max | ||
return this.validation( | ||
{ message: msg, hashKey: 'max', params: { max: max } } | ||
, function(value){ | ||
return value <= max | ||
}) | ||
min: function (min, msg) { | ||
return this.validation({ hashKey: "min", params: { min: min }, message: msg || locale.min }, function (value) { | ||
return value >= min; | ||
}); | ||
}, | ||
positive: function(max, msg){ | ||
msg = msg || locale.positive | ||
return this.min(0, msg) | ||
max: function (max, msg) { | ||
return this.validation({ hashKey: "max", params: { max: max }, message: msg || locale.max }, function (value) { | ||
return value <= max; | ||
}); | ||
}, | ||
negative: function(max, msg){ | ||
msg = msg || locale.negative | ||
return this.max(0, msg) | ||
positive: function (max, msg) { | ||
return this.min(0, msg || locale.positive); | ||
}, | ||
integer: function(msg){ | ||
msg = msg || locale.integer | ||
negative: function (max, msg) { | ||
return this.max(0, msg || locale.negative); | ||
}, | ||
return this | ||
.transform(function(v){ | ||
return v | 0; | ||
}) | ||
.validation(msg, function(val){ | ||
return val === (val | 0); | ||
}) | ||
integer: function (msg) { | ||
msg = msg || locale.integer; | ||
return this.transform(function (v) { | ||
return v | 0; | ||
}).validation(msg, function (val) { | ||
return val === (val | 0); | ||
}); | ||
}, | ||
round: function(method){ | ||
var avail = ['ceil', 'floor', 'round'] | ||
method = (method && method.toLowerCase()) || 'round' | ||
round: function (method) { | ||
var avail = ["ceil", "floor", "round"]; | ||
method = method && method.toLowerCase() || "round"; | ||
if( !_.contains(avail, method.toLowerCase()) ) | ||
throw new TypeError('Only valid options for round() are: ' + avail.join(', ')) | ||
if (avail.indexOf(method.toLowerCase()) === -1) throw new TypeError("Only valid options for round() are: " + avail.join(", ")); | ||
return this.transform(function(v){ | ||
return this.transform(function (v) { | ||
return Math[method](v); | ||
}) | ||
}); | ||
} | ||
}) | ||
}); |
@@ -1,168 +0,162 @@ | ||
'use strict'; | ||
var SchemaObject = require('./mixed') | ||
, Promise = require('es6-promise').Promise | ||
, locale = require('./locale.js').object | ||
, _ = require('lodash') | ||
, Case = require('case') | ||
, Topo = require('./util/topo') | ||
"use strict"; | ||
var babelHelpers = require("./util/babelHelpers.js"); | ||
var MixedSchema = require("./mixed"); | ||
var Promise = require("es6-promise").Promise; | ||
var locale = require("./locale.js").object; | ||
var cloneDeep = require("./util/clone"); | ||
var Topo = require("./util/topo"); | ||
var c = require("case"); | ||
var _Object = module.exports = SchemaObject.extend({ | ||
var _require = require("./util/_"); | ||
constructor: function(spec){ | ||
if ( !(this instanceof _Object)) | ||
return new _Object(spec) | ||
var isObject = _require.isObject; | ||
var isPlainObject = _require.isPlainObject; | ||
var transform = _require.transform; | ||
var assign = _require.assign; | ||
var inherits = _require.inherits; | ||
var has = _require.has; | ||
if ( spec ) | ||
return this.clone().shape(spec); | ||
module.exports = ObjectSchema; | ||
this.fields = {}; | ||
SchemaObject.call(this) | ||
function ObjectSchema(spec) { | ||
if (!(this instanceof ObjectSchema)) return new ObjectSchema(spec); | ||
this._type = 'object' | ||
}, | ||
MixedSchema.call(this, { type: "object", default: function () { | ||
var _this = this; | ||
isType: function(value){ | ||
if( this._nullable && value === null) return true | ||
return value && typeof value === 'object' && !Array.isArray(value) | ||
}, | ||
var dft = transform(this._nodes, function (obj, key) { | ||
var fieldDft = _this.fields[key].default(); | ||
if (fieldDft !== undefined) obj[key] = fieldDft; | ||
}, {}); | ||
_coerce: function(value) { | ||
if (typeof value === 'string') { | ||
return Object.keys(dft).length === 0 ? undefined : dft; | ||
} | ||
}); | ||
this.transforms.push(function (value) { | ||
if (typeof value === "string") { | ||
try { | ||
value = JSON.parse(value) | ||
} catch (err){ value = null } | ||
value = JSON.parse(value); | ||
} catch (err) { | ||
value = null; | ||
} | ||
} | ||
if( value === undefined || this.isType(value) ) | ||
return value | ||
if (this.isType(value)) return value; | ||
return null | ||
}, | ||
return null; | ||
}); | ||
_cast: function(_value, _opts){ | ||
var schema = this | ||
, value = SchemaObject.prototype._cast.call(schema, _value) | ||
this.fields = Object.create(null); | ||
this._nodes = []; | ||
if( schema.isType(value) ) { | ||
var fields = schema.fields | ||
, strip = schema._option('stripUnknown', _opts) === true | ||
, extra = _.without.apply(_, [_.keys(value)].concat(schema._nodes)) | ||
, props = schema._nodes.concat(extra ) | ||
if (spec) return this.shape(spec); | ||
} | ||
//console.log(props) | ||
inherits(ObjectSchema, MixedSchema, { | ||
return _.transform(props, function(obj, prop) { | ||
var exists = _.has(value, prop); | ||
_typeCheck: function (value) { | ||
return value && typeof value === "object" && !Array.isArray(value); | ||
}, | ||
if( exists && fields[prop] ) | ||
obj[prop] = fields[prop].cast(value[prop], { context: obj }) | ||
_cast: function (_value, _opts) { | ||
var schema = this, | ||
value = MixedSchema.prototype._cast.call(schema, _value); | ||
else if( exists && !strip) | ||
obj[prop] = _.cloneDeep(value[prop]) | ||
if (schema.isType(value)) { | ||
var fields = schema.fields, | ||
strip = schema._option("stripUnknown", _opts) === true, | ||
extra = Object.keys(value).filter(function (v) { | ||
return schema._nodes.indexOf(v) === -1; | ||
}), | ||
props = schema._nodes.concat(extra); | ||
else if(fields[prop]) | ||
obj[prop] = fields[prop].default() | ||
return transform(props, function (obj, prop) { | ||
var exists = has(value, prop); | ||
}, {}) | ||
if (exists && fields[prop]) obj[prop] = fields[prop].cast(value[prop], { context: obj });else if (exists && !strip) obj[prop] = cloneDeep(value[prop]);else if (fields[prop]) { | ||
var fieldDefault = fields[prop].default(); | ||
if (fieldDefault !== undefined) obj[prop] = fieldDefault; | ||
} | ||
}, {}); | ||
} | ||
return value | ||
return value; | ||
}, | ||
_validate: function(_value, _opts, _state){ | ||
_validate: function (_value, _opts, _state) { | ||
var context, schema; | ||
_state = _state || {} | ||
context = _state.parent || (_opts || {}).context | ||
schema = this._resolve(context) | ||
_state = _state || {}; | ||
context = _state.parent || (_opts || {}).context; | ||
schema = this._resolve(context); | ||
return SchemaObject.prototype._validate | ||
.call(this, _value, _opts, _state) | ||
.then((value) => { | ||
if(!_.isObject(value)) // only iterate though actual objects | ||
return value | ||
return MixedSchema.prototype._validate.call(this, _value, _opts, _state).then(function (value) { | ||
//console.log('validate ', value) | ||
if (!isObject(value)) // only iterate though actual objects | ||
return value; | ||
return Promise | ||
.all(schema._nodes.map(function(key){ | ||
var field = schema.fields[key] | ||
, path = (_state.path ? (_state.path + '.') : '') + key; | ||
return field._validate(value[key], _opts | ||
, _.defaults({ key, path, parent: value }, _state)) | ||
})) | ||
.then(() => value) | ||
}) | ||
}, | ||
return Promise.all(schema._nodes.map(function (key) { | ||
var field = schema.fields[key], | ||
path = (_state.path ? _state.path + "." : "") + key; | ||
shape: function(schema){ | ||
var next = this.clone() | ||
, toposort = new Topo() | ||
, fields = _.extend(next.fields, schema); | ||
_.each(fields, function(val, key){ | ||
toposort.add(key, { after: val._deps, group: key }) | ||
return field._validate(value[key], _opts, babelHelpers._extends({}, _state, { | ||
key: key, | ||
path: path, | ||
parent: value | ||
})); | ||
})).then(function () { | ||
return value; | ||
}); | ||
}); | ||
next.fields = fields | ||
next._nodes = toposort.nodes | ||
return next | ||
}, | ||
default: function(def){ | ||
var hasDefault = _.has(this, '_default'); | ||
shape: function (schema) { | ||
var next = this.clone(), | ||
toposort = new Topo(), | ||
fields = assign(next.fields, schema); | ||
if(def === undefined) | ||
return hasDefault | ||
? SchemaObject.prototype.default.call(this) | ||
: createDefault(this.fields, this._nodes) | ||
for (var key in schema) if (has(schema, key)) toposort.add(key, { after: schema[key]._deps, group: key }); | ||
return SchemaObject.prototype.default.call(this, def) | ||
next.fields = fields; | ||
next._nodes = toposort.nodes; | ||
return next; | ||
}, | ||
required: function(msg){ | ||
return this.validation( | ||
{ hashKey: 'required', message: msg || locale.required }, | ||
function(value, params){ | ||
return !!value && _.isPlainObject(value) | ||
}) | ||
required: function (msg) { | ||
return this.validation({ hashKey: "required", message: msg || locale.required }, function (value) { | ||
return !!value && isPlainObject(value); | ||
}); | ||
}, | ||
from: function(from, to, alias){ | ||
return this.transform(function(obj){ | ||
var newObj = _.omit(obj, from) | ||
from: function (from, to, alias) { | ||
return this.transform(function (obj) { | ||
var newObj = transform(obj, function (o, val, key) { | ||
return key !== from && (o[key] = val); | ||
}, {}); | ||
newObj[to] = obj[from] | ||
if(alias) newObj[from] = obj[from] | ||
newObj[to] = obj[from]; | ||
if (alias) newObj[from] = obj[from]; | ||
return newObj | ||
}) | ||
return newObj; | ||
}); | ||
}, | ||
camelcase: function(key){ | ||
return this.transform(function(obj){ | ||
return _.transform(obj, function(newobj, val, key ){ | ||
newobj[Case.camel(key)] = val | ||
}, {}) | ||
}) | ||
camelcase: function () { | ||
return this.transform(function (obj) { | ||
return transform(obj, function (newobj, val, key) { | ||
return newobj[c.camel(key)] = val; | ||
}); | ||
}); | ||
}, | ||
constantcase: function(key){ | ||
return this.transform(function(obj){ | ||
return _.transform(obj, function(newobj, val, key ){ | ||
newobj[Case.constant(key)] = val | ||
}, {}) | ||
}) | ||
constantcase: function () { | ||
return this.transform(function (obj) { | ||
return transform(obj, function (newobj, val, key) { | ||
return newobj[c.constant(key)] = val; | ||
}); | ||
}); | ||
} | ||
}) | ||
function createDefault(fields, nodes){ | ||
var dft = _.transform(nodes, function(obj, key){ | ||
var dft = fields[key].default() | ||
if(dft !== undefined ) obj[key] = dft | ||
}, {}) | ||
return Object.keys(dft).length === 0 ? undefined : dft | ||
} | ||
}); |
@@ -1,70 +0,59 @@ | ||
'use strict'; | ||
var SchemaObject = require('./mixed') | ||
, locale = require('./locale.js').string | ||
, _ = require('lodash') | ||
"use strict"; | ||
var MixedSchema = require("./mixed"), | ||
locale = require("./locale.js").string, | ||
inherits = require("./util/_").inherits; | ||
var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g; | ||
var rEmail = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i; | ||
var rUrl = /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i; | ||
var rUrl = /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i; | ||
module.exports = StringSchema; | ||
var _String = module.exports = SchemaObject.extend({ | ||
function StringSchema() { | ||
if (!(this instanceof StringSchema)) return new StringSchema(); | ||
constructor: function(){ | ||
if ( !(this instanceof _String)) | ||
return new _String() | ||
MixedSchema.call(this, { type: "string" }); | ||
} | ||
SchemaObject.call(this) | ||
this._type = 'string' | ||
}, | ||
inherits(StringSchema, MixedSchema, { | ||
isType: function(v){ | ||
if( this._nullable && v === null) return true | ||
return _.isString(v) | ||
_typeCheck: function (value) { | ||
return typeof value === "string"; | ||
}, | ||
_coerce: function(value, nullable) { | ||
if(value == null || this.isType(value)) return value | ||
return value.toString ? value.toString() : '' + value | ||
_coerce: function (value) { | ||
if (this.isType(value)) return value; | ||
return value == null ? "" : value.toString ? value.toString() : "" + value; | ||
}, | ||
required: function(msg){ | ||
return this.validation( | ||
{ hashKey: 'required', message: msg || locale.required }, | ||
function(value, params){ | ||
return value && !!value.length | ||
}) | ||
required: function (msg) { | ||
return this.validation({ hashKey: "required", message: msg || locale.required }, function (value) { | ||
return value && !!value.length; | ||
}); | ||
}, | ||
min: function(min, msg){ | ||
msg = msg || locale.min | ||
min: function (min, msg) { | ||
msg = msg || locale.min; | ||
return this.validation( | ||
{ message: msg, hashKey: 'min', params: { min: min } } | ||
, function(value){ | ||
return value && value.length >= min | ||
}) | ||
return this.validation({ message: msg, hashKey: "min", params: { min: min } }, function (value) { | ||
return value && value.length >= min; | ||
}); | ||
}, | ||
max: function(max, msg){ | ||
msg = msg || locale.max | ||
return this.validation( | ||
{ message: msg, hashKey: 'max', params: { max: max } } | ||
, function(value){ | ||
return value && value.length <= max | ||
}) | ||
max: function (max, msg) { | ||
msg = msg || locale.max; | ||
return this.validation({ message: msg, hashKey: "max", params: { max: max } }, function (value) { | ||
return value && value.length <= max; | ||
}); | ||
}, | ||
matches: function(regex, msg){ | ||
msg = msg || locale.matches | ||
matches: function (regex, msg) { | ||
msg = msg || locale.matches; | ||
return this.validation( | ||
{ message: msg, params: { regex: regex } } | ||
, function(value){ | ||
return regex.test(value) | ||
}) | ||
return this.validation({ message: msg, params: { regex: regex } }, function (value) { | ||
return regex.test(value); | ||
}); | ||
}, | ||
email: function(msg){ | ||
msg = msg || locale.email | ||
email: function (msg) { | ||
msg = msg || locale.email; | ||
@@ -74,4 +63,4 @@ return this.matches(rEmail, msg); | ||
url: function(msg){ | ||
msg = msg || locale.url | ||
url: function (msg) { | ||
msg = msg || locale.url; | ||
@@ -82,37 +71,31 @@ return this.matches(rUrl, msg); | ||
//-- transforms -- | ||
trim: function(msg){ | ||
msg = msg || locale.trim | ||
trim: function (msg) { | ||
msg = msg || locale.trim; | ||
return this | ||
.transform(trim) | ||
.validation(msg, function(val){ | ||
return val === trim(val) | ||
}) | ||
return this.transform(function (v) { | ||
return v.trim(); | ||
}).validation(msg, function (val) { | ||
return val === val.trim(); | ||
}); | ||
}, | ||
lowercase: function(msg){ | ||
msg = msg || locale.lowercase | ||
lowercase: function (msg) { | ||
msg = msg || locale.lowercase; | ||
return this.validation(msg, function(val){ | ||
return val === val.toLowerCase() | ||
}) | ||
.transform(function(v){ | ||
return v.toLowerCase() | ||
}) | ||
return this.validation(msg, function (val) { | ||
return val === val.toLowerCase(); | ||
}).transform(function (v) { | ||
return v.toLowerCase(); | ||
}); | ||
}, | ||
uppercase: function(msg){ | ||
msg = msg || locale.uppercase | ||
uppercase: function (msg) { | ||
msg = msg || locale.uppercase; | ||
return this.validation(msg, function(val){ | ||
return val === val.toUpperCase() | ||
}) | ||
.transform(function(v){ | ||
return v.toUpperCase() | ||
}) | ||
return this.validation(msg, function (val) { | ||
return val === val.toUpperCase(); | ||
}).transform(function (v) { | ||
return v.toUpperCase(); | ||
}); | ||
} | ||
}) | ||
function trim(v){ | ||
return v.trim ? v.trim() : v.replace(rtrim, ""); | ||
} | ||
}); |
@@ -1,50 +0,50 @@ | ||
'use strict'; | ||
var _ = require('lodash') | ||
"use strict"; | ||
var babelHelpers = require("./babelHelpers.js"); | ||
module.exports = Conditional | ||
var _require = require("./_"); | ||
function Conditional(key, current, options){ | ||
var type = current._type; | ||
var has = _require.has; | ||
this.key = key | ||
module.exports = Conditional; | ||
if ( typeof options === 'function') | ||
this.fn = options | ||
else | ||
{ | ||
if( !_.has(options, 'is') ) throw new TypeError('.is must be provided') | ||
if( !options.then && !options.otherwise ) throw new TypeError('.then or .otherwise must be provided') | ||
if( options.then && options.then._type !== type || options.otherwise && options.otherwise._type !== type) | ||
throw new TypeError('cannot return polymorphic conditionals') | ||
var Conditional = (function () { | ||
function Conditional(key, current, options) { | ||
babelHelpers.classCallCheck(this, Conditional); | ||
this.is = options.is | ||
this.then = options.then | ||
this.otherwise = options.otherwise | ||
} | ||
} | ||
var type = current._type; | ||
Conditional.prototype = { | ||
this.key = key; | ||
constructor: Conditional, | ||
if (typeof options === "function") this.fn = options;else { | ||
if (!has(options, "is")) throw new TypeError(".is must be provided"); | ||
if (!options.then && !options.otherwise) throw new TypeError(".then or .otherwise must be provided"); | ||
if (options.then && options.then._type !== type || options.otherwise && options.otherwise._type !== type) throw new TypeError("cannot return polymorphic conditionals"); | ||
resolve: function(ctx, value){ | ||
this.is = options.is; | ||
this.then = options.then; | ||
this.otherwise = options.otherwise; | ||
} | ||
} | ||
Conditional.prototype.resolve = function resolve(ctx, value) { | ||
var schema, matches, then, otherwise; | ||
if( this.fn ) { | ||
schema = this.fn.call(ctx, value) | ||
if (schema !== undefined && !schema.__isYupSchema__) | ||
throw new TypeError('conditions must return a schema object') | ||
if (this.fn) { | ||
schema = this.fn.call(ctx, value, ctx); | ||
if (schema !== undefined && !schema.__isYupSchema__) throw new TypeError("conditions must return a schema object"); | ||
return schema || ctx | ||
return schema || ctx; | ||
} | ||
matches = this.is.__isYupSchema__ | ||
? this.is.isValid(value) | ||
: this.is === value | ||
matches = typeof this.is === "function" ? this.is(value) : this.is === value; | ||
then = this.then ? ctx.concat(this.then) : ctx | ||
otherwise = this.otherwise ? ctx.concat(this.otherwise) : ctx | ||
then = this.then ? ctx.concat(this.then) : ctx; | ||
otherwise = this.otherwise ? ctx.concat(this.otherwise) : ctx; | ||
return matches ? then : otherwise | ||
} | ||
} | ||
return matches ? then : otherwise; | ||
}; | ||
return Conditional; | ||
})(); | ||
module.exports = Conditional; |
@@ -0,10 +1,15 @@ | ||
"use strict"; | ||
var expr = require("property-expr"); | ||
var strReg = /\$\{\s*(\w+)\s*\}/g; | ||
var expr = require('property-expr') | ||
, _ = require('lodash') | ||
, strReg = /\$\{\s*(\w+)\s*\}/g; | ||
module.exports = function strInterpolate(str, obj) { | ||
if (arguments.length === 1) return function (obj) { | ||
return str.replace(strReg, function (_, key) { | ||
return expr.getter(key)(obj) || ""; | ||
}); | ||
}; | ||
var strInterpolate = module.exports = _.curry(function (str, obj){ | ||
return str.replace(strReg, function(s, key){ | ||
return expr.getter(key)(obj) || ''; | ||
}) | ||
}) | ||
return str.replace(strReg, function (_, key) { | ||
return expr.getter(key)(obj) || ""; | ||
}); | ||
}; |
@@ -8,13 +8,13 @@ /** | ||
// 1 YYYY 2 MM 3 DD 4 HH 5 mm 6 ss 7 msec 8 Z 9 ± 10 tzHH 11 tzmm | ||
var isoReg = /^(\d{4}|[+\-]\d{6})(?:-?(\d{2})(?:-?(\d{2}))?)?(?:[ T]?(\d{2}):?(\d{2})(?::?(\d{2})(?:[,\.](\d{1,}))?)?(?:(Z)|([+\-])(\d{2})(?::?(\d{2}))?)?)?$/ | ||
var isoReg = /^(\d{4}|[+\-]\d{6})(?:-?(\d{2})(?:-?(\d{2}))?)?(?:[ T]?(\d{2}):?(\d{2})(?::?(\d{2})(?:[,\.](\d{1,}))?)?(?:(Z)|([+\-])(\d{2})(?::?(\d{2}))?)?)?$/; | ||
module.exports = function parseIsoDate(date) { | ||
var numericKeys = [ 1, 4, 5, 6, 7, 10, 11 ] | ||
, minutesOffset = 0 | ||
, timestamp, struct; | ||
var numericKeys = [1, 4, 5, 6, 7, 10, 11], | ||
minutesOffset = 0, | ||
timestamp, | ||
struct; | ||
if ((struct = isoReg.exec(date))) { | ||
if (struct = isoReg.exec(date)) { | ||
// avoid NaN timestamps caused by “undefined” values being passed to Date.UTC | ||
for (var i = 0, k; (k = numericKeys[i]); ++i) | ||
struct[k] = +struct[k] || 0; | ||
for (var i = 0, k; k = numericKeys[i]; ++i) struct[k] = +struct[k] || 0; | ||
@@ -26,14 +26,10 @@ // allow undefined days and months | ||
// allow arbitrary sub-second precision beyond milliseconds | ||
struct[7] = struct[7] ? + (struct[7] + "00").substr(0, 3) : 0; | ||
struct[7] = struct[7] ? +(struct[7] + "00").substr(0, 3) : 0; | ||
// timestamps without timezone identifiers should be considered local time | ||
if ((struct[8] === undefined || struct[8] === '') && (struct[9] === undefined || struct[9] === '')) | ||
timestamp = +new Date(struct[1], struct[2], struct[3], struct[4], struct[5], struct[6], struct[7]); | ||
else { | ||
if (struct[8] !== 'Z' && struct[9] !== undefined) { | ||
if ((struct[8] === undefined || struct[8] === "") && (struct[9] === undefined || struct[9] === "")) timestamp = +new Date(struct[1], struct[2], struct[3], struct[4], struct[5], struct[6], struct[7]);else { | ||
if (struct[8] !== "Z" && struct[9] !== undefined) { | ||
minutesOffset = struct[10] * 60 + struct[11]; | ||
if (struct[9] === '+') | ||
minutesOffset = 0 - minutesOffset; | ||
if (struct[9] === "+") minutesOffset = 0 - minutesOffset; | ||
} | ||
@@ -43,7 +39,5 @@ | ||
} | ||
} | ||
else | ||
timestamp = Date.parse ? Date.parse(date) : NaN; | ||
} else timestamp = Date.parse ? Date.parse(date) : NaN; | ||
return timestamp; | ||
} | ||
}; |
@@ -1,24 +0,20 @@ | ||
'use strict'; | ||
"use strict"; | ||
module.exports = function expr(obj, path, context){ | ||
var parts = (path || "").split('.') | ||
, part, idx; | ||
module.exports = function expr(obj, path) { | ||
var parts = (path || "").split("."), | ||
part, | ||
idx; | ||
//obj = obj._resolve(context || {}) | ||
while (parts.length) { | ||
part = parts.shift(); | ||
while(parts.length) { | ||
part = parts.shift() | ||
if ((idx = part.indexOf("[")) !== -1) part = part.substr(0, idx); | ||
if( (idx = part.indexOf('[')) !== -1 ) | ||
part = part.substr(0, idx) | ||
if (obj.fields) { | ||
obj = obj.fields[part] || {} | ||
if(idx !== -1) obj = obj._subType ||{} | ||
} | ||
else if (obj._subType) | ||
obj = obj._subType || {} | ||
obj = obj.fields[part] || {}; | ||
if (idx !== -1) obj = obj._subType || {}; | ||
} else if (obj._subType) obj = obj._subType || {}; | ||
} | ||
return obj | ||
} | ||
return obj; | ||
}; |
@@ -1,3 +0,1 @@ | ||
var _ = require('lodash') | ||
// Copyright (c) 2012-2014, Walmart and other contributors. | ||
@@ -16,3 +14,3 @@ // All rights reserved. | ||
// permission. | ||
// | ||
// | ||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||
@@ -31,8 +29,7 @@ // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
module.exports = Topo; | ||
function Topo() { | ||
this._items = []; | ||
this.nodes = []; | ||
this._items = []; | ||
this.nodes = []; | ||
} | ||
@@ -48,10 +45,10 @@ | ||
var after = [].concat(options.after || []); | ||
var group = options.group || '?'; | ||
var group = options.group || "?"; | ||
assert(before.indexOf(group) === -1, 'Item cannot come before itself:' + group); | ||
assert(before.indexOf('?') === -1, 'Item cannot come before unassociated items'); | ||
assert(after.indexOf(group) === -1, 'Item cannot come after itself: ' + group); | ||
assert(after.indexOf('?') === -1, 'Item cannot come after unassociated items'); | ||
assert(before.indexOf(group) === -1, "Item cannot come before itself:" + group); | ||
assert(before.indexOf("?") === -1, "Item cannot come before unassociated items"); | ||
assert(after.indexOf(group) === -1, "Item cannot come after itself: " + group); | ||
assert(after.indexOf("?") === -1, "Item cannot come after unassociated items"); | ||
_.each([].concat(nodes), function (node, i) { | ||
[].concat(nodes).forEach(function (node, i) { | ||
@@ -72,3 +69,3 @@ var item = { | ||
assert(!error, 'item' + (group !== '?' ? ' added into group `' + group : '`') + ' created a dependencies error'); | ||
assert(!error, "item" + (group !== "?" ? " added into group `" + group : "`") + " created a dependencies error"); | ||
@@ -78,3 +75,2 @@ return this.nodes; | ||
Topo.prototype._sort = function () { | ||
@@ -89,3 +85,3 @@ | ||
var item = this._items[i]; | ||
var seq = item.seq; // Unique across all items | ||
var seq = item.seq; // Unique across all items | ||
var group = item.group; | ||
@@ -112,3 +108,3 @@ | ||
var graphNodes = _.keys(graph); | ||
var graphNodes = Object.keys(graph); | ||
for (i = 0, il = graphNodes.length; i < il; ++i) { | ||
@@ -118,3 +114,3 @@ var node = graphNodes[i]; | ||
var graphNodeItems = _.keys(graph[node]); | ||
var graphNodeItems = Object.keys(graph[node]); | ||
for (j = 0, jl = graphNodeItems.length; j < jl; ++j) { | ||
@@ -124,4 +120,4 @@ var group = graph[node][graphNodeItems[j]]; | ||
_.each(groups[group], function (d) { | ||
expandedGroups.push(d); | ||
groups[group].forEach(function (d) { | ||
return expandedGroups.push(d); | ||
}); | ||
@@ -135,3 +131,3 @@ } | ||
var afterNodes = _.keys(graphAfters); | ||
var afterNodes = Object.keys(graphAfters); | ||
for (i = 0, il = afterNodes.length; i < il; ++i) { | ||
@@ -142,4 +138,4 @@ var group = afterNodes[i]; | ||
for (j = 0, jl = groups[group].length; j < jl; ++j) { | ||
var node = groups[group][j]; | ||
graph[node] = graph[node].concat(graphAfters[group]); | ||
var node = groups[group][j]; | ||
graph[node] = graph[node].concat(graphAfters[group]); | ||
} | ||
@@ -152,3 +148,3 @@ } | ||
var ancestors = {}; | ||
graphNodes = _.keys(graph); | ||
graphNodes = Object.keys(graph); | ||
for (i = 0, il = graphNodes.length; i < il; ++i) { | ||
@@ -198,3 +194,3 @@ var node = graphNodes[i]; | ||
if (next !== null) { | ||
next = next.toString(); // Normalize to string TODO: replace with seq | ||
next = next.toString(); // Normalize to string TODO: replace with seq | ||
visited[next] = true; | ||
@@ -206,16 +202,15 @@ sorted.push(next); | ||
if (sorted.length !== this._items.length) { | ||
return new Error('Invalid dependencies'); | ||
return new Error("Invalid dependencies"); | ||
} | ||
var seqIndex = {}; | ||
_.each(this._items, function (item) { | ||
seqIndex[item.seq] = item; | ||
this._items.forEach(function (item) { | ||
return seqIndex[item.seq] = item; | ||
}); | ||
var sortedNodes = []; | ||
this._items = _.map(sorted, function (value) { | ||
var item = seqIndex[value]; | ||
sortedNodes.push(item.node); | ||
return item; | ||
this._items = sorted.map(function (value) { | ||
var item = seqIndex[value]; | ||
sortedNodes.push(item.node); | ||
return item; | ||
}); | ||
@@ -226,4 +221,4 @@ | ||
function assert(condition, msg){ | ||
if(!condition) throw new Error(msg) | ||
function assert(condition, msg) { | ||
if (!condition) throw new Error(msg); | ||
} |
@@ -1,11 +0,10 @@ | ||
'use strict'; | ||
"use strict"; | ||
module.exports = ValidationError; | ||
function ValidationError(errors) { | ||
this.name = "ValidationError"; | ||
this.errors = errors == null ? [] : [].concat(errors) | ||
this.message = this.errors[0] | ||
this.name = "ValidationError"; | ||
this.errors = errors == null ? [] : [].concat(errors); | ||
this.message = this.errors[0]; | ||
if (Error.captureStackTrace) | ||
Error.captureStackTrace(this, ValidationError); | ||
if (Error.captureStackTrace) Error.captureStackTrace(this, ValidationError); | ||
} | ||
@@ -12,0 +11,0 @@ |
{ | ||
"name": "yup", | ||
"version": "0.5.1", | ||
"version": "0.6.0", | ||
"description": "Dead simple Object schema validation", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "mocha -R spec ./test/*.js" | ||
"test": "gulp compile && mocha -R spec ./test/*.js" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/theporchrat/yup.git" | ||
"url": "https://github.com/jquense/yup.git" | ||
}, | ||
"author": "@theporchrat Jason Quense", | ||
"author": "@monasticpanic Jason Quense", | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/theporchrat/yup/issues" | ||
"url": "https://github.com/jquense/yup/issues" | ||
}, | ||
"homepage": "https://github.com/theporchrat/yup", | ||
"homepage": "https://github.com/jquense/yup", | ||
"devDependencies": { | ||
"chai": "^1.9.1", | ||
"chai-as-promised": "^4.1.1", | ||
"event-stream": "^3.1.7", | ||
"del": "^1.1.1", | ||
"gulp": "~3.8.8", | ||
"gulp-babel-helpers": "^1.1.2", | ||
"gulp-mocha": "^1.0.0", | ||
"gulp-plumber": "^0.6.5", | ||
"jstransform": "^8.2.0", | ||
"gulp-rename": "^1.2.2", | ||
"mocha": "^1.21.4", | ||
@@ -32,8 +33,7 @@ "sinon": "^1.10.3", | ||
"dependencies": { | ||
"case": "^1.0.3", | ||
"case": "^1.2.1", | ||
"es6-promise": "^2.0.0", | ||
"fn-name": "~1.0.1", | ||
"lodash": "^2.4.1", | ||
"property-expr": "~1.0.1" | ||
} | ||
} |
149
README.md
@@ -8,10 +8,29 @@ Yup | ||
## Changes 5.0 | ||
## Changes in 0.6.0 | ||
- __breaking__ `isValid` is now async, provide a node style callback, or use the promise the method returns to read the validity. This change allows | ||
for more robust validations, specifically remote ones for client code (or db queries for server code). The `cast` method is still, and will remain, synchronuous. | ||
- added `validate` method (also async) which resolves to the value, and rejects with a new `ValidationError` | ||
__breaking__ | ||
- Removed the `extend` and `create` methods. Use whatever javascript inheritance patterns you want instead. | ||
- the resolution order of defaults and coercions has changed. as well as the general handling of `null` values. | ||
+ Number: `null` will coerce to `false` when `nullable()` is not specified. `NaN` values will now fail `isType()` checks | ||
+ String: `null` will coerce to `''` when `nullable()` is not specified | ||
+ Date: Invalid dates will not be coerced to `null`, but left as invalid date, This is probably not a problem for anyone as invalid dates will also fail `isType()` checks | ||
- default values are cloned everytime they are returned, so it is impossible to share references to defaults across schemas. No one should be doing that anyway | ||
- stopped pretending that using schemas as conditions in `when()` actually worked (it didn't) | ||
__other changes__ | ||
- `transform()` now passes the original value to each transformer. Allowing you to recover from a bad transform. | ||
- added the `equals()` alias for `oneOf` | ||
## Usage | ||
- [Yup](#yup-1) | ||
+ [`mixed`](#mixed) | ||
+ [`string`](#string) | ||
+ [`number`](#number) | ||
+ [`boolean`](#boolean) | ||
+ [`date`](#date) | ||
+ [`array`](#array) | ||
+ [`object`](#array) | ||
- [Extending Schema Types](#extending-schema-types) | ||
You define and create schema objects. Schema objects are immutable, so each call of a method returns a _new_ schema object. | ||
@@ -49,2 +68,3 @@ | ||
### `yup` | ||
@@ -68,2 +88,3 @@ | ||
yup.reach | ||
yup.ValidationError | ||
``` | ||
@@ -75,3 +96,9 @@ | ||
### `ValidationError` | ||
Thrown on failed validations, with the following properties | ||
- `name`: ValidationError | ||
- `errors`: array of error messages | ||
### `mixed` | ||
@@ -145,3 +172,3 @@ | ||
Runs a type check against the passed in `value`. It returns true if it matches, it does not cast the value. When `nullable()` is set `null` is considered a valid value of the type. | ||
Runs a type check against the passed in `value`. It returns true if it matches, it does not cast the value. When `nullable()` is set `null` is considered a valid value of the type. You should use `isType` for all Schema type checks. | ||
@@ -154,14 +181,25 @@ #### `mixed.strict()` (default: `false`) | ||
Sets a default value to use when the value is missing. The `value` argument can also be a function that returns a default value (useful for setting defaults of by reference types like arrays or objects). | ||
Sets a default value to use when the value is `undefined`. The default value will be cloned on each use wich can incur performance penalty for objects and arrays. To avoid this overhead you can also pass a function that returns an new default. | ||
```js | ||
yup.string.default('nothing'); | ||
yup.object.default({ number: 5}); // object will be cloned every time a default is needed | ||
yup.object.default(() => ({ number: 5})); // this is cheaper | ||
yup.date.default(() => new Date()); //also helpful for defaults that change over time | ||
``` | ||
#### `mixed.nullable(isNullable)` (default: `false`) | ||
Indicates that `null` is a valid value for the schema. Without `nullable()` | ||
`null` is treated as an empty value and will fail `isType()` checks. | ||
`null` is treated as a different type and will fail `isType()` checks. | ||
#### `mixed.required(msg)` | ||
Mark the schema as required. All field values asside from `undefined` meet this requirement. | ||
Mark the schema as required. All field values apart from `undefined` meet this requirement. | ||
#### `mixed.oneOf(arrayOfValues)` | ||
#### `mixed.oneOf(arrayOfValues)` Alias: `equals` | ||
@@ -189,4 +227,8 @@ Whitelist a set of values. Values added are automatically removed from any blacklist if they are in it. | ||
Adjust the schema based on a sibling or sibling children fields. You can provide an object literal where the key `is` is a yup schema type, `then` provides the true schema and/or `otherwise` for the failure condition. Alternatively you can provide a function the returns a schema ( the `this` value is the current schema). `when` conditions are additive. | ||
Adjust the schema based on a sibling or sibling children fields. You can provide an object literal where the key `is` is value or a matcher function, `then` provides the true schema and/or `otherwise` for the failure condition. | ||
`is` conditions are strictly compared (`===`) if you want to use a different form of equality you can provide a function like: `is: (value) => value == true`. | ||
Alternatively you can provide a function the returns a schema (called with teh value of the key and teh current schema). `when` conditions are additive. | ||
```javascript | ||
@@ -198,15 +240,17 @@ var inst = yup.object({ | ||
.when('isBig', { | ||
is: true, | ||
is: true, // alternatively: (val) => val == true | ||
then: yup.number().min(5), | ||
otherwise: yup.number().min(0) | ||
}) | ||
.when('other', function(v){ | ||
if (v === 4) return this.max(6) | ||
}) | ||
.when('other', (other, schema) => other === 4 | ||
? schema.max(6) | ||
: schema) | ||
}) | ||
``` | ||
__note: because `when` conditions must be resolved during `cast()`, a synchronous api, `is` cannot be a schema object as checking schema validity it is asynchronous__ | ||
#### `mixed.validation(message, fn, [callbackStyleAsync])` | ||
Adds a validation function to the validation chain. Validations are run after any object is cast. Many types have some validations built in, but you can create custom ones easily. All validations are run asynchronously, as such their order cannot be guaranteed. The validation function should either return `true` or `false` directly, or return a promsie that resolves `true` or `false. If you perfer the Node callback style, pass `true` for `callbackStyleAsync` and the validation function will pass in an additional `done` function as the last parameter, which should be called with the validity. | ||
Adds a validation function to the validation chain. Validations are run after any object is cast. Many types have some validations built in, but you can create custom ones easily. All validations are run asynchronously, as such their order cannot be guaranteed. The validation function should either return `true` or `false` directly, or return a promsie that resolves `true` or `false`. If you perfer the Node callback style, pass `true` for `callbackStyleAsync` and the validation function will pass in an additional `done` function as the last parameter, which should be called with the validity. | ||
@@ -238,3 +282,3 @@ for the `message` argument you can provide a string which is will interpolate certain keys if specified, all validations are given a `path` value which indicates location. | ||
```javascript | ||
var schema = yup.string().transform(function(value){ | ||
var schema = yup.string().transform(function(value, originalvalue){ | ||
return value.toUpperCase() | ||
@@ -245,11 +289,5 @@ }); | ||
#### Static Methods | ||
- `Mixed.create(props)` - creates a new instance of a type with the specified props | ||
- `Mixed.extend(protoProps)` - Backbone-esque object inheritance. extend returns a new constructor function that inherits from the type. All types inherit `mixed` in this manner. Be sure to include a `constructor` property it is not automatically created. | ||
### string | ||
Define a string schema. __note: strings are nullable by default.__ | ||
Define a string schema. Supports all the same methods as [`mixed`](#mixed). | ||
@@ -303,3 +341,3 @@ ```javascript | ||
Define a number schema. | ||
Define a number schema. Supports all the same methods as [`mixed`](#mixed). | ||
@@ -341,3 +379,3 @@ ```javascript | ||
Define a boolean schema. | ||
Define a boolean schema. Supports all the same methods as [`mixed`](#mixed). | ||
@@ -351,3 +389,3 @@ ```javascript | ||
Define a Date schema. By default ISO date strings will parse correctly. | ||
Define a Date schema. By default ISO date strings will parse correctly, for more robust parsing options see the extending schema types at the end of the readme. Supports all the same methods as [`mixed`](#mixed). | ||
@@ -369,3 +407,3 @@ ```javascript | ||
Define an array schema. Arrays can be typed or not, When specifying the element type, `cast` and `isValid` will apply to the elements as well. Options passed into `isValid` are passed also passed to child schemas. | ||
Define an array schema. Arrays can be typed or not, When specifying the element type, `cast` and `isValid` will apply to the elements as well. Options passed into `isValid` are passed also passed to child schemas. Supports all the same methods as [`mixed`](#mixed). | ||
@@ -410,3 +448,3 @@ ```javascript | ||
Define an object schema. Options passed into `isValid` are also passed to child schemas. | ||
Define an object schema. Options passed into `isValid` are also passed to child schemas. Supports all the same methods as [`mixed`](#mixed). | ||
@@ -449,1 +487,56 @@ ```javascript | ||
Transforms all object keys to CONSTANT_CASE. | ||
## Extending Schema Types | ||
The simplest way to extend an existing type is just to cache a configured schema and use that through yuor application. | ||
```js | ||
var yup = require('yup'); | ||
var parseFormats = [ 'MMM dd, yyy'] | ||
var invalidDate = new Date(''); | ||
module.exports = yup.date() | ||
.transform(function(vale, originalValue){ | ||
if ( this.isType(value) ) return value | ||
//the default transform failed so lets try it with Moment instead | ||
value = Moment(originalValue, parseFormats) | ||
return date.isValid() ? date.toDate() : invalidDate | ||
}) | ||
``` | ||
Alternatively, each schema is a normal javascript constructor function that you can inherit from. Generally you should not inherit from `mixed` unless you know what you are doing, better to think of it as an abastract class. The other types are fair game though. | ||
```js | ||
var inherits = require('inherits') | ||
var invalidDate = new Date(''); // our failed to coerce value | ||
function MomentDateSchemaType(){ | ||
// so we don't need to use the `new` keyword | ||
if ( !(this instanceof MomentDateSchemaType)) | ||
return new MomentDateSchemaType() | ||
yup.date.call(this) | ||
this._formats = []; | ||
// add to the default transforms | ||
this.transforms.push(function(value, originalValue) { | ||
if ( this.isType(value) ) // we have a valid value | ||
return value | ||
//the previous transform failed so lets try it with Moment instead | ||
value = Moment(originalValue, this._formats) | ||
return date.isValid() ? date.toDate() : invalidDate | ||
}) | ||
} | ||
inherits(MomentDateSchemaType, yup.date) | ||
MomentDateSchemaType.prototype.format(format){ | ||
if (!format) throw new Error('must enter a valid format') | ||
var next = this.clone(); //never mutate a schema | ||
next = [next._formats].concat(format) | ||
return next | ||
} | ||
``` |
@@ -6,5 +6,5 @@ 'use strict'; | ||
, Promise = require('es6-promise').Promise | ||
, string = require('../dist/string') | ||
, number = require('../dist/number') | ||
, array = require('../dist/array'); | ||
, string = require('../lib/string') | ||
, number = require('../lib/number') | ||
, array = require('../lib/array'); | ||
@@ -23,4 +23,4 @@ chai.use(chaiAsPromised); | ||
inst.of(number()).cast(['4', 5, false]).should.eql([4,5,0]) | ||
inst.of(string()).cast(['4', 5, false]).should.eql(['4','5','false']) | ||
inst.of(number()).cast(['4', 5, false]).should.eql([4, 5, 0]) | ||
inst.of(string()).cast(['4', 5, false]).should.eql(['4', '5', 'false']) | ||
@@ -35,3 +35,3 @@ chai.expect( | ||
chai.expect(inst.default()).to.equal(undefined) | ||
inst.default(function(){ return [1,2,3] }).default().should.eql([1,2,3]) | ||
inst.default(function(){ return [1, 2, 3] }).default().should.eql([1, 2, 3]) | ||
}) | ||
@@ -59,2 +59,5 @@ | ||
return Promise.all([ | ||
array().of(number().max(5)).isValid().should.eventually.equal(true), | ||
array().isValid(null).should.eventually.equal(false), | ||
@@ -61,0 +64,0 @@ array().nullable().isValid(null).should.eventually.equal(true), |
@@ -6,3 +6,3 @@ 'use strict'; | ||
, Promise = require('es6-promise').Promise | ||
, bool = require('../dist/boolean'); | ||
, bool = require('../lib/boolean'); | ||
@@ -23,3 +23,6 @@ chai.use(chaiAsPromised); | ||
chai.expect( | ||
inst.cast(null)).to.equal(null) | ||
inst.cast(null)).to.equal(false) | ||
chai.expect( | ||
inst.nullable().cast(null)).to.equal(null) | ||
}) | ||
@@ -52,6 +55,9 @@ | ||
return Promise.all([ | ||
bool().isValid(null).should.eventually.equal(false), | ||
bool().isValid(null).should.eventually.equal(true), //coerced to false | ||
bool().strict().isValid(null).should.eventually.equal(false), | ||
bool().nullable().isValid(null).should.eventually.equal(true), | ||
inst.validate().should.be.rejected.then(null, function(err){ | ||
inst.validate().should.be.rejected.then(function(err){ | ||
err.errors.length.should.equal(1) | ||
@@ -58,0 +64,0 @@ err.errors[0].should.contain('required') |
@@ -5,3 +5,3 @@ 'use strict'; | ||
, Promise = require('es6-promise').Promise | ||
, date = require('../dist/date'); | ||
, date = require('../lib/date'); | ||
@@ -11,2 +11,5 @@ chai.use(chaiAsPromised); | ||
function isValidDate(date){ | ||
return date instanceof Date && !isNaN(date.getTime()) | ||
} | ||
@@ -19,4 +22,8 @@ describe('Date types', function(){ | ||
inst.cast(new Date).should.be.a('date') | ||
inst.cast('jan 15 2014').should.eql(new Date(2014,0,15)) | ||
inst.cast(null).should.not.satisfy(isValidDate) | ||
inst.cast('').should.not.satisfy(isValidDate) | ||
inst.cast(new Date()).should.be.a('date') | ||
inst.cast(new Date()).should.be.a('date') | ||
inst.cast('jan 15 2014').should.eql(new Date(2014, 0, 15)) | ||
inst.cast('2014-09-23T19:25:25Z').should.eql(new Date(1411500325000)) | ||
@@ -28,7 +35,7 @@ }) | ||
inst.isType(new Date).should.equal(true) | ||
inst.isType(new Date()).should.equal(true) | ||
inst.isType(false).should.equal(false) | ||
inst.isType(null).should.equal(false) | ||
inst.isType(NaN).should.equal(false) | ||
inst.nullable().isType(new Date).should.equal(true) | ||
inst.nullable().isType(new Date()).should.equal(true) | ||
}) | ||
@@ -44,4 +51,4 @@ | ||
inst.isValid(new Date(2014,0,15)).should.eventually.equal(true), | ||
inst.isValid(new Date(2014,7,15)).should.eventually.equal(false), | ||
inst.isValid(new Date(2014, 0, 15)).should.eventually.equal(true), | ||
inst.isValid(new Date(2014, 7, 15)).should.eventually.equal(false), | ||
inst.isValid('5').should.eventually.equal(true), | ||
@@ -77,3 +84,3 @@ | ||
v.isValid(new Date(2014, 9, 15)).should.eventually.equal(false), | ||
v.nullable(true).isValid(null).should.eventually.equal(true), | ||
v.nullable(true).isValid(null).should.eventually.equal(true) | ||
]) | ||
@@ -80,0 +87,0 @@ }) |
@@ -5,6 +5,6 @@ 'use strict'; | ||
, chaiAsPromised = require('chai-as-promised') | ||
, ValidationError = require('../dist/util/validation-error') | ||
, ValidationError = require('../lib/util/validation-error') | ||
, Promise = require('es6-promise').Promise | ||
, mixed = require('../dist/mixed') | ||
, string = require('../dist/string'); | ||
, mixed = require('../lib/mixed') | ||
, string = require('../lib/string'); | ||
@@ -62,3 +62,3 @@ chai.use(chaiAsPromised); | ||
it('should respect strict', function(){ | ||
var inst = string().oneOf(['hello', '5']) | ||
var inst = string().equals(['hello', '5']) | ||
@@ -113,3 +113,3 @@ return Promise.all([ | ||
inst._validate(undefined, {}, { parent: { prop: 1 }}).should.be.fulfilled, | ||
inst._validate('hello', {}, { parent: { prop: 5 }}).should.be.fulfilled, | ||
inst._validate('hello', {}, { parent: { prop: 5 }}).should.be.fulfilled | ||
]) | ||
@@ -119,3 +119,3 @@ .then(function(){ | ||
inst = string().when('prop', { | ||
is: 5, | ||
is: function(val) { return val === 5 }, | ||
then: string().required(), | ||
@@ -128,3 +128,3 @@ otherwise: string().min(4) | ||
inst._validate('hello', {}, { parent: { prop: 1 }}).should.be.fulfilled, | ||
inst._validate('hel', {}, { parent: { prop: 1 }}).should.be.rejected, | ||
inst._validate('hel', {}, { parent: { prop: 1 }}).should.be.rejected | ||
]) | ||
@@ -131,0 +131,0 @@ }) |
@@ -6,3 +6,3 @@ 'use strict'; | ||
, Promise = require('es6-promise').Promise | ||
, number = require('../dist/number'); | ||
, number = require('../lib/number'); | ||
@@ -16,6 +16,6 @@ chai.use(chaiAsPromised); | ||
var inst = number(), date = new Date | ||
var inst = number(), date = new Date() | ||
chai.expect( | ||
inst.cast(null)).to.equal(null) | ||
inst.cast(null)).to.eql(NaN) | ||
@@ -33,2 +33,3 @@ inst.cast('5').should.equal(5) | ||
inst.round().cast(45.444444).should.equal(45) | ||
;(function(){ inst.round('fasf') }).should.throw(TypeError) | ||
@@ -35,0 +36,0 @@ }) |
@@ -6,9 +6,9 @@ 'use strict'; | ||
, Promise = require('es6-promise').Promise | ||
, mixed = require('../dist/mixed') | ||
, string = require('../dist/string') | ||
, date = require('../dist/date') | ||
, number = require('../dist/number') | ||
, bool = require('../dist/boolean') | ||
, array = require('../dist/array') | ||
, object = require('../dist/object'); | ||
, mixed = require('../lib/mixed') | ||
, string = require('../lib/string') | ||
, date = require('../lib/date') | ||
, number = require('../lib/number') | ||
, bool = require('../lib/boolean') | ||
, array = require('../lib/array') | ||
, object = require('../lib/object'); | ||
@@ -35,6 +35,6 @@ chai.use(chaiAsPromised); | ||
.shape({ hello: number() }) | ||
.cast("{ \"hello\": \"5\" }").should.eql({ hello: 5 }) | ||
.cast('{ \"hello\": \"5\" }').should.eql({ hello: 5 }) | ||
chai.expect( | ||
object().cast('dfhdfh')).to.equal(null) | ||
object().cast('dfhdfh')).to.eql(null) | ||
@@ -93,2 +93,6 @@ inst = inst.shape({ | ||
// return inst.validate().should.be.rejected.then(function(err){ | ||
// console.log(err) | ||
// }) | ||
return inst.validate(obj).should.be.rejected | ||
@@ -105,2 +109,3 @@ .then(function(err){ | ||
inst.isValid().should.eventually.equal(true), | ||
inst.validate(obj).should.be.rejected.then(function(err){ | ||
@@ -115,3 +120,3 @@ err.errors[0].should.contain('this.arr[1]') | ||
var inst = object({ | ||
prop: mixed(), | ||
prop: mixed() | ||
}) | ||
@@ -123,6 +128,7 @@ | ||
it('should create a reasonable default', function(){ | ||
object({ | ||
str: string(), | ||
nest: object({ | ||
str: string().default('hi'), | ||
str: string().default('hi') | ||
}) | ||
@@ -145,3 +151,3 @@ }) | ||
}) | ||
.default()).to.equal(undefined) | ||
.default()).to.eql(undefined) | ||
}) | ||
@@ -151,6 +157,13 @@ | ||
var inst = object().shape({ | ||
prop: mixed(), | ||
prop: mixed() | ||
}) | ||
// return inst.isValid({}).should.be.fulfilled.then(function(err){ | ||
// console.log(err) | ||
// }) | ||
return Promise.all([ | ||
// inst.validate({}).should.be.rejected.then(function(err){ | ||
// console.log(err) | ||
// }), | ||
inst.isValid({}).should.eventually.equal(true), | ||
@@ -166,3 +179,3 @@ | ||
prop: mixed(), | ||
other: mixed(), | ||
other: mixed() | ||
}) | ||
@@ -247,3 +260,3 @@ | ||
value.should.deep.equal(10) | ||
}), | ||
}) | ||
]) | ||
@@ -250,0 +263,0 @@ }) |
@@ -5,6 +5,5 @@ 'use strict'; | ||
, Promise = require('es6-promise').Promise | ||
, ValidationError = require('../dist/util/validation-error') | ||
, sinonChai = require("sinon-chai") | ||
, sinonChai = require('sinon-chai') | ||
, chaiAsPromised = require('chai-as-promised') | ||
, string = require('../dist/string'); | ||
, string = require('../lib/string'); | ||
@@ -15,3 +14,2 @@ chai.use(chaiAsPromised); | ||
describe('String types', function(){ | ||
@@ -26,4 +24,7 @@ | ||
chai.expect( | ||
inst.cast(null)).to.equal(null) | ||
inst.cast(null)).to.equal('') | ||
chai.expect( | ||
inst.nullable().cast(null)).to.equal(null) | ||
inst.cast('3').should.equal('3') | ||
@@ -30,0 +31,0 @@ inst.cast(false).should.equal('false') |
'use strict'; | ||
/*global describe, it */ | ||
var chai = require('chai') | ||
, reach = require('../dist/util/reach') | ||
, number = require('../dist/number') | ||
, array = require('../dist/array') | ||
, bool = require('../dist/boolean') | ||
, object = require('../dist/object'); | ||
, reach = require('../lib/util/reach') | ||
, number = require('../lib/number') | ||
, array = require('../lib/array') | ||
, bool = require('../lib/boolean') | ||
, object = require('../lib/object'); | ||
@@ -10,0 +10,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
132678
4
53
526
0
11
2946
1
- Removedlodash@^2.4.1
- Removedlodash@2.4.2(transitive)
Updatedcase@^1.2.1