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 🙏

© 2025 – Pkg Stats / Ryan Hefner

pbfish

v0.2.2

Published

Error-tolerant schema validation

Readme

pbfish

npm GitHub Workflow Status Codecov

pbfish is yet another schema validation library whose API resembles the famous joi, only with fewer methods. Different from joi which focuses on the ability to describe and validate schemas with powerful abstractions, pbfish mainly aims to parse the provided data into the desired shape with good default behavior for later processing. The idea is to make interacting with unknown data as comfortable as interacting with well-defined RPC request/response and be error-tolearnt.

Generally, pbfish aims to achieve the following goals:

  • Simple API, easy to use
  • Intuitive and well-defined behavior with erroneous input
  • Accurate types support for TypeScript

NOTE: Arguably what pbfish does is schema parsing rather than schema validation. However, since the existing libraries in "schema validation" category also does some parsing work and don't make clear distinctions here, we decide to remain indifferent to the ambiguity.

Why not joi, yup, or other data validation libraries?

joi and various other schema validation libraries have done a great job in validating schemas, but are hard to use otherwise when we need to work with data provided by external service that may contain errors and do not strictly follow API requirements. This is where pbfish would come to rescue.

pbfish provides compatibility for data that may contain errors and can act as the "front guard" before any actual work is done on the unknown data. Common errors like missing fields and wrong types will be resolved automatically so that the application side can focus on actual business logic.

Also, pbfish provides accurate types support and null safety for typescript projects. Who doesn't love safe types?

Installation

$ npm i pbfish

Example

The API is almost identical with yup, here is a minimal example:

import * as pf from "pbfish";

const s = {
  a: 1,
  b: "hello",
  c: "true",
  d: {},
  e: [1, 2],
}

const schema = pf.object({
  a: pf.number(),
  b: pf.string(),
  c: pf.boolean(),
  d: pf.object({
    k: pb.number(),
  }),
  e: pf.string(),
  f: pf.object({
    l: pb.string(),
  })
});

const parsed: pf.Target<typeof schema> = schema.parse(o);
// The value of `parsed`:
// {
//   a: 1,        matching type will preserve original value
//   b: "hello",
//   c: true,     conflict type will be transformed to the desired type intuitively
//   d: {
//        k: 0,   missing fields of object will have default value
//      },
//   e: "",       types that can't be intuitively parsed will have default value
//   f: null,     default value of object is null
// }

For more examples on usage, see the tests.

Specification

We first describe the schema object using literal types number(), string() and boolean(), in combination with compositional types object() and array() like the example above. Then we call parse(input) on the object where input is the value to be parsed. The parsed value will strictly follow the specification according to the defined schema.

parse() does not throw errors, nor does it provide any mechanism for investigating "errors" encountered in parsing. What parse() does is to transform the input into the desired shape with best efforts. When in doubt about the parsed result, the recommended approach is to investigate the original input value.

TL;DR

Generally, the specification follows these guidelines:

  • Intuitive and tolerant of input. For number type, both "1" and true will be parsed into 1 in spite of the type difference. For boolean type, both 1234 and "true" will be parsed into true while null and "" will be parsed into false. The result should be intuitive to an experienced programmer, and be tolerant in the face of erroneous input.
  • Default value. When input is missing (being null, undefined) or can't be meaningfully parsed (1 being input to object()), the result will be a default zero value. object() is a bit special here because if it's not null, then all of its fields will be present and have default value even if it's not present in the input, just like in protobuf3. Below is the zero value map.

|Type| Zero Value| |----| ----------| |number| 0| |string| ""| |boolean| false| |array| [] | |object| null|

We hope that the above guidelines would suffice for one to use pbfish productively. However, if under any condition the parsed result surprises you, it would help to look at the detailed specification.

number

| Input Type | Input Value Type | Parsed Value| | ---------- | ----------- | ------- | | number | | the original value | | boolean | true | 1 | | boolean | false | 0 | | string | | See below table for detail | | Other | | 0 |

For string type, the parsing rule is complex, and is the same as the Number() constructor rule:

|x | Number(x) | |----|-----------| |"123"| 123| |"+123"| 123| |"-123"| -123| |"123.45"| 123.45| |"-123.45"| -123.45| |"12e5"| 1200000 | |"12e-5"| 0.00012| |"0123"| 123| |"0000123"| 123| |"0b111"| 7| |"0o10"| 8| |"0xBABE"| 47806| |"4294967295"| 4294967295| |"123456789012345678"|123456789012345680| |"12e999"| Infinity| |""| 0| |"123foo"| 0| |"123.45foo"| 0| |" 123 "| 123| |"foo"| 0| |"12e"| 0| |"0b567"| 0| |"0o999"| 0| |"0xFUZZ"| 0| |"+0"| 0| |"-0"| 0| |"Infinity"| Infinity| |"+Infinity"| Infinity| |"-Infinity"| -Infinity|

string

| Input Type | Input Value Type | Parsed Value| | ---------- | ----------- | ------- | | number | | the string representation of its plain form | | boolean | true | "true" | | boolean | false | "false" | | string | | the original value | | Other | | "" |

boolean

| Input Type | Input Value Type | Parsed Value| | ---------- | ----------- | ------- | | number | 0 | false | | number | any value except 0 | true | | boolean | | the original value | | string | "" | false | | string | case-insensitive false eg. false, False, FALSE | false | | string | any value except empty string and "false" | true | | null | | false | | undefined | | false | | array | [] | false | | array | any value except empty array | true | | Other | | true |

array

| Input Type | Input Value Type | Parsed Value| | ---------- | ----------- | ------- | | array | | array of mapped parsed values | | Other | | [] |

object

| Input Type | Input Value Type | Parsed Value| | ---------- | ----------- | ------- | | object | | object of mapped parsed values, with all the fields present and have default zero value | | Other | | null |

enumeration

Currently, enumeration support number and string types. Input will first be parsed according to member type and then compared to the values in provided value set.

| Input Type | Input Value Type | Parsed Value| | ---------- | ----------- | ------- | | any | The parsed value is in enumeration value set | original value | | any | The parsed value is not in enumeration value set | default value for member type. (eg. 0, "") |