Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

attostore

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

attostore - npm Package Compare versions

Comparing version 0.3.0 to 0.4.0

src/_store.js

357

browser.js
/* hugov@runbox.com | https://github.com/hville/attostore.git | license:MIT */
var attostore = (function () {
(function (exports) {
'use strict';

@@ -19,97 +19,54 @@

function getKey(obj, key) {
if (isObj(obj)) return obj[key]
function missingKeys(reference, value) {
var keys = isObj(reference) ? Object.keys(reference) : [];
return isObj(value) ? keys.filter(filterVoid, value) : keys
}
function pathKeys(path) {
return Array.isArray(path) ? path : (path && path.split) ? path.split('/') : cType(path) === Number ? [path] : []
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 once(key, fcn, ctx) {
var wrap = fcn.length > 2
? function(a,b,c,d,e) { this.off(key, wrap, this); fcn.call(ctx, a,b,c,d,e); }
: function(a,b) { this.off(key, wrap, this); fcn.call(ctx, a,b); };
return this.on(key, wrap, this)
function filterVoid(k) {
return this[k] === undefined
}
/*
patchAsync: function(patch, ondone) {
return promisify(setTimeout, [patchSync, 0, this, patch], ondone)
},
function getKey(obj, key) {
if (isObj(obj)) return obj[key]
}
patchSync: function(patch, ondone) {
return promisify(patchSync, [this, patch], ondone)
}
*/
function promisify(fcn, args, cb) {
// avoids promises and return void if a callback is provided
if (cb) fcn.apply(null, args.concat(cb));
// return a promise only if no callback provided
else return new Promise(function(done, fail) {
fcn.apply(null, args.concat(function(err, res) {
if (err) fail(err);
else done(res);
}));
})
function pathKeys(path) {
var ct = cType(path);
return ct === Array ? path : ct === Number ? [path] : !path ? [] : path.split('/')
}
/**
* @constructor
* @param {!Object} root
* @param {!Array} keys
* deep Equal check on JSON-like objects
* @function
* @param {*} obj - object to check
* @param {*} ref - the reference
* @return {boolean|void} true if equal
*/
function Ref(root, keys) {
this.store = root;
this._ks = keys;
}
Ref.prototype = {
get parent() { return new Ref(this.store, this._ks.slice(0,-1)) },
get root() { return new Ref(this.store, []) },
keys: function(path) {
return this._ks.concat(pathKeys(path))
},
/**
* @memberof Ref
* @param {Array|string} [path]
* @return {!Object}
*/
ref: function(path) {
return new Ref(this.store, this.keys(path))
},
on: function(path, fcn, ctx) {
this.store.on(this.keys(path), fcn, ctx);
return this
},
off: function(path, fcn, ctx) {
this.store.off(this.keys(path), fcn, ctx);
return this
},
once: once,
set: function(path, val, ondone) {
return promisify(setTimeout, [storeSet, 0, this.store, this.keys(path), val], ondone)
},
del: function(path, ondone) {
return promisify(setTimeout, [storeSet, 0, this.store, this.keys(path), undefined], ondone)
},
query: function(transform) {
var query = new Trie;
query._set(transform(this.store.data));
this.store.on(this._ks, function(v) { query._set(transform(v)); });
return query
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 storeSet(src, key, val, cb) {
return src.patch([val === undefined ? {k:key} : {k:key, v:val}], cb)
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
}

@@ -119,68 +76,68 @@

* @constructor
* @param {*} [data]
*/
function Trie() {
function Store(data) {
this._ks = new Map;
this._fs = [];
this.data = undefined;
this.data = data;
}
/**
* @memberof Store
* @param {Array|string|number} [path]
* @param {Array|string|number} key
* @param {Function} fcn
* @param {*} [ctx]
* @return {!Object}
*/
Trie.prototype = {
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||null});
return this
};
ref: function(path) {
return new Ref(this, pathKeys(path))
},
/**
* @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
};
on: function(key, fcn, ctx) {
var leaf = set(this, pathKeys(key)),
list = leaf._fs;
if (indexOf(list, fcn, ctx) === -1) list.push({f: fcn, c:ctx||null});
return this
},
/**
* @param {Array|string|number} key
* @param {Function} fcn
* @param {*} [ctx]
* @return {!Object}
*/
Store.prototype.once = function(key, fcn, ctx) {
function wrap(a,b) {
this.off(key, wrap, this);
fcn.call(ctx, a,b);
}
return this.on(key, wrap, this)
};
off: function(key, fcn, ctx) {
var keys = pathKeys(key),
itm = get(this, keys),
arr = itm && itm._fs,
idx = indexOf(arr, fcn, ctx);
if (idx !== -1) {
arr.splice(idx, 1);
if (!arr.length && !itm._ks.size) del(this, keys, 0);
}
return this
},
Store.prototype.set = set;
once: once,
/**
* @param {*} val
* @return {void}
*/
_set: function(val) {
if (val !== this.data) {
var old = this.data,
dif = null;
// update kids first
this._ks.forEach(updateKid, val);
// update self
this.data = val;
for (var i=0, fs=this._fs; i<fs.length; ++i) {
var fcn = fs[i].f;
//compute changes only once and only if required
if (fcn.length > 2) {
if (!dif) dif = compare(val, old);
fcn.call(fs[i].c, val, old, dif[0], dif[1], dif[2]);
}
else fcn.call(fs[i].c, val, old);
}
}
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
};
function get(root, keys) {
function getLeaf(root, keys) {
for (var i=0, itm = root; i<keys.length; ++i) {

@@ -192,6 +149,6 @@ if (itm !== undefined) itm = itm._ks.get(''+keys[i]);

function set(root, keys) {
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 Trie);
if (!itm._ks.has(key)) itm._ks.set(key, new Store(getKey(itm.data, key)));
itm = itm._ks.get(key);

@@ -202,7 +159,7 @@ }

function del(trie, keys, idx) {
function delLeaf(trie, keys, idx) {
var key = keys[idx++],
kid = trie._ks.get(key);
if (kid) {
if (idx !== keys.length) del(kid, keys, idx);
if (idx !== keys.length) delLeaf(kid, keys, idx);
if (!kid._ks.size && !kid._fs.length) trie._ks.delete(key);

@@ -217,71 +174,20 @@ }

function compare(val, old) {
var kvs = isObj(val) ? Object.keys(val) : [],
kos = isObj(old) ? Object.keys(old) : [];
if (!kvs.length || !kos.length) return [kvs, [], kos]
var dif = [[],[],[]];
for (var i=0; i<kvs.length; ++i) {
if (old[kvs[i]] === undefined) dif[0].push(kvs[i]); // added
if (val[kvs[i]] !== old[kvs[i]]) dif[1].push(kvs[i]); // changed
function set(acts, ondone) {
var data = Array.isArray(acts) ? acts.reduce(setRed, this.data) : setRed(this.data, acts);
if (data instanceof Error) {
if (!ondone) return Promise.reject(data)
ondone(data);
return
}
for (var j=0; j<kos.length; ++j) {
if (val[kos[j]] === undefined) dif[2].push(kos[j]); // removed
}
return dif
var done = data === this.data ? null : acts;
update(this, data);
if (!ondone) return Promise.resolve(done)
ondone(null, done);
}
function updateKid(kid, k) {
kid._set(getKey(this, k));
function setRed(res, act) {
return res instanceof Error ? res : setKeys(res, pathKeys(act.key), act.val, 0)
}
/**
* 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
}
// @ts-check
var module$1 = function (initValue) {
var root = new Trie();
root.patch = patch;
root._set(initValue);
return new Ref(root, [])
};
function patch(acts, done) {
var newV = this.data;
for (var i=0; i<acts.length; ++i) {
newV = setPath(newV, acts[i].k, acts[i].v, 0);
if (newV instanceof Error) {
done(newV);
return
}
}
if (newV !== this.data) {
this._set(newV);
done(null, acts);
}
else done(null, null);
}
/**

@@ -294,3 +200,3 @@ * @param {*} obj

*/
function setPath(obj, keys, val, idx) {
function setKeys(obj, keys, val, idx) {
if (val instanceof Error) return val

@@ -302,9 +208,7 @@

// recursive calls to end of path
if (!isObj(obj)) return Error('invalid path ' + keys.join('/'))
if (!isObj(obj)) return Error('invalid path: ' + keys.join('.'))
var k = keys[idx],
o = obj[k],
v = setPath(o, keys, val, idx+1);
return v === o ? obj
: Array.isArray(obj) ? aSet(obj, +k, v)
: oSet(obj, k, v)
v = setKeys(o, keys, val, idx+1);
return v === o ? obj : Array.isArray(obj) ? aSet(obj, +k, v) : oSet(obj, k, v)
}

@@ -326,3 +230,3 @@

}
if (key < arr.length) {
if (key <= arr.length) {
tgt[key] = val;

@@ -346,4 +250,35 @@ return tgt

return module$1;
/**
* @param {!Object} store
* @param {*} val
* @return {void}
*/
function update(store, val) {
if (val !== store.data) {
var old = store.data;
store.data = val;
}());
// fire kids first...
store._ks.forEach(updateKid, val);
// ...then self
for (var i=0, fs=store._fs; i<fs.length; ++i) {
fs[i].f.call(fs[i].c, val, old);
}
}
}
function updateKid(kid, k) {
update(kid, getKey(this, k));
}
// @ts-check
// @ts-check
function createStore(initialValue) {
return new Store(initialValue)
}
exports.createStore = createStore;
exports.changedKeys = changedKeys;
exports.missingKeys = missingKeys;
}((this.attostore = this.attostore || {})));
/* hugov@runbox.com | https://github.com/hville/attostore.git | license:MIT */
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
/**

@@ -18,97 +20,54 @@ * @function

function getKey(obj, key) {
if (isObj(obj)) return obj[key]
function missingKeys(reference, value) {
var keys = isObj(reference) ? Object.keys(reference) : [];
return isObj(value) ? keys.filter(filterVoid, value) : keys
}
function pathKeys(path) {
return Array.isArray(path) ? path : (path && path.split) ? path.split('/') : cType(path) === Number ? [path] : []
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 once(key, fcn, ctx) {
var wrap = fcn.length > 2
? function(a,b,c,d,e) { this.off(key, wrap, this); fcn.call(ctx, a,b,c,d,e); }
: function(a,b) { this.off(key, wrap, this); fcn.call(ctx, a,b); };
return this.on(key, wrap, this)
function filterVoid(k) {
return this[k] === undefined
}
/*
patchAsync: function(patch, ondone) {
return promisify(setTimeout, [patchSync, 0, this, patch], ondone)
},
function getKey(obj, key) {
if (isObj(obj)) return obj[key]
}
patchSync: function(patch, ondone) {
return promisify(patchSync, [this, patch], ondone)
}
*/
function promisify(fcn, args, cb) {
// avoids promises and return void if a callback is provided
if (cb) fcn.apply(null, args.concat(cb));
// return a promise only if no callback provided
else return new Promise(function(done, fail) {
fcn.apply(null, args.concat(function(err, res) {
if (err) fail(err);
else done(res);
}));
})
function pathKeys(path) {
var ct = cType(path);
return ct === Array ? path : ct === Number ? [path] : !path ? [] : path.split('/')
}
/**
* @constructor
* @param {!Object} root
* @param {!Array} keys
* deep Equal check on JSON-like objects
* @function
* @param {*} obj - object to check
* @param {*} ref - the reference
* @return {boolean|void} true if equal
*/
function Ref(root, keys) {
this.store = root;
this._ks = keys;
}
Ref.prototype = {
get parent() { return new Ref(this.store, this._ks.slice(0,-1)) },
get root() { return new Ref(this.store, []) },
keys: function(path) {
return this._ks.concat(pathKeys(path))
},
/**
* @memberof Ref
* @param {Array|string} [path]
* @return {!Object}
*/
ref: function(path) {
return new Ref(this.store, this.keys(path))
},
on: function(path, fcn, ctx) {
this.store.on(this.keys(path), fcn, ctx);
return this
},
off: function(path, fcn, ctx) {
this.store.off(this.keys(path), fcn, ctx);
return this
},
once: once,
set: function(path, val, ondone) {
return promisify(setTimeout, [storeSet, 0, this.store, this.keys(path), val], ondone)
},
del: function(path, ondone) {
return promisify(setTimeout, [storeSet, 0, this.store, this.keys(path), undefined], ondone)
},
query: function(transform) {
var query = new Trie;
query._set(transform(this.store.data));
this.store.on(this._ks, function(v) { query._set(transform(v)); });
return query
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 storeSet(src, key, val, cb) {
return src.patch([val === undefined ? {k:key} : {k:key, v:val}], cb)
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
}

@@ -118,68 +77,68 @@

* @constructor
* @param {*} [data]
*/
function Trie() {
function Store(data) {
this._ks = new Map;
this._fs = [];
this.data = undefined;
this.data = data;
}
/**
* @memberof Store
* @param {Array|string|number} [path]
* @param {Array|string|number} key
* @param {Function} fcn
* @param {*} [ctx]
* @return {!Object}
*/
Trie.prototype = {
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||null});
return this
};
ref: function(path) {
return new Ref(this, pathKeys(path))
},
/**
* @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
};
on: function(key, fcn, ctx) {
var leaf = set(this, pathKeys(key)),
list = leaf._fs;
if (indexOf(list, fcn, ctx) === -1) list.push({f: fcn, c:ctx||null});
return this
},
/**
* @param {Array|string|number} key
* @param {Function} fcn
* @param {*} [ctx]
* @return {!Object}
*/
Store.prototype.once = function(key, fcn, ctx) {
function wrap(a,b) {
this.off(key, wrap, this);
fcn.call(ctx, a,b);
}
return this.on(key, wrap, this)
};
off: function(key, fcn, ctx) {
var keys = pathKeys(key),
itm = get(this, keys),
arr = itm && itm._fs,
idx = indexOf(arr, fcn, ctx);
if (idx !== -1) {
arr.splice(idx, 1);
if (!arr.length && !itm._ks.size) del(this, keys, 0);
}
return this
},
Store.prototype.set = set;
once: once,
/**
* @param {*} val
* @return {void}
*/
_set: function(val) {
if (val !== this.data) {
var old = this.data,
dif = null;
// update kids first
this._ks.forEach(updateKid, val);
// update self
this.data = val;
for (var i=0, fs=this._fs; i<fs.length; ++i) {
var fcn = fs[i].f;
//compute changes only once and only if required
if (fcn.length > 2) {
if (!dif) dif = compare(val, old);
fcn.call(fs[i].c, val, old, dif[0], dif[1], dif[2]);
}
else fcn.call(fs[i].c, val, old);
}
}
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
};
function get(root, keys) {
function getLeaf(root, keys) {
for (var i=0, itm = root; i<keys.length; ++i) {

@@ -191,6 +150,6 @@ if (itm !== undefined) itm = itm._ks.get(''+keys[i]);

function set(root, keys) {
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 Trie);
if (!itm._ks.has(key)) itm._ks.set(key, new Store(getKey(itm.data, key)));
itm = itm._ks.get(key);

@@ -201,7 +160,7 @@ }

function del(trie, keys, idx) {
function delLeaf(trie, keys, idx) {
var key = keys[idx++],
kid = trie._ks.get(key);
if (kid) {
if (idx !== keys.length) del(kid, keys, idx);
if (idx !== keys.length) delLeaf(kid, keys, idx);
if (!kid._ks.size && !kid._fs.length) trie._ks.delete(key);

@@ -216,71 +175,20 @@ }

function compare(val, old) {
var kvs = isObj(val) ? Object.keys(val) : [],
kos = isObj(old) ? Object.keys(old) : [];
if (!kvs.length || !kos.length) return [kvs, [], kos]
var dif = [[],[],[]];
for (var i=0; i<kvs.length; ++i) {
if (old[kvs[i]] === undefined) dif[0].push(kvs[i]); // added
if (val[kvs[i]] !== old[kvs[i]]) dif[1].push(kvs[i]); // changed
function set(acts, ondone) {
var data = Array.isArray(acts) ? acts.reduce(setRed, this.data) : setRed(this.data, acts);
if (data instanceof Error) {
if (!ondone) return Promise.reject(data)
ondone(data);
return
}
for (var j=0; j<kos.length; ++j) {
if (val[kos[j]] === undefined) dif[2].push(kos[j]); // removed
}
return dif
var done = data === this.data ? null : acts;
update(this, data);
if (!ondone) return Promise.resolve(done)
ondone(null, done);
}
function updateKid(kid, k) {
kid._set(getKey(this, k));
function setRed(res, act) {
return res instanceof Error ? res : setKeys(res, pathKeys(act.key), act.val, 0)
}
/**
* 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
}
// @ts-check
var module$1 = function (initValue) {
var root = new Trie();
root.patch = patch;
root._set(initValue);
return new Ref(root, [])
};
function patch(acts, done) {
var newV = this.data;
for (var i=0; i<acts.length; ++i) {
newV = setPath(newV, acts[i].k, acts[i].v, 0);
if (newV instanceof Error) {
done(newV);
return
}
}
if (newV !== this.data) {
this._set(newV);
done(null, acts);
}
else done(null, null);
}
/**

@@ -293,3 +201,3 @@ * @param {*} obj

*/
function setPath(obj, keys, val, idx) {
function setKeys(obj, keys, val, idx) {
if (val instanceof Error) return val

@@ -301,9 +209,7 @@

// recursive calls to end of path
if (!isObj(obj)) return Error('invalid path ' + keys.join('/'))
if (!isObj(obj)) return Error('invalid path: ' + keys.join('.'))
var k = keys[idx],
o = obj[k],
v = setPath(o, keys, val, idx+1);
return v === o ? obj
: Array.isArray(obj) ? aSet(obj, +k, v)
: oSet(obj, k, v)
v = setKeys(o, keys, val, idx+1);
return v === o ? obj : Array.isArray(obj) ? aSet(obj, +k, v) : oSet(obj, k, v)
}

@@ -325,3 +231,3 @@

}
if (key < arr.length) {
if (key <= arr.length) {
tgt[key] = val;

@@ -345,2 +251,33 @@ return tgt

module.exports = module$1;
/**
* @param {!Object} store
* @param {*} val
* @return {void}
*/
function update(store, val) {
if (val !== store.data) {
var old = store.data;
store.data = val;
// fire kids first...
store._ks.forEach(updateKid, val);
// ...then self
for (var i=0, fs=store._fs; i<fs.length; ++i) {
fs[i].f.call(fs[i].c, val, old);
}
}
}
function updateKid(kid, k) {
update(kid, getKey(this, k));
}
// @ts-check
// @ts-check
function createStore(initialValue) {
return new Store(initialValue)
}
exports.createStore = createStore;
exports.changedKeys = changedKeys;
exports.missingKeys = missingKeys;
// @ts-check
import {Trie} from './src/_trie'
import {Ref} from './src/_ref'
import {isEqual} from './src/is-eq'
import {isObj} from './src/type'
export {changedKeys, missingKeys} from './src/compare'
export default function (initValue) {
var root = new Trie()
root.patch = patch
root._set(initValue)
return new Ref(root, [])
}
// @ts-check
import {Store} from './src/_store'
function patch(acts, done) {
var newV = this.data
for (var i=0; i<acts.length; ++i) {
newV = setPath(newV, acts[i].k, acts[i].v, 0)
if (newV instanceof Error) {
done(newV)
return
}
}
if (newV !== this.data) {
this._set(newV)
done(null, acts)
}
else done(null, null)
export function createStore(initialValue) {
return new Store(initialValue)
}
/**
* @param {*} obj
* @param {!Array} keys
* @param {*} val
* @param {number} idx
* @return {*}
*/
function setPath(obj, keys, val, idx) {
if (val instanceof Error) return val
// 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 = setPath(o, keys, val, idx+1)
return 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
}
{
"name": "attostore",
"version": "0.3.0",
"version": "0.4.0",
"description": "async json data store",

@@ -5,0 +5,0 @@ "keywords": [

# attodom
*small async json in-memory store with cursors and events, < 3kb min, < 2kb gz*
*small async json in-memory store with path events, ~3kb min, ~1kb gz*

@@ -17,33 +17,31 @@ • [Example](#example) • [Why](#why) • [API](#api) • [License](#license)

store.ref().on('b/*', function(val, old, key, obj) {
console.log('key "c" changed')
store.on('b/c', function(val, old) {
console.log('key "c" changed from',old.c,'to',val.c)
})
store.ref(['b', 'c']).on('', function(val, old, key, obj) {
console.log('key "c" changed')
store.set({key: ['b', 'c'], val: 'newValue'}, function(err, act) {
if (!err) console.log(!!act ? 'changed' : 'not changed')
})
store.ref('b/c').set('newValue').then(function(patch) {
console.log(patch && patch.length ? 'changed' : 'not changed')
store.set([{key: 'b/c', val:'anotherValue'}]).then(function(act) {
if (act) console.log(act.length 'changes')
})
store.ref(['b', 'c']).set('anotherValue', function(err, acts) {
if (!err) console.log(patch && patch.length ? 'changed' : 'not changed')
})
```
supports different environments
* CJS: `var create = require('attostore')`
* ES modules: `import create from 'attostore'`
* browser: `var create = window.attostore`
* CJS: `var create = require('attostore').createStore`
* ES modules: `import {createStore} from 'attostore'`
* browser: `var create = window.attostore.createStore`
### Features and Limitations
### Features, Limitations, Gotcha
* set operations are async to let other external queued operation first
* available in CommonJS, ES6 modules and browser versions
* no Promise polyfill included. Not required if callbacks are provided
* no Promise polyfill included. Not required if callbacks are provided to the set function
* only the last item of an Array can be deleted to avoid shifting of keys
* No Array splicing to keep the keys unchanged. additions and removals from the end only (eg. push pop)
* only JSON types supported (Array, Object, string, number, boolean, null)
* set triggers a deletion if the value is undefined and/or absent
## API

@@ -55,29 +53,16 @@

store.ref(path: `Array|string|number`): `Ref`
store.patch(acts: `Array`, ondone: `(err, acts) => void`]): `void`
.set(acts: `Action|Actions`, ondone: `(err, acts) => void`): `void`
.set(acts: `Action|Actions`): `Promise`
### Ref
.on(path: `Path`, handler: `(val, old, key)=>void`, [, context: `any`]): `Ref`
.once(path: `Path`, handler: `(val, old, key)=>void`, [, context: `any`]): `Ref`
.off(path: `Path`, handler: `(val, old, key)=>void`, [, context: `any`]): `Ref`
ref.root: `Ref`
ref.parent: `Ref`
### Acts
ref.keys(path: `Path`): `Array`
Simple patch format for atomic changes:
* single or multiple changes: `[{key: path, val: 'c'}]`, `{key: path, val: 'c'}`
* set if a value is present, delete is the value is missing or undefined
* the store is only changed if all actions pass without errors
ref.set(path: `Path`, value: `any`, ondone: `(err, acts) => void`): `void`
ref.set(path: `Path`, value: `any`): `Promise`
ref.del(path: `Path`, ondone: `(err, acts) => void`): `void`
ref.del(path: `Path`): `Promise`
ref.on(path: `Path`, handler: `(val, old, key)=>void`, [, context: `any`]): `Ref`
ref.once(path: `Path`, handler: `(val, old, key)=>void`, [, context: `any`]): `Ref`
ref.off(path: `Path`, handler: `(val, old, key)=>void`, [, context: `any`]): `Ref`
ref.query(transform: `any => any`): `Query`
### Query
query.on(path: `Path`, handler: `(val, old, key)=>void`, [, context: `any`]): `Ref`
query.once(path: `Path`, handler: `(val, old, key)=>void`, [, context: `any`]): `Ref`
query.off(path: `Path`, handler: `(val, old, key)=>void`, [, context: `any`]): `Ref`
### Path

@@ -89,18 +74,5 @@

* `a/b`, `["a", "b"]` are equivalent
* wildcards can be used for listeners: `ref.on('*/id', cb)`
### Acts
Simple patch format for atomic changes:
* array of keys or string path: `[{k: 'a/b/c', v: 'abc'}, {k:['a','b','c']}]`
* set if a value is present, delete is the value is missing
### Gotcha
* No Array splicing to keep the keys unchanged. additions and removals from the end only (eg. push pop)
## License
[MIT](http://www.opensource.org/licenses/MIT) © [Hugo Villeneuve](https://github.com/hville)
import {cType} from './type'
export function pathKeys(path) {
return Array.isArray(path) ? path : (path && path.split) ? path.split('/') : cType(path) === Number ? [path] : []
var ct = cType(path)
return ct === Array ? path : ct === Number ? [path] : !path ? [] : path.split('/')
}

@@ -1,23 +0,28 @@

// @ts-check
var DB = require( '../index' ),
var S = require( '../index' ),
t = require('cotest')
function compare(v,o,as,ms,ds) {
var create = S.createStore,
changed = S.changedKeys,
missing = S.missingKeys
function compare(v,o) {
t('!==', v, o, 'child event only if value changed: ')
t('{===}', as, this[0])
t('{===}', ms, this[1])
t('{===}', ds, this[2])
if (this) {
t('{==}', missing(v,o), this[0], 'added keys')
t('{==}', changed(v,o), this[1], 'changed keys'+v+'|'+o)// TODO keys string vs No
t('{==}', missing(o,v), this[2], 'deleted keys')
}
}
t('ref - added keys', function(end) {
var ref = DB()
var store = create()
ref.ref('aa').once('',compare, [['bb','b'],[],[]])
ref.once('aa/bb', compare, [['cc','c'],[],[]])
ref.ref('aa').once('bb/cc', compare, [[],[],[]])
ref.on('',compare, [['aa','a'],[],[]])
store.once('aa',compare, [['bb','b'],[],[]])
store.once('aa/bb', compare, [['cc','c'],[],[]])
store.once('aa/bb/cc', compare, [[],[],[]])
store.on('',compare, [['aa','a'],[],[]])
ref.set('', {aa: {bb:{cc:{}, c: 'c'}, b: 'b'}, a:'a'}, function(err) {
store.set([{val: {aa: {bb:{cc:{}, c: 'c'}, b: 'b'}, a:'a'}}], function(err) {
t('!', err)
t('{===}', ref.store.data, {aa: {bb:{cc:{}, c: 'c'}, b: 'b'}, a:'a'})
t('{===}', store.data, {aa: {bb:{cc:{}, c: 'c'}, b: 'b'}, a:'a'})
end()

@@ -28,12 +33,12 @@ })

t('ref - del keys', function(end) {
var ref = DB({aa: {bb:{cc:{}, c: 'c'}, b: 'b'}, a:'a'})
var store = create({aa: {bb:{cc:{}, c: 'c'}, b: 'b'}, a:'a'})
ref.once('aa', compare, [[],[],['bb','b']])
ref.once('aa/bb', compare, [[],[],['cc','c']])
ref.ref('aa/bb/cc').once('',compare, [[],[],[]])
ref.on('',compare, [[],[],['aa','a']])
store.once('aa', compare, [[],[],['bb','b']])
store.once('aa/bb', compare, [[],[],['cc','c']])
store.once('aa/bb/cc',compare, [[],[],[]])
store.on('',compare, [[],[],['aa','a']])
ref.del('',function(err) {
store.set({},function(err) {
t('!', err)
t('{===}', ref.store.data, undefined)
t('{===}', store.data, undefined)
end()

@@ -44,12 +49,53 @@ })

t('db - query', function(end) {
var ref = DB([1,2,3,0]),
xfo = ref.query(function(v) { return v.slice().sort() })
var store = create({data: {list: [1,0]}, view:{}, acts:{}, done:[]})
xfo.once('',compare, [[],[0,3],[]])
ref.set('',[1,2,3,0], function(err) {
store.on('data/list', function(v) {
store.set({key:'view/sort', val: v.slice().sort()})
})
store.on('acts/push', function(v) {
store.set([{key: 'data/list', val: store.get('data/list').concat(v)}])
})
store.once('data/list',compare, [[2],[],[]])
store.once('view/sort',compare, [[0,1,2],[],[]])
store.once('acts/push',compare, [[],[],[]])
store.set({key: 'acts/push', val: 9}, function(err) {
t('!', err)
t('{===}', ref.store.data, [1,2,3,0])
t('{===}', xfo.data, [0,1,2,3])
t('{===}', store.data.data.list, [1,0,9])//[1,0]
t('{===}', store.data.view.sort, [0,1,9])//undefined
t('{===}', store.data.acts.push, 9)
end()
})
})
t('db - actions', function() {
var store = create(),
history = [],
actions = {
init: function() { return { val:[] }},
push: function(v) { var arr = this.get(); return {key: arr.length, val: v}}
},
expected = {newVal:[], oldVal:undefined, history:[]}
store.act = function(name, val) {
this.set(actions[name].call(this, val), function(err, res) {
if (!err && !res) return
var act = {act: name}
if (val !== undefined) act.arg = val
if (err) act.err = err.message
history.push(act)
})
}
store.on('', function(v,o) {
t('{===}', v, expected.newVal)
t('{===}', o, expected.oldVal)
t('{===}', history, expected.history)
t('{===}', store.data, expected.newVal)
})
store.act('init')
expected = {newVal:[4], oldVal:[], history:[{act: 'init'}]}
store.act('push', 4)
expected = {newVal:[4,2], oldVal:[4], history:[{act: 'init'}, {act: 'push', arg:4}]}
store.act('push', 2)
})
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc