@rexform/validation
Version:
A monad for incremental validation without information loss.
358 lines (292 loc) • 9.38 kB
JavaScript
(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