mirror of
https://github.com/amark/gun.git
synced 2025-03-30 15:08:33 +00:00
Merge branch 'master' of https://github.com/amark/gun into amark-master
This commit is contained in:
commit
3520f04b36
9
.gitignore
vendored
9
.gitignore
vendored
@ -7,7 +7,14 @@ yarn.lock
|
||||
.idea/
|
||||
*.bak
|
||||
*.new
|
||||
*.log
|
||||
v8.json
|
||||
*.DS_store
|
||||
isolate*.log
|
||||
.esm-cache
|
||||
.sessionStorage
|
||||
.localStorage
|
||||
.localStorage
|
||||
/types/**/*.ts
|
||||
!/types/**/*.d.ts
|
||||
/gun.ts
|
||||
/temp/
|
3
.npmignore
Normal file
3
.npmignore
Normal file
@ -0,0 +1,3 @@
|
||||
*.ts
|
||||
/temp/
|
||||
!*.d.ts
|
2
Procfile
2
Procfile
@ -1 +1 @@
|
||||
web: node --optimize_for_size --gc_interval=100 examples/http.js
|
||||
web: node examples/http.js
|
74
axe.js
74
axe.js
@ -1,22 +1,17 @@
|
||||
;(function(){
|
||||
|
||||
/* 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){
|
||||
return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){
|
||||
arg(mod = {exports: {}});
|
||||
USE[R(path)] = mod.exports;
|
||||
}
|
||||
function R(p){
|
||||
return p.split('/').slice(-1).toString().replace('.js','');
|
||||
}
|
||||
}
|
||||
if(typeof module !== "undefined"){ var common = module }
|
||||
/* UNBUILD */
|
||||
/* UNBUILD */
|
||||
function USE(arg, req){
|
||||
return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){
|
||||
arg(mod = {exports: {}});
|
||||
USE[R(path)] = mod.exports;
|
||||
}
|
||||
function R(p){
|
||||
return p.split('/').slice(-1).toString().replace('.js','');
|
||||
}
|
||||
}
|
||||
if(typeof module !== "undefined"){ var MODULE = module }
|
||||
/* UNBUILD */
|
||||
|
||||
;USE(function(module){
|
||||
if(typeof window !== "undefined"){ module.window = window }
|
||||
@ -24,7 +19,7 @@
|
||||
var AXE = tmp.AXE || function(){};
|
||||
|
||||
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;
|
||||
})(USE, './root');
|
||||
|
||||
@ -32,6 +27,7 @@
|
||||
|
||||
var AXE = USE('./root'), Gun = (AXE.window||{}).Gun || USE('./gun', 1);
|
||||
(Gun.AXE = AXE).GUN = AXE.Gun = Gun;
|
||||
var ST = 0;
|
||||
|
||||
Gun.on('opt', function(at){
|
||||
start(at);
|
||||
@ -73,11 +69,12 @@
|
||||
The mob threshold might be determined by other factors,
|
||||
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);
|
||||
console.log("AXE enabled.");
|
||||
|
||||
function verify(dht, msg) {
|
||||
var S = (+new Date);
|
||||
var puts = Object.keys(msg.put);
|
||||
var soul = puts[0]; /// TODO: verify all souls in puts. Copy the msg only with subscribed souls?
|
||||
var subs = dht(soul);
|
||||
@ -93,17 +90,20 @@
|
||||
if (opt.super) {
|
||||
dht(soul, tmp.join(','));
|
||||
}
|
||||
console.STAT && console.STAT(S, +new Date - S, 'axe verify');
|
||||
}
|
||||
function route(get){ var tmp;
|
||||
if(!get){ return }
|
||||
if('string' != typeof (tmp = get['#'])){ return }
|
||||
return tmp;
|
||||
}
|
||||
// TODO: AXE NEEDS TO BE CHECKED FOR NEW CODE SYSTEM!!!!!!!!!!
|
||||
|
||||
var Rad = (Gun.window||{}).Radix || USE('./lib/radix', 1);
|
||||
at.opt.dht = Rad();
|
||||
at.on('in', function input(msg){
|
||||
var to = this.to, peer = (msg._||{}).via;
|
||||
at.on('in', input);
|
||||
function input(msg){
|
||||
var to = this.to, peer = (msg._||'').via; // warning! mesh.leap could be buggy!
|
||||
var dht = opt.dht;
|
||||
var routes = axe.routes || (axe.routes = {}); // USE RAD INSTEAD! TMP TESTING!
|
||||
var get = msg.get, hash, tmp;
|
||||
@ -128,8 +128,8 @@
|
||||
}
|
||||
}*/
|
||||
}
|
||||
if((tmp = msg['@']) && (tmp = at.dup.s[tmp]) && (tmp = tmp.it)){
|
||||
(tmp = (tmp._||{})).ack = (tmp.ack || 0) + 1; // count remote ACKs to GET.
|
||||
if((tmp = msg['@']) && (tmp = at.dup.s[tmp])){
|
||||
tmp.ack = (tmp.ack || 0) + 1; // count remote ACKs to GET.
|
||||
}
|
||||
to.next(msg);
|
||||
|
||||
@ -144,7 +144,7 @@
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
//try{console.log(req.connection.remoteAddress)}catch(e){};
|
||||
mesh.hear['opt'] = function(msg, peer){
|
||||
@ -189,6 +189,7 @@
|
||||
var peers = routes[hash];
|
||||
function chat(peers, old){ // what about optimizing for directed peers?
|
||||
if(!peers){ return chat(opt.peers) }
|
||||
var S = (+new Date); // STATS!
|
||||
var ids = Object.keys(peers); // TODO: BUG! THIS IS BAD PERFORMANCE!!!!
|
||||
var meta = (msg._||yes);
|
||||
clearTimeout(meta.lack);
|
||||
@ -198,12 +199,13 @@
|
||||
meta.turn = (meta.turn || 0) + 1;
|
||||
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);
|
||||
if(0 < c){
|
||||
if(peers === opt.peers){ return } // prevent infinite lack loop.
|
||||
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(){
|
||||
if(ack && hash && hash === msg['##']){ return }
|
||||
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!
|
||||
if(msg.put){
|
||||
var S = (+new Date); // STATS!
|
||||
var routes = axe.routes || (axe.routes = {}); // USE RAD INSTEAD! TMP TESTING!
|
||||
var peers = {};
|
||||
Gun.obj.map(msg.put, function(node, soul){
|
||||
@ -223,6 +226,7 @@
|
||||
if(!to){ return }
|
||||
Gun.obj.to(to, peers);
|
||||
});
|
||||
console.STAT && (ST = +new Date - S) > 9 && console.STAT(S, ST, 'axe put');
|
||||
mesh.say(msg, peers);
|
||||
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 = {};
|
||||
at.on('hi', function(peer){
|
||||
@ -273,12 +259,14 @@
|
||||
});
|
||||
at.on('bye', function(peer){ this.to.next(peer);
|
||||
if(peer.url){ delete axe.up[peer.id] }
|
||||
var S = +new Date;
|
||||
Gun.obj.map(peer.routes, function(route, hash){
|
||||
delete route[peer.id];
|
||||
if(Gun.obj.empty(route)){
|
||||
delete axe.routes[hash];
|
||||
}
|
||||
});
|
||||
console.STAT && console.STAT(S, +new Date - S, 'axe bye');
|
||||
});
|
||||
|
||||
// handle rebalancing a mob of peers:
|
||||
|
@ -22,6 +22,7 @@
|
||||
<script src="../../../gun/sea.js"></script>
|
||||
|
||||
<script>
|
||||
//var gun = Gun('http://localhost:8765/gun');
|
||||
var gun = Gun(); //Gun(['http://localhost:8765/gun', 'https://guntest.herokuapp.com/gun']);
|
||||
var user = gun.user().recall({sessionStorage: true});
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
;(function(){
|
||||
var cluster = require('cluster');
|
||||
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');
|
||||
|
@ -29,7 +29,7 @@
|
||||
.tall { height: 5em; }
|
||||
</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">
|
||||
|
||||
@ -44,19 +44,24 @@
|
||||
<script src="./jquery.js"></script>
|
||||
<script src="./smoothie.js" charset="utf-8"></script>
|
||||
<script>
|
||||
var up, br = 0, bt = 0;
|
||||
var fetchData = async function(){
|
||||
// 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();
|
||||
$('#block').text(((br += (+new Date - S)/1000) / ++bt).toFixed(1));
|
||||
data.over = (data.over/1000) || 15;
|
||||
$('#peers').text(data.peers.count);
|
||||
$('#time').text((data.peers.time / 1000 / 60).toFixed(0));
|
||||
$('#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);
|
||||
try{ Stats('dam # in').line.append(+new Date, data.dam.in.count); }catch(e){}
|
||||
try{ Stats('dam in MB').line.append(+new Date, data.dam.in.done / 1024 / 1024); }catch(e){}
|
||||
try{ Stats('dam # out').line.append(+new Date, data.dam.out.count); }catch(e){}
|
||||
try{ Stats('dam out MB').line.append(+new Date, data.dam.out.done / 1024 / 1024); }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/s').line.append(+new Date, data.dam.in.done / 1024 / 1024 / data.over); }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/s').line.append(+new Date, data.dam.out.done / 1024 / 1024 / data.over); }catch(e){}
|
||||
|
||||
console.log('data',data);
|
||||
//fetch keys in all, these may be dynamically changing
|
||||
@ -83,16 +88,27 @@
|
||||
}
|
||||
// create a new Series for this key
|
||||
// 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.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.find('span').text(key);
|
||||
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
|
||||
// tbd later
|
||||
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>
|
||||
</body>
|
||||
</html>
|
@ -113,13 +113,12 @@
|
||||
|
||||
<script src="/jquery.js"></script>
|
||||
<script src="/gun.js"></script>
|
||||
<script src="/gun/nts.js"></script>
|
||||
<script>
|
||||
// Check out the interactive tutorial
|
||||
// for how to build a simplified version
|
||||
// of this example: https://scrimba.com/c/cW2Vsa
|
||||
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 typing, throttle;
|
||||
$('.thought__add').on('click', function () {
|
||||
|
4
global.d.ts
vendored
Normal file
4
global.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
import { IGunStatic } from './types/static';
|
||||
declare global {
|
||||
var Gun: IGunStatic;
|
||||
}
|
3
gun.d.ts
vendored
Normal file
3
gun.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
import { IGunStatic } from './types/static';
|
||||
declare const Gun: IGunStatic;
|
||||
export = Gun;
|
504
gun.js
504
gun.js
@ -1,11 +1,6 @@
|
||||
;(function(){
|
||||
|
||||
/* 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){
|
||||
return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){
|
||||
arg(mod = {exports: {}});
|
||||
@ -15,7 +10,7 @@
|
||||
return p.split('/').slice(-1).toString().replace('.js','');
|
||||
}
|
||||
}
|
||||
if(typeof module !== "undefined"){ var common = module }
|
||||
if(typeof module !== "undefined"){ var MODULE = module }
|
||||
/* UNBUILD */
|
||||
|
||||
;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!
|
||||
}
|
||||
;(function(){
|
||||
function empty(v,i){ var n = this.n;
|
||||
function empty(v,i){ var n = this.n, u;
|
||||
if(n && (i === n || (obj_is(n) && obj_has(n, i)))){ return }
|
||||
if(i){ return true }
|
||||
if(u !== i){ return true }
|
||||
}
|
||||
Type.obj.empty = function(o, n){
|
||||
if(!o){ return true }
|
||||
@ -115,20 +110,21 @@
|
||||
} t.r = t.r || [];
|
||||
t.r.push(k);
|
||||
};
|
||||
var keys = Object.keys, map;
|
||||
var keys = Object.keys, map, u;
|
||||
Object.keys = Object.keys || function(o){ return map(o, function(v,k,t){t(k)}) }
|
||||
Type.obj.map = map = function(l, c, _){
|
||||
var u, i = 0, x, r, ll, lle, f = fn_is(c);
|
||||
t.r = null;
|
||||
var u, i = 0, x, r, ll, lle, f = 'function' == typeof c;
|
||||
t.r = u;
|
||||
if(keys && obj_is(l)){
|
||||
ll = keys(l); lle = true;
|
||||
}
|
||||
_ = _ || {};
|
||||
if(list_is(l) || ll){
|
||||
x = (ll || l).length;
|
||||
for(;i < x; i++){
|
||||
var ii = (i + Type.list.index);
|
||||
if(f){
|
||||
r = lle? c.call(_ || this, l[ll[i]], ll[i], t) : c.call(_ || this, l[i], ii, t);
|
||||
r = lle? c.call(_, l[ll[i]], ll[i], t) : c.call(_, l[i], ii, t);
|
||||
if(r !== u){ return r }
|
||||
} else {
|
||||
//if(Type.test.is(c,l[i])){ return ii } // should implement deep equality testing!
|
||||
@ -171,7 +167,7 @@
|
||||
tmp.next(arg);
|
||||
}}
|
||||
}});
|
||||
if(arg instanceof Function){
|
||||
if('function' == typeof arg){
|
||||
var be = {
|
||||
off: onto.off ||
|
||||
(onto.off = function(){
|
||||
@ -200,6 +196,20 @@
|
||||
};
|
||||
})(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){
|
||||
/* Based on the Hypothetical Amnesia Machine thought experiment */
|
||||
function HAM(machineState, incomingState, currentState, incomingValue, currentValue){
|
||||
@ -323,7 +333,7 @@
|
||||
Node.ify = function(obj, o, as){ // returns a node from a shallow object.
|
||||
if(!o){ o = {} }
|
||||
else if(typeof o === 'string'){ o = {soul: o} }
|
||||
else if(o instanceof Function){ o = {map: o} }
|
||||
else if('function' == typeof o){ o = {map: o} }
|
||||
if(o.map){ o.node = o.map.call(as, obj, u, o.node || {}) }
|
||||
if(o.node = Node.soul.ify(o.node || {}, o)){
|
||||
obj_map(obj, map, {o:o,as:as});
|
||||
@ -359,7 +369,7 @@
|
||||
/*if(perf){
|
||||
t = start + perf.now(); // Danger: Accuracy decays significantly over time, even if precise.
|
||||
} else {*/
|
||||
t = time();
|
||||
t = +new Date;
|
||||
//}
|
||||
if(last < t){
|
||||
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 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.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 }
|
||||
return num_is(tmp = tmp[k])? tmp : -Infinity;
|
||||
}
|
||||
@ -383,7 +393,7 @@
|
||||
}
|
||||
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(num_is(s)){
|
||||
tmp[k] = s; // add the valid state.
|
||||
@ -464,9 +474,13 @@
|
||||
if(typeof env === 'string'){
|
||||
env = {soul: env};
|
||||
} else
|
||||
if(env instanceof Function){
|
||||
if('function' == typeof env){
|
||||
env.map = env;
|
||||
}
|
||||
if(typeof as === 'string'){
|
||||
env.soul = env.soul || as;
|
||||
as = u;
|
||||
}
|
||||
if(env.soul){
|
||||
at.link = Val.link.ify(env.soul);
|
||||
}
|
||||
@ -596,9 +610,9 @@
|
||||
USE('./onto'); // depends upon onto!
|
||||
module.exports = function ask(cb, as){
|
||||
if(!this.on){ return }
|
||||
if(!(cb instanceof Function)){
|
||||
if(!('function' == typeof cb)){
|
||||
if(!cb || !as){ return }
|
||||
var id = cb['#'] || cb, tmp = (this.tag||empty)[id];
|
||||
var id = cb['#'] || cb, tmp = (this.tag||'')[id];
|
||||
if(!tmp){ return }
|
||||
tmp = this.on(id, as);
|
||||
clearTimeout(tmp.err);
|
||||
@ -608,7 +622,7 @@
|
||||
if(!cb){ return id }
|
||||
var to = this.on(id, cb, as);
|
||||
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();
|
||||
}, (this.opt||{}).lack || 9000);
|
||||
return id;
|
||||
@ -618,45 +632,43 @@
|
||||
;USE(function(module){
|
||||
var Type = USE('./type');
|
||||
function Dup(opt){
|
||||
var dup = {s:{}};
|
||||
var dup = {s:{}}, s = dup.s;
|
||||
opt = opt || {max: 1000, age: /*1000 * 9};//*/ 1000 * 9 * 3};
|
||||
dup.check = function(id){ var tmp;
|
||||
if(!(tmp = dup.s[id])){ return false }
|
||||
if(tmp.pass){ return tmp.pass = false }
|
||||
return dup.track(id);
|
||||
dup.check = function(id){
|
||||
if(!s[id]){ return false }
|
||||
return dt(id);
|
||||
}
|
||||
dup.track = function(id, pass){
|
||||
var it = dup.s[id] || (dup.s[id] = {});
|
||||
it.was = time_is();
|
||||
if(pass){ it.pass = true }
|
||||
var dt = dup.track = function(id){
|
||||
var it = s[id] || (s[id] = {});
|
||||
it.was = +new Date;
|
||||
if(!dup.to){ dup.to = setTimeout(dup.drop, opt.age + 9) }
|
||||
return it;
|
||||
}
|
||||
dup.drop = function(age){
|
||||
var now = time_is();
|
||||
Type.obj.map(dup.s, function(it, id){
|
||||
var now = +new Date;
|
||||
Type.obj.map(s, function(it, id){
|
||||
if(it && (age || opt.age) > (now - it.was)){ return }
|
||||
Type.obj.del(dup.s, id);
|
||||
delete s[id];
|
||||
});
|
||||
dup.to = null;
|
||||
console.STAT && (age = +new Date - now) > 9 && console.STAT(now, age, 'dup drop');
|
||||
}
|
||||
return dup;
|
||||
}
|
||||
var time_is = Type.time.is;
|
||||
module.exports = Dup;
|
||||
})(USE, './dup');
|
||||
|
||||
;USE(function(module){
|
||||
|
||||
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) }
|
||||
return Gun.create(this._ = {gun: this, $: this, opt: o});
|
||||
return Gun.create(this._ = {$: this, opt: o});
|
||||
}
|
||||
|
||||
Gun.is = function($){ return ($ instanceof Gun) || ($ && $._ && ($ === $._.$)) || false }
|
||||
|
||||
Gun.version = 0.9;
|
||||
Gun.version = 0.2020;
|
||||
|
||||
Gun.chain = Gun.prototype;
|
||||
Gun.chain.toJSON = function(){};
|
||||
@ -671,6 +683,7 @@
|
||||
Gun.on = USE('./onto');
|
||||
Gun.ask = USE('./ask');
|
||||
Gun.dup = USE('./dup');
|
||||
Gun.puff = USE('./puff');
|
||||
|
||||
;(function(){
|
||||
Gun.create = function(at){
|
||||
@ -681,45 +694,131 @@
|
||||
at.dup = at.dup || Gun.dup();
|
||||
var gun = at.$.opt(at.opt);
|
||||
if(!at.once){
|
||||
at.on('in', root, at);
|
||||
at.on('out', root, {at: at, out: root});
|
||||
at.on('in', universe, at);
|
||||
at.on('out', universe, at);
|
||||
at.on('put', map, at);
|
||||
Gun.on('create', at);
|
||||
at.on('create', at);
|
||||
}
|
||||
at.once = 1;
|
||||
return gun;
|
||||
}
|
||||
function root(msg){
|
||||
//add to.next(at); // TODO: MISSING FEATURE!!!
|
||||
var ev = this, as = ev.as, at = as.at || as, gun = at.$, dup, tmp;
|
||||
if(!(tmp = msg['#'])){ tmp = msg['#'] = text_rand(9) }
|
||||
if((dup = at.dup).check(tmp)){
|
||||
if(as.out === msg.out){
|
||||
msg.out = u;
|
||||
ev.to.next(msg);
|
||||
function universe(msg){
|
||||
if(!msg){ return }
|
||||
if(msg.out === universe){ this.to.next(msg); return }
|
||||
var eve = this, as = eve.as, at = as.at || as, gun = at.$, dup = at.dup, tmp, DBG = msg.DBG;
|
||||
(tmp = msg['#']) || (tmp = msg['#'] = text_rand(9));
|
||||
if(dup.check(tmp)){ return } dup.track(tmp);
|
||||
tmp = msg._; msg._ = ('function' == typeof tmp)? tmp : function(){};
|
||||
(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;
|
||||
}
|
||||
dup.track(tmp);
|
||||
if(tmp = msg['@']){ dup.track(tmp) } // HNPERF: Bump original request's liveliness.
|
||||
if(!at.ask(tmp, msg)){
|
||||
if(msg.get){
|
||||
Gun.on.get(msg, gun); //at.on('get', get(msg));
|
||||
}
|
||||
if(msg.put){
|
||||
Gun.on.put(msg, gun); //at.on('put', put(msg));
|
||||
}
|
||||
}
|
||||
ev.to.next(msg);
|
||||
if(!as.out){
|
||||
msg.out = root;
|
||||
at.on('out', msg);
|
||||
}
|
||||
(lot = ctx.lot||'').s++; lot.more++;
|
||||
(ctx.stun || (ctx.stun = {}))[soul+key] = 1;
|
||||
var DBG = ctx.DBG; DBG && (DBG.ph = DBG.ph || +new Date);
|
||||
root.on('put', {'#': msg['#'], '@': msg['@'], put: {'#': soul, '.': key, ':': val, '>': state}, _: ctx});
|
||||
}
|
||||
function map(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);
|
||||
}
|
||||
function chain(ctx, soul, key,val, state){
|
||||
var root = ctx.root, put, tmp;
|
||||
(root.opt||'').super && root.$.get(soul); // I think we need super for now, but since we are rewriting, should consider getting rid of it.
|
||||
if(!root || !(tmp = root.next) || !(tmp = tmp[soul]) || !tmp.$){ return }
|
||||
(put = 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(){
|
||||
Gun.on.put = function(msg, gun){
|
||||
Gun.on._put = function(msg, gun){
|
||||
var at = gun._, ctx = {$: gun, graph: at.graph, put: {}, map: {}, souls: {}, machine: Gun.state(), ack: msg['@'], cat: at, stop: {}};
|
||||
if(!Gun.obj.map(msg.put, perf, ctx)){ return } // HNPERF: performance test, not core code, do not port.
|
||||
if(!Gun.graph.is(msg.put, null, verify, ctx)){ ctx.err = "Error: Invalid graph!" }
|
||||
@ -729,13 +828,12 @@
|
||||
if(u !== ctx.defer){
|
||||
var to = ctx.defer - ctx.machine;
|
||||
setTimeout(function(){
|
||||
Gun.on.put(msg, gun);
|
||||
Gun.on._put(msg, gun);
|
||||
}, to > MD? MD : to ); // setTimeout Max Defer 32bit :(
|
||||
}
|
||||
if(!ctx.diff){ return }
|
||||
at.on('put', obj_to(msg, {put: ctx.diff}));
|
||||
};
|
||||
var MD = 2147483647;
|
||||
function verify(val, key, node, soul){ var ctx = this;
|
||||
var state = Gun.state.is(node, key), tmp;
|
||||
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!
|
||||
|
||||
Gun.on.get = function(msg, gun){
|
||||
Gun.on._get = function(msg, gun){
|
||||
var root = gun._, get = msg.get, soul = get[_soul], node = root.graph[soul], has = get[_has], tmp;
|
||||
var next = root.next || (root.next = {}), at = next[soul];
|
||||
// queue concurrent GETs?
|
||||
var ctx = msg._||'', DBG = ctx.DBG = msg.DBG;
|
||||
DBG && (DBG.g = +new Date);
|
||||
if(!node){ return root.on('get', msg) }
|
||||
if(has){
|
||||
if('string' != typeof has || !obj_has(node, has)){ return root.on('get', msg) }
|
||||
@ -819,16 +919,19 @@
|
||||
}
|
||||
node = Gun.graph.node(node);
|
||||
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', {
|
||||
'@': msg['#'],
|
||||
how: 'mem',
|
||||
put: node,
|
||||
ram: 1,
|
||||
$: gun,
|
||||
_: faith
|
||||
});
|
||||
DBG && (DBG.gm = +new Date);
|
||||
//if(0 < tmp){ return }
|
||||
root.on('get', msg);
|
||||
DBG && (DBG.gd = +new Date);
|
||||
}
|
||||
}());
|
||||
|
||||
@ -854,40 +957,31 @@
|
||||
}, at.opt);
|
||||
Gun.on('opt', at);
|
||||
at.opt.uuid = at.opt.uuid || function(){ return state_lex() + text_rand(12) }
|
||||
Gun.obj.native();
|
||||
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 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 state_lex = Gun.state.lex, _soul = Gun.val.link._, _has = '.', node_ = Gun.node._, rel_is = Gun.val.link.is;
|
||||
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, 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 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 && 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!";
|
||||
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) };
|
||||
|
||||
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;
|
||||
|
||||
(Gun.window||'').console = (Gun.window||'').console || {log: function(){}};
|
||||
(C = console).only = function(i, s){ return (C.only.i && i === C.only.i && C.only.i++) && (C.log.apply(C, arguments) || s) };
|
||||
|
||||
/*Gun.on('opt', function(ctx){ // FOR TESTING PURPOSES
|
||||
this.to.next(ctx);
|
||||
if(ctx.once){ return }
|
||||
ctx.on('node', function(msg){
|
||||
var to = this.to;
|
||||
//Gun.node.is(msg.put, function(v,k){ msg.put[k] = v + v });
|
||||
setTimeout(function(){
|
||||
to.next(msg);
|
||||
},1);
|
||||
});
|
||||
});*/
|
||||
;"Please do not remove welcome log unless you are paying for a monthly sponsorship, thanks!";
|
||||
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!");
|
||||
})(USE, './root');
|
||||
|
||||
;USE(function(module){
|
||||
@ -917,7 +1011,7 @@
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(n instanceof Function){
|
||||
if('function' == typeof n){
|
||||
var yes, tmp = {back: at};
|
||||
while((tmp = tmp.back)
|
||||
&& u === (yes = n(tmp, opt))){}
|
||||
@ -990,6 +1084,12 @@
|
||||
put._ = meta;
|
||||
back.on('in', {$: back.$, put: put, get: back.get})
|
||||
}
|
||||
if(tmp = at.lex){
|
||||
tmp = (tmp._) || (tmp._ = function(){});
|
||||
if(back.ack < tmp.ask){ tmp.ask = back.ack }
|
||||
if(tmp.ask){ return }
|
||||
tmp.ask = 1;
|
||||
}
|
||||
}
|
||||
root.ask(ack, msg);
|
||||
return root.on('in', msg);
|
||||
@ -1204,7 +1304,7 @@
|
||||
Gun.obj.del(at, 'ask'); // TODO: PERFORMANCE? More elegant way?
|
||||
}
|
||||
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(!msg.put || ('string' == typeof get['.'] && !obj_has(tmp, at.get))){
|
||||
if(at.put !== u){ return }
|
||||
@ -1220,7 +1320,7 @@
|
||||
at.on('in', {get: at.get, put: Gun.val.link.ify(get['#']), $: at.$, '@': msg['@']});
|
||||
return;
|
||||
}
|
||||
Gun.on.put(msg, at.root.$);
|
||||
Gun.on.put(msg);
|
||||
}
|
||||
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;
|
||||
@ -1240,7 +1340,7 @@
|
||||
}
|
||||
gun = gun.$;
|
||||
} else
|
||||
if(key instanceof Function){
|
||||
if('function' == typeof key){
|
||||
if(true === cb){ return soul(this, key, cb, as), this }
|
||||
gun = this;
|
||||
var at = gun._, root = at.root, tmp = root.now, ev;
|
||||
@ -1276,7 +1376,7 @@
|
||||
if(tmp = this._.stun){ // TODO: Refactor?
|
||||
gun._.stun = gun._.stun || tmp;
|
||||
}
|
||||
if(cb && cb instanceof Function){
|
||||
if(cb && 'function' == typeof cb){
|
||||
gun.get(cb, as);
|
||||
}
|
||||
return gun;
|
||||
@ -1331,7 +1431,7 @@
|
||||
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 = data) && tmp[rel._] && (tmp = rel.is(tmp))){
|
||||
tmp = ((msg.$$ = at.root.gun.get(tmp))._);
|
||||
tmp = ((msg.$$ = at.root.$.get(tmp))._);
|
||||
if(u !== tmp.put){
|
||||
msg = obj_to(msg, {put: data = tmp.put});
|
||||
}
|
||||
@ -1370,21 +1470,7 @@
|
||||
;USE(function(module){
|
||||
var Gun = USE('./root');
|
||||
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;
|
||||
/*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.data = data;
|
||||
as.via = as.$ = as.via || as.$ || gun;
|
||||
@ -1439,6 +1525,14 @@
|
||||
}
|
||||
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){
|
||||
as.batch = batch;
|
||||
@ -1470,7 +1564,7 @@
|
||||
}
|
||||
|
||||
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(function(){
|
||||
var cat = (as.$.back(-1)._), ask = cat.ask(function(ack){
|
||||
@ -1514,7 +1608,7 @@
|
||||
at.soul(id);
|
||||
return;
|
||||
}
|
||||
(as.stun = as.stun || {})[path] = true;
|
||||
(as.stun = as.stun || {})[path] = 1;
|
||||
ref.get(soul, true, {as: {at: at, as: as, p:path}});
|
||||
}, {as: as, at: at});
|
||||
//if(is){ return {} }
|
||||
@ -1538,7 +1632,7 @@
|
||||
function solve(at, id, cat, as){
|
||||
at.$.back(-1).get(id);
|
||||
cat.soul(id);
|
||||
as.stun[cat.path] = false;
|
||||
delete as.stun[cat.path];
|
||||
as.batch();
|
||||
}
|
||||
|
||||
@ -1595,7 +1689,7 @@
|
||||
}
|
||||
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 node_ = Gun.node._;
|
||||
})(USE, './put');
|
||||
@ -1847,7 +1941,7 @@
|
||||
}
|
||||
|
||||
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['@']){
|
||||
id = msg['#'];
|
||||
Gun.graph.is(msg.put, null, map);
|
||||
@ -1897,10 +1991,11 @@
|
||||
var lS = function(){}, u;
|
||||
root.on('localStorage', disk); // NON-STANDARD EVENT!
|
||||
|
||||
root.on('put', function(at){
|
||||
this.to.next(at);
|
||||
Gun.graph.is(at.put, null, map);
|
||||
if(!at['@']){ acks[at['#']] = true; } // only ack non-acks.
|
||||
root.on('put', function(msg){
|
||||
this.to.next(msg);
|
||||
var put = msg.put, soul = put['#'], key = put['.'], val = put[':'], state = put['>'], tmp;
|
||||
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;
|
||||
if(count >= (opt.batch || 1000)){
|
||||
return flush();
|
||||
@ -1921,7 +2016,7 @@
|
||||
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?
|
||||
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();
|
||||
});
|
||||
@ -1945,6 +2040,10 @@
|
||||
}
|
||||
if(!err && !Gun.obj.empty(opt.peers)){ return } // only ack if there are no peers.
|
||||
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', {
|
||||
'@': id,
|
||||
err: err,
|
||||
@ -1957,148 +2056,176 @@
|
||||
|
||||
;USE(function(module){
|
||||
var Type = USE('../type');
|
||||
var puff = (typeof setImmediate !== "undefined")? setImmediate : setTimeout;
|
||||
|
||||
function Mesh(root){
|
||||
var mesh = function(){};
|
||||
var opt = root.opt || {};
|
||||
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.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!
|
||||
mesh.hear = function(raw, peer){
|
||||
var hear = mesh.hear = function(raw, peer){
|
||||
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('{' != 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){
|
||||
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 }
|
||||
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(){
|
||||
var S; LOG && (S = +new Date); // STATS!
|
||||
var m, c = 100; // hardcoded for now?
|
||||
while(c-- && (m = msg.shift())){
|
||||
mesh.hear(m, peer);
|
||||
}
|
||||
LOG && opt.log(S, +new Date - S, 'batch heard');
|
||||
var S = +new Date;
|
||||
//var P = peer.puff || opt.puff, s = +new Date; // TODO: For future, but in mix?
|
||||
var i = 0, m; while(i < P && (m = msg[i++])){ 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.
|
||||
console.STAT && console.STAT(S, +new Date - S, 'hear loop');
|
||||
flush(peer); // force send all synchronously batched acks.
|
||||
if(!msg.length){ return }
|
||||
puff(go, 0);
|
||||
}());
|
||||
return;
|
||||
}
|
||||
if('{' === tmp || (Type.obj.is(raw) && (msg = raw))){
|
||||
if('{' === tmp || ((raw['#'] || obj_is(raw)) && (msg = raw))){
|
||||
try{msg = msg || JSON.parse(raw);
|
||||
}catch(e){return opt.log('DAM JSON parse error', e)}
|
||||
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(msg.DBG_s){ opt.log(+new Date - msg.DBG_s, 'to hear', id) }
|
||||
if(dup.check(id)){ return }
|
||||
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(tmp = dup_check(id)){ return }
|
||||
/*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(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?
|
||||
}
|
||||
(msg._ = function(){}).via = peer;
|
||||
if(tmp = msg['><']){ (msg._).to = Type.obj.map(tmp.split(','), tomap) }
|
||||
if(msg.dam){
|
||||
if(tmp = mesh.hear[msg.dam]){
|
||||
*/ // TOOD: COME BACK TO THIS LATER!!! IMPORTANT MESH STUFF!!
|
||||
(msg._ = function(){}).via = mesh.leap = peer;
|
||||
if(tmp = msg.dam){
|
||||
if(tmp = mesh.hear[tmp]){
|
||||
tmp(msg, peer, root);
|
||||
}
|
||||
dup_track(id);
|
||||
return;
|
||||
}
|
||||
var S, ST; LOG && (S = +new Date);
|
||||
var S = +new Date, ST;
|
||||
DBG && (DBG.is = S);
|
||||
root.on('in', msg);
|
||||
LOG && !msg.nts && (ST = +new Date - S) > 9 && opt.log(S, ST, 'msg', msg['#']);
|
||||
return;
|
||||
//ECHO = msg.put || ECHO; !(msg.ok !== -3740) && mesh.say({ok: -3740, put: ECHO, '@': msg['#']}, peer);
|
||||
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)};
|
||||
mesh.hear.c = mesh.hear.d = 0;
|
||||
var noop = function(){};
|
||||
hear.c = hear.d = 0;
|
||||
|
||||
;(function(){
|
||||
var SMIA = 0;
|
||||
var message;
|
||||
var message, loop;
|
||||
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(!msg){ return false }
|
||||
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(){});
|
||||
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)){
|
||||
raw = mesh.raw(msg);
|
||||
if(hash && (tmp = msg['@'])){
|
||||
/*if(hash && (tmp = msg['@'])){
|
||||
dup.track(tmp+hash).it = it(msg);
|
||||
if(tmp = (dup.s[tmp]||ok).it){
|
||||
if(hash === tmp['##']){ return false }
|
||||
tmp['##'] = hash;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
//LOG && opt.log(S, +new Date - S, 'say prep');
|
||||
dup.track(id).it = it(msg); // track for 9 seconds, default. Earth<->Mars would need more!
|
||||
if(!peer){ peer = (tmp = dup.s[msg['@']]) && (tmp = tmp.it) && (tmp = tmp._) && (tmp = tmp.via) }
|
||||
S && console.STAT && console.STAT(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.
|
||||
//console.log("SEND!", JSON.parse(JSON.stringify(msg)));
|
||||
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['@']){
|
||||
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;
|
||||
} // 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 || !peer.id){ message = msg;
|
||||
if(!Type.obj.is(peer || opt.peers)){ return false }
|
||||
//var S; LOG && (S = +new Date);
|
||||
Type.obj.map(peer || opt.peers, each); // in case peer is a peer list.
|
||||
//LOG && opt.log(S, +new Date - S, 'say loop');
|
||||
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.
|
||||
;(function go(){
|
||||
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;
|
||||
}
|
||||
// TODO: PERF: consider splitting function here, so say loops do less work.
|
||||
if(!peer.wire && mesh.wire){ mesh.wire(peer) }
|
||||
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(peer.batch){
|
||||
peer.tail = (tmp = peer.tail || 0) + raw.length;
|
||||
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;
|
||||
}
|
||||
flush(peer);
|
||||
}
|
||||
peer.batch = []; // peer.batch = '['; // TODO: Prevent double JSON!
|
||||
setTimeout(function(){flush(peer)}, opt.gap);
|
||||
//peer.batch = [];
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}());
|
||||
|
||||
|
||||
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.
|
||||
function send(raw, peer){ try{
|
||||
var wire = peer.wire;
|
||||
var S, ST; LOG && (S = +new Date);
|
||||
if(peer.say){
|
||||
peer.say(raw);
|
||||
} else
|
||||
if(wire.send){
|
||||
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!
|
||||
}catch(e){
|
||||
(peer.queue = peer.queue || []).push(raw);
|
||||
@ -2110,18 +2237,22 @@
|
||||
if(!msg){ return '' }
|
||||
var meta = (msg._) || {}, put, hash, tmp;
|
||||
if(tmp = meta.raw){ return tmp }
|
||||
if(typeof msg === 'string'){ return msg }
|
||||
if(!msg.dam){
|
||||
if('string' == typeof msg){ return msg }
|
||||
/*if(!msg.dam){ // TOOD: COME BACK TO THIS LATER!!! IMPORTANT MESH STUFF!!
|
||||
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.
|
||||
}); 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?
|
||||
/*if(u !== put){
|
||||
tmp = raw.indexOf(_, raw.indexOf('put'));
|
||||
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
|
||||
}*/
|
||||
// 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.
|
||||
return raw;
|
||||
}
|
||||
@ -2145,12 +2276,12 @@
|
||||
Type.obj.map(tmp, function(msg){
|
||||
send(msg, peer);
|
||||
});
|
||||
Type.obj.native && Type.obj.native(); // dirty place to check if other JS polluted.
|
||||
}
|
||||
mesh.bye = function(peer){
|
||||
root.on('bye', peer);
|
||||
var tmp = +(new Date); tmp = (tmp - (peer.met||tmp));
|
||||
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){
|
||||
@ -2183,6 +2314,7 @@
|
||||
});
|
||||
root.on('hi', function(peer, tmp){ this.to.next(peer);
|
||||
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){
|
||||
tmp = {}; tmp[soul] = root.graph[soul];
|
||||
mesh.say({'##': Type.obj.hash(tmp), get: {'#': soul}}, peer);
|
||||
@ -2214,7 +2346,9 @@
|
||||
|
||||
function sort(k, v){ var tmp;
|
||||
if(!(v instanceof Object)){ return v }
|
||||
var S = +new Date;
|
||||
Type.obj.map(Object.keys(v).sort(), map, {to: tmp = {}, on: v});
|
||||
console.STAT && console.STAT(S, +new Date - S, 'sort');
|
||||
return tmp;
|
||||
}
|
||||
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.
|
||||
|
||||
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){}
|
||||
|
||||
|
3
index.d.ts
vendored
Normal file
3
index.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
import { IGunStatic } from './types/static';
|
||||
declare const Gun: IGunStatic;
|
||||
export = Gun;
|
32
lib/crashed.js
Normal file
32
lib/crashed.js
Normal 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){}
|
||||
}());
|
12
lib/evict.js
12
lib/evict.js
@ -10,26 +10,28 @@
|
||||
try{ heap = require('v8').getHeapStatistics }catch(e){}
|
||||
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);
|
||||
function check(){
|
||||
var used = util().rss / 1024 / 1024;
|
||||
var hused = heap().used_heap_size / 1024 / 1024;
|
||||
//if(hused < ev.max && used < ev.max){ return }
|
||||
if(used < ev.max){ return }
|
||||
console.LOG && Gun.log('evict memory:', hused.toFixed(), used.toFixed(), ev.max.toFixed());
|
||||
if(hused < ev.max && used < ev.max){ return }
|
||||
//if(used < ev.max){ return }
|
||||
console.STAT && console.STAT('evict memory:', hused.toFixed(), used.toFixed(), ev.max.toFixed());
|
||||
GC();//setTimeout(GC, 1);
|
||||
}
|
||||
function GC(){
|
||||
var S = +new Date;
|
||||
var souls = Object.keys(root.graph||empty);
|
||||
var toss = Math.ceil(souls.length * 0.01);
|
||||
//var S = +new Date;
|
||||
Gun.list.map(souls, function(soul){
|
||||
if(--toss < 0){ return }
|
||||
root.gun.get(soul).off();
|
||||
root.$.get(soul).off();
|
||||
});
|
||||
root.dup.drop(1000 * 9); // clean up message tracker
|
||||
console.STAT && console.STAT(S, +new Date - S, 'evict');
|
||||
}
|
||||
/*
|
||||
root.on('in', function(msg){
|
||||
|
@ -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(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);
|
||||
|
682
lib/radisk.js
682
lib/radisk.js
@ -16,10 +16,14 @@
|
||||
opt.code.from = opt.code.from || '!';
|
||||
opt.jsonify = true;
|
||||
|
||||
|
||||
function ename(t){ return encodeURIComponent(t).replace(/\*/g, '%2A') }
|
||||
function atomic(v){ return u !== v && (!v || 'object' != typeof v) }
|
||||
var timediate = (typeof setImmediate === "undefined")? setTimeout : setImmediate;
|
||||
var puff = setTimeout.puff || timediate;
|
||||
var map = Gun.obj.map;
|
||||
var LOG = console.LOG;
|
||||
var obj_empty = Gun.obj.empty;
|
||||
var ST = 0;
|
||||
|
||||
if(!opt.store){
|
||||
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.
|
||||
2. If a batch exceeds a certain number of writes, we should immediately write to disk when physically possible. This caps total performance, but reduces potential loss.
|
||||
*/
|
||||
var r = function(key, val, cb){
|
||||
key = ''+key;
|
||||
if(val instanceof Function){
|
||||
var r = function(key, data, cb, tag, DBG){
|
||||
if('function' === typeof data){
|
||||
var o = cb || {};
|
||||
cb = val;
|
||||
var S; LOG && (S = +new Date);
|
||||
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});
|
||||
cb = data;
|
||||
r.read(key, cb, o, DBG || tag);
|
||||
return;
|
||||
}
|
||||
//console.only(99); var ID = Gun.text.random(2), S = (+new Date); console.log("[[[[[[[[", ID);
|
||||
r.save.ing = [];
|
||||
var ack = cb;
|
||||
var s = function Span(err, ok){
|
||||
var tmp = r.save.ing;
|
||||
//console.only(99); var TMP; console.log("]]]]]]]]", ID, (TMP = +new Date) - S, 'more?', !!tmp);
|
||||
r.save.ing = null;
|
||||
map(tmp, function(q){ // if many, not the most efficient to requeue, but works for now.
|
||||
if(!q || !q.rad || !q.ack){ return }
|
||||
r.save(q.rad, q.ack);
|
||||
})
|
||||
ack(err, ok);
|
||||
};
|
||||
cb = s;
|
||||
s.files = {};
|
||||
s.i = 0; // TODO: revise? Using counter for critical path not my favorite.
|
||||
s.place = function(tree, key){
|
||||
var go = function(file, last){
|
||||
file = decodeURIComponent(file || last || opt.code.from);
|
||||
(s.files[file] || (s.files[file] = Radix()))(key, tree);
|
||||
if(!(--s.i)){ s.go() } // TODO: See above, revise?
|
||||
return true;
|
||||
}
|
||||
go.reverse = 1;
|
||||
go.end = key;
|
||||
r.list(go);
|
||||
++s.i; // TODO: See above, revise?
|
||||
}
|
||||
s.go = function(){
|
||||
if(s.gone){ return } s.gone = true;
|
||||
s.seq = [];
|
||||
map(s.files, function(mem, file){ s.seq.push({file: file, mem: mem}) });
|
||||
s.files = null;
|
||||
s.c = 0;
|
||||
s.merge(s.c);
|
||||
}
|
||||
s.merge = function(i){
|
||||
i = i || 0;
|
||||
//var at = s.seq[i];
|
||||
var at = s.seq.shift();
|
||||
if(!at){
|
||||
if(s.ok){ return cb(null, s.ok) }
|
||||
return cb("No file to save data to.");
|
||||
}
|
||||
var file = at.file, mem = at.mem;
|
||||
r.parse(file, function(err, disk){
|
||||
if(err){ return cb(err) }
|
||||
if(!disk && file !== opt.code.from){ // corrupt file?
|
||||
r.list.bad(file); // remove from dir list
|
||||
r.save(rad, cb); // try again
|
||||
return;
|
||||
}
|
||||
disk = disk || Radix();
|
||||
Radix.map(mem, function(val, key){
|
||||
// PLUGIN: consider adding HAM as an extra layer of protection
|
||||
disk(key, val); // merge batch[key] -> disk[key]
|
||||
});
|
||||
r.write(file, disk, s.pop);
|
||||
})
|
||||
}
|
||||
s.pop = function(err, ok){
|
||||
if(s.err = err || s.err){ return cb(err) }
|
||||
s.ok = ok || s.ok || 1;
|
||||
s.merge(++s.c);
|
||||
}
|
||||
Radix.map(rad, s.place);
|
||||
if(!s.i){ s.go() }; // TODO: See above, revise?
|
||||
//var tmp = (tmp = r.batch = r.batch || {})[key] = tmp[key] || {};
|
||||
//var tmp = (tmp = r.batch = r.batch || {})[key] = data;
|
||||
r.save(key, data, cb, tag, DBG);
|
||||
}
|
||||
r.save = function(key, data, cb, tag, DBG){
|
||||
var s = {key: key}, tags, f, d, q;
|
||||
s.find = function(file){ var tmp;
|
||||
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.mix = function(err, disk){
|
||||
DBG && (DBG.sml = +new Date);
|
||||
DBG && (DBG.sm = DBG.sm || +new Date);
|
||||
if(s.err = err || s.err){ cb(err); return } // TODO: HANDLE BATCH EMIT
|
||||
var file = s.file = (disk||'').file || s.file, tmp;
|
||||
if(!disk && file !== opt.code.from){ // corrupt file?
|
||||
r.find.bad(file); // remove from dir list
|
||||
r.save(key, data, cb, tag); // try again
|
||||
return;
|
||||
}
|
||||
(disk = r.disk[file] || (r.disk[file] = disk || Radix())).file || (disk.file = file);
|
||||
if(opt.compare){
|
||||
data = opt.compare(disk(key), data, key, file);
|
||||
if(u === data){ cb(err, -1); return } // 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.find(key, s.find);
|
||||
}
|
||||
r.disk = {};
|
||||
r.one = {};
|
||||
r.tags = {};
|
||||
|
||||
/*
|
||||
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,
|
||||
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};
|
||||
var f = function Fractal(){};
|
||||
var f = function Fractal(){}, a, b;
|
||||
f.text = '';
|
||||
f.count = 0;
|
||||
f.file = file;
|
||||
f.each = function(val, key, k, pre){
|
||||
//console.log("RAD:::", JSON.stringify([val, key, k, pre]));
|
||||
if(u !== val){ f.count++ }
|
||||
if(opt.pack <= (val||'').length){ return cb("Record too big!"), true }
|
||||
var enc = Radisk.encode(pre.length) +'#'+ Radisk.encode(k) + (u === val? '' : ':'+ Radisk.encode(val)) +'\n';
|
||||
if((opt.chunk < f.text.length + enc.length) && (1 < f.count) && !o.force){
|
||||
f.text = '';
|
||||
f.limit = Math.ceil(f.count/2);
|
||||
f.count = 0;
|
||||
f.sub = Radix();
|
||||
// IMPORTANT: DO THIS IN REVERSE, SO LAST HALF OF DATA MOVED TO NEW FILE BEFORE DROPPING FROM CURRENT FILE.
|
||||
Radix.map(rad, f.slice, {reverse: true});
|
||||
return true;
|
||||
}
|
||||
f.text += enc;
|
||||
}
|
||||
f.file = file = rad.file || (rad.file = file);
|
||||
if(!file){ cb('What file?'); return }
|
||||
f.write = function(){
|
||||
var tmp = ename(file);
|
||||
var S; LOG && (S = +new Date);
|
||||
r.list.add(tmp, function(err){
|
||||
LOG && opt.log(S, +new Date - S, "wrote disk", tmp);
|
||||
if(err){ return cb(err) }
|
||||
opt.store.put(tmp, f.text, cb);
|
||||
var text = rad.raw = f.text;
|
||||
r.disk[file = rad.file || f.file || file] = rad;
|
||||
var S = +new Date;
|
||||
DBG && (DBG.wd = S);
|
||||
r.find.add(file, function add(err){
|
||||
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.sub(f.end = key, val);
|
||||
if(f.limit <= (++f.count)){
|
||||
r.write(key, f.sub, f.swap, o);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
f.swap = function(err){
|
||||
if(err){ return cb(err) }
|
||||
f.sub = Radix();
|
||||
Radix.map(rad, f.stop);
|
||||
r.write(f.file, f.sub, cb, o);
|
||||
if(f.limit <= (++f.count)){ return true }
|
||||
}
|
||||
f.stop = function(val, key){
|
||||
if(key >= f.end){ return true }
|
||||
f.sub(key, val);
|
||||
f.hub(key, val);
|
||||
}
|
||||
if(opt.jsonify){ return r.write.jsonify(f, file, rad, cb, o) } // temporary testing idea
|
||||
f.both = function(err, ok){
|
||||
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() }
|
||||
}
|
||||
|
||||
r.write.jsonify = function(f, file, rad, cb, o){
|
||||
r.write.jsonify = function(f, rad, cb, o, DBG){
|
||||
var raw;
|
||||
var S; LOG && (S = +new Date);
|
||||
var S = +new Date;
|
||||
DBG && (DBG.w = S);
|
||||
try{raw = JSON.stringify(rad.$);
|
||||
}catch(e){ return cb("Record too big!") }
|
||||
LOG && opt.log(S, +new Date - S, "rad stringified JSON");
|
||||
}catch(e){ cb("Cannot radisk!"); return }
|
||||
DBG && (DBG.ws = +new Date);
|
||||
console.STAT && console.STAT(S, +new Date - S, "rad stringified JSON");
|
||||
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.write();
|
||||
@ -264,132 +243,85 @@
|
||||
if(u === o.start && u === o.end){ return tree }
|
||||
if(atomic(tree)){ return tree }
|
||||
var sub = Radix();
|
||||
Radix.map(tree, function(v,k){
|
||||
sub(k,v);
|
||||
}, o);
|
||||
Radix.map(tree, function(v,k){ sub(k,v) }, o); // ONLY PLACE THAT TAKES TREE, maybe reduce API for better perf?
|
||||
return sub('');
|
||||
}
|
||||
|
||||
;(function(){
|
||||
var Q = {};
|
||||
r.read = function(key, cb, o){
|
||||
r.read = function(key, cb, o, DBG){
|
||||
o = o || {};
|
||||
if(RAD && !o.next){ // cache
|
||||
var S; LOG && (S = +new Date);
|
||||
var val = RAD(key);
|
||||
LOG && opt.log(S, +new Date - S, 'rad cached');
|
||||
//if(u !== val){
|
||||
//cb(u, val, o);
|
||||
if(atomic(val)){ cb(u, val, o); return }
|
||||
// if a node is requested and some of it is cached... the other parts might not be.
|
||||
//}
|
||||
var g = {key: key};
|
||||
g.find = function(file){ var tmp;
|
||||
g.file = file || (file = opt.code.from);
|
||||
DBG && (DBG = DBG[file] = DBG[file] || {});
|
||||
DBG && (DBG.rf = DBG.rf || +new Date);
|
||||
if(tmp = r.disk[g.file = file]){ g.check(u, tmp); return }
|
||||
r.parse(file, g.check, u, DBG);
|
||||
}
|
||||
o.span = (u !== o.start) || (u !== o.end); // is there a start or end?
|
||||
var g = function Get(){};
|
||||
g.lex = function(file){ var tmp; // // TODO: this had a out-of-memory crash!
|
||||
file = (u === file)? u : decodeURIComponent(file);
|
||||
tmp = o.next || key || (o.reverse? o.end || '\uffff' : o.start || '');
|
||||
if(!file || (o.reverse? file < tmp : file > tmp)){
|
||||
LOG && opt.log(S, +new Date - S, 'rad read lex'); S = +new Date;
|
||||
if(o.next || o.reverse){ g.file = file }
|
||||
if(tmp = Q[g.file]){
|
||||
tmp.push({key: key, ack: cb, file: g.file, opt: o});
|
||||
return true;
|
||||
}
|
||||
Q[g.file] = [{key: key, ack: cb, file: g.file, opt: o}];
|
||||
if(!g.file){
|
||||
g.it(null, u, {});
|
||||
return true;
|
||||
}
|
||||
r.parse(g.file, g.check);
|
||||
return true;
|
||||
}
|
||||
g.file = file;
|
||||
}
|
||||
g.it = function(err, disk, info){
|
||||
if(g.err = err){ opt.log('err', err) }
|
||||
if(!disk && g.file){ // corrupt file?
|
||||
r.list.bad(g.file); // remove from dir list
|
||||
r.read(key, cb, o); // look again
|
||||
g.get = function(err, disk, info){
|
||||
DBG && (DBG.rgl = +new Date);
|
||||
DBG && (DBG.rg = DBG.rg || +new Date);
|
||||
if(g.err = err || g.err){ cb(err); return }
|
||||
var file = g.file = (disk||'').file || g.file;
|
||||
if(!disk && file !== opt.code.from){ // corrupt file?
|
||||
r.find.bad(file); // remove from dir list
|
||||
r.read(key, cb, o); // try again
|
||||
return;
|
||||
}
|
||||
g.info = info;
|
||||
if(disk){ RAD = g.disk = disk }
|
||||
disk = Q[g.file]; delete Q[g.file];
|
||||
LOG && opt.log(S, +new Date - S, 'rad read in, ack', disk.length); S = +new Date;
|
||||
var STMP = disk.length; // TMP STATS! DELETE!
|
||||
map(disk, g.ack);
|
||||
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);
|
||||
disk = r.disk[file] || (r.disk[file] = disk);
|
||||
if(!disk){ cb(file === opt.code.from? u : "No file!"); return }
|
||||
disk.file || (disk.file = file);
|
||||
var data = r.range(disk(key), o);
|
||||
DBG && (DBG.rr = +new Date);
|
||||
o.unit = disk.unit;
|
||||
o.chunks = (o.chunks || 0) + 1;
|
||||
o.more = true;
|
||||
if((!as.file) // if no more places to look
|
||||
|| (!o.span && last === key) // if our key exactly matches the very last atomic record
|
||||
|| (!o.span && last && last > key && 0 != last.indexOf(key)) // 'zach' may be lexically larger than 'za', but there still might be more, like 'zane' in the 'za' prefix bucket so do not end here.
|
||||
){
|
||||
o.more = u;
|
||||
as.ack(g.err, data, o);
|
||||
return
|
||||
o.parsed = (o.parsed || 0) + ((info||'').parsed||(o.chunks*opt.chunk));
|
||||
o.more = 1;
|
||||
o.next = u;
|
||||
Radix.map(r.list, function next(v,f){
|
||||
if(!v || file === f){ return }
|
||||
o.next = f;
|
||||
return 1;
|
||||
}, o.reverse? {reverse: 1, end: file} : {start: file});
|
||||
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){
|
||||
var S = +new Date;
|
||||
as.ack(g.err, data, o); // more might be coming!
|
||||
LOG && opt.log(S, +new Date - S, 'rad range ack.'); // 1.4s
|
||||
if(o.parsed >= o.limit){ return } // even if more, we've hit our limit, asking peer will need to make a new ask with a new starting point.
|
||||
}
|
||||
o.next = as.file;
|
||||
r.read(key, as.ack, o);
|
||||
if(!o.more){ cb(g.err, data, o); return }
|
||||
if(data){ cb(g.err, data, o) }
|
||||
if(o.parsed >= o.limit){ return }
|
||||
var S = +new Date;
|
||||
DBG && (DBG.rm = S);
|
||||
var next = o.next;
|
||||
timediate(function(){
|
||||
console.STAT && console.STAT(S, +new Date - S, 'rad more');
|
||||
r.parse(next, g.check);
|
||||
},0);
|
||||
}
|
||||
g.check = function(err, disk, info){
|
||||
g.it(err, disk, info);
|
||||
var good = true;
|
||||
g.get(err, disk, info);
|
||||
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){
|
||||
// assume in memory for now, since both write/read already call r.list which will init it.
|
||||
var go = function(file){
|
||||
if(info.file !== file){
|
||||
good = false
|
||||
}
|
||||
return true;
|
||||
}
|
||||
go.reverse = 1;
|
||||
go.end = key;
|
||||
r.list(go);
|
||||
});
|
||||
if(good){ return }
|
||||
var id = Gun.text.random(3); 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);
|
||||
// assume in memory for now, since both write/read already call r.find which will init it.
|
||||
r.find(key, function(file){
|
||||
if((file || (file = opt.code.from)) === info.file){ return }
|
||||
var id = Gun.text.random(3);
|
||||
puff(function(){
|
||||
r.save(key, val, function ack(err, ok){
|
||||
if(err){ r.save(key, val, ack); return } // ad infinitum???
|
||||
// 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?
|
||||
console.STAT && console.STAT("MISLOCATED DATA CORRECTED", id, ename(key), ename(info.file), ename(file));
|
||||
});
|
||||
},0);
|
||||
})
|
||||
});
|
||||
console.STAT && console.STAT(S, +new Date - S, "rad check");
|
||||
}
|
||||
/*g.check2 = function(err, disk, info){
|
||||
if(err || !disk){ return g.it(err, disk, info) }
|
||||
var good = true;
|
||||
Radix.map(disk, function(val, key){
|
||||
// assume in memory for now, since both write/read already call r.list which will init it.
|
||||
var go = function(file){
|
||||
if(info.file !== file){ good = false }
|
||||
return true;
|
||||
}
|
||||
go.reverse = 1;
|
||||
go.end = key;
|
||||
r.list(go);
|
||||
});
|
||||
if(good){ return g.it(err, disk, info) }
|
||||
var id = Gun.text.random(3); console.log("MISLOCATED DATA", id);
|
||||
r.save(disk, function ack(err, ok){
|
||||
if(err){ return r.save(disk, ack) } // ad infinitum???
|
||||
console.log("MISLOCATED CORRECTED", id);
|
||||
r.read(key, cb, o);
|
||||
});
|
||||
}*/
|
||||
if(o.reverse){ g.lex.reverse = true }
|
||||
LOG && (S = +new Date);
|
||||
r.list(g.lex);
|
||||
r.find(key, g.find);
|
||||
}
|
||||
function rev(a,b){ return b }
|
||||
var revo = {reverse: true};
|
||||
@ -403,18 +335,19 @@
|
||||
with how much performance and scale we can get out of only one.
|
||||
Then we can work on the harder problem of being multi-process.
|
||||
*/
|
||||
var RPC = 0;
|
||||
var Q = {}, s = String.fromCharCode(31);
|
||||
r.parse = function(file, cb, raw){ var q;
|
||||
if(q = Q[file]){ return q.push(cb) } q = Q[file] = [cb];
|
||||
var p = function Parse(){}, info = {file: ename(file)};
|
||||
p.disk = Radix();
|
||||
r.parse = function(file, cb, raw, DBG){ var q;
|
||||
if(!file){ return cb(); }
|
||||
if(q = Q[file]){ q.push(cb); return } q = Q[file] = [cb];
|
||||
var p = function Parse(){}, info = {file: file};
|
||||
(p.disk = Radix()).file = file;
|
||||
p.read = function(err, data){ var tmp;
|
||||
LOG && opt.log(S, +new Date - S, 'read disk', ename(file));
|
||||
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];
|
||||
if((p.err = err) || (p.not = !data)){
|
||||
return map(q, p.ack);
|
||||
}
|
||||
if(typeof data !== 'string'){
|
||||
if((p.err = err) || (p.not = !data)){ p.map(q, p.ack); return }
|
||||
if('string' !== typeof data){
|
||||
try{
|
||||
if(opt.pack <= data.length){
|
||||
p.err = "Chunk too big!";
|
||||
@ -422,29 +355,55 @@
|
||||
data = data.toString(); // If it crashes, it crashes here. How!?? We check size first!
|
||||
}
|
||||
}catch(e){ p.err = e }
|
||||
if(p.err){ return map(q, p.ack) }
|
||||
if(p.err){ p.map(q, p.ack); return }
|
||||
}
|
||||
info.parsed = data.length;
|
||||
|
||||
LOG && (S = +new Date);
|
||||
if(opt.jsonify || '{' === data[0]){ // temporary testing idea
|
||||
DBG && (DBG.rpl = info.parsed);
|
||||
DBG && (DBG.rpa = q.length);
|
||||
S = +new Date;
|
||||
if(opt.jsonify || '{' === data[0]){
|
||||
try{
|
||||
var json = JSON.parse(data); // TODO: this caused a out-of-memory crash!
|
||||
p.disk.$ = json;
|
||||
LOG && opt.log(S, +new Date - S, 'rad parsed JSON');
|
||||
map(q, p.ack);
|
||||
console.STAT && (ST = +new Date - S) > 9 && console.STAT(S, ST, 'rad parsed JSON');
|
||||
DBG && (DBG.rpd = +new Date);
|
||||
p.map(q, p.ack); // hmmm, v8 profiler can't see into this cause of try/catch?
|
||||
return;
|
||||
}catch(e){ tmp = e }
|
||||
if('{' === data[0]){
|
||||
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;
|
||||
if(!tmp || 0 !== tmp[1]){
|
||||
p.err = "File '"+file+"' does not have root radix! ";
|
||||
return map(q, p.ack);
|
||||
p.map(q, p.ack);
|
||||
return;
|
||||
}
|
||||
while(tmp){
|
||||
k = v = u;
|
||||
@ -463,9 +422,8 @@
|
||||
if(u !== k && u !== v){ p.disk(pre.join(''), v) }
|
||||
tmp = p.split(tmp[2]);
|
||||
}
|
||||
LOG && opt.log(S, +new Date - S, 'parsed RAD');
|
||||
//cb(err, p.disk);
|
||||
map(q, p.ack);
|
||||
console.STAT && console.STAT(S, +new Date - S, 'parsed RAD');
|
||||
p.map(q, p.ack);
|
||||
};
|
||||
p.split = function(t){
|
||||
if(!t){ return }
|
||||
@ -478,86 +436,76 @@
|
||||
l[2] = t.slice(i + o.i);
|
||||
return l;
|
||||
}
|
||||
p.ack = function(cb){
|
||||
if(!cb){ return }
|
||||
if(p.err || p.not){ return cb(p.err, u, info) }
|
||||
cb(u, p.disk, info);
|
||||
}
|
||||
var S; LOG && (S = +new Date);
|
||||
if(raw){ return p.read(null, raw) }
|
||||
if(r.disk){ raw || (raw = (r.disk[file]||'').raw) }
|
||||
var S = +new Date, SM, SL;
|
||||
DBG && (DBG.rp = S);
|
||||
if(raw){ return puff(function(){ p.read(u, raw) }, 0) }
|
||||
opt.store.get(ename(file), p.read);
|
||||
// TODO: What if memory disk gets filled with updates, and we get an old one back?
|
||||
}
|
||||
}());
|
||||
|
||||
;(function(){
|
||||
var dir, q, f = String.fromCharCode(28), ef = ename(f);
|
||||
r.list = function(cb){
|
||||
if(dir){
|
||||
var last, tmp = {reverse: (cb.reverse)? 1 : 0, start: cb.start, end: cb.end};
|
||||
Radix.map(dir, function(val, key){
|
||||
if(!val){ return }
|
||||
return cb(last = key);
|
||||
}, tmp) || cb(u, last);
|
||||
var dir, f = String.fromCharCode(28), Q;
|
||||
r.find = function(key, cb){
|
||||
if(!dir){
|
||||
if(Q){ Q.push([key, cb]); return } Q = [[key, cb]];
|
||||
r.parse(f, init);
|
||||
return;
|
||||
}
|
||||
if(q){ return q.push(cb) } q = [cb];
|
||||
r.parse(f, r.list.init);
|
||||
Radix.map(r.list = dir, function(val, key){
|
||||
if(!val){ return }
|
||||
return cb(key) || true;
|
||||
}, {reverse: 1, end: key}) || cb(opt.code.from);
|
||||
}
|
||||
r.list.add = function(file, cb){
|
||||
r.find.add = function(file, cb){
|
||||
var has = dir(file);
|
||||
if(has || file === ef){
|
||||
return cb(u, 1);
|
||||
}
|
||||
if(has || file === f){ cb(u, 1); return }
|
||||
dir(file, 1);
|
||||
cb.listed = (cb.listed || 0) + 1;
|
||||
cb.found = (cb.found || 0) + 1;
|
||||
r.write(f, dir, function(err, ok){
|
||||
if(err){ return cb(err) }
|
||||
cb.listed = (cb.listed || 0) - 1;
|
||||
if(cb.listed !== 0){ return }
|
||||
if(err){ cb(err); return }
|
||||
cb.found = (cb.found || 0) - 1;
|
||||
if(0 !== cb.found){ return }
|
||||
cb(u, 1);
|
||||
}, true);
|
||||
}
|
||||
r.list.bad = function(file, cb){
|
||||
dir(ename(file), 0);
|
||||
r.find.bad = function(file, cb){
|
||||
dir(file, 0);
|
||||
r.write(f, dir, cb||noop);
|
||||
}
|
||||
r.list.init = function(err, disk){
|
||||
function init(err, disk){
|
||||
if(err){
|
||||
opt.log('list', err);
|
||||
setTimeout(function(){ r.parse(f, r.list.init) }, 1000);
|
||||
return;
|
||||
}
|
||||
if(disk){
|
||||
r.list.drain(disk);
|
||||
return;
|
||||
}
|
||||
if(!opt.store.list){
|
||||
r.list.drain(Radix());
|
||||
setTimeout(function(){ r.parse(f, init) }, 1000);
|
||||
return;
|
||||
}
|
||||
if(disk){ drain(disk); return }
|
||||
dir = dir || disk || Radix();
|
||||
if(!opt.store.list){ drain(dir); return }
|
||||
// import directory.
|
||||
opt.store.list(function(file){
|
||||
dir = dir || Radix();
|
||||
if(!file){ return r.list.drain(dir) }
|
||||
r.list.add(file, noop);
|
||||
if(!file){ drain(dir); return }
|
||||
r.find.add(file, noop);
|
||||
});
|
||||
}
|
||||
r.list.drain = function(rad, tmp){
|
||||
r.list.dir = dir = rad;
|
||||
tmp = q; q = null;
|
||||
Gun.list.map(tmp, function(cb){
|
||||
r.list(cb);
|
||||
function drain(rad, tmp){
|
||||
dir = dir || rad;
|
||||
dir.file = f;
|
||||
tmp = Q; Q = null;
|
||||
Gun.list.map(tmp, function(arg){
|
||||
r.find(arg[0], arg[1]);
|
||||
});
|
||||
}
|
||||
}());
|
||||
|
||||
try{ !Gun.window && require('./radmigtmp')(r) }catch(e){}
|
||||
|
||||
var noop = function(){}, RAD, u;
|
||||
Radisk.has[opt.file] = r;
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
;(function(){
|
||||
var _ = String.fromCharCode(31), u;
|
||||
Radisk.encode = function(d, o, s){ s = s || _;
|
||||
|
30
lib/radix.js
30
lib/radix.js
@ -2,13 +2,14 @@
|
||||
|
||||
function Radix(){
|
||||
var radix = function(key, val, t){
|
||||
key = ''+key;
|
||||
radix.unit = 0;
|
||||
if(!t && u !== val){
|
||||
radix.last = (key < radix.last)? radix.last : key;
|
||||
radix.last = (''+key < radix.last)? radix.last : ''+key;
|
||||
delete (radix.$||{})[_];
|
||||
}
|
||||
t = t || radix.$ || (radix.$ = {});
|
||||
if(!key && Object.keys(t).length){ return t }
|
||||
key = ''+key;
|
||||
var i = 0, l = key.length-1, k = key[i], at, tmp;
|
||||
while(!(at = t[k]) && i < l){
|
||||
k += key[++i];
|
||||
@ -22,52 +23,61 @@
|
||||
if(kk){
|
||||
if(u === val){
|
||||
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 __ = {};
|
||||
__[s.slice(ii)] = r;
|
||||
ii = key.slice(ii);
|
||||
('' === ii)? (__[''] = val) : ((__[ii] = {})[''] = val);
|
||||
//(__[_] = function $(){ $.sort = Object.keys(__).sort(); return $ }());
|
||||
t[kk] = __;
|
||||
delete t[s];
|
||||
//(t[_] = function $(){ $.sort = Object.keys(t).sort(); return $ }());
|
||||
return true;
|
||||
}
|
||||
})){
|
||||
if(u === val){ return; }
|
||||
(t[k] || (t[k] = {}))[''] = val;
|
||||
//(t[_] = function $(){ $.sort = Object.keys(t).sort(); return $ }());
|
||||
}
|
||||
if(u === val){
|
||||
return tmp;
|
||||
}
|
||||
} else
|
||||
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[_] = function $(){ $.sort = Object.keys(at).sort(); return $ }());
|
||||
} else {
|
||||
if(u !== val){ delete at[_] }
|
||||
//at && (at[_] = function $(){ $.sort = Object.keys(at).sort(); return $ }());
|
||||
return radix(key.slice(++i), val, at || (at = {}));
|
||||
}
|
||||
}
|
||||
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;
|
||||
//!opt && console.log("WHAT IS T?", JSON.stringify(t).length);
|
||||
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();
|
||||
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 i = 0, l = keys.length;
|
||||
for(;i < l; i++){ var key = keys[i], tree = t[key], tmp, p, pt;
|
||||
if(!tree || '' === key || _ === key){ continue }
|
||||
p = pre.slice(); p.push(key);
|
||||
p = pre.slice(0); p.push(key);
|
||||
pt = p.join('');
|
||||
if(u !== start && pt < (start||'').slice(0,pt.length)){ continue }
|
||||
if(u !== end && (end || END) < pt){ continue }
|
||||
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 = tree[''])){
|
||||
@ -85,7 +95,7 @@
|
||||
}
|
||||
pre = p;
|
||||
if(!rev){
|
||||
tmp = map(tree, cb, opt, pre);
|
||||
tmp = rap(tree, cb, opt, pre);
|
||||
if(u !== tmp){ return tmp }
|
||||
}
|
||||
pre.pop();
|
||||
|
22
lib/radmigtmp.js
Normal file
22
lib/radmigtmp.js
Normal 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);
|
||||
})
|
||||
};
|
11
lib/rfs.js
11
lib/rfs.js
@ -10,22 +10,27 @@ function Store(opt){
|
||||
return Store[opt.file];
|
||||
}
|
||||
Store[opt.file] = store;
|
||||
var puts = {};
|
||||
|
||||
// TODO!!! ADD ZLIB INFLATE / DEFLATE COMPRESSION!
|
||||
store.put = function(file, data, cb){
|
||||
puts[file] = data;
|
||||
var random = Math.random().toString(36).slice(-3);
|
||||
var tmp = opt.file+'-'+file+'-'+random+'.tmp';
|
||||
fs.writeFile(tmp, data, function(err, ok){
|
||||
delete puts[file];
|
||||
if(err){ return cb(err) }
|
||||
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){
|
||||
if(err){
|
||||
if('ENOENT' === (err.code||'').toUpperCase()){
|
||||
return cb(null);
|
||||
return cb();
|
||||
}
|
||||
opt.log("ERROR:", err)
|
||||
opt.log("ERROR:", err);
|
||||
}
|
||||
cb(err, data);
|
||||
});
|
||||
|
23
lib/rfsmix.js
Normal file
23
lib/rfsmix.js
Normal 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;
|
||||
}
|
29
lib/rs3.js
29
lib/rs3.js
@ -52,24 +52,26 @@ function Store(opt){
|
||||
store.put = function(file, data, cb){
|
||||
var params = {Bucket: opts.bucket, Key: file, Body: data};
|
||||
//console.log("RS3 PUT ---->", (data||"").slice(0,20));
|
||||
Gun.obj.del(c.g, file);
|
||||
Gun.obj.del(c.l, 1);
|
||||
s3.putObject(params, cb);
|
||||
c.p[file] = data;
|
||||
delete c.g[file];//Gun.obj.del(c.g, file);
|
||||
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){
|
||||
if(c.g[file]){ return c.g[file].push(cb) }
|
||||
store.get = function(file, cb){ var tmp;
|
||||
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 params = {Bucket: opts.bucket, Key: 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));
|
||||
Gun.obj.del(c.g, file);
|
||||
var data, cbe = function(cb){
|
||||
if(!ack){ cb(null); return; }
|
||||
cb(err, data);
|
||||
};
|
||||
data = (ack||{}).Body; //if(data = (ack||{}).Body){ data = data.toString() }
|
||||
Gun.obj.map(cbs, cbe);
|
||||
delete c.g[file];//Gun.obj.del(c.g, file);
|
||||
var data, data = (ack||'').Body;
|
||||
var i = 0, cba; while(cba = cbs[i++]){ cba && cba(err, data) }//Gun.obj.map(cbs, cbe);
|
||||
});
|
||||
};
|
||||
store.list = function(cb, match, params, cbs){
|
||||
@ -98,6 +100,7 @@ function Store(opt){
|
||||
});
|
||||
};
|
||||
//store.list(function(){ return true });
|
||||
require('./rfsmix')(opt, store); // ugly, but gotta move fast for now.
|
||||
return store;
|
||||
}
|
||||
|
||||
|
22
lib/serve.js
22
lib/serve.js
@ -8,10 +8,10 @@ function CDN(dir){
|
||||
req.url = (req.url||'').replace(dot,'').replace(slash,'/');
|
||||
if(serve(req, res)){ return } // filters GUN requests!
|
||||
fs.createReadStream(path.join(dir, req.url)).on('error',function(tmp){ // static files!
|
||||
try{ tmp = fs.readFileSync(path.join(dir, 'index.html')) }catch(e){}
|
||||
try{ res.writeHead(200, {'Content-Type': 'text/html'});
|
||||
res.end(tmp+''); }catch(e){} // or default to index
|
||||
}).pipe(res); // stream
|
||||
fs.readFile(path.join(dir, 'index.html'), function(err, tmp){
|
||||
try{ res.writeHead(200, {'Content-Type': 'text/html'});
|
||||
res.end(tmp+''); }catch(e){} // or default to index
|
||||
})}).pipe(res); // stream
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,13 +27,17 @@ function serve(req, res, next){ var tmp;
|
||||
return true;
|
||||
}
|
||||
if(0 <= req.url.indexOf('gun/')){
|
||||
res.writeHead(200, {'Content-Type': 'text/javascript'});
|
||||
var path = __dirname + '/../' + req.url.split('/').slice(2).join('/'), file;
|
||||
try{file = require('fs').readFileSync(path)}catch(e){}
|
||||
if(file){
|
||||
res.end(file);
|
||||
var path = __dirname + '/../' + req.url.split('/').slice(2).join('/');
|
||||
if('/' === path.slice(-1)){
|
||||
fs.readdir(path, function(err, dir){ res.end((dir || (err && 404))+'') });
|
||||
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 = tmp[(((req.url||'').slice(1)).split('/')[0]||'').split('.')[0]]){
|
||||
|
@ -2,7 +2,7 @@
|
||||
var Gun = require('../gun'), u;
|
||||
Gun.serve = require('./serve');
|
||||
//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){
|
||||
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.
|
||||
@ -15,10 +15,9 @@
|
||||
require('./wire');
|
||||
try{require('../sea');}catch(e){}
|
||||
try{require('../axe');}catch(e){}
|
||||
require('./file');
|
||||
//require('./file');
|
||||
require('./evict');
|
||||
require('./multicast');
|
||||
require('./stats');
|
||||
if('debug' === process.env.GUN_ENV){ require('./debug') }
|
||||
module.exports = Gun;
|
||||
}());
|
||||
|
15
lib/stats.js
15
lib/stats.js
@ -26,10 +26,13 @@ Gun.on('opt', function(root){
|
||||
root.stats.up.count = (root.stats.up.count || 0) + 1;
|
||||
root.stats.stay = root.stats.stay || {};
|
||||
root.stats.gap = {};
|
||||
root.stats.over = +new Date;
|
||||
},1);
|
||||
setInterval(function(){
|
||||
if(!root.stats){ root.stats = {} }
|
||||
var S = +new Date;
|
||||
var stats = root.stats, tmp;
|
||||
stats.over = S - (stats.over||S);
|
||||
(stats.up||{}).time = process.uptime();
|
||||
stats.memory = process.memoryUsage() || {};
|
||||
stats.memory.totalmem = os.totalmem();
|
||||
@ -43,18 +46,19 @@ Gun.on('opt', function(root){
|
||||
stats.all = all;
|
||||
var dam = root.opt.mesh;
|
||||
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
|
||||
stats.peers.time = dam.bye.time || 0;
|
||||
dam.hear.long = [];
|
||||
}
|
||||
var rad = root.opt.store; rad = rad && rad.stats;
|
||||
if(rad){
|
||||
stats.rad = rad;
|
||||
root.opt.store.stats = {get:{time:{}, count:0}, put: {time:{}, count:0}}; // reset
|
||||
}
|
||||
|
||||
fs.writeFile(__dirname+'/../stats.'+root.opt.file, JSON.stringify(stats, null, 2), function(err){});
|
||||
console.STAT && console.STAT(S, +new Date - S, 'stats');
|
||||
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 = {};
|
||||
}, 1000 * 15);
|
||||
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;
|
||||
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){
|
||||
var tmp = (all[c] || (all[c] = []));
|
||||
if(max < tmp.push([a,b])){ all[c] = [] } // reset
|
||||
//return;
|
||||
}
|
||||
if(!console.LOG || log.off){ return }
|
||||
return log.apply(Gun, arguments);
|
||||
}
|
129
lib/store.js
129
lib/store.js
@ -7,51 +7,32 @@ Gun.on('create', function(root){
|
||||
if(false === opt.radisk){ return }
|
||||
var Radisk = (Gun.window && Gun.window.Radisk) || require('./radisk');
|
||||
var Radix = Radisk.Radix;
|
||||
var LOG = console.LOG;
|
||||
var ST = 0;
|
||||
|
||||
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){
|
||||
this.to.next(msg);
|
||||
var id = msg['#'] || Gun.text.random(3), track = !msg['@'], acks = track? 0 : u; // only ack non-acks.
|
||||
var got = (msg._||empty).rad, now = Gun.state();
|
||||
var S = (+new Date); // STATS!
|
||||
Gun.graph.is(msg.put, null, function(val, key, node, soul){
|
||||
if(!track && got){
|
||||
var at = (root.next||empty)[soul];
|
||||
if(!at){ return }
|
||||
if(u !== got['.']){ at = (at.next||empty)[key] }
|
||||
if(!at){ return }
|
||||
at.rad = now;
|
||||
return;
|
||||
}
|
||||
if(track){ ++acks }
|
||||
//console.log('put:', soul, key, val);
|
||||
val = Radisk.encode(val, null, esc)+'>'+Radisk.encode(Gun.state.is(node, key), null, esc);
|
||||
rad(soul+esc+key, val, (track? ack : u));
|
||||
});
|
||||
//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');
|
||||
}
|
||||
if((msg._||'').rad){ return } // don't save what just came from a read.
|
||||
var id = msg['#'], put = msg.put, soul = put['#'], key = put['.'], val = put[':'], state = put['>'], tmp;
|
||||
var DBG = (msg._||'').DBG; DBG && (DBG.sp = DBG.sp || +new Date);
|
||||
var lot = (msg._||'').lot||''; count[id] = (count[id] || 0) + 1;
|
||||
var S = (msg._||'').RPS || ((msg._||'').RPS = +new Date);
|
||||
dare(soul+esc+key, {':': val, '>': state}, dare.one[id] || function(err, ok){
|
||||
DBG && (DBG.spd = DBG.spd || +new Date);
|
||||
console.STAT && console.STAT(S, +new Date - S, 'put');
|
||||
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];
|
||||
if(err){ root.on('in', {'@': id, err: err, DBG: DBG}); return }
|
||||
root.on('in', {'@': id, ok: ok, DBG: DBG});
|
||||
}, id, DBG && (DBG.r = DBG.r || {}));
|
||||
DBG && (DBG.sps = DBG.sps || +new Date);
|
||||
});
|
||||
var count = {}, obj_empty = Gun.obj.empty;
|
||||
|
||||
root.on('get', function(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;
|
||||
if('string' == typeof soul){
|
||||
key = soul;
|
||||
@ -78,60 +59,86 @@ Gun.on('create', function(root){
|
||||
o.limit = (tmp <= (o.pack || (1000 * 100)))? tmp : 1;
|
||||
}
|
||||
if(has['-'] || (soul||{})['-']){ o.reverse = true }
|
||||
if((tmp = (root.next||empty)[soul]) && tmp.put){
|
||||
var SPUT = tmp.put;
|
||||
if((tmp = (root.next||'')[soul]) && tmp.put){
|
||||
if(o.atom){
|
||||
tmp = (tmp.next||empty)[o.atom] ;
|
||||
if(tmp && tmp.rad){
|
||||
LOG && Gun.log("still cached atom", JSON.stringify(get), Object.keys(SPUT||{}).length);
|
||||
return;
|
||||
}
|
||||
tmp = (tmp.next||'')[o.atom] ;
|
||||
if(tmp && tmp.rad){ return }
|
||||
} else
|
||||
if(tmp && tmp.rad){
|
||||
LOG && Gun.log("still cached", JSON.stringify(get), Object.keys(SPUT||{}).length);
|
||||
return;
|
||||
}
|
||||
if(tmp && tmp.rad){ return }
|
||||
}
|
||||
var S = (+new Date), C = 0, CC = 0; // STATS!
|
||||
rad(key||'', function(err, data, o){
|
||||
var now = Gun.state();
|
||||
var S = (+new Date), C = 0, SPT = 0; // STATS!
|
||||
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;
|
||||
opt.store.stats.get.count++;
|
||||
if(err){ opt.store.stats.get.err = err }
|
||||
}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.
|
||||
LOG && Gun.log(S, +new Date - S, 'got'); S = +new Date;
|
||||
if(data){
|
||||
//if(u === data && info.chunks > 1){ return } // if we already sent a chunk, ignore ending empty responses. // this causes tests to fail.
|
||||
console.STAT && console.STAT(S, +new Date - S, 'got', JSON.stringify(key)); S = +new Date;
|
||||
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(o.atom){
|
||||
data = u;
|
||||
} else {
|
||||
Radix.map(data, each)
|
||||
Radix.map(data, each); // IS A RADIX TREE, NOT FUNCTION!
|
||||
}
|
||||
}
|
||||
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.
|
||||
root.on('in', {'@': id, put: graph, '%': o.more? 1 : u, err: err? err : u, _: faith});
|
||||
LOG && Gun.log(S, +new Date - S, 'got sent nodes in graph', Object.keys(graph||{}).length);
|
||||
root.on('in', {'@': id, put: graph, '%': info.more? 1 : u, err: err? err : u, _: faith, DBG: DBG});
|
||||
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!
|
||||
}, o);
|
||||
LOG && Gun.log(S, +new Date - S, 'get call');
|
||||
}, o, DBG && (DBG.r = DBG.r || {}));
|
||||
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!!!!
|
||||
C++; ++CC;
|
||||
C++;
|
||||
if(!val){ return }
|
||||
has = (key+has).split(esc);
|
||||
var soul = has.slice(0,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;
|
||||
var tmp = val.lastIndexOf('>');
|
||||
var state = Radisk.decode(val.slice(tmp+1), null, esc);
|
||||
val = Radisk.decode(val.slice(0,tmp), null, esc);
|
||||
(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!
|
||||
var statg = 0, statp = 0; // STATS!
|
||||
});
|
@ -11,11 +11,12 @@ Gun.chain.promise = function(cb) {
|
||||
};
|
||||
|
||||
// Returns a promise for the data, key of the gun call
|
||||
Gun.chain.then = function() {
|
||||
Gun.chain.then = function(cb) {
|
||||
var gun = this;
|
||||
return (new Promise((res, rej)=>{
|
||||
var p = (new Promise((res, rej)=>{
|
||||
gun.once(function (data, key) {
|
||||
res(data, key); //call resolve when data is returned
|
||||
})
|
||||
}))
|
||||
return cb ? p.then(cb) : p;
|
||||
};
|
||||
|
@ -78,7 +78,7 @@ Gun.on('opt', function(root){
|
||||
opt.mesh.bye(peer);
|
||||
});
|
||||
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?
|
||||
});
|
||||
}
|
||||
|
||||
|
1131
package-lock.json
generated
1131
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
20
package.json
20
package.json
@ -1,13 +1,15 @@
|
||||
{
|
||||
"name": "gun",
|
||||
"version": "0.2020.116",
|
||||
"version": "0.2020.301",
|
||||
"description": "A realtime, decentralized, offline-first, graph data synchronization engine.",
|
||||
"types": "index.d.ts",
|
||||
"main": "index.js",
|
||||
"browser": "browser.js",
|
||||
"ios": "browser.ios.js",
|
||||
"android": "browser.android.js",
|
||||
"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",
|
||||
"prepublishOnly": "npm run unbuild",
|
||||
"test": "mocha",
|
||||
@ -55,19 +57,27 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"buffer": "^5.4.3",
|
||||
"ws": "^7.1.2"
|
||||
"ws": "^7.2.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"text-encoding": "^0.7.0",
|
||||
"emailjs": "^2.2.0",
|
||||
"isomorphic-webcrypto": "^2.3.2",
|
||||
"emailjs": "^2.2.0"
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": "^5.0.2",
|
||||
"text-encoding": "^0.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/ip": "^1.1.0",
|
||||
"@types/mocha": "^7.0.1",
|
||||
"@types/node": "^13.7.0",
|
||||
"@types/uglify-js": "^3.0.4",
|
||||
"@types/ws": "^7.2.1",
|
||||
"aws-sdk": "^2.528.0",
|
||||
"ip": "^1.1.5",
|
||||
"mocha": "^6.2.0",
|
||||
"panic-manager": "^1.2.0",
|
||||
"panic-server": "^1.1.1",
|
||||
"ts-type": "^1.2.13",
|
||||
"uglify-js": "^3.6.0"
|
||||
}
|
||||
}
|
||||
|
133
sea.js
133
sea.js
@ -1,12 +1,6 @@
|
||||
;(function(){
|
||||
|
||||
/* 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){
|
||||
return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){
|
||||
arg(mod = {exports: {}});
|
||||
@ -16,7 +10,7 @@
|
||||
return p.split('/').slice(-1).toString().replace('.js','');
|
||||
}
|
||||
}
|
||||
if(typeof module !== "undefined"){ var common = module }
|
||||
if(typeof module !== "undefined"){ var MODULE = module }
|
||||
/* UNBUILD */
|
||||
|
||||
;USE(function(module){
|
||||
@ -32,7 +26,7 @@
|
||||
|
||||
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;
|
||||
})(USE, './root');
|
||||
|
||||
@ -50,10 +44,10 @@
|
||||
;USE(function(module){
|
||||
if(typeof btoa === "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"); };
|
||||
root.atob = function (data) { return Buffer.from(data, "base64").toString("binary"); };
|
||||
global.btoa = function (data) { return Buffer.from(data, "binary").toString("base64"); };
|
||||
global.atob = function (data) { return Buffer.from(data, "base64").toString("binary"); };
|
||||
}
|
||||
})(USE, './base64');
|
||||
|
||||
@ -275,13 +269,13 @@
|
||||
cb = salt;
|
||||
salt = u;
|
||||
}
|
||||
salt = salt || shim.random(9);
|
||||
data = (typeof data == 'string')? data : JSON.stringify(data);
|
||||
if('sha' === (opt.name||'').toLowerCase().slice(0,3)){
|
||||
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)} }
|
||||
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 work = await (shim.ossl || shim.subtle).deriveBits({
|
||||
name: opt.name || 'PBKDF2',
|
||||
@ -439,7 +433,7 @@
|
||||
opt = opt || {};
|
||||
// SEA.I // verify is free! Requires no user permission.
|
||||
var pub = pair.pub || pair;
|
||||
var key = SEA.opt.slow_leak? await SEA.opt.slow_leak(pub) : await (shim.ossl || shim.subtle).importKey('jwk', jwk, {name: 'ECDSA', namedCurve: 'P-256'}, false, ['verify']);
|
||||
var key = SEA.opt.slow_leak? await SEA.opt.slow_leak(pub) : await (shim.ossl || shim.subtle).importKey('jwk', S.jwk(pub), {name: 'ECDSA', namedCurve: 'P-256'}, false, ['verify']);
|
||||
var hash = await sha(json.m);
|
||||
var buf, sig, check, tmp; try{
|
||||
buf = shim.Buffer.from(json.s, opt.encode || 'base64'); // NEW DEFAULT!
|
||||
@ -474,9 +468,11 @@
|
||||
return knownKeys[pair];
|
||||
};
|
||||
|
||||
|
||||
var O = SEA.opt;
|
||||
SEA.opt.fall_verify = async function(data, pair, cb, opt, f){
|
||||
if(f === SEA.opt.fallback){ throw "Signature did not match" } f = f || 1;
|
||||
var tmp = data||'';
|
||||
data = SEA.opt.unpack(data) || data;
|
||||
var json = S.parse(data), pub = pair.pub || pair, key = await SEA.opt.slow_leak(pub);
|
||||
var hash = (f <= SEA.opt.fallback)? shim.Buffer.from(await shim.subtle.digest({name: 'SHA-256'}, new shim.TextEncoder().encode(S.parse(json.m)))) : await sha(json.m); // this line is old bad buggy code but necessary for old compatibility.
|
||||
var buf; var sig; var check; try{
|
||||
@ -491,6 +487,7 @@
|
||||
if(!check){ throw "Signature did not match." }
|
||||
}
|
||||
var r = check? S.parse(json.m) : u;
|
||||
O.fall_soul = tmp['#']; O.fall_key = tmp['.']; O.fall_val = data; O.fall_state = tmp['>'];
|
||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||
return r;
|
||||
}
|
||||
@ -652,7 +649,7 @@
|
||||
|
||||
;USE(function(module){
|
||||
var shim = USE('./shim');
|
||||
// Practical examples about usage found from ./test/common.js
|
||||
// Practical examples about usage found in tests.
|
||||
var SEA = USE('./root');
|
||||
SEA.work = USE('./work');
|
||||
SEA.sign = USE('./sign');
|
||||
@ -701,7 +698,7 @@
|
||||
// But all other behavior needs to be equally easy, like opinionated ways of
|
||||
// Adding friends (trusted public keys), sending private messages, etc.
|
||||
// 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;
|
||||
SEA.GUN = SEA.Gun = Gun;
|
||||
|
||||
@ -1128,17 +1125,17 @@
|
||||
})(USE, './create');
|
||||
|
||||
;USE(function(module){
|
||||
const SEA = USE('./sea')
|
||||
const Gun = SEA.Gun;
|
||||
var SEA = USE('./sea')
|
||||
var Gun = SEA.Gun;
|
||||
// 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)
|
||||
Gun.on('opt', function(at){
|
||||
if(!at.sea){ // only add SEA once per instance, on the "at" context.
|
||||
at.sea = {own: {}};
|
||||
at.on('in', security, at); // now listen to all input data, acting as a firewall.
|
||||
at.on('out', signature, at); // and output listeners, to encrypt outgoing data.
|
||||
at.on('node', each, at);
|
||||
//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('put', check, at);
|
||||
}
|
||||
this.to.next(at); // make sure to call the "next" middleware adapter.
|
||||
});
|
||||
@ -1187,6 +1184,85 @@
|
||||
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.
|
||||
// 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:
|
||||
@ -1212,6 +1288,11 @@
|
||||
}
|
||||
}
|
||||
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!!!
|
||||
var check = {}, each = {}, u;
|
||||
each.node = function(node, soul){
|
||||
@ -1342,6 +1423,7 @@
|
||||
if(!s || !(s = s[1])){ return }
|
||||
s = s.split('.');
|
||||
if(!s || 2 > s.length){ return }
|
||||
if('@' === (s[0]||'')[0]){ return } // TODO: Should check ~X.Y. are alphanumeric, not just not @.
|
||||
s = s.slice(0,2).join('.');
|
||||
return s;
|
||||
}
|
||||
@ -1350,16 +1432,18 @@
|
||||
}
|
||||
SEA.opt.pack = function(d,k, n,s){ // pack for verifying
|
||||
if(SEA.opt.check(d)){ return d }
|
||||
var meta = (Gun.obj.ify(d)||noop), sig = meta['~'];
|
||||
return sig? {m: {'#':s,'.':k,':':meta[':'],'>':Gun.state.is(n, k)}, s: sig} : d;
|
||||
var meta = (Gun.obj.ify((d && d[':'])||d)||''), sig = meta['~'];
|
||||
return sig? {m: {'#':s||d['#'],'.':k||d['.'],':':meta[':'],'>':d['>']||Gun.state.is(n, k)}, s: sig} : d;
|
||||
}
|
||||
var O = SEA.opt;
|
||||
SEA.opt.unpack = function(d, k, n){ var tmp;
|
||||
if(u === d){ return }
|
||||
if(d && (u !== (tmp = d[':']))){ return tmp }
|
||||
k = k || O.fall_key; if(!n && O.fall_val){ n = {}; n[k] = O.fall_val }
|
||||
if(!k || !n){ return }
|
||||
if(d === n[k]){ return d }
|
||||
if(!SEA.opt.check(n[k])){ return d }
|
||||
var soul = Gun.node.soul(n), s = Gun.state.is(n, k);
|
||||
var soul = Gun.node.soul(n) || O.fall_soul, s = Gun.state.is(n, k) || O.fall_state;
|
||||
if(d && 4 === d.length && soul === d[0] && k === d[1] && fl(s) === fl(d[3])){
|
||||
return d[2];
|
||||
}
|
||||
@ -1371,6 +1455,7 @@
|
||||
var noop = function(){}, u;
|
||||
var fl = Math.floor; // TODO: Still need to fix inconsistent state issue.
|
||||
var rel_is = Gun.val.rel.is;
|
||||
var obj_ify = Gun.obj.ify;
|
||||
// TODO: Potential bug? If pub/priv key starts with `-`? IDK how possible.
|
||||
|
||||
})(USE, './index');
|
||||
|
50
test/bug/322.js
Executable file
50
test/bug/322.js
Executable 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
59
test/bug/686.js
Executable 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
46
test/bug/783.js
Normal 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();
|
||||
});
|
||||
});
|
||||
});
|
||||
} );
|
||||
});
|
193
test/common.js
193
test/common.js
@ -1279,6 +1279,14 @@ describe('Gun', function(){
|
||||
this.to.next(root);
|
||||
});*/
|
||||
}
|
||||
Gun.on('create', function(root){
|
||||
root.on('test', function(msg){
|
||||
var put = msg.put;
|
||||
this.to.next(msg);
|
||||
root.on('out', msg);
|
||||
Gun.graph.is(put, function(n,s){ root.$.get(s).off() });
|
||||
})
|
||||
})
|
||||
var gun = Gun();
|
||||
|
||||
it.skip('gun chain separation', function(done){ // TODO: UNDO!
|
||||
@ -1397,8 +1405,7 @@ describe('Gun', function(){
|
||||
- Performant read lock on write contexts.
|
||||
- Proxying event across maps.
|
||||
*/
|
||||
var s = Gun.state.map();s.soul = 'u/m';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
alice: {
|
||||
age: 26,
|
||||
name: "Alice",
|
||||
@ -1409,7 +1416,7 @@ describe('Gun', function(){
|
||||
name: "Bob!",
|
||||
pet: {b:2, name: "Frisky"}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'u/m')});
|
||||
var check = {}, count = {};
|
||||
gun.get('u/m').map().on(function(v,f){
|
||||
check[f] = v;
|
||||
@ -1433,8 +1440,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('uncached synchronous map get on', function(done){
|
||||
var s = Gun.state.map();s.soul = 'u/m/p';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
alice: {
|
||||
age: 26,
|
||||
name: "alice",
|
||||
@ -1445,7 +1451,7 @@ describe('Gun', function(){
|
||||
name: "bob",
|
||||
pet: {b:2, name: "Frisky"}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'u/m/p')});
|
||||
var check = {}, count = {};
|
||||
gun.get('u/m/p').map().get('name').on(function(v,f){
|
||||
//console.log("*****************", f, v);
|
||||
@ -1465,8 +1471,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('uncached synchronous map get on node', function(done){
|
||||
var s = Gun.state.map();s.soul = 'u/m/p/n';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
alice: {
|
||||
age: 26,
|
||||
name: "alice",
|
||||
@ -1477,7 +1482,7 @@ describe('Gun', function(){
|
||||
name: "bob",
|
||||
pet: {b:2, name: "Frisky"}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'u/m/p/n')});
|
||||
var check = {}, count = {};
|
||||
gun.get('u/m/p/n').map().get('pet').on(function(v,f){
|
||||
//console.log("********************", f,v);
|
||||
@ -1500,8 +1505,7 @@ describe('Gun', function(){
|
||||
|
||||
it('uncached synchronous map get on node get', function(done){
|
||||
var gun = Gun();
|
||||
var s = Gun.state.map();s.soul = 'u/m/p/n/p';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
alice: {
|
||||
age: 26,
|
||||
name: "alice",
|
||||
@ -1512,7 +1516,7 @@ describe('Gun', function(){
|
||||
name: "bob",
|
||||
pet: {b:2, name: "Frisky"}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'u/m/p/n/p')});
|
||||
var check = {}, count = {};
|
||||
//console.debug.i=1;console.log('-------------------');
|
||||
gun.get('u/m/p/n/p').map().get('pet').get('name').on(function(v,f){
|
||||
@ -1539,8 +1543,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('uncached synchronous map on mutate', function(done){
|
||||
var s = Gun.state.map();s.soul = 'u/m/mutate';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
alice: {
|
||||
age: 26,
|
||||
name: "Alice",
|
||||
@ -1551,7 +1554,7 @@ describe('Gun', function(){
|
||||
name: "Bob",
|
||||
pet: {b:2, name: "Frisky"}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'u/m/mutate')});
|
||||
var check = {}, count = {};
|
||||
gun.get('u/m/mutate').map().get('name').get(function(at,ev){
|
||||
var e = at.err, v = at.put, f = at.get;
|
||||
@ -1575,8 +1578,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('uncached synchronous map on mutate node', function(done){
|
||||
var s = Gun.state.map();s.soul = 'u/m/mutate/n';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
alice: {_:{'#':'umaliceo'},
|
||||
age: 26,
|
||||
name: "Alice",
|
||||
@ -1587,7 +1589,7 @@ describe('Gun', function(){
|
||||
name: "Bob",
|
||||
pet: {b:2, name: "Frisky"}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'u/m/mutate/n')});
|
||||
var check = {}, count = {};
|
||||
gun.get('u/m/mutate/n').map().get('name').get(function(at,ev){
|
||||
var e = at.err, v = at.put, f = at.get;
|
||||
@ -1622,8 +1624,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('uncached synchronous map on mutate node uncached', function(done){
|
||||
var s = Gun.state.map();s.soul = 'u/m/mutate/n/u';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
alice: {_:{'#':'umaliceo1'},
|
||||
age: 26,
|
||||
name: "Alice",
|
||||
@ -1634,7 +1635,7 @@ describe('Gun', function(){
|
||||
name: "Bob",
|
||||
pet: {b:2, name: "Frisky"}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'u/m/mutate/n/u')});
|
||||
var check = {}, count = {};
|
||||
gun.get('u/m/mutate/n/u').map().on(function(v,f){
|
||||
check[v.name] = f;
|
||||
@ -1654,10 +1655,9 @@ describe('Gun', function(){
|
||||
}
|
||||
});
|
||||
setTimeout(function(){
|
||||
var s = Gun.state.map();s.soul = 'u/m/m/n/u/soul';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
name: 'Alice Zzxyz'
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'u/m/m/n/u/soul')});
|
||||
//console.debug.i=1;console.log("---------------");
|
||||
gun.get('u/m/mutate/n/u').put({
|
||||
alice: {'#':'u/m/m/n/u/soul'},
|
||||
@ -1679,8 +1679,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('uncached synchronous map on get mutate node uncached', function(done){
|
||||
var s = Gun.state.map();s.soul = 'u/m/p/mutate/n/u';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
alice: {_:{'#':'umaliceo2'},
|
||||
age: 26,
|
||||
name: "Alice",
|
||||
@ -1691,7 +1690,7 @@ describe('Gun', function(){
|
||||
name: "Bob",
|
||||
pet: {b:2, name: "Frisky"}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'u/m/p/mutate/n/u')});
|
||||
var check = {}, count = {};
|
||||
gun.get('u/m/p/mutate/n/u').map().get('name').on(function(v,f){
|
||||
check[v] = f;
|
||||
@ -1712,10 +1711,9 @@ describe('Gun', function(){
|
||||
}
|
||||
});
|
||||
setTimeout(function(){
|
||||
var s = Gun.state.map();s.soul = 'u/m/p/m/n/u/soul';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
name: 'Alice Zzxyz', age: 34
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'u/m/p/m/n/u/soul')});
|
||||
gun.get('u/m/p/mutate/n/u').put({
|
||||
alice: {'#':'u/m/p/m/n/u/soul'},
|
||||
});
|
||||
@ -1729,8 +1727,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('uncached synchronous map on get node mutate node uncached', function(done){
|
||||
var s = Gun.state.map();s.soul = 'u/m/p/n/mutate/n/u';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
alice: {_:{'#':'umaliceo3'},
|
||||
age: 26,
|
||||
name: "Alice",
|
||||
@ -1741,7 +1738,7 @@ describe('Gun', function(){
|
||||
name: "Bob",
|
||||
pet: {b:2, name: "Frisky"}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'u/m/p/n/mutate/n/u')});
|
||||
var check = {}, count = {};
|
||||
gun.get('u/m/p/n/mutate/n/u').map().get('pet').on(function(v,f){
|
||||
check[v.name] = f;
|
||||
@ -1760,11 +1757,10 @@ describe('Gun', function(){
|
||||
}
|
||||
});
|
||||
setTimeout(function(){
|
||||
var s = Gun.state.map();s.soul = 'alice/fuzz/soul';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
name: 'Alice Zzxyz', age: 34,
|
||||
pet: {c:3, name: "Fuzzball"}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'alice/fuzz/soul')});
|
||||
gun.get('u/m/p/n/mutate/n/u').put({
|
||||
alice: {'#':'alice/fuzz/soul'},
|
||||
});
|
||||
@ -2844,15 +2840,13 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('get node after recursive field', function(done){
|
||||
var s = Gun.state.map();s.soul = 'node/circle';
|
||||
var bob = {age: 29, name: "Bob!"};
|
||||
var cat = {name: "Fluffy", species: "kitty"};
|
||||
var user = {bob: bob};
|
||||
bob.pet = cat;
|
||||
cat.slave = bob;
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify(user, s)});
|
||||
//console.debug.i=1;console.log("-------------");
|
||||
gun.get(s.soul).get('bob').get('pet').get('slave').once(function(data){
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify(user, Gun.state.map(), 'node/circle')});
|
||||
gun.get('node/circle').get('bob').get('pet').get('slave').once(function(data){
|
||||
//clearTimeout(done.to);
|
||||
//setTimeout(function(){
|
||||
//console.log("*****************", data);return;
|
||||
@ -2945,7 +2939,9 @@ describe('Gun', function(){
|
||||
list.set(gun.get('dave').put({name: "Dave", group: "awesome", married: true}));
|
||||
|
||||
var check = {}, count = {};
|
||||
list.map().once(function(data, id){
|
||||
//console.log("===============================");
|
||||
//console.only.i=1;
|
||||
list.map().on(function(data, id){
|
||||
//console.log("***************", id, data);
|
||||
check[id] = data;
|
||||
count[id] = (count[id] || 0) + 1;
|
||||
@ -3090,13 +3086,12 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('get get get any parallel', function(done){
|
||||
var s = Gun.state.map();s.soul = 'parallel';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
bob: {
|
||||
age: 29,
|
||||
name: "Bob!"
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'parallel')});
|
||||
gun.get('parallel').get('bob').get('age').get(function(at, ev){
|
||||
var err = at.err, data = at.put, field = at.get;
|
||||
//console.log("***** age", data, at.$._.ack);//return;
|
||||
@ -3117,13 +3112,12 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('get get get any later', function(done){
|
||||
var s = Gun.state.map();s.soul = 'parallel/later';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
bob: {_:{'#':'ddfsa'},
|
||||
age: 29,
|
||||
name: "Bob!"
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'parallel/later')});
|
||||
gun.get('parallel/later').get('bob').get('age').get(function(at, ev){
|
||||
var err = at.err, data = at.put, field = at.get;
|
||||
//console.log("***** age", data);
|
||||
@ -3189,11 +3183,10 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('get any any', function(done){
|
||||
var s = Gun.state.map();s.soul = 'full';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
hello: 'world',
|
||||
goodbye: 'mars'
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'full')});
|
||||
gun.get('full').get(function(at, ev){
|
||||
var err = at.err, data = at.$._.put || at.put, field = at.get;
|
||||
//console.log("*****1", data);
|
||||
@ -3211,11 +3204,10 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('get any any later', function(done){
|
||||
var s = Gun.state.map();s.soul = 'full/later';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
hello: 'world',
|
||||
goodbye: 'mars'
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'full/later')});
|
||||
gun.get('full/later').get(function(at, ev){
|
||||
var err = at.err, data = at.$._.put || at.put, field = at.get;
|
||||
//console.log("*****", data);
|
||||
@ -3303,8 +3295,7 @@ describe('Gun', function(){
|
||||
it('multiple times partial', function(done){
|
||||
var gun = Gun();
|
||||
|
||||
var s = Gun.state.map();s.soul = 'mult/times/part';
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
alias: {
|
||||
mark: {
|
||||
pub: {_:{'#':'PUB'},
|
||||
@ -3314,9 +3305,9 @@ describe('Gun', function(){
|
||||
}
|
||||
}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'mult/times/part')});
|
||||
|
||||
var app = gun.get(s.soul);
|
||||
var app = gun.get('mult/times/part');
|
||||
|
||||
//console.debug.i=1;console.log("===================");
|
||||
app.get('alias').get('mark').map().once(function(alias){
|
||||
@ -3457,6 +3448,7 @@ describe('Gun', function(){
|
||||
list.get('message').put('hello world'); // outputs "message: hello world"
|
||||
list.get('message').put(null); // throws Uncaught TypeError: Cannot read property '#' of null
|
||||
});
|
||||
return;
|
||||
|
||||
it('Check multi instance message passing', function(done){
|
||||
try{ require('fs').unlinkSync('bdata') }catch(e){}
|
||||
@ -3541,10 +3533,10 @@ describe('Gun', function(){
|
||||
it('If chain cannot be called, ack', function(done){
|
||||
var gun = Gun(), u;
|
||||
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
wat: 1,
|
||||
a: true
|
||||
}, 'nl/app')});
|
||||
}, Gun.state.map(), 'nl/app')}); // prev had no state_map?
|
||||
|
||||
var app = gun.get('nl/app');
|
||||
|
||||
@ -3565,11 +3557,11 @@ describe('Gun', function(){
|
||||
it('Chain on known nested object should ack', function(done){
|
||||
var gun = Gun(), u;
|
||||
|
||||
gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
bar: {
|
||||
wat: 1
|
||||
}
|
||||
}, 'nl/app')});
|
||||
}, Gun.state.map(), 'nl/app')});
|
||||
|
||||
var app = gun.get('nl/app').get('bar');
|
||||
|
||||
@ -3953,13 +3945,12 @@ describe('Gun', function(){
|
||||
});return;
|
||||
|
||||
it('get get any parallel', function(done){
|
||||
var s = Gun.state.map();s.soul = 'parallel/get/get';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
bob: {
|
||||
age: 29,
|
||||
name: "Bob!"
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'parallel/get/get')});
|
||||
gun.get('parallel/get/get').path('bob').any(function(err, data, field, at, ev){
|
||||
//console.log("***** 1", data);
|
||||
expect(data.age).to.be(29);
|
||||
@ -3974,13 +3965,12 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('get get any parallel later', function(done){
|
||||
var s = Gun.state.map();s.soul = 'parallel/get/get/later';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
bob: {
|
||||
age: 29,
|
||||
name: "Bob!"
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'parallel/get/get/later')});
|
||||
gun.get('parallel/get/get/later').path('bob').any(function(err, data, field, at, ev){
|
||||
//console.log("***** 1", data);
|
||||
expect(data.age).to.be(29);
|
||||
@ -3997,13 +3987,12 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('get get any none', function(done){
|
||||
var s = Gun.state.map();s.soul = 'get/get/none';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
alice: {
|
||||
age: 31,
|
||||
name: "alice"
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'get/get/none')});
|
||||
var c = 0, s = 0;
|
||||
gun.get('get/get/none').path('bob').any(function(err, data, field, at, ev){
|
||||
//console.log("***** 1", data);
|
||||
@ -4025,13 +4014,12 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('get get any none later', function(done){
|
||||
var s = Gun.state.map();s.soul = 'get/get/none/later';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
alice: {
|
||||
age: 31,
|
||||
name: "alice"
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'get/get/none/later')});
|
||||
var c = 0;
|
||||
gun.get('get/get/none/later').path('bob').any(function(err, data, field, at, ev){
|
||||
//console.log("***** 1", data);
|
||||
@ -4051,10 +4039,9 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('get get primitive get any', function(done){
|
||||
var s = Gun.state.map();s.soul = 'get/get/prim';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
bob: "is awesome"
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'get/get/prim')});
|
||||
gun.get('get/get/prim').path('bob').path('age').any(function(err, data, field, at, ev){
|
||||
//console.log("***** 1", data);
|
||||
expect(data).to.be(undefined);
|
||||
@ -4067,10 +4054,9 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('get put any', function(done){
|
||||
var s = Gun.state.map();s.soul = 'get/put/any';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
here: "we go"
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'get/put/any')});
|
||||
//console.debug.i=1;console.log("---------------");
|
||||
gun.get('get/put/any')
|
||||
.put({})
|
||||
@ -4081,10 +4067,9 @@ describe('Gun', function(){
|
||||
});
|
||||
return;
|
||||
it('get any, get put any', function(done){
|
||||
var s = Gun.state.map();s.soul = 'get/any/get/put/any';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
here: "we go"
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'get/any/get/put/any')});
|
||||
gun.get('get/any/get/put/any')
|
||||
.any(function(err, data, field, at, ev){
|
||||
if(done.first){ return } // it is okay for `any` to get called multiple times.
|
||||
@ -4110,8 +4095,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('mutate pointer to primitive deep on', function(done){
|
||||
var s = Gun.state.map();s.soul = 'change/pointer';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
bob: {
|
||||
age: 29,
|
||||
name: "Bob!",
|
||||
@ -4120,7 +4104,7 @@ describe('Gun', function(){
|
||||
species: "kitty"
|
||||
}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'change/pointer')});
|
||||
gun.get('change/pointer').path('bob').path('pet').any(function(err, data, f, at, ev){
|
||||
//console.log("***", data);return setTimeout(function(){asdf},500);
|
||||
if(done.c){
|
||||
@ -4159,8 +4143,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('get only soul', function(done){
|
||||
var s = Gun.state.map();s.soul = 'only/soul';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
bob: {
|
||||
age: 29,
|
||||
name: "Bob!",
|
||||
@ -4169,7 +4152,7 @@ describe('Gun', function(){
|
||||
species: "kitty"
|
||||
}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'only/soul')});
|
||||
gun.get('only/soul')/*.path('bob')*/.any(function(err, data){
|
||||
expect(Gun.obj.empty(data, '_')).to.be.ok();
|
||||
done();
|
||||
@ -4177,8 +4160,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('get path only soul', function(done){
|
||||
var s = Gun.state.map();s.soul = 'only/p/soul';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
bob: {
|
||||
age: 29,
|
||||
name: "Bob!",
|
||||
@ -4187,7 +4169,7 @@ describe('Gun', function(){
|
||||
species: "kitty"
|
||||
}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'only/p/soul')});
|
||||
gun.get('only/p/soul').path('bob').any(function(err, data){
|
||||
//console.log("*********", err, data);
|
||||
expect(Gun.val.link.is(data)).to.be.ok();
|
||||
@ -4197,8 +4179,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('mutate pointer to self', function(done){
|
||||
var s = Gun.state.map();s.soul = 'change/pointer/point';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
bob: {
|
||||
age: 29,
|
||||
name: "Bob!",
|
||||
@ -4207,7 +4188,7 @@ describe('Gun', function(){
|
||||
species: "kitty"
|
||||
}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'change/pointer/point')});
|
||||
gun.get('change/pointer/point').path('bob').any(function(err, data){
|
||||
if(done.c){
|
||||
expect(data.age).to.be(30);
|
||||
@ -4228,8 +4209,7 @@ describe('Gun', function(){
|
||||
},400);
|
||||
});
|
||||
it('mutate pointer to self deep', function(done){
|
||||
var s = Gun.state.map();s.soul = 'change/pointer/point/deep';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
bob: {
|
||||
age: 29,
|
||||
name: "Bob!",
|
||||
@ -4238,7 +4218,7 @@ describe('Gun', function(){
|
||||
species: "kitty"
|
||||
}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'change/pointer/point/deep')});
|
||||
gun.get('change/pointer/point/deep').path('bob').any(function(err, data){
|
||||
//console.log("***", data);
|
||||
if(done.c){
|
||||
@ -4259,8 +4239,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('mutate pointer to primitive after any', function(done){
|
||||
var s = Gun.state.map();s.soul = 'change/pointer/to/prime';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
bob: {_: {'#': 'asdffdsa'},
|
||||
age: 29,
|
||||
name: "Bob!",
|
||||
@ -4269,7 +4248,7 @@ describe('Gun', function(){
|
||||
species: "kitty"
|
||||
}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'change/pointer/to/prime')});
|
||||
var bob = gun.get('asdffdsa').any(function(err, data){
|
||||
//console.log("***", data);
|
||||
});
|
||||
@ -4294,8 +4273,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('mutate pointer to primitive after any deep', function(done){
|
||||
var s = Gun.state.map();s.soul = 'change/pointer/to/prime/deep';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
bob: {
|
||||
age: 29,
|
||||
name: "Bob!",
|
||||
@ -4304,7 +4282,7 @@ describe('Gun', function(){
|
||||
species: "kitty"
|
||||
}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'change/pointer/to/prime/deep')});
|
||||
var cat = gun.get('sadffads').any(function(err, data){
|
||||
//console.log("***", data);
|
||||
});
|
||||
@ -4328,8 +4306,7 @@ describe('Gun', function(){
|
||||
});
|
||||
return;
|
||||
it.only('mutate pointer to another pointer after any', function(done){
|
||||
var s = Gun.state.map();s.soul = 'change/pointer/to/pointer';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({
|
||||
bob: {_: {'#': 'dafssfad'},
|
||||
age: 29,
|
||||
name: "Bob!",
|
||||
@ -4338,7 +4315,7 @@ describe('Gun', function(){
|
||||
species: "kitty"
|
||||
}
|
||||
}
|
||||
}, s)});
|
||||
}, Gun.state.map(), 'change/pointer/to/pointer')});
|
||||
var bob = gun.get('dafssfad').any(function(err, data){
|
||||
console.log("***", data);
|
||||
});
|
||||
@ -4929,9 +4906,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('get get not', function(done){
|
||||
var s = Gun.state.map();
|
||||
s.soul = 'a';
|
||||
Gun.on('put', {$: gun, put: Gun.graph.ify({b: 1, c: 2}, s)});
|
||||
Gun.on('test', {$: gun, put: Gun.graph.ify({b: 1, c: 2}, Gun.state.map(), 'a')});
|
||||
function cb(e,d,f,a){
|
||||
if('b' === f && 1 === d){
|
||||
done.b = true;
|
||||
|
@ -17,12 +17,7 @@
|
||||
<script src="./expect.js"></script>
|
||||
<script></script>
|
||||
<script src="../gun.js"></script>
|
||||
<!-- script
|
||||
src="https://cdn.jsdelivr.net/npm/gun/sea.js">
|
||||
</script -->
|
||||
<script
|
||||
src="../sea.js">
|
||||
</script>
|
||||
<script src="../sea.js"></script>
|
||||
|
||||
<script src="../lib/radix.js"></script>
|
||||
<script src="../lib/radisk.js"></script>
|
||||
|
@ -16,7 +16,6 @@
|
||||
******* START AT THE BOTTOM AND READ UP *******
|
||||
*/
|
||||
window.i = 1;
|
||||
window.localStorage = window.localStorage || {clear: function(){}};
|
||||
if(!this.stool){ return }
|
||||
setTimeout(function(){
|
||||
stool.run();
|
||||
|
@ -16,11 +16,140 @@
|
||||
stool.run();
|
||||
},1);
|
||||
stool.setup(window.setup = function(){
|
||||
window.BigText = Gun.text.random(1024, 'abcdef');
|
||||
window.MedText = Gun.text.random(200, 'abcdef');
|
||||
window.jsonText = JSON.stringify(window.BigText);
|
||||
window.radText = Radisk.encode(window.BigText);
|
||||
//window.BigText = Gun.text.random(1024, 'abcdef');
|
||||
//window.MedText = Gun.text.random(200, 'abcdef');
|
||||
//window.jsonText = JSON.stringify(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(){
|
||||
JSON.stringify(window.BigText);
|
||||
});
|
||||
|
101
test/rad/bench.js
Normal file
101
test/rad/bench.js
Normal 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;
|
||||
}());
|
||||
}());
|
@ -16,87 +16,6 @@
|
||||
<div id='debugs'></div>
|
||||
<div id='debug'></div>
|
||||
|
||||
<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>
|
||||
<script src="../../../gun/test/rad/bench.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -45,6 +45,20 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam
|
||||
|
||||
describe('Radix', function(){
|
||||
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){
|
||||
var all = {};
|
||||
names.forEach(function(v,i){
|
||||
@ -110,6 +124,16 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam
|
||||
expect(Gun.obj.empty(all)).to.be.ok();
|
||||
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){
|
||||
var r = Radix(), tmp;
|
||||
@ -240,6 +264,7 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam
|
||||
if(v.indexOf(find) == 0){ all[v] = true }
|
||||
});
|
||||
rad(find, function(err, data, info){
|
||||
expect(data).to.be.ok();
|
||||
Radix.map(data, function(v,k){
|
||||
delete all[find+k];
|
||||
});
|
||||
@ -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){
|
||||
var all = {}, find = 'm', to;
|
||||
names.forEach(function(v){
|
||||
v = v.toLowerCase();
|
||||
if(v.indexOf(find) == 0){ all[v] = true }
|
||||
});
|
||||
//console.log("<<<<<<<<<");
|
||||
gun.get('names').get({'.': {'*': find}, '%': 1000 * 100}).once().map().once(function(data, key){
|
||||
expect(data.name).to.be.ok();
|
||||
expect(data.age).to.be.ok();
|
||||
@ -304,6 +339,7 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam
|
||||
done();
|
||||
},100);
|
||||
});
|
||||
//console.log(">>>>>>>>>");
|
||||
});
|
||||
|
||||
it('read contacts again', function(done){
|
||||
|
@ -3,7 +3,7 @@ var expect = global.expect = require("./expect");
|
||||
var Radix = require('../lib/radix');
|
||||
var _ = String.fromCharCode(29);
|
||||
|
||||
describe('Radix', function(){
|
||||
describe('Radix', function(){ // moved to ./rad/rad.js
|
||||
it('read', function(){
|
||||
var rad = Radix();
|
||||
rad('asdf.pub', 'yum');
|
||||
|
@ -162,20 +162,20 @@ describe('SEA', function(){
|
||||
expect(dec.priv).to.be(okey.priv);
|
||||
expect(dec.epriv).to.be(okey.epriv);
|
||||
|
||||
var gun = Gun(), tmp = Gun.node.soul(old);
|
||||
var gun = Gun({super: true}), tmp = Gun.node.soul(old);
|
||||
var graph = {};
|
||||
graph[tmp] = old;
|
||||
var alias = await SEA.verify(old.alias, false);
|
||||
expect(alias).to.be('bob');
|
||||
alias = Gun.state.ify({}, tmp, 1, Gun.val.rel.ify(tmp), tmp = '~@'+alias);
|
||||
graph[tmp] = alias;
|
||||
gun.on('put', {$: gun, put: graph});
|
||||
gun.on('test', {$: gun, put: graph});
|
||||
var use = gun.user();
|
||||
use.auth('bob', 'test123', function(ack){
|
||||
expect(ack.err).to.not.be.ok();
|
||||
done();
|
||||
});
|
||||
}())})
|
||||
}())});
|
||||
|
||||
it('legacy []', function(done){ (async function(){
|
||||
var pw = 'test123';
|
||||
@ -183,14 +183,14 @@ describe('SEA', function(){
|
||||
var old = JSON.parse(atob("eyJfIjp7IiMiOiJ+VThkS0dySFJhX01sMFZ1YlR5OUZBYTlQS1ZlYlh0eTFjS05zWWxnYjduNC5QeVd5cUVVb0ZpYVduUElOV0Nad0xBbzFobjN1MldPWTU3SzZHZnpsNjhVIiwiPiI6eyJwdWIiOjE1NDY5MDI1MDQ5NzksImFsaWFzIjoxNTQ2OTAyNTA0OTc5LCJlcHViIjoxNTQ2OTAyNTA0OTc5LCJhdXRoIjoxNTQ2OTAyNTA0OTc5fX0sInB1YiI6IlU4ZEtHckhSYV9NbDBWdWJUeTlGQWE5UEtWZWJYdHkxY0tOc1lsZ2I3bjQuUHlXeXFFVW9GaWFXblBJTldDWndMQW8xaG4zdTJXT1k1N0s2R2Z6bDY4VSIsImFsaWFzIjoiU0VBe1wibVwiOltcIn5VOGRLR3JIUmFfTWwwVnViVHk5RkFhOVBLVmViWHR5MWNLTnNZbGdiN240LlB5V3lxRVVvRmlhV25QSU5XQ1p3TEFvMWhuM3UyV09ZNTdLNkdmemw2OFVcIixcImFsaWFzXCIsXCJhbGljZVwiLDE1NDY5MDI1MDQ5NzldLFwic1wiOlwienpuaGtIZjhZdFpZM2lGd3FVd0lJUldMTjhZMmlHbmNkcnVTaStGNDNmU1BLYWpSZlI0VzhXVHM4bElSMDBndGJmTWJxS0NjQkpGN3VNSkdGRC9WV2c9PVwifSIsImVwdWIiOiJTRUF7XCJtXCI6W1wiflU4ZEtHckhSYV9NbDBWdWJUeTlGQWE5UEtWZWJYdHkxY0tOc1lsZ2I3bjQuUHlXeXFFVW9GaWFXblBJTldDWndMQW8xaG4zdTJXT1k1N0s2R2Z6bDY4VVwiLFwiZXB1YlwiLFwiRkRzM1VvNTNFZEp6eFNocEpDaVctRGZPQ3lUS0M2U3cxeS1PZVJxam5ZRS5xVGdyYTlFQk1maEpNdVlMVmNaejRZYklLRm85enNBMHpMcV82dEVPMHI0XCIsMTU0NjkwMjUwNDk3OV0sXCJzXCI6XCJPZzRVVjY4OTluSjE4dC9ybWVnV0lkdnNqN01KaEpFc29ranZYQmdteVVRUXVNVjFTdnh4cXJqOFoyV1o2Q25XSkZnTlVDbEVYYWxuMURjUFE3M1R6UT09XCJ9IiwiYXV0aCI6IlNFQXtcIm1cIjpbXCJ+VThkS0dySFJhX01sMFZ1YlR5OUZBYTlQS1ZlYlh0eTFjS05zWWxnYjduNC5QeVd5cUVVb0ZpYVduUElOV0Nad0xBbzFobjN1MldPWTU3SzZHZnpsNjhVXCIsXCJhdXRoXCIsXCJ7XFxcImVrXFxcIjpcXFwiU0VBe1xcXFxcXFwiY3RcXFxcXFxcIjpcXFxcXFxcIi94ZnNPdVNkQUtrNkJiR00zbUV6MnVlSjI3Y0tJNThYMEtUL1FsaExSZXpWcjRkNzVZb2M5QlZNRjkzejl4QXI4N080S2FDNjJUWGVoeERQN0FFa2V4N1paaEpYL2hsVm9kK1FIcVFaaUZMK2lVQzFvL2hpUEJGWElBZmtINGRrcklGOFdqcEVaU3NIVmRSOVRhY2ZzbTB3aHN5NGJXN1ZLSEUySGc9PVxcXFxcXFwiLFxcXFxcXFwiaXZcXFxcXFxcIjpcXFxcXFxcIjhWekduTStEc1lTUktIU3Z4cSszTGc9PVxcXFxcXFwiLFxcXFxcXFwic1xcXFxcXFwiOlxcXFxcXFwibVVSSlJ4TzUvdXM9XFxcXFxcXCJ9XFxcIixcXFwic1xcXCI6XFxcImE1SlA3VFpuVE9jYjEwMGJOejlscEU4dnpqcUE3TWl0NHcwN3pjQTdIOFV0bml1WnVHSmdpZnNNQlFNSGdRdE5cXFwifVwiLDE1NDY5MDI1MDQ5NzldLFwic1wiOlwiSGFzMytJaHFEZTYyN016cElXZVE1cVFrZ2NOMlk3WHRpNGw0TFU3T2JyaktxSlBnSllrVWE2bk9YdlRmQkFzV1BPVzVnemh4Q2RPVGNFQm5icWlpWXc9PVwifSJ9"));
|
||||
var okey = {"pub":"U8dKGrHRa_Ml0VubTy9FAa9PKVebXty1cKNsYlgb7n4.PyWyqEUoFiaWnPINWCZwLAo1hn3u2WOY57K6Gfzl68U","epub":"FDs3Uo53EdJzxShpJCiW-DfOCyTKC6Sw1y-OeRqjnYE.qTgra9EBMfhJMuYLVcZz4YbIKFo9zsA0zLq_6tEO0r4","priv":"jMy7WfcldJ4esZEijAj4LTb99smtY_H0yKJLemJl2HI","epriv":"1DszMh-85pGTPLYtRunG-Q-xB78AE4k07PPkbedYYwk"}
|
||||
|
||||
var gun = Gun(), tmp = Gun.node.soul(old);
|
||||
var gun = Gun({super: true}), tmp = Gun.node.soul(old);
|
||||
var graph = {};
|
||||
graph[tmp] = old;
|
||||
var alias = SEA.opt.unpack(await SEA.verify(old.alias, false), 'alias', old);
|
||||
expect(alias).to.be('alice');
|
||||
alias = Gun.state.ify({}, tmp, 1, Gun.val.rel.ify(tmp), tmp = '~@'+alias);
|
||||
graph[tmp] = alias;
|
||||
gun.on('put', {$: gun, put: graph});
|
||||
gun.on('test', {$: gun, put: graph});
|
||||
var use = gun.user();
|
||||
use.auth('alice', 'test123', function(ack){
|
||||
expect(ack.err).to.not.be.ok();
|
||||
@ -242,8 +242,30 @@ describe('SEA', function(){
|
||||
});
|
||||
|
||||
describe('User', function(){
|
||||
var gun = Gun(), gtmp;
|
||||
|
||||
it('test', function(done){
|
||||
var g = Gun();
|
||||
user = g.user();
|
||||
var gid;
|
||||
SEA.pair(function(p){
|
||||
user.is = user._.sea = p;
|
||||
gtmp = gid = 'test~'+p.pub;
|
||||
g.get(gid).put({yo: 'hi'}, function(ack){
|
||||
var data = SEA.opt.parse(g._.graph[gid].yo);
|
||||
expect(data[':']).to.be('hi');
|
||||
expect(data['~']).to.be.ok();
|
||||
g.get(gid).get('yo').once(function(r){
|
||||
expect(r).to.be('hi');
|
||||
user.leave();
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
it('is instantiable', function(done){
|
||||
gun = Gun();
|
||||
user.leave();
|
||||
user = gun.user();
|
||||
done();
|
||||
})
|
||||
@ -254,7 +276,7 @@ describe('SEA', function(){
|
||||
expect(ack.err).to.not.be.ok();
|
||||
done();
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
it('login users', function(done){
|
||||
user.auth('carl', 'test123', function(ack){
|
||||
@ -277,6 +299,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){
|
||||
this.timeout(9000);
|
||||
setTimeout(function(){
|
||||
@ -312,6 +363,7 @@ describe('SEA', function(){
|
||||
var ref = user.get('who').get('all').set(msg);
|
||||
user.get('who').get('said').set(ref);
|
||||
user.get('who').get('said').map().once(function(data){
|
||||
//console.log("*****", data);
|
||||
expect(data.what).to.be.ok();
|
||||
done();
|
||||
})
|
||||
@ -321,6 +373,7 @@ describe('SEA', function(){
|
||||
it('set user ref null override', function(done){
|
||||
this.timeout(9000);
|
||||
var gun = Gun();
|
||||
//user.leave();
|
||||
var user = gun.user();
|
||||
var msg = {what: 'hello world'};
|
||||
user.create('xavier', 'password');
|
||||
|
6
tsconfig.json
Normal file
6
tsconfig.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"emitDeclarationOnly": true,
|
||||
"declaration": true
|
||||
}
|
||||
}
|
256
types/chain.d.ts
vendored
Normal file
256
types/chain.d.ts
vendored
Normal file
@ -0,0 +1,256 @@
|
||||
import { AlwaysDisallowedType, DisallowPrimitives, DisallowArray, AckCallback, ArrayOf, ArrayAsRecord, Saveable, IGunCryptoKeyPair } from './types';
|
||||
import { IGunConstructorOptions } from './options';
|
||||
import { ITSResolvable } from 'ts-type';
|
||||
export interface IGunChainReference<DataType = Record<string, any>, ReferenceKey = any, IsTop extends 'pre_root' | 'root' | false = false> {
|
||||
/**
|
||||
* Save data into gun, syncing it with your connected peers.
|
||||
*
|
||||
* * You cannot save primitive values at the root level.
|
||||
*
|
||||
* @param data You do not need to re-save the entire object every time,
|
||||
* gun will automatically merge your data into what already exists as a "partial" update.
|
||||
*
|
||||
* * `undefined`, `NaN`, `Infinity`, `array`, will be rejected.
|
||||
* * Traditional arrays are dangerous in real-time apps. Use `gun.set` instead.
|
||||
*
|
||||
* @param callback invoked on each acknowledgment
|
||||
*/
|
||||
put(data: Partial<AlwaysDisallowedType<DisallowPrimitives<IsTop, DisallowArray<DataType>>>>, callback?: AckCallback): IGunChainReference<DataType, ReferenceKey, IsTop>;
|
||||
/**
|
||||
* Where to read data from.
|
||||
* @param key The key is the ID or property name of the data that you saved from earlier
|
||||
* (or that will be saved later).
|
||||
* * Note that if you use .put at any depth after a get it first reads the data and then writes, merging the data as a partial update.
|
||||
* @param callback You will usually be using gun.on or gun.once to actually retrieve your data,
|
||||
* not this callback (it is intended for more low level control, for module and extensions).
|
||||
*
|
||||
* **Avoid use callback. The type in the document may be wrong.**
|
||||
*
|
||||
* **Here the type of callback respect to the actual behavior**
|
||||
*/
|
||||
get<K extends keyof DataType>(key: ArrayOf<DataType> extends never ? K : ArrayOf<DataType>, callback?: (
|
||||
/** the raw data. Internal node of gun. Will not typed here. */
|
||||
paramA: Record<'gun' | '$' | 'root' | 'id' | 'back' | 'on' | 'tag' | 'get' | 'soul' | 'ack' | 'put', any>,
|
||||
/** the key, ID, or property name of the data. */
|
||||
paramB: Record<'off' | 'to' | 'next' | 'the' | 'on' | 'as' | 'back' | 'rid' | 'id', any>) => void): IGunChainReference<DataType[K], K, IsTop extends 'pre_root' ? 'root' : false>;
|
||||
/**
|
||||
* Change the configuration of the gun database instance.
|
||||
* @param options The options argument is the same object you pass to the constructor.
|
||||
*
|
||||
* The options's properties replace those in the instance's configuration but options.peers are **added** to peers known to the gun instance.
|
||||
* @returns No mention in the document, behavior as `ChainReference<DataType, ReferenceKey>`
|
||||
*/
|
||||
opt(options: IGunConstructorOptions): IGunChainReference<DataType, ReferenceKey>;
|
||||
/**
|
||||
* Move up to the parent context on the chain.
|
||||
*
|
||||
* Every time a new chain is created, a reference to the old context is kept to go back to.
|
||||
* @param amount The number of times you want to go back up the chain.
|
||||
* `-1` or `Infinity` will take you to the root.
|
||||
* @returns Impossible to determinate final type. You must cast it by yourself.
|
||||
*/
|
||||
back(amount?: number): IGunChainReference;
|
||||
/**
|
||||
* Subscribe to updates and changes on a node or property in realtime.
|
||||
* @param option Currently, the only option is to filter out old data, and just be given the changes.
|
||||
* If you're listening to a node with 100 fields, and just one changes,
|
||||
* you'll instead be passed a node with a single property representing that change rather than the full node every time.
|
||||
* @param callback
|
||||
* Once initially and whenever the property or node you're focused on changes, this callback is immediately fired with the data as it is at that point in time.
|
||||
*
|
||||
* Since gun streams data, the callback will probably be called multiple times as new chunk comes in.
|
||||
* To remove a listener call .off() on the same property or node.
|
||||
*/
|
||||
on(callback: (data: DisallowPrimitives<IsTop, AlwaysDisallowedType<ArrayAsRecord<DataType>>>, key: ReferenceKey) => void, option?: {
|
||||
change: boolean;
|
||||
} | boolean): IGunChainReference<DataType, ReferenceKey>;
|
||||
/**
|
||||
* Get the current data without subscribing to updates. Or `undefined` if it cannot be found.
|
||||
* @returns In the document, it said the return value may change in the future. Don't rely on it.
|
||||
*/
|
||||
once(callback?: (data: (DisallowPrimitives<IsTop, AlwaysDisallowedType<ArrayAsRecord<DataType>>>) | undefined, key: ReferenceKey) => void, option?: {
|
||||
wait: number;
|
||||
}): IGunChainReference<DataType, ReferenceKey>;
|
||||
/**
|
||||
* **.set does not means 'set data', it means a Mathematical Set**
|
||||
*
|
||||
* Add a unique item to an unordered list.
|
||||
* `gun.set` works like a mathematical set, where each item in the list is unique.
|
||||
* If the item is added twice, it will be merged.
|
||||
*
|
||||
* **This means only objects, for now, are supported.**
|
||||
*/
|
||||
set(data: AlwaysDisallowedType<DataType extends Array<infer U> ? U extends {
|
||||
[key: string]: any;
|
||||
[key: number]: any;
|
||||
} ? ArrayOf<DataType> : never : never>, callback?: AckCallback): IGunChainReference<ArrayOf<DataType>>;
|
||||
/**
|
||||
* Map iterates over each property and item on a node, passing it down the chain,
|
||||
* behaving like a forEach on your data.
|
||||
* It also subscribes to every item as well and listens for newly inserted items.
|
||||
*/
|
||||
map(callback?: (value: ArrayOf<DataType>, key: DataType) => ArrayOf<DataType> | undefined): IGunChainReference<ArrayOf<DataType>, ReferenceKey>;
|
||||
/**
|
||||
* Undocumented, but extremely useful and mentioned in the document
|
||||
*
|
||||
* Remove **all** listener on this node.
|
||||
*/
|
||||
off(): void;
|
||||
/**
|
||||
*
|
||||
* Path does the same thing as `.get` but has some conveniences built in.
|
||||
* @deprecated This is not friendly with type system.
|
||||
*
|
||||
* **Warning**: This extension was removed from core, you probably shouldn't be using it!
|
||||
*
|
||||
* **Warning**: Not included by default! You must include it yourself via `require('gun/lib/path.js')` or
|
||||
* `<script src="https://cdn.jsdelivr.net/npm/gun/lib/path.js"></script>`!
|
||||
*/
|
||||
path?(path: string | string[]): IGunChainReference;
|
||||
/**
|
||||
* Handle cases where data can't be found.
|
||||
*
|
||||
* **Warning**: Not included by default! You must include it yourself via `require('gun/lib/not.js')` or
|
||||
* `<script src="https://cdn.jsdelivr.net/npm/gun/lib/not.js"></script>`!
|
||||
*/
|
||||
not?(callback: (key: ReferenceKey) => void): IGunChainReference<DataType, ReferenceKey>;
|
||||
/**
|
||||
* Open behaves very similarly to gun.on, except it gives you the **full depth of a document** on every update.
|
||||
* It also works with graphs, tables, or other data structures. Think of it as opening up a live connection to a document.
|
||||
*
|
||||
* **Warning**: Not included by default! You must include it yourself via `require('gun/lib/open.js')` or
|
||||
* `<script src="https://cdn.jsdelivr.net/npm/gun/lib/open.js"></script>`!
|
||||
*/
|
||||
open?(callback: (data: ArrayAsRecord<DataType>) => void): IGunChainReference<DataType, ReferenceKey>;
|
||||
/**
|
||||
* Loads the full object once. It is the same as `open` but with the behavior of `once`.
|
||||
*
|
||||
* **Warning**: Not included by default! You must include it yourself via `require('gun/lib/load.js')` or
|
||||
* `<script src="https://cdn.jsdelivr.net/npm/gun/lib/load.js"></script>`!
|
||||
*/
|
||||
load?(callback: (data: ArrayAsRecord<DataType>) => void): IGunChainReference<DataType, ReferenceKey>;
|
||||
/**
|
||||
* Returns a promise for you to use.
|
||||
*
|
||||
* **Warning**: Not included by default! You must include it yourself via `require('gun/lib/then.js')` or
|
||||
* `<script src="https://cdn.jsdelivr.net/npm/gun/lib/then.js"></script>`!
|
||||
*/
|
||||
then?<R, TResult1 = ArrayAsRecord<DataType>>(onfulfilled: (value: TResult1) => ITSResolvable<R>): Promise<R>;
|
||||
then?<TResult1 = ArrayAsRecord<DataType>>(): Promise<TResult1>;
|
||||
/**
|
||||
* Returns a promise for you to use.
|
||||
*
|
||||
* **Warning**: Not included by default! You must include it yourself via `require('gun/lib/then.js')` or
|
||||
* `<script src="https://cdn.jsdelivr.net/npm/gun/lib/then.js"></script>`!
|
||||
*/
|
||||
promise?<R, TResult1 = {
|
||||
put: ArrayAsRecord<DataType>;
|
||||
key: ReferenceKey;
|
||||
gun: IGunChainReference<DataType, ReferenceKey>;
|
||||
}>(onfulfilled: (value: TResult1) => ITSResolvable<R>): Promise<R>;
|
||||
promise?<TResult1 = {
|
||||
put: ArrayAsRecord<DataType>;
|
||||
key: ReferenceKey;
|
||||
gun: IGunChainReference<DataType, ReferenceKey>;
|
||||
}>(): Promise<TResult1>;
|
||||
/**
|
||||
* bye lets you change data after that browser peer disconnects.
|
||||
* This is useful for games and status messages,
|
||||
* that if a player leaves you can remove them from the game or set a user's status to "away".
|
||||
*
|
||||
* **Warning**: Not included by default! You must include it yourself via `require('gun/lib/bye.js')` or
|
||||
* `<script src="https://cdn.jsdelivr.net/npm/gun/lib/bye.js"></script>`!
|
||||
*/
|
||||
bye?(): {
|
||||
put(data: DisallowArray<Saveable<DataType>>): void;
|
||||
};
|
||||
/**
|
||||
* Say you save some data, but want to do something with it later, like expire it or refresh it.
|
||||
* Well, then `later` is for you! You could use this to easily implement a TTL or similar behavior.
|
||||
*
|
||||
* **Warning**: Not included by default! You must include it yourself via `require('gun/lib/later.js')` or
|
||||
* `<script src="https://cdn.jsdelivr.net/npm/gun/lib/later.js"></script>`!
|
||||
*/
|
||||
later?(callback: (this: IGunChainReference<DataType, ReferenceKey>, data: ArrayAsRecord<DataType>, key: ReferenceKey) => void, seconds: number): IGunChainReference<DataType, ReferenceKey>;
|
||||
/**
|
||||
* After you save some data in an unordered list, you may need to remove it.
|
||||
*
|
||||
* **Warning**: Not included by default! You must include it yourself via `require('gun/lib/unset.js')` or
|
||||
* `<script src="https://cdn.jsdelivr.net/npm/gun/lib/unset.js"></script>`!
|
||||
*/
|
||||
unset?(data: ArrayOf<DataType>): IGunChainReference<DataType, ReferenceKey>;
|
||||
/**
|
||||
* Subscribes to all future events that occur on the Timegraph and retrieve a specified number of old events
|
||||
*
|
||||
* **Warning**: The Timegraph extension isn't required by default, you would need to include at "gun/lib/time.js"
|
||||
*/
|
||||
time?(callback: (data: ArrayOf<DataType>, key: ReferenceKey, time: number) => void, alsoReceiveNOldEvents?: number): IGunChainReference<DataType, ReferenceKey>;
|
||||
/** Pushes data to a Timegraph with it's time set to Gun.state()'s time */
|
||||
time?(data: ArrayOf<DataType>): void;
|
||||
/**
|
||||
* Creates a new user and calls callback upon completion.
|
||||
* @param alias Username or Alias which can be used to find a user.
|
||||
* @param pass Passphrase that will be extended with PBKDF2 to make it a secure way to login.
|
||||
* @param cb Callback that is to be called upon creation of the user.
|
||||
* @param opt Option Object containing options for creation. (In gun options are added at end of syntax. opt is rarely used, hence is added at the end.)
|
||||
*/
|
||||
create(alias: string, pass: string, cb?: (ack: {
|
||||
ok: 0;
|
||||
pub: string;
|
||||
} | {
|
||||
err: string;
|
||||
}) => void, opt?: {}): IGunChainReference;
|
||||
/**
|
||||
* Authenticates a user, previously created via User.create.
|
||||
* @param alias Username or Alias which can be used to find a user.
|
||||
* @param pass Passphrase for the user
|
||||
* @param cb Callback that is to be called upon authentication of the user.
|
||||
* @param opt Option Object containing options for authentiaction. (In gun options are added at end of syntax. opt is rarely used, hence is added at the end.)
|
||||
*/
|
||||
auth(alias: string, pass: string, cb?: (ack: {
|
||||
ack: 2;
|
||||
get: string;
|
||||
on: (...args: [unknown, unknown, unknown]) => unknown;
|
||||
put: {
|
||||
alias: string;
|
||||
auth: any;
|
||||
epub: string;
|
||||
pub: string;
|
||||
};
|
||||
sea: IGunCryptoKeyPair;
|
||||
soul: string;
|
||||
} | {
|
||||
err: string;
|
||||
}) => void, opt?: {}): IGunChainReference;
|
||||
/**
|
||||
* Returns the key pair in the form of an object as below.
|
||||
*/
|
||||
pair(): IGunCryptoKeyPair;
|
||||
/**
|
||||
* Log out currently authenticated user. Parameters are unused in the current implementation.
|
||||
* @param opt unused in current implementation.
|
||||
* @param cb unused in current implementation.
|
||||
*/
|
||||
leave(opt?: never, cb?: never): IGunChainReference;
|
||||
/**
|
||||
* Deletes a user from the current gun instance and propagates the delete to other peers.
|
||||
* @param alias Username or alias.
|
||||
* @param pass Passphrase for the user.
|
||||
* @param cb Callback that is called when the user was successfully deleted.
|
||||
*/
|
||||
delete(alias: string, pass: string, cb?: (ack: {
|
||||
ok: 0;
|
||||
}) => void): Promise<void>;
|
||||
/**
|
||||
* Recall saves a users credentials in sessionStorage of the browser. As long as the tab of your app is not closed the user stays logged in, even through page refreshes and reloads.
|
||||
* @param opt option object If you want to use browser sessionStorage to allow users to stay logged in as long as the session is open, set opt.sessionStorage to true
|
||||
* @param cb internally the callback is passed on to the user.auth function to logged the user back in. Refer to user.auth for callback documentation.
|
||||
*/
|
||||
recall(opt?: {
|
||||
sessionStorage: boolean;
|
||||
}, cb?: Parameters<IGunChainReference['auth']>[2]): IGunChainReference;
|
||||
/**
|
||||
* @param publicKey If you know a users publicKey you can get their user graph and see any unencrypted data they may have stored there.
|
||||
*/
|
||||
user(publicKey?: string): IGunChainReference;
|
||||
}
|
31
types/options.d.ts
vendored
Normal file
31
types/options.d.ts
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* options['module name'] allows you to pass options to a 3rd party module.
|
||||
* Their project README will likely list the exposed options
|
||||
* https://github.com/amark/gun/wiki/Modules
|
||||
*/
|
||||
export interface IGunConstructorOptions extends Partial<{
|
||||
/** Undocumented but mentioned. Write data to a JSON. */
|
||||
file: string;
|
||||
/** Undocumented but mentioned. Create a websocket server */
|
||||
web: any;
|
||||
/** Undocumented but mentioned. Amazon S3 */
|
||||
s3: {
|
||||
key: any;
|
||||
secret: any;
|
||||
bucket: any;
|
||||
};
|
||||
/** the URLs are properties, and the value is an empty object. */
|
||||
peers: string[] | Record<string, {}>;
|
||||
/** default: true, creates and persists local (nodejs) data using Radisk. */
|
||||
radisk: boolean;
|
||||
/** default: true, persists local (browser) data to localStorage. */
|
||||
localStorage: boolean;
|
||||
/** uuid allows you to override the default 24 random alphanumeric soul generator with your own function. */
|
||||
uuid(): string;
|
||||
/**
|
||||
* allows you to pass options to a 3rd party module. Their project README will likely list the exposed options
|
||||
* @see https://github.com/amark/gun/wiki/Modules
|
||||
*/
|
||||
[key: string]: any;
|
||||
}> {
|
||||
}
|
26
types/static.d.ts
vendored
Normal file
26
types/static.d.ts
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
import { IGunChainReference } from './chain';
|
||||
import { IGunConstructorOptions } from './options';
|
||||
import { IGunStaticNode } from './static/node';
|
||||
import { IGunStaticSEA } from './static/sea';
|
||||
export interface IGunStatic {
|
||||
/**
|
||||
* @description
|
||||
* no parameters creates a local datastore using the default persistence layer, either localStorage or Radisk.
|
||||
* @param options
|
||||
* passing a URL creates the above local datastore that also tries to sync with the URL.
|
||||
*
|
||||
* or you can pass in an array of URLs to sync with multiple peers.
|
||||
*/
|
||||
<DataType = any>(options?: string | string[] | IGunConstructorOptions): IGunChainReference<DataType, any, 'pre_root'>;
|
||||
new <DataType = any>(options?: string | string[] | IGunConstructorOptions): IGunChainReference<DataType, any, 'pre_root'>;
|
||||
readonly node: IGunStaticNode;
|
||||
/** @see https://gun.eco/docs/SEA */
|
||||
readonly SEA: IGunStaticSEA;
|
||||
readonly version: string;
|
||||
readonly chain: IGunChainReference;
|
||||
readonly log: {
|
||||
(...argv: any[]): void;
|
||||
once(...argv: any[]): void;
|
||||
off: boolean;
|
||||
};
|
||||
}
|
13
types/static/node.d.ts
vendored
Normal file
13
types/static/node.d.ts
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
import { IGunChainReference } from '../chain';
|
||||
export interface IGunStaticNode {
|
||||
/** Returns true if data is a gun node, otherwise false. */
|
||||
is(anything: any): anything is IGunChainReference;
|
||||
/**
|
||||
* Returns data's gun ID (instead of manually grabbing its metadata i.e. data["_"]["#"], which is faster but could change in the future)
|
||||
*
|
||||
* Returns undefined if data is not correct gun data.
|
||||
*/
|
||||
soul(data: IGunChainReference): string;
|
||||
/** Returns a "gun-ified" variant of the json input by injecting a new gun ID into the metadata field. */
|
||||
ify(json: any): any;
|
||||
}
|
56
types/static/sea.d.ts
vendored
Normal file
56
types/static/sea.d.ts
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
/** @see https://gun.eco/docs/SEA */
|
||||
export interface IGunStaticSEA {
|
||||
/** If you want SEA to throw while in development, turn SEA.throw = true on, but please do not use this in production. */
|
||||
throw?: boolean;
|
||||
/** Last known error */
|
||||
err?: Error;
|
||||
/**
|
||||
* This gives you a Proof of Work (POW) / Hashing of Data
|
||||
* @param data The data to be hashed, work to be performed on.
|
||||
* @param pair (salt) You can pass pair of keys to use as salt. Salt will prevent others to pre-compute the work,
|
||||
* so using your public key is not a good idea. If it is not specified, it will be random,
|
||||
* which ruins your chance of ever being able to re-derive the work deterministically
|
||||
* @param callback function to executed upon execution of proof
|
||||
* @param opt default: {name: 'PBKDF2', encode: 'base64'}
|
||||
*/
|
||||
work(data: any, pair?: any, callback?: (data: string | undefined) => void, opt?: Partial<{
|
||||
name: 'SHA-256' | 'PBKDF2';
|
||||
encode: 'base64' | 'base32' | 'base16';
|
||||
/** iterations to use on subtle.deriveBits */
|
||||
iterations: number;
|
||||
salt: any;
|
||||
hash: string;
|
||||
length: any;
|
||||
}>): Promise<string | undefined>;
|
||||
/**
|
||||
* This generates a cryptographically secure public/private key pair - be careful not to leak the private keys!
|
||||
* Note: API subject to change we may change the parameters to accept data and work, in addition to generation.
|
||||
* You will need this for most of SEA's API, see those method's examples.
|
||||
* The default cryptographic primitives for the asymmetric keys are ECDSA for signing and ECDH for encryption.
|
||||
*/
|
||||
pair(cb: (data: CryptoKeyPair) => void, opt?: {}): Promise<CryptoKeyPair | undefined>;
|
||||
/**
|
||||
* Adds a signature to a message, for data that you want to prevent attackers tampering with.
|
||||
* @param data is the content that you want to prove is authorized.
|
||||
* @param pair is from .pair.
|
||||
*/
|
||||
sign(data: any, pair: CryptoKeyPair): Promise<string | undefined>;
|
||||
/**
|
||||
* Gets the data if and only if the message can be verified as coming from the person you expect.
|
||||
* @param message is what comes from .sign.
|
||||
* @param pair from .pair or its public key text (pair.pub).
|
||||
*/
|
||||
verify(message: any, pair: CryptoKeyPair | string): Promise<unknown>;
|
||||
/**
|
||||
* Takes some data that you want to keep secret and encrypts it so nobody else can read it.
|
||||
* @param data is the content that you want to encrypt.
|
||||
* @param pair from .pair or a passphrase you want to use as a cypher to encrypt with.
|
||||
*/
|
||||
encrypt(data: any, pair: CryptoKeyPair | string): Promise<string>;
|
||||
/**
|
||||
* Read the secret data, if and only if you are allowed to.
|
||||
* @param message is what comes from .encrypt.
|
||||
* @param pair from .pair or the passphrase to decypher the message.
|
||||
*/
|
||||
decrypt(message: any, pair: CryptoKeyPair | string): Promise<unknown>;
|
||||
}
|
37
types/types.d.ts
vendored
Normal file
37
types/types.d.ts
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
import { IGunChainReference } from './chain';
|
||||
export declare type ArrayOf<T> = T extends Array<infer U> ? U : never;
|
||||
/** Gun does not accept Array value, so we need extract to make types correct */
|
||||
export declare type AllowArray<T> = ArrayOf<T> extends never ? T : ArrayOf<T>;
|
||||
export declare type DisallowArray<T> = ArrayOf<T> extends never ? T : never;
|
||||
/** These types cannot be stored on Gun */
|
||||
export declare type AlwaysDisallowedType<T> = T extends (...args: any[]) => void ? never : T extends {
|
||||
new (...args: any[]): any;
|
||||
} ? never : AccessObject<T>;
|
||||
export declare type AccessObject<T> = T extends object ? {
|
||||
[key in keyof T]: (AlwaysDisallowedType<T[key]> extends never ? never : AccessObject<T[key]>);
|
||||
} : T;
|
||||
/** These types cannot be stored on Gun's root level */
|
||||
export declare type DisallowPrimitives<Open, T> = Open extends false ? T : T extends string ? never : T extends number ? never : T extends boolean ? never : T extends null ? never : T extends undefined ? never : T;
|
||||
export declare type ArrayAsRecord<DataType> = ArrayOf<DataType> extends never ? DataType : Record<string, any>;
|
||||
export declare type Saveable<DataType> = Partial<DataType> | string | number | boolean | null | IGunChainReference<DataType>;
|
||||
export declare type AckCallback = (ack: {
|
||||
err: Error;
|
||||
ok: any;
|
||||
} | {
|
||||
err: undefined;
|
||||
ok: string;
|
||||
}) => void;
|
||||
export declare type IGunCryptoKeyPair = Record<'pub' | 'priv' | 'epub' | 'epriv', string>;
|
||||
export interface IGunRecordNodeRawBase {
|
||||
'#': string;
|
||||
}
|
||||
export interface IGunRecordNodeRawExtra<DataType> extends IGunRecordNodeRawBase {
|
||||
'>': Record<keyof DataType, number>;
|
||||
}
|
||||
export interface IGunRecordNodeRaw<DataType> {
|
||||
'_': IGunRecordNodeRawExtra<DataType>;
|
||||
}
|
||||
export declare type IGunRecordNode<DataType> = {
|
||||
[K in keyof DataType]: IGunRecordNodeRawBase;
|
||||
} & IGunRecordNodeRaw<DataType>;
|
||||
export declare type IGunRecordData<DataType> = DataType & IGunRecordNodeRaw<DataType>;
|
Loading…
x
Reference in New Issue
Block a user