0.6.2 • Published 7 years ago

flow-validator v0.6.2

Weekly downloads
9
License
MIT
Repository
github
Last release
7 years ago

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

TypeFlow syntaxRuntime type
stringstringstring
numbernumbernumber
booleanbooleanboolean
generic objectObjectobjectType
generic functionFunctionFunction
instance of CCinstanceOf(C)
class of CClass<C>classOf(C)
arrayArray<A>arrayOf(A)
intersectionA & Bintersection(A, B)
unionA | Bunion(A, B)
literal's'literal('s')
optional?Aoptional(A)
map{ [key: A]: B }mapping(A, B)
refinementnumber.refine(n => { if (n > 10) return n; throw new Error(); })
object{ name: string }object({ name: string })
exact object{| name: string |}objectExact({ name: string })
nullnullisNull
undefinedvoidisUndefined
not checkedanyisAny
all typesmixedisMixed
function(a: A) => Btakes(A)(returns(B)(...))

Included refinements

TypeRefinementsTransformations
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

0.6.2

7 years ago

0.6.1

7 years ago

0.6.0

7 years ago

0.5.4

7 years ago

0.5.3

7 years ago

0.5.2

7 years ago

0.5.1

7 years ago

0.5.0

7 years ago

0.4.2

7 years ago

0.4.1

7 years ago

0.4.0

7 years ago

0.3.8

7 years ago

0.3.7

7 years ago

0.3.6

7 years ago

0.3.5

7 years ago

0.3.4

7 years ago

0.3.3

7 years ago

0.3.2

7 years ago

0.3.1

7 years ago

0.3.0

7 years ago

0.2.0

7 years ago

0.1.5

7 years ago

0.1.4

7 years ago

0.1.3

7 years ago

0.1.2

7 years ago

0.1.1

7 years ago

0.1.0

7 years ago

0.0.2

7 years ago