From 42e0ac89ea7a3fdba365b902bbbc8cdb12668f90 Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Mon, 16 Oct 2017 00:03:59 -0700 Subject: [PATCH 1/2] fix async/sync --- examples/engage/index.html | 78 ----- gun.js | 605 +++++++++++++++++------------------ gun.min.js | 2 +- lib/file.js | 8 +- lib/memdisk.js | 4 +- lib/radisk.js | 471 +++++++++++++++++---------- lib/radisk_.js | 242 ++++++++++++++ lib/radix.js | 101 +++--- lib/rs3.js | 72 +++++ lib/store.js | 104 ++++++ package.json | 7 +- src/adapters/localStorage.js | 7 +- src/dup.js | 2 +- src/on.js | 16 + src/onto.js | 3 + src/put.js | 12 +- src/root.js | 13 +- test/common.js | 68 ++-- test/ptsd/radisk.js | 110 +++++-- 19 files changed, 1255 insertions(+), 670 deletions(-) delete mode 100644 examples/engage/index.html create mode 100644 lib/radisk_.js create mode 100644 lib/rs3.js create mode 100644 lib/store.js diff --git a/examples/engage/index.html b/examples/engage/index.html deleted file mode 100644 index fabfeb9e..00000000 --- a/examples/engage/index.html +++ /dev/null @@ -1,78 +0,0 @@ -
-
- - - -
-
- - - \ No newline at end of file diff --git a/gun.js b/gun.js index c0fc6247..84e92366 100644 --- a/gun.js +++ b/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 } diff --git a/gun.min.js b/gun.min.js index 9fc6d7ae..5753ca2e 100644 --- a/gun.min.js +++ b/gun.min.js @@ -1 +1 @@ -!function(){function t(n){function o(t){return t.split("/").slice(-1).toString().replace(".js","")}return n.slice?t[o(n)]:function(e,i){n(e={exports:{}}),t[o(i)]=e.exports}}var n;"undefined"!=typeof window&&(n=window),"undefined"!=typeof global&&(n=global),n=n||{};var o=n.console||{log:function(){}};if("undefined"!=typeof module)var e=module;t(function(t){var n={};n.fns=n.fn={is:function(t){return!!t&&"function"==typeof t}},n.bi={is:function(t){return t instanceof Boolean||"boolean"==typeof t}},n.num={is:function(t){return!e(t)&&(t-parseFloat(t)+1>=0||1/0===t||-(1/0)===t)}},n.text={is:function(t){return"string"==typeof t}},n.text.ify=function(t){return n.text.is(t)?t:"undefined"!=typeof JSON?JSON.stringify(t):t&&t.toString?t.toString():t},n.text.random=function(t,n){var o="";for(t=t||24,n=n||"0123456789ABCDEFGHIJKLMNOPQRSTUVWXZabcdefghijklmnopqrstuvwxyz";t>0;)o+=n.charAt(Math.floor(Math.random()*n.length)),t--;return o},n.text.match=function(t,o){function e(t,n){for(var o,e=-1,i=0;o=n[i++];)if(!~(e=t.indexOf(o,e+1)))return!1;return!0}var i=!1;if(t=t||"",o=n.text.is(o)?{"=":o}:o||{},n.obj.has(o,"~")&&(t=t.toLowerCase(),o["="]=(o["="]||o["~"]).toLowerCase()),n.obj.has(o,"="))return t===o["="];if(n.obj.has(o,"*")){if(t.slice(0,o["*"].length)!==o["*"])return!1;i=!0,t=t.slice(o["*"].length)}if(n.obj.has(o,"!")){if(t.slice(-o["!"].length)!==o["!"])return!1;i=!0}if(n.obj.has(o,"+")&&n.list.map(n.list.is(o["+"])?o["+"]:[o["+"]],function(n){return t.indexOf(n)>=0?void(i=!0):!0}))return!1;if(n.obj.has(o,"-")&&n.list.map(n.list.is(o["-"])?o["-"]:[o["-"]],function(n){return t.indexOf(n)<0?void(i=!0):!0}))return!1;if(n.obj.has(o,">")){if(!(t>o[">"]))return!1;i=!0}if(n.obj.has(o,"<")){if(!(tn?-1:n>o?1:0):0}},n.list.map=function(t,n,o){return a(t,n,o)},n.list.index=1,n.obj={is:function(t){return t?t instanceof Object&&t.constructor===Object||"Object"===Object.prototype.toString.call(t).match(/^\[object (\w+)\]$/)[1]:!1}},n.obj.put=function(t,n,o){return(t||{})[n]=o,t},n.obj.has=function(t,n){return t&&Object.prototype.hasOwnProperty.call(t,n)},n.obj.del=function(t,n){return t?(t[n]=null,delete t[n],t):void 0},n.obj.as=function(t,n,o,e){return t[n]=t[n]||(e===o?{}:o)},n.obj.ify=function(t){if(r(t))return t;try{t=JSON.parse(t)}catch(n){t={}}return t},function(){function t(t,n){u(this,n)&&o!==this[n]||(this[n]=t)}var o;n.obj.to=function(n,o){return o=o||{},a(n,t,o),o}}(),n.obj.copy=function(t){return t?JSON.parse(JSON.stringify(t)):t},function(){function t(t,n){var o=this.n;if(!o||!(n===o||r(o)&&u(o,n)))return n?!0:void 0}n.obj.empty=function(n,o){return n&&a(n,t,{n:o})?!1:!0}}(),function(){function t(n,o){return 2===arguments.length?(t.r=t.r||{},void(t.r[n]=o)):(t.r=t.r||[],void t.r.push(n))}var i=Object.keys;n.obj.map=function(a,s,f){var c,l,p,g,d,h=0,v=o(s);if(t.r=null,i&&r(a)&&(g=i(a),d=!0),e(a)||g)for(l=(g||a).length;l>h;h++){var _=h+n.list.index;if(v){if(p=d?s.call(f||this,a[g[h]],g[h],t):s.call(f||this,a[h],_,t),p!==c)return p}else if(s===a[d?g[h]:h])return g?g[h]:_}else for(h in a)if(v){if(u(a,h)&&(p=f?s.call(f,a[h],h,t):s(a[h],h,t),p!==c))return p}else if(s===a[h])return h;return v?t.r:n.list.index?0:-1}}(),n.time={},n.time.is=function(t){return t?t instanceof Date:+(new Date).getTime()};var o=n.fn.is,e=n.list.is,i=n.obj,r=i.is,u=i.has,a=i.map;t.exports=n})(t,"./type"),t(function(t){t.exports=function n(t,o,e){if(!t)return{to:n};var t=(this.tag||(this.tag={}))[t]||(this.tag[t]={tag:t,to:n._={next:function(){}}});if(o instanceof Function){var i={off:n.off||(n.off=function(){return this.next===n._.next?!0:(this===this.the.last&&(this.the.last=this.back),this.to.back=this.back,this.next=n._.next,void(this.back.to=this.to))}),to:n._,next:o,the:t,on:this,as:e};return(i.back=t.last||t).to=i,t.last=i}return(t=t.to).next(o),t}})(t,"./onto"),t(function(t){function n(t,n,e,i,r){if(n>t)return{defer:!0};if(e>n)return{historical:!0};if(n>e)return{converge:!0,incoming:!0};if(n===e){if(i=o(i)||"",r=o(r)||"",i===r)return{state:!0};if(r>i)return{converge:!0,current:!0};if(i>r)return{converge:!0,incoming:!0}}return{err:"Invalid CRDT Data: "+i+" to "+r+" at "+n+" to "+e+"!"}}if("undefined"==typeof JSON)throw new Error("JSON is not included in this browser. Please load it first: ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js");var o=JSON.stringify;t.exports=n})(t,"./HAM"),t(function(n){var o=t("./type"),e={};e.is=function(t){return t===i?!1:null===t?!0:t===1/0?!1:s(t)||u(t)||a(t)?!0:e.rel.is(t)||!1},e.rel={_:"#"},function(){function t(t,n){var o=this;return o.id?o.id=!1:n==r&&s(t)?void(o.id=t):o.id=!1}e.rel.is=function(n){if(n&&n[r]&&!n._&&c(n)){var o={};if(p(n,t,o),o.id)return o.id}return!1}}(),e.rel.ify=function(t){return l({},r,t)};var i,r=e.rel._,u=o.bi.is,a=o.num.is,s=o.text.is,f=o.obj,c=f.is,l=f.put,p=f.map;n.exports=e})(t,"./val"),t(function(n){var o=t("./type"),e=t("./val"),i={_:"_"};i.soul=function(t,n){return t&&t._&&t._[n||p]},i.soul.ify=function(t,n){return n="string"==typeof n?{soul:n}:n||{},t=t||{},t._=t._||{},t._[p]=n.soul||t._[p]||l(),t},i.soul._=e.rel._,function(){function t(t,n){return n!==i._?e.is(t)?void(this.cb&&this.cb.call(this.as,t,n,this.n,this.s)):!0:void 0}i.is=function(n,o,e){var r;return a(n)&&(r=i.soul(n))?!f(n,t,{as:e,cb:o,s:r,n:n}):!1}}(),function(){function t(t,n){var o,i,r=this.o;return r.map?(o=r.map.call(this.as,t,""+n,r.node),void(i===o?s(r.node,n):r.node&&(r.node[n]=o))):void(e.is(t)&&(r.node[n]=t))}i.ify=function(n,o,e){return o?"string"==typeof o?o={soul:o}:o instanceof Function&&(o={map:o}):o={},o.map&&(o.node=o.map.call(e,n,r,o.node||{})),(o.node=i.soul.ify(o.node||{},o))&&f(n,t,{o:o,as:e}),o.node}}();var r,u=o.obj,a=u.is,s=u.del,f=u.map,c=o.text,l=c.random,p=i.soul._;n.exports=i})(t,"./node"),t(function(n){function o(){var t;return t=f?c+f.now():r(),t>u?(a=0,u=t+o.drift):u=t+(a+=1)/s+o.drift}var e=t("./type"),i=t("./node"),r=e.time.is,u=-(1/0),a=0,s=1e3,f="undefined"!=typeof performance?performance.timing&&performance:!1,c=f&&f.timing&&f.timing.navigationStart||(f=!1);o._=">",o.drift=0,o.is=function(t,n,e){var i=n&&t&&t[j]&&t[j][o._]||e;if(i)return m(i=i[n])?i:-(1/0)},o.ify=function(t,n,e,r,u){if(!t||!t[j]){if(!u)return;t=i.soul.ify(t,u)}var a=g(t[j],o._);return l!==n&&n!==j&&(m(e)&&(a[n]=e),l!==r&&(t[n]=r)),t},o.to=function(t,n,e){var r=t[n];return h(r)&&(r=_(r)),o.ify(e,n,o.is(t,n),r,i.soul(t))},function(){function t(t,n){j!==n&&o.ify(this.o,n,this.s)}o.map=function(n,e,i){var r,u=h(u=n||e)?u:null;return n=y(n=n||e)?n:null,u&&!n?(e=m(e)?e:o(),u[j]=u[j]||{},v(u,t,{o:u,s:e}),u):(i=i||h(e)?e:r,e=m(e)?e:o(),function(o,u,a,s){return n?(n.call(i||this||{},o,u,a,s),void(d(a,u)&&r===a[u]||t.call({o:a,s:e},o,u))):(t.call({o:a,s:e},o,u),o)})}}();var l,p=e.obj,g=p.as,d=p.has,h=p.is,v=p.map,_=p.copy,b=e.num,m=b.is,k=e.fn,y=k.is,j=i._;n.exports=o})(t,"./state"),t(function(n){var o=t("./type"),e=t("./val"),i=t("./node"),r={};!function(){function t(t,o){return t&&o===i.soul(t)&&i.is(t,this.fn,this.as)?void(this.cb&&(n.n=t,n.as=this.as,this.cb.call(n.as,t,o,n))):!0}function n(t){t&&i.is(n.n,t,n.as)}r.is=function(n,o,e,i){return n&&s(n)&&!l(n)?!g(n,t,{cb:o,fn:e,as:i}):!1}}(),function(){function t(t,r){var u;return(u=l(t,r))?u:(r.env=t,r.soul=o,i.ify(r.obj,n,r)&&(t.graph[e.rel.is(r.rel)]=r.node),r)}function n(n,o,r){var s,l,p=this,g=p.env;if(i._===o&&c(n,e.rel._))return r._;if(s=a(n,o,r,p,g)){if(o||(p.node=p.node||r||{},c(n,i._)&&(p.node._=d(n._)),p.node=i.soul.ify(p.node,e.rel.is(p.rel)),p.rel=p.rel||e.rel.ify(i.soul(p.node))),(l=g.map)&&(l.call(g.as||{},n,o,r,p),c(r,o))){if(n=r[o],u===n)return void f(r,o);if(!(s=a(n,o,r,p,g)))return}if(!o)return p.node;if(!0===s)return n;if(l=t(g,{obj:n,path:p.path.concat(o)}),l.node)return l.rel}}function o(t){var n=this,o=e.rel.is(n.rel),r=n.env.graph;n.rel=n.rel||e.rel.ify(t),n.rel[e.rel._]=t,n.node&&n.node[i._]&&(n.node[i._][e.rel._]=t),c(r,o)&&(r[t]=r[o],f(r,o))}function a(t,n,o,i,r){var u;return e.is(t)?!0:s(t)?1:(u=r.invalid)?(t=u.call(r.as||{},t,n,o),a(t,n,o,i,r)):void(r.err="Invalid value at '"+i.path.concat(n).join(".")+"'!")}function l(t,n){for(var o,e=t.seen,i=e.length;i--;)if(o=e[i],n.obj===o.obj)return o;e.push(n)}r.ify=function(n,o,i){var r={path:[],obj:n};return o?"string"==typeof o?o={soul:o}:o instanceof Function&&(o.map=o):o={},o.soul&&(r.rel=e.rel.ify(o.soul)),o.graph=o.graph||{},o.seen=o.seen||[],o.as=o.as||i,t(o,r),o.root=r.node,o.graph}}(),r.node=function(t){var n=i.soul(t);if(n)return p({},n,t)},function(){function t(t,n){var o,u;if(i._===n){if(l(t,e.rel._))return;return void(this.obj[n]=d(t))}return(o=e.rel.is(t))?(u=this.opt.seen[o])?void(this.obj[n]=u):void(this.obj[n]=this.opt.seen[o]=r.to(this.graph,o,this.opt)):void(this.obj[n]=t)}r.to=function(n,o,e){if(n){var i={};return e=e||{seen:{}},g(n[o],t,{obj:i,graph:n,opt:e}),i}}}();var u,a=(o.fn.is,o.obj),s=a.is,f=a.del,c=a.has,l=a.empty,p=a.put,g=a.map,d=a.copy;n.exports=r})(t,"./graph"),t(function(n){function o(t){var n={s:{}};return t=t||{max:1e3,age:12e4},n.check=function(t){return n.s[t]?n.track(t):!1},n.track=function(o){return n.s[o]=i(),n.to||(n.to=setTimeout(function(){e.obj.map(n.s,function(o,r){t.age>i()-o||e.obj.del(n.s,r)}),n.to=null},t.age)),o},n}var e=t("./type"),i=e.time.is;n.exports=o})(t,"./dup"),t(function(n){function i(t){return t instanceof i?(this._={gun:this}).gun:this instanceof i?i.create(this._={gun:this,opt:t}):new i(t)}i.is=function(t){return t instanceof i},i.version=.8,i.chain=i.prototype,i.chain.toJSON=function(){};var r=t("./type");r.obj.to(r,i),i.HAM=t("./HAM"),i.val=t("./val"),i.node=t("./node"),i.state=t("./state"),i.graph=t("./graph"),i.dup=t("./dup"),i.on=t("./onto"),i._={node:i.node._,soul:i.val.rel._,state:i.state._,field:".",value:"="},function(){function t(t){var n,o,e=this,r=e.as;t.gun||(t.gun=r.gun),(o=t["#"])||(o=t["#"]=c(9)),r.dup.check(o)||(r.dup.track(o),n=d(t,{gun:r.gun}),r.ack(t["@"],t)||(t.get&&i.on.get(n),t.put&&i.on.put(n)),r.on("out",n))}i.create=function(n){n.on=n.on||i.on,n.root=n.root||n.gun,n.graph=n.graph||{},n.dup=n.dup||i.dup(),n.ask=i.on.ask,n.ack=i.on.ack;var o=n.gun.opt(n.opt);return n.once||(n.on("in",t,n),n.on("out",t,n)),n.once=1,o}}(),function(){function t(t,n,o,e){var r=this,u=i.state.is(o,n);if(!u)return r.err="Error: No state on '"+n+"' in node '"+e+"'!";var a=r.graph[e]||m,s=i.state.is(a,n,!0),f=a[n],c=i.HAM(r.machine,u,s,t,f);return c.incoming?(r.put[e]=i.state.to(o,n,r.put[e]),void((r.diff||(r.diff={}))[e]=i.state.to(o,n,r.diff[e]))):void(c.defer&&(r.defer=u<(r.defer||1/0)?u:r.defer))}function n(t,n){var e=this.gun._,i=(e.next||m)[n];if(i){var r=this.map[n]={put:this.node=t,get:this.soul=n,gun:this.ref=i};h(t,o,this),e.on("node",r)}}function o(t,n){var o=this.graph,e=this.soul,r=this.ref._;o[e]=i.state.to(this.node,n,o[e]),(r.put||(r.put={}))[n]=t}function e(t){t.gun&&t.gun._.on("in",t)}i.on.put=function(o){var r=o.gun._,a={gun:o.gun,graph:o.gun._.graph,put:{},map:{},machine:i.state()};return i.graph.is(o.put,null,t,a)||(a.err="Error: Invalid graph!"),a.err?r.on("in",{"@":o["#"],err:i.log(a.err)}):(h(a.put,n,a),h(a.map,e,a),u!==a.defer&&setTimeout(function(){i.on.put(o)},a.defer-r.machine),void(a.diff&&r.on("put",d(o,{put:a.diff}))))},i.on.get=function(t){var n,o=t.gun._,e=t.get[_],r=o.graph[e],u=t.get[b],a=o.next||(o.next={}),s=(a[e]||m)._;if(!r||!s)return o.on("get",t);if(u){if(!g(r,u))return o.on("get",t);r=i.state.to(r,u)}else r=i.obj.copy(r);r=i.graph.node(r),n=s.ack,o.on("in",{"@":t["#"],how:"mem",put:r,gun:s.gun}),n>0||o.on("get",t)}}(),function(){i.on.ask=function(t,n){if(this.on){var o=c(9);return t&&this.on(o,t,n),o}},i.on.ack=function(t,n){if(t&&n&&this.on){var o=t["#"]||t;if(this.tag&&this.tag[o])return this.on(o,n),!0}}}(),function(){i.chain.opt=function(t){t=t||{};var n=this,o=n._,e=t.peers||t;return p(t)||(t={}),p(o.opt)||(o.opt=t),f(e)&&(e=[e]),a(e)&&(e=h(e,function(t,n,o){o(t,{url:t})}),p(o.opt.peers)||(o.opt.peers={}),o.opt.peers=d(e,o.opt.peers)),o.opt.uuid=o.opt.uuid||function(){return v().toString(36).replace(".","")+c(12)},o.opt.peers=o.opt.peers||{},d(t,o.opt),i.on("opt",o),n}}();var u,a=i.list.is,s=i.text,f=s.is,c=s.random,l=i.obj,p=l.is,g=l.has,d=l.to,h=l.map,v=(l.copy,i.state),_=i._.soul,b=i._.field,m=(i.val.rel.is,{});o.debug=function(t,n){return o.debug.i&&t===o.debug.i&&o.debug.i++&&(o.log.apply(o,arguments)||n)},i.log=function(){return!i.log.off&&o.log.apply(o,arguments),[].slice.call(arguments).join(" ")},i.log.once=function(t,n,o){return(o=i.log.once)[t]=o[t]||0,o[t]++||i.log(n)},i.log.once("welcome","Hello wonderful person! :) Thanks for using GUN, feel free to ask for help on https://gitter.im/amark/gun and ask StackOverflow questions tagged with 'gun'!"),"undefined"!=typeof window&&(window.Gun=i),"undefined"!=typeof e&&(e.exports=i),n.exports=i,i.log.once("0.8","0.8 WARNING! Breaking changes, test that your app works before upgrading! The adapter interface has been upgraded (non-default storage and transport layers probably won't work). Also, `.path()` and `.not()` are outside core and now in 'lib/'.")})(t,"./root"),t(function(){var n=t("./root");n.chain.back=function(t,n){var i;if(-1===t||1/0===t)return this._.root;if(1===t)return this._.back||this;var r=this,u=r._;if("string"==typeof t&&(t=t.split(".")),t instanceof Array){var a=0,s=t.length,i=u;for(a;s>a;a++)i=(i||e)[t[a]];if(o!==i)return n?r:i;if(i=u.back)return i.back(t,n)}else if(t instanceof Function){for(var f,i={back:r};(i=i.back)&&(i=i._)&&!(f=t(i,n)););return f}};var o,e={}})(t,"./back"),t(function(){function n(t){var n,o,e,i=this.as,r=i.gun,u=r.back(-1);if(t.gun||(t.gun=r),o=t.get)if(e=o[m])e=u.get(e)._,d(o,k)?d(n=e.put,o=o[k])&&e.on("in",{get:e.get,put:c.state.to(n,o),gun:e.gun}):d(e,"put")&&e.on("in",e);else if(d(o,k)){o=o[k];var a=o?r.get(o)._:i;if(l!==a.put)return void a.on("in",a);if(d(i,"put")){var s,p=i.put;if((s=c.node.soul(p))&&(p=c.val.rel.ify(s)),s=c.val.rel.is(p)){if(!t.gun._)return;return void t.gun._.on("out",{get:e={"#":s,".":o,gun:t.gun},"#":u._.ask(f,e),gun:t.gun})}if(l===p||c.val.is(p)){if(!t.gun._)return;return void t.gun._.on("in",{get:o,gun:t.gun})}}else i.map&&b(i.map,function(t){t.at.on("in",t.at)});if(i.soul){if(!t.gun._)return;return void t.gun._.on("out",{get:e={"#":i.soul,".":o,gun:t.gun},"#":u._.ask(f,e),gun:t.gun})}if(i.get){if(!i.back._)return;return void i.back._.on("out",{get:h({},k,i.get),gun:r})}t=_(t,{get:{}})}else{if(d(i,"put")?i.on("in",i):i.map&&b(i.map,function(t){t.at.on("in",t.at)}),i.ack&&!d(i,"put"))return;if(i.ack=-1,i.soul)return void i.on("out",{get:e={"#":i.soul,gun:i.gun},"#":u._.ask(f,e),gun:i.gun});if(i.get){if(!i.back._)return;return void i.back._.on("out",{get:h({},k,i.get),gun:i.gun})}}i.back._.on("out",t)}function o(t){t=t._||t;{var n,o=this,r=this.as,s=t.gun,f=s._,g=t.put;r.back._||p}if(0>r.ack&&!t.ack&&!c.val.rel.is(g)&&(r.ack=1),r.get&&t.get!==r.get&&(t=_(t,{get:r.get})),r.field&&f!==r&&(t=_(t,{gun:r.gun}),f.ack&&(r.ack=r.ack||f.ack)),l===g){if(o.to.next(t),r.soul)return;return i(r,t,o),r.field&&a(r,t),v(f.echo,r.id),void v(r.map,f.id)}return r.soul?(r.root._.now&&(t=_(t,{put:g=f.put})),o.to.next(t),i(r,t,o),void b(g,u,{at:t,cat:r})):(n=c.val.rel.is(g))?(e(r,t,f,n),o.to.next(t),void i(r,t,o)):c.val.is(g)?(r.field||r.soul?a(r,t):(f.field||f.soul)&&((f.echo||(f.echo={}))[r.id]=r,(r.map||(r.map={}))[f.id]=r.map[f.id]||{at:f}),o.to.next(t),void i(r,t,o)):(r.field&&f!==r&&d(f,"put")&&(r.put=f.put),(n=c.node.soul(g))&&f.field&&(f.put=r.root.get(n)._.put),o.to.next(t),i(r,t,o),e(r,t,f,n),void b(g,u,{at:t,cat:r}))}function e(t,n,o,i){if(i&&y!==t.get){var r=t.root.get(i)._;t.field?o=r:o.field&&e(o,n,o,i),o!==t&&((o.echo||(o.echo={}))[t.id]=t,t.field&&!(t.map||p)[o.id]&&a(t,n),r=(t.map||(t.map={}))[o.id]=t.map[o.id]||{at:o},i!==r.rel&&s(t,r.rel=i))}}function i(t,n,o){t.echo&&(t.field&&(n=_(n,{event:o})),b(t.echo,r,n))}function r(t){t.on("in",this)}function u(t,n){var o,e,i,r=this.cat,u=r.next||p,a=this.at;(y!==n||u[n])&&(o=u[n])&&(i=o._,i.field?(t&&t[m]&&c.val.rel.is(t)===c.node.soul(i.put)||(i.put=t),e=o):e=a.gun.get(n),i.on("in",{put:t,get:n,gun:e,via:a}))}function a(t){if(t.field||t.soul){var n=t.map;t.map=null,null!==n&&(l!==n||t.put===l)&&(b(n,function(n){(n=n.at)&&v(n.echo,t.id)}),b(t.next,function(t,n){var o=t._;o.put=l,o.ack&&(o.ack=-1),o.on("in",{get:n,gun:t,put:l})}))}}function s(t,n){var o=t.root.get(n)._;return t.ack?(o.ack=o.ack||-1,void o.on("out",{get:o={"#":n,gun:o.gun},"#":t.root._.ask(f,o)})):void b(t.next,function(o,e){o._.on("out",{get:o={"#":n,".":e,gun:o},"#":t.root._.ask(f,o)})})}function f(t){var n=this.as,o=n.gun._;if(!t.put||n["."]&&!d(t.put[n["#"]],o.get)){if(o.put!==l)return;return void o.on("in",{get:o.get,put:o.put=l,gun:o.gun})}t.gun=o.root,c.on.put(t)}var c=t("./root");c.chain.chain=function(){var t,e=this._,i=new this.constructor(this),r=i._;return r.root=t=e.root,r.id=++t._.once,r.back=this,r.on=c.on,r.on("in",o,r),r.on("out",n,r),i},c.chain.chain.input=o;var l,p={},g=c.obj,d=g.has,h=g.put,v=g.del,_=g.to,b=g.map,m=c._.soul,k=c._.field,y=c.node._})(t,"./chain"),t(function(){function n(t,n){var o=n._,e=o.next,i=n.chain(),r=i._;return e||(e=o.next={}),e[r.get=t]=i,o.root===n?r.soul=t:(o.soul||o.field)&&(r.field=t),i}function o(t){var n,o=this,e=o.as,r=t.gun,a=r._,f=t.put;i===f&&(f=a.put),(n=f)&&n[s._]&&(n=s.is(n))&&(n=a.root.get(n)._,i!==n.put&&(t=u(t,{put:n.put}))),e.use(t,t.event||o),o.to.next(t)}var e=t("./root");e.chain.get=function(t,i,r){if("string"!=typeof t){if(t instanceof Function){var u=this,s=u._;return r=i||{},r.use=t,r.out=r.out||{cap:1},r.out.get=r.out.get||{},"_"!=s.get&&(s.root._.now=!0),s.on("in",o,r),s.on("out",r.out),s.root._.now=!1,u}return a(t)?this.get(""+t,i,r):((r=this.chain())._.err={err:e.log("Invalid get request!",t)},i&&i.call(r,r._.err),r)}var u,c,l=this,p=l._,g=p.next||f;return(u=g[t])||(u=n(t,l)),(c=p.stun)&&(u._.stun=u._.stun||c),i&&i instanceof Function&&u.get(i,r),u};var i,r=e.obj,u=(r.has,e.obj.to),a=e.num.is,s=e.val.rel,f=(e.node._,{})})(t,"./get"),t(function(){function n(t){t.batch=e;var n=t.opt||{},o=t.env=s.state.map(r,n.state);return o.soul=t.soul,t.graph=s.graph.ify(t.data,o,t),o.err?((t.ack||h).call(t,t.out={err:s.log(o.err)}),void(t.res&&t.res())):void t.batch()}function e(){var t=this;t.graph&&!g(t.stun,i)&&((t.res||v)(function(){t.ref._.on("out",{cap:3,gun:t.ref,put:t.out=t.env.graph,opt:t.opt,"#":t.gun.back(-1)._.ask(function(n){this.off(),t.ack&&t.ack(n,this)},t.opt)})},t),t.res&&t.res())}function i(t){return t?!0:void 0}function r(t,n,o,e){var i=this;!n&&e.path.length&&(i.res||v)(function(){var t=e.path,n=i.ref,o=(i.opt,0),r=t.length;for(o;r>o;o++)n=n.get(t[o]);if(i.not||s.node.soul(e.obj)){var a=s.node.soul(e.obj)||((i.opt||{}).uuid||i.gun.back("opt.uuid")||s.text.random)();return n.back(-1).get(a),void e.soul(a)}(i.stun=i.stun||{})[t]=!0,n.get("_").get(u,{as:{at:e,as:i}})},{as:i,at:e})}function u(t,n){var o=this.as,e=o.at;if(o=o.as,t.gun&&t.gun._.back){n.off(),t=t.gun._.back._;var i=s.node.soul(e.obj)||s.node.soul(t.put)||s.val.rel.is(t.put)||((o.opt||{}).uuid||o.gun.back("opt.uuid")||s.text.random)();t.gun.back(-1).get(i),e.soul(i),o.stun[e.path]=!1,o.batch()}}function a(t,n){var e=this.as;if(t.gun&&t.gun._){if(t.err)return void o.log("Please report this as an issue! Put.any.err");var i,r=t.gun._.back._,u=r.put,a=e.opt||{};if(n.off(),e.ref!==e.gun){if(i=e.gun._.get||r.get,!i)return void o.log("Please report this as an issue! Put.no.get");e.data=p({},i,e.data),i=null}if(f===u){if(!r.get)return;r.soul||(i=r.gun.back(function(t){return t.soul?t.soul:void(e.data=p({},t.get,e.data))})),i=i||r.get,r=r.root.get(i)._,e.not=e.soul=i,u=e.data}e.not||(e.soul=s.node.soul(u))||(e.soul=e.path&&l(e.data)?(a.uuid||r.root._.opt.uuid||s.text.random)():t.soul||r.soul||(a.uuid||r.root._.opt.uuid||s.text.random)()),e.ref.put(e.data,e.soul,e)}}var s=t("./root");s.chain.put=function(t,o,e){var i,r=this,u=r._,f=u.root;return e=e||{},e.data=t,e.gun=e.gun||r,"string"==typeof o?e.soul=o:e.ack=o,u.soul&&(e.soul=u.soul),e.soul||f===r?l(e.data)?(e.gun=r=f.get(e.soul=e.soul||(e.not=s.node.soul(e.data)||(f._.opt.uuid||s.text.random)())),e.ref=e.gun,n(e),r):((e.ack||h).call(e,e.out={err:s.log("Data saved to the root level of the graph must be a node (an object), not a",typeof e.data,'of "'+e.data+'"!')}),e.res&&e.res(),r):s.is(t)?(t.get(function(t,n){n.off();var i=s.node.soul(t.put);return i?void r.put(s.val.rel.ify(i),o,e):void s.log("The reference you are saving is a",typeof t.put,'"'+e.put+'", not a node (object)!')}),r):(e.ref=e.ref||f===(i=u.back)?r:i,e.ref._.soul&&s.val.is(e.data)&&u.get?(e.data=p({},u.get,e.data),e.ref.put(e.data,e.soul,e),r):(e.ref.get("_").get(a,{as:e}),e.out||(e.res=e.res||h,e.gun._.stun=e.ref._.stun),r))};var f,c=s.obj,l=c.is,p=c.put,g=c.map,d={},h=function(){},v=function(t,n){t.call(n||d)}})(t,"./put"),t(function(n){var o=t("./root");t("./opt"),t("./chain"),t("./back"),t("./put"),t("./get"),n.exports=o})(t,"./index"),t(function(){function n(t,n){var o,r=this,u=t.gun,s=u._,f=s.put||t.put,o=r.last,c=s.id+t.get;if(i!==f){if(f&&f[a._]&&(o=a.is(f))){if(o=s.root.get(o)._,i===o.put)return;f=o.put}r.change&&(f=t.put),(o.put!==f||o.get!==c||e.node.soul(f))&&(o.put=f,o.get=c,s.last=f,r.as?r.ok.call(r.as,t,n):r.ok.call(u,f,t.get,t,n))}}function o(t,n){var e,r=this.as,u=r.cat,s=t.gun,f=s._,c=f.put||t.put;if(c&&c[a._]&&(e=a.is(c))){if(e=u.root.get(e)._,i===e.put)return;c=e.put}if(n.wait&&clearTimeout(n.wait),!r.async)return void(n.wait=setTimeout(function(){o.call({as:r},t,n,n.wait||1)},r.wait||99));if(u.field||u.soul){if(n.off())return}else{if((r.seen=r.seen||{})[f.id])return;r.seen[f.id]=!0}r.ok.call(t.gun||r.gun,c,t.get)}var e=t("./index");e.chain.on=function(t,o,e,i){var r,u,a=this,f=a._;if("string"==typeof t)return o?(r=f.on(t,o,e||f,i),e&&e.gun&&(e.subs||(e.subs=[])).push(r),u=function(){r&&r.off&&r.off(),u.off()},u.off=a.off.bind(a)||s,a.off=u,a):f.on(t);var c=o;return c=!0===c?{change:!0}:c||{},c.ok=t,c.last={},a.get(n,c),a},e.chain.val=function(t,n){var r=this,u=r._,a=u.put;if(0=(n.batch||1e3)?s():void(e||(e=setTimeout(s,n.wait||1)))}),t.on("get",function(t){this.to.next(t);var n,o,e,i=t.gun,r=t.get;if(r&&(n=r[Gun._.soul])){var a=r["."];o=u[n]||e,o&&a&&(o=Gun.state.to(o,a)),(o||Gun.obj.empty(i.back("opt.peers")))&&i.on("in",{"@":t["#"],put:Gun.graph.node(o),how:"lS"})}});var a=function(t,n,o,e){u[e]=Gun.state.to(o,n,u[e])},s=function(){var a;r=0,clearTimeout(e),e=!1;var s=i;i={};try{o.setItem(n.file,JSON.stringify(u))}catch(f){a=f||"localStorage failure"}(a||Gun.obj.empty(n.peers))&&Gun.obj.map(s,function(n,o){t.on("in",{"@":o,err:a,ok:0})})}}})}})(t,"./adapters/localStorage"),t(function(){var n,o=t("./index");if("undefined"!=typeof window){n=window.WebSocket||window.webkitWebSocket||window.mozWebSocket,o.on("opt",function(t){function i(t){var n=this,o=c,e=t.wire||u(t,n);return e?e.readyState===e.OPEN?void e.send(o):void(t.queue=t.queue||[]).push(o):void 0}function r(t,n,o){if(o&&t){try{t=JSON.parse(t.data||t)}catch(i){}if(t instanceof Array)for(var u,a=0;u=t[a++];)r(u,n,o);else 1==f.who&&(t.ws=e),o.on("in",t)}}function u(t,e){if(t&&t.url){var u=t.url.replace("http","ws"),s=t.wire=new n(u);return s.onclose=function(){a(t,e)},s.onerror=function(n){a(t,e),n&&"ECONNREFUSED"===n.code},s.onopen=function(){var n=t.queue;t.queue=[],o.obj.map(n,function(n){c=n,i.call(e,t)})},s.onmessage=function(n){r(n,t,e)},s}}function a(t,n){clearTimeout(t.defer),t.defer=setTimeout(function(){u(t,n)},2e3)}this.to.next(t);var s=t.opt;if(!t.once&&!1!==s.WebSocket){var f=s.ws||(s.ws={});if(f.who=0,o.obj.map(s.peers,function(){++f.who}),!t.once){var c;t.on("out",function(n){if(this.to.next(n),!n.ws||1!=f.who){if(c=JSON.stringify(n),f.drain)return void f.drain.push(c);f.drain=[],setTimeout(function(){if(f.drain){var n=f.drain;f.drain=null,n.length&&(c=JSON.stringify(n),o.obj.map(s.peers,i,t))}},s.wait||1),o.obj.map(s.peers,i,t)}})}}});var e=function(){}}})(t,"./adapters/websocket")}(); \ No newline at end of file +!function(){function t(n){function o(t){return t.split("/").slice(-1).toString().replace(".js","")}return n.slice?t[o(n)]:function(e,i){n(e={exports:{}}),t[o(i)]=e.exports}}var n;"undefined"!=typeof window&&(n=window),"undefined"!=typeof global&&(n=global),n=n||{};var o=n.console||{log:function(){}};if("undefined"!=typeof module)var e=module;t(function(t){var n={};n.fns=n.fn={is:function(t){return!!t&&"function"==typeof t}},n.bi={is:function(t){return t instanceof Boolean||"boolean"==typeof t}},n.num={is:function(t){return!e(t)&&(t-parseFloat(t)+1>=0||1/0===t||-(1/0)===t)}},n.text={is:function(t){return"string"==typeof t}},n.text.ify=function(t){return n.text.is(t)?t:"undefined"!=typeof JSON?JSON.stringify(t):t&&t.toString?t.toString():t},n.text.random=function(t,n){var o="";for(t=t||24,n=n||"0123456789ABCDEFGHIJKLMNOPQRSTUVWXZabcdefghijklmnopqrstuvwxyz";t>0;)o+=n.charAt(Math.floor(Math.random()*n.length)),t--;return o},n.text.match=function(t,o){function e(t,n){for(var o,e=-1,i=0;o=n[i++];)if(!~(e=t.indexOf(o,e+1)))return!1;return!0}var i=!1;if(t=t||"",o=n.text.is(o)?{"=":o}:o||{},n.obj.has(o,"~")&&(t=t.toLowerCase(),o["="]=(o["="]||o["~"]).toLowerCase()),n.obj.has(o,"="))return t===o["="];if(n.obj.has(o,"*")){if(t.slice(0,o["*"].length)!==o["*"])return!1;i=!0,t=t.slice(o["*"].length)}if(n.obj.has(o,"!")){if(t.slice(-o["!"].length)!==o["!"])return!1;i=!0}if(n.obj.has(o,"+")&&n.list.map(n.list.is(o["+"])?o["+"]:[o["+"]],function(n){return t.indexOf(n)>=0?void(i=!0):!0}))return!1;if(n.obj.has(o,"-")&&n.list.map(n.list.is(o["-"])?o["-"]:[o["-"]],function(n){return t.indexOf(n)<0?void(i=!0):!0}))return!1;if(n.obj.has(o,">")){if(!(t>o[">"]))return!1;i=!0}if(n.obj.has(o,"<")){if(!(tn?-1:n>o?1:0):0}},n.list.map=function(t,n,o){return a(t,n,o)},n.list.index=1,n.obj={is:function(t){return t?t instanceof Object&&t.constructor===Object||"Object"===Object.prototype.toString.call(t).match(/^\[object (\w+)\]$/)[1]:!1}},n.obj.put=function(t,n,o){return(t||{})[n]=o,t},n.obj.has=function(t,n){return t&&Object.prototype.hasOwnProperty.call(t,n)},n.obj.del=function(t,n){return t?(t[n]=null,delete t[n],t):void 0},n.obj.as=function(t,n,o,e){return t[n]=t[n]||(e===o?{}:o)},n.obj.ify=function(t){if(r(t))return t;try{t=JSON.parse(t)}catch(n){t={}}return t},function(){function t(t,n){u(this,n)&&o!==this[n]||(this[n]=t)}var o;n.obj.to=function(n,o){return o=o||{},a(n,t,o),o}}(),n.obj.copy=function(t){return t?JSON.parse(JSON.stringify(t)):t},function(){function t(t,n){var o=this.n;if(!o||!(n===o||r(o)&&u(o,n)))return n?!0:void 0}n.obj.empty=function(n,o){return n&&a(n,t,{n:o})?!1:!0}}(),function(){function t(n,o){return 2===arguments.length?(t.r=t.r||{},void(t.r[n]=o)):(t.r=t.r||[],void t.r.push(n))}var i=Object.keys;n.obj.map=function(a,s,f){var c,l,p,g,d,h=0,v=o(s);if(t.r=null,i&&r(a)&&(g=i(a),d=!0),e(a)||g)for(l=(g||a).length;l>h;h++){var _=h+n.list.index;if(v){if(p=d?s.call(f||this,a[g[h]],g[h],t):s.call(f||this,a[h],_,t),p!==c)return p}else if(s===a[d?g[h]:h])return g?g[h]:_}else for(h in a)if(v){if(u(a,h)&&(p=f?s.call(f,a[h],h,t):s(a[h],h,t),p!==c))return p}else if(s===a[h])return h;return v?t.r:n.list.index?0:-1}}(),n.time={},n.time.is=function(t){return t?t instanceof Date:+(new Date).getTime()};var o=n.fn.is,e=n.list.is,i=n.obj,r=i.is,u=i.has,a=i.map;t.exports=n})(t,"./type"),t(function(t){t.exports=function n(t,o,e){if(!t)return{to:n};var t=(this.tag||(this.tag={}))[t]||(this.tag[t]={tag:t,to:n._={next:function(){}}});if(o instanceof Function){var i={off:n.off||(n.off=function(){return this.next===n._.next?!0:(this===this.the.last&&(this.the.last=this.back),this.to.back=this.back,this.next=n._.next,this.back.to=this.to,void(this.the.last===this.the&&delete this.on.tag[this.the.tag]))}),to:n._,next:o,the:t,on:this,as:e};return(i.back=t.last||t).to=i,t.last=i}return(t=t.to).next(o),t}})(t,"./onto"),t(function(t){function n(t,n,e,i,r){if(n>t)return{defer:!0};if(e>n)return{historical:!0};if(n>e)return{converge:!0,incoming:!0};if(n===e){if(i=o(i)||"",r=o(r)||"",i===r)return{state:!0};if(r>i)return{converge:!0,current:!0};if(i>r)return{converge:!0,incoming:!0}}return{err:"Invalid CRDT Data: "+i+" to "+r+" at "+n+" to "+e+"!"}}if("undefined"==typeof JSON)throw new Error("JSON is not included in this browser. Please load it first: ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js");var o=JSON.stringify;t.exports=n})(t,"./HAM"),t(function(n){var o=t("./type"),e={};e.is=function(t){return t===i?!1:null===t?!0:t===1/0?!1:s(t)||u(t)||a(t)?!0:e.rel.is(t)||!1},e.rel={_:"#"},function(){function t(t,n){var o=this;return o.id?o.id=!1:n==r&&s(t)?void(o.id=t):o.id=!1}e.rel.is=function(n){if(n&&n[r]&&!n._&&c(n)){var o={};if(p(n,t,o),o.id)return o.id}return!1}}(),e.rel.ify=function(t){return l({},r,t)};var i,r=e.rel._,u=o.bi.is,a=o.num.is,s=o.text.is,f=o.obj,c=f.is,l=f.put,p=f.map;n.exports=e})(t,"./val"),t(function(n){var o=t("./type"),e=t("./val"),i={_:"_"};i.soul=function(t,n){return t&&t._&&t._[n||p]},i.soul.ify=function(t,n){return n="string"==typeof n?{soul:n}:n||{},t=t||{},t._=t._||{},t._[p]=n.soul||t._[p]||l(),t},i.soul._=e.rel._,function(){function t(t,n){return n!==i._?e.is(t)?void(this.cb&&this.cb.call(this.as,t,n,this.n,this.s)):!0:void 0}i.is=function(n,o,e){var r;return a(n)&&(r=i.soul(n))?!f(n,t,{as:e,cb:o,s:r,n:n}):!1}}(),function(){function t(t,n){var o,i,r=this.o;return r.map?(o=r.map.call(this.as,t,""+n,r.node),void(i===o?s(r.node,n):r.node&&(r.node[n]=o))):void(e.is(t)&&(r.node[n]=t))}i.ify=function(n,o,e){return o?"string"==typeof o?o={soul:o}:o instanceof Function&&(o={map:o}):o={},o.map&&(o.node=o.map.call(e,n,r,o.node||{})),(o.node=i.soul.ify(o.node||{},o))&&f(n,t,{o:o,as:e}),o.node}}();var r,u=o.obj,a=u.is,s=u.del,f=u.map,c=o.text,l=c.random,p=i.soul._;n.exports=i})(t,"./node"),t(function(n){function o(){var t;return t=f?c+f.now():r(),t>u?(a=0,u=t+o.drift):u=t+(a+=1)/s+o.drift}var e=t("./type"),i=t("./node"),r=e.time.is,u=-(1/0),a=0,s=1e3,f="undefined"!=typeof performance?performance.timing&&performance:!1,c=f&&f.timing&&f.timing.navigationStart||(f=!1);o._=">",o.drift=0,o.is=function(t,n,e){var i=n&&t&&t[j]&&t[j][o._]||e;if(i)return m(i=i[n])?i:-(1/0)},o.ify=function(t,n,e,r,u){if(!t||!t[j]){if(!u)return;t=i.soul.ify(t,u)}var a=g(t[j],o._);return l!==n&&n!==j&&(m(e)&&(a[n]=e),l!==r&&(t[n]=r)),t},o.to=function(t,n,e){var r=t[n];return h(r)&&(r=_(r)),o.ify(e,n,o.is(t,n),r,i.soul(t))},function(){function t(t,n){j!==n&&o.ify(this.o,n,this.s)}o.map=function(n,e,i){var r,u=h(u=n||e)?u:null;return n=y(n=n||e)?n:null,u&&!n?(e=m(e)?e:o(),u[j]=u[j]||{},v(u,t,{o:u,s:e}),u):(i=i||h(e)?e:r,e=m(e)?e:o(),function(o,u,a,s){return n?(n.call(i||this||{},o,u,a,s),void(d(a,u)&&r===a[u]||t.call({o:a,s:e},o,u))):(t.call({o:a,s:e},o,u),o)})}}();var l,p=e.obj,g=p.as,d=p.has,h=p.is,v=p.map,_=p.copy,b=e.num,m=b.is,k=e.fn,y=k.is,j=i._;n.exports=o})(t,"./state"),t(function(n){var o=t("./type"),e=t("./val"),i=t("./node"),r={};!function(){function t(t,o){return t&&o===i.soul(t)&&i.is(t,this.fn,this.as)?void(this.cb&&(n.n=t,n.as=this.as,this.cb.call(n.as,t,o,n))):!0}function n(t){t&&i.is(n.n,t,n.as)}r.is=function(n,o,e,i){return n&&s(n)&&!l(n)?!g(n,t,{cb:o,fn:e,as:i}):!1}}(),function(){function t(t,r){var u;return(u=l(t,r))?u:(r.env=t,r.soul=o,i.ify(r.obj,n,r)&&(t.graph[e.rel.is(r.rel)]=r.node),r)}function n(n,o,r){var s,l,p=this,g=p.env;if(i._===o&&c(n,e.rel._))return r._;if(s=a(n,o,r,p,g)){if(o||(p.node=p.node||r||{},c(n,i._)&&(p.node._=d(n._)),p.node=i.soul.ify(p.node,e.rel.is(p.rel)),p.rel=p.rel||e.rel.ify(i.soul(p.node))),(l=g.map)&&(l.call(g.as||{},n,o,r,p),c(r,o))){if(n=r[o],u===n)return void f(r,o);if(!(s=a(n,o,r,p,g)))return}if(!o)return p.node;if(!0===s)return n;if(l=t(g,{obj:n,path:p.path.concat(o)}),l.node)return l.rel}}function o(t){var n=this,o=e.rel.is(n.rel),r=n.env.graph;n.rel=n.rel||e.rel.ify(t),n.rel[e.rel._]=t,n.node&&n.node[i._]&&(n.node[i._][e.rel._]=t),c(r,o)&&(r[t]=r[o],f(r,o))}function a(t,n,o,i,r){var u;return e.is(t)?!0:s(t)?1:(u=r.invalid)?(t=u.call(r.as||{},t,n,o),a(t,n,o,i,r)):void(r.err="Invalid value at '"+i.path.concat(n).join(".")+"'!")}function l(t,n){for(var o,e=t.seen,i=e.length;i--;)if(o=e[i],n.obj===o.obj)return o;e.push(n)}r.ify=function(n,o,i){var r={path:[],obj:n};return o?"string"==typeof o?o={soul:o}:o instanceof Function&&(o.map=o):o={},o.soul&&(r.rel=e.rel.ify(o.soul)),o.graph=o.graph||{},o.seen=o.seen||[],o.as=o.as||i,t(o,r),o.root=r.node,o.graph}}(),r.node=function(t){var n=i.soul(t);if(n)return p({},n,t)},function(){function t(t,n){var o,u;if(i._===n){if(l(t,e.rel._))return;return void(this.obj[n]=d(t))}return(o=e.rel.is(t))?(u=this.opt.seen[o])?void(this.obj[n]=u):void(this.obj[n]=this.opt.seen[o]=r.to(this.graph,o,this.opt)):void(this.obj[n]=t)}r.to=function(n,o,e){if(n){var i={};return e=e||{seen:{}},g(n[o],t,{obj:i,graph:n,opt:e}),i}}}();var u,a=(o.fn.is,o.obj),s=a.is,f=a.del,c=a.has,l=a.empty,p=a.put,g=a.map,d=a.copy;n.exports=r})(t,"./graph"),t(function(n){function o(t){var n={s:{}};return t=t||{max:1e3,age:9e3},n.check=function(t){return n.s[t]?n.track(t):!1},n.track=function(o){return n.s[o]=i(),n.to||(n.to=setTimeout(function(){e.obj.map(n.s,function(o,r){t.age>i()-o||e.obj.del(n.s,r)}),n.to=null},t.age)),o},n}var e=t("./type"),i=e.time.is;n.exports=o})(t,"./dup"),t(function(n){function i(t){return t instanceof i?(this._={gun:this}).gun:this instanceof i?i.create(this._={gun:this,opt:t}):new i(t)}i.is=function(t){return t instanceof i},i.version=.8,i.chain=i.prototype,i.chain.toJSON=function(){};var r=t("./type");r.obj.to(r,i),i.HAM=t("./HAM"),i.val=t("./val"),i.node=t("./node"),i.state=t("./state"),i.graph=t("./graph"),i.dup=t("./dup"),i.on=t("./onto"),i._={node:i.node._,soul:i.val.rel._,state:i.state._,field:".",value:"="},function(){function t(t){var n,o,e=this,r=e.as;t.gun||(t.gun=r.gun),(o=t["#"])||(o=t["#"]=c(9)),r.dup.check(o)||(r.dup.track(o),n=d(t,{gun:r.gun}),r.ack(t["@"],t)||(t.get&&i.on.get(n),t.put&&i.on.put(n)),r.on("out",n))}i.create=function(n){n.on=n.on||i.on,n.root=n.root||n.gun,n.graph=n.graph||{},n.dup=n.dup||i.dup(),n.ask=i.on.ask,n.ack=i.on.ack;var o=n.gun.opt(n.opt);return n.once||(n.on("in",t,n),n.on("out",t,n)),n.once=1,o}}(),function(){function t(t,n,o,e){var r=this,u=i.state.is(o,n);if(!u)return r.err="Error: No state on '"+n+"' in node '"+e+"'!";var a=r.graph[e]||m,s=i.state.is(a,n,!0),f=a[n],c=i.HAM(r.machine,u,s,t,f);return c.incoming?(r.put[e]=i.state.to(o,n,r.put[e]),void((r.diff||(r.diff={}))[e]=i.state.to(o,n,r.diff[e]))):void(c.defer&&(r.defer=u<(r.defer||1/0)?u:r.defer))}function n(t,n){var e=this.gun._,i=(e.next||m)[n];if(i){var r=this.map[n]={put:this.node=t,get:this.soul=n,gun:this.ref=i};h(t,o,this),e.on("node",r)}}function o(t,n){var o=this.graph,e=this.soul,r=this.ref._;o[e]=i.state.to(this.node,n,o[e]),(r.put||(r.put={}))[n]=t}function e(t){t.gun&&t.gun._.on("in",t)}i.on.put=function(o){var r=o.gun._,a={gun:o.gun,graph:o.gun._.graph,put:{},map:{},machine:i.state()};return i.graph.is(o.put,null,t,a)||(a.err="Error: Invalid graph!"),a.err?r.on("in",{"@":o["#"],err:i.log(a.err)}):(h(a.put,n,a),h(a.map,e,a),u!==a.defer&&setTimeout(function(){i.on.put(o)},a.defer-r.machine),void(a.diff&&r.on("put",d(o,{put:a.diff}))))},i.on.get=function(t){var n,o=t.gun._,e=t.get[_],r=o.graph[e],u=t.get[b],a=o.next||(o.next={}),s=(a[e]||m)._;if(!r||!s)return o.on("get",t);if(u){if(!g(r,u))return o.on("get",t);r=i.state.to(r,u)}else r=i.obj.copy(r);r=i.graph.node(r),n=s.ack,o.on("in",{"@":t["#"],how:"mem",put:r,gun:s.gun}),n>0||o.on("get",t)}}(),function(){i.on.ask=function(t,n){if(this.on){var o=c(9);if(t){var e=this.on(o,t,n);e.err=setTimeout(function(){e.next({err:"Error: No ACK received yet."}),e.off()},9e3)}return o}},i.on.ack=function(t,n){if(t&&n&&this.on){var o=t["#"]||t,e=(this.tag||m)[o];if(e)return this.on(o,n),clearTimeout(e.err),!0}}}(),function(){i.chain.opt=function(t){t=t||{};var n=this,o=n._,e=t.peers||t;return p(t)||(t={}),p(o.opt)||(o.opt=t),f(e)&&(e=[e]),a(e)&&(e=h(e,function(t,n,o){o(t,{url:t})}),p(o.opt.peers)||(o.opt.peers={}),o.opt.peers=d(e,o.opt.peers)),o.opt.uuid=o.opt.uuid||function(){return v().toString(36).replace(".","")+c(12)},o.opt.peers=o.opt.peers||{},d(t,o.opt),i.on("opt",o),n}}();var u,a=i.list.is,s=i.text,f=s.is,c=s.random,l=i.obj,p=l.is,g=l.has,d=l.to,h=l.map,v=(l.copy,i.state),_=i._.soul,b=i._.field,m=(i.val.rel.is,{});o.debug=function(t,n){return o.debug.i&&t===o.debug.i&&o.debug.i++&&(o.log.apply(o,arguments)||n)},i.log=function(){return!i.log.off&&o.log.apply(o,arguments),[].slice.call(arguments).join(" ")},i.log.once=function(t,n,o){return(o=i.log.once)[t]=o[t]||0,o[t]++||i.log(n)},i.log.once("welcome","Hello wonderful person! :) Thanks for using GUN, feel free to ask for help on https://gitter.im/amark/gun and ask StackOverflow questions tagged with 'gun'!"),"undefined"!=typeof window&&(window.Gun=i),"undefined"!=typeof e&&(e.exports=i),n.exports=i,i.log.once("0.8","0.8 WARNING! Breaking changes, test that your app works before upgrading! The adapter interface has been upgraded (non-default storage and transport layers probably won't work). Also, `.path()` and `.not()` are outside core and now in 'lib/'.")})(t,"./root"),t(function(){var n=t("./root");n.chain.back=function(t,n){var i;if(-1===t||1/0===t)return this._.root;if(1===t)return this._.back||this;var r=this,u=r._;if("string"==typeof t&&(t=t.split(".")),t instanceof Array){var a=0,s=t.length,i=u;for(a;s>a;a++)i=(i||e)[t[a]];if(o!==i)return n?r:i;if(i=u.back)return i.back(t,n)}else if(t instanceof Function){for(var f,i={back:r};(i=i.back)&&(i=i._)&&!(f=t(i,n)););return f}};var o,e={}})(t,"./back"),t(function(){function n(t){var n,o,e,i=this.as,r=i.gun,u=r.back(-1);if(t.gun||(t.gun=r),o=t.get)if(e=o[m])e=u.get(e)._,d(o,k)?d(n=e.put,o=o[k])&&e.on("in",{get:e.get,put:c.state.to(n,o),gun:e.gun}):d(e,"put")&&e.on("in",e);else if(d(o,k)){o=o[k];var a=o?r.get(o)._:i;if(l!==a.put)return void a.on("in",a);if(d(i,"put")){var s,p=i.put;if((s=c.node.soul(p))&&(p=c.val.rel.ify(s)),s=c.val.rel.is(p)){if(!t.gun._)return;return void t.gun._.on("out",{get:e={"#":s,".":o,gun:t.gun},"#":u._.ask(f,e),gun:t.gun})}if(l===p||c.val.is(p)){if(!t.gun._)return;return void t.gun._.on("in",{get:o,gun:t.gun})}}else i.map&&b(i.map,function(t){t.at.on("in",t.at)});if(i.soul){if(!t.gun._)return;return void t.gun._.on("out",{get:e={"#":i.soul,".":o,gun:t.gun},"#":u._.ask(f,e),gun:t.gun})}if(i.get){if(!i.back._)return;return void i.back._.on("out",{get:h({},k,i.get),gun:r})}t=_(t,{get:{}})}else{if(d(i,"put")?i.on("in",i):i.map&&b(i.map,function(t){t.at.on("in",t.at)}),i.ack&&!d(i,"put"))return;if(i.ack=-1,i.soul)return void i.on("out",{get:e={"#":i.soul,gun:i.gun},"#":u._.ask(f,e),gun:i.gun});if(i.get){if(!i.back._)return;return void i.back._.on("out",{get:h({},k,i.get),gun:i.gun})}}i.back._.on("out",t)}function o(t){t=t._||t;{var n,o=this,r=this.as,s=t.gun,f=s._,g=t.put;r.back._||p}if(0>r.ack&&!t.ack&&!c.val.rel.is(g)&&(r.ack=1),r.get&&t.get!==r.get&&(t=_(t,{get:r.get})),r.field&&f!==r&&(t=_(t,{gun:r.gun}),f.ack&&(r.ack=r.ack||f.ack)),l===g){if(o.to.next(t),r.soul)return;return i(r,t,o),r.field&&a(r,t),v(f.echo,r.id),void v(r.map,f.id)}return r.soul?(r.root._.now&&(t=_(t,{put:g=f.put})),o.to.next(t),i(r,t,o),void b(g,u,{at:t,cat:r})):(n=c.val.rel.is(g))?(e(r,t,f,n),o.to.next(t),void i(r,t,o)):c.val.is(g)?(r.field||r.soul?a(r,t):(f.field||f.soul)&&((f.echo||(f.echo={}))[r.id]=r,(r.map||(r.map={}))[f.id]=r.map[f.id]||{at:f}),o.to.next(t),void i(r,t,o)):(r.field&&f!==r&&d(f,"put")&&(r.put=f.put),(n=c.node.soul(g))&&f.field&&(f.put=r.root.get(n)._.put),o.to.next(t),i(r,t,o),e(r,t,f,n),void b(g,u,{at:t,cat:r}))}function e(t,n,o,i){if(i&&y!==t.get){var r=t.root.get(i)._;t.field?o=r:o.field&&e(o,n,o,i),o!==t&&((o.echo||(o.echo={}))[t.id]=t,t.field&&!(t.map||p)[o.id]&&a(t,n),r=(t.map||(t.map={}))[o.id]=t.map[o.id]||{at:o},i!==r.rel&&s(t,r.rel=i))}}function i(t,n,o){t.echo&&(t.field&&(n=_(n,{event:o})),b(t.echo,r,n))}function r(t){t.on("in",this)}function u(t,n){var o,e,i,r=this.cat,u=r.next||p,a=this.at;(y!==n||u[n])&&(o=u[n])&&(i=o._,i.field?(t&&t[m]&&c.val.rel.is(t)===c.node.soul(i.put)||(i.put=t),e=o):e=a.gun.get(n),i.on("in",{put:t,get:n,gun:e,via:a}))}function a(t){if(t.field||t.soul){var n=t.map;t.map=null,null!==n&&(l!==n||t.put===l)&&(b(n,function(n){(n=n.at)&&v(n.echo,t.id)}),b(t.next,function(t,n){var o=t._;o.put=l,o.ack&&(o.ack=-1),o.on("in",{get:n,gun:t,put:l})}))}}function s(t,n){var o=t.root.get(n)._;return t.ack?(o.ack=o.ack||-1,void o.on("out",{get:o={"#":n,gun:o.gun},"#":t.root._.ask(f,o)})):void b(t.next,function(o,e){o._.on("out",{get:o={"#":n,".":e,gun:o},"#":t.root._.ask(f,o)})})}function f(t){var n=this.as,o=n.gun._;if(!t.put||n["."]&&!d(t.put[n["#"]],o.get)){if(o.put!==l)return;return void o.on("in",{get:o.get,put:o.put=l,gun:o.gun})}t.gun=o.root,c.on.put(t)}var c=t("./root");c.chain.chain=function(){var t,e=this._,i=new this.constructor(this),r=i._;return r.root=t=e.root,r.id=++t._.once,r.back=this,r.on=c.on,r.on("in",o,r),r.on("out",n,r),i},c.chain.chain.input=o;var l,p={},g=c.obj,d=g.has,h=g.put,v=g.del,_=g.to,b=g.map,m=c._.soul,k=c._.field,y=c.node._})(t,"./chain"),t(function(){function n(t,n){var o=n._,e=o.next,i=n.chain(),r=i._;return e||(e=o.next={}),e[r.get=t]=i,o.root===n?r.soul=t:(o.soul||o.field)&&(r.field=t),i}function o(t){var n,o=this,e=o.as,r=t.gun,a=r._,f=t.put;i===f&&(f=a.put),(n=f)&&n[s._]&&(n=s.is(n))&&(n=a.root.get(n)._,i!==n.put&&(t=u(t,{put:n.put}))),e.use(t,t.event||o),o.to.next(t)}var e=t("./root");e.chain.get=function(t,i,r){if("string"!=typeof t){if(t instanceof Function){var u=this,s=u._;return r=i||{},r.use=t,r.out=r.out||{cap:1},r.out.get=r.out.get||{},"_"!=s.get&&(s.root._.now=!0),s.on("in",o,r),s.on("out",r.out),s.root._.now=!1,u}return a(t)?this.get(""+t,i,r):((r=this.chain())._.err={err:e.log("Invalid get request!",t)},i&&i.call(r,r._.err),r)}var u,c,l=this,p=l._,g=p.next||f;return(u=g[t])||(u=n(t,l)),(c=p.stun)&&(u._.stun=u._.stun||c),i&&i instanceof Function&&u.get(i,r),u};var i,r=e.obj,u=(r.has,e.obj.to),a=e.num.is,s=e.val.rel,f=(e.node._,{})})(t,"./get"),t(function(){function n(t){t.batch=e;var n=t.opt||{},o=t.env=s.state.map(r,n.state);return o.soul=t.soul,t.graph=s.graph.ify(t.data,o,t),o.err?((t.ack||h).call(t,t.out={err:s.log(o.err)}),void(t.res&&t.res())):void t.batch()}function e(){var t=this;t.graph&&!g(t.stun,i)&&((t.res||v)(function(){var n=t.gun.back(-1)._,o=n.ask(function(n){this.off(),t.ack&&t.ack(n,this)},t.opt);t.ref._.on("out",{gun:t.ref,put:t.out=t.env.graph,opt:t.opt,"#":o})},t),t.res&&t.res())}function i(t){return t?!0:void 0}function r(t,n,o,e){var i=this;!n&&e.path.length&&(i.res||v)(function(){var t=e.path,n=i.ref,o=(i.opt,0),r=t.length;for(o;r>o;o++)n=n.get(t[o]);if(i.not||s.node.soul(e.obj)){var a=s.node.soul(e.obj)||((i.opt||{}).uuid||i.gun.back("opt.uuid")||s.text.random)();return n.back(-1).get(a),void e.soul(a)}(i.stun=i.stun||{})[t]=!0,n.get("_").get(u,{as:{at:e,as:i}})},{as:i,at:e})}function u(t,n){var o=this.as,e=o.at;if(o=o.as,t.gun&&t.gun._.back){n.off(),t=t.gun._.back._;var i=s.node.soul(e.obj)||s.node.soul(t.put)||s.val.rel.is(t.put)||((o.opt||{}).uuid||o.gun.back("opt.uuid")||s.text.random)();t.gun.back(-1).get(i),e.soul(i),o.stun[e.path]=!1,o.batch()}}function a(t,n){var e=this.as;if(t.gun&&t.gun._){if(t.err)return void o.log("Please report this as an issue! Put.any.err");var i,r=t.gun._.back._,u=r.put,a=e.opt||{};if(n.off(),e.ref!==e.gun){if(i=e.gun._.get||r.get,!i)return void o.log("Please report this as an issue! Put.no.get");e.data=p({},i,e.data),i=null}if(f===u){if(!r.get)return;r.soul||(i=r.gun.back(function(t){return t.soul?t.soul:void(e.data=p({},t.get,e.data))})),i=i||r.get,r=r.root.get(i)._,e.not=e.soul=i,u=e.data}e.not||(e.soul=s.node.soul(u))||(e.soul=e.path&&l(e.data)?(a.uuid||r.root._.opt.uuid||s.text.random)():t.soul||r.soul||(a.uuid||r.root._.opt.uuid||s.text.random)()),e.ref.put(e.data,e.soul,e)}}var s=t("./root");s.chain.put=function(t,o,e){var i,r=this,u=r._,f=u.root;return e=e||{},e.data=t,e.gun=e.gun||r,"string"==typeof o?e.soul=o:e.ack=o,u.soul&&(e.soul=u.soul),e.soul||f===r?l(e.data)?(e.gun=r=f.get(e.soul=e.soul||(e.not=s.node.soul(e.data)||(f._.opt.uuid||s.text.random)())),e.ref=e.gun,n(e),r):((e.ack||h).call(e,e.out={err:s.log("Data saved to the root level of the graph must be a node (an object), not a",typeof e.data,'of "'+e.data+'"!')}),e.res&&e.res(),r):s.is(t)?(t.get(function(t,n){n.off();var i=s.node.soul(t.put);return i?void r.put(s.val.rel.ify(i),o,e):void s.log("The reference you are saving is a",typeof t.put,'"'+e.put+'", not a node (object)!')}),r):(e.ref=e.ref||f===(i=u.back)?r:i,e.ref._.soul&&s.val.is(e.data)&&u.get?(e.data=p({},u.get,e.data),e.ref.put(e.data,e.soul,e),r):(e.ref.get("_").get(a,{as:e}),e.out||(e.res=e.res||h,e.gun._.stun=e.ref._.stun),r))};var f,c=s.obj,l=c.is,p=c.put,g=c.map,d={},h=function(){},v=function(t,n){t.call(n||d)}})(t,"./put"),t(function(n){var o=t("./root");t("./opt"),t("./chain"),t("./back"),t("./put"),t("./get"),n.exports=o})(t,"./index"),t(function(){function n(t,n){var o,r=this,u=t.gun,s=u._,f=s.put||t.put,o=r.last,c=s.id+t.get;if(i!==f){if(f&&f[a._]&&(o=a.is(f))){if(o=s.root.get(o)._,i===o.put)return;f=o.put}r.change&&(f=t.put),(o.put!==f||o.get!==c||e.node.soul(f))&&(o.put=f,o.get=c,s.last=f,r.as?r.ok.call(r.as,t,n):r.ok.call(u,f,t.get,t,n))}}function o(t,n){var e,r=this.as,u=r.cat,s=t.gun,f=s._,c=f.put||t.put;if(c&&c[a._]&&(e=a.is(c))){if(e=u.root.get(e)._,i===e.put)return;c=e.put}if(n.wait&&clearTimeout(n.wait),!r.async)return void(n.wait=setTimeout(function(){o.call({as:r},t,n,n.wait||1)},r.wait||99));if(u.field||u.soul){if(n.off())return}else{if((r.seen=r.seen||{})[f.id])return;r.seen[f.id]=!0}r.ok.call(t.gun||r.gun,c,t.get)}var e=t("./index");e.chain.on=function(t,o,e,i){var r,u,a=this,f=a._;if("string"==typeof t)return o?(r=f.on(t,o,e||f,i),e&&e.gun&&(e.subs||(e.subs=[])).push(r),u=function(){r&&r.off&&r.off(),u.off()},u.off=a.off.bind(a)||s,a.off=u,a):f.on(t);var c=o;return c=!0===c?{change:!0}:c||{},c.ok=t,c.last={},a.get(n,c),a},e.chain.val=function(t,n){var r=this,u=r._,a=u.put;if(0=(n.batch||1e3)?s():void(e||(e=setTimeout(s,n.wait||1)))}),t.on("get",function(t){this.to.next(t);var n,o,e,i=t.gun,r=t.get;if(r&&(n=r[Gun._.soul])){var a=r["."];o=u[n]||e,o&&a&&(o=Gun.state.to(o,a)),(o||Gun.obj.empty(i.back("opt.peers")))&&i.on("in",{"@":t["#"],put:Gun.graph.node(o),how:"lS"})}});var a=function(t,n,o,e){u[e]=Gun.state.to(o,n,u[e])},s=function(){var a;r=0,clearTimeout(e),e=!1;var s=i;i={};try{o.setItem(n.file,JSON.stringify(u))}catch(f){Gun.log(a=f||"localStorage failure")}(a||Gun.obj.empty(n.peers))&&Gun.obj.map(s,function(n,o){t.on("in",{"@":o,err:a,ok:0})})}}})}})(t,"./adapters/localStorage"),t(function(){var n,o=t("./index");if("undefined"!=typeof window){n=window.WebSocket||window.webkitWebSocket||window.mozWebSocket,o.on("opt",function(t){function i(t){var n=this,o=c,e=t.wire||u(t,n);return e?e.readyState===e.OPEN?void e.send(o):void(t.queue=t.queue||[]).push(o):void 0}function r(t,n,o){if(o&&t){try{t=JSON.parse(t.data||t)}catch(i){}if(t instanceof Array)for(var u,a=0;u=t[a++];)r(u,n,o);else 1==f.who&&(t.ws=e),o.on("in",t)}}function u(t,e){if(t&&t.url){var u=t.url.replace("http","ws"),s=t.wire=new n(u);return s.onclose=function(){a(t,e)},s.onerror=function(n){a(t,e),n&&"ECONNREFUSED"===n.code},s.onopen=function(){var n=t.queue;t.queue=[],o.obj.map(n,function(n){c=n,i.call(e,t)})},s.onmessage=function(n){r(n,t,e)},s}}function a(t,n){clearTimeout(t.defer),t.defer=setTimeout(function(){u(t,n)},2e3)}this.to.next(t);var s=t.opt;if(!t.once&&!1!==s.WebSocket){var f=s.ws||(s.ws={});if(f.who=0,o.obj.map(s.peers,function(){++f.who}),!t.once){var c;t.on("out",function(n){if(this.to.next(n),!n.ws||1!=f.who){if(c=JSON.stringify(n),f.drain)return void f.drain.push(c);f.drain=[],setTimeout(function(){if(f.drain){var n=f.drain;f.drain=null,n.length&&(c=JSON.stringify(n),o.obj.map(s.peers,i,t))}},s.wait||1),o.obj.map(s.peers,i,t)}})}}});var e=function(){}}})(t,"./adapters/websocket")}(); \ No newline at end of file diff --git a/lib/file.js b/lib/file.js index ed57ebaa..dbe41a8e 100644 --- a/lib/file.js +++ b/lib/file.js @@ -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() } diff --git a/lib/memdisk.js b/lib/memdisk.js index 795e3200..03e8fb6d 100644 --- a/lib/memdisk.js +++ b/lib/memdisk.js @@ -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); }); diff --git a/lib/radisk.js b/lib/radisk.js index 2e0fb512..7ef2f176 100644 --- a/lib/radisk.js +++ b/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; \ No newline at end of file diff --git a/lib/radisk_.js b/lib/radisk_.js new file mode 100644 index 00000000..6f457ee6 --- /dev/null +++ b/lib/radisk_.js @@ -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; \ No newline at end of file diff --git a/lib/radix.js b/lib/radix.js index 1dcd10e4..bc5a9b89 100644 --- a/lib/radix.js +++ b/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; \ No newline at end of file + Object.keys = Object.keys || function(o){ return map(o, function(v,k,t){t(k)}) } + + module.exports = Radix; +}()); \ No newline at end of file diff --git a/lib/rs3.js b/lib/rs3.js new file mode 100644 index 00000000..1e2ff958 --- /dev/null +++ b/lib/rs3.js @@ -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; \ No newline at end of file diff --git a/lib/store.js b/lib/store.js new file mode 100644 index 00000000..0132b19d --- /dev/null +++ b/lib/store.js @@ -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'); +}()); \ No newline at end of file diff --git a/package.json b/package.json index bc4ff99e..697e0e67 100644 --- a/package.json +++ b/package.json @@ -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" }, diff --git a/src/adapters/localStorage.js b/src/adapters/localStorage.js index f4db3b1e..e7c3e38b 100644 --- a/src/adapters/localStorage.js +++ b/src/adapters/localStorage.js @@ -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', { diff --git a/src/dup.js b/src/dup.js index 1103159d..67f79250 100644 --- a/src/dup.js +++ b/src/dup.js @@ -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; } diff --git a/src/on.js b/src/on.js index 3ae85e76..1716ec1f 100644 --- a/src/on.js +++ b/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; diff --git a/src/onto.js b/src/onto.js index a21faa05..dca13c39 100644 --- a/src/onto.js +++ b/src/onto.js @@ -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, diff --git a/src/put.js b/src/put.js index d6549535..1177a353 100644 --- a/src/put.js +++ b/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() } diff --git a/src/root.js b/src/root.js index 824df819..5284e473 100644 --- a/src/root.js +++ b/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; } }()); diff --git a/test/common.js b/test/common.js index 022d5058..af6640a5 100644 --- a/test/common.js +++ b/test/common.js @@ -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); } diff --git a/test/ptsd/radisk.js b/test/ptsd/radisk.js index d4a6ad8d..6860e402 100644 --- a/test/ptsd/radisk.js +++ b/test/ptsd/radisk.js @@ -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); } \ No newline at end of file From f19aeabb78caf7cc1d4f1fa3c66f857e44d9151e Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Mon, 16 Oct 2017 01:16:08 -0700 Subject: [PATCH 2/2] Critical fix for async/sync! --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b536f627..b5a9e60a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,5 @@ language: node_js node_js: - - 0.11 - - 0.12 - 4.0 - 4.2 - 5.0