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

not-me

v5.3.0

Published

Easy and type-safe validation

Downloads

47

Readme

Not-Me

Easy and type-safe validation

Features:

  • Planned out to be used and shared between backend and frontend
  • Powerful type inference: no need to write types manually. Even Discriminated Union types are guessed from your schemas.
    • Example of a Discriminated Union Type:
    type UnionType =
      | {
          discriminator: "has-a";
          a: string;
        }
      | {
          discriminator: "has-b";
          b: boolean;
        };
  • Simple DSL-like API
  • No need for try/catches: the final result will always be returned as the transformed input, or a tree of error messages. This way, there is no need to rearrange the flow to accomodate the try/catch, while reminding you to deal with validation errors
  • Easy to extend. You can create a new schema just by extending the classes from the ones provided here
  • Implementation that is easy to read and change. If you really need so, you can fork this library and change it without much hassle
  • API inspired by yup and joi

Quick links:

Motivation behind this package

This package was built in order to fix some shortcomings (specially with type safety) that many validation libraries have. Most validation libraries try to do a lot, and their code starts getting confusing and with a lot of back and forths. As consequence, unsolved Github issues start pilling up, improving or extending the libraries ourselves becomes hard since there are too many flows with some history behind them, and a lot of broad types (like any or object) start surfacing and undermining the type safety of a project.

Our take on validation with not-me is almost to provide you with enough boilerplate for you to build your own validations schemas. This way, it's easy to maintain and improve this package.

How to use it

Installing:

$ npm install not-me

Imports:

Most IDEs and Javascript text editors (like Visual Studio Code) import modules automatically just by starting to write the name of the value you want to use.

Keeping an app's code splitted into small lazy-loaded chunks is a priority in frontend development. Since legacy systems and some bundlers, like React Native's Metro, do not have tree-shaking, this package does not provide a single index.js import with all the code bundled in it. Instead, you are encouraged to import what you need from within the directories the package has. For example, the schemas are inside the lib/schemas directory, so if you want to import a schema for an object type, you need to import it like this import { object } from 'not-me/lib/schemas/object/object-schema

Building schemas:

This library offers the following basic types for you to build more complex validation schemas:

  • array(elementsSchema)
  • boolean()
  • date()
  • equals([...allowed values])
    • use as const for when you want the types to be the exact value literals. Example: equals([2, 'hello']) validated value will be typed as number | string but equals([2, 'hello'] as const) validated value will be typed as 2 | 'hello'
  • number()
  • object({ property: schemaForTheProperty })
  • objectOf(schemaForAllProperties) - same as object() but for objects whose keys can be any string
  • string()
  • or([...schemas]) - the value is filtered by multiple schemas till one matches. It's the equivalent to an union type

With these basic blocks, you can build more complex validations, by chaining...

  • test((v) => <condition> ? null : "Error message") - will validate if your value matches a condition. If it does, return null. If it doesn't match the condition, return a string with the error message you want to return.
  • transform((v) => <transform input value into any other value>) - will allow you to modify the input value
  • required() - sets the schema to reject undefined and null values
  • defined() - sets the schema to reject undefined values
  • notNull() - sets the schema to reject null values

Typescript will guide you in the recommended order by which you should chain validations.

If you follow what auto-complete presents to you, you will be fine.

Error messages:

Most of these schemas and their methods (except transform) have a last parameter that allows you to set a customized error message for when the value fails to meet the conditions.

You can also customize the default error messages by using the DefaultErrorMessagesManager in error-messages/default-messages/default-error-messages-manager.

Union typed schemas:

/*
  schema will output
  { common: string } & ({ a: "a"; c: number } | { a: "b"; d: boolean })

  `as const` statements are needed to infer the literal value (like 'a' | 'b')
  instead of a generic value like `string`
*/
const schema = object({
  common: equals(["common"]).required(),
  a: equals(["a", "b"] as const).required(),
})
  .union((v) => {
    if (v.a === "a") {
      return {
        a: equals(["a"] as const).required(),
        c: equals([0]).required(),
      };
    } else {
      return {
        a: equals(["b"] as const).required(),
        d: equals([false]).required(),
      };
    }
  })
  .required();

Type utilities (at not-me/lib/schemas/schema):

  • InferType<typeof schema>: get the output type of a schema
  • Schema<T>: dictates that a value is a schema that has an output type of T

Validation options

  • abortEarly: stop validation when the first invalid field is found.

Creating a custom schema:

import { number } from "not-me/lib/schemas/number/number-schema";

export function positiveInteger() {
  return number()
    .test((n) => {
      // Skip nullable values
      if (n == null) {
        return null;
      }

      if (Number.isInteger(n)) {
        return null;
      } else {
        return "Not an integer";
      }
    })
    .test((n) => {
      // Skip nullable values
      if (n == null) {
        return null;
      }

      if (n >= 0) {
        return null;
      } else {
        return "Not a positive number";
      }
    });
}

How it works under the hood:

When you set up a schema, you're just pilling up filter functions that will test and transform your initial value. These are the types of filters that are called during validation, by this order:

  • Type filter will validate if your input is in a specific type (example: a number, an object, an array, etc...)
  • Shape filters will validate the fields in your value. This only applies to object and array values
  • Test and Transform filters will run basic true or false checks on your value, or transform your value.

Library development

Changing the supported Node version

  • Files to be changed
    • .nvmrc
    • Dockerfile.dev
    • package.json
      • engine field
      • @types/node version
    • tsconfig.json
    • .github/workflows/main.yml and other CI config files
  • delete all node_modules directories and package-lock.json files
  • run npm run install