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

@codeparticle/formal

v2.0.1

Published

A <2kb library for validating data of any kind

Downloads

332

Readme

@codeparticle/formal

A simple data and form validation library with a wide range of possibilities.

🔧 Installation

yarn add @codeparticle/formal

Usage

Formal makes it simple to validate arbitrary data with whatever rules you'd like. It also guarantees that when things fail, you know why, and have a plan B in place.

Here's a playful example:

import { Validator } from '@codeparticle/formal'
import { isString, minLength } from '@codeparticle/lib/rules'

const techPitchValidator = Validator.of(isString, minLength(32), maxLength(256))

const validPitch = "It's like your favorite social network, but for dogs"
const invalidPitch = "It's an AI to rule us all"

const validResult = techPitchValidator
  .validate(validPitch)
  .map((str) => str.toUpperCase())
// returns Success("IT'S LIKE YOUR FAVORITE SOCIAL NETWORK, BUT FOR DOGS") - maps can change successfully checked values

const invalidResult = techPitchValidator
  .validate(invalidPitch)
  .map((str) => str.toUpperCase())
// returns Fail('Must be at least 32 characters') - maps have no effect on failures

validResult.then({
  onSuccess: () => alert("We love it kid. Here's 5 million."),
  onFail: (errs) => console.log(errs), // can also simply be console.log
})

invalidResult.then({
  onSuccess: () => alert("We love it kid. Here's 5 million."),
  onFail: (errs) => errs.map(console.log), // 'Must be at least 32 characters'
})

validateObject

For validating forms or large API responses, formal exposes a validateObject utility. Here's an example of a form with a required name and email field.

import { rules, validateObject } from '@codeparticle/formal'

const { isNonEmptyString, isValidEmail } = rules

const validateForm = validateObject({
  name: [isNonEmptyString],
  email: [isNonEmptyString, isValidEmail],
})

const formValues = {
  name: 'hello',
  email: '',
}

validateForm(formValues)

calling this will return a schema like this:

 {
    hasErrors: true,
    errors: {
      email: ['Value must not be empty.', 'Must be a valid email']
    },
    values: {
      name: 'hello',
      email: ''
    }
 }

formal is flexible to your style, and exposes a pipeValidators function for writing validations in a more functional way. It condenses multiple checks into a function that encloses a value into a Success or Fail container.

Once run, these validation containers are supplied with an isSuccess property for use in filters,with the ability to reach for the internally held value. While not recommended for control flow, it's useful in cases where you're running validation over a long list of items, as well as in writing test cases.

import { pipeValidators, rules } from '@codeparticle/formal'
const { isString, minLength } = rules

// ...
const isLongString = pipeValidators([isString, minLength(50)])

values
  .filter((val) => isLongString(val).isSuccess)
  .map((container) => container.value)
  .map((str) => console.log(str))

// this technique can make testing a breeze.

// here, we want all of our objects to have a common property; maybe a required prop in a react component.

// while a bit contrived here, this method makes tests over complex, nested objects
// or other data simple to do.

const testObjects = [
  { required: 'present' },
  { required: 'present' },
  { required: 'wrong' },
]

const check = pipeValidators([isObject, hasProp('required')])

for (const test of testObjects) {
  expect(check(test).isSuccess).toBe(true) // passes

  expect(check(test).value).toBe('present') // fails
}

Built-in Validators

Formal has a small set of useful checks built in to validate simple data.

import {
  // Basic validations that take no arguments when used in Validator.of //
  isString,
  isNumber,
  isObject,
  isArray,
  isNonEmptyString,
  isNonEmptyObject,
  isNonEmptyArray,
  isValidEmail, // Only validates format, not ownership.

  /**
   * checks if something is equal to another value.
   * if you're using this in validateObject to check that two fields match,
   * use it as [values => isEqualTo(values['otherFormField'])]
   */
  isEqualTo,

  // Validations that take arguments before being supplied to Validator or pipeValidators() //


  // Check that a value matches a given regex. matchesRegex(/[A-Z]) || matchesRegex(RegExp('[A-Z]'))
  matchesRegex,
  // Check that a string is x-characters long. Takes a value for the minimum. `minLength(10)`
  minLength,
  // Check that a string is no more than x-characters long. Takes a value for the maximum. `maxLength(50)`
  maxLength,
  // Check that a number is less than a certain amount. Takes a value for the minimum. `lessThan(50)`
  lessThan,
  // Check that a number is less than a certain amount. Takes a value for the maximum. `greaterThan(50)`
  greaterThan,
  // check that an object has a certain property. Takes a drilldown path supplied as strings. `hasProp('fieldName', 'subfield')`
  hasProp,
  // check that an object has a property at the given drilldown path, then return a Success object with its value. `getProp('fieldName', 'subfield')`
  getProp
} from '@codeparticle/formal';
...

Customizing built-in or existing checks

Sometimes, the messages included with built-in or existing checks need to be modified after the fact. Formal supports this via the withMessage function.

withMessage creates a new copy of the rule, so don't worry about accidentally overwriting something important when using it. Like createRule, you can supply a string, or a function that returns a string.

import { withMessage, rules } from '@codeparticle/formal'

const withAdminFormErrorMessage = withMessage(
  `Admins must enter an administrator ID.`
)
const withUserFormErrorMessage = withMessage(
  `Users must enter their first and last name to sign up`
)

const withInternationalizedErrorMessage = withMessage(
  intl.formatMessage('form.error.message')
)

const withNewMessageFn = withMessage(
  (badValue) => `${badValue} is invalid for this field.`
)

const adminFormFieldCheck = withAdminFormErrorMessage(rules.isNonEmptyString)

const userFormFieldCheck = withUserFormErrorMessage(rules.isNonEmptyString)

const internationalizedFieldCheck = withInternationalizedErrorMessage(
  rules.isString
)

const customMessageFunctionFieldCheck = withNewMessageFn(rules.isNonEmptyString)

Creating your own validators

Formal gives you the ability to create your own rules to supply to Validator. There are two ways to do so.

Using createRule is a quick way to check things that don't change the value that comes out.

createRule takes two (required) options - a condition function, and a message. The message can be a string, or it can be a function that returns a string, in case you'd like to tailor your messages.

import { createRule } from '@codeparticle/formal'
export const containsMatchingString = (match) =>
  createRule({
    condition: (str) => str.includes(match),
    message: (failedStr) => `Value ${failedStr} does not include today's date`,
  })

createRule also allows more customized checks through an optional parameter called transform that allows for a transformation of the value before it's handed off to the next validation check.

import { createRule } from '../rule'
import { hasProp } from './has-prop'

// below is the actual source code of the built-in getProp function.
export const getProp = (property) =>
  createRule({
    condition: (obj) => hasProp(property).check(obj).isSuccess,
    message: `Property '${property}' does not exist`,
    // transform the object to the value of the successfully found property
    // before handing off to the next check / function.
    transform: (obj) => obj[property],
  })

Usage with Typescript

This package exports TS type definitions in the /types folder.

import {
  // interfaces for the Success and Fail classes
  Success,
  Fail,
  // used to specify the argument structure to `createValidator`
  CustomValidationOptions,
  // options supplied to Validator.then
  ValidationActions,
  // alias for `Success | Fail` in cases where both are needed.
  ValidationM,
  // aliases for a single or array of validation rules provided to Validator.of
  ValidationRule,
  // alias for a function that returns a Success or Failure.
  ValidationCheck,
  ValidationRuleSet,
  // interface for the Validator class
  Validator,
} from '@codeparticle/formal/types'

Build Status NPM version Downloads Standard Version styled with prettier Conventional Commits


🥂 License

MIT as always