Simple JSON schema implementation designed to be used for JSON configuration files
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
lib
test
.gitignore
.travis.yml
CHANGELOG.MD
LICENSE
README.md
package.json

README.md

confine finely-tuned configuration

Build Status Coverage Status npm

We live in a post-Sublime Text age. The cool way to store persistant state is in raw JSON. confine makes it easy to make some simple assertions about what is in that JSON before passing it to your application logic.

Clearly specifying configuration also gives us power when it comes to GUI auto-generation. For example, see react-confine.

confine works similarly to the Config system in the Atom editor, but with no type coercion. It is also modular and extendable, with support for custom types. confine also supports the null type.

Installation

npm install confine

Usage

var Confine = require('confine')
var confine = new Confine()
var schema = {
  type: 'object',
  properties: {
    name: {type: 'string'},
    age: {type: 'integer', min: 0, max: 120},
    income: {type: 'number', min: 0},
    universe: {type: 'string', enum: ['Marvel', 'DC']},
    living: {type: 'boolean', default: true},
    alterEgos: {type: 'array', items: {type: 'string'}},
    location: {
      type: 'object',
      properties: {
        city: {type: 'string'},
        state: {type: 'string', regex: /[A-Z]{2}/}
      }
    }
  }
}

var object = {
  name: 'Peter Parker',
  age: 17,
  income: 38123.52,
  alterEgos: ['Spider-Man'],
  universe: 'Marvel',
  location: {city: 'New York', state: 'NY'},
  girlfriend: 'Mary Jane'
}

console.log(confine.validateSchema(schema)) // true
console.log(confine.normalize(object, schema)) /* {
  name: 'Peter Parker',
  age: 17,
  income: 38123.52,
  living: true,
  alterEgos: ['Spider-Man'],
  universe: 'Marvel',
  location: {city: 'New York', state: 'NY'}
} */

Methods

confine.validateSchema(schema)

  • Returns a boolean indicating if schema is valid. This should be checked for any untrusted schema before calling any other method - using an invalid schema with the other confine methods results in undetermined behavior.

confine.normalize(obj, schema)

  • Returns an adjusted representation of obj, removing invalid or unnecessary fields and filling in defaults.
  • This method should be used to normalize data before it is passed to the logic that uses it. It ensures strictness about types.
  • Please note that you do not need to check validate before calling normalize. normalize expects an invalid obj, and will adjust it appropriately. You still must call validateSchema, though.

confine.clean(obj, schema)

  • Returns an adjusted representation of obj like normalize, but does not remove unnecessary fields or fill in defaults.
  • This method should be used to clean data before it is stored longterm or used by an automatic form generation tool (like react-confine). This allows for improved user experience, because the undefined/default distinction is made clear, and will not result in a loss of data as schemas change.
  • You do not need to check validate before calling clean. You still must call validateSchema, though.
  • Due to the fact that JSON does not allow for undefined, cleaning sparse arrays could result in an object with integer keys and a length property being generated. Such an object may look strange, but is handled in the same way by lodash, and prevents unnecessary overriding of null. For example:
var schema = {type: 'array', items: {type: 'string', default: 'x'}}
confine.clean(['a'], schema) // ['a']
confine.normalize(['a'], schema) // ['a']
confine.clean(['a', 42, 'c']) // {0: 'a', 2: 'c', length: 3}
confine.normalize(['a', 42, 'c']) // ['a', 'x', 'c']

confine.validate(obj, schema)

  • Returns a boolean indicating if obj is strictly valid according to schema.
  • A schema being valid means that _.isEqual(confine.normalize(obj, schema), obj). That is, normalizeing the object will not change it.
  • This method should be used rarely. In most cases, normalize or clean should be preferred.

Confine Schemas

Confine uses a simplified version of JSON Schema to describe JSON objects. Each schema is a JSON object that contains a type property. 7 types are built-in, and custom types can also be added. Types may make use of additional properties, as defined below. Additionally, all types make use of 2 additional properties: default and enum.

  • default is an object that will be returned by confine.normalize or confine.getDefault if the provided input is undefined. It must be a valid object itself (that is to say, confine.validate(schema.default, schema) must return true).
  • enum is an array that enumerates all possible options the value can take. Any input not listed in this array will be rejected. Every array emtry must be a valid object itself (that is to say, _.every(schema.enum, function (e) {return confine.validate(e, schema)}) must return true.

Please see test/test.js for examples of all of these types in use.

object

Specifies a JSON object mapping string keys to any JSON entity. properties should be itself an object mapping string keys to sub-schemas.

{ type: 'object'
  properties: {
    myInt: { type: 'integer' }
    myString: { type: 'string' } } }

// { myInt: 3, myString: 'something' }

array

Specifies a JSON array - an ordered list of entities, all of the same type. items should be a single sub-schema that all array entries match.

{ type: 'array',
  items: { type: 'integer' } }

// [ 1, 2, 3 ]

number

Specifies a JSON number (integer or decimal). This is held to the same limitations of all numbers in Javascript. max and min can (inclusively) limit possible number ranges.

{ type: 'number',
  min: 1.5,
  max: 11 }

// 7.2

integer

Specifies an integer. This is held to the same limitations of all integers in Javascript. max and min can (inclusively) limit possible number ranges.

{ type: 'integer',
  min: 4,
  max: 8 }

// 6

string

Specifies a JSON string. regex can limit the possible input given a Javascript RegExp object, or a string that can be evaulated as a regular expression.

{ type: 'string',
  regex: /.*/ }

// 'something'

boolean

Specifies a JSON boolean.

{ type: 'boolean' }

/// false

null

Specifies JSON null.

{ type: 'null' }

/// null
  • null works slightly differently than other types. confine.normalize(obj, {type: 'null'}) will always return null, even if _.isUndefined(obj). Thus, confine.validate(undefined, {type: 'null'}) returns false.
  • This means that confine.normalize(obj, {type: ['string', 'null']}) will never return undefined. If obj is not a valid string, it will return null. This is a good way of ensuring that there is something in the normalize output (even if it's null).

Multiple types

type can be an array of type names. All type names must be valid. When normalizeing undefined, the returned value will be the first one that does not return undefined, or undefined if all types do.

Please note that because number includes integers, {type: ['number', 'integer]} is strictly equivalent to {type: 'number'} alone.

Custom Types

You can add custom types by setting properties on confine.types. By default, it understands integer, number, string, boolean, array, object, and null. A custom type is simply an object that contains the following functions.

confine.types['typeName'] = {
  validateSchema: function (schema, confine) {...},
  validate: function (obj, schema, confine) {...},
  normalize: function (obj, schema, confine) {...} // optional
}

validateSchema(schema, confine)

  • Should return a boolean indicating if schema is valid.
  • confine is provided for subschema parsing (see lib/array).

validate(obj, schema, confine)

  • should return a boolean indiciating if the obj fits schema.
  • confine is provided for subschema parsing (see lib/array).

normalize(obj, schema, confine) // optional

  • should return a version of obj adjusted to fit the schema
  • if not provided, normalize will return default if it exists, or undefined
  • do not use this to coerce values - this should only be used for adjusting subschema parsing
  • see lib/object for an example