Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More โ†’
Socket
Sign inDemoInstall
Socket

fefe

Package Overview
Dependencies
Maintainers
1
Versions
25
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

fefe - npm Package Compare versions

Comparing version 1.0.0-beta.1 to 1.0.0-beta.2

44

dist/index.test.js

@@ -6,4 +6,4 @@ "use strict";

const _1 = require(".");
describe('validate integration tests', () => {
describe('Person', () => {
describe('Integration tests', () => {
describe('Basic validation', () => {
const validatePerson = _1.validate.object({

@@ -28,9 +28,10 @@ name: _1.validate.string(),

};
it('should validate a person', () => {
it('validates a person', () => {
const person = validatePerson(validPerson);
chai_1.expect(person).to.eql(validPerson);
});
it('should throw with an invalid person', () => {
it('throws with an invalid person', () => {
const invalidPerson = Object.assign({}, validPerson, { address: { street: 'Ackerstr', zip: 'foo' } });
chai_1.expect(() => validatePerson(invalidPerson)).to.throw(_1.FefeError, 'address.zip: Not a number.')
chai_1.expect(() => validatePerson(invalidPerson))
.to.throw(_1.FefeError, 'address.zip: Not a number.')
.that.deep.include({ value: invalidPerson, path: ['address', 'zip'] })

@@ -40,5 +41,26 @@ .and.has.property('originalError').that.include({ value: 'foo' });

});
});
describe('Complex example', () => {
describe('Config', () => {
describe('Basic transformation (sanitization)', () => {
const sanitizeMovie = _1.validate.object({
title: _1.validate.string(),
releasedAt: ramda_1.pipe(_1.validate.string(), _1.transform.parseDate())
});
it('validates a movie and parses the date string', () => {
const movie = sanitizeMovie({
title: 'Star Wars',
releasedAt: '1977-05-25T12:00:00.000Z'
});
chai_1.expect(movie).to.eql({
title: 'Star Wars',
releasedAt: new Date('1977-05-25T12:00:00.000Z')
});
});
it('throws with an invalid date', () => {
const invalidMovie = { title: 'Star Wars', releasedAt: 'foo' };
chai_1.expect(() => sanitizeMovie(invalidMovie))
.to.throw(_1.FefeError, 'releasedAt: Not a date.')
.that.deep.include({ value: invalidMovie, path: ['releasedAt'] })
.and.has.property('originalError').that.include({ value: 'foo' });
});
});
describe('Complex transformation and validation', () => {
const parseConfig = _1.validate.object({

@@ -56,9 +78,9 @@ gcloudCredentials: ramda_1.pipe(_1.validate.string(), _1.transform.parseJson(), _1.validate.object({ key: _1.validate.string() })),

};
it('should parse a config', () => {
it('parses a config', () => {
const config = parseConfig(validConfigInput);
chai_1.expect(config).to.eql(validConfig);
});
it('should throw with an invalid config', () => {
it('throws with an invalid config', () => {
const invalidConfigInput = Object.assign({}, validConfigInput, { gcloudCredentials: '{ "key": "secret", "foo": "bar" }' });
chai_1.expect(() => parseConfig(invalidConfigInput)).to.throw(_1.FefeError, 'gcloudCredentials: Key(s) not allowed: foo')
chai_1.expect(() => parseConfig(invalidConfigInput)).to.throw(_1.FefeError, 'gcloudCredentials: Properties not allowed: foo')
.that.deep.include({ value: invalidConfigInput, path: ['gcloudCredentials'] })

@@ -65,0 +87,0 @@ .and.has.property('originalError').that.include({ value: { key: 'secret', foo: 'bar' } });

import { Validate } from './validate';
export interface ValidateArrayOptions<R> {
elementValidate: Validate<R>;
minLength?: number;

@@ -8,2 +7,2 @@ maxLength?: number;

export declare type ValidateArrayValue<R> = Validate<R> | ValidateArrayOptions<R>;
export declare function validateArray<R>(_options: ValidateArrayOptions<R> | Validate<R>): (value: unknown) => R[];
export declare function validateArray<R>(elementValidate: Validate<R>, { minLength, maxLength }?: ValidateArrayOptions<R>): (value: unknown) => R[];
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const errors_1 = require("../errors");
function validateArray(_options) {
const { elementValidate, minLength, maxLength } = typeof _options === 'object'
? _options
: { elementValidate: _options };
function validateArray(elementValidate, { minLength, maxLength } = {}) {
return (value) => {

@@ -9,0 +6,0 @@ if (!Array.isArray(value))

@@ -9,3 +9,3 @@ "use strict";

it('should throw if not a array', () => {
const validate = validate_array_1.validateArray({ elementValidate: validate_string_1.validateString() });
const validate = validate_array_1.validateArray(validate_string_1.validateString());
chai_1.expect(() => validate('foo'))

@@ -16,3 +16,3 @@ .to.throw(errors_1.FefeError, 'Not an array.')

it('should throw if nested validation fails', () => {
const validate = validate_array_1.validateArray({ elementValidate: validate_string_1.validateString() });
const validate = validate_array_1.validateArray(validate_string_1.validateString());
const value = ['foo', 1];

@@ -24,3 +24,3 @@ chai_1.expect(() => validate(value))

it('should return a valid array', () => {
const validate = validate_array_1.validateArray({ elementValidate: validate_string_1.validateString() });
const validate = validate_array_1.validateArray(validate_string_1.validateString());
const value = ['foo', 'bar'];

@@ -30,5 +30,3 @@ chai_1.expect(validate(value)).to.eql(value);

it('should return a valid array with transformed values', () => {
const validate = validate_array_1.validateArray({
elementValidate: value => `transformed: ${validate_string_1.validateString()(value)}`
});
const validate = validate_array_1.validateArray(value => `transformed: ${validate_string_1.validateString()(value)}`);
chai_1.expect(validate(['foo', 'bar'])).to.eql(['transformed: foo', 'transformed: bar']);

@@ -35,0 +33,0 @@ });

@@ -12,5 +12,5 @@ import { Validate } from './validate';

} ? undefined : never) : never;
export declare function validateObject<D extends ValidateObjectDefinition>(definition: D, { noExcessKeys }?: {
noExcessKeys?: boolean;
export declare function validateObject<D extends ValidateObjectDefinition>(definition: D, { allowExcessProperties }?: {
allowExcessProperties?: boolean;
}): (value: unknown) => { [k in keyof D]: ValidateObjectReturnType<D[k]>; };
export {};
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const errors_1 = require("../errors");
function validateObject(definition, { noExcessKeys = true } = {}) {
function validateObject(definition, { allowExcessProperties = false } = {}) {
Object.entries(definition).forEach(([key, definitionValue]) => {

@@ -17,6 +17,6 @@ if (typeof definitionValue !== 'object')

throw new errors_1.FefeError(value, 'Not an object.');
if (noExcessKeys) {
const excessKeys = Object.keys(value).filter(key => !definition[key]);
if (excessKeys.length > 0)
throw new errors_1.FefeError(value, `Key(s) not allowed: ${excessKeys.join(', ')}`);
if (!allowExcessProperties) {
const excessProperties = Object.keys(value).filter(key => !definition[key]);
if (excessProperties.length > 0)
throw new errors_1.FefeError(value, `Properties not allowed: ${excessProperties.join(', ')}`);
}

@@ -23,0 +23,0 @@ const validated = {};

{
"name": "fefe",
"version": "1.0.0-beta.1",
"version": "1.0.0-beta.2",
"description": "Validate, sanitize and transform values with proper types.",

@@ -5,0 +5,0 @@ "main": "dist/index.js",

@@ -14,4 +14,12 @@ # fefe

## ๐Ÿ”Ž Validation example
## Installation
```bash
npm install fefe
```
## Usage
### ๐Ÿ”Ž Validation example
Validation only checks the provided value and returns it with proper types.

@@ -31,3 +39,3 @@

You can also use `fefe` to define your types easily:
โ˜๏ธ You can also use `fefe` to define your types easily:

@@ -38,10 +46,13 @@ ```typescript

## โš™๏ธ Sanitization example
### โš™๏ธ Basic transformation example (sanitization/parsing)
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`).
```typescript
import { sanitize, validate } from 'fefe'
import { transform, validate } from 'fefe'
import { pipe } from 'ramda'
const sanitizeMovie = validate.object({
title: validate.string(),
releasedAt: sanitize.date()
releasedAt: pipe(validate.string(), transform.parseDate())
})

@@ -52,3 +63,3 @@

const book: Book = sanitizeMovie({
const movie: Movie = sanitizeMovie({
title: 'Star Wars',

@@ -59,7 +70,7 @@ releasedAt: '1977-05-25T12:00:00.000Z'

Then `book` equals `{ title: 'Star Wars', releasedAt: Date(1977-05-25T12:00:00.000Z) }` (`releasedAt` now is a date).
Then `movie` equals `{ title: 'Star Wars', releasedAt: Date(1977-05-25T12:00:00.000Z) }` (`releasedAt` now is a date).
## ๐Ÿ› ๏ธ Transformation example
### ๐Ÿ› ๏ธ Complex transformation example
This is an 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 transform and validate a value (here we use `ramda`).
This is a more complex example that can be applied to parsing environment variables or query string parameters.

@@ -92,1 +103,78 @@ ```typescript

## Documentation
### `FefeError`
`fefe` throws a `FefeError` if a value can't be validated/transformed. A `FefeError` has the following properties:
* `reason`: the reason for the error.
* `value`: the value that was passed.
* `path`: the path in `value` to where the error occured.
### `validate.array(elementValidate, 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.
Options:
* `elementValidate`: 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()`
Returns a function `(value: unknown) => boolean` that checks that whether `value` is a boolean.
### `validate.date(options?)`
Returns a function `(value: unknown) => Date` that checks that whether `value` is a Date.
Options:
* `options.min?`, `options.max?`: restrict date
### `validate.number(options?)`
Returns a function `(value: unknown) => number` that checks that whether `value` is a number.
Options:
* `options.min?`, `options.max?`: restrict number
* `options.integer?`: require number to be an integer (default: `false`)
* `options.allowNaN?`, `options.allowInfinity?`: allow `NaN` or `infinity` (default: `false`)
### `validate.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.
Options:
* `definition`: an object where each value is either:
* a validator functions `(value: unknown) => T` or
* an object with the following properties:
* `validate`: validator function `(value: unknown) => T`
* `optional?`: allow undefined values (default: `false`)
* `default?`: default value of type `T` or function `() => T` that returns a default value
* `allowExcessProperties?`: allow excess properties (default: `false`)
### `validate.string(options?)`
Returns a function `(value: unknown) => string` that checks that whether `value` is a string.
Options:
* `options.minLength?`, `options.maxLength?`: restrict length of string
* `options.regex?`: require string to match regex
### `transform.parseBoolean()`
Returns a function `(value: string) => boolean` that parses a string as a boolean.
### `transform.parseDate(options?)`
Returns a function `(value: string) => Date` that parses a string as a date.
Options:
* `options.iso?`: require value to be an ISO 8601 string.
### `transform.parseJson()`
Returns a function `(value: string) => any` that parses JSON.
### `transform.parseNumber()`
Returns a function `(value: string) => number` that parses a number.

@@ -6,4 +6,4 @@ import { expect } from 'chai'

describe('validate integration tests', () => {
describe('Person', () => {
describe('Integration tests', () => {
describe('Basic validation', () => {
const validatePerson = validate.object({

@@ -32,3 +32,3 @@ name: validate.string(),

it('should validate a person', () => {
it('validates a person', () => {
const person = validatePerson(validPerson)

@@ -38,5 +38,6 @@ expect(person).to.eql(validPerson)

it('should throw with an invalid person', () => {
it('throws with an invalid person', () => {
const invalidPerson = { ...validPerson, address: { street: 'Ackerstr', zip: 'foo' } }
expect(() => validatePerson(invalidPerson)).to.throw(FefeError, 'address.zip: Not a number.')
expect(() => validatePerson(invalidPerson))
.to.throw(FefeError, 'address.zip: Not a number.')
.that.deep.include({ value: invalidPerson, path: ['address', 'zip'] })

@@ -46,6 +47,33 @@ .and.has.property('originalError').that.include({ value: 'foo' })

})
})
describe('Complex example', () => {
describe('Config', () => {
describe('Basic transformation (sanitization)', () => {
const sanitizeMovie = validate.object({
title: validate.string(),
releasedAt: pipe(validate.string(), transform.parseDate())
})
// { title: string, releasedAt: Date }
type Movie = ReturnType<typeof sanitizeMovie>
it('validates a movie and parses the date string', () => {
const movie = sanitizeMovie({
title: 'Star Wars',
releasedAt: '1977-05-25T12:00:00.000Z'
})
expect(movie).to.eql({
title: 'Star Wars',
releasedAt: new Date('1977-05-25T12:00:00.000Z')
})
})
it('throws with an invalid date', () => {
const invalidMovie = { title: 'Star Wars', releasedAt: 'foo' }
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' })
})
})
describe('Complex transformation and validation', () => {
const parseConfig = validate.object({

@@ -68,3 +96,3 @@ gcloudCredentials: pipe(validate.string(), transform.parseJson(), validate.object({ key: validate.string() })),

it('should parse a config', () => {
it('parses a config', () => {
const config = parseConfig(validConfigInput)

@@ -74,5 +102,5 @@ expect(config).to.eql(validConfig)

it('should throw with an invalid config', () => {
it('throws with an invalid config', () => {
const invalidConfigInput = { ...validConfigInput, gcloudCredentials: '{ "key": "secret", "foo": "bar" }' }
expect(() => parseConfig(invalidConfigInput)).to.throw(FefeError, 'gcloudCredentials: Key(s) not allowed: foo')
expect(() => parseConfig(invalidConfigInput)).to.throw(FefeError, 'gcloudCredentials: Properties not allowed: foo')
.that.deep.include({ value: invalidConfigInput, path: ['gcloudCredentials'] })

@@ -79,0 +107,0 @@ .and.has.property('originalError').that.include({ value: { key: 'secret', foo: 'bar' } })

@@ -9,3 +9,3 @@ import { expect } from 'chai'

it('should throw if not a array', () => {
const validate = validateArray({ elementValidate: validateString() })
const validate = validateArray(validateString())
expect(() => validate('foo'))

@@ -17,3 +17,3 @@ .to.throw(FefeError, 'Not an array.')

it('should throw if nested validation fails', () => {
const validate = validateArray({ elementValidate: validateString() })
const validate = validateArray(validateString())
const value = ['foo', 1]

@@ -26,3 +26,3 @@ expect(() => validate(value))

it('should return a valid array', () => {
const validate = validateArray({ elementValidate: validateString() })
const validate = validateArray(validateString())
const value = ['foo', 'bar']

@@ -33,7 +33,5 @@ expect(validate(value)).to.eql(value)

it('should return a valid array with transformed values', () => {
const validate = validateArray({
elementValidate: value => `transformed: ${validateString()(value)}`
})
const validate = validateArray(value => `transformed: ${validateString()(value)}`)
expect(validate(['foo', 'bar'])).to.eql(['transformed: foo', 'transformed: bar'])
})
})

@@ -5,3 +5,2 @@ import { FefeError } from '../errors'

export interface ValidateArrayOptions<R> {
elementValidate: Validate<R>
minLength?: number

@@ -13,6 +12,5 @@ maxLength?: number

export function validateArray<R> (_options: ValidateArrayOptions<R> | Validate<R>): (value: unknown) => R[] {
const { elementValidate, minLength, maxLength }: ValidateArrayOptions<R> = typeof _options === 'object'
? _options
: { elementValidate: _options }
export function validateArray<R> (
elementValidate: Validate<R>,
{ minLength, maxLength }: ValidateArrayOptions<R> = {}): (value: unknown) => R[] {

@@ -19,0 +17,0 @@ return (value: unknown) => {

@@ -20,3 +20,3 @@ import { FefeError } from '../errors'

definition: D,
{ noExcessKeys = true }: { noExcessKeys?: boolean } = {}
{ allowExcessProperties = false }: { allowExcessProperties?: boolean } = {}
) {

@@ -35,5 +35,5 @@ Object.entries(definition).forEach(([key, definitionValue]) => {

if (noExcessKeys) {
const excessKeys = Object.keys(value).filter(key => !definition[key])
if (excessKeys.length > 0) throw new FefeError(value, `Key(s) not allowed: ${excessKeys.join(', ')}`)
if (!allowExcessProperties) {
const excessProperties = Object.keys(value).filter(key => !definition[key])
if (excessProperties.length > 0) throw new FefeError(value, `Properties not allowed: ${excessProperties.join(', ')}`)
}

@@ -40,0 +40,0 @@

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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with โšก๏ธ by Socket Inc