diff --git a/README.md b/README.md index 2ad4399d4..984120fc6 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,8 @@ Thanks to:
Alan Mimms, Dário Freire, John Williamson, -Robin Bron +Robin Bron, +Elie Makhoul

- Join others in sponsoring code: https://www.patreon.com/gunDB ! @@ -232,6 +233,13 @@ now --npm amark/gun Then visit the URL in the output of the 'now --npm' step, in your browser. +### [Unebo](https://unubo.app/) + +Fork this GUN repo (Unebo only deploys from your own GitHub repo's). +Add a Node.js app, select your GUN fork, set `npm start` start as the command and deploy. + +Then visit the deployed app by following the 'view app' button, in your browser. + ### [Docker](https://www.docker.com/) [![Docker Automated buil](https://img.shields.io/docker/automated/gundb/gun.svg)](https://hub.docker.com/r/gundb/gun/) [![](https://images.microbadger.com/badges/image/gundb/gun.svg)](https://microbadger.com/images/gundb/gun "Get your own image badge on microbadger.com") [![Docker Pulls](https://img.shields.io/docker/pulls/gundb/gun.svg)](https://hub.docker.com/r/gundb/gun/) [![Docker Stars](https://img.shields.io/docker/stars/gundb/gun.svg)](https://hub.docker.com/r/gundb/gun/) diff --git a/examples/index.html b/examples/index.html index d8d5274a2..eef4824a1 100644 --- a/examples/index.html +++ b/examples/index.html @@ -20,9 +20,9 @@ border: none; } - + - + diff --git a/gun.js b/gun.js index a06a6c755..cb02e16fb 100644 --- a/gun.js +++ b/gun.js @@ -811,16 +811,20 @@ // Maybe... in case the in-memory key we have is a local write // we still need to trigger a pull/merge from peers. } else { + //var S = +new Date; node = Gun.obj.copy(node); + //console.log(+new Date - S, 'copy node'); } node = Gun.graph.node(node); tmp = (at||empty).ack; + //var S = +new Date; root.on('in', { '@': msg['#'], how: 'mem', put: node, $: gun }); + //console.log(+new Date - S, 'root got send'); //if(0 < tmp){ return } root.on('get', msg); } @@ -1295,19 +1299,18 @@ if(tmp = cat.soul || cat.link || cat.dub){ return cb(tmp, as, cat) } if(cat.jam){ return cat.jam.push([cb, as]) } cat.jam = [[cb,as]]; - gun.get(function(msg, eve){ + gun.get(function go(msg, eve){ if(u === msg.put && (tmp = Object.keys(cat.root.opt.peers).length) && ++acks < tmp){ return; } eve.rid(msg); - var at = ((at = msg.$) && at._) || {}; - tmp = cat.jam; Gun.obj.del(cat, 'jam'); - Gun.obj.map(tmp, function(as, cb){ - cb = as[0]; as = as[1]; - if(!cb){ return } - var id = at.link || at.soul || rel.is(msg.put) || node_soul(msg.put) || at.dub; - cb(id, as, msg, eve); - }); + var at = ((at = msg.$) && at._) || {}, i = 0, as; + tmp = cat.jam; delete cat.jam; // tmp = cat.jam.splice(0, 100); + //if(tmp.length){ process.nextTick(function(){ go(msg, eve) }) } + while(as = tmp[i++]){ //Gun.obj.map(tmp, function(as, cb){ + var cb = as[0], id; as = as[1]; + cb && cb(id = at.link || at.soul || rel.is(msg.put) || node_soul(msg.put) || at.dub, as, msg, eve); + } //); }, {out: {get: {'.':true}}}); return gun; } @@ -1701,7 +1704,7 @@ return; } if(link && u === link.put && (tmp = rel.is(data))){ data = Gun.node.ify({}, tmp) } - eve.rid(msg); + eve.rid? eve.rid(msg) : eve.off(); opt.ok.call(gun || opt.$, data, msg.get); } @@ -1954,6 +1957,7 @@ ;USE(function(module){ var Type = USE('../type'); + var puff = (typeof setImmediate !== "undefined")? setImmediate : setTimeout; function Mesh(root){ var mesh = function(){}; @@ -1972,12 +1976,18 @@ if('[' === tmp){ try{msg = JSON.parse(raw);}catch(e){opt.log('DAM JSON parse error', e)} if(!msg){ return } - var i = 0, m; - var S = +new Date; // STATS! - while(m = msg[i++]){ - mesh.hear(m, peer); - } - (mesh.hear.long || (mesh.hear.long = [])).push(+new Date - S); + //console.log('hear batch length of', msg.length); + (function go(){ + var S = +new Date; // STATS! + var m, c = 100; // hardcoded for now? + while(c-- && (m = msg.shift())){ + mesh.hear(m, peer); + } + //console.log(+new Date - S, 'hear batch'); + (mesh.hear.long || (mesh.hear.long = [])).push(+new Date - S); + if(!msg.length){ return } + puff(go, 0); + }()); return; } if('{' === tmp || (Type.obj.is(raw) && (msg = raw))){ @@ -1985,6 +1995,7 @@ }catch(e){return opt.log('DAM JSON parse error', e)} if(!msg){ return } if(!(id = msg['#'])){ id = msg['#'] = Type.text.random(9) } + if(msg.DBG_s){ console.log(+new Date - msg.DBG_s, 'to hear', id) } 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) } @@ -2000,7 +2011,9 @@ } return; } + //var S = +new Date; root.on('in', msg); + //!msg.nts && console.log(+new Date - S, 'msg', msg['#']); return; } } @@ -2014,6 +2027,7 @@ if(this.to){ this.to.next(msg) } // compatible with middleware adapters. if(!msg){ return false } var id, hash, tmp, raw; + //var S = +new Date; //msg.DBG_s = msg.DBG_s || +new Date; 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) } @@ -2027,12 +2041,15 @@ } } } + //console.log(+new Date - S, 'mesh say prep'); 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 } + //var S = +new Date; Type.obj.map(peer || opt.peers, each); // in case peer is a peer list. + //console.log(+new Date - S, 'mesh say loop'); return; } if(!peer.wire && mesh.wire){ mesh.wire(peer) } @@ -2056,8 +2073,10 @@ peer.batch = peer.tail = null; if(!tmp){ return } if(!tmp.length){ return } // if(3 > tmp.length){ return } // TODO: ^ + //var S = +new Date; try{tmp = (1 === tmp.length? tmp[0] : JSON.stringify(tmp)); }catch(e){return opt.log('DAM JSON stringify error', e)} + //console.log(+new Date - S, 'mesh flush', tmp.length); if(!tmp){ return } send(tmp, peer); } @@ -2067,12 +2086,14 @@ // for now - find better place later. function send(raw, peer){ try{ var wire = peer.wire; + //var S = +new Date; if(peer.say){ peer.say(raw); } else if(wire.send){ wire.send(raw); } + //console.log(+new Date - S, 'wire send', raw.length); mesh.say.d += raw.length||0; ++mesh.say.c; // STATS! }catch(e){ (peer.queue = peer.queue || []).push(raw); @@ -2251,6 +2272,8 @@ return wire; }catch(e){}} + setTimeout(function(){ root.on('out', {dam:'hi'}) },1); // it can take a while to open a socket, so maybe no longer lazy load for perf reasons? + var wait = 2 * 1000; function reconnect(peer){ clearTimeout(peer.defer); diff --git a/gun.min.js b/gun.min.js index 274f73458..4b43f0e59 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 j=(t=t||{}).console||{log:function(){}};function $(o,t){return t?require(o):o.slice?$[e(o)]:function(t,n){o(t={exports:{}}),$[e(n)]=t.exports};function e(t){return t.split("/").slice(-1).toString().replace(".js","")}}if("undefined"!=typeof module)var o=module;$(function(t){var n,c,l={};function o(t,n){v(this,n)&&void 0!==this[n]||(this[n]=t)}function e(t,n){var o=this.n;if(!o||!(n===o||g(o)&&v(o,n)))return!!n||void 0}function p(t,n){2!==arguments.length?(p.r=p.r||[]).push(t):(p.r=p.r||{})[t]=n}l.fn={is:function(t){return!!t&&"function"==typeof t}},l.bi={is:function(t){return t instanceof Boolean||"boolean"==typeof t}},l.num={is:function(t){return!d(t)&&(0<=t-parseFloat(t)+1||1/0===t||-1/0===t)}},l.text={is:function(t){return"string"==typeof t}},l.text.ify=function(t){return l.text.is(t)?t:"undefined"!=typeof JSON?JSON.stringify(t):t&&t.toString?t.toString():t},l.text.random=function(t,n){var o="";for(t=t||24,n=n||"0123456789ABCDEFGHIJKLMNOPQRSTUVWXZabcdefghijklmnopqrstuvwxyz";0"]||n["<"])||e===n["="]&&(o=n["*"]||n[">"]||n["<"],t.slice(0,(o||"").length)===o||e===n["*"]&&(e!==n[">"]&&e!==n["<"]?t>=n[">"]&&t<=n["<"]:e!==n[">"]&&t>=n[">"]||e!==n["<"]&&t<=n["<"])))},l.list={is:function(t){return t instanceof Array}},l.list.slit=Array.prototype.slice,l.list.sort=function(o){return function(t,n){return t&&n?(t=t[o])<(n=n[o])?-1:n",u.drift=0,u.is=function(t,n,o){var e=n&&t&&t[k]&&t[k][u._]||o;if(e)return v(e=e[n])?e:-1/0},u.lex=function(){return u().toString(36).replace(".","")},u.ify=function(t,n,o,e,i){if(!t||!t[k]){if(!i)return;t=a.soul.ify(t,i)}var r=l(t[k],u._);return void 0!==n&&n!==k&&(v(o)&&(r[n]=o),void 0!==e&&(t[n]=e)),t},u.to=function(t,n,o){var e=(t||{})[n];return h(e)&&(e=g(e)),u.ify(o,n,u.is(t,n),e,a.soul(t))},u.map=function(i,r,a){var t=h(t=i||r)?t:null;return i=m(i=i||r)?i:null,t&&!i?(r=v(r)?r:u(),t[k]=t[k]||{},d(t,f,{o:t,s:r}),t):(a=a||h(r)?r:void 0,r=v(r)?r:u(),function(t,n,o,e){if(!i)return f.call({o:o,s:r},t,n),t;i.call(a||this||{},t,n,o,e),p(o,n)&&void 0===o[n]||f.call({o:o,s:r},t,n)})};var c=n.obj,l=c.as,p=c.has,h=c.is,d=c.map,g=c.copy,v=n.num.is,m=n.fn.is,k=a._;t.exports=u})($,"./state"),$(function(t){var a=$("./type"),u=$("./val"),s=$("./node"),i={};function r(t,n){if(!t||n!==s.soul(t)||!s.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&&s.is(o.n,t,o.as)}function f(t,n){var o;return(o=function(t,n){var o,e=t.seen,i=e.length;for(;i--;)if(o=e[i],n.obj===o.obj)return o;e.push(n)}(t,n))?o:(n.env=t,n.soul=c,s.ify(n.obj,e,n)&&(n.link=n.link||u.link.ify(s.soul(n.node)),n.obj!==t.shell&&(t.graph[u.link.is(n.link)]=n.node)),n)}function e(t,n,o){var e,i,r=this,a=r.env;if(s._===n&&g(t,u.link._))return o._;if(e=l(t,n,o,r,a)){if(n||(r.node=r.node||o||{},g(t,s._)&&s.soul(t)&&(r.node._=b(t._)),r.node=s.soul.ify(r.node,u.link.is(r.link)),r.link=r.link||u.link.ify(s.soul(r.node))),(i=a.map)&&(i.call(a.as||{},t,n,o,r),g(o,n))){if(void 0===(t=o[n]))return void d(o,n);if(!(e=l(t,n,o,r,a)))return}if(!n)return r.node;if(!0===e)return t;if((i=f(a,{obj:t,path:r.path.concat(n)})).node)return i.link}}function c(t){var n=this,o=u.link.is(n.link),e=n.env.graph;n.link=n.link||u.link.ify(t),n.link[u.link._]=t,n.node&&n.node[s._]&&(n.node[s._][u.link._]=t),g(e,o)&&(e[t]=e[o],d(e,o))}function l(t,n,o,e,i){var r;return!!u.is(t)||(h(t)?1:(r=i.invalid)?l(t=r.call(i.as||{},t,n,o),n,o,e,i):(i.err="Invalid value at '"+e.path.concat(n).join(".")+"'!",void(a.list.is(t)&&(i.err+=" Use `.set(item)` instead of an Array."))))}function p(t,n){var o,e;if(s._!==n)(o=u.link.is(t))?(e=this.opt.seen[o])?this.obj[n]=e:this.obj[n]=this.opt.seen[o]=i.to(this.graph,o,this.opt):this.obj[n]=t;else{if(v(t,u.link._))return;this.obj[n]=b(t)}}i.is=function(t,n,o,e){return!(!t||!h(t)||v(t)||k(t,r,{cb:n,fn:o,as:e}))},i.ify=function(t,n,o){var e={path:[],obj:t};return n?"string"==typeof n?n={soul:n}:n instanceof Function&&(n.map=n):n={},n.soul&&(e.link=u.link.ify(n.soul)),n.shell=(o||{}).shell,n.graph=n.graph||{},n.seen=n.seen||[],n.as=n.as||o,f(n,e),n.root=e.node,n.graph},i.node=function(t){var n=s.soul(t);if(n)return m({},n,t)},i.to=function(t,n,o){if(t){var e={};return o=o||{seen:{}},k(t[n],p,{obj:e,graph:t,opt:o}),e}};a.fn.is;var n=a.obj,h=n.is,d=n.del,g=n.has,v=n.empty,m=n.put,k=n.map,b=n.copy;t.exports=i})($,"./graph"),$(function(t){$("./onto"),t.exports=function(t,n){if(this.on){if(!(t instanceof Function)){if(!t||!n)return;var o=t["#"]||t,e=(this.tag||empty)[o];if(!e)return;return e=this.on(o,n),clearTimeout(e.err),!0}o=n&&n["#"]||Math.random().toString(36).slice(2);if(!t)return o;var i=this.on(o,t,n);return i.err=i.err||setTimeout(function(){i.next({err:"Error: No ACK received yet.",lack:!0}),i.off()},(this.opt||{}).lack||9e3),o}}})($,"./ask"),$(function(t){var r=$("./type");var a=r.time.is;t.exports=function(e){var i={s:{}};return e=e||{max:1e3,age:9e3},i.check=function(t){var n;return!!(n=i.s[t])&&(n.pass?n.pass=!1:i.track(t))},i.track=function(t,n){var o=i.s[t]||(i.s[t]={});return o.was=a(),n&&(o.pass=!0),i.to||(i.to=setTimeout(function(){var o=a();r.obj.map(i.s,function(t,n){t&&e.age>o-t.was||r.obj.del(i.s,n)}),i.to=null},e.age+9)),o},i}})($,"./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");function a(t){var n,o,e=this.as,i=e.at||e,r=i.$;(o=t["#"])||(o=t["#"]=h(9)),(n=i.dup).check(o)?e.out===t.out&&(t.out=void 0,this.to.next(t)):(n.track(o),i.ask(t["@"],t)||(t.get&&c.on.get(t,r),t.put&&c.on.put(t,r)),this.to.next(t),e.out||(t.out=a,i.on("out",t)))}function i(t,n,o,e){var i=this,r=c.state.is(o,n);if(!r)return i.err="Error: No state on '"+n+"' in node '"+e+"'!";var a=i.graph[e]||x,u=c.state.is(a,n,!0),s=a[n],f=c.HAM(i.machine,r,u,t,s);f.incoming?(i.put[e]=c.state.to(o,n,i.put[e]),(i.diff||(i.diff={}))[e]=c.state.to(o,n,i.diff[e]),i.souls[e]=!0):f.defer&&(i.defer=r<(i.defer||1/0)?r:i.defer)}function r(t,n){var o=this,e=o.$._,i=(e.next||x)[n];if(!i){if(!(e.opt||x).super)return void(o.souls[n]=!1);i=o.$.get(n)._}var r=o.map[n]={put:t,get:n,$:i.$},a={ctx:o,msg:r};o.async=!!e.tag.node,o.ack&&(r["@"]=o.ack),k(t,u,a),o.async&&(o.and||e.on("node",function(t){this.to.next(t),t===o.map[t.get]&&(o.souls[t.get]=!1,k(t.put,s,t),k(o.souls,function(t){if(t)return t})||o.c||(o.c=1,this.off(),k(o.map,f,o)))}),o.and=!0,e.on("node",r))}function u(t,n){var o=this.ctx,e=o.graph,i=this.msg,r=i.get,a=i.put,u=i.$._;e[r]=c.state.to(a,n,e[r]),o.async||(u.put=c.state.to(a,n,u.put))}function s(t,n){var o=this.put,e=this.$._;e.put=c.state.to(o,n,e.put)}function f(t,n){t.$&&(this.cat.stop=this.stop,t.$._.on("in",t),this.cat.stop=null)}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"),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},c.on.put=function(t,n){var o=n._,e={$:n,graph:o.graph,put:{},map:{},souls:{},machine:c.state(),ack:t["@"],cat:o,stop:{}};if(c.graph.is(t.put,null,i,e)||(e.err="Error: Invalid graph!"),e.err)return o.on("in",{"@":t["#"],err:c.log(e.err)});k(e.put,r,e),e.async||k(e.map,f,e),void 0!==e.defer&&setTimeout(function(){c.on.put(t,n)},e.defer-e.machine),e.diff&&o.on("put",m(t,{put:e.diff}))},c.on.get=function(t,n){var o=n._,e=t.get,i=e[y],r=o.graph[i],a=e[_],u=(o.next||(o.next={}))[i];if(!r)return o.on("get",t);if(a){if("string"!=typeof a||!v(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||x).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 g(t)||(t={}),g(n.opt)||(n.opt=t),p(o)&&(o=[o]),e(o)&&(o=k(o,function(t,n,o){(n={}).id=n.url=t,o(t,n)}),g(n.opt.peers)||(n.opt.peers={}),n.opt.peers=m(o,n.opt.peers)),n.opt.peers=n.opt.peers||{},k(t,function t(n,o){!v(this,o)||l.is(n)||d.empty(n)?this[o]=n:n&&n.constructor!==Object&&!e(n)||k(n,t,this[o])},n.opt),c.on("opt",n),n.opt.uuid=n.opt.uuid||function(){return b()+h(12)},this};var e=c.list.is,l=c.text,p=l.is,h=l.random,d=c.obj,g=d.is,v=d.has,m=d.to,k=d.map,b=(d.copy,c.state.lex),y=c.val.link._,_=".",x=(c.node._,c.val.link.is,{});j.only=function(t,n){return j.only.i&&t===j.only.i&&j.only.i++&&(j.log.apply(j,arguments)||n)},(c.log=function(){return c.log.off||j.log.apply(j,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!==o&&(o.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 e=0,i=t.length,r=o;e(r.acks||0)&&this.off(),r.ack&&r.ack(t,this)},r.opt),o=0,e=n.root.now;h.del(n.root,"now");var i=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=i?h.to(i,n.root.mum):i,n.root.now=e},r),r.res&&r.res())}function n(t,n){if(t)return!0}function c(r,t,n,a){var u=this,s=f.is(r);!t&&a.path.length&&(u.res||e)(function(){for(var t=a.path,n=u.ref,o=(u.opt,0),e=t.length;o .once, apologies unexpected."),this.once(t,n)},f.chain.once=function(t,n){var o=this,e=o._,i=e.put;if(0=(e.batch||1e3))return f();i=i||setTimeout(f,e.wait||1)}),a.on("get",function(n){this.to.next(n);var o,e,i,r=n.get;function t(){if(r&&(o=r["#"])){var t=r["."];(e=s[o]||i)&&t&&(e=Gun.state.to(e,t)),a.on("in",{"@":n["#"],put:Gun.graph.node(e),how:"lS",lS:n.$})}}Gun.debug?setTimeout(t,1):t()});var n=function(t,n,o,e){s[e]=Gun.state.to(o,n,s[e])},f=function(t){var o;u=0,clearTimeout(i),i=!1;var n=r;r={},t&&(s=t);try{p.setItem(e.prefix,JSON.stringify(s))}catch(t){Gun.log(o=(t||"localStorage failure")+" Consider using GUN's IndexedDB plugin for RAD for more storage space, https://gun.eco/docs/RAD#install"),a.on("localStorage:error",{err:o,file:e.prefix,flush:s,retry:f})}(o||Gun.obj.empty(e.peers))&&Gun.obj.map(n,function(t,n){a.on("in",{"@":n,err:o,ok:0})})}}})}})($,"./adapters/localStorage"),$(function(t){var v=$("../type");!function(){v.text.hash=function(t){if("string"!=typeof t)return{err:1};var n=0;if(!t.length)return n;for(var o=0,e=t.length;o<"])&&(o._.to=v.obj.map(r.split(","),p)),o.dam?void((r=f.hear[o.dam])&&r(o,n,s)):void s.on("in",o)}}else{try{o=JSON.parse(t)}catch(t){c.log("DAM JSON parse error",t)}if(!o)return;for(var a,u=0;a=o[u++];)f.hear(a,n)}}};var u,p=function(t,n,o){o(t,!0)};function h(t){f.say(u,t)}function d(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 c.log("DAM JSON stringify error",t)}n&&g(n,t)}}function g(n,o){try{var t=o.wire;o.say?o.say(n):t.send&&t.send(n),f.say.d+=n.length||0,++f.say.c}catch(t){(o.queue=o.queue||[]).push(n)}}f.hear.c=f.hear.d=0,f.say=function(t,n){if(this.to&&this.to.next(t),!t)return!1;var o,e,i,r,a=t._||(t._=function(){});if((o=t["#"])||(o=t["#"]=v.text.random(9)),(e=t["##"])||void 0===t.put||(e=t["##"]=v.obj.hash(t.put)),!(r=a.raw)&&(r=a.raw=f.raw(t),e&&(i=t["@"])&&(l.track(i+e).it=t,i=(l.s[i]||!0).it))){if(e===i["##"])return!1;i["##"]=e}if(l.track(o).it=t,!(n=n||(i=l.s[t["@"]])&&(i=i.it)&&(i=i._)&&(i=i.via))&&f.way)return f.way(t);if(!n||!n.id)return u=t,!!v.obj.is(n||c.peers)&&void v.obj.map(n||c.peers,h);if(!n.wire&&f.wire&&f.wire(n),o!==n.last){if(n.last=o,n===a.via)return!1;if((i=a.to)&&(i[n.url]||i[n.pid]||i[n.id]))return!1;if(n.batch){if(n.tail=(i=n.tail||0)+r.length,n.tail<=c.pack)return void n.batch.push(r);d(n)}n.batch=[],setTimeout(function(){d(n)},c.gap),g(r,n)}},f.say.c=f.say.d=0,function(){f.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 e=0,i=[];v.obj.map(c.peers,function(t){if(i.push(t.url||t.pid||t.id),9<++e)return!0}),1<"]=i.join())}var r=a(t);return o&&(o.raw=r),r};var a=JSON.stringify}(),f.hi=function(n){var t=n.wire||{};n.id?c.peers[n.url||n.id]=n:(t=n.id=n.id||v.text.random(9),f.say({dam:"?"},c.peers[t]=n)),n.met=n.met||+new Date,t.hied||s.on(t.hied="hi",n),t=n.queue,n.queue=[],v.obj.map(t,function(t){g(t,n)})},f.bye=function(t){s.on("bye",t);var n=+new Date;n-=t.met||n,f.bye.time=((f.bye.time||n)+n)/2},f.hear["!"]=function(t,n){c.log("Error:",t.err)},f.hear["?"]=function(t,n){t.pid?n.pid||(n.pid=t.pid):f.say({dam:"?",pid:c.pid,"@":t["#"]},n)},s.on("create",function(t){t.opt.pid=t.opt.pid||v.text.random(9),this.to.next(t),t.on("out",f.say)}),s.on("bye",function(t,n){t=c.peers[t.id||t]||t,this.to.next(t),t.bye?t.bye():(n=t.wire)&&n.close&&n.close(),v.obj.del(c.peers,t.id),t.wire=null});var i={};return s.on("bye",function(t,n){this.to.next(t),(n=t.url)&&(i[n]=!0,setTimeout(function(){delete i[n]},c.lack||9e3))}),s.on("hi",function(o,e){this.to.next(o),(e=o.url)&&i[e]&&(delete i[e],v.obj.map(s.next,function(t,n){(e={})[n]=s.graph[n],f.say({"##":v.obj.hash(e),get:{"#":n}},o)}))}),f}}catch(t){}})($,"./adapters/mesh"),$(function(t){var f=$("../index");f.Mesh=$("./mesh"),f.on("opt",function(t){this.to.next(t);var e=t.opt;if(!t.once&&!1!==e.WebSocket){var n;"undefined"!=typeof window&&(n=window),"undefined"!=typeof global&&(n=global),n=n||{};var o=e.WebSocket||n.WebSocket||n.webkitWebSocket||n.mozWebSocket;if(o){e.WebSocket=o;var i=e.mesh=e.mesh||f.Mesh(t);i.wire||e.wire;i.wire=e.wire=u;var r=2e3,a="undefined"!=typeof document&&document}}function u(n){try{if(!n||!n.url)return o&&o(n);var t=n.url.replace("http","ws"),o=n.wire=new e.WebSocket(t);return o.onclose=function(){e.mesh.bye(n),s(n)},o.onerror=function(t){s(n)},o.onopen=function(){e.mesh.hi(n)},o.onmessage=function(t){t&&e.mesh.hear(t.data||t,n)},o}catch(t){}}function s(n){clearTimeout(n.defer),a&&n.retry<=0||(n.retry=(n.retry||e.retry||60)-1,n.defer=setTimeout(function t(){if(a&&a.hidden)return setTimeout(t,r);u(n)},r))}})})($,"./adapters/websocket")}(); \ No newline at end of file +!function(){var t;"undefined"!=typeof window&&(t=window),"undefined"!=typeof global&&(t=global);var j=(t=t||{}).console||{log:function(){}};function $(o,t){return t?require(o):o.slice?$[e(o)]:function(t,n){o(t={exports:{}}),$[e(n)]=t.exports};function e(t){return t.split("/").slice(-1).toString().replace(".js","")}}if("undefined"!=typeof module)var o=module;$(function(t){var n,c,l={};function o(t,n){v(this,n)&&void 0!==this[n]||(this[n]=t)}function e(t,n){var o=this.n;if(!o||!(n===o||g(o)&&v(o,n)))return!!n||void 0}function p(t,n){2!==arguments.length?(p.r=p.r||[]).push(t):(p.r=p.r||{})[t]=n}l.fn={is:function(t){return!!t&&"function"==typeof t}},l.bi={is:function(t){return t instanceof Boolean||"boolean"==typeof t}},l.num={is:function(t){return!d(t)&&(0<=t-parseFloat(t)+1||1/0===t||-1/0===t)}},l.text={is:function(t){return"string"==typeof t}},l.text.ify=function(t){return l.text.is(t)?t:"undefined"!=typeof JSON?JSON.stringify(t):t&&t.toString?t.toString():t},l.text.random=function(t,n){var o="";for(t=t||24,n=n||"0123456789ABCDEFGHIJKLMNOPQRSTUVWXZabcdefghijklmnopqrstuvwxyz";0"]||n["<"])||e===n["="]&&(o=n["*"]||n[">"]||n["<"],t.slice(0,(o||"").length)===o||e===n["*"]&&(e!==n[">"]&&e!==n["<"]?t>=n[">"]&&t<=n["<"]:e!==n[">"]&&t>=n[">"]||e!==n["<"]&&t<=n["<"])))},l.list={is:function(t){return t instanceof Array}},l.list.slit=Array.prototype.slice,l.list.sort=function(o){return function(t,n){return t&&n?(t=t[o])<(n=n[o])?-1:n",u.drift=0,u.is=function(t,n,o){var e=n&&t&&t[k]&&t[k][u._]||o;if(e)return v(e=e[n])?e:-1/0},u.lex=function(){return u().toString(36).replace(".","")},u.ify=function(t,n,o,e,i){if(!t||!t[k]){if(!i)return;t=a.soul.ify(t,i)}var r=l(t[k],u._);return void 0!==n&&n!==k&&(v(o)&&(r[n]=o),void 0!==e&&(t[n]=e)),t},u.to=function(t,n,o){var e=(t||{})[n];return h(e)&&(e=g(e)),u.ify(o,n,u.is(t,n),e,a.soul(t))},u.map=function(i,r,a){var t=h(t=i||r)?t:null;return i=m(i=i||r)?i:null,t&&!i?(r=v(r)?r:u(),t[k]=t[k]||{},d(t,f,{o:t,s:r}),t):(a=a||h(r)?r:void 0,r=v(r)?r:u(),function(t,n,o,e){if(!i)return f.call({o:o,s:r},t,n),t;i.call(a||this||{},t,n,o,e),p(o,n)&&void 0===o[n]||f.call({o:o,s:r},t,n)})};var c=n.obj,l=c.as,p=c.has,h=c.is,d=c.map,g=c.copy,v=n.num.is,m=n.fn.is,k=a._;t.exports=u})($,"./state"),$(function(t){var a=$("./type"),u=$("./val"),s=$("./node"),i={};function r(t,n){if(!t||n!==s.soul(t)||!s.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&&s.is(o.n,t,o.as)}function f(t,n){var o;return(o=function(t,n){var o,e=t.seen,i=e.length;for(;i--;)if(o=e[i],n.obj===o.obj)return o;e.push(n)}(t,n))?o:(n.env=t,n.soul=c,s.ify(n.obj,e,n)&&(n.link=n.link||u.link.ify(s.soul(n.node)),n.obj!==t.shell&&(t.graph[u.link.is(n.link)]=n.node)),n)}function e(t,n,o){var e,i,r=this,a=r.env;if(s._===n&&g(t,u.link._))return o._;if(e=l(t,n,o,r,a)){if(n||(r.node=r.node||o||{},g(t,s._)&&s.soul(t)&&(r.node._=b(t._)),r.node=s.soul.ify(r.node,u.link.is(r.link)),r.link=r.link||u.link.ify(s.soul(r.node))),(i=a.map)&&(i.call(a.as||{},t,n,o,r),g(o,n))){if(void 0===(t=o[n]))return void d(o,n);if(!(e=l(t,n,o,r,a)))return}if(!n)return r.node;if(!0===e)return t;if((i=f(a,{obj:t,path:r.path.concat(n)})).node)return i.link}}function c(t){var n=this,o=u.link.is(n.link),e=n.env.graph;n.link=n.link||u.link.ify(t),n.link[u.link._]=t,n.node&&n.node[s._]&&(n.node[s._][u.link._]=t),g(e,o)&&(e[t]=e[o],d(e,o))}function l(t,n,o,e,i){var r;return!!u.is(t)||(h(t)?1:(r=i.invalid)?l(t=r.call(i.as||{},t,n,o),n,o,e,i):(i.err="Invalid value at '"+e.path.concat(n).join(".")+"'!",void(a.list.is(t)&&(i.err+=" Use `.set(item)` instead of an Array."))))}function p(t,n){var o,e;if(s._!==n)(o=u.link.is(t))?(e=this.opt.seen[o])?this.obj[n]=e:this.obj[n]=this.opt.seen[o]=i.to(this.graph,o,this.opt):this.obj[n]=t;else{if(v(t,u.link._))return;this.obj[n]=b(t)}}i.is=function(t,n,o,e){return!(!t||!h(t)||v(t)||k(t,r,{cb:n,fn:o,as:e}))},i.ify=function(t,n,o){var e={path:[],obj:t};return n?"string"==typeof n?n={soul:n}:n instanceof Function&&(n.map=n):n={},n.soul&&(e.link=u.link.ify(n.soul)),n.shell=(o||{}).shell,n.graph=n.graph||{},n.seen=n.seen||[],n.as=n.as||o,f(n,e),n.root=e.node,n.graph},i.node=function(t){var n=s.soul(t);if(n)return m({},n,t)},i.to=function(t,n,o){if(t){var e={};return o=o||{seen:{}},k(t[n],p,{obj:e,graph:t,opt:o}),e}};a.fn.is;var n=a.obj,h=n.is,d=n.del,g=n.has,v=n.empty,m=n.put,k=n.map,b=n.copy;t.exports=i})($,"./graph"),$(function(t){$("./onto"),t.exports=function(t,n){if(this.on){if(!(t instanceof Function)){if(!t||!n)return;var o=t["#"]||t,e=(this.tag||empty)[o];if(!e)return;return e=this.on(o,n),clearTimeout(e.err),!0}o=n&&n["#"]||Math.random().toString(36).slice(2);if(!t)return o;var i=this.on(o,t,n);return i.err=i.err||setTimeout(function(){i.next({err:"Error: No ACK received yet.",lack:!0}),i.off()},(this.opt||{}).lack||9e3),o}}})($,"./ask"),$(function(t){var r=$("./type");var a=r.time.is;t.exports=function(e){var i={s:{}};return e=e||{max:1e3,age:9e3},i.check=function(t){var n;return!!(n=i.s[t])&&(n.pass?n.pass=!1:i.track(t))},i.track=function(t,n){var o=i.s[t]||(i.s[t]={});return o.was=a(),n&&(o.pass=!0),i.to||(i.to=setTimeout(function(){var o=a();r.obj.map(i.s,function(t,n){t&&e.age>o-t.was||r.obj.del(i.s,n)}),i.to=null},e.age+9)),o},i}})($,"./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");function a(t){var n,o,e=this.as,i=e.at||e,r=i.$;(o=t["#"])||(o=t["#"]=h(9)),(n=i.dup).check(o)?e.out===t.out&&(t.out=void 0,this.to.next(t)):(n.track(o),i.ask(t["@"],t)||(t.get&&c.on.get(t,r),t.put&&c.on.put(t,r)),this.to.next(t),e.out||(t.out=a,i.on("out",t)))}function i(t,n,o,e){var i=this,r=c.state.is(o,n);if(!r)return i.err="Error: No state on '"+n+"' in node '"+e+"'!";var a=i.graph[e]||x,u=c.state.is(a,n,!0),s=a[n],f=c.HAM(i.machine,r,u,t,s);f.incoming?(i.put[e]=c.state.to(o,n,i.put[e]),(i.diff||(i.diff={}))[e]=c.state.to(o,n,i.diff[e]),i.souls[e]=!0):f.defer&&(i.defer=r<(i.defer||1/0)?r:i.defer)}function r(t,n){var o=this,e=o.$._,i=(e.next||x)[n];if(!i){if(!(e.opt||x).super)return void(o.souls[n]=!1);i=o.$.get(n)._}var r=o.map[n]={put:t,get:n,$:i.$},a={ctx:o,msg:r};o.async=!!e.tag.node,o.ack&&(r["@"]=o.ack),k(t,u,a),o.async&&(o.and||e.on("node",function(t){this.to.next(t),t===o.map[t.get]&&(o.souls[t.get]=!1,k(t.put,s,t),k(o.souls,function(t){if(t)return t})||o.c||(o.c=1,this.off(),k(o.map,f,o)))}),o.and=!0,e.on("node",r))}function u(t,n){var o=this.ctx,e=o.graph,i=this.msg,r=i.get,a=i.put,u=i.$._;e[r]=c.state.to(a,n,e[r]),o.async||(u.put=c.state.to(a,n,u.put))}function s(t,n){var o=this.put,e=this.$._;e.put=c.state.to(o,n,e.put)}function f(t,n){t.$&&(this.cat.stop=this.stop,t.$._.on("in",t),this.cat.stop=null)}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"),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},c.on.put=function(t,n){var o=n._,e={$:n,graph:o.graph,put:{},map:{},souls:{},machine:c.state(),ack:t["@"],cat:o,stop:{}};if(c.graph.is(t.put,null,i,e)||(e.err="Error: Invalid graph!"),e.err)return o.on("in",{"@":t["#"],err:c.log(e.err)});k(e.put,r,e),e.async||k(e.map,f,e),void 0!==e.defer&&setTimeout(function(){c.on.put(t,n)},e.defer-e.machine),e.diff&&o.on("put",m(t,{put:e.diff}))},c.on.get=function(t,n){var o=n._,e=t.get,i=e[y],r=o.graph[i],a=e[_],u=(o.next||(o.next={}))[i];if(!r)return o.on("get",t);if(a){if("string"!=typeof a||!v(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||x).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 g(t)||(t={}),g(n.opt)||(n.opt=t),p(o)&&(o=[o]),e(o)&&(o=k(o,function(t,n,o){(n={}).id=n.url=t,o(t,n)}),g(n.opt.peers)||(n.opt.peers={}),n.opt.peers=m(o,n.opt.peers)),n.opt.peers=n.opt.peers||{},k(t,function t(n,o){!v(this,o)||l.is(n)||d.empty(n)?this[o]=n:n&&n.constructor!==Object&&!e(n)||k(n,t,this[o])},n.opt),c.on("opt",n),n.opt.uuid=n.opt.uuid||function(){return b()+h(12)},this};var e=c.list.is,l=c.text,p=l.is,h=l.random,d=c.obj,g=d.is,v=d.has,m=d.to,k=d.map,b=(d.copy,c.state.lex),y=c.val.link._,_=".",x=(c.node._,c.val.link.is,{});j.only=function(t,n){return j.only.i&&t===j.only.i&&j.only.i++&&(j.log.apply(j,arguments)||n)},(c.log=function(){return c.log.off||j.log.apply(j,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!==o&&(o.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 e=0,i=t.length,r=o;e(r.acks||0)&&this.off(),r.ack&&r.ack(t,this)},r.opt),o=0,e=n.root.now;h.del(n.root,"now");var i=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=i?h.to(i,n.root.mum):i,n.root.now=e},r),r.res&&r.res())}function n(t,n){if(t)return!0}function c(r,t,n,a){var u=this,s=f.is(r);!t&&a.path.length&&(u.res||e)(function(){for(var t=a.path,n=u.ref,o=(u.opt,0),e=t.length;o .once, apologies unexpected."),this.once(t,n)},f.chain.once=function(t,n){var o=this,e=o._,i=e.put;if(0=(e.batch||1e3))return f();i=i||setTimeout(f,e.wait||1)}),a.on("get",function(n){this.to.next(n);var o,e,i,r=n.get;function t(){if(r&&(o=r["#"])){var t=r["."];(e=s[o]||i)&&t&&(e=Gun.state.to(e,t)),a.on("in",{"@":n["#"],put:Gun.graph.node(e),how:"lS",lS:n.$})}}Gun.debug?setTimeout(t,1):t()});var n=function(t,n,o,e){s[e]=Gun.state.to(o,n,s[e])},f=function(t){var o;u=0,clearTimeout(i),i=!1;var n=r;r={},t&&(s=t);try{p.setItem(e.prefix,JSON.stringify(s))}catch(t){Gun.log(o=(t||"localStorage failure")+" Consider using GUN's IndexedDB plugin for RAD for more storage space, https://gun.eco/docs/RAD#install"),a.on("localStorage:error",{err:o,file:e.prefix,flush:s,retry:f})}(o||Gun.obj.empty(e.peers))&&Gun.obj.map(n,function(t,n){a.on("in",{"@":n,err:o,ok:0})})}}})}})($,"./adapters/localStorage"),$(function(t){var g=$("../type"),v="undefined"!=typeof setImmediate?setImmediate:setTimeout;!function(){g.text.hash=function(t){if("string"!=typeof t)return{err:1};var n=0;if(!t.length)return n;for(var o=0,e=t.length;o<"])&&(r._.to=g.obj.map(e.split(","),l)),r.dam?void((e=u.hear[r.dam])&&e(r,i,a)):void a.on("in",r)}}else{try{r=JSON.parse(t)}catch(t){s.log("DAM JSON parse error",t)}if(!r)return;(function t(){for(var n,o=+new Date,e=100;e--&&(n=r.shift());)u.hear(n,i);(u.hear.long||(u.hear.long=[])).push(+new Date-o),r.length&&v(t,0)})()}}};var c,l=function(t,n,o){o(t,!0)};function p(t){u.say(c,t)}function h(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 s.log("DAM JSON stringify error",t)}n&&d(n,t)}}function d(n,o){try{var t=o.wire;o.say?o.say(n):t.send&&t.send(n),u.say.d+=n.length||0,++u.say.c}catch(t){(o.queue=o.queue||[]).push(n)}}u.hear.c=u.hear.d=0,u.say=function(t,n){if(this.to&&this.to.next(t),!t)return!1;var o,e,i,r,a=t._||(t._=function(){});if((o=t["#"])||(o=t["#"]=g.text.random(9)),(e=t["##"])||void 0===t.put||(e=t["##"]=g.obj.hash(t.put)),!(r=a.raw)&&(r=a.raw=u.raw(t),e&&(i=t["@"])&&(f.track(i+e).it=t,i=(f.s[i]||!0).it))){if(e===i["##"])return!1;i["##"]=e}if(f.track(o).it=t,!(n=n||(i=f.s[t["@"]])&&(i=i.it)&&(i=i._)&&(i=i.via))&&u.way)return u.way(t);if(!n||!n.id)return c=t,!!g.obj.is(n||s.peers)&&void g.obj.map(n||s.peers,p);if(!n.wire&&u.wire&&u.wire(n),o!==n.last){if(n.last=o,n===a.via)return!1;if((i=a.to)&&(i[n.url]||i[n.pid]||i[n.id]))return!1;if(n.batch){if(n.tail=(i=n.tail||0)+r.length,n.tail<=s.pack)return void n.batch.push(r);h(n)}n.batch=[],setTimeout(function(){h(n)},s.gap),d(r,n)}},u.say.c=u.say.d=0,function(){u.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 e=0,i=[];g.obj.map(s.peers,function(t){if(i.push(t.url||t.pid||t.id),9<++e)return!0}),1<"]=i.join())}var r=a(t);return o&&(o.raw=r),r};var a=JSON.stringify}(),u.hi=function(n){var t=n.wire||{};n.id?s.peers[n.url||n.id]=n:(t=n.id=n.id||g.text.random(9),u.say({dam:"?"},s.peers[t]=n)),n.met=n.met||+new Date,t.hied||a.on(t.hied="hi",n),t=n.queue,n.queue=[],g.obj.map(t,function(t){d(t,n)})},u.bye=function(t){a.on("bye",t);var n=+new Date;n-=t.met||n,u.bye.time=((u.bye.time||n)+n)/2},u.hear["!"]=function(t,n){s.log("Error:",t.err)},u.hear["?"]=function(t,n){t.pid?n.pid||(n.pid=t.pid):u.say({dam:"?",pid:s.pid,"@":t["#"]},n)},a.on("create",function(t){t.opt.pid=t.opt.pid||g.text.random(9),this.to.next(t),t.on("out",u.say)}),a.on("bye",function(t,n){t=s.peers[t.id||t]||t,this.to.next(t),t.bye?t.bye():(n=t.wire)&&n.close&&n.close(),g.obj.del(s.peers,t.id),t.wire=null});var i={};return a.on("bye",function(t,n){this.to.next(t),(n=t.url)&&(i[n]=!0,setTimeout(function(){delete i[n]},s.lack||9e3))}),a.on("hi",function(o,e){this.to.next(o),(e=o.url)&&i[e]&&(delete i[e],g.obj.map(a.next,function(t,n){(e={})[n]=a.graph[n],u.say({"##":g.obj.hash(e),get:{"#":n}},o)}))}),u}}catch(t){}})($,"./adapters/mesh"),$(function(t){var f=$("../index");f.Mesh=$("./mesh"),f.on("opt",function(t){this.to.next(t);var e=t.opt;if(!t.once&&!1!==e.WebSocket){var n;"undefined"!=typeof window&&(n=window),"undefined"!=typeof global&&(n=global),n=n||{};var o=e.WebSocket||n.WebSocket||n.webkitWebSocket||n.mozWebSocket;if(o){e.WebSocket=o;var i=e.mesh=e.mesh||f.Mesh(t);i.wire||e.wire;i.wire=e.wire=u,setTimeout(function(){t.on("out",{dam:"hi"})},1);var r=2e3,a="undefined"!=typeof document&&document}}function u(n){try{if(!n||!n.url)return o&&o(n);var t=n.url.replace("http","ws"),o=n.wire=new e.WebSocket(t);return o.onclose=function(){e.mesh.bye(n),s(n)},o.onerror=function(t){s(n)},o.onopen=function(){e.mesh.hi(n)},o.onmessage=function(t){t&&e.mesh.hear(t.data||t,n)},o}catch(t){}}function s(n){clearTimeout(n.defer),a&&n.retry<=0||(n.retry=(n.retry||e.retry||60)-1,n.defer=setTimeout(function t(){if(a&&a.hidden)return setTimeout(t,r);u(n)},r))}})})($,"./adapters/websocket")}(); \ No newline at end of file diff --git a/lib/bye.js b/lib/bye.js index a7fa5c0db..5224f636d 100644 --- a/lib/bye.js +++ b/lib/bye.js @@ -1,24 +1,22 @@ var Gun = (typeof window !== "undefined")? window.Gun : require('../gun'); -Gun.on('opt', function(root){ +Gun.on('create', function(root){ this.to.next(root); - if(root.once){ return } - root.on('in', function(msg){ - //Msg did not have a peer property saved before, so nothing ever went further - if(!msg._ || !msg.BYE){ return this.to.next(msg) } - var peer = msg._.via; - (peer.bye = peer.bye || []).push(msg.BYE); - }) + var mesh = root.opt.mesh; + if(!mesh){ return } + mesh.hear['bye'] = function(msg, peer){ + (peer.byes = peer.byes || []).push(msg.bye); + } root.on('bye', function(peer){ this.to.next(peer); - if(!peer.bye){ return } - var gun = root.gun; - Gun.obj.map(peer.bye, function(data){ + if(!peer.byes){ return } + var gun = root.$; + Gun.obj.map(peer.byes, function(data){ Gun.obj.map(data, function(put, soul){ gun.get(soul).put(put); }); }); - peer.bye = []; + peer.byes = []; }); }); @@ -30,7 +28,7 @@ Gun.chain.bye = function(){ var tmp = data; (data = {})[at.get] = tmp; }); - root.on('out', {BYE: data}); + root.on('out', {bye: data}); return gun; } return bye; diff --git a/lib/evict.js b/lib/evict.js index 6aa2582a2..2470fbdf1 100644 --- a/lib/evict.js +++ b/lib/evict.js @@ -19,12 +19,12 @@ function GC(){ var souls = Object.keys(root.graph||empty); var toss = Math.ceil(souls.length * 0.01); - //var start = Gun.state(), i = toss; + //var S = +new Date; Gun.list.map(souls, function(soul){ if(--toss < 0){ return } root.gun.get(soul).off(); }); - //console.log("evicted", i, 'nodes in', ((Gun.state() - start)/1000).toFixed(2), 'sec.'); + //console.log(+new Date - S, 'gc'); } /* root.on('in', function(msg){ diff --git a/lib/multicast.js b/lib/multicast.js index 75ebb9a0b..94f0e39ba 100644 --- a/lib/multicast.js +++ b/lib/multicast.js @@ -19,7 +19,11 @@ Gun.on('create', function(root){ try{ dgram = require("dgram") }catch(e){ return } var socket = dgram.createSocket({type: "udp4", reuseAddr: true}); - socket.bind(udp.port); + socket.bind({port: udp.port, exclusive: true}, function(){ + socket.setBroadcast(true); + socket.setMulticastTTL(128); + socket.addMembership(udp.address); + }); socket.on("listening", function(){ try { socket.addMembership(udp.address) }catch(e){ return } diff --git a/lib/promise.js b/lib/promise.js index 49cea272e..875f89a83 100644 --- a/lib/promise.js +++ b/lib/promise.js @@ -1,4 +1,4 @@ -/* Promise Library v1.0 for GUN DB +/* Promise Library v1.1 for GUN DB * Turn any part of a gun chain into a promise, that you can then use * .then().catch() pattern. * In normal gun doing var item = gun.get('someKey'), gun returns a reference @@ -67,7 +67,7 @@ Gun.chain.promPut = async function (item, opt) { var gun = this; return (new Promise((res, rej)=>{ gun.put(item, function(ack) { - if(ack.err){rej(ack.err)} + if(ack.err){console.log(ack.err); ack.ok=-1; res({ref:gun, ack:ack})} res({ref:gun, ack:ack}); }, opt); })) diff --git a/lib/radisk.js b/lib/radisk.js index 5f6a5f092..0c1b82d49 100644 --- a/lib/radisk.js +++ b/lib/radisk.js @@ -11,15 +11,15 @@ opt.pack = opt.pack || (opt.memory? (opt.memory * 1000 * 1000) : 1399000000) * 0.3; // max_old_space_size defaults to 1400 MB. opt.until = opt.until || opt.wait || 250; opt.batch = opt.batch || (10 * 1000); - opt.chunk = opt.chunk || (1024 * 1024 * 10); // 10MB + opt.chunk = opt.chunk || (1024 * 1024 * 1); // 1MB opt.code = opt.code || {}; opt.code.from = opt.code.from || '!'; - //opt.jsonify = true; if(opt.jsonify){ console.log("JSON RAD!!!") } // TODO: REMOVE!!!! + opt.jsonify = true; function ename(t){ return encodeURIComponent(t).replace(/\*/g, '%2A') } function atomic(v){ return u !== v && (!v || 'object' != typeof v) } var map = Gun.obj.map; - var LOG = false;//true; + var LOG = false; if(!opt.store){ return opt.log("ERROR: Radisk needs `opt.store` interface with `{get: fn, put: fn (, list: fn)}`!"); @@ -44,7 +44,9 @@ if(val instanceof Function){ var o = cb || {}; cb = val; + var S; LOG && (S = +new Date); val = r.batch(key); + LOG && console.log(+new Date - S, 'rad mem'); if(u !== val){ cb(u, r.range(val, o), o); if(atomic(val)){ return } @@ -230,19 +232,22 @@ r.read = function(key, cb, o){ o = o || {}; if(RAD && !o.next){ // cache + var S; LOG && (S = +new Date); var val = RAD(key); + LOG && console.log(+new Date - S, 'rad cached'); //if(u !== val){ //cb(u, val, o); if(atomic(val)){ cb(u, val, o); return } // if a node is requested and some of it is cached... the other parts might not be. //} } - o.span = (u !== o.start) || (u !== o.end); + o.span = (u !== o.start) || (u !== o.end); // is there a start or end? var g = function Get(){}; g.lex = function(file){ var tmp; file = (u === file)? u : decodeURIComponent(file); tmp = o.next || key || (o.reverse? o.end || '\uffff' : o.start || ''); if(!file || (o.reverse? file < tmp : file > tmp)){ + LOG && console.log(+new Date - S, 'rad read lex'); S = +new Date; if(o.next || o.reverse){ g.file = file } if(tmp = Q[g.file]){ tmp.push({key: key, ack: cb, file: g.file, opt: o}); @@ -263,25 +268,33 @@ g.info = info; if(disk){ RAD = g.disk = disk } disk = Q[g.file]; delete Q[g.file]; + LOG && console.log(+new Date - S, 'rad read it in, now ack to:', disk.length); S = +new Date; map(disk, g.ack); + LOG && console.log(+new Date - S, 'rad read acked'); } g.ack = function(as){ if(!as.ack){ return } - var tmp = as.key, o = as.opt, info = g.info, rad = g.disk || noop, data = r.range(rad(tmp), o), last = rad.last || Radix.map(rad, rev, revo); + var key = as.key, o = as.opt, info = g.info, rad = g.disk || noop, data = r.range(rad(key), o), last = rad.last || Radix.map(rad, rev, revo); o.parsed = (o.parsed || 0) + (info.parsed||0); o.chunks = (o.chunks || 0) + 1; - if(!o.some){ o.some = (u !== data) } - if(u !== data){ as.ack(g.err, data, o) } - else if(!as.file){ !o.some && as.ack(g.err, u, o); return } - if(!o.span){ - if(/*!last || */last === tmp){ !o.some && as.ack(g.err, u, o); return } - if(last && last > tmp && 0 != last.indexOf(tmp)){ !o.some && as.ack(g.err, u, o); return } + o.more = true; + if((!as.file) // if no more places to look + || (!o.span && last === key) // if our key exactly matches the very last atomic record + || (!o.span && last && last > key && 0 != last.indexOf(key)) // 'zach' may be lexically larger than 'za', but there still might be more, like 'zane' in the 'za' prefix bucket so do not end here. + ){ + o.more = u; + as.ack(g.err, data, o); + return } - if(o.some && o.parsed >= o.limit){ return } + if(u !== data){ + as.ack(g.err, data, o); // more might be coming! + if(o.parsed >= o.limit){ return } // even if more, we've hit our limit, asking peer will need to make a new ask with a new starting point. + } o.next = as.file; - r.read(tmp, as.ack, o); + r.read(key, as.ack, o); } if(o.reverse){ g.lex.reverse = true } + LOG && (S = +new Date); r.list(g.lex); } function rev(a,b){ return b } @@ -302,7 +315,7 @@ var p = function Parse(){}, info = {}; p.disk = Radix(); p.read = function(err, data){ var tmp; - LOG && console.log('read disk in', +new Date - start, ename(file)); // keep this commented out in + LOG && console.log('read disk in', +new Date - S, ename(file)); // keep this commented out in delete Q[file]; if((p.err = err) || (p.not = !data)){ return map(q, p.ack); @@ -319,12 +332,12 @@ } info.parsed = data.length; - LOG && (start = +new Date); // keep this commented out in production! - if(opt.jsonify){ // temporary testing idea + LOG && (S = +new Date); // keep this commented out in production! + if(opt.jsonify || '{' === data[0]){ // temporary testing idea try{ var json = JSON.parse(data); p.disk.$ = json; - LOG && console.log('parsed JSON in', +new Date - start); // keep this commented out in production! + LOG && console.log('parsed JSON in', +new Date - S); // keep this commented out in production! map(q, p.ack); return; }catch(e){ tmp = e } @@ -333,7 +346,7 @@ return map(q, p.ack); } } - LOG && (start = +new Date); // keep this commented out in production! + LOG && (S = +new Date); // keep this commented out in production! var tmp = p.split(data), pre = [], i, k, v; if(!tmp || 0 !== tmp[1]){ p.err = "File '"+file+"' does not have root radix! "; @@ -356,7 +369,7 @@ if(u !== k && u !== v){ p.disk(pre.join(''), v) } tmp = p.split(tmp[2]); } - LOG && console.log('parsed RAD in', +new Date - start); // keep this commented out in production! + LOG && console.log('parsed RAD in', +new Date - S); // keep this commented out in production! //cb(err, p.disk); map(q, p.ack); }; @@ -376,7 +389,7 @@ if(p.err || p.not){ return cb(p.err, u, info) } cb(u, p.disk, info); } - var start; LOG && (start = +new Date); // keep this commented out in production! + var S; LOG && (S = +new Date); // keep this commented out in production! if(raw){ return p.read(null, raw) } opt.store.get(ename(file), p.read); } diff --git a/lib/radisk2.js b/lib/radisk2.js index 87fc7be07..4441938c6 100644 --- a/lib/radisk2.js +++ b/lib/radisk2.js @@ -12,7 +12,7 @@ opt.pack = opt.pack || (opt.memory? (opt.memory * 1000 * 1000) : 1399000000) * 0.3; // max_old_space_size defaults to 1400 MB. opt.until = opt.until || opt.wait || 250; opt.batch = opt.batch || (10 * 1000); - opt.chunk = opt.chunk || (1024 * 1024 * 10); // 10MB + opt.chunk = opt.chunk || (1024 * 1024 * 1); // 1MB opt.code = opt.code || {}; opt.code.from = opt.code.from || '!'; //opt.jsonify = true; // TODO: REMOVE!!!! diff --git a/lib/rindexed.js b/lib/rindexed.js index 281120bab..d3adda27a 100644 --- a/lib/rindexed.js +++ b/lib/rindexed.js @@ -3,7 +3,6 @@ function Store(opt){ opt = opt || {}; opt.file = String(opt.file || 'radata'); - opt.chunk = opt.chunk || (1024 * 1024); // 1MB var db = null, u; try{opt.indexedDB = opt.indexedDB || indexedDB}catch(e){} diff --git a/lib/rs3.js b/lib/rs3.js index 339582a09..e4f57f7d9 100644 --- a/lib/rs3.js +++ b/lib/rs3.js @@ -8,9 +8,9 @@ Gun.on('create', function(root){ this.to.next(root); var opt = root.opt; if(!opt.s3 && !process.env.AWS_S3_BUCKET){ return } - opt.batch = opt.batch || (1000 * 10); - opt.until = opt.until || (1000 * 3); - opt.chunk = opt.chunk || (1024 * 1024 * 10); // 10MB + //opt.batch = opt.batch || (1000 * 10); + //opt.until = opt.until || (1000 * 3); // ignoring these now, cause perf > cost + //opt.chunk = opt.chunk || (1024 * 1024 * 10); // 10MB // when cost only cents try{AWS = require('aws-sdk'); }catch(e){ diff --git a/lib/store.js b/lib/store.js index 56c4dcda5..3384e93c2 100644 --- a/lib/store.js +++ b/lib/store.js @@ -3,7 +3,7 @@ var Gun = (typeof window !== "undefined")? window.Gun : require('../gun'); Gun.on('create', function(root){ if(Gun.TESTING){ root.opt.file = 'radatatest' } this.to.next(root); - var opt = root.opt, u; + var opt = root.opt, empty = {}, u; if(false === opt.radisk){ return } var Radisk = (Gun.window && Gun.window.Radisk) || require('./radisk'); var Radix = Radisk.Radix; @@ -14,14 +14,23 @@ Gun.on('create', function(root){ root.on('put', function(msg){ this.to.next(msg); var id = msg['#'] || Gun.text.random(3), track = !msg['@'], acks = track? 0 : u; // only ack non-acks. - if(msg.rad && !track){ return } // don't save our own acks - var start = (+new Date); // STATS! + var got = (msg._||empty).rad, now = Gun.state(); + var S = (+new Date); // STATS! Gun.graph.is(msg.put, null, function(val, key, node, soul){ + if(!track && got){ + var at = (root.next||empty)[soul]; + if(!at){ return } + if(u !== got['.']){ at = (at.next||empty)[key] } + if(!at){ return } + at.rad = now; + return; + } if(track){ ++acks } //console.log('put:', soul, key, val); val = Radisk.encode(val, null, esc)+'>'+Radisk.encode(Gun.state.is(node, key), null, esc); rad(soul+esc+key, val, (track? ack : u)); }); + //console.log(+new Date - S, 'put loop'); function ack(err, ok){ acks--; if(ack.err){ return } @@ -31,11 +40,12 @@ Gun.on('create', function(root){ return; } if(acks){ return } - try{opt.store.stats.put.time[statp % 50] = (+new Date) - start; ++statp; + try{opt.store.stats.put.time[statp % 50] = (+new Date) - S; ++statp; opt.store.stats.put.count++; }catch(e){} // STATS! - //console.log("PAT!", id); + //console.log(+new Date - S, 'put'); S = +new Date; root.on('in', {'@': id, ok: 1}); + //console.log(+new Date - S, 'put sent'); } }); @@ -67,12 +77,19 @@ Gun.on('create', function(root){ o.limit = (tmp <= (o.pack || (1000 * 100)))? tmp : 1; } if(has['-'] || (soul||{})['-']){ o.reverse = true } - var start = (+new Date); // STATS! // console.log("GET!", id, JSON.stringify(key)); + if(tmp = (root.next||empty)[soul]){ + if(tmp && tmp.rad){ return } + if(o.atom){ tmp = (tmp.next||empty)[o.atom] } + if(tmp && tmp.rad){ return } + } + var S = (+new Date); // STATS! rad(key||'', function(err, data, o){ - try{opt.store.stats.get.time[statg % 50] = (+new Date) - start; ++statg; + try{opt.store.stats.get.time[statg % 50] = (+new Date) - S; ++statg; opt.store.stats.get.count++; if(err){ opt.store.stats.get.err = err } }catch(e){} // STATS! + //if(u === data && o.chunks > 1){ return } // if we already sent a chunk, ignore ending empty responses. // this causes tests to fail. + //console.log(+new Date - S, 'got'); S = +new Date; if(data){ if(typeof data !== 'string'){ if(o.atom){ @@ -83,20 +100,24 @@ Gun.on('create', function(root){ } if(!graph && data){ each(data, '') } } - root.on('in', {'@': id, put: graph, err: err? err : u, rad: Radix}); + //console.log(+new Date - S, 'got prep'); S = +new Date; + root.on('in', {'@': id, put: graph, '%': o.more? 1 : u, err: err? err : u, _: each}); + //console.log(+new Date - S, 'got sent'); }, o); + //console.log(+new Date - S, 'get call'); function each(val, has, a,b){ if(!val){ return } has = (key+has).split(esc); var soul = has.slice(0,1)[0]; has = has.slice(-1)[0]; o.count = (o.count || 0) + val.length; - tmp = val.lastIndexOf('>'); + var tmp = val.lastIndexOf('>'); var state = Radisk.decode(val.slice(tmp+1), null, esc); val = Radisk.decode(val.slice(0,tmp), null, esc); (graph = graph || {})[soul] = Gun.state.ify(graph[soul], has, state, val, soul); if(o.limit && o.limit <= o.count){ return true } } + each.rad = get; }); opt.store.stats = {get:{time:{}, count:0}, put: {time:{}, count:0}}; // STATS! var statg = 0, statp = 0; // STATS! diff --git a/package.json b/package.json index cee14d76d..53f7ba31d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gun", - "version": "0.2019.915", + "version": "0.2019.930", "description": "A realtime, decentralized, offline-first, graph data synchronization engine.", "main": "index.js", "browser": "gun.js", diff --git a/src/adapters/mesh.js b/src/adapters/mesh.js index f65fd4924..f25965296 100644 --- a/src/adapters/mesh.js +++ b/src/adapters/mesh.js @@ -1,5 +1,6 @@ var Type = require('../type'); +var puff = (typeof setImmediate !== "undefined")? setImmediate : setTimeout; function Mesh(root){ var mesh = function(){}; @@ -18,10 +19,18 @@ function Mesh(root){ 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); - } + //console.log('hear batch length of', msg.length); + (function go(){ + var S = +new Date; // STATS! + var m, c = 100; // hardcoded for now? + while(c-- && (m = msg.shift())){ + mesh.hear(m, peer); + } + //console.log(+new Date - S, 'hear batch'); + (mesh.hear.long || (mesh.hear.long = [])).push(+new Date - S); + if(!msg.length){ return } + puff(go, 0); + }()); return; } if('{' === tmp || (Type.obj.is(raw) && (msg = raw))){ @@ -29,6 +38,7 @@ function Mesh(root){ }catch(e){return opt.log('DAM JSON parse error', e)} if(!msg){ return } if(!(id = msg['#'])){ id = msg['#'] = Type.text.random(9) } + if(msg.DBG_s){ console.log(+new Date - msg.DBG_s, 'to hear', id) } 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) } @@ -44,7 +54,9 @@ function Mesh(root){ } return; } + //var S = +new Date; root.on('in', msg); + //!msg.nts && console.log(+new Date - S, 'msg', msg['#']); return; } } @@ -58,6 +70,7 @@ function Mesh(root){ if(this.to){ this.to.next(msg) } // compatible with middleware adapters. if(!msg){ return false } var id, hash, tmp, raw; + //var S = +new Date; //msg.DBG_s = msg.DBG_s || +new Date; 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) } @@ -71,12 +84,15 @@ function Mesh(root){ } } } + //console.log(+new Date - S, 'mesh say prep'); 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 } + //var S = +new Date; Type.obj.map(peer || opt.peers, each); // in case peer is a peer list. + //console.log(+new Date - S, 'mesh say loop'); return; } if(!peer.wire && mesh.wire){ mesh.wire(peer) } @@ -100,8 +116,10 @@ function Mesh(root){ peer.batch = peer.tail = null; if(!tmp){ return } if(!tmp.length){ return } // if(3 > tmp.length){ return } // TODO: ^ + //var S = +new Date; try{tmp = (1 === tmp.length? tmp[0] : JSON.stringify(tmp)); }catch(e){return opt.log('DAM JSON stringify error', e)} + //console.log(+new Date - S, 'mesh flush', tmp.length); if(!tmp){ return } send(tmp, peer); } @@ -111,12 +129,14 @@ function Mesh(root){ // for now - find better place later. function send(raw, peer){ try{ var wire = peer.wire; + //var S = +new Date; if(peer.say){ peer.say(raw); } else if(wire.send){ wire.send(raw); } + //console.log(+new Date - S, 'wire send', raw.length); mesh.say.d += raw.length||0; ++mesh.say.c; // STATS! }catch(e){ (peer.queue = peer.queue || []).push(raw); diff --git a/src/adapters/websocket.js b/src/adapters/websocket.js index a5987470e..c386bd271 100644 --- a/src/adapters/websocket.js +++ b/src/adapters/websocket.js @@ -42,6 +42,8 @@ Gun.on('opt', function(root){ return wire; }catch(e){}} + setTimeout(function(){ root.on('out', {dam:'hi'}) },1); // it can take a while to open a socket, so maybe no longer lazy load for perf reasons? + var wait = 2 * 1000; function reconnect(peer){ clearTimeout(peer.defer); diff --git a/src/get.js b/src/get.js index e29e62db9..68a14167d 100644 --- a/src/get.js +++ b/src/get.js @@ -71,19 +71,18 @@ function soul(gun, cb, opt, as){ if(tmp = cat.soul || cat.link || cat.dub){ return cb(tmp, as, cat) } if(cat.jam){ return cat.jam.push([cb, as]) } cat.jam = [[cb,as]]; - gun.get(function(msg, eve){ + gun.get(function go(msg, eve){ if(u === msg.put && (tmp = Object.keys(cat.root.opt.peers).length) && ++acks < tmp){ return; } eve.rid(msg); - var at = ((at = msg.$) && at._) || {}; - tmp = cat.jam; Gun.obj.del(cat, 'jam'); - Gun.obj.map(tmp, function(as, cb){ - cb = as[0]; as = as[1]; - if(!cb){ return } - var id = at.link || at.soul || rel.is(msg.put) || node_soul(msg.put) || at.dub; - cb(id, as, msg, eve); - }); + var at = ((at = msg.$) && at._) || {}, i = 0, as; + tmp = cat.jam; delete cat.jam; // tmp = cat.jam.splice(0, 100); + //if(tmp.length){ process.nextTick(function(){ go(msg, eve) }) } + while(as = tmp[i++]){ //Gun.obj.map(tmp, function(as, cb){ + var cb = as[0], id; as = as[1]; + cb && cb(id = at.link || at.soul || rel.is(msg.put) || node_soul(msg.put) || at.dub, as, msg, eve); + } //); }, {out: {get: {'.':true}}}); return gun; } diff --git a/src/on.js b/src/on.js index 2b145457a..aa2477fe6 100644 --- a/src/on.js +++ b/src/on.js @@ -94,7 +94,7 @@ function val(msg, eve, to){ return; } if(link && u === link.put && (tmp = rel.is(data))){ data = Gun.node.ify({}, tmp) } - eve.rid(msg); + eve.rid? eve.rid(msg) : eve.off(); opt.ok.call(gun || opt.$, data, msg.get); } diff --git a/src/root.js b/src/root.js index 187a3bdce..af58dc83b 100644 --- a/src/root.js +++ b/src/root.js @@ -162,16 +162,20 @@ Gun.dup = require('./dup'); // Maybe... in case the in-memory key we have is a local write // we still need to trigger a pull/merge from peers. } else { + //var S = +new Date; node = Gun.obj.copy(node); + //console.log(+new Date - S, 'copy node'); } node = Gun.graph.node(node); tmp = (at||empty).ack; + //var S = +new Date; root.on('in', { '@': msg['#'], how: 'mem', put: node, $: gun }); + //console.log(+new Date - S, 'root got send'); //if(0 < tmp){ return } root.on('get', msg); } diff --git a/test/panic/1putackget.js b/test/panic/1putackget.js index c23193975..8e3d025b2 100644 --- a/test/panic/1putackget.js +++ b/test/panic/1putackget.js @@ -116,12 +116,13 @@ describe("Put ACK", function(){ }, {acks: c}); function wire(){ + ref.hear = ref.hear || []; var hear = ref._.root.opt.mesh.hear; ref._.root.opt.mesh.hear = function(raw, peer){ var msg = Gun.obj.ify(raw); console.log('hear:', msg); hear(raw, peer); - (ref.hear || (ref.hear = [])).push(msg); + ref.hear.push(msg); } var say = ref._.root.opt.mesh.say; ref._.root.opt.mesh.say = function(raw, peer){ @@ -150,12 +151,13 @@ describe("Put ACK", function(){ console.log("I AM DAVE"); test.async(); var c = 0, to; + ref.hear = ref.hear || []; var hear = ref._.root.opt.mesh.hear; ref._.root.opt.mesh.hear = function(raw, peer){ var msg = Gun.obj.ify(raw); console.log('hear:', msg); hear(raw, peer); - (ref.hear || (ref.hear = [])).push(msg); + ref.hear.push(msg); if(msg.put){ ++c } } diff --git a/test/panic/3puts.js b/test/panic/3puts.js new file mode 100644 index 000000000..734dd901e --- /dev/null +++ b/test/panic/3puts.js @@ -0,0 +1,137 @@ +/* +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: 2, + puts: 1000, + 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); + +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.ref = gun.get('test'); + }, {i: i += 1, config: config})); + }); + return Promise.all(tests); + }); + + it("Puts", function(){ + return alice.run(function(test){ + console.log("I AM ALICE"); + test.async(); + var i = test.props.puts, d = 0; + while(i--){ go(i) } + function go(i){ + ref.get(i).put({hello: 'world'}, function(ack){ + if(ack.err){ put_failed } + if(++d !== test.props.puts){ return } + console.log("all success", d); + test.done(); + }); + } + }, {puts: config.puts}); + }); + + 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 diff --git a/test/rad/rad.js b/test/rad/rad.js index 6d07686ea..d1a638d2e 100644 --- a/test/rad/rad.js +++ b/test/rad/rad.js @@ -190,12 +190,13 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam if(opt.end < v){ return } if(v.indexOf(find) == 0){ all[v] = true } }); - rad(find, function(err, data){ + rad(find, function(err, data, o){ Radix.map(data, function(v,k){ //console.log(find+k, v); delete all[find+k]; }); if(!Gun.obj.empty(all)){ return } + if(!data){ return } // in case there is "more" that returned empty done(); }, opt); });