UNPKG

@rexform/validation

Version:

A monad for incremental validation without information loss.

358 lines (292 loc) 9.38 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = global || self, factory(global['@rexform/validation'] = {})); }(this, function (exports) { 'use strict'; const curry1 = (fn, a) => a === undefined ? a2 => fn(a2) : fn(a); const arrayOrItemToArray = x => [].concat(x); var Variant; (function (Variant) { Variant["Valid"] = "Valid"; Variant["Invalid"] = "Invalid"; })(Variant || (Variant = {})); class Valid { constructor(value) { this.variant = Variant.Valid; this.value = value; } isValid() { return true; } isInvalid() { return false; } errorsOr(alt) { return alt; } empty() { return empty(); } concat(val) { return concat(val, map(arrayOrItemToArray, this)); } concatErr(val) { return concatErr(val, this); } map(fn) { return map(fn, this); } mapErrors(fn) { return mapErrors(fn, this); } mapError(fn) { return mapError(fn, this); } ap(validation) { return ap(validation, this); } chain(fn) { return chain(fn, this); } fold(fnInvalid, fnValid) { return fold(fnInvalid, fnValid, this); } validateEither(either) { return validateEither(this, either); } validateEitherList(eitherList) { return validateEitherList(this, eitherList); } validate(validator) { return validate(this, validator); } validateAll(validators) { return validateAll(this, validators); } } class Invalid { constructor(value, errors) { this.variant = Variant.Invalid; if (errors.length === 0) { throw new Error('Tried to construct `Invalid` with an empty array of errors'); } this.errors = errors; this.value = value; } isValid() { return false; } isInvalid() { return true; } errorsOr(alt) { return errorsOr(alt, this); } empty() { return empty(); } concat(val) { return concat(val, map(arrayOrItemToArray, this)); } concatErr(val) { return concatErr(val, this); } map(fn) { return map(fn, this); } mapErrors(fn) { return mapErrors(fn, this); } mapError(fn) { return mapError(fn, this); } ap(validation) { return ap(validation, this); } chain(fn) { return chain(fn, this); } fold(fnInvalid, fnValid) { return fold(fnInvalid, fnValid, this); } validateEither(either) { return validateEither(this, either); } validateEitherList(eitherList) { return validateEitherList(this, eitherList); } validate(validator) { return validate(this, validator); } validateAll(validators) { return validateAll(this, validators); } } const valid = value => new Valid(value); function invalid(value, errors) { const op = errors2 => new Invalid(value, arrayOrItemToArray(errors2)); return curry1(op, errors); } function isValid(validation) { return validation.variant === Variant.Valid; } function isInvalid(validation) { return validation.variant === Variant.Invalid; } const of = (value, errors) => { if (value instanceof Valid || value instanceof Invalid) { return value; } return errors && errors.length > 0 ? invalid(value, errors) : valid(value); }; function fromEither(initialValue, either) { const op = either2 => either2.fold(invalid(initialValue), valid); return curry1(op, either); } function property(property, obj) { const op = obj2 => obj2[property] === undefined || obj2[property] === null ? invalid(obj2[property], `Property "${property}" not found or null.`) : valid(obj2[property]); return curry1(op, obj); } const objWithPropertyIfNotUndefined = (obj, key) => value => value === undefined ? obj : { ...obj, [key]: value }; const allProperties = obj => { return Object.keys(obj).reduce((validation, key) => validation.chain(previousProperties => obj[key].fold((e, v) => invalid(undefined, e), valid).map(objWithPropertyIfNotUndefined(previousProperties, key))), valid({})); }; function validateProperties(validations, obj) { const op = obj2 => Object.keys(validations).reduce((acc, k) => ({ ...acc, [k]: property(k, obj2).chain(validations[k]) }), {}); return curry1(op, obj); } function fromPredicateOr(errorFn, predicate) { const op = predicate2 => v => predicate2(v) ? valid(v) : invalid(v, errorFn(v)); return curry1(op, predicate); } function errorsOr(alt, validation) { const op = v => v.isValid() ? alt : v.errors; return curry1(op, validation); } function empty() { return valid([]); } function concat(listVal, val) { const op = val2 => listVal.concatErr(val2).map(val2.isValid() ? a => [...listVal.value, ...a] : () => listVal.value); return curry1(op, val); } const sequence = validations => validations.reduce((acc, b) => b.concat(acc), empty()); function concatErr(valB, valA) { const op = v => { const shouldBeValid = valB.isValid() && v.isValid(); return shouldBeValid ? valB : invalid(valB.value, [...v.errorsOr([]), ...valB.errorsOr([])]); }; return curry1(op, valA); } function map(fn, validation) { const op = v => v.isValid() ? valid(fn(v.value)) : invalid(fn(v.value), v.errors); return curry1(op, validation); } function mapErrors(mappingFn, validation) { const op = validation2 => validation2.isValid() ? validation2 : invalid(validation2.value, mappingFn(validation2.errorsOr([]))); return curry1(op, validation); } function mapError(mappingFn, validation) { const op = validation2 => mapErrors(errors => errors.map(mappingFn), validation2); return curry1(op, validation); } function ap(valFn, valA) { const op = v => { const shouldBeValid = v.isValid() && valFn.isValid(); return shouldBeValid ? valid(valFn.value(v.value)) : invalid(valFn.value(v.value), [...v.errorsOr([]), ...valFn.errorsOr([])]); }; return curry1(op, valA); } function chain(fn, validation) { const op = v => { const newValidation = fn(v.value); return concatErr(newValidation, v); }; return curry1(op, validation); } function fold(fnInvalid, fnValid, validation) { const op = v => v.isValid() ? fnValid(v.value) : fnInvalid(v.errors, v.value); return curry1(op, validation); } function validateEither(validation, either) { const op = e => { const newVal = e.fold(errors => invalid(validation.value, arrayOrItemToArray(errors)), value => valid(value)); return concatErr(newVal, validation); }; return curry1(op, either); } function validateEitherList(validation, eitherList) { const op = el => el.reduce((v, e) => validateEither(v, e), validation); return curry1(op, eitherList); } function validate(validation, validator) { const op = v => validateEither(validation, v(validation.value)); return curry1(op, validator); } function validateAll(validation, validators) { const op = vl => vl.reduce((v, f) => validate(v, f), validation); return curry1(op, validators); } const Validation = { Variant, Valid, Invalid, valid, invalid, isValid, isInvalid, of, fromEither, property, allProperties, validateProperties, fromPredicateOr, errorsOr, empty, concat, sequence, concatErr, map, mapErrors, mapError, ap, chain, fold, validateEither, validateEitherList, validate, validateAll }; exports.Validation = Validation; exports.allProperties = allProperties; exports.ap = ap; exports.chain = chain; exports.concat = concat; exports.concatErr = concatErr; exports.default = Validation; exports.empty = empty; exports.errorsOr = errorsOr; exports.fold = fold; exports.fromEither = fromEither; exports.fromPredicateOr = fromPredicateOr; exports.invalid = invalid; exports.isInvalid = isInvalid; exports.isValid = isValid; exports.map = map; exports.mapError = mapError; exports.mapErrors = mapErrors; exports.of = of; exports.property = property; exports.sequence = sequence; exports.valid = valid; exports.validate = validate; exports.validateAll = validateAll; exports.validateEither = validateEither; exports.validateEitherList = validateEitherList; exports.validateProperties = validateProperties; })); //# sourceMappingURL=rexformvalidation.umd.development.js.map