$ npm install edictor
{ Edictor } support both Browser & Node environment.
There's no javascript building tool is expected in browser environment.
Therefore, all dependencies are included in edictor.js
.
Just copy node_modules/edictor/dist/browser/edictor.js
to somewhere
which can be accessed by browsers. Then import
inside <script type="module">
<script type="module">
import { Model, defineField } from 'url/to/edictor';
</script>
Edictor
is built with Parcel. However, it should be compatible with any Javascript bulid tools.
ES Module (Preferred)
import { Model, defineField } from 'edictor';
CommonJS Module
const edictor = require('edictor');
// edictor.Model;
// edictor.defineField;
To begins, let's try to define a models / schemas for some parts of npm's package.json, which is a good example since it has a good documentation and complex enough to demonstrate {Edictor} APIs.
class Model
and defineField()
is two main cores APIs to start with.
They are carfully designed to be readable by themselve. If you know
Javascript, just read one by one line and you will get an idea how
{Edictor} APIs work in no time at all, trust me ! :)
/** ES Module */
import { Model, defineField } from 'edictor';
/** Every defined model class must extended from `Model` */
class Package extends Model {};
class People extends Model {};
/** Define reusable `defineField()` as `urlDef` to validates that:
* - The value must be a string instance.
* - The value is a valid URL by applying value to built-in `URL()`,
* which expect function to throw error if the data is invalid.
*/
const urlDef = defineField()
.instance('string')
.apply((value) => { new URL(value) })
/** Define `People` schema
* - definedField() can be used directly as a schema's field.
*/
People.define({
name: defineField({required: true})
.instance('string'),
email: defineField()
.instance('string')
.regexp(/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/),
url: urlDef // Reuseable `urlDef`
})
/** Define `Package` schema */
Package.define({
name: defineField({required: true})
.instance('string')
.assert((value) => { return value.length <= 214 },
"The name must be less than or equal to 214 characters"),
version: // https://ihateregex.io/expr/semver/
defineField({required: true})
.instance('string')
.regexp(/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/),
homepage: urlDef, // Reuseable `urlDef`
author: // Nesting data using `model()`
defineField()
.model(People),
contributors: // Array data using `arrayOf()`
defineField({initial: []}) // Set initial data for calling `push()`
.arrayOf(People)
})
/** Schema validation */
Package.validate({
'name': 'example',
'version': '0.0.1',
'homepage': 'http://example.com'
})
/** Atomic Instance */
const pk = new Package({
'name': 'example', // Required field
'version': '0.0.1' // Required field
})
pk['version'] = '0.0.2';
class Model {
/** Expected to throws errors if data is invalid */
static validate(data: Object, option?: ModelOption): Object;
}
Calling Model.validate(data)
, this scenario will validate
data with the schema, expected to throw errors if data is invalid.
// Data is valid
Package.validate({
name: 'example',
version: '0.0.1'
})
// Data is invalid, throw errors.
Package.validate({
version: '0.0.1',
homepage: 'http://example.com'
})
Uncaugth ValidateError ...
class Model {
/** Expected to throws errors if data is invalid */
static partial(data: Object, option?: ModelOption): Object;
}
Calling Model.partial(data)
, this scenario validates only
fields correspond to input data, or in others words, the schema will ignore
required fields. This might come in handy when you want to use/reuse only
some of schema fields to validate data.
Package.partial({
'version': '0.0.1'
})
class Model {
constructor(data?: Object, option?: ModelOption);
}
Atomic instance can keep data object state and garantees that
it's always valid at any point of your code. To apply this scenario,
use new Model(data)
to create an atomic instance.
const package = new Package({
'name': 'edictor',
'version': '0.4.0',
'homepage': 'https://nitipit.github.io/edictor/'
})
package['version'] = '0.4.1';
/** This line will throw errors and new data won't be assigned */
package['author'] = 'someone';
const packageData = {...package}; // data object
const packageJSON = JSON.stringify(package); // data in JSON
model.update
updates the data
into instance
only if the whole data
is valid, in the other hand,
it will throws errors and reject new data if it's invalid.
import { update } from 'edictor';
try {
update(package, {
name: "example",
version: "a.b.c" // Validation error
})
} catch (e) {};
From the schema definition, Package contains nesting data in the field named author.
const authur = new People({
name: 'author',
email: 'author@example.com'
})
package['author'] = author; // valid
package['author'] = {...author}; // valid
package['author']['url'] = 'url'; // invalid, throws errors.