All Projects → syuilo → Cafy

syuilo / Cafy

Licence: mit
☕️ Simple, lightweight, flexible validator generator

Programming Languages

javascript
184084 projects - #8 most used programming language
typescript
32286 projects

Projects that are alternatives of or similar to Cafy

Structure
A simple schema/attributes library built on top of modern JavaScript
Stars: ✭ 292 (+1227.27%)
Mutual labels:  schema, validation
Schema
📐 Validating data structures against a given Schema.
Stars: ✭ 359 (+1531.82%)
Mutual labels:  schema, validation
Jsonschema
An implementation of the JSON Schema specification for Python
Stars: ✭ 3,474 (+15690.91%)
Mutual labels:  schema, validation
Truss
Assertions API for Clojure/Script
Stars: ✭ 239 (+986.36%)
Mutual labels:  schema, validation
Pandera
A light-weight, flexible, and expressive pandas data validation library
Stars: ✭ 506 (+2200%)
Mutual labels:  schema, validation
openapi-schema-validator
OpenAPI schema validator for Python
Stars: ✭ 35 (+59.09%)
Mutual labels:  schema, validation
Skema
🛰 Skema provides a handy & composable way to validate / transform / purify the input data.
Stars: ✭ 359 (+1531.82%)
Mutual labels:  schema, validation
Joiful
TypeScript Declarative Validation for Joi
Stars: ✭ 177 (+704.55%)
Mutual labels:  schema, validation
Strictyaml
Type-safe YAML parser and validator.
Stars: ✭ 836 (+3700%)
Mutual labels:  schema, validation
Specs
Technical specifications and guidelines for implementing Frictionless Data.
Stars: ✭ 403 (+1731.82%)
Mutual labels:  schema, validation
Password Validator
Validates password according to flexible and intuitive specification
Stars: ✭ 224 (+918.18%)
Mutual labels:  schema, validation
Meteor Astronomy
Model layer for Meteor
Stars: ✭ 608 (+2663.64%)
Mutual labels:  schema, validation
Schematics
Project documentation: https://schematics.readthedocs.io/en/latest/
Stars: ✭ 2,461 (+11086.36%)
Mutual labels:  schema, validation
fefe
Validate, sanitize and transform values with proper TypeScript types and zero dependencies.
Stars: ✭ 34 (+54.55%)
Mutual labels:  schema, validation
Computed Types
🦩 Joi like validations for TypeScript
Stars: ✭ 197 (+795.45%)
Mutual labels:  schema, validation
Joi
The most powerful data validation library for JS
Stars: ✭ 17,989 (+81668.18%)
Mutual labels:  schema, validation
Nope Validator
A small, simple and fast JS validator. Like, wow thats fast. 🚀
Stars: ✭ 142 (+545.45%)
Mutual labels:  schema, validation
Schema Utils
Options Validation
Stars: ✭ 162 (+636.36%)
Mutual labels:  schema, validation
Pyschemes
PySchemes is a library for validating data structures in python
Stars: ✭ 365 (+1559.09%)
Mutual labels:  schema, validation
Superstruct
A simple and composable way to validate data in JavaScript (and TypeScript).
Stars: ✭ 5,604 (+25372.73%)
Mutual labels:  schema, validation

☕ cafy

Simple, lightweight, flexible validator generator

cafyは、アサーションのようにメソッドチェーンで値のバリデーションを行うライブラリです。 cafyを使えばバリデーションを簡単かつ柔軟に書くことができます。すべてTypeScriptで書かれていて、型定義との相性も抜群です。 Try it out!

NPM

🤔 Why cafy

たとえばサーバー側で、クライアントから送信されてきたパラメータが正しい形式であるかどうか確認しないと、データベースのエラーやプログラムの例外を引き起こしたりする可能性があります。 「このパラメータはnullやundefinedではない文字列でなくてはならず、1文字以上100文字以下でなくてはならず、a-z0-9の文字種で構成されてなければならない」といった長いバリデーションを、cafyを使えば一行で簡潔に書くことができます。 例外も行うバリデーションごとに用意されているので、ユーザーにわかりやすいエラーメッセージを返すこともできます。 また、バリデータの型文字列を取得する機能があるので、それを使えばドキュメントを生成するときにも役立ちます。 TypeScriptのstrictNullChecksオプションもサポートしています。

✨ 特徴

  • 軽量 ... 依存関係無し。ブラウザでも使えます
  • 簡単 ... 複雑にネストされたオブジェクトも直感的にバリデーションできる
  • 柔軟 ... メソッドチェーンで制約を追加したり、独自の型を追加できる
  • 強力な型サポート ... 型注釈不要で、バリデータに即した型を取得できる
    • strictNullChecksサポート
    • Type Guardサポート
    • Assertion Functionsサポート

📦 Installation

Just:

npm install cafy

Happy validation👍

☘ Usage

TL;DR

import $ from 'cafy';

const isFruits = $.str.or(['apple', 'banana', 'orange']).ok;

isFruits('apple')  // true
isFruits('banana') // true
isFruits('alice')  // false
isFruits(42)       // false
isFruits(null)     // false

まずその値がどんな型でなければならないかを示し、 そのあとに追加の制約をメソッドチェーンで追加していきます。

(以下のドキュメントでは、import $ from 'cafy';している前提で書いていきます(実際にはcafy関数にどんな名前を付けるかは自由です)。)

たとえば 「それは文字列でなければならない」 という制約を表すにはこう書きます:

$.str

rangeメソッドを利用して、さらに 「10文字以上20文字以下でなければならない」 という制約を追加してみます:

$.str.range(10, 20)

実際にバリデーションしてみましょう。 okメソッドに検証する値を渡すと、それが条件を満たせばtrueが返り、そうでなければfalseが返ります:

$.str.range(10, 20).ok('strawberry pasta') // true

$.str.range(10, 20).ok('alice') // false (短すぎるので)

$.str.range(10, 20).ok('i love strawberry pasta') // false (長すぎるので)

もちろん、上記の例はこのようにまとめられます:

const validate = $.str.range(10, 20).ok;

validate('strawberry pasta') // true
validate('alice') // false (短すぎるので)
validate('i love strawberry pasta') // false (長すぎるので)

cafyは様々な型をサポートしています:

  • 文字列 ... $.str
  • 数値 ... $.num
  • 真理値 ... $.bool
  • 配列 ... $.arr()
  • オブジェクト ... $.obj
  • ユーザー定義型 ... $.type()
  • ユニオン ... $.either()
  • リテラル ... $.literal()
  • なんでも ... $.any

ℹ JavaScriptの仕様上では配列はobjectですが、cafyでは配列はobjectとは見なされません。

後述するように、ユーザー定義型を使えば独自の型を追加することもできます。

それぞれの型がどのようなメソッドを持っているかなどは、APIのセクションをご確認ください。

null と undefined の扱い

cafyは、デフォルトでnullundefinedも許容しません。 nullundefinedを許容したい場合は、これらのオプションを使用します:

undefined を許容する (optional)

デフォルトでundefinedはエラーになります:

$.str.ok(undefined) // false

undefinedを許容する場合はoptionalを型の前にプリフィクスします:

$.optional.str.ok(undefined) // true

null を許容する (nullable)

デフォルトでnullはエラーになります:

$.str.ok(null) // false

nullを許容する場合はnullableを型の前にプリフィクスします:

$.nullable.str.ok(null) // true

null と undefined を許容する

nullableoptionalは併用できます:

$.nullable.optional.str...
$.optional.nullable.str...
$.optionalNullable.str...
undefined null
(default) x x
optional o x
nullable x o
optional + nullable o o

📖 API

Context

cafyの実体はContextクラスです。そして、cafyで実装されている全ての型はContextクラスを継承したクラスです。 従って、Contextクラスにある次のメソッドおよびプロパティは全ての型で利用可能です。

メソッド

.get(value) => [any, Error]

テスト対象の値とテスト結果のペア(配列)を取得します。

.nok(value) => boolean

バリデーションを実行します。 合格した場合はfalseで、そうでない場合はtrueです。 .ok()の否定です。 (noknot ok の略です)

.ok(value) => boolean

バリデーションを実行します。 合格した場合はtrueで、そうでない場合はfalseです。 .test() == nullと同義です。

ℹ TypeScriptを使っているなら、このメソッドの結果で分岐を行うことで、以後対象の変数の型を推論することができます(Type Guard)。これについては、後述の「TypeScriptとの親和性」で詳しく説明します。

.pipe(fn) => Context

カスタムのバリデーションを実行できます。 引数の関数がtrueを返すと妥当ということになり、falseまたはErrorを返すと不正な値とします。

$.str.pipe(x => x.indexOf('alice') == -1).ok('strawberry pasta') // true
$.arr().pipe(x => x[1] != 'b').ok(['a', 'b', 'c']) // false

値がnullまたはundefinedのときはpipeは実行されないため、pipe内でnullチェックする必要はありません。

.assert(value) => void

バリデーションを実行します。 不合格の場合はErrorをthrowします。

TypeScriptを使っているなら、このメソッドを呼び出すことで、以後対象の変数の型を推論することができます。これについては、後述の「TypeScriptとの親和性」で詳しく説明します。 現在正しく動作しません。詳しくは: https://github.com/microsoft/TypeScript/issues/34596

.test(value) => Error

バリデーションを実行します。 合格した場合はnullで、そうでない場合はErrorです。

.throw(value) => any

バリデーションを実行します。 合格した場合は値を返し、そうでない場合はErrorをthrowします。

.getType() => string

このインスタンスの型を表す文字列を取得します。

$.str string
$.optional.str string?
$.nullable.str (string | null)
$.optional.nullable.str (string | null)?
$.arr($.str) string[]
$.either($.str, $.num) (string | number)

プロパティ

.isOptional: Boolean

optionalか否か(読み取り専用)

.isNullable: Boolean

nullableか否か(読み取り専用)


バリデータ: Any

.any

Anyバリデータを使うと、「undefinednullはダメだけど、型は何でもいい」といった値を検証したいときに便利です:

$.any.ok('strawberry pasta') // true

メソッド

Any固有のメソッドはありません。


バリデータ: Array

.arr(query)
.array(query)

配列をバリデーションしたいときはこのバリデータを使用します。

配列の要素をバリデーションする

配列の各々の要素に対してバリデーションを定義できます:

$.arr($.num)         // 数値の配列でなければならない
$.arr($.str.min(10)) // 10文字以上の文字列の配列でなければならない

もちろんarrayを入れ子にもできます:

$.arr($.arr($.num))         // 「数値の配列」の配列でなければならない
$.arr($.arr($.str.min(10))) // 「10文字以上の文字列の配列」の配列でなければならない

メソッド

.min(threshold)

要素の数がthreshold以上でなければならないという制約を追加します。

.max(threshold)

要素の数がthreshold以下でなければならないという制約を追加します。

.range(min, max)

min以上max以下の数の要素を持っていなければならないという制約を追加します。

$.arr().range(2, 5).ok(['a', 'b', 'c'])                // true
$.arr().range(2, 5).ok(['a', 'b', 'c', 'd', 'e', 'f']) // false
$.arr().range(2, 5).ok(['a'])                          // false

ℹ️ range(30, 50)min(30).max(50)と同義です。

.length(length)

要素の数がlengthでなければならないという制約を追加します。

.unique()

ユニークな配列(=重複した値を持っていない)でなければならないという制約を追加します。

$.arr().unique().ok(['a', 'b', 'c'])      // true
$.arr().unique().ok(['a', 'b', 'c', 'b']) // false
.item(index, fn)

特定のインデックスの要素に対してカスタムのバリデーションを実行できます。 引数の関数がtrueを返すと妥当ということになり、falseまたはErrorを返すと不正な値とします。 引数にはcafyインスタンスも渡せます。

$.arr().item(1, $.num).ok(['a', 42, 'c'])  // true
$.arr().item(1, $.num).ok(['a', 'b', 'c']) // false
.each(fn)

各要素に対してカスタムのバリデーションを実行できます。 引数の関数がtrueを返すと妥当ということになり、falseまたはErrorを返すと不正な値とします。 引数にはcafyインスタンスも渡せます。

$.arr().each(x => x < 4).ok([1, 2, 3]) // true
$.arr().each(x => x < 4).ok([1, 4, 3]) // false

バリデータ: Boolean

.bool
.boolean

真理値(truefalse)をバリデーションしたいときはこのバリデータを使用します。

メソッド

固有のメソッドはありません。


バリデータ: Number

.num
.number

数値をバリデーションしたいときはこのバリデータを使用します。

メソッド

.int()

整数でなければならないという制約を追加します。

$.num.int().ok(0)        // true
$.num.int().ok(1)        // true
$.num.int().ok(-100)     // true

$.num.int().ok(0.1)      // false
$.num.int().ok(Math.PI)  // false

$.num.int().ok(NaN)      // false
$.num.int().ok(Infinity) // false
.min(threshold)

threshold以上の数値でなければならないという制約を追加します。

.max(threshold)

threshold以下の数値でなければならないという制約を追加します。

.range(min, max)

min以上max以下の数値でなければならないという制約を追加します。

ℹ️ range(30, 50)min(30).max(50)と同義です。


バリデータ: Object

.obj(props)
.object(props)

オブジェクトをバリデーションしたいときはこのバリデータを使用します。

プロパティを定義する

引数にプロパティの定義を与えて、複雑なオブジェクトも簡単にバリデーションできます。

例えば次のようなオブジェクトをバリデーションしたいとします:

const x = {
  some: {
    strawberry: 'pasta',
    alice: false,
    tachibana: {
      bwh: [68, 52, 67]
    }
  },
  thing: 42
};

バリデータはこのように定義できます:

$.obj({
  some: $.obj({
    strawberry: $.str,
    alice: $.bool,
    tachibana: $.obj({
      bwh: $.arr($.num)
    })
  }),
  thing: $.num
}).ok(x) // true

エラー

この型では、エラーに次のプロパティが含まれています:

  • prop ... バリデーションに不合格になったプロパティ名
  • path ... 不合格になった子のプロパティまでのパス
  • error ... エラー内容

例えば次のような検証を行った時、エラーは次のようになります:

$.obj({
  x: $.obj({
    y: $.obj({
      z: $.num
    })
  })
}).test({
  x: {
    y: {
      z: 'foo'
    }
  }
});
Thrown:
{ Error: x.y.z: must-be-a-number
    at ...
  path: [ 'x', 'y', 'z' ],
  error:
   Error: must-be-a-number
       at ... }

メソッド

.strict()

引数のプロパティ定義で言及した以外のプロパティを持っている場合にエラーにします。

デフォルト:

$.obj({ foo: $.num }).ok({ foo: 42, bar: 24 }) // true

strict:

$.obj({ foo: $.num }).strict().ok({ foo: 42, bar: 24 }) // false

バリデータ: String

.str
.string

文字列をバリデーションしたいときはこのバリデータを使用します。

メソッド

.match(pattern)

与えられた正規表現とマッチしていなければならないという制約を追加します。

$.str.match(/^([0-9]{4})\-([0-9]{2})-([0-9]{2})$/).ok('2017-03-07') // true
.notMatch(pattern)

matchの否定。

.or(pattern)

与えられたパターン内の文字列のいずれかでなければならないという制約を追加します。 patternは文字列の配列または|で区切られた文字列です。

$.str.or(['strawberry', 'pasta']).ok('strawberry') // true
$.str.or(['strawberry', 'pasta']).ok('alice')      // false
$.str.or('strawberry|pasta').ok('pasta')           // true
.notInclude(str | str[])

引数に与えられた文字列を含んでいてはならないという制約を追加します。

$.str.notInclude('fuck').ok('She is fucking rich.') // false
$.str.notInclude(['strawberry', 'alice']).ok('strawberry pasta') // false
.min(threshold)

threshold以上の文字数でなければならないという制約を追加します。

.max(threshold)

threshold以下の文字数でなければならないという制約を追加します。

.range(min, max)

min以上max以下の文字数でなければならないという制約を追加します。

ℹ️ range(30, 50)min(30).max(50)と同義です。

.length(length)

文字数がlengthでなければならないという制約を追加します。


Either

.either(queryA, queryB)

「文字列または数値」とか「真理値または真理値の配列」のようなバリデーションを行いたいときは、eitherバリデータを使うことができます。 例:

// 文字列または数値
$.either($.str, $.num).ok(42) // true

3種類以上の型

eitherを任意の数入れ子にする事で実現できます:

// 文字列または数値または真理値
$.either($.str, $.either($.num, $.bool)).ok(42) // true

Literal

.literal(literal)

特定の値であることを保証するバリデーションを行いたいときは、literalバリデータを使うことができます。 例:

// 文字列'foo'でなければならない
$.literal('foo').ok('foo') // true

TypeScriptで使うときに便利です。


Use

.use(query)

既存のContextを拡張したいときに使います。

const other = $.str;
$.optional.use(other).ok(undefined) // true
$.nullable.use(other).ok(null) // true

Type (ユーザー定義型)

.type(type)

cafyで標準で用意されているstringnumber等の基本的な型以外にも、ユーザーが型を登録してバリデーションすることができます。 型を定義するには、まずcafyのContextクラスを継承したContextクラスを作ります。 TypeScriptでの例:

import $, { Context } from 'cafy';

// あなたのクラス
class Foo {
  bar: number;
}

// あなたのクラスを検証するための、cafyのContextクラスを継承したクラス
class FooContext<Maybe = Foo> extends Context<Foo | Maybe> {
  // 型の名前
  public readonly name = 'Foo';

  constructor(optional = false, nullable = false) {
    // ✨おまじない✨
    super(optional, nullable);

    // 値が Foo のインスタンスであるかチェック
    this.push(v => v instanceof Foo);
  }

  //#region ✨もっとおまじない✨
  public makeOptional(): FooContext<undefined> {
    return new FooContext(true, false);
  }

  public makeNullable(): FooContext<null> {
    return new FooContext(false, true);
  }

  public makeOptionalNullable(): FooContext<undefined | null> {
    return new FooContext(true, true);
  }
  //#endregion
}

バリデーションするときは、typeメソッドにクラスを渡します:

$.type(FooContext).ok(new Foo()); // true
$.type(FooContext).ok('abc');     // false

カスタムメソッド

また、Contextを継承するクラスにメソッドを実装することで、Context中でそのメソッドを利用することもできます。 例として、上述のFooContextに、「プロパティbarが指定された値以上でなければならない」という制約を追加するメソッドminを定義してみましょう:

class FooContext<Maybe = Foo> extends Context<Foo | Maybe> {
  ...

  public min(threshold: number) {
    this.push(v => v.bar >= threshold);
    return this;
  }
}

return this;しているのは、メソッドチェーンできるようにするためです。

このメソッドを使う例:

const foo = new Foo();
foo.bar = 42;

$.type(FooContext).min(40).ok(foo); // true
$.type(FooContext).min(48).ok(foo); // false

TypeScriptとの親和性

cafyはTypeScriptで書かれているため、強力な型定義を持ち、バリデーションに応じて変数の型を推論し、それ以降のフローで型を絞り込むことができます。

Type Guard

例えば、「x文字列でなければならない」とバリデーションした後のxの型は明らかに文字列です(バリデータの実装にミスが無いと仮定した場合)。 okメソッドは型定義においてTypeScriptのType Guardを実装しており、okメソッドの返り値を使って条件分岐を行うと、そのスコープではバリデーションした変数の型が正しいものに絞り込まれます(ナローイング)。これは、okメソッドにバリデーションに合格しない値を渡すとfalseが返り分岐が実行されないことが判るので、分岐先スコープの変数の型は必ず求めている型になることが保証されるからです。 例:

const x = 42 as unknown;

// この時点でxの型は unknown

if ($.str.ok(x)) {
	x;
	// ↑この時点でxの型は string
	// この例ではxはnumberなので、実際にはここに到達することはない
}

次のように書いても同じです:

function something(x: unknown) {
	// この時点でxの型は unknown

	if (!$.str.ok(x)) return;

	x;
	// ↑この時点でxの型は string
}

詳しくはTypeScriptのType Guardのドキュメントを参照してください。

Assertion Functions

また、TypeScript 3.7で導入されたAssertion Functionsもサポートしていて、 assertメソッドにある変数を渡して呼び出すと、その後の変数の型はバリデーションされた型になります。これは、assertメソッドにバリデーションに合格しない値を渡すと、即座に例外がthrowされるので、 その後の変数の型は必ず求めている型になることが保証されるからです。TypeScript 3.7のAssertion Functionsによりこれを推論することが可能になります。例:

const x = 42 as unknown;

// この時点でxの型は unknown

$.str.assert(x);

x;
// ↑この時点でxの型は string
// この例ではxはnumberなので、実際にはここに到達することはない

詳しくはTypeScriptのAssertion Functionsのドキュメントを参照してください。

Literal Types

cafyの$.literal()バリデータは、TypeScriptのconst assertionを使ったときのように型が値そのものになります。例:

if ($.literal('foo').ok(x)) {
	x;
	// ↑xの型は 'foo' (stringではなく)
}

if ($.either($.literal('foo'), $.literal('bar')).ok(x)) {
	x;
	// ↑xの型は 'foo' | 'bar'
}

Array, Object, Unionな型

配列、オブジェクト、ユニオン型といった複雑な型も、正しく推論することができます。 いくつかバリデーション後の型がどうなるのかの例を示します:

const b = $.arr($.num).get(foo)[0];
// ↑ b の型は number[]

const c = $.either($.str, $.num).get(foo)[0];
// ↑ c の型は string | number

const d = $.obj({
  foo: $.obj({
    bar: $.obj({
      baz: $.num
    }),
    qux: $.arr($.arr($.bool))
  })
}).get(foo)[0];
/* ↑ d の型は:
{
  foo: {
    bar: {
      baz: number;
    };
    qux: boolean[][];
  };
}
*/

strictNullChecks

cafyはTypeScriptのstrictNullChecksをサポートしていて、型定義においてnullundefined、またはそうでないかを区別できます。例:

const a =                   $.str.get(foo)[0]; // a の型は string
const b =          $.optional.str.get(foo)[0]; // b の型は string | undefined
const c =          $.nullable.str.get(foo)[0]; // c の型は string | null
const d = $.optional.nullable.str.get(foo)[0]; // d の型は string | undefined | null

Release Notes

Please see ChangeLog!

License

MIT

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].