unit tests passing

This commit is contained in:
Mark Nadal 2020-02-10 15:13:37 -08:00
parent 3127b41f38
commit 0936c326f8
13 changed files with 443 additions and 1180 deletions

86
gun.js
View File

@ -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;

View File

@ -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 || _;

View File

@ -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;
}());

View File

@ -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 {

View File

@ -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);
});

View File

@ -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
View File

@ -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');

View File

@ -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;

View File

@ -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>

View File

@ -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){}

View File

@ -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 -->

View File

@ -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){

View File

@ -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');