npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

cafy

v15.2.1

Published

Simple, lightweight, flexible validator generator

Downloads

945

Readme

☕ 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