Merge branch 'amark-master'

This commit is contained in:
Hadar Rottenberg 2020-03-30 15:59:55 +03:00
commit 22a76f489d
37 changed files with 3053 additions and 2470 deletions

3
.gitignore vendored
View File

@ -7,7 +7,10 @@ yarn.lock
.idea/ .idea/
*.bak *.bak
*.new *.new
*.log
v8.json
*.DS_store *.DS_store
isolate*.log
.esm-cache .esm-cache
.sessionStorage .sessionStorage
.localStorage .localStorage

View File

@ -1 +1 @@
web: node --optimize_for_size --gc_interval=100 examples/http.js web: node examples/http.js

52
axe.js
View File

@ -1,11 +1,6 @@
;(function(){ ;(function(){
/* UNBUILD */ /* UNBUILD */
var root;
if(typeof window !== "undefined"){ root = window }
if(typeof global !== "undefined"){ root = global }
root = root || {};
var console = root.console || {log: function(){}};
function USE(arg, req){ function USE(arg, req){
return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){ return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){
arg(mod = {exports: {}}); arg(mod = {exports: {}});
@ -15,7 +10,7 @@
return p.split('/').slice(-1).toString().replace('.js',''); return p.split('/').slice(-1).toString().replace('.js','');
} }
} }
if(typeof module !== "undefined"){ var common = module } if(typeof module !== "undefined"){ var MODULE = module }
/* UNBUILD */ /* UNBUILD */
;USE(function(module){ ;USE(function(module){
@ -24,7 +19,7 @@
var AXE = tmp.AXE || function(){}; var AXE = tmp.AXE || function(){};
if(AXE.window = module.window){ AXE.window.AXE = AXE } if(AXE.window = module.window){ AXE.window.AXE = AXE }
try{ if(typeof common !== "undefined"){ common.exports = AXE } }catch(e){} try{ if(typeof MODULE !== "undefined"){ MODULE.exports = AXE } }catch(e){}
module.exports = AXE; module.exports = AXE;
})(USE, './root'); })(USE, './root');
@ -32,6 +27,7 @@
var AXE = USE('./root'), Gun = (AXE.window||{}).Gun || USE('./gun', 1); var AXE = USE('./root'), Gun = (AXE.window||{}).Gun || USE('./gun', 1);
(Gun.AXE = AXE).GUN = AXE.Gun = Gun; (Gun.AXE = AXE).GUN = AXE.Gun = Gun;
var ST = 0;
Gun.on('opt', function(at){ Gun.on('opt', function(at){
start(at); start(at);
@ -73,11 +69,12 @@
The mob threshold might be determined by other factors, The mob threshold might be determined by other factors,
like how much RAM or CPU stress we have. like how much RAM or CPU stress we have.
*/ */
opt.mob = opt.mob || Infinity; opt.mob = opt.mob || 9876 || Infinity;
var mesh = opt.mesh = opt.mesh || Gun.Mesh(at); var mesh = opt.mesh = opt.mesh || Gun.Mesh(at);
console.log("AXE enabled."); console.log("AXE enabled.");
function verify(dht, msg) { function verify(dht, msg) {
var S = (+new Date);
var puts = Object.keys(msg.put); var puts = Object.keys(msg.put);
var soul = puts[0]; /// TODO: verify all souls in puts. Copy the msg only with subscribed souls? var soul = puts[0]; /// TODO: verify all souls in puts. Copy the msg only with subscribed souls?
var subs = dht(soul); var subs = dht(soul);
@ -93,17 +90,20 @@
if (opt.super) { if (opt.super) {
dht(soul, tmp.join(',')); dht(soul, tmp.join(','));
} }
console.STAT && console.STAT(S, +new Date - S, 'axe verify');
} }
function route(get){ var tmp; function route(get){ var tmp;
if(!get){ return } if(!get){ return }
if('string' != typeof (tmp = get['#'])){ return } if('string' != typeof (tmp = get['#'])){ return }
return tmp; return tmp;
} }
// TODO: AXE NEEDS TO BE CHECKED FOR NEW CODE SYSTEM!!!!!!!!!!
var Rad = (Gun.window||{}).Radix || USE('./lib/radix', 1); var Rad = (Gun.window||{}).Radix || USE('./lib/radix', 1);
at.opt.dht = Rad(); at.opt.dht = Rad();
at.on('in', function input(msg){ at.on('in', input);
var to = this.to, peer = (msg._||{}).via; function input(msg){
var to = this.to, peer = (msg._||'').via; // warning! mesh.leap could be buggy!
var dht = opt.dht; var dht = opt.dht;
var routes = axe.routes || (axe.routes = {}); // USE RAD INSTEAD! TMP TESTING! var routes = axe.routes || (axe.routes = {}); // USE RAD INSTEAD! TMP TESTING!
var get = msg.get, hash, tmp; var get = msg.get, hash, tmp;
@ -128,8 +128,8 @@
} }
}*/ }*/
} }
if((tmp = msg['@']) && (tmp = at.dup.s[tmp]) && (tmp = tmp.it)){ if((tmp = msg['@']) && (tmp = at.dup.s[tmp])){
(tmp = (tmp._||{})).ack = (tmp.ack || 0) + 1; // count remote ACKs to GET. tmp.ack = (tmp.ack || 0) + 1; // count remote ACKs to GET.
} }
to.next(msg); to.next(msg);
@ -144,7 +144,7 @@
}); });
}); });
} }
}); };
//try{console.log(req.connection.remoteAddress)}catch(e){}; //try{console.log(req.connection.remoteAddress)}catch(e){};
mesh.hear['opt'] = function(msg, peer){ mesh.hear['opt'] = function(msg, peer){
@ -189,6 +189,7 @@
var peers = routes[hash]; var peers = routes[hash];
function chat(peers, old){ // what about optimizing for directed peers? function chat(peers, old){ // what about optimizing for directed peers?
if(!peers){ return chat(opt.peers) } if(!peers){ return chat(opt.peers) }
var S = (+new Date); // STATS!
var ids = Object.keys(peers); // TODO: BUG! THIS IS BAD PERFORMANCE!!!! var ids = Object.keys(peers); // TODO: BUG! THIS IS BAD PERFORMANCE!!!!
var meta = (msg._||yes); var meta = (msg._||yes);
clearTimeout(meta.lack); clearTimeout(meta.lack);
@ -198,12 +199,13 @@
meta.turn = (meta.turn || 0) + 1; meta.turn = (meta.turn || 0) + 1;
if((old && old[id]) || false === mesh.say(msg, peer)){ ++c } if((old && old[id]) || false === mesh.say(msg, peer)){ ++c }
} }
console.STAT && (ST = +new Date - S) > 9 && console.STAT(S, ST, 'axe chat');
//console.log("AXE:", Gun.obj.copy(msg), meta.turn, c, ids, opt.peers === peers); //console.log("AXE:", Gun.obj.copy(msg), meta.turn, c, ids, opt.peers === peers);
if(0 < c){ if(0 < c){
if(peers === opt.peers){ return } // prevent infinite lack loop. if(peers === opt.peers){ return } // prevent infinite lack loop.
return meta.turn = 0, chat(opt.peers, peers) return meta.turn = 0, chat(opt.peers, peers)
} }
var hash = msg['##'], ack = meta.ack; var hash = msg['##'], ack = meta.ack || at.dup.s[msg['#']];
meta.lack = setTimeout(function(){ meta.lack = setTimeout(function(){
if(ack && hash && hash === msg['##']){ return } if(ack && hash && hash === msg['##']){ return }
if(meta.turn >= (axe.turns || 3)){ return } // variable for later! Also consider ACK based turn limit. if(meta.turn >= (axe.turns || 3)){ return } // variable for later! Also consider ACK based turn limit.
@ -215,6 +217,7 @@
} }
// TODO: PUTs need to only go to subs! // TODO: PUTs need to only go to subs!
if(msg.put){ if(msg.put){
var S = (+new Date); // STATS!
var routes = axe.routes || (axe.routes = {}); // USE RAD INSTEAD! TMP TESTING! var routes = axe.routes || (axe.routes = {}); // USE RAD INSTEAD! TMP TESTING!
var peers = {}; var peers = {};
Gun.obj.map(msg.put, function(node, soul){ Gun.obj.map(msg.put, function(node, soul){
@ -223,6 +226,7 @@
if(!to){ return } if(!to){ return }
Gun.obj.to(to, peers); Gun.obj.to(to, peers);
}); });
console.STAT && (ST = +new Date - S) > 9 && console.STAT(S, ST, 'axe put');
mesh.say(msg, peers); mesh.say(msg, peers);
return; return;
} }
@ -246,24 +250,6 @@
} }
}); });
}; };
/*var connections = 0; // THIS HAS BEEN MOVED TO CORE NOW!
at.on('hi', function(opt) {
this.to.next(opt);
//console.log('AXE PEER [HI]', new Date(), opt);
connections++;
/// The first connection don't need to resubscribe the nodes.
if (connections === 1) { return; }
/// Resubscribe all nodes.
setTimeout(function() {
var souls = Object.keys(at.graph);
for (var i=0; i < souls.length; ++i) {
//at.gun.get(souls[i]).off();
at.next[souls[i]].ack = 0;
at.gun.get(souls[i]).once(function(){});
}
//location.reload();
}, 500);
}, at);*/
} }
axe.up = {}; axe.up = {};
at.on('hi', function(peer){ at.on('hi', function(peer){
@ -273,12 +259,14 @@
}); });
at.on('bye', function(peer){ this.to.next(peer); at.on('bye', function(peer){ this.to.next(peer);
if(peer.url){ delete axe.up[peer.id] } if(peer.url){ delete axe.up[peer.id] }
var S = +new Date;
Gun.obj.map(peer.routes, function(route, hash){ Gun.obj.map(peer.routes, function(route, hash){
delete route[peer.id]; delete route[peer.id];
if(Gun.obj.empty(route)){ if(Gun.obj.empty(route)){
delete axe.routes[hash]; delete axe.routes[hash];
} }
}); });
console.STAT && console.STAT(S, +new Date - S, 'axe bye');
}); });
// handle rebalancing a mob of peers: // handle rebalancing a mob of peers:

View File

@ -22,6 +22,7 @@
<script src="../../../gun/sea.js"></script> <script src="../../../gun/sea.js"></script>
<script> <script>
//var gun = Gun('http://localhost:8765/gun');
var gun = Gun(); //Gun(['http://localhost:8765/gun', 'https://guntest.herokuapp.com/gun']); var gun = Gun(); //Gun(['http://localhost:8765/gun', 'https://guntest.herokuapp.com/gun']);
var user = gun.user().recall({sessionStorage: true}); var user = gun.user().recall({sessionStorage: true});

View File

@ -1,7 +1,7 @@
;(function(){ ;(function(){
var cluster = require('cluster'); var cluster = require('cluster');
if(cluster.isMaster){ if(cluster.isMaster){
return cluster.fork() && cluster.on('exit', function(){ cluster.fork() }); return cluster.fork() && cluster.on('exit', function(){ cluster.fork(); require('../lib/crashed'); });
} }
var fs = require('fs'); var fs = require('fs');

View File

@ -29,7 +29,7 @@
.tall { height: 5em; } .tall { height: 5em; }
</style> </style>
<div class="center"><span class="shout" id="peers">0</span> peers <span class="shout" id="time">0</span> min <span class="shout" id="nodes">0</span> nodes <span class="shout" id="hours">0</span> hours</div> <div class="center"><span class="shout" id="peers">0</span> peers <span class="shout" id="time">0</span> min <span class="shout" id="nodes">0</span> nodes <span class="shout" id="hours">0</span> hours <span class="shout" id="block">0</span> block</div>
<input id="url" class="center input crack" placeholder="enter peer stats source url"> <input id="url" class="center input crack" placeholder="enter peer stats source url">
@ -44,19 +44,24 @@
<script src="./jquery.js"></script> <script src="./jquery.js"></script>
<script src="./smoothie.js" charset="utf-8"></script> <script src="./smoothie.js" charset="utf-8"></script>
<script> <script>
var up, br = 0, bt = 0;
var fetchData = async function(){ var fetchData = async function(){
// fetch the data from server // fetch the data from server
var S = +new Date;
var data = await (await fetch(url.value||(location.origin+'/gun/stats.radata'), {method: 'GET',mode: 'cors'})).json(); var data = await (await fetch(url.value||(location.origin+'/gun/stats.radata'), {method: 'GET',mode: 'cors'})).json();
$('#block').text(((br += (+new Date - S)/1000) / ++bt).toFixed(1));
data.over = (data.over/1000) || 15;
$('#peers').text(data.peers.count); $('#peers').text(data.peers.count);
$('#time').text((data.peers.time / 1000 / 60).toFixed(0)); $('#time').text((data.peers.time / 1000 / 60).toFixed(0));
$('#nodes').text(data.node.count); $('#nodes').text(data.node.count);
$('#hours').text((data.up.time / 60 / 60).toFixed(0)); $('#hours').text((data.up.time / 60 / 60).toFixed(1));
if(data.up.time === up){ console.log("up same as before") } up = data.up.time;
Stats('memory').line.append(+new Date, data.memory.heapTotal / 1024 / 1024); Stats('memory').line.append(+new Date, data.memory.heapTotal / 1024 / 1024);
try{ Stats('dam # in').line.append(+new Date, data.dam.in.count); }catch(e){} try{ Stats('dam # in/s').line.append(+new Date, Math.round(data.dam.in.count / data.over)); }catch(e){}
try{ Stats('dam in MB').line.append(+new Date, data.dam.in.done / 1024 / 1024); }catch(e){} try{ Stats('dam in MB/s').line.append(+new Date, data.dam.in.done / 1024 / 1024 / data.over); }catch(e){}
try{ Stats('dam # out').line.append(+new Date, data.dam.out.count); }catch(e){} try{ Stats('dam # out/s').line.append(+new Date, Math.round(data.dam.out.count / data.over)); }catch(e){}
try{ Stats('dam out MB').line.append(+new Date, data.dam.out.done / 1024 / 1024); }catch(e){} try{ Stats('dam out MB/s').line.append(+new Date, data.dam.out.done / 1024 / 1024 / data.over); }catch(e){}
console.log('data',data); console.log('data',data);
//fetch keys in all, these may be dynamically changing //fetch keys in all, these may be dynamically changing
@ -83,16 +88,27 @@
} }
// create a new Series for this key // create a new Series for this key
// add it into the map // add it into the map
chart = Stats[key] = new SmoothieChart({responsive: true, minValue: 0, grid:{strokeStyle:'rgba(100%,100%,100%,0.2)'},labels:{fontSize:20}}); chart = Stats[key] = new SmoothieChart({millisPerPixel:500, limitFPS: 16, responsive: true, minValue: 0, grid:{strokeStyle:'rgba(100%,100%,100%,0.2)'},labels:{fontSize:20}, grid: {verticalSections: 0, millisPerLine: 15000 * 4 /*, strokeStyle:'rgb(125, 0, 0)'*/}});
chart.line = new TimeSeries(); chart.line = new TimeSeries();
chart.addTimeSeries(chart.line,{ strokeStyle:'rgb('+Math.random()*255+', '+Math.random()*255+','+Math.random()*255+')', lineWidth:5 }); chart.addTimeSeries(chart.line,{ strokeStyle:'rgb('+Math.random()*255+', '+Math.random()*255+','+Math.random()*255+')', lineWidth:5 });
chart.canvas = $('.model').find('.chart').clone(true).appendTo('.charts'); chart.canvas = $('.model').find('.chart').clone(true).appendTo('.charts');
chart.canvas.find('span').text(key); chart.canvas.find('span').text(key);
chart.streamTo(chart.canvas.find('canvas').get(0), 15 * 1000); chart.streamTo(chart.canvas.find('canvas').get(0), 15 * 1000);
chart.line.append(0, 0);
// check first two characters of key to determine other charts to add this in // check first two characters of key to determine other charts to add this in
// tbd later // tbd later
return chart; return chart;
} }
/*
Notes to Self about Debugging:
1. Read Disks can spike up to 1min, I suspect other operations are blocking it from resolving as fast as it otherwise would.
2. JSON parsing/stringifying sometimes way slower than other times, why?
3. Looks like RAD lex read is not optimized.
4. got prep + got emit = non-RAD problems, compare against read disk & got differentials (should be same).
5. Radix map/place ops could be slow?
6. SINGLE MESSAGE PROCESS TIME occasionally is huge, should get emailed.
7. Watch out for get/put loops times, maybe indicating (5) issues?
*/
</script> </script>
</body> </body>
</html> </html>

View File

@ -113,13 +113,12 @@
<script src="/jquery.js"></script> <script src="/jquery.js"></script>
<script src="/gun.js"></script> <script src="/gun.js"></script>
<script src="/gun/nts.js"></script>
<script> <script>
// Check out the interactive tutorial // Check out the interactive tutorial
// for how to build a simplified version // for how to build a simplified version
// of this example: https://scrimba.com/c/cW2Vsa // of this example: https://scrimba.com/c/cW2Vsa
var gun = Gun(location.origin + '/gun'); var gun = Gun(location.origin + '/gun');
var think = gun.get('think/' + location.hash.slice(1)); var think = gun.get('think1/' + location.hash.slice(1));
var thoughtItemStr = function(id) { return '<li class="thought__item"><label class="visually-hidden" for="' + id + '">Thought</label><input id="' + id + '" class="thought__input huet"><li/>'} var thoughtItemStr = function(id) { return '<li class="thought__item"><label class="visually-hidden" for="' + id + '">Thought</label><input id="' + id + '" class="thought__input huet"><li/>'}
var typing, throttle; var typing, throttle;
$('.thought__add').on('click', function () { $('.thought__add').on('click', function () {

494
gun.js
View File

@ -1,11 +1,6 @@
;(function(){ ;(function(){
/* UNBUILD */ /* UNBUILD */
var root;
if(typeof window !== "undefined"){ root = window }
if(typeof global !== "undefined"){ root = global }
root = root || {};
var console = root.console || {log: function(){}};
function USE(arg, req){ function USE(arg, req){
return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){ return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){
arg(mod = {exports: {}}); arg(mod = {exports: {}});
@ -15,7 +10,7 @@
return p.split('/').slice(-1).toString().replace('.js',''); return p.split('/').slice(-1).toString().replace('.js','');
} }
} }
if(typeof module !== "undefined"){ var common = module } if(typeof module !== "undefined"){ var MODULE = module }
/* UNBUILD */ /* UNBUILD */
;USE(function(module){ ;USE(function(module){
@ -97,9 +92,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! 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(){
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(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){ Type.obj.empty = function(o, n){
if(!o){ return true } if(!o){ return true }
@ -115,20 +110,21 @@
} t.r = t.r || []; } t.r = t.r || [];
t.r.push(k); 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)}) } Object.keys = Object.keys || function(o){ return map(o, function(v,k,t){t(k)}) }
Type.obj.map = map = function(l, c, _){ Type.obj.map = map = function(l, c, _){
var u, i = 0, x, r, ll, lle, f = fn_is(c); var u, i = 0, x, r, ll, lle, f = 'function' == typeof c;
t.r = null; t.r = u;
if(keys && obj_is(l)){ if(keys && obj_is(l)){
ll = keys(l); lle = true; ll = keys(l); lle = true;
} }
_ = _ || {};
if(list_is(l) || ll){ if(list_is(l) || ll){
x = (ll || l).length; x = (ll || l).length;
for(;i < x; i++){ for(;i < x; i++){
var ii = (i + Type.list.index); var ii = (i + Type.list.index);
if(f){ 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 } if(r !== u){ return r }
} else { } else {
//if(Type.test.is(c,l[i])){ return ii } // should implement deep equality testing! //if(Type.test.is(c,l[i])){ return ii } // should implement deep equality testing!
@ -171,7 +167,7 @@
tmp.next(arg); tmp.next(arg);
}} }}
}}); }});
if(arg instanceof Function){ if('function' == typeof arg){
var be = { var be = {
off: onto.off || off: onto.off ||
(onto.off = function(){ (onto.off = function(){
@ -200,6 +196,20 @@
}; };
})(USE, './onto'); })(USE, './onto');
;USE(function(module){
var to = (typeof setImmediate !== "undefined")? setImmediate : setTimeout, puff = function(cb){
if(Q.length){ Q.push(cb); return } Q = [cb];
to(function go(S){ S = S || +new Date;
var i = 0, cb; while(i < 9 && (cb = Q[i++])){ cb() }
console.STAT && console.STAT(S, +new Date - S, 'puff');
if(cb && !(+new Date - S)){ return go(S) }
if(!(Q = Q.slice(i)).length){ return }
to(go, 0);
}, 0);
}, Q = [];
module.exports = setTimeout.puff = puff;
})(USE, './puff');
;USE(function(module){ ;USE(function(module){
/* Based on the Hypothetical Amnesia Machine thought experiment */ /* Based on the Hypothetical Amnesia Machine thought experiment */
function HAM(machineState, incomingState, currentState, incomingValue, currentValue){ function HAM(machineState, incomingState, currentState, incomingValue, currentValue){
@ -323,7 +333,7 @@
Node.ify = function(obj, o, as){ // returns a node from a shallow object. Node.ify = function(obj, o, as){ // returns a node from a shallow object.
if(!o){ o = {} } if(!o){ o = {} }
else if(typeof o === 'string'){ o = {soul: 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.map){ o.node = o.map.call(as, obj, u, o.node || {}) }
if(o.node = Node.soul.ify(o.node || {}, o)){ if(o.node = Node.soul.ify(o.node || {}, o)){
obj_map(obj, map, {o:o,as:as}); obj_map(obj, map, {o:o,as:as});
@ -359,7 +369,7 @@
/*if(perf){ /*if(perf){
t = start + perf.now(); // Danger: Accuracy decays significantly over time, even if precise. t = start + perf.now(); // Danger: Accuracy decays significantly over time, even if precise.
} else {*/ } else {*/
t = time(); t = +new Date;
//} //}
if(last < t){ if(last < t){
return N = 0, last = t + State.drift; return N = 0, last = t + State.drift;
@ -368,10 +378,10 @@
} }
var time = Type.time.is, last = -Infinity, N = 0, D = 1000; // WARNING! In the future, on machines that are D times faster than 2016AD machines, you will want to increase D by another several orders of magnitude so the processing speed never out paces the decimal resolution (increasing an integer effects the state accuracy). var time = Type.time.is, last = -Infinity, N = 0, D = 1000; // WARNING! In the future, on machines that are D times faster than 2016AD machines, you will want to increase D by another several orders of magnitude so the processing speed never out paces the decimal resolution (increasing an integer effects the state accuracy).
var perf = (typeof performance !== 'undefined')? (performance.timing && performance) : false, start = (perf && perf.timing && perf.timing.navigationStart) || (perf = false); var perf = (typeof performance !== 'undefined')? (performance.timing && performance) : false, start = (perf && perf.timing && perf.timing.navigationStart) || (perf = false);
State._ = '>'; var S_ = State._ = '>';
State.drift = 0; State.drift = 0;
State.is = function(n, k, o){ // convenience function to get the state on a key on a node and return it. State.is = function(n, k, o){ // convenience function to get the state on a key on a node and return it.
var tmp = (k && n && n[N_] && n[N_][State._]) || o; var tmp = (k && n && n[N_] && n[N_][S_]) || o;
if(!tmp){ return } if(!tmp){ return }
return num_is(tmp = tmp[k])? tmp : -Infinity; return num_is(tmp = tmp[k])? tmp : -Infinity;
} }
@ -383,7 +393,7 @@
} }
n = Node.soul.ify(n, soul); // then make it so! n = Node.soul.ify(n, soul); // then make it so!
} }
var tmp = obj_as(n[N_], State._); // grab the states data. var tmp = obj_as(n[N_], S_); // grab the states data.
if(u !== k && k !== N_){ if(u !== k && k !== N_){
if(num_is(s)){ if(num_is(s)){
tmp[k] = s; // add the valid state. tmp[k] = s; // add the valid state.
@ -464,9 +474,13 @@
if(typeof env === 'string'){ if(typeof env === 'string'){
env = {soul: env}; env = {soul: env};
} else } else
if(env instanceof Function){ if('function' == typeof env){
env.map = env; env.map = env;
} }
if(typeof as === 'string'){
env.soul = env.soul || as;
as = u;
}
if(env.soul){ if(env.soul){
at.link = Val.link.ify(env.soul); at.link = Val.link.ify(env.soul);
} }
@ -596,9 +610,9 @@
USE('./onto'); // depends upon onto! USE('./onto'); // depends upon onto!
module.exports = function ask(cb, as){ module.exports = function ask(cb, as){
if(!this.on){ return } if(!this.on){ return }
if(!(cb instanceof Function)){ if(!('function' == typeof cb)){
if(!cb || !as){ return } if(!cb || !as){ return }
var id = cb['#'] || cb, tmp = (this.tag||empty)[id]; var id = cb['#'] || cb, tmp = (this.tag||'')[id];
if(!tmp){ return } if(!tmp){ return }
tmp = this.on(id, as); tmp = this.on(id, as);
clearTimeout(tmp.err); clearTimeout(tmp.err);
@ -608,7 +622,7 @@
if(!cb){ return id } if(!cb){ return id }
var to = this.on(id, cb, as); var to = this.on(id, cb, as);
to.err = to.err || setTimeout(function(){ to.err = to.err || setTimeout(function(){
to.next({err: "Error: No ACK received yet.", lack: true}); to.next({err: "Error: No ACK yet.", lack: true});
to.off(); to.off();
}, (this.opt||{}).lack || 9000); }, (this.opt||{}).lack || 9000);
return id; return id;
@ -618,45 +632,43 @@
;USE(function(module){ ;USE(function(module){
var Type = USE('./type'); var Type = USE('./type');
function Dup(opt){ function Dup(opt){
var dup = {s:{}}; var dup = {s:{}}, s = dup.s;
opt = opt || {max: 1000, age: /*1000 * 9};//*/ 1000 * 9 * 3}; opt = opt || {max: 1000, age: /*1000 * 9};//*/ 1000 * 9 * 3};
dup.check = function(id){ var tmp; dup.check = function(id){
if(!(tmp = dup.s[id])){ return false } if(!s[id]){ return false }
if(tmp.pass){ return tmp.pass = false } return dt(id);
return dup.track(id);
} }
dup.track = function(id, pass){ var dt = dup.track = function(id){
var it = dup.s[id] || (dup.s[id] = {}); var it = s[id] || (s[id] = {});
it.was = time_is(); it.was = +new Date;
if(pass){ it.pass = true }
if(!dup.to){ dup.to = setTimeout(dup.drop, opt.age + 9) } if(!dup.to){ dup.to = setTimeout(dup.drop, opt.age + 9) }
return it; return it;
} }
dup.drop = function(age){ dup.drop = function(age){
var now = time_is(); var now = +new Date;
Type.obj.map(dup.s, function(it, id){ Type.obj.map(s, function(it, id){
if(it && (age || opt.age) > (now - it.was)){ return } if(it && (age || opt.age) > (now - it.was)){ return }
Type.obj.del(dup.s, id); delete s[id];
}); });
dup.to = null; dup.to = null;
console.STAT && (age = +new Date - now) > 9 && console.STAT(now, age, 'dup drop');
} }
return dup; return dup;
} }
var time_is = Type.time.is;
module.exports = Dup; module.exports = Dup;
})(USE, './dup'); })(USE, './dup');
;USE(function(module){ ;USE(function(module){
function Gun(o){ function Gun(o){
if(o instanceof Gun){ return (this._ = {gun: this, $: this}).$ } if(o instanceof Gun){ return (this._ = {$: this}).$ }
if(!(this instanceof Gun)){ return new Gun(o) } if(!(this instanceof Gun)){ return new Gun(o) }
return Gun.create(this._ = {gun: this, $: this, opt: o}); return Gun.create(this._ = {$: this, opt: o});
} }
Gun.is = function($){ return ($ instanceof Gun) || ($ && $._ && ($ === $._.$)) || false } Gun.is = function($){ return ($ instanceof Gun) || ($ && $._ && ($ === $._.$)) || false }
Gun.version = 0.9; Gun.version = 0.2020;
Gun.chain = Gun.prototype; Gun.chain = Gun.prototype;
Gun.chain.toJSON = function(){}; Gun.chain.toJSON = function(){};
@ -671,6 +683,7 @@
Gun.on = USE('./onto'); Gun.on = USE('./onto');
Gun.ask = USE('./ask'); Gun.ask = USE('./ask');
Gun.dup = USE('./dup'); Gun.dup = USE('./dup');
Gun.puff = USE('./puff');
;(function(){ ;(function(){
Gun.create = function(at){ Gun.create = function(at){
@ -681,45 +694,131 @@
at.dup = at.dup || Gun.dup(); at.dup = at.dup || Gun.dup();
var gun = at.$.opt(at.opt); var gun = at.$.opt(at.opt);
if(!at.once){ if(!at.once){
at.on('in', root, at); at.on('in', universe, at);
at.on('out', root, {at: at, out: root}); at.on('out', universe, at);
at.on('put', map, at);
Gun.on('create', at); Gun.on('create', at);
at.on('create', at); at.on('create', at);
} }
at.once = 1; at.once = 1;
return gun; return gun;
} }
function root(msg){ function universe(msg){
//add to.next(at); // TODO: MISSING FEATURE!!! if(!msg){ return }
var ev = this, as = ev.as, at = as.at || as, gun = at.$, dup, tmp; if(msg.out === universe){ this.to.next(msg); return }
if(!(tmp = msg['#'])){ tmp = msg['#'] = text_rand(9) } var eve = this, as = eve.as, at = as.at || as, gun = at.$, dup = at.dup, tmp, DBG = msg.DBG;
if((dup = at.dup).check(tmp)){ (tmp = msg['#']) || (tmp = msg['#'] = text_rand(9));
if(as.out === msg.out){ if(dup.check(tmp)){ return } dup.track(tmp);
msg.out = u; tmp = msg._; msg._ = ('function' == typeof tmp)? tmp : function(){};
ev.to.next(msg); (msg.$ && (msg.$ === (msg.$._||'').$)) || (msg.$ = gun);
if(!at.ask(msg['@'], msg)){ // is this machine listening for an ack?
DBG && (DBG.u = +new Date);
if(msg.get){ Gun.on._get(msg, gun) }
if(msg.put){ put(msg); return }
}
DBG && (DBG.uc = +new Date);
eve.to.next(msg);
DBG && (DBG.ua = +new Date);
msg.out = universe; at.on('out', msg);
DBG && (DBG.ue = +new Date);
}
function put(msg){
if(!msg){ return }
var ctx = msg._||'', root = ctx.root = ((msg.$||'')._||'').root;
var put = msg.put, id = msg['#'], err, tmp;
var DBG = ctx.DBG = msg.DBG;
if(put['#']){ root.on('put', msg); return }
/*root.on(id, function(m){
console.log('ack:', m);
});*/
ctx.out = msg;
ctx.lot = {s: 0, more: 1};
var S = +new Date;
DBG && (DBG.p = S);
for(var soul in put){ // Gun.obj.native() makes this safe.
var node = put[soul], states;
if(!node){ err = ERR+cut(soul)+"no node."; break }
if(!(tmp = node._)){ err = ERR+cut(soul)+"no meta."; break }
if(soul !== tmp[_soul]){ err = ERR+cut(soul)+"soul not same."; break }
if(!(states = tmp[state_])){ err = ERR+cut(soul)+"no state."; break }
for(var key in node){ // double loop uncool, but have to support old format.
if(node_ === key){ continue }
var val = node[key], state = states[key];
if(u === state){ err = ERR+cut(key)+"on"+cut(soul)+"no state."; break }
if(!val_is(val)){ err = ERR+cut(key)+"on"+cut(soul)+"bad "+(typeof val)+cut(val); break }
ham(val, key, soul, state, msg);
}
if(err){ break }
}
DBG && (DBG.pe = +new Date);
if(console.STAT){ console.STAT(S, +new Date - S, 'mix');console.STAT(S, ctx.lot.s, 'mix #') }
if(ctx.err = err){ root.on('in', {'@': id, err: Gun.log(err)}); return }
if(!(--ctx.lot.more)){ fire(ctx) } // if synchronous.
if(!ctx.stun && !msg['@']){ root.on('in', {'@': id, ok: -1}) } // in case no diff sent to storage, etc., still ack.
} Gun.on.put = put;
function ham(val, key, soul, state, msg){
var ctx = msg._||'', root = ctx.root, graph = root.graph, lot;
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;
if(!is.incoming){
if(is.defer){
var to = state - machine;
setTimeout(function(){
ham(val, key, soul, state, msg);
}, to > MD? MD : to); // setTimeout Max Defer 32bit :(
if(!ctx.to){ root.on('in', {'@': msg['#'], err: to}) } ctx.to = 1;
return to;
} }
return; return;
} }
dup.track(tmp); (lot = ctx.lot||'').s++; lot.more++;
if(tmp = msg['@']){ dup.track(tmp) } // HNPERF: Bump original request's liveliness. (ctx.stun || (ctx.stun = {}))[soul+key] = 1;
if(!at.ask(tmp, msg)){ var DBG = ctx.DBG; DBG && (DBG.ph = DBG.ph || +new Date);
if(msg.get){ root.on('put', {'#': msg['#'], '@': msg['@'], put: {'#': soul, '.': key, ':': val, '>': state}, _: ctx});
Gun.on.get(msg, gun); //at.on('get', get(msg));
} }
if(msg.put){ function map(msg){
Gun.on.put(msg, gun); //at.on('put', put(msg)); var DBG; if(DBG = (msg._||'').DBG){ DBG.pa = +new Date; DBG.pm = DBG.pm || +new Date}
var eve = this, root = eve.as, graph = root.graph, ctx = msg._, put = msg.put, soul = put['#'], key = put['.'], val = put[':'], state = put['>'], id = msg['#'], tmp;
graph[soul] = state_ify(graph[soul], key, state, val, soul); // TODO: Only put in graph if subscribed? Relays vs Browsers?
chain(ctx, 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.
if((tmp = ctx.out) && (tmp = tmp.put)){
tmp[soul] = state_ify(tmp[soul], key, state, val, soul); // TODO: Hacky, fix & come back later, for actual pushing messages.
} }
if(!(--ctx.lot.more)){ fire(ctx) }
eve.to.next(msg);
} }
ev.to.next(msg); function chain(ctx, soul, key,val, state){
if(!as.out){ var root = ctx.root, put, tmp;
msg.out = root; (root.opt||'').super && root.$.get(soul); // I think we need super for now, but since we are rewriting, should consider getting rid of it.
at.on('out', msg); if(!root || !(tmp = root.next) || !(tmp = tmp[soul]) || !tmp.$){ return }
(put = ctx.put || (ctx.put = {}))[soul] = state_ify(put[soul], key, state, val, soul);
tmp.put = state_ify(tmp.put, key, state, val, soul);
} }
function fire(ctx){
if(ctx.err){ return }
var stop = {};
var root = ctx.root, next = root.next||'', put = ctx.put, tmp;
var S = +new Date;
//Gun.graph.is(put, function(node, soul){
for(var soul in put){ var node = put[soul]; // Gun.obj.native() makes this safe.
if(!(tmp = next[soul]) || !tmp.$){ continue }
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?
} }
console.STAT && console.STAT(S, +new Date - S, 'fire');
ctx.DBG && (ctx.DBG.f = +new Date);
if(!(tmp = ctx.out)){ return }
tmp.out = universe;
root.on('out', tmp);
}
var ERR = "Error: Invalid graph!";
var cut = function(s){ return " '"+(''+s).slice(0,9)+"...' " }
var HAM = Gun.HAM, MD = 2147483647, State = Gun.state;
}()); }());
;(function(){ ;(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: {}}; 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.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!" } if(!Gun.graph.is(msg.put, null, verify, ctx)){ ctx.err = "Error: Invalid graph!" }
@ -729,13 +828,12 @@
if(u !== ctx.defer){ if(u !== ctx.defer){
var to = ctx.defer - ctx.machine; var to = ctx.defer - ctx.machine;
setTimeout(function(){ setTimeout(function(){
Gun.on.put(msg, gun); Gun.on._put(msg, gun);
}, to > MD? MD : to ); // setTimeout Max Defer 32bit :( }, to > MD? MD : to ); // setTimeout Max Defer 32bit :(
} }
if(!ctx.diff){ return } if(!ctx.diff){ return }
at.on('put', obj_to(msg, {put: ctx.diff})); at.on('put', obj_to(msg, {put: ctx.diff}));
}; };
var MD = 2147483647;
function verify(val, key, node, soul){ var ctx = this; function verify(val, key, node, soul){ var ctx = this;
var state = Gun.state.is(node, key), tmp; var state = Gun.state.is(node, key), tmp;
if(!state){ return ctx.err = "Error: No state on '"+key+"' in node '"+soul+"'!" } if(!state){ return ctx.err = "Error: No state on '"+key+"' in node '"+soul+"'!" }
@ -803,10 +901,12 @@
} }
function perf(node, soul){ if(node !== this.graph[soul]){ return true } } // HNPERF: do not port! 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 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]; var next = root.next || (root.next = {}), at = next[soul];
// queue concurrent GETs? // queue concurrent GETs?
var ctx = msg._||'', DBG = ctx.DBG = msg.DBG;
DBG && (DBG.g = +new Date);
if(!node){ return root.on('get', msg) } if(!node){ return root.on('get', msg) }
if(has){ if(has){
if('string' != typeof has || !obj_has(node, has)){ return root.on('get', msg) } if('string' != typeof has || !obj_has(node, has)){ return root.on('get', msg) }
@ -819,16 +919,19 @@
} }
node = Gun.graph.node(node); node = Gun.graph.node(node);
tmp = (at||empty).ack; tmp = (at||empty).ack;
var faith = function(){}; faith.faith = true; // HNPERF: We're testing performance improvement by skipping going through security again, but this should be audited. var faith = function(){}; faith.ram = faith.faith = true; // HNPERF: We're testing performance improvement by skipping going through security again, but this should be audited.
DBG && (DBG.ga = +new Date);
root.on('in', { root.on('in', {
'@': msg['#'], '@': msg['#'],
how: 'mem',
put: node, put: node,
ram: 1,
$: gun, $: gun,
_: faith _: faith
}); });
DBG && (DBG.gm = +new Date);
//if(0 < tmp){ return } //if(0 < tmp){ return }
root.on('get', msg); root.on('get', msg);
DBG && (DBG.gd = +new Date);
} }
}()); }());
@ -854,40 +957,31 @@
}, at.opt); }, at.opt);
Gun.on('opt', at); Gun.on('opt', at);
at.opt.uuid = at.opt.uuid || function(){ return state_lex() + text_rand(12) } at.opt.uuid = at.opt.uuid || function(){ return state_lex() + text_rand(12) }
Gun.obj.native();
return gun; return gun;
} }
}()); }());
Gun.obj.native = function(){ var p = Object.prototype; for(var i in p){ console.log("Native Object.prototype polluted, reverting", i); delete p[i]; } };
var list_is = Gun.list.is; var list_is = Gun.list.is;
var text = Gun.text, text_is = text.is, text_rand = text.random; var text = Gun.text, text_is = text.is, text_rand = text.random;
var obj = Gun.obj, obj_is = obj.is, obj_has = obj.has, obj_to = obj.to, obj_map = obj.map, obj_copy = obj.copy; var obj = Gun.obj, obj_empty = obj.empty, obj_is = obj.is, obj_has = obj.has, obj_to = obj.to, obj_map = obj.map, obj_copy = obj.copy;
var state_lex = Gun.state.lex, _soul = Gun.val.link._, _has = '.', node_ = Gun.node._, rel_is = Gun.val.link.is; var state_lex = Gun.state.lex, state_ify = Gun.state.ify, state_is = Gun.state.is, _soul = Gun.val.link._, _has = '.', node_ = Gun.node._, val_is = Gun.val.is, rel_is = Gun.val.link.is, state_ = Gun.state._;
var empty = {}, u; var empty = {}, u;
var C;
console.only = function(i, s){ return (console.only.i && i === console.only.i && console.only.i++) && (console.log.apply(console, arguments) || s) }; Gun.log = function(){ return (!Gun.log.off && C.log.apply(C, arguments)), [].slice.call(arguments).join(' ') };
Gun.log.once = function(w,s,o){ return (o = Gun.log.once)[w] = o[w] || 0, o[w]++ || Gun.log(s) };
Gun.log = function(){ return (!Gun.log.off && console.log.apply(console, arguments)), [].slice.call(arguments).join(' ') }
Gun.log.once = function(w,s,o){ return (o = Gun.log.once)[w] = o[w] || 0, o[w]++ || Gun.log(s) }
;"Please do not remove these messages unless you are paying for a monthly sponsorship, thanks!";
Gun.log.once("welcome", "Hello wonderful person! :) Thanks for using GUN, feel free to ask for help on https://gitter.im/amark/gun and ask StackOverflow questions tagged with 'gun'!");
;"Please do not remove these messages unless you are paying for a monthly sponsorship, thanks!";
if(typeof window !== "undefined"){ (window.GUN = window.Gun = Gun).window = window } if(typeof window !== "undefined"){ (window.GUN = window.Gun = Gun).window = window }
try{ if(typeof common !== "undefined"){ common.exports = Gun } }catch(e){} try{ if(typeof MODULE !== "undefined"){ MODULE.exports = Gun } }catch(e){}
module.exports = Gun; module.exports = Gun;
/*Gun.on('opt', function(ctx){ // FOR TESTING PURPOSES (Gun.window||'').console = (Gun.window||'').console || {log: function(){}};
this.to.next(ctx); (C = console).only = function(i, s){ return (C.only.i && i === C.only.i && C.only.i++) && (C.log.apply(C, arguments) || s) };
if(ctx.once){ return }
ctx.on('node', function(msg){ ;"Please do not remove welcome log unless you are paying for a monthly sponsorship, thanks!";
var to = this.to; Gun.log.once("welcome", "Hello wonderful person! :) Thanks for using GUN, please ask for help on http://chat.gun.eco if anything takes you longer than 5min to figure out!");
//Gun.node.is(msg.put, function(v,k){ msg.put[k] = v + v });
setTimeout(function(){
to.next(msg);
},1);
});
});*/
})(USE, './root'); })(USE, './root');
;USE(function(module){ ;USE(function(module){
@ -917,7 +1011,7 @@
} }
return; return;
} }
if(n instanceof Function){ if('function' == typeof n){
var yes, tmp = {back: at}; var yes, tmp = {back: at};
while((tmp = tmp.back) while((tmp = tmp.back)
&& u === (yes = n(tmp, opt))){} && u === (yes = n(tmp, opt))){}
@ -990,6 +1084,12 @@
put._ = meta; put._ = meta;
back.on('in', {$: back.$, put: put, get: back.get}) 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); root.ask(ack, msg);
return root.on('in', msg); return root.on('in', msg);
@ -1204,7 +1304,7 @@
Gun.obj.del(at, 'ask'); // TODO: PERFORMANCE? More elegant way? Gun.obj.del(at, 'ask'); // TODO: PERFORMANCE? More elegant way?
} }
function ack(msg, ev){ function ack(msg, ev){
var as = this.as, get = as.get || empty, at = as.$._, tmp = (msg.put||empty)[get['#']]; var as = this.as, get = as.get||'', at = as.$._, tmp = (msg.put||'')[get['#']];
if(at.ack){ at.ack = (at.ack + 1) || 1; } if(at.ack){ at.ack = (at.ack + 1) || 1; }
if(!msg.put || ('string' == typeof get['.'] && !obj_has(tmp, at.get))){ if(!msg.put || ('string' == typeof get['.'] && !obj_has(tmp, at.get))){
if(at.put !== u){ return } if(at.put !== u){ return }
@ -1220,7 +1320,7 @@
at.on('in', {get: at.get, put: Gun.val.link.ify(get['#']), $: at.$, '@': msg['@']}); at.on('in', {get: at.get, put: Gun.val.link.ify(get['#']), $: at.$, '@': msg['@']});
return; return;
} }
Gun.on.put(msg, at.root.$); Gun.on.put(msg);
} }
var empty = {}, u; 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; var obj = Gun.obj, obj_has = obj.has, obj_put = obj.put, obj_del = obj.del, obj_to = obj.to, obj_map = obj.map;
@ -1240,7 +1340,7 @@
} }
gun = gun.$; gun = gun.$;
} else } else
if(key instanceof Function){ if('function' == typeof key){
if(true === cb){ return soul(this, key, cb, as), this } if(true === cb){ return soul(this, key, cb, as), this }
gun = this; gun = this;
var at = gun._, root = at.root, tmp = root.now, ev; var at = gun._, root = at.root, tmp = root.now, ev;
@ -1276,7 +1376,7 @@
if(tmp = this._.stun){ // TODO: Refactor? if(tmp = this._.stun){ // TODO: Refactor?
gun._.stun = gun._.stun || tmp; gun._.stun = gun._.stun || tmp;
} }
if(cb && cb instanceof Function){ if(cb && 'function' == typeof cb){
gun.get(cb, as); gun.get(cb, as);
} }
return gun; return gun;
@ -1331,7 +1431,7 @@
if(eve.seen && at.id && eve.seen[at.id]){ return eve.to.next(msg) } if(eve.seen && at.id && eve.seen[at.id]){ return eve.to.next(msg) }
//if((tmp = root.stop)){ if(tmp[at.id]){ return } tmp[at.id] = msg.root; } // temporary fix till a better solution? //if((tmp = root.stop)){ if(tmp[at.id]){ return } tmp[at.id] = msg.root; } // temporary fix till a better solution?
if((tmp = data) && tmp[rel._] && (tmp = rel.is(tmp))){ if((tmp = data) && tmp[rel._] && (tmp = rel.is(tmp))){
tmp = ((msg.$$ = at.root.gun.get(tmp))._); tmp = ((msg.$$ = at.root.$.get(tmp))._);
if(u !== tmp.put){ if(u !== tmp.put){
msg = obj_to(msg, {put: data = tmp.put}); msg = obj_to(msg, {put: data = tmp.put});
} }
@ -1370,21 +1470,7 @@
;USE(function(module){ ;USE(function(module){
var Gun = USE('./root'); var Gun = USE('./root');
Gun.chain.put = function(data, cb, as){ Gun.chain.put = function(data, cb, as){
// #soul.has=value>state
// ~who#where.where=what>when@was
// TODO: BUG! Put probably cannot handle plural chains! `!as` is quickfix test.
var gun = this, at = (gun._), root = at.root.$, ctx = root._, M = 100, tmp; var gun = this, at = (gun._), root = at.root.$, ctx = root._, M = 100, tmp;
/*if(!ctx.puta && !as){ if(tmp = ctx.puts){ if(tmp > M){ // without this, when synchronous, writes to a 'not found' pile up, when 'not found' resolves it recursively calls `put` which incrementally resolves each write. Stack overflow limits can be as low as 10K, so this limit is hardcoded to 1% of 10K.
(ctx.stack || (ctx.stack = [])).push([gun, data, cb, as]);
if(ctx.puto){ return }
ctx.puto = setTimeout(function drain(){
var d = ctx.stack.splice(0,M), i = 0, at; ctx.puta = true;
while(at = d[i++]){ at[0].put(at[1], at[2], at[3]) } delete ctx.puta;
if(ctx.stack.length){ return ctx.puto = setTimeout(drain, 0) }
ctx.stack = ctx.puts = ctx.puto = null;
}, 0);
return gun;
} ++ctx.puts } else { ctx.puts = 1 } }*/
as = as || {}; as = as || {};
as.data = data; as.data = data;
as.via = as.$ = as.via || as.$ || gun; as.via = as.$ = as.via || as.$ || gun;
@ -1439,6 +1525,14 @@
} }
return gun; return gun;
}; };
/*Gun.chain.put = function(data, cb, as){ // don't rewrite! :(
var gun = this, at = gun._;
as = as || {};
as.soul || (as.soul = at.soul || ('string' == typeof cb && cb));
if(!as.soul){ return get(data, cb, as) }
return gun;
}*/
function ify(as){ function ify(as){
as.batch = batch; as.batch = batch;
@ -1470,7 +1564,7 @@
} }
function batch(){ var as = this; function batch(){ var as = this;
if(!as.graph || obj_map(as.stun, no)){ return } if(!as.graph || !obj_empty(as.stun)){ return }
as.res = as.res || function(cb){ if(cb){ cb() } }; as.res = as.res || function(cb){ if(cb){ cb() } };
as.res(function(){ as.res(function(){
var cat = (as.$.back(-1)._), ask = cat.ask(function(ack){ var cat = (as.$.back(-1)._), ask = cat.ask(function(ack){
@ -1514,7 +1608,7 @@
at.soul(id); at.soul(id);
return; return;
} }
(as.stun = as.stun || {})[path] = true; (as.stun = as.stun || {})[path] = 1;
ref.get(soul, true, {as: {at: at, as: as, p:path}}); ref.get(soul, true, {as: {at: at, as: as, p:path}});
}, {as: as, at: at}); }, {as: as, at: at});
//if(is){ return {} } //if(is){ return {} }
@ -1538,7 +1632,7 @@
function solve(at, id, cat, as){ function solve(at, id, cat, as){
at.$.back(-1).get(id); at.$.back(-1).get(id);
cat.soul(id); cat.soul(id);
as.stun[cat.path] = false; delete as.stun[cat.path];
as.batch(); as.batch();
} }
@ -1595,7 +1689,7 @@
} }
as.ref.put(as.data, as.soul, as); as.ref.put(as.data, as.soul, as);
} }
var obj = Gun.obj, obj_is = obj.is, obj_put = obj.put, obj_map = obj.map; var obj = Gun.obj, obj_is = obj.is, obj_put = obj.put, obj_map = obj.map, obj_empty = obj.empty;
var u, empty = {}, noop = function(){}, iife = function(fn,as){fn.call(as||empty)}; var u, empty = {}, noop = function(){}, iife = function(fn,as){fn.call(as||empty)};
var node_ = Gun.node._; var node_ = Gun.node._;
})(USE, './put'); })(USE, './put');
@ -1847,7 +1941,7 @@
} }
root.on('out', function(msg){ root.on('out', function(msg){
if(msg.lS){ return } // TODO: for IndexedDB and others, shouldn't send to peers ACKs to our own GETs. if(msg.lS){ return } // TODO: for IndexedDB and others, shouldn't send to peers ACKs to our own GETs. // THIS IS BLOCKING BROWSERS REPLYING TO REQUESTS, NO??? CHANGE THIS SOON!! UNDER CONTROLLED CIRCUMSTANCES!! Or maybe in-memory already doe sit?
if(Gun.is(msg.$) && msg.put && !msg['@']){ if(Gun.is(msg.$) && msg.put && !msg['@']){
id = msg['#']; id = msg['#'];
Gun.graph.is(msg.put, null, map); Gun.graph.is(msg.put, null, map);
@ -1897,10 +1991,11 @@
var lS = function(){}, u; var lS = function(){}, u;
root.on('localStorage', disk); // NON-STANDARD EVENT! root.on('localStorage', disk); // NON-STANDARD EVENT!
root.on('put', function(at){ root.on('put', function(msg){
this.to.next(at); this.to.next(msg);
Gun.graph.is(at.put, null, map); var put = msg.put, soul = put['#'], key = put['.'], val = put[':'], state = put['>'], tmp;
if(!at['@']){ acks[at['#']] = true; } // only ack non-acks. disk[soul] = Gun.state.ify(disk[soul], key, state, val, soul);
if(!msg['@']){ (acks[msg['#']] = (tmp = (msg._||'').lot || {})).lS = (tmp.lS||0)+1; } // only ack non-acks.
count += 1; count += 1;
if(count >= (opt.batch || 1000)){ if(count >= (opt.batch || 1000)){
return flush(); return flush();
@ -1921,7 +2016,7 @@
data = Gun.state.to(data, has); data = Gun.state.to(data, has);
} }
//if(!data && !Gun.obj.empty(opt.peers)){ return } // if data not found, don't ack if there are peers. // Hmm, what if we have peers but we are disconnected? //if(!data && !Gun.obj.empty(opt.peers)){ return } // if data not found, don't ack if there are peers. // Hmm, what if we have peers but we are disconnected?
root.on('in', {'@': msg['#'], put: Gun.graph.node(data), how: 'lS', lS: msg.$});// || root.$}); root.on('in', {'@': msg['#'], put: Gun.graph.node(data), lS:1});// || root.$});
}; };
Gun.debug? setTimeout(to,1) : to(); Gun.debug? setTimeout(to,1) : to();
}); });
@ -1945,6 +2040,10 @@
} }
if(!err && !Gun.obj.empty(opt.peers)){ return } // only ack if there are no peers. if(!err && !Gun.obj.empty(opt.peers)){ return } // only ack if there are no peers.
Gun.obj.map(ack, function(yes, id){ Gun.obj.map(ack, function(yes, id){
if(yes){
if(yes.more){ acks[id] = yes; return }
if(yes.s !== yes.lS){ err = "localStorage batch not same." }
}
root.on('in', { root.on('in', {
'@': id, '@': id,
err: err, err: err,
@ -1957,148 +2056,176 @@
;USE(function(module){ ;USE(function(module){
var Type = USE('../type'); var Type = USE('../type');
var puff = (typeof setImmediate !== "undefined")? setImmediate : setTimeout;
function Mesh(root){ function Mesh(root){
var mesh = function(){}; var mesh = function(){};
var opt = root.opt || {}; var opt = root.opt || {};
opt.log = opt.log || console.log; opt.log = opt.log || console.log;
opt.gap = opt.gap || opt.wait || 1; opt.gap = opt.gap || opt.wait || 0;
opt.pack = opt.pack || (opt.memory? (opt.memory * 1000 * 1000) : 1399000000) * 0.3; // max_old_space_size defaults to 1400 MB. opt.pack = opt.pack || (opt.memory? (opt.memory * 1000 * 1000) : 1399000000) * 0.3; // max_old_space_size defaults to 1400 MB.
opt.puff = opt.puff || 9; // IDEA: do a start/end benchmark, divide ops/result.
var puff = setTimeout.puff || setTimeout;
var dup = root.dup; var dup = root.dup, dup_check = dup.check, dup_track = dup.track;
// TODO: somewhere in here caused a out-of-memory crash! How? It is inbound! var hear = mesh.hear = function(raw, peer){
mesh.hear = function(raw, peer){
if(!raw){ return } if(!raw){ return }
var msg, id, hash, tmp = raw[0];
if(opt.pack <= raw.length){ return mesh.say({dam: '!', err: "Message too big!"}, peer) } if(opt.pack <= raw.length){ return mesh.say({dam: '!', err: "Message too big!"}, peer) }
if('{' != raw[2]){ mesh.hear.d += raw.length||0; ++mesh.hear.c; } // STATS! // ugh, stupid double JSON encoding var msg, id, hash, tmp = raw[0], DBG;
if(mesh === this){ hear.d += raw.length||0 ; ++hear.c } // STATS!
if('[' === tmp){ if('[' === tmp){
try{msg = JSON.parse(raw);}catch(e){opt.log('DAM JSON parse error', e)} try{msg = JSON.parse(raw)}catch(e){opt.log('DAM JSON parse error', e)}
raw = '';
if(!msg){ return } if(!msg){ return }
LOG && opt.log(+new Date, msg.length, 'in hear batch'); console.STAT && console.STAT(+new Date, msg.length, '# on hear batch');
var P = opt.puff;
(function go(){ (function go(){
var S; LOG && (S = +new Date); // STATS! var S = +new Date;
var m, c = 100; // hardcoded for now? //var P = peer.puff || opt.puff, s = +new Date; // TODO: For future, but in mix?
while(c-- && (m = msg.shift())){ var i = 0, m; while(i < P && (m = msg[i++])){ hear(m, peer) }
mesh.hear(m, peer); //peer.puff = Math.ceil((+new Date - s)? P * 1.1 : P * 0.9);
} msg = msg.slice(i); // slicing after is faster than shifting during.
LOG && opt.log(S, +new Date - S, 'batch heard'); console.STAT && console.STAT(S, +new Date - S, 'hear loop');
flush(peer); // force send all synchronously batched acks.
if(!msg.length){ return } if(!msg.length){ return }
puff(go, 0); puff(go, 0);
}()); }());
return; return;
} }
if('{' === tmp || (Type.obj.is(raw) && (msg = raw))){ if('{' === tmp || ((raw['#'] || obj_is(raw)) && (msg = raw))){
try{msg = msg || JSON.parse(raw); try{msg = msg || JSON.parse(raw);
}catch(e){return opt.log('DAM JSON parse error', e)} }catch(e){return opt.log('DAM JSON parse error', e)}
if(!msg){ return } if(!msg){ return }
if(msg.DBG){ msg.DBG = DBG = {DBG: msg.DBG} }
DBG && (DBG.hp = +new Date);
if(!(id = msg['#'])){ id = msg['#'] = Type.text.random(9) } if(!(id = msg['#'])){ id = msg['#'] = Type.text.random(9) }
if(msg.DBG_s){ opt.log(+new Date - msg.DBG_s, 'to hear', id) } if(tmp = dup_check(id)){ return }
if(dup.check(id)){ return } /*if(!(hash = msg['##']) && u !== msg.put){ hash = msg['##'] = Type.obj.hash(msg.put) }
dup.track(id, true).it = it(msg); // GUN core also dedups, so `true` is needed. // Does GUN core need to dedup anymore?
if(!(hash = msg['##']) && u !== msg.put){ hash = msg['##'] = Type.obj.hash(msg.put) }
if(hash && (tmp = msg['@'] || (msg.get && id))){ // Reduces backward daisy in case varying hashes at different daisy depths are the same. if(hash && (tmp = msg['@'] || (msg.get && id))){ // Reduces backward daisy in case varying hashes at different daisy depths are the same.
if(dup.check(tmp+hash)){ return } if(dup.check(tmp+hash)){ return }
dup.track(tmp+hash, true).it = it(msg); // GUN core also dedups, so `true` is needed. // Does GUN core need to dedup anymore? dup.track(tmp+hash, true).it = it(msg); // GUN core also dedups, so `true` is needed. // Does GUN core need to dedup anymore?
} }
(msg._ = function(){}).via = peer;
if(tmp = msg['><']){ (msg._).to = Type.obj.map(tmp.split(','), tomap) } if(tmp = msg['><']){ (msg._).to = Type.obj.map(tmp.split(','), tomap) }
if(msg.dam){ */ // TOOD: COME BACK TO THIS LATER!!! IMPORTANT MESH STUFF!!
if(tmp = mesh.hear[msg.dam]){ (msg._ = function(){}).via = mesh.leap = peer;
if(tmp = msg.dam){
if(tmp = mesh.hear[tmp]){
tmp(msg, peer, root); tmp(msg, peer, root);
} }
dup_track(id);
return; return;
} }
var S, ST; LOG && (S = +new Date); var S = +new Date, ST;
DBG && (DBG.is = S);
root.on('in', msg); root.on('in', msg);
LOG && !msg.nts && (ST = +new Date - S) > 9 && opt.log(S, ST, 'msg', msg['#']); //ECHO = msg.put || ECHO; !(msg.ok !== -3740) && mesh.say({ok: -3740, put: ECHO, '@': msg['#']}, peer);
return; DBG && (DBG.hd = +new Date);
console.STAT && (ST = +new Date - S) > 9 && console.STAT(S, ST, 'msg'); // TODO: PERF: caught one > 1.5s on tgif
dup_track(id).via = peer;
mesh.leap = null; // warning! mesh.leap could be buggy.
} }
} }
var tomap = function(k,i,m){m(k,true)}; var tomap = function(k,i,m){m(k,true)};
mesh.hear.c = mesh.hear.d = 0; var noop = function(){};
hear.c = hear.d = 0;
;(function(){ ;(function(){
var SMIA = 0; var SMIA = 0;
var message; var message, loop;
function each(peer){ mesh.say(message, peer) } function each(peer){ mesh.say(message, peer) }
mesh.say = function(msg, peer){ var say = mesh.say = function(msg, peer){
if(this.to){ this.to.next(msg) } // compatible with middleware adapters. if(this.to){ this.to.next(msg) } // compatible with middleware adapters.
if(!msg){ return false } if(!msg){ return false }
var id, hash, tmp, raw; var id, hash, tmp, raw;
var S; LOG && (S = +new Date); //msg.DBG_s = msg.DBG_s || +new Date; var DBG = msg.DBG, S; if(!peer){ S = +new Date ; DBG && (DBG.y = S) }
var meta = msg._||(msg._=function(){}); var meta = msg._||(msg._=function(){});
if(!(id = msg['#'])){ id = msg['#'] = Type.text.random(9) } if(!(id = msg['#'])){ id = msg['#'] = Type.text.random(9) }
if(!(hash = msg['##']) && u !== msg.put){ hash = msg['##'] = Type.obj.hash(msg.put) } //if(!(hash = msg['##']) && u !== msg.put){ hash = msg['##'] = Type.obj.hash(msg.put) }
if(!(raw = meta.raw)){ if(!(raw = meta.raw)){
raw = mesh.raw(msg); raw = mesh.raw(msg);
if(hash && (tmp = msg['@'])){ /*if(hash && (tmp = msg['@'])){
dup.track(tmp+hash).it = it(msg); dup.track(tmp+hash).it = it(msg);
if(tmp = (dup.s[tmp]||ok).it){ if(tmp = (dup.s[tmp]||ok).it){
if(hash === tmp['##']){ return false } if(hash === tmp['##']){ return false }
tmp['##'] = hash; tmp['##'] = hash;
} }
}*/
} }
} S && console.STAT && console.STAT(S, +new Date - S, 'say prep');
//LOG && opt.log(S, +new Date - S, 'say prep'); !loop && dup_track(id);//.it = it(msg); // track for 9 seconds, default. Earth<->Mars would need more! // always track, maybe move this to the 'after' logic if we split function.
dup.track(id).it = it(msg); // track for 9 seconds, default. Earth<->Mars would need more! //console.log("SEND!", JSON.parse(JSON.stringify(msg)));
if(!peer){ peer = (tmp = dup.s[msg['@']]) && (tmp = tmp.it) && (tmp = tmp._) && (tmp = tmp.via) } if(!peer && (tmp = msg['@'])){ peer = ((tmp = dup.s[tmp]) && (tmp.via || ((tmp = tmp.it) && (tmp = tmp._) && tmp.via))) || mesh.leap } // warning! mesh.leap could be buggy!
if(!peer && msg['@']){ if(!peer && msg['@']){
LOG && opt.log(+new Date, ++SMIA, 'total no peer to ack to'); console.STAT && console.STAT(+new Date, ++SMIA, 'total no peer to ack to');
return false; return false;
} // TODO: Temporary? If ack via trace has been lost, acks will go to all peers, which trashes browser bandwidth. Not relaying the ack will force sender to ask for ack again. Note, this is technically wrong for mesh behavior. } // TODO: Temporary? If ack via trace has been lost, acks will go to all peers, which trashes browser bandwidth. Not relaying the ack will force sender to ask for ack again. Note, this is technically wrong for mesh behavior.
if(!peer && mesh.way){ return mesh.way(msg) } if(!peer && mesh.way){ return mesh.way(msg) }
if(!peer || !peer.id){ message = msg; if(!peer || !peer.id){ message = msg;
if(!Type.obj.is(peer || opt.peers)){ return false } if(!Type.obj.is(peer || opt.peers)){ return false }
//var S; LOG && (S = +new Date); var P = opt.puff, ps = opt.peers, pl = Object.keys(peer || opt.peers || {}); // TODO: BETTER PERF? No object.keys? It is polyfilled by Type.js tho.
Type.obj.map(peer || opt.peers, each); // in case peer is a peer list. ;(function go(){
//LOG && opt.log(S, +new Date - S, 'say loop'); var S = +new Date;
//Type.obj.map(peer || opt.peers, each); // in case peer is a peer list.
loop = 1; var wr = meta.raw; meta.raw = raw; // quick perf hack
var i = 0, p; while(i < 9 && (p = (pl||'')[i++])){
if(!(p = ps[p])){ continue }
say(msg, p);
}
meta.raw = wr; loop = 0;
pl = pl.slice(i); // slicing after is faster than shifting during.
console.STAT && console.STAT(S, +new Date - S, 'say loop');
if(!pl.length){ return }
puff(go, 0);
dup_track(msg['@']); // keep for later
}());
return; return;
} }
// TODO: PERF: consider splitting function here, so say loops do less work.
if(!peer.wire && mesh.wire){ mesh.wire(peer) } if(!peer.wire && mesh.wire){ mesh.wire(peer) }
if(id === peer.last){ return } peer.last = id; // was it just sent? if(id === peer.last){ return } peer.last = id; // was it just sent?
if(peer === meta.via){ return false } if(peer === meta.via){ return false } // don't send back to self.
if((tmp = meta.to) && (tmp[peer.url] || tmp[peer.pid] || tmp[peer.id]) /*&& !o*/){ return false } if((tmp = meta.to) && (tmp[peer.url] || tmp[peer.pid] || tmp[peer.id]) /*&& !o*/){ return false }
if(peer.batch){ if(peer.batch){
peer.tail = (tmp = peer.tail || 0) + raw.length; peer.tail = (tmp = peer.tail || 0) + raw.length;
if(peer.tail <= opt.pack){ if(peer.tail <= opt.pack){
peer.batch.push(raw); // peer.batch += (tmp?'':',')+raw; // TODO: Prevent double JSON! // FOR v1.0 !? //peer.batch.push(raw);
peer.batch += (tmp?',':'')+raw; // TODO: Prevent double JSON! // FOR v1.0 !?
return; return;
} }
flush(peer); flush(peer);
} }
peer.batch = []; // peer.batch = '['; // TODO: Prevent double JSON! //peer.batch = [];
setTimeout(function(){flush(peer)}, opt.gap); peer.batch = '['; // TODO: Prevent double JSON!
var S = +new Date, ST;
setTimeout(function(){
console.STAT && (ST = +new Date - S) > 9 && console.STAT(S, ST, '0ms TO', id, peer.id);
flush(peer);
}, opt.gap);
send(raw, peer); send(raw, peer);
} }
function flush(peer){
var tmp = peer.batch; // var tmp = peer.batch + ']'; // TODO: Prevent double JSON!
peer.batch = peer.tail = null;
if(!tmp){ return }
if(!tmp.length){ return } // if(3 > tmp.length){ return } // TODO: ^
var S; LOG && (S = +new Date);
try{tmp = (1 === tmp.length? tmp[0] : JSON.stringify(tmp));
}catch(e){return opt.log('DAM JSON stringify error', e)}
LOG && opt.log(S, +new Date - S, 'say stringify', tmp.length);
if(!tmp){ return }
send(tmp, peer);
}
mesh.say.c = mesh.say.d = 0; mesh.say.c = mesh.say.d = 0;
}()); }());
function flush(peer){
var tmp = peer.batch, t = 'string' == typeof tmp, l;
if(t){ tmp += ']' }// TODO: Prevent double JSON!
peer.batch = peer.tail = null;
if(!tmp){ return }
if(t? 3 > tmp.length : !tmp.length){ return } // TODO: ^
if(!t){try{tmp = (1 === tmp.length? tmp[0] : JSON.stringify(tmp));
}catch(e){return opt.log('DAM JSON stringify error', e)}}
if(!tmp){ return }
send(tmp, peer);
}
// for now - find better place later. // for now - find better place later.
function send(raw, peer){ try{ function send(raw, peer){ try{
var wire = peer.wire; var wire = peer.wire;
var S, ST; LOG && (S = +new Date);
if(peer.say){ if(peer.say){
peer.say(raw); peer.say(raw);
} else } else
if(wire.send){ if(wire.send){
wire.send(raw); wire.send(raw);
} }
LOG && (ST = +new Date - S) > 9 && opt.log(S, ST, 'wire send', raw.length);
mesh.say.d += raw.length||0; ++mesh.say.c; // STATS! mesh.say.d += raw.length||0; ++mesh.say.c; // STATS!
}catch(e){ }catch(e){
(peer.queue = peer.queue || []).push(raw); (peer.queue = peer.queue || []).push(raw);
@ -2110,18 +2237,22 @@
if(!msg){ return '' } if(!msg){ return '' }
var meta = (msg._) || {}, put, hash, tmp; var meta = (msg._) || {}, put, hash, tmp;
if(tmp = meta.raw){ return tmp } if(tmp = meta.raw){ return tmp }
if(typeof msg === 'string'){ return msg } if('string' == typeof msg){ return msg }
if(!msg.dam){ /*if(!msg.dam){ // TOOD: COME BACK TO THIS LATER!!! IMPORTANT MESH STUFF!!
var i = 0, to = []; Type.obj.map(opt.peers, function(p){ var i = 0, to = []; Type.obj.map(opt.peers, function(p){
to.push(p.url || p.pid || p.id); if(++i > 3){ return true } // limit server, fast fix, improve later! // For "tower" peer, MUST include 6 surrounding ids. // REDUCED THIS TO 3 for temporary relay peer performance, towers still should list neighbors. to.push(p.url || p.pid || p.id); if(++i > 3){ return true } // limit server, fast fix, improve later! // For "tower" peer, MUST include 6 surrounding ids. // REDUCED THIS TO 3 for temporary relay peer performance, towers still should list neighbors.
}); if(i > 1){ msg['><'] = to.join() } }); if(i > 1){ msg['><'] = to.join() }
} }*/ // TOOD: COME BACK TO THIS LATER!!! IMPORTANT MESH STUFF!!
var raw = $(msg); // optimize by reusing put = the JSON.stringify from .hash? var raw = $(msg); // optimize by reusing put = the JSON.stringify from .hash?
/*if(u !== put){ /*if(u !== put){
tmp = raw.indexOf(_, raw.indexOf('put')); tmp = raw.indexOf(_, raw.indexOf('put'));
raw = raw.slice(0, tmp-1) + put + raw.slice(tmp + _.length + 1); raw = raw.slice(0, tmp-1) + put + raw.slice(tmp + _.length + 1);
//raw = raw.replace('"'+ _ +'"', put); // NEVER USE THIS! ALSO NEVER DELETE IT TO NOT MAKE SAME MISTAKE! https://github.com/amark/gun/wiki/@$$ Heisenbug //raw = raw.replace('"'+ _ +'"', put); // NEVER USE THIS! ALSO NEVER DELETE IT TO NOT MAKE SAME MISTAKE! https://github.com/amark/gun/wiki/@$$ Heisenbug
}*/ }*/
// TODO: PERF: tgif, CPU way too much on re-JSONifying ^ it.
/*
// NOTE TO SELF: Switch NTS to DAM now.
*/
if(meta && (raw||'').length < (1000 * 100)){ meta.raw = raw } // HNPERF: If string too big, don't keep in memory. if(meta && (raw||'').length < (1000 * 100)){ meta.raw = raw } // HNPERF: If string too big, don't keep in memory.
return raw; return raw;
} }
@ -2145,12 +2276,12 @@
Type.obj.map(tmp, function(msg){ Type.obj.map(tmp, function(msg){
send(msg, peer); send(msg, peer);
}); });
Type.obj.native && Type.obj.native(); // dirty place to check if other JS polluted.
} }
mesh.bye = function(peer){ mesh.bye = function(peer){
root.on('bye', peer); root.on('bye', peer);
var tmp = +(new Date); tmp = (tmp - (peer.met||tmp)); var tmp = +(new Date); tmp = (tmp - (peer.met||tmp));
mesh.bye.time = ((mesh.bye.time || tmp) + tmp) / 2; mesh.bye.time = ((mesh.bye.time || tmp) + tmp) / 2;
LOG = console.LOG; // dirty place to cheaply update LOG settings over time.
} }
mesh.hear['!'] = function(msg, peer){ opt.log('Error:', msg.err) } mesh.hear['!'] = function(msg, peer){ opt.log('Error:', msg.err) }
mesh.hear['?'] = function(msg, peer){ mesh.hear['?'] = function(msg, peer){
@ -2183,6 +2314,7 @@
}); });
root.on('hi', function(peer, tmp){ this.to.next(peer); root.on('hi', function(peer, tmp){ this.to.next(peer);
if(!(tmp = peer.url) || !gets[tmp]){ return } delete gets[tmp]; if(!(tmp = peer.url) || !gets[tmp]){ return } delete gets[tmp];
if(opt.super){ return } // temporary (?) until we have better fix/solution?
Type.obj.map(root.next, function(node, soul){ Type.obj.map(root.next, function(node, soul){
tmp = {}; tmp[soul] = root.graph[soul]; tmp = {}; tmp[soul] = root.graph[soul];
mesh.say({'##': Type.obj.hash(tmp), get: {'#': soul}}, peer); mesh.say({'##': Type.obj.hash(tmp), get: {'#': soul}}, peer);
@ -2214,7 +2346,9 @@
function sort(k, v){ var tmp; function sort(k, v){ var tmp;
if(!(v instanceof Object)){ return v } if(!(v instanceof Object)){ return v }
var S = +new Date;
Type.obj.map(Object.keys(v).sort(), map, {to: tmp = {}, on: v}); Type.obj.map(Object.keys(v).sort(), map, {to: tmp = {}, on: v});
console.STAT && console.STAT(S, +new Date - S, 'sort');
return tmp; return tmp;
} }
Type.obj.hash.sort = sort; Type.obj.hash.sort = sort;
@ -2227,7 +2361,7 @@
function it(msg){ return msg || {_: msg._, '##': msg['##']} } // HNPERF: Only need some meta data, not full reference (took up too much memory). // HNPERF: Garrrgh! We add meta data to msg over time, copying the object happens to early. function it(msg){ return msg || {_: msg._, '##': msg['##']} } // HNPERF: Only need some meta data, not full reference (took up too much memory). // HNPERF: Garrrgh! We add meta data to msg over time, copying the object happens to early.
var empty = {}, ok = true, u; var empty = {}, ok = true, u;
var LOG = console.LOG; var obj_is = Type.obj.is, obj_map = Type.obj.map;
try{ module.exports = Mesh }catch(e){} try{ module.exports = Mesh }catch(e){}

32
lib/crashed.js Normal file
View File

@ -0,0 +1,32 @@
;(function(){ try {
var fs = require('fs'), logs = [], up = __dirname+'/../';
fs.readdir(up, function(err, list){ try{
var i = 0, f; while(f = list[i++]){
if(0 === f.indexOf('isolate-') && '.log' === f.slice(-4)){ logs.push(f) }
}
logs = logs.sort();
var i = 0, f, lf; while(f = list[i++]){
if(0 <= f.indexOf('-v8-') && '.log' === f.slice(-4)){ lf = f }
} f = lf;
if(!f){ return }
fs.rename(up+f, up+'v8.log', function(err,ok){
var i = 0, f; while(f = logs[i++]){ fs.unlink(up+f, noop) }
if(!process.env.EMAIL){ return } // ONLY EMAIL IF DEVELOPER OPTS IN!!!
email(); // ONLY EMAIL IF DEVELOPER OPTS IN!!!
});
}catch(e){} });
function noop(){};
function email(){ try{
if(!process.env.EMAIL){ return } // ONLY EMAIL IF DEVELOPER OPTS IN!!!
var address = process.env.EMAIL || "mark@gun.eco";
// you also have to specify your EMAIL_KEY gmail 2F' app's password (not reg) to send out.
require('./email').send({
text: "log attached",
from: address,
to: address,
subject: "GUN V8 LOG",
attachment:[{path: up+'v8.log', type:"text/plain", name:"v8.log"}]
}, noop);
}catch(e){} };
}catch(e){}
}());

View File

@ -10,26 +10,28 @@
try{ heap = require('v8').getHeapStatistics }catch(e){} try{ heap = require('v8').getHeapStatistics }catch(e){}
if(!heap){ return } if(!heap){ return }
ev.max = parseFloat(root.opt.memory || (heap().heap_size_limit / 1024 / 1024) || process.env.WEB_MEMORY || 1399) * 0.8; // max_old_space_size defaults to 1400 MB. Note: old space !== memory space though. ev.max = parseFloat(root.opt.memory || (heap().heap_size_limit / 1024 / 1024) || process.env.WEB_MEMORY || 1399) * 0.8; // max_old_space_size defaults to 1400 MB. Note: old space !== memory space though. // KEEPING USED_HEA_SIZE < HEAP_SIZE_LIMIT ONLY THING TO BE BELOW TO PREVENT CRASH!
setInterval(check, 1000); setInterval(check, 1000);
function check(){ function check(){
var used = util().rss / 1024 / 1024; var used = util().rss / 1024 / 1024;
var hused = heap().used_heap_size / 1024 / 1024; var hused = heap().used_heap_size / 1024 / 1024;
//if(hused < ev.max && used < ev.max){ return } if(hused < ev.max && used < ev.max){ return }
if(used < ev.max){ return } //if(used < ev.max){ return }
console.LOG && Gun.log('evict memory:', hused.toFixed(), used.toFixed(), ev.max.toFixed()); console.STAT && console.STAT('evict memory:', hused.toFixed(), used.toFixed(), ev.max.toFixed());
GC();//setTimeout(GC, 1); GC();//setTimeout(GC, 1);
} }
function GC(){ function GC(){
var S = +new Date;
var souls = Object.keys(root.graph||empty); var souls = Object.keys(root.graph||empty);
var toss = Math.ceil(souls.length * 0.01); var toss = Math.ceil(souls.length * 0.01);
//var S = +new Date; //var S = +new Date;
Gun.list.map(souls, function(soul){ Gun.list.map(souls, function(soul){
if(--toss < 0){ return } if(--toss < 0){ return }
root.gun.get(soul).off(); root.$.get(soul).off();
}); });
root.dup.drop(1000 * 9); // clean up message tracker root.dup.drop(1000 * 9); // clean up message tracker
console.STAT && console.STAT(S, +new Date - S, 'evict');
} }
/* /*
root.on('in', function(msg){ root.on('in', function(msg){

View File

@ -83,7 +83,7 @@ Gun.on('create', function(root){
} }
if((tmp = root.stats) && (tmp = tmp.gap) && info){ (tmp.near || (tmp.near = {}))[info.address] = info.port || 1 } // STATS! if((tmp = root.stats) && (tmp = tmp.gap) && info){ (tmp.near || (tmp.near = {}))[info.address] = info.port || 1 } // STATS!
if(check.on || id === pid){ return } if(check.on || id === pid){ return }
root.on('out', check.on = say); root.on('out', check.on = say); // TODO: MULTICAST NEEDS TO BE CHECKED FOR NEW CODE SYSTEM!!!!!!!!!!
} }
setInterval(check, 1000 * 1); setInterval(check, 1000 * 1);

View File

@ -16,10 +16,14 @@
opt.code.from = opt.code.from || '!'; opt.code.from = opt.code.from || '!';
opt.jsonify = true; opt.jsonify = true;
function ename(t){ return encodeURIComponent(t).replace(/\*/g, '%2A') } function ename(t){ return encodeURIComponent(t).replace(/\*/g, '%2A') }
function atomic(v){ return u !== v && (!v || 'object' != typeof v) } function atomic(v){ return u !== v && (!v || 'object' != typeof v) }
var timediate = (typeof setImmediate === "undefined")? setTimeout : setImmediate;
var puff = setTimeout.puff || timediate;
var map = Gun.obj.map; var map = Gun.obj.map;
var LOG = console.LOG; var obj_empty = Gun.obj.empty;
var ST = 0;
if(!opt.store){ if(!opt.store){
return opt.log("ERROR: Radisk needs `opt.store` interface with `{get: fn, put: fn (, list: fn)}`!"); return opt.log("ERROR: Radisk needs `opt.store` interface with `{get: fn, put: fn (, list: fn)}`!");
@ -39,154 +43,96 @@
1. Because writing to disk takes time, we should batch data to disk. This improves performance, and reduces potential disk corruption. 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. 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){ var r = function(key, data, cb, tag, DBG){
key = ''+key; if('function' === typeof data){
if(val instanceof Function){
var o = cb || {}; var o = cb || {};
cb = val; cb = data;
var S; LOG && (S = +new Date); r.read(key, cb, o, DBG || tag);
val = r.batch(key);
LOG && opt.log(S, +new Date - S, '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});
return; return;
} }
//console.only(99); var ID = Gun.text.random(2), S = (+new Date); console.log("[[[[[[[[", ID); //var tmp = (tmp = r.batch = r.batch || {})[key] = tmp[key] || {};
r.save.ing = []; //var tmp = (tmp = r.batch = r.batch || {})[key] = data;
var ack = cb; r.save(key, data, cb, tag, DBG);
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; r.save = function(key, data, cb, tag, DBG){
go.end = key; var s = {key: key}, tags, f, d, q;
r.list(go); s.find = function(file){ var tmp;
++s.i; // TODO: See above, revise? s.file = file || (file = opt.code.from);
DBG && (DBG = DBG[file] = DBG[file] || {});
DBG && (DBG.sf = DBG.sf || +new Date);
if(tmp = r.disk[file]){ s.mix(u, tmp); return }
r.parse(file, s.mix, u, DBG);
} }
s.go = function(){ s.mix = function(err, disk){
if(s.gone){ return } s.gone = true; DBG && (DBG.sml = +new Date);
s.seq = []; DBG && (DBG.sm = DBG.sm || +new Date);
map(s.files, function(mem, file){ s.seq.push({file: file, mem: mem}) }); if(s.err = err || s.err){ cb(err); return } // TODO: HANDLE BATCH EMIT
s.files = null; var file = s.file = (disk||'').file || s.file, tmp;
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? if(!disk && file !== opt.code.from){ // corrupt file?
r.list.bad(file); // remove from dir list r.find.bad(file); // remove from dir list
r.save(rad, cb); // try again r.save(key, data, cb, tag); // try again
return; return;
} }
disk = disk || Radix(); (disk = r.disk[file] || (r.disk[file] = disk || Radix())).file || (disk.file = file);
Radix.map(mem, function(val, key){ if(opt.compare){
// PLUGIN: consider adding HAM as an extra layer of protection data = opt.compare(disk(key), data, key, file);
disk(key, val); // merge batch[key] -> disk[key] if(u === data){ cb(err, -1); return } // TODO: HANDLE BATCH EMIT
}
(s.disk = disk)(key, data);
if(tag){
(tmp = (tmp = disk.tags || (disk.tags = {}))[tag] || (tmp[tag] = r.tags[tag] || (r.tags[tag] = {})))[file] || (tmp[file] = r.one[tag] || (r.one[tag] = cb));
cb = null;
}
DBG && (DBG.st = DBG.st || +new Date);
if(disk.Q){ cb && disk.Q.push(cb); return } disk.Q = (cb? [cb] : []);
disk.to = setTimeout(s.write, opt.until);
}
s.write = function(){
DBG && (DBG.sto = DBG.sto || +new Date);
var file = f = s.file, disk = d = s.disk;
q = s.q = disk.Q;
tags = s.tags = disk.tags;
delete disk.Q;
delete r.disk[file];
delete disk.tags;
r.write(file, disk, s.ack, u, DBG);
}
s.ack = function(err, ok){
DBG && (DBG.sa = DBG.sa || +new Date);
DBG && (DBG.sal = q.length);
var ack, tmp;
// TODO!!!! CHANGE THIS INTO PUFF!!!!!!!!!!!!!!!!
for(var id in r.tags){
if(!r.tags.hasOwnProperty(id)){ continue } var tag = r.tags[id];
if((tmp = r.disk[f]) && (tmp = tmp.tags) && tmp[tag]){ continue }
ack = tag[f];
delete tag[f];
if(!obj_empty(tag)){ continue }
delete r.tags[tag];
ack && ack(err, ok);
}
!q && (q = '');
var l = q.length, i = 0;
// TODO: PERF: Why is acks so slow, what work do they do??? CHECK THIS!!
// TODO: PERF: Why is acks so slow, what work do they do??? CHECK THIS!!
// TODO: PERF: Why is acks so slow, what work do they do??? CHECK THIS!!
// TODO: PERF: Why is acks so slow, what work do they do??? CHECK THIS!!
// TODO: PERF: Why is acks so slow, what work do they do??? CHECK THIS!!
// TODO: PERF: Why is acks so slow, what work do they do??? CHECK THIS!!
// TODO: PERF: Why is acks so slow, what work do they do??? CHECK THIS!!
var S = +new Date;
for(;i < l; i++){ (ack = q[i]) && ack(err, ok) }
console.STAT && console.STAT(S, +new Date - S, 'rad acks', ename(s.file));
console.STAT && console.STAT(S, q.length, 'rad acks #', ename(s.file));
}
cb || (cb = function(err, ok){ // test delete!
if(!err){ return }
}); });
r.write(file, disk, s.pop); r.find(key, s.find);
})
}
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?
} }
r.disk = {};
r.one = {};
r.tags = {};
/* /*
Any storage engine at some point will have to do a read in order to write. Any storage engine at some point will have to do a read in order to write.
@ -194,66 +140,99 @@
Therefore it is unavoidable that a read will have to happen, Therefore it is unavoidable that a read will have to happen,
the question is just how long you delay it. the question is just how long you delay it.
*/ */
r.write = function(file, rad, cb, o){ var RWC = 0;
r.write = function(file, rad, cb, o, DBG){
if(!rad){ cb('No radix!'); return }
o = ('object' == typeof o)? o : {force: o}; o = ('object' == typeof o)? o : {force: o};
var f = function Fractal(){}; var f = function Fractal(){}, a, b;
f.text = ''; f.text = '';
f.count = 0; f.file = file = rad.file || (rad.file = file);
f.file = file; if(!file){ cb('What file?'); return }
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.write = function(){ f.write = function(){
var tmp = ename(file); var text = rad.raw = f.text;
var S; LOG && (S = +new Date); r.disk[file = rad.file || f.file || file] = rad;
r.list.add(tmp, function(err){ var S = +new Date;
LOG && opt.log(S, +new Date - S, "wrote disk", tmp); DBG && (DBG.wd = S);
if(err){ return cb(err) } r.find.add(file, function add(err){
opt.store.put(tmp, f.text, cb); DBG && (DBG.wa = +new Date);
if(err){ cb(err); return }
opt.store.put(ename(file), text, function safe(err, ok){
DBG && (DBG.wp = +new Date);
console.STAT && console.STAT(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(){
var S = +new Date;
DBG && (DBG.wf = S);
f.text = '';
if(!f.count){ f.count = 0;
Radix.map(rad, function count(){ f.count++ }); // TODO: Perf? Any faster way to get total length?
}
DBG && (DBG.wfc = f.count);
f.limit = Math.ceil(f.count/2);
var SC = f.count;
f.count = 0;
DBG && (DBG.wf1 = +new Date);
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.
DBG && (DBG.wf2 = +new Date);
r.write(f.end, f.sub, f.both, o);
DBG && (DBG.wf3 = +new Date);
f.hub = Radix();
Radix.map(rad, f.stop);
DBG && (DBG.wf4 = +new Date);
r.write(rad.file, f.hub, f.both, o);
DBG && (DBG.wf5 = +new Date);
console.STAT && console.STAT(S, +new Date - S, "rad split", ename(rad.file), SC);
return true;
} }
f.slice = function(val, key){ f.slice = function(val, key){
f.sub(f.end = key, val); f.sub(f.end = key, val);
if(f.limit <= (++f.count)){ if(f.limit <= (++f.count)){ return true }
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);
} }
f.stop = function(val, key){ f.stop = function(val, key){
if(key >= f.end){ return true } 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){
DBG && (DBG.wfd = +new Date);
if(b){ cb(err || b); return }
if(a){ cb(err, ok); return }
a = true;
b = err;
}
f.each = function(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, DBG); return } // temporary testing idea
if(!Radix.map(rad, f.each, true)){ f.write() } 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, DBG){
var raw; var raw;
var S; LOG && (S = +new Date); var S = +new Date;
DBG && (DBG.w = S);
try{raw = JSON.stringify(rad.$); 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"); DBG && (DBG.ws = +new Date);
console.STAT && console.STAT(S, +new Date - S, "rad stringified JSON");
if(opt.chunk < raw.length && !o.force){ if(opt.chunk < raw.length && !o.force){
if(Radix.map(rad, f.each, true)){ return } var c = 0;
Radix.map(rad, function(){
if(c++){ return true } // more than 1 item
});
if(c > 1){
return f.split();
}
} }
f.text = raw; f.text = raw;
f.write(); f.write();
@ -264,132 +243,85 @@
if(u === o.start && u === o.end){ return tree } if(u === o.start && u === o.end){ return tree }
if(atomic(tree)){ return tree } if(atomic(tree)){ return tree }
var sub = Radix(); var sub = Radix();
Radix.map(tree, function(v,k){ Radix.map(tree, function(v,k){ sub(k,v) }, o); // ONLY PLACE THAT TAKES TREE, maybe reduce API for better perf?
sub(k,v);
}, o);
return sub(''); return sub('');
} }
;(function(){ ;(function(){
var Q = {}; r.read = function(key, cb, o, DBG){
r.read = function(key, cb, o){
o = o || {}; o = o || {};
if(RAD && !o.next){ // cache var g = {key: key};
var S; LOG && (S = +new Date); g.find = function(file){ var tmp;
var val = RAD(key); g.file = file || (file = opt.code.from);
LOG && opt.log(S, +new Date - S, 'rad cached'); DBG && (DBG = DBG[file] = DBG[file] || {});
//if(u !== val){ DBG && (DBG.rf = DBG.rf || +new Date);
//cb(u, val, o); if(tmp = r.disk[g.file = file]){ g.check(u, tmp); return }
if(atomic(val)){ cb(u, val, o); return } r.parse(file, g.check, u, DBG);
// 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? g.get = function(err, disk, info){
var g = function Get(){}; DBG && (DBG.rgl = +new Date);
g.lex = function(file){ var tmp; // // TODO: this had a out-of-memory crash! DBG && (DBG.rg = DBG.rg || +new Date);
file = (u === file)? u : decodeURIComponent(file); if(g.err = err || g.err){ cb(err); return }
tmp = o.next || key || (o.reverse? o.end || '\uffff' : o.start || ''); var file = g.file = (disk||'').file || g.file;
if(!file || (o.reverse? file < tmp : file > tmp)){ if(!disk && file !== opt.code.from){ // corrupt file?
LOG && opt.log(S, +new Date - S, 'rad read lex'); S = +new Date; r.find.bad(file); // remove from dir list
if(o.next || o.reverse){ g.file = file } r.read(key, cb, o); // try again
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
return; return;
} }
g.info = info; disk = r.disk[file] || (r.disk[file] = disk);
if(disk){ RAD = g.disk = disk } if(!disk){ cb(file === opt.code.from? u : "No file!"); return }
disk = Q[g.file]; delete Q[g.file]; disk.file || (disk.file = file);
LOG && opt.log(S, +new Date - S, 'rad read in, ack', disk.length); S = +new Date; var data = r.range(disk(key), o);
var STMP = disk.length; // TMP STATS! DELETE! DBG && (DBG.rr = +new Date);
map(disk, g.ack); o.unit = disk.unit;
LOG && opt.log(S, +new Date - S, 'rad read acked', STMP, JSON.stringify(g.file));
}
g.ack = function(as){
if(!as.ack){ return }
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);
o.parsed = (o.parsed || 0) + (info.parsed||0);
o.chunks = (o.chunks || 0) + 1; o.chunks = (o.chunks || 0) + 1;
o.more = true; o.parsed = (o.parsed || 0) + ((info||'').parsed||(o.chunks*opt.chunk));
if((!as.file) // if no more places to look o.more = 1;
|| (!o.span && last === key) // if our key exactly matches the very last atomic record o.next = u;
|| (!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. Radix.map(r.list, function next(v,f){
){ if(!v || file === f){ return }
o.more = u; o.next = f;
as.ack(g.err, data, o); return 1;
return }, o.reverse? {reverse: 1, end: file} : {start: file});
DBG && (DBG.rl = +new Date);
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){ if(!o.more){ cb(g.err, data, o); return }
if(data){ cb(g.err, data, o) }
if(o.parsed >= o.limit){ return }
var S = +new Date; var S = +new Date;
as.ack(g.err, data, o); // more might be coming! DBG && (DBG.rm = S);
LOG && opt.log(S, +new Date - S, 'rad range ack.'); // 1.4s var next = o.next;
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. timediate(function(){
} console.STAT && console.STAT(S, +new Date - S, 'rad more');
o.next = as.file; r.parse(next, g.check);
r.read(key, as.ack, o); },0);
} }
g.check = function(err, disk, info){ g.check = function(err, disk, info){
g.it(err, disk, info); g.get(err, disk, info);
var good = true; if(!disk || disk.check){ return } disk.check = 1;
var S = +new Date;
(info || (info = {})).file || (info.file = g.file);
Radix.map(disk, function(val, key){ Radix.map(disk, function(val, key){
// assume in memory for now, since both write/read already call r.list which will init it. // assume in memory for now, since both write/read already call r.find which will init it.
var go = function(file){ r.find(key, function(file){
if(info.file !== file){ if((file || (file = opt.code.from)) === info.file){ return }
good = false var id = Gun.text.random(3);
} puff(function(){
return true; r.save(key, val, function ack(err, ok){
} if(err){ r.save(key, val, ack); return } // ad infinitum???
go.reverse = 1; // TODO: NOTE!!! Mislocated data could be because of a synchronous `put` from the `g.get(` other than perf shouldn't we do the check first before acking?
go.end = key; console.STAT && console.STAT("MISLOCATED DATA CORRECTED", id, ename(key), ename(info.file), ename(file));
r.list(go);
}); });
if(good){ return } },0);
var id = Gun.text.random(3); opt.log("MISLOCATED DATA", id); })
r.save(disk, function ack(err, ok){
if(err){ return r.save(disk, ack) } // ad infinitum???
opt.log("MISLOCATED CORRECTED", id);
}); });
console.STAT && console.STAT(S, +new Date - S, "rad check");
} }
/*g.check2 = function(err, disk, info){ r.find(key, g.find);
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);
} }
function rev(a,b){ return b } function rev(a,b){ return b }
var revo = {reverse: true}; var revo = {reverse: true};
@ -403,18 +335,19 @@
with how much performance and scale we can get out of only one. 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. Then we can work on the harder problem of being multi-process.
*/ */
var RPC = 0;
var Q = {}, s = String.fromCharCode(31); var Q = {}, s = String.fromCharCode(31);
r.parse = function(file, cb, raw){ var q; r.parse = function(file, cb, raw, DBG){ var q;
if(q = Q[file]){ return q.push(cb) } q = Q[file] = [cb]; if(!file){ return cb(); }
var p = function Parse(){}, info = {file: ename(file)}; if(q = Q[file]){ q.push(cb); return } q = Q[file] = [cb];
p.disk = Radix(); var p = function Parse(){}, info = {file: file};
(p.disk = Radix()).file = file;
p.read = function(err, data){ var tmp; p.read = function(err, data){ var tmp;
LOG && opt.log(S, +new Date - S, 'read disk', ename(file)); DBG && (DBG.rpg = +new Date);
console.STAT && console.STAT(S, +new Date - S, 'read disk', JSON.stringify(file), ++RPC, 'total all parses.');
delete Q[file]; delete Q[file];
if((p.err = err) || (p.not = !data)){ if((p.err = err) || (p.not = !data)){ p.map(q, p.ack); return }
return map(q, p.ack); if('string' !== typeof data){
}
if(typeof data !== 'string'){
try{ try{
if(opt.pack <= data.length){ if(opt.pack <= data.length){
p.err = "Chunk too big!"; p.err = "Chunk too big!";
@ -422,29 +355,55 @@
data = data.toString(); // If it crashes, it crashes here. How!?? We check size first! data = data.toString(); // If it crashes, it crashes here. How!?? We check size first!
} }
}catch(e){ p.err = e } }catch(e){ p.err = e }
if(p.err){ return map(q, p.ack) } if(p.err){ p.map(q, p.ack); return }
} }
info.parsed = data.length; info.parsed = data.length;
DBG && (DBG.rpl = info.parsed);
LOG && (S = +new Date); DBG && (DBG.rpa = q.length);
if(opt.jsonify || '{' === data[0]){ // temporary testing idea S = +new Date;
if(opt.jsonify || '{' === data[0]){
try{ try{
var json = JSON.parse(data); // TODO: this caused a out-of-memory crash! var json = JSON.parse(data); // TODO: this caused a out-of-memory crash!
p.disk.$ = json; p.disk.$ = json;
LOG && opt.log(S, +new Date - S, 'rad parsed JSON'); console.STAT && (ST = +new Date - S) > 9 && console.STAT(S, ST, 'rad parsed JSON');
map(q, p.ack); DBG && (DBG.rpd = +new Date);
p.map(q, p.ack); // hmmm, v8 profiler can't see into this cause of try/catch?
return; return;
}catch(e){ tmp = e } }catch(e){ tmp = e }
if('{' === data[0]){ if('{' === data[0]){
p.err = tmp || "JSON error!"; p.err = tmp || "JSON error!";
return map(q, p.ack); p.map(q, p.ack);
return;
} }
} }
LOG && (S = +new Date); p.radec(err, data);
}
p.map = function(){
if(!q || !q.length){ return }
//var i = 0, l = q.length, ack;
var S = +new Date;
var err = p.err, data = p.not? u : p.disk;
var i = 0, ack; while(i < 9 && (ack = q[i++])){ ack(err, data, info) } // too much?
console.STAT && console.STAT(S, +new Date - S, 'rad packs', ename(file));
console.STAT && console.STAT(S, i, 'rad packs #', ename(file));
if(!(q = q.slice(i)).length){ return }
puff(p.map, 0);
}
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){
S = +new Date;
var tmp = p.split(data), pre = [], i, k, v; var tmp = p.split(data), pre = [], i, k, v;
if(!tmp || 0 !== tmp[1]){ if(!tmp || 0 !== tmp[1]){
p.err = "File '"+file+"' does not have root radix! "; p.err = "File '"+file+"' does not have root radix! ";
return map(q, p.ack); p.map(q, p.ack);
return;
} }
while(tmp){ while(tmp){
k = v = u; k = v = u;
@ -463,9 +422,8 @@
if(u !== k && u !== v){ p.disk(pre.join(''), v) } if(u !== k && u !== v){ p.disk(pre.join(''), v) }
tmp = p.split(tmp[2]); tmp = p.split(tmp[2]);
} }
LOG && opt.log(S, +new Date - S, 'parsed RAD'); console.STAT && console.STAT(S, +new Date - S, 'parsed RAD');
//cb(err, p.disk); p.map(q, p.ack);
map(q, p.ack);
}; };
p.split = function(t){ p.split = function(t){
if(!t){ return } if(!t){ return }
@ -478,86 +436,76 @@
l[2] = t.slice(i + o.i); l[2] = t.slice(i + o.i);
return l; return l;
} }
p.ack = function(cb){ if(r.disk){ raw || (raw = (r.disk[file]||'').raw) }
if(!cb){ return } var S = +new Date, SM, SL;
if(p.err || p.not){ return cb(p.err, u, info) } DBG && (DBG.rp = S);
cb(u, p.disk, info); if(raw){ return puff(function(){ p.read(u, raw) }, 0) }
}
var S; LOG && (S = +new Date);
if(raw){ return p.read(null, raw) }
opt.store.get(ename(file), p.read); opt.store.get(ename(file), p.read);
// TODO: What if memory disk gets filled with updates, and we get an old one back?
} }
}()); }());
;(function(){ ;(function(){
var dir, q, f = String.fromCharCode(28), ef = ename(f); var dir, f = String.fromCharCode(28), Q;
r.list = function(cb){ r.find = function(key, cb){
if(dir){ if(!dir){
var last, tmp = {reverse: (cb.reverse)? 1 : 0, start: cb.start, end: cb.end}; if(Q){ Q.push([key, cb]); return } Q = [[key, cb]];
Radix.map(dir, function(val, key){ r.parse(f, init);
if(!val){ return }
return cb(last = key);
}, tmp) || cb(u, last);
return; return;
} }
if(q){ return q.push(cb) } q = [cb]; Radix.map(r.list = dir, function(val, key){
r.parse(f, r.list.init); 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); var has = dir(file);
if(has || file === ef){ if(has || file === f){ cb(u, 1); return }
return cb(u, 1);
}
dir(file, 1); dir(file, 1);
cb.listed = (cb.listed || 0) + 1; cb.found = (cb.found || 0) + 1;
r.write(f, dir, function(err, ok){ r.write(f, dir, function(err, ok){
if(err){ return cb(err) } if(err){ cb(err); return }
cb.listed = (cb.listed || 0) - 1; cb.found = (cb.found || 0) - 1;
if(cb.listed !== 0){ return } if(0 !== cb.found){ return }
cb(u, 1); cb(u, 1);
}, true); }, true);
} }
r.list.bad = function(file, cb){ r.find.bad = function(file, cb){
dir(ename(file), 0); dir(file, 0);
r.write(f, dir, cb||noop); r.write(f, dir, cb||noop);
} }
r.list.init = function(err, disk){ function init(err, disk){
if(err){ if(err){
opt.log('list', err); opt.log('list', err);
setTimeout(function(){ r.parse(f, r.list.init) }, 1000); setTimeout(function(){ r.parse(f, init) }, 1000);
return;
}
if(disk){
r.list.drain(disk);
return;
}
if(!opt.store.list){
r.list.drain(Radix());
return; return;
} }
if(disk){ drain(disk); return }
dir = dir || disk || Radix();
if(!opt.store.list){ drain(dir); return }
// import directory. // import directory.
opt.store.list(function(file){ opt.store.list(function(file){
dir = dir || Radix(); if(!file){ drain(dir); return }
if(!file){ return r.list.drain(dir) } r.find.add(file, noop);
r.list.add(file, noop);
}); });
} }
r.list.drain = function(rad, tmp){ function drain(rad, tmp){
r.list.dir = dir = rad; dir = dir || rad;
tmp = q; q = null; dir.file = f;
Gun.list.map(tmp, function(cb){ tmp = Q; Q = null;
r.list(cb); Gun.list.map(tmp, function(arg){
r.find(arg[0], arg[1]);
}); });
} }
}()); }());
try{ !Gun.window && require('./radmigtmp')(r) }catch(e){}
var noop = function(){}, RAD, u; var noop = function(){}, RAD, u;
Radisk.has[opt.file] = r; Radisk.has[opt.file] = r;
return r; return r;
} }
;(function(){ ;(function(){
var _ = String.fromCharCode(31), u; var _ = String.fromCharCode(31), u;
Radisk.encode = function(d, o, s){ s = s || _; Radisk.encode = function(d, o, s){ s = s || _;

View File

@ -2,13 +2,14 @@
function Radix(){ function Radix(){
var radix = function(key, val, t){ var radix = function(key, val, t){
key = ''+key; radix.unit = 0;
if(!t && u !== val){ if(!t && u !== val){
radix.last = (key < radix.last)? radix.last : key; radix.last = (''+key < radix.last)? radix.last : ''+key;
delete (radix.$||{})[_]; delete (radix.$||{})[_];
} }
t = t || radix.$ || (radix.$ = {}); t = t || radix.$ || (radix.$ = {});
if(!key && Object.keys(t).length){ return t } if(!key && Object.keys(t).length){ return t }
key = ''+key;
var i = 0, l = key.length-1, k = key[i], at, tmp; var i = 0, l = key.length-1, k = key[i], at, tmp;
while(!(at = t[k]) && i < l){ while(!(at = t[k]) && i < l){
k += key[++i]; k += key[++i];
@ -22,52 +23,61 @@
if(kk){ if(kk){
if(u === val){ if(u === val){
if(ii <= l){ return } if(ii <= l){ return }
return (tmp || (tmp = {}))[s.slice(ii)] = r; (tmp || (tmp = {}))[s.slice(ii)] = r;
//(tmp[_] = function $(){ $.sort = Object.keys(tmp).sort(); return $ }()); // get rid of this one, cause it is on read?
return r;
} }
var __ = {}; var __ = {};
__[s.slice(ii)] = r; __[s.slice(ii)] = r;
ii = key.slice(ii); ii = key.slice(ii);
('' === ii)? (__[''] = val) : ((__[ii] = {})[''] = val); ('' === ii)? (__[''] = val) : ((__[ii] = {})[''] = val);
//(__[_] = function $(){ $.sort = Object.keys(__).sort(); return $ }());
t[kk] = __; t[kk] = __;
delete t[s]; delete t[s];
//(t[_] = function $(){ $.sort = Object.keys(t).sort(); return $ }());
return true; return true;
} }
})){ })){
if(u === val){ return; } if(u === val){ return; }
(t[k] || (t[k] = {}))[''] = val; (t[k] || (t[k] = {}))[''] = val;
//(t[_] = function $(){ $.sort = Object.keys(t).sort(); return $ }());
} }
if(u === val){ if(u === val){
return tmp; return tmp;
} }
} else } else
if(i == l){ if(i == l){
if(u === val){ return (u === (tmp = at['']))? at : tmp } //if(u === val){ return (u === (tmp = at['']))? at : tmp } // THIS CODE IS CORRECT, below is
if(u === val){ return (u === (tmp = at['']))? at : ((radix.unit = 1) && tmp) } // temporary help??
at[''] = val; at[''] = val;
//(at[_] = function $(){ $.sort = Object.keys(at).sort(); return $ }());
} else { } else {
if(u !== val){ delete at[_] } if(u !== val){ delete at[_] }
//at && (at[_] = function $(){ $.sort = Object.keys(at).sort(); return $ }());
return radix(key.slice(++i), val, at || (at = {})); return radix(key.slice(++i), val, at || (at = {}));
} }
} }
return radix; return radix;
}; };
Radix.map = function map(radix, cb, opt, pre){ pre = pre || []; Radix.map = function rap(radix, cb, opt, pre){ pre = pre || []; // TODO: BUG: most out-of-memory crashes come from here.
var t = ('function' == typeof radix)? radix.$ || {} : radix; var t = ('function' == typeof radix)? radix.$ || {} : radix;
//!opt && console.log("WHAT IS T?", JSON.stringify(t).length);
if(!t){ return } if(!t){ return }
var keys = (t[_]||no).sort || (t[_] = function $(){ $.sort = Object.keys(t).sort(); return $ }()).sort, rev; var keys = (t[_]||no).sort || (t[_] = function $(){ $.sort = Object.keys(t).sort(); return $ }()).sort, rev; // ONLY 17% of ops are pre-sorted!
//var keys = Object.keys(t).sort(); //var keys = Object.keys(t).sort();
opt = (true === opt)? {branch: true} : (opt || {}); opt = (true === opt)? {branch: true} : (opt || {});
if(rev = opt.reverse){ keys = keys.slice().reverse() } if(rev = opt.reverse){ keys = keys.slice(0).reverse() }
var start = opt.start, end = opt.end, END = '\uffff'; var start = opt.start, end = opt.end, END = '\uffff';
var i = 0, l = keys.length; var i = 0, l = keys.length;
for(;i < l; i++){ var key = keys[i], tree = t[key], tmp, p, pt; for(;i < l; i++){ var key = keys[i], tree = t[key], tmp, p, pt;
if(!tree || '' === key || _ === key){ continue } if(!tree || '' === key || _ === key){ continue }
p = pre.slice(); p.push(key); p = pre.slice(0); p.push(key);
pt = p.join(''); pt = p.join('');
if(u !== start && pt < (start||'').slice(0,pt.length)){ continue } if(u !== start && pt < (start||'').slice(0,pt.length)){ continue }
if(u !== end && (end || END) < pt){ continue } if(u !== end && (end || END) < pt){ continue }
if(rev){ // children must be checked first when going in reverse. if(rev){ // children must be checked first when going in reverse.
tmp = map(tree, cb, opt, p); tmp = rap(tree, cb, opt, p);
if(u !== tmp){ return tmp } if(u !== tmp){ return tmp }
} }
if(u !== (tmp = tree[''])){ if(u !== (tmp = tree[''])){
@ -85,7 +95,7 @@
} }
pre = p; pre = p;
if(!rev){ if(!rev){
tmp = map(tree, cb, opt, pre); tmp = rap(tree, cb, opt, pre);
if(u !== tmp){ return tmp } if(u !== tmp){ return tmp }
} }
pre.pop(); pre.pop();

22
lib/radmigtmp.js Normal file
View File

@ -0,0 +1,22 @@
module.exports = function(r){
var Radix = require('./radix');
r.find('a', function(){
var l = [];
Radix.map(r.list, function(v,f){
if(!(f.indexOf('%1B') + 1)){ return }
if(!v){ return }
l.push([f,v]);
});
if(l.length){
console.log("\n! ! ! WARNING ! ! !\nRAD v0.2020.x has detected OLD v0.2019.x data & automatically migrating. Automatic migration will be turned OFF in future versions! If you are just developing/testing, we recommend you reset your data. Please contact us if you have any concerns.\nThis message should only log once.")
}
var f, v;
l.forEach(function(a){
f = a[0]; v = a[1];
r.list(decodeURIComponent(f), v);
r.list(f, 0);
});
if(!f){ return }
r.find.bad(f);
})
};

View File

@ -10,22 +10,27 @@ function Store(opt){
return Store[opt.file]; return Store[opt.file];
} }
Store[opt.file] = store; Store[opt.file] = store;
var puts = {};
// TODO!!! ADD ZLIB INFLATE / DEFLATE COMPRESSION!
store.put = function(file, data, cb){ store.put = function(file, data, cb){
puts[file] = data;
var random = Math.random().toString(36).slice(-3); var random = Math.random().toString(36).slice(-3);
var tmp = opt.file+'-'+file+'-'+random+'.tmp'; var tmp = opt.file+'-'+file+'-'+random+'.tmp';
fs.writeFile(tmp, data, function(err, ok){ fs.writeFile(tmp, data, function(err, ok){
delete puts[file];
if(err){ return cb(err) } if(err){ return cb(err) }
move(tmp, opt.file+'/'+file, cb); move(tmp, opt.file+'/'+file, cb);
}); });
}; };
store.get = function(file, cb){ store.get = function(file, cb){ var tmp; // this took 3s+?
if(tmp = puts[file]){ cb(u, tmp); return }
fs.readFile(opt.file+'/'+file, function(err, data){ fs.readFile(opt.file+'/'+file, function(err, data){
if(err){ if(err){
if('ENOENT' === (err.code||'').toUpperCase()){ if('ENOENT' === (err.code||'').toUpperCase()){
return cb(null); return cb();
} }
opt.log("ERROR:", err) opt.log("ERROR:", err);
} }
cb(err, data); cb(err, data);
}); });

23
lib/rfsmix.js Normal file
View File

@ -0,0 +1,23 @@
module.exports = function(opt, store){
var rfs = require('./rfs')(opt);
var p = store.put;
var g = store.get;
store.put = function(file, data, cb){
var a, b, c = function(err, ok){
if(b){ return cb(err || b) }
if(a){ return cb(err, ok) }
a = true;
b = err;
}
p(file, data, c); // parallel
rfs.put(file, data, c); // parallel
}
store.get = function(file, cb){
rfs.get(file, function(err, data){
//console.log("rfs3 hijacked", file);
if(data){ return cb(err, data) }
g(file, cb);
});
}
return store;
}

View File

@ -52,24 +52,26 @@ function Store(opt){
store.put = function(file, data, cb){ store.put = function(file, data, cb){
var params = {Bucket: opts.bucket, Key: file, Body: data}; var params = {Bucket: opts.bucket, Key: file, Body: data};
//console.log("RS3 PUT ---->", (data||"").slice(0,20)); //console.log("RS3 PUT ---->", (data||"").slice(0,20));
Gun.obj.del(c.g, file); c.p[file] = data;
Gun.obj.del(c.l, 1); delete c.g[file];//Gun.obj.del(c.g, file);
s3.putObject(params, cb); delete c.l[1];//Gun.obj.del(c.l, 1);
s3.putObject(params, function(err, ok){
delete c.p[file];
cb(err, 's3');
});
}; };
store.get = function(file, cb){ store.get = function(file, cb){ var tmp;
if(c.g[file]){ return c.g[file].push(cb) } if(tmp = c.p[file]){ cb(u, tmp); return }
if(tmp = c.g[file]){ tmp.push(cb); return }
var cbs = c.g[file] = [cb]; var cbs = c.g[file] = [cb];
var params = {Bucket: opts.bucket, Key: file||''}; var params = {Bucket: opts.bucket, Key: file||''};
//console.log("RS3 GET ---->", file); //console.log("RS3 GET ---->", file);
s3.getObject(params, function(err, ack){ s3.getObject(params, function got(err, ack){
if(err && 'NoSuchKey' === err.code){ err = u }
//console.log("RS3 GOT <----", err, file, cbs.length, ((ack||{}).Body||'').toString().slice(0,20)); //console.log("RS3 GOT <----", err, file, cbs.length, ((ack||{}).Body||'').toString().slice(0,20));
Gun.obj.del(c.g, file); delete c.g[file];//Gun.obj.del(c.g, file);
var data, cbe = function(cb){ var data, data = (ack||'').Body;
if(!ack){ cb(null); return; } var i = 0, cba; while(cba = cbs[i++]){ cba && cba(err, data) }//Gun.obj.map(cbs, cbe);
cb(err, data);
};
data = (ack||{}).Body; //if(data = (ack||{}).Body){ data = data.toString() }
Gun.obj.map(cbs, cbe);
}); });
}; };
store.list = function(cb, match, params, cbs){ store.list = function(cb, match, params, cbs){
@ -98,6 +100,7 @@ function Store(opt){
}); });
}; };
//store.list(function(){ return true }); //store.list(function(){ return true });
require('./rfsmix')(opt, store); // ugly, but gotta move fast for now.
return store; return store;
} }

View File

@ -8,10 +8,10 @@ function CDN(dir){
req.url = (req.url||'').replace(dot,'').replace(slash,'/'); req.url = (req.url||'').replace(dot,'').replace(slash,'/');
if(serve(req, res)){ return } // filters GUN requests! if(serve(req, res)){ return } // filters GUN requests!
fs.createReadStream(path.join(dir, req.url)).on('error',function(tmp){ // static files! fs.createReadStream(path.join(dir, req.url)).on('error',function(tmp){ // static files!
try{ tmp = fs.readFileSync(path.join(dir, 'index.html')) }catch(e){} fs.readFile(path.join(dir, 'index.html'), function(err, tmp){
try{ res.writeHead(200, {'Content-Type': 'text/html'}); try{ res.writeHead(200, {'Content-Type': 'text/html'});
res.end(tmp+''); }catch(e){} // or default to index res.end(tmp+''); }catch(e){} // or default to index
}).pipe(res); // stream })}).pipe(res); // stream
} }
} }
@ -27,13 +27,17 @@ function serve(req, res, next){ var tmp;
return true; return true;
} }
if(0 <= req.url.indexOf('gun/')){ if(0 <= req.url.indexOf('gun/')){
res.writeHead(200, {'Content-Type': 'text/javascript'}); var path = __dirname + '/../' + req.url.split('/').slice(2).join('/');
var path = __dirname + '/../' + req.url.split('/').slice(2).join('/'), file; if('/' === path.slice(-1)){
try{file = require('fs').readFileSync(path)}catch(e){} fs.readdir(path, function(err, dir){ res.end((dir || (err && 404))+'') });
if(file){
res.end(file);
return true; return true;
} }
var S = +new Date;
var rs = fs.createReadStream(path);
rs.on('open', function(){ console.STAT && console.STAT(S, +new Date - S, 'serve file open'); rs.pipe(res) });
rs.on('error', function(err){ res.end(404+'') });
rs.on('end', function(){ console.STAT && console.STAT(S, +new Date - S, 'serve file end') });
return true;
} }
if((tmp = req.socket) && (tmp = tmp.server) && (tmp = tmp.route)){ var url; if((tmp = req.socket) && (tmp = tmp.server) && (tmp = tmp.route)){ var url;
if(tmp = tmp[(((req.url||'').slice(1)).split('/')[0]||'').split('.')[0]]){ if(tmp = tmp[(((req.url||'').slice(1)).split('/')[0]||'').split('.')[0]]){

View File

@ -2,7 +2,7 @@
var Gun = require('../gun'), u; var Gun = require('../gun'), u;
Gun.serve = require('./serve'); Gun.serve = require('./serve');
//process.env.GUN_ENV = process.env.GUN_ENV || 'debug'; //process.env.GUN_ENV = process.env.GUN_ENV || 'debug';
console.LOG = true; // only do this for dev. //console.LOG = {}; // only do this for dev.
Gun.on('opt', function(root){ Gun.on('opt', function(root){
if(u === root.opt.super){ root.opt.super = true } if(u === root.opt.super){ root.opt.super = true }
if(u === root.opt.faith){ root.opt.faith = true } // HNPERF: This should probably be off, but we're testing performance improvements, please audit. if(u === root.opt.faith){ root.opt.faith = true } // HNPERF: This should probably be off, but we're testing performance improvements, please audit.
@ -15,10 +15,9 @@
require('./wire'); require('./wire');
try{require('../sea');}catch(e){} try{require('../sea');}catch(e){}
try{require('../axe');}catch(e){} try{require('../axe');}catch(e){}
require('./file'); //require('./file');
require('./evict'); require('./evict');
require('./multicast'); require('./multicast');
require('./stats'); require('./stats');
if('debug' === process.env.GUN_ENV){ require('./debug') }
module.exports = Gun; module.exports = Gun;
}()); }());

View File

@ -26,10 +26,13 @@ Gun.on('opt', function(root){
root.stats.up.count = (root.stats.up.count || 0) + 1; root.stats.up.count = (root.stats.up.count || 0) + 1;
root.stats.stay = root.stats.stay || {}; root.stats.stay = root.stats.stay || {};
root.stats.gap = {}; root.stats.gap = {};
root.stats.over = +new Date;
},1); },1);
setInterval(function(){ setInterval(function(){
if(!root.stats){ root.stats = {} } if(!root.stats){ root.stats = {} }
var S = +new Date;
var stats = root.stats, tmp; var stats = root.stats, tmp;
stats.over = S - (stats.over||S);
(stats.up||{}).time = process.uptime(); (stats.up||{}).time = process.uptime();
stats.memory = process.memoryUsage() || {}; stats.memory = process.memoryUsage() || {};
stats.memory.totalmem = os.totalmem(); stats.memory.totalmem = os.totalmem();
@ -43,18 +46,19 @@ Gun.on('opt', function(root){
stats.all = all; stats.all = all;
var dam = root.opt.mesh; var dam = root.opt.mesh;
if(dam){ if(dam){
stats.dam = {'in': {count: dam.hear.c, done: dam.hear.d, long: dam.hear.long}, 'out': {count: dam.say.c, done: dam.say.d}}; stats.dam = {'in': {count: dam.hear.c, done: dam.hear.d}, 'out': {count: dam.say.c, done: dam.say.d}};
dam.hear.c = dam.hear.d = dam.say.c = dam.say.d = 0; // reset dam.hear.c = dam.hear.d = dam.say.c = dam.say.d = 0; // reset
stats.peers.time = dam.bye.time || 0; stats.peers.time = dam.bye.time || 0;
dam.hear.long = [];
} }
var rad = root.opt.store; rad = rad && rad.stats; var rad = root.opt.store; rad = rad && rad.stats;
if(rad){ if(rad){
stats.rad = rad; stats.rad = rad;
root.opt.store.stats = {get:{time:{}, count:0}, put: {time:{}, count:0}}; // reset root.opt.store.stats = {get:{time:{}, count:0}, put: {time:{}, count:0}}; // reset
} }
console.STAT && console.STAT(S, +new Date - S, 'stats');
fs.writeFile(__dirname+'/../stats.'+root.opt.file, JSON.stringify(stats, null, 2), function(err){}); S = +new Date;
fs.writeFile(__dirname+'/../stats.'+root.opt.file, JSON.stringify(stats, null, 2), function(err){ console.STAT && console.STAT(S, +new Date - S, 'stats stash') });
stats.over = S;
stats.gap = {}; stats.gap = {};
}, 1000 * 15); }, 1000 * 15);
Object.keys = Object.keys || function(o){ return Gun.obj.map(o, function(v,k,t){t(k)}) } Object.keys = Object.keys || function(o){ return Gun.obj.map(o, function(v,k,t){t(k)}) }
@ -62,11 +66,12 @@ Gun.on('opt', function(root){
var log = Gun.log, all = {}, max = 1000; var log = Gun.log, all = {}, max = 1000;
Gun.log = function(a,b,c,d){ Gun.log = console.STAT = function(a,b,c,d){
if('number' == typeof a && 'number' == typeof b && 'string' == typeof c){ if('number' == typeof a && 'number' == typeof b && 'string' == typeof c){
var tmp = (all[c] || (all[c] = [])); var tmp = (all[c] || (all[c] = []));
if(max < tmp.push([a,b])){ all[c] = [] } // reset if(max < tmp.push([a,b])){ all[c] = [] } // reset
//return; //return;
} }
if(!console.LOG || log.off){ return }
return log.apply(Gun, arguments); return log.apply(Gun, arguments);
} }

View File

@ -7,51 +7,32 @@ Gun.on('create', function(root){
if(false === opt.radisk){ return } if(false === opt.radisk){ return }
var Radisk = (Gun.window && Gun.window.Radisk) || require('./radisk'); var Radisk = (Gun.window && Gun.window.Radisk) || require('./radisk');
var Radix = Radisk.Radix; var Radix = Radisk.Radix;
var LOG = console.LOG; var ST = 0;
opt.store = opt.store || (!Gun.window && require('./rfs')(opt)); opt.store = opt.store || (!Gun.window && require('./rfs')(opt));
var rad = Radisk(opt), esc = String.fromCharCode(27); var dare = Radisk(opt), esc = String.fromCharCode(27);
root.on('put', function(msg){ root.on('put', function(msg){
this.to.next(msg); this.to.next(msg);
var id = msg['#'] || Gun.text.random(3), track = !msg['@'], acks = track? 0 : u; // only ack non-acks. if((msg._||'').rad){ return } // don't save what just came from a read.
var got = (msg._||empty).rad, now = Gun.state(); var id = msg['#'], put = msg.put, soul = put['#'], key = put['.'], val = put[':'], state = put['>'], tmp;
var S = (+new Date); // STATS! var DBG = (msg._||'').DBG; DBG && (DBG.sp = DBG.sp || +new Date);
Gun.graph.is(msg.put, null, function(val, key, node, soul){ var lot = (msg._||'').lot||''; count[id] = (count[id] || 0) + 1;
if(!track && got){ var S = (msg._||'').RPS || ((msg._||'').RPS = +new Date);
var at = (root.next||empty)[soul]; dare(soul+esc+key, {':': val, '>': state}, dare.one[id] || function(err, ok){
if(!at){ return } DBG && (DBG.spd = DBG.spd || +new Date);
if(u !== got['.']){ at = (at.next||empty)[key] } console.STAT && console.STAT(S, +new Date - S, 'put');
if(!at){ return } if(!err && count[id] !== lot.s){ console.log(err = "Disk count not same as ram count."); console.STAT && console.STAT(+new Date, lot.s - count[id], 'put ack != count') } delete count[id];
at.rad = now; if(err){ root.on('in', {'@': id, err: err, DBG: DBG}); return }
return; root.on('in', {'@': id, ok: ok, DBG: DBG});
} }, id, DBG && (DBG.r = DBG.r || {}));
if(track){ ++acks } DBG && (DBG.sps = DBG.sps || +new Date);
//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));
});
//LOG && Gun.log(S, +new Date - S, 'put loop');
function ack(err, ok){
acks--;
if(ack.err){ return }
if(ack.err = err){
//Gun.log(); //try{opt.store.stats.put.err = err}catch(e){} // STATS!
root.on('in', {'@': id, err: err});
return;
}
if(acks){ return }
try{opt.store.stats.put.time[statp % 50] = (+new Date) - S; ++statp;
opt.store.stats.put.count++;
}catch(e){} // STATS!
//console.log(+new Date - S, 'put'); S = +new Date;
root.on('in', {'@': id, ok: 1});
//console.log(S, +new Date - S, 'put sent');
}
}); });
var count = {}, obj_empty = Gun.obj.empty;
root.on('get', function(msg){ root.on('get', function(msg){
this.to.next(msg); this.to.next(msg);
var ctx = msg._||'', DBG = ctx.DBG = msg.DBG; DBG && (DBG.sg = +new Date);
var id = msg['#'], get = msg.get, soul = msg.get['#'], has = msg.get['.']||'', o = {}, graph, lex, key, tmp, force; var id = msg['#'], get = msg.get, soul = msg.get['#'], has = msg.get['.']||'', o = {}, graph, lex, key, tmp, force;
if('string' == typeof soul){ if('string' == typeof soul){
key = soul; key = soul;
@ -78,60 +59,86 @@ Gun.on('create', function(root){
o.limit = (tmp <= (o.pack || (1000 * 100)))? tmp : 1; o.limit = (tmp <= (o.pack || (1000 * 100)))? tmp : 1;
} }
if(has['-'] || (soul||{})['-']){ o.reverse = true } if(has['-'] || (soul||{})['-']){ o.reverse = true }
if((tmp = (root.next||empty)[soul]) && tmp.put){ if((tmp = (root.next||'')[soul]) && tmp.put){
var SPUT = tmp.put;
if(o.atom){ if(o.atom){
tmp = (tmp.next||empty)[o.atom] ; tmp = (tmp.next||'')[o.atom] ;
if(tmp && tmp.rad){ if(tmp && tmp.rad){ return }
LOG && Gun.log("still cached atom", JSON.stringify(get), Object.keys(SPUT||{}).length);
return;
}
} else } else
if(tmp && tmp.rad){ if(tmp && tmp.rad){ return }
LOG && Gun.log("still cached", JSON.stringify(get), Object.keys(SPUT||{}).length);
return;
} }
} var now = Gun.state();
var S = (+new Date), C = 0, CC = 0; // STATS! var S = (+new Date), C = 0, SPT = 0; // STATS!
rad(key||'', function(err, data, o){ DBG && (DBG.sgm = S);
dare(key||'', function(err, data, info){
DBG && (DBG.sgr = +new Date);
DBG && (DBG.sgi = info);
try{opt.store.stats.get.time[statg % 50] = (+new Date) - S; ++statg; try{opt.store.stats.get.time[statg % 50] = (+new Date) - S; ++statg;
opt.store.stats.get.count++; opt.store.stats.get.count++;
if(err){ opt.store.stats.get.err = err } if(err){ opt.store.stats.get.err = err }
}catch(e){} // STATS! }catch(e){} // STATS!
//if(u === data && o.chunks > 1){ return } // if we already sent a chunk, ignore ending empty responses. // this causes tests to fail. //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'); S = +new Date; console.STAT && console.STAT(S, +new Date - S, 'got', JSON.stringify(key)); S = +new Date;
if(data){ info = info || '';
var va, ve;
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;
// REMEMBER TO ADD _rad TO NODE/SOUL QUERY!
} else
if(data){ // old code path
if(typeof data !== 'string'){ if(typeof data !== 'string'){
if(o.atom){ if(o.atom){
data = u; data = u;
} else { } else {
Radix.map(data, each) Radix.map(data, each); // IS A RADIX TREE, NOT FUNCTION!
} }
} }
if(!graph && data){ each(data, '') } if(!graph && data){ each(data, '') }
// TODO: !has what about soul lookups?
if(!o.atom && !has & 'string' == typeof soul && !o.limit && !o.more){
root.$.get(soul)._.rad = now;
} }
LOG && Gun.log(S, +new Date - S, 'got prep count', CC, C); S = +new Date; CC = 0; }
DBG && (DBG.sgp = +new Date);
// TODO: PERF NOTES! This is like 0.2s, but for each ack, or all? Can you cache these preps?
// TODO: PERF NOTES! This is like 0.2s, but for each ack, or all? Can you cache these preps?
// TODO: PERF NOTES! This is like 0.2s, but for each ack, or all? Can you cache these preps?
// TODO: PERF NOTES! This is like 0.2s, but for each ack, or all? Can you cache these preps?
// TODO: PERF NOTES! This is like 0.2s, but for each ack, or all? Can you cache these preps?
// Or benchmark by reusing first start date.
if(console.STAT && (ST = +new Date - S) > 9){ console.STAT(S, ST, 'got prep time'); console.STAT(S, C, 'got prep #') } SPT += ST; C = 0; S = +new Date;
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. 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, '%': o.more? 1 : u, err: err? err : u, _: faith}); root.on('in', {'@': id, put: graph, '%': info.more? 1 : u, err: err? err : u, _: faith, DBG: DBG});
LOG && Gun.log(S, +new Date - S, 'got sent nodes in graph', Object.keys(graph||{}).length); console.STAT && (ST = +new Date - S) > 9 && console.STAT(S, ST, 'got emit', Object.keys(graph||{}).length);
graph = u; // each is outside our scope, we have to reset graph to nothing! graph = u; // each is outside our scope, we have to reset graph to nothing!
}, o); }, o, DBG && (DBG.r = DBG.r || {}));
LOG && Gun.log(S, +new Date - S, 'get call'); DBG && (DBG.sgd = +new Date);
console.STAT && (ST = +new Date - S) > 9 && console.STAT(S, ST, 'get call'); // TODO: Perf: this was half a second??????
function each(val, has, a,b){ // TODO: THIS CODE NEEDS TO BE FASTER!!!! function each(val, has, a,b){ // TODO: THIS CODE NEEDS TO BE FASTER!!!!
C++; ++CC; C++;
if(!val){ return } if(!val){ return }
has = (key+has).split(esc); has = (key+has).split(esc);
var soul = has.slice(0,1)[0]; var soul = has.slice(0,1)[0];
has = has.slice(-1)[0]; has = has.slice(-1)[0];
if(o.limit && o.limit <= o.count){ return true }
var va, ve, so = soul, ha = has;
//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;
}
o.count = (o.count || 0) + val.length; o.count = (o.count || 0) + val.length;
var tmp = val.lastIndexOf('>'); var tmp = val.lastIndexOf('>');
var state = Radisk.decode(val.slice(tmp+1), null, esc); var state = Radisk.decode(val.slice(tmp+1), null, esc);
val = Radisk.decode(val.slice(0,tmp), null, esc); val = Radisk.decode(val.slice(0,tmp), null, esc);
(graph = graph || {})[soul] = Gun.state.ify(graph[soul], has, state, val, soul); (graph = graph || {})[soul] = Gun.state.ify(graph[soul], has, state, val, soul);
if(o.limit && o.limit <= o.count){ return true }
} }
LOG = console.LOG;
}); });
var val_is = Gun.val.is
opt.store.stats = {get:{time:{}, count:0}, put: {time:{}, count:0}}; // STATS! opt.store.stats = {get:{time:{}, count:0}, put: {time:{}, count:0}}; // STATS!
var statg = 0, statp = 0; // STATS! var statg = 0, statp = 0; // STATS!
}); });

View File

@ -78,7 +78,7 @@ Gun.on('opt', function(root){
opt.mesh.bye(peer); opt.mesh.bye(peer);
}); });
wire.on('error', function(e){}); wire.on('error', function(e){});
setTimeout(function heart(){ if(!opt.peers[peer.id]){ return } try{ wire.send("[]"); setTimeout(heart, 1000 * 20) }catch(e){} }, 1000 * 20); // Some systems, like Heroku, require heartbeats to not time out. // TODO: Make this configurable? setTimeout(function heart(){ if(!opt.peers[peer.id]){ return } try{ wire.send("[]") }catch(e){} ;setTimeout(heart, 1000 * 20) }, 1000 * 20); // Some systems, like Heroku, require heartbeats to not time out. // TODO: Make this configurable? // TODO: PERF: Find better approach than try/timeouts?
}); });
} }

77
package-lock.json generated
View File

@ -1,42 +1,9 @@
{ {
"name": "gun", "name": "gun",
"version": "0.2020.116", "version": "0.2020.301",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
"@peculiar/asn1-schema": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-1.0.3.tgz",
"integrity": "sha512-Tfgj9eNJ6cTKEtEuidKenLHMx/Q5M8KEE9hnohHqvdpqHJXWYr5RlT3GjAHPjGXy5+mr7sSfuXfzE6aAkEGN7A==",
"optional": true,
"requires": {
"asn1js": "^2.0.22",
"tslib": "^1.9.3"
}
},
"@peculiar/json-schema": {
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/@peculiar/json-schema/-/json-schema-1.1.9.tgz",
"integrity": "sha1-t0bgRreHYHobKAT2RDf9olJ7PmI=",
"optional": true,
"requires": {
"tslib": "^1.10.0"
}
},
"@peculiar/webcrypto": {
"version": "1.0.22",
"resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.0.22.tgz",
"integrity": "sha1-na5lL85rrNnfFbyRlleXzuM632c=",
"optional": true,
"requires": {
"@peculiar/asn1-schema": "^1.0.3",
"@peculiar/json-schema": "^1.1.6",
"asn1js": "^2.0.26",
"pvtsutils": "^1.0.9",
"tslib": "^1.10.0",
"webcrypto-core": "^1.0.17"
}
},
"@types/ip": { "@types/ip": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@types/ip/-/ip-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@types/ip/-/ip-1.1.0.tgz",
@ -134,14 +101,6 @@
"integrity": "sha1-8zshWfBTKj8xB6JywMz70a0peco=", "integrity": "sha1-8zshWfBTKj8xB6JywMz70a0peco=",
"dev": true "dev": true
}, },
"asn1js": {
"version": "2.0.26",
"resolved": "https://registry.npmjs.org/asn1js/-/asn1js-2.0.26.tgz",
"integrity": "sha512-yG89F0j9B4B0MKIcFyWWxnpZPLaNTjCj4tkE3fjbAoo0qmpGw0PYYqSbX/4ebnd9Icn8ZgK4K1fvDyEtW1JYtQ==",
"requires": {
"pvutils": "^1.0.17"
}
},
"aws-sdk": { "aws-sdk": {
"version": "2.611.0", "version": "2.611.0",
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.611.0.tgz", "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.611.0.tgz",
@ -165,9 +124,9 @@
"integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
"dev": true, "dev": true,
"requires": { "requires": {
"base64-js": "1.3.1", "base64-js": "^1.0.2",
"ieee754": "1.1.13", "ieee754": "^1.1.4",
"isarray": "1.0.0" "isarray": "^1.0.0"
} }
}, },
"isarray": { "isarray": {
@ -1067,19 +1026,6 @@
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
"dev": true "dev": true
}, },
"pvtsutils": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.0.9.tgz",
"integrity": "sha1-DrYQbyeHjMqlXn379r0sda9GHe4=",
"requires": {
"tslib": "^1.10.0"
}
},
"pvutils": {
"version": "1.0.17",
"resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.0.17.tgz",
"integrity": "sha512-wLHYUQxWaXVQvKnwIDWFVKDJku9XDCvyhhxoq8dc5MFdIlRenyPI9eSfEtcvgHgD7FlvCyGAlWgOzRnZD99GZQ=="
},
"querystring": { "querystring": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
@ -1296,11 +1242,6 @@
"typedarray-dts": "^1.0.0" "typedarray-dts": "^1.0.0"
} }
}, },
"tslib": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
},
"typedarray-dts": { "typedarray-dts": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/typedarray-dts/-/typedarray-dts-1.0.0.tgz", "resolved": "https://registry.npmjs.org/typedarray-dts/-/typedarray-dts-1.0.0.tgz",
@ -1339,16 +1280,6 @@
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
"dev": true "dev": true
}, },
"webcrypto-core": {
"version": "1.0.17",
"resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.0.17.tgz",
"integrity": "sha1-qTVLwLG6ZzXogvQTft4sQ2bmrZs=",
"optional": true,
"requires": {
"pvtsutils": "^1.0.9",
"tslib": "^1.10.0"
}
},
"which": { "which": {
"version": "1.3.1", "version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",

View File

@ -1,6 +1,6 @@
{ {
"name": "gun", "name": "gun",
"version": "0.2020.116", "version": "0.2020.301",
"description": "A realtime, decentralized, offline-first, graph data synchronization engine.", "description": "A realtime, decentralized, offline-first, graph data synchronization engine.",
"types": "index.d.ts", "types": "index.d.ts",
"main": "index.js", "main": "index.js",
@ -8,7 +8,8 @@
"ios": "browser.ios.js", "ios": "browser.ios.js",
"android": "browser.android.js", "android": "browser.android.js",
"scripts": { "scripts": {
"start": "node examples/http.js", "start": "node --prof examples/http.js",
"debug": "node --prof-process --preprocess -j isolate*.log > v8.json && rm isolate*.log && echo 'drag & drop ./v8.json into https://mapbox.github.io/flamebearer/'",
"https": "HTTPS_KEY=test/https/server.key HTTPS_CERT=test/https/server.crt npm start", "https": "HTTPS_KEY=test/https/server.key HTTPS_CERT=test/https/server.crt npm start",
"prepublishOnly": "npm run unbuild", "prepublishOnly": "npm run unbuild",
"test": "mocha", "test": "mocha",
@ -55,16 +56,19 @@
"node": ">=0.8.4" "node": ">=0.8.4"
}, },
"dependencies": { "dependencies": {
"ws": "^7.1.2" "buffer": "^5.4.3",
"ws": "^7.2.1"
}, },
"optionalDependencies": { "optionalDependencies": {
"text-encoding": "^0.7.0", "text-encoding": "^0.7.0",
"emailjs": "^2.2.0", "emailjs": "^2.2.0",
"@peculiar/webcrypto": "^1.0.22",
"buffer": "^5.4.3" "buffer": "^5.4.3"
}, },
"peerDependencies": { "peerDependencies": {
"@gooddollar/react-native-webview-crypto": "^0.*" "@gooddollar/react-native-webview-crypto": "^0.*",
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2",
"text-encoding": "^0.7.0"
}, },
"devDependencies": { "devDependencies": {
"@types/ip": "^1.1.0", "@types/ip": "^1.1.0",

133
sea.js
View File

@ -1,12 +1,6 @@
;(function(){ ;(function(){
/* UNBUILD */ /* UNBUILD */
var root;
if(typeof window !== "undefined"){ root = window }
if(typeof global !== "undefined"){ root = global }
root = root || {};
var console = root.console || {log: function(){}};
function USE(arg, req){ function USE(arg, req){
return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){ return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){
arg(mod = {exports: {}}); arg(mod = {exports: {}});
@ -16,7 +10,7 @@
return p.split('/').slice(-1).toString().replace('.js',''); return p.split('/').slice(-1).toString().replace('.js','');
} }
} }
if(typeof module !== "undefined"){ var common = module } if(typeof module !== "undefined"){ var MODULE = module }
/* UNBUILD */ /* UNBUILD */
;USE(function(module){ ;USE(function(module){
@ -32,7 +26,7 @@
if(SEA.window = module.window){ SEA.window.SEA = SEA } if(SEA.window = module.window){ SEA.window.SEA = SEA }
try{ if(typeof common !== "undefined"){ common.exports = SEA } }catch(e){} try{ if(typeof MODULE !== "undefined"){ MODULE.exports = SEA } }catch(e){}
module.exports = SEA; module.exports = SEA;
})(USE, './root'); })(USE, './root');
@ -50,10 +44,10 @@
;USE(function(module){ ;USE(function(module){
if(typeof btoa === "undefined"){ if(typeof btoa === "undefined"){
if(typeof Buffer === "undefined") { if(typeof Buffer === "undefined") {
root.Buffer = require("buffer").Buffer global.Buffer = require("buffer").Buffer
} }
root.btoa = function (data) { return Buffer.from(data, "binary").toString("base64"); }; global.btoa = function (data) { return Buffer.from(data, "binary").toString("base64"); };
root.atob = function (data) { return Buffer.from(data, "base64").toString("binary"); }; global.atob = function (data) { return Buffer.from(data, "base64").toString("binary"); };
} }
})(USE, './base64'); })(USE, './base64');
@ -278,13 +272,13 @@
cb = salt; cb = salt;
salt = u; salt = u;
} }
salt = salt || shim.random(9);
data = (typeof data == 'string')? data : JSON.stringify(data); data = (typeof data == 'string')? data : JSON.stringify(data);
if('sha' === (opt.name||'').toLowerCase().slice(0,3)){ if('sha' === (opt.name||'').toLowerCase().slice(0,3)){
var rsha = shim.Buffer.from(await sha(data, opt.name), 'binary').toString(opt.encode || 'base64') var rsha = shim.Buffer.from(await sha(data, opt.name), 'binary').toString(opt.encode || 'base64')
if(cb){ try{ cb(rsha) }catch(e){console.log(e)} } if(cb){ try{ cb(rsha) }catch(e){console.log(e)} }
return rsha; return rsha;
} }
salt = salt || shim.random(9);
var key = await (shim.ossl || shim.subtle).importKey('raw', new shim.TextEncoder().encode(data), {name: opt.name || 'PBKDF2'}, false, ['deriveBits']); var key = await (shim.ossl || shim.subtle).importKey('raw', new shim.TextEncoder().encode(data), {name: opt.name || 'PBKDF2'}, false, ['deriveBits']);
var work = await (shim.ossl || shim.subtle).deriveBits({ var work = await (shim.ossl || shim.subtle).deriveBits({
name: opt.name || 'PBKDF2', name: opt.name || 'PBKDF2',
@ -442,7 +436,7 @@
opt = opt || {}; opt = opt || {};
// SEA.I // verify is free! Requires no user permission. // SEA.I // verify is free! Requires no user permission.
var pub = pair.pub || pair; 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 hash = await sha(json.m);
var buf, sig, check, tmp; try{ var buf, sig, check, tmp; try{
buf = shim.Buffer.from(json.s, opt.encode || 'base64'); // NEW DEFAULT! buf = shim.Buffer.from(json.s, opt.encode || 'base64'); // NEW DEFAULT!
@ -477,9 +471,11 @@
return knownKeys[pair]; return knownKeys[pair];
}; };
var O = SEA.opt;
SEA.opt.fall_verify = async function(data, pair, cb, opt, f){ SEA.opt.fall_verify = async function(data, pair, cb, opt, f){
if(f === SEA.opt.fallback){ throw "Signature did not match" } f = f || 1; 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 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 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{ var buf; var sig; var check; try{
@ -494,6 +490,7 @@
if(!check){ throw "Signature did not match." } if(!check){ throw "Signature did not match." }
} }
var r = check? S.parse(json.m) : u; 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)} } if(cb){ try{ cb(r) }catch(e){console.log(e)} }
return r; return r;
} }
@ -655,7 +652,7 @@
;USE(function(module){ ;USE(function(module){
var shim = USE('./shim'); var shim = USE('./shim');
// Practical examples about usage found from ./test/common.js // Practical examples about usage found in tests.
var SEA = USE('./root'); var SEA = USE('./root');
SEA.work = USE('./work'); SEA.work = USE('./work');
SEA.sign = USE('./sign'); SEA.sign = USE('./sign');
@ -704,7 +701,7 @@
// But all other behavior needs to be equally easy, like opinionated ways of // But all other behavior needs to be equally easy, like opinionated ways of
// Adding friends (trusted public keys), sending private messages, etc. // Adding friends (trusted public keys), sending private messages, etc.
// Cheers! Tell me what you think. // Cheers! Tell me what you think.
var Gun = (SEA.window||{}).Gun || USE((typeof common == "undefined"?'.':'')+'./gun', 1); var Gun = (SEA.window||{}).Gun || USE((typeof MODULE == "undefined"?'.':'')+'./gun', 1);
Gun.SEA = SEA; Gun.SEA = SEA;
SEA.GUN = SEA.Gun = Gun; SEA.GUN = SEA.Gun = Gun;
@ -1131,17 +1128,17 @@
})(USE, './create'); })(USE, './create');
;USE(function(module){ ;USE(function(module){
const SEA = USE('./sea') var SEA = USE('./sea')
const Gun = SEA.Gun; var Gun = SEA.Gun;
// After we have a GUN extension to make user registration/login easy, we then need to handle everything else. // After we have a GUN extension to make user registration/login easy, we then need to handle everything else.
// We do this with a GUN adapter, we first listen to when a gun instance is created (and when its options change) // We do this with a GUN adapter, we first listen to when a gun instance is created (and when its options change)
Gun.on('opt', function(at){ Gun.on('opt', function(at){
if(!at.sea){ // only add SEA once per instance, on the "at" context. if(!at.sea){ // only add SEA once per instance, on the "at" context.
at.sea = {own: {}}; at.sea = {own: {}};
at.on('in', security, at); // now listen to all input data, acting as a firewall. //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('put', check, at);
} }
this.to.next(at); // make sure to call the "next" middleware adapter. this.to.next(at); // make sure to call the "next" middleware adapter.
}); });
@ -1190,6 +1187,85 @@
security.call(this, msg); security.call(this, msg);
} }
var u;
function check(msg){ // REVISE / IMPROVE, NO NEED TO PASS MSG/EVE EACH SUB?
var eve = this, at = eve.as, put = msg.put, soul = put['#'], key = put['.'], val = put[':'], state = put['>'], id = msg['#'], tmp;
if((msg._||'').faith && (at.opt||'').faith && 'function' == typeof msg._){
SEA.verify(SEA.opt.pack(put), false, function(data){ // this is synchronous if false
put['='] = SEA.opt.unpack(data);
eve.to.next(msg);
});
return
}
var no = function(why){ at.on('in', {'@': id, err: why}) };
//var no = function(why){ msg.ack(why) };
(msg._||'').DBG && ((msg._||'').DBG.c = +new Date);
if('#' === soul[0]){ // special case for content addressing immutable hashed data.
check.hash(eve, msg, val, key, soul, at, no); return;
}
if('~@' === soul){ // special case for shared system data, the list of aliases.
check.alias(eve, msg, val, key, soul, at, no); return;
}
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.
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, at.user||''); return;
eve.to.next(msg); // not handled
}
check.hash = function(eve, msg, val, key, soul, at, no){
SEA.work(val, null, function(data){
if(data && data === key.split('#').slice(-1)[0]){ return eve.to.next(msg) }
no("Data hash not same as hash!");
}, {name: 'SHA-256'});
}
check.alias = function(eve, msg, val, key, soul, at, no){ // Example: {_:#~@, ~@alice: {#~@alice}}
if(!val){ return no("Data must exist!") } // data MUST exist
if('~@'+key === link_is(val)){ return eve.to.next(msg) } // in fact, it must be EXACTLY equal to itself
no("Alias not same!"); // if it isn't, reject.
};
check.pubs = function(eve, msg, val, key, soul, at, no){ // Example: {_:#~@alice, ~asdf: {#~asdf}}
if(!val){ return no("Alias must exist!") } // data MUST exist
if(key === link_is(val)){ return eve.to.next(msg) } // and the ID must be EXACTLY equal to its property
no("Alias not same!"); // that way nobody can tamper with the list of public keys.
};
check.pub = function(eve, msg, val, key, soul, at, no, user, pub){ var tmp; // Example: {_:#~asdf, hello:'world'~fdsa}}
if('pub' === key && '~'+pub === soul){
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((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(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);
}, {raw: 1});
return;
}
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((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(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, state_ify = Gun.state.ify;
// okay! The security function handles all the heavy lifting. // 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. // It needs to deal read and write of input and output of system data, account/public key data, and regular data.
// This is broken down into some pretty clear edge cases, let's go over them: // This is broken down into some pretty clear edge cases, let's go over them:
@ -1215,6 +1291,11 @@
} }
} }
if(msg.put){ if(msg.put){
/*
NOTICE: THIS IS OLD AND GETTING DEPRECATED.
ANY SECURITY CHANGES SHOULD HAPPEN ABOVE FIRST
THEN PORTED TO HERE.
*/
// potentially parallel async operations!!! // potentially parallel async operations!!!
var check = {}, each = {}, u; var check = {}, each = {}, u;
each.node = function(node, soul){ each.node = function(node, soul){
@ -1345,6 +1426,7 @@
if(!s || !(s = s[1])){ return } if(!s || !(s = s[1])){ return }
s = s.split('.'); s = s.split('.');
if(!s || 2 > s.length){ return } 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('.'); s = s.slice(0,2).join('.');
return s; return s;
} }
@ -1353,16 +1435,18 @@
} }
SEA.opt.pack = function(d,k, n,s){ // pack for verifying SEA.opt.pack = function(d,k, n,s){ // pack for verifying
if(SEA.opt.check(d)){ return d } if(SEA.opt.check(d)){ return d }
var meta = (Gun.obj.ify(d)||noop), sig = meta['~']; var meta = (Gun.obj.ify((d && d[':'])||d)||''), sig = meta['~'];
return sig? {m: {'#':s,'.':k,':':meta[':'],'>':Gun.state.is(n, k)}, s: sig} : d; 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; SEA.opt.unpack = function(d, k, n){ var tmp;
if(u === d){ return } if(u === d){ return }
if(d && (u !== (tmp = d[':']))){ return tmp } 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(!k || !n){ return }
if(d === n[k]){ return d } if(d === n[k]){ return d }
if(!SEA.opt.check(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])){ if(d && 4 === d.length && soul === d[0] && k === d[1] && fl(s) === fl(d[3])){
return d[2]; return d[2];
} }
@ -1374,6 +1458,7 @@
var noop = function(){}, u; var noop = function(){}, u;
var fl = Math.floor; // TODO: Still need to fix inconsistent state issue. var fl = Math.floor; // TODO: Still need to fix inconsistent state issue.
var rel_is = Gun.val.rel.is; 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. // TODO: Potential bug? If pub/priv key starts with `-`? IDK how possible.
})(USE, './index'); })(USE, './index');

50
test/bug/322.js Executable file
View File

@ -0,0 +1,50 @@
describe('Gun', function(){
var root;
(function(){
var env;
if(typeof global !== 'undefined'){ env = global }
if(typeof window !== 'undefined'){ env = window }
root = env.window? env.window : global;
try{ env.window && root.localStorage && root.localStorage.clear() }catch(e){}
try{ localStorage.clear() }catch(e){}
try{ indexedDB.deleteDatabase('radatatest') }catch(e){}
try{ require('fs').unlinkSync('data.json') }catch(e){}
try{ require('../../lib/fsrm')('radatatest') }catch(e){}
try{ var expect = global.expect = require("../expect") }catch(e){}
//root.Gun = root.Gun || require('../gun');
if(root.Gun){
root.Gun = root.Gun;
root.Gun.TESTING = true;
} else {
root.Gun = require('../../gun');
root.Gun.TESTING = true;
Gun.serve = require('../../lib/serve');
// require('../../lib/file');
require('../../lib/store');
require('../../lib/rfs');
// require('../../sea.js');
}
}(this));
var opt = { file: 'radatatest' };
describe('API - map', function(){
it('Save example data', function(done) {
var gun = Gun(opt);
gun.get('users').set({u:1});
gun.get('users').set({u:2});
gun.get('users').set({u:2});
gun.get('users').map().on(function(user) { user.index = 'someIndex'; });
setTimeout(function() { done(); }, 200);
});
it('Make sure the value "someIndex" not be saved in storage', function(done) {
var gun = Gun(opt), values=[];
gun.get('users').map().once(function(v) { values.push(v.index); });
setTimeout(function() {
expect(values.indexOf('someIndex')===-1).to.be(true);
done();
}, 200);
});
});
});

59
test/bug/686.js Executable file
View File

@ -0,0 +1,59 @@
describe('Gun', function(){
var root;
(function(){
var env;
if(typeof global !== 'undefined'){ env = global }
if(typeof window !== 'undefined'){ env = window }
root = env.window? env.window : global;
try{ env.window && root.localStorage && root.localStorage.clear() }catch(e){}
try{ localStorage.clear() }catch(e){}
try{ indexedDB.deleteDatabase('radatatest') }catch(e){}
try{ require('fs').unlinkSync('data.json') }catch(e){}
try{ require('../../lib/fsrm')('radatatest') }catch(e){}
try{ var expect = global.expect = require("../expect") }catch(e){}
//root.Gun = root.Gun || require('../gun');
if(root.Gun){
root.Gun = root.Gun;
root.Gun.TESTING = true;
} else {
root.Gun = require('../../gun');
root.Gun.TESTING = true;
Gun.serve = require('../../lib/serve');
//require('../lib/file');
require('../../lib/store');
require('../../lib/rfs');
require('../../sea.js');
}
}(this));
var opt = { file: 'radatatest' };
describe('SEA', function() {
it('put null string', function(done) {
var gun = Gun(opt);
gun.get('test').get('key').put('null', function(ack) {
if (ack.err) { expect(!ack.err).to.be(true); done(); }
gun.get('test').get('key').once(function(v) {
expect(v === 'null').to.be(true);
done();
});
});
});
it('put null string in user land', function(done) {
var gun = Gun(opt);
var user = gun.user();
var u={a:'usr', p:'pass'};
var value = 'null';
user.create(u.a, u.p, function(ack) {
usr = user.auth(u.a, u.p, function() {
usr.get('test').get('key').put(value, function(ack) {
if (ack.err) { expect(!ack.err).to.be(true); done(); }
usr.get('test').get('key').once(function(v) {
expect(v === value).to.be(true); /// must be null string.
done();
});
});
});
});
});
});
});

46
test/bug/783.js Normal file
View File

@ -0,0 +1,46 @@
///// bug-783
describe('Gun', function(){
var root;
(function(){
var env;
if(typeof global !== 'undefined'){ env = global }
if(typeof window !== 'undefined'){ env = window }
root = env.window? env.window : global;
try{ env.window && root.localStorage && root.localStorage.clear() }catch(e){}
try{ localStorage.clear() }catch(e){}
try{ indexedDB.deleteDatabase('radatatest') }catch(e){}
try{ require('fs').unlinkSync('data.json') }catch(e){}
try{ require('../../lib/fsrm')('radatatest') }catch(e){}
try{ var expect = global.expect = require("../expect") }catch(e){}
//root.Gun = root.Gun || require('../gun');
if(root.Gun){
root.Gun = root.Gun;
root.Gun.TESTING = true;
} else {
root.Gun = require('../../gun');
root.Gun.TESTING = true;
Gun.serve = require('../../lib/serve');
//require('../lib/file');
require('../../lib/store');
require('../../lib/rfs');
require('../../sea.js');
}
}(this));
describe('erro sea', function(){
it('verbose console.log debugging', function(done) {
var gun = Gun({multicast:false, axe:false});
var ref = gun.get('test').get('1');
var vput = 'SEA{}';
ref.put(vput, function(ack, yay){ console.log('ACK: ', ack); /// must ack all
ref.once(function(v,k) { console.log('SALVOU k:%s, v:', k, v);
expect(v===vput).to.be(true);
done();
});
});
});
} );
});

View File

@ -1279,6 +1279,14 @@ describe('Gun', function(){
this.to.next(root); 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(); var gun = Gun();
it.skip('gun chain separation', function(done){ // TODO: UNDO! it.skip('gun chain separation', function(done){ // TODO: UNDO!
@ -1397,8 +1405,7 @@ describe('Gun', function(){
- Performant read lock on write contexts. - Performant read lock on write contexts.
- Proxying event across maps. - Proxying event across maps.
*/ */
var s = Gun.state.map();s.soul = 'u/m'; gun.on('test', {$: gun, put: Gun.graph.ify({
gun.on('put', {$: gun, put: Gun.graph.ify({
alice: { alice: {
age: 26, age: 26,
name: "Alice", name: "Alice",
@ -1409,7 +1416,7 @@ describe('Gun', function(){
name: "Bob!", name: "Bob!",
pet: {b:2, name: "Frisky"} pet: {b:2, name: "Frisky"}
} }
}, s)}); }, Gun.state.map(), 'u/m')});
var check = {}, count = {}; var check = {}, count = {};
gun.get('u/m').map().on(function(v,f){ gun.get('u/m').map().on(function(v,f){
check[f] = v; check[f] = v;
@ -1433,8 +1440,7 @@ describe('Gun', function(){
}); });
it('uncached synchronous map get on', function(done){ it('uncached synchronous map get on', function(done){
var s = Gun.state.map();s.soul = 'u/m/p'; gun.on('test', {$: gun, put: Gun.graph.ify({
gun.on('put', {$: gun, put: Gun.graph.ify({
alice: { alice: {
age: 26, age: 26,
name: "alice", name: "alice",
@ -1445,7 +1451,7 @@ describe('Gun', function(){
name: "bob", name: "bob",
pet: {b:2, name: "Frisky"} pet: {b:2, name: "Frisky"}
} }
}, s)}); }, Gun.state.map(), 'u/m/p')});
var check = {}, count = {}; var check = {}, count = {};
gun.get('u/m/p').map().get('name').on(function(v,f){ gun.get('u/m/p').map().get('name').on(function(v,f){
//console.log("*****************", f, v); //console.log("*****************", f, v);
@ -1465,8 +1471,7 @@ describe('Gun', function(){
}); });
it('uncached synchronous map get on node', function(done){ it('uncached synchronous map get on node', function(done){
var s = Gun.state.map();s.soul = 'u/m/p/n'; gun.on('test', {$: gun, put: Gun.graph.ify({
gun.on('put', {$: gun, put: Gun.graph.ify({
alice: { alice: {
age: 26, age: 26,
name: "alice", name: "alice",
@ -1477,7 +1482,7 @@ describe('Gun', function(){
name: "bob", name: "bob",
pet: {b:2, name: "Frisky"} pet: {b:2, name: "Frisky"}
} }
}, s)}); }, Gun.state.map(), 'u/m/p/n')});
var check = {}, count = {}; var check = {}, count = {};
gun.get('u/m/p/n').map().get('pet').on(function(v,f){ gun.get('u/m/p/n').map().get('pet').on(function(v,f){
//console.log("********************", f,v); //console.log("********************", f,v);
@ -1500,8 +1505,7 @@ describe('Gun', function(){
it('uncached synchronous map get on node get', function(done){ it('uncached synchronous map get on node get', function(done){
var gun = Gun(); var gun = Gun();
var s = Gun.state.map();s.soul = 'u/m/p/n/p'; gun.on('test', {$: gun, put: Gun.graph.ify({
gun.on('put', {$: gun, put: Gun.graph.ify({
alice: { alice: {
age: 26, age: 26,
name: "alice", name: "alice",
@ -1512,7 +1516,7 @@ describe('Gun', function(){
name: "bob", name: "bob",
pet: {b:2, name: "Frisky"} pet: {b:2, name: "Frisky"}
} }
}, s)}); }, Gun.state.map(), 'u/m/p/n/p')});
var check = {}, count = {}; var check = {}, count = {};
//console.debug.i=1;console.log('-------------------'); //console.debug.i=1;console.log('-------------------');
gun.get('u/m/p/n/p').map().get('pet').get('name').on(function(v,f){ 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){ it('uncached synchronous map on mutate', function(done){
var s = Gun.state.map();s.soul = 'u/m/mutate'; gun.on('test', {$: gun, put: Gun.graph.ify({
gun.on('put', {$: gun, put: Gun.graph.ify({
alice: { alice: {
age: 26, age: 26,
name: "Alice", name: "Alice",
@ -1551,7 +1554,7 @@ describe('Gun', function(){
name: "Bob", name: "Bob",
pet: {b:2, name: "Frisky"} pet: {b:2, name: "Frisky"}
} }
}, s)}); }, Gun.state.map(), 'u/m/mutate')});
var check = {}, count = {}; var check = {}, count = {};
gun.get('u/m/mutate').map().get('name').get(function(at,ev){ gun.get('u/m/mutate').map().get('name').get(function(at,ev){
var e = at.err, v = at.put, f = at.get; 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){ it('uncached synchronous map on mutate node', function(done){
var s = Gun.state.map();s.soul = 'u/m/mutate/n'; gun.on('test', {$: gun, put: Gun.graph.ify({
gun.on('put', {$: gun, put: Gun.graph.ify({
alice: {_:{'#':'umaliceo'}, alice: {_:{'#':'umaliceo'},
age: 26, age: 26,
name: "Alice", name: "Alice",
@ -1587,7 +1589,7 @@ describe('Gun', function(){
name: "Bob", name: "Bob",
pet: {b:2, name: "Frisky"} pet: {b:2, name: "Frisky"}
} }
}, s)}); }, Gun.state.map(), 'u/m/mutate/n')});
var check = {}, count = {}; var check = {}, count = {};
gun.get('u/m/mutate/n').map().get('name').get(function(at,ev){ gun.get('u/m/mutate/n').map().get('name').get(function(at,ev){
var e = at.err, v = at.put, f = at.get; 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){ it('uncached synchronous map on mutate node uncached', function(done){
var s = Gun.state.map();s.soul = 'u/m/mutate/n/u'; gun.on('test', {$: gun, put: Gun.graph.ify({
gun.on('put', {$: gun, put: Gun.graph.ify({
alice: {_:{'#':'umaliceo1'}, alice: {_:{'#':'umaliceo1'},
age: 26, age: 26,
name: "Alice", name: "Alice",
@ -1634,7 +1635,7 @@ describe('Gun', function(){
name: "Bob", name: "Bob",
pet: {b:2, name: "Frisky"} pet: {b:2, name: "Frisky"}
} }
}, s)}); }, Gun.state.map(), 'u/m/mutate/n/u')});
var check = {}, count = {}; var check = {}, count = {};
gun.get('u/m/mutate/n/u').map().on(function(v,f){ gun.get('u/m/mutate/n/u').map().on(function(v,f){
check[v.name] = f; check[v.name] = f;
@ -1654,10 +1655,9 @@ describe('Gun', function(){
} }
}); });
setTimeout(function(){ setTimeout(function(){
var s = Gun.state.map();s.soul = 'u/m/m/n/u/soul'; gun.on('test', {$: gun, put: Gun.graph.ify({
gun.on('put', {$: gun, put: Gun.graph.ify({
name: 'Alice Zzxyz' name: 'Alice Zzxyz'
}, s)}); }, Gun.state.map(), 'u/m/m/n/u/soul')});
//console.debug.i=1;console.log("---------------"); //console.debug.i=1;console.log("---------------");
gun.get('u/m/mutate/n/u').put({ gun.get('u/m/mutate/n/u').put({
alice: {'#':'u/m/m/n/u/soul'}, 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){ 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('test', {$: gun, put: Gun.graph.ify({
gun.on('put', {$: gun, put: Gun.graph.ify({
alice: {_:{'#':'umaliceo2'}, alice: {_:{'#':'umaliceo2'},
age: 26, age: 26,
name: "Alice", name: "Alice",
@ -1691,7 +1690,7 @@ describe('Gun', function(){
name: "Bob", name: "Bob",
pet: {b:2, name: "Frisky"} pet: {b:2, name: "Frisky"}
} }
}, s)}); }, Gun.state.map(), 'u/m/p/mutate/n/u')});
var check = {}, count = {}; var check = {}, count = {};
gun.get('u/m/p/mutate/n/u').map().get('name').on(function(v,f){ gun.get('u/m/p/mutate/n/u').map().get('name').on(function(v,f){
check[v] = f; check[v] = f;
@ -1712,10 +1711,9 @@ describe('Gun', function(){
} }
}); });
setTimeout(function(){ setTimeout(function(){
var s = Gun.state.map();s.soul = 'u/m/p/m/n/u/soul'; gun.on('test', {$: gun, put: Gun.graph.ify({
gun.on('put', {$: gun, put: Gun.graph.ify({
name: 'Alice Zzxyz', age: 34 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({ gun.get('u/m/p/mutate/n/u').put({
alice: {'#':'u/m/p/m/n/u/soul'}, 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){ 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('test', {$: gun, put: Gun.graph.ify({
gun.on('put', {$: gun, put: Gun.graph.ify({
alice: {_:{'#':'umaliceo3'}, alice: {_:{'#':'umaliceo3'},
age: 26, age: 26,
name: "Alice", name: "Alice",
@ -1741,7 +1738,7 @@ describe('Gun', function(){
name: "Bob", name: "Bob",
pet: {b:2, name: "Frisky"} pet: {b:2, name: "Frisky"}
} }
}, s)}); }, Gun.state.map(), 'u/m/p/n/mutate/n/u')});
var check = {}, count = {}; var check = {}, count = {};
gun.get('u/m/p/n/mutate/n/u').map().get('pet').on(function(v,f){ gun.get('u/m/p/n/mutate/n/u').map().get('pet').on(function(v,f){
check[v.name] = f; check[v.name] = f;
@ -1760,11 +1757,10 @@ describe('Gun', function(){
} }
}); });
setTimeout(function(){ setTimeout(function(){
var s = Gun.state.map();s.soul = 'alice/fuzz/soul'; gun.on('test', {$: gun, put: Gun.graph.ify({
gun.on('put', {$: gun, put: Gun.graph.ify({
name: 'Alice Zzxyz', age: 34, name: 'Alice Zzxyz', age: 34,
pet: {c:3, name: "Fuzzball"} pet: {c:3, name: "Fuzzball"}
}, s)}); }, Gun.state.map(), 'alice/fuzz/soul')});
gun.get('u/m/p/n/mutate/n/u').put({ gun.get('u/m/p/n/mutate/n/u').put({
alice: {'#':'alice/fuzz/soul'}, alice: {'#':'alice/fuzz/soul'},
}); });
@ -2844,15 +2840,13 @@ describe('Gun', function(){
}); });
it('get node after recursive field', function(done){ it('get node after recursive field', function(done){
var s = Gun.state.map();s.soul = 'node/circle';
var bob = {age: 29, name: "Bob!"}; var bob = {age: 29, name: "Bob!"};
var cat = {name: "Fluffy", species: "kitty"}; var cat = {name: "Fluffy", species: "kitty"};
var user = {bob: bob}; var user = {bob: bob};
bob.pet = cat; bob.pet = cat;
cat.slave = bob; cat.slave = bob;
gun.on('put', {$: gun, put: Gun.graph.ify(user, s)}); gun.on('test', {$: gun, put: Gun.graph.ify(user, Gun.state.map(), 'node/circle')});
//console.debug.i=1;console.log("-------------"); gun.get('node/circle').get('bob').get('pet').get('slave').once(function(data){
gun.get(s.soul).get('bob').get('pet').get('slave').once(function(data){
//clearTimeout(done.to); //clearTimeout(done.to);
//setTimeout(function(){ //setTimeout(function(){
//console.log("*****************", data);return; //console.log("*****************", data);return;
@ -2945,7 +2939,9 @@ describe('Gun', function(){
list.set(gun.get('dave').put({name: "Dave", group: "awesome", married: true})); list.set(gun.get('dave').put({name: "Dave", group: "awesome", married: true}));
var check = {}, count = {}; 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); //console.log("***************", id, data);
check[id] = data; check[id] = data;
count[id] = (count[id] || 0) + 1; count[id] = (count[id] || 0) + 1;
@ -3090,13 +3086,12 @@ describe('Gun', function(){
}); });
it('get get get any parallel', function(done){ it('get get get any parallel', function(done){
var s = Gun.state.map();s.soul = 'parallel'; gun.on('test', {$: gun, put: Gun.graph.ify({
gun.on('put', {$: gun, put: Gun.graph.ify({
bob: { bob: {
age: 29, age: 29,
name: "Bob!" name: "Bob!"
} }
}, s)}); }, Gun.state.map(), 'parallel')});
gun.get('parallel').get('bob').get('age').get(function(at, ev){ gun.get('parallel').get('bob').get('age').get(function(at, ev){
var err = at.err, data = at.put, field = at.get; var err = at.err, data = at.put, field = at.get;
//console.log("***** age", data, at.$._.ack);//return; //console.log("***** age", data, at.$._.ack);//return;
@ -3117,13 +3112,12 @@ describe('Gun', function(){
}); });
it('get get get any later', function(done){ it('get get get any later', function(done){
var s = Gun.state.map();s.soul = 'parallel/later'; gun.on('test', {$: gun, put: Gun.graph.ify({
gun.on('put', {$: gun, put: Gun.graph.ify({
bob: {_:{'#':'ddfsa'}, bob: {_:{'#':'ddfsa'},
age: 29, age: 29,
name: "Bob!" name: "Bob!"
} }
}, s)}); }, Gun.state.map(), 'parallel/later')});
gun.get('parallel/later').get('bob').get('age').get(function(at, ev){ gun.get('parallel/later').get('bob').get('age').get(function(at, ev){
var err = at.err, data = at.put, field = at.get; var err = at.err, data = at.put, field = at.get;
//console.log("***** age", data); //console.log("***** age", data);
@ -3189,11 +3183,10 @@ describe('Gun', function(){
}); });
it('get any any', function(done){ it('get any any', function(done){
var s = Gun.state.map();s.soul = 'full'; gun.on('test', {$: gun, put: Gun.graph.ify({
gun.on('put', {$: gun, put: Gun.graph.ify({
hello: 'world', hello: 'world',
goodbye: 'mars' goodbye: 'mars'
}, s)}); }, Gun.state.map(), 'full')});
gun.get('full').get(function(at, ev){ gun.get('full').get(function(at, ev){
var err = at.err, data = at.$._.put || at.put, field = at.get; var err = at.err, data = at.$._.put || at.put, field = at.get;
//console.log("*****1", data); //console.log("*****1", data);
@ -3211,11 +3204,10 @@ describe('Gun', function(){
}); });
it('get any any later', function(done){ it('get any any later', function(done){
var s = Gun.state.map();s.soul = 'full/later'; gun.on('test', {$: gun, put: Gun.graph.ify({
gun.on('put', {$: gun, put: Gun.graph.ify({
hello: 'world', hello: 'world',
goodbye: 'mars' goodbye: 'mars'
}, s)}); }, Gun.state.map(), 'full/later')});
gun.get('full/later').get(function(at, ev){ gun.get('full/later').get(function(at, ev){
var err = at.err, data = at.$._.put || at.put, field = at.get; var err = at.err, data = at.$._.put || at.put, field = at.get;
//console.log("*****", data); //console.log("*****", data);
@ -3303,8 +3295,7 @@ describe('Gun', function(){
it('multiple times partial', function(done){ it('multiple times partial', function(done){
var gun = Gun(); var gun = Gun();
var s = Gun.state.map();s.soul = 'mult/times/part'; gun.on('test', {$: gun, put: Gun.graph.ify({
gun.on('put', {$: gun, put: Gun.graph.ify({
alias: { alias: {
mark: { mark: {
pub: {_:{'#':'PUB'}, 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("==================="); //console.debug.i=1;console.log("===================");
app.get('alias').get('mark').map().once(function(alias){ 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('hello world'); // outputs "message: hello world"
list.get('message').put(null); // throws Uncaught TypeError: Cannot read property '#' of null list.get('message').put(null); // throws Uncaught TypeError: Cannot read property '#' of null
}); });
return;
it('Check multi instance message passing', function(done){ it('Check multi instance message passing', function(done){
try{ require('fs').unlinkSync('bdata') }catch(e){} try{ require('fs').unlinkSync('bdata') }catch(e){}
@ -3541,10 +3533,10 @@ describe('Gun', function(){
it('If chain cannot be called, ack', function(done){ it('If chain cannot be called, ack', function(done){
var gun = Gun(), u; var gun = Gun(), u;
gun.on('put', {$: gun, put: Gun.graph.ify({ gun.on('test', {$: gun, put: Gun.graph.ify({
wat: 1, wat: 1,
a: true a: true
}, 'nl/app')}); }, Gun.state.map(), 'nl/app')}); // prev had no state_map?
var app = gun.get('nl/app'); var app = gun.get('nl/app');
@ -3565,11 +3557,11 @@ describe('Gun', function(){
it('Chain on known nested object should ack', function(done){ it('Chain on known nested object should ack', function(done){
var gun = Gun(), u; var gun = Gun(), u;
gun.on('put', {$: gun, put: Gun.graph.ify({ gun.on('test', {$: gun, put: Gun.graph.ify({
bar: { bar: {
wat: 1 wat: 1
} }
}, 'nl/app')}); }, Gun.state.map(), 'nl/app')});
var app = gun.get('nl/app').get('bar'); var app = gun.get('nl/app').get('bar');
@ -3953,13 +3945,12 @@ describe('Gun', function(){
});return; });return;
it('get get any parallel', function(done){ it('get get any parallel', function(done){
var s = Gun.state.map();s.soul = 'parallel/get/get'; Gun.on('test', {$: gun, put: Gun.graph.ify({
Gun.on('put', {$: gun, put: Gun.graph.ify({
bob: { bob: {
age: 29, age: 29,
name: "Bob!" name: "Bob!"
} }
}, s)}); }, Gun.state.map(), 'parallel/get/get')});
gun.get('parallel/get/get').path('bob').any(function(err, data, field, at, ev){ gun.get('parallel/get/get').path('bob').any(function(err, data, field, at, ev){
//console.log("***** 1", data); //console.log("***** 1", data);
expect(data.age).to.be(29); expect(data.age).to.be(29);
@ -3974,13 +3965,12 @@ describe('Gun', function(){
}); });
it('get get any parallel later', function(done){ it('get get any parallel later', function(done){
var s = Gun.state.map();s.soul = 'parallel/get/get/later'; Gun.on('test', {$: gun, put: Gun.graph.ify({
Gun.on('put', {$: gun, put: Gun.graph.ify({
bob: { bob: {
age: 29, age: 29,
name: "Bob!" 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){ gun.get('parallel/get/get/later').path('bob').any(function(err, data, field, at, ev){
//console.log("***** 1", data); //console.log("***** 1", data);
expect(data.age).to.be(29); expect(data.age).to.be(29);
@ -3997,13 +3987,12 @@ describe('Gun', function(){
}); });
it('get get any none', function(done){ it('get get any none', function(done){
var s = Gun.state.map();s.soul = 'get/get/none'; Gun.on('test', {$: gun, put: Gun.graph.ify({
Gun.on('put', {$: gun, put: Gun.graph.ify({
alice: { alice: {
age: 31, age: 31,
name: "alice" name: "alice"
} }
}, s)}); }, Gun.state.map(), 'get/get/none')});
var c = 0, s = 0; var c = 0, s = 0;
gun.get('get/get/none').path('bob').any(function(err, data, field, at, ev){ gun.get('get/get/none').path('bob').any(function(err, data, field, at, ev){
//console.log("***** 1", data); //console.log("***** 1", data);
@ -4025,13 +4014,12 @@ describe('Gun', function(){
}); });
it('get get any none later', function(done){ it('get get any none later', function(done){
var s = Gun.state.map();s.soul = 'get/get/none/later'; Gun.on('test', {$: gun, put: Gun.graph.ify({
Gun.on('put', {$: gun, put: Gun.graph.ify({
alice: { alice: {
age: 31, age: 31,
name: "alice" name: "alice"
} }
}, s)}); }, Gun.state.map(), 'get/get/none/later')});
var c = 0; var c = 0;
gun.get('get/get/none/later').path('bob').any(function(err, data, field, at, ev){ gun.get('get/get/none/later').path('bob').any(function(err, data, field, at, ev){
//console.log("***** 1", data); //console.log("***** 1", data);
@ -4051,10 +4039,9 @@ describe('Gun', function(){
}); });
it('get get primitive get any', function(done){ it('get get primitive get any', function(done){
var s = Gun.state.map();s.soul = 'get/get/prim'; Gun.on('test', {$: gun, put: Gun.graph.ify({
Gun.on('put', {$: gun, put: Gun.graph.ify({
bob: "is awesome" 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){ gun.get('get/get/prim').path('bob').path('age').any(function(err, data, field, at, ev){
//console.log("***** 1", data); //console.log("***** 1", data);
expect(data).to.be(undefined); expect(data).to.be(undefined);
@ -4067,10 +4054,9 @@ describe('Gun', function(){
}); });
it('get put any', function(done){ it('get put any', function(done){
var s = Gun.state.map();s.soul = 'get/put/any'; Gun.on('test', {$: gun, put: Gun.graph.ify({
Gun.on('put', {$: gun, put: Gun.graph.ify({
here: "we go" here: "we go"
}, s)}); }, Gun.state.map(), 'get/put/any')});
//console.debug.i=1;console.log("---------------"); //console.debug.i=1;console.log("---------------");
gun.get('get/put/any') gun.get('get/put/any')
.put({}) .put({})
@ -4081,10 +4067,9 @@ describe('Gun', function(){
}); });
return; return;
it('get any, get put any', function(done){ it('get any, get put any', function(done){
var s = Gun.state.map();s.soul = 'get/any/get/put/any'; Gun.on('test', {$: gun, put: Gun.graph.ify({
Gun.on('put', {$: gun, put: Gun.graph.ify({
here: "we go" here: "we go"
}, s)}); }, Gun.state.map(), 'get/any/get/put/any')});
gun.get('get/any/get/put/any') gun.get('get/any/get/put/any')
.any(function(err, data, field, at, ev){ .any(function(err, data, field, at, ev){
if(done.first){ return } // it is okay for `any` to get called multiple times. 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){ it('mutate pointer to primitive deep on', function(done){
var s = Gun.state.map();s.soul = 'change/pointer'; Gun.on('test', {$: gun, put: Gun.graph.ify({
Gun.on('put', {$: gun, put: Gun.graph.ify({
bob: { bob: {
age: 29, age: 29,
name: "Bob!", name: "Bob!",
@ -4120,7 +4104,7 @@ describe('Gun', function(){
species: "kitty" species: "kitty"
} }
} }
}, s)}); }, Gun.state.map(), 'change/pointer')});
gun.get('change/pointer').path('bob').path('pet').any(function(err, data, f, at, ev){ gun.get('change/pointer').path('bob').path('pet').any(function(err, data, f, at, ev){
//console.log("***", data);return setTimeout(function(){asdf},500); //console.log("***", data);return setTimeout(function(){asdf},500);
if(done.c){ if(done.c){
@ -4159,8 +4143,7 @@ describe('Gun', function(){
}); });
it('get only soul', function(done){ it('get only soul', function(done){
var s = Gun.state.map();s.soul = 'only/soul'; Gun.on('test', {$: gun, put: Gun.graph.ify({
Gun.on('put', {$: gun, put: Gun.graph.ify({
bob: { bob: {
age: 29, age: 29,
name: "Bob!", name: "Bob!",
@ -4169,7 +4152,7 @@ describe('Gun', function(){
species: "kitty" species: "kitty"
} }
} }
}, s)}); }, Gun.state.map(), 'only/soul')});
gun.get('only/soul')/*.path('bob')*/.any(function(err, data){ gun.get('only/soul')/*.path('bob')*/.any(function(err, data){
expect(Gun.obj.empty(data, '_')).to.be.ok(); expect(Gun.obj.empty(data, '_')).to.be.ok();
done(); done();
@ -4177,8 +4160,7 @@ describe('Gun', function(){
}); });
it('get path only soul', function(done){ it('get path only soul', function(done){
var s = Gun.state.map();s.soul = 'only/p/soul'; Gun.on('test', {$: gun, put: Gun.graph.ify({
Gun.on('put', {$: gun, put: Gun.graph.ify({
bob: { bob: {
age: 29, age: 29,
name: "Bob!", name: "Bob!",
@ -4187,7 +4169,7 @@ describe('Gun', function(){
species: "kitty" species: "kitty"
} }
} }
}, s)}); }, Gun.state.map(), 'only/p/soul')});
gun.get('only/p/soul').path('bob').any(function(err, data){ gun.get('only/p/soul').path('bob').any(function(err, data){
//console.log("*********", err, data); //console.log("*********", err, data);
expect(Gun.val.link.is(data)).to.be.ok(); expect(Gun.val.link.is(data)).to.be.ok();
@ -4197,8 +4179,7 @@ describe('Gun', function(){
}); });
it('mutate pointer to self', function(done){ it('mutate pointer to self', function(done){
var s = Gun.state.map();s.soul = 'change/pointer/point'; Gun.on('test', {$: gun, put: Gun.graph.ify({
Gun.on('put', {$: gun, put: Gun.graph.ify({
bob: { bob: {
age: 29, age: 29,
name: "Bob!", name: "Bob!",
@ -4207,7 +4188,7 @@ describe('Gun', function(){
species: "kitty" species: "kitty"
} }
} }
}, s)}); }, Gun.state.map(), 'change/pointer/point')});
gun.get('change/pointer/point').path('bob').any(function(err, data){ gun.get('change/pointer/point').path('bob').any(function(err, data){
if(done.c){ if(done.c){
expect(data.age).to.be(30); expect(data.age).to.be(30);
@ -4228,8 +4209,7 @@ describe('Gun', function(){
},400); },400);
}); });
it('mutate pointer to self deep', function(done){ it('mutate pointer to self deep', function(done){
var s = Gun.state.map();s.soul = 'change/pointer/point/deep'; Gun.on('test', {$: gun, put: Gun.graph.ify({
Gun.on('put', {$: gun, put: Gun.graph.ify({
bob: { bob: {
age: 29, age: 29,
name: "Bob!", name: "Bob!",
@ -4238,7 +4218,7 @@ describe('Gun', function(){
species: "kitty" species: "kitty"
} }
} }
}, s)}); }, Gun.state.map(), 'change/pointer/point/deep')});
gun.get('change/pointer/point/deep').path('bob').any(function(err, data){ gun.get('change/pointer/point/deep').path('bob').any(function(err, data){
//console.log("***", data); //console.log("***", data);
if(done.c){ if(done.c){
@ -4259,8 +4239,7 @@ describe('Gun', function(){
}); });
it('mutate pointer to primitive after any', function(done){ it('mutate pointer to primitive after any', function(done){
var s = Gun.state.map();s.soul = 'change/pointer/to/prime'; Gun.on('test', {$: gun, put: Gun.graph.ify({
Gun.on('put', {$: gun, put: Gun.graph.ify({
bob: {_: {'#': 'asdffdsa'}, bob: {_: {'#': 'asdffdsa'},
age: 29, age: 29,
name: "Bob!", name: "Bob!",
@ -4269,7 +4248,7 @@ describe('Gun', function(){
species: "kitty" species: "kitty"
} }
} }
}, s)}); }, Gun.state.map(), 'change/pointer/to/prime')});
var bob = gun.get('asdffdsa').any(function(err, data){ var bob = gun.get('asdffdsa').any(function(err, data){
//console.log("***", data); //console.log("***", data);
}); });
@ -4294,8 +4273,7 @@ describe('Gun', function(){
}); });
it('mutate pointer to primitive after any deep', function(done){ it('mutate pointer to primitive after any deep', function(done){
var s = Gun.state.map();s.soul = 'change/pointer/to/prime/deep'; Gun.on('test', {$: gun, put: Gun.graph.ify({
Gun.on('put', {$: gun, put: Gun.graph.ify({
bob: { bob: {
age: 29, age: 29,
name: "Bob!", name: "Bob!",
@ -4304,7 +4282,7 @@ describe('Gun', function(){
species: "kitty" species: "kitty"
} }
} }
}, s)}); }, Gun.state.map(), 'change/pointer/to/prime/deep')});
var cat = gun.get('sadffads').any(function(err, data){ var cat = gun.get('sadffads').any(function(err, data){
//console.log("***", data); //console.log("***", data);
}); });
@ -4328,8 +4306,7 @@ describe('Gun', function(){
}); });
return; return;
it.only('mutate pointer to another pointer after any', function(done){ it.only('mutate pointer to another pointer after any', function(done){
var s = Gun.state.map();s.soul = 'change/pointer/to/pointer'; Gun.on('test', {$: gun, put: Gun.graph.ify({
Gun.on('put', {$: gun, put: Gun.graph.ify({
bob: {_: {'#': 'dafssfad'}, bob: {_: {'#': 'dafssfad'},
age: 29, age: 29,
name: "Bob!", name: "Bob!",
@ -4338,7 +4315,7 @@ describe('Gun', function(){
species: "kitty" species: "kitty"
} }
} }
}, s)}); }, Gun.state.map(), 'change/pointer/to/pointer')});
var bob = gun.get('dafssfad').any(function(err, data){ var bob = gun.get('dafssfad').any(function(err, data){
console.log("***", data); console.log("***", data);
}); });
@ -4929,9 +4906,7 @@ describe('Gun', function(){
}); });
it('get get not', function(done){ it('get get not', function(done){
var s = Gun.state.map(); Gun.on('test', {$: gun, put: Gun.graph.ify({b: 1, c: 2}, Gun.state.map(), 'a')});
s.soul = 'a';
Gun.on('put', {$: gun, put: Gun.graph.ify({b: 1, c: 2}, s)});
function cb(e,d,f,a){ function cb(e,d,f,a){
if('b' === f && 1 === d){ if('b' === f && 1 === d){
done.b = true; done.b = true;

View File

@ -17,12 +17,7 @@
<script src="./expect.js"></script> <script src="./expect.js"></script>
<script></script> <script></script>
<script src="../gun.js"></script> <script src="../gun.js"></script>
<!-- script <script src="../sea.js"></script>
src="https://cdn.jsdelivr.net/npm/gun/sea.js">
</script -->
<script
src="../sea.js">
</script>
<script src="../lib/radix.js"></script> <script src="../lib/radix.js"></script>
<script src="../lib/radisk.js"></script> <script src="../lib/radisk.js"></script>

View File

@ -16,7 +16,6 @@
******* START AT THE BOTTOM AND READ UP ******* ******* START AT THE BOTTOM AND READ UP *******
*/ */
window.i = 1; window.i = 1;
window.localStorage = window.localStorage || {clear: function(){}};
if(!this.stool){ return } if(!this.stool){ return }
setTimeout(function(){ setTimeout(function(){
stool.run(); stool.run();

View File

@ -16,11 +16,140 @@
stool.run(); stool.run();
},1); },1);
stool.setup(window.setup = function(){ stool.setup(window.setup = function(){
window.BigText = Gun.text.random(1024, 'abcdef'); //window.BigText = Gun.text.random(1024, 'abcdef');
window.MedText = Gun.text.random(200, 'abcdef'); //window.MedText = Gun.text.random(200, 'abcdef');
window.jsonText = JSON.stringify(window.BigText); //window.jsonText = JSON.stringify(window.BigText);
window.radText = Radisk.encode(window.BigText); //window.radText = Radisk.encode(window.BigText);
window.namez = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammamaria","Andy","Anselme","Ardeen","Armand","Ashelman","Aube","Averyl","Baker","Barger","Baten","Bee","Benia","Bernat","Bevers","Bittner","Bobbe","Bonny","Boyce","Breech","Brittaney","Bryn","Burkitt","Cadmann","Campagna","Carlee","Carver","Cavallaro","Chainey","Chaunce","Ching","Cianca","Claudina","Clyve","Colon","Cooke","Corrina","Crawley","Cullie","Dacy","Daniela","Daryn","Deedee","Denie","Devland","Dimitri","Dolphin","Dorinda","Dream","Dunham","Eachelle","Edina","Eisenstark","Elish","Elvis","Eng","Erland","Ethan","Evelyn","Fairman","Faus","Fenner","Fillander","Flip","Foskett","Fredette","Fullerton","Gamali","Gaspar","Gemina","Germana","Gilberto","Giuditta","Goer","Gotcher","Greenstein","Grosvenor","Guthrey","Haldane","Hankins","Harriette","Hayman","Heise","Hepsiba","Hewie","Hiroshi","Holtorf","Howlond","Hurless","Ieso","Ingold","Isidora","Jacoba","Janelle","Jaye","Jennee","Jillana","Johnson","Josy","Justinian","Kannan","Kast","Keeley","Kennett","Kho","Kiran","Knowles","Koser","Kroll","LaMori","Lanctot","Lasky","Laverna","Leff","Leonanie","Lewert","Lilybel","Lissak","Longerich","Lou","Ludeman","Lyman","Madai","Maia","Malvina","Marcy","Maris","Martens","Mathilda","Maye","McLain","Melamie","Meras","Micco","Millburn","Mittel","Montfort","Moth","Mutz","Nananne","Nazler","Nesta","Nicolina","Noellyn","Nuli","Ody","Olympie","Orlena","Other","Pain","Parry","Paynter","Pentheas","Pettifer","Phyllida","Plath","Posehn","Proulx","Quinlan","Raimes","Ras","Redmer","Renelle","Ricard","Rior","Rocky","Ron","Rosetta","Rubia","Ruttger","Salbu","Sandy","Saw","Scholz","Secor","September","Shanleigh","Shenan","Sholes","Sig","Sisely","Soble","Spanos","Stanwinn","Stevie","Stu","Suzanne","Tacy","Tanney","Tekla","Thackeray","Thomasin","Tilla","Tomas","Tracay","Tristis","Ty","Urana","Valdis","Vasta","Vezza","Vitoria","Wait","Warring","Weissmann","Whetstone","Williamson","Wittenburg","Wymore","Yoho","Zamir","Zimmermann"];
window.radix = window.radix || Radix();
window.arr = []; var i = 1000; while(--i){ arr.push(Math.random()) }
window.arrs = arr.slice(0).sort();
window.ALLZ = window.ALLZ || {};
window.namez.forEach(function(v,i){ ALLZ[v] = i });
}); });
/* TEMPORARY COPY OF RADIX UNIT TESTS TO BOOST SPEED */
/* THESE ARE PROBABLY STALE AND NEED TO BE COPIED FROM UNIT TESTS AGAIN */
/*stool.add('map', function(){
Gun.obj.map(ALLZ, function(v,i){
v;
});
});
stool.add('for', function(){
for(var k in ALLZ){
ALLZ[k];
}
});
stool.add('for', function(){
Object.keys(ALLZ).forEach(function(k){
ALLZ[k];
})
});
return;*/
stool.add('1', function(){
var rad = Radix();
rad('asdf.pub', 'yum');
rad('ablah', 'cool');
rad('ab', {yes: 1});
rad('node/circle.bob', 'awesome');
(JSON.stringify(rad('asdf.')) !== JSON.stringify({pub: {'': 'yum'}})) && bada;
(rad('nv/foo.bar') != undefined) && badb;
(JSON.stringify(rad('ab')) != JSON.stringify({yes: 1})) && badc
(JSON.stringify(rad()) != JSON.stringify({"a":{"sdf.pub":{"":"yum"},"b":{"lah":{"":"cool"},"":{"yes":1}}},"node/circle.bob":{"":"awesome"}})) && badd;
});
stool.add('2', function(){
var all = {};
namez.forEach(function(v,i){
v = v.toLowerCase();
all[v] = v;
ALLZ[v] = i;
radix(v, i)
});
(Gun.obj.empty(all) === true) && bad3;
Radix.map(radix, function(v,k){
delete all[k];
});
(Gun.obj.empty(all) !== true) && bad4;
});
stool.add('fast?', function(){
ALLZ['rubia'];
});
stool.add('fastest?', function(){
namez.indexOf('Rubia');
});
stool.add('3', function(){
var all = {};
namez.forEach(function(v,i){
v = v.toLowerCase();
all[v] = v;
//rad(v, i)
});
(Gun.obj.empty(all) === true) && bad5;
Radix.map(radix, function(v,k){
delete all[k];
});
(Gun.obj.empty(all) !== true) && bad6;
});
stool.add('4', function(){
var all = {}, start = 'Warring'.toLowerCase(), end = 'Zamir'.toLowerCase();
namez.forEach(function(v,i){
v = v.toLowerCase();
if(v < start){ return }
if(end < v){ return }
all[v] = v;
//rad(v, i)
});
(Gun.obj.empty(all) === true) && bad7;
Radix.map(radix, function(v,k, a,b){
//if(!all[k]){ throw "out of range!" }
delete all[k];
}, {start: start, end: end});
(Gun.obj.empty(all) !== true) && bad8;
});
stool.add('5', function(){
var all = {}, start = 'Warrinf'.toLowerCase(), end = 'Zamis'.toLowerCase();
namez.forEach(function(v,i){
v = v.toLowerCase();
if(v < start){ return }
if(end < v){ return }
all[v] = v;
//rad(v, i)
});
(Gun.obj.empty(all) === true) && bad9;
Radix.map(radix, function(v,k, a,b){
//if(!all[k]){ throw "out of range!" }
delete all[k];
}, {start: start, end: end});
(Gun.obj.empty(all) !== true) && bad10;
});
stool.add('reverse item', function(){
Radix.map(radix, function(v,k, a,b){
(k !== 'ieso') && badri;
(v !== 96) && badri2;
return true;
}, {reverse: 1, end: 'iesogon'});
});
stool.add('6', function(){
var r = Radix(), tmp;
r('alice', 1);r('bob', 2);r('carl', 3);r('carlo',4);
r('dave', 5);r('zach',6);r('zachary',7);
var by = ['alice','bob','carl','carlo','dave','zach','zachary'];
Gun.obj.map(by, function(k,i){
r(k,i);
});
Radix.map(r, function(v,k, a,b){
(by.pop() !== k) && bad11;
tmp = v;
}, {reverse: 1});
(tmp !== 1) && bad12;
(by.length !== 0) && bad13;
Radix.map(r, function(v,k, a,b){
tmp = v;
});
(tmp !== 7) && bad14;
});
return;
stool.add('JSON encode string', function(){ stool.add('JSON encode string', function(){
JSON.stringify(window.BigText); JSON.stringify(window.BigText);
}); });

101
test/rad/bench.js Normal file
View File

@ -0,0 +1,101 @@
var Gun = (typeof window !== "undefined")? window.Gun : require('../../../gun/gun');
var Radisk = (Gun.window && window.Radisk) || require('../../../gun/lib/radisk');
Gun.TESTING = true;
try{localStorage.clear()}catch(e){}
try{indexedDB.deleteDatabase('radatatest');}catch(e){}
try{indexedDB.deleteDatabase('radata');}catch(e){}
var opt = {localStorage: false};
//opt.chunk = 1024;
opt.store = (Gun.window && window.RindexedDB(opt)) || require('../../../gun/lib/rfs')(opt)
var rad = Radisk(opt);
//var gun = Gun(opt);
var gun = Gun('http://localhost:8765/gun');
Gun.window && (wait.onchange = function(){ spam.wait = this.value })
Gun.window && (burst.onchange = function(){ spam.burst = this.value })
//setTimeout(spam, 1);
function spam(){
//spam.max = 100000; spam.left = spam.max; spam.wait = 1; spam.burst = 250; spam.c = 0; spam.s = (+new Date);
//spam.max = 1000000; spam.left = spam.max; spam.wait = 0; spam.burst = 100; spam.c = 0; spam.s = (+new Date);
//spam.max = 300000; spam.left = spam.max; spam.wait = 1; spam.burst = 5; spam.c = 0; spam.s = (+new Date);
//spam.max = 100000; spam.left = spam.max; spam.wait = 0; spam.burst = 2; spam.c = 0; spam.s = (+new Date);
//spam.max = 100000; spam.left = spam.max; spam.wait = 20; spam.burst = 2; spam.c = 0; spam.s = (+new Date);
//spam.max = 100; spam.left = spam.max; spam.wait = 1; spam.burst = 1; spam.c = 0; spam.s = (+new Date);
spam.max = 100000; spam.left = spam.max; spam.wait = 100; spam.burst = 100; spam.c = 99; spam.s = (+new Date);
var S = +new Date, slow = 0; console.only.i = 1;
var to = setTimeout(function gap(){
if(spam.c >= spam.max){ clearTimeout(to); return; }
setTimeout(gap, Math.random() * 100);
var b = spam.burst;
b = Math.ceil(Math.random() * b);
//console.log('spam', +new Date - S, spam.c); S = +new Date;
if(!b){ b = burst = 1 }
while(b--){ go(++spam.c) }
function go(i){ var d = 0, s = +new Date;
i = Math.random().toString(32).slice(2);
//console.log('go', spam.c, i);
//setTimeout(function(){ var ack = {err: 0, ok: 1};
//loc.put(i, {test: i}, function(err, ok){ var ack = {err: err, ok: ok};
//ind.put(i, {test: i}, function(err, ok){ var ack = {err: err, ok: ok};
//rad(i, {test: i}, function(err, ok){ var ack = {err: err, ok: ok};
var ref = gun.get(i).put({test: i}, function(ack){
var t = (+new Date - s)/1000; if(1 < t){ ++slow; }
if(ack.err){ console.log(ack); }
if(d++){ return }
if(--spam.left){ return }
spam.end = (+new Date) - spam.s;
console.log('DONE!\n', spam.max, 'in', spam.end/1000, 'seconds\n', Math.round(spam.max / (spam.end/1000)), 'per second. Slow:' + slow);
Gun.window && (document.body.style.backgroundColor = 'lime');
});
}
},spam.wait);
setInterval(function(){
if(spam.end === true){ return }
if(spam.end){ spam.end = true }
var t = (+new Date) - spam.s, tmp, sec;
var status = 'saved\n'+ (tmp = (spam.max - spam.left)) +' in '+ (sec = (t/1000)) +' seconds\n'+ Math.round(tmp / sec) +' per second';
(Gun.window && (debugs.innerText = status)) || console.log(status.replace(/\n/ig,' '));
}, 500);
}
!Gun.window && setTimeout(spam,1);
;(function(){
if(!Gun.window){ return }
;(function(){
var f = 'index';
indexedDB.deleteDatabase(f);
var o = indexedDB.open(f, 1), ind = {}, db;
o.onupgradeneeded = function(eve){ (eve.target.result).createObjectStore(f) }
o.onsuccess = function(){ db = o.result }
o.onerror = function(eve){ console.log(eve||1); }
ind.put = function(key, data, cb){
if(!db){ setTimeout(function(){ ind.put(key, data, cb) },9); return }
var tx = db.transaction([f], 'readwrite');
var obj = tx.objectStore(f);
var req = obj.put(data, ''+key);
req.onsuccess = obj.onsuccess = tx.onsuccess = function(){ cb(null, 1) }
req.onabort = obj.onabort = tx.onabort = function(eve){ cb(eve||2) }
req.onerror = obj.onerror = tx.onerror = function(eve){ cb(eve||3) }
}
ind.get = function(key, cb){
if(!db){ setTimeout(function(){ ind.get(key, cb) },9); return }
var tx = db.transaction([f], 'readwrite');
var obj = tx.objectStore(f);
var req = obj.get(''+key);
req.onsuccess = function(){ cb(null, req.result) }
req.onabort = function(eve){ cb(eve||4) }
req.onerror = function(eve){ cb(eve||5) }
}
window.ind = ind;
}());
;(function(){
localStorage.clear();
var ls = localStorage, loc = {};
loc.put = function(key, data, cb){ ls[''+key] = data; cb(null, 1) }
loc.get = function(key, cb){ cb(null, ls[''+key]) }
window.loc = loc;
}());
}());

View File

@ -16,87 +16,6 @@
<div id='debugs'></div> <div id='debugs'></div>
<div id='debug'></div> <div id='debug'></div>
<script> <script src="../../../gun/test/rad/bench.js"></script>
Gun.TESTING = true;
try{localStorage.clear()}catch(e){}
indexedDB.deleteDatabase('radatatest');
var opt = {localStorage: false};
//opt.store = RindexedDB(opt);
var gun = Gun(opt);
</script>
<script>
wait.onchange = function(){ spam.wait = this.value }
burst.onchange = function(){ spam.burst = this.value }
//setTimeout(spam, 1);
function spam(){
//spam.max = 100000; spam.left = spam.max; spam.wait = 1; spam.burst = 250; spam.c = 0; spam.s = (+new Date);
//spam.max = 100000; spam.left = spam.max; spam.wait = 1; spam.burst = 100; spam.c = 0; spam.s = (+new Date);
spam.max = 1000000; spam.left = spam.max; spam.wait = 1; spam.burst = 250; spam.c = 0; spam.s = (+new Date);
//spam.max = 100; spam.left = spam.max; spam.wait = 1; spam.burst = 1; spam.c = 0; spam.s = (+new Date);
var to = setInterval(function(){ var b = spam.burst;
if(spam.c >= spam.max){ clearTimeout(to); return; }
if(!b){ b = burst = 1 }
while(b--){ go(++spam.c) }
function go(i){ var d = 0;
//loc.put(i, {test: i}, function(err, ok){ var ack = {err: err, ok: ok};
//ind.put(i, {test: i}, function(err, ok){ var ack = {err: err, ok: ok};
var ref = gun.get(i).put({test: i}, function(ack){
if(ack.err){ console.log(ack); }
if(d++){ return }
if(--spam.left){ return }
spam.end = (+new Date) - spam.s;
console.log('DONE!\n', spam.max, 'in', spam.end/1000, 'seconds\n', Math.round(spam.max / (spam.end/1000)), 'per second.');
document.body.style.backgroundColor = 'lime';
});
}
},wait);
setInterval(function(){
if(spam.end === true){ return }
if(spam.end){ spam.end = true }
var t = (+new Date) - spam.s, tmp, sec;
var status = 'saved\n'+ (tmp = (spam.max - spam.left)) +' in '+ (sec = (t/1000)) +' seconds\n'+ Math.round(tmp / sec) +' per second';
debugs.innerText = status;
}, 500);
}
</script>
<script>
;(function(){
var f = 'index';
indexedDB.deleteDatabase(f);
var o = indexedDB.open(f, 1), ind = {}, db;
o.onupgradeneeded = function(eve){ (eve.target.result).createObjectStore(f) }
o.onsuccess = function(){ db = o.result }
o.onerror = function(eve){ console.log(eve||1); }
ind.put = function(key, data, cb){
if(!db){ setTimeout(function(){ ind.put(key, data, cb) },9); return }
var tx = db.transaction([f], 'readwrite');
var obj = tx.objectStore(f);
var req = obj.put(data, ''+key);
req.onsuccess = obj.onsuccess = tx.onsuccess = function(){ cb(null, 1) }
req.onabort = obj.onabort = tx.onabort = function(eve){ cb(eve||2) }
req.onerror = obj.onerror = tx.onerror = function(eve){ cb(eve||3) }
}
ind.get = function(key, cb){
if(!db){ setTimeout(function(){ ind.get(key, cb) },9); return }
var tx = db.transaction([f], 'readwrite');
var obj = tx.objectStore(f);
var req = obj.get(''+key);
req.onsuccess = function(){ cb(null, req.result) }
req.onabort = function(eve){ cb(eve||4) }
req.onerror = function(eve){ cb(eve||5) }
}
window.ind = ind;
}());
</script>
<script>
;(function(){
localStorage.clear();
var ls = localStorage, loc = {};
loc.put = function(key, data, cb){ ls[''+key] = data; cb(null, 1) }
loc.get = function(key, cb){ cb(null, ls[''+key]) }
window.loc = loc;
}());
</script>
</body> </body>
</html> </html>

View File

@ -45,6 +45,20 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam
describe('Radix', function(){ describe('Radix', function(){
var radix = Radix(); var radix = Radix();
it('unit', function(){
var rad = Radix();
rad('asdf.pub', 'yum');
rad('ablah', 'cool');
rad('ab', {yes: 1});
rad('node/circle.bob', 'awesome');
expect(Gun.obj.copy(rad('asdf.'))).to.be.eql({pub: {'': 'yum'}});
expect(rad('nv/foo.bar')).to.be(undefined);
expect(rad('ab')).to.eql({yes: 1});
expect(Gun.obj.copy(rad())).to.be.eql({"a":{"sdf.pub":{"":"yum"},"b":{"lah":{"":"cool"},"":{"yes":1}}},"node/circle.bob":{"":"awesome"}});
});
it('radix write read', function(done){ it('radix write read', function(done){
var all = {}; var all = {};
names.forEach(function(v,i){ names.forEach(function(v,i){
@ -111,6 +125,16 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam
done(); done();
}); });
it('radix reverse item', function(done){
var opt = {reverse: 1, end: 'iesogon'};
Radix.map(radix, function(v,k, a,b){
expect(k).to.be('ieso');
expect(v).to.be(96);
return true;
}, opt);
done();
});
it('radix reverse', function(done){ it('radix reverse', function(done){
var r = Radix(), tmp; var r = Radix(), tmp;
r('alice', 1);r('bob', 2);r('carl', 3);r('carlo',4); r('alice', 1);r('bob', 2);r('carl', 3);r('carlo',4);
@ -240,6 +264,7 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam
if(v.indexOf(find) == 0){ all[v] = true } if(v.indexOf(find) == 0){ all[v] = true }
}); });
rad(find, function(err, data, info){ rad(find, function(err, data, info){
expect(data).to.be.ok();
Radix.map(data, function(v,k){ Radix.map(data, function(v,k){
delete all[find+k]; delete all[find+k];
}); });
@ -288,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){ it('read contacts', function(done){
var all = {}, find = 'm', to; var all = {}, find = 'm', to;
names.forEach(function(v){ names.forEach(function(v){
v = v.toLowerCase(); v = v.toLowerCase();
if(v.indexOf(find) == 0){ all[v] = true } if(v.indexOf(find) == 0){ all[v] = true }
}); });
//console.log("<<<<<<<<<");
gun.get('names').get({'.': {'*': find}, '%': 1000 * 100}).once().map().once(function(data, key){ gun.get('names').get({'.': {'*': find}, '%': 1000 * 100}).once().map().once(function(data, key){
expect(data.name).to.be.ok(); expect(data.name).to.be.ok();
expect(data.age).to.be.ok(); expect(data.age).to.be.ok();
@ -304,6 +339,7 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam
done(); done();
},100); },100);
}); });
//console.log(">>>>>>>>>");
}); });
it('read contacts again', function(done){ it('read contacts again', function(done){

View File

@ -3,7 +3,7 @@ var expect = global.expect = require("./expect");
var Radix = require('../lib/radix'); var Radix = require('../lib/radix');
var _ = String.fromCharCode(29); var _ = String.fromCharCode(29);
describe('Radix', function(){ describe('Radix', function(){ // moved to ./rad/rad.js
it('read', function(){ it('read', function(){
var rad = Radix(); var rad = Radix();
rad('asdf.pub', 'yum'); rad('asdf.pub', 'yum');

View File

@ -197,20 +197,20 @@ describe('SEA', function(){
expect(dec.priv).to.be(okey.priv); expect(dec.priv).to.be(okey.priv);
expect(dec.epriv).to.be(okey.epriv); 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 = {}; var graph = {};
graph[tmp] = old; graph[tmp] = old;
var alias = await SEA.verify(old.alias, false); var alias = await SEA.verify(old.alias, false);
expect(alias).to.be('bob'); expect(alias).to.be('bob');
alias = Gun.state.ify({}, tmp, 1, Gun.val.rel.ify(tmp), tmp = '~@'+alias); alias = Gun.state.ify({}, tmp, 1, Gun.val.rel.ify(tmp), tmp = '~@'+alias);
graph[tmp] = alias; graph[tmp] = alias;
gun.on('put', {$: gun, put: graph}); gun.on('test', {$: gun, put: graph});
var use = gun.user(); var use = gun.user();
use.auth('bob', 'test123', function(ack){ use.auth('bob', 'test123', function(ack){
expect(ack.err).to.not.be.ok(); expect(ack.err).to.not.be.ok();
done(); done();
}); });
}())}) }())});
it('legacy []', function(done){ (async function(){ it('legacy []', function(done){ (async function(){
var pw = 'test123'; var pw = 'test123';
@ -218,14 +218,14 @@ describe('SEA', function(){
var old = JSON.parse(atob("eyJfIjp7IiMiOiJ+VThkS0dySFJhX01sMFZ1YlR5OUZBYTlQS1ZlYlh0eTFjS05zWWxnYjduNC5QeVd5cUVVb0ZpYVduUElOV0Nad0xBbzFobjN1MldPWTU3SzZHZnpsNjhVIiwiPiI6eyJwdWIiOjE1NDY5MDI1MDQ5NzksImFsaWFzIjoxNTQ2OTAyNTA0OTc5LCJlcHViIjoxNTQ2OTAyNTA0OTc5LCJhdXRoIjoxNTQ2OTAyNTA0OTc5fX0sInB1YiI6IlU4ZEtHckhSYV9NbDBWdWJUeTlGQWE5UEtWZWJYdHkxY0tOc1lsZ2I3bjQuUHlXeXFFVW9GaWFXblBJTldDWndMQW8xaG4zdTJXT1k1N0s2R2Z6bDY4VSIsImFsaWFzIjoiU0VBe1wibVwiOltcIn5VOGRLR3JIUmFfTWwwVnViVHk5RkFhOVBLVmViWHR5MWNLTnNZbGdiN240LlB5V3lxRVVvRmlhV25QSU5XQ1p3TEFvMWhuM3UyV09ZNTdLNkdmemw2OFVcIixcImFsaWFzXCIsXCJhbGljZVwiLDE1NDY5MDI1MDQ5NzldLFwic1wiOlwienpuaGtIZjhZdFpZM2lGd3FVd0lJUldMTjhZMmlHbmNkcnVTaStGNDNmU1BLYWpSZlI0VzhXVHM4bElSMDBndGJmTWJxS0NjQkpGN3VNSkdGRC9WV2c9PVwifSIsImVwdWIiOiJTRUF7XCJtXCI6W1wiflU4ZEtHckhSYV9NbDBWdWJUeTlGQWE5UEtWZWJYdHkxY0tOc1lsZ2I3bjQuUHlXeXFFVW9GaWFXblBJTldDWndMQW8xaG4zdTJXT1k1N0s2R2Z6bDY4VVwiLFwiZXB1YlwiLFwiRkRzM1VvNTNFZEp6eFNocEpDaVctRGZPQ3lUS0M2U3cxeS1PZVJxam5ZRS5xVGdyYTlFQk1maEpNdVlMVmNaejRZYklLRm85enNBMHpMcV82dEVPMHI0XCIsMTU0NjkwMjUwNDk3OV0sXCJzXCI6XCJPZzRVVjY4OTluSjE4dC9ybWVnV0lkdnNqN01KaEpFc29ranZYQmdteVVRUXVNVjFTdnh4cXJqOFoyV1o2Q25XSkZnTlVDbEVYYWxuMURjUFE3M1R6UT09XCJ9IiwiYXV0aCI6IlNFQXtcIm1cIjpbXCJ+VThkS0dySFJhX01sMFZ1YlR5OUZBYTlQS1ZlYlh0eTFjS05zWWxnYjduNC5QeVd5cUVVb0ZpYVduUElOV0Nad0xBbzFobjN1MldPWTU3SzZHZnpsNjhVXCIsXCJhdXRoXCIsXCJ7XFxcImVrXFxcIjpcXFwiU0VBe1xcXFxcXFwiY3RcXFxcXFxcIjpcXFxcXFxcIi94ZnNPdVNkQUtrNkJiR00zbUV6MnVlSjI3Y0tJNThYMEtUL1FsaExSZXpWcjRkNzVZb2M5QlZNRjkzejl4QXI4N080S2FDNjJUWGVoeERQN0FFa2V4N1paaEpYL2hsVm9kK1FIcVFaaUZMK2lVQzFvL2hpUEJGWElBZmtINGRrcklGOFdqcEVaU3NIVmRSOVRhY2ZzbTB3aHN5NGJXN1ZLSEUySGc9PVxcXFxcXFwiLFxcXFxcXFwiaXZcXFxcXFxcIjpcXFxcXFxcIjhWekduTStEc1lTUktIU3Z4cSszTGc9PVxcXFxcXFwiLFxcXFxcXFwic1xcXFxcXFwiOlxcXFxcXFwibVVSSlJ4TzUvdXM9XFxcXFxcXCJ9XFxcIixcXFwic1xcXCI6XFxcImE1SlA3VFpuVE9jYjEwMGJOejlscEU4dnpqcUE3TWl0NHcwN3pjQTdIOFV0bml1WnVHSmdpZnNNQlFNSGdRdE5cXFwifVwiLDE1NDY5MDI1MDQ5NzldLFwic1wiOlwiSGFzMytJaHFEZTYyN016cElXZVE1cVFrZ2NOMlk3WHRpNGw0TFU3T2JyaktxSlBnSllrVWE2bk9YdlRmQkFzV1BPVzVnemh4Q2RPVGNFQm5icWlpWXc9PVwifSJ9")); 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 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 = {}; var graph = {};
graph[tmp] = old; graph[tmp] = old;
var alias = SEA.opt.unpack(await SEA.verify(old.alias, false), 'alias', old); var alias = SEA.opt.unpack(await SEA.verify(old.alias, false), 'alias', old);
expect(alias).to.be('alice'); expect(alias).to.be('alice');
alias = Gun.state.ify({}, tmp, 1, Gun.val.rel.ify(tmp), tmp = '~@'+alias); alias = Gun.state.ify({}, tmp, 1, Gun.val.rel.ify(tmp), tmp = '~@'+alias);
graph[tmp] = alias; graph[tmp] = alias;
gun.on('put', {$: gun, put: graph}); gun.on('test', {$: gun, put: graph});
var use = gun.user(); var use = gun.user();
use.auth('alice', 'test123', function(ack){ use.auth('alice', 'test123', function(ack){
expect(ack.err).to.not.be.ok(); expect(ack.err).to.not.be.ok();
@ -277,8 +277,30 @@ describe('SEA', function(){
}); });
describe('User', 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){ it('is instantiable', function(done){
gun = Gun(); user.leave();
user = gun.user(); user = gun.user();
done(); done();
}) })
@ -289,7 +311,7 @@ describe('SEA', function(){
expect(ack.err).to.not.be.ok(); expect(ack.err).to.not.be.ok();
done(); done();
}) })
}) });
it('login users', function(done){ it('login users', function(done){
user.auth('carl', 'test123', function(ack){ user.auth('carl', 'test123', function(ack){
@ -312,6 +334,35 @@ describe('SEA', function(){
}); });
}) })
it('save json', function(done){
user.get('a').get('c').put(JSON.stringify({hello:'world'}), function(ack){
expect(ack.err).to.not.be.ok();
done();
});
})
it('read json', function(done){
user.get('a').get('c').once(function(data){
expect(data).to.be(JSON.stringify({hello:'world'}));
done();
});
})
it('save & read encrypt', function(done){
SEA.encrypt('hi', user._.sea, function(data){
var is = data.slice();
user.get('a').get('d').put(data, function(ack){
expect(ack.err).to.not.be.ok();
setTimeout(function(){
user.get('a').get('d').once(function(data){
expect(data).to.be(is);
done();
});
})
});
})
})
it('refresh login', function(done){ it('refresh login', function(done){
this.timeout(9000); this.timeout(9000);
setTimeout(function(){ setTimeout(function(){
@ -347,6 +398,7 @@ describe('SEA', function(){
var ref = user.get('who').get('all').set(msg); var ref = user.get('who').get('all').set(msg);
user.get('who').get('said').set(ref); user.get('who').get('said').set(ref);
user.get('who').get('said').map().once(function(data){ user.get('who').get('said').map().once(function(data){
//console.log("*****", data);
expect(data.what).to.be.ok(); expect(data.what).to.be.ok();
done(); done();
}) })
@ -356,6 +408,7 @@ describe('SEA', function(){
it('set user ref null override', function(done){ it('set user ref null override', function(done){
this.timeout(9000); this.timeout(9000);
var gun = Gun(); var gun = Gun();
//user.leave();
var user = gun.user(); var user = gun.user();
var msg = {what: 'hello world'}; var msg = {what: 'hello world'};
user.create('xavier', 'password'); user.create('xavier', 'password');