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

js-match

v1.0.10

Published

Validates an entire Javascript object against a set of nested matchers

Downloads

9

Readme

js-match

NPM License

Build Status Dependencies Dev dependencies

Validates an entire Javascript object against a set of nested matchers. This can be useful to quickly validate:

  • a JSON config file
  • the structure of an HTTP request payload

Note: js-match will always ignore extra fields, as long as the set of matchers passes. This conforms with the robustness principle of "be conservative in what you do, be liberal in what you accept".

Basic usage

npm install js-match --save
jsm = require 'js-match'

person =
  name: { match: 'string' }
  age:  { match: 'number' }

# success
errors = jsm.validate {name: 'Bob', age: 30}, person
errors.should.eql []

# failure
errors = jsm.validate {age: 'foo'}, person
errors.should.eql [
  {path: 'name', error: 'required'},
  {path: 'age',  value: 'foo', error: 'should be a number'}
]

Matchers

Values can be tested against a default set of matchers, for ex:

{ match: 'string'  }
{ match: 'number'  }
{ match: 'boolean' }
{ match: 'ip'      }
{ match: 'host'    }
{ match: 'url'     }
{ match: 'uri'     }
{ match: 'file'    }
{ match: 'dollars' }
{ match: 'uuid-v4' }
{ match: 'enum', values: ['foo', 'bar'] }

You can also register custom matchers for advanced logic:

jsm.matchers['custom-id'] = (value) ->
  if not value.match /[0-9a-f]{10}/i
    return "should be an ID of the form 8d9f0ab3c5"

jsm.validate object,
  name:     { match: 'string'    }
  uniqueId: { match: 'custom-id' }

Matchers can also take optional parameters:

jsm.matchers['age'] = (value, options) ->
  if value < options.min or value > options.max
    return "should be an age between #{options.min} and #{options.max}"

jsm.validate object,
  name:  { match: 'string' }
  age:   { match: 'age', min: 1, max: 100 }

Matchers can also return custom messages:

person =
  name: { match: 'string', message: 'this is a custom message' }
  age:  { match: 'number' }

# success
errors = jsm.validate {name: 'Bob', age: 30}, person
errors.should.eql []

# failure
errors = jsm.validate {name: 12, age: 30}, person
errors.should.eql [
  {path: 'name', value: 12, error: 'this is a custom message'}
]

Nested configs

js-match supports nested objects, arrays, and primitives:

jsm.validate object,
  credentials:
    user:  { match: 'string' }
    pass:  { match: 'string' }
  machines: [
    host:  { match: 'string' }
    port:  { match: 'number' }
  ]
  values: [
    { match: 'number' }
  ]

which matches the following object:

{
  "credentials": {
    "user": "bob",
    "pass": "p@ssw0rd"
  },
  "machines": [
    { "host": "serverX", "port": 3000 },
    { "host": "serverY", "port": 3000 }
  ],
  "values": [1, 2, 3, 4, 5]
}

Any errors will be returned with fully qualified paths, for ex:

[
  {path: 'credentials.user', error: 'required'},
  {path: 'machines[1].host', value: 1234, error: 'should be a string'},
  {path: 'values[3]', value: true, error: 'should be a number'}
]

Custom schemas

You can either nest the matchers directly, like above, or extract them into separate "schemas":

auth =
  accountNumber: { match: 'integer' }
  password:      { match: 'string' }

jsm.validate object,
  name: { match: 'string' }
  auth: { schema: auth }

You can also pass a function that dynamically returns a schema:

dynamic = (parent, value) ->
  if parent.type is 'A'
    { match: 'number' }
  else
    { match: 'string' }

jsm.validate object,
  type:  { match: 'string' }
  value: { schema: dynamic }

Required and optional fields

By default, all fields are assumed to be required. Fields can be manually marked as optional:

jsm.validate object,
  username: { match: 'string' }
  password: { match: 'string', optional: true }

Validation will ignore missing optional fields, but will run the matcher if an optional field is present. E.g. it won't complain if password isn't there, but will if password is a number.

You could also use optional on a "schema" matcher:

account =
  number:  { match 'integer' }
  suburb:  { match 'string'  }

jsm.validate object,
  username: { match: 'string' }
  password: { match: 'string' }
  account:  { schema: account, optional: true }

Validation will pass if account is missing, but will run the matcher if account node is present. E.g. it will complain if you have the account node without presenting the number or suburb leaf under the account node.

Arrays

You can also do more complex matching on arrays

You could also use optional on an array of simple matchers:

jsm.validate object,
  username: { match: 'string' }
  password: { match: 'string' }
  accountIds:  [match: 'number', optional: true ]

Validation will pass if accountIds is missing, but will run the matcher if accountIds node is present. E.g it will complain if accountIds is present but is not an array of numbers

You could also use optional on an array of schema:

account =
  number:  { match 'integer' }
  suburb:  { match 'string'  }

jsm.validate object,
  username: { match: 'string' }
  password: { match: 'string' }
  accounts: [ schema: account, optional: true ]

Validation will pass if accounts is missing, but will run the matcher if accounts node is present. E.g it will complain if accounts is present but is not an array of accounts

You could specify the min and max length of an array of schema:

account =
  number:  { match 'integer' }
  suburb:  { match 'string'  }

jsm.validate object,
  username: { match: 'string' }
  password: { match: 'string' }
  accounts: [ schema: account, min: 2, max: 5 ]

Validation will pass if accounts is an array of accounts. With length 2-5 E.g it will complain if accounts is an array of 6 accounts.