Comparing version 0.8.0 to 0.9.0
268
browser.js
@@ -19,2 +19,22 @@ /* hugov@runbox.com | https://github.com/hville/attostore.git | license:MIT */ | ||
function missingKeys(reference, value) { | ||
var keys = isObj(reference) ? Object.keys(reference) : []; | ||
return isObj(value) ? keys.filter(filterVoid, value) : keys | ||
} | ||
function changedKeys(reference, value) { | ||
var keys = isObj(reference) ? Object.keys(reference) : [], | ||
diff = []; | ||
if (isObj(value)) for (var i=0; i<keys.length; ++i) { | ||
var key = keys[i], | ||
val = value[key]; | ||
if (val !== reference[key] && val !== undefined) diff.push(key); | ||
} | ||
return diff | ||
} | ||
function filterVoid(k) { | ||
return this[k] === undefined | ||
} | ||
function getKey(obj, key) { | ||
@@ -24,2 +44,27 @@ if (isObj(obj)) return obj[key] | ||
/** | ||
* deep Equal check on JSON-like objects | ||
* @function | ||
* @param {*} obj - object to check | ||
* @param {*} ref - the reference | ||
* @return {boolean|void} true if equal | ||
*/ | ||
function isEqual(obj, ref) { | ||
var cO = cType(obj), | ||
cR = cType(ref); | ||
if (cO !== cR) return false | ||
if (cO === Array) { | ||
if (obj.length !== ref.length) return false | ||
for (var i=0; i<obj.length; ++i) if (!isEqual(obj[i], ref[i])) return false | ||
return true | ||
} | ||
if (cO === Object) { | ||
var ko = Object.keys(obj); //TODO type annotation obj: Object | ||
if (ko.length !== Object.keys(ref).length) return false //TODO type annotation typeof ref: Object | ||
for (i=0; i<ko.length; ++i) if (!isEqual(obj[ko[i]], ref[ko[i]])) return false | ||
return true | ||
} | ||
else return obj === ref | ||
} | ||
function pathKeys(path) { | ||
@@ -32,8 +77,8 @@ var ct = cType(path); | ||
* @constructor | ||
* @param {*} [data] | ||
* @param {*} initValue | ||
*/ | ||
function Trie(data) { | ||
function Store(initValue) { | ||
this._ks = new Map; | ||
this._fs = []; | ||
this.data = data; | ||
this.data = initValue; | ||
} | ||
@@ -48,3 +93,3 @@ | ||
*/ | ||
Trie.prototype.on = function(key, fcn, ctx) { | ||
Store.prototype.on = function(key, fcn, ctx) { | ||
var leaf = setLeaf(this, pathKeys(key)), | ||
@@ -56,2 +101,3 @@ list = leaf._fs; | ||
/** | ||
@@ -63,3 +109,3 @@ * @param {Array|string|number} key | ||
*/ | ||
Trie.prototype.off = function(key, fcn, ctx) { | ||
Store.prototype.off = function(key, fcn, ctx) { | ||
var keys = pathKeys(key), | ||
@@ -76,2 +122,3 @@ itm = getLeaf(this, keys), | ||
/** | ||
@@ -83,3 +130,3 @@ * @param {Array|string|number} key | ||
*/ | ||
Trie.prototype.once = function(key, fcn, ctx) { | ||
Store.prototype.once = function(key, fcn, ctx) { | ||
var store = this; | ||
@@ -94,6 +141,41 @@ function wrap(v,k,o) { | ||
Store.prototype.get = function(path) { | ||
var keys = pathKeys(path); | ||
for (var i=0, itm = this.data; i<keys.length; ++i) { | ||
if (isObj(itm)) itm = itm[keys[i]]; | ||
else return | ||
} | ||
return itm | ||
}; | ||
/** | ||
* @param {Trie} root | ||
* @param {Array|string} path | ||
* @param {*} data | ||
* @return {Error|void} | ||
*/ | ||
Store.prototype.set = function(path, data) { | ||
var res = setKeys(this.data, pathKeys(path), data, 0); | ||
if (res instanceof Error) return res | ||
update(this, res, null); | ||
}; | ||
/** | ||
* @param {Array} ops | ||
* @return {Error|void} | ||
*/ | ||
Store.prototype.run = function(ops) { | ||
var res = this.data; | ||
for (var i=0; i<ops.length; ++i) { | ||
res = setKeys(res, pathKeys(ops[i].path), ops[i].data, 0); | ||
if (res instanceof Error) return res | ||
} | ||
update(this, res, null); | ||
}; | ||
/** | ||
* @param {Store} root | ||
* @param {Array<string>} keys | ||
* @return {Trie} | ||
* @return {Store} | ||
*/ | ||
@@ -107,6 +189,7 @@ function getLeaf(root, keys) { | ||
/** | ||
* @param {Trie} root | ||
* @param {Store} root | ||
* @param {Array<string>} keys | ||
* @return {Trie} | ||
* @return {Store} | ||
*/ | ||
@@ -116,3 +199,3 @@ function setLeaf(root, keys) { | ||
var key = ''+keys[i]; | ||
if (!itm._ks.has(key)) itm._ks.set(key, new Trie(getKey(itm.data, key))); | ||
if (!itm._ks.has(key)) itm._ks.set(key, new Store(getKey(itm.data, key))); | ||
itm = itm._ks.get(key); | ||
@@ -123,4 +206,5 @@ } | ||
/** | ||
* @param {Trie} trie | ||
* @param {Store} trie | ||
* @param {Array<string>} keys | ||
@@ -133,3 +217,3 @@ * @param {number} idx | ||
kid = trie._ks.get(key); | ||
if (kid instanceof Trie) { | ||
if (kid instanceof Store) { | ||
if (idx !== keys.length) delLeaf(kid, keys, idx); | ||
@@ -140,2 +224,3 @@ if (!kid._ks.size && !kid._fs.length) trie._ks.delete(key); | ||
/** | ||
@@ -152,64 +237,27 @@ * @param {Array} arr | ||
/** | ||
* deep Equal check on JSON-like objects | ||
* @function | ||
* @param {*} obj - object to check | ||
* @param {*} ref - the reference | ||
* @return {boolean|void} true if equal | ||
* @param {!Object} trie | ||
* @param {*} val | ||
* @param {string} key | ||
* @return {void|Error} | ||
*/ | ||
function isEqual(obj, ref) { | ||
var cO = cType(obj), | ||
cR = cType(ref); | ||
if (cO !== cR) return false | ||
if (cO === Array) { | ||
if (obj.length !== ref.length) return false | ||
for (var i=0; i<obj.length; ++i) if (!isEqual(obj[i], ref[i])) return false | ||
return true | ||
function update(trie, val, key) { | ||
if (val !== trie.data) { | ||
var old = trie.data; | ||
trie.data = val; | ||
// fire kids first... | ||
trie._ks.forEach(updateKid, val); | ||
// ...then self | ||
for (var i=0, fs=trie._fs; i<fs.length; ++i) { | ||
fs[i].f.call(fs[i].c, val, key, old); | ||
} | ||
} | ||
if (cO === Object) { | ||
var ko = Object.keys(obj); //TODO type annotation obj: Object | ||
if (ko.length !== Object.keys(ref).length) return false //TODO type annotation typeof ref: Object | ||
for (i=0; i<ko.length; ++i) if (!isEqual(obj[ko[i]], ref[ko[i]])) return false | ||
return true | ||
} | ||
else return obj === ref | ||
} | ||
/** | ||
* @constructor | ||
* @param {*} [data] | ||
* @param {Object} [commands] | ||
*/ | ||
function State(data) { | ||
this.data = data; | ||
function updateKid(kid, k) { | ||
update(kid, getKey(this, k), k); | ||
} | ||
State.prototype.get = function(path) { | ||
var keys = pathKeys(path); | ||
for (var i=0, itm = this.data; i<keys.length; ++i) { | ||
if (isObj(itm)) itm = itm[keys[i]]; | ||
else return | ||
} | ||
return itm | ||
}; | ||
/** | ||
* @param {null|string|Array} path | ||
* @param {*} data | ||
* @return {Object} | ||
*/ | ||
State.prototype.set = function(path, data) { | ||
if (!(this.data instanceof Error)) this.data = setKeys(this.data, pathKeys(path), data, 0); | ||
return this | ||
}; | ||
/** | ||
* @param {null|string|Array} path | ||
* @return {Object} | ||
*/ | ||
State.prototype.delete = function(path) { | ||
return this.set(path, undefined) | ||
}; | ||
/** | ||
* @param {*} obj | ||
@@ -267,90 +315,4 @@ * @param {!Array} keys | ||
/** | ||
* @constructor | ||
* @param {*} initValue | ||
* @param {Object} commands | ||
*/ | ||
function Store(initValue, commands) { | ||
this._ks = new Map; | ||
this._fs = []; | ||
this._cs = commands || {}; | ||
this.data = initValue; | ||
} | ||
Store.prototype.on = Trie.prototype.on; | ||
Store.prototype.off = Trie.prototype.off; | ||
Store.prototype.once = Trie.prototype.once; | ||
Store.prototype.get = State.prototype.get; | ||
/** | ||
* @param {string} name | ||
* @param {...*} [param] | ||
* @return {Error|void} | ||
*/ | ||
Store.prototype.run = function(name, param) { //eslint-disable-line no-unused-vars | ||
var state = new State(this.data), | ||
cmd = this._cs[name]; | ||
if (!cmd) return Error('invalid command ' + name) | ||
for (var i=1, args=[]; i<arguments.length; ++i) args[i-1] = arguments[i]; | ||
cmd.apply(state, args); | ||
return state.data instanceof Error ? state.data : update(this, state.data, null) | ||
}; | ||
/** | ||
* @param {!Object} trie | ||
* @param {*} val | ||
* @param {string} key | ||
* @return {void|Error} | ||
*/ | ||
function update(trie, val, key) { | ||
if (val instanceof Error) return val | ||
if (val !== trie.data) { | ||
var old = trie.data; | ||
trie.data = val; | ||
// fire kids first... | ||
trie._ks.forEach(updateKid, val); | ||
// ...then self | ||
for (var i=0, fs=trie._fs; i<fs.length; ++i) { | ||
fs[i].f.call(fs[i].c, val, key, old); | ||
} | ||
} | ||
} | ||
function updateKid(kid, k) { | ||
update(kid, getKey(this, k), k); | ||
} | ||
function missingKeys(reference, value) { | ||
var keys = isObj(reference) ? Object.keys(reference) : []; | ||
return isObj(value) ? keys.filter(filterVoid, value) : keys | ||
} | ||
function changedKeys(reference, value) { | ||
var keys = isObj(reference) ? Object.keys(reference) : [], | ||
diff = []; | ||
if (isObj(value)) for (var i=0; i<keys.length; ++i) { | ||
var key = keys[i], | ||
val = value[key]; | ||
if (val !== reference[key] && val !== undefined) diff.push(key); | ||
} | ||
return diff | ||
} | ||
function filterVoid(k) { | ||
return this[k] === undefined | ||
} | ||
// @ts-check | ||
/** | ||
* @param {*} initValue | ||
* @param {Object} commands | ||
* @return {Store} | ||
*/ | ||
function createStore(initValue, commands) { | ||
return new Store(initValue, commands) | ||
} | ||
exports.createStore = createStore; | ||
exports.changedKeys = changedKeys; | ||
@@ -357,0 +319,0 @@ exports.missingKeys = missingKeys; |
268
index.js
@@ -20,2 +20,22 @@ /* hugov@runbox.com | https://github.com/hville/attostore.git | license:MIT */ | ||
function missingKeys(reference, value) { | ||
var keys = isObj(reference) ? Object.keys(reference) : []; | ||
return isObj(value) ? keys.filter(filterVoid, value) : keys | ||
} | ||
function changedKeys(reference, value) { | ||
var keys = isObj(reference) ? Object.keys(reference) : [], | ||
diff = []; | ||
if (isObj(value)) for (var i=0; i<keys.length; ++i) { | ||
var key = keys[i], | ||
val = value[key]; | ||
if (val !== reference[key] && val !== undefined) diff.push(key); | ||
} | ||
return diff | ||
} | ||
function filterVoid(k) { | ||
return this[k] === undefined | ||
} | ||
function getKey(obj, key) { | ||
@@ -25,2 +45,27 @@ if (isObj(obj)) return obj[key] | ||
/** | ||
* deep Equal check on JSON-like objects | ||
* @function | ||
* @param {*} obj - object to check | ||
* @param {*} ref - the reference | ||
* @return {boolean|void} true if equal | ||
*/ | ||
function isEqual(obj, ref) { | ||
var cO = cType(obj), | ||
cR = cType(ref); | ||
if (cO !== cR) return false | ||
if (cO === Array) { | ||
if (obj.length !== ref.length) return false | ||
for (var i=0; i<obj.length; ++i) if (!isEqual(obj[i], ref[i])) return false | ||
return true | ||
} | ||
if (cO === Object) { | ||
var ko = Object.keys(obj); //TODO type annotation obj: Object | ||
if (ko.length !== Object.keys(ref).length) return false //TODO type annotation typeof ref: Object | ||
for (i=0; i<ko.length; ++i) if (!isEqual(obj[ko[i]], ref[ko[i]])) return false | ||
return true | ||
} | ||
else return obj === ref | ||
} | ||
function pathKeys(path) { | ||
@@ -33,8 +78,8 @@ var ct = cType(path); | ||
* @constructor | ||
* @param {*} [data] | ||
* @param {*} initValue | ||
*/ | ||
function Trie(data) { | ||
function Store(initValue) { | ||
this._ks = new Map; | ||
this._fs = []; | ||
this.data = data; | ||
this.data = initValue; | ||
} | ||
@@ -49,3 +94,3 @@ | ||
*/ | ||
Trie.prototype.on = function(key, fcn, ctx) { | ||
Store.prototype.on = function(key, fcn, ctx) { | ||
var leaf = setLeaf(this, pathKeys(key)), | ||
@@ -57,2 +102,3 @@ list = leaf._fs; | ||
/** | ||
@@ -64,3 +110,3 @@ * @param {Array|string|number} key | ||
*/ | ||
Trie.prototype.off = function(key, fcn, ctx) { | ||
Store.prototype.off = function(key, fcn, ctx) { | ||
var keys = pathKeys(key), | ||
@@ -77,2 +123,3 @@ itm = getLeaf(this, keys), | ||
/** | ||
@@ -84,3 +131,3 @@ * @param {Array|string|number} key | ||
*/ | ||
Trie.prototype.once = function(key, fcn, ctx) { | ||
Store.prototype.once = function(key, fcn, ctx) { | ||
var store = this; | ||
@@ -95,6 +142,41 @@ function wrap(v,k,o) { | ||
Store.prototype.get = function(path) { | ||
var keys = pathKeys(path); | ||
for (var i=0, itm = this.data; i<keys.length; ++i) { | ||
if (isObj(itm)) itm = itm[keys[i]]; | ||
else return | ||
} | ||
return itm | ||
}; | ||
/** | ||
* @param {Trie} root | ||
* @param {Array|string} path | ||
* @param {*} data | ||
* @return {Error|void} | ||
*/ | ||
Store.prototype.set = function(path, data) { | ||
var res = setKeys(this.data, pathKeys(path), data, 0); | ||
if (res instanceof Error) return res | ||
update(this, res, null); | ||
}; | ||
/** | ||
* @param {Array} ops | ||
* @return {Error|void} | ||
*/ | ||
Store.prototype.run = function(ops) { | ||
var res = this.data; | ||
for (var i=0; i<ops.length; ++i) { | ||
res = setKeys(res, pathKeys(ops[i].path), ops[i].data, 0); | ||
if (res instanceof Error) return res | ||
} | ||
update(this, res, null); | ||
}; | ||
/** | ||
* @param {Store} root | ||
* @param {Array<string>} keys | ||
* @return {Trie} | ||
* @return {Store} | ||
*/ | ||
@@ -108,6 +190,7 @@ function getLeaf(root, keys) { | ||
/** | ||
* @param {Trie} root | ||
* @param {Store} root | ||
* @param {Array<string>} keys | ||
* @return {Trie} | ||
* @return {Store} | ||
*/ | ||
@@ -117,3 +200,3 @@ function setLeaf(root, keys) { | ||
var key = ''+keys[i]; | ||
if (!itm._ks.has(key)) itm._ks.set(key, new Trie(getKey(itm.data, key))); | ||
if (!itm._ks.has(key)) itm._ks.set(key, new Store(getKey(itm.data, key))); | ||
itm = itm._ks.get(key); | ||
@@ -124,4 +207,5 @@ } | ||
/** | ||
* @param {Trie} trie | ||
* @param {Store} trie | ||
* @param {Array<string>} keys | ||
@@ -134,3 +218,3 @@ * @param {number} idx | ||
kid = trie._ks.get(key); | ||
if (kid instanceof Trie) { | ||
if (kid instanceof Store) { | ||
if (idx !== keys.length) delLeaf(kid, keys, idx); | ||
@@ -141,2 +225,3 @@ if (!kid._ks.size && !kid._fs.length) trie._ks.delete(key); | ||
/** | ||
@@ -153,64 +238,27 @@ * @param {Array} arr | ||
/** | ||
* deep Equal check on JSON-like objects | ||
* @function | ||
* @param {*} obj - object to check | ||
* @param {*} ref - the reference | ||
* @return {boolean|void} true if equal | ||
* @param {!Object} trie | ||
* @param {*} val | ||
* @param {string} key | ||
* @return {void|Error} | ||
*/ | ||
function isEqual(obj, ref) { | ||
var cO = cType(obj), | ||
cR = cType(ref); | ||
if (cO !== cR) return false | ||
if (cO === Array) { | ||
if (obj.length !== ref.length) return false | ||
for (var i=0; i<obj.length; ++i) if (!isEqual(obj[i], ref[i])) return false | ||
return true | ||
function update(trie, val, key) { | ||
if (val !== trie.data) { | ||
var old = trie.data; | ||
trie.data = val; | ||
// fire kids first... | ||
trie._ks.forEach(updateKid, val); | ||
// ...then self | ||
for (var i=0, fs=trie._fs; i<fs.length; ++i) { | ||
fs[i].f.call(fs[i].c, val, key, old); | ||
} | ||
} | ||
if (cO === Object) { | ||
var ko = Object.keys(obj); //TODO type annotation obj: Object | ||
if (ko.length !== Object.keys(ref).length) return false //TODO type annotation typeof ref: Object | ||
for (i=0; i<ko.length; ++i) if (!isEqual(obj[ko[i]], ref[ko[i]])) return false | ||
return true | ||
} | ||
else return obj === ref | ||
} | ||
/** | ||
* @constructor | ||
* @param {*} [data] | ||
* @param {Object} [commands] | ||
*/ | ||
function State(data) { | ||
this.data = data; | ||
function updateKid(kid, k) { | ||
update(kid, getKey(this, k), k); | ||
} | ||
State.prototype.get = function(path) { | ||
var keys = pathKeys(path); | ||
for (var i=0, itm = this.data; i<keys.length; ++i) { | ||
if (isObj(itm)) itm = itm[keys[i]]; | ||
else return | ||
} | ||
return itm | ||
}; | ||
/** | ||
* @param {null|string|Array} path | ||
* @param {*} data | ||
* @return {Object} | ||
*/ | ||
State.prototype.set = function(path, data) { | ||
if (!(this.data instanceof Error)) this.data = setKeys(this.data, pathKeys(path), data, 0); | ||
return this | ||
}; | ||
/** | ||
* @param {null|string|Array} path | ||
* @return {Object} | ||
*/ | ||
State.prototype.delete = function(path) { | ||
return this.set(path, undefined) | ||
}; | ||
/** | ||
* @param {*} obj | ||
@@ -268,92 +316,6 @@ * @param {!Array} keys | ||
/** | ||
* @constructor | ||
* @param {*} initValue | ||
* @param {Object} commands | ||
*/ | ||
function Store(initValue, commands) { | ||
this._ks = new Map; | ||
this._fs = []; | ||
this._cs = commands || {}; | ||
this.data = initValue; | ||
} | ||
Store.prototype.on = Trie.prototype.on; | ||
Store.prototype.off = Trie.prototype.off; | ||
Store.prototype.once = Trie.prototype.once; | ||
Store.prototype.get = State.prototype.get; | ||
/** | ||
* @param {string} name | ||
* @param {...*} [param] | ||
* @return {Error|void} | ||
*/ | ||
Store.prototype.run = function(name, param) { //eslint-disable-line no-unused-vars | ||
var state = new State(this.data), | ||
cmd = this._cs[name]; | ||
if (!cmd) return Error('invalid command ' + name) | ||
for (var i=1, args=[]; i<arguments.length; ++i) args[i-1] = arguments[i]; | ||
cmd.apply(state, args); | ||
return state.data instanceof Error ? state.data : update(this, state.data, null) | ||
}; | ||
/** | ||
* @param {!Object} trie | ||
* @param {*} val | ||
* @param {string} key | ||
* @return {void|Error} | ||
*/ | ||
function update(trie, val, key) { | ||
if (val instanceof Error) return val | ||
if (val !== trie.data) { | ||
var old = trie.data; | ||
trie.data = val; | ||
// fire kids first... | ||
trie._ks.forEach(updateKid, val); | ||
// ...then self | ||
for (var i=0, fs=trie._fs; i<fs.length; ++i) { | ||
fs[i].f.call(fs[i].c, val, key, old); | ||
} | ||
} | ||
} | ||
function updateKid(kid, k) { | ||
update(kid, getKey(this, k), k); | ||
} | ||
function missingKeys(reference, value) { | ||
var keys = isObj(reference) ? Object.keys(reference) : []; | ||
return isObj(value) ? keys.filter(filterVoid, value) : keys | ||
} | ||
function changedKeys(reference, value) { | ||
var keys = isObj(reference) ? Object.keys(reference) : [], | ||
diff = []; | ||
if (isObj(value)) for (var i=0; i<keys.length; ++i) { | ||
var key = keys[i], | ||
val = value[key]; | ||
if (val !== reference[key] && val !== undefined) diff.push(key); | ||
} | ||
return diff | ||
} | ||
function filterVoid(k) { | ||
return this[k] === undefined | ||
} | ||
// @ts-check | ||
/** | ||
* @param {*} initValue | ||
* @param {Object} commands | ||
* @return {Store} | ||
*/ | ||
function createStore(initValue, commands) { | ||
return new Store(initValue, commands) | ||
} | ||
exports.createStore = createStore; | ||
exports.changedKeys = changedKeys; | ||
exports.missingKeys = missingKeys; | ||
exports.Store = Store; |
// @ts-check | ||
import {Store} from './src/_store' | ||
/** | ||
* @param {*} initValue | ||
* @param {Object} commands | ||
* @return {Store} | ||
*/ | ||
export function createStore(initValue, commands) { | ||
return new Store(initValue, commands) | ||
} | ||
export {changedKeys, missingKeys} from './src/compare' | ||
export {Store} from './src/_store' |
{ | ||
"name": "attostore", | ||
"version": "0.8.0", | ||
"version": "0.9.0", | ||
"description": "json data store", | ||
@@ -17,3 +17,4 @@ "keywords": [ | ||
"@types/node": "^8.0.12", | ||
"cotest": "^2.1.0" | ||
"cotest": "^2.1.0", | ||
"node-dynamic-import": "0.0.1" | ||
}, | ||
@@ -20,0 +21,0 @@ "scripts": { |
@@ -10,16 +10,12 @@ # attodom | ||
```javascript | ||
import {createStore, setOperation, delOperation} from 'attostore' | ||
import {Store} from 'attostore' | ||
var store = create({}, { | ||
yell: function(name) { this.set('yell', name) }, | ||
sing: function(name) { this.set('sing', name) }, | ||
stop: function() { this.delete('yell').delete('sing')] }, | ||
}) | ||
var store = new Store({}) | ||
store.on('yell', function(val, key, old) { | ||
console.log(val ? 'yelling '+val : 'being quiet') | ||
store.on('a/b', function(val, key, old) { | ||
console.log('changed', key, 'from', old, 'to', val) | ||
}) | ||
store.run('yell', 'YO!') | ||
store.run('stop') | ||
store.run([{path:'a/b', data:'hello'}]) | ||
store.set(['a', 'b'], data:'world') | ||
``` | ||
@@ -30,3 +26,2 @@ | ||
* ES modules: `import {createStore} from 'attostore'` | ||
* browser: `var create = window.attostore.createStore` | ||
@@ -36,3 +31,3 @@ | ||
* available in CommonJS, ES6 modules and browser versions | ||
* available in CommonJS and ES6 modules | ||
* only the last item of an Array can be deleted to avoid shifting of keys | ||
@@ -46,3 +41,3 @@ * No Array splicing to keep the keys unchanged. additions and removals from the end only (eg. push pop) | ||
createStore(initValue: `any`, commands: `{commandName: Command}`): `Store` | ||
new Store(initValue: `any`): `Store` | ||
@@ -55,2 +50,5 @@ ### Store | ||
.run(commandName: `string`, ...args: `any`): `Error|void` | ||
.get(path: `Path`): `any` | ||
.run(command[]): `Error|void` | ||
.set(path: `Path` [, data: `any`]): `Error|void` | ||
@@ -60,3 +58,3 @@ | ||
`(this:{set, delete, get}, any) => void` | ||
`{path: Path, data: any}` | ||
@@ -63,0 +61,0 @@ |
@@ -1,38 +0,159 @@ | ||
import {Trie} from './_trie' | ||
import {State} from './_state' | ||
import {getKey} from './get-key' | ||
import {isEqual} from './is-eq' | ||
import {pathKeys} from './path-keys' | ||
import {isObj} from './type' | ||
/** | ||
* @constructor | ||
* @param {*} initValue | ||
* @param {Object} commands | ||
*/ | ||
export function Store(initValue, commands) { | ||
export function Store(initValue) { | ||
this._ks = new Map | ||
this._fs = [] | ||
this._cs = commands || {} | ||
this.data = initValue | ||
} | ||
Store.prototype.on = Trie.prototype.on | ||
Store.prototype.off = Trie.prototype.off | ||
Store.prototype.once = Trie.prototype.once | ||
Store.prototype.get = State.prototype.get | ||
/** | ||
* @param {Array|string|number} key | ||
* @param {Function} fcn | ||
* @param {*} [ctx] | ||
* @return {!Object} | ||
*/ | ||
Store.prototype.on = function(key, fcn, ctx) { | ||
var leaf = setLeaf(this, pathKeys(key)), | ||
list = leaf._fs | ||
if (indexOf(list, fcn, ctx) === -1) list.push({f: fcn, c:ctx}) | ||
return this | ||
} | ||
/** | ||
* @param {string} name | ||
* @param {...*} [param] | ||
* @param {Array|string|number} key | ||
* @param {Function} fcn | ||
* @param {*} [ctx] | ||
* @return {!Object} | ||
*/ | ||
Store.prototype.off = function(key, fcn, ctx) { | ||
var keys = pathKeys(key), | ||
itm = getLeaf(this, keys), | ||
arr = itm && itm._fs, | ||
idx = indexOf(arr, fcn, ctx) | ||
if (idx !== -1) { | ||
arr.splice(idx, 1) | ||
if (!arr.length && !itm._ks.size) delLeaf(this, keys, 0) | ||
} | ||
return this | ||
} | ||
/** | ||
* @param {Array|string|number} key | ||
* @param {Function} fcn | ||
* @param {*} [ctx] | ||
* @return {!Object} | ||
*/ | ||
Store.prototype.once = function(key, fcn, ctx) { | ||
var store = this | ||
function wrap(v,k,o) { | ||
store.off(key, wrap, ctx); | ||
fcn.call(this, v,k,o) | ||
} | ||
return this.on(key, wrap, ctx) | ||
} | ||
Store.prototype.get = function(path) { | ||
var keys = pathKeys(path) | ||
for (var i=0, itm = this.data; i<keys.length; ++i) { | ||
if (isObj(itm)) itm = itm[keys[i]] | ||
else return | ||
} | ||
return itm | ||
} | ||
/** | ||
* @param {Array|string} path | ||
* @param {*} data | ||
* @return {Error|void} | ||
*/ | ||
Store.prototype.run = function(name, param) { //eslint-disable-line no-unused-vars | ||
var state = new State(this.data), | ||
cmd = this._cs[name] | ||
if (!cmd) return Error('invalid command ' + name) | ||
for (var i=1, args=[]; i<arguments.length; ++i) args[i-1] = arguments[i] | ||
cmd.apply(state, args) | ||
return state.data instanceof Error ? state.data : update(this, state.data, null) | ||
Store.prototype.set = function(path, data) { | ||
var res = setKeys(this.data, pathKeys(path), data, 0) | ||
if (res instanceof Error) return res | ||
update(this, res, null) | ||
} | ||
/** | ||
* @param {Array} ops | ||
* @return {Error|void} | ||
*/ | ||
Store.prototype.run = function(ops) { | ||
var res = this.data | ||
for (var i=0; i<ops.length; ++i) { | ||
res = setKeys(res, pathKeys(ops[i].path), ops[i].data, 0) | ||
if (res instanceof Error) return res | ||
} | ||
update(this, res, null) | ||
} | ||
/** | ||
* @param {Store} root | ||
* @param {Array<string>} keys | ||
* @return {Store} | ||
*/ | ||
function getLeaf(root, keys) { | ||
for (var i=0, itm = root; i<keys.length; ++i) { | ||
if (itm !== undefined) itm = itm._ks.get(''+keys[i]) | ||
} | ||
return itm | ||
} | ||
/** | ||
* @param {Store} root | ||
* @param {Array<string>} keys | ||
* @return {Store} | ||
*/ | ||
function setLeaf(root, keys) { | ||
for (var i=0, itm = root; i<keys.length; ++i) { | ||
var key = ''+keys[i] | ||
if (!itm._ks.has(key)) itm._ks.set(key, new Store(getKey(itm.data, key))) | ||
itm = itm._ks.get(key) | ||
} | ||
return itm | ||
} | ||
/** | ||
* @param {Store} trie | ||
* @param {Array<string>} keys | ||
* @param {number} idx | ||
* @return {void} | ||
*/ | ||
function delLeaf(trie, keys, idx) { | ||
var key = keys[idx++], | ||
kid = trie._ks.get(key) | ||
if (kid instanceof Store) { | ||
if (idx !== keys.length) delLeaf(kid, keys, idx) | ||
if (!kid._ks.size && !kid._fs.length) trie._ks.delete(key) | ||
} | ||
} | ||
/** | ||
* @param {Array} arr | ||
* @param {Function} fcn | ||
* @param {*} ctx | ||
* @return {number} | ||
*/ | ||
function indexOf(arr, fcn, ctx) { | ||
if (arr) for (var i=0; i<arr.length; ++i) if (arr[i].f === fcn && arr[i].c === ctx) return i | ||
return -1 | ||
} | ||
/** | ||
* @param {!Object} trie | ||
@@ -44,4 +165,2 @@ * @param {*} val | ||
function update(trie, val, key) { | ||
if (val instanceof Error) return val | ||
if (val !== trie.data) { | ||
@@ -58,5 +177,58 @@ var old = trie.data | ||
} | ||
function updateKid(kid, k) { | ||
update(kid, getKey(this, k), k) | ||
} | ||
/** | ||
* @param {*} obj | ||
* @param {!Array} keys | ||
* @param {*} val | ||
* @param {number} idx | ||
* @return {*} | ||
*/ | ||
function setKeys(obj, keys, val, idx) { | ||
// last key reached => close | ||
if (idx === keys.length) return isEqual(obj, val) ? obj : val | ||
// recursive calls to end of path | ||
if (!isObj(obj)) return Error('invalid path: ' + keys.join('/')) | ||
var k = keys[idx], | ||
o = obj[k], | ||
v = setKeys(o, keys, val, idx+1) | ||
return v instanceof Error ? v : v === o ? obj : Array.isArray(obj) ? aSet(obj, +k, v) : oSet(obj, k, v) | ||
} | ||
/** | ||
* @param {!Array} arr | ||
* @param {number} key | ||
* @param {*} val | ||
* @return {!Array|Error} | ||
*/ | ||
function aSet(arr, key, val) { | ||
var tgt = arr.slice() | ||
if (val === undefined) { | ||
if (key !== arr.length-1) return Error('only the last array item can be deleted') | ||
tgt.length = key | ||
return tgt | ||
} | ||
if (key <= arr.length) { | ||
tgt[key] = val | ||
return tgt | ||
} | ||
return Error('invalid array index: ' + key) | ||
} | ||
/** | ||
* @param {!Object} obj | ||
* @param {string} key | ||
* @param {*} val | ||
* @return {!Object} | ||
*/ | ||
function oSet(obj, key, val) { | ||
for (var i=0, ks=Object.keys(obj), res={}; i<ks.length; ++i) if (ks[i] !== key) res[ks[i]] = obj[ks[i]] | ||
if (val !== undefined) res[key] = val | ||
return res | ||
} |
var S = require( '../index' ), | ||
t = require('cotest') | ||
var create = S.createStore, | ||
var Store = S.Store, | ||
changed = S.changedKeys, | ||
@@ -19,5 +19,3 @@ missing = S.missingKeys | ||
t('ref - added keys', function() { | ||
var store = create({}, { | ||
init: function(v) { this.set('', v) } | ||
}) | ||
var store = new Store({}) | ||
@@ -29,3 +27,3 @@ store.once('aa',compare, [['bb','b'],[],[]]) | ||
t('!', store.run('init', {aa: {bb:{cc:{}, c: 'c'}, b: 'b'}, a:'a'}), 'no errors') | ||
t('!', store.set(null, {aa: {bb:{cc:{}, c: 'c'}, b: 'b'}, a:'a'}), 'no errors') | ||
t('{===}', store.data, {aa: {bb:{cc:{}, c: 'c'}, b: 'b'}, a:'a'}) | ||
@@ -36,7 +34,4 @@ }) | ||
t('ref - del keys', function() { | ||
var store = create(null, { | ||
init: function(v) { this.set([], v) }, | ||
clear: function() { this.delete() } | ||
}) | ||
store.run('init', {aa: {bb:{cc:{}, c: 'c'}, b: 'b'}, a:'a'}) | ||
var store = new Store(null) | ||
store.run([{data:{aa: {bb:{cc:{}, c: 'c'}, b: 'b'}, a:'a'}}]) | ||
@@ -48,3 +43,3 @@ store.once('aa', compare, [[],[],['bb','b']]) | ||
t('!', store.run('clear')) | ||
t('!', store.run([{}])) | ||
t('{===}', store.data, undefined) | ||
@@ -54,7 +49,3 @@ }) | ||
t('auto purge trie', function() { | ||
var store = create(null, { | ||
init: function(v) { this.set(null, v) }, | ||
set: function(p,v) { this.set(p,v) }, | ||
clear: function() { this.delete() } | ||
}) | ||
var store = new Store(null) | ||
function noop(){} | ||
@@ -71,14 +62,10 @@ t('===', store._ks.size, 0) | ||
t('db - query', function() { | ||
var store = create(null, { | ||
init: function(v) { this.set(null, v) }, | ||
set: function(p,v) { this.set(p,v) }, | ||
clear: function() { this.delete() } | ||
}) | ||
store.run('init', {data: {list: [1,0]}, view:{}, acts:{}, done:[]}) | ||
var store = new Store(null) | ||
store.set('', {data: {list: [1,0]}, view:{}, acts:{}, done:[]}) | ||
store.on('data/list', function(v) { | ||
store.run('set', 'view/sort', v.slice().sort()) | ||
store.run([{path:'view/sort', data:v.slice().sort()}]) | ||
}) | ||
store.on('acts/push', function(v) { | ||
store.run('set', 'data/list', store.get('data/list').concat(v)) | ||
store.run([{path:'data/list', data:store.get('data/list').concat(v)}]) | ||
}) | ||
@@ -90,3 +77,3 @@ | ||
t('!', store.run('set', 'acts/push', 9)) | ||
t('!', store.set('acts/push', 9)) | ||
t('{===}', store.data.data.list, [1,0,9])//[1,0] | ||
@@ -97,35 +84,4 @@ t('{===}', store.data.view.sort, [0,1,9])//undefined | ||
t('store - commands', function() { | ||
var commands = { | ||
init: function() { this.set('', {}) }, | ||
yell: function(x) { this.set('yell', !!x) }, | ||
sing: function(x) { this.set('sing', !!x) }, | ||
stop: function() { this.delete('yell').delete('sing') } | ||
}, | ||
store = create(null, commands), | ||
expected = {newVal:{}, oldVal: null} | ||
store.on('', function(v,k,o) { | ||
t('===', this, store) | ||
t('{===}', v, expected.newVal) | ||
t('===', k, null) | ||
t('{===}', o, expected.oldVal) | ||
t('{===}', store.data, expected.newVal) | ||
}, store) | ||
store.run('init') | ||
expected = {newVal:{yell:true}, oldVal:{}} | ||
store.run('yell', true) | ||
expected = {newVal:{yell:true, sing:true}, oldVal:{yell:true}} | ||
store.run('sing', 2) | ||
expected = {newVal:{}, oldVal:{yell:true, sing:true}} | ||
store.run('stop') | ||
}) | ||
t('store - errors', function() { | ||
var store = create(null, { | ||
set: function(p,v) { this.set(p,v) }, | ||
del: function(p) { this.delete(p) } | ||
}) | ||
var store = new Store(null) | ||
@@ -136,6 +92,6 @@ store.on('', function() { | ||
t('===', store.run('set', 'a/b', 9) instanceof Error, true) | ||
t('===', store.run('del', 'a/b') instanceof Error, true) | ||
var res = store.run('set', 'a/b', 9) | ||
t('===', store.set('a/b', 9) instanceof Error, true) | ||
t('===', store.set('a/b') instanceof Error, true) | ||
var res = store.run([{path:'a/b', data:9}]) | ||
t('===', res instanceof Error && res.message, 'invalid path: a/b') | ||
}) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
0
26751
3
15
889
66