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

@cullylarson/validate

v1.0.15

Published

A compossible data validation library.

Downloads

46

Readme

@cullylarson/validate

A compossible validation library.

Motivation

  1. I like using error codes. It makes it easier to recognize a specific error if there are many, and to change the wording of an error message. All validators in @cullylarson/validate provide an error code and a default message. There are easy ways to provide custom messages.

  2. I wanted something that's declarative, but also easy to extend without editing the library itself. The validation definition in @cullylarson/validate is a function composed of validators and other functions. Modifying functionality is as simple as creating new functions and adding them to the definition.

  3. I wanted a validation result that could be used directly as the result of a REST API call. So validating the data provided to the call is as simple as running the validation function and returning the result if it isn't valid.

  4. I wanted to be able to provide general errors–errors that pertain to the data as a whole (e.g. "Data must be provided in JSON format.")–as well as errors specific to parameters (e.g. "You must provide an email address"). This allows the consumer of an API to display general errors as well as display parameter errors next to the parameter itself (e.g. next to its input in a form).

  5. I wanted a validator that can handle lists of data (e.g. an array of user IDs) as well as repeater fields, with sub-fields (e.g. a list of keywords with each keyword having a "label" and a "position" field–{keywords: [ {label: "something", position: 100}, {label: "festival", 3} ]}).

  6. I wanted to be able to handle cases where data is only validated if it passes certain conditions (e.g. only validate if data is not empty).

  7. I wanted to do all of this using functions that could be composed to easily extend the functionality of the library.

Defintions

Whenever any of these terms are used in the documentation, they wil be capitalized.

  • Param Validators. Validators run on a parameter.
  • General Validators. Validators run on all of the provided data.

TODO -- link all term uses to these definitions

Functional Notes

If a list of validators is provided for a parameter, those validators will be run until one fails, in which case the later validators will not be run. This is not true of the General Validators. All of them are run, even if some fail. At some point I'll add a function that allows all Param Validators to be run, even if some fail.

Validators

All validators must be curried.

A validator is a function that takes the following parameters and returns a promise:

  • value (mixed) The value to be validated.
  • params (object) All of the data being validated. This is useful if you need to validate by comparing the value to the value of another parameter (e.g. make sure one valid is less/greater than another value).
  • paramName (string) The name of the parameter being validated. I'm not sure why this would be useful, but it's provided.

A validator must return a Promise that resolves to an object with these parameters:

  • isValid (boolean) Whether the data is valid.
  • messages (array) An array of error messages (empty if isValid is true). Each message is itself an object with these parameters
    • code (string) An error code. The convension I've been using is all lower case, with dash spacing. Make it human readable and reasonably description, even if it's a bit long. Changing this code should be considered a breaking change. (example: not-url)
    • message (string) A default error message. Even though the message can be changed by consumers, try to make this something that people won't want/need to change. Something that could be displayed directly to users. (example: "Please provide a valid URL.")

If you want to accept configuration parameters to your validator, accept them before the standard parameters listed above. For example: export default curry((min, max, value, params, paramName) => {...})

TODO -- document the exceptions e.g. validateObjectList and validateList

Example

This example covers all use-cases for the library.

const validateUser = validate([
    validateNoDuplicates(pool, 'users', 'id', id, { email: 'email' }),
], {
    id: [validateNotEmpty, validateInteger],
    name: [validateNotEmpty],
    email: [
        customMessages({'is-empty': 'You must provide an email address.'}, validateNotEmpty),
        validateEmail,
    ],
    description: [],
    keywords: [validateObjectList({
        keyword: [validateNotEmpty],
        position: [validateNotEmpty, validateInteger],
    })],
    url: [
        onlyIf(notEmpty, validateUrl),
    ],
    startDate: [
        validateNotEmpty,
        validateDate(dateToComponents),
        validateDateIsBefore(dateToComponents, 'endDate'),
    ],
    endDate: [
        validateNotEmpty,
        validateDate(dateToComponents),
        validateDateIsAfter(dateToComponents, 'startDate'),
    ],
    friendIds: [validateListAsOne([
        customMessages({'not-found': 'One of the friends you have selected could not be found.'}, validateMustExist(pool, 'friends', 'id')),
        customMessages({'has-duplicates': 'One friend has been selected twice.'}, validateNoListDuplicates),
    ])],
    projectIds: [validateList([validateNotEmpty, validateMustExist(pool, 'projects', 'id')])],
})