flow-validator

0.6.2 • Public • Published

npm version Build Status davidDm Dependencies Known Vulnerabilities Coverage Status Inline docs npm downloads Licence Stability: Stable contributions welcome PRs Welcome

flow-validator

Object validation with proper flow types and more.

Installation

npm install flow-validator

Usage

import { arrayOf, string, number, object, instanceOf, Type, Vobject, asyncArrayOf, tuple, takes, match } from 'flow-validator';
import { express } from 'flow-validator/express';
 
describe("readme code", () => {
  it("works", () => {
    // { name: string, age: ?number, toys: Array<string> }
    const Person = object({
      name: string,
      age: number.optional(),
      toys: arrayOf(string)
    });
    const fred = Person.parse({
      name: "Fred",
      age: 89,
      toys: ["teddy bear", "shotgun"]
    });
    console.log(fred); // eslint-disable-line no-console
 
    // Array<string> validated asynchronously
    const InventoryObjects = asyncArrayOf(
      string.async().refine(checkInventory)
    );
    const shoppingCart = InventoryObjects.parse(["AK47", "stuffed bunny"]);
    shoppingCart.then(items => console.log(items)); // eslint-disable-line no-console
 
    async function checkInventory(item: string, error): Promise<string> {
      if (~["AK47", "stuffed bunny"].indexOf(item)) return item;
      return Promise.reject(error("no supplies"));
    }
 
    // pattern matching -- MORE WORK NEEDED
    // const x = match(1,
    //   number, n => new Date(n*2),
    //   Person, ({ name }) => [name, name]
    // );
    // (x: Date);
 
    // express middleware example
    const middleware = express.middleware(
      object({ headers: object({ "my-custom-header": string }) }),
      (req, res, next) => next()
    );
 
    // express endpoint matching middleware (inspired to Spring RequestMapping)
    const requestMap1 = express.requestMapping(
      object({ body: Person }),
      (req, res) => res.json(req.body.age)
    );
    const requestMap2 = express.requestMapping(
      object({ body: object({ username: string, password: string }) }),
      (req, res) => {
        /* authenticate */
      }
    );
    // app.use('/user', requestMap1);
    // app.use('/user', requestMap2);
 
    const Contact = object({
      name: string,
      birth: string.toDate(),
      email: string.isEmail().optional()
    });
    console.log(
      // eslint-disable-line no-console
      Contact.parse({
        name: "fred",
        birth: String(new Date()),
        email: "gobi301@gmail.com"
      })
    );
 
    // ensure functions params, useful on user input functions
    const signUpUser = takes(string.isEmail(), number)(
      (email, secretCoupon) =>
        `user ${email} added with coupon: ${secretCoupon}`
    );
    signUpUser("gobi301@gmail.com", 666);
 
    // Don't Repeat Yourself
    // you can use a type of a defined schema, instead of
    // var yogi: { name: string, age: ?number, toys: Array<string> }
    var yogi: typeof Person.type;
 
    // runtime introspection
    const Name: Type<string= Person.schema.name;
    const Age: Type<?number= Person.schema.age;
 
    // const tup: [string, number, Date] = ...
    const tup = tuple([string, number, instanceOf(Date)]).parse([
      "hello",
      4,
      new Date()
    ]);
 
    // { a: string, b: number, c: Array<string | number | Date>, d: string, e: Date }
    const Schema = object({
      a: string,
      b: number.optional(),
      c: arrayOf(string.or(number).or(instanceOf(Date))),
      d: string
        .refine((s, error) => {
          // refinements must return the same type
          if (/el/.test(s)) return s;
          throw error(String(/el/)); // this throws proper error
        })
        .revalidate(), // add a revalidate if want to be sure not changed type during refinement
      e: string.to(s => new Date(s)) // with .to() you can convert types
    });
 
    const toBeValidated = {
      a: "hi",
      c: [1, new Date(), "2017"],
      d: "hello",
      e: "Mon Feb 27 2017 10:00:15 GMT-0800 (PST)"
    };
 
    // validate input object, returns original object if valid, throws otherwise
    // VType object has .validate() method that returns original object
    // arrayOf, tuple, mapping, object, objectExact ha V prefixed variants,
    // and can't contain validators that change input
    Vobject({ a: string }).validate({ a: "hello" }) === toBeValidated; // = true
 
    // same as validate, but it make a copy in case of: arrayOf, tuple, mapping, object, objectExact
    // it can be used when using refinemnts that return not the original value
    // and with .to() for conversions
    Schema.parse(toBeValidated) === toBeValidated; // = false
    // deepEqual(Schema.parse(toBeValidated), toBeValidated); // = true
 
    // shortcuts
    Vobject({ a: string }).isValid({ a: "hello" }); // : boolean
    Vobject({ a: string }).validateResult({ a: "hello" }); // : { value: ... } | { error: ... }
    Schema.parseResult(toBeValidated); // : { value: ... } | { error: ... }
 
    // you can chain validators, to reuse them or check if your custom type converter works
    string.to(s => new Date(s)).chain(instanceOf(Date));
 
    // to get JSON serializable error report
    try {
      Schema.parse();
    } catch (e) {
      console.log(e.toJSON()); // eslint-disable-line no-console
    }
 
    // sometimes flow will not remember types, ex:
    object({ x: number.to(n => new Date(n)) }).parse({ x: 4 }); // unknown type
 
    // solution
    const x2: Type<Date= number.to(n => new Date(n));
    object({ x2 }).parse({ x2: 4 }); // : { x: Date }
 
    // type-safe composition
    const str2num = (s: string) => Number(s);
    const div = (n: number) => n / 2;
    const num2str = (n: number) => String(n);
    const str2arr = (s: string) => s.split("1");
    const nonSense = string
      .compose(str2num)
      .compose(div)
      .compose(num2str)
      .compose(str2arr);
    nonSense.parseResult("1234567890"); // : Array<string>
 
    // you can convert sync type to async one
    string.async();
  });
});

if you do not want import the entire library, you can find single validators in import { object } from 'flow-validator/sync/object'; or import { asyncArrayOf } from 'flow-validator/async/asyncArrayOf';

Implemented types / combinators

Type Flow syntax Runtime type
string string string
number number number
boolean boolean boolean
generic object Object objectType
generic function Function Function
instance of C C instanceOf(C)
class of C Class<C> classOf(C)
array Array<A> arrayOf(A)
intersection A & B intersection(A, B)
union A | B union(A, B)
literal 's' literal('s')
optional ?A optional(A)
map { [key: A]: B } mapping(A, B)
refinement number.refine(n => { if (n > 10) return n; throw new Error(); })
object { name: string } object({ name: string })
exact object {| name: string |} objectExact({ name: string })
null null isNull
undefined void isUndefined
not checked any isAny
all types mixed isMixed
function (a: A) => B takes(A)(returns(B)(...))

Included refinements

Type Refinements Transformations
string .isEmail() .isValidDate() .minLength() .maxLength() .regexp() .toDate()

Technical documentation

for older versions:

git clone https://github.com/freddi301/flow-validator.git
cd flow-validator
yarn
npm run doc:serve

Feature Requests Wanted

(open issue, include examples or links)

Inspiration

flow-io (checkout io-ts too for typescript)

Alternatives

Other Alternatives - not flow typed

Planned Features

  • 0.7.0
  • 0.8.0
    • generate documentation from types (md, html, jsonschema, blueprint, mson, graphql-schema)
    • generate flow-validator validators from flow annotations, jsonschema and graphql-schema (cli, runtime, compiletime, startuptime)
    • json schema validation
  • 0.9.0
    • refactor validators to contain metadata
    • refactor errors to contain metadata
    • write visitors for validators and errors
    • write default interpreters for errors json and optionally localized text
  • 1.0.0
    • test 100%
    • doc examples for all validators
    • better flow coverage where possible
    • readme += new type example
  • 2.0.0

Experimental

  • readme += alternate use: json graphql alternative
  • rewrite match
  • Vmatch asyncMatch asyncVmatch
  • overloading
  • monad do notation using row polymorphism
  • auto row currying (aka builder)
  • literal values

Other

  • move documentation to surge.sh

Code Climate Test Coverage Issue Count

bitHound Overall Score bitHound Code bitHound Dependencies bitHound Dev Dependencies

NSP Status repo size

NPM

Package Sidebar

Install

npm i flow-validator

Weekly Downloads

3

Version

0.6.2

License

MIT

Last publish

Collaborators

  • freddi301