mirror of
https://github.com/amark/gun.git
synced 2025-06-03 20:56:43 +00:00
unit tests passing
This commit is contained in:
parent
3127b41f38
commit
0936c326f8
86
gun.js
86
gun.js
@ -97,9 +97,9 @@
|
||||
return !o? o : JSON.parse(JSON.stringify(o)); // is shockingly faster than anything else, and our data has to be a subset of JSON anyways!
|
||||
}
|
||||
;(function(){
|
||||
function empty(v,i){ var n = this.n;
|
||||
function empty(v,i){ var n = this.n, u;
|
||||
if(n && (i === n || (obj_is(n) && obj_has(n, i)))){ return }
|
||||
if(i){ return true }
|
||||
if(u !== i){ return true }
|
||||
}
|
||||
Type.obj.empty = function(o, n){
|
||||
if(!o){ return true }
|
||||
@ -115,20 +115,21 @@
|
||||
} t.r = t.r || [];
|
||||
t.r.push(k);
|
||||
};
|
||||
var keys = Object.keys, map;
|
||||
var keys = Object.keys, map, u;
|
||||
Object.keys = Object.keys || function(o){ return map(o, function(v,k,t){t(k)}) }
|
||||
Type.obj.map = map = function(l, c, _){
|
||||
var u, i = 0, x, r, ll, lle, f = fn_is(c);
|
||||
t.r = null;
|
||||
var u, i = 0, x, r, ll, lle, f = 'function' == typeof c;
|
||||
t.r = u;
|
||||
if(keys && obj_is(l)){
|
||||
ll = keys(l); lle = true;
|
||||
}
|
||||
_ = _ || {};
|
||||
if(list_is(l) || ll){
|
||||
x = (ll || l).length;
|
||||
for(;i < x; i++){
|
||||
var ii = (i + Type.list.index);
|
||||
if(f){
|
||||
r = lle? c.call(_ || this, l[ll[i]], ll[i], t) : c.call(_ || this, l[i], ii, t);
|
||||
r = lle? c.call(_, l[ll[i]], ll[i], t) : c.call(_, l[i], ii, t);
|
||||
if(r !== u){ return r }
|
||||
} else {
|
||||
//if(Type.test.is(c,l[i])){ return ii } // should implement deep equality testing!
|
||||
@ -171,7 +172,7 @@
|
||||
tmp.next(arg);
|
||||
}}
|
||||
}});
|
||||
if(arg instanceof Function){
|
||||
if('function' == typeof arg){
|
||||
var be = {
|
||||
off: onto.off ||
|
||||
(onto.off = function(){
|
||||
@ -323,7 +324,7 @@
|
||||
Node.ify = function(obj, o, as){ // returns a node from a shallow object.
|
||||
if(!o){ o = {} }
|
||||
else if(typeof o === 'string'){ o = {soul: o} }
|
||||
else if(o instanceof Function){ o = {map: o} }
|
||||
else if('function' == typeof o){ o = {map: o} }
|
||||
if(o.map){ o.node = o.map.call(as, obj, u, o.node || {}) }
|
||||
if(o.node = Node.soul.ify(o.node || {}, o)){
|
||||
obj_map(obj, map, {o:o,as:as});
|
||||
@ -464,9 +465,13 @@
|
||||
if(typeof env === 'string'){
|
||||
env = {soul: env};
|
||||
} else
|
||||
if(env instanceof Function){
|
||||
if('function' == typeof env){
|
||||
env.map = env;
|
||||
}
|
||||
if(typeof as === 'string'){
|
||||
env.soul = env.soul || as;
|
||||
as = u;
|
||||
}
|
||||
if(env.soul){
|
||||
at.link = Val.link.ify(env.soul);
|
||||
}
|
||||
@ -681,7 +686,8 @@
|
||||
at.on('in', root, at);
|
||||
at.on('in2', root2, at);
|
||||
at.on('put2', map, at);
|
||||
at.on('out', root, {at: at, out: root});
|
||||
//at.on('out', root, {at: at, out: root});
|
||||
at.on('out', root2, at);
|
||||
Gun.on('create', at);
|
||||
at.on('create', at);
|
||||
}
|
||||
@ -704,10 +710,10 @@
|
||||
if(tmp = msg['@']){ dup.track(tmp) } // HNPERF: Bump original request's liveliness.
|
||||
if(!at.ask(tmp, msg)){
|
||||
if(msg.get){
|
||||
Gun.on.get(msg, gun); //at.on('get', get(msg));
|
||||
Gun.on._get(msg, gun); //at.on('get', get(msg));
|
||||
}
|
||||
if(msg.put){
|
||||
Gun.on.put(msg, gun); //at.on('put', put(msg));
|
||||
//Gun.on._put(msg, gun); //at.on('put', put(msg));
|
||||
}
|
||||
}
|
||||
ev.to.next(msg);
|
||||
@ -718,6 +724,7 @@
|
||||
}
|
||||
function root2(msg){
|
||||
if(!msg){ return }
|
||||
if(msg.out === root2 || msg.out === root){ this.to.next(msg); return }
|
||||
var eve = this, as = eve.as, at = as.at || as, gun = at.$, dup = at.dup, tmp;
|
||||
if(!(tmp = msg['#'])){ tmp = msg['#'] = text_rand(9) }
|
||||
if(dup.check(tmp)){ return } dup.track(tmp);
|
||||
@ -741,15 +748,16 @@
|
||||
root.on('in', {'@': id, ok: ok || 1});
|
||||
id = u;
|
||||
};
|
||||
var set = root.set || (root.set = {'':1});
|
||||
all.err = obj_map(put, valid, msg);
|
||||
ctx.node = ctx.state = u;
|
||||
mid = msg = ctx = u;
|
||||
all();
|
||||
}
|
||||
all(); // if synchronous
|
||||
fire(root, ''); // if synchronous
|
||||
} Gun.on.put = put;
|
||||
function valid(node, soul){
|
||||
if(!node){ return ERR+cut(soul)+"no node." }
|
||||
var ctx = this._, tmp;
|
||||
(ctx.root.opt||'').super && ctx.root.$.get(soul); // I think we need super for now, but since we are rewriting, should consider getting rid of it.
|
||||
if(!(tmp = node._)){ return ERR+cut(soul)+"no meta." }
|
||||
ctx.node = node;
|
||||
if(soul !== tmp[_soul]){ return ERR+cut(soul)+"soul not same." }
|
||||
@ -769,6 +777,7 @@
|
||||
var vertex = graph[soul] || empty, was = state_is(vertex, key, 1), known = vertex[key];
|
||||
var machine = State(), is = HAM(machine, state, was, val, known), u;
|
||||
(alls = all.s || (all.s = {}))[id] = 1;
|
||||
(root.set || (root.set = {}))[id] = 1; // tmp code;
|
||||
if(!is.incoming){
|
||||
if(is.defer){
|
||||
var to = is.defer - machine;
|
||||
@ -789,29 +798,46 @@
|
||||
function map(msg){
|
||||
var eve = this, root = eve.as, graph = root.graph, put = msg.put, soul = put['#'], key = put['.'], val = put[':'], state = put['>'], id = msg['#'], tmp;
|
||||
graph[soul] = state_ify(graph[soul], key, state, val, soul);
|
||||
debounce(root, soul, key,val, state); // TODO: This should NOT be how the API works, this should be done at an extension layer, but hacky solution to migrate with old code for now.
|
||||
chain(root, soul, key, (u !== (tmp = put['=']))? tmp : val, state); // TODO: This should NOT be how the API works, this should be done at an extension layer, but hacky solution to migrate with old code for now.
|
||||
fire(root, soul+key); // ^
|
||||
eve.to.next(msg);
|
||||
}
|
||||
function debounce(root, soul, key,val, state){ var tmp;
|
||||
if(!(tmp = root.next[soul]) || !tmp.$){ return }
|
||||
tmp.change = state_ify(tmp.change, key, state, val, soul);
|
||||
function chain(root, soul, key,val, state){ var tmp, put;
|
||||
(root.opt||'').super && root.$.get(soul); // I think we need super for now, but since we are rewriting, should consider getting rid of it.
|
||||
if(!root || !(tmp = root.next) || !(tmp = tmp[soul]) || !tmp.$){ return }
|
||||
(put = root.put || (root.put = {}))[soul] = state_ify(put[soul], key, state, val, soul);
|
||||
tmp.put = state_ify(tmp.put, key, state, val, soul);
|
||||
return;
|
||||
tmp.change = state_ify(tmp.change, key, state, val, soul);
|
||||
if(tmp.wait){ return }
|
||||
var stop = {};
|
||||
tmp.wait = setTimeout(function(){
|
||||
var change = tmp.change; tmp.change = tmp.wait = null;
|
||||
root.stop = this.stop; // temporary fix till a better solution?
|
||||
root.stop = stop; // temporary fix till a better solution?
|
||||
tmp.on('in', {$: tmp.$, get: soul, put: change});
|
||||
root.stop = null; // temporary fix till a better solution?
|
||||
},0);
|
||||
|
||||
}
|
||||
function fire(root, id){ var set;
|
||||
if(set = root.set){ delete set[id] }
|
||||
if(!obj_empty(set)){ return }
|
||||
var stop = {};
|
||||
var next = root.next||'', put = root.put; root.put = root.set = null;
|
||||
Gun.graph.is(put, function(node,soul){ var tmp;
|
||||
if(!(tmp = next[soul]) || !tmp.$){ return }
|
||||
root.stop = stop; // temporary fix till a better solution?
|
||||
tmp.on('in', {$: tmp.$, get: soul, put: node});
|
||||
root.stop = null; // temporary fix till a better solution?
|
||||
})
|
||||
}
|
||||
var ERR = "Error: Invalid graph!";
|
||||
var cut = function(s){ return " '"+(''+s).slice(0,9)+"...' " }
|
||||
var HAM = Gun.HAM, MD = 2147483647, State = Gun.state;
|
||||
}());
|
||||
|
||||
;(function(){
|
||||
Gun.on.put = function(msg, gun){
|
||||
Gun.on._put = function(msg, gun){
|
||||
var at = gun._, ctx = {$: gun, graph: at.graph, put: {}, map: {}, souls: {}, machine: Gun.state(), ack: msg['@'], cat: at, stop: {}};
|
||||
if(!Gun.obj.map(msg.put, perf, ctx)){ return } // HNPERF: performance test, not core code, do not port.
|
||||
if(!Gun.graph.is(msg.put, null, verify, ctx)){ ctx.err = "Error: Invalid graph!" }
|
||||
@ -821,7 +847,7 @@
|
||||
if(u !== ctx.defer){
|
||||
var to = ctx.defer - ctx.machine;
|
||||
setTimeout(function(){
|
||||
Gun.on.put(msg, gun);
|
||||
Gun.on._put(msg, gun);
|
||||
}, to > MD? MD : to ); // setTimeout Max Defer 32bit :(
|
||||
}
|
||||
if(!ctx.diff){ return }
|
||||
@ -894,7 +920,7 @@
|
||||
}
|
||||
function perf(node, soul){ if(node !== this.graph[soul]){ return true } } // HNPERF: do not port!
|
||||
|
||||
Gun.on.get = function(msg, gun){
|
||||
Gun.on._get = function(msg, gun){
|
||||
var root = gun._, get = msg.get, soul = get[_soul], node = root.graph[soul], has = get[_has], tmp;
|
||||
var next = root.next || (root.next = {}), at = next[soul];
|
||||
// queue concurrent GETs?
|
||||
@ -1008,7 +1034,7 @@
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(n instanceof Function){
|
||||
if('function' == typeof n){
|
||||
var yes, tmp = {back: at};
|
||||
while((tmp = tmp.back)
|
||||
&& u === (yes = n(tmp, opt))){}
|
||||
@ -1081,6 +1107,12 @@
|
||||
put._ = meta;
|
||||
back.on('in', {$: back.$, put: put, get: back.get})
|
||||
}
|
||||
if(tmp = at.lex){
|
||||
tmp = (tmp._) || (tmp._ = function(){});
|
||||
if(back.ack < tmp.ask){ tmp.ask = back.ack }
|
||||
if(tmp.ask){ return }
|
||||
tmp.ask = 1;
|
||||
}
|
||||
}
|
||||
root.ask(ack, msg);
|
||||
return root.on('in', msg);
|
||||
@ -1311,7 +1343,7 @@
|
||||
at.on('in', {get: at.get, put: Gun.val.link.ify(get['#']), $: at.$, '@': msg['@']});
|
||||
return;
|
||||
}
|
||||
Gun.on.put(msg, at.root.$);
|
||||
Gun.on._put(msg, at.root.$);
|
||||
}
|
||||
var empty = {}, u;
|
||||
var obj = Gun.obj, obj_has = obj.has, obj_put = obj.put, obj_del = obj.del, obj_to = obj.to, obj_map = obj.map;
|
||||
@ -1331,7 +1363,7 @@
|
||||
}
|
||||
gun = gun.$;
|
||||
} else
|
||||
if(key instanceof Function){
|
||||
if('function' == typeof key){
|
||||
if(true === cb){ return soul(this, key, cb, as), this }
|
||||
gun = this;
|
||||
var at = gun._, root = at.root, tmp = root.now, ev;
|
||||
@ -1367,7 +1399,7 @@
|
||||
if(tmp = this._.stun){ // TODO: Refactor?
|
||||
gun._.stun = gun._.stun || tmp;
|
||||
}
|
||||
if(cb && cb instanceof Function){
|
||||
if(cb && 'function' == typeof cb){
|
||||
gun.get(cb, as);
|
||||
}
|
||||
return gun;
|
||||
|
559
lib/radisk.js
559
lib/radisk.js
@ -40,159 +40,57 @@
|
||||
1. Because writing to disk takes time, we should batch data to disk. This improves performance, and reduces potential disk corruption.
|
||||
2. If a batch exceeds a certain number of writes, we should immediately write to disk when physically possible. This caps total performance, but reduces potential loss.
|
||||
*/
|
||||
var r = function(key, val, cb){
|
||||
key = ''+key;
|
||||
if(val instanceof Function){
|
||||
var r = function(key, data, cb){
|
||||
if('function' === typeof data){
|
||||
var o = cb || {};
|
||||
cb = val;
|
||||
var S; LOG && (S = +new Date);
|
||||
val = r.batch(key);
|
||||
LOG && (ST = +new Date - S) > 9 && opt.log(S, ST, 'rad mem');
|
||||
if(u !== val){
|
||||
cb(u, r.range(val, o), o);
|
||||
if(atomic(val)){ return }
|
||||
// if a node is requested and some of it is cached... the other parts might not be.
|
||||
}
|
||||
if(r.thrash.at){
|
||||
val = r.thrash.at(key);
|
||||
if(u !== val){
|
||||
cb(u, r.range(val, o), o);
|
||||
if(atomic(val)){ cb(u, val, o); return }
|
||||
// if a node is requested and some of it is cached... the other parts might not be.
|
||||
}
|
||||
}
|
||||
return r.read(key, cb, o);
|
||||
}
|
||||
r.batch(key, val);
|
||||
if(cb){ r.batch.acks.push(cb) }
|
||||
if(++r.batch.ed >= opt.batch){ return r.thrash() } // (2)
|
||||
if(r.batch.to){ return }
|
||||
//clearTimeout(r.batch.to); // (1) // THIS LINE IS EVIL! NEVER USE IT! ALSO NEVER DELETE THIS SO WE NEVER MAKE THE SAME MISTAKE AGAIN!
|
||||
r.batch.to = setTimeout(r.thrash, opt.until || 1);
|
||||
}
|
||||
|
||||
r.batch = Radix();
|
||||
r.batch.acks = [];
|
||||
r.batch.ed = 0;
|
||||
|
||||
r.thrash = function(){
|
||||
var thrash = r.thrash;
|
||||
if(thrash.ing){ return thrash.more = true }
|
||||
LOG = console.LOG; // dirty place to cheaply update LOG settings over time.
|
||||
thrash.more = false;
|
||||
thrash.ing = true;
|
||||
var batch = thrash.at = r.batch, i = 0;
|
||||
clearTimeout(r.batch.to);
|
||||
r.batch = null;
|
||||
r.batch = Radix();
|
||||
r.batch.acks = [];
|
||||
r.batch.ed = 0;
|
||||
//console.debug(99); var ID = Gun.text.random(2), S = (+new Date); console.log("[[[[[[[[", ID, batch.acks.length);
|
||||
r.save(batch, function(err, ok){
|
||||
if(++i > 1){ opt.log('RAD ERR: Radisk has callbacked multiple times, please report this as a BUG at github.com/amark/gun/issues ! ' + i); return }
|
||||
if(err){ opt.log('err', err) }
|
||||
//console.debug(99); var TMP; console.log("]]]]]]]]", ID, batch.acks.length, (TMP = +new Date) - S, 'more?', thrash.more);
|
||||
map(batch.acks, function(cb){ cb(err, ok) });
|
||||
//console.log("][", +new Date - TMP, thrash.more);
|
||||
thrash.at = null;
|
||||
thrash.ing = false;
|
||||
if(thrash.more){ thrash() }
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
1. Find the first radix item in memory.
|
||||
2. Use that as the starting index in the directory of files.
|
||||
3. Find the first file that is lexically larger than it,
|
||||
4. Read the previous file to that into memory
|
||||
5. Scan through the in memory radix for all values lexically less than the limit.
|
||||
6. Merge and write all of those to the in-memory file and back to disk.
|
||||
7. If file too large, split. More details needed here.
|
||||
*/
|
||||
/* NEW APPROACH:
|
||||
1. For each item in radix memory
|
||||
2. Add it to a radix bucket corresponding to directory of files
|
||||
3. Iterate over each bucket
|
||||
4. Resume old approach.
|
||||
*/
|
||||
r.save = function(rad, cb){
|
||||
if(r.save.ing){
|
||||
r.save.ing.push({rad: rad, ack: cb});
|
||||
cb = data;
|
||||
r.read(key, cb, o);
|
||||
return;
|
||||
}
|
||||
//console.only(99); var ID = Gun.text.random(2), S = (+new Date); console.log("[[[[[[[[", ID);
|
||||
r.save.ing = [];
|
||||
var ack = cb;
|
||||
var s = function Span(err, ok){
|
||||
var tmp = r.save.ing;
|
||||
//console.only(99); var TMP; console.log("]]]]]]]]", ID, (TMP = +new Date) - S, 'more?', !!tmp);
|
||||
r.save.ing = null;
|
||||
map(tmp, function(q){ // if many, not the most efficient to requeue, but works for now.
|
||||
if(!q || !q.rad || !q.ack){ return }
|
||||
r.save(q.rad, q.ack);
|
||||
})
|
||||
ack(err, ok);
|
||||
};
|
||||
cb = s;
|
||||
s.files = {};
|
||||
s.i = 0; // TODO: revise? Using counter for critical path not my favorite.
|
||||
s.place = function(tree, key){
|
||||
var go = function(file, last){
|
||||
file = decodeURIComponent(file || last || opt.code.from);
|
||||
(s.files[file] || (s.files[file] = Radix()))(key, tree);
|
||||
if(!(--s.i)){ s.go() } // TODO: See above, revise?
|
||||
return true;
|
||||
}
|
||||
go.reverse = 1;
|
||||
go.end = key;
|
||||
r.list(go);
|
||||
++s.i; // TODO: See above, revise?
|
||||
}
|
||||
s.go = function(){
|
||||
if(s.gone){ return } s.gone = true;
|
||||
s.seq = [];
|
||||
map(s.files, function(mem, file){ s.seq.push({file: file, mem: mem}) });
|
||||
LOG && opt.log(+new Date, s.seq.length, "rad files #");
|
||||
s.files = null;
|
||||
s.c = 0;
|
||||
s.merge(s.c);
|
||||
}
|
||||
s.merge = function(i){
|
||||
i = i || 0;
|
||||
//var at = s.seq[i];
|
||||
var at = s.seq.shift();
|
||||
if(!at){
|
||||
if(s.ok){ return cb(null, s.ok) }
|
||||
return cb("No file to save data to.");
|
||||
}
|
||||
var file = at.file, mem = at.mem;
|
||||
r.parse(file, function(err, disk){
|
||||
if(err){ return cb(err) }
|
||||
if(!disk && file !== opt.code.from){ // corrupt file?
|
||||
r.list.bad(file); // remove from dir list
|
||||
r.save(rad, cb); // try again
|
||||
return;
|
||||
}
|
||||
disk = disk || Radix();
|
||||
var S, C = 0; LOG && (S = +new Date);
|
||||
Radix.map(mem, function(val, key){
|
||||
C++;
|
||||
// PLUGIN: consider adding HAM as an extra layer of protection
|
||||
disk(key, val); // merge batch[key] -> disk[key]
|
||||
});
|
||||
LOG && opt.log(S, +new Date - S, "rad merge");
|
||||
LOG && opt.log(S, C, "rad merge #");
|
||||
r.write(file, disk, s.pop);
|
||||
})
|
||||
}
|
||||
s.pop = function(err, ok){
|
||||
if(s.err = err || s.err){ return cb(err) }
|
||||
s.ok = ok || s.ok || 1;
|
||||
s.merge(++s.c);
|
||||
}
|
||||
Radix.map(rad, s.place);
|
||||
if(!s.i){ s.go() }; // TODO: See above, revise?
|
||||
//var tmp = (tmp = r.batch = r.batch || {})[key] = tmp[key] || {};
|
||||
//var tmp = (tmp = r.batch = r.batch || {})[key] = data;
|
||||
r.save(key, data, cb);
|
||||
}
|
||||
r.save = function(key, data, cb){
|
||||
var s = {key: key};
|
||||
s.find = function(file){ var tmp;
|
||||
s.file = file || (file = opt.code.from);
|
||||
if(tmp = r.disk[file]){ s.mix(u, tmp); return }
|
||||
r.parse(file, s.mix);
|
||||
}
|
||||
s.mix = function(err, disk){
|
||||
if(s.err = err || s.err){ cb(err); return }
|
||||
var file = s.file = (disk||'').file || s.file;
|
||||
if(!disk && file !== opt.code.from){ // corrupt file?
|
||||
r.find.bad(file); // remove from dir list
|
||||
r.save(key, data, cb); // try again
|
||||
return;
|
||||
}
|
||||
(disk = r.disk[file] || (r.disk[file] = disk || Radix())).file || (disk.file = file);
|
||||
if(opt.compare){
|
||||
data = opt.compare(disk(key), data, key, file);
|
||||
if(u === data){ cb(err, -1); return }
|
||||
}
|
||||
(s.disk = disk)(key, data);
|
||||
if(disk.Q){ disk.Q.push(cb); return } disk.Q = [cb];
|
||||
disk.to = setTimeout(s.write, opt.until);
|
||||
}
|
||||
s.write = function(){
|
||||
var file = s.file, disk = s.disk;
|
||||
s.q = disk.Q;
|
||||
delete disk.Q;
|
||||
delete r.disk[file];
|
||||
r.write(file, disk, s.ack);
|
||||
}
|
||||
s.ack = function(err, ok){
|
||||
var q = s.q || [], i = 0, ack;
|
||||
//var S = +new Date;
|
||||
while(ack = q[i++]){ ack(err, ok) }
|
||||
//console.log('acks:', +new Date - S, s.file, q.length);
|
||||
}
|
||||
r.find(key, s.find);
|
||||
}
|
||||
r.disk = {};
|
||||
|
||||
/*
|
||||
Any storage engine at some point will have to do a read in order to write.
|
||||
@ -200,70 +98,77 @@
|
||||
Therefore it is unavoidable that a read will have to happen,
|
||||
the question is just how long you delay it.
|
||||
*/
|
||||
var RWC = 0;
|
||||
r.write = function(file, rad, cb, o){
|
||||
if(!rad){ cb('No radix!'); return }
|
||||
o = ('object' == typeof o)? o : {force: o};
|
||||
var f = function Fractal(){};
|
||||
var f = function Fractal(){}, a, b;
|
||||
f.text = '';
|
||||
f.count = 0;
|
||||
f.file = file;
|
||||
f.each = function(val, key, k, pre){
|
||||
//console.log("RAD:::", JSON.stringify([val, key, k, pre]));
|
||||
if(u !== val){ f.count++ }
|
||||
if(opt.pack <= (val||'').length){ return cb("Record too big!"), true }
|
||||
var enc = Radisk.encode(pre.length) +'#'+ Radisk.encode(k) + (u === val? '' : ':'+ Radisk.encode(val)) +'\n';
|
||||
if((opt.chunk < f.text.length + enc.length) && (1 < f.count) && !o.force){
|
||||
f.text = '';
|
||||
f.limit = Math.ceil(f.count/2);
|
||||
f.count = 0;
|
||||
f.sub = Radix();
|
||||
// IMPORTANT: DO THIS IN REVERSE, SO LAST HALF OF DATA MOVED TO NEW FILE BEFORE DROPPING FROM CURRENT FILE.
|
||||
Radix.map(rad, f.slice, {reverse: true});
|
||||
return true;
|
||||
}
|
||||
f.text += enc;
|
||||
}
|
||||
f.file = file = rad.file || (rad.file = file);
|
||||
if(!file){ cb('What file?'); return }
|
||||
f.write = function(){
|
||||
var tmp = ename(file);
|
||||
var text = rad.raw = f.text;
|
||||
r.disk[file = rad.file || f.file || file] = rad;
|
||||
var S; LOG && (S = +new Date);
|
||||
r.list.add(tmp, function(err){
|
||||
if(err){ return cb(err) }
|
||||
//opt.store.put(tmp, f.text, cb); // revert to this after stats done below:
|
||||
opt.store.put(tmp, f.text, function(err,ok){
|
||||
LOG && opt.log(S, ST = +new Date - S, "wrote disk", tmp);
|
||||
cb(err,ok);
|
||||
r.find.add(file, function add(err){
|
||||
if(err){ cb(err); return }
|
||||
opt.store.put(ename(file), text, function safe(err, ok){
|
||||
LOG && opt.log(S, ST = +new Date - S, "wrote disk", JSON.stringify(file), ++RWC, 'total all writes.');
|
||||
cb(err, ok || 1);
|
||||
if(!rad.Q){ delete r.disk[file] } // VERY IMPORTANT! Clean up memory, but not if there is already queued writes on it!
|
||||
});
|
||||
});
|
||||
}
|
||||
f.split = function(){
|
||||
f.text = '';
|
||||
if(!f.count){ f.count = 0;
|
||||
Radix.map(rad, function count(){ f.count++ }); // TODO: Perf? Any faster way to get total length?
|
||||
}
|
||||
f.limit = Math.ceil(f.count/2);
|
||||
f.count = 0;
|
||||
f.sub = Radix();
|
||||
Radix.map(rad, f.slice, {reverse: 1}); // IMPORTANT: DO THIS IN REVERSE, SO LAST HALF OF DATA MOVED TO NEW FILE BEFORE DROPPING FROM CURRENT FILE.
|
||||
r.write(f.end, f.sub, f.both, o);
|
||||
f.hub = Radix();
|
||||
Radix.map(rad, f.stop);
|
||||
r.write(rad.file, f.hub, f.both, o);
|
||||
return true;
|
||||
}
|
||||
f.slice = function(val, key){
|
||||
f.sub(f.end = key, val);
|
||||
if(f.limit <= (++f.count)){
|
||||
r.write(key, f.sub, f.swap, o);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
f.swap = function(err){
|
||||
if(err){ return cb(err) }
|
||||
f.sub = Radix();
|
||||
Radix.map(rad, f.stop);
|
||||
r.write(f.file, f.sub, cb, o);
|
||||
if(f.limit <= (++f.count)){ return true }
|
||||
}
|
||||
f.stop = function(val, key){
|
||||
if(key >= f.end){ return true }
|
||||
f.sub(key, val);
|
||||
f.hub(key, val);
|
||||
}
|
||||
if(opt.jsonify){ return r.write.jsonify(f, file, rad, cb, o) } // temporary testing idea
|
||||
f.both = function(err, ok){
|
||||
if(b){ cb(err || b); return }
|
||||
if(a){ cb(err, ok); return }
|
||||
a = true;
|
||||
b = err;
|
||||
}
|
||||
f.each = function(val, key, k, pre){
|
||||
//console.log("RAD:::", JSON.stringify([val, key, k, pre]));
|
||||
if(u !== val){ f.count++ }
|
||||
if(opt.pack <= (val||'').length){ return cb("Data too big!"), true }
|
||||
var enc = Radisk.encode(pre.length) +'#'+ Radisk.encode(k) + (u === val? '' : ':'+ Radisk.encode(val)) +'\n';
|
||||
if((opt.chunk < f.text.length + enc.length) && (1 < f.count) && !o.force){
|
||||
return f.split();
|
||||
}
|
||||
f.text += enc;
|
||||
}
|
||||
if(opt.jsonify){ r.write.jsonify(f, rad, cb, o); return } // temporary testing idea
|
||||
if(!Radix.map(rad, f.each, true)){ f.write() }
|
||||
}
|
||||
|
||||
r.write.jsonify = function(f, file, rad, cb, o){
|
||||
r.write.jsonify = function(f, rad, cb, o){
|
||||
var raw;
|
||||
var S; LOG && (S = +new Date);
|
||||
try{raw = JSON.stringify(rad.$);
|
||||
}catch(e){ return cb("Record too big!") }
|
||||
}catch(e){ cb("Cannot radisk!"); return }
|
||||
LOG && opt.log(S, +new Date - S, "rad stringified JSON");
|
||||
if(opt.chunk < raw.length && !o.force){
|
||||
if(Radix.map(rad, f.each, true)){ return }
|
||||
}
|
||||
if(opt.chunk < raw.length && !o.force){ return f.split() }
|
||||
f.text = raw;
|
||||
f.write();
|
||||
}
|
||||
@ -273,129 +178,68 @@
|
||||
if(u === o.start && u === o.end){ return tree }
|
||||
if(atomic(tree)){ return tree }
|
||||
var sub = Radix();
|
||||
Radix.map(tree, function(v,k){ // ONLY PLACE THAT TAKES TREE, maybe reduce API for better perf?
|
||||
sub(k,v);
|
||||
}, o);
|
||||
Radix.map(tree, function(v,k){ sub(k,v) }, o); // ONLY PLACE THAT TAKES TREE, maybe reduce API for better perf?
|
||||
return sub('');
|
||||
}
|
||||
|
||||
;(function(){
|
||||
var Q = {};
|
||||
r.read = function(key, cb, o){
|
||||
o = o || {};
|
||||
if(RAD && !o.next){ // cache
|
||||
var S; LOG && (S = +new Date);
|
||||
var val = RAD(key);
|
||||
LOG && (ST = +new Date - S) > 9 && opt.log(S, ST, 'rad cached');
|
||||
//if(u !== val){
|
||||
//cb(u, val, o);
|
||||
if(atomic(val)){ cb(u, val, o); return }
|
||||
// if a node is requested and some of it is cached... the other parts might not be.
|
||||
//}
|
||||
var g = {key: key};
|
||||
g.find = function(file){ var tmp;
|
||||
g.file = file || (file = opt.code.from);
|
||||
if(tmp = r.disk[g.file = file]){ g.check(u, tmp); return }
|
||||
r.parse(file, g.check);
|
||||
}
|
||||
o.span = (u !== o.start) || (u !== o.end); // is there a start or end?
|
||||
var g = function Get(){};
|
||||
g.lex = function(file){ var tmp; // // TODO: this had a out-of-memory crash!
|
||||
file = (u === file)? u : decodeURIComponent(file);
|
||||
tmp = o.next || key || (o.reverse? o.end || '\uffff' : o.start || '');
|
||||
if(!file || (o.reverse? file < tmp : file > tmp)){
|
||||
LOG && opt.log(S, +new Date - S, 'rad read lex'); S = +new Date;
|
||||
if(o.next || o.reverse){ g.file = file }
|
||||
if(tmp = Q[g.file]){
|
||||
tmp.push({key: key, ack: cb, file: g.file, opt: o});
|
||||
return true;
|
||||
}
|
||||
Q[g.file] = [{key: key, ack: cb, file: g.file, opt: o}];
|
||||
if(!g.file){
|
||||
g.it(null, u, {});
|
||||
return true;
|
||||
}
|
||||
r.parse(g.file, g.check);
|
||||
return true;
|
||||
}
|
||||
g.file = file;
|
||||
}
|
||||
g.it = function(err, disk, info){
|
||||
if(g.err = err){ opt.log('err', err) }
|
||||
if(!disk && g.file){ // corrupt file?
|
||||
r.list.bad(g.file); // remove from dir list
|
||||
r.read(key, cb, o); // look again
|
||||
g.get = function(err, disk, info){
|
||||
if(g.err = err || g.err){ cb(err); return }
|
||||
var file = g.file = (disk||'').file || g.file;
|
||||
if(!disk && file !== opt.code.from){ // corrupt file?
|
||||
r.find.bad(file); // remove from dir list
|
||||
r.read(key, cb, o); // try again
|
||||
return;
|
||||
}
|
||||
g.info = info;
|
||||
if(disk){ RAD = g.disk = disk }
|
||||
disk = Q[g.file]; delete Q[g.file];
|
||||
map(disk, g.ack);
|
||||
}
|
||||
g.ack = function(as){
|
||||
if(!as.ack){ return }
|
||||
var S; LOG && (S = +new Date);
|
||||
var key = as.key, o = as.opt, info = g.info, rad = g.disk || noop, data = r.range(rad(key), o), last = rad.last || Radix.map(rad, rev, revo);
|
||||
LOG && (ST = +new Date - S) > 9 && opt.log(S, ST, "rad range loaded");
|
||||
o.parsed = (o.parsed || 0) + (info.parsed||0);
|
||||
disk = r.disk[file] || (r.disk[file] = disk);
|
||||
if(!disk){ cb("No file!"); return }
|
||||
disk.file || (disk.file = file);
|
||||
var data = r.range(disk(key), o);
|
||||
o.unit = disk.unit;
|
||||
o.chunks = (o.chunks || 0) + 1;
|
||||
o.more = true;
|
||||
if((!as.file) // if no more places to look
|
||||
|| (!o.span && last === key) // if our key exactly matches the very last atomic record
|
||||
|| (!o.span && last && last > key && 0 != last.indexOf(key)) // 'zach' may be lexically larger than 'za', but there still might be more, like 'zane' in the 'za' prefix bucket so do not end here.
|
||||
){
|
||||
o.more = u;
|
||||
as.ack(g.err, data, o);
|
||||
return
|
||||
o.parsed = (o.parsed || 0) + ((info||'').parsed||(o.chunks*opt.chunk));
|
||||
o.more = 1;
|
||||
o.next = u;
|
||||
Radix.map(r.list, function next(v,f){
|
||||
if(!v || file === f){ return }
|
||||
o.next = f;
|
||||
return 1;
|
||||
}, o.reverse? {reverse: 1, end: file} : {start: file});
|
||||
if(!o.next){ o.more = 0 }
|
||||
if(o.next){
|
||||
if(!o.reverse && (key < o.next && 0 != o.next.indexOf(key)) || (u !== o.end && (o.end || '\uffff') < o.next)){ o.more = 0 }
|
||||
if(o.reverse && (key > o.next && 0 != key.indexOf(o.next)) || (u !== o.start && (o.start || '') > o.next)){ o.more = 0 }
|
||||
}
|
||||
if(u !== data){
|
||||
as.ack(g.err, data, o); // more might be coming!
|
||||
if(o.parsed >= o.limit){ return } // even if more, we've hit our limit, asking peer will need to make a new ask with a new starting point.
|
||||
}
|
||||
o.next = as.file;
|
||||
r.read(key, as.ack, o);
|
||||
if(!o.more){ cb(g.err, data, o); return }
|
||||
if(data){ cb(g.err, data, o) }
|
||||
if(o.parsed >= o.limit){ return }
|
||||
r.parse(o.next, g.check);
|
||||
}
|
||||
g.check = function(err, disk, info){
|
||||
g.it(err, disk, info);
|
||||
var good = true;
|
||||
g.get(err, disk, info);
|
||||
(info || (info = {})).file || (info.file = g.file);
|
||||
Radix.map(disk, function(val, key){
|
||||
// assume in memory for now, since both write/read already call r.list which will init it.
|
||||
var go = function(file){
|
||||
if(info.file !== file){
|
||||
good = false
|
||||
}
|
||||
return true;
|
||||
}
|
||||
go.reverse = 1;
|
||||
go.end = key;
|
||||
r.list(go);
|
||||
});
|
||||
if(good){ return }
|
||||
var id = Gun.text.random(3);
|
||||
r.save(disk, function ack(err, ok){
|
||||
if(err){ return r.save(disk, ack) } // ad infinitum???
|
||||
console.log("MISLOCATED DATA CORRECTED", id);
|
||||
// assume in memory for now, since both write/read already call r.find which will init it.
|
||||
r.find(key, function(file){
|
||||
if((file || (file = opt.code.from)) === info.file){ return }
|
||||
var id = Gun.text.random(3);
|
||||
console.log("MISLOCATED DATA", id, key, info.file, file);
|
||||
r.save(key, val, function ack(err, ok){
|
||||
if(err){ r.save(key, val, ack); return } // ad infinitum???
|
||||
console.log("MISLOCATED DATA CORRECTED", id);
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
/*g.check2 = function(err, disk, info){
|
||||
if(err || !disk){ return g.it(err, disk, info) }
|
||||
var good = true;
|
||||
Radix.map(disk, function(val, key){
|
||||
// assume in memory for now, since both write/read already call r.list which will init it.
|
||||
var go = function(file){
|
||||
if(info.file !== file){ good = false }
|
||||
return true;
|
||||
}
|
||||
go.reverse = 1;
|
||||
go.end = key;
|
||||
r.list(go);
|
||||
});
|
||||
if(good){ return g.it(err, disk, info) }
|
||||
var id = Gun.text.random(3); console.log("MISLOCATED DATA", id);
|
||||
r.save(disk, function ack(err, ok){
|
||||
if(err){ return r.save(disk, ack) } // ad infinitum???
|
||||
console.log("MISLOCATED CORRECTED", id);
|
||||
r.read(key, cb, o);
|
||||
});
|
||||
}*/
|
||||
if(o.reverse){ g.lex.reverse = true }
|
||||
LOG && (S = +new Date);
|
||||
r.list(g.lex);
|
||||
r.find(key, g.find);
|
||||
}
|
||||
function rev(a,b){ return b }
|
||||
var revo = {reverse: true};
|
||||
@ -409,18 +253,18 @@
|
||||
with how much performance and scale we can get out of only one.
|
||||
Then we can work on the harder problem of being multi-process.
|
||||
*/
|
||||
var RPC = 0;
|
||||
var Q = {}, s = String.fromCharCode(31);
|
||||
r.parse = function(file, cb, raw){ var q;
|
||||
if(q = Q[file]){ return q.push(cb) } q = Q[file] = [cb];
|
||||
var p = function Parse(){}, info = {file: ename(file)};
|
||||
p.disk = Radix();
|
||||
if(!file){ return cb(); }
|
||||
if(q = Q[file]){ q.push(cb); return } q = Q[file] = [cb];
|
||||
var p = function Parse(){}, info = {file: file};
|
||||
(p.disk = Radix()).file = file;
|
||||
p.read = function(err, data){ var tmp;
|
||||
LOG && opt.log(S, +new Date - S, 'read disk', ename(file));
|
||||
LOG && opt.log(S, +new Date - S, 'read disk', JSON.stringify(file), ++RPC, 'total all parses.');
|
||||
delete Q[file];
|
||||
if((p.err = err) || (p.not = !data)){
|
||||
return map(q, p.ack);
|
||||
}
|
||||
if(typeof data !== 'string'){
|
||||
if((p.err = err) || (p.not = !data)){ map(q, p.ack); return }
|
||||
if('string' !== typeof data){
|
||||
try{
|
||||
if(opt.pack <= data.length){
|
||||
p.err = "Chunk too big!";
|
||||
@ -428,12 +272,11 @@
|
||||
data = data.toString(); // If it crashes, it crashes here. How!?? We check size first!
|
||||
}
|
||||
}catch(e){ p.err = e }
|
||||
if(p.err){ return map(q, p.ack) }
|
||||
if(p.err){ map(q, p.ack); return }
|
||||
}
|
||||
info.parsed = data.length;
|
||||
|
||||
LOG && (S = +new Date);
|
||||
if(opt.jsonify || '{' === data[0]){ // temporary testing idea
|
||||
if(opt.jsonify || '{' === data[0]){
|
||||
try{
|
||||
var json = JSON.parse(data); // TODO: this caused a out-of-memory crash!
|
||||
p.disk.$ = json;
|
||||
@ -443,14 +286,27 @@
|
||||
}catch(e){ tmp = e }
|
||||
if('{' === data[0]){
|
||||
p.err = tmp || "JSON error!";
|
||||
return map(q, p.ack);
|
||||
map(q, p.ack);
|
||||
return;
|
||||
}
|
||||
}
|
||||
p.radec(err, data);
|
||||
}
|
||||
p.ack = function(cb){
|
||||
if(!cb){ return }
|
||||
if(p.err || p.not){
|
||||
cb(p.err, u, info);
|
||||
return;
|
||||
}
|
||||
cb(u, p.disk, info);
|
||||
}
|
||||
p.radec = function(err, data){
|
||||
LOG && (S = +new Date);
|
||||
var tmp = p.split(data), pre = [], i, k, v;
|
||||
if(!tmp || 0 !== tmp[1]){
|
||||
p.err = "File '"+file+"' does not have root radix! ";
|
||||
return map(q, p.ack);
|
||||
map(q, p.ack);
|
||||
return;
|
||||
}
|
||||
while(tmp){
|
||||
k = v = u;
|
||||
@ -470,7 +326,6 @@
|
||||
tmp = p.split(tmp[2]);
|
||||
}
|
||||
LOG && opt.log(S, +new Date - S, 'parsed RAD');
|
||||
//cb(err, p.disk);
|
||||
map(q, p.ack);
|
||||
};
|
||||
p.split = function(t){
|
||||
@ -484,75 +339,63 @@
|
||||
l[2] = t.slice(i + o.i);
|
||||
return l;
|
||||
}
|
||||
p.ack = function(cb){
|
||||
if(!cb){ return }
|
||||
if(p.err || p.not){ return cb(p.err, u, info) }
|
||||
cb(u, p.disk, info);
|
||||
}
|
||||
var S; LOG && (S = +new Date);
|
||||
if(raw){ return p.read(null, raw) }
|
||||
if(r.disk){ raw || (raw = (r.disk[file]||'').raw) }
|
||||
if(raw){ return p.read(u, raw) }
|
||||
opt.store.get(ename(file), p.read);
|
||||
}
|
||||
}());
|
||||
|
||||
;(function(){
|
||||
var dir, q, f = String.fromCharCode(28), ef = ename(f);
|
||||
r.list = function(cb){
|
||||
if(dir){
|
||||
var last, tmp = {reverse: (cb.reverse)? 1 : 0, start: cb.start, end: cb.end};
|
||||
Radix.map(dir, function(val, key){
|
||||
if(!val){ return }
|
||||
return cb(last = key);
|
||||
}, tmp) || cb(u, last);
|
||||
var dir, f = String.fromCharCode(28), Q;
|
||||
r.find = function(key, cb){
|
||||
if(!dir){
|
||||
if(Q){ Q.push([key, cb]); return } Q = [[key, cb]];
|
||||
r.parse(f, init);
|
||||
return;
|
||||
}
|
||||
if(q){ return q.push(cb) } q = [cb];
|
||||
r.parse(f, r.list.init);
|
||||
Radix.map(r.list = dir, function(val, key){
|
||||
if(!val){ return }
|
||||
return cb(key) || true;
|
||||
}, {reverse: 1, end: key}) || cb(opt.code.from);
|
||||
}
|
||||
r.list.add = function(file, cb){
|
||||
r.find.add = function(file, cb){
|
||||
var has = dir(file);
|
||||
if(has || file === ef){
|
||||
return cb(u, 1);
|
||||
}
|
||||
if(has || file === f){ cb(u, 1); return }
|
||||
dir(file, 1);
|
||||
cb.listed = (cb.listed || 0) + 1;
|
||||
cb.found = (cb.found || 0) + 1;
|
||||
r.write(f, dir, function(err, ok){
|
||||
if(err){ return cb(err) }
|
||||
cb.listed = (cb.listed || 0) - 1;
|
||||
if(cb.listed !== 0){ return }
|
||||
if(err){ cb(err); return }
|
||||
cb.found = (cb.found || 0) - 1;
|
||||
if(0 !== cb.found){ return }
|
||||
cb(u, 1);
|
||||
}, true);
|
||||
}
|
||||
r.list.bad = function(file, cb){
|
||||
dir(ename(file), 0);
|
||||
r.find.bad = function(file, cb){
|
||||
dir(file, 0);
|
||||
r.write(f, dir, cb||noop);
|
||||
}
|
||||
r.list.init = function(err, disk){
|
||||
function init(err, disk){
|
||||
if(err){
|
||||
opt.log('list', err);
|
||||
setTimeout(function(){ r.parse(f, r.list.init) }, 1000);
|
||||
return;
|
||||
}
|
||||
if(disk){
|
||||
r.list.drain(disk);
|
||||
return;
|
||||
}
|
||||
if(!opt.store.list){
|
||||
r.list.drain(Radix());
|
||||
setTimeout(function(){ r.parse(f, init) }, 1000);
|
||||
return;
|
||||
}
|
||||
if(disk){ drain(disk); return }
|
||||
dir = dir || disk || Radix();
|
||||
if(!opt.store.list){ drain(dir); return }
|
||||
// import directory.
|
||||
opt.store.list(function(file){
|
||||
dir = dir || Radix();
|
||||
if(!file){ return r.list.drain(dir) }
|
||||
r.list.add(file, noop);
|
||||
if(!file){ drain(dir); return }
|
||||
r.find.add(file, noop);
|
||||
});
|
||||
}
|
||||
r.list.drain = function(rad, tmp){
|
||||
r.list.dir = dir = rad;
|
||||
tmp = q; q = null;
|
||||
Gun.list.map(tmp, function(cb){
|
||||
r.list(cb);
|
||||
function drain(rad, tmp){
|
||||
dir = dir || rad;
|
||||
dir.file = f;
|
||||
tmp = Q; Q = null;
|
||||
Gun.list.map(tmp, function(arg){
|
||||
r.find(arg[0], arg[1]);
|
||||
});
|
||||
}
|
||||
}());
|
||||
@ -562,8 +405,6 @@
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
;(function(){
|
||||
var _ = String.fromCharCode(31), u;
|
||||
Radisk.encode = function(d, o, s){ s = s || _;
|
||||
|
600
lib/radiskip.js
600
lib/radiskip.js
@ -1,600 +0,0 @@
|
||||
;(function(){
|
||||
|
||||
function Radisk(opt){
|
||||
|
||||
opt = opt || {};
|
||||
opt.log = opt.log || console.log;
|
||||
opt.file = String(opt.file || 'radata');
|
||||
var has = (Radisk.has || (Radisk.has = {}))[opt.file];
|
||||
if(has){ return has }
|
||||
|
||||
opt.pack = opt.pack || (opt.memory? (opt.memory * 1000 * 1000) : 1399000000) * 0.3; // max_old_space_size defaults to 1400 MB.
|
||||
opt.until = opt.until || opt.wait || 250;
|
||||
opt.batch = opt.batch || (10 * 1000);
|
||||
opt.chunk = opt.chunk || (1024 * 1024 * 1); // 1MB
|
||||
opt.code = opt.code || {};
|
||||
opt.code.from = opt.code.from || '!';
|
||||
opt.jsonify = true;
|
||||
|
||||
function ename(t){ return encodeURIComponent(t).replace(/\*/g, '%2A') }
|
||||
function atomic(v){ return u !== v && (!v || 'object' != typeof v) }
|
||||
var map = Gun.obj.map;
|
||||
var LOG = console.LOG;
|
||||
var ST = 0;
|
||||
|
||||
if(!opt.store){
|
||||
return opt.log("ERROR: Radisk needs `opt.store` interface with `{get: fn, put: fn (, list: fn)}`!");
|
||||
}
|
||||
if(!opt.store.put){
|
||||
return opt.log("ERROR: Radisk needs `store.put` interface with `(file, data, cb)`!");
|
||||
}
|
||||
if(!opt.store.get){
|
||||
return opt.log("ERROR: Radisk needs `store.get` interface with `(file, cb)`!");
|
||||
}
|
||||
if(!opt.store.list){
|
||||
//opt.log("WARNING: `store.list` interface might be needed!");
|
||||
}
|
||||
|
||||
/*
|
||||
Any and all storage adapters should...
|
||||
1. Because writing to disk takes time, we should batch data to disk. This improves performance, and reduces potential disk corruption.
|
||||
2. If a batch exceeds a certain number of writes, we should immediately write to disk when physically possible. This caps total performance, but reduces potential loss.
|
||||
*/
|
||||
var r = function(key, data, cb){
|
||||
if('function' === typeof data){
|
||||
var o = cb || {};
|
||||
cb = data;
|
||||
r.read(key, cb);
|
||||
return;
|
||||
}
|
||||
//var tmp = (tmp = r.batch = r.batch || {})[key] = tmp[key] || {};
|
||||
//var tmp = (tmp = r.batch = r.batch || {})[key] = data;
|
||||
r.save(key, data, cb);
|
||||
}
|
||||
r.save = function(key, data, cb){
|
||||
var s = {key: key};
|
||||
s.find = function(file){ var tmp;
|
||||
s.file = file || (file = opt.code.from);
|
||||
if(tmp = r.disk[file]){ s.mix(u, tmp); return }
|
||||
r.parse(file, s.mix);
|
||||
}
|
||||
s.mix = function(err, disk){
|
||||
if(err){ cb(err); return }
|
||||
var file = s.file = (disk||'').file || s.file;
|
||||
if(!disk && file !== opt.code.from){ // corrupt file?
|
||||
r.find.bad(file); // remove from dir list
|
||||
r.save(key, data, cb); // try again
|
||||
return;
|
||||
}
|
||||
(disk = r.disk[file] || (r.disk[file] = disk || Radix())).file || (disk.file = file);
|
||||
if(opt.compare){
|
||||
data = opt.compare(disk(key), data, key, file);
|
||||
if(u === data){ cb(err, -1); return }
|
||||
}
|
||||
(s.disk = disk)(key, data);
|
||||
if(disk.Q){ disk.Q.push(cb); return } disk.Q = [cb];
|
||||
disk.to = setTimeout(s.write, opt.until);
|
||||
}
|
||||
s.write = function(){
|
||||
var file = s.file, disk = s.disk;
|
||||
s.q = disk.Q;
|
||||
delete disk.Q;
|
||||
delete r.disk[file];
|
||||
r.write(file, disk, s.ack);
|
||||
}
|
||||
s.ack = function(err, ok){
|
||||
var q = s.q || [], i = 0, ack;
|
||||
//var S = +new Date;
|
||||
while(ack = q[i++]){ ack(err, ok) }
|
||||
//console.log('acks:', +new Date - S, s.file, q.length);
|
||||
}
|
||||
r.find(key, s.find);
|
||||
}
|
||||
r.disk = {};
|
||||
|
||||
/*
|
||||
Any storage engine at some point will have to do a read in order to write.
|
||||
This is true of even systems that use an append only log, if they support updates.
|
||||
Therefore it is unavoidable that a read will have to happen,
|
||||
the question is just how long you delay it.
|
||||
*/
|
||||
var RWC = 0;
|
||||
r.write = function(file, rad, cb, o){
|
||||
if(!rad){ cb('No radix!'); return }
|
||||
o = ('object' == typeof o)? o : {force: o};
|
||||
var f = function Fractal(){}, a, b;
|
||||
f.text = '';
|
||||
f.file = file = rad.file || (rad.file = file);
|
||||
if(!file){ cb('What file?'); return }
|
||||
f.write = function(){
|
||||
var text = rad.raw = f.text;
|
||||
r.disk[file = rad.file || f.file || file] = rad;
|
||||
var S; LOG && (S = +new Date);
|
||||
r.find.add(file, function add(err){
|
||||
if(err){ cb(err); return }
|
||||
opt.store.put(ename(file), text, function safe(err, ok){
|
||||
LOG && opt.log(S, ST = +new Date - S, "wrote disk", JSON.stringify(file), ++RWC, 'total all writes.');
|
||||
cb(err, ok || 1);
|
||||
if(!rad.Q){ delete r.disk[file] } // VERY IMPORTANT! Clean up memory, but not if there is already queued writes on it!
|
||||
});
|
||||
});
|
||||
}
|
||||
f.split = function(){
|
||||
f.text = '';
|
||||
if(!f.count){ f.count = 0;
|
||||
Radix.map(rad, function count(){ f.count++ }); // TODO: Perf? Any faster way to get total length?
|
||||
}
|
||||
f.limit = Math.ceil(f.count/2);
|
||||
f.count = 0;
|
||||
f.sub = Radix();
|
||||
Radix.map(rad, f.slice, {reverse: 1}); // IMPORTANT: DO THIS IN REVERSE, SO LAST HALF OF DATA MOVED TO NEW FILE BEFORE DROPPING FROM CURRENT FILE.
|
||||
r.write(f.end, f.sub, f.both, o);
|
||||
f.hub = Radix();
|
||||
Radix.map(rad, f.stop);
|
||||
r.write(rad.file, f.hub, f.both, o);
|
||||
return true;
|
||||
}
|
||||
f.slice = function(val, key){
|
||||
f.sub(f.end = key, val);
|
||||
if(f.limit <= (++f.count)){ return true }
|
||||
}
|
||||
f.stop = function(val, key){
|
||||
if(key >= f.end){ return true }
|
||||
f.hub(key, val);
|
||||
}
|
||||
f.both = function(err, ok){
|
||||
if(b){ cb(err || b); return }
|
||||
if(a){ cb(err, ok); return }
|
||||
a = true;
|
||||
b = err;
|
||||
}
|
||||
f.each = function(val, key, k, pre){
|
||||
//console.log("RAD:::", JSON.stringify([val, key, k, pre]));
|
||||
if(u !== val){ f.count++ }
|
||||
if(opt.pack <= (val||'').length){ return cb("Record too big!"), true }
|
||||
var enc = Radisk.encode(pre.length) +'#'+ Radisk.encode(k) + (u === val? '' : ':'+ Radisk.encode(val)) +'\n';
|
||||
if((opt.chunk < f.text.length + enc.length) && (1 < f.count) && !o.force){
|
||||
return f.split();
|
||||
}
|
||||
f.text += enc;
|
||||
}
|
||||
if(opt.jsonify){ r.write.jsonify(f, rad, cb, o); return } // temporary testing idea
|
||||
if(!Radix.map(rad, f.each, true)){ f.write() }
|
||||
}
|
||||
|
||||
r.write.jsonify = function(f, rad, cb, o){
|
||||
var raw;
|
||||
var S; LOG && (S = +new Date);
|
||||
try{raw = JSON.stringify(rad.$);
|
||||
}catch(e){ cb("Cannot radisk!"); return }
|
||||
LOG && opt.log(S, +new Date - S, "rad stringified JSON");
|
||||
if(opt.chunk < raw.length && !o.force){ return f.split() }
|
||||
f.text = raw;
|
||||
f.write();
|
||||
}
|
||||
|
||||
r.range = function(tree, o){
|
||||
if(!tree || !o){ return }
|
||||
if(u === o.start && u === o.end){ return tree }
|
||||
if(atomic(tree)){ return tree }
|
||||
var sub = Radix();
|
||||
Radix.map(tree, function(v,k){ sub(k,v) }, o); // ONLY PLACE THAT TAKES TREE, maybe reduce API for better perf?
|
||||
return sub('');
|
||||
}
|
||||
|
||||
;(function(){
|
||||
var Q = {};
|
||||
r.read = function(key, cb, o){
|
||||
o = o || {};
|
||||
if(RAD && !o.next){ // cache
|
||||
var S; LOG && (S = +new Date);
|
||||
var val = RAD(key);
|
||||
LOG && (ST = +new Date - S) > 9 && opt.log(S, ST, 'rad cached');
|
||||
//if(u !== val){
|
||||
//cb(u, val, o);
|
||||
if(atomic(val)){ cb(u, val, o); return }
|
||||
// if a node is requested and some of it is cached... the other parts might not be.
|
||||
//}
|
||||
}
|
||||
o.span = (u !== o.start) || (u !== o.end); // is there a start or end?
|
||||
var g = function Get(){};
|
||||
g.lex = function(file){ var tmp; // // TODO: this had a out-of-memory crash!
|
||||
file = (u === file)? u : decodeURIComponent(file);
|
||||
tmp = o.next || key || (o.reverse? o.end || '\uffff' : o.start || '');
|
||||
if(!file || (o.reverse? file < tmp : file > tmp)){
|
||||
LOG && opt.log(S, +new Date - S, 'rad read lex'); S = +new Date;
|
||||
if(o.next || o.reverse){ g.file = file }
|
||||
if(tmp = Q[g.file]){
|
||||
tmp.push({key: key, ack: cb, file: g.file, opt: o});
|
||||
return true;
|
||||
}
|
||||
Q[g.file] = [{key: key, ack: cb, file: g.file, opt: o}];
|
||||
if(!g.file){
|
||||
g.it(u, u, {});
|
||||
return true;
|
||||
}
|
||||
r.parse(g.file, g.check);
|
||||
return true;
|
||||
}
|
||||
g.file = file;
|
||||
}
|
||||
g.it = function(err, disk, info){
|
||||
if(g.err = err){ opt.log('err', err) }
|
||||
if(!disk && g.file){ // corrupt file?
|
||||
r.find.bad(g.file); // remove from dir list
|
||||
r.read(key, cb, o); // look again
|
||||
return;
|
||||
}
|
||||
g.info = info;
|
||||
if(disk){ RAD = g.disk = disk }
|
||||
disk = Q[g.file]; delete Q[g.file];
|
||||
map(disk, g.ack);
|
||||
}
|
||||
g.ack = function(as){
|
||||
if(!as.ack){ return }
|
||||
var S; LOG && (S = +new Date);
|
||||
var key = as.key, o = as.opt, info = g.info, rad = g.disk || noop, data = r.range(rad(key), o), last = rad.last || Radix.map(rad, rev, revo);
|
||||
LOG && (ST = +new Date - S) > 9 && opt.log(S, ST, "rad range loaded");
|
||||
o.parsed = (o.parsed || 0) + (info.parsed||0);
|
||||
o.chunks = (o.chunks || 0) + 1;
|
||||
o.more = true;
|
||||
if((!as.file) // if no more places to look
|
||||
|| (!o.span && last === key) // if our key exactly matches the very last atomic record
|
||||
|| (!o.span && last && last > key && 0 != last.indexOf(key)) // 'zach' may be lexically larger than 'za', but there still might be more, like 'zane' in the 'za' prefix bucket so do not end here.
|
||||
){
|
||||
o.more = u;
|
||||
as.ack(g.err, data, o);
|
||||
return;
|
||||
}
|
||||
if(u !== data){
|
||||
as.ack(g.err, data, o); // more might be coming!
|
||||
if(o.parsed >= o.limit){ return } // even if more, we've hit our limit, asking peer will need to make a new ask with a new starting point.
|
||||
}
|
||||
o.next = as.file;
|
||||
r.read(key, as.ack, o);
|
||||
}
|
||||
g.check = function(err, disk, info){
|
||||
g.it(err, disk, info);
|
||||
var good = true;
|
||||
Radix.map(disk, function(val, key){
|
||||
// assume in memory for now, since both write/read already call r.find which will init it.
|
||||
var go = function(file){
|
||||
if(info.file !== file){
|
||||
good = false
|
||||
}
|
||||
return true;
|
||||
}
|
||||
go.reverse = 1;
|
||||
go.end = key;
|
||||
r.list(go);
|
||||
});
|
||||
if(good){ return }
|
||||
var id = Gun.text.random(3);
|
||||
r.save(disk, function ack(err, ok){
|
||||
if(err){ r.save(disk, ack); return } // ad infinitum???
|
||||
console.log("MISLOCATED DATA CORRECTED", id);
|
||||
});
|
||||
}
|
||||
/*g.check2 = function(err, disk, info){
|
||||
if(err || !disk){ g.it(err, disk, info); return }
|
||||
var good = true;
|
||||
Radix.map(disk, function(val, key){
|
||||
// assume in memory for now, since both write/read already call r.find which will init it.
|
||||
var go = function(file){
|
||||
if(info.file !== file){ good = false }
|
||||
return true;
|
||||
}
|
||||
go.reverse = 1;
|
||||
go.end = key;
|
||||
r.list(go);
|
||||
});
|
||||
if(good){ g.it(err, disk, info); return }
|
||||
var id = Gun.text.random(3); console.log("MISLOCATED DATA", id);
|
||||
r.save(disk, function ack(err, ok){
|
||||
if(err){ r.save(disk, ack); return } // ad infinitum???
|
||||
console.log("MISLOCATED CORRECTED", id);
|
||||
r.read(key, cb, o);
|
||||
});
|
||||
}*/
|
||||
if(o.reverse){ g.lex.reverse = true }
|
||||
LOG && (S = +new Date);
|
||||
r.find(key, g.lex);
|
||||
}
|
||||
|
||||
r.read = function(key, cb, o){
|
||||
var g = {key: key};
|
||||
g.find = function(file){ var tmp;
|
||||
g.file = file || (file = opt.code.from); // this may not be true for reads? Hit "end of dir"?
|
||||
if(tmp = r.disk[file]){ g.check(u, tmp); return }
|
||||
r.parse(file, g.check);
|
||||
}
|
||||
g.get = function(err, disk, info){
|
||||
if(err){ cb(err); return }
|
||||
var file = g.file = (disk||'').file || g.file;
|
||||
if(!disk && file !== opt.code.from){ // corrupt file?
|
||||
r.find.bad(file); // remove from dir list
|
||||
r.save(key, cb); // try again
|
||||
return;
|
||||
}
|
||||
disk = r.disk[file] || (r.disk[file] = disk);
|
||||
if(!disk){ cb(); return }
|
||||
disk.file || (disk.file = file);
|
||||
// ----------------------------
|
||||
info = info || {};
|
||||
var data = disk(key);
|
||||
info.atom = disk.atom;
|
||||
if(u !== data){ cb(u, data, info); return }
|
||||
// ----------------------------
|
||||
return;
|
||||
var S; LOG && (S = +new Date);
|
||||
var rad = disk || noop, data = r.range(rad(key), o), last = rad.last || Radix.map(rad, rev, revo);
|
||||
LOG && (ST = +new Date - S) > 9 && opt.log(S, ST, "rad range loaded");
|
||||
o.parsed = (o.parsed || 0) + (info.parsed||0);
|
||||
o.chunks = (o.chunks || 0) + 1;
|
||||
o.more = true;
|
||||
if((!as.file) // if no more places to look
|
||||
|| (!o.span && last === key) // if our key exactly matches the very last atomic record
|
||||
|| (!o.span && last && last > key && 0 != last.indexOf(key)) // 'zach' may be lexically larger than 'za', but there still might be more, like 'zane' in the 'za' prefix bucket so do not end here.
|
||||
){
|
||||
o.more = u;
|
||||
as.ack(g.err, data, o);
|
||||
return;
|
||||
}
|
||||
if(u !== data){
|
||||
as.ack(g.err, data, o); // more might be coming!
|
||||
if(o.parsed >= o.limit){ return } // even if more, we've hit our limit, asking peer will need to make a new ask with a new starting point.
|
||||
}
|
||||
o.next = as.file;
|
||||
r.read(key, as.ack, o);
|
||||
}
|
||||
g.check = function(err, disk, info){
|
||||
g.get(err, disk, info);
|
||||
(info || (info = {})).file || (info.file = g.file);
|
||||
Radix.map(disk, function(val, key){
|
||||
// assume in memory for now, since both write/read already call r.find which will init it.
|
||||
r.find(key, function(file){
|
||||
if(file === info.file){ return }
|
||||
var id = Gun.text.random(3);
|
||||
console.log("MISLOCATED DATA", id, key, info.file, file);
|
||||
r.save(key, val, function ack(err, ok){
|
||||
if(err){ r.save(key, val, ack); return } // ad infinitum???
|
||||
console.log("MISLOCATED DATA CORRECTED", id);
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
r.find(key, g.find);
|
||||
}
|
||||
function rev(a,b){ return b }
|
||||
var revo = {reverse: true};
|
||||
}());
|
||||
|
||||
;(function(){
|
||||
/*
|
||||
Let us start by assuming we are the only process that is
|
||||
changing the directory or bucket. Not because we do not want
|
||||
to be multi-process/machine, but because we want to experiment
|
||||
with how much performance and scale we can get out of only one.
|
||||
Then we can work on the harder problem of being multi-process.
|
||||
*/
|
||||
var RPC = 0;
|
||||
var Q = {}, s = String.fromCharCode(31);
|
||||
r.parse = function(file, cb, raw){ var q;
|
||||
if(q = Q[file]){ q.push(cb); return } q = Q[file] = [cb];
|
||||
var p = function Parse(){}, info = {file: file};
|
||||
(p.disk = Radix()).file = file;
|
||||
p.read = function(err, data){ var tmp;
|
||||
LOG && opt.log(S, +new Date - S, 'read disk', JSON.stringify(file), ++RPC, 'total all parses.');
|
||||
delete Q[file];
|
||||
if((p.err = err) || (p.not = !data)){ map(q, p.ack); return }
|
||||
if('string' !== typeof data){
|
||||
try{
|
||||
if(opt.pack <= data.length){
|
||||
p.err = "Chunk too big!";
|
||||
} else {
|
||||
data = data.toString(); // If it crashes, it crashes here. How!?? We check size first!
|
||||
}
|
||||
}catch(e){ p.err = e }
|
||||
if(p.err){ map(q, p.ack); return }
|
||||
}
|
||||
info.parsed = data.length;
|
||||
LOG && (S = +new Date);
|
||||
if(opt.jsonify || '{' === data[0]){
|
||||
try{
|
||||
var json = JSON.parse(data); // TODO: this caused a out-of-memory crash!
|
||||
p.disk.$ = json;
|
||||
LOG && (ST = +new Date - S) > 9 && opt.log(S, ST, 'rad parsed JSON');
|
||||
map(q, p.ack);
|
||||
return;
|
||||
}catch(e){ tmp = e }
|
||||
if('{' === data[0]){
|
||||
p.err = tmp || "JSON error!";
|
||||
map(q, p.ack);
|
||||
return;
|
||||
}
|
||||
}
|
||||
p.radec(err, data);
|
||||
}
|
||||
p.ack = function(cb){
|
||||
if(!cb){ return }
|
||||
if(p.err || p.not){
|
||||
cb(p.err, u, info);
|
||||
return;
|
||||
}
|
||||
cb(u, p.disk, info);
|
||||
}
|
||||
p.radec = function(err, data){
|
||||
LOG && (S = +new Date);
|
||||
var tmp = p.split(data), pre = [], i, k, v;
|
||||
if(!tmp || 0 !== tmp[1]){
|
||||
p.err = "File '"+file+"' does not have root radix! ";
|
||||
map(q, p.ack);
|
||||
return;
|
||||
}
|
||||
while(tmp){
|
||||
k = v = u;
|
||||
i = tmp[1];
|
||||
tmp = p.split(tmp[2])||'';
|
||||
if('#' == tmp[0]){
|
||||
k = tmp[1];
|
||||
pre = pre.slice(0,i);
|
||||
if(i <= pre.length){
|
||||
pre.push(k);
|
||||
}
|
||||
}
|
||||
tmp = p.split(tmp[2])||'';
|
||||
if('\n' == tmp[0]){ continue }
|
||||
if('=' == tmp[0] || ':' == tmp[0]){ v = tmp[1] }
|
||||
if(u !== k && u !== v){ p.disk(pre.join(''), v) }
|
||||
tmp = p.split(tmp[2]);
|
||||
}
|
||||
LOG && opt.log(S, +new Date - S, 'parsed RAD');
|
||||
map(q, p.ack);
|
||||
};
|
||||
p.split = function(t){
|
||||
if(!t){ return }
|
||||
var l = [], o = {}, i = -1, a = '', b, c;
|
||||
i = t.indexOf(s);
|
||||
if(!t[i]){ return }
|
||||
a = t.slice(0, i);
|
||||
l[0] = a;
|
||||
l[1] = b = Radisk.decode(t.slice(i), o);
|
||||
l[2] = t.slice(i + o.i);
|
||||
return l;
|
||||
}
|
||||
var S; LOG && (S = +new Date);
|
||||
if(r.disk){ raw || (raw = (r.disk[file]||'').raw) }
|
||||
if(raw){ return p.read(u, raw) }
|
||||
opt.store.get(ename(file), p.read);
|
||||
}
|
||||
}());
|
||||
|
||||
;(function(){
|
||||
var dir, f = String.fromCharCode(28), Q;
|
||||
r.find = function(key, cb){
|
||||
if(!dir){
|
||||
if(Q){ Q.push([key, cb]); return } Q = [[key, cb]];
|
||||
r.parse(f, init);
|
||||
return;
|
||||
}
|
||||
Radix.map(dir, function(val, key){
|
||||
if(!val){ return }
|
||||
return cb(key) || true;
|
||||
}, {reverse: 1, end: key}) || cb();
|
||||
}
|
||||
r.find.add = function(file, cb){
|
||||
var has = dir(file);
|
||||
if(has || file === f){ cb(u, 1); return }
|
||||
dir(file, 1);
|
||||
cb.found = (cb.found || 0) + 1;
|
||||
r.write(f, dir, function(err, ok){
|
||||
if(err){ cb(err); return }
|
||||
cb.found = (cb.found || 0) - 1;
|
||||
if(0 !== cb.found){ return }
|
||||
cb(u, 1);
|
||||
}, true);
|
||||
}
|
||||
r.find.bad = function(file, cb){
|
||||
dir(file, 0);
|
||||
r.write(f, dir, cb||noop);
|
||||
}
|
||||
function init(err, disk){
|
||||
if(err){
|
||||
opt.log('list', err);
|
||||
setTimeout(function(){ r.parse(f, init) }, 1000);
|
||||
return;
|
||||
}
|
||||
if(disk){ drain(disk); return }
|
||||
dir = dir || disk || Radix();
|
||||
if(!opt.store.list){ drain(dir); return }
|
||||
// import directory.
|
||||
opt.store.list(function(file){
|
||||
if(!file){ drain(dir); return }
|
||||
r.find.add(file, noop);
|
||||
});
|
||||
}
|
||||
function drain(rad, tmp){
|
||||
dir = dir || rad;
|
||||
dir.file = f;
|
||||
tmp = Q; Q = null;
|
||||
Gun.list.map(tmp, function(arg){
|
||||
r.find(arg[0], arg[1]);
|
||||
});
|
||||
}
|
||||
}());
|
||||
|
||||
var noop = function(){}, RAD, u;
|
||||
Radisk.has[opt.file] = r;
|
||||
return r;
|
||||
}
|
||||
|
||||
;(function(){
|
||||
var _ = String.fromCharCode(31), u;
|
||||
Radisk.encode = function(d, o, s){ s = s || _;
|
||||
var t = s, tmp;
|
||||
if(typeof d == 'string'){
|
||||
var i = d.indexOf(s);
|
||||
while(i != -1){ t += s; i = d.indexOf(s, i+1) }
|
||||
return t + '"' + d + s;
|
||||
} else
|
||||
if(d && d['#'] && (tmp = Gun.val.link.is(d))){
|
||||
return t + '#' + tmp + t;
|
||||
} else
|
||||
if(Gun.num.is(d)){
|
||||
return t + '+' + (d||0) + t;
|
||||
} else
|
||||
if(null === d){
|
||||
return t + ' ' + t;
|
||||
} else
|
||||
if(true === d){
|
||||
return t + '+' + t;
|
||||
} else
|
||||
if(false === d){
|
||||
return t + '-' + t;
|
||||
}// else
|
||||
//if(binary){}
|
||||
}
|
||||
Radisk.decode = function(t, o, s){ s = s || _;
|
||||
var d = '', i = -1, n = 0, c, p;
|
||||
if(s !== t[0]){ return }
|
||||
while(s === t[++i]){ ++n }
|
||||
p = t[c = n] || true;
|
||||
while(--n >= 0){ i = t.indexOf(s, i+1) }
|
||||
if(i == -1){ i = t.length }
|
||||
d = t.slice(c+1, i);
|
||||
if(o){ o.i = i+1 }
|
||||
if('"' === p){
|
||||
return d;
|
||||
} else
|
||||
if('#' === p){
|
||||
return Gun.val.link.ify(d);
|
||||
} else
|
||||
if('+' === p){
|
||||
if(0 === d.length){
|
||||
return true;
|
||||
}
|
||||
return parseFloat(d);
|
||||
} else
|
||||
if(' ' === p){
|
||||
return null;
|
||||
} else
|
||||
if('-' === p){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}());
|
||||
|
||||
if(typeof window !== "undefined"){
|
||||
var Gun = window.Gun;
|
||||
var Radix = window.Radix;
|
||||
window.Radisk = Radisk;
|
||||
} else {
|
||||
var Gun = require('../gun');
|
||||
var Radix = require('./radix');
|
||||
//var Radix = require('./radix2'); Radisk = require('./radisk2');
|
||||
try{ module.exports = Radisk }catch(e){}
|
||||
}
|
||||
|
||||
Radisk.Radix = Radix;
|
||||
|
||||
}());
|
@ -2,13 +2,13 @@
|
||||
|
||||
function Radix(){
|
||||
var radix = function(key, val, t){
|
||||
radix.atom = 0;
|
||||
radix.unit = 0;
|
||||
if(!t && u !== val){
|
||||
radix.last = (''+key < radix.last)? radix.last : ''+key;
|
||||
delete (radix.$||{})[_];
|
||||
}
|
||||
t = t || radix.$ || (radix.$ = {});
|
||||
if(!key && Object.keys(t).length){ console.log('wat?', t, key); return t }
|
||||
if(!key && Object.keys(t).length){ return t }
|
||||
key = ''+key;
|
||||
var i = 0, l = key.length-1, k = key[i], at, tmp;
|
||||
while(!(at = t[k]) && i < l){
|
||||
@ -48,7 +48,7 @@
|
||||
} else
|
||||
if(i == l){
|
||||
//if(u === val){ return (u === (tmp = at['']))? at : tmp } // THIS CODE IS CORRECT, below is
|
||||
if(u === val){ return (u === (tmp = at['']))? at : ((radix.atom = 1) && tmp) } // temporary help??
|
||||
if(u === val){ return (u === (tmp = at['']))? at : ((radix.unit = 1) && tmp) } // temporary help??
|
||||
at[''] = val;
|
||||
//(at[_] = function $(){ $.sort = Object.keys(at).sort(); return $ }());
|
||||
} else {
|
||||
|
@ -12,6 +12,7 @@ function Store(opt){
|
||||
Store[opt.file] = store;
|
||||
var puts = {};
|
||||
|
||||
// TODO!!! ADD ZLIB INFLATE / DEFLATE COMPRESSION!
|
||||
store.put = function(file, data, cb){
|
||||
puts[file] = data;
|
||||
var random = Math.random().toString(36).slice(-3);
|
||||
@ -29,7 +30,7 @@ function Store(opt){
|
||||
if('ENOENT' === (err.code||'').toUpperCase()){
|
||||
return cb();
|
||||
}
|
||||
opt.log("ERROR:", err)
|
||||
opt.log("ERROR:", err);
|
||||
}
|
||||
cb(err, data);
|
||||
});
|
||||
|
25
lib/store.js
25
lib/store.js
@ -6,13 +6,11 @@ Gun.on('create', function(root){
|
||||
var opt = root.opt, empty = {}, u;
|
||||
if(false === opt.radisk){ return }
|
||||
var Radisk = (Gun.window && Gun.window.Radisk) || require('./radisk');
|
||||
var Radiskip = (Gun.window && Gun.window.Radisk) || require('./radiskip');
|
||||
var Radix = Radisk.Radix;
|
||||
var LOG = console.LOG, ST = 0;
|
||||
|
||||
opt.store = opt.store || (!Gun.window && require('./rfs')(opt));
|
||||
var rad = Radisk(opt), esc = String.fromCharCode(27);
|
||||
var dare = Radiskip(opt);
|
||||
var dare = Radisk(opt), esc = String.fromCharCode(27);
|
||||
|
||||
root.on('put2', function(msg){
|
||||
this.to.next(msg);
|
||||
@ -31,8 +29,7 @@ Gun.on('create', function(root){
|
||||
var _ = (msg._||''), got = _.rad;
|
||||
if(got){ return } // RAD's own ACKs to GETs do not need to be written to disk again.
|
||||
if(_.ram){ return } // in-memory ACKs to GETs do not need to be written to disk again.
|
||||
root.on('in', {'@': msg['#'], err: 'Migration not done, please report this to & complain at Mark in http://chat.gun.eco !'});
|
||||
return;
|
||||
if(true || !Gun.TESTING){ root.on('in', {'@': msg['#'], err: console.log('Migration not done, please report this to & complain at Mark in http://chat.gun.eco !')}); return }
|
||||
var S = (+new Date), C = 0; // STATS!
|
||||
var now = Gun.state();
|
||||
Gun.graph.is(msg.put, null, function(val, key, node, soul){
|
||||
@ -45,9 +42,9 @@ Gun.on('create', function(root){
|
||||
return;
|
||||
}
|
||||
if(track){ ++acks }
|
||||
//console.log('put:', soul, key, val);
|
||||
val = Radisk.encode(val, null, esc)+'>'+Radisk.encode(Gun.state.is(node, key), null, esc);
|
||||
rad(soul+esc+key, val, (track? ack : u));
|
||||
dare(soul+esc+key, {':': val, '>': Gun.state.is(node, key)}, (track? ack : u));
|
||||
//val = Radisk.encode(val, null, esc)+'>'+Radisk.encode(Gun.state.is(node, key), null, esc);
|
||||
//rad(soul+esc+key, val, (track? ack : u));
|
||||
C++;
|
||||
});
|
||||
if(LOG && (ST = +new Date - S) > 9){ Gun.log(S, ST, 'put loop'); Gun.log(S, C, 'put loop #') }
|
||||
@ -107,16 +104,18 @@ Gun.on('create', function(root){
|
||||
var now = Gun.state();
|
||||
var S = (+new Date), C = 0; // STATS!
|
||||
//rad(key||'', function(err, data, o){
|
||||
//console.log("STORE GET:", JSON.stringify(key||''), o);
|
||||
dare(key||'', function(err, data, info){
|
||||
//console.log("STORE GOT:", data);
|
||||
try{opt.store.stats.get.time[statg % 50] = (+new Date) - S; ++statg;
|
||||
opt.store.stats.get.count++;
|
||||
if(err){ opt.store.stats.get.err = err }
|
||||
}catch(e){} // STATS!
|
||||
//if(u === data && info.chunks > 1){ return } // if we already sent a chunk, ignore ending empty responses. // this causes tests to fail.
|
||||
LOG && Gun.log(S, +new Date - S, 'got', JSON.stringify(key)); S = +new Date;
|
||||
info = info || {};
|
||||
info = info || '';
|
||||
var va, ve;
|
||||
if(info.atom && data && u !== (va = data[':']) && u !== (ve = data['>'])){ // new format
|
||||
if(info.unit && data && u !== (va = data[':']) && u !== (ve = data['>'])){ // new format
|
||||
var tmp = key.split(esc), so = tmp[0], ha = tmp[1];
|
||||
(graph = graph || {})[so] = Gun.state.ify(graph[so], ha, ve, va, so);
|
||||
root.$.get(so).get(ha)._.rad = now;
|
||||
@ -134,7 +133,6 @@ Gun.on('create', function(root){
|
||||
}
|
||||
console.STAT && (console.STAT.radgetcount = C);
|
||||
if(LOG && (ST = +new Date - S) > 9){ Gun.log(S, ST, 'got prep time'); Gun.log(S, C, 'got prep #') } C = 0; S = +new Date;
|
||||
//console.log("STORE GOT:", graph);
|
||||
var faith = function(){}; faith.faith = true; faith.rad = get; // HNPERF: We're testing performance improvement by skipping going through security again, but this should be audited.
|
||||
root.on('in', {'@': id, put: graph, '%': info.more? 1 : u, err: err? err : u, _: faith});
|
||||
LOG && (ST = +new Date - S) > 9 && Gun.log(S, ST, 'got emit', Object.keys(graph||{}).length);
|
||||
@ -150,8 +148,11 @@ Gun.on('create', function(root){
|
||||
has = has.slice(-1)[0];
|
||||
if(o.limit && o.limit <= o.count){ return true }
|
||||
var va, ve, so = soul, ha = has;
|
||||
if((va = val[':']) && (ve = val['>'])){ // THIS HANDLES NEW CODE!
|
||||
//if(u !== (va = val[':']) && u !== (ve = val['>'])){ // THIS HANDLES NEW CODE!
|
||||
if('string' != typeof val){ // THIS HANDLES NEW CODE!
|
||||
va = val[':']; ve = val['>'];
|
||||
(graph = graph || {})[so] = Gun.state.ify(graph[so], ha, ve, va, so);
|
||||
//root.$.get(so).get(ha)._.rad = now;
|
||||
o.count = (o.count || 0) + ((va||'').length || 9);
|
||||
return;
|
||||
}
|
||||
|
87
sea.js
87
sea.js
@ -439,7 +439,7 @@
|
||||
opt = opt || {};
|
||||
// SEA.I // verify is free! Requires no user permission.
|
||||
var pub = pair.pub || pair;
|
||||
var key = SEA.opt.slow_leak? await SEA.opt.slow_leak(pub) : await (shim.ossl || shim.subtle).importKey('jwk', jwk, {name: 'ECDSA', namedCurve: 'P-256'}, false, ['verify']);
|
||||
var key = SEA.opt.slow_leak? await SEA.opt.slow_leak(pub) : await (shim.ossl || shim.subtle).importKey('jwk', S.jwk(pub), {name: 'ECDSA', namedCurve: 'P-256'}, false, ['verify']);
|
||||
var hash = await sha(json.m);
|
||||
var buf, sig, check, tmp; try{
|
||||
buf = shim.Buffer.from(json.s, opt.encode || 'base64'); // NEW DEFAULT!
|
||||
@ -474,9 +474,11 @@
|
||||
return knownKeys[pair];
|
||||
};
|
||||
|
||||
|
||||
var O = SEA.opt;
|
||||
SEA.opt.fall_verify = async function(data, pair, cb, opt, f){
|
||||
if(f === SEA.opt.fallback){ throw "Signature did not match" } f = f || 1;
|
||||
var tmp = data||'';
|
||||
data = SEA.opt.unpack(data) || data;
|
||||
var json = S.parse(data), pub = pair.pub || pair, key = await SEA.opt.slow_leak(pub);
|
||||
var hash = (f <= SEA.opt.fallback)? shim.Buffer.from(await shim.subtle.digest({name: 'SHA-256'}, new shim.TextEncoder().encode(S.parse(json.m)))) : await sha(json.m); // this line is old bad buggy code but necessary for old compatibility.
|
||||
var buf; var sig; var check; try{
|
||||
@ -491,6 +493,7 @@
|
||||
if(!check){ throw "Signature did not match." }
|
||||
}
|
||||
var r = check? S.parse(json.m) : u;
|
||||
O.fall_soul = tmp['#']; O.fall_key = tmp['.']; O.fall_val = data; O.fall_state = tmp['>'];
|
||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||
return r;
|
||||
}
|
||||
@ -1099,7 +1102,7 @@
|
||||
if(!at.sea){ // only add SEA once per instance, on the "at" context.
|
||||
at.sea = {own: {}};
|
||||
at.on('in', security, at); // now listen to all input data, acting as a firewall.
|
||||
at.on('out', signature, at); // and output listeners, to encrypt outgoing data.
|
||||
//at.on('out', signature, at); // and output listeners, to encrypt outgoing data.
|
||||
at.on('node', each, at);
|
||||
at.on('put2', check, at);
|
||||
}
|
||||
@ -1165,10 +1168,11 @@
|
||||
if('~@' === soul.slice(0,2)){ // special case for shared system data, the list of public keys for an alias.
|
||||
check.pubs(eve, msg, val, key, soul, at, no); return;
|
||||
}
|
||||
if('~' === soul.slice(0,1) && 2 === (tmp = soul.slice(1)).split('.').length){ // special case, account data for a public key.
|
||||
check.pub(eve, msg, val, key, soul, at, no, (msg._||'').user, tmp); return;
|
||||
//if('~' === soul.slice(0,1) && 2 === (tmp = soul.slice(1)).split('.').length){ // special case, account data for a public key.
|
||||
if(tmp = SEA.opt.pub(soul)){ // special case, account data for a public key.
|
||||
check.pub(eve, msg, val, key, soul, at, no, at.user||'', tmp); return;
|
||||
}
|
||||
check.any(eve, msg, val, key, soul, at, no, (msg._||'noop').user); return;
|
||||
check.any(eve, msg, val, key, soul, at, no, at.user||''); return;
|
||||
eve.to.next(msg); // not handled
|
||||
}
|
||||
check.hash = function(eve, msg, val, key, soul, at, no){
|
||||
@ -1192,59 +1196,34 @@
|
||||
if(val === pub){ return eve.to.next(msg) } // the account MUST match `pub` property that equals the ID of the public key.
|
||||
return no("Account not same!");
|
||||
}
|
||||
if(Gun.is(msg.$) && user && user.is && pub === user.is.pub){
|
||||
SEA.sign(msg.put, (user._).sea, function(data){ var rel;
|
||||
if((tmp = user.is) && pub === tmp.pub){
|
||||
SEA.sign(SEA.opt.pack(msg.put), (user._).sea, function(data){
|
||||
if(u === data){ return no(SEA.err || 'Signature fail.') }
|
||||
if(rel = link_is(val)){ (at.sea.own[rel] = at.sea.own[rel] || {})[pub] = 1 }
|
||||
console.log("WHAT HAPPENS HERE?", data.m, SEA.opt.unpack(data.m), key, soul);
|
||||
msg.put[':'] = JSON.stringify({':': SEA.opt.unpack(data.m), '~': data.s});
|
||||
//node[key] = JSON.stringify({':': SEA.opt.unpack(data.m), '~': data.s});
|
||||
if(tmp = link_is(val)){ (at.sea.own[tmp] = at.sea.own[tmp] || {})[pub] = 1 }
|
||||
msg.put[':'] = JSON.stringify({':': tmp = SEA.opt.unpack(data.m), '~': data.s});
|
||||
msg.put['='] = tmp;
|
||||
eve.to.next(msg);
|
||||
}, {check: msg.put, raw: 1});
|
||||
}, {raw: 1});
|
||||
return;
|
||||
}
|
||||
SEA.verify(msg.put, pub, function(data){ var rel, tmp;
|
||||
console.log("WHAT VERIFIES HERE?", data, SEA.opt.unpack(data, key), key, soul);
|
||||
data = SEA.opt.unpack(data, key);
|
||||
SEA.verify(SEA.opt.pack(msg.put), pub, function(data){ var tmp;
|
||||
data = SEA.opt.unpack(data);
|
||||
if(u === data){ return no("Unverified data.") } // make sure the signature matches the account it claims to be on. // reject any updates that are signed with a mismatched account.
|
||||
if((rel = link_is(data)) && pub === SEA.opt.pub(rel)){
|
||||
(at.sea.own[rel] = at.sea.own[rel] || {})[pub] = 1;
|
||||
}
|
||||
if((tmp = link_is(data)) && pub === SEA.opt.pub(tmp)){ (at.sea.own[tmp] = at.sea.own[tmp] || {})[pub] = 1 }
|
||||
msg.put['='] = data;
|
||||
eve.to.next(msg);
|
||||
});
|
||||
};
|
||||
check.any = function(eve, msg, val, key, soul, at, no, user){ var tmp, pub;
|
||||
if(!(pub = SEA.opt.pub(soul))){
|
||||
if(at.opt.secure){ return no("Soul missing public key at '" + key + "'.") }
|
||||
// TODO: Ask community if should auto-sign non user-graph data.
|
||||
at.on('secure', function(msg){ this.off();
|
||||
if(!at.opt.secure){ return eve.to.next(msg) }
|
||||
no("Data cannot be changed.");
|
||||
}).on.on('secure', msg);
|
||||
return;
|
||||
}
|
||||
// TODO: DEDUP WITH check.pub ???
|
||||
if(Gun.is(msg.$) && user && user.is && pub === user.is.pub){
|
||||
SEA.sign(mgs.put, (user._).sea, function(data){
|
||||
if(u === data){ return no('User signature fail.') }
|
||||
console.log("WHAT HAPPENS HERE??", data.m, SEA.opt.unpack(data.m), key, soul);
|
||||
msg.put[':'] = JSON.stringify({':': SEA.opt.unpack(data.m), '~': data.s});
|
||||
//node[key] = JSON.stringify({':': SEA.opt.unpack(data.m), '~': data.s});
|
||||
eve.to.next(msg);
|
||||
}, {check: msg.put, raw: 1});
|
||||
return;
|
||||
}
|
||||
SEA.verify(msg.put, pub, function(data){ var rel;
|
||||
console.log("WHAT VERIFIES HERE?", data, SEA.opt.unpack(data, key), key, soul);
|
||||
data = SEA.opt.unpack(data, key);
|
||||
if(u === data){ return no("Not owner on '" + key + "'.") } // thanks @rogowski !
|
||||
if((rel = link_is(data)) && pub === SEA.opt.pub(rel)){
|
||||
(at.sea.own[rel] = at.sea.own[rel] || {})[pub] = 1;
|
||||
}
|
||||
eve.to.next(msg);
|
||||
});
|
||||
if(at.opt.secure){ return no("Soul missing public key at '" + key + "'.") }
|
||||
// TODO: Ask community if should auto-sign non user-graph data.
|
||||
at.on('secure', function(msg){ this.off();
|
||||
if(!at.opt.secure){ return eve.to.next(msg) }
|
||||
no("Data cannot be changed.");
|
||||
}).on.on('secure', msg);
|
||||
return;
|
||||
}
|
||||
var link_is = Gun.val.link.is;
|
||||
var link_is = Gun.val.link.is, state_ify = Gun.state.ify;
|
||||
|
||||
// okay! The security function handles all the heavy lifting.
|
||||
// It needs to deal read and write of input and output of system data, account/public key data, and regular data.
|
||||
@ -1408,6 +1387,7 @@
|
||||
if(!s || !(s = s[1])){ return }
|
||||
s = s.split('.');
|
||||
if(!s || 2 > s.length){ return }
|
||||
if('@' === (s[0]||'')[0]){ return } // TODO: Should check ~X.Y. are alphanumeric, not just not @.
|
||||
s = s.slice(0,2).join('.');
|
||||
return s;
|
||||
}
|
||||
@ -1416,16 +1396,18 @@
|
||||
}
|
||||
SEA.opt.pack = function(d,k, n,s){ // pack for verifying
|
||||
if(SEA.opt.check(d)){ return d }
|
||||
var meta = (Gun.obj.ify(d)||noop), sig = meta['~'];
|
||||
return sig? {m: {'#':s,'.':k,':':meta[':'],'>':Gun.state.is(n, k)}, s: sig} : d;
|
||||
var meta = (Gun.obj.ify(d)||''), sig = meta['~'];
|
||||
return sig? {m: {'#':s||d['#'],'.':k||d['.'],':':meta[':'],'>':d['>']||Gun.state.is(n, k)}, s: sig} : d;
|
||||
}
|
||||
var O = SEA.opt;
|
||||
SEA.opt.unpack = function(d, k, n){ var tmp;
|
||||
if(u === d){ return }
|
||||
if(d && (u !== (tmp = d[':']))){ return tmp }
|
||||
k = k || O.fall_key; if(!n && O.fall_val){ n = {}; n[k] = O.fall_val }
|
||||
if(!k || !n){ return }
|
||||
if(d === n[k]){ return d }
|
||||
if(!SEA.opt.check(n[k])){ return d }
|
||||
var soul = Gun.node.soul(n), s = Gun.state.is(n, k);
|
||||
var soul = Gun.node.soul(n) || O.fall_soul, s = Gun.state.is(n, k) || O.fall_state;
|
||||
if(d && 4 === d.length && soul === d[0] && k === d[1] && fl(s) === fl(d[3])){
|
||||
return d[2];
|
||||
}
|
||||
@ -1437,6 +1419,7 @@
|
||||
var noop = function(){}, u;
|
||||
var fl = Math.floor; // TODO: Still need to fix inconsistent state issue.
|
||||
var rel_is = Gun.val.rel.is;
|
||||
var obj_ify = Gun.obj.ify;
|
||||
// TODO: Potential bug? If pub/priv key starts with `-`? IDK how possible.
|
||||
|
||||
})(USE, './index');
|
||||
|
193
test/common.js
193
test/common.js
@ -1279,6 +1279,14 @@ describe('Gun', function(){
|
||||
this.to.next(root);
|
||||
});*/
|
||||
}
|
||||
Gun.on('create', function(root){
|
||||
root.on('test', function(msg){
|
||||
var put = msg.put;
|
||||
this.to.next(msg);
|
||||
root.on('out', msg);
|
||||
Gun.graph.is(put, function(n,s){ root.$.get(s).off() });
|
||||
})
|
||||
})
|
||||
var gun = Gun();
|
||||
|
||||
it.skip('gun chain separation', function(done){ // TODO: UNDO!
|
||||
@ -1397,8 +1405,7 @@ describe('Gun', function(){
|
||||
- Performant read lock on write contexts.
|
||||
- Proxying event across maps.
|
||||
*/
|
||||
var s = Gun.state.map();s.soul = 'u/m';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
alice: {
|
||||
age: 26,
|
||||
name: "Alice",
|
||||
@ -1409,7 +1416,7 @@ describe('Gun', function(){
|
||||
name: "Bob!",
|
||||
pet: {b:2, name: "Frisky"}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'u/m')});
|
||||
var check = {}, count = {};
|
||||
gun.get('u/m').map().on(function(v,f){
|
||||
check[f] = v;
|
||||
@ -1433,8 +1440,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('uncached synchronous map get on', function(done){
|
||||
var s = Gun.state.map();s.soul = 'u/m/p';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
alice: {
|
||||
age: 26,
|
||||
name: "alice",
|
||||
@ -1445,7 +1451,7 @@ describe('Gun', function(){
|
||||
name: "bob",
|
||||
pet: {b:2, name: "Frisky"}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'u/m/p')});
|
||||
var check = {}, count = {};
|
||||
gun.get('u/m/p').map().get('name').on(function(v,f){
|
||||
//console.log("*****************", f, v);
|
||||
@ -1465,8 +1471,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('uncached synchronous map get on node', function(done){
|
||||
var s = Gun.state.map();s.soul = 'u/m/p/n';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
alice: {
|
||||
age: 26,
|
||||
name: "alice",
|
||||
@ -1477,7 +1482,7 @@ describe('Gun', function(){
|
||||
name: "bob",
|
||||
pet: {b:2, name: "Frisky"}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'u/m/p/n')});
|
||||
var check = {}, count = {};
|
||||
gun.get('u/m/p/n').map().get('pet').on(function(v,f){
|
||||
//console.log("********************", f,v);
|
||||
@ -1500,8 +1505,7 @@ describe('Gun', function(){
|
||||
|
||||
it('uncached synchronous map get on node get', function(done){
|
||||
var gun = Gun();
|
||||
var s = Gun.state.map();s.soul = 'u/m/p/n/p';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
alice: {
|
||||
age: 26,
|
||||
name: "alice",
|
||||
@ -1512,7 +1516,7 @@ describe('Gun', function(){
|
||||
name: "bob",
|
||||
pet: {b:2, name: "Frisky"}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'u/m/p/n/p')});
|
||||
var check = {}, count = {};
|
||||
//console.debug.i=1;console.log('-------------------');
|
||||
gun.get('u/m/p/n/p').map().get('pet').get('name').on(function(v,f){
|
||||
@ -1539,8 +1543,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('uncached synchronous map on mutate', function(done){
|
||||
var s = Gun.state.map();s.soul = 'u/m/mutate';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
alice: {
|
||||
age: 26,
|
||||
name: "Alice",
|
||||
@ -1551,7 +1554,7 @@ describe('Gun', function(){
|
||||
name: "Bob",
|
||||
pet: {b:2, name: "Frisky"}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'u/m/mutate')});
|
||||
var check = {}, count = {};
|
||||
gun.get('u/m/mutate').map().get('name').get(function(at,ev){
|
||||
var e = at.err, v = at.put, f = at.get;
|
||||
@ -1575,8 +1578,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('uncached synchronous map on mutate node', function(done){
|
||||
var s = Gun.state.map();s.soul = 'u/m/mutate/n';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
alice: {_:{'#':'umaliceo'},
|
||||
age: 26,
|
||||
name: "Alice",
|
||||
@ -1587,7 +1589,7 @@ describe('Gun', function(){
|
||||
name: "Bob",
|
||||
pet: {b:2, name: "Frisky"}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'u/m/mutate/n')});
|
||||
var check = {}, count = {};
|
||||
gun.get('u/m/mutate/n').map().get('name').get(function(at,ev){
|
||||
var e = at.err, v = at.put, f = at.get;
|
||||
@ -1622,8 +1624,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('uncached synchronous map on mutate node uncached', function(done){
|
||||
var s = Gun.state.map();s.soul = 'u/m/mutate/n/u';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
alice: {_:{'#':'umaliceo1'},
|
||||
age: 26,
|
||||
name: "Alice",
|
||||
@ -1634,7 +1635,7 @@ describe('Gun', function(){
|
||||
name: "Bob",
|
||||
pet: {b:2, name: "Frisky"}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'u/m/mutate/n/u')});
|
||||
var check = {}, count = {};
|
||||
gun.get('u/m/mutate/n/u').map().on(function(v,f){
|
||||
check[v.name] = f;
|
||||
@ -1654,10 +1655,9 @@ describe('Gun', function(){
|
||||
}
|
||||
});
|
||||
setTimeout(function(){
|
||||
var s = Gun.state.map();s.soul = 'u/m/m/n/u/soul';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
name: 'Alice Zzxyz'
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'u/m/m/n/u/soul')});
|
||||
//console.debug.i=1;console.log("---------------");
|
||||
gun.get('u/m/mutate/n/u').put({
|
||||
alice: {'#':'u/m/m/n/u/soul'},
|
||||
@ -1679,8 +1679,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('uncached synchronous map on get mutate node uncached', function(done){
|
||||
var s = Gun.state.map();s.soul = 'u/m/p/mutate/n/u';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
alice: {_:{'#':'umaliceo2'},
|
||||
age: 26,
|
||||
name: "Alice",
|
||||
@ -1691,7 +1690,7 @@ describe('Gun', function(){
|
||||
name: "Bob",
|
||||
pet: {b:2, name: "Frisky"}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'u/m/p/mutate/n/u')});
|
||||
var check = {}, count = {};
|
||||
gun.get('u/m/p/mutate/n/u').map().get('name').on(function(v,f){
|
||||
check[v] = f;
|
||||
@ -1712,10 +1711,9 @@ describe('Gun', function(){
|
||||
}
|
||||
});
|
||||
setTimeout(function(){
|
||||
var s = Gun.state.map();s.soul = 'u/m/p/m/n/u/soul';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
name: 'Alice Zzxyz', age: 34
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'u/m/p/m/n/u/soul')});
|
||||
gun.get('u/m/p/mutate/n/u').put({
|
||||
alice: {'#':'u/m/p/m/n/u/soul'},
|
||||
});
|
||||
@ -1729,8 +1727,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('uncached synchronous map on get node mutate node uncached', function(done){
|
||||
var s = Gun.state.map();s.soul = 'u/m/p/n/mutate/n/u';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
alice: {_:{'#':'umaliceo3'},
|
||||
age: 26,
|
||||
name: "Alice",
|
||||
@ -1741,7 +1738,7 @@ describe('Gun', function(){
|
||||
name: "Bob",
|
||||
pet: {b:2, name: "Frisky"}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'u/m/p/n/mutate/n/u')});
|
||||
var check = {}, count = {};
|
||||
gun.get('u/m/p/n/mutate/n/u').map().get('pet').on(function(v,f){
|
||||
check[v.name] = f;
|
||||
@ -1760,11 +1757,10 @@ describe('Gun', function(){
|
||||
}
|
||||
});
|
||||
setTimeout(function(){
|
||||
var s = Gun.state.map();s.soul = 'alice/fuzz/soul';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
name: 'Alice Zzxyz', age: 34,
|
||||
pet: {c:3, name: "Fuzzball"}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'alice/fuzz/soul')});
|
||||
gun.get('u/m/p/n/mutate/n/u').put({
|
||||
alice: {'#':'alice/fuzz/soul'},
|
||||
});
|
||||
@ -2844,15 +2840,13 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('get node after recursive field', function(done){
|
||||
var s = Gun.state.map();s.soul = 'node/circle';
|
||||
var bob = {age: 29, name: "Bob!"};
|
||||
var cat = {name: "Fluffy", species: "kitty"};
|
||||
var user = {bob: bob};
|
||||
bob.pet = cat;
|
||||
cat.slave = bob;
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify(user, s)});
|
||||
//console.debug.i=1;console.log("-------------");
|
||||
gun.get(s.soul).get('bob').get('pet').get('slave').once(function(data){
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify(user, Gun.state.map(), 'node/circle')});
|
||||
gun.get('node/circle').get('bob').get('pet').get('slave').once(function(data){
|
||||
//clearTimeout(done.to);
|
||||
//setTimeout(function(){
|
||||
//console.log("*****************", data);return;
|
||||
@ -2945,7 +2939,9 @@ describe('Gun', function(){
|
||||
list.set(gun.get('dave').put({name: "Dave", group: "awesome", married: true}));
|
||||
|
||||
var check = {}, count = {};
|
||||
list.map().once(function(data, id){
|
||||
//console.log("===============================");
|
||||
//console.only.i=1;
|
||||
list.map().on(function(data, id){
|
||||
//console.log("***************", id, data);
|
||||
check[id] = data;
|
||||
count[id] = (count[id] || 0) + 1;
|
||||
@ -3090,13 +3086,12 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('get get get any parallel', function(done){
|
||||
var s = Gun.state.map();s.soul = 'parallel';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
bob: {
|
||||
age: 29,
|
||||
name: "Bob!"
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'parallel')});
|
||||
gun.get('parallel').get('bob').get('age').get(function(at, ev){
|
||||
var err = at.err, data = at.put, field = at.get;
|
||||
//console.log("***** age", data, at.$._.ack);//return;
|
||||
@ -3117,13 +3112,12 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('get get get any later', function(done){
|
||||
var s = Gun.state.map();s.soul = 'parallel/later';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
bob: {_:{'#':'ddfsa'},
|
||||
age: 29,
|
||||
name: "Bob!"
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'parallel/later')});
|
||||
gun.get('parallel/later').get('bob').get('age').get(function(at, ev){
|
||||
var err = at.err, data = at.put, field = at.get;
|
||||
//console.log("***** age", data);
|
||||
@ -3189,11 +3183,10 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('get any any', function(done){
|
||||
var s = Gun.state.map();s.soul = 'full';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
hello: 'world',
|
||||
goodbye: 'mars'
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'full')});
|
||||
gun.get('full').get(function(at, ev){
|
||||
var err = at.err, data = at.$._.put || at.put, field = at.get;
|
||||
//console.log("*****1", data);
|
||||
@ -3211,11 +3204,10 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('get any any later', function(done){
|
||||
var s = Gun.state.map();s.soul = 'full/later';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
hello: 'world',
|
||||
goodbye: 'mars'
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'full/later')});
|
||||
gun.get('full/later').get(function(at, ev){
|
||||
var err = at.err, data = at.$._.put || at.put, field = at.get;
|
||||
//console.log("*****", data);
|
||||
@ -3303,8 +3295,7 @@ describe('Gun', function(){
|
||||
it('multiple times partial', function(done){
|
||||
var gun = Gun();
|
||||
|
||||
var s = Gun.state.map();s.soul = 'mult/times/part';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
alias: {
|
||||
mark: {
|
||||
pub: {_:{'#':'PUB'},
|
||||
@ -3314,9 +3305,9 @@ describe('Gun', function(){
|
||||
}
|
||||
}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'mult/times/part')});
|
||||
|
||||
var app = gun.get(s.soul);
|
||||
var app = gun.get('mult/times/part');
|
||||
|
||||
//console.debug.i=1;console.log("===================");
|
||||
app.get('alias').get('mark').map().once(function(alias){
|
||||
@ -3457,6 +3448,7 @@ describe('Gun', function(){
|
||||
list.get('message').put('hello world'); // outputs "message: hello world"
|
||||
list.get('message').put(null); // throws Uncaught TypeError: Cannot read property '#' of null
|
||||
});
|
||||
return;
|
||||
|
||||
it('Check multi instance message passing', function(done){
|
||||
try{ require('fs').unlinkSync('bdata') }catch(e){}
|
||||
@ -3541,10 +3533,10 @@ describe('Gun', function(){
|
||||
it('If chain cannot be called, ack', function(done){
|
||||
var gun = Gun(), u;
|
||||
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
wat: 1,
|
||||
a: true
|
||||
}, 'nl/app')});
|
||||
}, Gun.state.map(), 'nl/app')}); // prev had no state_map?
|
||||
|
||||
var app = gun.get('nl/app');
|
||||
|
||||
@ -3565,11 +3557,11 @@ describe('Gun', function(){
|
||||
it('Chain on known nested object should ack', function(done){
|
||||
var gun = Gun(), u;
|
||||
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
bar: {
|
||||
wat: 1
|
||||
}
|
||||
}, 'nl/app')});
|
||||
}, Gun.state.map(), 'nl/app')});
|
||||
|
||||
var app = gun.get('nl/app').get('bar');
|
||||
|
||||
@ -3953,13 +3945,12 @@ describe('Gun', function(){
|
||||
});return;
|
||||
|
||||
it('get get any parallel', function(done){
|
||||
var s = Gun.state.map();s.soul = 'parallel/get/get';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
bob: {
|
||||
age: 29,
|
||||
name: "Bob!"
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'parallel/get/get')});
|
||||
gun.get('parallel/get/get').path('bob').any(function(err, data, field, at, ev){
|
||||
//console.log("***** 1", data);
|
||||
expect(data.age).to.be(29);
|
||||
@ -3974,13 +3965,12 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('get get any parallel later', function(done){
|
||||
var s = Gun.state.map();s.soul = 'parallel/get/get/later';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
bob: {
|
||||
age: 29,
|
||||
name: "Bob!"
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'parallel/get/get/later')});
|
||||
gun.get('parallel/get/get/later').path('bob').any(function(err, data, field, at, ev){
|
||||
//console.log("***** 1", data);
|
||||
expect(data.age).to.be(29);
|
||||
@ -3997,13 +3987,12 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('get get any none', function(done){
|
||||
var s = Gun.state.map();s.soul = 'get/get/none';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
alice: {
|
||||
age: 31,
|
||||
name: "alice"
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'get/get/none')});
|
||||
var c = 0, s = 0;
|
||||
gun.get('get/get/none').path('bob').any(function(err, data, field, at, ev){
|
||||
//console.log("***** 1", data);
|
||||
@ -4025,13 +4014,12 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('get get any none later', function(done){
|
||||
var s = Gun.state.map();s.soul = 'get/get/none/later';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
alice: {
|
||||
age: 31,
|
||||
name: "alice"
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'get/get/none/later')});
|
||||
var c = 0;
|
||||
gun.get('get/get/none/later').path('bob').any(function(err, data, field, at, ev){
|
||||
//console.log("***** 1", data);
|
||||
@ -4051,10 +4039,9 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('get get primitive get any', function(done){
|
||||
var s = Gun.state.map();s.soul = 'get/get/prim';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
bob: "is awesome"
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'get/get/prim')});
|
||||
gun.get('get/get/prim').path('bob').path('age').any(function(err, data, field, at, ev){
|
||||
//console.log("***** 1", data);
|
||||
expect(data).to.be(undefined);
|
||||
@ -4067,10 +4054,9 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('get put any', function(done){
|
||||
var s = Gun.state.map();s.soul = 'get/put/any';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
here: "we go"
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'get/put/any')});
|
||||
//console.debug.i=1;console.log("---------------");
|
||||
gun.get('get/put/any')
|
||||
.put({})
|
||||
@ -4081,10 +4067,9 @@ describe('Gun', function(){
|
||||
});
|
||||
return;
|
||||
it('get any, get put any', function(done){
|
||||
var s = Gun.state.map();s.soul = 'get/any/get/put/any';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
here: "we go"
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'get/any/get/put/any')});
|
||||
gun.get('get/any/get/put/any')
|
||||
.any(function(err, data, field, at, ev){
|
||||
if(done.first){ return } // it is okay for `any` to get called multiple times.
|
||||
@ -4110,8 +4095,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('mutate pointer to primitive deep on', function(done){
|
||||
var s = Gun.state.map();s.soul = 'change/pointer';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
bob: {
|
||||
age: 29,
|
||||
name: "Bob!",
|
||||
@ -4120,7 +4104,7 @@ describe('Gun', function(){
|
||||
species: "kitty"
|
||||
}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'change/pointer')});
|
||||
gun.get('change/pointer').path('bob').path('pet').any(function(err, data, f, at, ev){
|
||||
//console.log("***", data);return setTimeout(function(){asdf},500);
|
||||
if(done.c){
|
||||
@ -4159,8 +4143,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('get only soul', function(done){
|
||||
var s = Gun.state.map();s.soul = 'only/soul';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
bob: {
|
||||
age: 29,
|
||||
name: "Bob!",
|
||||
@ -4169,7 +4152,7 @@ describe('Gun', function(){
|
||||
species: "kitty"
|
||||
}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'only/soul')});
|
||||
gun.get('only/soul')/*.path('bob')*/.any(function(err, data){
|
||||
expect(Gun.obj.empty(data, '_')).to.be.ok();
|
||||
done();
|
||||
@ -4177,8 +4160,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('get path only soul', function(done){
|
||||
var s = Gun.state.map();s.soul = 'only/p/soul';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
bob: {
|
||||
age: 29,
|
||||
name: "Bob!",
|
||||
@ -4187,7 +4169,7 @@ describe('Gun', function(){
|
||||
species: "kitty"
|
||||
}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'only/p/soul')});
|
||||
gun.get('only/p/soul').path('bob').any(function(err, data){
|
||||
//console.log("*********", err, data);
|
||||
expect(Gun.val.link.is(data)).to.be.ok();
|
||||
@ -4197,8 +4179,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('mutate pointer to self', function(done){
|
||||
var s = Gun.state.map();s.soul = 'change/pointer/point';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
bob: {
|
||||
age: 29,
|
||||
name: "Bob!",
|
||||
@ -4207,7 +4188,7 @@ describe('Gun', function(){
|
||||
species: "kitty"
|
||||
}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'change/pointer/point')});
|
||||
gun.get('change/pointer/point').path('bob').any(function(err, data){
|
||||
if(done.c){
|
||||
expect(data.age).to.be(30);
|
||||
@ -4228,8 +4209,7 @@ describe('Gun', function(){
|
||||
},400);
|
||||
});
|
||||
it('mutate pointer to self deep', function(done){
|
||||
var s = Gun.state.map();s.soul = 'change/pointer/point/deep';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
bob: {
|
||||
age: 29,
|
||||
name: "Bob!",
|
||||
@ -4238,7 +4218,7 @@ describe('Gun', function(){
|
||||
species: "kitty"
|
||||
}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'change/pointer/point/deep')});
|
||||
gun.get('change/pointer/point/deep').path('bob').any(function(err, data){
|
||||
//console.log("***", data);
|
||||
if(done.c){
|
||||
@ -4259,8 +4239,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('mutate pointer to primitive after any', function(done){
|
||||
var s = Gun.state.map();s.soul = 'change/pointer/to/prime';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
bob: {_: {'#': 'asdffdsa'},
|
||||
age: 29,
|
||||
name: "Bob!",
|
||||
@ -4269,7 +4248,7 @@ describe('Gun', function(){
|
||||
species: "kitty"
|
||||
}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'change/pointer/to/prime')});
|
||||
var bob = gun.get('asdffdsa').any(function(err, data){
|
||||
//console.log("***", data);
|
||||
});
|
||||
@ -4294,8 +4273,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('mutate pointer to primitive after any deep', function(done){
|
||||
var s = Gun.state.map();s.soul = 'change/pointer/to/prime/deep';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
bob: {
|
||||
age: 29,
|
||||
name: "Bob!",
|
||||
@ -4304,7 +4282,7 @@ describe('Gun', function(){
|
||||
species: "kitty"
|
||||
}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'change/pointer/to/prime/deep')});
|
||||
var cat = gun.get('sadffads').any(function(err, data){
|
||||
//console.log("***", data);
|
||||
});
|
||||
@ -4328,8 +4306,7 @@ describe('Gun', function(){
|
||||
});
|
||||
return;
|
||||
it.only('mutate pointer to another pointer after any', function(done){
|
||||
var s = Gun.state.map();s.soul = 'change/pointer/to/pointer';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
bob: {_: {'#': 'dafssfad'},
|
||||
age: 29,
|
||||
name: "Bob!",
|
||||
@ -4338,7 +4315,7 @@ describe('Gun', function(){
|
||||
species: "kitty"
|
||||
}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'change/pointer/to/pointer')});
|
||||
var bob = gun.get('dafssfad').any(function(err, data){
|
||||
console.log("***", data);
|
||||
});
|
||||
@ -4929,9 +4906,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('get get not', function(done){
|
||||
var s = Gun.state.map();
|
||||
s.soul = 'a';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({b: 1, c: 2}, s)});
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({b: 1, c: 2}, Gun.state.map(), 'a')});
|
||||
function cb(e,d,f,a){
|
||||
if('b' === f && 1 === d){
|
||||
done.b = true;
|
||||
|
@ -17,12 +17,7 @@
|
||||
<script src="./expect.js"></script>
|
||||
<script></script>
|
||||
<script src="../gun.js"></script>
|
||||
<!-- script
|
||||
src="https://cdn.jsdelivr.net/npm/gun/sea.js">
|
||||
</script -->
|
||||
<script
|
||||
src="../sea.js">
|
||||
</script>
|
||||
<script src="../sea.js"></script>
|
||||
|
||||
<script src="../lib/radix.js"></script>
|
||||
<script src="../lib/radisk.js"></script>
|
||||
|
@ -1,6 +1,6 @@
|
||||
var Gun = (typeof window !== "undefined")? window.Gun : require('../../../gun/gun');
|
||||
|
||||
var Radisk = (Gun.window && window.Radisk) || require('../../../gun/lib/radiskip');
|
||||
var Radisk = (Gun.window && window.Radisk) || require('../../../gun/lib/radisk');
|
||||
Gun.TESTING = true;
|
||||
try{localStorage.clear()}catch(e){}
|
||||
try{indexedDB.deleteDatabase('radatatest');}catch(e){}
|
||||
|
@ -4,8 +4,7 @@
|
||||
|
||||
<script src="../../../gun/gun.js"></script>
|
||||
<script src="../../../gun/lib/radix.js"></script>
|
||||
<!-- script src="../../../gun/lib/radisk.js"></script -->
|
||||
<script src="../../../gun/lib/radiskip.js"></script>
|
||||
<script src="../../../gun/lib/radisk.js"></script>
|
||||
<script src="../../../gun/lib/store.js"></script>
|
||||
<script src="../../../gun/lib/rindexed.js"></script>
|
||||
<!-- script src="../../../gun/lib/rls.js"></script -->
|
||||
|
@ -43,7 +43,7 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam
|
||||
|
||||
//console.log("HYPER TEST");var z = 10000; while(--z){ names.push(Gun.text.random(7)) }this.timeout(9000);
|
||||
|
||||
describe.only('Radix', function(){
|
||||
describe('Radix', function(){
|
||||
var radix = Radix();
|
||||
|
||||
it('unit', function(){
|
||||
@ -264,6 +264,7 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam
|
||||
if(v.indexOf(find) == 0){ all[v] = true }
|
||||
});
|
||||
rad(find, function(err, data, info){
|
||||
expect(data).to.be.ok();
|
||||
Radix.map(data, function(v,k){
|
||||
delete all[find+k];
|
||||
});
|
||||
@ -312,12 +313,22 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam
|
||||
})
|
||||
});
|
||||
|
||||
it('read one', function(done){
|
||||
//gun.get('names').get({'.': {'*': find}, '%': 1000 * 100}).once().map().once(function(data, key){
|
||||
gun.get('names').get('stu').once(function(data, key){
|
||||
expect(data.name).to.be.ok();
|
||||
expect(data.age).to.be.ok();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('read contacts', function(done){
|
||||
var all = {}, find = 'm', to;
|
||||
names.forEach(function(v){
|
||||
v = v.toLowerCase();
|
||||
if(v.indexOf(find) == 0){ all[v] = true }
|
||||
});
|
||||
//console.log("<<<<<<<<<");
|
||||
gun.get('names').get({'.': {'*': find}, '%': 1000 * 100}).once().map().once(function(data, key){
|
||||
expect(data.name).to.be.ok();
|
||||
expect(data.age).to.be.ok();
|
||||
@ -328,6 +339,7 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam
|
||||
done();
|
||||
},100);
|
||||
});
|
||||
//console.log(">>>>>>>>>");
|
||||
});
|
||||
|
||||
it('read contacts again', function(done){
|
||||
|
@ -197,20 +197,20 @@ describe('SEA', function(){
|
||||
expect(dec.priv).to.be(okey.priv);
|
||||
expect(dec.epriv).to.be(okey.epriv);
|
||||
|
||||
var gun = Gun(), tmp = Gun.node.soul(old);
|
||||
var gun = Gun({super: true}), tmp = Gun.node.soul(old);
|
||||
var graph = {};
|
||||
graph[tmp] = old;
|
||||
var alias = await SEA.verify(old.alias, false);
|
||||
expect(alias).to.be('bob');
|
||||
alias = Gun.state.ify({}, tmp, 1, Gun.val.rel.ify(tmp), tmp = '~@'+alias);
|
||||
graph[tmp] = alias;
|
||||
gun.on('put', {$: gun, put: graph});
|
||||
gun.on('test', {$: gun, put: graph});
|
||||
var use = gun.user();
|
||||
use.auth('bob', 'test123', function(ack){
|
||||
expect(ack.err).to.not.be.ok();
|
||||
done();
|
||||
});
|
||||
}())})
|
||||
}())});
|
||||
|
||||
it('legacy []', function(done){ (async function(){
|
||||
var pw = 'test123';
|
||||
@ -218,14 +218,14 @@ describe('SEA', function(){
|
||||
var old = JSON.parse(atob("eyJfIjp7IiMiOiJ+VThkS0dySFJhX01sMFZ1YlR5OUZBYTlQS1ZlYlh0eTFjS05zWWxnYjduNC5QeVd5cUVVb0ZpYVduUElOV0Nad0xBbzFobjN1MldPWTU3SzZHZnpsNjhVIiwiPiI6eyJwdWIiOjE1NDY5MDI1MDQ5NzksImFsaWFzIjoxNTQ2OTAyNTA0OTc5LCJlcHViIjoxNTQ2OTAyNTA0OTc5LCJhdXRoIjoxNTQ2OTAyNTA0OTc5fX0sInB1YiI6IlU4ZEtHckhSYV9NbDBWdWJUeTlGQWE5UEtWZWJYdHkxY0tOc1lsZ2I3bjQuUHlXeXFFVW9GaWFXblBJTldDWndMQW8xaG4zdTJXT1k1N0s2R2Z6bDY4VSIsImFsaWFzIjoiU0VBe1wibVwiOltcIn5VOGRLR3JIUmFfTWwwVnViVHk5RkFhOVBLVmViWHR5MWNLTnNZbGdiN240LlB5V3lxRVVvRmlhV25QSU5XQ1p3TEFvMWhuM3UyV09ZNTdLNkdmemw2OFVcIixcImFsaWFzXCIsXCJhbGljZVwiLDE1NDY5MDI1MDQ5NzldLFwic1wiOlwienpuaGtIZjhZdFpZM2lGd3FVd0lJUldMTjhZMmlHbmNkcnVTaStGNDNmU1BLYWpSZlI0VzhXVHM4bElSMDBndGJmTWJxS0NjQkpGN3VNSkdGRC9WV2c9PVwifSIsImVwdWIiOiJTRUF7XCJtXCI6W1wiflU4ZEtHckhSYV9NbDBWdWJUeTlGQWE5UEtWZWJYdHkxY0tOc1lsZ2I3bjQuUHlXeXFFVW9GaWFXblBJTldDWndMQW8xaG4zdTJXT1k1N0s2R2Z6bDY4VVwiLFwiZXB1YlwiLFwiRkRzM1VvNTNFZEp6eFNocEpDaVctRGZPQ3lUS0M2U3cxeS1PZVJxam5ZRS5xVGdyYTlFQk1maEpNdVlMVmNaejRZYklLRm85enNBMHpMcV82dEVPMHI0XCIsMTU0NjkwMjUwNDk3OV0sXCJzXCI6XCJPZzRVVjY4OTluSjE4dC9ybWVnV0lkdnNqN01KaEpFc29ranZYQmdteVVRUXVNVjFTdnh4cXJqOFoyV1o2Q25XSkZnTlVDbEVYYWxuMURjUFE3M1R6UT09XCJ9IiwiYXV0aCI6IlNFQXtcIm1cIjpbXCJ+VThkS0dySFJhX01sMFZ1YlR5OUZBYTlQS1ZlYlh0eTFjS05zWWxnYjduNC5QeVd5cUVVb0ZpYVduUElOV0Nad0xBbzFobjN1MldPWTU3SzZHZnpsNjhVXCIsXCJhdXRoXCIsXCJ7XFxcImVrXFxcIjpcXFwiU0VBe1xcXFxcXFwiY3RcXFxcXFxcIjpcXFxcXFxcIi94ZnNPdVNkQUtrNkJiR00zbUV6MnVlSjI3Y0tJNThYMEtUL1FsaExSZXpWcjRkNzVZb2M5QlZNRjkzejl4QXI4N080S2FDNjJUWGVoeERQN0FFa2V4N1paaEpYL2hsVm9kK1FIcVFaaUZMK2lVQzFvL2hpUEJGWElBZmtINGRrcklGOFdqcEVaU3NIVmRSOVRhY2ZzbTB3aHN5NGJXN1ZLSEUySGc9PVxcXFxcXFwiLFxcXFxcXFwiaXZcXFxcXFxcIjpcXFxcXFxcIjhWekduTStEc1lTUktIU3Z4cSszTGc9PVxcXFxcXFwiLFxcXFxcXFwic1xcXFxcXFwiOlxcXFxcXFwibVVSSlJ4TzUvdXM9XFxcXFxcXCJ9XFxcIixcXFwic1xcXCI6XFxcImE1SlA3VFpuVE9jYjEwMGJOejlscEU4dnpqcUE3TWl0NHcwN3pjQTdIOFV0bml1WnVHSmdpZnNNQlFNSGdRdE5cXFwifVwiLDE1NDY5MDI1MDQ5NzldLFwic1wiOlwiSGFzMytJaHFEZTYyN016cElXZVE1cVFrZ2NOMlk3WHRpNGw0TFU3T2JyaktxSlBnSllrVWE2bk9YdlRmQkFzV1BPVzVnemh4Q2RPVGNFQm5icWlpWXc9PVwifSJ9"));
|
||||
var okey = {"pub":"U8dKGrHRa_Ml0VubTy9FAa9PKVebXty1cKNsYlgb7n4.PyWyqEUoFiaWnPINWCZwLAo1hn3u2WOY57K6Gfzl68U","epub":"FDs3Uo53EdJzxShpJCiW-DfOCyTKC6Sw1y-OeRqjnYE.qTgra9EBMfhJMuYLVcZz4YbIKFo9zsA0zLq_6tEO0r4","priv":"jMy7WfcldJ4esZEijAj4LTb99smtY_H0yKJLemJl2HI","epriv":"1DszMh-85pGTPLYtRunG-Q-xB78AE4k07PPkbedYYwk"}
|
||||
|
||||
var gun = Gun(), tmp = Gun.node.soul(old);
|
||||
var gun = Gun({super: true}), tmp = Gun.node.soul(old);
|
||||
var graph = {};
|
||||
graph[tmp] = old;
|
||||
var alias = SEA.opt.unpack(await SEA.verify(old.alias, false), 'alias', old);
|
||||
expect(alias).to.be('alice');
|
||||
alias = Gun.state.ify({}, tmp, 1, Gun.val.rel.ify(tmp), tmp = '~@'+alias);
|
||||
graph[tmp] = alias;
|
||||
gun.on('put', {$: gun, put: graph});
|
||||
gun.on('test', {$: gun, put: graph});
|
||||
var use = gun.user();
|
||||
use.auth('alice', 'test123', function(ack){
|
||||
expect(ack.err).to.not.be.ok();
|
||||
@ -277,8 +277,30 @@ describe('SEA', function(){
|
||||
});
|
||||
|
||||
describe('User', function(){
|
||||
var gun = Gun(), gtmp;
|
||||
|
||||
it('test', function(done){
|
||||
var g = Gun();
|
||||
user = g.user();
|
||||
var gid;
|
||||
SEA.pair(function(p){
|
||||
user.is = user._.sea = p;
|
||||
gtmp = gid = 'test~'+p.pub;
|
||||
g.get(gid).put({yo: 'hi'}, function(ack){
|
||||
var data = SEA.opt.parse(g._.graph[gid].yo);
|
||||
expect(data[':']).to.be('hi');
|
||||
expect(data['~']).to.be.ok();
|
||||
g.get(gid).get('yo').once(function(r){
|
||||
expect(r).to.be('hi');
|
||||
user.leave();
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
it('is instantiable', function(done){
|
||||
gun = Gun();
|
||||
user.leave();
|
||||
user = gun.user();
|
||||
done();
|
||||
})
|
||||
@ -289,7 +311,7 @@ describe('SEA', function(){
|
||||
expect(ack.err).to.not.be.ok();
|
||||
done();
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
it('login users', function(done){
|
||||
user.auth('carl', 'test123', function(ack){
|
||||
@ -347,6 +369,7 @@ describe('SEA', function(){
|
||||
var ref = user.get('who').get('all').set(msg);
|
||||
user.get('who').get('said').set(ref);
|
||||
user.get('who').get('said').map().once(function(data){
|
||||
//console.log("*****", data);
|
||||
expect(data.what).to.be.ok();
|
||||
done();
|
||||
})
|
||||
@ -356,6 +379,7 @@ describe('SEA', function(){
|
||||
it('set user ref null override', function(done){
|
||||
this.timeout(9000);
|
||||
var gun = Gun();
|
||||
//user.leave();
|
||||
var user = gun.user();
|
||||
var msg = {what: 'hello world'};
|
||||
user.create('xavier', 'password');
|
||||
|
Loading…
x
Reference in New Issue
Block a user