mirror of
https://github.com/amark/gun.git
synced 2025-03-30 15:08:33 +00:00
fix async/sync
This commit is contained in:
parent
9ce98ffcc0
commit
42e0ac89ea
@ -1,78 +0,0 @@
|
||||
<div id="be">
|
||||
<div id="view"></div>
|
||||
<video id="video" style="display: none; height:300px;width:320px" autoplay="true"></video>
|
||||
<canvas id="canvas" style="display: none; height:240px; width:320px"></canvas>
|
||||
<textarea id="debug" style="height:240px;width:420px;"></textarea>
|
||||
<div id="audio"></div>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
|
||||
<script src="gun.js"></script>
|
||||
<script>
|
||||
;(function(){
|
||||
console.log("WARNING! THIS EXAMPLE / DEMO IS NOT FINISHED! DO NOT USE IT.");
|
||||
navigator.getUserMedia = navigator.getUserMedia
|
||||
|| navigator.webkitGetUserMedia
|
||||
|| navigator.mozGetUserMedia
|
||||
|| navigator.msGetUserMedia;
|
||||
var gun = Gun(location.origin + '/gun');
|
||||
var blast = gun.get('audio'), sight = gun.get('video');
|
||||
var be = $("#be"), vid = $('#video'), aud, NO;
|
||||
blast.on(function(data){
|
||||
console.log("<<<<<", data.frame);
|
||||
debug.value = data.frame;
|
||||
play(data.frame);
|
||||
});
|
||||
sight.on(function(data){
|
||||
show(data.frame);
|
||||
});
|
||||
navigator.getUserMedia({audio: true, video: false}, function talk(stream){
|
||||
return;
|
||||
var arr = [], mr = new MediaRecorder(stream, {audioBitsPerSecond: 6000});
|
||||
mr.start();
|
||||
setTimeout(function(){ talk(stream); setTimeout(function(){ mr.stop() },5); },250);
|
||||
mr.ondataavailable = function(e){
|
||||
//console.info('data available', e.data);
|
||||
arr.push(e.data);
|
||||
}
|
||||
mr.onstop = function(){
|
||||
encode(arr, function(b64){
|
||||
//console.log(">>>>>", b64);
|
||||
blast.put({frame: b64});
|
||||
});
|
||||
//arr = [];
|
||||
}
|
||||
}, function(err){ console.log(err) });
|
||||
navigator.getUserMedia({audio: false, video: true}, function(stream){
|
||||
var el = vid[0], draw = canvas.getContext('2d');
|
||||
el.src = (window.URL || window.webkitURL).createObjectURL(stream);
|
||||
el.play(); // Start the video in webcam
|
||||
;(function see(){
|
||||
canvas.width = el.videoWidth;
|
||||
canvas.height = el.videoHeight;
|
||||
draw.drawImage(el, 0, 0);
|
||||
var data = canvas.toDataURL('image/jpeg'); // Gets the Base64 encoded text for image
|
||||
sight.put({frame: data});
|
||||
setTimeout(see, 1000 / 10);
|
||||
}());
|
||||
}, function(err){ console.log(err) });
|
||||
|
||||
function encode(arr, cb){
|
||||
var blob = new Blob(arr, {type: 'audio/ogg'});
|
||||
var fileReader = new window.FileReader();
|
||||
fileReader.onloadend = function() {
|
||||
cb(fileReader.result);
|
||||
};
|
||||
fileReader.readAsDataURL(blob);
|
||||
//fileReader.readAsArrayBuffer(blob);
|
||||
}
|
||||
function play(b64){
|
||||
return;
|
||||
if(NO){ return }
|
||||
$('#audio').html('<audio autoplay><source src="'+b64+'"></audio>');
|
||||
}
|
||||
function show(frame){
|
||||
//if(NO){ return console.log(val); }
|
||||
$("#view").html("<img src='" + frame + "' width='75%'>");
|
||||
}
|
||||
}());
|
||||
</script>
|
605
gun.js
605
gun.js
@ -587,6 +587,31 @@
|
||||
module.exports = Graph;
|
||||
})(require, './graph');
|
||||
|
||||
;require(function(module){
|
||||
// request / response module, for asking and acking messages.
|
||||
require('./onto'); // depends upon onto!
|
||||
module.exports = function ask(cb, as){
|
||||
if(!this.on){ return }
|
||||
if(!(cb instanceof Function)){
|
||||
if(!cb || !as){ return }
|
||||
var id = cb['#'] || cb, tmp = (this.tag||empty)[id];
|
||||
if(!tmp){ return }
|
||||
tmp = this.on(id, as);
|
||||
clearTimeout(tmp.err);
|
||||
return true;
|
||||
}
|
||||
var id = (as && as['#']) || Math.random().toString(36).slice(2);
|
||||
if(!cb){ return id }
|
||||
var to = this.on(id, cb, as);
|
||||
to.err = to.err || setTimeout(function(){
|
||||
console.log(50, 'TIME OUT', to.err, id);
|
||||
to.next({err: "Error: No ACK received yet."});
|
||||
to.off();
|
||||
}, 1000 * 9); // TODO: Make configurable!!!
|
||||
return id;
|
||||
}
|
||||
})(require, './ask');
|
||||
|
||||
;require(function(module){
|
||||
var Type = require('./type');
|
||||
function Dup(opt){
|
||||
@ -636,9 +661,10 @@
|
||||
Gun.node = require('./node');
|
||||
Gun.state = require('./state');
|
||||
Gun.graph = require('./graph');
|
||||
Gun.dup = require('./dup');
|
||||
Gun.on = require('./onto');
|
||||
|
||||
Gun.ask = require('./ask');
|
||||
Gun.dup = require('./dup');
|
||||
|
||||
Gun._ = { // some reserved key words, these are not the only ones.
|
||||
node: Gun.node._ // all metadata of a node is stored in the meta property on the node.
|
||||
,soul: Gun.val.rel._ // a soul is a UUID of a node but it always points to the "latest" data known.
|
||||
@ -649,12 +675,11 @@
|
||||
|
||||
;(function(){
|
||||
Gun.create = function(at){
|
||||
at.on = at.on || Gun.on;
|
||||
at.root = at.root || at.gun;
|
||||
at.graph = at.graph || {};
|
||||
at.on = at.on || Gun.on;
|
||||
at.ask = at.ask || Gun.ask;
|
||||
at.dup = at.dup || Gun.dup();
|
||||
at.ask = Gun.on.ask;
|
||||
at.ack = Gun.on.ack;
|
||||
var gun = at.gun.opt(at.opt);
|
||||
if(!at.once){
|
||||
at.on('in', root, at);
|
||||
@ -663,42 +688,42 @@
|
||||
at.once = 1;
|
||||
return gun;
|
||||
}
|
||||
function root(at){
|
||||
function root(msg){
|
||||
//console.log("add to.next(at)"); // TODO: BUG!!!
|
||||
var ev = this, cat = ev.as, coat, tmp;
|
||||
if(!at.gun){ at.gun = cat.gun }
|
||||
if(!(tmp = at['#'])){ tmp = at['#'] = text_rand(9) }
|
||||
if(cat.dup.check(tmp)){ return }
|
||||
cat.dup.track(tmp);
|
||||
coat = obj_to(at, {gun: cat.gun});
|
||||
if(!cat.ack(at['@'], at)){
|
||||
if(at.get){
|
||||
Gun.on.get(coat);
|
||||
//cat.on('get', get(coat));
|
||||
var ev = this, at = ev.as, gun = at.gun, tmp;
|
||||
//if(!msg.gun){ msg.gun = at.gun }
|
||||
if(!(tmp = msg['#'])){ tmp = msg['#'] = text_rand(9) }
|
||||
if(at.dup.check(tmp)){ return }
|
||||
at.dup.track(tmp);
|
||||
msg = obj_to(msg);//, {gun: at.gun});
|
||||
if(!at.ask(msg['@'], msg)){
|
||||
if(msg.get){
|
||||
Gun.on.get(msg, gun);
|
||||
//at.on('get', get(msg));
|
||||
}
|
||||
if(at.put){
|
||||
Gun.on.put(coat);
|
||||
//cat.on('put', put(coat));
|
||||
if(msg.put){
|
||||
Gun.on.put(msg, gun);
|
||||
//at.on('put', put(msg));
|
||||
}
|
||||
}
|
||||
cat.on('out', coat);
|
||||
at.on('out', msg);
|
||||
}
|
||||
}());
|
||||
|
||||
;(function(){
|
||||
Gun.on.put = function(at){
|
||||
var cat = at.gun._, ctx = {gun: at.gun, graph: at.gun._.graph, put: {}, map: {}, machine: Gun.state()};
|
||||
if(!Gun.graph.is(at.put, null, verify, ctx)){ ctx.err = "Error: Invalid graph!" }
|
||||
if(ctx.err){ return cat.on('in', {'@': at['#'], err: Gun.log(ctx.err) }) }
|
||||
Gun.on.put = function(msg, gun){
|
||||
var at = gun._, ctx = {gun: gun, graph: at.graph, put: {}, map: {}, machine: Gun.state(), ack: msg['@']};
|
||||
if(!Gun.graph.is(msg.put, null, verify, ctx)){ ctx.err = "Error: Invalid graph!" }
|
||||
if(ctx.err){ return at.on('in', {'@': msg['#'], err: Gun.log(ctx.err) }) }
|
||||
obj_map(ctx.put, merge, ctx);
|
||||
obj_map(ctx.map, map, ctx);
|
||||
if(u !== ctx.defer){
|
||||
setTimeout(function(){
|
||||
Gun.on.put(at);
|
||||
}, ctx.defer - cat.machine);
|
||||
Gun.on.put(msg, gun);
|
||||
}, ctx.defer - at.machine);
|
||||
}
|
||||
if(!ctx.diff){ return }
|
||||
cat.on('put', obj_to(at, {put: ctx.diff}));
|
||||
at.on('put', obj_to(msg, {put: ctx.diff}));
|
||||
};
|
||||
function verify(val, key, node, soul){ var ctx = this;
|
||||
var state = Gun.state.is(node, key), tmp;
|
||||
@ -715,71 +740,49 @@
|
||||
(ctx.diff || (ctx.diff = {}))[soul] = Gun.state.to(node, key, ctx.diff[soul]);
|
||||
}
|
||||
function merge(node, soul){
|
||||
var cat = this.gun._, ref = (cat.next || empty)[soul];
|
||||
if(!ref){ return }
|
||||
var at = this.map[soul] = {
|
||||
var cat = this.gun._, at = (cat.next || empty)[soul];
|
||||
if(!at){ return }
|
||||
var msg = this.map[soul] = {
|
||||
put: this.node = node,
|
||||
get: this.soul = soul,
|
||||
gun: this.ref = ref
|
||||
gun: this.at = at
|
||||
};
|
||||
if(this.ack){ msg['@'] = this.ack }
|
||||
obj_map(node, each, this);
|
||||
cat.on('node', at);
|
||||
cat.on('node', msg);
|
||||
}
|
||||
function each(val, key){
|
||||
var graph = this.graph, soul = this.soul, cat = (this.ref._), tmp;
|
||||
var graph = this.graph, soul = this.soul, at = (this.at._), tmp;
|
||||
graph[soul] = Gun.state.to(this.node, key, graph[soul]);
|
||||
(cat.put || (cat.put = {}))[key] = val;
|
||||
at.put = Gun.state.to(this.node, key, at.put);
|
||||
}
|
||||
function map(at, soul){
|
||||
if(!at.gun){ return }
|
||||
(at.gun._).on('in', at);
|
||||
function map(msg, soul){
|
||||
if(!msg.gun){ return }
|
||||
(msg.gun._).on('in', msg);
|
||||
}
|
||||
|
||||
Gun.on.get = function(at){
|
||||
var cat = at.gun._, soul = at.get[_soul], node = cat.graph[soul], field = at.get[_field], tmp;
|
||||
var next = cat.next || (cat.next = {}), as = ((next[soul] || empty)._);
|
||||
if(!node || !as){ return cat.on('get', at) }
|
||||
Gun.on.get = function(msg, gun){
|
||||
var root = gun._, soul = msg.get[_soul], node = root.graph[soul], field = msg.get[_field], tmp;
|
||||
var next = root.next || (root.next = {}), at = ((next[soul] || empty)._);
|
||||
if(!node || !at){ return root.on('get', msg) }
|
||||
if(field){
|
||||
if(!obj_has(node, field)){ return cat.on('get', at) }
|
||||
if(!obj_has(node, field)){ return root.on('get', msg) }
|
||||
node = Gun.state.to(node, field);
|
||||
} else {
|
||||
node = Gun.obj.copy(node);
|
||||
}
|
||||
node = Gun.graph.node(node);
|
||||
tmp = as.ack;
|
||||
cat.on('in', {
|
||||
'@': at['#'],
|
||||
how: 'mem',
|
||||
//tmp = at.ack;
|
||||
root.on('in', {
|
||||
'@': msg['#'],
|
||||
//how: 'mem',
|
||||
put: node,
|
||||
gun: as.gun
|
||||
gun: gun
|
||||
});
|
||||
if(0 < tmp){
|
||||
return;
|
||||
}
|
||||
cat.on('get', at);
|
||||
}
|
||||
}());
|
||||
|
||||
;(function(){
|
||||
Gun.on.ask = function(cb, as){
|
||||
if(!this.on){ return }
|
||||
var id = text_rand(9);
|
||||
if(cb){
|
||||
var to = this.on(id, cb, as);
|
||||
to.err = setTimeout(function(){
|
||||
to.next({err: "Error: No ACK received yet."});
|
||||
to.off();
|
||||
}, 1000 * 9); // TODO: Make configurable!!!
|
||||
}
|
||||
return id;
|
||||
}
|
||||
Gun.on.ack = function(at, reply){
|
||||
if(!at || !reply || !this.on){ return }
|
||||
var id = at['#'] || at, tmp = (this.tag||empty)[id];
|
||||
if(!tmp){ return }
|
||||
this.on(id, reply);
|
||||
clearTimeout(tmp.err);
|
||||
return true;
|
||||
//if(0 < tmp){
|
||||
// return;
|
||||
//}
|
||||
root.on('get', msg);
|
||||
}
|
||||
}());
|
||||
|
||||
@ -810,7 +813,7 @@
|
||||
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 = Gun.state, _soul = Gun._.soul, _field = Gun._.field, rel_is = Gun.val.rel.is;
|
||||
var state = Gun.state, _soul = Gun._.soul, _field = Gun._.field, node_ = Gun._.node, rel_is = Gun.val.rel.is;
|
||||
var empty = {}, u;
|
||||
|
||||
console.debug = function(i, s){ return (console.debug.i && i === console.debug.i && console.debug.i++) && (console.log.apply(console, arguments) || s) };
|
||||
@ -881,132 +884,74 @@
|
||||
cat.on('out', output, cat); // However for output, there isn't really the global option. I must listen by adding my own listener individually BEFORE this one is ever called.
|
||||
return chain;
|
||||
}
|
||||
function output(at){
|
||||
var cat = this.as, gun = cat.gun, root = gun.back(-1), put, get, now, tmp;
|
||||
if(!at.gun){
|
||||
at.gun = gun;
|
||||
}
|
||||
if(get = at.get){
|
||||
if(tmp = get[_soul]){
|
||||
tmp = (root.get(tmp)._);
|
||||
if(obj_has(get, _field)){
|
||||
if(obj_has(put = tmp.put, get = get[_field])){
|
||||
tmp.on('in', {get: tmp.get, put: Gun.state.to(put, get), gun: tmp.gun}); // TODO: Ugly, clean up? Simplify all these if conditions (without ruining the whole chaining API)?
|
||||
|
||||
function output(msg){
|
||||
var put, get, at = this.as, back = at.back._, root = at.root._;
|
||||
if(!msg.gun){ msg.gun = at.gun }
|
||||
this.to.next(msg);
|
||||
if(get = msg.get){
|
||||
/*if(u !== at.put){
|
||||
at.on('in', at);
|
||||
return;
|
||||
}*/
|
||||
if(get['#'] || at.soul){
|
||||
get['#'] = get['#'] || at.soul;
|
||||
msg['#'] || (msg['#'] = root.opt.uuid());
|
||||
back = (root.gun.get(get['#'])._);
|
||||
if(!(get = get['.'])){
|
||||
if(obj_has(back, 'put')){
|
||||
//if(u !== back.put){
|
||||
back.on('in', back);
|
||||
}
|
||||
if(back.ack){
|
||||
return;
|
||||
}
|
||||
msg.gun = back.gun;
|
||||
back.ack = -1;
|
||||
} else
|
||||
if(obj_has(tmp, 'put')){
|
||||
//if(u !== tmp.put){
|
||||
tmp.on('in', tmp);
|
||||
if(obj_has(back.put, get)){
|
||||
back.on('in', {
|
||||
gun: back.gun,
|
||||
put: Gun.state.to(back.put, get),
|
||||
get: back.get
|
||||
});
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if(obj_has(get, _field)){
|
||||
get = get[_field];
|
||||
var next = get? (gun.get(get)._) : cat;
|
||||
// TODO: BUG! Handle plural chains by iterating over them.
|
||||
//if(obj_has(next, 'put')){ // potentially incorrect? Maybe?
|
||||
if(u !== next.put){ // potentially incorrect? Maybe?
|
||||
//next.tag['in'].last.next(next);
|
||||
next.on('in', next);
|
||||
return;
|
||||
}
|
||||
if(obj_has(cat, 'put')){
|
||||
//if(u !== cat.put){
|
||||
var val = cat.put, rel;
|
||||
if(rel = Gun.node.soul(val)){
|
||||
val = Gun.val.rel.ify(rel);
|
||||
}
|
||||
if(rel = Gun.val.rel.is(val)){
|
||||
if(!at.gun._){ return }
|
||||
(at.gun._).on('out', {
|
||||
get: tmp = {'#': rel, '.': get, gun: at.gun},
|
||||
'#': root._.ask(ack, tmp),
|
||||
gun: at.gun
|
||||
});
|
||||
return;
|
||||
}
|
||||
if(u === val || Gun.val.is(val)){
|
||||
if(!at.gun._){ return }
|
||||
(at.gun._).on('in', {
|
||||
get: get,
|
||||
gun: at.gun
|
||||
});
|
||||
return;
|
||||
}
|
||||
} else
|
||||
if(cat.map){
|
||||
obj_map(cat.map, function(proxy){
|
||||
proxy.at.on('in', proxy.at);
|
||||
});
|
||||
};
|
||||
if(cat.soul){
|
||||
if(!at.gun._){ return }
|
||||
(at.gun._).on('out', {
|
||||
get: tmp = {'#': cat.soul, '.': get, gun: at.gun},
|
||||
'#': root._.ask(ack, tmp),
|
||||
gun: at.gun
|
||||
});
|
||||
return;
|
||||
}
|
||||
if(cat.get){
|
||||
if(!cat.back._){ return }
|
||||
(cat.back._).on('out', {
|
||||
get: obj_put({}, _field, cat.get),
|
||||
gun: gun
|
||||
});
|
||||
return;
|
||||
}
|
||||
at = obj_to(at, {get: {}});
|
||||
} else {
|
||||
if(obj_has(cat, 'put')){
|
||||
//if(u !== cat.put){
|
||||
cat.on('in', cat);
|
||||
} else
|
||||
if(cat.map){
|
||||
obj_map(cat.map, function(proxy){
|
||||
proxy.at.on('in', proxy.at);
|
||||
});
|
||||
}
|
||||
if(cat.ack){
|
||||
if(!obj_has(cat, 'put')){ // u !== cat.put instead?
|
||||
//if(u !== cat.put){
|
||||
return;
|
||||
}
|
||||
}
|
||||
cat.ack = -1;
|
||||
if(cat.soul){
|
||||
cat.on('out', {
|
||||
get: tmp = {'#': cat.soul, gun: cat.gun},
|
||||
'#': root._.ask(ack, tmp),
|
||||
gun: cat.gun
|
||||
});
|
||||
return;
|
||||
}
|
||||
if(cat.get){
|
||||
if(!cat.back._){ return }
|
||||
(cat.back._).on('out', {
|
||||
get: obj_put({}, _field, cat.get),
|
||||
gun: cat.gun
|
||||
});
|
||||
return;
|
||||
}
|
||||
root.ask(ack, msg);
|
||||
return root.on('in', msg);
|
||||
}
|
||||
if(root.now){
|
||||
root.now[at.id] = root.now[at.id] || true;
|
||||
}
|
||||
if(get['.']){
|
||||
if(at.get){
|
||||
msg = {get: {'.': at.get}, gun: at.gun};
|
||||
return back.on('out', msg);
|
||||
}
|
||||
msg = {get: {}, gun: at.gun};
|
||||
return back.on('out', msg);
|
||||
}
|
||||
at.ack = at.ack || -1;
|
||||
if(at.get){
|
||||
msg.gun = at.gun;
|
||||
get['.'] = at.get;
|
||||
return back.on('out', msg);
|
||||
}
|
||||
}
|
||||
(cat.back._).on('out', at);
|
||||
return back.on('out', msg);
|
||||
}
|
||||
|
||||
function input(at){
|
||||
at = at._ || at;
|
||||
var ev = this, cat = this.as, gun = at.gun, coat = gun._, change = at.put, back = cat.back._ || empty, rel, tmp;
|
||||
if(0 > cat.ack && !at.ack && !Gun.val.rel.is(change)){ // for better behavior?
|
||||
cat.ack = 1;
|
||||
}
|
||||
if(cat.get && at.get !== cat.get){
|
||||
at = obj_to(at, {get: cat.get});
|
||||
}
|
||||
if(cat.field && coat !== cat){
|
||||
at = obj_to(at, {gun: cat.gun});
|
||||
if(coat.ack){
|
||||
cat.ack = cat.ack || coat.ack;
|
||||
cat.ack = coat.ack;
|
||||
//cat.ack = cat.ack || coat.ack;
|
||||
}
|
||||
}
|
||||
if(u === change){
|
||||
@ -1021,12 +966,19 @@
|
||||
return;
|
||||
}
|
||||
if(cat.soul){
|
||||
if(cat.root._.now){ at = obj_to(at, {put: change = coat.put}) } // TODO: Ugly hack for uncached synchronous maps.
|
||||
//if(cat.root._.now){ at = obj_to(at, {put: change = coat.put}) } // TODO: Ugly hack for uncached synchronous maps.
|
||||
ev.to.next(at);
|
||||
echo(cat, at, ev);
|
||||
obj_map(change, map, {at: at, cat: cat});
|
||||
return;
|
||||
}
|
||||
/*if(rel = Gun.val.rel.is(change)){
|
||||
if(tmp = (gun.back(-1).get(rel)._).put){
|
||||
change = tmp; // this will cause performance to turn to mush, maybe use `.now` check?
|
||||
}
|
||||
//if(tmp.put){ change = tmp.put; }
|
||||
}
|
||||
if(!rel || tmp){*/
|
||||
if(!(rel = Gun.val.rel.is(change))){
|
||||
if(Gun.val.is(change)){
|
||||
if(cat.field || cat.soul){
|
||||
@ -1058,31 +1010,36 @@
|
||||
echo(cat, at, ev);
|
||||
}
|
||||
Gun.chain.chain.input = input;
|
||||
function relate(cat, at, coat, rel){
|
||||
if(!rel || node_ === cat.get){ return }
|
||||
var tmp = (cat.root.get(rel)._);
|
||||
if(cat.field){
|
||||
coat = tmp;
|
||||
function relate(at, msg, from, rel){
|
||||
if(!rel || node_ === at.get){ return }
|
||||
var tmp = (at.root.get(rel)._);
|
||||
if(at.has){
|
||||
from = tmp;
|
||||
} else
|
||||
if(coat.field){
|
||||
relate(coat, at, coat, rel);
|
||||
if(from.has){
|
||||
relate(from, msg, from, rel);
|
||||
}
|
||||
if(coat === cat){ return }
|
||||
(coat.echo || (coat.echo = {}))[cat.id] = cat;
|
||||
if(cat.field && !(cat.map||empty)[coat.id]){
|
||||
not(cat, at);
|
||||
if(from === at){ return }
|
||||
(from.echo || (from.echo = {}))[at.id] = at;
|
||||
if(at.has && !(at.map||empty)[from.id]){ // if we haven't seen this before.
|
||||
not(at, msg);
|
||||
}
|
||||
tmp = (cat.map || (cat.map = {}))[coat.id] = cat.map[coat.id] || {at: coat};
|
||||
if(rel === tmp.rel){ return }
|
||||
ask(cat, tmp.rel = rel);
|
||||
tmp = (at.map || (at.map = {}))[from.id] = at.map[from.id] || {at: from};
|
||||
var now = at.root._.now;
|
||||
if(rel === tmp.rel){
|
||||
if(!now){ return }
|
||||
if(u === now[at.id]){ return }
|
||||
if((now._ || (now._ = {}))[at.id]){ return } now._[at.id] = true;
|
||||
}
|
||||
ask(at, tmp.rel = rel);
|
||||
}
|
||||
function echo(cat, at, ev){
|
||||
if(!cat.echo){ return } // || node_ === at.get ????
|
||||
if(cat.field){ at = obj_to(at, {event: ev}) }
|
||||
obj_map(cat.echo, reverb, at);
|
||||
function echo(at, msg, ev){
|
||||
if(!at.echo){ return } // || node_ === at.get ?
|
||||
if(at.has || at.field){ msg = obj_to(msg, {event: ev}) }
|
||||
obj_map(at.echo, reverb, msg);
|
||||
}
|
||||
function reverb(cat){
|
||||
cat.on('in', this);
|
||||
function reverb(to){
|
||||
to.on('in', this);
|
||||
}
|
||||
function map(data, key){ // Map over only the changes on every update.
|
||||
var cat = this.cat, next = cat.next || empty, via = this.at, gun, chain, at, tmp;
|
||||
@ -1109,17 +1066,19 @@
|
||||
via: via
|
||||
});
|
||||
}
|
||||
function not(cat, at){
|
||||
if(!(cat.field || cat.soul)){ return }
|
||||
var tmp = cat.map;
|
||||
cat.map = null;
|
||||
if(null === tmp){ return }
|
||||
if(u === tmp && cat.put !== u){ return } // TODO: Bug? Threw second condition in for a particular test, not sure if a counter example is tested though.
|
||||
function not(at, msg){
|
||||
if(!(at.has || at.soul)){ return }
|
||||
var tmp = at.map, root = at.root._;
|
||||
at.map = null;
|
||||
if(!root.now || !root.now[at.id]){
|
||||
if((u === msg.put && !msg['@']) && null === tmp){ return }
|
||||
}
|
||||
if(u === tmp && at.put !== u){ return } // TODO: Bug? Threw second condition in for a particular test, not sure if a counter example is tested though.
|
||||
obj_map(tmp, function(proxy){
|
||||
if(!(proxy = proxy.at)){ return }
|
||||
obj_del(proxy.echo, cat.id);
|
||||
obj_del(proxy.echo, at.id);
|
||||
});
|
||||
obj_map(cat.next, function(gun, key){
|
||||
obj_map(at.next, function(gun, key){
|
||||
var coat = (gun._);
|
||||
coat.put = u;
|
||||
if(coat.ack){
|
||||
@ -1132,37 +1091,39 @@
|
||||
});
|
||||
});
|
||||
}
|
||||
function ask(cat, soul){
|
||||
var tmp = (cat.root.get(soul)._);
|
||||
if(cat.ack){
|
||||
tmp.ack = tmp.ack || -1;
|
||||
tmp.on('out', {
|
||||
get: tmp = {'#': soul, gun: tmp.gun},
|
||||
'#': cat.root._.ask(ack, tmp)
|
||||
});
|
||||
function ask(at, soul){
|
||||
var tmp = (at.root.get(soul)._);
|
||||
if(at.ack){
|
||||
//tmp.ack = tmp.ack || -1;
|
||||
tmp.on('out', {get: {'#': soul}});
|
||||
return;
|
||||
}
|
||||
obj_map(cat.next, function(gun, key){
|
||||
(gun._).on('out', {
|
||||
get: gun = {'#': soul, '.': key, gun: gun},
|
||||
'#': cat.root._.ask(ack, gun)
|
||||
});
|
||||
obj_map(at.next, function(gun, key){
|
||||
//(tmp.gun.get(key)._).on('out', {get: {'#': soul, '.': key}});
|
||||
//tmp.on('out', {get: {'#': soul, '.': key}});
|
||||
(gun._).on('out', {get: {'#': soul, '.': key}});
|
||||
//at.on('out', {get: {'#': soul, '.': key}});
|
||||
});
|
||||
}
|
||||
function ack(at, ev){
|
||||
var as = this.as, cat = as.gun._;
|
||||
if(!at.put || (as['.'] && !obj_has(at.put[as['#']], cat.get))){
|
||||
if(cat.put !== u){ return }
|
||||
cat.on('in', {
|
||||
get: cat.get,
|
||||
put: cat.put = u,
|
||||
gun: cat.gun,
|
||||
function ack(msg, ev){
|
||||
var as = this.as, get = as.get || empty, at = as.gun._;
|
||||
if(at.ack){ at.ack = (at.ack + 1) || 1 }
|
||||
if(!msg.put || node_ == get['.'] || (get['.'] && !obj_has(msg.put[get['#']], at.get))){
|
||||
if(at.put !== u){ return }
|
||||
//at.ack = 0;
|
||||
at.on('in', {
|
||||
get: at.get,
|
||||
put: at.put = u,
|
||||
gun: at.gun,
|
||||
'@': msg['@']
|
||||
})
|
||||
return;
|
||||
}
|
||||
at.gun = cat.root;
|
||||
//if(/*!msg.gun &&*/ !get['.'] && get['#']){ at.ack = (at.ack + 1) || 1 }
|
||||
//msg = obj_to(msg);
|
||||
msg.gun = at.root;
|
||||
//Gun.on('put', at);
|
||||
Gun.on.put(at);
|
||||
Gun.on.put(msg, at.root);
|
||||
}
|
||||
var empty = {}, u;
|
||||
var obj = Gun.obj, obj_has = obj.has, obj_put = obj.put, obj_del = obj.del, obj_to = obj.to, obj_map = obj.map;
|
||||
@ -1180,15 +1141,16 @@
|
||||
}
|
||||
} else
|
||||
if(key instanceof Function){
|
||||
var gun = this, at = gun._;
|
||||
var gun = this, at = gun._, root = at.root._;
|
||||
as = cb || {};
|
||||
as.use = key;
|
||||
as.out = as.out || {cap: 1};
|
||||
as.out = as.out || {};
|
||||
as.out.get = as.out.get || {};
|
||||
'_' != at.get && ((at.root._).now = true); // ugly hack for now.
|
||||
at.on('in', use, as);
|
||||
var tmp = at.on('in', use, as);
|
||||
if(root.now){ ++root.now.$ }
|
||||
(root.now || (root.now = {$:1}))[as.now = at.id] = tmp;
|
||||
at.on('out', as.out);
|
||||
(at.root._).now = false;
|
||||
if(!(--root.now.$)){ obj.del(root, 'now'); }
|
||||
return gun;
|
||||
} else
|
||||
if(num_is(key)){
|
||||
@ -1210,23 +1172,33 @@
|
||||
var cat = back._, next = cat.next, gun = back.chain(), at = gun._;
|
||||
if(!next){ next = cat.next = {} }
|
||||
next[at.get = key] = gun;
|
||||
if(cat.root === back){ at.soul = key }
|
||||
else if(cat.soul || cat.field){ at.field = key }
|
||||
return gun;
|
||||
}
|
||||
function use(at){
|
||||
var ev = this, as = ev.as, gun = at.gun, cat = gun._, data = at.put, tmp;
|
||||
if(u === data){
|
||||
data = cat.put;
|
||||
}
|
||||
if((tmp = data) && tmp[rel._] && (tmp = rel.is(tmp))){
|
||||
tmp = (cat.root.get(tmp)._);
|
||||
if(u !== tmp.put){
|
||||
at = obj_to(at, {put: tmp.put});
|
||||
if(cat.root === back){
|
||||
at.soul = key;
|
||||
} else
|
||||
if(cat.soul || cat.field){ // TODO: Convert field to has!
|
||||
at.field = at.has = key;
|
||||
if(obj_has(cat.put, key)){
|
||||
//at.put = cat.put[key];
|
||||
}
|
||||
}
|
||||
as.use(at, at.event || ev);
|
||||
ev.to.next(at);
|
||||
return gun;
|
||||
}
|
||||
function use(msg){
|
||||
var ev = this, as = ev.as, gun = msg.gun, at = gun._, root = at.root._, data = msg.put, tmp;
|
||||
if((tmp = root.now) && ev !== tmp[as.now]){
|
||||
return ev.to.next(msg);
|
||||
}
|
||||
if(u === data){
|
||||
data = at.put;
|
||||
}
|
||||
if((tmp = data) && tmp[rel._] && (tmp = rel.is(tmp))){
|
||||
tmp = (at.root.get(tmp)._);
|
||||
if(u !== tmp.put){
|
||||
msg = obj_to(msg, {put: tmp.put});
|
||||
}
|
||||
}
|
||||
as.use(msg, msg.event || ev);
|
||||
ev.to.next(msg);
|
||||
}
|
||||
var obj = Gun.obj, obj_has = obj.has, obj_to = Gun.obj.to;
|
||||
var num_is = Gun.num.is;
|
||||
@ -1264,10 +1236,11 @@
|
||||
return gun;
|
||||
}
|
||||
if(Gun.is(data)){
|
||||
data.get(function(at,ev){ev.off();
|
||||
var s = Gun.node.soul(at.put);
|
||||
if(!s){Gun.log("The reference you are saving is a", typeof at.put, '"'+ as.put +'", not a node (object)!');return}
|
||||
gun.put(Gun.val.rel.ify(s), cb, as);
|
||||
data.get('_').get(function(at, ev, tmp){ ev.off();
|
||||
if(!(tmp = at.gun) || !(tmp = tmp._.back) || !tmp._.soul){
|
||||
return Gun.log("The reference you are saving is a", typeof at.put, '"'+ as.put +'", not a node (object)!');
|
||||
}
|
||||
gun.put(Gun.val.rel.ify(tmp._.soul), cb, as);
|
||||
});
|
||||
return gun;
|
||||
}
|
||||
@ -1280,7 +1253,7 @@
|
||||
as.ref.get('_').get(any, {as: as});
|
||||
if(!as.out){
|
||||
// TODO: Perf idea! Make a global lock, that blocks everything while it is on, but if it is on the lock it does the expensive lookup to see if it is a dependent write or not and if not then it proceeds full speed. Meh? For write heavy async apps that would be terrible.
|
||||
as.res = as.res || noop; // Gun.on.stun(as.ref); // TODO: BUG! Deal with locking?
|
||||
as.res = as.res || stun; // Gun.on.stun(as.ref); // TODO: BUG! Deal with locking?
|
||||
as.gun._.stun = as.ref._.stun;
|
||||
}
|
||||
return gun;
|
||||
@ -1299,17 +1272,37 @@
|
||||
as.batch();
|
||||
}
|
||||
|
||||
function stun(cb){
|
||||
if(cb){ cb() }
|
||||
return;
|
||||
var as = this;
|
||||
if(!as.ref){ return }
|
||||
if(cb){
|
||||
as.after = as.ref._.tag;
|
||||
as.now = as.ref._.tag = {};
|
||||
cb();
|
||||
return;
|
||||
}
|
||||
if(as.after){
|
||||
as.ref._.tag = as.after;
|
||||
}
|
||||
}
|
||||
function batch(){ var as = this;
|
||||
if(!as.graph || obj_map(as.stun, no)){ return }
|
||||
(as.res||iife)(function(){
|
||||
as.res = as.res || function(cb){ if(cb){ cb() } };
|
||||
as.res(function(){
|
||||
var cat = (as.gun.back(-1)._), ask = cat.ask(function(ack){
|
||||
this.off(); // One response is good enough for us currently. Later we may want to adjust this.
|
||||
if(!as.ack){ return }
|
||||
as.ack(ack, this);
|
||||
}, as.opt);
|
||||
var tmp = cat.root._.now; obj.del(cat.root._, 'now');
|
||||
(as.ref._).now = true;
|
||||
(as.ref._).on('out', {
|
||||
gun: as.ref, put: as.out = as.env.graph, opt: as.opt, '#': ask
|
||||
});
|
||||
obj.del((as.ref._), 'now');
|
||||
cat.root._.now = tmp;
|
||||
}, as);
|
||||
if(as.res){ as.res() }
|
||||
} function no(v,f){ if(v){ return true } }
|
||||
@ -1353,6 +1346,7 @@
|
||||
return;
|
||||
}
|
||||
var cat = (at.gun._.back._), data = cat.put, opt = as.opt||{}, root, tmp;
|
||||
if((tmp = as.ref) && tmp._.now){ return }
|
||||
ev.off();
|
||||
if(as.ref !== as.gun){
|
||||
tmp = (as.gun._).get || cat.get;
|
||||
@ -1422,7 +1416,7 @@
|
||||
opt = (true === opt)? {change: true} : opt || {};
|
||||
opt.ok = tag;
|
||||
opt.last = {};
|
||||
gun.get(ok, opt); // TODO: PERF! Event listener leak!!!????
|
||||
gun.get(ok, opt); // TODO: PERF! Event listener leak!!!?
|
||||
return gun;
|
||||
}
|
||||
|
||||
@ -1463,6 +1457,7 @@
|
||||
if(cb){
|
||||
(opt = opt || {}).ok = cb;
|
||||
opt.cat = at;
|
||||
opt.out = {'#': Gun.text.random(9)};
|
||||
gun.get(val, {as: opt});
|
||||
opt.async = true; //opt.async = at.stun? 1 : true;
|
||||
} else {
|
||||
@ -1476,23 +1471,25 @@
|
||||
return gun;
|
||||
}
|
||||
|
||||
function val(at, ev, to){
|
||||
var opt = this.as, cat = opt.cat, gun = at.gun, coat = gun._, data = coat.put || at.put, tmp;
|
||||
function val(msg, ev, to){
|
||||
var opt = this.as, cat = opt.cat, gun = msg.gun, coat = gun._, data = coat.put || msg.put, tmp;
|
||||
if(u === data){
|
||||
//return;
|
||||
}
|
||||
if(data && data[rel._] && (tmp = rel.is(data))){
|
||||
//if(coat.soul && !(0 < coat.ack)){ return }
|
||||
if(tmp = Gun.node.soul(data) || rel.is(data)){
|
||||
//if(data && data[rel._] && (tmp = rel.is(data))){
|
||||
tmp = (cat.root.get(tmp)._);
|
||||
if(u === tmp.put){
|
||||
if(u === tmp.put){//} || !(0 < tmp.ack)){
|
||||
return;
|
||||
}
|
||||
data = tmp.put;
|
||||
}
|
||||
if(ev.wait){ clearTimeout(ev.wait) }
|
||||
//if(!to && (!(0 < coat.ack) || ((true === opt.async) && 0 !== opt.wait))){
|
||||
if(!opt.async){
|
||||
if(!to){
|
||||
ev.wait = setTimeout(function(){
|
||||
val.call({as:opt}, at, ev, ev.wait || 1)
|
||||
val.call({as:opt}, msg, ev, ev.wait || 1);
|
||||
}, opt.wait || 99);
|
||||
return;
|
||||
}
|
||||
@ -1502,7 +1499,7 @@
|
||||
if((opt.seen = opt.seen || {})[coat.id]){ return }
|
||||
opt.seen[coat.id] = true;
|
||||
}
|
||||
opt.ok.call(at.gun || opt.gun, data, at.get);
|
||||
opt.ok.call(msg.gun || opt.gun, data, msg.get);
|
||||
}
|
||||
|
||||
Gun.chain.off = function(){
|
||||
@ -1558,6 +1555,7 @@
|
||||
if(chain = cat.fields){ return chain }
|
||||
chain = cat.fields = gun.chain();
|
||||
chain._.val = gun.back('val');
|
||||
chain._.MAPOF = cat.soul;
|
||||
gun.on('in', map, chain._);
|
||||
return chain;
|
||||
}
|
||||
@ -1574,16 +1572,16 @@
|
||||
});
|
||||
return chain;
|
||||
}
|
||||
function map(at){
|
||||
if(!at.put || Gun.val.is(at.put)){ return }
|
||||
function map(msg){
|
||||
if(!msg.put || Gun.val.is(msg.put)){ return }
|
||||
if(this.as.val){ this.off() } // TODO: Ugly hack!
|
||||
obj_map(at.put, each, {cat: this.as, gun: at.gun});
|
||||
this.to.next(at);
|
||||
obj_map(msg.put, each, {at: this.as, msg: msg});
|
||||
this.to.next(msg);
|
||||
}
|
||||
function each(v,f){
|
||||
if(n_ === f){ return }
|
||||
var cat = this.cat, gun = this.gun.get(f), at = (gun._);
|
||||
(at.echo || (at.echo = {}))[cat.id] = cat;
|
||||
var msg = this.msg, gun = msg.gun, at = this.at, tmp = (gun.get(f)._);
|
||||
(tmp.echo || (tmp.echo = {}))[at.id] = at;
|
||||
}
|
||||
var obj_map = Gun.obj.map, noop = function(){}, event = {stun: noop, off: noop}, n_ = Gun.node._, u;
|
||||
})(require, './map');
|
||||
@ -1593,6 +1591,7 @@
|
||||
Gun.chain.set = function(item, cb, opt){
|
||||
var gun = this, soul;
|
||||
cb = cb || function(){};
|
||||
opt = opt || {}; opt.item = opt.item || item;
|
||||
if(soul = Gun.node.soul(item)){ return gun.set(gun.back(-1).get(soul), cb, opt) }
|
||||
if(!Gun.is(item)){
|
||||
if(Gun.obj.is(item)){ return gun.set(gun._.root.put(item), cb, opt) }
|
||||
@ -1622,16 +1621,16 @@
|
||||
If you update anything here, consider updating the other adapters as well.
|
||||
*/
|
||||
|
||||
Gun.on('opt', function(ctx){
|
||||
this.to.next(ctx);
|
||||
var opt = ctx.opt;
|
||||
if(ctx.once){ return }
|
||||
Gun.on('opt', function(root){
|
||||
this.to.next(root);
|
||||
var opt = root.opt;
|
||||
if(root.once){ return }
|
||||
if(false === opt.localStorage){ return }
|
||||
opt.file = opt.file || opt.prefix || 'gun/'; // support old option name.
|
||||
var graph = ctx.graph, acks = {}, count = 0, to;
|
||||
var graph = root.graph, acks = {}, count = 0, to;
|
||||
var disk = Gun.obj.ify(store.getItem(opt.file)) || {};
|
||||
|
||||
ctx.on('put', function(at){
|
||||
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.
|
||||
@ -1643,9 +1642,9 @@
|
||||
to = setTimeout(flush, opt.wait || 1);
|
||||
});
|
||||
|
||||
ctx.on('get', function(at){
|
||||
root.on('get', function(at){
|
||||
this.to.next(at);
|
||||
var gun = at.gun, lex = at.get, soul, data, opt, u;
|
||||
var lex = at.get, soul, data, u;
|
||||
//setTimeout(function(){
|
||||
if(!lex || !(soul = lex[Gun._.soul])){ return }
|
||||
//if(0 >= at.cap){ return }
|
||||
@ -1654,11 +1653,11 @@
|
||||
if(data && field){
|
||||
data = Gun.state.to(data, field);
|
||||
}
|
||||
if(!data && !Gun.obj.empty(gun.back('opt.peers'))){ // if data not found, don't ack if there are peers.
|
||||
if(!data && !Gun.obj.empty(opt.peers)){ // if data not found, don't ack if there are peers.
|
||||
return; // Hmm, what if we have peers but we are disconnected?
|
||||
}
|
||||
gun.on('in', {'@': at['#'], put: Gun.graph.node(data), how: 'lS'});
|
||||
//},11);
|
||||
root.on('in', {'@': at['#'], put: Gun.graph.node(data), how: 'lS'});
|
||||
//},1);
|
||||
});
|
||||
|
||||
var map = function(val, key, node, soul){
|
||||
@ -1676,7 +1675,7 @@
|
||||
}catch(e){ Gun.log(err = e || "localStorage failure") }
|
||||
if(!err && !Gun.obj.empty(opt.peers)){ return } // only ack if there are no peers.
|
||||
Gun.obj.map(ack, function(yes, id){
|
||||
ctx.on('in', {
|
||||
root.on('in', {
|
||||
'@': id,
|
||||
err: err,
|
||||
ok: 0 // localStorage isn't reliable, so make its `ok` code be a low number.
|
||||
@ -1694,17 +1693,17 @@
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
Gun.on('opt', function(ctx){
|
||||
this.to.next(ctx);
|
||||
var opt = ctx.opt;
|
||||
if(ctx.once){ return }
|
||||
Gun.on('opt', function(root){
|
||||
this.to.next(root);
|
||||
var opt = root.opt;
|
||||
if(root.once){ return }
|
||||
if(false === opt.WebSocket){ return }
|
||||
var ws = opt.ws || (opt.ws = {}); ws.who = 0;
|
||||
Gun.obj.map(opt.peers, function(){ ++ws.who });
|
||||
if(ctx.once){ return }
|
||||
if(root.once){ return }
|
||||
var batch;
|
||||
|
||||
ctx.on('out', function(at){
|
||||
root.on('out', function(at){
|
||||
this.to.next(at);
|
||||
if(at.ws && 1 == ws.who){ return } // performance hack for reducing echoes.
|
||||
batch = JSON.stringify(at);
|
||||
@ -1719,13 +1718,13 @@
|
||||
ws.drain = null;
|
||||
if(!tmp.length){ return }
|
||||
batch = JSON.stringify(tmp);
|
||||
Gun.obj.map(opt.peers, send, ctx);
|
||||
Gun.obj.map(opt.peers, send, root);
|
||||
}, opt.wait || 1);
|
||||
Gun.obj.map(opt.peers, send, ctx);
|
||||
Gun.obj.map(opt.peers, send, root);
|
||||
});
|
||||
function send(peer){
|
||||
var ctx = this, msg = batch;
|
||||
var wire = peer.wire || open(peer, ctx);
|
||||
var root = this, msg = batch;
|
||||
var wire = peer.wire || open(peer, root);
|
||||
if(!wire){ return }
|
||||
if(wire.readyState === wire.OPEN){
|
||||
wire.send(msg);
|
||||
@ -1733,19 +1732,19 @@
|
||||
}
|
||||
(peer.queue = peer.queue || []).push(msg);
|
||||
}
|
||||
function receive(msg, peer, ctx){
|
||||
if(!ctx || !msg){ return }
|
||||
function receive(msg, peer, root){
|
||||
if(!root || !msg){ return }
|
||||
try{msg = JSON.parse(msg.data || msg);
|
||||
}catch(e){}
|
||||
if(msg instanceof Array){
|
||||
var i = 0, m;
|
||||
while(m = msg[i++]){
|
||||
receive(m, peer, ctx);
|
||||
receive(m, peer, root);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(1 == ws.who){ msg.ws = noop } // If there is only 1 client, just use noop since it doesn't matter.
|
||||
ctx.on('in', msg);
|
||||
root.on('in', msg);
|
||||
}
|
||||
function open(peer, as){
|
||||
if(!peer || !peer.url){ return }
|
||||
|
2
gun.min.js
vendored
2
gun.min.js
vendored
File diff suppressed because one or more lines are too long
@ -36,7 +36,7 @@ Gun.on('opt', function(ctx){
|
||||
|
||||
ctx.on('get', function(at){
|
||||
this.to.next(at);
|
||||
var gun = at.gun, lex = at.get, soul, data, opt, u;
|
||||
var lex = at.get, soul, data, opt, u;
|
||||
//setTimeout(function(){
|
||||
if(!lex || !(soul = lex[Gun._.soul])){ return }
|
||||
//if(0 >= at.cap){ return }
|
||||
@ -45,7 +45,7 @@ Gun.on('opt', function(ctx){
|
||||
if(data && field){
|
||||
data = Gun.state.to(data, field);
|
||||
}
|
||||
gun.on('in', {'@': at['#'], put: Gun.graph.node(data)});
|
||||
ctx.on('in', {'@': at['#'], put: Gun.graph.node(data)});
|
||||
//},11);
|
||||
});
|
||||
|
||||
@ -53,7 +53,7 @@ Gun.on('opt', function(ctx){
|
||||
disk[soul] = Gun.state.to(node, key, disk[soul]);
|
||||
}
|
||||
|
||||
var wait;
|
||||
var wait, u;
|
||||
var flush = function(){
|
||||
if(wait){ return }
|
||||
clearTimeout(to);
|
||||
@ -68,7 +68,7 @@ Gun.on('opt', function(ctx){
|
||||
ctx.on('in', {
|
||||
'@': id,
|
||||
err: err,
|
||||
ok: 1
|
||||
ok: err? u : 1
|
||||
});
|
||||
});
|
||||
if(1 < tmp){ flush() }
|
||||
|
@ -30,7 +30,7 @@ Gun.on('opt', function(ctx){
|
||||
|
||||
ctx.on('get', function(at){
|
||||
this.to.next(at);
|
||||
var gun = at.gun, lex = at.get, soul, data, opt, u;
|
||||
var lex = at.get, soul, data, opt, u;
|
||||
//setTimeout(function(){
|
||||
if(!lex || !(soul = lex[Gun._.soul])){ return }
|
||||
//if(0 >= at.cap){ return }
|
||||
@ -39,7 +39,7 @@ Gun.on('opt', function(ctx){
|
||||
if(data && field){
|
||||
data = Gun.state.to(data, field);
|
||||
}
|
||||
gun.on('in', {'@': at['#'], put: Gun.graph.node(data)});
|
||||
ctx.on('in', {'@': at['#'], put: Gun.graph.node(data)});
|
||||
//},11);
|
||||
});
|
||||
|
||||
|
471
lib/radisk.js
471
lib/radisk.js
@ -3,71 +3,80 @@ var Gun = require('../gun');
|
||||
var Radix = require('./radix');
|
||||
|
||||
function Radisk(opt){
|
||||
/*
|
||||
Any and all storage adapters should...
|
||||
1. If not busy, write to disk immediately.
|
||||
2. If busy, batch to disk. (Improves performance, reduces potential disk corruption)
|
||||
3. If a batch exceeds a certain number of writes, force atomic batch to disk. (This caps total performance, but reduces potential loss)
|
||||
*/
|
||||
var radisk = function(key, val, cb){
|
||||
if(0 <= key.indexOf('_') || 0 <= key.indexOf('$')){ // TODO: BUG! Fix!
|
||||
var err = "ERROR: Radix and Radisk not tested against _ or $ keys!";
|
||||
console.log(err);
|
||||
cb = cb || val;
|
||||
if(cb instanceof Function){ cb(err) }
|
||||
return;
|
||||
}
|
||||
if(val instanceof Function){
|
||||
cb = val;
|
||||
val = radisk.batch(key);
|
||||
if(u !== val){
|
||||
return cb(null, val);
|
||||
}
|
||||
if(radisk.was){
|
||||
val = radisk.was(key);
|
||||
if(u !== val){
|
||||
return cb(null, val);
|
||||
}
|
||||
}
|
||||
console.log("READ FROM DISK");
|
||||
return cb(null, val);
|
||||
}
|
||||
radisk.batch(key, val);
|
||||
if(cb){ radisk.batch.acks.push(cb) }
|
||||
if(!count++){ return thrash() } // (1)
|
||||
if(opt.batch <= count){ return thrash() } // (3)
|
||||
clearTimeout(to); // (2)
|
||||
to = setTimeout(thrash, opt.wait);
|
||||
};
|
||||
radisk.batch = Radix();
|
||||
radisk.batch.acks = [];
|
||||
var count = 0, wait, to, u;
|
||||
|
||||
opt = opt || {};
|
||||
opt.file = String(opt.file || 'radata');
|
||||
opt.size = opt.size || (1024 * 1024 * 10); // 10MB
|
||||
opt.batch = opt.batch || 10 * 1000;
|
||||
opt.wait = opt.wait || 1;
|
||||
opt.nest = opt.nest || ' ';
|
||||
opt.size = opt.size || (1024 * 1024 * 10); // 10MB
|
||||
opt.code = opt.code || {};
|
||||
opt.code.from = opt.code.from || '!';
|
||||
|
||||
console.log("Warning: Radix storage engine has not been tested with all types of values and keys yet.");
|
||||
if(!fs.existsSync(opt.file)){ fs.mkdirSync(opt.file) }
|
||||
if(!opt.store){
|
||||
return Gun.log("ERROR: Radisk needs `opt.store` interface with `{get: fn, put: fn, list: fn}`!");
|
||||
}
|
||||
if(!opt.store.put){
|
||||
return Gun.log("ERROR: Radisk needs `store.put` interface with `(file, data, cb)`!");
|
||||
}
|
||||
if(!opt.store.get){
|
||||
return Gun.log("ERROR: Radisk needs `store.get` interface with `(file, cb)`!");
|
||||
}
|
||||
if(!opt.store.list){
|
||||
return Gun.log("ERROR: Radisk needs a streaming `store.list` interface with `(cb)`!");
|
||||
}
|
||||
|
||||
var thrash = function(){
|
||||
if(wait){ return }
|
||||
clearTimeout(to);
|
||||
wait = true;
|
||||
var was = radisk.was = radisk.batch;
|
||||
radisk.batch = null;
|
||||
radisk.batch = Radix();
|
||||
radisk.batch.acks = [];
|
||||
chunk(radisk.was, function(err, ok){
|
||||
radisk.was = null;
|
||||
wait = false;
|
||||
var tmp = count;
|
||||
count = 0;
|
||||
Gun.obj.map(was.acks, function(cb){cb(err, ok)});
|
||||
if(1 < tmp){ thrash() }
|
||||
/*
|
||||
Any and all storage adapters should...
|
||||
1. Because writing to disk takes time, we should batch data to disk. This improves performance, and reduces potential disk corruption.
|
||||
2. If a batch exceeds a certain number of writes, we should immediately write to disk when physically possible. This caps total performance, but reduces potential loss.
|
||||
*/
|
||||
var r = function(key, val, cb){
|
||||
key = ''+key;
|
||||
if(val instanceof Function){
|
||||
cb = val;
|
||||
val = r.batch(key);
|
||||
console.debug(3, 'batch', val);
|
||||
if(u !== val){
|
||||
return cb(u, val);
|
||||
}
|
||||
if(r.thrash.at){
|
||||
val = r.thrash.at(key);
|
||||
if(u !== val){
|
||||
return cb(u, val);
|
||||
}
|
||||
}
|
||||
//console.log("READ FROM DISK");
|
||||
return r.read(key, cb);
|
||||
}
|
||||
r.batch(key, val);
|
||||
if(cb){ r.batch.acks.push(cb) }
|
||||
if(++r.batch.ed >= opt.batch){ return r.thrash() } // (2)
|
||||
clearTimeout(r.batch.to); // (1)
|
||||
r.batch.to = setTimeout(r.thrash, opt.wait);
|
||||
}
|
||||
|
||||
r.batch = Radix();
|
||||
r.batch.acks = [];
|
||||
r.batch.ed = 0;
|
||||
|
||||
r.thrash = function(){
|
||||
var thrash = r.thrash;
|
||||
if(thrash.ing){ return thrash.more = true }
|
||||
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;
|
||||
r.save(batch, function(err, ok){
|
||||
if(++i > 1){ return }
|
||||
if(err){ Gun.log(err) }
|
||||
Gun.obj.map(batch.acks, function(cb){ cb(err, ok) });
|
||||
thrash.at = null;
|
||||
thrash.ing = false;
|
||||
if(thrash.more){ thrash() }
|
||||
});
|
||||
}
|
||||
|
||||
@ -80,125 +89,255 @@ function Radisk(opt){
|
||||
6. Merge and write all of those to the in-memory file and back to disk.
|
||||
7. If file to large, split. More details needed here.
|
||||
*/
|
||||
function chunk(radix, cb){
|
||||
var step = {
|
||||
check: function(tree, key){
|
||||
if(key < step.start){ return }
|
||||
step.start = key;
|
||||
fs.readdir(opt.file, step.match);
|
||||
r.save = function(rad, cb){
|
||||
var s = function Span(){};
|
||||
s.find = function(tree, key){
|
||||
if(key < s.start){ return }
|
||||
s.start = key;
|
||||
opt.store.list(s.lex);
|
||||
return true;
|
||||
}
|
||||
s.lex = function(file){
|
||||
if(!file || file > s.start){
|
||||
s.mix(s.file || opt.code.from, s.start, s.end = file);
|
||||
return true;
|
||||
},
|
||||
match: function(err, dir){
|
||||
step.dir = dir;
|
||||
if(!dir.length){
|
||||
step.file = '0';
|
||||
return step.merge(null, Radix());
|
||||
}
|
||||
Gun.obj.map(dir, step.lex);
|
||||
read(step.file, step.merge);
|
||||
},
|
||||
lex: function(file){
|
||||
if(file > step.start){
|
||||
return step.end = file;
|
||||
}
|
||||
step.file = file;
|
||||
},
|
||||
merge: function(err, disk){
|
||||
if(err){ return console.log("ERROR!!!", err) }
|
||||
step.disk = disk;
|
||||
Radix.map(radix, step.add);
|
||||
write(step.file, step.disk, step.done);
|
||||
},
|
||||
add: function(val, key){
|
||||
if(key < step.start){ return }
|
||||
if(step.end && step.end < key){ return step.next = key; }
|
||||
step.disk(key, val);
|
||||
},
|
||||
done: function(err){
|
||||
if(err){ console.log("ERROR!!!", err) }
|
||||
if(!step.next){
|
||||
return cb(err);
|
||||
}
|
||||
step.start = step.next;
|
||||
step.end = step.next = step.file = u;
|
||||
Radix.map(radix, step.check);
|
||||
}
|
||||
s.file = file;
|
||||
}
|
||||
Radix.map(radix, step.check);
|
||||
s.mix = function(file, start, end){
|
||||
s.start = s.end = s.file = u;
|
||||
r.parse(file, function(err, disk){
|
||||
if(err){ return cb(err) }
|
||||
Radix.map(rad, function(val, key){
|
||||
if(key < start){ return }
|
||||
if(end && end < key){ return s.start = key }
|
||||
disk(key, val);
|
||||
});
|
||||
r.write(file, disk, s.next);
|
||||
});
|
||||
}
|
||||
s.next = function(err, ok){
|
||||
if(s.err = err){ return cb(err) }
|
||||
if(s.start){ return Radix.map(rad, s.find) }
|
||||
cb(err, ok);
|
||||
}
|
||||
Radix.map(rad, s.find);
|
||||
}
|
||||
|
||||
var write = function(file, radix, cb){
|
||||
var step = {
|
||||
rest: "",
|
||||
count: 0,
|
||||
file: file,
|
||||
each: function(val, key, k, pre){
|
||||
step.count++;
|
||||
if(opt.size < step.rest.length){
|
||||
step.rest = "";
|
||||
step.limit = Math.ceil(step.count/2);
|
||||
step.count = 0;
|
||||
step.sub = Radix();
|
||||
Radix.map(radix, step.slice);
|
||||
return true;
|
||||
}
|
||||
var i = pre.length;
|
||||
while(i--){ step.rest += opt.nest };
|
||||
step.rest += k + (u === val? '' : '=' + val) + '\n';
|
||||
},
|
||||
dump: function(){
|
||||
var rest = step.rest;
|
||||
step.rest = "";
|
||||
fs.writeFile(opt.file +'/'+ file, rest, cb);
|
||||
if(opt.disk){ opt.disk(opt.file+'/'+file, rest, cb) }
|
||||
},
|
||||
slice: function(val, key){
|
||||
if(key < step.file){ return }
|
||||
if(step.limit < (++step.count)){
|
||||
var name = step.file;
|
||||
step.file = key;
|
||||
step.count = 0;
|
||||
write(name, step.sub, step.next);
|
||||
return true;
|
||||
}
|
||||
step.sub(key, val);
|
||||
},
|
||||
next: function(err){
|
||||
if(err){ console.log("ERR!!!!") }
|
||||
step.sub = Radix();
|
||||
if(!Radix.map(radix, step.slice)){
|
||||
write(step.file, step.sub, cb);
|
||||
}
|
||||
/*
|
||||
Any storage engine at some point will have to do a read in order to write.
|
||||
This is true of even systems that use an append only log, if they support updates.
|
||||
Therefore it is unavoidable that a read will have to happen,
|
||||
the question is just how long you delay it.
|
||||
*/
|
||||
r.write = function(file, rad, cb){
|
||||
var f = function Fractal(){};
|
||||
f.text = '';
|
||||
f.count = 0;
|
||||
f.file = file;
|
||||
f.each = function(val, key, k, pre){
|
||||
f.count++;
|
||||
var enc = Radisk.encode(pre.length) +'#'+ Radisk.encode(k) + (u === val? '' : '='+ Radisk.encode(val)) +'\n';
|
||||
if(opt.size < f.text.length + enc.length){
|
||||
f.text = '';
|
||||
f.limit = Math.ceil(f.count/2);
|
||||
f.count = 0;
|
||||
f.sub = Radix();
|
||||
Radix.map(rad, f.slice);
|
||||
return true;
|
||||
}
|
||||
f.text += enc;
|
||||
}
|
||||
f.write = function(){ opt.store.put(file, f.text, cb) }
|
||||
f.slice = function(val, key){
|
||||
if(key < f.file){ return }
|
||||
if(f.limit < (++f.count)){
|
||||
var name = f.file;
|
||||
f.file = key;
|
||||
f.count = 0;
|
||||
r.write(name, f.sub, f.next);
|
||||
return true;
|
||||
}
|
||||
f.sub(key, val);
|
||||
}
|
||||
f.next = function(err){
|
||||
if(err){ return cb(err) }
|
||||
f.sub = Radix();
|
||||
if(!Radix.map(rad, f.slice)){
|
||||
r.write(f.file, f.sub, cb);
|
||||
}
|
||||
}
|
||||
if(!Radix.map(rad, f.each, true)){ f.write() }
|
||||
}
|
||||
|
||||
r.read = function(key, cb){
|
||||
// TODO: BUG!!! If a node spans multiple file chunks, it won't return all!
|
||||
// TODO: BUG!!! If a node spans multiple file chunks, it won't return all!
|
||||
// TODO: BUG!!! If a node spans multiple file chunks, it won't return all!
|
||||
// TODO: BUG!!! If a node spans multiple file chunks, it won't return all!
|
||||
// TODO: BUG!!! If a node spans multiple file chunks, it won't return all!
|
||||
// TODO: BUG!!! If a node spans multiple file chunks, it won't return all!
|
||||
// TODO: BUG!!! If a node spans multiple file chunks, it won't return all!
|
||||
var id = Gun.text.random(3);
|
||||
console.debug(4, 'read', key, !!RAD, id);
|
||||
if(RAD){ // cache
|
||||
var val = RAD(key);
|
||||
if(u !== val){
|
||||
return cb(u, val);
|
||||
}
|
||||
}
|
||||
var g = function Get(){}, tmp;
|
||||
g.lex = function(file){
|
||||
if(!file || file > key){
|
||||
if(tmp = q[g.file]){
|
||||
tmp.push(cb);
|
||||
return true;
|
||||
}
|
||||
q[g.file] = [cb];
|
||||
r.parse(g.file, g.it);
|
||||
return true;
|
||||
}
|
||||
g.file = file;
|
||||
}
|
||||
g.it = function(err, disk){
|
||||
console.debug(5, 'readit', id);
|
||||
if(g.err = err){ Gun.log(err) }
|
||||
if(disk){
|
||||
RAD = disk;
|
||||
g.val = disk(key);
|
||||
}
|
||||
Gun.obj.map(q[g.file], g.ack);
|
||||
Gun.obj.del(q[g.file]);
|
||||
}
|
||||
g.ack = function(ack){
|
||||
ack(g.err, g.val);
|
||||
}
|
||||
opt.store.list(g.lex);
|
||||
}
|
||||
/*
|
||||
Let us start by assuming we are the only process that is
|
||||
changing the directory or bucket. Not because we do not want
|
||||
to be multi-process/machine, but because we want to experiment
|
||||
with how much performance and scale we can get out of only one.
|
||||
Then we can work on the harder problem of being multi-process.
|
||||
*/
|
||||
r.parse = function(file, cb){
|
||||
var p = function Parse(){}, s = String.fromCharCode(31);
|
||||
p.disk = Radix();
|
||||
p.read = function(err, data){ var tmp;
|
||||
if(err){ return cb(err) }
|
||||
if(!data){ return cb(u, p.disk) }
|
||||
var tmp = p.split(data), pre = [], i, k, v;
|
||||
while(tmp){
|
||||
k = v = u;
|
||||
i = tmp[1];
|
||||
tmp = p.split(tmp[2])||'';
|
||||
if('#' == tmp[0]){
|
||||
k = tmp[1];
|
||||
pre = pre.slice(0,i);
|
||||
if(i <= pre.length){
|
||||
pre.push(k);
|
||||
}
|
||||
}
|
||||
tmp = p.split(tmp[2])||'';
|
||||
if('\n' == tmp[0]){ continue }
|
||||
if('=' == tmp[0]){ v = tmp[1] }
|
||||
if(u !== k && u !== v){ p.disk(pre.join(''), v) }
|
||||
tmp = p.split(tmp[2]);
|
||||
}
|
||||
cb(u, p.disk);
|
||||
};
|
||||
if(!Radix.map(radix, step.each, true)){ step.dump() }
|
||||
}
|
||||
|
||||
var read = function(file, cb){
|
||||
var step = {
|
||||
nest: 0,
|
||||
rad: Radix(),
|
||||
data: function(err, data){
|
||||
if(err){ return console.log("ERROR READING FILE!", err) }
|
||||
step.pre = [];
|
||||
Gun.obj.map(data.toString().split('\n'), step.split); // TODO: Escape!
|
||||
cb(null, step.rad);
|
||||
},
|
||||
split: function(line){ var LINE = line;
|
||||
var nest = -1; while(opt.nest === line[++nest]){};
|
||||
if(nest){ line = line.slice(nest) }
|
||||
if(nest <= step.nest){ step.pre = step.pre.slice(0, nest - step.nest - 1) }
|
||||
line = line.split('='); step.pre.push(line[0]);
|
||||
if(1 < line.length){ step.rad(step.pre.join(''), line[1]) }
|
||||
step.nest = nest;
|
||||
p.split = function(t){
|
||||
if(!t){ return }
|
||||
var l = [], o = {}, i = -1, a = '', b, c;
|
||||
while(c = t[++i]){
|
||||
if(s === c){ break }
|
||||
a += c;
|
||||
}
|
||||
if(!c){ return }
|
||||
l[0] = a;
|
||||
l[1] = b = Radisk.decode(t.slice(i), o);
|
||||
l[2] = t.slice(i + o.i);
|
||||
return l;
|
||||
}
|
||||
fs.readFile(opt.file +'/'+ file, step.data);
|
||||
opt.store.get(file, p.read);
|
||||
}
|
||||
|
||||
radisk.read = read;
|
||||
|
||||
return radisk;
|
||||
var q = {}, RAD, u;
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
;(function(){
|
||||
s = String.fromCharCode(31);
|
||||
Radisk.encode = function(d, o){
|
||||
var t = s, tmp;
|
||||
if(typeof d == 'string'){
|
||||
var i = -1, c;
|
||||
while(c = d[++i]){
|
||||
if(s === c){
|
||||
t += s;
|
||||
}
|
||||
}
|
||||
return t + '"' + d + s;
|
||||
} else
|
||||
if(d && d['#'] && (tmp = Gun.val.rel.is(d))){
|
||||
return t + '#' + tmp + t;
|
||||
} else
|
||||
if(Gun.num.is(d)){
|
||||
return t + '+' + (d||0) + t;
|
||||
} else
|
||||
if(null === d){
|
||||
return t + ' ' + t;
|
||||
} else
|
||||
if(true === d){
|
||||
return t + '+' + t;
|
||||
} else
|
||||
if(false === d){
|
||||
return t + '-' + t;
|
||||
}// else
|
||||
//if(binary){}
|
||||
}
|
||||
Radisk.decode = function(t, o){
|
||||
var d = '', i = -1, n = 0, c, p;
|
||||
if(s !== t[0]){ return }
|
||||
while(c = t[++i]){
|
||||
if(p){
|
||||
if(s === c){
|
||||
if(--n <= 0){
|
||||
break;
|
||||
}
|
||||
}
|
||||
d += c;
|
||||
} else
|
||||
if(s === c){
|
||||
++n;
|
||||
} else {
|
||||
p = c || true;
|
||||
}
|
||||
}
|
||||
if(o){ o.i = i+1 }
|
||||
if('"' === p){
|
||||
return d;
|
||||
} else
|
||||
if('#' === p){
|
||||
return Gun.val.rel.ify(d);
|
||||
} else
|
||||
if('+' === p){
|
||||
if(0 === d.length){
|
||||
return true;
|
||||
}
|
||||
return parseFloat(d);
|
||||
} else
|
||||
if(' ' === p){
|
||||
return null;
|
||||
} else
|
||||
if('-' === p){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}());
|
||||
|
||||
Radisk.Radix = Radix;
|
||||
|
||||
module.exports = Radisk;
|
242
lib/radisk_.js
Normal file
242
lib/radisk_.js
Normal file
@ -0,0 +1,242 @@
|
||||
var fs = require('fs');
|
||||
var Gun = require('../gun');
|
||||
var Radix = require('./radix');
|
||||
|
||||
function Radisk(opt){
|
||||
/*
|
||||
Any and all storage adapters should...
|
||||
1. If not busy, write to disk immediately.
|
||||
2. If busy, batch to disk. (Improves performance, reduces potential disk corruption)
|
||||
3. If a batch exceeds a certain number of writes, force atomic batch to disk. (This caps total performance, but reduces potential loss)
|
||||
*/
|
||||
var radisk = function(key, val, cb){
|
||||
key = ''+key;
|
||||
if(0 <= key.indexOf('_') || 0 <= key.indexOf('$')){ // TODO: BUG! Fix!
|
||||
var err = "ERROR: Radix and Radisk not tested against _ or $ keys!";
|
||||
console.log(err);
|
||||
cb = cb || val;
|
||||
if(cb instanceof Function){ cb(err) }
|
||||
return;
|
||||
}
|
||||
if(val instanceof Function){
|
||||
cb = val;
|
||||
val = radisk.batch(key);
|
||||
if(u !== val){
|
||||
return cb(null, val);
|
||||
}
|
||||
if(radisk.was){
|
||||
val = radisk.was(key);
|
||||
if(u !== val){
|
||||
return cb(null, val);
|
||||
}
|
||||
}
|
||||
console.log("READ FROM DISK");
|
||||
return cb(null, val);
|
||||
}
|
||||
radisk.batch(key, val);
|
||||
if(cb){ radisk.batch.acks.push(cb) }
|
||||
if(!count++){ return thrash() } // (1)
|
||||
if(opt.batch <= count){ return thrash() } // (3)
|
||||
clearTimeout(to); // (2)
|
||||
to = setTimeout(thrash, opt.wait);
|
||||
};
|
||||
radisk.batch = Radix();
|
||||
radisk.batch.acks = [];
|
||||
var count = 0, wait, to, u;
|
||||
|
||||
opt = opt || {};
|
||||
opt.file = String(opt.file || 'radata');
|
||||
opt.size = opt.size || (1024 * 1024 * 10); // 10MB
|
||||
opt.batch = opt.batch || 10 * 1000;
|
||||
opt.wait = opt.wait || 1;
|
||||
opt.nest = opt.nest || ' ';
|
||||
|
||||
console.log("Warning: Radix storage engine has not been tested with all types of values and keys yet.");
|
||||
if(!fs.existsSync(opt.file)){ fs.mkdirSync(opt.file) }
|
||||
|
||||
var thrash = function(){
|
||||
if(wait){ return }
|
||||
clearTimeout(to);
|
||||
wait = true;
|
||||
var was = radisk.was = radisk.batch;
|
||||
radisk.batch = null;
|
||||
radisk.batch = Radix();
|
||||
radisk.batch.acks = [];
|
||||
chunk(radisk.was, function(err, ok){
|
||||
radisk.was = null;
|
||||
wait = false;
|
||||
var tmp = count;
|
||||
count = 0;
|
||||
Gun.obj.map(was.acks, function(cb){cb(err, ok)});
|
||||
if(1 < tmp){ 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 to large, split. More details needed here.
|
||||
*/
|
||||
function chunk(radix, cb){
|
||||
var step = {
|
||||
check: function(tree, key){
|
||||
if(key < step.start){ return }
|
||||
step.start = key;
|
||||
fs.readdir(opt.file, step.match);
|
||||
return true;
|
||||
},
|
||||
match: function(err, dir){
|
||||
step.dir = dir;
|
||||
if(!dir.length){
|
||||
step.file = '0';
|
||||
return step.merge(null, Radix());
|
||||
}
|
||||
Gun.obj.map(dir, step.lex);
|
||||
read(step.file, step.merge);
|
||||
},
|
||||
lex: function(file){
|
||||
if(file > step.start){
|
||||
return step.end = file;
|
||||
}
|
||||
step.file = file;
|
||||
},
|
||||
merge: function(err, disk){
|
||||
if(err){ return console.log("ERROR!!!", err) }
|
||||
step.disk = disk;
|
||||
Radix.map(radix, step.add);
|
||||
write(step.file, step.disk, step.done);
|
||||
},
|
||||
add: function(val, key){
|
||||
if(key < step.start){ return }
|
||||
if(step.end && step.end < key){ return step.next = key; }
|
||||
step.disk(key, val);
|
||||
},
|
||||
done: function(err){
|
||||
if(err){ console.log("ERROR!!!", err) }
|
||||
if(!step.next){
|
||||
return cb(err);
|
||||
}
|
||||
step.start = step.next;
|
||||
step.end = step.next = step.file = u;
|
||||
Radix.map(radix, step.check);
|
||||
}
|
||||
}
|
||||
Radix.map(radix, step.check);
|
||||
}
|
||||
|
||||
/*
|
||||
Any storage engine at some point will have to do a read in order to write.
|
||||
This is true of even systems that use an append only log, if they support updates.
|
||||
Therefore it is unavoidable that a read will have to happen,
|
||||
the question is just how long you delay it.
|
||||
*/
|
||||
var write = function(file, radix, cb){
|
||||
var step = {
|
||||
rest: "",
|
||||
count: 0,
|
||||
file: file,
|
||||
each: function(val, key, k, pre){
|
||||
step.count++;
|
||||
if(opt.size < step.rest.length){
|
||||
step.rest = "";
|
||||
step.limit = Math.ceil(step.count/2);
|
||||
step.count = 0;
|
||||
step.sub = Radix();
|
||||
Radix.map(radix, step.slice);
|
||||
return true;
|
||||
}
|
||||
var i = pre.length;
|
||||
while(i--){ step.rest += opt.nest };
|
||||
step.rest += encode(k) + (u === val? '' : '=' + encode(val)) + '\n';
|
||||
},
|
||||
dump: function(){
|
||||
var rest = step.rest;
|
||||
step.rest = "";
|
||||
fs.writeFile(opt.file +'/'+ file, rest, cb);
|
||||
console.log("DUMP");
|
||||
if(opt.disk){ opt.disk(opt.file+'/'+file, rest, cb) }
|
||||
},
|
||||
slice: function(val, key){
|
||||
if(key < step.file){ return }
|
||||
if(step.limit < (++step.count)){
|
||||
var name = step.file;
|
||||
step.file = key;
|
||||
step.count = 0;
|
||||
write(name, step.sub, step.next);
|
||||
return true;
|
||||
}
|
||||
step.sub(key, val);
|
||||
},
|
||||
next: function(err){
|
||||
if(err){ console.log("ERR!!!!") }
|
||||
step.sub = Radix();
|
||||
if(!Radix.map(radix, step.slice)){
|
||||
write(step.file, step.sub, cb);
|
||||
}
|
||||
}
|
||||
};
|
||||
if(!Radix.map(radix, step.each, true)){ step.dump() }
|
||||
}
|
||||
|
||||
var split = String.fromCharCode(31), esc = JSON.stringify, fix = JSON.parse;
|
||||
function encode(data){
|
||||
return split + esc(data) + split;
|
||||
}
|
||||
function decode(data){
|
||||
if(!data){ return '' }
|
||||
var i = -1, c, f, t = '';
|
||||
while(c = data[++i]){
|
||||
if(f){
|
||||
if(split === c){
|
||||
try{ t = fix(t);
|
||||
}catch(e){ t = '' }
|
||||
return t;
|
||||
}
|
||||
t += c;
|
||||
} else
|
||||
if(split === c){
|
||||
f = true;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/*
|
||||
Let us start by assuming we are the only process that is
|
||||
changing the directory or bucket. Not because we do not want
|
||||
to be multi-process/machine, but because we want to experiment
|
||||
with how much performance and scale we can get out of only one.
|
||||
Then we can work on the harder problem of being multi-process.
|
||||
*/
|
||||
var read = function(file, cb){
|
||||
var step = {
|
||||
nest: 0,
|
||||
rad: Radix(),
|
||||
data: function(err, data){
|
||||
if(err){ return console.log("ERROR READING FILE!", err) }
|
||||
step.pre = [];
|
||||
Gun.obj.map(data.toString().split('\n'), step.split); // TODO: Escape!
|
||||
cb(null, step.rad);
|
||||
},
|
||||
split: function(line){ var LINE = line;
|
||||
var nest = -1; while(opt.nest === line[++nest]){};
|
||||
if(nest){ line = line.slice(nest) }
|
||||
if(nest <= step.nest){ step.pre = step.pre.slice(0, nest - step.nest - 1) }
|
||||
line = line.split('='); step.pre.push(line[0]);
|
||||
if(1 < line.length){ step.rad(step.pre.join(''), line[1]) }
|
||||
step.nest = nest;
|
||||
}
|
||||
}
|
||||
fs.readFile(opt.file +'/'+ file, step.data);
|
||||
}
|
||||
|
||||
radisk.read = read;
|
||||
|
||||
return radisk;
|
||||
}
|
||||
|
||||
module.exports = Radisk;
|
101
lib/radix.js
101
lib/radix.js
@ -1,57 +1,60 @@
|
||||
var Gun = require('../gun');
|
||||
var gbm = Gun.obj.map, no = {}, u;
|
||||
|
||||
function Radix(){
|
||||
var radix = function(key, val, t){
|
||||
t = t || radix._ || (radix._ = {});
|
||||
var i = 0, l = key.length-1, k = key[i], at, tmp;
|
||||
while(!(at = t[k]) && i < l){
|
||||
k += key[++i];
|
||||
}
|
||||
if(!at){
|
||||
if(!gbm(t, function(r, s){
|
||||
var ii = 0, kk = '';
|
||||
while(s[ii] == key[ii]){
|
||||
kk += s[ii++];
|
||||
}
|
||||
if(kk){
|
||||
if(u === val){ return (tmp || (tmp = {}))[s.slice(ii)] = r; }
|
||||
var _ = {};
|
||||
_[s.slice(ii)] = r;
|
||||
_[key.slice(ii)] = {$: val};
|
||||
t[kk] = {_: _};
|
||||
delete t[s];
|
||||
return true;
|
||||
}
|
||||
})){
|
||||
if(u === val){ return; }
|
||||
(t[k] || (t[k] = {})).$ = val;
|
||||
} else
|
||||
if(u === val){
|
||||
return tmp;
|
||||
}
|
||||
} else
|
||||
if(i == l){
|
||||
if(u === val){ return (u === (tmp = at.$))? at._ : tmp }
|
||||
at.$ = val;
|
||||
} else {
|
||||
return radix(key.slice(++i), val, at._ || (at._ = {}));
|
||||
}
|
||||
}
|
||||
return radix;
|
||||
};
|
||||
;(function(){
|
||||
var map = Gun.obj.map, no = {}, u;
|
||||
|
||||
var $ = String.fromCharCode(30), _ = String.fromCharCode(29);
|
||||
|
||||
function Radix(){
|
||||
var radix = function(key, val, t){
|
||||
t = t || radix[_] || (radix[_] = {});
|
||||
var i = 0, l = key.length-1, k = key[i], at, tmp;
|
||||
while(!(at = t[k]) && i < l){
|
||||
k += key[++i];
|
||||
}
|
||||
if(!at){
|
||||
if(u === val && i == l){ return }
|
||||
if(!map(t, function(r, s){
|
||||
var ii = 0, kk = '';
|
||||
while(s[ii] == key[ii]){
|
||||
kk += s[ii++];
|
||||
}
|
||||
if(kk){
|
||||
if(u === val){ return (tmp || (tmp = {}))[s.slice(ii)] = r; }
|
||||
var __ = {};
|
||||
__[s.slice(ii)] = r;
|
||||
(__[key.slice(ii)] = {})[$] = val;
|
||||
(t[kk] = {})[_] = __;
|
||||
delete t[s];
|
||||
return true;
|
||||
}
|
||||
})){
|
||||
if(u === val){ return; }
|
||||
(t[k] || (t[k] = {}))[$] = val;
|
||||
} else
|
||||
if(u === val){
|
||||
return tmp;
|
||||
}
|
||||
} else
|
||||
if(i == l){
|
||||
if(u === val){ return (u === (tmp = at[$]))? at[_] : tmp }
|
||||
at[$] = val;
|
||||
} else {
|
||||
return radix(key.slice(++i), val, at[_] || (at[_] = {}));
|
||||
}
|
||||
}
|
||||
return radix;
|
||||
};
|
||||
Radix.map = function map(radix, cb, opt, pre){ pre = pre || [];
|
||||
var _ = radix._ || radix, keys = Object.keys(_).sort(), i = 0, l = keys.length;
|
||||
for(;i < l; i++){ var key = keys[i], tree = _[key], tmp;
|
||||
if(u !== (tmp = tree.$)){
|
||||
var t = radix[_] || radix, keys = Object.keys(t).sort(), i = 0, l = keys.length;
|
||||
for(;i < l; i++){ var key = keys[i], tree = t[key], tmp;
|
||||
if(u !== (tmp = tree[$])){
|
||||
tmp = cb(tmp, pre.join('') + key, key, pre);
|
||||
if(u !== tmp){ return tmp }
|
||||
} else
|
||||
if(opt){
|
||||
cb(u, pre.join(''), key, pre);
|
||||
}
|
||||
if(tmp = tree._){
|
||||
if(tmp = tree[_]){
|
||||
pre.push(key);
|
||||
tmp = map(tmp, cb, opt, pre);
|
||||
if(u !== tmp){ return tmp }
|
||||
@ -59,7 +62,7 @@ function Radix(){
|
||||
}
|
||||
}
|
||||
};
|
||||
Object.keys = Object.keys || function(o){ return gbm(o, function(v,k,t){t(k)}) }
|
||||
}());
|
||||
|
||||
module.exports = Radix;
|
||||
Object.keys = Object.keys || function(o){ return map(o, function(v,k,t){t(k)}) }
|
||||
|
||||
module.exports = Radix;
|
||||
}());
|
72
lib/rs3.js
Normal file
72
lib/rs3.js
Normal file
@ -0,0 +1,72 @@
|
||||
var Gun = require('gun/gun');
|
||||
var Radisk = require('./radisk');
|
||||
var fs = require('fs');
|
||||
var Radix = Radisk.Radix;
|
||||
var u;
|
||||
var AWS = require('aws-sdk');
|
||||
|
||||
Gun.on('opt', function(ctx){
|
||||
this.to.next(ctx);
|
||||
var opt = ctx.opt;
|
||||
if(ctx.once){ return }
|
||||
if(!process.env.AWS_S3_BUCKET){ return }
|
||||
opt.batch = opt.batch || 10 * 1000;
|
||||
opt.wait = opt.wait || 1000 * 15;
|
||||
opt.size = opt.size || (1024 * 1024 * 10); // 10MB
|
||||
|
||||
var opts = opt.s3 || (opt.s3 = {});
|
||||
opts.bucket = opts.bucket || process.env.AWS_S3_BUCKET;
|
||||
opts.region = opts.region || process.AWS_REGION || "us-east-1";
|
||||
opts.accessKeyId = opts.key = opts.key || opts.accessKeyId || process.env.AWS_ACCESS_KEY_ID;
|
||||
opts.secretAccessKey = opts.secret = opts.secret || opts.secretAccessKey || process.env.AWS_SECRET_ACCESS_KEY;
|
||||
|
||||
if(opt.fakes3 = opt.fakes3 || process.env.fakes3){
|
||||
opts.endpoint = opt.fakes3;
|
||||
opts.sslEnabled = false;
|
||||
opts.bucket = opts.bucket.replace('.','p');
|
||||
}
|
||||
|
||||
opts.config = new AWS.Config(opts);
|
||||
opts.s3 = opts.s3 || new AWS.S3(opts.config);
|
||||
|
||||
opt.store = opt.store || Store(opt);
|
||||
});
|
||||
|
||||
function Store(opt){
|
||||
opt = opt || {};
|
||||
opt.file = String(opt.file || 'radata');
|
||||
var opts = opt.s3, s3 = opts.s3;
|
||||
|
||||
var store = function Store(){};
|
||||
store.put = function(file, data, cb){
|
||||
var params = {Bucket: opts.bucket, Key: file, Body: data};
|
||||
s3.putObject(params, cb);
|
||||
};
|
||||
store.get = function(file, cb){
|
||||
var params = {Bucket: opts.bucket, Key: file};
|
||||
s3.getObject(params, function(err, ack){
|
||||
if(!ack){ return cb(err) }
|
||||
var data = ack.Body;
|
||||
if(data){ data = data.toString() }
|
||||
console.log("HERE WE GO!", data);
|
||||
cb(err, data);
|
||||
});
|
||||
};
|
||||
store.list = function(cb, match, params){
|
||||
params = params || {Bucket: opts.bucket};
|
||||
s3.listObjectsV2(params, function(err, data){
|
||||
if(err){ return Gun.log(err, err.stack) }
|
||||
if(Gun.obj.map(data.Contents, function(content){
|
||||
return cb(content.Key);
|
||||
})){ return }
|
||||
if(!data.IsTruncated){ return cb() } // Stream interface requires a final call to know when to be done.
|
||||
params.ContinuationToken = data.NextContinuationToken;
|
||||
console.log("get further list...");
|
||||
store.list(cb, match, params);
|
||||
});
|
||||
};
|
||||
//store.list(function(){ return true });
|
||||
return store;
|
||||
}
|
||||
|
||||
module.exports = Store;
|
104
lib/store.js
Normal file
104
lib/store.js
Normal file
@ -0,0 +1,104 @@
|
||||
var Gun = require('gun/gun');
|
||||
var Radisk = require('./radisk');
|
||||
var fs = require('fs');
|
||||
var Radix = Radisk.Radix;
|
||||
var u;
|
||||
|
||||
Gun.on('opt', function(ctx){
|
||||
this.to.next(ctx);
|
||||
var opt = ctx.opt;
|
||||
if(ctx.once){ return }
|
||||
opt.store = opt.store || Store(opt);
|
||||
var rad = Radisk(opt);
|
||||
|
||||
ctx.on('put', function(at){
|
||||
this.to.next(at);
|
||||
var id = at['#'], acks = at['@']? u : 0; // only ack non-acks.
|
||||
Gun.graph.is(at.put, null, function(val, key, node, soul){
|
||||
++acks;
|
||||
val = Radisk.encode(val)+'>'+Radisk.encode(Gun.state.is(node, key));
|
||||
rad(soul+'.'+key, val, (u === acks? u : ack));
|
||||
});
|
||||
function ack(err, ok){
|
||||
acks--;
|
||||
if(ack.err){ return }
|
||||
if(ack.err = err){
|
||||
ctx.on('in', {'@': id, err: Gun.log(err)});
|
||||
return;
|
||||
}
|
||||
if(acks){ return }
|
||||
ctx.on('in', {'@': id, ok: 1});
|
||||
}
|
||||
});
|
||||
|
||||
ctx.on('get', function(at){
|
||||
this.to.next(at);
|
||||
var id = at['#'], soul = at.get['#'], key = at.get['.']||'', tmp = soul+'.'+key, node;
|
||||
console.debug(2,"store GET", tmp);
|
||||
console.debug(1,"store GET", tmp);
|
||||
rad(tmp, function(err, val){
|
||||
console.debug(7,"store GOT", val);
|
||||
if(val){
|
||||
Radix.map(val, each);
|
||||
if(!node){ each(val, key) }
|
||||
}
|
||||
console.debug(8,"store GOT", node);
|
||||
console.debug(6,"store GOT", node);
|
||||
ctx.on('in', {'@': id, put: Gun.graph.node(node), err: err? err : u});
|
||||
});
|
||||
function each(val, key){
|
||||
tmp = val.lastIndexOf('>');
|
||||
var state = Radisk.decode(val.slice(tmp+1));
|
||||
val = Radisk.decode(val.slice(0,tmp));
|
||||
node = Gun.state.ify(node, key, state, val, soul);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function Store(opt){
|
||||
opt = opt || {};
|
||||
opt.file = String(opt.file || 'radata');
|
||||
|
||||
var store = function Store(){};
|
||||
store.put = function(file, data, cb){
|
||||
fs.writeFile(opt.file+'.tmp', data, function(err, ok){
|
||||
if(err){ return cb(err) }
|
||||
fs.rename(opt.file+'.tmp', opt.file+'/'+file, cb);
|
||||
});
|
||||
};
|
||||
store.get = function(file, cb){
|
||||
fs.readFile(opt.file+'/'+file, function(err, data){
|
||||
if(err){
|
||||
if('ENOENT' === (err.code||'').toUpperCase()){
|
||||
return cb(null);
|
||||
}
|
||||
Gun.log("ERROR:", err)
|
||||
}
|
||||
if(data){ data = data.toString() }
|
||||
cb(err, data);
|
||||
});
|
||||
};
|
||||
store.list = function(cb, match){
|
||||
fs.readdir(opt.file, function(err, dir){
|
||||
Gun.obj.map(dir, cb) || cb(); // Stream interface requires a final call to know when to be done.
|
||||
});
|
||||
};
|
||||
if(!fs.existsSync(opt.file)){ fs.mkdirSync(opt.file) }
|
||||
//store.list(function(){ return true });
|
||||
return store;
|
||||
}
|
||||
|
||||
module.exports = Store;
|
||||
|
||||
|
||||
;(function(){//return;
|
||||
global.Gun = require('../gun');
|
||||
|
||||
/*process.env.AWS_S3_BUCKET = 'test-s3';
|
||||
process.env.AWS_ACCESS_KEY_ID = 'asdf';
|
||||
process.env.AWS_SECRET_ACCESS_KEY = 'fdsa';
|
||||
process.env.fakes3 = 'http://localhost:4567';
|
||||
process.env.AWS_S3_THROTTLE = 0;*/
|
||||
require('../test/abc');
|
||||
}());
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gun",
|
||||
"version": "0.8.1",
|
||||
"version": "0.8.8",
|
||||
"description": "Graph engine",
|
||||
"main": "index.js",
|
||||
"browser": "gun.min.js",
|
||||
@ -43,11 +43,8 @@
|
||||
"url": "https://github.com/amark/gun/issues"
|
||||
},
|
||||
"homepage": "https://github.com/amark/gun#readme",
|
||||
"engines": {
|
||||
"node": ">=0.6.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"aws-sdk": ">=2.41.0",
|
||||
"aws-sdk": ">=2.86.0",
|
||||
"formidable": ">=1.1.1",
|
||||
"ws": "~>2.2.3"
|
||||
},
|
||||
|
@ -5,6 +5,11 @@ var root, noop = function(){}, u;
|
||||
if(typeof window !== 'undefined'){ root = window }
|
||||
var store = root.localStorage || {setItem: noop, removeItem: noop, getItem: noop};
|
||||
|
||||
/*
|
||||
NOTE: Both `lib/file.js` and `lib/memdisk.js` are based on this design!
|
||||
If you update anything here, consider updating the other adapters as well.
|
||||
*/
|
||||
|
||||
Gun.on('opt', function(ctx){
|
||||
this.to.next(ctx);
|
||||
var opt = ctx.opt;
|
||||
@ -56,7 +61,7 @@ Gun.on('opt', function(ctx){
|
||||
var ack = acks;
|
||||
acks = {};
|
||||
try{store.setItem(opt.file, JSON.stringify(disk));
|
||||
}catch(e){ err = e || "localStorage failure" }
|
||||
}catch(e){ Gun.log(err = e || "localStorage failure") }
|
||||
if(!err && !Gun.obj.empty(opt.peers)){ return } // only ack if there are no peers.
|
||||
Gun.obj.map(ack, function(yes, id){
|
||||
ctx.on('in', {
|
||||
|
@ -2,7 +2,7 @@
|
||||
var Type = require('./type');
|
||||
function Dup(opt){
|
||||
var dup = {s:{}};
|
||||
opt = opt || {max: 1000, age: 1000 * 60 * 2};
|
||||
opt = opt || {max: 1000, age: 1000 * 9};//1000 * 60 * 2};
|
||||
dup.check = function(id){
|
||||
return dup.s[id]? dup.track(id) : false;
|
||||
}
|
||||
|
16
src/on.js
16
src/on.js
@ -127,6 +127,22 @@ Gun.chain.off = function(){
|
||||
}
|
||||
return gun;
|
||||
}
|
||||
Gun.chain.off = function(){
|
||||
var gun = this, at = gun._, tmp;
|
||||
var back = at.back || {}, cat = back._;
|
||||
if(!cat){ return }
|
||||
if(tmp = cat.next){
|
||||
if(tmp[at.get]){
|
||||
obj_del(tmp, at.get);
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
if(tmp = at.soul){
|
||||
obj_del(cat.root._.graph, tmp);
|
||||
}
|
||||
return gun;
|
||||
}
|
||||
var obj = Gun.obj, obj_has = obj.has, obj_del = obj.del, obj_to = obj.to;
|
||||
var rel = Gun.val.rel;
|
||||
var empty = {}, noop = function(){}, u;
|
||||
|
@ -17,6 +17,9 @@ module.exports = function onto(tag, arg, as){
|
||||
this.to.back = this.back;
|
||||
this.next = onto._.next;
|
||||
this.back.to = this.to;
|
||||
if(this.the.last === this.the){
|
||||
delete this.on.tag[this.the.tag];
|
||||
}
|
||||
}),
|
||||
to: onto._,
|
||||
next: arg,
|
||||
|
12
src/put.js
12
src/put.js
@ -66,13 +66,13 @@ function ify(as){
|
||||
function batch(){ var as = this;
|
||||
if(!as.graph || obj_map(as.stun, no)){ return }
|
||||
(as.res||iife)(function(){
|
||||
var cat = (as.gun.back(-1)._), ask = cat.ask(function(ack){
|
||||
this.off(); // One response is good enough for us currently. Later we may want to adjust this.
|
||||
if(!as.ack){ return }
|
||||
as.ack(ack, this);
|
||||
}, as.opt);
|
||||
(as.ref._).on('out', {
|
||||
cap: 3,
|
||||
gun: as.ref, put: as.out = as.env.graph, opt: as.opt,
|
||||
'#': as.gun.back(-1)._.ask(function(ack){ this.off(); // One response is good enough for us currently. Later we may want to adjust this.
|
||||
if(!as.ack){ return }
|
||||
as.ack(ack, this);
|
||||
}, as.opt)
|
||||
gun: as.ref, put: as.out = as.env.graph, opt: as.opt, '#': ask
|
||||
});
|
||||
}, as);
|
||||
if(as.res){ as.res() }
|
||||
|
13
src/root.js
13
src/root.js
@ -148,14 +148,21 @@ Gun._ = { // some reserved key words, these are not the only ones.
|
||||
Gun.on.ask = function(cb, as){
|
||||
if(!this.on){ return }
|
||||
var id = text_rand(9);
|
||||
if(cb){ this.on(id, cb, as) }
|
||||
if(cb){
|
||||
var to = this.on(id, cb, as);
|
||||
to.err = setTimeout(function(){
|
||||
to.next({err: "Error: No ACK received yet."});
|
||||
to.off();
|
||||
}, 1000 * 9); // TODO: Make configurable!!!
|
||||
}
|
||||
return id;
|
||||
}
|
||||
Gun.on.ack = function(at, reply){
|
||||
if(!at || !reply || !this.on){ return }
|
||||
var id = at['#'] || at;
|
||||
if(!this.tag || !this.tag[id]){ return }
|
||||
var id = at['#'] || at, tmp = (this.tag||empty)[id];
|
||||
if(!tmp){ return }
|
||||
this.on(id, reply);
|
||||
clearTimeout(tmp.err);
|
||||
return true;
|
||||
}
|
||||
}());
|
||||
|
@ -1642,6 +1642,7 @@ describe('Gun', function(){
|
||||
done.to = setTimeout(function(){
|
||||
expect(check.Fluffy).to.be('name');
|
||||
expect(check.Frisky).to.be('name');
|
||||
//console.log("????", gun._.graph);
|
||||
Gun.obj.map(gun._.graph, function(n,s){
|
||||
if('u/m/p/n/p' === s){ return }
|
||||
var a = Gun.obj.map(n, function(v,f,t){t(v)});
|
||||
@ -1724,7 +1725,6 @@ describe('Gun', function(){
|
||||
}
|
||||
});
|
||||
setTimeout(function(){
|
||||
//console.debug.i=1;console.log("-----------------------");
|
||||
gun.get('u/m/mutate/n').get('alice').put({
|
||||
_:{'#':'u/m/m/n/soul'},
|
||||
name: 'Alice Zzxyz'
|
||||
@ -3011,6 +3011,7 @@ describe('Gun', function(){
|
||||
gun.get('val/follow').val(function(data){
|
||||
//console.log("val", data);
|
||||
}).get(function(at){
|
||||
//console.log("?????", at);
|
||||
if(done.c){ return } done.c = 1;
|
||||
done();
|
||||
});
|
||||
@ -3018,29 +3019,28 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('map val get put', function(done){
|
||||
|
||||
var gun = Gun().get('chat/asdf');
|
||||
|
||||
var check = {}, count = {};
|
||||
gun.map().val(function(v,f){
|
||||
check[f] = v;
|
||||
count[f] = (count[f] || 0) + 1;
|
||||
if(check['1_1'] && check['2_2']){
|
||||
// console.log("**************", f, v);
|
||||
if(check['1-1'] && check['2-2']){
|
||||
clearTimeout(done.to);
|
||||
done.to = setTimeout(function(){
|
||||
expect(check['1_1'].what).to.be('hi');
|
||||
expect(check['2_2'].what).to.be('you.');
|
||||
expect(count['1_1']).to.be(1);
|
||||
expect(count['2_2']).to.be(1);
|
||||
expect(check['1-1'].what).to.be('hi');
|
||||
expect(check['2-2'].what).to.be('you.');
|
||||
expect(count['1-1']).to.be(1);
|
||||
expect(count['2-2']).to.be(1);
|
||||
done();
|
||||
},50);
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(function(){
|
||||
gun.get('1_1').put({what: "hi"});
|
||||
gun.get('1-1').put({what: "hi"});
|
||||
setTimeout(function(){
|
||||
gun.get('2_2').put({what: "you."});
|
||||
gun.get('2-2').put({what: "you."});
|
||||
},40);
|
||||
},40);
|
||||
});
|
||||
@ -3132,6 +3132,7 @@ describe('Gun', function(){
|
||||
salt: 'random'
|
||||
})
|
||||
);
|
||||
//return;
|
||||
setTimeout(function(){
|
||||
gun.get(function(at){
|
||||
//console.log('*', at.put);
|
||||
@ -3144,8 +3145,9 @@ describe('Gun', function(){
|
||||
gun.get('alias').get(function(at){
|
||||
//console.log("***", at.put);
|
||||
done.alias = done.alias || at.put.mark;
|
||||
//!console.debug.i&&(console.debug.i=1)&&console.log("---------------------");
|
||||
}).get('mark').get(function(at){
|
||||
//console.log("************", at.put);return;
|
||||
//console.log("************", at.put);//return;
|
||||
setTimeout(function(){
|
||||
done.mark = done.mark || at.put.pub;
|
||||
expect(Gun.val.rel.is(done.mark)).to.be('pub');
|
||||
@ -3160,6 +3162,7 @@ describe('Gun', function(){
|
||||
});
|
||||
|
||||
it('get put get get put reload get get then get', function(done){
|
||||
this.timeout(6000);
|
||||
var gun = Gun();
|
||||
|
||||
gun.get('stef').put({name:'Stef'});
|
||||
@ -3172,7 +3175,10 @@ describe('Gun', function(){
|
||||
// reload
|
||||
setTimeout(function(){
|
||||
var gun2 = Gun();
|
||||
//console.log(require('fs').readFileSync('./radata/!').toString());
|
||||
//console.debug.i=1;console.log("-----------------");
|
||||
gun2.get('stef').get('address').val(function(data){ // Object {_: Object, country: "Netherlands", zip: "1766KP"} "adress"
|
||||
//console.log("******", data);
|
||||
done.a = true;
|
||||
expect(data.country).to.be('Netherlands');
|
||||
expect(data.zip).to.be('999999');
|
||||
@ -3181,6 +3187,8 @@ describe('Gun', function(){
|
||||
done();
|
||||
});
|
||||
gun2.get('stef').val(function(data){ //Object {_: Object, address: Object} "stef"
|
||||
//console.log("**************", data);
|
||||
//return;
|
||||
done.s = true;
|
||||
expect(data.name).to.be('Stef');
|
||||
expect(data.address).to.be.ok();
|
||||
@ -3188,7 +3196,7 @@ describe('Gun', function(){
|
||||
if(done.c){ return } done.c = 1;
|
||||
done();
|
||||
});
|
||||
},300);
|
||||
},5000);
|
||||
});
|
||||
|
||||
it('get get get any parallel', function(done){
|
||||
@ -3201,7 +3209,7 @@ describe('Gun', function(){
|
||||
}, s)});
|
||||
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.gun._.ack);return;
|
||||
//console.log("***** age", data, at.gun._.ack);//return;
|
||||
expect(data).to.be(29);
|
||||
expect(field).to.be('age');
|
||||
done.age = true;
|
||||
@ -3276,6 +3284,7 @@ describe('Gun', function(){
|
||||
done.age = true;
|
||||
});
|
||||
setTimeout(function(){
|
||||
//console.debug.i=1;console.log('---------------');
|
||||
gun.get('parallel/not/later').get('bob').get('name').get(function(at, ev){
|
||||
var err = at.err, data = at.put, field = at.get;
|
||||
//console.log("*********** name", field, data);
|
||||
@ -3382,11 +3391,13 @@ describe('Gun', function(){
|
||||
var app = gun.get(s.soul);
|
||||
|
||||
app.get('alias').get('mark').map().val(function(alias){
|
||||
//console.log("***", alias);
|
||||
done.alias = alias;
|
||||
});
|
||||
|
||||
setTimeout(function(){
|
||||
app.get('alias').map().map().get('born').on(function(data){
|
||||
//console.log("*******", data);
|
||||
expect(data).to.be(1);
|
||||
expect(done.alias.pub).to.be("asdf");
|
||||
expect(done.alias.alias).to.be("mark");
|
||||
@ -3394,11 +3405,32 @@ describe('Gun', function(){
|
||||
if(done.c){ return } done.c = 1;
|
||||
done();
|
||||
});
|
||||
},300);
|
||||
},400);
|
||||
});
|
||||
|
||||
it('put on a put', function(done){
|
||||
var gun = Gun();
|
||||
var foo = gun.get('put/on/put').get('a').get('b');
|
||||
var bar = gun.get('put/on/put/ok').get('a').get('b');
|
||||
|
||||
bar.put({a:1})
|
||||
|
||||
bar.on(function(data){
|
||||
if(1 === data.a && 3 === data.c){
|
||||
if(done.c){ return } done.c = 1;
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
foo.on(function(ack){
|
||||
bar.put({c:3});
|
||||
});
|
||||
|
||||
foo.put({b:2});
|
||||
|
||||
});
|
||||
|
||||
it('map with map function', function(done){
|
||||
console.debug.i=0;
|
||||
var gun = Gun(), s = 'map/mapfunc', u;
|
||||
var app = gun.get(s);
|
||||
var list = app.get('list');
|
||||
@ -3474,13 +3506,13 @@ describe('Gun', function(){
|
||||
Gun.on('opt', function(ctx){
|
||||
ctx.on('out', function(msg){
|
||||
this.to.next(msg);
|
||||
var onGun = msg.gun.back(-1);
|
||||
if(onGun === b) {
|
||||
var onGun = ctx;
|
||||
if(onGun.gun === b) {
|
||||
if(d){
|
||||
//console.log("b can send to d....", Gun.obj.copy(msg));
|
||||
d.on("in", msg);
|
||||
}
|
||||
} else if(onGun === d){
|
||||
} else if(onGun.gun === d){
|
||||
//console.log("d sends to b....", Gun.obj.copy(msg));
|
||||
b.on("in", msg);
|
||||
}
|
||||
|
@ -1,71 +1,115 @@
|
||||
var Radix = require('../../lib/radix');
|
||||
var Radisk = require('../../lib/radisk');
|
||||
var Store = require('../../lib/store');
|
||||
//var Store = require('../../lib/rs3');
|
||||
var Gun = require('../../gun');
|
||||
var fs = require('fs');
|
||||
|
||||
var TOTAL = 1000000;
|
||||
var TOTAL = 25000;
|
||||
var c = 0;
|
||||
var acked = 0;
|
||||
var start;
|
||||
var diff;
|
||||
|
||||
(function(){//return;
|
||||
var radix = Radisk();
|
||||
var gtr = Gun()._.opt.uuid;
|
||||
var l = 500000;
|
||||
//var opt = {size: 1024 * 1024};
|
||||
//opt.store = Store(opt);
|
||||
//var radix = Radisk();
|
||||
var gun = Gun();
|
||||
var gtr = gun._.opt.uuid;
|
||||
var l = 2000;
|
||||
var last = start;
|
||||
var t = Gun.time.is;
|
||||
var at = c;
|
||||
;(function(){
|
||||
|
||||
start = Gun.time.is();
|
||||
gun.get('j59an5jj2LUW8IJXl0u3').get('foo').on(function(data){
|
||||
/*Radix.map(data, function(val, key){
|
||||
console.log('>>>', key, val);
|
||||
})*/
|
||||
console.log("************", data, 'in', (Gun.time.is() - start)/1000);
|
||||
setTimeout(function(){
|
||||
console.debug.i=1;console.log("----------------------");
|
||||
start = Gun.time.is();
|
||||
gun.get('j59an5jj2LUW8IJXl0u3').on(function(data){
|
||||
console.log("*****", data, 'in', (Gun.time.is() - start)/1000);
|
||||
})
|
||||
},2000);
|
||||
});
|
||||
|
||||
}());
|
||||
return;
|
||||
|
||||
var toc, alldone = function(){
|
||||
acked++;
|
||||
if(acked < TOTAL){ return }
|
||||
diff = (Gun.time.is() - start) / 1000;
|
||||
clearTimeout(toc);
|
||||
toc = setTimeout(CHECK,1000);
|
||||
toc = setTimeout(CHECK,5000);
|
||||
}
|
||||
function bench(){
|
||||
if(c >= (TOTAL)){ return clearInterval(it); }
|
||||
for(var i = 0; i < l; i++){
|
||||
radix(++c, gtr(), alldone);
|
||||
if(c % 50000 === 0){
|
||||
var now = t();
|
||||
console.log(c);//, (now - last)/1000);
|
||||
at = c;
|
||||
last = now;
|
||||
}
|
||||
act(i);
|
||||
}
|
||||
}
|
||||
start = Gun.time.is();
|
||||
var it = setInterval(bench, 1);
|
||||
|
||||
function act(i){
|
||||
if(c >= (TOTAL)){ clearInterval(it); return; }
|
||||
++c;
|
||||
var ref = gun.get(gtr());
|
||||
ref.put({
|
||||
val: c,
|
||||
foo: 'hello ' + c,
|
||||
why: c + 'not?'
|
||||
}, alldone);
|
||||
setTimeout(function(){ ref.off(); },1);
|
||||
//radix(gtr(), Gun.text.random(3), alldone);
|
||||
//radix(c, Math.random()/* + '\n' + Gun.text.random(3)*/, alldone);
|
||||
//radix(Gun.text.random(5), Math.random(), alldone);
|
||||
if(c % 50000 === 0){
|
||||
var now = t();
|
||||
console.log(c);//, (now - last)/1000);
|
||||
at = c;
|
||||
last = now;
|
||||
}
|
||||
}
|
||||
}());
|
||||
|
||||
function CHECK(){
|
||||
console.log(Math.floor(c / diff), 'disk writes per second');
|
||||
var disk = Radisk();
|
||||
console.log(Math.floor(c / diff), 'disk writes per second', acked, 'of', c);
|
||||
var opt = {batch: 5, wait: 500};
|
||||
opt.store = Store(opt);
|
||||
var radix = Radisk(opt);
|
||||
var all = {};
|
||||
var to;
|
||||
var i = TOTAL;
|
||||
var i = c || TOTAL;
|
||||
/*while(--i){
|
||||
all[i] = true;
|
||||
}*/
|
||||
var dir = fs.readdirSync('radata');
|
||||
dir.forEach(function(file){
|
||||
disk.read(file, function(err, rad){
|
||||
var dir = fs.readdirSync('radata'), i = 0;
|
||||
function readcheck(i){
|
||||
var file = dir[i];
|
||||
if(!file){
|
||||
var len = Object.keys(all).length;
|
||||
console.log("how many?", len);
|
||||
if(len < TOTAL){ return }
|
||||
var missing = [];
|
||||
var fail = Gun.obj.map(all, function(val, key){
|
||||
if(val){ missing.push(key); return true }
|
||||
});
|
||||
//console.log(all);
|
||||
console.log("DONE!", 'Verify ALL writes:', fail? '!!!FAIL!!!!' : 'YES');// '. Missing:', missing);
|
||||
return;
|
||||
}
|
||||
radix.parse(file, function(err, rad){
|
||||
Radix.map(rad, function(val, key){
|
||||
all[key] = false;
|
||||
clearTimeout(to);
|
||||
to = setTimeout(function(){
|
||||
var len = Object.keys(all).length;
|
||||
console.log("how many?", len);
|
||||
if(len < TOTAL){ return }
|
||||
var missing = [];
|
||||
var fail = Gun.obj.map(all, function(val, key){
|
||||
if(val){ missing.push(key); return true }
|
||||
});
|
||||
//console.log(all);
|
||||
console.log("DONE!", 'Verify ALL writes:', fail? '!!!FAIL!!!!' : 'YES');// '. Missing:', missing);
|
||||
},1000);
|
||||
})
|
||||
});
|
||||
readcheck(++i);
|
||||
})
|
||||
})
|
||||
}
|
||||
readcheck(i);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user