Merge pull request #435 from amark/master

master -> sea
This commit is contained in:
Mark Nadal 2017-10-16 01:35:55 -07:00 committed by GitHub
commit aacdcbc9ac
33 changed files with 11499 additions and 9044 deletions

View File

@ -1,29 +0,0 @@
{
"parser": "babel-eslint",
"extends": [
"standard",
"eslint:recommended"
],
"plugins": [
"babel",
"promise"
],
"env": {
"browser" : true
},
"globals": {
"__DEV__" : false,
"__TEST__" : false,
"__PROD__" : false,
"__COVERAGE__" : false
},
"rules": {
"key-spacing" : 0,
"jsx-quotes" : [2, "prefer-single"],
"max-len" : [2, 80, 2],
"object-curly-spacing" : [2, "always"],
"semi" : [2, "never"],
"no-mixed-spaces-and-tabs": [2],
"arrow-parens" : [2, "always"]
}
}

View File

@ -1,5 +1,7 @@
language: node_js
node_js:
- 4.0
- 4.2
- 5.0
- 6.8
- 7.9
- 8.7.0
- 7.9

View File

@ -68,26 +68,8 @@
</div>
<script src="/jquery.js"></script>
<script src="/gun.js"></script>
<!--
<script src="/gun/lib/cryptomodules.js" type="text/javascript"></script>
<script src="/gun/sea.js"></script>
-->
<script>
var gun = Gun(location.origin+'/gun');
var user = gun.user && gun.user();
if(user){
// 1st: call create. 2nd call auth. After that, call recall
// user.create('dude', 'my secret').then(function(ack) { console.log('created ack:', ack) });
// user.recall().then(function(ack){
// if (!ack || !ack.sea){
// console.log('user.recall not bootstrapping...');
// user.auth('dude', 'my secret', {pin: 'PIN'}).then(function(user) { console.log('authenticated user:', user) });
// // user.auth('dude', 'my secret', {pin: 'PIN', newpass: 'my new secret'}).then(function(user) { console.log('authenticated user:', user) });
// } else {
// console.log('user.recall authenticated user:', ack.alias);
// }
// });
}
var chat = gun.get('converse');
chat.map().val(function(msg, field){
var ul = $('ul'), last = sort(field, ul.children('li').last()), li;

22
examples/chat/user.html Normal file
View File

@ -0,0 +1,22 @@
<script src="/gun.js"></script>
<!--
<script src="/gun/lib/cryptomodules.js" type="text/javascript"></script>
<script src="/gun/sea.js"></script>
-->
<script>
var gun = Gun(location.origin+'/gun');
var user = gun.user && gun.user();
if(user){
// 1st: call create. 2nd call auth. After that, call recall
// user.create('dude', 'my secret').then(function(ack) { console.log('created ack:', ack) });
// user.recall().then(function(ack){
// if (!ack || !ack.sea){
// console.log('user.recall not bootstrapping...');
// user.auth('dude', 'my secret', {pin: 'PIN'}).then(function(user) { console.log('authenticated user:', user) });
// // user.auth('dude', 'my secret', {pin: 'PIN', newpass: 'my new secret'}).then(function(user) { console.log('authenticated user:', user) });
// } else {
// console.log('user.recall authenticated user:', ack.alias);
// }
// });
}
</script>

View File

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

594
gun.js
View File

@ -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), lack = (this.gun._.opt.lack || 9000);
to.err = setTimeout(function(){
to.next({err: "Error: No ACK received yet."});
to.off();
}, lack < 1000 ? 1000 : lack);
}
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,133 +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;
}
this.to.next(at);
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 >= 0){
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){
@ -1022,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){
@ -1059,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;
@ -1110,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){
@ -1133,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;
@ -1181,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)){
@ -1211,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;
@ -1265,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;
}
@ -1318,15 +1290,20 @@
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 } }
@ -1370,6 +1347,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;
@ -1439,7 +1417,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;
}
@ -1480,6 +1458,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 {
@ -1493,23 +1472,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;
}
@ -1519,7 +1500,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(){
@ -1575,6 +1556,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;
}
@ -1591,16 +1573,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');
@ -1610,6 +1592,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) }
@ -1639,16 +1622,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.
@ -1660,9 +1643,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 }
@ -1671,11 +1654,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){
@ -1693,7 +1676,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.
@ -1716,17 +1699,18 @@
websocket = mozWebSocket;
}
}
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(!websocket || 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);
@ -1741,13 +1725,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);
@ -1755,26 +1739,26 @@
}
(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 }
var url = peer.url.replace('http', 'ws');
var wire = peer.wire = new websocket(url);
wire.onclose = function(){
ctx.on('bye', peer);
root.on('bye', peer);
reconnect(peer, as);
};
wire.onerror = function(error){
@ -1785,7 +1769,7 @@
}
};
wire.onopen = function(){
ctx.on('hi', peer);
root.on('hi', peer);
var queue = peer.queue;
peer.queue = [];
Gun.obj.map(queue, function(msg){

2
gun.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,8 +1 @@
const Gun = require('./lib/gunwrapper')
const myDir = __dirname // TODO: where did __dirname go ?
// From here on we're ES6 import compatible...
require = require('@std/esm')(module) // eslint-disable-line no-global-assign
module.exports = require('./lib/server.mjs').default(Gun, myDir)
module.exports = require('./lib/server');

View File

@ -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 }
@ -46,7 +46,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);
});
@ -54,7 +54,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);
@ -69,7 +69,7 @@ Gun.on('opt', function(ctx){
ctx.on('in', {
'@': id,
err: err,
ok: 1
ok: err? u : 1
});
});
if(1 < tmp){ flush() }

View File

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

View File

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

View File

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

20
lib/serve.js Normal file
View File

@ -0,0 +1,20 @@
module.exports = function serve(req, res, next){
if(!req || !res){ return false }
next = next || serve;
if(!req.url){ return next() }
if(0 <= req.url.indexOf('gun.js')){
res.writeHead(200, {'Content-Type': 'text/javascript'});
res.end(serve.js = serve.js || require('fs').readFileSync(__dirname + '/../gun.js'));
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);
return true;
}
}
return next();
}

11
lib/server.js Normal file
View File

@ -0,0 +1,11 @@
;(function(){
var Gun = require('../gun');
Gun.serve = require('./serve');
require('../nts');
require('./s3');
try{require('./ws');}catch(e){require('./wsp/server');}
require('./verify');
require('./file');
require('./bye');
module.exports = Gun;
}());

104
lib/store.js Normal file
View 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');
}());

1923
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,13 @@
{
"name": "gun",
"version": "0.8.7",
"version": "0.8.8",
"description": "Graph engine",
"main": "index.js",
"browser": "gun.min.js",
"scripts": {
"start": "node examples/http.js 8080",
"prepublish": "npm run unbuild",
"test": "mocha && SEA=true mocha",
"test": "mocha",
"e2e": "mocha e2e/distributed.js",
"docker": "hooks/build",
"unbuild": "node lib/unbuild.js && uglifyjs gun.js -o gun.min.js -c -m"
@ -44,16 +44,15 @@
},
"homepage": "https://github.com/amark/gun#readme",
"engines": {
"node": ">=8.6.0"
"node": ">=0.6.6"
},
"dependencies": {
"@std/esm": "^0.8.3",
"aws-sdk": ">=2.41.0",
"formidable": ">=1.1.1",
"spark-md5": "^3.0.0",
"aws-sdk": ">=2.86.0",
"ws": "~>2.2.3"
},
"devDependencies": {
"spark-md5": "^3.0.0",
"@std/esm": "^0.8.3",
"@trust/webcrypto": "^0.5.0",
"buffer": "^5.0.7",
"express": ">=4.15.2",

View File

@ -10,16 +10,16 @@ var store = root.localStorage || {setItem: noop, removeItem: noop, getItem: noop
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.
@ -31,9 +31,9 @@ Gun.on('opt', function(ctx){
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 }
@ -42,11 +42,11 @@ Gun.on('opt', function(ctx){
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){
@ -64,7 +64,7 @@ Gun.on('opt', function(ctx){
}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.

View File

@ -11,17 +11,18 @@ if(typeof WebSocket !== 'undefined'){
websocket = mozWebSocket;
}
}
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(!websocket || 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);
@ -36,13 +37,13 @@ Gun.on('opt', function(ctx){
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);
@ -50,26 +51,26 @@ Gun.on('opt', function(ctx){
}
(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 }
var url = peer.url.replace('http', 'ws');
var wire = peer.wire = new websocket(url);
wire.onclose = function(){
ctx.on('bye', peer);
root.on('bye', peer);
reconnect(peer, as);
};
wire.onerror = function(error){
@ -80,7 +81,7 @@ Gun.on('opt', function(ctx){
}
};
wire.onopen = function(){
ctx.on('hi', peer);
root.on('hi', peer);
var queue = peer.queue;
peer.queue = [];
Gun.obj.map(queue, function(msg){

24
src/ask.js Normal file
View File

@ -0,0 +1,24 @@
// 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;
}

View File

@ -13,133 +13,74 @@ Gun.chain.chain = function(){
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;
}
this.to.next(at);
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 >= 0){
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){
@ -154,12 +95,19 @@ function input(at){
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){
@ -191,31 +139,36 @@ function input(at){
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;
@ -242,17 +195,19 @@ function map(data, key){ // Map over only the changes on every update.
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){
@ -265,37 +220,39 @@ function not(cat, at){
});
});
}
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;

View File

@ -9,15 +9,16 @@ Gun.chain.get = function(key, cb, as){
}
} 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)){
@ -39,23 +40,33 @@ function cache(key, back){
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;

View File

@ -6,6 +6,7 @@ Gun.chain.map = function(cb, opt, t){
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;
}
@ -22,16 +23,16 @@ Gun.chain.map = function(cb, opt, t){
});
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;

View File

@ -20,7 +20,7 @@ Gun.chain.on = function(tag, arg, eas, as){
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;
}
@ -61,6 +61,7 @@ Gun.chain.val = function(cb, opt){
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 {
@ -74,23 +75,25 @@ Gun.chain.val = function(cb, opt){
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;
}
@ -100,7 +103,7 @@ function val(at, ev, to){
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(){

View File

@ -28,10 +28,11 @@ Gun.chain.put = function(data, cb, as){
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;
}
@ -81,15 +82,20 @@ function stun(cb){
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 } }
@ -133,6 +139,7 @@ function any(at, ev){
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;

View File

@ -20,8 +20,9 @@ Gun.val = require('./val');
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.
@ -33,12 +34,11 @@ Gun._ = { // some reserved key words, these are not the only ones.
;(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);
@ -47,42 +47,42 @@ Gun._ = { // some reserved key words, these are not the only ones.
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;
@ -99,71 +99,49 @@ Gun._ = { // some reserved key words, these are not the only ones.
(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), lack = (this.gun._.opt.lack || 9000);
to.err = setTimeout(function(){
to.next({err: "Error: No ACK received yet."});
to.off();
}, lack < 1000 ? 1000 : lack);
}
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);
}
}());
@ -194,7 +172,7 @@ Gun._ = { // some reserved key words, these are not the only ones.
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) };

View File

@ -3,6 +3,7 @@ var Gun = require('./index');
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) }

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
/*
// Let's do all old-fashion require stuff before '@std/mjs' steps in...
const Gun = require('../gun')
@ -14,3 +14,5 @@ const myDir = __dirname // TODO: where did __dirname go ?
require = require('@std/esm')(module) // eslint-disable-line no-global-assign
module.exports = require('../lib/server.mjs').default(Gun, myDir)
*/

View File

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

View File

@ -3,10 +3,25 @@
/*eslint semi: ["error", "always", { "omitLastInOneLineBlock": true}]*/
/*eslint object-curly-spacing: ["error", "never"]*/
/*eslint node/no-deprecated-api: [error, {ignoreModuleItems: ["new buffer.Buffer()"]}] */
var root;
var root;
(function(env){
root = env.window ? env.window : global;
env.window && root.localStorage && root.localStorage.clear();
try{ require('fs').unlinkSync('data.json') }catch(e){}
//root.Gun = root.Gun || require('../gun');
if(root.Gun){
root.Gun = root.Gun;
} else {
var expect = global.expect = require("./expect");
root.Gun = require('../gun');
Gun.serve = require('../lib/serve');
//require('./s3');
//require('./uws');
//require('./wsp/server');
require('../lib/file');
require('../sea');
}
}(this));
if(typeof Buffer === 'undefined'){