Comparing version 1.0.0-beta.4 to 1.0.0-beta.5
export { FefeError } from './errors'; | ||
import * as transform from './transform/transform'; | ||
export { transform }; | ||
import * as validate from './validate/validate'; | ||
export { validate }; | ||
export { Validator } from './validate'; | ||
export { array } from './array'; | ||
export { boolean } from './boolean'; | ||
export { date } from './date'; | ||
export { _enum as enum } from './enum'; | ||
export { number } from './number'; | ||
export { object } from './object'; | ||
export { parseBoolean } from './parse-boolean'; | ||
export { parseDate } from './parse-date'; | ||
export { parseJson } from './parse-json'; | ||
export { parseNumber } from './parse-number'; | ||
export { string } from './string'; | ||
export { union } from './union'; |
"use strict"; | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; | ||
result["default"] = mod; | ||
return result; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var errors_1 = require("./errors"); | ||
exports.FefeError = errors_1.FefeError; | ||
const transform = __importStar(require("./transform/transform")); | ||
exports.transform = transform; | ||
const validate = __importStar(require("./validate/validate")); | ||
exports.validate = validate; | ||
var array_1 = require("./array"); | ||
exports.array = array_1.array; | ||
var boolean_1 = require("./boolean"); | ||
exports.boolean = boolean_1.boolean; | ||
var date_1 = require("./date"); | ||
exports.date = date_1.date; | ||
var enum_1 = require("./enum"); | ||
exports.enum = enum_1._enum; | ||
var number_1 = require("./number"); | ||
exports.number = number_1.number; | ||
var object_1 = require("./object"); | ||
exports.object = object_1.object; | ||
var parse_boolean_1 = require("./parse-boolean"); | ||
exports.parseBoolean = parse_boolean_1.parseBoolean; | ||
var parse_date_1 = require("./parse-date"); | ||
exports.parseDate = parse_date_1.parseDate; | ||
var parse_json_1 = require("./parse-json"); | ||
exports.parseJson = parse_json_1.parseJson; | ||
var parse_number_1 = require("./parse-number"); | ||
exports.parseNumber = parse_number_1.parseNumber; | ||
var string_1 = require("./string"); | ||
exports.string = string_1.string; | ||
var union_1 = require("./union"); | ||
exports.union = union_1.union; | ||
//# sourceMappingURL=index.js.map |
"use strict"; | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; | ||
result["default"] = mod; | ||
return result; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const chai_1 = require("chai"); | ||
const ramda_1 = require("ramda"); | ||
const _1 = require("."); | ||
const fefe = __importStar(require(".")); | ||
describe('Integration tests', () => { | ||
describe('Basic validation', () => { | ||
const validatePerson = _1.validate.object({ | ||
name: _1.validate.string(), | ||
age: _1.validate.number({ min: 0 }), | ||
address: _1.validate.object({ | ||
street: _1.validate.string(), | ||
zip: _1.validate.number() | ||
const validatePerson = fefe.object({ | ||
name: fefe.string(), | ||
age: fefe.number({ min: 0 }), | ||
address: fefe.object({ | ||
street: fefe.string(), | ||
zip: fefe.number() | ||
}), | ||
isVerified: _1.validate.boolean(), | ||
verifiedAt: _1.validate.union(_1.validate.date(), _1.validate.enum('never')), | ||
joinedAt: _1.validate.date(), | ||
favoriteDishes: _1.validate.array(_1.validate.string()), | ||
notifications: _1.validate.enum('immediately', 'daily', 'never') | ||
isVerified: fefe.boolean(), | ||
verifiedAt: fefe.union(fefe.date(), fefe.enum('never')), | ||
joinedAt: fefe.date(), | ||
favoriteDishes: fefe.array(fefe.string()), | ||
notifications: fefe.enum('immediately', 'daily', 'never') | ||
}); | ||
@@ -38,3 +45,3 @@ const validPerson = { | ||
chai_1.expect(() => validatePerson(invalidPerson)) | ||
.to.throw(_1.FefeError, 'address.zip: Not a number.') | ||
.to.throw(fefe.FefeError, 'address.zip: Not a number.') | ||
.that.deep.include({ value: invalidPerson, path: ['address', 'zip'] }) | ||
@@ -45,5 +52,5 @@ .and.has.property('originalError').that.include({ value: 'foo' }); | ||
describe('Basic transformation (sanitization)', () => { | ||
const sanitizeMovie = _1.validate.object({ | ||
title: _1.validate.string(), | ||
releasedAt: ramda_1.pipe(_1.validate.string(), _1.transform.parseDate()) | ||
const sanitizeMovie = fefe.object({ | ||
title: fefe.string(), | ||
releasedAt: fefe.parseDate() | ||
}); | ||
@@ -63,3 +70,3 @@ it('validates a movie and parses the date string', () => { | ||
chai_1.expect(() => sanitizeMovie(invalidMovie)) | ||
.to.throw(_1.FefeError, 'releasedAt: Not a date.') | ||
.to.throw(fefe.FefeError, 'releasedAt: Not a date.') | ||
.that.deep.include({ value: invalidMovie, path: ['releasedAt'] }) | ||
@@ -69,6 +76,22 @@ .and.has.property('originalError').that.include({ value: 'foo' }); | ||
}); | ||
describe('Basic transformation (on-demand sanitization)', () => { | ||
const sanitizeDate = fefe.union(fefe.date(), fefe.parseDate()); | ||
const date = new Date(); | ||
it('returns a date', () => { | ||
const sanitizedDate = sanitizeDate(date); | ||
chai_1.expect(sanitizedDate).to.equal(date); | ||
}); | ||
it('returns a parsed date', () => { | ||
const sanitizedDate = sanitizeDate(date.toISOString()); | ||
chai_1.expect(sanitizedDate).to.eql(date); | ||
}); | ||
it('throws with an invalid date', () => { | ||
chai_1.expect(() => sanitizeDate('foo')) | ||
.to.throw(fefe.FefeError, 'Not of any expected type.'); | ||
}); | ||
}); | ||
describe('Complex transformation and validation', () => { | ||
const parseConfig = _1.validate.object({ | ||
gcloudCredentials: ramda_1.pipe(_1.validate.string(), _1.transform.parseJson(), _1.validate.object({ key: _1.validate.string() })), | ||
whitelist: ramda_1.pipe(_1.validate.string(), value => value.split(',')) | ||
const parseConfig = fefe.object({ | ||
gcloudCredentials: ramda_1.pipe(fefe.parseJson(), fefe.object({ key: fefe.string() })), | ||
whitelist: ramda_1.pipe(fefe.string(), value => value.split(',')) | ||
}); | ||
@@ -89,3 +112,3 @@ const validConfig = { | ||
const invalidConfigInput = Object.assign({}, validConfigInput, { gcloudCredentials: '{ "key": "secret", "foo": "bar" }' }); | ||
chai_1.expect(() => parseConfig(invalidConfigInput)).to.throw(_1.FefeError, 'gcloudCredentials: Properties not allowed: foo') | ||
chai_1.expect(() => parseConfig(invalidConfigInput)).to.throw(fefe.FefeError, 'gcloudCredentials: Properties not allowed: foo') | ||
.that.deep.include({ value: invalidConfigInput, path: ['gcloudCredentials'] }) | ||
@@ -92,0 +115,0 @@ .and.has.property('originalError').that.include({ value: { key: 'secret', foo: 'bar' } }); |
{ | ||
"name": "fefe", | ||
"version": "1.0.0-beta.4", | ||
"version": "1.0.0-beta.5", | ||
"description": "Validate, sanitize and transform values with proper types.", | ||
@@ -42,3 +42,3 @@ "main": "dist/index.js", | ||
"@types/mocha": "^5.2.5", | ||
"@types/ramda": "^0.25.42", | ||
"@types/ramda": "^0.25.45", | ||
"chai": "^4.2.0", | ||
@@ -50,6 +50,6 @@ "codecov": "^3.1.0", | ||
"ts-node": "^7.0.1", | ||
"tslint": "^5.11.0", | ||
"tslint": "^5.12.0", | ||
"tslint-config-standard": "^8.0.1", | ||
"typescript": "^3.1.6" | ||
"typescript": "^3.2.2" | ||
} | ||
} |
@@ -12,3 +12,3 @@ # fefe | ||
**🛠️ Transformation:** transforms a value (example: parse JSON)<br/> | ||
**🔌 Schemas are functions**: easily extendable | ||
**🔌 Everything is a function**: functional approach makes it easy to extend – just plug in your own function anywhere! | ||
@@ -28,5 +28,5 @@ ## Installation | ||
```typescript | ||
import { validate } from 'fefe' | ||
import { object, string } from 'fefe' | ||
const validatePerson = validate.object({ name: validate.string() }) | ||
const validatePerson = object({ name: string() }) | ||
@@ -46,13 +46,14 @@ // result is of type { name: string } | ||
### ⚙️ Basic transformation example (sanitization/parsing) | ||
### ⚙️ Basic transformation example | ||
In this example a `string` needs to be parsed as a `Date`. Note how easy it is to apply a chain of functions to validate and transform a value (here we use `ramda`). | ||
#### Parse a value | ||
In this example a `string` needs to be parsed as a `Date`. | ||
```typescript | ||
import { transform, validate } from 'fefe' | ||
import { pipe } from 'ramda' | ||
import { object, parseDate, string } from 'fefe' | ||
const sanitizeMovie = validate.object({ | ||
title: validate.string(), | ||
releasedAt: pipe(validate.string(), transform.parseDate()) | ||
const sanitizeMovie = object({ | ||
title: string(), | ||
releasedAt: parseDate() | ||
}) | ||
@@ -71,24 +72,33 @@ | ||
#### Parse a value on demand (sanitize) | ||
Sometimes a value might already be of the right type. In the following example we use `union()` to create a sanitizer that returns a provided value if it is a Date already and parse it otherwise. If it can't be parsed either the function will throw: | ||
```typescript | ||
import { date, parseDate, union } from 'fefe' | ||
const sanitizeDate = union(date(), parseDate()) | ||
``` | ||
### 🛠️ Complex transformation example | ||
This is a more complex example that can be applied to parsing environment variables or query string parameters. | ||
This is a more complex example that can be applied to parsing environment variables or query string parameters. Note how easy it is to apply a chain of functions to validate and transform a value (here we use `ramda`). | ||
```typescript | ||
import { transform, validate } from 'fefe' | ||
import { object, parseJson, string } from 'fefe' | ||
import { pipe } from 'ramda' | ||
const parseConfig = validate.object({ | ||
const parseConfig = object({ | ||
gcloudCredentials: pipe( | ||
validate.string(), | ||
transform.parseJson(), | ||
validate.object({ key: validate.string() }) | ||
parseJson(), | ||
object({ secret: string() }) | ||
), | ||
whitelist: pipe(validate.string(), str => str.split(',')) | ||
whitelist: pipe(string(), secret => str.split(',')) | ||
}) | ||
// { gcloudCredentials: { key: string }, whitelist: string[] } | ||
// { gcloudCredentials: { secret: string }, whitelist: string[] } | ||
type Config = ReturnType<typeof parseConfig> | ||
const config: Config = parseConfig({ | ||
gcloudCredentials: '{"key":"secret"}', | ||
gcloudCredentials: '{"secret":"foobar"}', | ||
whitelist: 'alice,bob' | ||
@@ -98,6 +108,4 @@ }) | ||
Then `config` will equal `{ gcloudCredentials: { key: 'secret'}, whitelist: ['alice', 'bob'] }`. | ||
Then `config` will equal `{ gcloudCredentials: { secret: 'foobar'}, whitelist: ['alice', 'bob'] }`. | ||
**Note:** you can use validations in transformations. | ||
## Documentation | ||
@@ -113,17 +121,17 @@ | ||
### `validate.array(elementValidate, options?)` | ||
### `array(elementValidator, options?)` | ||
Returns a function `(value: unknown) => T[]` that checks that the given value is an array and that runs `elementValidate` on all elements. A new array with the results is returned. | ||
Returns a function `(value: unknown) => T[]` that checks that the given value is an array and that runs `elementValidator` on all elements. A new array with the results is returned. | ||
Options: | ||
* `elementValidate`: validator function `(value: unknown) => T` that is applied to each element. The return values are returned as a new array. | ||
* `elementValidator`: validator function `(value: unknown) => T` that is applied to each element. The return values are returned as a new array. | ||
* `options.minLength?`, `options.maxLength?`: restrict length of array | ||
### `validate.boolean()` | ||
### `boolean()` | ||
Returns a function `(value: unknown) => boolean` that checks that whether `value` is a boolean. | ||
Returns a function `(value: unknown) => boolean` that returns `value` if it is a boolean and throws otherwise. | ||
### `validate.date(options?)` | ||
### `date(options?)` | ||
Returns a function `(value: unknown) => Date` that checks that whether `value` is a Date. | ||
Returns a function `(value: unknown) => Date` that returns `value` if it is a Date and throws otherwise. | ||
@@ -133,9 +141,9 @@ Options: | ||
### `validate.enum(value1, value2, ...)` | ||
### `enum(value1, value2, ...)` | ||
Returns a function `(value: unknown) => value1 | value2 | ...` that checks whether value equals one of the strings `value1`, `value2`, .... | ||
Returns a function `(value: unknown) => value1 | value2 | ...` that returns `value` if if equals one of the strings `value1`, `value2`, .... and throws otherwise. | ||
### `validate.number(options?)` | ||
### `number(options?)` | ||
Returns a function `(value: unknown) => number` that checks that whether `value` is a number. | ||
Returns a function `(value: unknown) => number` that returns `value` if it is a number and throws otherwise. | ||
@@ -147,5 +155,5 @@ Options: | ||
### `validate.object(definition, options?)` | ||
### `object(definition, options?)` | ||
Returns a function `(value: unknown) => {...}` that checks that whether `value` is an object and all values pass the validation as specified in `definition`. A new object is returned that has the results of the validator functions as values. | ||
Returns a function `(value: unknown) => {...}` that returns `value` if it is an object and all values pass the validation as specified in `definition`, otherwise it throws. A new object is returned that has the results of the validator functions as values. | ||
@@ -159,7 +167,7 @@ Options: | ||
* `default?`: default value of type `T` or function `() => T` that returns a default value | ||
* `allowExcessProperties?`: allow excess properties (default: `false`) | ||
* `allowExcessProperties?`: allow excess properties in `value` (default: `false`). Excess properties are not copied to the returned object. | ||
### `validate.string(options?)` | ||
### `string(options?)` | ||
Returns a function `(value: unknown) => string` that checks that whether `value` is a string. | ||
Returns a function `(value: unknown) => string` that returns `value` if it is a string and throws otherwise. | ||
@@ -170,11 +178,11 @@ Options: | ||
### `validate.union(validator1, validator2, ...)` | ||
### `union(validator1, validator2, ...)` | ||
Returns a function `(value: unknown) => return1 | return2 | ...` that returns the return value of the first validator called with `value` that does not throw. The function throws if all validators throw. | ||
### `transform.parseBoolean()` | ||
### `parseBoolean()` | ||
Returns a function `(value: string) => boolean` that parses a string as a boolean. | ||
### `transform.parseDate(options?)` | ||
### `parseDate(options?)` | ||
@@ -186,8 +194,8 @@ Returns a function `(value: string) => Date` that parses a string as a date. | ||
### `transform.parseJson()` | ||
### `parseJson()` | ||
Returns a function `(value: string) => any` that parses JSON. | ||
Returns a function `(value: string) => any` that parses a JSON string. | ||
### `transform.parseNumber()` | ||
### `parseNumber()` | ||
Returns a function `(value: string) => number` that parses a number. | ||
Returns a function `(value: string) => number` that parses a number string. |
import { expect } from 'chai' | ||
import { pipe } from 'ramda' | ||
import { FefeError, transform, validate } from '.' | ||
import * as fefe from '.' | ||
describe('Integration tests', () => { | ||
describe('Basic validation', () => { | ||
const validatePerson = validate.object({ | ||
name: validate.string(), | ||
age: validate.number({ min: 0 }), | ||
address: validate.object({ | ||
street: validate.string(), | ||
zip: validate.number() | ||
const validatePerson = fefe.object({ | ||
name: fefe.string(), | ||
age: fefe.number({ min: 0 }), | ||
address: fefe.object({ | ||
street: fefe.string(), | ||
zip: fefe.number() | ||
}), | ||
isVerified: validate.boolean(), | ||
verifiedAt: validate.union(validate.date(), validate.enum('never')), | ||
joinedAt: validate.date(), | ||
favoriteDishes: validate.array(validate.string()), | ||
notifications: validate.enum('immediately', 'daily', 'never') | ||
isVerified: fefe.boolean(), | ||
verifiedAt: fefe.union(fefe.date(), fefe.enum('never')), | ||
joinedAt: fefe.date(), | ||
favoriteDishes: fefe.array(fefe.string()), | ||
notifications: fefe.enum('immediately', 'daily', 'never') | ||
}) | ||
@@ -43,3 +43,3 @@ | ||
expect(() => validatePerson(invalidPerson)) | ||
.to.throw(FefeError, 'address.zip: Not a number.') | ||
.to.throw(fefe.FefeError, 'address.zip: Not a number.') | ||
.that.deep.include({ value: invalidPerson, path: ['address', 'zip'] }) | ||
@@ -51,5 +51,5 @@ .and.has.property('originalError').that.include({ value: 'foo' }) | ||
describe('Basic transformation (sanitization)', () => { | ||
const sanitizeMovie = validate.object({ | ||
title: validate.string(), | ||
releasedAt: pipe(validate.string(), transform.parseDate()) | ||
const sanitizeMovie = fefe.object({ | ||
title: fefe.string(), | ||
releasedAt: fefe.parseDate() | ||
}) | ||
@@ -74,12 +74,32 @@ | ||
expect(() => sanitizeMovie(invalidMovie)) | ||
.to.throw(FefeError, 'releasedAt: Not a date.') | ||
.that.deep.include({ value: invalidMovie, path: ['releasedAt'] }) | ||
.and.has.property('originalError').that.include({ value: 'foo' }) | ||
.to.throw(fefe.FefeError, 'releasedAt: Not a date.') | ||
.that.deep.include({ value: invalidMovie, path: ['releasedAt'] }) | ||
.and.has.property('originalError').that.include({ value: 'foo' }) | ||
}) | ||
}) | ||
describe('Basic transformation (on-demand sanitization)', () => { | ||
const sanitizeDate = fefe.union(fefe.date(), fefe.parseDate()) | ||
const date = new Date() | ||
it('returns a date', () => { | ||
const sanitizedDate: Date = sanitizeDate(date) | ||
expect(sanitizedDate).to.equal(date) | ||
}) | ||
it('returns a parsed date', () => { | ||
const sanitizedDate: Date = sanitizeDate(date.toISOString()) | ||
expect(sanitizedDate).to.eql(date) | ||
}) | ||
it('throws with an invalid date', () => { | ||
expect(() => sanitizeDate('foo')) | ||
.to.throw(fefe.FefeError, 'Not of any expected type.') | ||
}) | ||
}) | ||
describe('Complex transformation and validation', () => { | ||
const parseConfig = validate.object({ | ||
gcloudCredentials: pipe(validate.string(), transform.parseJson(), validate.object({ key: validate.string() })), | ||
whitelist: pipe(validate.string(), value => value.split(',')) | ||
const parseConfig = fefe.object({ | ||
gcloudCredentials: pipe(fefe.parseJson(), fefe.object({ key: fefe.string() })), | ||
whitelist: pipe(fefe.string(), value => value.split(',')) | ||
}) | ||
@@ -106,3 +126,3 @@ | ||
const invalidConfigInput = { ...validConfigInput, gcloudCredentials: '{ "key": "secret", "foo": "bar" }' } | ||
expect(() => parseConfig(invalidConfigInput)).to.throw(FefeError, 'gcloudCredentials: Properties not allowed: foo') | ||
expect(() => parseConfig(invalidConfigInput)).to.throw(fefe.FefeError, 'gcloudCredentials: Properties not allowed: foo') | ||
.that.deep.include({ value: invalidConfigInput, path: ['gcloudCredentials'] }) | ||
@@ -109,0 +129,0 @@ .and.has.property('originalError').that.include({ value: { key: 'secret', foo: 'bar' } }) |
export { FefeError } from './errors' | ||
import * as transform from './transform/transform' | ||
export { transform } | ||
import * as validate from './validate/validate' | ||
export { validate } | ||
export { Validator } from './validate' | ||
export { array } from './array' | ||
export { boolean } from './boolean' | ||
export { date } from './date' | ||
export { _enum as enum } from './enum' | ||
export { number } from './number' | ||
export { object } from './object' | ||
export { parseBoolean } from './parse-boolean' | ||
export { parseDate } from './parse-date' | ||
export { parseJson } from './parse-json' | ||
export { parseNumber } from './parse-number' | ||
export { string } from './string' | ||
export { union } from './union' |
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
146567
198
2075
191
1