0.3.7 • Published 4 years ago

perfecto v0.3.7

Weekly downloads
2
License
MIT
Repository
github
Last release
4 years ago

Perfecto

A tiny dead-simple declarative validation framework. Some people may call it a "specification framework". It helps you to build your validators by composing simple error checkers aka predicates to build a final function, that's going to receive your state or form data and return a promise resolving to the array of the errors found.

Installation

Using npm:

npm install perfecto --save

Use cases

Perfecto was initially designed to work as an asynchronous validation framework used with redux-form. It also works great with Formik and was used as the go-to server-side validation library.

Features

  • Asynchronous by nature, but usable with synchronous validators with no efforts.
  • Parallel validation for asynchronous validation operations.
  • Full access to the validated object (at will).
  • Composable.
  • Code reuse between front- and backend (if you know how to DI).
  • Curried validator functions.

Examples

Formik validation - shamelessly stolen from Formik demo with some polish

Redux Form validation - stolen from Redux-Form async validatione example

Asynchronous validation

Express server-side validation

const perfecto = require("perfecto");

const isPresent = value => !!value;
const isPresentValidator = perfecto.path(isPresent, "is required!");

// We only do some basic validation in this case
const orderValidator = perfecto.validate([
  isPresentValidator(["name"]),
  isPresentValidator(["address"]),
  isPresentValidator(["product"])
]);

module.exports = async (req, resp, next) => {
  const order = req.body;
  const errors = await orderValidator(order);
  // Errors array contains the list of the error paths and messages, if any
  if (errors.length) {
    res.send({ errors });
  } else {
    res.send({ ok: true });
  }
};

Validator with direct access to the object

Play with it at CodeSandbox

import renderErrors from "./errors";

import { validate, check, array } from "perfecto";

const isTheOnlyJediPredicate = (value, validationContext) =>
  validationContext.object.length === 1 && value.isJedi;
// Build a validator with an error message used when the predicate returns falsy value.
const isTheOnlyValidator = check(
  isTheOnlyJediPredicate,
  "The only should he be"
);
const people = [
  {
    name: "Luke",
    isJedi: true
  },
  { name: "Obi Wan", isJedi: true }
];
validate([array([isTheOnlyValidator])], { object: people }).then(console.info);

Nested objects

Play with it at CodeSandbox

import { path, validate, nest } from "perfecto";

// Checks if the person name is Darth Vader
const isDarth = path(
  name => name === "Darth Vader",
  "His father is Darth Vader not!",
  ["name"]
);

// If father's name is Darth Vader
const fatherIsDarth = nest(["father"], [isDarth]);

/// Of course it is not!
validate([fatherIsDarth], {
  object: { name: "Carl", father: { name: "Tom" } }
}).then(console.info);

// Output: [{path: ["father", "name"], message: "His father is darth vader not!"}]

Arrays

Play with it at CodeSandbox

import { validate, check, array } from "perfecto";

const isJediPredicate = person => !!person.isJedi;
// Checks if the person is Jedi
const isJedi = check(isJediPredicate, "Jedi is he not!");

const everyoneIsAJedi = array([isJedi]);

validate([everyoneIsAJedi], {
  object: [
    { name: "Han" },
    { name: "Luke", isJedi: true },
    { name: "Darth Vader", isJedi: true }
  ]
}).then(console.info);

Conditional validation

Play with it at CodeSandbox

import { validate, check, path, predicate, array } from "perfecto";

const isJediPredicate = person => !!person.isJedi;
// Checks if the person is Jedi
const isJedi = check(isJediPredicate, "Jedi is he not!");
const isPresentPredicate = value => !!value;
const hasSword = path(isPresentPredicate, "Should be there", ["sword"]);

const isJediCheck = context => context.object[context.path[0]].isJedi;

// Luke is a Jedi so he has to have a sword... But he does not for some reason
// And Han is not a Jedi at all :)
validate([array([predicate(isJediCheck, [hasSword])])], {
  object: [{ name: "Luke", isJedi: true }, { name: "Han", isJedi: false }]
}).then(console.info);
0.3.7

4 years ago

0.3.6

5 years ago

0.3.5

5 years ago

0.3.4

5 years ago

0.3.3

5 years ago

0.3.2

5 years ago

0.3.1

5 years ago

0.3.0

5 years ago

0.2.1

5 years ago

0.2.0

5 years ago

0.1.0

5 years ago

0.0.3

6 years ago

0.0.2

7 years ago

0.0.1

7 years ago