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

@alxandr/validated

v2.0.0

Published

JSON configuration utilities

Downloads

3

Readme

validated

Join the chat at https://gitter.im/andreypopp/sitegen Travis build status Type System

Validate your configurations with precise error messages:

  • Define schema with validators which are agnostic to the actual representation of data, be it a JSON string, object in memory or any other format.

  • Use schema with runners specific for formats (object and JSON5 runners are included). Get error messages with precise info (line and column numbers for example).

  • Get the result of a validation as an object: either a plain JSON or some domain specific classes if schema is defined in that way.

Table of Contents

Installation

% npm install validated

Usage

Schema

Schema is defined with validators which are agnostic to the actual representation of data, be it a JSON string or an object in memory:

import {
  mapping, arrayOf, object, partialObject, oneOf, maybe, enumeration, recur,
  any, string, number, boolean
} from 'validated/schema'

There's schema validator for JSON objects in memory:

import {
  validate as validateObject
} from 'validated/object'

And schema validator for strings with JSON/JSON5 encoded data:

import {
  validate as validateJSON5
} from 'validated/json5'

Let's define some schema first:

let person = object({
  name: string,
  age: number,
})

let pet = object({
  nickName: string,
  age: number,
})

let collection = arrayOf(oneOf(person, pet))

validateJSON5(collection, '[{name: "John", age: 26}, {nickName: "Tima", age: 3}]')
// => [ { name: 'John', age: 26 }, { nickName: 'Tima', age: 3 } ]

validateObject(collection, [{name: "John", age: 26}, {nickName: "Tima", age: 3}])
// => [ { name: 'John', age: 26 }, { nickName: 'Tima', age: 3 } ]

List of schema primitives

any

Validates any value but not undefined or null:

validateObject(any, 'ok')
// => 'ok'

validateObject(any, 42)
// => 42

validateObject(any, null)
// ValidationError: Expected a value but got null

validateObject(any, undefined)
// ValidationError: Expected a value but got undefined

If you want to validated any value and even an absence of one then wrap it in maybe:

validateObject(maybe(any), null)
// => null

validateObject(maybe(any), undefined)
// => undefined
string, number, boolean

Validate strings, numbers and booleans correspondingly.

validateObject(string, 'ok')
// => 'ok'

validateObject(number, 42)
// => 42

validateObject(boolean, true)
// => true
enumeration

Validate enumerations:

validateObject(enumeration('yes', 'no'), 'yes')
// => 'yes'

validateObject(enumeration('yes', 'no'), 'no')
// => 'no'

validateObject(enumeration('yes', 'no'), 'oops')
// ValidationError: Expected value to be one of "yes", "no" but got "oops"
mapping

Validate mappings from string keys to values.

Untyped values (value validator defaults to any):

validateObject(mapping(), {})
// => {}

validateObject(mapping(), {a: 1, b: 'ok'})
// => { a: 1, b: 'ok' }

validateObject(mapping(), 'oops')
// ValidationError: Expected a mapping but got string

Typed value:

validateObject(mapping(number), {a: 1})
// => { a: 1 }

validateObject(mapping(number), {a: 1, b: 'ok'})
// ValidationError: Expected value of type number but got string
// While validating value at key "b"
arrayOf

Validate arrays.

Untyped values (value validator defaults to any):

validateObject(arrayOf(any), [])
// => []

validateObject(arrayOf(any), [1, 2, 'ok'])
// => [ 1, 2, 'ok' ]

validateObject(arrayOf(any), 'oops')
// ValidationError: Expected an array but got string

Typed value:

validateObject(arrayOf(number), [1, 2])
// => [ 1, 2 ]

validateObject(arrayOf(number), [1, 2, 'ok'])
// ValidationError: Expected value of type number but got string
// While validating value at index 2
object

Validate objects, objects must specify validator for each of its keys:

let person = object({
  name: string,
  age: number,
})

validateObject(person, {name: 'john', age: 27})
// => { name: 'john', age: 27 }

validateObject(person, {name: 'john'})
// ValidationError: Expected value of type number but got undefined
// While validating missing value for key "age"

validateObject(person, {name: 'john', age: 'notok'})
// ValidationError: Expected value of type number but got string
// While validating value at key "age"

validateObject(person, {name: 'john', age: 42, extra: 'oops'})
// ValidationError: Unexpected key: "extra"
// While validating key "extra"

validateObject(person, {nam: 'john', age: 42})
// ValidationError: Unexpected key: "nam", did you mean "name"?
// While validating key "nam"

If some key is optional, wrap its validator in maybe:

let person = object({
  name: string,
  age: number,
  nickName: maybe(string),
})

validateObject(person, {name: 'john', age: 27})
// => { name: 'john', age: 27 }

validateObject(person, {name: 'john', age: 27, nickName: 'J'})
// => { name: 'john', age: 27, nickName: 'J' }

You can also specify default values for keys:

let person = object({
  name: string,
  age: number,
  nickName: string,
}, {
  nickName: 'John Doe'
})

validateObject(person, {name: 'john', age: 27})
// => { name: 'john', age: 27, nickName: 'John Doe' }

validateObject(person, {name: 'john', age: 27, nickName: 'J'})
// => { name: 'john', age: 27, nickName: 'J' }
partialObject

Validate a subset of the keys from the object, passing all extra keys through:

let person = partialObject({
  name: string,
  age: number,
})

validateObject(person, {name: 'john', age: 27})
// => { name: 'john', age: 27 }

validateObject(person, {name: 'john', age: 42, extra: 'ok'})
// => { name: 'john', age: 42, extra: 'ok' }
maybe

Validates null and undefined but passes through any other value to the underlying validator:

validateObject(maybe(string), null)
// => null

validateObject(maybe(string), undefined)
// => undefined

validateObject(maybe(string), 'ok')
// => 'ok'

validateObject(maybe(string), 42)
// ValidationError: Expected value of type string but got number
oneOf

Tries a multiple validators and choose the one which succeeds first:

validateObject(oneOf(string, number), 'ok')
// => 'ok'

validateObject(oneOf(string, number), 42)
// => 42

validateObject(oneOf(string, number), true)
// ValidationError: Either:
//
//   Expected value of type string but got boolean
//
//   Expected value of type number but got boolean
//
recur

Allows to define recursive validators:

let tree = recur(tree =>
  object({
    value: any,
    children: maybe(arrayOf(tree))
  })
)

validateObject(tree, {value: 'ok'})
// => { value: 'ok' }

validateObject(tree, {value: 'ok', children: [{value: 'child'}]})
// => { value: 'ok', children: [ { value: 'child' } ] }

Refining validations

Example:

class Point {

  constructor(x, y) {
    this.x = x
    this.y = y
  }
}

let point = arrayOf(number).andThen((value, error) => {
  if (value.length !== 2) {
    throw error('Expected an array of length 2 but got: ' + value.length)
  }
  return new Point(value[0], value[1])
})

validateObject(point, [1, 2])
// => Point { x: 1, y: 2 }

validateJSON5(point, '[1, 2]')
// => Point { x: 1, y: 2 }

validateJSON5(point, '[1]')
// ValidationError: Expected an array of length 2 but got: 1 (line 1 column 1)

Defining new schema types

Example:

import {Node} from 'validated/schema'

class Point {

  constructor(x, y) {
    this.x = x
    this.y = y
  }
}

class PointNode extends Node {

  validate(context) {
    // prevalidate value with primitive validators
    let prevalidator = arrayOf(number)
    let {value, context: nextContext} = prevalidator.validate(context)

    // perform additional validations
    if (value.length !== 2) {

      // just report an error, context information such as line/column
      // numbers will be injected automatically
      throw context.error('Expected an array of length 2 but got: ' + value.length)
    }

    // construct a Point object, do whatever you want here
    let [x, y] = value
    let point = new Point(x, y)

    // return constructed value and the next context
    return {value: point, context: nextContext}
  }
}

validateObject(new PointNode(), [1, 2])
// => Point { x: 1, y: 2 }

validateJSON5(new PointNode(), '[1, 2]')
// => Point { x: 1, y: 2 }

validateJSON5(new PointNode(), '[1]')
// ValidationError: Expected an array of length 2 but got: 1 (line 1 column 1)

Integration with FlowType

Validated library uses FlowType extensively. Its API is defined in a way which automatically infers types for produced values:

import {object, string, number} from 'validated/schema'
import {validate} from 'validated/json5'

let personSchema = object({
  name: string,
  age: number,
})

let value: {name: string; age: number} = validate(
  personSchema,
  '{"name": "Andrey", age: 29}'
)

Note that the type annotation isn't needed — FlowType infers the type automatically based on a schema.