You're Invited:Meet the Socket Team at BlackHat and DEF CON in Las Vegas, Aug 4-6.RSVP
Socket
Book a DemoInstallSign in
Socket

immutable-validation

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

immutable-validation - npm Package Compare versions

Comparing version

to
0.1.0

.eslintrc

237

dist/ImmutableValidation.js

@@ -96,4 +96,8 @@ (function webpackUniversalModuleDefinition(root, factory) {

var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })();
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; }; })();
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

@@ -106,3 +110,84 @@

var hasProp = Object.prototype.hasOwnProperty;
var SELF = 'self';
function buildRuleReducer(dataToValidate) {
return function (messagesForProp, rule) {
var _runRule = runRule(rule, dataToValidate);
var isValid = _runRule.isValid;
var message = _runRule.message;
if (!isValid) {
return messagesForProp.add(message);
}
return messagesForProp;
};
}
function resultReducer(isValid, _ref) {
var _ref2 = _slicedToArray(_ref, 2);
var prop = _ref2[1];
if (isValid === false) return false;
if (prop.has('isValid')) return prop.get('isValid');
if (prop.has(SELF) && prop.get(SELF).count()) return false;
return prop.entrySeq().filter(function (_ref3) {
var _ref32 = _slicedToArray(_ref3, 1);
var key = _ref32[0];
return key !== 'isValid' && key !== SELF;
}).reduce(resultReducer, isValid);
}
function runRule(rule, val) {
var result = rule(val);
if (!hasProp.call(result, 'isValid') || !hasProp.call(result, 'message')) {
throw new Error('Rules must return a result with the properties `isValid` and `message`. Use ImmutableValidation.rule()');
}
return result;
}
function getIterable(propName, accessor, data) {
var iter = accessor(data);
if (!(0, _util.isIterable)(iter)) {
throw new TypeError('`' + propName + '` is a non-iterable value. ruleForEach applies a validation rule to each item in an iterable');
}
return iter;
}
function setMerged(vState, propName, newData) {
if (vState.has(propName)) {
newData = vState.get(propName).merge(newData);
}
return vState.set(propName, (0, _immutable.Map)(newData));
}
var ValidatorWrapper = (function () {
function ValidatorWrapper(chooseValidator) {
_classCallCheck(this, ValidatorWrapper);
this.getValidator = chooseValidator;
}
_createClass(ValidatorWrapper, [{
key: 'validate',
value: function validate(dataToValidate) {
var v = this.getValidator(dataToValidate);
if (!Validator.isInstance(v)) {
return (0, _immutable.Map)({ isValid: true });
}
return v.validate(dataToValidate);
}
}]);
return ValidatorWrapper;
})();
var Validator = (function () {

@@ -113,6 +198,4 @@ function Validator() {

this.ruleSets = [];
this.validationState = new _immutable.Map({
isValid: true,
messages: new _immutable.Map()
this.validationState = (0, _immutable.Map)({
isValid: true
});

@@ -128,13 +211,38 @@ }

return rules.length === 1 && Validator.isInstance(rules[0]) ? this._addValidatorToRuleSets(propName, accessor, rules[0]) : this._addRuleSetToRuleSets(propName, accessor, rules);
return Validator.isInstance(rules[0]) ? this._addValidatorToRuleSets(propName, accessor, rules[0]) : this._addRuleSetToRuleSets(propName, accessor, rules);
}
}, {
key: 'ruleForEach',
value: function ruleForEach(propName, accessor) {
for (var _len2 = arguments.length, rules = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
rules[_key2 - 2] = arguments[_key2];
}
return Validator.isInstance(rules[0]) ? this._addForEachValidatorToRuleSets(propName, accessor, rules[0]) : this._addForEachRuleSetToRuleSets(propName, accessor, rules);
}
}, {
key: 'validate',
value: function validate(dataToValidate) {
var newVState = this.ruleSets.reduce(function (messages, ruleSet) {
return ruleSet(dataToValidate, messages);
}, (0, _immutable.Map)());
var isValid = newVState.entrySeq().filter(function (_ref4) {
var _ref42 = _slicedToArray(_ref4, 1);
var key = _ref42[0];
return key !== 'isValid';
}).reduce(resultReducer, true);
this.validationState = newVState.set('isValid', isValid);
return this.validationState;
}
}, {
key: '_addValidatorToRuleSets',
value: function _addValidatorToRuleSets(propName, accessor, validator) {
this.validationState = this.validationState.setIn(['messages', propName], new _immutable.Map());
this.ruleSets.push(function (dataToValidate, validationMessages) {
var val = accessor(dataToValidate, propName);
this.ruleSets.push(function (data, vState) {
var val = accessor(data);
var validatorResult = validator.validate(val);
return validationMessages.set(propName, validatorResult);
return setMerged(vState, propName, validatorResult);
});

@@ -147,19 +255,24 @@

value: function _addRuleSetToRuleSets(propName, accessor, rules) {
var _this = this;
this.ruleSets.push(function (data, vState) {
var existing = vState.getIn([propName, SELF]) || (0, _immutable.Set)();
var ruleReducer = buildRuleReducer(accessor(data));
var messages = rules.reduce(ruleReducer, existing);
return vState.set(propName, (0, _immutable.Map)(_defineProperty({}, SELF, messages)));
});
this.validationState = this.validationState.setIn(['messages', propName], new _immutable.Set());
return this;
}
}, {
key: '_addForEachValidatorToRuleSets',
value: function _addForEachValidatorToRuleSets(propName, accessor, validator) {
this.ruleSets.push(function (data, vState) {
var iter = getIterable(propName, accessor, data);
var validatorResultMap = (0, _immutable.Map)();
this.ruleSets.push(function (dataToValidate, validationMessages) {
return rules.reduce(function (allMessages, rule) {
var messages = allMessages.get(propName);
for (var i = 0; i < (0, _util.iterableLength)(iter); i++) {
var validatorResult = validator.validate((0, _util.get)(iter, i));
validatorResultMap = validatorResultMap.set(i, validatorResult);
}
var val = accessor(dataToValidate, propName);
var _runRule2 = _this._runRule(rule, val);
var isValid = _runRule2.isValid;
var message = _runRule2.message;
return isValid ? allMessages.set(propName, messages['delete'](message)) : allMessages.set(propName, messages.add(message));
}, validationMessages);
return setMerged(vState, propName, validatorResultMap);
});

@@ -170,26 +283,18 @@

}, {
key: '_runRule',
value: function _runRule(rule, val) {
var result = rule(val);
if (!hasProp.call(result, 'isValid') || !hasProp.call(result, 'message')) {
throw new Error('Rules must return a result with the properties `isValid` and `message`. Use ImmutableValidation.rule()');
}
return result;
}
}, {
key: 'validate',
value: function validate(dataToValidate) {
var newMessages = this.ruleSets.reduce(function (messages, ruleSet) {
return ruleSet(dataToValidate, messages);
}, this.validationState.get('messages'));
key: '_addForEachRuleSetToRuleSets',
value: function _addForEachRuleSetToRuleSets(propName, accessor, rules) {
this.ruleSets.push(function (data, vState) {
var iter = getIterable(propName, accessor, data);
var resultMap = (0, _immutable.Map)();
var isValid = newMessages.every(function (prop) {
return !prop.count();
});
for (var i = 0; i < (0, _util.iterableLength)(iter); i++) {
var ruleReducer = buildRuleReducer((0, _util.get)(iter, i));
var itemResult = rules.reduce(ruleReducer, (0, _immutable.Set)());
resultMap = resultMap.set(i, (0, _immutable.Map)(_defineProperty({}, SELF, itemResult)));
}
this.validationState = this.validationState.withMutations(function (vState) {
return vState.set('isValid', isValid).set('messages', newMessages);
return setMerged(vState, propName, resultMap);
});
return this.validationState;
return this;
}

@@ -199,4 +304,14 @@ }], [{

value: function isInstance(obj) {
return (0, _util.isFunction)(obj.validate) && (0, _util.isFunction)(obj.ruleFor);
if (!obj) return false;
return (0, _util.isFunction)(obj.validate);
}
}, {
key: 'which',
value: function which(chooseValidator) {
if (!(0, _util.isFunction)(chooseValidator)) {
throw new TypeError('Call Validator.which with a function returning a validator or null');
}
return new ValidatorWrapper(chooseValidator);
}
}]);

@@ -230,3 +345,8 @@

exports.isString = isString;
exports.isObject = isObject;
exports.partial = partial;
exports.isIterable = isIterable;
exports.iterableLength = iterableLength;
exports.unwrapImmutable = unwrapImmutable;
exports.get = get;
exports.asCallable = asCallable;

@@ -258,2 +378,6 @@

function isObject(obj) {
return toType(obj) === 'object';
}
function partial(fn) {

@@ -273,2 +397,25 @@ for (var _len = arguments.length, argsToApply = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {

function isIterable(maybeIter) {
if (!maybeIter || typeof maybeIter !== 'object') return false;
return 'length' in maybeIter || 'count' in maybeIter && isFunction(maybeIter.count);
}
function iterableLength(iter) {
if (!isIterable(iter)) return 0;
if (isFunction(iter.count)) return iter.count();
return iter.length;
}
function unwrapImmutable(maybeImmutable) {
if (!maybeImmutable) return maybeImmutable;
if (isFunction(maybeImmutable.toJS)) return maybeImmutable.toJS();
return maybeImmutable;
}
function get(maybeImmutable, propOrIndex) {
if (!maybeImmutable || typeof maybeImmutable !== 'object') return maybeImmutable;
if (isFunction(maybeImmutable.get)) return maybeImmutable.get(propOrIndex);
return maybeImmutable[propOrIndex];
}
/**

@@ -275,0 +422,0 @@ * Ensures that the given argument can be called as a function:

2

dist/ImmutableValidation.min.js

@@ -1,1 +0,1 @@

!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("immutable")):"function"==typeof define&&define.amd?define(["immutable"],t):"object"==typeof exports?exports.ImmutableValidation=t(require("immutable")):e.ImmutableValidation=t(e.Immutable)}(this,function(e){return function(e){function t(r){if(n[r])return n[r].exports;var u=n[r]={exports:{},id:r,loaded:!1};return e[r].call(u.exports,u,u.exports,t),u.loaded=!0,u.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){"use strict";function r(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t["default"]=e,t}function u(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(1),a=u(i),o=n(4),s=r(o),l=n(5),c=u(l);t["default"]={Validator:a["default"],facts:s,rule:c["default"]},e.exports=t["default"]},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(t,"__esModule",{value:!0});var u=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),i=n(2),a=n(3),o=Object.prototype.hasOwnProperty,s=function(){function e(){r(this,e),this.ruleSets=[],this.validationState=new i.Map({isValid:!0,messages:new i.Map})}return u(e,[{key:"ruleFor",value:function(t,n){for(var r=arguments.length,u=Array(r>2?r-2:0),i=2;r>i;i++)u[i-2]=arguments[i];return 1===u.length&&e.isInstance(u[0])?this._addValidatorToRuleSets(t,n,u[0]):this._addRuleSetToRuleSets(t,n,u)}},{key:"_addValidatorToRuleSets",value:function(e,t,n){return this.validationState=this.validationState.setIn(["messages",e],new i.Map),this.ruleSets.push(function(r,u){var i=t(r,e),a=n.validate(i);return u.set(e,a)}),this}},{key:"_addRuleSetToRuleSets",value:function(e,t,n){var r=this;return this.validationState=this.validationState.setIn(["messages",e],new i.Set),this.ruleSets.push(function(u,i){return n.reduce(function(n,i){var a=n.get(e),o=t(u,e),s=r._runRule(i,o),l=s.isValid,c=s.message;return l?n.set(e,a["delete"](c)):n.set(e,a.add(c))},i)}),this}},{key:"_runRule",value:function(e,t){var n=e(t);if(!o.call(n,"isValid")||!o.call(n,"message"))throw new Error("Rules must return a result with the properties `isValid` and `message`. Use ImmutableValidation.rule()");return n}},{key:"validate",value:function(e){var t=this.ruleSets.reduce(function(t,n){return n(e,t)},this.validationState.get("messages")),n=t.every(function(e){return!e.count()});return this.validationState=this.validationState.withMutations(function(e){return e.set("isValid",n).set("messages",t)}),this.validationState}}],[{key:"isInstance",value:function(e){return a.isFunction(e.validate)&&a.isFunction(e.ruleFor)}}]),e}();t["default"]=s,e.exports=t["default"]},function(t,n){t.exports=e},function(e,t){"use strict";function n(e){return e}function r(){return void 0}function u(e){return null===e?"null":void 0===e?"undefined":{}.toString.call(e).match(/\s([a-zA-Z]+)/)[1].toLowerCase()}function i(e){return"function"===u(e)}function a(e){return"string"===u(e)}function o(e){for(var t=arguments.length,n=Array(t>1?t-1:0),r=1;t>r;r++)n[r-1]=arguments[r];return function(){for(var t=arguments.length,r=Array(t),u=0;t>u;u++)r[u]=arguments[u];return e.apply(this,n.concat(r))}}function s(e,t){return i(e)?e:i(t)?o(t,e):"identity"===t?o(n,e):r}Object.defineProperty(t,"__esModule",{value:!0}),t.identity=n,t.noop=r,t.toType=u,t.isFunction=i,t.isString=a,t.partial=o,t.asCallable=s},function(e,t){"use strict";function n(e){return!!e}function r(e){return function(t){return t.length>=e}}function u(e){return function(t){return t.length<=e}}function i(e,t){return function(n){var r=parseInt(n,10);return r>=e&&t>=r}}Object.defineProperty(t,"__esModule",{value:!0}),t.required=n,t.minLength=r,t.maxLength=u,t.between=i},function(e,t,n){"use strict";function r(e,t){return e=u.asCallable(e,function(){return!0}),t=u.asCallable(t,function(e){return u.isString(e)?e:""}),function(n){var r=e(n),u=t(n);return{isValid:r,message:u}}}Object.defineProperty(t,"__esModule",{value:!0}),t["default"]=r;var u=n(3);e.exports=t["default"]}])});
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("immutable")):"function"==typeof define&&define.amd?define(["immutable"],e):"object"==typeof exports?exports.ImmutableValidation=e(require("immutable")):t.ImmutableValidation=e(t.Immutable)}(this,function(t){return function(t){function e(r){if(n[r])return n[r].exports;var u=n[r]={exports:{},id:r,loaded:!1};return t[r].call(u.exports,u,u.exports,e),u.loaded=!0,u.exports}var n={};return e.m=t,e.c=n,e.p="",e(0)}([function(t,e,n){"use strict";function r(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e["default"]=t,e}function u(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),a=u(i),o=n(4),l=r(o),s=n(5),c=u(s);e["default"]={Validator:a["default"],facts:l,rule:c["default"]},t.exports=e["default"]},function(t,e,n){"use strict";function r(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function u(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t){return function(e,n){var r=o(n,t),u=r.isValid,i=r.message;return u?e:e.add(i)}}function a(t,e){var n=c(e,2),r=n[1];return t===!1?!1:r.has("isValid")?r.get("isValid"):r.has(p)&&r.get(p).count()?!1:r.entrySeq().filter(function(t){var e=c(t,1),n=e[0];return"isValid"!==n&&n!==p}).reduce(a,t)}function o(t,e){var n=t(e);if(!v.call(n,"isValid")||!v.call(n,"message"))throw new Error("Rules must return a result with the properties `isValid` and `message`. Use ImmutableValidation.rule()");return n}function l(t,e,n){var r=e(n);if(!h.isIterable(r))throw new TypeError("`"+t+"` is a non-iterable value. ruleForEach applies a validation rule to each item in an iterable");return r}function s(t,e,n){return t.has(e)&&(n=t.get(e).merge(n)),t.set(e,d.Map(n))}Object.defineProperty(e,"__esModule",{value:!0});var c=function(){function t(t,e){var n=[],r=!0,u=!1,i=void 0;try{for(var a,o=t[Symbol.iterator]();!(r=(a=o.next()).done)&&(n.push(a.value),!e||n.length!==e);r=!0);}catch(l){u=!0,i=l}finally{try{!r&&o["return"]&&o["return"]()}finally{if(u)throw i}}return n}return function(e,n){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),f=function(){function t(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}return function(e,n,r){return n&&t(e.prototype,n),r&&t(e,r),e}}(),d=n(2),h=n(3),v=Object.prototype.hasOwnProperty,p="self",y=function(){function t(e){u(this,t),this.getValidator=e}return f(t,[{key:"validate",value:function(t){var e=this.getValidator(t);return b.isInstance(e)?e.validate(t):d.Map({isValid:!0})}}]),t}(),b=function(){function t(){u(this,t),this.ruleSets=[],this.validationState=d.Map({isValid:!0})}return f(t,[{key:"ruleFor",value:function(e,n){for(var r=arguments.length,u=Array(r>2?r-2:0),i=2;r>i;i++)u[i-2]=arguments[i];return t.isInstance(u[0])?this._addValidatorToRuleSets(e,n,u[0]):this._addRuleSetToRuleSets(e,n,u)}},{key:"ruleForEach",value:function(e,n){for(var r=arguments.length,u=Array(r>2?r-2:0),i=2;r>i;i++)u[i-2]=arguments[i];return t.isInstance(u[0])?this._addForEachValidatorToRuleSets(e,n,u[0]):this._addForEachRuleSetToRuleSets(e,n,u)}},{key:"validate",value:function(t){var e=this.ruleSets.reduce(function(e,n){return n(t,e)},d.Map()),n=e.entrySeq().filter(function(t){var e=c(t,1),n=e[0];return"isValid"!==n}).reduce(a,!0);return this.validationState=e.set("isValid",n),this.validationState}},{key:"_addValidatorToRuleSets",value:function(t,e,n){return this.ruleSets.push(function(r,u){var i=e(r),a=n.validate(i);return s(u,t,a)}),this}},{key:"_addRuleSetToRuleSets",value:function(t,e,n){return this.ruleSets.push(function(u,a){var o=a.getIn([t,p])||d.Set(),l=i(e(u)),s=n.reduce(l,o);return a.set(t,d.Map(r({},p,s)))}),this}},{key:"_addForEachValidatorToRuleSets",value:function(t,e,n){return this.ruleSets.push(function(r,u){for(var i=l(t,e,r),a=d.Map(),o=0;o<h.iterableLength(i);o++){var c=n.validate(h.get(i,o));a=a.set(o,c)}return s(u,t,a)}),this}},{key:"_addForEachRuleSetToRuleSets",value:function(t,e,n){return this.ruleSets.push(function(u,a){for(var o=l(t,e,u),c=d.Map(),f=0;f<h.iterableLength(o);f++){var v=i(h.get(o,f)),y=n.reduce(v,d.Set());c=c.set(f,d.Map(r({},p,y)))}return s(a,t,c)}),this}}],[{key:"isInstance",value:function(t){return t?h.isFunction(t.validate):!1}},{key:"which",value:function(t){if(!h.isFunction(t))throw new TypeError("Call Validator.which with a function returning a validator or null");return new y(t)}}]),t}();e["default"]=b,t.exports=e["default"]},function(e,n){e.exports=t},function(t,e){"use strict";function n(t){return t}function r(){return void 0}function u(t){return null===t?"null":void 0===t?"undefined":{}.toString.call(t).match(/\s([a-zA-Z]+)/)[1].toLowerCase()}function i(t){return"function"===u(t)}function a(t){return"string"===u(t)}function o(t){return"object"===u(t)}function l(t){for(var e=arguments.length,n=Array(e>1?e-1:0),r=1;e>r;r++)n[r-1]=arguments[r];return function(){for(var e=arguments.length,r=Array(e),u=0;e>u;u++)r[u]=arguments[u];return t.apply(this,n.concat(r))}}function s(t){return t&&"object"==typeof t?"length"in t||"count"in t&&i(t.count):!1}function c(t){return s(t)?i(t.count)?t.count():t.length:0}function f(t){return t&&i(t.toJS)?t.toJS():t}function d(t,e){return t&&"object"==typeof t?i(t.get)?t.get(e):t[e]:t}function h(t,e){return i(t)?t:i(e)?l(e,t):"identity"===e?l(n,t):r}Object.defineProperty(e,"__esModule",{value:!0}),e.identity=n,e.noop=r,e.toType=u,e.isFunction=i,e.isString=a,e.isObject=o,e.partial=l,e.isIterable=s,e.iterableLength=c,e.unwrapImmutable=f,e.get=d,e.asCallable=h},function(t,e){"use strict";function n(t){return!!t}function r(t){return function(e){return e.length>=t}}function u(t){return function(e){return e.length<=t}}function i(t,e){return function(n){var r=parseInt(n,10);return r>=t&&e>=r}}Object.defineProperty(e,"__esModule",{value:!0}),e.required=n,e.minLength=r,e.maxLength=u,e.between=i},function(t,e,n){"use strict";function r(t,e){return t=u.asCallable(t,function(){return!0}),e=u.asCallable(e,function(t){return u.isString(t)?t:""}),function(n){var r=t(n),u=e(n);return{isValid:r,message:u}}}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=r;var u=n(3);t.exports=e["default"]}])});
{
"name": "immutable-validation",
"version": "0.0.2",
"version": "0.1.0",
"description": "React form validation using top-down, immutable data",

@@ -8,3 +8,3 @@ "main": "dist",

"test": "karma start",
"test:ci": "karma start --no-single-run --browsers Chrome",
"test:ci": "karma start --no-single-run --browsers Chrome --auto-watch",
"build": "webpack src/validation.js dist/ImmutableValidation.min.js",

@@ -11,0 +11,0 @@ "build:dev": "webpack src/validation.js dist/ImmutableValidation.js --config webpack.config.dev.js",

@@ -25,2 +25,6 @@ export function identity (x) {

export function isObject (obj) {
return toType(obj) === 'object';
}
export function partial (fn, ...argsToApply) {

@@ -32,2 +36,25 @@ return function (...args) {

export function isIterable (maybeIter) {
if (!maybeIter || typeof maybeIter !== 'object') return false;
return ('length' in maybeIter || ('count' in maybeIter && isFunction(maybeIter.count)));
}
export function iterableLength (iter) {
if (!isIterable(iter)) return 0;
if (isFunction(iter.count)) return iter.count();
return iter.length;
}
export function unwrapImmutable (maybeImmutable) {
if (!maybeImmutable) return maybeImmutable;
if (isFunction(maybeImmutable.toJS)) return maybeImmutable.toJS();
return maybeImmutable;
}
export function get (maybeImmutable, propOrIndex) {
if (!maybeImmutable || typeof maybeImmutable !== 'object') return maybeImmutable;
if (isFunction(maybeImmutable.get)) return maybeImmutable.get(propOrIndex);
return maybeImmutable[propOrIndex];
}
/**

@@ -34,0 +61,0 @@ * Ensures that the given argument can be called as a function:

import { Map, Set } from 'immutable';
import { isFunction } from './util';
import { isFunction, isIterable, iterableLength, get } from './util';
const hasProp = Object.prototype.hasOwnProperty;
const SELF = 'self';
function buildRuleReducer (dataToValidate) {
return (messagesForProp, rule) => {
const { isValid, message } = runRule(rule, dataToValidate);
if (!isValid) {
return messagesForProp.add(message);
}
return messagesForProp;
}
}
function resultReducer (isValid, [ , prop ]) {
if (isValid === false) return false;
if (prop.has('isValid')) return prop.get('isValid');
if (prop.has(SELF) && prop.get(SELF).count()) return false;
return prop.entrySeq()
.filter(([ key ]) => key !== 'isValid' && key !== SELF)
.reduce(resultReducer, isValid);
}
function runRule (rule, val) {
const result = rule(val);
if (!hasProp.call(result, 'isValid') || !hasProp.call(result, 'message')) {
throw new Error('Rules must return a result with the properties `isValid` and `message`. Use ImmutableValidation.rule()');
}
return result;
}
function getIterable (propName, accessor, data) {
const iter = accessor(data);
if (!isIterable(iter)) {
throw new TypeError(`\`${propName}\` is a non-iterable value. ruleForEach applies a validation rule to each item in an iterable`);
}
return iter;
}
function setMerged (vState, propName, newData) {
if (vState.has(propName)) {
newData = vState.get(propName).merge(newData);
}
return vState.set(propName, Map(newData));
}
class ValidatorWrapper {
constructor (chooseValidator) {
this.getValidator = chooseValidator;
}
validate (dataToValidate) {
const v = this.getValidator(dataToValidate);
if (!Validator.isInstance(v)) {
return Map({ isValid: true });
}
return v.validate(dataToValidate);
}
}
class Validator {
constructor () {
this.ruleSets = [];
this.validationState = new Map({
isValid: true,
messages: new Map()
this.validationState = Map({
isValid: true
});

@@ -17,3 +79,3 @@ }

ruleFor (propName, accessor, ...rules) {
return (rules.length === 1 && Validator.isInstance(rules[0]))
return (Validator.isInstance(rules[0]))
? this._addValidatorToRuleSets(propName, accessor, rules[0])

@@ -23,9 +85,26 @@ : this._addRuleSetToRuleSets(propName, accessor, rules);

ruleForEach (propName, accessor, ...rules) {
return (Validator.isInstance(rules[0]))
? this._addForEachValidatorToRuleSets(propName, accessor, rules[0])
: this._addForEachRuleSetToRuleSets(propName, accessor, rules);
}
validate (dataToValidate) {
const newVState = this.ruleSets.reduce((messages, ruleSet) =>
ruleSet(dataToValidate, messages), Map());
const isValid = newVState.entrySeq()
.filter(([ key ]) => key !== 'isValid')
.reduce(resultReducer, true);
this.validationState = newVState.set('isValid', isValid);
return this.validationState;
}
_addValidatorToRuleSets (propName, accessor, validator) {
this.validationState = this.validationState.setIn(['messages', propName], new Map());
this.ruleSets.push((dataToValidate, validationMessages) => {
const val = accessor(dataToValidate, propName);
this.ruleSets.push((data, vState) => {
const val = accessor(data);
const validatorResult = validator.validate(val);
return validationMessages.set(propName, validatorResult);
return setMerged(vState, propName, validatorResult);
});

@@ -37,16 +116,23 @@

_addRuleSetToRuleSets (propName, accessor, rules) {
this.validationState = this.validationState.setIn(['messages', propName], new Set());
this.ruleSets.push((data, vState) => {
const existing = vState.getIn([ propName, SELF ]) || Set();
const ruleReducer = buildRuleReducer(accessor(data));
const messages = rules.reduce(ruleReducer, existing);
return vState.set(propName, Map({ [SELF]: messages }));
});
this.ruleSets.push((dataToValidate, validationMessages) => {
return rules.reduce((allMessages, rule) => {
const messages = allMessages.get(propName);
return this;
}
const val = accessor(dataToValidate, propName);
const { isValid, message } = this._runRule(rule, val);
_addForEachValidatorToRuleSets (propName, accessor, validator) {
this.ruleSets.push((data, vState) => {
const iter = getIterable(propName, accessor, data);
let validatorResultMap = Map();
return (isValid)
? allMessages.set(propName, messages.delete(message))
: allMessages.set(propName, messages.add(message));
for (let i = 0; i < iterableLength(iter); i++) {
const validatorResult = validator.validate(get(iter, i));
validatorResultMap = validatorResultMap.set(i, validatorResult);
}
}, validationMessages);
return setMerged(vState, propName, validatorResultMap);
});

@@ -57,30 +143,33 @@

_runRule (rule, val) {
const result = rule(val);
if (!hasProp.call(result, 'isValid') || !hasProp.call(result, 'message')) {
throw new Error('Rules must return a result with the properties `isValid` and `message`. Use ImmutableValidation.rule()');
}
return result;
}
_addForEachRuleSetToRuleSets (propName, accessor, rules) {
this.ruleSets.push((data, vState) => {
const iter = getIterable(propName, accessor, data);
let resultMap = Map();
validate (dataToValidate) {
const newMessages = this.ruleSets.reduce((messages, ruleSet) =>
ruleSet(dataToValidate, messages), this.validationState.get('messages'));
for (let i = 0; i < iterableLength(iter); i++) {
const ruleReducer = buildRuleReducer(get(iter, i));
const itemResult = rules.reduce(ruleReducer, Set());
resultMap = resultMap.set(i, Map({ [SELF]: itemResult }));
}
const isValid = newMessages.every((prop) => !prop.count());
this.validationState = this.validationState.withMutations((vState) => {
return vState
.set('isValid', isValid)
.set('messages', newMessages);
return setMerged(vState, propName, resultMap);
});
return this.validationState;
return this;
}
static isInstance (obj) {
return (isFunction(obj.validate) && isFunction(obj.ruleFor));
if (!obj) return false;
return (isFunction(obj.validate));
}
static which (chooseValidator) {
if (!isFunction(chooseValidator)) {
throw new TypeError(`Call Validator.which with a function returning a validator or null`);
}
return new ValidatorWrapper(chooseValidator);
}
}
export default Validator;

@@ -0,1 +1,2 @@

/*eslint-env mocha */
import { expect } from 'chai';

@@ -10,2 +11,8 @@ import Immutable from 'immutable';

lastName: 'Smith',
foodAllergies: [ 'Tree Nuts', 'Raisins' ],
contactMethods: [
{ type: 'phone', value: 210444779 },
{ type: 'fax', value: 2104447796 },
{ type: 'email', value: 'johnsmith_gmail.com' }
],
address: {

@@ -20,8 +27,19 @@ zipCode: '7870'

const zipCodeRequired = rule(required, 'Please enter a ZIP code');
const zipCodeIsValid = rule((val) => val.length === 5);
const zipCodeIsValid = rule((val) => val.length === 5, 'Bad zip code');
const phoneNumberIsValid = rule((val) => ('' + val).length === 10, 'Bad phone number');
const emailIsValid = rule((val) => val.indexOf('@') > 0, 'Bad email address');
const isRealAllergen = rule(
(val) => ['Tree Nuts', 'Gluten'].indexOf(val) > -1,
(val) => `No one is allergic to ${val}`
);
const isUsableContactMethod = rule(
(val) => ['phone', 'email'].indexOf(val) > -1,
(val) => `We can't use ${val} to contact you`
);
describe('Validator', () => {
describe('when validating', () => {
it('returns a validationState object', () => {
const personValidator = new Validator();

@@ -31,3 +49,3 @@

expect(validationState).to.contain.keys('isValid', 'messages');
expect(validationState).to.contain.keys('isValid');
});

@@ -45,3 +63,3 @@

it('returns a keyed collection of messages for each rule set', () => {
it('returns a property for each rule set that contains results for that rule set', () => {
const personValidator = new Validator()

@@ -53,25 +71,25 @@ .ruleFor('firstNameStuff', (p) => p.get('firstName'), fNameRequired)

expect(validationState.messages).to.contain.keys('firstNameStuff', 'lastNameStuff');
expect(validationState).to.contain.keys('firstNameStuff', 'lastNameStuff');
});
it('fails validation if any rule in a set fails', () => {
it('returns lists of messages describing failed validation rules for each rule set', () => {
const personValidator = new Validator()
.ruleFor('lastName', (p) => p.get('lastName'), lNameRequired, lNameStartsWithP);
.ruleFor('lastName', (p) => p.get('lastName'), lNameStartsWithP);
const validationState = personValidator.validate(personData).toJS();
expect(validationState.isValid).to.be.false;
expect(validationState.lastName.self.length).to.equal(1);
expect(validationState.lastName.self[0]).to.equal('Your last name must start with a P');
});
it('contains a message describing failed validation rules', () => {
it('fails validation if any rule in a single set fails', () => {
const personValidator = new Validator()
.ruleFor('lastName', (p) => p.get('lastName'), lNameStartsWithP);
.ruleFor('lastName', (p) => p.get('lastName'), lNameRequired, lNameStartsWithP);
const validationState = personValidator.validate(personData).toJS();
expect(validationState.messages.lastName.length).to.equal(1);
expect(validationState.messages.lastName[0]).to.equal('Your last name must start with a P');
expect(validationState.isValid).to.be.false;
});
it('fails validation if any rule set contains a failing rule', () => {
it('fails validation if any rule set contains failing rules', () => {
const personValidator = new Validator()

@@ -86,2 +104,15 @@ .ruleFor('firstName', (p) => p.get('firstName'), fNameRequired)

it('can run multiple rule sets for the same property name', () => {
const failingRule1 = rule(() => false, 'Failing rule 1');
const failingRule2 = rule(() => false, 'Failing rule 2');
const personValidator = new Validator()
.ruleFor('firstName', (p) => p.get('firstName'), failingRule1)
.ruleFor('firstName', (p) => p.get('firstName'), failingRule2);
const validationState = personValidator.validate(personData).toJS();
expect(validationState.firstName.self.length).to.equal(2);
});
it('throws when running a rule that does not return a well-formed response', () => {

@@ -94,25 +125,108 @@ const personValidator = new Validator()

it('returns a keyed validationState from nested validators', () => {
const personValidator = new Validator()
.ruleFor('firstName', (p) => p.get('firstName'), fNameRequired)
.ruleFor('address', (p) => p.get('address'), new Validator()
.ruleFor('zipCode', (a) => a.get('zipCode'), zipCodeRequired)
);
describe('a nested validator', () => {
const validationState = personValidator.validate(personData).toJS();
it('returns a nested validationState', () => {
const personValidator = new Validator()
.ruleFor('firstName', (p) => p.get('firstName'), fNameRequired)
.ruleFor('address', (p) => p.get('address'), new Validator()
.ruleFor('zipCode', (a) => a.get('zipCode'), zipCodeRequired)
);
expect(validationState.messages.address).to.contain.keys('isValid', 'messages');
const validationState = personValidator.validate(personData).toJS();
expect(validationState.address).to.contain.keys('isValid', 'zipCode');
});
it('fails validation if the nested validator fails', () => {
const personValidator = new Validator()
.ruleFor('firstName', (p) => p.get('firstName'), fNameRequired)
.ruleFor('address', (p) => p.get('address'), new Validator()
.ruleFor('zipCode', (a) => a.get('zipCode'), zipCodeIsValid)
);
const validationState = personValidator.validate(personData).toJS();
expect(validationState.isValid).to.be.false;
});
});
it('fails validation if a nested validator fails validation', () => {
const personValidator = new Validator()
.ruleFor('firstName', (p) => p.get('firstName'), fNameRequired)
.ruleFor('address', (p) => p.get('address'), new Validator()
.ruleFor('zipCode', (a) => a.get('zipCode'), zipCodeIsValid)
);
describe('using ruleForEach', () => {
const validationState = personValidator.validate(personData).toJS();
it('runs the given rule set for every value in an iterable', () => {
const personValidator = new Validator()
.ruleForEach('foodAllergies', (p) => p.get('foodAllergies'), isRealAllergen);
const validationState = personValidator.validate(personData).toJS();
expect(validationState.isValid).to.be.false;
expect(validationState.isValid).to.be.false;
expect(validationState.foodAllergies[1].self[0]).to.equal('No one is allergic to Raisins');
});
it('applies nested validators to every value in an iterable', () => {
const personValidator = new Validator()
.ruleForEach('contactMethods', (p) => p.get('contactMethods'), new Validator()
.ruleFor('type', (c) => c.get('type'), isUsableContactMethod)
);
const validationState = personValidator.validate(personData).toJS();
expect(validationState.isValid).to.be.false;
expect(validationState.contactMethods[1].type.self[0]).to.equal('We can\'t use fax to contact you');
});
it('throws if the value returned by the accessor function isn\'t iterable', () => {
const personValidator = new Validator()
.ruleForEach('lastName', (p) => p.get('lastName'), isRealAllergen);
expect(() => personValidator.validate(personData)).to.throw(/is a non-iterable value/);
});
});
describe('using Validator.which', () => {
it('chooses a validator to use at runtime', () => {
const contactMethodValidators = {
email: new Validator()
.ruleFor('value', (c) => c.get('value'), emailIsValid),
phone: new Validator()
.ruleFor('value', (c) => c.get('value'), phoneNumberIsValid)
};
const personValidator = new Validator()
.ruleForEach(
'contactMethods',
(p) => p.get('contactMethods'),
Validator.which((cm) => contactMethodValidators[cm.get('type')])
);
const validationState = personValidator.validate(personData).toJS();
expect(validationState.contactMethods[0].value.self[0]).to.equal('Bad phone number');
expect(validationState.contactMethods[2].value.self[0]).to.equal('Bad email address');
});
it('does not perform validation if no validator matches', () => {
const personValidator = new Validator()
.ruleForEach(
'contactMethods',
(p) => p.get('contactMethods'),
Validator.which(() => null)
);
const validationState = personValidator.validate(personData).toJS();
expect(validationState.contactMethods[0].isValid).to.be.true;
expect(validationState.contactMethods[1].isValid).to.be.true;
expect(validationState.contactMethods[2].isValid).to.be.true;
});
it('throws if not passed a function as its parameter', () => {
expect(() => Validator.which()).to.throw(/a function returning a validator or null/);
});
});
});

@@ -127,3 +241,3 @@

it('returns false if the checked object does not implement `validate` or `ruleFor`', () => {
it('returns false if the checked object does not implement `validate`', () => {
const noValidateMethod = {

@@ -135,12 +249,5 @@ ruleFor () {

const noRuleForMethod = {
validate () {
return undefined;
}
};
expect(Validator.isInstance(noValidateMethod)).to.be.false;
expect(Validator.isInstance(noRuleForMethod)).to.be.false;
});
});
});
SocketSocket SOC 2 Logo

Product

About

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.

  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc

U.S. Patent No. 12,346,443 & 12,314,394. Other pending.