diff --git a/gun.js b/gun.js index 8c49d684..7945d324 100644 --- a/gun.js +++ b/gun.js @@ -621,21 +621,25 @@ function Dup(opt){ var dup = {s:{}}; opt = opt || {max: 1000, age: 1000 * 9};//1000 * 60 * 2}; - dup.check = function(id){ - return dup.s[id]? dup.track(id) : false; + dup.check = function(id){ var tmp; + if(!(tmp = dup.s[id])){ return false } + if(tmp.pass){ return tmp.pass = false } + return dup.track(id); } - dup.track = function(id){ - dup.s[id] = time_is(); + dup.track = function(id, pass){ + var it = dup.s[id] || (dup.s[id] = {}); + it.was = time_is(); + if(pass){ it.pass = true } if(!dup.to){ dup.to = setTimeout(function(){ - Type.obj.map(dup.s, function(time, id){ - if(opt.age > (time_is() - time)){ return } + Type.obj.map(dup.s, function(it, id){ + if(opt.age > (time_is() - it.was)){ return } Type.obj.del(dup.s, id); }); dup.to = null; }, opt.age); } - return id; + return it; } return dup; } @@ -685,13 +689,13 @@ return gun; } function root(msg){ - //console.log("add to.next(at)"); // TODO: BUG!!! - var ev = this, at = ev.as, gun = at.gun, tmp; + //console.log("add to.next(at)"); // TODO: MISSING FEATURE!!! + var ev = this, at = ev.as, gun = at.gun, dup, tmp; //if(!msg.gun){ msg.gun = at.gun } if(!(tmp = msg['#'])){ tmp = msg['#'] = text_rand(9) } - if(at.dup.check(tmp)){ return } - at.dup.track(tmp); - msg = obj_to(msg);//, {gun: at.gun}); + if((dup = at.dup).check(tmp)){ return } + dup.track(tmp); + //msg = obj_to(msg);//, {gun: at.gun}); // can we delete this now? if(!at.ask(msg['@'], msg)){ if(msg.get){ Gun.on.get(msg, gun); @@ -804,7 +808,7 @@ //tmp = at.ack; root.on('in', { '@': msg['#'], - //how: 'mem', + how: 'mem', put: node, gun: gun }); @@ -940,7 +944,7 @@ }*/ if(get['#'] || at.soul){ get['#'] = get['#'] || at.soul; - msg['#'] || (msg['#'] = text_rand()); + msg['#'] || (msg['#'] = text_rand(9)); back = (root.gun.get(get['#'])._); if(!(get = get['.'])){ if(obj_has(back, 'put')){ @@ -1555,6 +1559,10 @@ } Gun.chain.val = function(cb, opt){ + Gun.log.once("onceval", "Future Breaking API Change: .val -> .once, apologies unexpected."); + return this.once(cb, opt); + } + Gun.chain.once = function(cb, opt){ var gun = this, at = gun._, data = at.put; if(0 < at.ack && u !== data){ (cb || noop).call(gun, data, at.get); @@ -1569,7 +1577,7 @@ } else { Gun.log.once("valonce", "Chainable val is experimental, its behavior and API may change moving forward. Please play with it and report bugs and ideas on how to improve it."); var chain = gun.chain(); - chain._.val = gun.val(function(){ + chain._.val = gun.once(function(){ chain._.on('in', gun._); }); return chain; @@ -1793,105 +1801,249 @@ })(USE, './adapters/localStorage'); ;USE(function(module){ - var Gun = USE('./index'); - var websocket; - if(typeof WebSocket !== 'undefined'){ - websocket = WebSocket; - } else { - if(typeof webkitWebSocket !== 'undefined'){ - websocket = webkitWebSocket; + var Type = USE('./type'); + + function Mesh(ctx){ + var mesh = function(){}; + + mesh.out = function(msg){ var tmp; + if(this.to){ this.to.next(msg) } + if((tmp = msg['@']) + && (tmp = ctx.dup.s[tmp]) + && (tmp = tmp.it)){ + mesh.say(msg, tmp.mesh.via); + tmp['##'] = msg['##']; + return; + } + // add hook for AXE? + mesh.say(msg); } - if(typeof mozWebSocket !== 'undefined'){ - websocket = mozWebSocket; + + mesh.hear = function(msg, peer){ + if(!msg){ return } + var dup = ctx.dup, id, hash, tmp = msg[0]; + try{msg = JSON.parse(msg); + }catch(e){} + if('{' === tmp){ + + 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.mesh = function(){}).via = peer; + if((tmp = msg['><'])){ + msg.mesh.to = Type.obj.map(tmp.split(','), function(k,i,m){m(k,true)}); + } + ctx.on('in', msg); + + return; + } else + if('[' === tmp){ + + var i = 0, m; + while(m = msg[i++]){ + mesh.hear(m, peer); + } + + return; + } } + + ;(function(){ + mesh.say = function(msg, peer){ + /* + TODO: Plenty of performance optimizations + that can be made just based off of ordering, + and reducing function calls for cached writes. + */ + if(!peer){ + Type.obj.map(ctx.opt.peers, function(peer){ + mesh.say(msg, peer); + }); + return; + } + var tmp, wire = peer.wire || ((ctx.opt.wire) && ctx.opt.wire(peer)), msh, raw;// || open(peer, ctx); // TODO: Reopen! + if(!wire){ return } + msh = msg.mesh || 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! + } + } + if((tmp = msh.to) && (tmp[peer.url] || tmp[peer.id])){ return } // TODO: still needs to be tested + if(peer.batch){ + peer.batch.push(raw); + return; + } + peer.batch = []; + setTimeout(function(){ + var tmp = peer.batch; + if(!tmp){ return } + peer.batch = null; + if(!tmp.length){ return } + send(JSON.stringify(tmp), peer); + }, ctx.opt.wait || 1); + send(raw, peer); + } + function send(raw, peer){ + var wire = peer.wire; + try{ + if(wire.send){ + if(wire.readyState === wire.OPEN){ + wire.send(raw); + } else { + (peer.queue = peer.queue || []).push(raw); + } + } else + if(peer.say){ + peer.say(raw); + } + }catch(e){ + (peer.queue = peer.queue || []).push(raw); + } + } + + }()); + + ;(function(){ + + mesh.raw = function(msg){ + if(!msg){ return '' } + var dup = ctx.dup, msh = msg.mesh || {}, put, hash, tmp; + if(tmp = msh.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; + if(put){ (msg = Type.obj.to(msg)).put = _ } + } + msg['><'] = (Type.obj.map(ctx.opt.peers, function(p,k,m){ + m(p.url || p.id); + }) || []).join(); + var raw = $(msg); + if(u !== put){ + raw = raw.replace('"'+ _ +'"', put); + } + if(msh){ + msh.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){ + ctx.on('hi', peer); + var queue = peer.queue; + peer.queue = []; + Type.obj.map(queue, function(msg){ + mesh.say(msg, 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=0||1/0===t||-1/0===t)}},n.text={is:function(t){return"string"==typeof t}},n.text.ify=function(t){return n.text.is(t)?t:"undefined"!=typeof JSON?JSON.stringify(t):t&&t.toString?t.toString():t},n.text.random=function(t,n){var o="";for(t=t||24,n=n||"0123456789ABCDEFGHIJKLMNOPQRSTUVWXZabcdefghijklmnopqrstuvwxyz";t>0;)o+=n.charAt(Math.floor(Math.random()*n.length)),t--;return o},n.text.match=function(t,o){var e=!1;if(t=t||"",o=n.text.is(o)?{"=":o}:o||{},n.obj.has(o,"~")&&(t=t.toLowerCase(),o["="]=(o["="]||o["~"]).toLowerCase()),n.obj.has(o,"="))return t===o["="];if(n.obj.has(o,"*")){if(t.slice(0,o["*"].length)!==o["*"])return!1;e=!0,t=t.slice(o["*"].length)}if(n.obj.has(o,"!")){if(t.slice(-o["!"].length)!==o["!"])return!1;e=!0}if(n.obj.has(o,"+")&&n.list.map(n.list.is(o["+"])?o["+"]:[o["+"]],function(n){if(!(t.indexOf(n)>=0))return!0;e=!0}))return!1;if(n.obj.has(o,"-")&&n.list.map(n.list.is(o["-"])?o["-"]:[o["-"]],function(n){if(!(t.indexOf(n)<0))return!0;e=!0}))return!1;if(n.obj.has(o,">")){if(!(t>o[">"]))return!1;e=!0}if(n.obj.has(o,"<")){if(!(to?1:0):0}},n.list.map=function(t,n,o){return a(t,n,o)},n.list.index=1,n.obj={is:function(t){return!!t&&(t instanceof Object&&t.constructor===Object||"Object"===Object.prototype.toString.call(t).match(/^\[object (\w+)\]$/)[1])}},n.obj.put=function(t,n,o){return(t||{})[n]=o,t},n.obj.has=function(t,n){return t&&Object.prototype.hasOwnProperty.call(t,n)},n.obj.del=function(t,n){if(t)return t[n]=null,delete t[n],t},n.obj.as=function(t,n,o,e){return t[n]=t[n]||(e===o?{}:o)},n.obj.ify=function(t){if(r(t))return t;try{t=JSON.parse(t)}catch(n){t={}}return t},function(){function t(t,n){u(this,n)&&o!==this[n]||(this[n]=t)}var o;n.obj.to=function(n,o){return o=o||{},a(n,t,o),o}}(),n.obj.copy=function(t){return t?JSON.parse(JSON.stringify(t)):t},function(){function t(t,n){var o=this.n;if(!o||!(n===o||r(o)&&u(o,n)))return!!n||void 0}n.obj.empty=function(n,o){return!n||!a(n,t,{n:o})}}(),function(){function t(n,o){if(2===arguments.length)return t.r=t.r||{},void(t.r[n]=o);t.r=t.r||[],t.r.push(n)}var i=Object.keys;n.obj.map=function(a,s,f){var c,l,p,g,h,d=0,v=o(s);if(t.r=null,i&&r(a)&&(g=i(a),h=!0),e(a)||g)for(l=(g||a).length;d",o.drift=0,o.is=function(t,n,e){var i=n&&t&&t[k]&&t[k][o._]||e;if(i)return b(i=i[n])?i:-1/0},o.lex=function(){return o().toString(36).replace(".","")},o.ify=function(t,n,e,r,u){if(!t||!t[k]){if(!u)return;t=i.soul.ify(t,u)}var a=g(t[k],o._);return l!==n&&n!==k&&(b(e)&&(a[n]=e),l!==r&&(t[n]=r)),t},o.to=function(t,n,e){var r=t[n];return d(r)&&(r=_(r)),o.ify(e,n,o.is(t,n),r,i.soul(t))},function(){function t(t,n){k!==n&&o.ify(this.o,n,this.s)}o.map=function(n,e,i){var r,u=d(u=n||e)?u:null;return n=m(n=n||e)?n:null,u&&!n?(e=b(e)?e:o(),u[k]=u[k]||{},v(u,t,{o:u,s:e}),u):(i=i||d(e)?e:r,e=b(e)?e:o(),function(o,u,a,s){if(!n)return t.call({o:a,s:e},o,u),o;n.call(i||this||{},o,u,a,s),h(a,u)&&r===a[u]||t.call({o:a,s:e},o,u)})}}();var l,p=e.obj,g=p.as,h=p.has,d=p.is,v=p.map,_=p.copy,b=e.num.is,m=e.fn.is,k=i._;n.exports=o})(t,"./state"),t(function(n){var o=t("./type"),e=t("./val"),i=t("./node"),r={};!function(){function t(t,o){if(!t||o!==i.soul(t)||!i.is(t,this.fn,this.as))return!0;this.cb&&(n.n=t,n.as=this.as,this.cb.call(n.as,t,o,n))}function n(t){t&&i.is(n.n,t,n.as)}r.is=function(n,o,e,i){return!(!n||!s(n)||l(n))&&!g(n,t,{cb:o,fn:e,as:i})}}(),function(){function t(t,r){var u;return(u=l(t,r))?u:(r.env=t,r.soul=o,i.ify(r.obj,n,r)&&(t.graph[e.rel.is(r.rel)]=r.node),r)}function n(n,o,r){var s,l,p=this,g=p.env;if(i._===o&&c(n,e.rel._))return r._;if(s=a(n,o,r,p,g)){if(o||(p.node=p.node||r||{},c(n,i._)&&(p.node._=h(n._)),p.node=i.soul.ify(p.node,e.rel.is(p.rel)),p.rel=p.rel||e.rel.ify(i.soul(p.node))),(l=g.map)&&(l.call(g.as||{},n,o,r,p),c(r,o))){if(n=r[o],u===n)return void f(r,o);if(!(s=a(n,o,r,p,g)))return}if(!o)return p.node;if(!0===s)return n;if((l=t(g,{obj:n,path:p.path.concat(o)})).node)return l.rel}}function o(t){var n=this,o=e.rel.is(n.rel),r=n.env.graph;n.rel=n.rel||e.rel.ify(t),n.rel[e.rel._]=t,n.node&&n.node[i._]&&(n.node[i._][e.rel._]=t),c(r,o)&&(r[t]=r[o],f(r,o))}function a(t,n,o,i,r){var u;return!!e.is(t)||(s(t)?1:(u=r.invalid)?(t=u.call(r.as||{},t,n,o),a(t,n,o,i,r)):void(r.err="Invalid value at '"+i.path.concat(n).join(".")+"'!"))}function l(t,n){for(var o,e=t.seen,i=e.length;i--;)if(o=e[i],n.obj===o.obj)return o;e.push(n)}r.ify=function(n,o,i){var r={path:[],obj:n};return o?"string"==typeof o?o={soul:o}:o instanceof Function&&(o.map=o):o={},o.soul&&(r.rel=e.rel.ify(o.soul)),o.graph=o.graph||{},o.seen=o.seen||[],o.as=o.as||i,t(o,r),o.root=r.node,o.graph}}(),r.node=function(t){var n=i.soul(t);if(n)return p({},n,t)},function(){function t(t,n){var o,u;if(i._!==n)(o=e.rel.is(t))?(u=this.opt.seen[o])?this.obj[n]=u:this.obj[n]=this.opt.seen[o]=r.to(this.graph,o,this.opt):this.obj[n]=t;else{if(l(t,e.rel._))return;this.obj[n]=h(t)}}r.to=function(n,o,e){if(n){var i={};return e=e||{seen:{}},g(n[o],t,{obj:i,graph:n,opt:e}),i}}}();o.fn.is;var u,a=o.obj,s=a.is,f=a.del,c=a.has,l=a.empty,p=a.put,g=a.map,h=a.copy;n.exports=r})(t,"./graph"),t(function(n){t("./onto"),n.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."}),i.off()},(this.opt||{}).lack||9e3),o}}})(t,"./ask"),t(function(n){var o=t("./type"),e=o.time.is;n.exports=function(t){var n={s:{}};return t=t||{max:1e3,age:9e3},n.check=function(t){return!!n.s[t]&&n.track(t)},n.track=function(i){return n.s[i]=e(),n.to||(n.to=setTimeout(function(){o.obj.map(n.s,function(i,r){t.age>e()-i||o.obj.del(n.s,r)}),n.to=null},t.age)),i},n}})(t,"./dup"),t(function(n){function i(t){return t instanceof i?(this._={gun:this}).gun:this instanceof i?i.create(this._={gun:this,opt:t}):new i(t)}i.is=function(t){return t instanceof i},i.version=.9,i.chain=i.prototype,i.chain.toJSON=function(){};var r=t("./type");r.obj.to(r,i),i.HAM=t("./HAM"),i.val=t("./val"),i.node=t("./node"),i.state=t("./state"),i.graph=t("./graph"),i.on=t("./onto"),i.ask=t("./ask"),i.dup=t("./dup"),function(){function t(t){var n,o=this.as,e=o.gun;(n=t["#"])||(n=t["#"]=c(9)),o.dup.check(n)||(o.dup.track(n),t=h(t),o.ask(t["@"],t)||(t.get&&i.on.get(t,e),t.put&&i.on.put(t,e)),o.on("out",t))}i.create=function(n){n.root=n.root||n.gun,n.graph=n.graph||{},n.on=n.on||i.on,n.ask=n.ask||i.ask,n.dup=n.dup||i.dup();var o=n.gun.opt(n.opt);return n.once||(n.on("in",t,n),n.on("out",t,n)),n.once=1,o}}(),function(){function t(t,n,o,e){var r=this,u=i.state.is(o,n);if(!u)return r.err="Error: No state on '"+n+"' in node '"+e+"'!";var a=r.graph[e]||m,s=i.state.is(a,n,!0),f=a[n],c=i.HAM(r.machine,u,s,t,f);c.incoming?(r.put[e]=i.state.to(o,n,r.put[e]),(r.diff||(r.diff={}))[e]=i.state.to(o,n,r.diff[e]),r.souls[e]=!0):c.defer&&(r.defer=u<(r.defer||1/0)?u:r.defer)}function n(t,n){var i=this,u=i.gun._,a=(u.next||m)[n];if(a){var s=i.map[n]={put:t,get:n,gun:a},f={ctx:i,msg:s};i.async=!!u.tag.node,i.ack&&(s["@"]=i.ack),d(t,o,f),i.async&&(i.and||u.on("node",function(t){this.to.next(t),t===i.map[t.get]&&(i.souls[t.get]=!1,d(t.put,e,t),d(i.souls,function(t){if(t)return t})||i.c||(i.c=1,this.off(),u.stop={},d(i.map,r,i)))}),i.and=!0,u.on("node",s))}else i.souls[n]=!1}function o(t,n){var o=this.ctx,e=o.graph,r=this.msg,u=r.get,a=r.put,s=r.gun._;e[u]=i.state.to(a,n,e[u]),o.async||(s.put=i.state.to(a,n,s.put))}function e(t,n){var o=this,e=o.put,r=o.gun._;r.put=i.state.to(e,n,r.put)}function r(t,n){t.gun&&t.gun._.on("in",t)}i.on.put=function(o,e){var a=e._,s={gun:e,graph:a.graph,put:{},map:{},souls:{},machine:i.state(),ack:o["@"]};if(i.graph.is(o.put,null,t,s)||(s.err="Error: Invalid graph!"),s.err)return a.on("in",{"@":o["#"],err:i.log(s.err)});d(s.put,n,s),s.async||(a.stop={},d(s.map,r,s)),u!==s.defer&&setTimeout(function(){i.on.put(o,e)},s.defer-s.machine),s.diff&&a.on("put",h(o,{put:s.diff}))},i.on.get=function(t,n){var o=n._,e=t.get[_],r=o.graph[e],u=t.get[b],a=((o.next||(o.next={}))[e]||m)._;if(!r||!a)return o.on("get",t);if(u){if(!g(r,u))return o.on("get",t);r=i.state.to(r,u)}else r=i.obj.copy(r);r=i.graph.node(r),o.on("in",{"@":t["#"],put:r,gun:n}),o.on("get",t)}}(),i.chain.opt=function(t){t=t||{};var n=this,o=n._,e=t.peers||t;return p(t)||(t={}),p(o.opt)||(o.opt=t),f(e)&&(e=[e]),a(e)&&(e=d(e,function(t,n,o){o(t,{url:t})}),p(o.opt.peers)||(o.opt.peers={}),o.opt.peers=h(e,o.opt.peers)),o.opt.peers=o.opt.peers||{},h(t,o.opt),i.on("opt",o),o.opt.uuid=o.opt.uuid||function(){return v()+c(12)},n};var u,a=i.list.is,s=i.text,f=s.is,c=s.random,l=i.obj,p=l.is,g=l.has,h=l.to,d=l.map,v=(l.copy,i.state.lex),_=i.val.rel._,b=".",m=(i.node._,i.val.rel.is,{});o.debug=function(t,n){return o.debug.i&&t===o.debug.i&&o.debug.i++&&(o.log.apply(o,arguments)||n)},i.log=function(){return!i.log.off&&o.log.apply(o,arguments),[].slice.call(arguments).join(" ")},i.log.once=function(t,n,o){return(o=i.log.once)[t]=o[t]||0,o[t]++||i.log(n)},i.log.once("welcome","Hello wonderful person! :) Thanks for using GUN, feel free to ask for help on https://gitter.im/amark/gun and ask StackOverflow questions tagged with 'gun'!"),"undefined"!=typeof window&&(window.Gun=i);try{void 0!==e&&(e.exports=i)}catch(t){}n.exports=i})(t,"./root"),t(function(n){var o=t("./root");o.chain.back=function(t,n){if(-1===(t=t||1)||1/0===t)return this._.root;if(1===t)return this._.back||this;var r=this,u=r._;if("string"==typeof t&&(t=t.split(".")),!(t instanceof Array)){if(t instanceof Function){for(var a,s={back:r};(s=s.back)&&(s=s._)&&!(a=t(s,n)););return a}return o.num.is(t)?u.back.back(t-1):this}var f=0,c=t.length,s=u;for(f;f=(n.batch||1e3))return s();o||(o=setTimeout(s,n.wait||1))}),t.on("get",function(o){this.to.next(o);var e,i,r=o.get;if(r&&(e=r["#"])){var a=r["."];(i=u[e]||void 0)&&a&&(i=Gun.state.to(i,a)),(i||Gun.obj.empty(n.peers))&&t.on("in",{"@":o["#"],put:Gun.graph.node(i),how:"lS"})}});var a=function(t,n,o,e){u[e]=Gun.state.to(o,n,u[e])},s=function(){var a;r=0,clearTimeout(o),o=!1;var s=i;i={};try{e.setItem(n.file,JSON.stringify(u))}catch(t){Gun.log(a=t||"localStorage failure")}(a||Gun.obj.empty(n.peers))&&Gun.obj.map(s,function(n,o){t.on("in",{"@":o,err:a,ok:0})})}}})}})(t,"./adapters/localStorage"),t(function(n){var o,e=t("./index");"undefined"!=typeof WebSocket?o=WebSocket:("undefined"!=typeof webkitWebSocket&&(o=webkitWebSocket),"undefined"!=typeof mozWebSocket&&(o=mozWebSocket)),e.on("opt",function(t){function n(t){var n=this,o=c,e=t.wire||u(t,n);e&&(e.readyState!==e.OPEN?(t.queue=t.queue||[]).push(o):e.send(o))}function r(t,n,o){if(o&&t){try{t=JSON.parse(t.data||t)}catch(t){}if(t instanceof Array)for(var e,u=0;e=t[u++];)r(e,n,o);else 1==f.who&&(t.ws=i),o.on("in",t)}}function u(i,u){if(i&&i.url){var s=i.url.replace("http","ws"),f=i.wire=new o(s);return f.onclose=function(){t.on("bye",i),a(i,u)},f.onerror=function(t){a(i,u),t&&t.code},f.onopen=function(){t.on("hi",i);var o=i.queue;i.queue=[],e.obj.map(o,function(t){c=t,n.call(u,i)})},f.onmessage=function(t){r(t,i,u)},f}}function a(t,n){clearTimeout(t.defer),t.defer=setTimeout(function(){u(t,n)},2e3)}this.to.next(t);var s=t.opt;if(!t.once&&o&&!1!==s.WebSocket){var f=s.ws||(s.ws={});if(f.who=0,e.obj.map(s.peers,function(){++f.who}),!t.once){var c;t.on("out",function(o){this.to.next(o),o.ws&&1==f.who||(c=JSON.stringify(o),f.drain?f.drain.push(c):(f.drain=[],setTimeout(function(){if(f.drain){var o=f.drain;f.drain=null,o.length&&(c=JSON.stringify(o),e.obj.map(s.peers,n,t))}},s.wait||1),e.obj.map(s.peers,n,t)))})}}});var i=function(){}})(t,"./adapters/websocket")}(); \ No newline at end of file +!function(){function t(n){function o(t){return t.split("/").slice(-1).toString().replace(".js","")}return n.slice?t[o(n)]:function(e,i){n(e={exports:{}}),t[o(i)]=e.exports}}var n;"undefined"!=typeof window&&(n=window),"undefined"!=typeof global&&(n=global);var o=(n=n||{}).console||{log:function(){}};if("undefined"!=typeof module)var e=module;t(function(t){var n={};n.fns=n.fn={is:function(t){return!!t&&"function"==typeof t}},n.bi={is:function(t){return t instanceof Boolean||"boolean"==typeof t}},n.num={is:function(t){return!e(t)&&(t-parseFloat(t)+1>=0||1/0===t||-1/0===t)}},n.text={is:function(t){return"string"==typeof t}},n.text.ify=function(t){return n.text.is(t)?t:"undefined"!=typeof JSON?JSON.stringify(t):t&&t.toString?t.toString():t},n.text.random=function(t,n){var o="";for(t=t||24,n=n||"0123456789ABCDEFGHIJKLMNOPQRSTUVWXZabcdefghijklmnopqrstuvwxyz";t>0;)o+=n.charAt(Math.floor(Math.random()*n.length)),t--;return o},n.text.match=function(t,o){var e=!1;if(t=t||"",o=n.text.is(o)?{"=":o}:o||{},n.obj.has(o,"~")&&(t=t.toLowerCase(),o["="]=(o["="]||o["~"]).toLowerCase()),n.obj.has(o,"="))return t===o["="];if(n.obj.has(o,"*")){if(t.slice(0,o["*"].length)!==o["*"])return!1;e=!0,t=t.slice(o["*"].length)}if(n.obj.has(o,"!")){if(t.slice(-o["!"].length)!==o["!"])return!1;e=!0}if(n.obj.has(o,"+")&&n.list.map(n.list.is(o["+"])?o["+"]:[o["+"]],function(n){if(!(t.indexOf(n)>=0))return!0;e=!0}))return!1;if(n.obj.has(o,"-")&&n.list.map(n.list.is(o["-"])?o["-"]:[o["-"]],function(n){if(!(t.indexOf(n)<0))return!0;e=!0}))return!1;if(n.obj.has(o,">")){if(!(t>o[">"]))return!1;e=!0}if(n.obj.has(o,"<")){if(!(to?1:0):0}},n.list.map=function(t,n,o){return a(t,n,o)},n.list.index=1,n.obj={is:function(t){return!!t&&(t instanceof Object&&t.constructor===Object||"Object"===Object.prototype.toString.call(t).match(/^\[object (\w+)\]$/)[1])}},n.obj.put=function(t,n,o){return(t||{})[n]=o,t},n.obj.has=function(t,n){return t&&Object.prototype.hasOwnProperty.call(t,n)},n.obj.del=function(t,n){if(t)return t[n]=null,delete t[n],t},n.obj.as=function(t,n,o,e){return t[n]=t[n]||(e===o?{}:o)},n.obj.ify=function(t){if(r(t))return t;try{t=JSON.parse(t)}catch(n){t={}}return t},function(){function t(t,n){u(this,n)&&o!==this[n]||(this[n]=t)}var o;n.obj.to=function(n,o){return o=o||{},a(n,t,o),o}}(),n.obj.copy=function(t){return t?JSON.parse(JSON.stringify(t)):t},function(){function t(t,n){var o=this.n;if(!o||!(n===o||r(o)&&u(o,n)))return!!n||void 0}n.obj.empty=function(n,o){return!n||!a(n,t,{n:o})}}(),function(){function t(n,o){if(2===arguments.length)return t.r=t.r||{},void(t.r[n]=o);t.r=t.r||[],t.r.push(n)}var i=Object.keys;n.obj.map=function(a,s,f){var c,l,p,h,g,d=0,v=o(s);if(t.r=null,i&&r(a)&&(h=i(a),g=!0),e(a)||h)for(l=(h||a).length;d",o.drift=0,o.is=function(t,n,e){var i=n&&t&&t[k]&&t[k][o._]||e;if(i)return _(i=i[n])?i:-1/0},o.lex=function(){return o().toString(36).replace(".","")},o.ify=function(t,n,e,r,u){if(!t||!t[k]){if(!u)return;t=i.soul.ify(t,u)}var a=h(t[k],o._);return l!==n&&n!==k&&(_(e)&&(a[n]=e),l!==r&&(t[n]=r)),t},o.to=function(t,n,e){var r=t[n];return d(r)&&(r=b(r)),o.ify(e,n,o.is(t,n),r,i.soul(t))},function(){function t(t,n){k!==n&&o.ify(this.o,n,this.s)}o.map=function(n,e,i){var r,u=d(u=n||e)?u:null;return n=m(n=n||e)?n:null,u&&!n?(e=_(e)?e:o(),u[k]=u[k]||{},v(u,t,{o:u,s:e}),u):(i=i||d(e)?e:r,e=_(e)?e:o(),function(o,u,a,s){if(!n)return t.call({o:a,s:e},o,u),o;n.call(i||this||{},o,u,a,s),g(a,u)&&r===a[u]||t.call({o:a,s:e},o,u)})}}();var l,p=e.obj,h=p.as,g=p.has,d=p.is,v=p.map,b=p.copy,_=e.num.is,m=e.fn.is,k=i._;n.exports=o})(t,"./state"),t(function(n){var o=t("./type"),e=t("./val"),i=t("./node"),r={};!function(){function t(t,o){if(!t||o!==i.soul(t)||!i.is(t,this.fn,this.as))return!0;this.cb&&(n.n=t,n.as=this.as,this.cb.call(n.as,t,o,n))}function n(t){t&&i.is(n.n,t,n.as)}r.is=function(n,o,e,i){return!(!n||!s(n)||l(n))&&!h(n,t,{cb:o,fn:e,as:i})}}(),function(){function t(t,r){var u;return(u=l(t,r))?u:(r.env=t,r.soul=o,i.ify(r.obj,n,r)&&(t.graph[e.rel.is(r.rel)]=r.node),r)}function n(n,o,r){var s,l,p=this,h=p.env;if(i._===o&&c(n,e.rel._))return r._;if(s=a(n,o,r,p,h)){if(o||(p.node=p.node||r||{},c(n,i._)&&(p.node._=g(n._)),p.node=i.soul.ify(p.node,e.rel.is(p.rel)),p.rel=p.rel||e.rel.ify(i.soul(p.node))),(l=h.map)&&(l.call(h.as||{},n,o,r,p),c(r,o))){if(n=r[o],u===n)return void f(r,o);if(!(s=a(n,o,r,p,h)))return}if(!o)return p.node;if(!0===s)return n;if((l=t(h,{obj:n,path:p.path.concat(o)})).node)return l.rel}}function o(t){var n=this,o=e.rel.is(n.rel),r=n.env.graph;n.rel=n.rel||e.rel.ify(t),n.rel[e.rel._]=t,n.node&&n.node[i._]&&(n.node[i._][e.rel._]=t),c(r,o)&&(r[t]=r[o],f(r,o))}function a(t,n,o,i,r){var u;return!!e.is(t)||(s(t)?1:(u=r.invalid)?(t=u.call(r.as||{},t,n,o),a(t,n,o,i,r)):void(r.err="Invalid value at '"+i.path.concat(n).join(".")+"'!"))}function l(t,n){for(var o,e=t.seen,i=e.length;i--;)if(o=e[i],n.obj===o.obj)return o;e.push(n)}r.ify=function(n,o,i){var r={path:[],obj:n};return o?"string"==typeof o?o={soul:o}:o instanceof Function&&(o.map=o):o={},o.soul&&(r.rel=e.rel.ify(o.soul)),o.graph=o.graph||{},o.seen=o.seen||[],o.as=o.as||i,t(o,r),o.root=r.node,o.graph}}(),r.node=function(t){var n=i.soul(t);if(n)return p({},n,t)},function(){function t(t,n){var o,u;if(i._!==n)(o=e.rel.is(t))?(u=this.opt.seen[o])?this.obj[n]=u:this.obj[n]=this.opt.seen[o]=r.to(this.graph,o,this.opt):this.obj[n]=t;else{if(l(t,e.rel._))return;this.obj[n]=g(t)}}r.to=function(n,o,e){if(n){var i={};return e=e||{seen:{}},h(n[o],t,{obj:i,graph:n,opt:e}),i}}}();o.fn.is;var u,a=o.obj,s=a.is,f=a.del,c=a.has,l=a.empty,p=a.put,h=a.map,g=a.copy;n.exports=r})(t,"./graph"),t(function(n){t("./onto"),n.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."}),i.off()},(this.opt||{}).lack||9e3),o}}})(t,"./ask"),t(function(n){var o=t("./type"),e=o.time.is;n.exports=function(t){var n={s:{}};return t=t||{max:1e3,age:9e3},n.check=function(t){var o;return!!(o=n.s[t])&&(o.pass?o.pass=!1:n.track(t))},n.track=function(i,r){var u=n.s[i]||(n.s[i]={});return u.was=e(),r&&(u.pass=!0),n.to||(n.to=setTimeout(function(){o.obj.map(n.s,function(i,r){t.age>e()-i.was||o.obj.del(n.s,r)}),n.to=null},t.age)),u},n}})(t,"./dup"),t(function(n){function i(t){return t instanceof i?(this._={gun:this}).gun:this instanceof i?i.create(this._={gun:this,opt:t}):new i(t)}i.is=function(t){return t instanceof i},i.version=.9,i.chain=i.prototype,i.chain.toJSON=function(){};var r=t("./type");r.obj.to(r,i),i.HAM=t("./HAM"),i.val=t("./val"),i.node=t("./node"),i.state=t("./state"),i.graph=t("./graph"),i.on=t("./onto"),i.ask=t("./ask"),i.dup=t("./dup"),function(){function t(t){var n,o,e=this.as,r=e.gun;(o=t["#"])||(o=t["#"]=c(9)),(n=e.dup).check(o)||(n.track(o),e.ask(t["@"],t)||(t.get&&i.on.get(t,r),t.put&&i.on.put(t,r)),e.on("out",t))}i.create=function(n){n.root=n.root||n.gun,n.graph=n.graph||{},n.on=n.on||i.on,n.ask=n.ask||i.ask,n.dup=n.dup||i.dup();var o=n.gun.opt(n.opt);return n.once||(n.on("in",t,n),n.on("out",t,n)),n.once=1,o}}(),function(){function t(t,n,o,e){var r=this,u=i.state.is(o,n);if(!u)return r.err="Error: No state on '"+n+"' in node '"+e+"'!";var a=r.graph[e]||m,s=i.state.is(a,n,!0),f=a[n],c=i.HAM(r.machine,u,s,t,f);c.incoming?(r.put[e]=i.state.to(o,n,r.put[e]),(r.diff||(r.diff={}))[e]=i.state.to(o,n,r.diff[e]),r.souls[e]=!0):c.defer&&(r.defer=u<(r.defer||1/0)?u:r.defer)}function n(t,n){var i=this,u=i.gun._,a=(u.next||m)[n];if(a){var s=i.map[n]={put:t,get:n,gun:a},f={ctx:i,msg:s};i.async=!!u.tag.node,i.ack&&(s["@"]=i.ack),d(t,o,f),i.async&&(i.and||u.on("node",function(t){this.to.next(t),t===i.map[t.get]&&(i.souls[t.get]=!1,d(t.put,e,t),d(i.souls,function(t){if(t)return t})||i.c||(i.c=1,this.off(),u.stop={},d(i.map,r,i)))}),i.and=!0,u.on("node",s))}else i.souls[n]=!1}function o(t,n){var o=this.ctx,e=o.graph,r=this.msg,u=r.get,a=r.put,s=r.gun._;e[u]=i.state.to(a,n,e[u]),o.async||(s.put=i.state.to(a,n,s.put))}function e(t,n){var o=this,e=o.put,r=o.gun._;r.put=i.state.to(e,n,r.put)}function r(t,n){t.gun&&t.gun._.on("in",t)}i.on.put=function(o,e){var a=e._,s={gun:e,graph:a.graph,put:{},map:{},souls:{},machine:i.state(),ack:o["@"]};if(i.graph.is(o.put,null,t,s)||(s.err="Error: Invalid graph!"),s.err)return a.on("in",{"@":o["#"],err:i.log(s.err)});d(s.put,n,s),s.async||(a.stop={},d(s.map,r,s)),u!==s.defer&&setTimeout(function(){i.on.put(o,e)},s.defer-s.machine),s.diff&&a.on("put",g(o,{put:s.diff}))},i.on.get=function(t,n){var o=n._,e=t.get[b],r=o.graph[e],u=t.get[_],a=((o.next||(o.next={}))[e]||m)._;if(!r||!a)return o.on("get",t);if(u){if(!h(r,u))return o.on("get",t);r=i.state.to(r,u)}else r=i.obj.copy(r);r=i.graph.node(r),o.on("in",{"@":t["#"],how:"mem",put:r,gun:n}),o.on("get",t)}}(),i.chain.opt=function(t){t=t||{};var n=this,o=n._,e=t.peers||t;return p(t)||(t={}),p(o.opt)||(o.opt=t),f(e)&&(e=[e]),a(e)&&(e=d(e,function(t,n,o){o(t,{url:t})}),p(o.opt.peers)||(o.opt.peers={}),o.opt.peers=g(e,o.opt.peers)),o.opt.peers=o.opt.peers||{},g(t,o.opt),i.on("opt",o),o.opt.uuid=o.opt.uuid||function(){return v()+c(12)},n};var u,a=i.list.is,s=i.text,f=s.is,c=s.random,l=i.obj,p=l.is,h=l.has,g=l.to,d=l.map,v=(l.copy,i.state.lex),b=i.val.rel._,_=".",m=(i.node._,i.val.rel.is,{});o.debug=function(t,n){return o.debug.i&&t===o.debug.i&&o.debug.i++&&(o.log.apply(o,arguments)||n)},i.log=function(){return!i.log.off&&o.log.apply(o,arguments),[].slice.call(arguments).join(" ")},i.log.once=function(t,n,o){return(o=i.log.once)[t]=o[t]||0,o[t]++||i.log(n)},i.log.once("welcome","Hello wonderful person! :) Thanks for using GUN, feel free to ask for help on https://gitter.im/amark/gun and ask StackOverflow questions tagged with 'gun'!"),"undefined"!=typeof window&&(window.Gun=i);try{void 0!==e&&(e.exports=i)}catch(t){}n.exports=i})(t,"./root"),t(function(n){var o=t("./root");o.chain.back=function(t,n){if(-1===(t=t||1)||1/0===t)return this._.root;if(1===t)return this._.back||this;var r=this,u=r._;if("string"==typeof t&&(t=t.split(".")),!(t instanceof Array)){if(t instanceof Function){for(var a,s={back:r};(s=s.back)&&(s=s._)&&!(a=t(s,n)););return a}return o.num.is(t)?u.back.back(t-1):this}var f=0,c=t.length,s=u;for(f;f .once, apologies unexpected."),this.once(t,n)},i.chain.once=function(t,n){var o=this,u=o._,a=u.put;if(0=(n.batch||1e3))return s();o||(o=setTimeout(s,n.wait||1))}),t.on("get",function(o){this.to.next(o);var e,i,r=o.get;if(r&&(e=r["#"])){var a=r["."];(i=u[e]||void 0)&&a&&(i=Gun.state.to(i,a)),(i||Gun.obj.empty(n.peers))&&t.on("in",{"@":o["#"],put:Gun.graph.node(i),how:"lS"})}});var a=function(t,n,o,e){u[e]=Gun.state.to(o,n,u[e])},s=function(){var a;r=0,clearTimeout(o),o=!1;var s=i;i={};try{e.setItem(n.file,JSON.stringify(u))}catch(t){Gun.log(a=t||"localStorage failure")}(a||Gun.obj.empty(n.peers))&&Gun.obj.map(s,function(n,o){t.on("in",{"@":o,err:a,ok:0})})}}})}})(t,"./adapters/localStorage"),t(function(n){function o(t){var n=function(){};return n.out=function(o){var e;if(this.to&&this.to.next(o),(e=o["@"])&&(e=t.dup.s[e])&&(e=e.it))return n.say(o,e.mesh.via),void(e["##"]=o["##"]);n.say(o)},n.hear=function(o,i){if(o){var r,u,a=t.dup,s=o[0];try{o=JSON.parse(o)}catch(t){}if("{"===s){if(a.check(r=o["#"]))return;if(a.track(r,!0).it=o,(s=o["@"])&&o.put&&(u=o["##"]||(o["##"]=n.hash(o)),(s+=u)!=r)){if(a.check(s))return;(s=a.s)[u]=s[r]}return(o.mesh=function(){}).via=i,(s=o["><"])&&(o.mesh.to=e.obj.map(s.split(","),function(t,n,o){o(t,!0)})),void t.on("in",o)}if("["!==s);else for(var f,c=0;f=o[c++];)n.hear(f,i)}},function(){function o(t,n){var o=n.wire;try{o.send?o.readyState===o.OPEN?o.send(t):(n.queue=n.queue||[]).push(t):n.say&&n.say(t)}catch(o){(n.queue=n.queue||[]).push(t)}}n.say=function(i,u){if(u){var a,s,f;(u.wire||t.opt.wire&&t.opt.wire(u))&&(s=i.mesh||r,u!==s.via&&((f=s.raw)||(f=n.raw(i)),(a=i["@"])&&(a=t.dup.s[a])&&(a=a.it)&&a.get&&a["##"]&&a["##"]===i["##"]||(a=s.to)&&(a[u.url]||a[u.id])||(u.batch?u.batch.push(f):(u.batch=[],setTimeout(function(){var t=u.batch;t&&(u.batch=null,t.length&&o(JSON.stringify(t),u))},t.opt.wait||1),o(f,u)))))}else e.obj.map(t.opt.peers,function(t){n.say(i,t)})}}(),function(){function r(t,n){var o;return n instanceof Object?(e.obj.map(Object.keys(n).sort(),u,{to:o={},on:n}),o):n}function u(t){this.to[t]=this.on[t]}n.raw=function(o){if(!o)return"";var u,f,c,l=t.dup,p=o.mesh||{};if(c=p.raw)return c;if("string"==typeof o)return o;o["@"]&&(c=o.put)&&((f=o["##"])||(u=a(c,r)||"",f=n.hash(o,u),o["##"]=f),(c=l.s)[f=o["@"]+f]=c[o["#"]],o["#"]=f,u&&((o=e.obj.to(o)).put=s)),o["><"]=(e.obj.map(t.opt.peers,function(t,n,o){o(t.url||t.id)})||[]).join();var h=a(o);return i!==u&&(h=h.replace('"'+s+'"',u)),p&&(p.raw=h),h},n.hash=function(t,n){return o.hash(n||a(t.put,r)||"")||t["#"]||e.text.random(9)};var a=JSON.stringify,s=":])([:"}(),n.hi=function(o){t.on("hi",o);var i=o.queue;o.queue=[],e.obj.map(i,function(t){n.say(t,o)})},n}var e=t("./type");o.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'+Radisk.encode(Gun.state.is(node, key)); - rad(soul+'.'+key, val, (u === acks? u : ack)); + rad(soul+'.'+key, val, (track? ack : u)); }); function ack(err, ok){ acks--; diff --git a/lib/wire.js b/lib/wire.js new file mode 100644 index 00000000..3daeaa8a --- /dev/null +++ b/lib/wire.js @@ -0,0 +1,91 @@ +var Gun = require('../gun'); + +/* + An Ad-Hoc Mesh-Network Daisy-Chain + should work even if humans are + communicating with each other blind. + + To prevent infinite broadcast loops, + we use a deduplication process + based on the message's identifier. + This is currently implemented in core. + + However, because this still creates a + N*2 (where N is the number of connections) + flood, it is not scalable for traditional + services that have a hub network topology. + + Does this mean we have to abandon mesh + algorithms? No, we can simply layer more + efficient optimizations in based on constraints. + If these constraints exist, it automatically + upgrades, but if not, it falls back to the + brute-force mesh based robust algorithm. + A simple example is to limit peer connections + and rely upon daisy chaining to relay messages. + + Another example, is if peers are willing to + identify themselves, then we can improve the + efficiency of the network by having each peer + include the names of peers it is connected in + each message. Then each subsequent peer will + not relay it to them, since it is unnecessary. + This should create N (where N is the number of + peers) messages (or possibly N+ if there is a + common peer of uncommon peers that receives it + and relays at exact latency timings), which is + optimal. + + Since computer networks aren't actually blind, + we will implement the above method to improve + the performance of the ad-hoc mesh network. + + But why not have every message contain the + whole history of peers that it relayed through? + Because in sufficiently large enough networks, + with extensive daisy chaining, this will cause + the message to become prohibitively slow and + increase indefinitely in size. + +*/ + +var WebSocket = require('ws'); + +var url = require('url'); + +Gun.on('opt', function(ctx){ + var opt = ctx.opt; + if(false === opt.ws){ + this.to.next(); + return; + } + + opt.WebSocket = opt.WebSocket || WebSocket; + + if(!opt.ws || !opt.ws.web){ + + var ws = opt.ws = opt.ws || {}; + ws.server = ws.server || opt.web; + ws.path = ws.path || '/gun'; + ws.web = new opt.WebSocket.Server(ws); + ws.web.on('connection', function(wire){ + wire.upgradeReq = wire.upgradeReq || {}; + wire.url = url.parse(wire.upgradeReq.url||'', true); + wire.id = wire.id || Gun.text.random(6); + var peer = opt.peers[wire.id] = {id: wire.id, wire: wire}; + wire.peer = function(){ return peer }; + ctx.on('hi', peer); + wire.on('message', function(msg){ + //console.log("MESSAGE", msg); + opt.mesh.hear(msg.data || msg, peer); + }); + wire.on('close', function(){ + ctx.on('bye', peer); + Gun.obj.del(opt.peers, wire.id); + }); + wire.on('error', function(e){}); + }); + } + + this.to.next(ctx); +}); \ No newline at end of file diff --git a/lib/ws.js b/lib/ws.js index 8ddf6d00..2cb2e88e 100644 --- a/lib/ws.js +++ b/lib/ws.js @@ -12,6 +12,7 @@ Gun.on('opt', function mount(ctx){ opt.peers = [opt]; if(ctx.once){ return } + if(false === opt.ws){ return } var ws = opt.ws || (opt.ws = {}), batch; if(opt.web){ diff --git a/package.json b/package.json index 2adf0d0e..8ed49796 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gun", - "version": "0.9.96", + "version": "0.9.97", "description": "A realtime, decentralized, offline-first, graph data synchronization engine.", "main": "index.js", "browser": "gun.min.js", diff --git a/src/adapters/mesh.js b/src/adapters/mesh.js new file mode 100644 index 00000000..f3dbd805 --- /dev/null +++ b/src/adapters/mesh.js @@ -0,0 +1,191 @@ + +var Type = require('./type'); + +function Mesh(ctx){ + var mesh = function(){}; + + mesh.out = function(msg){ var tmp; + if(this.to){ this.to.next(msg) } + if((tmp = msg['@']) + && (tmp = ctx.dup.s[tmp]) + && (tmp = tmp.it)){ + mesh.say(msg, tmp.mesh.via); + tmp['##'] = msg['##']; + return; + } + // add hook for AXE? + mesh.say(msg); + } + + mesh.hear = function(msg, peer){ + if(!msg){ return } + var dup = ctx.dup, id, hash, tmp = msg[0]; + try{msg = JSON.parse(msg); + }catch(e){} + if('{' === tmp){ + + 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.mesh = function(){}).via = peer; + if((tmp = msg['><'])){ + msg.mesh.to = Type.obj.map(tmp.split(','), function(k,i,m){m(k,true)}); + } + ctx.on('in', msg); + + return; + } else + if('[' === tmp){ + + var i = 0, m; + while(m = msg[i++]){ + mesh.hear(m, peer); + } + + return; + } + } + + ;(function(){ + mesh.say = function(msg, peer){ + /* + TODO: Plenty of performance optimizations + that can be made just based off of ordering, + and reducing function calls for cached writes. + */ + if(!peer){ + Type.obj.map(ctx.opt.peers, function(peer){ + mesh.say(msg, peer); + }); + return; + } + var tmp, wire = peer.wire || ((ctx.opt.wire) && ctx.opt.wire(peer)), msh, raw;// || open(peer, ctx); // TODO: Reopen! + if(!wire){ return } + msh = msg.mesh || 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! + } + } + if((tmp = msh.to) && (tmp[peer.url] || tmp[peer.id])){ return } // TODO: still needs to be tested + if(peer.batch){ + peer.batch.push(raw); + return; + } + peer.batch = []; + setTimeout(function(){ + var tmp = peer.batch; + if(!tmp){ return } + peer.batch = null; + if(!tmp.length){ return } + send(JSON.stringify(tmp), peer); + }, ctx.opt.wait || 1); + send(raw, peer); + } + function send(raw, peer){ + var wire = peer.wire; + try{ + if(wire.send){ + if(wire.readyState === wire.OPEN){ + wire.send(raw); + } else { + (peer.queue = peer.queue || []).push(raw); + } + } else + if(peer.say){ + peer.say(raw); + } + }catch(e){ + (peer.queue = peer.queue || []).push(raw); + } + } + + }()); + + ;(function(){ + + mesh.raw = function(msg){ + if(!msg){ return '' } + var dup = ctx.dup, msh = msg.mesh || {}, put, hash, tmp; + if(tmp = msh.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; + if(put){ (msg = Type.obj.to(msg)).put = _ } + } + msg['><'] = (Type.obj.map(ctx.opt.peers, function(p,k,m){ + m(p.url || p.id); + }) || []).join(); + var raw = $(msg); + if(u !== put){ + raw = raw.replace('"'+ _ +'"', put); + } + if(msh){ + msh.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){ + ctx.on('hi', peer); + var queue = peer.queue; + peer.queue = []; + Type.obj.map(queue, function(msg){ + mesh.say(msg, 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 (time_is() - time)){ return } + Type.obj.map(dup.s, function(it, id){ + if(opt.age > (time_is() - it.was)){ return } Type.obj.del(dup.s, id); }); dup.to = null; }, opt.age); } - return id; + return it; } return dup; } diff --git a/src/on.js b/src/on.js index b63eb80a..4b76dbbc 100644 --- a/src/on.js +++ b/src/on.js @@ -53,6 +53,10 @@ function ok(at, ev){ var opt = this; } Gun.chain.val = function(cb, opt){ + Gun.log.once("onceval", "Future Breaking API Change: .val -> .once, apologies unexpected."); + return this.once(cb, opt); +} +Gun.chain.once = function(cb, opt){ var gun = this, at = gun._, data = at.put; if(0 < at.ack && u !== data){ (cb || noop).call(gun, data, at.get); @@ -67,7 +71,7 @@ Gun.chain.val = function(cb, opt){ } else { Gun.log.once("valonce", "Chainable val is experimental, its behavior and API may change moving forward. Please play with it and report bugs and ideas on how to improve it."); var chain = gun.chain(); - chain._.val = gun.val(function(){ + chain._.val = gun.once(function(){ chain._.on('in', gun._); }); return chain; diff --git a/src/root.js b/src/root.js index e9616342..ef1eea78 100644 --- a/src/root.js +++ b/src/root.js @@ -40,13 +40,13 @@ Gun.dup = require('./dup'); return gun; } function root(msg){ - //console.log("add to.next(at)"); // TODO: BUG!!! - var ev = this, at = ev.as, gun = at.gun, tmp; + //console.log("add to.next(at)"); // TODO: MISSING FEATURE!!! + var ev = this, at = ev.as, gun = at.gun, dup, tmp; //if(!msg.gun){ msg.gun = at.gun } if(!(tmp = msg['#'])){ tmp = msg['#'] = text_rand(9) } - if(at.dup.check(tmp)){ return } - at.dup.track(tmp); - msg = obj_to(msg);//, {gun: at.gun}); + if((dup = at.dup).check(tmp)){ return } + dup.track(tmp); + //msg = obj_to(msg);//, {gun: at.gun}); // can we delete this now? if(!at.ask(msg['@'], msg)){ if(msg.get){ Gun.on.get(msg, gun); @@ -159,7 +159,7 @@ Gun.dup = require('./dup'); //tmp = at.ack; root.on('in', { '@': msg['#'], - //how: 'mem', + how: 'mem', put: node, gun: gun }); diff --git a/test/gun.html b/test/gun.html new file mode 100644 index 00000000..35701232 --- /dev/null +++ b/test/gun.html @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/test/panic/b2s2s2b.js b/test/panic/b2s2s2b.js index 4f9b0778..457a6855 100644 --- a/test/panic/b2s2s2b.js +++ b/test/panic/b2s2s2b.js @@ -3,8 +3,8 @@ var config = { port: 8080, servers: 2, browsers: 2, - each: 12000, - burst: 1000, + each: 10000, + burst: 100, wait: 1, route: { '/': __dirname + '/index.html', diff --git a/test/panic/load.js b/test/panic/load.js index 40966ec3..1a77db71 100644 --- a/test/panic/load.js +++ b/test/panic/load.js @@ -88,7 +88,7 @@ describe("Load test "+ config.browsers +" browser(s) across "+ config.servers +" // Launch the server and start gun! var Gun = require('gun'); // Attach the server to gun. - var gun = Gun({file: env.i+'data', web: server}); + var gun = Gun({file: env.i+'data', web: server, localStorage: false}); server.listen(env.config.port + env.i, function(){ // This server peer is now done with the test! // It has successfully launched. diff --git a/test/ptsd/spam.js b/test/ptsd/spam.js index bbbce0f8..89745a5b 100644 --- a/test/ptsd/spam.js +++ b/test/ptsd/spam.js @@ -1,34 +1,21 @@ ;(function(){ window.SPAM = function(cb, opt){ opt = Gun.num.is(opt)? {each: opt} : opt || {}; + opt.wait = opt.wait || 1; setInterval(burst, opt.wait); -var n = Gun.time.is(), i = 0, c = 0, b = opt.burst || 1, l = opt.each || 100; -var raw = "AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA " +var n = Gun.time.is(), i = 0, c = 0, b = opt.burst || 10, l = opt.each || 100; +var r = Gun.text.random, raw; function save(i){ if(!window.SPAM){ return } if(i > l){ return clearTimeout(t); } - cb(i, raw + i); - return; - var d; - var ref = window.gun.get('asdf'+i); - ref.put({hello: raw + i}, function(ack){ - if(d){ return } d = true; - c++; - !(i % b) && console.log(i+'/'+l);//, '@'+Math.floor(b/((-n + (n = Gun.time.is()))/1000))+'/sec'); - //localStorage.clear(); - ref.off(); - //console.log("gl:", Object.keys(window.gun._.graph).length); - if(c < l){ return } - setTimeout(function(){ - test.done(); - }, 1000); - }); + cb(i, i + raw + i); } function burst(){ + raw = r(1000000); for(var j = 0; j <= b; j++){ save(++i); } @@ -37,7 +24,29 @@ var t; } }()); +var gun = Gun({localStorage: false, peers: 'http://localhost:8080/gun'}); +var g = gun.get('test'); +var room = Gun.text.random(100); +var pub = Gun.text.random(1000); SPAM(function(i, v){ - $("#message-input").text(v); - $('.say').trigger('click'); -}, 10000); \ No newline at end of file + //console.log(Gun.state(), i);return; + console.log(i); + var ref = g.set({ + a: v, + b: i, + c: room, + d: pub + }, function(ack){ + ref.off(); + }); +}, 99999999999999); + +/* +;(function(){ + $("#say").on('submit', function(){ + setTimeout(function(){ + $("#say").find('input').first().val(Gun.text.random(1000)); + },1); + }); +}); +*/ \ No newline at end of file