Giter Site home page Giter Site logo

kettek / schisma Goto Github PK

View Code? Open in Web Editor NEW
0.0 3.0 0.0 169 KB

JavaScript schema validator, conformer, and object creator

License: Mozilla Public License 2.0

JavaScript 100.00%
schema schemata validator creator conformance conformer validation

schisma's Introduction

Schisma

Schisma is a zero-dependency object schema creator, validator, conformer, and object instantizer.

Features

Schema Creation

Creation of new schemas is easy.

const schisma = require('schisma')

let mySchema = schisma({
  name: String,
  age: {
    $type: Number,
    $default: () => new Date%104,
  },
  owns: {
    apples: Number,
    cats: [{
      hairless: Boolean,
      age: {
        $type: Number,
        $validate: (v) => {
          if (v > 38) {
            return {expected: '<=38', received: v}
          }
        },
      },
    }],
  },
})

Schema Definition Format

Objects passed to schisma can be declared a variety of ways.

  • As a primitive type.
  • As a null value.
  • As a containing object.
  • As a schisma-style object.
  • As a Schisma instance.
  • As an Array of any of the previous.
Primitive Types

The simplest way of declaring the value a key is supposed to represent is with a primitive type.

{
  name: String,
  age:  Number,
}
Null Value

Null values can be used to declare a field to be null or, if as part of a $typeof Array, that it can be null or otherwise.

{
  alwaysNull: null,
  age: {
    $typeof: [Number, null]
  }
}
Containing Objects

Objects can be used to compose more complex schemas. The key-value pairs can use any of the standard methods outlined earlier.

{
  mouse: {
    manufacturer: String,
    buttons: Number,
    dpi: Number,
  },
}
Regular expression key matching

Regular expressions can also be used via the special $/ key prefix. These allow schemas to receive arbitrary keys that map to concrete types. The value of these regex keys must always be an array of desired types.

{
  "$/.*": [Number],
}

Multiple regular expressions can be used. Matching attempts will be done in order of declaration.

{
  "$/*": [Number],
  "$/(.*)key": [String],
}

Regular keys may be mixed freely and take priority over regex matching.

{
  "$/*": [Number],
  "$/(.*)key": [String],
  myProperty: Boolean,
}
Schisma-style Object

A Schisma-style object can also be used.

{
  $type: Number,
  $default: 20,
}
{
  $typeof: [Number, String],
  $default: 20,
}

Unlike the other value declarations, schisma-style objects are how you can provide validation and default value generation.

  • $type - May be a primitive type, an array, or another object, as per the schisma formatting rules.
  • $typeof [] - An array of any of the types allowed within $type. This limits the given field/object's type to be one of the provided types. If $type and $typeof are used in the same declaration, $type will be ignored.
  • [$default] <Any|Function> - The default value to use when inserting a value or generating a default object. If it is a function, then the return value of said function is used.
  • [$required] - Whether the value should be considered as required for validation and conforming. Defaults to true
  • [$validate] <Function(value,where)> - The function used to validate the value. May return an object that is merged with the resulting SchismaError with any of the following fields. Additionally, if a plain string is returned, it is used as the message field.
    • [value] - The value of the entry. Defaults to the object's original value
    • [where] - Where in the tree the error occurred. Defaults to the full object path
    • [message] - Message of the error.
    • [expected] - Type expected to be seen.
    • [received] - Received type instead of the expected type.
{
  mouse: {
    manufacturer: {
      $type: String,
      $default: "Grojitech",
      $validate: v => {
        switch(v) {
          case 'Grojitech':
          case 'Bricoshift':
          case 'Tacerr':
          case 'Loresair:
            return
          default:
            return {expected: 'Grojitech, Bricoshift, Tacerr, or Loresair'}
        }
      },
    },
    buttons: {
      $type: Number,
      $default: () => { return new Date%2400 },
      $validate: v => {
        if (v < 1 || v > 10) {
          return {expected: 'greater than 1 and less than 10'}
        }
      }
    },
    dpi: {
      $type: Number,
      $required: true,
    }
  },
}
Schisma Instance

Schisma instances can also be used as values.

let ChildSchema = schisma({
  childPropA: Number,
  childPropB: String,
})

let ParentSchema = schisma({
  child: ChildSchema,
  children: [ChildSchema],
})

Schema Validation

A Schisma object can be used to validate provided objects.

let myPersonhood = {
  name: 'OXXO',
  height: 180,
  owns: {
    cats: [
      {
        hairless: true,
        age: 400,
      },
      {
        hairless: false,
        age: 10,
      },
      {
        hairless: 'maybe',
        age: 20,
      },
    ],
  },
}

mySchema.validate(myPersonhood)
/* Returns:
[
  SchismaResult { code: 'missing key', where: 'age' },
  SchismaResult { code: 'missing key', where: 'owns.apples' },
  SchismaResult {
    code: 'no match',
    where: 'owns.cats.2.hairless',
    expected: [Function: Boolean],
    received: 'string',
    value: 'maybe',
    __typeIndex: 0
  },
  SchismaResult {
    code: 'invalid',
    where: 'owns.cats.0.age',
    expected: '<=38',
    received: 400,
    value: 400
  },
  SchismaResult {
    code: 'unexpected key',
    where: 'height',
    received: 180
  }
]
*/

Options can also be passed to validate.

option value default description
ignoreUnexpected Boolean false Ignores unexpected object keys.
ignoreRequired Boolean false Ignores required object keys.
ignoreShortArrays Boolean true Ignores arrays that are shorter than the schema's array.
ignoreLongArrays Boolean true Ignores arrays that are longer than the schema's array.
flattenErrors Boolean true Flattens all errors to return as a single array rather than heirarchically.
filterNonErrors Boolean true Filters out non error results.
mySchema.validate(myPersonhood, {ignoreUnexpected: true, ignoreRequired: true})
/* Returns:
[
  SchismaResult {
    code: 'no match',
    where: 'owns.cats.2.hairless',
    expected: [Function: Boolean],
    received: 'string',
    value: 'maybe',
    __typeIndex: 0
  },
  SchismaResult {
    code: 'invalid',
    where: 'owns.cats.0.age',
    expected: '<=38',
    received: 400,
    value: 400
  }
]
*/

Schema Conforming

Schisma can also conform an object to itself.

let conformedPerson = mySchema.conform(myPersonhood)
/* Returns:
{ name: 'OXXO',
  age: 89,
  owns:
   { apples: 0,
     cats:
      [ { hairless: true, age: 400 },
        { hairless: false, age: 10 },
        { hairless: true, age: 20 } ] } }
*/

Just like validate, conform can also take options to adjust the output.

option value default description
removeUnexpected Boolean true Removes unexpected object keys.
insertMissing Boolean true Inserts missing object keys with default values.
growArrays Boolean false Grow arrays to match the length of the schema's array.
shrinkArrays Boolean false Shrink arrays to match the length of the schema's array.
populateArrays Boolean false Populate empty arrays with default instances of their schema elements.
let conformedPerson2 = mySchema.conform(myPersonhood, {removeUnexpected: false, insertMissing: false})
/* Returns:
{ name: 'OXXO',
  owns:
   { cats:
      [ { hairless: true, age: 400 },
        { hairless: false, age: 10 },
        { hairless: true, age: 20 } ] },
  height: 180 }
*/

Schema Default Object Creation

Schisma can create a default object that conforms to its schema.

let newPerson = mySchema.create()
/* Returns:
{ name: '', age: 75, owns: { apples: 0, cats: [] } }
*/

Just like validate and conform, create can take options to adjust object creation.

option value default description
populateArrays Boolean false Populate arrays with default instances of their schema elements.
let newPerson = mySchema.create({populateArrays: false})
/* Returns:
{ name: '',
  age: 81,
  owns: { apples: 0, cats: [ { hairless: false, age: 0 } ] } }
*/

schisma's People

Contributors

dependabot[bot] avatar kettek avatar

Watchers

 avatar  avatar  avatar

schisma's Issues

Arbitrary Hashmaps

We need to support objects with semi arbitrary key=>value pairs. If an object structure is used to hold an arbitrary amount of properties, we should be able to delineate properties to be of a selection of types.

Perhaps:

obj: {
  $valueof: [Number, String],
}

which would work with:

obj: {
  myKey: "32",
  myOtherKey: 32,
}

or:

obj: {
  "*": [Number, String]
}

which would also work with the above example. The benefit of this method is that we could maintain other key values in tandem, such as:

obj: {
  "*": [Number, String],
  otherKey: Boolean,
}

Additionally, future iterations could use pattern matches/regex..!

Implement $mixin

There should be a $mixin property that mixes in other schisma definitions. Perhaps the mixins should be able to just be object declarations as well as schema definitions.

Any schema definitions that define a $validate or $conform at the top-level should probably not be allowed to be a mixin, unless we can somehow separate the fields between the mixin and the original...

Allow targeting of schema properties for create()

From a complex Schema, we should be able to call create() on its child members. At the moment this can be done by issuing:

  MySchema.$type.someChild.create(...)

However, it would be more convenient to have an accessor, possibly done as such:

  MySchema.create("someChild.someSubChild")

conform: Primitive-only schemas are broken

Schemas that are defined as: schisma(Number) or schisma({$type: Number}) are broken when conform is called on them. If a schema has primitives as part of its structure, this is not a problem, only if it is a stand-alone primitive Schema.

Add tests

As it says. We need tests for:

  • Primitives
  • Objects
  • Arrays
  • Schisma-based objects
  • $typeof
  • Pattern-based arrays
  • $validation (return true, return false, return obj, return undefined)
  • grow arrays
  • shrink arrays
  • and more...

Marshalling & Unmarshalling

It would be of great use to implement (un)marshalling, likely via:

  • $marshal: (value) => Object
  • $unmarshal: (value) => JSON-like

Some forethought should also be put towards stringify and whether or not to expect toJSON on the marshalled Object for automatic unmarshalling.

Should we also have a schema.stringify() or toJSON method then...?

Allow properties to be only a single value but able to be any in an array

In short, when create() or other methods are called, schema properties that are of type Array but have a "singleValue" or similar flag should populate/validate that property as any of the types in Array.

In other words:

let MyScheme = schisma({
  myProp: {
    $type: [String, Number],
    $singleValue: true,
  }
})

MyScheme.validate({
  myProp: 32
})
// => valid!

MyScheme.validate({
  myProp: "32!"
})
// => valid!

MyScheme.validate({
  myProp: [55]
})
// => invalid!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.