Comparing version 0.0.1 to 0.1.0
@@ -1,1 +0,6 @@ | ||
# CHANGELOG | ||
# CHANGELOG | ||
## 0.1.0 | ||
* Allow for filtering schema entries | ||
* Add `isEmail`, `isMatching` and `isPassing` validators |
@@ -1,2 +0,2 @@ | ||
import { Source, Result } from "mappet"; | ||
import { Source, Result, Filter } from "mappet"; | ||
export interface Message { | ||
@@ -14,4 +14,5 @@ (value: any): string; | ||
} | ||
export declare type SchemaEntry = [string, string, EntryValidator[]]; | ||
export declare type Schema = SchemaEntry[]; | ||
export declare type BasicSchemaEntry = [string, string, EntryValidator[]]; | ||
export declare type FilterableSchemaEntry = [string, string, EntryValidator[], Filter]; | ||
export declare type Schema = [BasicSchemaEntry | FilterableSchemaEntry]; | ||
/** | ||
@@ -18,0 +19,0 @@ * Produce validator based on provided schema |
@@ -12,3 +12,4 @@ "use strict"; | ||
var dest = _a[0]; | ||
return valid && !get(errors, dest).length; | ||
var err = get(errors, dest); | ||
return valid && (err === undefined || get(errors, dest).length === 0); | ||
}, true); | ||
@@ -24,4 +25,4 @@ } | ||
.map(function (_a) { | ||
var dest = _a[0], source = _a[1], validators = _a[2]; | ||
return [dest, source, buildModifier(validators)]; | ||
var dest = _a[0], source = _a[1], validators = _a[2], filter = _a[3]; | ||
return [dest, source, buildModifier(validators), filter]; | ||
}); | ||
@@ -28,0 +29,0 @@ return function (source) { |
import { Message } from "./policeman"; | ||
export declare const isRequired: (message: Message) => (value: string) => string; | ||
export declare const minLength: (min: number) => (message: Message) => (value: string) => string; | ||
export declare const maxLength: (max: number) => (message: Message) => (value: string) => string; | ||
export declare const isRequired: _.CurriedFunction2<Message, string | boolean, string>; | ||
export declare const minLength: _.CurriedFunction3<number, Message, string, string>; | ||
export declare const maxLength: _.CurriedFunction3<number, Message, string, string>; | ||
export declare const isEmail: _.CurriedFunction2<Message, string, string>; | ||
export declare const isMatching: _.CurriedFunction3<RegExp, Message, string, string>; | ||
export declare const isPassing: _.CurriedFunction3<(value: any) => boolean, Message, string, string>; |
"use strict"; | ||
exports.isRequired = function (message) { return function (value) { | ||
var valid = value !== null && value !== undefined && value !== ""; | ||
var curry = require("lodash/curry"); | ||
exports.isRequired = curry(function (message, value) { | ||
var valid = value !== null && value !== undefined && value !== "" && value; | ||
return valid ? null : message(value); | ||
}; }; | ||
exports.minLength = function (min) { return function (message) { return function (value) { | ||
}); | ||
exports.minLength = curry(function (min, message, value) { | ||
var valid = value.length >= min; | ||
return valid ? null : message(value); | ||
}; }; }; | ||
exports.maxLength = function (max) { return function (message) { return function (value) { | ||
}); | ||
exports.maxLength = curry(function (max, message, value) { | ||
var valid = value.length <= max; | ||
return valid ? null : message(value); | ||
}; }; }; | ||
}); | ||
exports.isEmail = curry(function (message, value) { | ||
var valid = /^[a-z0-9_\-\.]{2,}@[a-z0-9_\-\.]{2,}\.[a-z]{2,}$/i.test(value); | ||
return valid ? null : message(value); | ||
}); | ||
exports.isMatching = curry(function (regexp, message, value) { | ||
var valid = regexp.test(value); | ||
return valid ? null : message(value); | ||
}); | ||
exports.isPassing = curry(function (predicate, message, value) { | ||
var valid = predicate(value); | ||
return valid ? null : message(value); | ||
}); | ||
//# sourceMappingURL=validators.js.map |
{ | ||
"name": "policeman", | ||
"version": "0.0.1", | ||
"version": "0.1.0", | ||
"description": "Lightweight yet powerful schema validator", | ||
@@ -5,0 +5,0 @@ "main": "lib/policeman.js", |
# policeman | ||
[![CircleCI](https://circleci.com/gh/MichalZalecki/policeman.svg?style=svg)](https://circleci.com/gh/MichalZalecki/policeman) | ||
Lightweight yet powerful schema validator | ||
*** | ||
[API Docs](https://michalzalecki.github.io/policeman) | ||
[API Docs](https://michalzalecki.github.io/policeman) | [Examples](#examples) | ||
*** | ||
@@ -16,2 +18,37 @@ | ||
npm i -S policeman | ||
``` | ||
``` | ||
## Built-in validators | ||
### `isRequired(message, value)` | ||
Validates presence. | ||
### `minLength(min, message, value)` | ||
Passed `value` must be a string longer or with length equal to `min`. | ||
### `maxLength(max, message, value)` | ||
Passed `value` must be a string shorther or with length equal to `max`. | ||
### `isEmail(message, value)` | ||
Passed `value` must be a valid email. | ||
### `isMatching(regexp, message, value)` | ||
Passed `value` must pass `regexp`. | ||
### `isPassing(predicate, message, value)` | ||
Passed `predicate` answers on "Is `value` valid?". When `predicate` returns `true` validator passes, | ||
when `predicate` returns `false` error message is returned. | ||
See [tests](src/test/validators.test.ts) for more examples. | ||
## Examples | ||
TODO | ||
See [tests](src/test/policeman.test.ts) for more examples. |
import * as get from "lodash/get"; | ||
import mappet, { Source, Result, Modifier, Schema as MappetSchema } from "mappet"; | ||
import mappet, { Source, Result, Modifier, Filter, Schema as MappetSchema } from "mappet"; | ||
@@ -16,4 +16,5 @@ export interface Message { | ||
export type SchemaEntry = [string, string, EntryValidator[]]; | ||
export type Schema = SchemaEntry[]; | ||
export type BasicSchemaEntry = [string, string, EntryValidator[]]; | ||
export type FilterableSchemaEntry = [string, string, EntryValidator[], Filter]; | ||
export type Schema = [BasicSchemaEntry | FilterableSchemaEntry]; | ||
@@ -27,3 +28,6 @@ function buildModifier(validators: EntryValidator[]): Modifier { | ||
function checkValidity(schema: Schema, errors: Source): boolean { | ||
return schema.reduce((valid, [dest]) => valid && !(<string[]>get(errors, dest)).length, true); | ||
return schema.reduce((valid, [dest]) => { | ||
const err = get(errors, dest); | ||
return valid && (err === undefined || (<string[]> get(errors, dest)).length === 0); | ||
}, true); | ||
} | ||
@@ -33,14 +37,15 @@ | ||
* Produce validator based on provided schema | ||
* | ||
* | ||
* @returns Validator based on provided schema | ||
*/ | ||
export default function policeman(schema: Schema): SchemaValidator { | ||
const validationSchema = <MappetSchema>schema | ||
.map(([dest, source, validators]) => [dest, source, buildModifier(validators)]); | ||
const validationSchema = <MappetSchema> schema | ||
.map(<FilterableSchemaEntry>([dest, source, validators, filter]) => | ||
[dest, source, buildModifier(validators), filter]); | ||
return (source: Source) => { | ||
const errors: Result = mappet(validationSchema)(source); | ||
const valid: boolean = checkValidity(schema, errors); | ||
const errors = mappet(validationSchema)(source); | ||
const valid = checkValidity(schema, errors); | ||
return { errors, valid }; | ||
}; | ||
} | ||
} |
@@ -0,16 +1,32 @@ | ||
import * as curry from "lodash/curry"; | ||
import { Message } from "./policeman"; | ||
export const isRequired = (message: Message) => (value: string) => { | ||
const valid = value !== null && value !== undefined && value !== ""; | ||
export const isRequired = curry((message: Message, value: string | boolean) => { | ||
const valid = value !== null && value !== undefined && value !== "" && value; | ||
return valid ? null : message(value); | ||
}; | ||
}); | ||
export const minLength = (min: number) => (message: Message) => (value: string) => { | ||
export const minLength = curry((min: number, message: Message, value: string) => { | ||
const valid = value.length >= min; | ||
return valid ? null : message(value); | ||
}; | ||
}); | ||
export const maxLength = (max: number) => (message: Message) => (value: string) => { | ||
export const maxLength = curry((max: number, message: Message, value: string) => { | ||
const valid = value.length <= max; | ||
return valid ? null : message(value); | ||
}; | ||
}); | ||
export const isEmail = curry((message: Message, value: string) => { | ||
const valid = /^[a-z0-9_\-\.]{2,}@[a-z0-9_\-\.]{2,}\.[a-z]{2,}$/i.test(value); | ||
return valid ? null : message(value); | ||
}); | ||
export const isMatching = curry((regexp: RegExp, message: Message, value: string) => { | ||
const valid = regexp.test(value); | ||
return valid ? null : message(value); | ||
}); | ||
export const isPassing = curry((predicate: ((value: any) => boolean), message: Message, value: string) => { | ||
const valid = predicate(value); | ||
return valid ? null : message(value); | ||
}); |
import * as tape from "tape"; | ||
import { Filter } from "mappet"; | ||
import policeman, { Schema } from "../lib/policeman"; | ||
@@ -20,9 +21,9 @@ import { isRequired, minLength } from "../lib/validators"; | ||
bio: "Lorem ipsum", | ||
} | ||
}; | ||
const expected = { | ||
firstName: <any[]>[], | ||
firstName: <any[]> [], | ||
lastName: ["is required"], | ||
other: { | ||
bio: ["should contain at least 10 characters"], | ||
} | ||
}, | ||
}; | ||
@@ -41,6 +42,26 @@ const { errors } = validator(source); | ||
function makeUseOfMapperFilter(t: tape.Test) { | ||
interface User { | ||
name: string; | ||
age: number; | ||
clubCard: string; | ||
} | ||
const required = isRequired(() => "is required"); | ||
const isUnderage: Filter = (_value, source) => (<User> source).age < 18; | ||
const schema: Schema = [ | ||
["name", "name", [required]], | ||
["clubCard", "clubCard", [required], isUnderage], | ||
]; | ||
const validator = policeman(schema); | ||
t.is(validator({ name: "Foo", age: 18 }).valid, true); | ||
t.is(validator({ name: "Foo", age: 17 }).valid, false); | ||
t.deepEqual((<User> validator({ name: "Foo", age: 17 }).errors).clubCard, ["is required"]); | ||
t.is(validator({ name: "Foo", age: 17, clubCard: "123ABC" }).valid, true); | ||
} | ||
tape("policeman", (t: tape.Test) => { | ||
t.plan(3); | ||
t.plan(7); | ||
returnsErrorObjects(t); | ||
determinesWhetherSourceIsValid(t); | ||
}); | ||
makeUseOfMapperFilter(t); | ||
}); |
import "./policeman.test"; | ||
import "./validators.test"; | ||
import "./validators.test"; |
@@ -7,22 +7,26 @@ import * as tape from "tape"; | ||
maxLength, | ||
isEmail, | ||
isMatching, | ||
isPassing, | ||
} from "../lib/validators"; | ||
function testIsRequired(t: tape.Test) { | ||
const required = isRequired(() => "Is required"); | ||
t.is(required(""), "Is required", "isRequired fails on empty string"); | ||
t.is(required(null), "Is required", "isRequired fails on null"); | ||
t.is(required(undefined), "Is required", "isRequired fails on undefined"); | ||
t.is(required(""), "Is required", "isRequired accepts not empty strings"); | ||
const required = isRequired(() => "is required"); | ||
t.is(required(""), "is required", "isRequired fails on empty string"); | ||
t.is(required(null), "is required", "isRequired fails on null"); | ||
t.is(required(undefined), "is required", "isRequired fails on undefined"); | ||
t.is(required(false), "is required", "isRequired fails on false"); | ||
t.is(required("foo"), null, "isRequired accepts not empty strings"); | ||
} | ||
function testMinLengthValidator(t: tape.Test) { | ||
const minLength4: EntryValidator = minLength(4)(() => "Should be at least 4"); | ||
const minLength4: EntryValidator = minLength(4, () => "should be at least 4"); | ||
t.is(minLength4("foo bar"), null, "minLength(4) accepts strings with 4+ characters"); | ||
t.is(minLength4("fooz"), null, "minLength(4) accepts strings with 4 characters"); | ||
t.is(minLength4("foo"), "Should be at least 4", "minLength(4) fails on strings with 4- characters"); | ||
t.is(minLength4("foo"), "should be at least 4", "minLength(4) fails on strings with 4- characters"); | ||
} | ||
function testMaxLengthValidator(t: tape.Test) { | ||
const maxLength4: EntryValidator = maxLength(4)(() => "Should be at most 4"); | ||
t.is(maxLength4("foo bar"), "Should be at most 4", "maxLength(4) fails on strings with 4+ characters"); | ||
const maxLength4: EntryValidator = maxLength(4, () => "should be at most 4"); | ||
t.is(maxLength4("foo bar"), "should be at most 4", "maxLength(4) fails on strings with 4+ characters"); | ||
t.is(maxLength4("fooz"), null, "maxLength(4) accepts strings with 4 characters"); | ||
@@ -32,7 +36,30 @@ t.is(maxLength4("foo"), null, "maxLength(4) accepts strings with 4- characters"); | ||
function testIsEmail(t: tape.Test) { | ||
const email = isEmail(() => "is invalid email"); | ||
t.is(email("foo@bar.com"), null, "isEmail accepts valid email"); | ||
t.is(email("foo@.com"), "is invalid email", "isEmail fails on invalid email"); | ||
} | ||
function testIsMatching(t: tape.Test) { | ||
const matching = isMatching(/^\d{3}-?\d{3}-?\d{3}$/, () => "is invalid number"); | ||
t.is(matching("777-888-999"), null, "isMatching(/^\d{3}-?\d{3}-?\d{3}$/) accepts valid number"); | ||
t.is(matching("777888999"), null, "isMatching(/^\d{3}-?\d{3}-?\d{3}$/) accepts valid number"); | ||
t.is(matching("aaa-bbb-ccc"), "is invalid number", "isMatching(/^\d{3}-?\d{3}-?\d{3}$/) fails on invalid number"); | ||
} | ||
function testIsPassing(t: tape.Test) { | ||
const isFoo = (str: string) => str === "foo"; | ||
const matching = isPassing(isFoo, () => "must be \"foo\""); | ||
t.is(matching("foo"), null, "predicate returns true on \"foo\""); | ||
t.is(matching("bar"), "must be \"foo\"", "predicate returns false on not \"foo\""); | ||
} | ||
tape("validators", (t: tape.Test) => { | ||
t.plan(10); | ||
t.plan(18); | ||
testIsRequired(t); | ||
testMinLengthValidator(t); | ||
testMaxLengthValidator(t); | ||
}); | ||
testIsEmail(t); | ||
testIsMatching(t); | ||
testIsPassing(t); | ||
}); |
@@ -25,3 +25,3 @@ "use strict"; | ||
bio: ["should contain at least 10 characters"], | ||
} | ||
}, | ||
}; | ||
@@ -38,7 +38,21 @@ var errors = validator(source).errors; | ||
} | ||
function makeUseOfMapperFilter(t) { | ||
var required = validators_1.isRequired(function () { return "is required"; }); | ||
var isUnderage = function (_value, source) { return source.age < 18; }; | ||
var schema = [ | ||
["name", "name", [required]], | ||
["clubCard", "clubCard", [required], isUnderage], | ||
]; | ||
var validator = policeman_1.default(schema); | ||
t.is(validator({ name: "Foo", age: 18 }).valid, true); | ||
t.is(validator({ name: "Foo", age: 17 }).valid, false); | ||
t.deepEqual(validator({ name: "Foo", age: 17 }).errors.clubCard, ["is required"]); | ||
t.is(validator({ name: "Foo", age: 17, clubCard: "123ABC" }).valid, true); | ||
} | ||
tape("policeman", function (t) { | ||
t.plan(3); | ||
t.plan(7); | ||
returnsErrorObjects(t); | ||
determinesWhetherSourceIsValid(t); | ||
makeUseOfMapperFilter(t); | ||
}); | ||
//# sourceMappingURL=policeman.test.js.map |
@@ -5,26 +5,47 @@ "use strict"; | ||
function testIsRequired(t) { | ||
var required = validators_1.isRequired(function () { return "Is required"; }); | ||
t.is(required(""), "Is required", "isRequired fails on empty string"); | ||
t.is(required(null), "Is required", "isRequired fails on null"); | ||
t.is(required(undefined), "Is required", "isRequired fails on undefined"); | ||
t.is(required(""), "Is required", "isRequired accepts not empty strings"); | ||
var required = validators_1.isRequired(function () { return "is required"; }); | ||
t.is(required(""), "is required", "isRequired fails on empty string"); | ||
t.is(required(null), "is required", "isRequired fails on null"); | ||
t.is(required(undefined), "is required", "isRequired fails on undefined"); | ||
t.is(required(false), "is required", "isRequired fails on false"); | ||
t.is(required("foo"), null, "isRequired accepts not empty strings"); | ||
} | ||
function testMinLengthValidator(t) { | ||
var minLength4 = validators_1.minLength(4)(function () { return "Should be at least 4"; }); | ||
var minLength4 = validators_1.minLength(4, function () { return "should be at least 4"; }); | ||
t.is(minLength4("foo bar"), null, "minLength(4) accepts strings with 4+ characters"); | ||
t.is(minLength4("fooz"), null, "minLength(4) accepts strings with 4 characters"); | ||
t.is(minLength4("foo"), "Should be at least 4", "minLength(4) fails on strings with 4- characters"); | ||
t.is(minLength4("foo"), "should be at least 4", "minLength(4) fails on strings with 4- characters"); | ||
} | ||
function testMaxLengthValidator(t) { | ||
var maxLength4 = validators_1.maxLength(4)(function () { return "Should be at most 4"; }); | ||
t.is(maxLength4("foo bar"), "Should be at most 4", "maxLength(4) fails on strings with 4+ characters"); | ||
var maxLength4 = validators_1.maxLength(4, function () { return "should be at most 4"; }); | ||
t.is(maxLength4("foo bar"), "should be at most 4", "maxLength(4) fails on strings with 4+ characters"); | ||
t.is(maxLength4("fooz"), null, "maxLength(4) accepts strings with 4 characters"); | ||
t.is(maxLength4("foo"), null, "maxLength(4) accepts strings with 4- characters"); | ||
} | ||
function testIsEmail(t) { | ||
var email = validators_1.isEmail(function () { return "is invalid email"; }); | ||
t.is(email("foo@bar.com"), null, "isEmail accepts valid email"); | ||
t.is(email("foo@.com"), "is invalid email", "isEmail fails on invalid email"); | ||
} | ||
function testIsMatching(t) { | ||
var matching = validators_1.isMatching(/^\d{3}-?\d{3}-?\d{3}$/, function () { return "is invalid number"; }); | ||
t.is(matching("777-888-999"), null, "isMatching(/^\d{3}-?\d{3}-?\d{3}$/) accepts valid number"); | ||
t.is(matching("777888999"), null, "isMatching(/^\d{3}-?\d{3}-?\d{3}$/) accepts valid number"); | ||
t.is(matching("aaa-bbb-ccc"), "is invalid number", "isMatching(/^\d{3}-?\d{3}-?\d{3}$/) fails on invalid number"); | ||
} | ||
function testIsPassing(t) { | ||
var isFoo = function (str) { return str === "foo"; }; | ||
var matching = validators_1.isPassing(isFoo, function () { return "must be \"foo\""; }); | ||
t.is(matching("foo"), null, "predicate returns true on \"foo\""); | ||
t.is(matching("bar"), "must be \"foo\"", "predicate returns false on not \"foo\""); | ||
} | ||
tape("validators", function (t) { | ||
t.plan(10); | ||
t.plan(18); | ||
testIsRequired(t); | ||
testMinLengthValidator(t); | ||
testMaxLengthValidator(t); | ||
testIsEmail(t); | ||
testIsMatching(t); | ||
testIsPassing(t); | ||
}); | ||
//# sourceMappingURL=validators.test.js.map |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
700965
21292
54
0