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

suit

v0.4.0

Published

Super simple object validation.

Downloads

3

Readme

Suit.js

Simple JS object validation.

Note: This library is not entirely ready for public consumption yet. Expect unexpected bugs.

Basic Usage

Run npm install suit. Then require as required in code.

var suit = require('suit');
var s = suit.constraints();

// suit.fit is a function that takes an input object and a schema object and returns a fitted object.
// suit.fit(input, schema) => output

var schema = {
  name: s.string,
  age: s.number,
  stats: {
    weight: s.number,
    height: s.number
  }
}

var input = {
  name: 'Bob',
  age: '42',
  stats: {
    weight: 84
  }
}

suit.fit(input, schema);
/*
Returns the following:
{
  name: 'Bob',
  age: 42,
  stats: {
    weight: 84,
    height: null
  }
}
*/

The suit library has only 2 main parts: 1) the suit.fit function, which conforms your input according to a desired schema, and 2) the suit.constraints function, which loads constraints you can use in building your schemas (including some common defaults like string, number, collection, etc.).

What it Does

suit.fit takes an input object, a schema object, and returns an output object.

For malformed input, suit's default policy is to try and knock it into shape so that it can pass. So types get auto-converted, decimals get rounded, nulls and undefineds pass, etc.

If that fails, then suit will throw an error listing all the properties that failed. Your app needs to catch that error and do something useful with it.

TODO: So some code examples for both good and bad input would be nice.

What constraints can I work with?

Things like:

  • Constraints that enforce type, like s.string, s.stringStrict, s.integer, etc.
  • Constraints that deal with necessaryness, like s.default(false) or s.required.
  • List-based constraints, like s.array(s.string) or s.collection({property: s.string}). They accept a schema as an argument and run the schema against all their members.
  • And other things. See files in lib/constraints to see what's currently available.

This list is not really helpful, because I haven't written the helpful list yet.

TODO: Write a better reference, rather than pointing to source.

Oh and constraints are just simple functions, so you can add your own fairly easily. See below below for more.

Multiple Constraints

You can supply an array of constraints for a property - they'll be run from left to right.

var schema = {
  percentage: [s.integer, s.min(0), s.max(100)]
}

suit.fit({percentage: 65.48}, schema); // {percentage: 65}
suit.fit({percentage: '44'}, schema); // {percentage: 44}
suit.fit({percentage: -11}, schema); // {percentage: 0}
suit.fit({}, schema); // {percentage: null}
suit.fit({percentage: {makeGo: 'kablooey'}}, schema); // Throws an error. s.integer doesn't know what to do with an object.

Nesting

suit.fit is really friendly to objects of arbitrary depth.

var schema = {
  id: [s.integer, s.required],
  admin: [s.boolean, s.default(false)],
  address: {
    street: s.string,
    postalCode: [s.integer, s.length(6)],
    country: {
      code: [s.string, s.length(2)],
      name: s.string
    }
  },
  pets: s.collection({
    name: s.string,
    color: s.string
  })
}

suit.fit({
  id: 2,
  address: {
    street: '11 Roadey Road',
    postalCode: 123456,
    country:
      code: 'SG',
      name: 'Singapore'
  },
  pets: [{
    name: 'spot',
    color: 'brown'
  }, {
    name: 'nyan',
    color: 'multi'
  }]
}, schema); // Everything passes, and the output has an additional admin property that's false.

Non-objects

Also, the input doesn't even need to be an object. So it works as a string/number/etc. validator.

suit.fit('Bob Schrodinger', [s.string, s.min(2)]); // Returns 'Bob Schrodinger'
suit.fit('i are very clever', [s.devowel, s.cap, s.nospace]); // Returns 'IRVRYCLVR'
// Note: devowel, cap, and nospace aren't official suit constraints, they're just there to stimulate your imagination. If you really want them you'll have to build them, see next section.

What's a constraint

A constraint is simply a function that takes a single input argument. If all is well it outputs the original input (or a slightly amended version of the input). If all is not well it throws an error.

// Source for s.integer
// noo() refers to a convenience function that checks whether the value is null or undefined - handling null/undefined is not this constraint's job, so it just lets it pass.
// Notice that we can just use other library's validators (e.g. lodash) instead of inventing our own.
module.exports.integer = function (value) {
  if (noo(value)) return value;
  if (_.isNumber(value) && !_.isNaN(value)) return Math.round(value);
  if (_.isString(value)) {
    if (!_.isNaN(parseInt(value))) return parseInt(value);
  }
  throw 'Expected an integer, but received: ' + value;
};

// Source for s.min
// s.min is used like s.min(17), which is why it's a function that returns a function.
module.exports.min = function (minValue) {
  return function (value) {
    if (noo(value)) return value;
    if (_.isNumber(value) && !_.isNaN(value)) {
      return Math.max(value, minValue);
    }
    if (_.isArray(value) || _.isString(value)) {
      if (value.length >= minValue) return value;
      throw 'Length of ' + value + ' (type ' + typeof value + ') is less than the required minimum of ' + minValue + ' (got ' + value.length + ')';
    }
    throw 'Expected a number, string, or array.';
  };
};

Adding your own constraints

You have a few ways of adding your own constraints:

1: You can put them in a seperate file and then load using suit.constraints(),

Actually, this method is offline right now, because I haven't settled how suit.constraints() pathfinds for custom constraint files yet.

// my-app/special-needs.js

module.exports.cap = function (value) {
  if (value === undefined || value === null) return value;
  if (_.isString(value)) return value.toUpperCase();
  throw 'Expected a string but got type: ' + typeof value;
}
var suit = require('suit');
var s = suit.constraints([
  'basic-types', // Included by default.
  'basic-ui', // Included by default.
  '/my-app/special-needs.js'
]);

suit.fit('im listening', s.cap); // 'IM LISTENING'

2: You can also pass suit.constraints an inline hash of constraints, if you're lazy,

var suit = require('suit');
var s = suit.constraints([
  'basic-types', // Included by default.
  'basic-ui', // Included by default.
  {
    cap: function (value) {
      if (value === undefined || value === null) return value;
      if (_.isString(value)) return value.toUpperCase();
      throw 'Expected a string but got type: ' + typeof value;
    }
  }
]);

suit.fit('im listening', s.cap); // 'IM LISTENING'

3: Heck, because constraints are simply functions, you can even declare them on-the-fly,

suit.fit('im listening', function (value) {
  if (value === undefined || value === null) return value;
  if (_.isString(value)) return value.toUpperCase();
  throw 'Expected a string but got type: ' + typeof value;
}); // 'IM LISTENING'