├── .editorconfig ├── .gitattributes ├── .gitignore ├── .jshintignore ├── .jshintrc ├── .travis.yml ├── README.md ├── SUMMARY.md ├── book.json ├── docs ├── change-control.md ├── serialization.md ├── state-pattern.md └── strict-typing.md ├── index.js ├── karma.conf.js ├── lib ├── classFactories │ ├── mArrayFactory.js │ ├── mGetterFactory.js │ ├── mMutatorFactory.js │ ├── mObjectFactory.js │ ├── mPrimitiveFactoryFactory.js │ ├── mRefFactory.js │ ├── mStateFactory.js │ └── mSugarFactory.js ├── classes │ ├── mArray.js │ ├── mObject.js │ └── mState.js └── utils │ ├── control.js │ ├── makeReadOnlyFn.js │ ├── overridableSnapshot.js │ ├── protoMapper.js │ └── traverseStates.js ├── npm-shrinkwrap.json ├── package.json └── test ├── .jshintrc ├── classFactories ├── array.js ├── getter.js ├── mutator.js ├── object.js ├── primitives.js ├── ref.js ├── state.js └── sugar.js ├── control.js ├── docs.js ├── protoMapper.js └── snapshot.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | npm-debug.log 3 | browser.js 4 | 5 | .idea 6 | -------------------------------------------------------------------------------- /.jshintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | browser.js 3 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "boss": true, 3 | "curly": true, 4 | "eqeqeq": true, 5 | "eqnull": true, 6 | "immed": true, 7 | "latedef": true, 8 | "newcap": false, 9 | "noarg": true, 10 | "node": true, 11 | "sub": true, 12 | "undef": true, 13 | "unused": true, 14 | "esnext": true 15 | } 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - '5' 5 | addons: 6 | firefox: "latest" 7 | before_script: 8 | - "export DISPLAY=:99.0" 9 | - "sh -e /etc/init.d/xvfb start" 10 | - sleep 3 # give xvfb some time to start 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Master-Class 2 | 3 | > JavaScript classes with an edge. 4 | 5 | 6 | This is library provides a [class](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Classes) [factory](https://en.wikipedia.org/wiki/Factory_%28object-oriented_programming%29). The instances of its generated classes have [many features](#Features) that will eventually allow them to: 7 | 8 | - Be distributively managed between machines 9 | - Be differently viewed in different contexts (e.g., depending on the viewer or an internal state) 10 | - Be efficiently stored 11 | - Keep precise track of their changes 12 | 13 | All while still feeling like plain JavaScript objects. 14 | 15 | # [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][daviddm-image]][daviddm-url] 16 | 17 | 18 | ## Install 19 | 20 | ```sh 21 | $ npm install --save master-class 22 | ``` 23 | 24 | 25 | ## Usage 26 | 27 | 0. Require the library 28 | 29 | ```js 30 | const M = require('master-class'); 31 | ``` 32 | 0. Create a new class: 33 | 34 | ```js 35 | const MyMClass = M({/* options */}); 36 | ``` 37 | 0. Create an instance: 38 | 39 | ```js 40 | const myInstance = new MyMClass(); 41 | ``` 42 | or 43 | ```js 44 | const myInstance = MyMClass.createInstance(); 45 | ``` 46 | 47 | ## Features 48 | 49 | * [Run-time strict typing](docs/strict-typing.md) 50 | * [Fine-grained change control](docs/change-control.md) 51 | * [Serialization](docs/serialization.md) 52 | * [The state pattern](docs/state-pattern.md) 53 | 54 | ## API Reference 55 | 56 | [WIP] For now, you could just read through the [tests](https://github.com/CardForest/master-class/tree/master/test). 57 | 58 | ## License 59 | 60 | [AGPL-3.0](http://www.gnu.org/licenses/agpl-3.0.en.html) © [Amit Portnoy](https://github.com/amitport) 61 | 62 | [npm-image]: https://badge.fury.io/js/master-class.svg 63 | [npm-url]: https://npmjs.org/package/master-class 64 | [travis-image]: https://travis-ci.org/CardForest/master-class.svg?branch=master 65 | [travis-url]: https://travis-ci.org/CardForest/master-class 66 | [daviddm-image]: https://david-dm.org/CardForest/master-class.svg?theme=shields.io 67 | [daviddm-url]: https://david-dm.org/CardForest/master-class 68 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [Read Me](README.md) 4 | * Features 5 | * [Strict Typing](docs/strict-typing.md) 6 | * [Change Control](docs/change-control.md) 7 | * [Serialization](docs/serialization.md) 8 | * [The State Pattern](docs/state-pattern.md) 9 | 10 | -------------------------------------------------------------------------------- /book.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "github", 4 | "edit-link" 5 | ], 6 | "pluginsConfig": { 7 | "edit-link": { 8 | "base": "https://github.com/CardForest/master-class/tree/master", 9 | "label": "Edit This Page" 10 | }, 11 | "github": { 12 | "url": "https://github.com/CardForest/master-class/" 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /docs/change-control.md: -------------------------------------------------------------------------------- 1 | # Change Control 2 | 3 | Using the non-enumerable property `control` we can temporarily prevent changes 4 | 5 | ```js 6 | const MyClass = M({n: Number}); 7 | let myInstance = new MyClass(); 8 | 9 | myInstance.n = 3; 10 | assert.equal(myInstance.n, 3); 11 | 12 | myInstance.control.isChangeAllowed = false; 13 | 14 | assert.throws(function() {myInstance.n = 4;}); 15 | assert.equal(myInstance.n, 3); 16 | ``` 17 | 18 | This feature is used to enforce no state mutations in getters 19 | 20 | ```js 21 | M({ 22 | n: Number, 23 | get g() { 24 | // if we would try and assign `this.n = 4` 25 | // it would throw a run-time exception 26 | return this.n + 1; 27 | } 28 | }).createInstance().g; 29 | ``` 30 | We can also use the `control` object to listen to changes 31 | 32 | ```js 33 | myInstance.control.on('change', 34 | function (changeType, changePayload, opt) { 35 | // triggered on any direct or nested changes 36 | } 37 | ); 38 | ``` 39 | 40 | Additionally, we can declare an object methods that may introduce mutations 41 | 42 | ```js 43 | myInstance = M({ 44 | n: Number, 45 | m1(x) { 46 | this.n += x; 47 | } 48 | }).createInstance(); 49 | ``` 50 | 51 | and those can be overridden by the `control`'s `onMutatorCall` method 52 | 53 | ```js 54 | myInstance.control.onMutatorCall = 55 | function (keyPath, args, mutator) { 56 | // apply the original methd (add args[0] to this.n) 57 | mutator.apply(this, args); 58 | // add another 4 - just because we can 59 | return this.n += 4; 60 | }; 61 | 62 | myInstance.m1(3); 63 | assert.equal(myInstance.n, 7); 64 | ``` 65 | 66 | and be guarded by an [idempotent](https://en.wikipedia.org/wiki/Idempotence) function (ensured internally by `control.isChangeAllowed`) 67 | 68 | > Note that to order to pass the `guard` property, we now need to explicitly use `M.Mutator` class factory 69 | 70 | ```js 71 | myInstance = M({ 72 | n: 5, 73 | m: M.Mutator({ 74 | guard: function () { 75 | return this.n < 10; 76 | }, 77 | fn: function (x) { 78 | this.n += x; 79 | } 80 | }) 81 | }).createInstance(); 82 | 83 | myInstance.m(6); 84 | assert.equal(myInstance.n, 11); 85 | 86 | assert.throws(function () { 87 | myInstance.m(1); 88 | }); 89 | assert.equal(myInstance.n, 11); 90 | ``` -------------------------------------------------------------------------------- /docs/serialization.md: -------------------------------------------------------------------------------- 1 | # Serialization 2 | 3 | Instances initial value can be determined by the class 4 | 5 | ```js 6 | const MyClass = M({ 7 | n: 3, 8 | s: 'test', 9 | b: true 10 | }); 11 | let myInstance = new MyClass(); 12 | 13 | assert.deepEqual( 14 | myInstance, 15 | {n: 3, s: 'test', b: true} 16 | ); 17 | ``` 18 | 19 | It can also be overridden in the constructor 20 | 21 | ```js 22 | assert.deepEqual( 23 | new MyClass({n: 4, s: 'test2', b: false}), 24 | {n: 4, s: 'test2', b: false} 25 | ); 26 | ``` 27 | 28 | We also have a non-enumerable `snapshot` method that converts the instance into a raw plain object, stripping it from any getters or setters 29 | 30 | ```js 31 | assert.deepEqual( 32 | MyClass.createInstance(myInstance.snapshot()), 33 | myInstance 34 | ); 35 | ``` 36 | 37 | Additionally, you can provide a `mapper` to override the snapshot process 38 | 39 | ```js 40 | assert.deepEqual( 41 | myInstance.snapshot( 42 | function (opt, instance, keyPath, snapshotFn) { 43 | if (keyPath[0] === 'n') { 44 | return 5; 45 | } else { 46 | return snapshotFn(); 47 | } 48 | } 49 | ), 50 | {n: 5, s: 'test', b: true} 51 | ); 52 | ``` 53 | 54 | Lastly, we have the `M.Ref` type that ensures that object references survive serialization 55 | 56 | ```js 57 | myInstance = M({ 58 | r: M.Ref(), 59 | o: { 60 | n: Number 61 | } 62 | }).createInstance(); 63 | myInstance.r = myInstance.o; 64 | 65 | const myInstanceCopy = new MyClass(myInstance.snapshot()); 66 | 67 | assert.strictEqual(myInstanceCopy.r, myInstanceCopy.o); 68 | ``` -------------------------------------------------------------------------------- /docs/state-pattern.md: -------------------------------------------------------------------------------- 1 | # The State Pattern 2 | 3 | First, if you are not familiar with this pattern, I recommend you read this [excellent article](http://gameprogrammingpatterns.com/state.html). 4 | 5 | Our Master-Class types include an `M.State` type. That type helps us turn objects into concurrent, hierarchical state machines. 6 | 7 | The `M.State` type basically merges object types together 8 | 9 | ```js 10 | let MyClass = M({ 11 | state: M.State([ 12 | { 13 | delegate: M({n: 3}), // (1) first object type 14 | subState: [{ 15 | delegate: M({m: 5}) // (1.1) first object type child 16 | }] 17 | }, 18 | { 19 | delegate: M({s: String}) // (2) second object type 20 | } 21 | ]) 22 | }); 23 | let myInstance = new MyClass(); 24 | assert.deepEqual(myInstance.state, {n: 3, m: 5, s: ''}); 25 | ``` 26 | 27 | *but* every merge can be conditional using the `when` option 28 | 29 | ```js 30 | MyClass = M({ 31 | flag1: true, 32 | state: M.State([ 33 | { 34 | when() {return this.flag2;}, 35 | delegate: M({n: 3}), // (1) first object type 36 | subState: [{ 37 | when() {return this.root.flag1;}, 38 | delegate: M({m: 5}) // (1.1) first object type child 39 | }] 40 | }, 41 | { 42 | delegate: M({ // (2) second object type 43 | flag2: Boolean, 44 | s: String 45 | }) 46 | } 47 | ]) 48 | }); 49 | myInstance = new MyClass(); 50 | assert.deepEqual(myInstance.state, {flag2: false, s: ''}); 51 | ``` 52 | 53 | Note that `myInstance.state.m` property is not included, even though `this.root.flag1` is `true`. This is because its parent state is blocked on `this.flag2`. Now, if we just assign `myInstance.state.flag2 = true`, we'll get 54 | 55 | ```js 56 | assert.deepEqual( 57 | myInstance.state, 58 | {flag2: true, s: '', n: 3, m: 5} 59 | ); 60 | ``` 61 | 62 | -------------------------------------------------------------------------------- /docs/strict-typing.md: -------------------------------------------------------------------------------- 1 | # Strict Typing 2 | 3 | We can generate classes that enforce run-time type checks and prevent object extensions 4 | 5 | ```js 6 | const MyClass = M({ 7 | str: String, 8 | arr: [Number], 9 | obj: { 10 | bool: Boolean 11 | } 12 | }); 13 | 14 | const myInstance = new MyClass(); 15 | ``` 16 | 17 | Now `myInstance` is a normal JavaScript object 18 | 19 | ```js 20 | assert.deepEqual(myInstance, { 21 | str: '', 22 | arr: [0], 23 | obj: { 24 | bool: false 25 | } 26 | }); 27 | ``` 28 | 29 | that prevents incorrect type assignments 30 | 31 | ```js 32 | assert.throws(function() { 33 | myInstance.obj.bool = 'will throw'; 34 | }); 35 | ``` 36 | 37 | and object extensions 38 | 39 | ```js 40 | assert.throws(function() { 41 | myInstance.newProperty = 'will throw'; 42 | }); 43 | ``` 44 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('./lib/classFactories/mSugarFactory'); 4 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | config.set({ 3 | browsers: ['Chrome', 'Firefox'], 4 | 5 | frameworks: ['browserify', 'mocha'], 6 | 7 | files: ['test/**/*.js'], 8 | preprocessors: { 9 | 'test/**/*.js': [ 'browserify' ] 10 | }, 11 | 12 | singleRun: true, 13 | autoWatch: false 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /lib/classFactories/mArrayFactory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const MArray = require('../classes/mArray'); 4 | 5 | module.exports = function mArrayFactory(opt) { 6 | return class extends MArray { 7 | constructor(value, keyPath, control, parent) { 8 | // TODO don't pass opt directly and use this.constructor.opt in the constructor 9 | // -> waiting for new.target and array inheritance to be better supported 10 | super(value, keyPath, control, parent); 11 | } 12 | 13 | static get opt() {return opt;} 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /lib/classFactories/mGetterFactory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const makeReadOnlyFn = require('../utils/makeReadOnlyFn'); 4 | 5 | module.exports = function (getter) { 6 | if (typeof getter !== 'function') { 7 | throw TypeError('expected function'); 8 | } 9 | return { 10 | createInstance() { 11 | return getter; 12 | }, 13 | 14 | toPropertyDescriptor(getter, control) { 15 | return { 16 | enumerable: false, 17 | configurable: false, 18 | get: makeReadOnlyFn(getter, control) 19 | }; 20 | } 21 | }; 22 | }; 23 | -------------------------------------------------------------------------------- /lib/classFactories/mMutatorFactory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const makeReadOnlyFn = require('../utils/makeReadOnlyFn'); 4 | 5 | module.exports = function mMutatorFactory(opt) { 6 | if (typeof opt === 'function') { 7 | // syntactic sugar 8 | opt = { 9 | fn: opt 10 | }; 11 | } else if (typeof opt.fn !== 'function') { 12 | // we can't find the mutator function 13 | throw TypeError('expected function'); 14 | } 15 | 16 | return { 17 | opt, 18 | 19 | createInstance(unused1, keyPath, control, parent) { 20 | const originMutatorFn = opt.fn; 21 | const mutatorFn = function () { 22 | return control.onMutatorCall.call(this, keyPath, Array.prototype.slice.call(arguments, 0), originMutatorFn); 23 | }; 24 | 25 | if (opt.hasOwnProperty('guard')) { 26 | var guard = makeReadOnlyFn(opt.guard, control); 27 | var fn = function () { 28 | if (guard.call(this) === true) { 29 | return mutatorFn.apply(this, arguments); 30 | } else { 31 | throw Error('mutator blocked by guard'); 32 | } 33 | }; 34 | Object.defineProperty(fn, 'guard', { 35 | enumerable: false, 36 | configurable: false, 37 | get: guard.bind(parent) 38 | }); 39 | return fn; 40 | } else { 41 | return mutatorFn; 42 | } 43 | }, 44 | 45 | toPropertyDescriptor(mutator) { 46 | return { 47 | enumerable: false, 48 | configurable: false, 49 | value: mutator, 50 | writable: false 51 | }; 52 | } 53 | }; 54 | }; 55 | -------------------------------------------------------------------------------- /lib/classFactories/mObjectFactory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const MObject = require('../classes/mObject'); 4 | 5 | module.exports = function mObjectFactory(opt) { 6 | if (!opt.hasOwnProperty('props')) { 7 | throw Error('object options must include \'props\''); 8 | } 9 | 10 | return class extends MObject { 11 | constructor(value, keyPath, control, parent) { 12 | super(value, keyPath, control, parent); 13 | } 14 | 15 | static get opt() {return opt;} 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /lib/classFactories/mPrimitiveFactoryFactory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function mPrimitiveFactoryFactory(primitive) { 4 | const primitiveTypeName = primitive.name.toLowerCase(); 5 | 6 | return function (opt) { 7 | opt = (opt != null) ? opt : {}; 8 | 9 | if (opt.hasOwnProperty('initialValue') && typeof opt.initialValue !== primitiveTypeName) { 10 | throw TypeError('expected ' + primitiveTypeName); 11 | } 12 | 13 | return { 14 | opt, 15 | 16 | createInstance(value) { 17 | if (value == null) { 18 | value = opt.initialValue; 19 | } 20 | if (value == null) { 21 | return primitive(); 22 | } else if (typeof value !== primitiveTypeName) { 23 | throw TypeError('expected ' + primitiveTypeName); 24 | } else { 25 | return primitive(value); 26 | } 27 | }, 28 | 29 | toPropertyDescriptor(value, control, keyPath, key) { 30 | return { 31 | enumerable: true, 32 | configurable: false, 33 | get: function () { 34 | return value; 35 | }, 36 | set: (newValue) => { 37 | if (!control.isChangeAllowed) { 38 | throw Error('change is not allowed in this context'); 39 | } 40 | if (typeof newValue !== primitiveTypeName) { 41 | throw TypeError('expected ' + primitiveTypeName); 42 | } 43 | value = newValue; 44 | control.emit('change', 'setValue', {newValue: newValue, trgKeyPath: keyPath, key: key}, opt); 45 | } 46 | }; 47 | }, 48 | 49 | snapshot(mapper, instance) { 50 | return instance; 51 | } 52 | }; 53 | }; 54 | }; 55 | -------------------------------------------------------------------------------- /lib/classFactories/mRefFactory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var KeyPath = require('key-path'); 4 | var MObject = require('../classes/mObject'); 5 | 6 | module.exports = function (opt) { 7 | opt = (opt != null) ? opt : {}; 8 | 9 | return { 10 | opt, 11 | 12 | createInstance(value) { 13 | if (value != null) { 14 | return value; 15 | } 16 | 17 | return { 18 | ref: null 19 | }; 20 | }, 21 | 22 | toPropertyDescriptor(value, control, keyPath, key) { 23 | return { 24 | enumerable: true, 25 | configurable: false, 26 | get() { 27 | if (value.hasOwnProperty('keyPath')) { 28 | // relink to referred object on first access after snapshot 29 | value.ref = KeyPath.get(value.keyPath).getValueFrom(this[control.rootPropertyName]); 30 | delete value.keyPath; 31 | } 32 | return value.ref; 33 | }, 34 | set(newValue) { 35 | if (!control.isChangeAllowed) { 36 | throw Error('change is not allowed in this context'); 37 | } 38 | if (!(newValue instanceof MObject)) { 39 | throw TypeError('expected MObject'); 40 | } 41 | //TODO support specific reference utils 42 | 43 | value.ref = newValue; 44 | control.emit('change', 'setValue', { 45 | newValue: {keyPath: newValue.keyPath}, 46 | trgKeyPath: keyPath, 47 | key: key 48 | }, opt); 49 | } 50 | }; 51 | }, 52 | 53 | snapshot(mapper, instance) { 54 | return (instance != null) ? {keyPath: instance.keyPath} : null; 55 | } 56 | }; 57 | }; 58 | -------------------------------------------------------------------------------- /lib/classFactories/mStateFactory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const MState = require('../classes/mState'); 4 | const traverseStates = require('../utils/traverseStates'); 5 | 6 | module.exports = function mStateFactory(opt) { 7 | if (!Array.isArray(opt)) { 8 | throw TypeError('expected objects array'); 9 | } 10 | 11 | let currentIdx = 0; 12 | traverseStates(opt, (state) => { 13 | state.idx = currentIdx++; 14 | }); 15 | 16 | return class extends MState { 17 | static get opt() {return opt;} 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /lib/classFactories/mSugarFactory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mObjectFactory = require('./mObjectFactory'); 4 | const mPrimitiveFactoryFactory = require('./mPrimitiveFactoryFactory'); 5 | const mNumberFactory = mPrimitiveFactoryFactory(Number); 6 | const mStringFactory = mPrimitiveFactoryFactory(String); 7 | const mBooleanFactory = mPrimitiveFactoryFactory(Boolean); 8 | const mArrayFactory = require('./mArrayFactory'); 9 | const mGetterFactory = require('./mGetterFactory'); 10 | const mMutatorFactory = require('./mMutatorFactory'); 11 | 12 | function mSugarFactory(opt) { 13 | switch (typeof opt) { 14 | case 'object': 15 | if (opt.hasOwnProperty('createInstance')) { 16 | // already a class 17 | return opt; 18 | } 19 | if (Array.isArray(opt)) { 20 | return mArrayFactory({elem: mSugarFactory(opt[0]), defaultLength: 1}); 21 | } 22 | 23 | let sugarProps; 24 | if (opt.hasOwnProperty('props')){ 25 | sugarProps = opt.props; 26 | opt.props = {}; 27 | } else { 28 | sugarProps = opt; 29 | opt = {props: {}}; 30 | } 31 | const keys = Object.getOwnPropertyNames(sugarProps); 32 | keys.forEach((key) => { 33 | if (key[0] === '$') { 34 | opt[key.slice(1)] = sugarProps[key]; 35 | return; 36 | } 37 | const propertyDescriptor = Object.getOwnPropertyDescriptor(sugarProps, key); 38 | if (propertyDescriptor.hasOwnProperty('get')) { 39 | opt.props[key] = mGetterFactory(propertyDescriptor.get); 40 | } else { 41 | opt.props[key] = mSugarFactory(propertyDescriptor.value); 42 | } 43 | }); 44 | return mObjectFactory(opt); 45 | case 'function': 46 | switch (opt.name) { 47 | case 'Number': 48 | return mNumberFactory(); 49 | case 'Boolean': 50 | return mBooleanFactory(); 51 | case 'String': 52 | return mStringFactory(); 53 | default: { 54 | return ('createInstance' in opt) ? opt : mMutatorFactory(opt); 55 | } 56 | } // jshint ignore:line 57 | case 'number': 58 | return mNumberFactory({initialValue: opt}); 59 | case 'string': 60 | return mStringFactory({initialValue: opt}); 61 | case 'boolean': 62 | return mBooleanFactory({initialValue: opt}); 63 | 64 | } 65 | } 66 | 67 | mSugarFactory.Object = mObjectFactory; 68 | mSugarFactory.Array = mArrayFactory; 69 | mSugarFactory.State = require('./mStateFactory'); 70 | 71 | mSugarFactory.Number = mNumberFactory; 72 | mSugarFactory.String = mStringFactory; 73 | mSugarFactory.Boolean = mBooleanFactory; 74 | 75 | mSugarFactory.Getter = mGetterFactory; 76 | mSugarFactory.Mutator = mMutatorFactory; 77 | mSugarFactory.Ref = require('./mRefFactory'); 78 | 79 | mSugarFactory.Sugar = mSugarFactory; 80 | 81 | module.exports = mSugarFactory; 82 | -------------------------------------------------------------------------------- /lib/classes/mArray.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const overridableSnapshot = require('../utils/overridableSnapshot'); 4 | const Control = require('../utils/control'); 5 | 6 | const MObject = require('./mObject'); 7 | 8 | class MArray extends Array { 9 | constructor(value, keyPath, control, parent) { 10 | super(); 11 | 12 | const opt = this.constructor.opt; 13 | 14 | if (keyPath == null) { 15 | keyPath = []; 16 | } 17 | if (control == null) { 18 | control = new Control(); 19 | } 20 | 21 | const root = parent == null ? this : parent.hasOwnProperty(control.rootPropertyName) ? parent[control.rootPropertyName] : parent; 22 | 23 | Object.defineProperty( 24 | this, 25 | control.rootPropertyName, 26 | { 27 | enumerable: false, 28 | configurable: false, 29 | value: root, 30 | writable: false 31 | } 32 | ); 33 | 34 | var length; 35 | if (value == null) { 36 | value = []; 37 | length = opt.defaultLength; 38 | } else { 39 | // allow overriding of length 40 | length = value.length; 41 | } 42 | this.length = length; // make sure this array has the correct length even if it has undefined elements 43 | 44 | var elem = opt.elem; 45 | 46 | for (var i = 0; i < length; i++) { 47 | this.constructor.defineProperty(this, value, keyPath, control, elem, i); 48 | } 49 | 50 | Object.freeze(this); 51 | } 52 | 53 | static get [Symbol.species]() {return Array;} 54 | 55 | static snapshot(mapper, instance, keyPath) { 56 | var res = []; 57 | for (var i = 0, length = instance.length; i < length; i++) { 58 | res[i] = overridableSnapshot(this.opt.elem, mapper, instance[i], keyPath.concat(i)); 59 | } 60 | return res; 61 | } 62 | } 63 | 64 | MArray.createInstance = MObject.createInstance; 65 | MArray.toPropertyDescriptor = MObject.toPropertyDescriptor; 66 | MArray.defineProperty = MObject.defineProperty; 67 | 68 | module.exports = MArray; 69 | -------------------------------------------------------------------------------- /lib/classes/mObject.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Control = require('../utils/control'); 4 | const forOwn = require('lodash.forown'); 5 | const overridableSnapshot = require('../utils/overridableSnapshot'); 6 | 7 | module.exports = class MObject { 8 | static handleRootObject(target, value, keyPath, control, parent) { 9 | const isRoot = parent == null; 10 | if (isRoot) { 11 | // this is the root object 12 | 13 | // allow passing of control overrides in root second parameter 14 | if (keyPath instanceof Control) { 15 | control = keyPath; 16 | } else { 17 | control = new Control(keyPath); 18 | } 19 | 20 | keyPath = []; 21 | 22 | Object.defineProperty( 23 | target, 24 | 'snapshot', 25 | { 26 | enumerable: false, 27 | configurable: false, 28 | writable: false, 29 | value: (mapper) => { 30 | if (mapper == null) { 31 | mapper = function noOpMapper(_1, _2, _3, snapshotFn) { 32 | return snapshotFn(); 33 | }; 34 | } 35 | return this.snapshot(mapper, target, keyPath); 36 | } 37 | } 38 | ); 39 | 40 | Object.defineProperty( 41 | target, 42 | 'control', 43 | { 44 | enumerable: false, 45 | configurable: false, 46 | writable: false, 47 | value: control 48 | } 49 | ); 50 | 51 | if (this.opt.hasOwnProperty('rootPropertyName')) { 52 | control.rootPropertyName = this.opt.rootPropertyName; 53 | } 54 | } 55 | return {isRoot, keyPath, control}; 56 | } 57 | 58 | constructor(value, _keyPath, _control, parent) { 59 | const handleRootObjectResponse = this.constructor.handleRootObject(this, value, _keyPath, _control, parent); 60 | // TODO replace this destructuring when available 61 | const isRoot = handleRootObjectResponse.isRoot, 62 | keyPath = handleRootObjectResponse.keyPath, 63 | control = handleRootObjectResponse.control; 64 | 65 | const opt = this.constructor.opt; 66 | 67 | if (isRoot) { 68 | if (opt.props.hasOwnProperty('snapshot')) { 69 | throw Error('\'snapshot\' property is reserved in the root objects'); 70 | } 71 | 72 | if (opt.props.hasOwnProperty('control')) { 73 | throw Error('\'control\' property is reserved in the root objects'); 74 | } 75 | } 76 | 77 | // setup ref to root object 78 | if (opt.props.hasOwnProperty(control.rootPropertyName)) { 79 | throw Error('\'' + control.rootPropertyName + '\' property is reserved in objects'); 80 | } 81 | 82 | const root = isRoot ? this : (parent.hasOwnProperty(control.rootPropertyName) ? parent[control.rootPropertyName] : parent); 83 | 84 | Object.defineProperty( 85 | this, 86 | control.rootPropertyName, 87 | { 88 | enumerable: false, 89 | configurable: false, 90 | value: root, 91 | writable: false 92 | } 93 | ); 94 | 95 | if (!isRoot) { 96 | Object.defineProperty( 97 | this, 98 | 'parent', 99 | { 100 | enumerable: false, 101 | configurable: false, 102 | value: parent, 103 | writable: false 104 | } 105 | ); 106 | } 107 | 108 | if (opt.props.hasOwnProperty('keyPath')) { 109 | throw Error('\'keyPath\' property is reserved in objects'); 110 | } 111 | 112 | Object.defineProperty( 113 | this, 114 | 'keyPath', 115 | { 116 | enumerable: false, 117 | configurable: false, 118 | value: keyPath, 119 | writable: false 120 | } 121 | ); 122 | 123 | if (value == null) { 124 | value = {}; 125 | } 126 | 127 | forOwn(opt.props, (Class, key) => { 128 | this.constructor.defineProperty(this, value, keyPath, control, Class, key); 129 | }); 130 | 131 | Object.freeze(this); 132 | 133 | if (isRoot) { 134 | control.emit('change'); 135 | } 136 | } 137 | 138 | static createInstance(value, keyPath, control, parent) { 139 | if (value != null && typeof value !== 'object') { 140 | // support overriding of object with new utils from mappers 141 | return value; 142 | } 143 | 144 | return new this(value, keyPath, control, parent); 145 | } 146 | 147 | static snapshot(mapper, instance, keyPath) { 148 | if (typeof instance !== 'object') { 149 | // we're currently allowing object to be overridden by other values 150 | return instance; 151 | } 152 | const res = {}; 153 | forOwn(instance, (value, key) => { 154 | res[key] = overridableSnapshot(this.opt.props[key], mapper, value, keyPath.concat(key)); 155 | }); 156 | 157 | return res; 158 | } 159 | 160 | static toPropertyDescriptor(value, control, keyPath, key, parent) { 161 | return { 162 | enumerable: true, 163 | configurable: false, 164 | set: (newValue) => { 165 | if (!control.isChangeAllowed) { 166 | throw Error('change is not allowed in this context'); 167 | } 168 | value = this.createInstance(newValue, keyPath.concat(key), control, parent); 169 | control.emit('change', 'setValue', {newValue: newValue, trgKeyPath: keyPath, key: key}, this.opt); 170 | }, 171 | get() { 172 | return value; 173 | }, 174 | }; 175 | } 176 | 177 | static defineProperty(self, value, keyPath, control, propFactory, key) { 178 | if ((typeof propFactory !== 'object' && typeof propFactory !== 'function') || !('createInstance' in propFactory)) { 179 | throw Error('element in [' + keyPath.concat(key) + '] does not have a valid type'); 180 | } 181 | if (value[key] !== '$hidden') { 182 | value = propFactory.createInstance(value[key], keyPath.concat(key), control, self); 183 | 184 | Object.defineProperty( 185 | self, 186 | key, 187 | propFactory.toPropertyDescriptor( 188 | value, 189 | control, 190 | keyPath, 191 | key, 192 | self 193 | ) 194 | ); 195 | } 196 | } 197 | 198 | static get props() { 199 | return this.opt.props; 200 | } 201 | }; 202 | -------------------------------------------------------------------------------- /lib/classes/mState.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const overridableSnapshot = require('../utils/overridableSnapshot'); 4 | const forOwn = require('lodash.forown'); 5 | const MObject = require('./mObject'); 6 | const traverseStates = require('../utils/traverseStates'); 7 | 8 | class MState { 9 | constructor(value, _keyPath, _control, parent) { 10 | const handleRootObjectResponse = this.constructor.handleRootObject(this, value, _keyPath, _control, parent); 11 | // TODO replace this destructuring when available 12 | const isRoot = handleRootObjectResponse.isRoot, 13 | keyPath = handleRootObjectResponse.keyPath, 14 | control = handleRootObjectResponse.control; 15 | 16 | const opt = this.constructor.opt; 17 | 18 | const root = isRoot ? this : (parent.hasOwnProperty(control.rootPropertyName) ? parent[control.rootPropertyName] : parent); 19 | 20 | Object.defineProperty( 21 | this, 22 | control.rootPropertyName, 23 | { 24 | enumerable: false, 25 | configurable: false, 26 | value: root, 27 | writable: false 28 | } 29 | ); 30 | 31 | Object.defineProperty( 32 | this, 33 | 'keyPath', 34 | { 35 | enumerable: false, 36 | configurable: false, 37 | value: keyPath, 38 | writable: false 39 | } 40 | ); 41 | 42 | const delegates = {}; 43 | Object.defineProperty( 44 | this, 45 | 'delegates', 46 | { 47 | enumerable: false, 48 | configurable: false, 49 | value: delegates, 50 | writable: false 51 | } 52 | ); 53 | 54 | let oldProps = []; 55 | const updateDelegates = () => { 56 | const props2DelegateIdx = {}; 57 | const traverse = (stateOpts, parentPredicate) => { 58 | stateOpts.forEach((stateOpt) => { // do traverse only once later just follow the array 59 | let predicate = (parentPredicate && (!stateOpt.when || stateOpt.when.call(this))); 60 | if (predicate) { 61 | if (!delegates.hasOwnProperty(stateOpt.idx)) { 62 | delegates[stateOpt.idx] = stateOpt.delegate.createInstance(value, keyPath, control, parent); 63 | } 64 | Object.keys(stateOpt.delegate.props).forEach((key) => { 65 | props2DelegateIdx[key] = stateOpt.idx; 66 | }); 67 | } else if (delegates.hasOwnProperty(stateOpt.idx)) { 68 | delete delegates[stateOpt.idx]; 69 | } 70 | 71 | if (stateOpt.hasOwnProperty('subState')) { 72 | traverse(stateOpt.subState, predicate); 73 | } 74 | }); 75 | }; 76 | 77 | traverse(opt, true); 78 | 79 | forOwn(props2DelegateIdx, (delegateIdx, key) => { 80 | Object.defineProperty( 81 | this, key, 82 | { 83 | configurable: true, 84 | enumerable: true, 85 | get() { 86 | return delegates[delegateIdx][key]; 87 | }, 88 | set(val) { 89 | delegates[delegateIdx][key] = val; 90 | } 91 | } 92 | ); 93 | }); 94 | 95 | oldProps.forEach((key) => { 96 | if (!props2DelegateIdx.hasOwnProperty(key)) { 97 | delete this[key]; 98 | } 99 | }); 100 | oldProps = Object.keys(props2DelegateIdx); 101 | }; 102 | 103 | traverseStates(opt, (state) => { 104 | control.addWatcher({ 105 | watchFn: (state.when) ? state.when.bind(this) : () => true, 106 | listenerFn: updateDelegates 107 | }); 108 | }); 109 | 110 | if (isRoot) { 111 | control.emit('change'); 112 | } 113 | } 114 | 115 | static snapshot(mapper, instance, keyPath) { 116 | var res = {}; 117 | forOwn(instance.delegates, (delegate, idx) => { 118 | Object.assign(res, overridableSnapshot(this.opt[idx].delegate, mapper, delegate, keyPath)); 119 | }); 120 | 121 | return res; 122 | } 123 | } 124 | 125 | MState.createInstance = MObject.createInstance; 126 | MState.toPropertyDescriptor = MObject.toPropertyDescriptor; 127 | MState.handleRootObject = MObject.handleRootObject; 128 | 129 | module.exports = MState; 130 | -------------------------------------------------------------------------------- /lib/utils/control.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const EventEmitter = require('events'); 4 | 5 | class Control extends EventEmitter { 6 | constructor(opt) { 7 | super(); 8 | 9 | this.isChangeAllowed = true; 10 | this.rootPropertyName = 'root'; 11 | this.watchers = []; 12 | 13 | this.on('change', this.digest.bind(this)); 14 | 15 | Object.assign(this, opt); 16 | } 17 | 18 | onMutatorCall(keyPath, args, mutator) { 19 | return mutator.apply(this, args); 20 | } 21 | 22 | addWatcher(watcher) { 23 | this.watchers.push(watcher); 24 | } 25 | 26 | digest() { 27 | let dirty; 28 | const processWatcher = (watcher) => { 29 | const newValue = watcher.watchFn(); 30 | const oldValue = watcher.lastValue; 31 | if (!!oldValue !== !!newValue) { // jshint ignore:line 32 | watcher.listenerFn(); 33 | dirty = true; 34 | watcher.lastValue = newValue; 35 | } 36 | }; 37 | do { 38 | dirty = false; 39 | this.watchers.forEach(processWatcher); 40 | } while (dirty); 41 | } 42 | } 43 | 44 | 45 | module.exports = Control; 46 | -------------------------------------------------------------------------------- /lib/utils/makeReadOnlyFn.js: -------------------------------------------------------------------------------- 1 | module.exports = function makeReadOnlyFn(fn, control) { 2 | return function () { 3 | if (control.isChangeAllowed) { 4 | control.isChangeAllowed = false; 5 | try { 6 | return fn.call(this); 7 | } finally { 8 | control.isChangeAllowed = true; 9 | } 10 | } else { 11 | return fn.call(this); 12 | } 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /lib/utils/overridableSnapshot.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function overridableSnapshot(factory, mapper, instance, keyPath) { 4 | return mapper(factory.opt, instance, keyPath, factory.snapshot.bind(factory, mapper, instance, keyPath)); 5 | }; 6 | -------------------------------------------------------------------------------- /lib/utils/protoMapper.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('reflect-metadata'); 4 | 5 | module.exports = class ProtoMapper { 6 | constructor(mappers) { 7 | this.cache = new WeakMap(); 8 | this.cache.set(Object.prototype, false); 9 | 10 | this.mappers = mappers; 11 | } 12 | 13 | map(lowestProto) { 14 | const protoChain = []; 15 | 16 | let highestProto = lowestProto; 17 | while (!this.cache.has(highestProto)) { 18 | protoChain.push(highestProto); 19 | highestProto = Object.getPrototypeOf(highestProto); 20 | } 21 | // protoChain contains all the proto that do require mapping 22 | 23 | // highestProto is now the highest proto that was previously seen 24 | // highestMappedProto is a cloned&mapped version of that proto or false if no mapping was required 25 | let highestMappedProto = this.cache.get(highestProto); 26 | 27 | while (protoChain.length !== 0) { 28 | const nextHighestProto = protoChain.pop(); 29 | const props = Object.getOwnPropertyNames(nextHighestProto); 30 | const propsToMap = props 31 | .map((_) => { // jshint ignore:line 32 | return { 33 | propName: _, 34 | metadataKeys: Reflect.getOwnMetadataKeys(nextHighestProto, _) 35 | .filter((_) => this.mappers.hasOwnProperty(_)) 36 | }; 37 | }) 38 | .filter((_) => _.metadataKeys.length !== 0); 39 | 40 | if (!highestMappedProto && propsToMap.length === 0) { 41 | // we never actually needed to map any prototype 42 | // and we don't need to map this prototype either 43 | this.cache.set(nextHighestProto, false); 44 | highestProto = nextHighestProto; 45 | continue; 46 | } 47 | 48 | // we need to map this prototype 49 | const nextHighestMappedProto = Object.create(highestMappedProto || highestProto); 50 | // first copy all previous properties 51 | props.forEach((_) => { // jshint ignore:line 52 | Object.defineProperty(nextHighestMappedProto, _, Object.getOwnPropertyDescriptor(nextHighestProto, _)); 53 | }); 54 | // next override from mappers 55 | propsToMap 56 | .forEach((_) => { // jshint ignore:line 57 | const propName = _.propName; 58 | let trgProp = nextHighestMappedProto[propName]; 59 | _.metadataKeys.forEach((metadataKey) => { 60 | trgProp = nextHighestMappedProto[propName] = this.mappers[metadataKey](trgProp); 61 | }); 62 | }); 63 | this.cache.set(nextHighestProto, nextHighestMappedProto); 64 | 65 | highestMappedProto = nextHighestMappedProto; 66 | highestProto = nextHighestProto; // (we don't really need this- but keeping this for coherence) 67 | } 68 | 69 | return highestMappedProto || highestProto; 70 | } 71 | }; 72 | -------------------------------------------------------------------------------- /lib/utils/traverseStates.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function traverseStates(states, cb) { 4 | states = states.slice(); 5 | 6 | while (states.length !== 0) { 7 | const state = states.shift(); 8 | cb(state); 9 | if (state.hasOwnProperty('subState')) { 10 | states.unshift.apply(states, state.subState); 11 | } 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /npm-shrinkwrap.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "master-class", 3 | "version": "0.5.9", 4 | "dependencies": { 5 | "accepts": { 6 | "version": "1.1.4", 7 | "from": "accepts@1.1.4", 8 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.1.4.tgz", 9 | "dependencies": { 10 | "mime-db": { 11 | "version": "1.12.0", 12 | "from": "mime-db@>=1.12.0 <1.13.0", 13 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.12.0.tgz" 14 | }, 15 | "mime-types": { 16 | "version": "2.0.14", 17 | "from": "mime-types@>=2.0.4 <2.1.0", 18 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.0.14.tgz" 19 | } 20 | } 21 | }, 22 | "acorn": { 23 | "version": "1.2.2", 24 | "from": "acorn@>=1.0.3 <2.0.0", 25 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-1.2.2.tgz" 26 | }, 27 | "after": { 28 | "version": "0.8.1", 29 | "from": "after@0.8.1", 30 | "resolved": "https://registry.npmjs.org/after/-/after-0.8.1.tgz" 31 | }, 32 | "amdefine": { 33 | "version": "1.0.0", 34 | "from": "amdefine@>=0.0.4", 35 | "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz" 36 | }, 37 | "anymatch": { 38 | "version": "1.3.0", 39 | "from": "anymatch@>=1.3.0 <2.0.0", 40 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.0.tgz" 41 | }, 42 | "arr-diff": { 43 | "version": "2.0.0", 44 | "from": "arr-diff@>=2.0.0 <3.0.0", 45 | "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz" 46 | }, 47 | "arr-flatten": { 48 | "version": "1.0.1", 49 | "from": "arr-flatten@>=1.0.1 <2.0.0", 50 | "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.0.1.tgz" 51 | }, 52 | "array-filter": { 53 | "version": "0.0.1", 54 | "from": "array-filter@>=0.0.0 <0.1.0", 55 | "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz" 56 | }, 57 | "array-map": { 58 | "version": "0.0.0", 59 | "from": "array-map@>=0.0.0 <0.1.0", 60 | "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz" 61 | }, 62 | "array-reduce": { 63 | "version": "0.0.0", 64 | "from": "array-reduce@>=0.0.0 <0.1.0", 65 | "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz" 66 | }, 67 | "array-slice": { 68 | "version": "0.2.3", 69 | "from": "array-slice@>=0.2.3 <0.3.0", 70 | "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz" 71 | }, 72 | "array-unique": { 73 | "version": "0.2.1", 74 | "from": "array-unique@>=0.2.1 <0.3.0", 75 | "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz" 76 | }, 77 | "arraybuffer.slice": { 78 | "version": "0.0.6", 79 | "from": "arraybuffer.slice@0.0.6", 80 | "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz" 81 | }, 82 | "arrify": { 83 | "version": "1.0.1", 84 | "from": "arrify@>=1.0.0 <2.0.0", 85 | "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz" 86 | }, 87 | "asn1.js": { 88 | "version": "4.5.2", 89 | "from": "asn1.js@>=4.0.0 <5.0.0", 90 | "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.5.2.tgz" 91 | }, 92 | "assert": { 93 | "version": "1.3.0", 94 | "from": "assert@>=1.3.0 <1.4.0", 95 | "resolved": "https://registry.npmjs.org/assert/-/assert-1.3.0.tgz" 96 | }, 97 | "astw": { 98 | "version": "2.0.0", 99 | "from": "astw@>=2.0.0 <3.0.0", 100 | "resolved": "https://registry.npmjs.org/astw/-/astw-2.0.0.tgz" 101 | }, 102 | "async-each": { 103 | "version": "1.0.0", 104 | "from": "async-each@>=1.0.0 <2.0.0", 105 | "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.0.tgz" 106 | }, 107 | "backo2": { 108 | "version": "1.0.2", 109 | "from": "backo2@1.0.2", 110 | "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz" 111 | }, 112 | "balanced-match": { 113 | "version": "0.3.0", 114 | "from": "balanced-match@>=0.3.0 <0.4.0", 115 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.3.0.tgz" 116 | }, 117 | "base64-arraybuffer": { 118 | "version": "0.1.2", 119 | "from": "base64-arraybuffer@0.1.2", 120 | "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.2.tgz" 121 | }, 122 | "base64-js": { 123 | "version": "1.1.2", 124 | "from": "base64-js@>=1.0.2 <2.0.0", 125 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.1.2.tgz" 126 | }, 127 | "base64id": { 128 | "version": "0.1.0", 129 | "from": "base64id@0.1.0", 130 | "resolved": "https://registry.npmjs.org/base64id/-/base64id-0.1.0.tgz" 131 | }, 132 | "batch": { 133 | "version": "0.5.3", 134 | "from": "batch@>=0.5.3 <0.6.0", 135 | "resolved": "https://registry.npmjs.org/batch/-/batch-0.5.3.tgz" 136 | }, 137 | "benchmark": { 138 | "version": "1.0.0", 139 | "from": "benchmark@1.0.0", 140 | "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-1.0.0.tgz" 141 | }, 142 | "better-assert": { 143 | "version": "1.0.2", 144 | "from": "better-assert@>=1.0.0 <1.1.0", 145 | "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz" 146 | }, 147 | "binary-extensions": { 148 | "version": "1.4.0", 149 | "from": "binary-extensions@>=1.0.0 <2.0.0", 150 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.4.0.tgz" 151 | }, 152 | "blob": { 153 | "version": "0.0.4", 154 | "from": "blob@0.0.4", 155 | "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz" 156 | }, 157 | "bluebird": { 158 | "version": "2.10.2", 159 | "from": "bluebird@>=2.9.27 <3.0.0", 160 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.10.2.tgz" 161 | }, 162 | "bn.js": { 163 | "version": "4.11.1", 164 | "from": "bn.js@>=4.1.1 <5.0.0", 165 | "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.1.tgz" 166 | }, 167 | "body-parser": { 168 | "version": "1.15.0", 169 | "from": "body-parser@>=1.12.4 <2.0.0", 170 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.15.0.tgz" 171 | }, 172 | "brace-expansion": { 173 | "version": "1.1.3", 174 | "from": "brace-expansion@>=1.0.0 <2.0.0", 175 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.3.tgz" 176 | }, 177 | "braces": { 178 | "version": "1.8.3", 179 | "from": "braces@>=1.8.2 <2.0.0", 180 | "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.3.tgz" 181 | }, 182 | "brorand": { 183 | "version": "1.0.5", 184 | "from": "brorand@>=1.0.1 <2.0.0", 185 | "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.0.5.tgz" 186 | }, 187 | "browser-pack": { 188 | "version": "6.0.1", 189 | "from": "browser-pack@>=6.0.1 <7.0.0", 190 | "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.0.1.tgz" 191 | }, 192 | "browser-resolve": { 193 | "version": "1.11.1", 194 | "from": "browser-resolve@>=1.11.0 <2.0.0", 195 | "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.1.tgz" 196 | }, 197 | "browserify-aes": { 198 | "version": "1.0.6", 199 | "from": "browserify-aes@>=1.0.4 <2.0.0", 200 | "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.0.6.tgz" 201 | }, 202 | "browserify-cipher": { 203 | "version": "1.0.0", 204 | "from": "browserify-cipher@>=1.0.0 <2.0.0", 205 | "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz" 206 | }, 207 | "browserify-des": { 208 | "version": "1.0.0", 209 | "from": "browserify-des@>=1.0.0 <2.0.0", 210 | "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz" 211 | }, 212 | "browserify-rsa": { 213 | "version": "4.0.1", 214 | "from": "browserify-rsa@>=4.0.0 <5.0.0", 215 | "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz" 216 | }, 217 | "browserify-sign": { 218 | "version": "4.0.0", 219 | "from": "browserify-sign@>=4.0.0 <5.0.0", 220 | "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.0.tgz" 221 | }, 222 | "browserify-zlib": { 223 | "version": "0.1.4", 224 | "from": "browserify-zlib@>=0.1.2 <0.2.0", 225 | "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz" 226 | }, 227 | "buffer": { 228 | "version": "4.5.1", 229 | "from": "buffer@>=4.1.0 <5.0.0", 230 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.5.1.tgz", 231 | "dependencies": { 232 | "isarray": { 233 | "version": "1.0.0", 234 | "from": "isarray@>=1.0.0 <2.0.0", 235 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" 236 | } 237 | } 238 | }, 239 | "buffer-xor": { 240 | "version": "1.0.3", 241 | "from": "buffer-xor@>=1.0.2 <2.0.0", 242 | "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz" 243 | }, 244 | "builtin-status-codes": { 245 | "version": "2.0.0", 246 | "from": "builtin-status-codes@>=2.0.0 <3.0.0", 247 | "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-2.0.0.tgz" 248 | }, 249 | "bytes": { 250 | "version": "2.2.0", 251 | "from": "bytes@2.2.0", 252 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.2.0.tgz" 253 | }, 254 | "callsite": { 255 | "version": "1.0.0", 256 | "from": "callsite@1.0.0", 257 | "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz" 258 | }, 259 | "chokidar": { 260 | "version": "1.4.3", 261 | "from": "chokidar@>=1.4.1 <2.0.0", 262 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.4.3.tgz" 263 | }, 264 | "cipher-base": { 265 | "version": "1.0.2", 266 | "from": "cipher-base@>=1.0.0 <2.0.0", 267 | "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.2.tgz" 268 | }, 269 | "cli": { 270 | "version": "0.6.6", 271 | "from": "cli@>=0.6.0 <0.7.0", 272 | "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz", 273 | "dependencies": { 274 | "glob": { 275 | "version": "3.2.11", 276 | "from": "glob@>=3.2.1 <3.3.0", 277 | "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz" 278 | }, 279 | "minimatch": { 280 | "version": "0.3.0", 281 | "from": "minimatch@>=0.3.0 <0.4.0", 282 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz" 283 | } 284 | } 285 | }, 286 | "colors": { 287 | "version": "1.1.2", 288 | "from": "colors@>=1.1.0 <2.0.0", 289 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz" 290 | }, 291 | "combine-source-map": { 292 | "version": "0.7.1", 293 | "from": "combine-source-map@>=0.7.1 <0.8.0", 294 | "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.7.1.tgz" 295 | }, 296 | "commander": { 297 | "version": "2.3.0", 298 | "from": "commander@2.3.0", 299 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.3.0.tgz" 300 | }, 301 | "component-bind": { 302 | "version": "1.0.0", 303 | "from": "component-bind@1.0.0", 304 | "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz" 305 | }, 306 | "component-emitter": { 307 | "version": "1.1.2", 308 | "from": "component-emitter@1.1.2", 309 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz" 310 | }, 311 | "component-inherit": { 312 | "version": "0.0.3", 313 | "from": "component-inherit@0.0.3", 314 | "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz" 315 | }, 316 | "concat-map": { 317 | "version": "0.0.1", 318 | "from": "concat-map@0.0.1", 319 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" 320 | }, 321 | "concat-stream": { 322 | "version": "1.5.1", 323 | "from": "concat-stream@>=1.5.1 <1.6.0", 324 | "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.1.tgz" 325 | }, 326 | "connect": { 327 | "version": "3.4.1", 328 | "from": "connect@>=3.3.5 <4.0.0", 329 | "resolved": "https://registry.npmjs.org/connect/-/connect-3.4.1.tgz" 330 | }, 331 | "console-browserify": { 332 | "version": "1.1.0", 333 | "from": "console-browserify@>=1.1.0 <2.0.0", 334 | "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz" 335 | }, 336 | "constants-browserify": { 337 | "version": "1.0.0", 338 | "from": "constants-browserify@>=1.0.0 <1.1.0", 339 | "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz" 340 | }, 341 | "content-type": { 342 | "version": "1.0.1", 343 | "from": "content-type@>=1.0.1 <1.1.0", 344 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.1.tgz" 345 | }, 346 | "convert-source-map": { 347 | "version": "1.1.3", 348 | "from": "convert-source-map@>=1.1.0 <1.2.0", 349 | "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz" 350 | }, 351 | "core-js": { 352 | "version": "2.2.1", 353 | "from": "core-js@>=2.1.0 <3.0.0", 354 | "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.2.1.tgz" 355 | }, 356 | "core-util-is": { 357 | "version": "1.0.2", 358 | "from": "core-util-is@>=1.0.0 <1.1.0", 359 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" 360 | }, 361 | "create-ecdh": { 362 | "version": "4.0.0", 363 | "from": "create-ecdh@>=4.0.0 <5.0.0", 364 | "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz" 365 | }, 366 | "create-hash": { 367 | "version": "1.1.2", 368 | "from": "create-hash@>=1.1.0 <2.0.0", 369 | "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.2.tgz" 370 | }, 371 | "create-hmac": { 372 | "version": "1.1.4", 373 | "from": "create-hmac@>=1.1.0 <2.0.0", 374 | "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.4.tgz" 375 | }, 376 | "crypto-browserify": { 377 | "version": "3.11.0", 378 | "from": "crypto-browserify@>=3.0.0 <4.0.0", 379 | "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.11.0.tgz" 380 | }, 381 | "custom-event": { 382 | "version": "1.0.0", 383 | "from": "custom-event@>=1.0.0 <1.1.0", 384 | "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.0.tgz" 385 | }, 386 | "date-now": { 387 | "version": "0.1.4", 388 | "from": "date-now@>=0.1.4 <0.2.0", 389 | "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz" 390 | }, 391 | "debug": { 392 | "version": "2.2.0", 393 | "from": "debug@>=2.2.0 <2.3.0", 394 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz" 395 | }, 396 | "defined": { 397 | "version": "1.0.0", 398 | "from": "defined@>=1.0.0 <2.0.0", 399 | "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz" 400 | }, 401 | "depd": { 402 | "version": "1.1.0", 403 | "from": "depd@>=1.1.0 <1.2.0", 404 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz" 405 | }, 406 | "deps-sort": { 407 | "version": "2.0.0", 408 | "from": "deps-sort@>=2.0.0 <3.0.0", 409 | "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.0.tgz" 410 | }, 411 | "des.js": { 412 | "version": "1.0.0", 413 | "from": "des.js@>=1.0.0 <2.0.0", 414 | "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz" 415 | }, 416 | "detective": { 417 | "version": "4.3.1", 418 | "from": "detective@>=4.0.0 <5.0.0", 419 | "resolved": "https://registry.npmjs.org/detective/-/detective-4.3.1.tgz" 420 | }, 421 | "di": { 422 | "version": "0.0.1", 423 | "from": "di@>=0.0.1 <0.0.2", 424 | "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz" 425 | }, 426 | "diff": { 427 | "version": "1.4.0", 428 | "from": "diff@1.4.0", 429 | "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz" 430 | }, 431 | "diffie-hellman": { 432 | "version": "5.0.2", 433 | "from": "diffie-hellman@>=5.0.0 <6.0.0", 434 | "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz" 435 | }, 436 | "dom-serialize": { 437 | "version": "2.2.1", 438 | "from": "dom-serialize@>=2.2.0 <3.0.0", 439 | "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz" 440 | }, 441 | "dom-serializer": { 442 | "version": "0.1.0", 443 | "from": "dom-serializer@>=0.0.0 <1.0.0", 444 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", 445 | "dependencies": { 446 | "domelementtype": { 447 | "version": "1.1.3", 448 | "from": "domelementtype@>=1.1.1 <1.2.0", 449 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz" 450 | }, 451 | "entities": { 452 | "version": "1.1.1", 453 | "from": "entities@>=1.1.1 <1.2.0", 454 | "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz" 455 | } 456 | } 457 | }, 458 | "domain-browser": { 459 | "version": "1.1.7", 460 | "from": "domain-browser@>=1.1.0 <1.2.0", 461 | "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz" 462 | }, 463 | "domelementtype": { 464 | "version": "1.3.0", 465 | "from": "domelementtype@>=1.0.0 <2.0.0", 466 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz" 467 | }, 468 | "domhandler": { 469 | "version": "2.3.0", 470 | "from": "domhandler@>=2.3.0 <2.4.0", 471 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz" 472 | }, 473 | "domutils": { 474 | "version": "1.5.1", 475 | "from": "domutils@>=1.5.0 <1.6.0", 476 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz" 477 | }, 478 | "duplexer2": { 479 | "version": "0.1.4", 480 | "from": "duplexer2@>=0.1.2 <0.2.0", 481 | "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz" 482 | }, 483 | "ee-first": { 484 | "version": "1.1.1", 485 | "from": "ee-first@1.1.1", 486 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" 487 | }, 488 | "elliptic": { 489 | "version": "6.2.3", 490 | "from": "elliptic@>=6.0.0 <7.0.0", 491 | "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.2.3.tgz" 492 | }, 493 | "engine.io": { 494 | "version": "1.6.8", 495 | "from": "engine.io@1.6.8", 496 | "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-1.6.8.tgz" 497 | }, 498 | "engine.io-client": { 499 | "version": "1.6.8", 500 | "from": "engine.io-client@1.6.8", 501 | "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-1.6.8.tgz" 502 | }, 503 | "engine.io-parser": { 504 | "version": "1.2.4", 505 | "from": "engine.io-parser@1.2.4", 506 | "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-1.2.4.tgz", 507 | "dependencies": { 508 | "has-binary": { 509 | "version": "0.1.6", 510 | "from": "has-binary@0.1.6", 511 | "resolved": "https://registry.npmjs.org/has-binary/-/has-binary-0.1.6.tgz" 512 | } 513 | } 514 | }, 515 | "ent": { 516 | "version": "2.2.0", 517 | "from": "ent@>=2.2.0 <2.3.0", 518 | "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz" 519 | }, 520 | "entities": { 521 | "version": "1.0.0", 522 | "from": "entities@>=1.0.0 <1.1.0", 523 | "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz" 524 | }, 525 | "escape-html": { 526 | "version": "1.0.3", 527 | "from": "escape-html@>=1.0.3 <1.1.0", 528 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" 529 | }, 530 | "escape-string-regexp": { 531 | "version": "1.0.2", 532 | "from": "escape-string-regexp@1.0.2", 533 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz" 534 | }, 535 | "eventemitter3": { 536 | "version": "1.2.0", 537 | "from": "eventemitter3@>=1.0.0 <2.0.0", 538 | "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz" 539 | }, 540 | "events": { 541 | "version": "1.1.0", 542 | "from": "events@>=1.1.0 <1.2.0", 543 | "resolved": "https://registry.npmjs.org/events/-/events-1.1.0.tgz" 544 | }, 545 | "evp_bytestokey": { 546 | "version": "1.0.0", 547 | "from": "evp_bytestokey@>=1.0.0 <2.0.0", 548 | "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.0.tgz" 549 | }, 550 | "exit": { 551 | "version": "0.1.2", 552 | "from": "exit@>=0.1.0 <0.2.0", 553 | "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" 554 | }, 555 | "expand-braces": { 556 | "version": "0.1.2", 557 | "from": "expand-braces@>=0.1.1 <0.2.0", 558 | "resolved": "https://registry.npmjs.org/expand-braces/-/expand-braces-0.1.2.tgz", 559 | "dependencies": { 560 | "braces": { 561 | "version": "0.1.5", 562 | "from": "braces@>=0.1.2 <0.2.0", 563 | "resolved": "https://registry.npmjs.org/braces/-/braces-0.1.5.tgz" 564 | }, 565 | "expand-range": { 566 | "version": "0.1.1", 567 | "from": "expand-range@>=0.1.0 <0.2.0", 568 | "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-0.1.1.tgz" 569 | }, 570 | "is-number": { 571 | "version": "0.1.1", 572 | "from": "is-number@>=0.1.1 <0.2.0", 573 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-0.1.1.tgz" 574 | }, 575 | "repeat-string": { 576 | "version": "0.2.2", 577 | "from": "repeat-string@>=0.2.2 <0.3.0", 578 | "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-0.2.2.tgz" 579 | } 580 | } 581 | }, 582 | "expand-brackets": { 583 | "version": "0.1.4", 584 | "from": "expand-brackets@>=0.1.4 <0.2.0", 585 | "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.4.tgz" 586 | }, 587 | "expand-range": { 588 | "version": "1.8.1", 589 | "from": "expand-range@>=1.8.1 <2.0.0", 590 | "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.1.tgz" 591 | }, 592 | "extend": { 593 | "version": "3.0.0", 594 | "from": "extend@>=3.0.0 <4.0.0", 595 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz" 596 | }, 597 | "extglob": { 598 | "version": "0.3.2", 599 | "from": "extglob@>=0.3.1 <0.4.0", 600 | "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz" 601 | }, 602 | "filename-regex": { 603 | "version": "2.0.0", 604 | "from": "filename-regex@>=2.0.0 <3.0.0", 605 | "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.0.tgz" 606 | }, 607 | "fill-range": { 608 | "version": "2.2.3", 609 | "from": "fill-range@>=2.1.0 <3.0.0", 610 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz" 611 | }, 612 | "finalhandler": { 613 | "version": "0.4.1", 614 | "from": "finalhandler@0.4.1", 615 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.4.1.tgz" 616 | }, 617 | "for-in": { 618 | "version": "0.1.4", 619 | "from": "for-in@>=0.1.4 <0.2.0", 620 | "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.4.tgz" 621 | }, 622 | "for-own": { 623 | "version": "0.1.3", 624 | "from": "for-own@>=0.1.3 <0.2.0", 625 | "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.3.tgz" 626 | }, 627 | "formatio": { 628 | "version": "1.1.1", 629 | "from": "formatio@1.1.1", 630 | "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.1.1.tgz" 631 | }, 632 | "fs-access": { 633 | "version": "1.0.0", 634 | "from": "fs-access@>=1.0.0 <2.0.0", 635 | "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.0.tgz" 636 | }, 637 | "function-bind": { 638 | "version": "1.1.0", 639 | "from": "function-bind@>=1.0.2 <2.0.0", 640 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz" 641 | }, 642 | "glob": { 643 | "version": "5.0.15", 644 | "from": "glob@>=5.0.15 <6.0.0", 645 | "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz" 646 | }, 647 | "glob-base": { 648 | "version": "0.3.0", 649 | "from": "glob-base@>=0.3.0 <0.4.0", 650 | "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz" 651 | }, 652 | "glob-parent": { 653 | "version": "2.0.0", 654 | "from": "glob-parent@>=2.0.0 <3.0.0", 655 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz" 656 | }, 657 | "graceful-fs": { 658 | "version": "4.1.3", 659 | "from": "graceful-fs@>=4.1.2 <5.0.0", 660 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.3.tgz" 661 | }, 662 | "growl": { 663 | "version": "1.8.1", 664 | "from": "growl@1.8.1", 665 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.8.1.tgz" 666 | }, 667 | "has": { 668 | "version": "1.0.1", 669 | "from": "has@>=1.0.0 <2.0.0", 670 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz" 671 | }, 672 | "has-binary": { 673 | "version": "0.1.7", 674 | "from": "has-binary@0.1.7", 675 | "resolved": "https://registry.npmjs.org/has-binary/-/has-binary-0.1.7.tgz" 676 | }, 677 | "has-cors": { 678 | "version": "1.1.0", 679 | "from": "has-cors@1.1.0", 680 | "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz" 681 | }, 682 | "hash.js": { 683 | "version": "1.0.3", 684 | "from": "hash.js@>=1.0.0 <2.0.0", 685 | "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.0.3.tgz" 686 | }, 687 | "hat": { 688 | "version": "0.0.3", 689 | "from": "hat@>=0.0.3 <0.0.4", 690 | "resolved": "https://registry.npmjs.org/hat/-/hat-0.0.3.tgz" 691 | }, 692 | "htmlescape": { 693 | "version": "1.1.1", 694 | "from": "htmlescape@>=1.1.0 <2.0.0", 695 | "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz" 696 | }, 697 | "htmlparser2": { 698 | "version": "3.8.3", 699 | "from": "htmlparser2@>=3.8.0 <3.9.0", 700 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", 701 | "dependencies": { 702 | "readable-stream": { 703 | "version": "1.1.13", 704 | "from": "readable-stream@>=1.1.0 <1.2.0", 705 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz" 706 | } 707 | } 708 | }, 709 | "http-errors": { 710 | "version": "1.4.0", 711 | "from": "http-errors@>=1.4.0 <1.5.0", 712 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.4.0.tgz" 713 | }, 714 | "http-proxy": { 715 | "version": "1.13.2", 716 | "from": "http-proxy@>=1.13.0 <2.0.0", 717 | "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.13.2.tgz" 718 | }, 719 | "https-browserify": { 720 | "version": "0.0.1", 721 | "from": "https-browserify@>=0.0.0 <0.1.0", 722 | "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz" 723 | }, 724 | "iconv-lite": { 725 | "version": "0.4.13", 726 | "from": "iconv-lite@0.4.13", 727 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz" 728 | }, 729 | "ieee754": { 730 | "version": "1.1.6", 731 | "from": "ieee754@>=1.1.4 <2.0.0", 732 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.6.tgz" 733 | }, 734 | "indexof": { 735 | "version": "0.0.1", 736 | "from": "indexof@0.0.1", 737 | "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz" 738 | }, 739 | "inflight": { 740 | "version": "1.0.4", 741 | "from": "inflight@>=1.0.4 <2.0.0", 742 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz" 743 | }, 744 | "inherits": { 745 | "version": "2.0.1", 746 | "from": "inherits@>=2.0.1 <2.1.0", 747 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" 748 | }, 749 | "inline-source-map": { 750 | "version": "0.6.1", 751 | "from": "inline-source-map@>=0.6.0 <0.7.0", 752 | "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.1.tgz" 753 | }, 754 | "insert-module-globals": { 755 | "version": "7.0.1", 756 | "from": "insert-module-globals@>=7.0.0 <8.0.0", 757 | "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.0.1.tgz" 758 | }, 759 | "is-absolute": { 760 | "version": "0.1.7", 761 | "from": "is-absolute@>=0.1.7 <0.2.0", 762 | "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.1.7.tgz" 763 | }, 764 | "is-binary-path": { 765 | "version": "1.0.1", 766 | "from": "is-binary-path@>=1.0.0 <2.0.0", 767 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz" 768 | }, 769 | "is-buffer": { 770 | "version": "1.1.3", 771 | "from": "is-buffer@>=1.1.0 <2.0.0", 772 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.3.tgz" 773 | }, 774 | "is-dotfile": { 775 | "version": "1.0.2", 776 | "from": "is-dotfile@>=1.0.0 <2.0.0", 777 | "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.2.tgz" 778 | }, 779 | "is-equal-shallow": { 780 | "version": "0.1.3", 781 | "from": "is-equal-shallow@>=0.1.1 <0.2.0", 782 | "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz" 783 | }, 784 | "is-extendable": { 785 | "version": "0.1.1", 786 | "from": "is-extendable@>=0.1.1 <0.2.0", 787 | "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz" 788 | }, 789 | "is-extglob": { 790 | "version": "1.0.0", 791 | "from": "is-extglob@>=1.0.0 <2.0.0", 792 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz" 793 | }, 794 | "is-glob": { 795 | "version": "2.0.1", 796 | "from": "is-glob@>=2.0.0 <3.0.0", 797 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz" 798 | }, 799 | "is-number": { 800 | "version": "2.1.0", 801 | "from": "is-number@>=2.1.0 <3.0.0", 802 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz" 803 | }, 804 | "is-primitive": { 805 | "version": "2.0.0", 806 | "from": "is-primitive@>=2.0.0 <3.0.0", 807 | "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz" 808 | }, 809 | "is-relative": { 810 | "version": "0.1.3", 811 | "from": "is-relative@>=0.1.0 <0.2.0", 812 | "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.1.3.tgz" 813 | }, 814 | "isarray": { 815 | "version": "0.0.1", 816 | "from": "isarray@0.0.1", 817 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" 818 | }, 819 | "isbinaryfile": { 820 | "version": "3.0.0", 821 | "from": "isbinaryfile@>=3.0.0 <4.0.0", 822 | "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.0.tgz" 823 | }, 824 | "isexe": { 825 | "version": "1.1.2", 826 | "from": "isexe@>=1.1.1 <2.0.0", 827 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-1.1.2.tgz" 828 | }, 829 | "isobject": { 830 | "version": "2.0.0", 831 | "from": "isobject@>=2.0.0 <3.0.0", 832 | "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.0.0.tgz" 833 | }, 834 | "jade": { 835 | "version": "0.26.3", 836 | "from": "jade@0.26.3", 837 | "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", 838 | "dependencies": { 839 | "commander": { 840 | "version": "0.6.1", 841 | "from": "commander@0.6.1", 842 | "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz" 843 | }, 844 | "mkdirp": { 845 | "version": "0.3.0", 846 | "from": "mkdirp@0.3.0", 847 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz" 848 | } 849 | } 850 | }, 851 | "js-string-escape": { 852 | "version": "1.0.1", 853 | "from": "js-string-escape@>=1.0.0 <2.0.0", 854 | "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz" 855 | }, 856 | "json-stable-stringify": { 857 | "version": "0.0.1", 858 | "from": "json-stable-stringify@>=0.0.0 <0.1.0", 859 | "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz" 860 | }, 861 | "json3": { 862 | "version": "3.2.6", 863 | "from": "json3@3.2.6", 864 | "resolved": "https://registry.npmjs.org/json3/-/json3-3.2.6.tgz" 865 | }, 866 | "jsonify": { 867 | "version": "0.0.0", 868 | "from": "jsonify@>=0.0.0 <0.1.0", 869 | "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" 870 | }, 871 | "jsonparse": { 872 | "version": "1.2.0", 873 | "from": "jsonparse@>=1.1.0 <2.0.0", 874 | "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.2.0.tgz" 875 | }, 876 | "JSONStream": { 877 | "version": "1.1.1", 878 | "from": "JSONStream@>=1.0.3 <2.0.0", 879 | "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.1.1.tgz" 880 | }, 881 | "key-path": { 882 | "version": "0.3.2", 883 | "from": "key-path@>=0.3.2 <0.4.0", 884 | "resolved": "https://registry.npmjs.org/key-path/-/key-path-0.3.2.tgz" 885 | }, 886 | "kind-of": { 887 | "version": "3.0.2", 888 | "from": "kind-of@>=3.0.2 <4.0.0", 889 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.0.2.tgz" 890 | }, 891 | "labeled-stream-splicer": { 892 | "version": "2.0.0", 893 | "from": "labeled-stream-splicer@>=2.0.0 <3.0.0", 894 | "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.0.tgz" 895 | }, 896 | "lexical-scope": { 897 | "version": "1.2.0", 898 | "from": "lexical-scope@>=1.2.0 <2.0.0", 899 | "resolved": "https://registry.npmjs.org/lexical-scope/-/lexical-scope-1.2.0.tgz" 900 | }, 901 | "lodash": { 902 | "version": "3.7.0", 903 | "from": "lodash@>=3.7.0 <3.8.0", 904 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.7.0.tgz" 905 | }, 906 | "lodash._basefor": { 907 | "version": "3.0.3", 908 | "from": "lodash._basefor@>=3.0.0 <4.0.0", 909 | "resolved": "https://registry.npmjs.org/lodash._basefor/-/lodash._basefor-3.0.3.tgz" 910 | }, 911 | "lodash.forown": { 912 | "version": "4.0.2", 913 | "from": "lodash.forown@4.0.2", 914 | "resolved": "https://registry.npmjs.org/lodash.forown/-/lodash.forown-4.0.2.tgz" 915 | }, 916 | "lodash.keys": { 917 | "version": "4.0.5", 918 | "from": "lodash.keys@>=4.0.0 <5.0.0", 919 | "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-4.0.5.tgz" 920 | }, 921 | "lodash.memoize": { 922 | "version": "3.0.4", 923 | "from": "lodash.memoize@>=3.0.3 <3.1.0", 924 | "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz" 925 | }, 926 | "log4js": { 927 | "version": "0.6.33", 928 | "from": "log4js@>=0.6.31 <0.7.0", 929 | "resolved": "https://registry.npmjs.org/log4js/-/log4js-0.6.33.tgz", 930 | "dependencies": { 931 | "readable-stream": { 932 | "version": "1.0.33", 933 | "from": "readable-stream@>=1.0.2 <1.1.0", 934 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz" 935 | } 936 | } 937 | }, 938 | "lolex": { 939 | "version": "1.3.2", 940 | "from": "lolex@1.3.2", 941 | "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.3.2.tgz" 942 | }, 943 | "lru-cache": { 944 | "version": "2.7.3", 945 | "from": "lru-cache@>=2.0.0 <3.0.0", 946 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz" 947 | }, 948 | "media-typer": { 949 | "version": "0.3.0", 950 | "from": "media-typer@0.3.0", 951 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" 952 | }, 953 | "micromatch": { 954 | "version": "2.3.7", 955 | "from": "micromatch@>=2.1.5 <3.0.0", 956 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.7.tgz" 957 | }, 958 | "miller-rabin": { 959 | "version": "4.0.0", 960 | "from": "miller-rabin@>=4.0.0 <5.0.0", 961 | "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.0.tgz" 962 | }, 963 | "mime": { 964 | "version": "1.3.4", 965 | "from": "mime@>=1.3.4 <2.0.0", 966 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz" 967 | }, 968 | "mime-db": { 969 | "version": "1.22.0", 970 | "from": "mime-db@>=1.22.0 <1.23.0", 971 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.22.0.tgz" 972 | }, 973 | "mime-types": { 974 | "version": "2.1.10", 975 | "from": "mime-types@>=2.1.10 <2.2.0", 976 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.10.tgz" 977 | }, 978 | "minimalistic-assert": { 979 | "version": "1.0.0", 980 | "from": "minimalistic-assert@>=1.0.0 <2.0.0", 981 | "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz" 982 | }, 983 | "minimatch": { 984 | "version": "3.0.0", 985 | "from": "minimatch@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0", 986 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.0.tgz" 987 | }, 988 | "minimist": { 989 | "version": "1.2.0", 990 | "from": "minimist@>=1.1.0 <2.0.0", 991 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" 992 | }, 993 | "mkdirp": { 994 | "version": "0.5.1", 995 | "from": "mkdirp@0.5.1", 996 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 997 | "dependencies": { 998 | "minimist": { 999 | "version": "0.0.8", 1000 | "from": "minimist@0.0.8", 1001 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" 1002 | } 1003 | } 1004 | }, 1005 | "module-deps": { 1006 | "version": "4.0.5", 1007 | "from": "module-deps@>=4.0.2 <5.0.0", 1008 | "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-4.0.5.tgz" 1009 | }, 1010 | "ms": { 1011 | "version": "0.7.1", 1012 | "from": "ms@0.7.1", 1013 | "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" 1014 | }, 1015 | "negotiator": { 1016 | "version": "0.4.9", 1017 | "from": "negotiator@0.4.9", 1018 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.4.9.tgz" 1019 | }, 1020 | "normalize-path": { 1021 | "version": "2.0.1", 1022 | "from": "normalize-path@>=2.0.1 <3.0.0", 1023 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.0.1.tgz" 1024 | }, 1025 | "null-check": { 1026 | "version": "1.0.0", 1027 | "from": "null-check@>=1.0.0 <2.0.0", 1028 | "resolved": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz" 1029 | }, 1030 | "object-component": { 1031 | "version": "0.0.3", 1032 | "from": "object-component@0.0.3", 1033 | "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz" 1034 | }, 1035 | "object.omit": { 1036 | "version": "2.0.0", 1037 | "from": "object.omit@>=2.0.0 <3.0.0", 1038 | "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.0.tgz" 1039 | }, 1040 | "on-finished": { 1041 | "version": "2.3.0", 1042 | "from": "on-finished@>=2.3.0 <2.4.0", 1043 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz" 1044 | }, 1045 | "once": { 1046 | "version": "1.3.3", 1047 | "from": "once@>=1.3.0 <2.0.0", 1048 | "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz" 1049 | }, 1050 | "optimist": { 1051 | "version": "0.6.1", 1052 | "from": "optimist@>=0.6.1 <0.7.0", 1053 | "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", 1054 | "dependencies": { 1055 | "minimist": { 1056 | "version": "0.0.10", 1057 | "from": "minimist@>=0.0.1 <0.1.0", 1058 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz" 1059 | } 1060 | } 1061 | }, 1062 | "options": { 1063 | "version": "0.0.6", 1064 | "from": "options@>=0.0.5", 1065 | "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz" 1066 | }, 1067 | "os-browserify": { 1068 | "version": "0.1.2", 1069 | "from": "os-browserify@>=0.1.1 <0.2.0", 1070 | "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.1.2.tgz" 1071 | }, 1072 | "os-shim": { 1073 | "version": "0.1.3", 1074 | "from": "os-shim@>=0.1.3 <0.2.0", 1075 | "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz" 1076 | }, 1077 | "pako": { 1078 | "version": "0.2.8", 1079 | "from": "pako@>=0.2.0 <0.3.0", 1080 | "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.8.tgz" 1081 | }, 1082 | "parents": { 1083 | "version": "1.0.1", 1084 | "from": "parents@>=1.0.1 <2.0.0", 1085 | "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz" 1086 | }, 1087 | "parse-asn1": { 1088 | "version": "5.0.0", 1089 | "from": "parse-asn1@>=5.0.0 <6.0.0", 1090 | "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.0.0.tgz" 1091 | }, 1092 | "parse-glob": { 1093 | "version": "3.0.4", 1094 | "from": "parse-glob@>=3.0.4 <4.0.0", 1095 | "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz" 1096 | }, 1097 | "parsejson": { 1098 | "version": "0.0.1", 1099 | "from": "parsejson@0.0.1", 1100 | "resolved": "https://registry.npmjs.org/parsejson/-/parsejson-0.0.1.tgz" 1101 | }, 1102 | "parseqs": { 1103 | "version": "0.0.2", 1104 | "from": "parseqs@0.0.2", 1105 | "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.2.tgz" 1106 | }, 1107 | "parseuri": { 1108 | "version": "0.0.4", 1109 | "from": "parseuri@0.0.4", 1110 | "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.4.tgz" 1111 | }, 1112 | "parseurl": { 1113 | "version": "1.3.1", 1114 | "from": "parseurl@>=1.3.1 <1.4.0", 1115 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz" 1116 | }, 1117 | "path-browserify": { 1118 | "version": "0.0.0", 1119 | "from": "path-browserify@>=0.0.0 <0.1.0", 1120 | "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz" 1121 | }, 1122 | "path-is-absolute": { 1123 | "version": "1.0.0", 1124 | "from": "path-is-absolute@>=1.0.0 <2.0.0", 1125 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" 1126 | }, 1127 | "path-platform": { 1128 | "version": "0.11.15", 1129 | "from": "path-platform@>=0.11.15 <0.12.0", 1130 | "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz" 1131 | }, 1132 | "pbkdf2": { 1133 | "version": "3.0.4", 1134 | "from": "pbkdf2@>=3.0.3 <4.0.0", 1135 | "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.4.tgz" 1136 | }, 1137 | "preserve": { 1138 | "version": "0.2.0", 1139 | "from": "preserve@>=0.2.0 <0.3.0", 1140 | "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz" 1141 | }, 1142 | "process": { 1143 | "version": "0.11.2", 1144 | "from": "process@>=0.11.0 <0.12.0", 1145 | "resolved": "https://registry.npmjs.org/process/-/process-0.11.2.tgz" 1146 | }, 1147 | "process-nextick-args": { 1148 | "version": "1.0.6", 1149 | "from": "process-nextick-args@>=1.0.6 <1.1.0", 1150 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.6.tgz" 1151 | }, 1152 | "public-encrypt": { 1153 | "version": "4.0.0", 1154 | "from": "public-encrypt@>=4.0.0 <5.0.0", 1155 | "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz" 1156 | }, 1157 | "punycode": { 1158 | "version": "1.4.1", 1159 | "from": "punycode@>=1.3.2 <2.0.0", 1160 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" 1161 | }, 1162 | "qs": { 1163 | "version": "6.1.0", 1164 | "from": "qs@6.1.0", 1165 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.1.0.tgz" 1166 | }, 1167 | "querystring": { 1168 | "version": "0.2.0", 1169 | "from": "querystring@0.2.0", 1170 | "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz" 1171 | }, 1172 | "querystring-es3": { 1173 | "version": "0.2.1", 1174 | "from": "querystring-es3@>=0.2.0 <0.3.0", 1175 | "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz" 1176 | }, 1177 | "randomatic": { 1178 | "version": "1.1.5", 1179 | "from": "randomatic@>=1.1.3 <2.0.0", 1180 | "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.5.tgz" 1181 | }, 1182 | "randombytes": { 1183 | "version": "2.0.3", 1184 | "from": "randombytes@>=2.0.0 <3.0.0", 1185 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.3.tgz" 1186 | }, 1187 | "raw-body": { 1188 | "version": "2.1.6", 1189 | "from": "raw-body@>=2.1.5 <2.2.0", 1190 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.1.6.tgz", 1191 | "dependencies": { 1192 | "bytes": { 1193 | "version": "2.3.0", 1194 | "from": "bytes@2.3.0", 1195 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.3.0.tgz" 1196 | } 1197 | } 1198 | }, 1199 | "read-only-stream": { 1200 | "version": "2.0.0", 1201 | "from": "read-only-stream@>=2.0.0 <3.0.0", 1202 | "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz" 1203 | }, 1204 | "readable-stream": { 1205 | "version": "2.0.6", 1206 | "from": "readable-stream@>=2.0.2 <3.0.0", 1207 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", 1208 | "dependencies": { 1209 | "isarray": { 1210 | "version": "1.0.0", 1211 | "from": "isarray@>=1.0.0 <1.1.0", 1212 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" 1213 | } 1214 | } 1215 | }, 1216 | "readdirp": { 1217 | "version": "2.0.0", 1218 | "from": "readdirp@>=2.0.0 <3.0.0", 1219 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.0.0.tgz", 1220 | "dependencies": { 1221 | "minimatch": { 1222 | "version": "2.0.10", 1223 | "from": "minimatch@>=2.0.10 <3.0.0", 1224 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz" 1225 | } 1226 | } 1227 | }, 1228 | "reflect-metadata": { 1229 | "version": "0.1.3", 1230 | "from": "reflect-metadata@latest", 1231 | "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.3.tgz" 1232 | }, 1233 | "regex-cache": { 1234 | "version": "0.4.2", 1235 | "from": "regex-cache@>=0.4.2 <0.5.0", 1236 | "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.2.tgz" 1237 | }, 1238 | "repeat-element": { 1239 | "version": "1.1.2", 1240 | "from": "repeat-element@>=1.1.2 <2.0.0", 1241 | "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz" 1242 | }, 1243 | "repeat-string": { 1244 | "version": "1.5.4", 1245 | "from": "repeat-string@>=1.5.2 <2.0.0", 1246 | "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.5.4.tgz" 1247 | }, 1248 | "requires-port": { 1249 | "version": "1.0.0", 1250 | "from": "requires-port@>=1.0.0 <2.0.0", 1251 | "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" 1252 | }, 1253 | "resolve": { 1254 | "version": "1.1.7", 1255 | "from": "resolve@>=1.1.4 <2.0.0", 1256 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz" 1257 | }, 1258 | "rimraf": { 1259 | "version": "2.5.2", 1260 | "from": "rimraf@>=2.3.3 <3.0.0", 1261 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.2.tgz", 1262 | "dependencies": { 1263 | "glob": { 1264 | "version": "7.0.3", 1265 | "from": "glob@>=7.0.0 <8.0.0", 1266 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.3.tgz" 1267 | } 1268 | } 1269 | }, 1270 | "ripemd160": { 1271 | "version": "1.0.1", 1272 | "from": "ripemd160@>=1.0.0 <2.0.0", 1273 | "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-1.0.1.tgz" 1274 | }, 1275 | "samsam": { 1276 | "version": "1.1.2", 1277 | "from": "samsam@1.1.2", 1278 | "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.2.tgz" 1279 | }, 1280 | "semver": { 1281 | "version": "4.3.6", 1282 | "from": "semver@>=4.3.3 <4.4.0", 1283 | "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz" 1284 | }, 1285 | "sha.js": { 1286 | "version": "2.4.5", 1287 | "from": "sha.js@>=2.3.6 <3.0.0", 1288 | "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.5.tgz" 1289 | }, 1290 | "shasum": { 1291 | "version": "1.0.2", 1292 | "from": "shasum@>=1.0.0 <2.0.0", 1293 | "resolved": "https://registry.npmjs.org/shasum/-/shasum-1.0.2.tgz" 1294 | }, 1295 | "shell-quote": { 1296 | "version": "1.5.0", 1297 | "from": "shell-quote@>=1.4.3 <2.0.0", 1298 | "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.5.0.tgz" 1299 | }, 1300 | "shelljs": { 1301 | "version": "0.3.0", 1302 | "from": "shelljs@>=0.3.0 <0.4.0", 1303 | "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz" 1304 | }, 1305 | "sigmund": { 1306 | "version": "1.0.1", 1307 | "from": "sigmund@>=1.0.0 <1.1.0", 1308 | "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" 1309 | }, 1310 | "socket.io": { 1311 | "version": "1.4.5", 1312 | "from": "socket.io@>=1.4.5 <2.0.0", 1313 | "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-1.4.5.tgz" 1314 | }, 1315 | "socket.io-adapter": { 1316 | "version": "0.4.0", 1317 | "from": "socket.io-adapter@0.4.0", 1318 | "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-0.4.0.tgz", 1319 | "dependencies": { 1320 | "socket.io-parser": { 1321 | "version": "2.2.2", 1322 | "from": "socket.io-parser@2.2.2", 1323 | "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-2.2.2.tgz", 1324 | "dependencies": { 1325 | "debug": { 1326 | "version": "0.7.4", 1327 | "from": "debug@0.7.4", 1328 | "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz" 1329 | } 1330 | } 1331 | } 1332 | } 1333 | }, 1334 | "socket.io-client": { 1335 | "version": "1.4.5", 1336 | "from": "socket.io-client@1.4.5", 1337 | "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-1.4.5.tgz", 1338 | "dependencies": { 1339 | "component-emitter": { 1340 | "version": "1.2.0", 1341 | "from": "component-emitter@1.2.0", 1342 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.0.tgz" 1343 | } 1344 | } 1345 | }, 1346 | "socket.io-parser": { 1347 | "version": "2.2.6", 1348 | "from": "socket.io-parser@2.2.6", 1349 | "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-2.2.6.tgz", 1350 | "dependencies": { 1351 | "json3": { 1352 | "version": "3.3.2", 1353 | "from": "json3@3.3.2", 1354 | "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz" 1355 | } 1356 | } 1357 | }, 1358 | "source-map": { 1359 | "version": "0.4.2", 1360 | "from": "source-map@0.4.2", 1361 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.2.tgz" 1362 | }, 1363 | "statuses": { 1364 | "version": "1.2.1", 1365 | "from": "statuses@>=1.2.1 <2.0.0", 1366 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.2.1.tgz" 1367 | }, 1368 | "stream-browserify": { 1369 | "version": "2.0.1", 1370 | "from": "stream-browserify@>=2.0.0 <3.0.0", 1371 | "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz" 1372 | }, 1373 | "stream-combiner2": { 1374 | "version": "1.1.1", 1375 | "from": "stream-combiner2@>=1.1.1 <2.0.0", 1376 | "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz" 1377 | }, 1378 | "stream-http": { 1379 | "version": "2.2.1", 1380 | "from": "stream-http@>=2.0.0 <3.0.0", 1381 | "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.2.1.tgz" 1382 | }, 1383 | "stream-splicer": { 1384 | "version": "2.0.0", 1385 | "from": "stream-splicer@>=2.0.0 <3.0.0", 1386 | "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.0.tgz" 1387 | }, 1388 | "string_decoder": { 1389 | "version": "0.10.31", 1390 | "from": "string_decoder@>=0.10.0 <0.11.0", 1391 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" 1392 | }, 1393 | "strip-json-comments": { 1394 | "version": "1.0.4", 1395 | "from": "strip-json-comments@>=1.0.0 <1.1.0", 1396 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz" 1397 | }, 1398 | "subarg": { 1399 | "version": "1.0.0", 1400 | "from": "subarg@>=1.0.0 <2.0.0", 1401 | "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz" 1402 | }, 1403 | "supports-color": { 1404 | "version": "1.2.0", 1405 | "from": "supports-color@1.2.0", 1406 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-1.2.0.tgz" 1407 | }, 1408 | "syntax-error": { 1409 | "version": "1.1.5", 1410 | "from": "syntax-error@>=1.1.1 <2.0.0", 1411 | "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.1.5.tgz", 1412 | "dependencies": { 1413 | "acorn": { 1414 | "version": "2.7.0", 1415 | "from": "acorn@>=2.7.0 <3.0.0", 1416 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-2.7.0.tgz" 1417 | } 1418 | } 1419 | }, 1420 | "through": { 1421 | "version": "2.3.8", 1422 | "from": "through@>=2.2.7 <3.0.0", 1423 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" 1424 | }, 1425 | "through2": { 1426 | "version": "2.0.1", 1427 | "from": "through2@>=2.0.0 <3.0.0", 1428 | "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.1.tgz" 1429 | }, 1430 | "timers-browserify": { 1431 | "version": "1.4.2", 1432 | "from": "timers-browserify@>=1.0.1 <2.0.0", 1433 | "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz" 1434 | }, 1435 | "to-array": { 1436 | "version": "0.1.4", 1437 | "from": "to-array@0.1.4", 1438 | "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz" 1439 | }, 1440 | "to-arraybuffer": { 1441 | "version": "1.0.1", 1442 | "from": "to-arraybuffer@>=1.0.0 <2.0.0", 1443 | "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz" 1444 | }, 1445 | "tty-browserify": { 1446 | "version": "0.0.0", 1447 | "from": "tty-browserify@>=0.0.0 <0.1.0", 1448 | "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz" 1449 | }, 1450 | "type-is": { 1451 | "version": "1.6.12", 1452 | "from": "type-is@>=1.6.11 <1.7.0", 1453 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.12.tgz" 1454 | }, 1455 | "typedarray": { 1456 | "version": "0.0.6", 1457 | "from": "typedarray@>=0.0.5 <0.1.0", 1458 | "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" 1459 | }, 1460 | "ultron": { 1461 | "version": "1.0.2", 1462 | "from": "ultron@>=1.0.0 <1.1.0", 1463 | "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz" 1464 | }, 1465 | "umd": { 1466 | "version": "3.0.1", 1467 | "from": "umd@>=3.0.0 <4.0.0", 1468 | "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.1.tgz" 1469 | }, 1470 | "unpipe": { 1471 | "version": "1.0.0", 1472 | "from": "unpipe@1.0.0", 1473 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" 1474 | }, 1475 | "url": { 1476 | "version": "0.11.0", 1477 | "from": "url@>=0.11.0 <0.12.0", 1478 | "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", 1479 | "dependencies": { 1480 | "punycode": { 1481 | "version": "1.3.2", 1482 | "from": "punycode@1.3.2", 1483 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz" 1484 | } 1485 | } 1486 | }, 1487 | "useragent": { 1488 | "version": "2.1.9", 1489 | "from": "useragent@>=2.1.6 <3.0.0", 1490 | "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.1.9.tgz", 1491 | "dependencies": { 1492 | "lru-cache": { 1493 | "version": "2.2.4", 1494 | "from": "lru-cache@>=2.2.0 <2.3.0", 1495 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.2.4.tgz" 1496 | } 1497 | } 1498 | }, 1499 | "utf8": { 1500 | "version": "2.1.0", 1501 | "from": "utf8@2.1.0", 1502 | "resolved": "https://registry.npmjs.org/utf8/-/utf8-2.1.0.tgz" 1503 | }, 1504 | "util": { 1505 | "version": "0.10.3", 1506 | "from": "util@>=0.10.1 <0.11.0", 1507 | "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz" 1508 | }, 1509 | "util-deprecate": { 1510 | "version": "1.0.2", 1511 | "from": "util-deprecate@>=1.0.1 <1.1.0", 1512 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" 1513 | }, 1514 | "utils-merge": { 1515 | "version": "1.0.0", 1516 | "from": "utils-merge@1.0.0", 1517 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz" 1518 | }, 1519 | "vm-browserify": { 1520 | "version": "0.0.4", 1521 | "from": "vm-browserify@>=0.0.1 <0.1.0", 1522 | "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz" 1523 | }, 1524 | "void-elements": { 1525 | "version": "2.0.1", 1526 | "from": "void-elements@>=2.0.0 <3.0.0", 1527 | "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz" 1528 | }, 1529 | "which": { 1530 | "version": "1.2.4", 1531 | "from": "which@>=1.2.1 <2.0.0", 1532 | "resolved": "https://registry.npmjs.org/which/-/which-1.2.4.tgz" 1533 | }, 1534 | "wordwrap": { 1535 | "version": "0.0.3", 1536 | "from": "wordwrap@>=0.0.2 <0.1.0", 1537 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz" 1538 | }, 1539 | "wrappy": { 1540 | "version": "1.0.1", 1541 | "from": "wrappy@>=1.0.0 <2.0.0", 1542 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" 1543 | }, 1544 | "ws": { 1545 | "version": "1.0.1", 1546 | "from": "ws@1.0.1", 1547 | "resolved": "https://registry.npmjs.org/ws/-/ws-1.0.1.tgz" 1548 | }, 1549 | "xmlhttprequest-ssl": { 1550 | "version": "1.5.1", 1551 | "from": "xmlhttprequest-ssl@1.5.1", 1552 | "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.1.tgz" 1553 | }, 1554 | "xtend": { 1555 | "version": "4.0.1", 1556 | "from": "xtend@>=4.0.0 <5.0.0", 1557 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" 1558 | }, 1559 | "yeast": { 1560 | "version": "0.1.2", 1561 | "from": "yeast@0.1.2", 1562 | "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz" 1563 | } 1564 | } 1565 | } 1566 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "master-class", 3 | "version": "0.5.9", 4 | "description": "JavaScript classes with an edge.", 5 | "homepage": "https://github.com/CardForest/master-class", 6 | "author": { 7 | "name": "Amit Portnoy", 8 | "email": "amit.portnoy@gmail.com", 9 | "url": "https://github.com/amitport" 10 | }, 11 | "repository": "https://github.com/CardForest/master-class.git", 12 | "license": "AGPL-3.0", 13 | "files": [ 14 | "index.js", 15 | "lib" 16 | ], 17 | "keywords": [ 18 | "master-class", 19 | "object", 20 | "class", 21 | "model" 22 | ], 23 | "dependencies": { 24 | "key-path": "^0.3.2", 25 | "lodash.forown": "^4.4.0", 26 | "reflect-metadata": "^0.1.8" 27 | }, 28 | "devDependencies": { 29 | "browserify": "^13.1.1", 30 | "jshint": "^2.9.4", 31 | "karma": "^1.3.0", 32 | "karma-browserify": "^5.1.0", 33 | "karma-chrome-launcher": "^2.0.0", 34 | "karma-firefox-launcher": "^1.0.0", 35 | "karma-mocha": "^1.3.0", 36 | "mocha": "^3.2.0", 37 | "sinon": "^1.17.6", 38 | "watchify": "^3.8.0" 39 | }, 40 | "scripts": { 41 | "karma": "karma start --browsers Firefox", 42 | "mocha": "mocha test/**/*.js", 43 | "lint": "jshint .", 44 | "browserify": "browserify index.js > browser.js", 45 | "pretest": "npm run lint", 46 | "test": "npm run mocha & npm run karma" 47 | }, 48 | "engines": { 49 | "node": "^5.8.0" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /test/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.jshintrc", 3 | "mocha" : true 4 | } 5 | -------------------------------------------------------------------------------- /test/classFactories/array.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var M = require('../..'); 5 | 6 | describe('array', function () { 7 | it('allows primitive elements', function () { 8 | var arr = M.Array({ 9 | defaultLength: 4, 10 | elem: M.Number() 11 | }).createInstance(); 12 | 13 | assert.strictEqual(arr.length, 4); 14 | assert.strictEqual(arr[2], 0); 15 | 16 | arr[2] = 3; 17 | 18 | assert.strictEqual(arr[2], 3); 19 | }); 20 | 21 | it('allows object elements', function () { 22 | var arr = M.Array({ 23 | defaultLength: 4, 24 | elem: M.Object({ 25 | props: { 26 | n: M.Number(), 27 | s: M.String(), 28 | b: M.Boolean() 29 | } 30 | }) 31 | }).createInstance(); 32 | 33 | assert.strictEqual(arr.length, 4); 34 | assert.strictEqual(arr[0].n, 0); 35 | assert.strictEqual(arr[1].s, ''); 36 | assert.strictEqual(arr[3].b, false); 37 | 38 | arr[2].n = 3; 39 | assert.strictEqual(arr[2].n, 3); 40 | }); 41 | 42 | it('allows nested array', function () { 43 | var arr = M.Array({ 44 | defaultLength: 4, 45 | elem: M.Array({ 46 | defaultLength: 2, 47 | elem: M.Number() 48 | }) 49 | }).createInstance(); 50 | 51 | assert.strictEqual(arr.length, 4); 52 | assert.strictEqual(arr[2].length, 2); 53 | assert.strictEqual(arr[0][1], 0); 54 | 55 | arr[0][1] = 3; 56 | assert.strictEqual(arr[0][1], 3); 57 | }); 58 | 59 | it('can be set from a value on instance creation', function () { 60 | var arr = M.Array({ 61 | defaultLength: 2, 62 | elem: M.Array({ 63 | defaultLength: 2, 64 | elem: M.Number() 65 | }) 66 | }).createInstance([[1, 2], [3, 4]]); 67 | 68 | assert.deepEqual(arr, [[1, 2], [3, 4]]); 69 | }); 70 | 71 | it('can be set length on instance creation', function () { 72 | var arr = M.Array({ 73 | defaultLength: 2, 74 | elem: M.Array({ 75 | defaultLength: 2, 76 | elem: M.Number() 77 | }) 78 | }).createInstance({length: 1}); 79 | 80 | assert.deepEqual(arr, [[0, 0]]); 81 | }); 82 | 83 | it('properties are frozen', function () { 84 | var arr = M.Array({ 85 | defaultLength: 4, 86 | elem: M.Number() 87 | }).createInstance(); 88 | 89 | assert.throws(function() {arr.s = 'should throw';}); 90 | assert.throws(function() {delete arr[0];}); 91 | }); 92 | 93 | it('throws exception on assignment of incorrect primitive type', function () { 94 | var arr = M.Array({ 95 | defaultLength: 4, 96 | elem: M.Number() 97 | }).createInstance(); 98 | 99 | assert.throws(function() {arr[0] = 'should throw';}); 100 | }); 101 | }); 102 | -------------------------------------------------------------------------------- /test/classFactories/getter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var M = require('../..'); 5 | 6 | describe('getter', function () { 7 | it('can access it\'s owner object', function () { 8 | var o = M.Object({ 9 | props: { 10 | n: M.Number(), 11 | g: M.Getter(function() { 12 | return this.n + 1; 13 | }) 14 | } 15 | }).createInstance(); 16 | 17 | assert.strictEqual(o.g, 1); 18 | }); 19 | 20 | 21 | it('can access other getters in it\'s owner object', function () { 22 | var o = M.Object({ 23 | props: { 24 | n: M.Number(), 25 | g1: M.Getter(function() { 26 | return this.n + 1; 27 | }), 28 | g2: M.Getter(function() { 29 | return this.g1 + 1; 30 | }) 31 | } 32 | }).createInstance(); 33 | 34 | assert.strictEqual(o.g2, 2); 35 | }); 36 | 37 | 38 | it('works in inner objects', function () { 39 | var o = M.Object({ 40 | props: { 41 | o: M.Object({ 42 | props: { 43 | n: M.Number(), 44 | g: M.Getter(function() { 45 | return this.n + 1; 46 | }) 47 | } 48 | }) 49 | } 50 | }).createInstance(); 51 | 52 | assert.strictEqual(o.o.g, 1); 53 | }); 54 | 55 | it('disallows change of owner', function () { 56 | var o = M.Object({ 57 | props: { 58 | n: M.Number(), 59 | g: M.Getter(function() { 60 | return this.n++; 61 | }) 62 | } 63 | }).createInstance(); 64 | 65 | assert.throws(function() {o.g;}); // jshint ignore:line 66 | }); 67 | 68 | it('disallows overriding', function () { 69 | var o = M.Object({ 70 | props: { 71 | n: M.Number(), 72 | g: M.Getter(function() { 73 | return this.n; 74 | }) 75 | } 76 | }).createInstance(); 77 | 78 | assert.throws(function() {o.g = function () {return 3;};}); 79 | }); 80 | 81 | it('can access keyPath attribute', function () { 82 | var o = M.Object({ 83 | props: { 84 | arr: M.Array({ 85 | defaultLength: 4, 86 | elem: M.Object({ 87 | props: { 88 | idx: M.Getter(function() { 89 | return this.keyPath[1]; 90 | }) 91 | } 92 | }) 93 | }) 94 | } 95 | }).createInstance(); 96 | 97 | assert.strictEqual(o.arr[2].idx, 2); 98 | }); 99 | }); 100 | -------------------------------------------------------------------------------- /test/classFactories/mutator.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var M = require('../..'); 5 | 6 | describe('mutator', function () { 7 | it('can receive parameter, read and change it\'s owner object', function () { 8 | var o = M.Object({ 9 | props: { 10 | n: M.Number(), 11 | m1: M.Mutator(function(x) { 12 | this.n = this.n + x; 13 | }), 14 | m2: M.Mutator(function() { 15 | this.n = this.n - 2; 16 | }) 17 | } 18 | }).createInstance(); 19 | 20 | o.m1(3); 21 | assert.strictEqual(o.n, 3); 22 | 23 | o.m2(); 24 | assert.strictEqual(o.n, 1); 25 | }); 26 | 27 | it('can be guarded by a function', function () { 28 | var o = M({ 29 | props: { 30 | n: M.Number({initialValue: 5}), 31 | isNLessThan10: M.Getter(function () {return this.n < 10;}), 32 | m: M.Mutator({ 33 | guard: function () {return this.isNLessThan10;}, 34 | fn: function(x){ 35 | this.n = this.n + x; 36 | } 37 | }) 38 | } 39 | }).createInstance(); 40 | 41 | o.m(3); 42 | assert.strictEqual(o.n, 8); 43 | 44 | o.m(3); 45 | assert.strictEqual(o.n, 11); 46 | 47 | assert.throws(function () { 48 | o.m(3); 49 | }); 50 | }); 51 | 52 | it('guard cannot make changes', function () { 53 | var o = M({ 54 | props: { 55 | n: M.Number({initialValue: 5}), 56 | m: M.Mutator({ 57 | guard: function () {return this.n++;}, 58 | fn: function(x){ 59 | this.n = this.n + x; 60 | } 61 | }) 62 | } 63 | }).createInstance(); 64 | 65 | assert.throws(function () { 66 | o.m(3); 67 | }); 68 | }); 69 | 70 | it('guards can be accessed through mutators', function () { 71 | var o = M({ 72 | props: { 73 | n: M.Number({initialValue: 5}), 74 | m: M.Mutator({ 75 | guard: function () {return this.n === 5;}, 76 | fn: function(x){ 77 | this.n = this.n + x; 78 | } 79 | }) 80 | } 81 | }).createInstance(); 82 | 83 | assert.strictEqual(o.m.guard, true); 84 | o.n = 4; 85 | assert.strictEqual(o.m.guard, false); 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /test/classFactories/object.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var M = require('../..'); 5 | 6 | describe('object', function () { 7 | it('allows primitive properties', function () { 8 | var o = M.Object({ 9 | props: { 10 | n: M.Number(), 11 | s: M.String(), 12 | b: M.Boolean() 13 | } 14 | }).createInstance(); 15 | 16 | assert.strictEqual(o.n, 0); 17 | assert.strictEqual(o.s, ''); 18 | assert.strictEqual(o.b, false); 19 | 20 | o.n = 3; 21 | o.s = 'test'; 22 | o.b = true; 23 | 24 | assert.strictEqual(o.n, 3); 25 | assert.strictEqual(o.s, 'test'); 26 | assert.strictEqual(o.b, true); 27 | }); 28 | 29 | 30 | it('allows nested objects', function () { 31 | var o = M.Object({ 32 | props: { 33 | o: M.Object({ 34 | props: { 35 | n: M.Number() 36 | } 37 | }) 38 | } 39 | }).createInstance(); 40 | 41 | assert(o.hasOwnProperty('o')); 42 | assert.strictEqual(o.o.n, 0); 43 | 44 | o.o.n = 3; 45 | assert.strictEqual(o.o.n, 3); 46 | }); 47 | 48 | it('can be set from a value on instance creation', function () { 49 | var o = M.Object({ 50 | props: { 51 | o: M.Object({ 52 | props: { 53 | n: M.Number(), 54 | notSet: M.Number({initialValue: 2}) 55 | } 56 | }), 57 | n: M.Number({initialValue: 5}) 58 | } 59 | }).createInstance({o: {n: 3}, n: 4}); 60 | 61 | assert.strictEqual(o.n, 4); 62 | assert.strictEqual(o.o.n, 3); 63 | assert.strictEqual(o.o.notSet, 2); 64 | }); 65 | 66 | it('properties are frozen', function () { 67 | var o = M.Object({ 68 | props: { 69 | n: M.Number() 70 | } 71 | }).createInstance(); 72 | 73 | assert.throws(function() {o.s = 'should throw';}); 74 | assert.throws(function() {delete o.n;}); 75 | }); 76 | 77 | it('allows assignment of nested objects', function () { 78 | var o = M.Object({ 79 | props: { 80 | o: M.Object({ 81 | props: { 82 | n: M.Number() 83 | } 84 | }) 85 | } 86 | }).createInstance(); 87 | 88 | o.o = {n: 3}; 89 | 90 | assert.deepEqual(o, {o: {n: 3}}); 91 | }); 92 | 93 | it('throws exception on assignment of incorrect primitive type', function () { 94 | var o = M.Object({ 95 | props: { 96 | n: M.Number() 97 | } 98 | }).createInstance(); 99 | 100 | assert.throws(function() {o.n = 'should throw';}); 101 | }); 102 | }); 103 | -------------------------------------------------------------------------------- /test/classFactories/primitives.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | 5 | var M = require('../..'); 6 | 7 | describe('primitives', function () { 8 | it('have default values', function () { 9 | var n = M.Number().createInstance(); 10 | assert.strictEqual(n, 0); 11 | 12 | var s = M.String().createInstance(); 13 | assert.strictEqual(s, ''); 14 | 15 | var b = M.Boolean().createInstance(); 16 | assert.strictEqual(b, false); 17 | }); 18 | 19 | it('accept initial values', function () { 20 | var n = M.Number({initialValue: 3}).createInstance(); 21 | assert.strictEqual(n, 3); 22 | 23 | var s = M.String({initialValue: 'test'}).createInstance(); 24 | assert.strictEqual(s, 'test'); 25 | 26 | var b = M.Boolean({initialValue: true}).createInstance(); 27 | assert.strictEqual(b, true); 28 | }); 29 | 30 | it('can override value on instance creation', function () { 31 | var n = M.Number({initialValue: 3}).createInstance(4); 32 | assert.strictEqual(n, 4); 33 | 34 | var s = M.String({initialValue: 'test'}).createInstance('test2'); 35 | assert.strictEqual(s, 'test2'); 36 | 37 | var b = M.Boolean({initialValue: true}).createInstance(false); 38 | assert.strictEqual(b, false); 39 | }); 40 | 41 | it('throws exception when setting the wrong type on initial value or instance creation', function () { 42 | assert.throws(function () { 43 | M.Number({initialValue: ''}); 44 | }); 45 | 46 | assert.throws(function () { 47 | M.String({initialValue: 3}); 48 | }); 49 | 50 | assert.throws(function () { 51 | M.Boolean({initialValue: 3}); 52 | }); 53 | 54 | assert.throws(function () { 55 | M.Number().createInstance(''); 56 | }); 57 | 58 | assert.throws(function () { 59 | M.String().createInstance(3); 60 | }); 61 | 62 | assert.throws(function () { 63 | M.Boolean().createInstance(3); 64 | }); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /test/classFactories/ref.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var M = require('../..'); 5 | 6 | describe('ref', function () { 7 | it('links with the referred object', function () { 8 | var opt = { 9 | props: { 10 | r: M.Ref(), 11 | o: M.Object({ 12 | props: { 13 | n: M.Number() 14 | } 15 | }) 16 | } 17 | }; 18 | 19 | var o = M(opt).createInstance(); 20 | 21 | assert.strictEqual(o.r, null); 22 | o.r = o.o; 23 | 24 | o.o.n = 3; 25 | assert.strictEqual(o.r.n, 3); 26 | 27 | o.r.n = 2; 28 | assert.strictEqual(o.o.n, 2); 29 | 30 | assert.strictEqual(o.r, o.o); 31 | }); 32 | 33 | it('links work transitively', function () { 34 | var opt = { 35 | props: { 36 | r1: M.Ref(), 37 | r2: M.Ref(), 38 | o: M.Object({ 39 | props: { 40 | n: M.Number() 41 | } 42 | }) 43 | } 44 | }; 45 | 46 | var o = M(opt).createInstance(); 47 | 48 | o.r2 = o.r1 = o.o; 49 | 50 | o.o.n = 3; 51 | assert.strictEqual(o.r1.n, 3); 52 | assert.strictEqual(o.r2.n, 3); 53 | 54 | o.r1.n = 2; 55 | assert.strictEqual(o.o.n, 2); 56 | assert.strictEqual(o.r2.n, 2); 57 | 58 | o.r2.n = 1; 59 | assert.strictEqual(o.o.n, 1); 60 | assert.strictEqual(o.r1.n, 1); 61 | 62 | assert.strictEqual(o.o, o.r1); 63 | assert.strictEqual(o.r1, o.r2); 64 | }); 65 | 66 | it('links survive snapshots', function () { 67 | var opt = { 68 | props: { 69 | r: M.Ref(), 70 | o: M.Object({ 71 | props: { 72 | n: M.Number() 73 | } 74 | }) 75 | } 76 | }; 77 | 78 | var origin = M(opt).createInstance(); 79 | origin.r = origin.o; 80 | origin.o.n = 4; 81 | 82 | var o = M(opt).createInstance(origin.snapshot()); 83 | 84 | assert.deepEqual(o, origin); 85 | 86 | assert.strictEqual(o.r.n, 4); 87 | 88 | o.r.n = 5; 89 | assert.strictEqual(o.o.n, 5); 90 | 91 | assert.strictEqual(o.r, o.o); 92 | }); 93 | 94 | it('transitive links survive snapshots ', function () { 95 | var opt = { 96 | props: { 97 | r1: M.Ref(), 98 | r2: M.Ref(), 99 | o: M.Object({ 100 | props: { 101 | n: M.Number() 102 | } 103 | }) 104 | } 105 | }; 106 | 107 | var origin = M(opt).createInstance(); 108 | origin.r2 = origin.r1 = origin.o; 109 | origin.o.n = 3; 110 | 111 | var o = M(opt).createInstance(origin.snapshot()); 112 | 113 | assert.deepEqual(o, origin); 114 | 115 | assert.strictEqual(o.r1.n, 3); 116 | assert.strictEqual(o.r2.n, 3); 117 | 118 | o.r1.n = 2; 119 | assert.strictEqual(o.o.n, 2); 120 | assert.strictEqual(o.r2.n, 2); 121 | 122 | o.r2.n = 1; 123 | assert.strictEqual(o.o.n, 1); 124 | assert.strictEqual(o.r1.n, 1); 125 | 126 | assert.strictEqual(o.o, o.r1); 127 | assert.strictEqual(o.r1, o.r2); 128 | }); 129 | }); 130 | -------------------------------------------------------------------------------- /test/classFactories/state.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var M = require('../..'); 5 | 6 | describe('state', function () { 7 | it('passes through a single object', function () { 8 | var o = M({ 9 | props: { 10 | state: M.State([ 11 | { 12 | delegate: M.Object({ 13 | props: { 14 | n: M.Number({initialValue: 3}) 15 | } 16 | }) 17 | } 18 | ]) 19 | } 20 | }).createInstance(); 21 | 22 | assert.strictEqual(o.state.n, 3); 23 | }); 24 | 25 | it('merges two objects', function () { 26 | var o = M({ 27 | props: { 28 | state: M.State([ 29 | { 30 | delegate: M.Object({ 31 | props: { 32 | n: M.Number({initialValue: 3}) 33 | } 34 | }) 35 | }, 36 | { 37 | delegate: M.Object({ 38 | props: { 39 | m: M.Number({initialValue: 1}) 40 | } 41 | }) 42 | } 43 | ]) 44 | } 45 | }).createInstance(); 46 | 47 | assert.strictEqual(o.state.n, 3); 48 | assert.strictEqual(o.state.m, 1); 49 | }); 50 | 51 | it('avoid merge on predicate', function () { 52 | var o = M({ 53 | props: { 54 | flag: M.Boolean(), 55 | state: M.State([ 56 | { 57 | when() { 58 | return this.root.flag; 59 | }, 60 | delegate: M.Object({ 61 | props: { 62 | n: M.Number({initialValue: 3}) 63 | } 64 | }) 65 | }, 66 | { 67 | when() { 68 | return !this.root.flag; 69 | }, 70 | delegate: M.Object({ 71 | props: { 72 | m: M.Number({initialValue: 1}) 73 | } 74 | }) 75 | } 76 | ]) 77 | } 78 | }).createInstance(); 79 | 80 | assert.strictEqual(o.state.n, undefined); 81 | assert.strictEqual(o.state.m, 1); 82 | 83 | o.flag = true; 84 | 85 | assert.strictEqual(o.state.n, 3); 86 | assert.strictEqual(o.state.m, undefined); 87 | }); 88 | 89 | it('supports snapshot round-trip', function () { 90 | var opt = { 91 | props: { 92 | state: M.State([ 93 | { 94 | delegate: M.Object({ 95 | props: { 96 | n: M.Number({initialValue: 3}) 97 | } 98 | }) 99 | }, 100 | { 101 | delegate: M.Object({ 102 | props: { 103 | m: M.Number({initialValue: 1}) 104 | } 105 | }) 106 | } 107 | ]) 108 | } 109 | }; 110 | var o = M(opt).createInstance(); 111 | var oCopy = M(opt).createInstance(o.snapshot()); 112 | 113 | assert.deepEqual(oCopy, o); 114 | }); 115 | 116 | describe('sub-state', function () { 117 | it('merges with parent', function () { 118 | var o = M({ 119 | props: { 120 | state: M.State([ 121 | { 122 | delegate: M.Object({ 123 | props: { 124 | n: M.Number({initialValue: 3}) 125 | } 126 | }), 127 | subState: [{ 128 | delegate: M.Object({ 129 | props: { 130 | m: M.Number({initialValue: 5}) 131 | } 132 | }) 133 | }] 134 | } 135 | ]) 136 | } 137 | }).createInstance(); 138 | 139 | assert.strictEqual(o.state.n, 3); 140 | assert.strictEqual(o.state.m, 5); 141 | }); 142 | 143 | it('overrides parent state', function () { 144 | var o = M({ 145 | props: { 146 | state: M.State([ 147 | { 148 | delegate: M.Object({ 149 | props: { 150 | n: M.Number({initialValue: 3}) 151 | } 152 | }), 153 | subState: [{ 154 | delegate: M.Object({ 155 | props: { 156 | n: M.Number({initialValue: 5}) 157 | } 158 | }) 159 | }] 160 | } 161 | ]) 162 | } 163 | }).createInstance(); 164 | 165 | assert.strictEqual(o.state.n, 5); 166 | }); 167 | 168 | it('respects predicate', function () { 169 | var o = M({ 170 | props: { 171 | flag: M.Boolean(), 172 | state: M.State([ 173 | { 174 | delegate: M.Object({ 175 | props: { 176 | n: M.Number({initialValue: 3}) 177 | } 178 | }), 179 | subState: [{ 180 | when() { 181 | return this.root.flag; 182 | }, 183 | delegate: M.Object({ 184 | props: { 185 | m: M.Number({initialValue: 5}) 186 | } 187 | }) 188 | }] 189 | } 190 | ]) 191 | } 192 | }).createInstance(); 193 | 194 | assert.strictEqual(o.state.m, undefined); 195 | o.flag = true; 196 | assert.strictEqual(o.state.m, 5); 197 | }); 198 | 199 | it('respects parent predicate', function () { 200 | var o = M({ 201 | props: { 202 | flag: M.Boolean(), 203 | state: M.State([ 204 | { 205 | when() { 206 | return this.root.flag; 207 | }, 208 | delegate: M.Object({ 209 | props: { 210 | n: M.Number({initialValue: 3}), 211 | flag2: M.Boolean() 212 | } 213 | }), 214 | subState: [{ 215 | when() { 216 | return this.flag2; 217 | }, 218 | delegate: M.Object({ 219 | props: { 220 | m: M.Number({initialValue: 5}) 221 | } 222 | }) 223 | }] 224 | } 225 | ]) 226 | } 227 | }).createInstance(); 228 | 229 | assert.strictEqual(o.state.n, undefined); 230 | assert.strictEqual(o.state.m, undefined); 231 | o.flag = true; 232 | assert.strictEqual(o.state.n, 3); 233 | assert.strictEqual(o.state.m, undefined); 234 | o.state.flag2 = true; 235 | assert.strictEqual(o.state.n, 3); 236 | assert.strictEqual(o.state.m, 5); 237 | o.state.flag2 = false; 238 | assert.strictEqual(o.state.n, 3); 239 | assert.strictEqual(o.state.m, undefined); 240 | }); 241 | 242 | }); 243 | }); 244 | -------------------------------------------------------------------------------- /test/classFactories/sugar.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var M = require('../..'); 5 | 6 | describe('sugar', function () { 7 | it('respects normal object syntax', function () { 8 | const o = M.Sugar({props: {n: M.Number()}}).createInstance(); 9 | 10 | assert.strictEqual(o.n, 0); 11 | }); 12 | 13 | it('converts object with no props field', function () { 14 | const o = M.Sugar({n: M.Number()}).createInstance(); 15 | 16 | assert.strictEqual(o.n, 0); 17 | }); 18 | 19 | it('converts array', function () { 20 | const o = M.Sugar([{n: M.Number()}]).createInstance(); 21 | 22 | assert.strictEqual(o[0].n, 0); 23 | }); 24 | 25 | it('converts from native primitive constructors', function () { 26 | const n = M.Sugar(Number).createInstance(); 27 | const b = M.Sugar(Boolean).createInstance(); 28 | const s = M.Sugar(String).createInstance(); 29 | 30 | assert.strictEqual(n, 0); 31 | assert.strictEqual(b, false); 32 | assert.strictEqual(s, ''); 33 | }); 34 | 35 | it('converts from primitives', function () { 36 | const n = M.Sugar(1).createInstance(); 37 | const b = M.Sugar(true).createInstance(); 38 | const s = M.Sugar('test').createInstance(); 39 | 40 | assert.strictEqual(n, 1); 41 | assert.strictEqual(b, true); 42 | assert.strictEqual(s, 'test'); 43 | }); 44 | 45 | it('passes keys that start with $ as object options', function () { 46 | const o = M.Sugar({ 47 | $rootPropertyName: 'test', 48 | inner: { 49 | get it() { 50 | return this.test; 51 | } 52 | } 53 | }).createInstance(); 54 | 55 | assert.strictEqual(o.inner.it, o); 56 | }); 57 | 58 | it('traverse to object properties', function () { 59 | const o = M.Sugar({ 60 | n: Number, 61 | o: { 62 | s: String('test'), 63 | a: [{b: true}] 64 | } 65 | }).createInstance(); 66 | 67 | assert.strictEqual(o.n, 0); 68 | assert.strictEqual(o.o.s, 'test'); 69 | assert.strictEqual(o.o.a[0].b, true); 70 | }); 71 | 72 | it('converts getters', function () { 73 | const o = M.Sugar({ 74 | n: 3, 75 | get g() { 76 | return this.n; 77 | } 78 | }).createInstance(); 79 | 80 | o.n = 2; 81 | assert.strictEqual(o.g, 2); 82 | }); 83 | 84 | it('converts mutators', function () { 85 | const o = M.Sugar({ 86 | n: 3, 87 | m(x) { 88 | this.n += x; 89 | } 90 | }).createInstance(); 91 | 92 | o.m(1); 93 | assert.strictEqual(o.n, 4); 94 | }); 95 | }); 96 | -------------------------------------------------------------------------------- /test/control.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var M = require('..'); 5 | 6 | var sinon = require('sinon'); 7 | 8 | describe('control', function () { 9 | it('can disallow change', function () { 10 | var o = 11 | M({ 12 | props: { 13 | n: M.Number({initialValue: 3}), 14 | o: M.Object({ 15 | props: { 16 | n: M.Number({initialValue: 5}) 17 | } 18 | }) 19 | } 20 | }).createInstance(); 21 | 22 | o.control.isChangeAllowed = false; 23 | 24 | 25 | assert.throws(function() {o.n = 4;}); 26 | assert.strictEqual(o.n, 3); 27 | 28 | assert.throws(function() {o.o.n = 6;}); 29 | assert.strictEqual(o.o.n, 5); 30 | assert.throws(function() {o.o = {n: 6};}); 31 | assert.strictEqual(o.o.n, 5); 32 | }); 33 | 34 | it('listens to change events', function () { 35 | var changeListenerSpy = sinon.spy(); 36 | 37 | var o = 38 | M({ 39 | props: { 40 | n: M.Number({initialValue: 3}), 41 | o: M.Object({ 42 | props: { 43 | n: M.Number({initialValue: 5}) 44 | } 45 | }) 46 | } 47 | }).createInstance(); 48 | 49 | o.control.on('change', changeListenerSpy); 50 | 51 | o.n = 4; 52 | o.o.n = 6; 53 | 54 | assert.strictEqual(changeListenerSpy.callCount, 2); 55 | assert.deepEqual(changeListenerSpy.firstCall.args, ['setValue', {newValue: 4, trgKeyPath: [], key: 'n'}, {initialValue: 3}]); 56 | assert.deepEqual(changeListenerSpy.secondCall.args, ['setValue', {newValue: 6, trgKeyPath: ['o'], key: 'n'}, {initialValue: 5}]); 57 | }); 58 | 59 | it('can override mutators', function () { 60 | var mutatorToWrap = function(x) { 61 | this.n = this.n + x; 62 | }; 63 | 64 | var o = M({ 65 | props: { 66 | n: M.Number(), 67 | m: M.Mutator(mutatorToWrap) 68 | } 69 | }).createInstance(); 70 | 71 | o.control.onMutatorCall = sinon.spy(function (keyPath, args, mutator) { 72 | mutator.apply(this, args); // add 3 to n 73 | return this.n += args[0] + 4; // add another using the same argument as the original mutator and another 4 74 | }); 75 | 76 | assert.strictEqual(o.m(3), 10); 77 | assert.strictEqual(o.n, 10); 78 | assert(o.control.onMutatorCall.calledOnce); 79 | assert.deepEqual(o.control.onMutatorCall.firstCall.args, [[ 'm' ], [ 3 ], mutatorToWrap]); 80 | }); 81 | 82 | it('can pass control params as root second parameter', function () { 83 | var o = M({ 84 | props: { 85 | n: M.Number() 86 | } 87 | }).createInstance(null, {isChangeAllowed: false}); 88 | 89 | assert.throws(function () {o.n = 3;}); 90 | }); 91 | 92 | it('can override rootPropertyName', function () { 93 | var o = M({ 94 | rootPropertyName: 'head', 95 | props: { 96 | n: M.Number({initialValue: 1}), 97 | o: M.Object({ 98 | props: { 99 | n: M.Number({initialValue: 5}) 100 | } 101 | }) 102 | } 103 | }).createInstance(); 104 | 105 | assert.strictEqual(o.o.head.n, 1); 106 | }); 107 | }); 108 | -------------------------------------------------------------------------------- /test/docs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var M = require('..'); 5 | 6 | // TODO automate this at som point 7 | describe('doc fragments', function () { 8 | it('strict-typing', function () { 9 | const MyClass = M({ 10 | str: String, 11 | arr: [Number], 12 | obj: { 13 | bool: Boolean 14 | } 15 | }); 16 | 17 | const myInstance = new MyClass(); 18 | 19 | assert.deepEqual(myInstance, { 20 | str: '', 21 | arr: [0], 22 | obj: { 23 | bool: false 24 | } 25 | }); 26 | 27 | assert.throws(function () { 28 | myInstance.obj.bool = 'will throw'; 29 | }); 30 | 31 | assert.throws(function () { 32 | myInstance.newProperty = 'will throw'; 33 | }); 34 | 35 | }); 36 | 37 | it('change-control', function () { 38 | const MyClass = M({ 39 | n: Number 40 | }); 41 | let myInstance = new MyClass(); 42 | 43 | myInstance.n = 3; 44 | assert.equal(myInstance.n, 3); 45 | 46 | myInstance.control.isChangeAllowed = false; 47 | 48 | assert.throws(function () { 49 | myInstance.n = 4; 50 | }); 51 | assert.equal(myInstance.n, 3); 52 | 53 | M({ 54 | n: Number, 55 | get g() { 56 | // if we would try and assign `this.n = 4` 57 | // it would throw a run-time exception 58 | return this.n + 1; 59 | } 60 | }).createInstance().g; // jshint ignore:line 61 | 62 | myInstance.control.on('change', 63 | function (changeType, changePayload, opt) { // jshint ignore:line 64 | // triggered on any direct or nested changes 65 | } 66 | ); 67 | 68 | myInstance = M({ 69 | n: Number, 70 | m1(x) { 71 | this.n += x; 72 | } 73 | }).createInstance(); 74 | 75 | myInstance.control.onMutatorCall = 76 | function (keyPath, args, mutator) { 77 | // apply the original methd (add args[0] to this.n) 78 | mutator.apply(this, args); 79 | // add another 4 - just because we can 80 | return this.n += 4; 81 | }; 82 | 83 | myInstance.m1(3); 84 | assert.equal(myInstance.n, 7); 85 | 86 | myInstance = M({ 87 | n: 5, 88 | m: M.Mutator({ 89 | guard: function () { 90 | return this.n < 10; 91 | }, 92 | fn: function (x) { 93 | this.n += x; 94 | } 95 | }) 96 | }).createInstance(); 97 | 98 | myInstance.m(6); 99 | assert.equal(myInstance.n, 11); 100 | 101 | assert.throws(function () { 102 | myInstance.m(1); 103 | }); 104 | assert.equal(myInstance.n, 11); 105 | }); 106 | 107 | it('serialization', function () { 108 | const MyClass = M({ 109 | n: 3, 110 | s: 'test', 111 | b: true 112 | }); 113 | let myInstance = new MyClass(); 114 | 115 | assert.deepEqual( 116 | myInstance, 117 | {n: 3, s: 'test', b: true} 118 | ); 119 | 120 | assert.deepEqual( 121 | new MyClass({n: 4, s: 'test2', b: false}), 122 | {n: 4, s: 'test2', b: false} 123 | ); 124 | 125 | assert.deepEqual( 126 | MyClass.createInstance(myInstance.snapshot()), 127 | myInstance 128 | ); 129 | 130 | assert.deepEqual( 131 | myInstance.snapshot( 132 | function (opt, instance, keyPath, snapshotFn) { 133 | if (keyPath[0] === 'n') { 134 | return 5; 135 | } else { 136 | return snapshotFn(); 137 | } 138 | } 139 | ), 140 | {n: 5, s: 'test', b: true} 141 | ); 142 | 143 | myInstance = M({ 144 | r: M.Ref(), 145 | o: { 146 | n: Number 147 | } 148 | }).createInstance(); 149 | myInstance.r = myInstance.o; 150 | 151 | const myInstanceCopy = new MyClass(myInstance.snapshot()); 152 | 153 | assert.strictEqual(myInstanceCopy.r, myInstanceCopy.o); 154 | }); 155 | 156 | it('state-pattern', function () { 157 | let MyClass = M({ 158 | state: M.State([ 159 | { 160 | delegate: M({n: 3}), // (1) first object type 161 | subState: [{ 162 | delegate: M({m: 5}) // (1.1) first object type child 163 | }] 164 | }, 165 | { 166 | delegate: M({s: String}) // (2) second object type 167 | } 168 | ]) 169 | }); 170 | let myInstance = new MyClass(); 171 | assert.deepEqual(myInstance.state, {n: 3, m: 5, s: ''}); 172 | 173 | MyClass = M({ 174 | flag1: true, 175 | state: M.State([ 176 | { 177 | when() {return this.flag2;}, 178 | delegate: M({n: 3}), // (1) first object type 179 | subState: [{ 180 | when() {return this.root.flag1;}, 181 | delegate: M({m: 5}) // (1.1) first object type child 182 | }] 183 | }, 184 | { 185 | delegate: M({ // (2) second object type 186 | flag2: Boolean, 187 | s: String 188 | }) 189 | } 190 | ]) 191 | }); 192 | myInstance = new MyClass(); 193 | assert.deepEqual(myInstance.state, {flag2: false, s: ''}); 194 | 195 | myInstance.state.flag2 = true; 196 | assert.deepEqual( 197 | myInstance.state, 198 | {flag2: true, s: '', n: 3, m: 5} 199 | ); 200 | }); 201 | }); 202 | -------------------------------------------------------------------------------- /test/protoMapper.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const ProtoMapper = require('../lib/utils/protoMapper'); 5 | 6 | describe('ProtoMapper', function () { 7 | class A { 8 | x() { 9 | return 'x'; 10 | } 11 | } 12 | 13 | class B extends A { 14 | y() { 15 | return 'y'; 16 | } 17 | 18 | z() { 19 | return 'z'; 20 | } 21 | } 22 | 23 | Reflect.defineMetadata('test-label', undefined, A.prototype, 'x'); 24 | Reflect.defineMetadata('test-label', undefined, B.prototype, 'z'); 25 | Reflect.defineMetadata('test-label2', undefined, B.prototype, 'z'); 26 | 27 | it('should override where label is found', function () { 28 | const protoMapper = new ProtoMapper({ 29 | 'test-label': (originalMethod) => () => 'override ' + originalMethod(), 30 | 'test-label2': (originalMethod) => () => 'override2 ' + originalMethod() 31 | }); 32 | const b = new B(); 33 | assert.strictEqual(b.x(), 'x'); 34 | assert.strictEqual(b.y(), 'y'); 35 | assert.strictEqual(b.z(), 'z'); 36 | 37 | Object.setPrototypeOf(b, protoMapper.map(B.prototype)); 38 | 39 | assert.strictEqual(b.x(), 'override x'); 40 | assert.strictEqual(b.y(), 'y'); 41 | assert.strictEqual(b.z(), 'override2 override z'); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /test/snapshot.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var sinon = require('sinon'); 5 | 6 | var M = require('..'); 7 | 8 | describe('snapshot', function () { 9 | it('copy object with primitive properties', function () { 10 | var o = 11 | M({ 12 | props: { 13 | n: M.Number({initialValue: 3}), 14 | s: M.String({initialValue: 'test'}), 15 | b: M.Boolean({initialValue: true}) 16 | } 17 | }).createInstance(); 18 | 19 | assert.deepEqual(o.snapshot(), {n: 3, s: 'test', b: true}); 20 | }); 21 | 22 | it('copy object with nested object', function () { 23 | var o = M({ 24 | props: { 25 | o: M.Object({ 26 | props: { 27 | n: M.Number({initialValue: 3}) 28 | } 29 | }) 30 | } 31 | }).createInstance(); 32 | 33 | assert.deepEqual(o.snapshot(), {o: {n: 3}}); 34 | }); 35 | 36 | it('copy object with nested array', function () { 37 | var o = M({ 38 | props: { 39 | a: M.Array({ 40 | defaultLength: 2, 41 | elem: M.Number({initialValue: 3}) 42 | }) 43 | } 44 | }).createInstance(); 45 | 46 | assert.deepEqual(o.snapshot(), {a: [3, 3]}); 47 | }); 48 | 49 | it('copy object with nested array with object element', function () { 50 | var o = M({ 51 | props: { 52 | a: M.Array({ 53 | defaultLength: 2, 54 | elem: M.Object({ 55 | props: { 56 | n: M.Number({initialValue: 3}) 57 | } 58 | }) 59 | }) 60 | } 61 | }).createInstance(); 62 | 63 | assert.deepEqual(o.snapshot(), {a: [{n: 3}, {n: 3}]}); 64 | }); 65 | 66 | it('does not include getters or mutators', function () { 67 | var o = M({ 68 | props: { 69 | g: M.Getter(function() { 70 | return this.n + 1; 71 | }), 72 | m: M.Mutator(function(x) { 73 | this.n = this.n + x; 74 | }) 75 | } 76 | }).createInstance(); 77 | 78 | assert.deepEqual(o.snapshot(), {}); 79 | }); 80 | 81 | it('can restore object from snapshot', function () { 82 | var opt = { 83 | props: { 84 | n: M.Number(), 85 | o: M.Object({ 86 | props: { 87 | a: M.Array({ 88 | defaultLength: 2, 89 | elem: M.Object({ 90 | props: { 91 | n: M.Number() 92 | } 93 | }) 94 | }) 95 | } 96 | }) 97 | } 98 | }; 99 | 100 | var o = M(opt).createInstance(); 101 | o.n = 3; 102 | o.o.a[0].n = 4; 103 | o.o.a[1].n = 5; 104 | 105 | assert.deepEqual(o, {n: 3, o: {a: [{n: 4}, {n: 5}]}}); 106 | 107 | var oCopy = M(opt).createInstance(o.snapshot()); 108 | 109 | assert.deepEqual(oCopy, o); 110 | }); 111 | 112 | it('call mapper on every property', function () { 113 | var mapperSpy = sinon.spy(function noOpMapper(opt, instance, keyPath, snapshotFn) { 114 | return snapshotFn(); 115 | }); 116 | 117 | var o = 118 | M({ 119 | props: { 120 | o: M.Object({ 121 | props: { 122 | a: M.Array({ 123 | defaultLength: 1, 124 | elem: M.Number({initialValue: 3, extraOption: 'n-extra'}), 125 | extraOption: 'a-extra' 126 | }) 127 | }, 128 | extraOption: 'o-extra' 129 | }) 130 | } 131 | }).createInstance(); 132 | 133 | o.snapshot(mapperSpy); 134 | 135 | assert.strictEqual(mapperSpy.callCount, 3); // called for 'o', 'o.a' and 'o.a[0]' 136 | 137 | assert.strictEqual(mapperSpy.firstCall.args.length, 4); 138 | assert.strictEqual(mapperSpy.firstCall.args[0].extraOption, 'o-extra'); // first arg is the property options 139 | assert.strictEqual(mapperSpy.firstCall.args[1].a[0], 3); // second arg is the property value (which is o.o) 140 | assert.deepEqual(mapperSpy.firstCall.args[2], ['o']); // third arg is the keyPath 141 | assert.strictEqual(typeof mapperSpy.firstCall.args[3], 'function'); // forth arg is the continuation function 142 | 143 | assert.strictEqual(mapperSpy.secondCall.args.length, 4); 144 | assert.strictEqual(mapperSpy.secondCall.args[0].extraOption, 'a-extra'); // first arg is the property options 145 | assert.strictEqual(mapperSpy.secondCall.args[1][0], 3); // second arg is the property value (which is o.o.a) 146 | assert.deepEqual(mapperSpy.secondCall.args[2], ['o', 'a']); // third arg is the keyPath 147 | assert.strictEqual(typeof mapperSpy.secondCall.args[3], 'function'); // forth arg is the continuation function 148 | 149 | assert.strictEqual(mapperSpy.thirdCall.args.length, 4); 150 | assert.strictEqual(mapperSpy.thirdCall.args[0].extraOption, 'n-extra'); // first arg is the property options 151 | assert.strictEqual(mapperSpy.thirdCall.args[1], 3); // second arg is the property value (which is o.o.a[0]) 152 | assert.deepEqual(mapperSpy.thirdCall.args[2], ['o', 'a', 0]); // third arg is the keyPath 153 | assert.strictEqual(typeof mapperSpy.thirdCall.args[3], 'function'); // forth arg is the continuation function 154 | }); 155 | 156 | it('mapper can alter the returned value', function () { 157 | var o = 158 | M({ 159 | props: { 160 | n: M.Number({initialValue: 3}), 161 | s: M.String({initialValue: 'test'}), 162 | b: M.Boolean({initialValue: true}) 163 | } 164 | }).createInstance(); 165 | 166 | var s = o.snapshot(function (opt, instance, keyPath, snapshotFn) { 167 | if (keyPath[0] === 'n') { 168 | return 5; 169 | } else { 170 | return snapshotFn(); 171 | } 172 | }); 173 | 174 | assert.deepEqual(s, {n: 5, s: 'test', b: true}); 175 | }); 176 | 177 | it('supports TBS use case', function () { 178 | var opt = { 179 | props: { 180 | secretObject: M.Object({ 181 | props: { 182 | s: M.String({initialValue: 'secret'}) 183 | }, 184 | scope: 'master' 185 | }), 186 | secretArray: M.Array({ 187 | defaultLength: 2, 188 | elem: M.String({initialValue: 'secret'}), 189 | scope: 'master' 190 | }), 191 | notSecret: M.String({initialValue: 'notSecret'}), 192 | players: M.Array({ 193 | defaultLength: 2, 194 | elem: M.Object({ 195 | props: { 196 | secret: M.String({initialValue: 'secret', scope: 'player'}), 197 | notSecret: M.String({initialValue: 'notSecret'}) 198 | } 199 | }) 200 | }) 201 | } 202 | }; 203 | 204 | var o = M(opt).createInstance(); 205 | 206 | var playerScopeMapper = function (playerIdx) { 207 | return function (opt, instance, keyPath, snapshotFn) { 208 | if (opt.scope === 'master') { 209 | return 'hidden'; 210 | } else if (opt.scope === 'player') { 211 | if (keyPath.length < 2 || keyPath[0] !== 'players') { 212 | throw Error('the \'player\' scope must be used inside of the \'players\' array'); 213 | } else { 214 | if (keyPath[1] !== playerIdx) { 215 | return 'hidden'; 216 | } 217 | } 218 | } 219 | return snapshotFn(); 220 | }; 221 | }; 222 | 223 | var player0Snapshot = o.snapshot(playerScopeMapper(0)); 224 | var player1Snapshot = o.snapshot(playerScopeMapper(1)); 225 | 226 | assert.deepEqual(player0Snapshot, { 227 | secretObject: 'hidden', 228 | secretArray: 'hidden', 229 | notSecret: 'notSecret', 230 | players: [ 231 | {secret: 'secret', notSecret: 'notSecret'}, 232 | {notSecret: 'notSecret', secret: 'hidden'} 233 | ]} 234 | ); 235 | assert.deepEqual(player1Snapshot, { 236 | secretObject: 'hidden', 237 | secretArray: 'hidden', 238 | notSecret: 'notSecret', 239 | players: [ 240 | {secret: 'hidden', notSecret: 'notSecret'}, 241 | {notSecret: 'notSecret', secret: 'secret'} 242 | ]} 243 | ); 244 | assert.deepEqual(M(opt).createInstance(player0Snapshot), player0Snapshot); 245 | }); 246 | 247 | }); 248 | --------------------------------------------------------------------------------