From 5b1a2d1b28b9cf678fcf2e8f6fc4b663a26a7cbb Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Mon, 20 May 2019 04:40:26 -0700 Subject: [PATCH 1/6] peer duration time --- examples/stats.html | 24 +++++++++++++----------- gun.js | 41 +++++++++++++++++++++++------------------ gun.min.js | 2 +- lib/stats.js | 1 + package.json | 2 +- 5 files changed, 39 insertions(+), 31 deletions(-) diff --git a/examples/stats.html b/examples/stats.html index b400bdd9..aa2c4576 100644 --- a/examples/stats.html +++ b/examples/stats.html @@ -15,31 +15,32 @@ } .ct-series-a .ct-line, .ct-series-a .ct-point { - stroke: blue; + str-oke: blue !important; } .ct-series-b .ct-line, .ct-series-b .ct-point { - stroke: green; + stroke: green !important; } + .tall { height: 10em; } -
0 peers 0 nodes 0 hours
+
0 peers 0 min 0 nodes 0 hours
-
+
-
-
+
+
-
+
@@ -52,6 +53,7 @@ setInterval(function(){ }, 1000 * 15); stats.show = function(){ $.getJSON(url.value||(location.origin+'/gun/stats.radata'), function(data){ console.log(data); $('#peers').text(data.peers.count); + $('#time').text((data.peers.time / 1000 / 60).toFixed(0)); $('#nodes').text(data.node.count); $('#hours').text((data.up.time / 60 / 60).toFixed(0)); @@ -76,8 +78,8 @@ stats.show = function(){ $.getJSON(url.value||(location.origin+'/gun/stats.radat // A labels array that can contain any sort of values labels: ['-1min', '-45s', '-30s', '-15s', '0'], // Our series array that contains series objects or in this case series data arrays - series: [stats.din, stats.dout] - }, {fullWidth: true, low: 0, axisY: { + series: [stats.dout, stats.din] + }, {fullWidth: true, axisY: { labelInterpolationFnc: function(v) { return v+'msgs' } }}); @@ -87,8 +89,8 @@ stats.show = function(){ $.getJSON(url.value||(location.origin+'/gun/stats.radat // A labels array that can contain any sort of values labels: ['-1min', '-45s', '-30s', '-15s', '0'], // Our series array that contains series objects or in this case series data arrays - series: [stats.dind, stats.doutd] - }, {fullWidth: true, low: 0, axisY: { + series: [stats.doutd, stats.dind] + }, {fullWidth: true, axisY: { labelInterpolationFnc: function(v) { return v+'MB' } }}); diff --git a/gun.js b/gun.js index 2d68c87d..4ed6ade0 100644 --- a/gun.js +++ b/gun.js @@ -2017,21 +2017,14 @@ ;(function(){ var message; function each(peer){ mesh.say(message, peer) } - mesh.say = function(msg, peer, o){ + mesh.say = function(msg, peer, o){ var tmp; /* TODO: Plenty of performance optimizations that can be made just based off of ordering, and reducing function calls for cached writes. */ - if(!peer){ message = msg; - Type.obj.map(opt.peers, each); - return; - } - var tmp, wire = peer.wire || ((opt.wire) && opt.wire(peer)), msh, raw;// || open(peer, ctx); // TODO: Reopen! - if(!wire){ return } - msh = (msg._) || empty; - if(peer === msh.via){ return } - if(!(raw = msh.raw)){ raw = mesh.raw(msg) } + var meta = (msg._) || empty, raw; + if(!(raw = meta.raw)){ raw = mesh.raw(msg) } if((tmp = msg['@']) && (tmp = ctx.dup.s[tmp]) && (tmp = tmp.it)){ @@ -2039,7 +2032,14 @@ return; // TODO: this still needs to be tested in the browser! } } - if((tmp = msh.to) && (tmp[peer.url] || tmp[peer.id]) && !o){ return } // TODO: still needs to be tested + if(!peer){ message = msg; + Type.obj.map(opt.peers, each); + return; + } + var wire = peer.wire || ((opt.wire) && opt.wire(peer));// || open(peer, ctx); // TODO: Reopen! + if(!wire){ return } + if(peer === meta.via){ return } + if((tmp = meta.to) && (tmp[peer.url] || tmp[peer.id]) && !o){ return } // TODO: still needs to be tested if(peer.batch){ peer.tail = (peer.tail || 0) + raw.length; if(peer.tail <= opt.pack){ @@ -2082,8 +2082,8 @@ mesh.raw = function(msg){ if(!msg){ return '' } - var dup = ctx.dup, msh = (msg._) || {}, put, hash, tmp; - if(tmp = msh.raw){ return tmp } + var dup = ctx.dup, meta = (msg._) || {}, put, hash, tmp; + if(tmp = meta.raw){ return tmp } if(typeof msg === 'string'){ return msg } if(msg['@'] && (tmp = msg.put)){ if(!(hash = msg['##'])){ @@ -2095,17 +2095,19 @@ msg['#'] = hash || msg['#']; if(put){ (msg = Type.obj.to(msg)).put = _ } } - var i = 0, to = []; Type.obj.map(opt.peers, function(p){ - to.push(p.url || p.id); if(++i > 9){ return true } // limit server, fast fix, improve later! - }); msg['><'] = to.join(); + if(!msg.dam){ + var i = 0, to = []; Type.obj.map(opt.peers, function(p){ + to.push(p.url || p.id); if(++i > 9){ return true } // limit server, fast fix, improve later! + }); msg['><'] = to.join(); + } var raw = $(msg); if(u !== put){ tmp = raw.indexOf(_, raw.indexOf('put')); raw = raw.slice(0, tmp-1) + put + raw.slice(tmp + _.length + 1); //raw = raw.replace('"'+ _ +'"', put); // https://github.com/amark/gun/wiki/@$$ Heisenbug } - if(msh){ - msh.raw = raw; + if(meta){ + meta.raw = raw; } return raw; } @@ -2135,6 +2137,7 @@ tmp = peer.id = tmp.pid = peer.id || Type.text.random(9); mesh.say({dam: '?'}, opt.peers[tmp] = peer); } + peer.met = peer.met || +(new Date); if(!tmp.hied){ ctx.on(tmp.hied = 'hi', peer) } // @rogowski I need this here by default for now to fix go1dfish's bug tmp = peer.queue; peer.queue = []; @@ -2145,6 +2148,8 @@ mesh.bye = function(peer){ Type.obj.del(opt.peers, peer.id); // assume if peer.url then reconnect ctx.on('bye', peer); + var tmp = +(new Date); tmp = (tmp - (peer.met||tmp)); + mesh.bye.time = ((mesh.bye.time || tmp) + tmp) / 2; } mesh.hear['!'] = function(msg, peer){ opt.log('Error:', msg.err) } mesh.hear['?'] = function(msg, peer){ diff --git a/gun.min.js b/gun.min.js index 14a5c717..4c75d163 100644 --- a/gun.min.js +++ b/gun.min.js @@ -1 +1 @@ -!function(){var t;"undefined"!=typeof window&&(t=window),"undefined"!=typeof global&&(t=global);var b=(t=t||{}).console||{log:function(){}};function _(o,t){return t?require(o):o.slice?_[i(o)]:function(t,n){o(t={exports:{}}),_[i(n)]=t.exports};function i(t){return t.split("/").slice(-1).toString().replace(".js","")}}if("undefined"!=typeof module)var f=module;_(function(t){var p={fn:{is:function(t){return!!t&&"function"==typeof t}}};p.bi={is:function(t){return t instanceof Boolean||"boolean"==typeof t}},p.num={is:function(t){return!d(t)&&(0<=t-parseFloat(t)+1||1/0===t||-1/0===t)}},p.text={is:function(t){return"string"==typeof t}},p.text.ify=function(t){return p.text.is(t)?t:"undefined"!=typeof JSON?JSON.stringify(t):t&&t.toString?t.toString():t},p.text.random=function(t,n){var o="";for(t=t||24,n=n||"0123456789ABCDEFGHIJKLMNOPQRSTUVWXZabcdefghijklmnopqrstuvwxyz";0")){if(!(n>t[">"]))return!1;o=!0}if(p.obj.has(t,"<")){if(!(n",s.drift=0,s.is=function(t,n,o){var i=n&&t&&t[m]&&t[m][s._]||o;if(i)return g(i=i[n])?i:-1/0},s.lex=function(){return s().toString(36).replace(".","")},s.ify=function(t,n,o,i,e){if(!t||!t[m]){if(!e)return;t=a.soul.ify(t,e)}var r=c(t[m],s._);return void 0!==n&&n!==m&&(g(o)&&(r[n]=o),void 0!==i&&(t[n]=i)),t},s.to=function(t,n,o){var i=(t||{})[n];return p(i)&&(i=d(i)),s.ify(o,n,s.is(t,n),i,a.soul(t))},function(){function u(t,n){m!==n&&s.ify(this.o,n,this.s)}s.map=function(e,r,a){var t=p(t=e||r)?t:null;return e=v(e=e||r)?e:null,t&&!e?(r=g(r)?r:s(),t[m]=t[m]||{},h(t,u,{o:t,s:r}),t):(a=a||p(r)?r:void 0,r=g(r)?r:s(),function(t,n,o,i){if(!e)return u.call({o:o,s:r},t,n),t;e.call(a||this||{},t,n,o,i),l(o,n)&&void 0===o[n]||u.call({o:o,s:r},t,n)})}}();var f=n.obj,c=f.as,l=f.has,p=f.is,h=f.map,d=f.copy,g=n.num.is,v=n.fn.is,m=a._;t.exports=s})(_,"./state"),_(function(t){var a=_("./type"),f=_("./val"),c=_("./node"),r={};!function(){function e(t,n){if(!t||n!==c.soul(t)||!c.is(t,this.fn,this.as))return!0;this.cb&&(o.n=t,o.as=this.as,this.cb.call(o.as,t,n,o))}function o(t){t&&c.is(o.n,t,o.as)}r.is=function(t,n,o,i){return!(!t||!l(t)||u(t))&&!s(t,e,{cb:n,fn:o,as:i})}}(),function(){function u(t,n){var o;return(o=function(t,n){var o,i=t.seen,e=i.length;for(;e--;)if(o=i[e],n.obj===o.obj)return o;i.push(n)}(t,n))?o:(n.env=t,n.soul=e,c.ify(n.obj,i,n)&&(n.link=n.link||f.link.ify(c.soul(n.node)),n.obj!==t.shell&&(t.graph[f.link.is(n.link)]=n.node)),n)}function i(t,n,o){var i,e,r=this,a=r.env;if(c._===n&&h(t,f.link._))return o._;if(i=s(t,n,o,r,a)){if(n||(r.node=r.node||o||{},h(t,c._)&&c.soul(t)&&(r.node._=d(t._)),r.node=c.soul.ify(r.node,f.link.is(r.link)),r.link=r.link||f.link.ify(c.soul(r.node))),(e=a.map)&&(e.call(a.as||{},t,n,o,r),h(o,n))){if(void 0===(t=o[n]))return void p(o,n);if(!(i=s(t,n,o,r,a)))return}if(!n)return r.node;if(!0===i)return t;if((e=u(a,{obj:t,path:r.path.concat(n)})).node)return e.link}}function e(t){var n=this,o=f.link.is(n.link),i=n.env.graph;n.link=n.link||f.link.ify(t),n.link[f.link._]=t,n.node&&n.node[c._]&&(n.node[c._][f.link._]=t),h(i,o)&&(i[t]=i[o],p(i,o))}function s(t,n,o,i,e){var r;return!!f.is(t)||(l(t)?1:(r=e.invalid)?s(t=r.call(e.as||{},t,n,o),n,o,i,e):(e.err="Invalid value at '"+i.path.concat(n).join(".")+"'!",void(a.list.is(t)&&(e.err+=" Use `.set(item)` instead of an Array."))))}r.ify=function(t,n,o){var i={path:[],obj:t};return n?"string"==typeof n?n={soul:n}:n instanceof Function&&(n.map=n):n={},n.soul&&(i.link=f.link.ify(n.soul)),n.shell=(o||{}).shell,n.graph=n.graph||{},n.seen=n.seen||[],n.as=n.as||o,u(n,i),n.root=i.node,n.graph}}(),r.node=function(t){var n=c.soul(t);if(n)return o({},n,t)},function(){function e(t,n){var o,i;if(c._!==n)(o=f.link.is(t))?(i=this.opt.seen[o])?this.obj[n]=i:this.obj[n]=this.opt.seen[o]=r.to(this.graph,o,this.opt):this.obj[n]=t;else{if(u(t,f.link._))return;this.obj[n]=d(t)}}r.to=function(t,n,o){if(t){var i={};return o=o||{seen:{}},s(t[n],e,{obj:i,graph:t,opt:o}),i}}}();a.fn.is;var n=a.obj,l=n.is,p=n.del,h=n.has,u=n.empty,o=n.put,s=n.map,d=n.copy;t.exports=r})(_,"./graph"),_(function(t){_("./onto"),t.exports=function(t,n){if(this.on){if(!(t instanceof Function)){if(!t||!n)return;var o=t["#"]||t,i=(this.tag||empty)[o];if(!i)return;return i=this.on(o,n),clearTimeout(i.err),!0}o=n&&n["#"]||Math.random().toString(36).slice(2);if(!t)return o;var e=this.on(o,t,n);return e.err=e.err||setTimeout(function(){e.next({err:"Error: No ACK received yet.",lack:!0}),e.off()},(this.opt||{}).lack||9e3),o}}})(_,"./ask"),_(function(t){var r=_("./type");var a=r.time.is;t.exports=function(i){var e={s:{}};return i=i||{max:1e3,age:9e3},e.check=function(t){var n;return!!(n=e.s[t])&&(n.pass?n.pass=!1:e.track(t))},e.track=function(t,n){var o=e.s[t]||(e.s[t]={});return o.was=a(),n&&(o.pass=!0),e.to||(e.to=setTimeout(function(){var o=a();r.obj.map(e.s,function(t,n){t&&i.age>o-t.was||r.obj.del(e.s,n)}),e.to=null},i.age+9)),o},e}})(_,"./dup"),_(function(t){function c(t){return t instanceof c?(this._={gun:this,$:this}).$:this instanceof c?c.create(this._={gun:this,$:this,opt:t}):new c(t)}c.is=function(t){return t instanceof c||t&&t._&&t===t._.$||!1},c.version=.9,(c.chain=c.prototype).toJSON=function(){};var n=_("./type");n.obj.to(n,c),c.HAM=_("./HAM"),c.val=_("./val"),c.node=_("./node"),c.state=_("./state"),c.graph=_("./graph"),c.on=_("./onto"),c.ask=_("./ask"),c.dup=_("./dup"),function(){function a(t){var n,o,i=this.as,e=i.at||i,r=e.$;(o=t["#"])||(o=t["#"]=u(9)),(n=e.dup).check(o)?i.out===t.out&&(t.out=void 0,this.to.next(t)):(n.track(o),e.ask(t["@"],t)||(t.get&&c.on.get(t,r),t.put&&c.on.put(t,r)),this.to.next(t),i.out||(t.out=a,e.on("out",t)))}c.create=function(t){t.root=t.root||t,t.graph=t.graph||{},t.on=t.on||c.on,t.ask=t.ask||c.ask,t.dup=t.dup||c.dup();var n=t.$.opt(t.opt);return t.once||(t.on("in",a,t),t.on("out",a,{at:t,out:a}),c.on("create",t),t.on("create",t)),t.once=1,n}}(),function(){function e(t,n,o,i){var e=this,r=c.state.is(o,n);if(!r)return e.err="Error: No state on '"+n+"' in node '"+i+"'!";var a=e.graph[i]||v,u=c.state.is(a,n,!0),s=a[n],f=c.HAM(e.machine,r,u,t,s);f.incoming?(e.put[i]=c.state.to(o,n,e.put[i]),(e.diff||(e.diff={}))[i]=c.state.to(o,n,e.diff[i]),e.souls[i]=!0):f.defer&&(e.defer=r<(e.defer||1/0)?r:e.defer)}function r(t,n){var o=this,i=o.$._,e=(i.next||v)[n];if(!e){if(!(i.opt||v).super)return void(o.souls[n]=!1);e=o.$.get(n)._}var r=o.map[n]={put:t,get:n,$:e.$},a={ctx:o,msg:r};o.async=!!i.tag.node,o.ack&&(r["@"]=o.ack),h(t,u,a),o.async&&(o.and||i.on("node",function(t){this.to.next(t),t===o.map[t.get]&&(o.souls[t.get]=!1,h(t.put,s,t),h(o.souls,function(t){if(t)return t})||o.c||(o.c=1,this.off(),h(o.map,f,o)))}),o.and=!0,i.on("node",r))}function u(t,n){var o=this.ctx,i=o.graph,e=this.msg,r=e.get,a=e.put,u=e.$._;i[r]=c.state.to(a,n,i[r]),o.async||(u.put=c.state.to(a,n,u.put))}function s(t,n){var o=this.put,i=this.$._;i.put=c.state.to(o,n,i.put)}function f(t,n){t.$&&(this.cat.stop=this.stop,t.$._.on("in",t),this.cat.stop=null)}c.on.put=function(t,n){var o=n._,i={$:n,graph:o.graph,put:{},map:{},souls:{},machine:c.state(),ack:t["@"],cat:o,stop:{}};if(c.graph.is(t.put,null,e,i)||(i.err="Error: Invalid graph!"),i.err)return o.on("in",{"@":t["#"],err:c.log(i.err)});h(i.put,r,i),i.async||h(i.map,f,i),void 0!==i.defer&&setTimeout(function(){c.on.put(t,n)},i.defer-i.machine),i.diff&&o.on("put",p(t,{put:i.diff}))},c.on.get=function(t,n){var o=n._,i=t.get,e=i[d],r=o.graph[e],a=i[g],u=(o.next||(o.next={}))[e];if(!r)return o.on("get",t);if(a){if("string"!=typeof a||!l(r,a))return o.on("get",t);r=c.state.to(r,a)}else r=c.obj.copy(r);r=c.graph.node(r),(u||v).ack,o.on("in",{"@":t["#"],how:"mem",put:r,$:n}),o.on("get",t)}}(),c.chain.opt=function(t){t=t||{};var n=this._,o=t.peers||t;return a(t)||(t={}),a(n.opt)||(n.opt=t),e(o)&&(o=[o]),i(o)&&(o=h(o,function(t,n,o){o(t,{url:t})}),a(n.opt.peers)||(n.opt.peers={}),n.opt.peers=p(o,n.opt.peers)),n.opt.peers=n.opt.peers||{},p(t,n.opt),c.on("opt",n),n.opt.uuid=n.opt.uuid||function(){return s()+u(12)},this};var i=c.list.is,o=c.text,e=o.is,u=o.random,r=c.obj,a=r.is,l=r.has,p=r.to,h=r.map,s=(r.copy,c.state.lex),d=c.val.link._,g=".",v=(c.node._,c.val.link.is,{});b.debug=function(t,n){return b.debug.i&&t===b.debug.i&&b.debug.i++&&(b.log.apply(b,arguments)||n)},(c.log=function(){return!c.log.off&&b.log.apply(b,arguments),[].slice.call(arguments).join(" ")}).once=function(t,n,o){return(o=c.log.once)[t]=o[t]||0,o[t]++||c.log(n)},c.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=window.Gun=c).window=window);try{void 0!==f&&(f.exports=c)}catch(t){}t.exports=c})(_,"./root"),_(function(t){var u=_("./root");u.chain.back=function(t,n){if(-1===(t=t||1)||1/0===t)return this._.root.$;if(1===t)return(this._.back||this._).$;var o=this._;if("string"==typeof t&&(t=t.split(".")),t instanceof Array){for(var i=0,e=t.length,r=o;i .once, apologies unexpected."),this.once(t,n)},f.chain.once=function(t,n){var o=this,i=o._,e=i.put;if(0=(u.batch||1e3))return f();i||(i=setTimeout(f,u.wait||1))}),a.on("get",function(n){this.to.next(n);var o,i,e,r=n.get;function t(){if(r&&(o=r["#"])){var t=r["."];(i=s[o]||e)&&t&&(i=Gun.state.to(i,t)),(i||Gun.obj.empty(u.peers))&&a.on("in",{"@":n["#"],put:Gun.graph.node(i),how:"lS",lS:n.$||a.$})}}Gun.debug?setTimeout(t,1):t()});var n=function(t,n,o,i){s[i]=Gun.state.to(o,n,s[i])},f=function(t){var o;r=0,clearTimeout(i),i=!1;var n=e;e={},t&&(s=t);try{p.setItem(u.prefix,JSON.stringify(s))}catch(t){Gun.log(o=(t||"localStorage failure")+" Consider using GUN's IndexedDB plugin for RAD for more storage space, temporary example at https://github.com/amark/gun/blob/master/test/tmp/indexedDB.html ."),a.on("localStorage:error",{err:o,file:u.prefix,flush:s,retry:f})}(o||Gun.obj.empty(u.peers))&&Gun.obj.map(n,function(t,n){a.on("in",{"@":n,err:o,ok:0})})}}})}})(_,"./adapters/localStorage"),_(function(t){var g=_("../type");function o(p){var h=function(){},d=p.opt||{};return d.log=d.log||b.log,d.gap=d.gap||d.wait||1,d.pack=d.pack||.3*(d.memory?1e3*d.memory*1e3:1399e6),h.out=function(t){var n;if(this.to&&this.to.next(t),(n=t["@"])&&(n=p.dup.s[n])&&(n=n.it)&&n._)return h.say(t,n._.via,1),void(n["##"]=t["##"]);h.say(t)},p.on("create",function(t){t.opt.pid=t.opt.pid||g.text.random(9),this.to.next(t),p.on("out",h.out)}),h.hear=function(t,n){if(t){var o,i,e,r=p.dup,a=t[0];if(d.pack<=t.length)return h.say({dam:"!",err:"Message too big!"},n);try{e=JSON.parse(t)}catch(t){d.log("DAM JSON parse error",t)}if("{"===a){if(!e)return;if(r.check(o=e["#"]))return;if((a=(r.track(o,!0).it=e)["@"])&&e.put&&(a+=i=e["##"]||(e["##"]=h.hash(e)))!=o){if(r.check(a))return;(a=r.s)[i]=a[o]}return(e._=function(){}).via=n,(a=e["><"])&&(e._.to=g.obj.map(a.split(","),function(t,n,o){o(t,!0)})),e.dam?void((a=h.hear[e.dam])&&a(e,n,p)):void p.on("in",e)}if("["!==a);else{if(!e)return;for(var u,s=0;u=e[s++];)h.hear(u,n)}}},function(){function a(t){var n=t.batch;if(n&&(t.batch=t.tail=null,n.length))try{u(JSON.stringify(n),t)}catch(t){d.log("DAM JSON stringify error",t)}}function u(n,o){var t=o.wire;try{t.send?t.send(n):o.say&&o.say(n)}catch(t){(o.queue=o.queue||[]).push(n)}}h.say=function(n,t,o){var i,e,r;if(t){if((t.wire||d.wire&&d.wire(t))&&(e=n._||s,t!==e.via&&((r=e.raw)||(r=h.raw(n)),!((i=n["@"])&&(i=p.dup.s[i])&&(i=i.it)&&i.get&&i["##"]&&i["##"]===n["##"])&&(!(i=e.to)||!i[t.url]&&!i[t.id]||o)))){if(t.batch){if(t.tail=(t.tail||0)+r.length,t.tail<=d.pack)return void t.batch.push(r);a(t)}t.batch=[],setTimeout(function(){a(t)},d.gap),u(r,t)}}else g.obj.map(d.peers,function(t){h.say(n,t)})}}(),function(){function f(t,n){var o;return n instanceof Object?(g.obj.map(Object.keys(n).sort(),i,{to:o={},on:n}),o):n}function i(t){this.to[t]=this.on[t]}h.raw=function(t){if(!t)return"";var n,o,i,e=p.dup,r=t._||{};if(i=r.raw)return i;if("string"==typeof t)return t;t["@"]&&(i=t.put)&&((o=t["##"])||(n=c(i,f)||"",o=h.hash(t,n),t["##"]=o),(i=e.s)[o=t["@"]+o]=i[t["#"]],t["#"]=o||t["#"],n&&((t=g.obj.to(t)).put=l));var a=0,u=[];g.obj.map(d.peers,function(t){if(u.push(t.url||t.id),9<++a)return!0}),t["><"]=u.join();var s=c(t);return v!==n&&(i=s.indexOf(l,s.indexOf("put")),s=s.slice(0,i-1)+n+s.slice(i+l.length+1)),r&&(r.raw=s),s},h.hash=function(t,n){return o.hash(n||c(t.put,f)||"")||t["#"]||g.text.random(9)};var c=JSON.stringify,l=":])([:"}(),h.hi=function(n){var t=n.wire||{};n.id||n.url?(d.peers[n.url||n.id]=n,g.obj.del(d.peers,t.id)):(t=t.id=t.id||g.text.random(9),h.say({dam:"?"},d.peers[t]=n)),t.hied||p.on(t.hied="hi",n),t=n.queue,n.queue=[],g.obj.map(t,function(t){h.say(t,n)})},h.bye=function(t){g.obj.del(d.peers,t.id),p.on("bye",t)},h.hear["!"]=function(t,n){d.log("Error:",t.err)},h.hear["?"]=function(t,n){if(!t.pid)return h.say({dam:"?",pid:d.pid,"@":t["#"]},n);n.id=n.id||t.pid,h.hi(n)},h}o.hash=function(t){if("string"!=typeof t)return{err:1};var n=0;if(!t.length)return n;for(var o=0,i=t.length;o"]||n["<"])||i===n["="]&&(o=n["*"]||n[">"]||n["<"],t.slice(0,(o||"").length)===o||i===n["*"]&&(i!==n[">"]&&i!==n["<"]?t>=n[">"]&&t<=n["<"]:i!==n[">"]&&t>=n[">"]||i!==n["<"]&&t<=n["<"])))},p.list={is:function(t){return t instanceof Array}},p.list.slit=Array.prototype.slice,p.list.sort=function(o){return function(t,n){return t&&n?(t=t[o])<(n=n[o])?-1:n",s.drift=0,s.is=function(t,n,o){var i=n&&t&&t[m]&&t[m][s._]||o;if(i)return g(i=i[n])?i:-1/0},s.lex=function(){return s().toString(36).replace(".","")},s.ify=function(t,n,o,i,e){if(!t||!t[m]){if(!e)return;t=a.soul.ify(t,e)}var r=c(t[m],s._);return void 0!==n&&n!==m&&(g(o)&&(r[n]=o),void 0!==i&&(t[n]=i)),t},s.to=function(t,n,o){var i=(t||{})[n];return p(i)&&(i=d(i)),s.ify(o,n,s.is(t,n),i,a.soul(t))},function(){function u(t,n){m!==n&&s.ify(this.o,n,this.s)}s.map=function(e,r,a){var t=p(t=e||r)?t:null;return e=v(e=e||r)?e:null,t&&!e?(r=g(r)?r:s(),t[m]=t[m]||{},h(t,u,{o:t,s:r}),t):(a=a||p(r)?r:void 0,r=g(r)?r:s(),function(t,n,o,i){if(!e)return u.call({o:o,s:r},t,n),t;e.call(a||this||{},t,n,o,i),l(o,n)&&void 0===o[n]||u.call({o:o,s:r},t,n)})}}();var f=n.obj,c=f.as,l=f.has,p=f.is,h=f.map,d=f.copy,g=n.num.is,v=n.fn.is,m=a._;t.exports=s})(_,"./state"),_(function(t){var a=_("./type"),f=_("./val"),c=_("./node"),r={};!function(){function e(t,n){if(!t||n!==c.soul(t)||!c.is(t,this.fn,this.as))return!0;this.cb&&(o.n=t,o.as=this.as,this.cb.call(o.as,t,n,o))}function o(t){t&&c.is(o.n,t,o.as)}r.is=function(t,n,o,i){return!(!t||!l(t)||u(t))&&!s(t,e,{cb:n,fn:o,as:i})}}(),function(){function u(t,n){var o;return(o=function(t,n){var o,i=t.seen,e=i.length;for(;e--;)if(o=i[e],n.obj===o.obj)return o;i.push(n)}(t,n))?o:(n.env=t,n.soul=e,c.ify(n.obj,i,n)&&(n.link=n.link||f.link.ify(c.soul(n.node)),n.obj!==t.shell&&(t.graph[f.link.is(n.link)]=n.node)),n)}function i(t,n,o){var i,e,r=this,a=r.env;if(c._===n&&h(t,f.link._))return o._;if(i=s(t,n,o,r,a)){if(n||(r.node=r.node||o||{},h(t,c._)&&c.soul(t)&&(r.node._=d(t._)),r.node=c.soul.ify(r.node,f.link.is(r.link)),r.link=r.link||f.link.ify(c.soul(r.node))),(e=a.map)&&(e.call(a.as||{},t,n,o,r),h(o,n))){if(void 0===(t=o[n]))return void p(o,n);if(!(i=s(t,n,o,r,a)))return}if(!n)return r.node;if(!0===i)return t;if((e=u(a,{obj:t,path:r.path.concat(n)})).node)return e.link}}function e(t){var n=this,o=f.link.is(n.link),i=n.env.graph;n.link=n.link||f.link.ify(t),n.link[f.link._]=t,n.node&&n.node[c._]&&(n.node[c._][f.link._]=t),h(i,o)&&(i[t]=i[o],p(i,o))}function s(t,n,o,i,e){var r;return!!f.is(t)||(l(t)?1:(r=e.invalid)?s(t=r.call(e.as||{},t,n,o),n,o,i,e):(e.err="Invalid value at '"+i.path.concat(n).join(".")+"'!",void(a.list.is(t)&&(e.err+=" Use `.set(item)` instead of an Array."))))}r.ify=function(t,n,o){var i={path:[],obj:t};return n?"string"==typeof n?n={soul:n}:n instanceof Function&&(n.map=n):n={},n.soul&&(i.link=f.link.ify(n.soul)),n.shell=(o||{}).shell,n.graph=n.graph||{},n.seen=n.seen||[],n.as=n.as||o,u(n,i),n.root=i.node,n.graph}}(),r.node=function(t){var n=c.soul(t);if(n)return o({},n,t)},function(){function e(t,n){var o,i;if(c._!==n)(o=f.link.is(t))?(i=this.opt.seen[o])?this.obj[n]=i:this.obj[n]=this.opt.seen[o]=r.to(this.graph,o,this.opt):this.obj[n]=t;else{if(u(t,f.link._))return;this.obj[n]=d(t)}}r.to=function(t,n,o){if(t){var i={};return o=o||{seen:{}},s(t[n],e,{obj:i,graph:t,opt:o}),i}}}();a.fn.is;var n=a.obj,l=n.is,p=n.del,h=n.has,u=n.empty,o=n.put,s=n.map,d=n.copy;t.exports=r})(_,"./graph"),_(function(t){_("./onto"),t.exports=function(t,n){if(this.on){if(!(t instanceof Function)){if(!t||!n)return;var o=t["#"]||t,i=(this.tag||empty)[o];if(!i)return;return i=this.on(o,n),clearTimeout(i.err),!0}o=n&&n["#"]||Math.random().toString(36).slice(2);if(!t)return o;var e=this.on(o,t,n);return e.err=e.err||setTimeout(function(){e.next({err:"Error: No ACK received yet.",lack:!0}),e.off()},(this.opt||{}).lack||9e3),o}}})(_,"./ask"),_(function(t){var r=_("./type");var a=r.time.is;t.exports=function(i){var e={s:{}};return i=i||{max:1e3,age:9e3},e.check=function(t){var n;return!!(n=e.s[t])&&(n.pass?n.pass=!1:e.track(t))},e.track=function(t,n){var o=e.s[t]||(e.s[t]={});return o.was=a(),n&&(o.pass=!0),e.to||(e.to=setTimeout(function(){var o=a();r.obj.map(e.s,function(t,n){t&&i.age>o-t.was||r.obj.del(e.s,n)}),e.to=null},i.age+9)),o},e}})(_,"./dup"),_(function(t){function c(t){return t instanceof c?(this._={gun:this,$:this}).$:this instanceof c?c.create(this._={gun:this,$:this,opt:t}):new c(t)}c.is=function(t){return t instanceof c||t&&t._&&t===t._.$||!1},c.version=.9,(c.chain=c.prototype).toJSON=function(){};var n=_("./type");n.obj.to(n,c),c.HAM=_("./HAM"),c.val=_("./val"),c.node=_("./node"),c.state=_("./state"),c.graph=_("./graph"),c.on=_("./onto"),c.ask=_("./ask"),c.dup=_("./dup"),function(){function a(t){var n,o,i=this.as,e=i.at||i,r=e.$;(o=t["#"])||(o=t["#"]=u(9)),(n=e.dup).check(o)?i.out===t.out&&(t.out=void 0,this.to.next(t)):(n.track(o),e.ask(t["@"],t)||(t.get&&c.on.get(t,r),t.put&&c.on.put(t,r)),this.to.next(t),i.out||(t.out=a,e.on("out",t)))}c.create=function(t){t.root=t.root||t,t.graph=t.graph||{},t.on=t.on||c.on,t.ask=t.ask||c.ask,t.dup=t.dup||c.dup();var n=t.$.opt(t.opt);return t.once||(t.on("in",a,t),t.on("out",a,{at:t,out:a}),c.on("create",t),t.on("create",t)),t.once=1,n}}(),function(){function e(t,n,o,i){var e=this,r=c.state.is(o,n);if(!r)return e.err="Error: No state on '"+n+"' in node '"+i+"'!";var a=e.graph[i]||v,u=c.state.is(a,n,!0),s=a[n],f=c.HAM(e.machine,r,u,t,s);f.incoming?(e.put[i]=c.state.to(o,n,e.put[i]),(e.diff||(e.diff={}))[i]=c.state.to(o,n,e.diff[i]),e.souls[i]=!0):f.defer&&(e.defer=r<(e.defer||1/0)?r:e.defer)}function r(t,n){var o=this,i=o.$._,e=(i.next||v)[n];if(!e){if(!(i.opt||v).super)return void(o.souls[n]=!1);e=o.$.get(n)._}var r=o.map[n]={put:t,get:n,$:e.$},a={ctx:o,msg:r};o.async=!!i.tag.node,o.ack&&(r["@"]=o.ack),h(t,u,a),o.async&&(o.and||i.on("node",function(t){this.to.next(t),t===o.map[t.get]&&(o.souls[t.get]=!1,h(t.put,s,t),h(o.souls,function(t){if(t)return t})||o.c||(o.c=1,this.off(),h(o.map,f,o)))}),o.and=!0,i.on("node",r))}function u(t,n){var o=this.ctx,i=o.graph,e=this.msg,r=e.get,a=e.put,u=e.$._;i[r]=c.state.to(a,n,i[r]),o.async||(u.put=c.state.to(a,n,u.put))}function s(t,n){var o=this.put,i=this.$._;i.put=c.state.to(o,n,i.put)}function f(t,n){t.$&&(this.cat.stop=this.stop,t.$._.on("in",t),this.cat.stop=null)}c.on.put=function(t,n){var o=n._,i={$:n,graph:o.graph,put:{},map:{},souls:{},machine:c.state(),ack:t["@"],cat:o,stop:{}};if(c.graph.is(t.put,null,e,i)||(i.err="Error: Invalid graph!"),i.err)return o.on("in",{"@":t["#"],err:c.log(i.err)});h(i.put,r,i),i.async||h(i.map,f,i),void 0!==i.defer&&setTimeout(function(){c.on.put(t,n)},i.defer-i.machine),i.diff&&o.on("put",p(t,{put:i.diff}))},c.on.get=function(t,n){var o=n._,i=t.get,e=i[d],r=o.graph[e],a=i[g],u=(o.next||(o.next={}))[e];if(!r)return o.on("get",t);if(a){if("string"!=typeof a||!l(r,a))return o.on("get",t);r=c.state.to(r,a)}else r=c.obj.copy(r);r=c.graph.node(r),(u||v).ack,o.on("in",{"@":t["#"],how:"mem",put:r,$:n}),o.on("get",t)}}(),c.chain.opt=function(t){t=t||{};var n=this._,o=t.peers||t;return a(t)||(t={}),a(n.opt)||(n.opt=t),e(o)&&(o=[o]),i(o)&&(o=h(o,function(t,n,o){o(t,{url:t})}),a(n.opt.peers)||(n.opt.peers={}),n.opt.peers=p(o,n.opt.peers)),n.opt.peers=n.opt.peers||{},p(t,n.opt),c.on("opt",n),n.opt.uuid=n.opt.uuid||function(){return s()+u(12)},this};var i=c.list.is,o=c.text,e=o.is,u=o.random,r=c.obj,a=r.is,l=r.has,p=r.to,h=r.map,s=(r.copy,c.state.lex),d=c.val.link._,g=".",v=(c.node._,c.val.link.is,{});b.debug=function(t,n){return b.debug.i&&t===b.debug.i&&b.debug.i++&&(b.log.apply(b,arguments)||n)},(c.log=function(){return!c.log.off&&b.log.apply(b,arguments),[].slice.call(arguments).join(" ")}).once=function(t,n,o){return(o=c.log.once)[t]=o[t]||0,o[t]++||c.log(n)},c.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=window.Gun=c).window=window);try{void 0!==f&&(f.exports=c)}catch(t){}t.exports=c})(_,"./root"),_(function(t){var u=_("./root");u.chain.back=function(t,n){if(-1===(t=t||1)||1/0===t)return this._.root.$;if(1===t)return(this._.back||this._).$;var o=this._;if("string"==typeof t&&(t=t.split(".")),t instanceof Array){for(var i=0,e=t.length,r=o;i .once, apologies unexpected."),this.once(t,n)},f.chain.once=function(t,n){var o=this,i=o._,e=i.put;if(0=(a.batch||1e3))return f();i||(i=setTimeout(f,a.wait||1))}),r.on("get",function(n){this.to.next(n);var o,i,e=n.get;function t(){if(e&&(o=e["#"])){var t=e["."];(i=s[o]||void 0)&&t&&(i=Gun.state.to(i,t)),(i||Gun.obj.empty(a.peers))&&r.on("in",{"@":n["#"],put:Gun.graph.node(i),how:"lS",lS:n.$||r.$})}}Gun.debug?setTimeout(t,1):t()});var n=function(t,n,o,i){s[i]=Gun.state.to(o,n,s[i])},f=function(t){var o;u=0,clearTimeout(i),i=!1;var n=e;e={},t&&(s=t);try{p.setItem(a.prefix,JSON.stringify(s))}catch(t){Gun.log(o=(t||"localStorage failure")+" Consider using GUN's IndexedDB plugin for RAD for more storage space, temporary example at https://github.com/amark/gun/blob/master/test/tmp/indexedDB.html ."),r.on("localStorage:error",{err:o,file:a.prefix,flush:s,retry:f})}(o||Gun.obj.empty(a.peers))&&Gun.obj.map(n,function(t,n){r.on("in",{"@":n,err:o,ok:0})})}}})}})(_,"./adapters/localStorage"),_(function(t){var o=_("../index"),g=_("../type");function e(p){var h=function(){},d=p.opt||{};d.log=d.log||b.log,d.gap=d.gap||d.wait||1,d.pack=d.pack||.3*(d.memory?1e3*d.memory*1e3:1399e6),h.out=function(t){var n;if(this.to&&this.to.next(t),(n=t["@"])&&(n=p.dup.s[n])&&(n=n.it)&&n._)return h.say(t,n._.via,1),void(n["##"]=t["##"]);o.AXE?o.AXE.say(t,h.say,this):h.say(t)},p.on("create",function(t){t.opt.pid=t.opt.pid||g.text.random(9),this.to.next(t),p.on("out",h.out)}),(h.hear=function(t,n){if(t){var o,i,e,r=p.dup,a=t[0];if(d.pack<=t.length)return h.say({dam:"!",err:"Message too big!"},n);if("{"===a){try{e=JSON.parse(t)}catch(t){d.log("DAM JSON parse error",t)}if(!e)return;if(h.hear.d+=t.length,++h.hear.c,r.check(o=e["#"]))return;if((a=(r.track(o,!0).it=e)["@"])&&e.put&&(a+=i=e["##"]||(e["##"]=h.hash(e)))!=o){if(r.check(a))return;(a=r.s)[i]=a[o]}return(e._=function(){}).via=n,(a=e["><"])&&(e._.to=g.obj.map(a.split(","),f)),e.dam?void((a=h.hear[e.dam])&&a(e,n,p)):void p.on("in",e)}if("["!==a);else{try{e=JSON.parse(t)}catch(t){d.log("DAM JSON parse error",t)}if(!e)return;for(var u,s=0;u=e[s++];)h.hear(u,n)}}}).c=h.hear.d=0;var f=function(t,n,o){o(t,!0)};return function(){var a;function u(t){h.say(a,t)}function s(t){var n=t.batch;if(n&&(t.batch=t.tail=null,n.length))try{f(JSON.stringify(n),t)}catch(t){d.log("DAM JSON stringify error",t)}}function f(n,o){var t=o.wire;try{o.say?o.say(n):t.send&&t.send(n),h.say.d+=n.length,++h.say.c}catch(t){(o.queue=o.queue||[]).push(n)}}(h.say=function(t,n,o){if(!n)return a=t,void g.obj.map(d.peers,u);var i,e,r;if((n.wire||d.wire&&d.wire(n))&&(e=t._||c,n!==e.via&&((r=e.raw)||(r=h.raw(t)),!((i=t["@"])&&(i=p.dup.s[i])&&(i=i.it)&&i.get&&i["##"]&&i["##"]===t["##"])&&(!(i=e.to)||!i[n.url]&&!i[n.id]||o)))){if(n.batch){if(n.tail=(n.tail||0)+r.length,n.tail<=d.pack)return void n.batch.push(r);s(n)}n.batch=[],setTimeout(function(){s(n)},d.gap),f(r,n)}}).c=h.say.d=0}(),function(){function f(t,n){var o;return n instanceof Object?(g.obj.map(Object.keys(n).sort(),i,{to:o={},on:n}),o):n}function i(t){this.to[t]=this.on[t]}h.raw=function(t){if(!t)return"";var n,o,i,e=p.dup,r=t._||{};if(i=r.raw)return i;if("string"==typeof t)return t;t["@"]&&(i=t.put)&&((o=t["##"])||(n=c(i,f)||"",o=h.hash(t,n),t["##"]=o),(i=e.s)[o=t["@"]+o]=i[t["#"]],t["#"]=o||t["#"],n&&((t=g.obj.to(t)).put=l));var a=0,u=[];g.obj.map(d.peers,function(t){if(u.push(t.url||t.id),9<++a)return!0}),t["><"]=u.join();var s=c(t);return v!==n&&(i=s.indexOf(l,s.indexOf("put")),s=s.slice(0,i-1)+n+s.slice(i+l.length+1)),r&&(r.raw=s),s},h.hash=function(t,n){return e.hash(n||c(t.put,f)||"")||t["#"]||g.text.random(9)};var c=JSON.stringify,l=":])([:"}(),h.hi=function(n){var t=n.wire||{};n.id||n.url?d.peers[n.url||n.id]=n:(t=n.id=t.pid=n.id||g.text.random(9),h.say({dam:"?"},d.peers[t]=n)),t.hied||p.on(t.hied="hi",n),t=n.queue,n.queue=[],g.obj.map(t,function(t){h.say(t,n)})},h.bye=function(t){g.obj.del(d.peers,t.id),p.on("bye",t)},h.hear["!"]=function(t,n){d.log("Error:",t.err)},h.hear["?"]=function(t,n){t.pid?n.wire&&n.wire.pid&&(g.obj.del(d.peers,n.wire.pid||n.id),delete n.wire.pid,n.id=t.pid,h.hi(n)):h.say({dam:"?",pid:d.pid,"@":t["#"]},n)},h}e.hash=function(t){if("string"!=typeof t)return{err:1};var n=0;if(!t.length)return n;for(var o=0,i=t.length;o Date: Mon, 20 May 2019 20:46:55 -0700 Subject: [PATCH 2/6] have AXE use DAM. --- axe.js | 47 +++++++++++++++++++++-------------------------- gun.js | 3 +-- 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/axe.js b/axe.js index dbcb9c0f..352f5ab5 100644 --- a/axe.js +++ b/axe.js @@ -19,20 +19,13 @@ /* UNBUILD */ ;USE(function(module){ - if(typeof window !== "undefined"){ module.window = window } - var tmp = module.window || module; + if(typeof window !== "undefined"){ module.window = window } + var tmp = module.window || module; var AXE = tmp.AXE || function(){}; - if(AXE.window = module.window){ try{ - AXE.window.AXE = AXE; - tmp = document.createEvent('CustomEvent'); - tmp.initCustomEvent('extension', false, false, {type: "AXE"}); - (window.dispatchEvent || window.fireEvent)(tmp); - window.postMessage({type: "AXE"}, '*'); - } catch(e){} } - - try{ if(typeof common !== "undefined"){ common.exports = AXE } }catch(e){} - module.exports = AXE; + if(AXE.window = module.window){ AXE.window.AXE = AXE } + try{ if(typeof common !== "undefined"){ common.exports = AXE } }catch(e){} + module.exports = AXE; })(USE, './root'); ;USE(function(module){ @@ -42,6 +35,7 @@ Gun.on('opt', function(at){ if(!at.axe){ at.axe = {}; + var opt = at.opt; var peers = at.opt.peers, tmp; // 1. If any remembered peers or from last cache or extension // 2. Fallback to use hard coded peers from dApp @@ -59,9 +53,10 @@ // with one common superpeer (with ready failovers) // in case the p2p linear latency is high. // Or there could be plenty of other better options. + var mesh = opt.mesh = opt.mesh || Gun.Mesh(at); console.log("axe"); - function verify(dht, msg, send, at) { + function verify(dht, msg) { var puts = Object.keys(msg.put); var soul = puts[0]; /// TODO: verify all souls in puts. Copy the msg only with subscribed souls? var subs = dht(soul); @@ -72,11 +67,11 @@ if (pid in peers) { tmp.push(pid); // console.log('[AXE] SEND TO >>>>> ', pid, msg.put.bob || msg.put); - send(msg, peers[pid]); + mesh.say(msg, peers[pid]); } }); /// Only connected peers in the tmp array. - if (at.on.opt.super) { + if (opt.super) { dht(soul, tmp.join(',')); } } @@ -89,33 +84,32 @@ // console.log('[AXE] out:', msg, a); // }, at); if(at.opt.super){ - AXE.say = function(msg, send, at) { + mesh.route = function(msg) { if (msg.rtc) { // console.log('[AXE] MSG WEBRTC: ', msg.rtc); if (msg.rtc.to) { /// Send announce to one peer only if the msg have 'to' attr - var peer = (at.on.opt.peers) ? at.on.opt.peers[msg.rtc.to] : null; -// if (peer) { at.on.opt.mesh.say(msg, peer); } - if (peer) { send(msg, peer); } + var peer = (peers) ? peers[msg.rtc.to] : null; + if (peer) { mesh.say(msg, peer); } return; } } - if (!msg.put) { send(msg); return; } + if (!msg.put) { mesh.say(msg); return; } //console.log('AXE HOOK!! ', msg); - verify(at.on.opt.dht, msg, send, at); + verify(opt.dht, msg); }; } else { - AXE.say = function(msg, send, at) { + mesh.route = function(msg) { if (msg.rtc) { // console.log('[AXE] MSG WEBRTC: ', msg.rtc); } - if (!msg.put) { send(msg); return; } - verify(at.on.opt.dht, msg, send, at); + if (!msg.put) { mesh.say(msg); return; } + verify(opt.dht, msg); /// Always send to superpeers? - Gun.obj.map(at.on.opt.peers, function(peer) { + Gun.obj.map(peers, function(peer) { if (peer.url) { // console.log('SEND TO SUPERPEER', msg); - send(msg, peer); + mesh.say(msg, peer); } }); }; @@ -141,6 +135,7 @@ } this.to.next(at); // make sure to call the "next" middleware adapter. }); + function joindht(dht, soul, pids) { if (!pids || !soul || !dht) { return; } var subs = dht(soul); diff --git a/gun.js b/gun.js index 4ed6ade0..59b83a0f 100644 --- a/gun.js +++ b/gun.js @@ -1958,8 +1958,7 @@ tmp['##'] = msg['##']; return; } - // add hook for AXE? - if (Gun.AXE) { Gun.AXE.say(msg, mesh.say, this); return; } + if(mesh.route){ mesh.route(msg); return } mesh.say(msg); } From a786944ed61454c94ffdf6891317c75b97677ab5 Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Tue, 21 May 2019 00:21:02 -0700 Subject: [PATCH 3/6] finally fix pid , I'm sure @rogowski will be delighted! --- gun.js | 14 +++++--------- test/axe/holy-grail.js | 2 +- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/gun.js b/gun.js index 59b83a0f..5eb92984 100644 --- a/gun.js +++ b/gun.js @@ -2038,7 +2038,7 @@ var wire = peer.wire || ((opt.wire) && opt.wire(peer));// || open(peer, ctx); // TODO: Reopen! if(!wire){ return } if(peer === meta.via){ return } - if((tmp = meta.to) && (tmp[peer.url] || tmp[peer.id]) && !o){ return } // TODO: still needs to be tested + if((tmp = meta.to) && (tmp[peer.url] || tmp[peer.pid] || tmp[peer.id]) && !o){ return } // TODO: still needs to be tested if(peer.batch){ peer.tail = (peer.tail || 0) + raw.length; if(peer.tail <= opt.pack){ @@ -2096,7 +2096,7 @@ } if(!msg.dam){ var i = 0, to = []; Type.obj.map(opt.peers, function(p){ - to.push(p.url || p.id); if(++i > 9){ return true } // limit server, fast fix, improve later! + to.push(p.url || p.pid || p.id); if(++i > 9){ return true } // limit server, fast fix, improve later! }); msg['><'] = to.join(); } var raw = $(msg); @@ -2133,7 +2133,7 @@ if(peer.id || peer.url){ opt.peers[peer.url || peer.id] = peer; } else { - tmp = peer.id = tmp.pid = peer.id || Type.text.random(9); + tmp = peer.id = peer.id || Type.text.random(9); mesh.say({dam: '?'}, opt.peers[tmp] = peer); } peer.met = peer.met || +(new Date); @@ -2161,12 +2161,8 @@ // }); return; } - if(!peer.wire){ return } - if(!peer.wire.pid){ return } // only run code below if wire.pid exists - Type.obj.del(opt.peers, peer.wire.pid || peer.id); - delete peer.wire.pid; - peer.id = msg.pid; - mesh.hi(peer); + if(peer.pid){ return } + peer.pid = msg.pid; } return mesh; } diff --git a/test/axe/holy-grail.js b/test/axe/holy-grail.js index 420b5bb1..93ba4bfc 100644 --- a/test/axe/holy-grail.js +++ b/test/axe/holy-grail.js @@ -169,7 +169,7 @@ describe("The Holy Grail AXE Test!", function(){ }) }); - it("Jhon Read what Bob say to Alice: Hi Alice!", function(){ + it("John Read what Bob say to Alice: Hi Alice!", function(){ return john.run(function(test){ test.async(); console.log("I AM JOHN"); From 209bdf2b06d2c02ddc79462a692b19ff1a65dee6 Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Sun, 26 May 2019 04:33:00 -0700 Subject: [PATCH 4/6] refactor DAM & add PANIC tests --- axe.js | 110 +++++++++------ gun.js | 283 +++++++++++++++++++-------------------- test/panic/1putackget.js | 198 +++++++++++++++++++++++++++ 3 files changed, 407 insertions(+), 184 deletions(-) create mode 100644 test/panic/1putackget.js diff --git a/axe.js b/axe.js index 352f5ab5..ae01f209 100644 --- a/axe.js +++ b/axe.js @@ -34,9 +34,8 @@ (Gun.AXE = AXE).GUN = AXE.Gun = Gun; Gun.on('opt', function(at){ if(!at.axe){ - at.axe = {}; - var opt = at.opt; - var peers = at.opt.peers, tmp; + var axe = at.axe = {}, tmp; + var opt = at.opt, peers = opt.peers; // 1. If any remembered peers or from last cache or extension // 2. Fallback to use hard coded peers from dApp // 3. Or any offered peers. @@ -54,7 +53,7 @@ // in case the p2p linear latency is high. // Or there could be plenty of other better options. var mesh = opt.mesh = opt.mesh || Gun.Mesh(at); - console.log("axe"); + console.log("AXE enabled."); function verify(dht, msg) { var puts = Object.keys(msg.put); @@ -83,8 +82,52 @@ // this.to.next(msg); // console.log('[AXE] out:', msg, a); // }, at); + + + function input(msg){ + var to = this.to, peer = (msg._||{}).via; + var dht = opt.dht; + var routes = axe.routes || (axe.routes = {}); // USE RAD INSTEAD! TMP TESTING! + var get = msg.get, hash, tmp; + if(get && opt.super && peer){ + hash = Gun.obj.hash(get); // USE RAD INSTEAD! + (routes[hash] || (routes[hash] = {}))[peer.id] = peer; + (peer.routes || (peer.routes = {}))[hash] = routes[hash]; + + + /*if(soul = get['#']){ // SWITCH BACK TO USING DHT! + if(key = get['.']){ + + } else { + + } + if (!peer.id) {console.log('[*** WARN] no peer.id %s', soul);} + var pids = joindht(dht, soul, peer.id); + if (pids) { + var dht = {}; + dht[soul] = pids; + mesh.say({dht:dht}, opt.peers[peer.id]); + } + }*/ + } + to.next(msg); + + if (opt.rtc && msg.dht) { + Gun.obj.map(msg.dht, function(pids, soul) { + dht(soul, pids); + Gun.obj.map(pids.split(','), function(pid) { + /// TODO: here we can put an algorithm of who must connect? + if (!pid || pid in opt.peers || pid === opt.pid || opt.announce[pid]) { return; } + opt.announce[pid] = true; /// To try only one connection to the same peer. + opt.announce(pid); + }); + }); + } + } + if(at.opt.super){ - mesh.route = function(msg) { + var rotate = 0; + mesh.way = function(msg) { if (msg.rtc) { // console.log('[AXE] MSG WEBRTC: ', msg.rtc); if (msg.rtc.to) { @@ -94,6 +137,24 @@ return; } } + if(msg.get){ + var hash = Gun.obj.hash(msg.get); + var routes = axe.routes || (axe.routes = {}); // USE RAD INSTEAD! TMP TESTING! + var peers = routes[hash]; + if(!peers){ return mesh.say(msg, opt.peers) } + var ids = Object.keys(peers); // TODO: BUG! THIS IS BAD PERFORMANCE!!!! + var meta = (msg._||yes); + var id, peer, c = 1; // opt. ?redundancy? + //console.log("AXE:", msg.get, ids); + while(c-- && (id = ids[(meta.turn = (meta.turn || 0) + 1) - 1])){ // TODO: This has many flaws to it! + peer = peers[id]; + if(false === mesh.say(msg, peer)){ ++c } + } + return; + } + mesh.say(msg, opt.peers); return; // TODO: DISABLE THIS!!! USE DHT! + + if (!msg.put) { mesh.say(msg); return; } //console.log('AXE HOOK!! ', msg); verify(opt.dht, msg); @@ -113,7 +174,7 @@ } }); }; - var connections = 0; + /*var connections = 0; // THIS HAS BEEN MOVED TO CORE NOW! at.on('hi', function(opt) { this.to.next(opt); //console.log('AXE PEER [HI]', new Date(), opt); @@ -130,7 +191,7 @@ } //location.reload(); }, 500); - }, at); + }, at);*/ } } this.to.next(at); // make sure to call the "next" middleware adapter. @@ -147,42 +208,9 @@ dht(soul, tmp); return tmp; } - function input(msg){ -// console.log('[AXE] input: ', msg); - var at = this.as, to = this.to, peer = (msg._||{}).via; - var opt = at.opt; - var dht = opt.dht; - var get = msg.get, soul, key; - if(peer && get){ - if(soul = get['#']){ - if(key = get['.']){ - } else { + var empty = {}, yes = true, u; - } - if (!peer.id) {console.log('[*** WARN] no peer.id %s', soul);} - var pids = joindht(dht, soul, peer.id); - if (pids) { - var dht = {}; - dht[soul] = pids; - at.opt.mesh.say({dht:dht}, opt.peers[peer.id]); - } - } - } - to.next(msg); - - if (opt.rtc && msg.dht) { - Gun.obj.map(msg.dht, function(pids, soul) { - dht(soul, pids); - Gun.obj.map(pids.split(','), function(pid) { - /// TODO: here we can put an algorithm of who must connect? - if (!pid || pid in opt.peers || pid === opt.pid || opt.announce[pid]) { return; } - opt.announce[pid] = true; /// To try only one connection to the same peer. - opt.announce(pid); - }); - }); - } - } module.exports = AXE; })(USE, './axe'); }()); diff --git a/gun.js b/gun.js index 5eb92984..214d4172 100644 --- a/gun.js +++ b/gun.js @@ -833,7 +833,7 @@ if(text_is(tmp)){ tmp = [tmp] } if(list_is(tmp)){ tmp = obj_map(tmp, function(url, i, map){ - map(url, {url: url}); + i = {}; i.id = i.url = url; map(url, i); }); if(!obj_is(at.opt.peers)){ at.opt.peers = {}} at.opt.peers = obj_to(tmp, at.opt.peers); @@ -1459,11 +1459,11 @@ var cat = (as.$.back(-1)._), ask = cat.ask(function(ack){ cat.root.on('ack', ack); if(ack.err){ Gun.log(ack) } - if(!ack.lack){ this.off() } // One response is good enough for us currently. Later we may want to adjust this. + if(++acks > (as.acks || 0)){ this.off() } // Adjustable ACKs! Only 1 by default. if(!as.ack){ return } as.ack(ack, this); //--C; - }, as.opt); + }, as.opt), acks = 0; //C++; // NOW is a hack to get synchronous replies to correctly call. // and STOP is a hack to get async behavior to correctly call. @@ -1937,66 +1937,43 @@ })(USE, './adapters/localStorage'); ;USE(function(module){ - var Gun = USE('../index'); var Type = USE('../type'); - function Mesh(ctx){ + function Mesh(root){ var mesh = function(){}; - var opt = ctx.opt || {}; + var opt = root.opt || {}; opt.log = opt.log || console.log; opt.gap = opt.gap || opt.wait || 1; opt.pack = opt.pack || (opt.memory? (opt.memory * 1000 * 1000) : 1399000000) * 0.3; // max_old_space_size defaults to 1400 MB. - mesh.out = function(msg){ var tmp; - if(this.to){ this.to.next(msg) } - //if(mesh.last != msg['#']){ return mesh.last = msg['#'], this.to.next(msg) } - if((tmp = msg['@']) - && (tmp = ctx.dup.s[tmp]) - && (tmp = tmp.it) - && tmp._){ - mesh.say(msg, (tmp._).via, 1); - tmp['##'] = msg['##']; - return; - } - if(mesh.route){ mesh.route(msg); return } - mesh.say(msg); - } - - ctx.on('create', function(root){ - root.opt.pid = root.opt.pid || Type.text.random(9); - this.to.next(root); - ctx.on('out', mesh.out); - }); + var dup = root.dup; mesh.hear = function(raw, peer){ if(!raw){ return } - var dup = ctx.dup, id, hash, msg, tmp = raw[0]; + var msg, id, hash, tmp = raw[0]; if(opt.pack <= raw.length){ return mesh.say({dam: '!', err: "Message too big!"}, peer) } if('{' === tmp){ - try{msg = JSON.parse(raw);}catch(e){opt.log('DAM JSON parse error', e)} + try{msg = JSON.parse(raw); + }catch(e){return opt.log('DAM JSON parse error', e)} if(!msg){ return } mesh.hear.d += raw.length; ++mesh.hear.c; // STATS! - if(dup.check(id = msg['#'])){ return } - dup.track(id, true).it = msg; // GUN core also dedups, so `true` is needed. - if((tmp = msg['@']) && msg.put){ - hash = msg['##'] || (msg['##'] = mesh.hash(msg)); - if((tmp = tmp + hash) != id){ - if(dup.check(tmp)){ return } - (tmp = dup.s)[hash] = tmp[id]; - } + if(!(id = msg['#'])){ id = msg['#'] = Type.text.random(9) } + if(dup.check(id)){ return } + dup.track(id, true).it = msg; // GUN core also dedups, so `true` is needed. // Does GUN core need to dedup anymore? + if(!(hash = msg['##']) && u !== msg.put){ hash = msg['##'] = Type.obj.hash(msg.put) } + if(hash && (tmp = msg['@'])){ + if(dup.check(tmp+hash)){ return } + dup.track(tmp+hash, true).it = msg; // GUN core also dedups, so `true` is needed. // Does GUN core need to dedup anymore? } (msg._ = function(){}).via = peer; - if((tmp = msg['><'])){ - (msg._).to = Type.obj.map(tmp.split(','), tomap); - } + if(tmp = msg['><']){ (msg._).to = Type.obj.map(tmp.split(','), tomap) } if(msg.dam){ if(tmp = mesh.hear[msg.dam]){ - tmp(msg, peer, ctx); + tmp(msg, peer, root); } return; } - ctx.on('in', msg); - + root.on('in', msg); return; } else if('[' === tmp){ @@ -2006,147 +1983,125 @@ while(m = msg[i++]){ mesh.hear(m, peer); } - return; } } - mesh.hear.c = mesh.hear.d = 0; var tomap = function(k,i,m){m(k,true)}; + mesh.hear.c = mesh.hear.d = 0; ;(function(){ var message; function each(peer){ mesh.say(message, peer) } - mesh.say = function(msg, peer, o){ var tmp; - /* - TODO: Plenty of performance optimizations - that can be made just based off of ordering, - and reducing function calls for cached writes. - */ - var meta = (msg._) || empty, raw; - if(!(raw = meta.raw)){ raw = mesh.raw(msg) } - if((tmp = msg['@']) - && (tmp = ctx.dup.s[tmp]) - && (tmp = tmp.it)){ - if(tmp.get && tmp['##'] && tmp['##'] === msg['##']){ // PERF: move this condition outside say? - return; // TODO: this still needs to be tested in the browser! + mesh.say = function(msg, peer){ + if(this.to){ this.to.next(msg) } // compatible with middleware adapters. + if(!msg){ return false } + var id, hash, tmp, raw; + var meta = msg._||(msg._=function(){}); + if(!(id = msg['#'])){ id = msg['#'] = Type.text.random(9) } + if(!(hash = msg['##']) && u !== msg.put){ hash = msg['##'] = Type.obj.hash(msg.put) } + if(!(raw = meta.raw)){ + raw = meta.raw = mesh.raw(msg); + if(hash && (tmp = msg['@'])){ + dup.track(tmp+hash).it = msg; + if(tmp = (dup.s[tmp]||ok).it){ + if(hash === tmp['##']){ return false } + tmp['##'] = hash; + } } } - if(!peer){ message = msg; - Type.obj.map(opt.peers, each); + dup.track(id).it = msg; // track for 9 seconds, default. Earth<->Mars would need more! + if(!peer){ peer = (tmp = dup.s[msg['@']]) && (tmp = tmp.it) && (tmp = tmp._) && (tmp = tmp.via) } + if(!peer && mesh.way){ return mesh.way(msg) } + if(!peer || !peer.id){ message = msg; + if(!Type.obj.is(peer || opt.peers)){ return false } + Type.obj.map(peer || opt.peers, each); // in case peer is a peer list. return; } - var wire = peer.wire || ((opt.wire) && opt.wire(peer));// || open(peer, ctx); // TODO: Reopen! - if(!wire){ return } - if(peer === meta.via){ return } - if((tmp = meta.to) && (tmp[peer.url] || tmp[peer.pid] || tmp[peer.id]) && !o){ return } // TODO: still needs to be tested + if(!peer.wire && mesh.wire){ mesh.wire(peer) } + if(peer === meta.via){ return false } + if((tmp = meta.to) && (tmp[peer.url] || tmp[peer.pid] || tmp[peer.id]) /*&& !o*/){ return false } if(peer.batch){ - peer.tail = (peer.tail || 0) + raw.length; + peer.tail = (tmp = peer.tail || 0) + raw.length; if(peer.tail <= opt.pack){ - peer.batch.push(raw); + peer.batch.push(raw); // peer.batch += (tmp?'':',')+raw; // TODO: Prevent double JSON! // FOR v1.0 !? return; } flush(peer); } - peer.batch = []; + peer.batch = []; // peer.batch = '['; // TODO: Prevent double JSON! setTimeout(function(){flush(peer)}, opt.gap); send(raw, peer); } function flush(peer){ - var tmp = peer.batch; - if(!tmp){ return } + var tmp = peer.batch; // var tmp = peer.batch + ']'; // TODO: Prevent double JSON! peer.batch = peer.tail = null; - if(!tmp.length){ return } - try{send(JSON.stringify(tmp), peer); - }catch(e){opt.log('DAM JSON stringify error', e)} - } - function send(raw, peer){ - var wire = peer.wire; - try{ - if(peer.say){ - peer.say(raw); - } else - if(wire.send){ - wire.send(raw); - } - mesh.say.d += raw.length; ++mesh.say.c; // STATS! - }catch(e){ - (peer.queue = peer.queue || []).push(raw); - } + if(!tmp){ return } + if(!tmp.length){ return } // if(3 > tmp.length){ return } // TODO: ^ + try{tmp = (1 === tmp.length? tmp[0] : JSON.stringify(tmp)); + }catch(e){return opt.log('DAM JSON stringify error', e)} + if(!tmp){ return } + send(tmp, peer); } mesh.say.c = mesh.say.d = 0; - }()); + + // for now - find better place later. + function send(raw, peer){ try{ + var wire = peer.wire; + if(peer.say){ + peer.say(raw); + } else + if(wire.send){ + wire.send(raw); + } + mesh.say.d += raw.length; ++mesh.say.c; // STATS! + }catch(e){ + (peer.queue = peer.queue || []).push(raw); + }} ;(function(){ - - mesh.raw = function(msg){ + mesh.raw = function(msg){ // TODO: Clean this up / delete it / move logic out! if(!msg){ return '' } - var dup = ctx.dup, meta = (msg._) || {}, put, hash, tmp; + var meta = (msg._) || {}, put, hash, tmp; if(tmp = meta.raw){ return tmp } if(typeof msg === 'string'){ return msg } - if(msg['@'] && (tmp = msg.put)){ - if(!(hash = msg['##'])){ - put = $(tmp, sort) || ''; - hash = mesh.hash(msg, put); - msg['##'] = hash; - } - (tmp = dup.s)[hash = msg['@']+hash] = tmp[msg['#']]; - msg['#'] = hash || msg['#']; - if(put){ (msg = Type.obj.to(msg)).put = _ } - } if(!msg.dam){ var i = 0, to = []; Type.obj.map(opt.peers, function(p){ - to.push(p.url || p.pid || p.id); if(++i > 9){ return true } // limit server, fast fix, improve later! + to.push(p.url || p.pid || p.id); if(++i > 9){ return true } // limit server, fast fix, improve later! // For "tower" peer, MUST include 6 surrounding ids. }); msg['><'] = to.join(); } - var raw = $(msg); - if(u !== put){ + var raw = $(msg); // optimize by reusing put = the JSON.stringify from .hash? + /*if(u !== put){ tmp = raw.indexOf(_, raw.indexOf('put')); raw = raw.slice(0, tmp-1) + put + raw.slice(tmp + _.length + 1); - //raw = raw.replace('"'+ _ +'"', put); // https://github.com/amark/gun/wiki/@$$ Heisenbug - } - if(meta){ - meta.raw = raw; - } + //raw = raw.replace('"'+ _ +'"', put); // NEVER USE THIS! ALSO NEVER DELETE IT TO NOT MAKE SAME MISTAKE! https://github.com/amark/gun/wiki/@$$ Heisenbug + }*/ + if(meta){ meta.raw = raw } return raw; } - - mesh.hash = function(msg, hash){ - return Mesh.hash(hash || $(msg.put, sort) || '') || msg['#'] || Type.text.random(9); - } - - function sort(k, v){ var tmp; - if(!(v instanceof Object)){ return v } - Type.obj.map(Object.keys(v).sort(), map, {to: tmp = {}, on: v}); - return tmp; - } - - function map(k){ - this.to[k] = this.on[k]; - } var $ = JSON.stringify, _ = ':])([:'; }()); mesh.hi = function(peer){ var tmp = peer.wire || {}; - if(peer.id || peer.url){ + if(peer.id){ opt.peers[peer.url || peer.id] = peer; } else { tmp = peer.id = peer.id || Type.text.random(9); mesh.say({dam: '?'}, opt.peers[tmp] = peer); } peer.met = peer.met || +(new Date); - if(!tmp.hied){ ctx.on(tmp.hied = 'hi', peer) } + if(!tmp.hied){ root.on(tmp.hied = 'hi', peer) } // @rogowski I need this here by default for now to fix go1dfish's bug tmp = peer.queue; peer.queue = []; Type.obj.map(tmp, function(msg){ - mesh.say(msg, peer); + send(msg, peer); }); } mesh.bye = function(peer){ Type.obj.del(opt.peers, peer.id); // assume if peer.url then reconnect - ctx.on('bye', peer); + root.on('bye', peer); var tmp = +(new Date); tmp = (tmp - (peer.met||tmp)); mesh.bye.time = ((mesh.bye.time || tmp) + tmp) / 2; } @@ -2155,31 +2110,73 @@ if(!msg.pid){ mesh.say({dam: '?', pid: opt.pid, '@': msg['#']}, peer); // @rogowski I want to re-enable this AXE logic with some fix/merge later. - // var tmp = peer.queue; peer.queue = []; - // Type.obj.map(tmp, function(msg){ - // mesh.say(msg, peer); - // }); + /* var tmp = peer.queue; peer.queue = []; + Type.obj.map(tmp, function(msg){ + mesh.say(msg, peer); + }); */ + // @rogowski 2: I think with my PID fix we can delete this and use the original. return; } if(peer.pid){ return } peer.pid = msg.pid; } + + root.on('create', function(root){ + root.opt.pid = root.opt.pid || Type.text.random(9); + this.to.next(root); + root.on('out', mesh.say); + }); + + if(!opt.super){ + var gets = {}; + root.on('bye', function(peer, tmp){ + if(!(tmp = peer.url)){ return } gets[tmp] = true; + setTimeout(function(){ delete gets[tmp] },opt.lack || 9000) + }); + root.on('hi', function(peer, tmp){ + if(!(tmp = peer.url) || !gets[tmp]){ return } delete gets[tmp]; + Type.obj.map(root.graph, function(node, soul){ tmp = {}; tmp[soul] = node; + mesh.say({'##': Type.obj.hash(tmp), get: {'#': soul}}, peer); + }) + }); + } + return mesh; } - Mesh.hash = function(s){ // via SO - if(typeof s !== 'string'){ return {err: 1} } - var c = 0; - if(!s.length){ return c } - for(var i=0,l=s.length,n; i 1){ too_many_acks } + + clearTimeout(to); + to = setTimeout(test.done, 1000); + }); + }, {acks: config.servers}); + }); + + it("DAM", function(){ + return alice.run(function(test){ + test.async(); + if(ref.say){ said_too_much } + if(ref.hear.length > 1){ heard_to_much } + test.done() + }, {acks: config.servers}); + }); + + it("All finished!", function(done){ + console.log("Done! Cleaning things up..."); + setTimeout(function(){ + done(); + },1000); + }); + + after("Everything shut down.", function(){ + browsers.run(function(){ + //location.reload(); + //setTimeout(function(){ + //}, 15 * 1000); + }); + return servers.run(function(){ + process.exit(); + }); + }); +}); \ No newline at end of file From 4aac986e94493c02ee04c0c052747b3ea7ed164c Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Mon, 27 May 2019 22:06:38 -0700 Subject: [PATCH 5/6] distribute & cap load --- axe.js | 45 +++++++++--- gun.js | 68 +++++++++-------- test/panic/2getget.js | 165 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 234 insertions(+), 44 deletions(-) create mode 100644 test/panic/2getget.js diff --git a/axe.js b/axe.js index ae01f209..a48d8279 100644 --- a/axe.js +++ b/axe.js @@ -110,6 +110,9 @@ } }*/ } + if((tmp = msg['@']) && (tmp = at.dup.s[tmp]) && (tmp = tmp.it)){ + (tmp = (tmp._||ok)).ack = (tmp.ack || 0) + 1; + } to.next(msg); if (opt.rtc && msg.dht) { @@ -141,17 +144,33 @@ var hash = Gun.obj.hash(msg.get); var routes = axe.routes || (axe.routes = {}); // USE RAD INSTEAD! TMP TESTING! var peers = routes[hash]; - if(!peers){ return mesh.say(msg, opt.peers) } - var ids = Object.keys(peers); // TODO: BUG! THIS IS BAD PERFORMANCE!!!! - var meta = (msg._||yes); - var id, peer, c = 1; // opt. ?redundancy? - //console.log("AXE:", msg.get, ids); - while(c-- && (id = ids[(meta.turn = (meta.turn || 0) + 1) - 1])){ // TODO: This has many flaws to it! - peer = peers[id]; - if(false === mesh.say(msg, peer)){ ++c } + function chat(peers, old){ // what about optimizing for directed peers? + if(!peers){ return chat(opt.peers) } + var ids = Object.keys(peers); // TODO: BUG! THIS IS BAD PERFORMANCE!!!! + var meta = (msg._||yes); + clearTimeout(meta.lack); + var id, peer, c = 1; // opt. ?redundancy? + while((id = ids[meta.turn || 0]) && c--){ // TODO: This hits peers in order, not necessarily best for load balancing. And what about optimizing for directed peers? + peer = peers[id]; + meta.turn = (meta.turn || 0) + 1; + if((old && old[id]) || false === mesh.say(msg, peer)){ ++c } + } + //console.log("AXE:", Gun.obj.copy(msg), meta.turn, c, ids, opt.peers === peers); + if(0 < c){ + if(peers === opt.peers){ return } // prevent infinite lack loop. + return meta.turn = 0, chat(opt.peers, peers) + } + var hash = msg['##'], ack = meta.ack; + meta.lack = setTimeout(function(){ + if(ack && hash && hash === msg['##']){ return } + if(meta.turn >= (axe.turns || 3)){ return } // variable for later! Also consider ACK based turn limit. + //console.log(msg['#'], "CONTINUE:", ack, hash, msg['##']); + chat(peers, old); // keep asking for data if there is mismatching hashes. + }, 25); } - return; + return chat(peers); } + // TODO: PUTs need to only go to subs! mesh.say(msg, opt.peers); return; // TODO: DISABLE THIS!!! USE DHT! @@ -193,6 +212,14 @@ }, 500); }, at);*/ } + at.on('bye', function(peer){ this.to.next(peer); + Gun.obj.map(peer.routes, function(route, hash){ + delete route[peer.id]; + if(Gun.obj.empty(route)){ + delete axe.routes[hash]; + } + }); + }); } this.to.next(at); // make sure to call the "next" middleware adapter. }); diff --git a/gun.js b/gun.js index 214d4172..a8646b05 100644 --- a/gun.js +++ b/gun.js @@ -1168,7 +1168,7 @@ if(u === tmp && u !== at.put){ return true } neat.put = u; if(neat.ack){ - neat.ack = -1; + neat.ack = -1; // TODO: BUG? Should this be 0? } neat.on('in', { get: key, @@ -1694,6 +1694,7 @@ var gun = this, at = gun._, tmp; var cat = at.back; if(!cat){ return } + at.ack = 0; // so can resubscribe. if(tmp = cat.next){ if(tmp[at.get]){ obj_del(tmp, at.get); @@ -1825,7 +1826,7 @@ } root.on('out', function(msg){ - if(msg.lS){ return } + if(msg.lS){ return } // TODO: for IndexedDB and others, shouldn't send to peers ACKs to our own GETs. if(Gun.is(msg.$) && msg.put && !msg['@'] && !empty(opt.peers)){ id = msg['#']; Gun.graph.is(msg.put, null, map); @@ -1898,11 +1899,9 @@ if(data && has){ data = Gun.state.to(data, has); } - 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? - } + //if(!data && !Gun.obj.empty(opt.peers)){ return } // if data not found, don't ack if there are peers. // Hmm, what if we have peers but we are disconnected? //console.log("lS get", lex, data); - root.on('in', {'@': msg['#'], put: Gun.graph.node(data), how: 'lS', lS: msg.$ || root.$}); + root.on('in', {'@': msg['#'], put: Gun.graph.node(data), how: 'lS', lS: msg.$});// || root.$}); }; Gun.debug? setTimeout(to,1) : to(); }); @@ -1951,17 +1950,26 @@ mesh.hear = function(raw, peer){ if(!raw){ return } var msg, id, hash, tmp = raw[0]; - if(opt.pack <= raw.length){ return mesh.say({dam: '!', err: "Message too big!"}, peer) } - if('{' === tmp){ - try{msg = JSON.parse(raw); + if(opt.pack <= raw.length){ return mesh.say({dam: '!', err: "Message too big!"}, peer) } + if('{' != raw[2]){ mesh.hear.d += raw.length||0; ++mesh.hear.c; } // STATS! // ugh, stupid double JSON encoding + if('[' === tmp){ + try{msg = JSON.parse(raw);}catch(e){opt.log('DAM JSON parse error', e)} + if(!msg){ return } + var i = 0, m; + while(m = msg[i++]){ + mesh.hear(m, peer); + } + return; + } + if('{' === tmp || (Type.obj.is(raw) && (msg = raw))){ + try{msg = msg || JSON.parse(raw); }catch(e){return opt.log('DAM JSON parse error', e)} if(!msg){ return } - mesh.hear.d += raw.length; ++mesh.hear.c; // STATS! if(!(id = msg['#'])){ id = msg['#'] = Type.text.random(9) } if(dup.check(id)){ return } dup.track(id, true).it = msg; // GUN core also dedups, so `true` is needed. // Does GUN core need to dedup anymore? if(!(hash = msg['##']) && u !== msg.put){ hash = msg['##'] = Type.obj.hash(msg.put) } - if(hash && (tmp = msg['@'])){ + if(hash && (tmp = msg['@'] || (msg.get && id))){ // Reduces backward daisy in case varying hashes at different daisy depths are the same. if(dup.check(tmp+hash)){ return } dup.track(tmp+hash, true).it = msg; // GUN core also dedups, so `true` is needed. // Does GUN core need to dedup anymore? } @@ -1975,15 +1983,6 @@ } root.on('in', msg); return; - } else - if('[' === tmp){ - try{msg = JSON.parse(raw);}catch(e){opt.log('DAM JSON parse error', e)} - if(!msg){ return } - var i = 0, m; - while(m = msg[i++]){ - mesh.hear(m, peer); - } - return; } } var tomap = function(k,i,m){m(k,true)}; @@ -2054,7 +2053,7 @@ if(wire.send){ wire.send(raw); } - mesh.say.d += raw.length; ++mesh.say.c; // STATS! + mesh.say.d += raw.length||0; ++mesh.say.c; // STATS! }catch(e){ (peer.queue = peer.queue || []).push(raw); }} @@ -2068,7 +2067,7 @@ if(!msg.dam){ var i = 0, to = []; Type.obj.map(opt.peers, function(p){ to.push(p.url || p.pid || p.id); if(++i > 9){ return true } // limit server, fast fix, improve later! // For "tower" peer, MUST include 6 surrounding ids. - }); msg['><'] = to.join(); + }); if(i > 1){ msg['><'] = to.join() } } var raw = $(msg); // optimize by reusing put = the JSON.stringify from .hash? /*if(u !== put){ @@ -2127,19 +2126,18 @@ root.on('out', mesh.say); }); - if(!opt.super){ - var gets = {}; - root.on('bye', function(peer, tmp){ - if(!(tmp = peer.url)){ return } gets[tmp] = true; - setTimeout(function(){ delete gets[tmp] },opt.lack || 9000) - }); - root.on('hi', function(peer, tmp){ - if(!(tmp = peer.url) || !gets[tmp]){ return } delete gets[tmp]; - Type.obj.map(root.graph, function(node, soul){ tmp = {}; tmp[soul] = node; - mesh.say({'##': Type.obj.hash(tmp), get: {'#': soul}}, peer); - }) - }); - } + var gets = {}; + root.on('bye', function(peer, tmp){ this.to.next(peer); + if(!(tmp = peer.url)){ return } gets[tmp] = true; + setTimeout(function(){ delete gets[tmp] },opt.lack || 9000); + }); + root.on('hi', function(peer, tmp){ this.to.next(peer); + if(!(tmp = peer.url) || !gets[tmp]){ return } delete gets[tmp]; + Type.obj.map(root.next, function(node, soul){ + tmp = {}; tmp[soul] = root.graph[soul]; + mesh.say({'##': Type.obj.hash(tmp), get: {'#': soul}}, peer); + }) + }); return mesh; } diff --git a/test/panic/2getget.js b/test/panic/2getget.js new file mode 100644 index 00000000..65bd2920 --- /dev/null +++ b/test/panic/2getget.js @@ -0,0 +1,165 @@ +/* +This is the first in a series of basic networking correctness tests. +Each test itself might be dumb and simple, but built up together, +they prove desired end goals for behavior at scale. +1. (this file) Is a browser write is confirmed as save by multiple peers even if by daisy chain. +2. +*/ + +var config = { + IP: require('ip').address(), + port: 8765, + servers: 1, + browsers: 3, + route: { + '/': __dirname + '/index.html', + '/gun.js': __dirname + '/../../gun.js', + '/jquery.js': __dirname + '/../../examples/jquery.js' + } +} + +var panic = require('panic-server'); +panic.server().on('request', function(req, res){ + config.route[req.url] && require('fs').createReadStream(config.route[req.url]).pipe(res); +}).listen(config.port); + +var clients = panic.clients; +var manager = require('panic-manager')(); + +manager.start({ + clients: Array(config.servers).fill().map(function(u, i){ + return { + type: 'node', + port: config.port + (i + 1) + } + }), + panic: 'http://' + config.IP + ':' + config.port +}); + +var servers = clients.filter('Node.js'); +var bob = servers.pluck(1); +var browsers = clients.excluding(servers); +var alice = browsers.pluck(1); +var carl = browsers.excluding(alice).pluck(1); +var dave = browsers.excluding([alice, carl]).pluck(1); +var cd = new panic.ClientList([carl, dave]); + +describe("Put ACK", function(){ + //this.timeout(5 * 60 * 1000); + this.timeout(10 * 60 * 1000); + + it("Servers have joined!", function(){ + return servers.atLeast(config.servers); + }); + + it("GUN started!", function(){ + var tests = [], i = 0; + servers.each(function(client){ + tests.push(client.run(function(test){ + var env = test.props; + test.async(); + try{ require('fs').unlinkSync(env.i+'data') }catch(e){} + try{ require('gun/lib/fsrm')(env.i+'data') }catch(e){} + var server = require('http').createServer(function(req, res){ + res.end("I am "+ env.i +"!"); + }); + var port = env.config.port + env.i; + var Gun = require('gun'); + var peers = [], i = env.config.servers; + while(i--){ + var tmp = (env.config.port + (i + 1)); + if(port != tmp){ // ignore ourselves + peers.push('http://'+ env.config.IP + ':' + tmp + '/gun'); + } + } + console.log(port, " connect to ", peers); + var gun = Gun({file: env.i+'data', peers: peers, web: server}); + server.listen(port, function(){ + test.done(); + }); + }, {i: i += 1, config: config})); + }); + return Promise.all(tests); + }); + + it(config.browsers +" browser(s) have joined!", function(){ + console.log("PLEASE OPEN http://"+ config.IP +":"+ config.port +" IN "+ config.browsers +" BROWSER(S)!"); + return browsers.atLeast(config.browsers); + }); + + it("Browsers initialized gun!", function(){ + var tests = [], i = 0; + browsers.each(function(client, id){ + tests.push(client.run(function(test){ + try{ localStorage.clear() }catch(e){} + try{ indexedDB.deleteDatabase('radata') }catch(e){} + var env = test.props; + var gun = Gun('http://'+ env.config.IP + ':' + (env.config.port + 1) + '/gun'); + window.gun = gun; + window.ref = gun.get('a'); + }, {i: i += 1, config: config})); + }); + return Promise.all(tests); + }); + + + it("connect", function(){ + return alice.run(function(test){ + console.log("I AM ALICE"); + test.async(); + gun.get('random').get(function(ack){ + setTimeout(function(){ + test.done(); + }, 1000); + }) + }); + }); + + it("Put", function(){ + return alice.run(function(test){ + test.async(); + + var say = ref._.root.opt.mesh.say; + ref._.root.opt.mesh.say = function(){}; // prevent from syncing + + var c = 0; + ref.put({hello: 'world'}, function(ack){ ++c }); + setTimeout(function(){ + ref._.root.opt.mesh.say = say; + if(c){ should_not_have_ack } + test.done(); + }, 1000); + }); + }); + + it("Get", function(){ + return cd.run(function(test){ + test.async(); + console.log("I am Carl or Dave"); + ref.get(function(ack){ + console.log('ack', ack); + if(ack.put){ + test.done(); + } + }); + }); + }); + + it("All finished!", function(done){ + console.log("Done! Cleaning things up..."); + setTimeout(function(){ + done(); + },1000); + }); + + after("Everything shut down.", function(){ + browsers.run(function(){ + //location.reload(); + //setTimeout(function(){ + //}, 15 * 1000); + }); + return servers.run(function(){ + process.exit(); + }); + }); +}); \ No newline at end of file From cfd7d1042d92fe15c89089611f1752840474098b Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Tue, 28 May 2019 00:14:23 -0700 Subject: [PATCH 6/6] "fix" holy grail, unbuild. --- gun.js | 3 +- gun.min.js | 2 +- src/adapters/localStorage.js | 11 +- src/adapters/mesh.js | 322 +++++++++++++++++------------------ src/adapters/websocket.js | 4 +- src/chain.js | 2 +- src/on.js | 1 + src/put.js | 4 +- src/root.js | 2 +- test/panic/holy-grail.js | 6 +- 10 files changed, 179 insertions(+), 178 deletions(-) diff --git a/gun.js b/gun.js index a8646b05..1dd3c2e2 100644 --- a/gun.js +++ b/gun.js @@ -1821,13 +1821,14 @@ }); }); setTimeout(function(){ + // TODO: Holy Grail dangling by this thread! If gap / offline resync doesn't trigger, it doesn't work. Ouch, and this is a localStorage specific adapter. :( root.on('out', {put: send, '#': root.ask(ack)}); },1); } root.on('out', function(msg){ if(msg.lS){ return } // TODO: for IndexedDB and others, shouldn't send to peers ACKs to our own GETs. - if(Gun.is(msg.$) && msg.put && !msg['@'] && !empty(opt.peers)){ + if(Gun.is(msg.$) && msg.put && !msg['@']){ id = msg['#']; Gun.graph.is(msg.put, null, map); if(!to){ to = setTimeout(flush, opt.wait || 1) } diff --git a/gun.min.js b/gun.min.js index 4c75d163..f9e7322f 100644 --- a/gun.min.js +++ b/gun.min.js @@ -1 +1 @@ -!function(){var t;"undefined"!=typeof window&&(t=window),"undefined"!=typeof global&&(t=global);var b=(t=t||{}).console||{log:function(){}};function _(o,t){return t?require(o):o.slice?_[i(o)]:function(t,n){o(t={exports:{}}),_[i(n)]=t.exports};function i(t){return t.split("/").slice(-1).toString().replace(".js","")}}if("undefined"!=typeof module)var f=module;_(function(t){var p={fn:{is:function(t){return!!t&&"function"==typeof t}}};p.bi={is:function(t){return t instanceof Boolean||"boolean"==typeof t}},p.num={is:function(t){return!d(t)&&(0<=t-parseFloat(t)+1||1/0===t||-1/0===t)}},p.text={is:function(t){return"string"==typeof t}},p.text.ify=function(t){return p.text.is(t)?t:"undefined"!=typeof JSON?JSON.stringify(t):t&&t.toString?t.toString():t},p.text.random=function(t,n){var o="";for(t=t||24,n=n||"0123456789ABCDEFGHIJKLMNOPQRSTUVWXZabcdefghijklmnopqrstuvwxyz";0"]||n["<"])||i===n["="]&&(o=n["*"]||n[">"]||n["<"],t.slice(0,(o||"").length)===o||i===n["*"]&&(i!==n[">"]&&i!==n["<"]?t>=n[">"]&&t<=n["<"]:i!==n[">"]&&t>=n[">"]||i!==n["<"]&&t<=n["<"])))},p.list={is:function(t){return t instanceof Array}},p.list.slit=Array.prototype.slice,p.list.sort=function(o){return function(t,n){return t&&n?(t=t[o])<(n=n[o])?-1:n",s.drift=0,s.is=function(t,n,o){var i=n&&t&&t[m]&&t[m][s._]||o;if(i)return g(i=i[n])?i:-1/0},s.lex=function(){return s().toString(36).replace(".","")},s.ify=function(t,n,o,i,e){if(!t||!t[m]){if(!e)return;t=a.soul.ify(t,e)}var r=c(t[m],s._);return void 0!==n&&n!==m&&(g(o)&&(r[n]=o),void 0!==i&&(t[n]=i)),t},s.to=function(t,n,o){var i=(t||{})[n];return p(i)&&(i=d(i)),s.ify(o,n,s.is(t,n),i,a.soul(t))},function(){function u(t,n){m!==n&&s.ify(this.o,n,this.s)}s.map=function(e,r,a){var t=p(t=e||r)?t:null;return e=v(e=e||r)?e:null,t&&!e?(r=g(r)?r:s(),t[m]=t[m]||{},h(t,u,{o:t,s:r}),t):(a=a||p(r)?r:void 0,r=g(r)?r:s(),function(t,n,o,i){if(!e)return u.call({o:o,s:r},t,n),t;e.call(a||this||{},t,n,o,i),l(o,n)&&void 0===o[n]||u.call({o:o,s:r},t,n)})}}();var f=n.obj,c=f.as,l=f.has,p=f.is,h=f.map,d=f.copy,g=n.num.is,v=n.fn.is,m=a._;t.exports=s})(_,"./state"),_(function(t){var a=_("./type"),f=_("./val"),c=_("./node"),r={};!function(){function e(t,n){if(!t||n!==c.soul(t)||!c.is(t,this.fn,this.as))return!0;this.cb&&(o.n=t,o.as=this.as,this.cb.call(o.as,t,n,o))}function o(t){t&&c.is(o.n,t,o.as)}r.is=function(t,n,o,i){return!(!t||!l(t)||u(t))&&!s(t,e,{cb:n,fn:o,as:i})}}(),function(){function u(t,n){var o;return(o=function(t,n){var o,i=t.seen,e=i.length;for(;e--;)if(o=i[e],n.obj===o.obj)return o;i.push(n)}(t,n))?o:(n.env=t,n.soul=e,c.ify(n.obj,i,n)&&(n.link=n.link||f.link.ify(c.soul(n.node)),n.obj!==t.shell&&(t.graph[f.link.is(n.link)]=n.node)),n)}function i(t,n,o){var i,e,r=this,a=r.env;if(c._===n&&h(t,f.link._))return o._;if(i=s(t,n,o,r,a)){if(n||(r.node=r.node||o||{},h(t,c._)&&c.soul(t)&&(r.node._=d(t._)),r.node=c.soul.ify(r.node,f.link.is(r.link)),r.link=r.link||f.link.ify(c.soul(r.node))),(e=a.map)&&(e.call(a.as||{},t,n,o,r),h(o,n))){if(void 0===(t=o[n]))return void p(o,n);if(!(i=s(t,n,o,r,a)))return}if(!n)return r.node;if(!0===i)return t;if((e=u(a,{obj:t,path:r.path.concat(n)})).node)return e.link}}function e(t){var n=this,o=f.link.is(n.link),i=n.env.graph;n.link=n.link||f.link.ify(t),n.link[f.link._]=t,n.node&&n.node[c._]&&(n.node[c._][f.link._]=t),h(i,o)&&(i[t]=i[o],p(i,o))}function s(t,n,o,i,e){var r;return!!f.is(t)||(l(t)?1:(r=e.invalid)?s(t=r.call(e.as||{},t,n,o),n,o,i,e):(e.err="Invalid value at '"+i.path.concat(n).join(".")+"'!",void(a.list.is(t)&&(e.err+=" Use `.set(item)` instead of an Array."))))}r.ify=function(t,n,o){var i={path:[],obj:t};return n?"string"==typeof n?n={soul:n}:n instanceof Function&&(n.map=n):n={},n.soul&&(i.link=f.link.ify(n.soul)),n.shell=(o||{}).shell,n.graph=n.graph||{},n.seen=n.seen||[],n.as=n.as||o,u(n,i),n.root=i.node,n.graph}}(),r.node=function(t){var n=c.soul(t);if(n)return o({},n,t)},function(){function e(t,n){var o,i;if(c._!==n)(o=f.link.is(t))?(i=this.opt.seen[o])?this.obj[n]=i:this.obj[n]=this.opt.seen[o]=r.to(this.graph,o,this.opt):this.obj[n]=t;else{if(u(t,f.link._))return;this.obj[n]=d(t)}}r.to=function(t,n,o){if(t){var i={};return o=o||{seen:{}},s(t[n],e,{obj:i,graph:t,opt:o}),i}}}();a.fn.is;var n=a.obj,l=n.is,p=n.del,h=n.has,u=n.empty,o=n.put,s=n.map,d=n.copy;t.exports=r})(_,"./graph"),_(function(t){_("./onto"),t.exports=function(t,n){if(this.on){if(!(t instanceof Function)){if(!t||!n)return;var o=t["#"]||t,i=(this.tag||empty)[o];if(!i)return;return i=this.on(o,n),clearTimeout(i.err),!0}o=n&&n["#"]||Math.random().toString(36).slice(2);if(!t)return o;var e=this.on(o,t,n);return e.err=e.err||setTimeout(function(){e.next({err:"Error: No ACK received yet.",lack:!0}),e.off()},(this.opt||{}).lack||9e3),o}}})(_,"./ask"),_(function(t){var r=_("./type");var a=r.time.is;t.exports=function(i){var e={s:{}};return i=i||{max:1e3,age:9e3},e.check=function(t){var n;return!!(n=e.s[t])&&(n.pass?n.pass=!1:e.track(t))},e.track=function(t,n){var o=e.s[t]||(e.s[t]={});return o.was=a(),n&&(o.pass=!0),e.to||(e.to=setTimeout(function(){var o=a();r.obj.map(e.s,function(t,n){t&&i.age>o-t.was||r.obj.del(e.s,n)}),e.to=null},i.age+9)),o},e}})(_,"./dup"),_(function(t){function c(t){return t instanceof c?(this._={gun:this,$:this}).$:this instanceof c?c.create(this._={gun:this,$:this,opt:t}):new c(t)}c.is=function(t){return t instanceof c||t&&t._&&t===t._.$||!1},c.version=.9,(c.chain=c.prototype).toJSON=function(){};var n=_("./type");n.obj.to(n,c),c.HAM=_("./HAM"),c.val=_("./val"),c.node=_("./node"),c.state=_("./state"),c.graph=_("./graph"),c.on=_("./onto"),c.ask=_("./ask"),c.dup=_("./dup"),function(){function a(t){var n,o,i=this.as,e=i.at||i,r=e.$;(o=t["#"])||(o=t["#"]=u(9)),(n=e.dup).check(o)?i.out===t.out&&(t.out=void 0,this.to.next(t)):(n.track(o),e.ask(t["@"],t)||(t.get&&c.on.get(t,r),t.put&&c.on.put(t,r)),this.to.next(t),i.out||(t.out=a,e.on("out",t)))}c.create=function(t){t.root=t.root||t,t.graph=t.graph||{},t.on=t.on||c.on,t.ask=t.ask||c.ask,t.dup=t.dup||c.dup();var n=t.$.opt(t.opt);return t.once||(t.on("in",a,t),t.on("out",a,{at:t,out:a}),c.on("create",t),t.on("create",t)),t.once=1,n}}(),function(){function e(t,n,o,i){var e=this,r=c.state.is(o,n);if(!r)return e.err="Error: No state on '"+n+"' in node '"+i+"'!";var a=e.graph[i]||v,u=c.state.is(a,n,!0),s=a[n],f=c.HAM(e.machine,r,u,t,s);f.incoming?(e.put[i]=c.state.to(o,n,e.put[i]),(e.diff||(e.diff={}))[i]=c.state.to(o,n,e.diff[i]),e.souls[i]=!0):f.defer&&(e.defer=r<(e.defer||1/0)?r:e.defer)}function r(t,n){var o=this,i=o.$._,e=(i.next||v)[n];if(!e){if(!(i.opt||v).super)return void(o.souls[n]=!1);e=o.$.get(n)._}var r=o.map[n]={put:t,get:n,$:e.$},a={ctx:o,msg:r};o.async=!!i.tag.node,o.ack&&(r["@"]=o.ack),h(t,u,a),o.async&&(o.and||i.on("node",function(t){this.to.next(t),t===o.map[t.get]&&(o.souls[t.get]=!1,h(t.put,s,t),h(o.souls,function(t){if(t)return t})||o.c||(o.c=1,this.off(),h(o.map,f,o)))}),o.and=!0,i.on("node",r))}function u(t,n){var o=this.ctx,i=o.graph,e=this.msg,r=e.get,a=e.put,u=e.$._;i[r]=c.state.to(a,n,i[r]),o.async||(u.put=c.state.to(a,n,u.put))}function s(t,n){var o=this.put,i=this.$._;i.put=c.state.to(o,n,i.put)}function f(t,n){t.$&&(this.cat.stop=this.stop,t.$._.on("in",t),this.cat.stop=null)}c.on.put=function(t,n){var o=n._,i={$:n,graph:o.graph,put:{},map:{},souls:{},machine:c.state(),ack:t["@"],cat:o,stop:{}};if(c.graph.is(t.put,null,e,i)||(i.err="Error: Invalid graph!"),i.err)return o.on("in",{"@":t["#"],err:c.log(i.err)});h(i.put,r,i),i.async||h(i.map,f,i),void 0!==i.defer&&setTimeout(function(){c.on.put(t,n)},i.defer-i.machine),i.diff&&o.on("put",p(t,{put:i.diff}))},c.on.get=function(t,n){var o=n._,i=t.get,e=i[d],r=o.graph[e],a=i[g],u=(o.next||(o.next={}))[e];if(!r)return o.on("get",t);if(a){if("string"!=typeof a||!l(r,a))return o.on("get",t);r=c.state.to(r,a)}else r=c.obj.copy(r);r=c.graph.node(r),(u||v).ack,o.on("in",{"@":t["#"],how:"mem",put:r,$:n}),o.on("get",t)}}(),c.chain.opt=function(t){t=t||{};var n=this._,o=t.peers||t;return a(t)||(t={}),a(n.opt)||(n.opt=t),e(o)&&(o=[o]),i(o)&&(o=h(o,function(t,n,o){o(t,{url:t})}),a(n.opt.peers)||(n.opt.peers={}),n.opt.peers=p(o,n.opt.peers)),n.opt.peers=n.opt.peers||{},p(t,n.opt),c.on("opt",n),n.opt.uuid=n.opt.uuid||function(){return s()+u(12)},this};var i=c.list.is,o=c.text,e=o.is,u=o.random,r=c.obj,a=r.is,l=r.has,p=r.to,h=r.map,s=(r.copy,c.state.lex),d=c.val.link._,g=".",v=(c.node._,c.val.link.is,{});b.debug=function(t,n){return b.debug.i&&t===b.debug.i&&b.debug.i++&&(b.log.apply(b,arguments)||n)},(c.log=function(){return!c.log.off&&b.log.apply(b,arguments),[].slice.call(arguments).join(" ")}).once=function(t,n,o){return(o=c.log.once)[t]=o[t]||0,o[t]++||c.log(n)},c.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=window.Gun=c).window=window);try{void 0!==f&&(f.exports=c)}catch(t){}t.exports=c})(_,"./root"),_(function(t){var u=_("./root");u.chain.back=function(t,n){if(-1===(t=t||1)||1/0===t)return this._.root.$;if(1===t)return(this._.back||this._).$;var o=this._;if("string"==typeof t&&(t=t.split(".")),t instanceof Array){for(var i=0,e=t.length,r=o;i .once, apologies unexpected."),this.once(t,n)},f.chain.once=function(t,n){var o=this,i=o._,e=i.put;if(0=(a.batch||1e3))return f();i||(i=setTimeout(f,a.wait||1))}),r.on("get",function(n){this.to.next(n);var o,i,e=n.get;function t(){if(e&&(o=e["#"])){var t=e["."];(i=s[o]||void 0)&&t&&(i=Gun.state.to(i,t)),(i||Gun.obj.empty(a.peers))&&r.on("in",{"@":n["#"],put:Gun.graph.node(i),how:"lS",lS:n.$||r.$})}}Gun.debug?setTimeout(t,1):t()});var n=function(t,n,o,i){s[i]=Gun.state.to(o,n,s[i])},f=function(t){var o;u=0,clearTimeout(i),i=!1;var n=e;e={},t&&(s=t);try{p.setItem(a.prefix,JSON.stringify(s))}catch(t){Gun.log(o=(t||"localStorage failure")+" Consider using GUN's IndexedDB plugin for RAD for more storage space, temporary example at https://github.com/amark/gun/blob/master/test/tmp/indexedDB.html ."),r.on("localStorage:error",{err:o,file:a.prefix,flush:s,retry:f})}(o||Gun.obj.empty(a.peers))&&Gun.obj.map(n,function(t,n){r.on("in",{"@":n,err:o,ok:0})})}}})}})(_,"./adapters/localStorage"),_(function(t){var o=_("../index"),g=_("../type");function e(p){var h=function(){},d=p.opt||{};d.log=d.log||b.log,d.gap=d.gap||d.wait||1,d.pack=d.pack||.3*(d.memory?1e3*d.memory*1e3:1399e6),h.out=function(t){var n;if(this.to&&this.to.next(t),(n=t["@"])&&(n=p.dup.s[n])&&(n=n.it)&&n._)return h.say(t,n._.via,1),void(n["##"]=t["##"]);o.AXE?o.AXE.say(t,h.say,this):h.say(t)},p.on("create",function(t){t.opt.pid=t.opt.pid||g.text.random(9),this.to.next(t),p.on("out",h.out)}),(h.hear=function(t,n){if(t){var o,i,e,r=p.dup,a=t[0];if(d.pack<=t.length)return h.say({dam:"!",err:"Message too big!"},n);if("{"===a){try{e=JSON.parse(t)}catch(t){d.log("DAM JSON parse error",t)}if(!e)return;if(h.hear.d+=t.length,++h.hear.c,r.check(o=e["#"]))return;if((a=(r.track(o,!0).it=e)["@"])&&e.put&&(a+=i=e["##"]||(e["##"]=h.hash(e)))!=o){if(r.check(a))return;(a=r.s)[i]=a[o]}return(e._=function(){}).via=n,(a=e["><"])&&(e._.to=g.obj.map(a.split(","),f)),e.dam?void((a=h.hear[e.dam])&&a(e,n,p)):void p.on("in",e)}if("["!==a);else{try{e=JSON.parse(t)}catch(t){d.log("DAM JSON parse error",t)}if(!e)return;for(var u,s=0;u=e[s++];)h.hear(u,n)}}}).c=h.hear.d=0;var f=function(t,n,o){o(t,!0)};return function(){var a;function u(t){h.say(a,t)}function s(t){var n=t.batch;if(n&&(t.batch=t.tail=null,n.length))try{f(JSON.stringify(n),t)}catch(t){d.log("DAM JSON stringify error",t)}}function f(n,o){var t=o.wire;try{o.say?o.say(n):t.send&&t.send(n),h.say.d+=n.length,++h.say.c}catch(t){(o.queue=o.queue||[]).push(n)}}(h.say=function(t,n,o){if(!n)return a=t,void g.obj.map(d.peers,u);var i,e,r;if((n.wire||d.wire&&d.wire(n))&&(e=t._||c,n!==e.via&&((r=e.raw)||(r=h.raw(t)),!((i=t["@"])&&(i=p.dup.s[i])&&(i=i.it)&&i.get&&i["##"]&&i["##"]===t["##"])&&(!(i=e.to)||!i[n.url]&&!i[n.id]||o)))){if(n.batch){if(n.tail=(n.tail||0)+r.length,n.tail<=d.pack)return void n.batch.push(r);s(n)}n.batch=[],setTimeout(function(){s(n)},d.gap),f(r,n)}}).c=h.say.d=0}(),function(){function f(t,n){var o;return n instanceof Object?(g.obj.map(Object.keys(n).sort(),i,{to:o={},on:n}),o):n}function i(t){this.to[t]=this.on[t]}h.raw=function(t){if(!t)return"";var n,o,i,e=p.dup,r=t._||{};if(i=r.raw)return i;if("string"==typeof t)return t;t["@"]&&(i=t.put)&&((o=t["##"])||(n=c(i,f)||"",o=h.hash(t,n),t["##"]=o),(i=e.s)[o=t["@"]+o]=i[t["#"]],t["#"]=o||t["#"],n&&((t=g.obj.to(t)).put=l));var a=0,u=[];g.obj.map(d.peers,function(t){if(u.push(t.url||t.id),9<++a)return!0}),t["><"]=u.join();var s=c(t);return v!==n&&(i=s.indexOf(l,s.indexOf("put")),s=s.slice(0,i-1)+n+s.slice(i+l.length+1)),r&&(r.raw=s),s},h.hash=function(t,n){return e.hash(n||c(t.put,f)||"")||t["#"]||g.text.random(9)};var c=JSON.stringify,l=":])([:"}(),h.hi=function(n){var t=n.wire||{};n.id||n.url?d.peers[n.url||n.id]=n:(t=n.id=t.pid=n.id||g.text.random(9),h.say({dam:"?"},d.peers[t]=n)),t.hied||p.on(t.hied="hi",n),t=n.queue,n.queue=[],g.obj.map(t,function(t){h.say(t,n)})},h.bye=function(t){g.obj.del(d.peers,t.id),p.on("bye",t)},h.hear["!"]=function(t,n){d.log("Error:",t.err)},h.hear["?"]=function(t,n){t.pid?n.wire&&n.wire.pid&&(g.obj.del(d.peers,n.wire.pid||n.id),delete n.wire.pid,n.id=t.pid,h.hi(n)):h.say({dam:"?",pid:d.pid,"@":t["#"]},n)},h}e.hash=function(t){if("string"!=typeof t)return{err:1};var n=0;if(!t.length)return n;for(var o=0,i=t.length;o"]||n["<"])||i===n["="]&&(o=n["*"]||n[">"]||n["<"],t.slice(0,(o||"").length)===o||i===n["*"]&&(i!==n[">"]&&i!==n["<"]?t>=n[">"]&&t<=n["<"]:i!==n[">"]&&t>=n[">"]||i!==n["<"]&&t<=n["<"])))},p.list={is:function(t){return t instanceof Array}},p.list.slit=Array.prototype.slice,p.list.sort=function(o){return function(t,n){return t&&n?(t=t[o])<(n=n[o])?-1:n",s.drift=0,s.is=function(t,n,o){var i=n&&t&&t[m]&&t[m][s._]||o;if(i)return g(i=i[n])?i:-1/0},s.lex=function(){return s().toString(36).replace(".","")},s.ify=function(t,n,o,i,e){if(!t||!t[m]){if(!e)return;t=a.soul.ify(t,e)}var r=c(t[m],s._);return void 0!==n&&n!==m&&(g(o)&&(r[n]=o),void 0!==i&&(t[n]=i)),t},s.to=function(t,n,o){var i=(t||{})[n];return p(i)&&(i=d(i)),s.ify(o,n,s.is(t,n),i,a.soul(t))},function(){function u(t,n){m!==n&&s.ify(this.o,n,this.s)}s.map=function(e,r,a){var t=p(t=e||r)?t:null;return e=v(e=e||r)?e:null,t&&!e?(r=g(r)?r:s(),t[m]=t[m]||{},h(t,u,{o:t,s:r}),t):(a=a||p(r)?r:void 0,r=g(r)?r:s(),function(t,n,o,i){if(!e)return u.call({o:o,s:r},t,n),t;e.call(a||this||{},t,n,o,i),l(o,n)&&void 0===o[n]||u.call({o:o,s:r},t,n)})}}();var f=n.obj,c=f.as,l=f.has,p=f.is,h=f.map,d=f.copy,g=n.num.is,v=n.fn.is,m=a._;t.exports=s})(_,"./state"),_(function(t){var a=_("./type"),f=_("./val"),c=_("./node"),r={};!function(){function e(t,n){if(!t||n!==c.soul(t)||!c.is(t,this.fn,this.as))return!0;this.cb&&(o.n=t,o.as=this.as,this.cb.call(o.as,t,n,o))}function o(t){t&&c.is(o.n,t,o.as)}r.is=function(t,n,o,i){return!(!t||!l(t)||u(t))&&!s(t,e,{cb:n,fn:o,as:i})}}(),function(){function u(t,n){var o;return(o=function(t,n){var o,i=t.seen,e=i.length;for(;e--;)if(o=i[e],n.obj===o.obj)return o;i.push(n)}(t,n))?o:(n.env=t,n.soul=e,c.ify(n.obj,i,n)&&(n.link=n.link||f.link.ify(c.soul(n.node)),n.obj!==t.shell&&(t.graph[f.link.is(n.link)]=n.node)),n)}function i(t,n,o){var i,e,r=this,a=r.env;if(c._===n&&h(t,f.link._))return o._;if(i=s(t,n,o,r,a)){if(n||(r.node=r.node||o||{},h(t,c._)&&c.soul(t)&&(r.node._=d(t._)),r.node=c.soul.ify(r.node,f.link.is(r.link)),r.link=r.link||f.link.ify(c.soul(r.node))),(e=a.map)&&(e.call(a.as||{},t,n,o,r),h(o,n))){if(void 0===(t=o[n]))return void p(o,n);if(!(i=s(t,n,o,r,a)))return}if(!n)return r.node;if(!0===i)return t;if((e=u(a,{obj:t,path:r.path.concat(n)})).node)return e.link}}function e(t){var n=this,o=f.link.is(n.link),i=n.env.graph;n.link=n.link||f.link.ify(t),n.link[f.link._]=t,n.node&&n.node[c._]&&(n.node[c._][f.link._]=t),h(i,o)&&(i[t]=i[o],p(i,o))}function s(t,n,o,i,e){var r;return!!f.is(t)||(l(t)?1:(r=e.invalid)?s(t=r.call(e.as||{},t,n,o),n,o,i,e):(e.err="Invalid value at '"+i.path.concat(n).join(".")+"'!",void(a.list.is(t)&&(e.err+=" Use `.set(item)` instead of an Array."))))}r.ify=function(t,n,o){var i={path:[],obj:t};return n?"string"==typeof n?n={soul:n}:n instanceof Function&&(n.map=n):n={},n.soul&&(i.link=f.link.ify(n.soul)),n.shell=(o||{}).shell,n.graph=n.graph||{},n.seen=n.seen||[],n.as=n.as||o,u(n,i),n.root=i.node,n.graph}}(),r.node=function(t){var n=c.soul(t);if(n)return o({},n,t)},function(){function e(t,n){var o,i;if(c._!==n)(o=f.link.is(t))?(i=this.opt.seen[o])?this.obj[n]=i:this.obj[n]=this.opt.seen[o]=r.to(this.graph,o,this.opt):this.obj[n]=t;else{if(u(t,f.link._))return;this.obj[n]=d(t)}}r.to=function(t,n,o){if(t){var i={};return o=o||{seen:{}},s(t[n],e,{obj:i,graph:t,opt:o}),i}}}();a.fn.is;var n=a.obj,l=n.is,p=n.del,h=n.has,u=n.empty,o=n.put,s=n.map,d=n.copy;t.exports=r})(_,"./graph"),_(function(t){_("./onto"),t.exports=function(t,n){if(this.on){if(!(t instanceof Function)){if(!t||!n)return;var o=t["#"]||t,i=(this.tag||empty)[o];if(!i)return;return i=this.on(o,n),clearTimeout(i.err),!0}o=n&&n["#"]||Math.random().toString(36).slice(2);if(!t)return o;var e=this.on(o,t,n);return e.err=e.err||setTimeout(function(){e.next({err:"Error: No ACK received yet.",lack:!0}),e.off()},(this.opt||{}).lack||9e3),o}}})(_,"./ask"),_(function(t){var r=_("./type");var a=r.time.is;t.exports=function(i){var e={s:{}};return i=i||{max:1e3,age:9e3},e.check=function(t){var n;return!!(n=e.s[t])&&(n.pass?n.pass=!1:e.track(t))},e.track=function(t,n){var o=e.s[t]||(e.s[t]={});return o.was=a(),n&&(o.pass=!0),e.to||(e.to=setTimeout(function(){var o=a();r.obj.map(e.s,function(t,n){t&&i.age>o-t.was||r.obj.del(e.s,n)}),e.to=null},i.age+9)),o},e}})(_,"./dup"),_(function(t){function c(t){return t instanceof c?(this._={gun:this,$:this}).$:this instanceof c?c.create(this._={gun:this,$:this,opt:t}):new c(t)}c.is=function(t){return t instanceof c||t&&t._&&t===t._.$||!1},c.version=.9,(c.chain=c.prototype).toJSON=function(){};var n=_("./type");n.obj.to(n,c),c.HAM=_("./HAM"),c.val=_("./val"),c.node=_("./node"),c.state=_("./state"),c.graph=_("./graph"),c.on=_("./onto"),c.ask=_("./ask"),c.dup=_("./dup"),function(){function a(t){var n,o,i=this.as,e=i.at||i,r=e.$;(o=t["#"])||(o=t["#"]=u(9)),(n=e.dup).check(o)?i.out===t.out&&(t.out=void 0,this.to.next(t)):(n.track(o),e.ask(t["@"],t)||(t.get&&c.on.get(t,r),t.put&&c.on.put(t,r)),this.to.next(t),i.out||(t.out=a,e.on("out",t)))}c.create=function(t){t.root=t.root||t,t.graph=t.graph||{},t.on=t.on||c.on,t.ask=t.ask||c.ask,t.dup=t.dup||c.dup();var n=t.$.opt(t.opt);return t.once||(t.on("in",a,t),t.on("out",a,{at:t,out:a}),c.on("create",t),t.on("create",t)),t.once=1,n}}(),function(){function e(t,n,o,i){var e=this,r=c.state.is(o,n);if(!r)return e.err="Error: No state on '"+n+"' in node '"+i+"'!";var a=e.graph[i]||v,u=c.state.is(a,n,!0),s=a[n],f=c.HAM(e.machine,r,u,t,s);f.incoming?(e.put[i]=c.state.to(o,n,e.put[i]),(e.diff||(e.diff={}))[i]=c.state.to(o,n,e.diff[i]),e.souls[i]=!0):f.defer&&(e.defer=r<(e.defer||1/0)?r:e.defer)}function r(t,n){var o=this,i=o.$._,e=(i.next||v)[n];if(!e){if(!(i.opt||v).super)return void(o.souls[n]=!1);e=o.$.get(n)._}var r=o.map[n]={put:t,get:n,$:e.$},a={ctx:o,msg:r};o.async=!!i.tag.node,o.ack&&(r["@"]=o.ack),h(t,u,a),o.async&&(o.and||i.on("node",function(t){this.to.next(t),t===o.map[t.get]&&(o.souls[t.get]=!1,h(t.put,s,t),h(o.souls,function(t){if(t)return t})||o.c||(o.c=1,this.off(),h(o.map,f,o)))}),o.and=!0,i.on("node",r))}function u(t,n){var o=this.ctx,i=o.graph,e=this.msg,r=e.get,a=e.put,u=e.$._;i[r]=c.state.to(a,n,i[r]),o.async||(u.put=c.state.to(a,n,u.put))}function s(t,n){var o=this.put,i=this.$._;i.put=c.state.to(o,n,i.put)}function f(t,n){t.$&&(this.cat.stop=this.stop,t.$._.on("in",t),this.cat.stop=null)}c.on.put=function(t,n){var o=n._,i={$:n,graph:o.graph,put:{},map:{},souls:{},machine:c.state(),ack:t["@"],cat:o,stop:{}};if(c.graph.is(t.put,null,e,i)||(i.err="Error: Invalid graph!"),i.err)return o.on("in",{"@":t["#"],err:c.log(i.err)});h(i.put,r,i),i.async||h(i.map,f,i),void 0!==i.defer&&setTimeout(function(){c.on.put(t,n)},i.defer-i.machine),i.diff&&o.on("put",p(t,{put:i.diff}))},c.on.get=function(t,n){var o=n._,i=t.get,e=i[d],r=o.graph[e],a=i[g],u=(o.next||(o.next={}))[e];if(!r)return o.on("get",t);if(a){if("string"!=typeof a||!l(r,a))return o.on("get",t);r=c.state.to(r,a)}else r=c.obj.copy(r);r=c.graph.node(r),(u||v).ack,o.on("in",{"@":t["#"],how:"mem",put:r,$:n}),o.on("get",t)}}(),c.chain.opt=function(t){t=t||{};var n=this._,o=t.peers||t;return a(t)||(t={}),a(n.opt)||(n.opt=t),e(o)&&(o=[o]),i(o)&&(o=h(o,function(t,n,o){(n={}).id=n.url=t,o(t,n)}),a(n.opt.peers)||(n.opt.peers={}),n.opt.peers=p(o,n.opt.peers)),n.opt.peers=n.opt.peers||{},p(t,n.opt),c.on("opt",n),n.opt.uuid=n.opt.uuid||function(){return s()+u(12)},this};var i=c.list.is,o=c.text,e=o.is,u=o.random,r=c.obj,a=r.is,l=r.has,p=r.to,h=r.map,s=(r.copy,c.state.lex),d=c.val.link._,g=".",v=(c.node._,c.val.link.is,{});b.debug=function(t,n){return b.debug.i&&t===b.debug.i&&b.debug.i++&&(b.log.apply(b,arguments)||n)},(c.log=function(){return!c.log.off&&b.log.apply(b,arguments),[].slice.call(arguments).join(" ")}).once=function(t,n,o){return(o=c.log.once)[t]=o[t]||0,o[t]++||c.log(n)},c.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=window.Gun=c).window=window);try{void 0!==f&&(f.exports=c)}catch(t){}t.exports=c})(_,"./root"),_(function(t){var u=_("./root");u.chain.back=function(t,n){if(-1===(t=t||1)||1/0===t)return this._.root.$;if(1===t)return(this._.back||this._).$;var o=this._;if("string"==typeof t&&(t=t.split(".")),t instanceof Array){for(var i=0,e=t.length,r=o;i(r.acks||0)&&this.off(),r.ack&&r.ack(t,this)},r.opt),o=0,i=n.root.now;u.del(n.root,"now");var e=n.root.mum;n.root.mum={},r.ref._.on("out",{$:r.ref,put:r.out=r.env.graph,opt:r.opt,"#":t}),n.root.mum=e?u.to(e,n.root.mum):e,n.root.now=i},r),r.res&&r.res())}function n(t,n){if(t)return!0}function l(r,t,n,a){var u=this,s=f.is(r);!t&&a.path.length&&(u.res||i)(function(){for(var t=a.path,n=u.ref,o=(u.opt,0),i=t.length;o .once, apologies unexpected."),this.once(t,n)},f.chain.once=function(t,n){var o=this,i=o._,e=i.put;if(0=(i.batch||1e3))return f();e||(e=setTimeout(f,i.wait||1))}),a.on("get",function(n){this.to.next(n);var o,i,e,r=n.get;function t(){if(r&&(o=r["#"])){var t=r["."];(i=s[o]||e)&&t&&(i=Gun.state.to(i,t)),a.on("in",{"@":n["#"],put:Gun.graph.node(i),how:"lS",lS:n.$})}}Gun.debug?setTimeout(t,1):t()});var n=function(t,n,o,i){s[i]=Gun.state.to(o,n,s[i])},f=function(t){var o;u=0,clearTimeout(e),e=!1;var n=r;r={},t&&(s=t);try{p.setItem(i.prefix,JSON.stringify(s))}catch(t){Gun.log(o=(t||"localStorage failure")+" Consider using GUN's IndexedDB plugin for RAD for more storage space, temporary example at https://github.com/amark/gun/blob/master/test/tmp/indexedDB.html ."),a.on("localStorage:error",{err:o,file:i.prefix,flush:s,retry:f})}(o||Gun.obj.empty(i.peers))&&Gun.obj.map(n,function(t,n){a.on("in",{"@":n,err:o,ok:0})})}}})}})(_,"./adapters/localStorage"),_(function(t){var d=_("../type");!function(){d.text.hash=function(t){if("string"!=typeof t)return{err:1};var n=0;if(!t.length)return n;for(var o=0,i=t.length;o<"])&&(o._.to=d.obj.map(r.split(","),f)),o.dam?void((r=c.hear[o.dam])&&r(o,n,s)):void s.on("in",o)}}else{try{o=JSON.parse(t)}catch(t){l.log("DAM JSON parse error",t)}if(!o)return;for(var a,u=0;a=o[u++];)c.hear(a,n)}}};var f=function(t,n,o){o(t,!0)};function h(n,o){try{var t=o.wire;o.say?o.say(n):t.send&&t.send(n),c.say.d+=n.length||0,++c.say.c}catch(t){(o.queue=o.queue||[]).push(n)}}c.hear.c=c.hear.d=0,function(){var u;function s(t){c.say(u,t)}function f(t){var n=t.batch;if(t.batch=t.tail=null,n&&n.length){try{n=1===n.length?n[0]:JSON.stringify(n)}catch(t){return l.log("DAM JSON stringify error",t)}n&&h(n,t)}}c.say=function(t,n){if(this.to&&this.to.next(t),!t)return!1;var o,i,e,r,a=t._||(t._=function(){});if((o=t["#"])||(o=t["#"]=d.text.random(9)),(i=t["##"])||void 0===t.put||(i=t["##"]=d.obj.hash(t.put)),!(r=a.raw)&&(r=a.raw=c.raw(t),i&&(e=t["@"])&&(p.track(e+i).it=t,e=(p.s[e]||!0).it))){if(i===e["##"])return!1;e["##"]=i}if(p.track(o).it=t,n||(n=(e=p.s[t["@"]])&&(e=e.it)&&(e=e._)&&(e=e.via)),!n&&c.way)return c.way(t);if(!n||!n.id)return u=t,!!d.obj.is(n||l.peers)&&void d.obj.map(n||l.peers,s);if(!n.wire&&c.wire&&c.wire(n),n===a.via)return!1;if((e=a.to)&&(e[n.url]||e[n.pid]||e[n.id]))return!1;if(n.batch){if(n.tail=(e=n.tail||0)+r.length,n.tail<=l.pack)return void n.batch.push(r);f(n)}n.batch=[],setTimeout(function(){f(n)},l.gap),h(r,n)},c.say.c=c.say.d=0}(),function(){c.raw=function(t){if(!t)return"";var n,o=t._||{};if(n=o.raw)return n;if("string"==typeof t)return t;if(!t.dam){var i=0,e=[];d.obj.map(l.peers,function(t){if(e.push(t.url||t.pid||t.id),9<++i)return!0}),1<"]=e.join())}var r=a(t);return o&&(o.raw=r),r};var a=JSON.stringify}(),c.hi=function(n){var t=n.wire||{};n.id?l.peers[n.url||n.id]=n:(t=n.id=n.id||d.text.random(9),c.say({dam:"?"},l.peers[t]=n)),n.met=n.met||+new Date,t.hied||s.on(t.hied="hi",n),t=n.queue,n.queue=[],d.obj.map(t,function(t){h(t,n)})},c.bye=function(t){d.obj.del(l.peers,t.id),s.on("bye",t);var n=+new Date;n-=t.met||n,c.bye.time=((c.bye.time||n)+n)/2},c.hear["!"]=function(t,n){l.log("Error:",t.err)},c.hear["?"]=function(t,n){t.pid?n.pid||(n.pid=t.pid):c.say({dam:"?",pid:l.pid,"@":t["#"]},n)},s.on("create",function(t){t.opt.pid=t.opt.pid||d.text.random(9),this.to.next(t),t.on("out",c.say)});var e={};return s.on("bye",function(t,n){this.to.next(t),(n=t.url)&&(e[n]=!0,setTimeout(function(){delete e[n]},l.lack||9e3))}),s.on("hi",function(o,i){this.to.next(o),(i=o.url)&&e[i]&&(delete e[i],d.obj.map(s.next,function(t,n){(i={})[n]=s.graph[n],c.say({"##":d.obj.hash(i),get:{"#":n}},o)}))}),c}}catch(t){}})(_,"./adapters/mesh"),_(function(t){var u=_("../index");u.Mesh=_("./mesh"),u.on("opt",function(t){this.to.next(t);var i=t.opt;if(!t.once&&!1!==i.WebSocket){var n;"undefined"!=typeof window&&(n=window),"undefined"!=typeof global&&(n=global),n=n||{};var o=i.WebSocket||n.WebSocket||n.webkitWebSocket||n.mozWebSocket;if(o){i.WebSocket=o;var e=i.mesh=i.mesh||u.Mesh(t);e.wire||i.wire;e.wire=i.wire=r}}function r(n){try{if(!n||!n.url)return o&&o(n);var t=n.url.replace("http","ws"),o=n.wire=new i.WebSocket(t);return o.onclose=function(){i.mesh.bye(n),a(n)},o.onerror=function(t){a(n),t&&t.code},o.onopen=function(){i.mesh.hi(n)},o.onmessage=function(t){t&&i.mesh.hear(t.data||t,n)},o}catch(t){}}function a(t){clearTimeout(t.defer),t.defer=setTimeout(function(){r(t)},2e3)}})})(_,"./adapters/websocket")}(); \ No newline at end of file diff --git a/src/adapters/localStorage.js b/src/adapters/localStorage.js index e2a5fdea..59c99390 100644 --- a/src/adapters/localStorage.js +++ b/src/adapters/localStorage.js @@ -30,13 +30,14 @@ Gun.on('create', function(root){ }); }); setTimeout(function(){ + // TODO: Holy Grail dangling by this thread! If gap / offline resync doesn't trigger, it doesn't work. Ouch, and this is a localStorage specific adapter. :( root.on('out', {put: send, '#': root.ask(ack)}); },1); } root.on('out', function(msg){ - if(msg.lS){ return } - if(Gun.is(msg.$) && msg.put && !msg['@'] && !empty(opt.peers)){ + if(msg.lS){ return } // TODO: for IndexedDB and others, shouldn't send to peers ACKs to our own GETs. + if(Gun.is(msg.$) && msg.put && !msg['@']){ id = msg['#']; Gun.graph.is(msg.put, null, map); if(!to){ to = setTimeout(flush, opt.wait || 1) } @@ -108,11 +109,9 @@ Gun.on('create', function(root){ if(data && has){ data = Gun.state.to(data, has); } - 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? - } + //if(!data && !Gun.obj.empty(opt.peers)){ return } // if data not found, don't ack if there are peers. // Hmm, what if we have peers but we are disconnected? //console.log("lS get", lex, data); - root.on('in', {'@': msg['#'], put: Gun.graph.node(data), how: 'lS', lS: msg.$ || root.$}); + root.on('in', {'@': msg['#'], put: Gun.graph.node(data), how: 'lS', lS: msg.$});// || root.$}); }; Gun.debug? setTimeout(to,1) : to(); }); diff --git a/src/adapters/mesh.js b/src/adapters/mesh.js index 190cca65..f801795e 100644 --- a/src/adapters/mesh.js +++ b/src/adapters/mesh.js @@ -1,67 +1,20 @@ -var Gun = require('../index'); var Type = require('../type'); -function Mesh(ctx){ +function Mesh(root){ var mesh = function(){}; - var opt = ctx.opt || {}; + var opt = root.opt || {}; opt.log = opt.log || console.log; opt.gap = opt.gap || opt.wait || 1; opt.pack = opt.pack || (opt.memory? (opt.memory * 1000 * 1000) : 1399000000) * 0.3; // max_old_space_size defaults to 1400 MB. - mesh.out = function(msg){ var tmp; - if(this.to){ this.to.next(msg) } - //if(mesh.last != msg['#']){ return mesh.last = msg['#'], this.to.next(msg) } - if((tmp = msg['@']) - && (tmp = ctx.dup.s[tmp]) - && (tmp = tmp.it) - && tmp._){ - mesh.say(msg, (tmp._).via, 1); - tmp['##'] = msg['##']; - return; - } - // add hook for AXE? - if (Gun.AXE) { Gun.AXE.say(msg, mesh.say, this); return; } - mesh.say(msg); - } - - ctx.on('create', function(root){ - root.opt.pid = root.opt.pid || Type.text.random(9); - this.to.next(root); - ctx.on('out', mesh.out); - }); + var dup = root.dup; mesh.hear = function(raw, peer){ if(!raw){ return } - var dup = ctx.dup, id, hash, msg, tmp = raw[0]; - if(opt.pack <= raw.length){ return mesh.say({dam: '!', err: "Message too big!"}, peer) } - if('{' === tmp){ - try{msg = JSON.parse(raw);}catch(e){opt.log('DAM JSON parse error', e)} - if(!msg){ return } - mesh.hear.d += raw.length; ++mesh.hear.c; // STATS! - if(dup.check(id = msg['#'])){ return } - dup.track(id, true).it = msg; // GUN core also dedups, so `true` is needed. - if((tmp = msg['@']) && msg.put){ - hash = msg['##'] || (msg['##'] = mesh.hash(msg)); - if((tmp = tmp + hash) != id){ - if(dup.check(tmp)){ return } - (tmp = dup.s)[hash] = tmp[id]; - } - } - (msg._ = function(){}).via = peer; - if((tmp = msg['><'])){ - (msg._).to = Type.obj.map(tmp.split(','), tomap); - } - if(msg.dam){ - if(tmp = mesh.hear[msg.dam]){ - tmp(msg, peer, ctx); - } - return; - } - ctx.on('in', msg); - - return; - } else + var msg, id, hash, tmp = raw[0]; + if(opt.pack <= raw.length){ return mesh.say({dam: '!', err: "Message too big!"}, peer) } + if('{' != raw[2]){ mesh.hear.d += raw.length||0; ++mesh.hear.c; } // STATS! // ugh, stupid double JSON encoding if('[' === tmp){ try{msg = JSON.parse(raw);}catch(e){opt.log('DAM JSON parse error', e)} if(!msg){ return } @@ -69,179 +22,222 @@ function Mesh(ctx){ while(m = msg[i++]){ mesh.hear(m, peer); } - + return; + } + if('{' === tmp || (Type.obj.is(raw) && (msg = raw))){ + try{msg = msg || JSON.parse(raw); + }catch(e){return opt.log('DAM JSON parse error', e)} + if(!msg){ return } + if(!(id = msg['#'])){ id = msg['#'] = Type.text.random(9) } + if(dup.check(id)){ return } + dup.track(id, true).it = msg; // GUN core also dedups, so `true` is needed. // Does GUN core need to dedup anymore? + if(!(hash = msg['##']) && u !== msg.put){ hash = msg['##'] = Type.obj.hash(msg.put) } + if(hash && (tmp = msg['@'] || (msg.get && id))){ // Reduces backward daisy in case varying hashes at different daisy depths are the same. + if(dup.check(tmp+hash)){ return } + dup.track(tmp+hash, true).it = msg; // GUN core also dedups, so `true` is needed. // Does GUN core need to dedup anymore? + } + (msg._ = function(){}).via = peer; + if(tmp = msg['><']){ (msg._).to = Type.obj.map(tmp.split(','), tomap) } + if(msg.dam){ + if(tmp = mesh.hear[msg.dam]){ + tmp(msg, peer, root); + } + return; + } + root.on('in', msg); return; } } - mesh.hear.c = mesh.hear.d = 0; var tomap = function(k,i,m){m(k,true)}; + mesh.hear.c = mesh.hear.d = 0; ;(function(){ var message; function each(peer){ mesh.say(message, peer) } - mesh.say = function(msg, peer, o){ - /* - TODO: Plenty of performance optimizations - that can be made just based off of ordering, - and reducing function calls for cached writes. - */ - if(!peer){ message = msg; - Type.obj.map(opt.peers, each); - return; - } - var tmp, wire = peer.wire || ((opt.wire) && opt.wire(peer)), msh, raw;// || open(peer, ctx); // TODO: Reopen! - if(!wire){ return } - msh = (msg._) || empty; - if(peer === msh.via){ return } - if(!(raw = msh.raw)){ raw = mesh.raw(msg) } - if((tmp = msg['@']) - && (tmp = ctx.dup.s[tmp]) - && (tmp = tmp.it)){ - if(tmp.get && tmp['##'] && tmp['##'] === msg['##']){ // PERF: move this condition outside say? - return; // TODO: this still needs to be tested in the browser! + mesh.say = function(msg, peer){ + if(this.to){ this.to.next(msg) } // compatible with middleware adapters. + if(!msg){ return false } + var id, hash, tmp, raw; + var meta = msg._||(msg._=function(){}); + if(!(id = msg['#'])){ id = msg['#'] = Type.text.random(9) } + if(!(hash = msg['##']) && u !== msg.put){ hash = msg['##'] = Type.obj.hash(msg.put) } + if(!(raw = meta.raw)){ + raw = meta.raw = mesh.raw(msg); + if(hash && (tmp = msg['@'])){ + dup.track(tmp+hash).it = msg; + if(tmp = (dup.s[tmp]||ok).it){ + if(hash === tmp['##']){ return false } + tmp['##'] = hash; + } } } - if((tmp = msh.to) && (tmp[peer.url] || tmp[peer.id]) && !o){ return } // TODO: still needs to be tested + dup.track(id).it = msg; // track for 9 seconds, default. Earth<->Mars would need more! + if(!peer){ peer = (tmp = dup.s[msg['@']]) && (tmp = tmp.it) && (tmp = tmp._) && (tmp = tmp.via) } + if(!peer && mesh.way){ return mesh.way(msg) } + if(!peer || !peer.id){ message = msg; + if(!Type.obj.is(peer || opt.peers)){ return false } + Type.obj.map(peer || opt.peers, each); // in case peer is a peer list. + return; + } + if(!peer.wire && mesh.wire){ mesh.wire(peer) } + if(peer === meta.via){ return false } + if((tmp = meta.to) && (tmp[peer.url] || tmp[peer.pid] || tmp[peer.id]) /*&& !o*/){ return false } if(peer.batch){ - peer.tail = (peer.tail || 0) + raw.length; + peer.tail = (tmp = peer.tail || 0) + raw.length; if(peer.tail <= opt.pack){ - peer.batch.push(raw); + peer.batch.push(raw); // peer.batch += (tmp?'':',')+raw; // TODO: Prevent double JSON! // FOR v1.0 !? return; } flush(peer); } - peer.batch = []; + peer.batch = []; // peer.batch = '['; // TODO: Prevent double JSON! setTimeout(function(){flush(peer)}, opt.gap); send(raw, peer); } function flush(peer){ - var tmp = peer.batch; - if(!tmp){ return } + var tmp = peer.batch; // var tmp = peer.batch + ']'; // TODO: Prevent double JSON! peer.batch = peer.tail = null; - if(!tmp.length){ return } - try{send(JSON.stringify(tmp), peer); - }catch(e){opt.log('DAM JSON stringify error', e)} - } - function send(raw, peer){ - var wire = peer.wire; - try{ - if(peer.say){ - peer.say(raw); - } else - if(wire.send){ - wire.send(raw); - } - mesh.say.d += raw.length; ++mesh.say.c; // STATS! - }catch(e){ - (peer.queue = peer.queue || []).push(raw); - } + if(!tmp){ return } + if(!tmp.length){ return } // if(3 > tmp.length){ return } // TODO: ^ + try{tmp = (1 === tmp.length? tmp[0] : JSON.stringify(tmp)); + }catch(e){return opt.log('DAM JSON stringify error', e)} + if(!tmp){ return } + send(tmp, peer); } mesh.say.c = mesh.say.d = 0; - }()); + + // for now - find better place later. + function send(raw, peer){ try{ + var wire = peer.wire; + if(peer.say){ + peer.say(raw); + } else + if(wire.send){ + wire.send(raw); + } + mesh.say.d += raw.length||0; ++mesh.say.c; // STATS! + }catch(e){ + (peer.queue = peer.queue || []).push(raw); + }} ;(function(){ - - mesh.raw = function(msg){ + mesh.raw = function(msg){ // TODO: Clean this up / delete it / move logic out! if(!msg){ return '' } - var dup = ctx.dup, msh = (msg._) || {}, put, hash, tmp; - if(tmp = msh.raw){ return tmp } + var meta = (msg._) || {}, put, hash, tmp; + if(tmp = meta.raw){ return tmp } if(typeof msg === 'string'){ return msg } - if(msg['@'] && (tmp = msg.put)){ - if(!(hash = msg['##'])){ - put = $(tmp, sort) || ''; - hash = mesh.hash(msg, put); - msg['##'] = hash; - } - (tmp = dup.s)[hash = msg['@']+hash] = tmp[msg['#']]; - msg['#'] = hash || msg['#']; - if(put){ (msg = Type.obj.to(msg)).put = _ } + if(!msg.dam){ + var i = 0, to = []; Type.obj.map(opt.peers, function(p){ + to.push(p.url || p.pid || p.id); if(++i > 9){ return true } // limit server, fast fix, improve later! // For "tower" peer, MUST include 6 surrounding ids. + }); if(i > 1){ msg['><'] = to.join() } } - var i = 0, to = []; Type.obj.map(opt.peers, function(p){ - to.push(p.url || p.id); if(++i > 9){ return true } // limit server, fast fix, improve later! - }); msg['><'] = to.join(); - var raw = $(msg); - if(u !== put){ + var raw = $(msg); // optimize by reusing put = the JSON.stringify from .hash? + /*if(u !== put){ tmp = raw.indexOf(_, raw.indexOf('put')); raw = raw.slice(0, tmp-1) + put + raw.slice(tmp + _.length + 1); - //raw = raw.replace('"'+ _ +'"', put); // https://github.com/amark/gun/wiki/@$$ Heisenbug - } - if(msh){ - msh.raw = raw; - } + //raw = raw.replace('"'+ _ +'"', put); // NEVER USE THIS! ALSO NEVER DELETE IT TO NOT MAKE SAME MISTAKE! https://github.com/amark/gun/wiki/@$$ Heisenbug + }*/ + if(meta){ meta.raw = raw } return raw; } - - mesh.hash = function(msg, hash){ - return Mesh.hash(hash || $(msg.put, sort) || '') || msg['#'] || Type.text.random(9); - } - - function sort(k, v){ var tmp; - if(!(v instanceof Object)){ return v } - Type.obj.map(Object.keys(v).sort(), map, {to: tmp = {}, on: v}); - return tmp; - } - - function map(k){ - this.to[k] = this.on[k]; - } var $ = JSON.stringify, _ = ':])([:'; }()); mesh.hi = function(peer){ var tmp = peer.wire || {}; - if(peer.id || peer.url){ + if(peer.id){ opt.peers[peer.url || peer.id] = peer; } else { - tmp = peer.id = tmp.pid = peer.id || Type.text.random(9); + tmp = peer.id = peer.id || Type.text.random(9); mesh.say({dam: '?'}, opt.peers[tmp] = peer); } - if(!tmp.hied){ ctx.on(tmp.hied = 'hi', peer) } + peer.met = peer.met || +(new Date); + if(!tmp.hied){ root.on(tmp.hied = 'hi', peer) } // @rogowski I need this here by default for now to fix go1dfish's bug tmp = peer.queue; peer.queue = []; Type.obj.map(tmp, function(msg){ - mesh.say(msg, peer); + send(msg, peer); }); } mesh.bye = function(peer){ Type.obj.del(opt.peers, peer.id); // assume if peer.url then reconnect - ctx.on('bye', peer); + root.on('bye', peer); + var tmp = +(new Date); tmp = (tmp - (peer.met||tmp)); + mesh.bye.time = ((mesh.bye.time || tmp) + tmp) / 2; } mesh.hear['!'] = function(msg, peer){ opt.log('Error:', msg.err) } mesh.hear['?'] = function(msg, peer){ if(!msg.pid){ mesh.say({dam: '?', pid: opt.pid, '@': msg['#']}, peer); // @rogowski I want to re-enable this AXE logic with some fix/merge later. - // var tmp = peer.queue; peer.queue = []; - // Type.obj.map(tmp, function(msg){ - // mesh.say(msg, peer); - // }); + /* var tmp = peer.queue; peer.queue = []; + Type.obj.map(tmp, function(msg){ + mesh.say(msg, peer); + }); */ + // @rogowski 2: I think with my PID fix we can delete this and use the original. return; } - if(!peer.wire){ return } - if(!peer.wire.pid){ return } // only run code below if wire.pid exists - Type.obj.del(opt.peers, peer.wire.pid || peer.id); - delete peer.wire.pid; - peer.id = msg.pid; - mesh.hi(peer); + if(peer.pid){ return } + peer.pid = msg.pid; } + + root.on('create', function(root){ + root.opt.pid = root.opt.pid || Type.text.random(9); + this.to.next(root); + root.on('out', mesh.say); + }); + + var gets = {}; + root.on('bye', function(peer, tmp){ this.to.next(peer); + if(!(tmp = peer.url)){ return } gets[tmp] = true; + setTimeout(function(){ delete gets[tmp] },opt.lack || 9000); + }); + root.on('hi', function(peer, tmp){ this.to.next(peer); + if(!(tmp = peer.url) || !gets[tmp]){ return } delete gets[tmp]; + Type.obj.map(root.next, function(node, soul){ + tmp = {}; tmp[soul] = root.graph[soul]; + mesh.say({'##': Type.obj.hash(tmp), get: {'#': soul}}, peer); + }) + }); + return mesh; } -Mesh.hash = function(s){ // via SO - if(typeof s !== 'string'){ return {err: 1} } - var c = 0; - if(!s.length){ return c } - for(var i=0,l=s.length,n; i (as.acks || 0)){ this.off() } // Adjustable ACKs! Only 1 by default. if(!as.ack){ return } as.ack(ack, this); //--C; - }, as.opt); + }, as.opt), acks = 0; //C++; // NOW is a hack to get synchronous replies to correctly call. // and STOP is a hack to get async behavior to correctly call. diff --git a/src/root.js b/src/root.js index fc9bc474..a33b31cb 100644 --- a/src/root.js +++ b/src/root.js @@ -185,7 +185,7 @@ Gun.dup = require('./dup'); if(text_is(tmp)){ tmp = [tmp] } if(list_is(tmp)){ tmp = obj_map(tmp, function(url, i, map){ - map(url, {url: url}); + i = {}; i.id = i.url = url; map(url, i); }); if(!obj_is(at.opt.peers)){ at.opt.peers = {}} at.opt.peers = obj_to(tmp, at.opt.peers); diff --git a/test/panic/holy-grail.js b/test/panic/holy-grail.js index d96dfa2a..0f865095 100644 --- a/test/panic/holy-grail.js +++ b/test/panic/holy-grail.js @@ -50,6 +50,8 @@ describe("The Holy Grail Test!", function(){ test.async(); try{ require('fs').unlinkSync(env.i+'data') }catch(e){} try{ require('fs').unlinkSync((env.i+1)+'data') }catch(e){} + try{ require('gun/lib/fsrm')(env.i+'data') }catch(e){} + try{ require('gun/lib/fsrm')((env.i+1)+'data') }catch(e){} var port = env.config.port + env.i; var server = require('http').createServer(function(req, res){ res.end("I am "+ env.i +"!"); @@ -104,7 +106,8 @@ describe("The Holy Grail Test!", function(){ return server.run(function(test){ console.log(3); var env = test.props; - try{ require('fs').unlinkSync(env.i+'data'); }catch(e){} + try{ require('fs').unlinkSync(env.i+'data') }catch(e){} + try{ require('gun/lib/fsrm')(env.i+'data') }catch(e){} process.exit(0); }, {i: 1, config: config}) }); @@ -176,6 +179,7 @@ describe("The Holy Grail Test!", function(){ var env = test.props; test.async(); try{ require('fs').unlinkSync(env.i+'data') }catch(e){} + try{ require('gun/lib/fsrm')(env.i+'data') }catch(e){} var port = env.config.port + env.i; var server = require('http').createServer(function(req, res){ res.end("I am "+ env.i +"!");