From eae893054a9e1ac3cd66a799ce01477a5401626e Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Thu, 9 Apr 2020 00:01:45 -0700 Subject: [PATCH] merge latest npm release into manhattan --- axe.js | 14 +- examples/docs.html | 103 ++- examples/game/furball.html | 4 +- gun.js | 1217 ++++++++---------------------------- lib/meta.js | 14 +- lib/radisk.js | 10 +- lib/radix.js | 14 +- lib/stats.js | 3 +- lib/store.js | 4 +- lib/yson.js | 2 +- sea.js | 2 +- 11 files changed, 404 insertions(+), 983 deletions(-) diff --git a/axe.js b/axe.js index 8b8348a7..d123920b 100644 --- a/axe.js +++ b/axe.js @@ -168,7 +168,7 @@ },1000 * 60) setTimeout(function(tmp){ if(!(tmp = at.stats && at.stats.stay)){ return } - Gun.obj.map((tmp.axe||{}).up, function(url){ mesh.hear.opt({opt: {peers: url}}) }) + ((tmp.axe||{}).up||[]).forEach(function(url){ mesh.hear.opt({opt: {peers: url}}) }) },1000); if(at.opt.super){ @@ -260,9 +260,9 @@ at.on('bye', function(peer){ this.to.next(peer); if(peer.url){ delete axe.up[peer.id] } var S = +new Date; - Gun.obj.map(peer.routes, function(route, hash){ + obj_map(peer.routes, function(route, hash){ delete route[peer.id]; - if(Gun.obj.empty(route)){ + if(Object.empty(route)){ delete axe.routes[hash]; } }); @@ -290,7 +290,7 @@ setTimeout(function(){ // must wait if(peer.pid !== opt.pid){ // this extra logic checks for duplicate connections between 2 peers. - if(!Gun.obj.map(axe.up, function(p){ + if(!obj_map(axe.up, function(p){ if(peer.pid === p.pid && peer !== p){ return yes = true; } @@ -305,6 +305,12 @@ peer.url = ''; } } + var obj_map = function(o, f, r){ + for(var k in o){ + if(!o.hasOwnProperty(k)){ continue } + if((r = f(o[k], k)) !== u){ return r } + } + } function joindht(dht, soul, pids) { if (!pids || !soul || !dht) { return; } diff --git a/examples/docs.html b/examples/docs.html index 1d23ae98..ea50c66c 100644 --- a/examples/docs.html +++ b/examples/docs.html @@ -22,10 +22,21 @@ [contenteditable]:focus { outline: none; } + + .meta-on, div:hover, ul:hover, ol:hover, li:hover, p:hover, span:hover, form:hover, button:hover, input:hover, textarea:hover, img:hover { + outline: 1px solid; + animation: meta-on 3s infinite; + transition: none !important; + } @keyframes meta-on { + 0% {outline-color: magenta;} + 33% {outline-color: cyan;} + 66% {outline-color: yellow;} + 100% {outline-color: magenta;} + }
-
+
@@ -37,7 +48,8 @@ diff --git a/examples/game/furball.html b/examples/game/furball.html index 88493168..6c88ced7 100644 --- a/examples/game/furball.html +++ b/examples/game/furball.html @@ -90,7 +90,7 @@
- +
@@ -114,7 +114,7 @@
- +
diff --git a/gun.js b/gun.js index fc414e86..cb52839f 100644 --- a/gun.js +++ b/gun.js @@ -14,26 +14,15 @@ /* UNBUILD */ ;USE(function(module){ - // Generic javascript utilities. - var Type = {}; - //Type.fns = Type.fn = {is: function(fn){ return (!!fn && fn instanceof Function) }} - Type.fn = {is: function(fn){ return (!!fn && 'function' == typeof fn) }} - Type.bi = {is: function(b){ return (b instanceof Boolean || typeof b == 'boolean') }} - Type.num = {is: function(n){ return !list_is(n) && ((n - parseFloat(n) + 1) >= 0 || Infinity === n || -Infinity === n) }} - Type.text = {is: function(t){ return (typeof t == 'string') }} - Type.text.ify = function(t){ - if(Type.text.is(t)){ return t } - if(typeof JSON !== "undefined"){ return JSON.stringify(t) } - return (t && t.toString)? t.toString() : t; - } - Type.text.random = function(l, c){ + // Shim for generic javascript utilities. + String.random = function(l, c){ var s = ''; l = l || 24; // you are not going to make a 0 length random number, so no need to check type c = c || '0123456789ABCDEFGHIJKLMNOPQRSTUVWXZabcdefghijklmnopqrstuvwxyz'; - while(l > 0){ s += c.charAt(Math.floor(Math.random() * c.length)); l-- } + while(l-- > 0){ s += c.charAt(Math.floor(Math.random() * c.length)) } return s; } - Type.text.match = function(t, o){ var tmp, u; + String.match = function(t, o){ var tmp, u; if('string' !== typeof t){ return false } if('string' == typeof o){ o = {'=': o} } o = o || {}; @@ -50,7 +39,7 @@ if(u !== o['<'] && t <= o['<']){ return true } return false; } - Type.text.hash = function(s, c){ // via SO + String.hash = function(s, c){ // via SO if(typeof s !== 'string'){ return } c = c || 0; if(!s.length){ return c } @@ -61,111 +50,40 @@ } return c; } - Type.list = {is: function(l){ return (l instanceof Array) }} - Type.list.slit = Array.prototype.slice; - Type.list.sort = function(k){ // creates a new sort function based off some key - return function(A,B){ - if(!A || !B){ return 0 } A = A[k]; B = B[k]; - if(A < B){ return -1 }else if(A > B){ return 1 } - else { return 0 } - } + var has = Object.prototype.hasOwnProperty; + Object.plain = function(o){ return o? (o instanceof Object && o.constructor === Object) || Object.prototype.toString.call(o).match(/^\[object (\w+)\]$/)[1] === 'Object' : false } + Object.empty = function(o, n){ + for(var k in o){ if(has.call(o, k) && k !== n){ return false } } + return true; } - Type.list.map = function(l, c, _){ return obj_map(l, c, _) } - Type.list.index = 1; // change this to 0 if you want non-logical, non-mathematical, non-matrix, non-convenient array notation - Type.obj = {is: function(o){ return o? (o instanceof Object && o.constructor === Object) || Object.prototype.toString.call(o).match(/^\[object (\w+)\]$/)[1] === 'Object' : false }} - Type.obj.put = function(o, k, v){ return (o||{})[k] = v, o } - Type.obj.has = function(o, k){ return o && Object.prototype.hasOwnProperty.call(o, k) } - Type.obj.del = function(o, k){ - if(!o){ return } - o[k] = null; - delete o[k]; - return o; + Object.keys = Object.keys || function(o){ + var l = []; + for(var k in o){ if(has.call(o, k)){ l.push(k) } } + return l; } - Type.obj.as = function(o, k, v, u){ return o[k] = o[k] || (u === v? {} : v) } - Type.obj.ify = function(o){ - if(obj_is(o)){ return o } - try{o = JSON.parse(o); - }catch(e){o={}}; - return o; - } - ;(function(){ var u; - function map(v,k){ - if(obj_has(this,k) && u !== this[k]){ return } - this[k] = v; - } - Type.obj.to = function(from, to){ - to = to || {}; - obj_map(from, map, to); - return to; + ;(function(){ // max ~1ms or before stack overflow + var u, l = 0, c = 0, sI = (typeof setImmediate !== ''+u && setImmediate) || setTimeout; + setTimeout.poll = function(f){ + if((1 >= (+new Date - l)) && c++ < 3333){ f(); return } + sI(function(){ l = +new Date; f() },c=0) } }()); - Type.obj.copy = function(o){ // because http://web.archive.org/web/20140328224025/http://jsperf.com/cloning-an-object/2 - return !o? o : JSON.parse(JSON.stringify(o)); // is shockingly faster than anything else, and our data has to be a subset of JSON anyways! - } - ;(function(){ - function empty(v,i){ var n = this.n, u; - if(n && (i === n || (obj_is(n) && obj_has(n, i)))){ return } - if(u !== i){ return true } - } - Type.obj.empty = function(o, n){ - if(!o){ return true } - return obj_map(o,empty,{n:n})? false : true; - } - }()); - ;(function(){ - function t(k,v){ - if(2 === arguments.length){ - t.r = t.r || {}; - t.r[k] = v; - return; - } t.r = t.r || []; - t.r.push(k); - }; - var keys = Object.keys, map, u; - Object.keys = Object.keys || function(o){ return map(o, function(v,k,t){t(k)}) } - Type.obj.map = map = function(l, c, _){ - var u, i = 0, x, r, ll, lle, f = 'function' == typeof c; - t.r = u; - if(keys && obj_is(l)){ - ll = keys(l); lle = true; + ;(function(){ // Too many polls block, this "threads" them in turns over a single thread in time. + var p = setTimeout.poll, s = [], i = 0, fn; + setTimeout.turn = function(f){ 1 == s.push(f) && p(turn) }; + function turn(){ + if(fn = s[i++]){ fn() } + if(i == s.length || 99 == i){ + s = s.slice(i); + i = 0; } - _ = _ || {}; - if(list_is(l) || ll){ - x = (ll || l).length; - for(;i < x; i++){ - var ii = (i + Type.list.index); - if(f){ - r = lle? c.call(_, l[ll[i]], ll[i], t) : c.call(_, l[i], ii, t); - if(r !== u){ return r } - } else { - //if(Type.test.is(c,l[i])){ return ii } // should implement deep equality testing! - if(c === l[lle? ll[i] : i]){ return ll? ll[i] : ii } // use this for now - } - } - } else { - for(i in l){ - if(f){ - if(obj_has(l,i)){ - r = _? c.call(_, l[i], i, t) : c(l[i], i, t); - if(r !== u){ return r } - } - } else { - //if(a.test.is(c,l[i])){ return i } // should implement deep equality testing! - if(c === l[i]){ return i } // use this for now - } - } - } - return f? t.r : Type.list.index? 0 : -1; + if(s.length){ p(turn) } } }()); - Type.time = {}; - Type.time.is = function(t){ return t? t instanceof Date : (+new Date().getTime()) } - - var fn_is = Type.fn.is; - var list_is = Type.list.is; - var obj = Type.obj, obj_is = obj.is, obj_has = obj.has, obj_map = obj.map; - module.exports = Type; - })(USE, './type'); + // JSON should not block CPU, this should be a standard. But browsers do not have it, so we are stuck polyfilling with the blocking version. + //JSON.parseAsync = JSON.parseAsync || function(t,cb,r){ var u; try{ cb(u, JSON.parse(t,r)) }catch(e){ cb(e) } } + //JSON.stringifyAsync = JSON.stringifyAsync || function(v,cb,r,s){ var u; try{ cb(u, JSON.stringify(v,r,s)) }catch(e){ cb(e) } } + })(USE, './shim'); ;USE(function(module){ // On event emitter generic javascript utility. @@ -208,205 +126,45 @@ })(USE, './onto'); ;USE(function(module){ - var to = (typeof setImmediate !== "undefined")? setImmediate : setTimeout, puff = function(cb){ - if(Q.length){ Q.push(cb); return } Q = [cb]; - to(function go(S){ S = S || +new Date; - var i = 0, cb; while(i < 9 && (cb = Q[i++])){ cb() } - console.STAT && console.STAT(S, +new Date - S, 'puff'); - if(cb && !(+new Date - S)){ return go(S) } - if(!(Q = Q.slice(i)).length){ return } - to(go, 0); - }, 0); - }, Q = []; - module.exports = setTimeout.puff = puff; - })(USE, './puff'); - - ;USE(function(module){ - /* Based on the Hypothetical Amnesia Machine thought experiment */ - function HAM(machineState, incomingState, currentState, incomingValue, currentValue){ - if(machineState < incomingState){ - return {defer: true}; // the incoming value is outside the boundary of the machine's state, it must be reprocessed in another state. - } - if(incomingState < currentState){ - return {historical: true}; // the incoming value is within the boundary of the machine's state, but not within the range. - - } - if(currentState < incomingState){ - return {converge: true, incoming: true}; // the incoming value is within both the boundary and the range of the machine's state. - - } - if(incomingState === currentState){ - incomingValue = Lexical(incomingValue) || ""; - currentValue = Lexical(currentValue) || ""; - if(incomingValue === currentValue){ // Note: while these are practically the same, the deltas could be technically different - return {state: true}; - } - /* - The following is a naive implementation, but will always work. - Never change it unless you have specific needs that absolutely require it. - If changed, your data will diverge unless you guarantee every peer's algorithm has also been changed to be the same. - As a result, it is highly discouraged to modify despite the fact that it is naive, - because convergence (data integrity) is generally more important. - Any difference in this algorithm must be given a new and different name. - */ - if(incomingValue < currentValue){ // Lexical only works on simple value types! - return {converge: true, current: true}; - } - if(currentValue < incomingValue){ // Lexical only works on simple value types! - return {converge: true, incoming: true}; - } - } - return {err: "Invalid CRDT Data: "+ incomingValue +" to "+ currentValue +" at "+ incomingState +" to "+ currentState +"!"}; - } - if(typeof JSON === 'undefined'){ - throw new Error( - 'JSON is not included in this browser. Please load it first: ' + - 'ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js' - ); - } - var Lexical = JSON.stringify, undefined; - module.exports = HAM; - })(USE, './HAM'); - - ;USE(function(module){ - var Type = USE('./type'); - var Val = {}; - Val.is = function(v){ // Valid values are a subset of JSON: null, binary, number (!Infinity), text, or a soul relation. Arrays need special algorithms to handle concurrency, so they are not supported directly. Use an extension that supports them if needed but research their problems first. - if(v === u){ return false } + USE('./shim'); + module.exports = function(v){ // Valid values are a subset of JSON: null, binary, number (!Infinity), text, or a soul relation. Arrays need special algorithms to handle concurrency, so they are not supported directly. Use an extension that supports them if needed but research their problems first. + if(v === undefined){ return false } if(v === null){ return true } // "deletes", nulling out keys. if(v === Infinity){ return false } // we want this to be, but JSON does not support it, sad face. - if(text_is(v) // by "text" we mean strings. - || bi_is(v) // by "binary" we mean boolean. - || num_is(v)){ // by "number" we mean integers or decimals. + if('string' == typeof v + || 'boolean' == typeof v // by "binary" we mean boolean. + || 'number' == typeof v){ // by "number" we mean integers or decimals. return true; // simple values are valid. } - return Val.link.is(v) || false; // is the value a soul relation? Then it is valid and return it. If not, everything else remaining is an invalid data type. Custom extensions can be built on top of these primitives to support other types. + if(v && ('string' == typeof (v['#']||0)) && Object.empty(v, '#')){ return v['#'] } // is link + return false; // If not, everything else remaining is an invalid data type. Custom extensions can be built on top of these primitives to support other types. } - Val.link = Val.rel = {_: '#'}; - ;(function(){ - Val.link.is = function(v){ // this defines whether an object is a soul relation or not, they look like this: {'#': 'UUID'} - if(v && v[rel_] && !v._ && obj_is(v)){ // must be an object. - var o = {}; - obj_map(v, map, o); - if(o.id){ // a valid id was found. - return o.id; // yay! Return it. - } - } - return false; // the value was not a valid soul relation. - } - function map(s, k){ var o = this; // map over the object... - if(o.id){ return o.id = false } // if ID is already defined AND we're still looping through the object, it is considered invalid. - if(k == rel_ && text_is(s)){ // the key should be '#' and have a text value. - o.id = s; // we found the soul! - } else { - return o.id = false; // if there exists anything else on the object that isn't the soul, then it is considered invalid. - } - } - }()); - Val.link.ify = function(t){ return obj_put({}, rel_, t) } // convert a soul into a relation and return it. - Type.obj.has._ = '.'; - var rel_ = Val.link._, u; - var bi_is = Type.bi.is; - var num_is = Type.num.is; - var text_is = Type.text.is; - var obj = Type.obj, obj_is = obj.is, obj_put = obj.put, obj_map = obj.map; - module.exports = Val; - })(USE, './val'); + })(USE, './valid'); ;USE(function(module){ - var Type = USE('./type'); - var Val = USE('./val'); - var Node = {_: '_'}; - Node.soul = function(n, o){ return (n && n._ && n._[o || soul_]) } // convenience function to check to see if there is a soul on a node and return it. - Node.soul.ify = function(n, o){ // put a soul on an object. - o = (typeof o === 'string')? {soul: o} : o || {}; - n = n || {}; // make sure it exists. - n._ = n._ || {}; // make sure meta exists. - n._[soul_] = o.soul || n._[soul_] || text_random(); // put the soul on it. - return n; - } - Node.soul._ = Val.link._; - ;(function(){ - Node.is = function(n, cb, as){ var s; // checks to see if an object is a valid node. - if(!obj_is(n)){ return false } // must be an object. - if(s = Node.soul(n)){ // must have a soul on it. - return !obj_map(n, map, {as:as,cb:cb,s:s,n:n}); - } - return false; // nope! This was not a valid node. - } - function map(v, k){ // we invert this because the way we check for this is via a negation. - if(k === Node._){ return } // skip over the metadata. - if(!Val.is(v)){ return true } // it is true that this is an invalid node. - if(this.cb){ this.cb.call(this.as, v, k, this.n, this.s) } // optionally callback each key/value. - } - }()); - ;(function(){ - Node.ify = function(obj, o, as){ // returns a node from a shallow object. - if(!o){ o = {} } - else if(typeof o === 'string'){ o = {soul: o} } - else if('function' == typeof o){ o = {map: o} } - if(o.map){ o.node = o.map.call(as, obj, u, o.node || {}) } - if(o.node = Node.soul.ify(o.node || {}, o)){ - obj_map(obj, map, {o:o,as:as}); - } - return o.node; // This will only be a valid node if the object wasn't already deep! - } - function map(v, k){ var o = this.o, tmp, u; // iterate over each key/value. - if(o.map){ - tmp = o.map.call(this.as, v, ''+k, o.node); - if(u === tmp){ - obj_del(o.node, k); - } else - if(o.node){ o.node[k] = tmp } - return; - } - if(Val.is(v)){ - o.node[k] = v; - } - } - }()); - var obj = Type.obj, obj_is = obj.is, obj_del = obj.del, obj_map = obj.map; - var text = Type.text, text_random = text.random; - var soul_ = Node.soul._; - var u; - module.exports = Node; - })(USE, './node'); - - ;USE(function(module){ - var Type = USE('./type'); - var Node = USE('./node'); + USE('./shim'); function State(){ - var t; - /*if(perf){ - t = start + perf.now(); // Danger: Accuracy decays significantly over time, even if precise. - } else {*/ - t = +new Date; - //} + var t = +new Date; if(last < t){ return N = 0, last = t + State.drift; } return last = t + ((N += 1) / D) + State.drift; } - var time = Type.time.is, last = -Infinity, N = 0, D = 1000; // WARNING! In the future, on machines that are D times faster than 2016AD machines, you will want to increase D by another several orders of magnitude so the processing speed never out paces the decimal resolution (increasing an integer effects the state accuracy). - var perf = (typeof performance !== 'undefined')? (performance.timing && performance) : false, start = (perf && perf.timing && perf.timing.navigationStart) || (perf = false); - var S_ = State._ = '>'; State.drift = 0; + var NI = -Infinity, N = 0, D = 999, last = NI, u; // WARNING! In the future, on machines that are D times faster than 2016AD machines, you will want to increase D by another several orders of magnitude so the processing speed never out paces the decimal resolution (increasing an integer effects the state accuracy). State.is = function(n, k, o){ // convenience function to get the state on a key on a node and return it. - var tmp = (k && n && n[N_] && n[N_][S_]) || o; + var tmp = (k && n && n['_'] && n['_']['>']) || o; if(!tmp){ return } - return num_is(tmp = tmp[k])? tmp : -Infinity; + return ('number' == typeof (tmp = tmp[k]))? tmp : NI; } - State.lex = function(){ return State().toString(36).replace('.','') } State.ify = function(n, k, s, v, soul){ // put a key's state on a node. - if(!n || !n[N_]){ // reject if it is not node-like. - if(!soul){ // unless they passed a soul - return; - } - n = Node.soul.ify(n, soul); // then make it so! + if(!n || !n['_']){ // reject if it is not node-like. + if(!soul){ return } // unless they passed a soul + ((n = n || {})._ = n._ || {})['#'] = soul || n._['#']; // then make it so! } - var tmp = obj_as(n[N_], S_); // grab the states data. - if(u !== k && k !== N_){ - if(num_is(s)){ + var tmp = n['_']['>'] || (n['_']['>'] = {}); // grab the states data. + if(u !== k && k !== '_'){ + if('number' == typeof s){ tmp[k] = s; // add the valid state. } if(u !== v){ // Note: Not its job to check for valid values! @@ -415,206 +173,37 @@ } return n; } - State.to = function(from, k, to){ - var val = (from||{})[k]; - if(obj_is(val)){ - val = obj_copy(val); - } - return State.ify(to, k, State.is(from, k), val, Node.soul(from)); - } - ;(function(){ - State.map = function(cb, s, as){ var u; // for use with Node.ify - var o = obj_is(o = cb || s)? o : null; - cb = fn_is(cb = cb || s)? cb : null; - if(o && !cb){ - s = num_is(s)? s : State(); - o[N_] = o[N_] || {}; - obj_map(o, map, {o:o,s:s}); - return o; - } - as = as || obj_is(s)? s : u; - s = num_is(s)? s : State(); - return function(v, k, o, opt){ - if(!cb){ - map.call({o: o, s: s}, v,k); - return v; - } - cb.call(as || this || {}, v, k, o, opt); - if(obj_has(o,k) && u === o[k]){ return } - map.call({o: o, s: s}, v,k); - } - } - function map(v,k){ - if(N_ === k){ return } - State.ify(this.o, k, this.s) ; - } - }()); - var obj = Type.obj, obj_as = obj.as, obj_has = obj.has, obj_is = obj.is, obj_map = obj.map, obj_copy = obj.copy; - var num = Type.num, num_is = num.is; - var fn = Type.fn, fn_is = fn.is; - var N_ = Node._, u; module.exports = State; })(USE, './state'); ;USE(function(module){ - var Type = USE('./type'); - var Val = USE('./val'); - var Node = USE('./node'); - var Graph = {}; - ;(function(){ - Graph.is = function(g, cb, fn, as){ // checks to see if an object is a valid graph. - if(!g || !obj_is(g) || obj_empty(g)){ return false } // must be an object. - return !obj_map(g, map, {cb:cb,fn:fn,as:as}); // makes sure it wasn't an empty object. + USE('./shim'); + function Dup(opt){ + var dup = {s:{}}, s = dup.s; + opt = opt || {max: 999, age: /*1000 * 9};//*/ 1000 * 9 * 3}; + dup.check = function(id){ + if(!s[id]){ return false } + return dt(id); } - function map(n, s){ // we invert this because the way'? we check for this is via a negation. - if(!n || s !== Node.soul(n) || !Node.is(n, this.fn, this.as)){ return true } // it is true that this is an invalid graph. - if(!this.cb){ return } - nf.n = n; nf.as = this.as; // sequential race conditions aren't races. - this.cb.call(nf.as, n, s, nf); + var dt = dup.track = function(id){ + var it = s[id] || (s[id] = {}); + it.was = +new Date; + if(!dup.to){ dup.to = setTimeout(dup.drop, opt.age + 9) } + return it; } - function nf(fn){ // optional callback for each node. - if(fn){ Node.is(nf.n, fn, nf.as) } // where we then have an optional callback for each key/value. + dup.drop = function(age){ + var now = +new Date; + Object.keys(s).forEach(function(id){ var it = s[id]; + if(it && (age || opt.age) > (now - it.was)){ return } + delete s[id]; + }); + dup.to = null; + console.STAT && (age = +new Date - now) > 9 && console.STAT(now, age, 'dup drop'); } - }()); - ;(function(){ - Graph.ify = function(obj, env, as){ - var at = {path: [], obj: obj}; - if(!env){ - env = {}; - } else - if(typeof env === 'string'){ - env = {soul: env}; - } else - if('function' == typeof env){ - env.map = env; - } - if(typeof as === 'string'){ - env.soul = env.soul || as; - as = u; - } - if(env.soul){ - at.link = Val.link.ify(env.soul); - } - env.shell = (as||{}).shell; - env.graph = env.graph || {}; - env.seen = env.seen || []; - env.as = env.as || as; - node(env, at); - env.root = at.node; - return env.graph; - } - function node(env, at){ var tmp; - if(tmp = seen(env, at)){ return tmp } - at.env = env; - at.soul = soul; - if(Node.ify(at.obj, map, at)){ - at.link = at.link || Val.link.ify(Node.soul(at.node)); - if(at.obj !== env.shell){ - env.graph[Val.link.is(at.link)] = at.node; - } - } - return at; - } - function map(v,k,n){ - var at = this, env = at.env, is, tmp; - if(Node._ === k && obj_has(v,Val.link._)){ - return n._; // TODO: Bug? - } - if(!(is = valid(v,k,n, at,env))){ return } - if(!k){ - at.node = at.node || n || {}; - if(obj_has(v, Node._) && Node.soul(v)){ // ? for safety ? - at.node._ = obj_copy(v._); - } - at.node = Node.soul.ify(at.node, Val.link.is(at.link)); - at.link = at.link || Val.link.ify(Node.soul(at.node)); - } - if(tmp = env.map){ - tmp.call(env.as || {}, v,k,n, at); - if(obj_has(n,k)){ - v = n[k]; - if(u === v){ - obj_del(n, k); - return; - } - if(!(is = valid(v,k,n, at,env))){ return } - } - } - if(!k){ return at.node } - if(true === is){ - return v; - } - tmp = node(env, {obj: v, path: at.path.concat(k)}); - if(!tmp.node){ return } - return tmp.link; //{'#': Node.soul(tmp.node)}; - } - function soul(id){ var at = this; - var prev = Val.link.is(at.link), graph = at.env.graph; - at.link = at.link || Val.link.ify(id); - at.link[Val.link._] = id; - if(at.node && at.node[Node._]){ - at.node[Node._][Val.link._] = id; - } - if(obj_has(graph, prev)){ - graph[id] = graph[prev]; - obj_del(graph, prev); - } - } - function valid(v,k,n, at,env){ var tmp; - if(Val.is(v)){ return true } - if(obj_is(v)){ return 1 } - if(tmp = env.invalid){ - v = tmp.call(env.as || {}, v,k,n); - return valid(v,k,n, at,env); - } - env.err = "Invalid value at '" + at.path.concat(k).join('.') + "'!"; - if(Type.list.is(v)){ env.err += " Use `.set(item)` instead of an Array." } - } - function seen(env, at){ - var arr = env.seen, i = arr.length, has; - while(i--){ has = arr[i]; - if(at.obj === has.obj){ return has } - } - arr.push(at); - } - }()); - Graph.node = function(node){ - var soul = Node.soul(node); - if(!soul){ return } - return obj_put({}, soul, node); + return dup; } - ;(function(){ - Graph.to = function(graph, root, opt){ - if(!graph){ return } - var obj = {}; - opt = opt || {seen: {}}; - obj_map(graph[root], map, {obj:obj, graph: graph, opt: opt}); - return obj; - } - function map(v,k){ var tmp, obj; - if(Node._ === k){ - if(obj_empty(v, Val.link._)){ - return; - } - this.obj[k] = obj_copy(v); - return; - } - if(!(tmp = Val.link.is(v))){ - this.obj[k] = v; - return; - } - if(obj = this.opt.seen[tmp]){ - this.obj[k] = obj; - return; - } - this.obj[k] = this.opt.seen[tmp] = Graph.to(this.graph, tmp, this.opt); - } - }()); - var fn_is = Type.fn.is; - var obj = Type.obj, obj_is = obj.is, obj_del = obj.del, obj_has = obj.has, obj_empty = obj.empty, obj_put = obj.put, obj_map = obj.map, obj_copy = obj.copy; - var u; - module.exports = Graph; - })(USE, './graph'); + module.exports = Dup; + })(USE, './dup'); ;USE(function(module){ // request / response module, for asking and acking messages. @@ -640,35 +229,6 @@ } })(USE, './ask'); - ;USE(function(module){ - var Type = USE('./type'); - function Dup(opt){ - var dup = {s:{}}, s = dup.s; - opt = opt || {max: 1000, age: /*1000 * 9};//*/ 1000 * 9 * 3}; - dup.check = function(id){ - if(!s[id]){ return false } - return dt(id); - } - var dt = dup.track = function(id){ - var it = s[id] || (s[id] = {}); - it.was = +new Date; - if(!dup.to){ dup.to = setTimeout(dup.drop, opt.age + 9) } - return it; - } - dup.drop = function(age){ - var now = +new Date; - Type.obj.map(s, function(it, id){ - if(it && (age || opt.age) > (now - it.was)){ return } - delete s[id]; - }); - dup.to = null; - console.STAT && (age = +new Date - now) > 9 && console.STAT(now, age, 'dup drop'); - } - return dup; - } - module.exports = Dup; - })(USE, './dup'); - ;USE(function(module){ function Gun(o){ @@ -684,17 +244,12 @@ Gun.chain = Gun.prototype; Gun.chain.toJSON = function(){}; - var Type = USE('./type'); - Type.obj.to(Type, Gun); - Gun.HAM = USE('./HAM'); - Gun.val = USE('./val'); - Gun.node = USE('./node'); + USE('./shim'); + Gun.valid = USE('./valid'); Gun.state = USE('./state'); - Gun.graph = USE('./graph'); Gun.on = USE('./onto'); - Gun.ask = USE('./ask'); Gun.dup = USE('./dup'); - Gun.puff = USE('./puff'); + Gun.ask = USE('./ask'); ;(function(){ Gun.create = function(at){ @@ -722,7 +277,6 @@ if(dup.check(tmp)){ return } dup.track(tmp); tmp = msg._; msg._ = ('function' == typeof tmp)? tmp : function(){}; (msg.$ && (msg.$ === (msg.$._||'').$)) || (msg.$ = gun); - if(msg.get && obj_empty(msg.get)){ return } // who sending these? Need better way to stop. if(!at.ask(msg['@'], msg)){ // is this machine listening for an ack? DBG && (DBG.u = +new Date); if(msg.get){ Gun.on._get(msg, gun) } @@ -737,59 +291,75 @@ function put(msg){ if(!msg){ return } var ctx = msg._||'', root = ctx.root = ((msg.$||'')._||'').root; - var put = msg.put, id = msg['#'], err, tmp; + if(msg['@'] && !ctx.me){ // TODO: WARNING! This will prevent relays from caching! + msg.out = universe; + root.on('out', msg); + return; + } + var put = msg.put, id = msg['#']; var DBG = ctx.DBG = msg.DBG; if(put['#'] && put['.']){ root.on('put', msg); return } - /*root.on(id, function(m){ - console.log('ack:', m); - });*/ - ctx.out = msg; - ctx.lot = {s: 0, more: 1}; - var S = +new Date; DBG && (DBG.p = S); - for(var soul in put){ // Gun.obj.native() makes this safe. - if((soul||'').indexOf('hackernoon!stats') === 0){ err = 'not right now'; break; } - var node = put[soul], states; - if(!node){ err = ERR+cut(soul)+"no node."; break } - if(!(tmp = node._)){ err = ERR+cut(soul)+"no meta."; break } - if(soul !== tmp[_soul]){ err = ERR+cut(soul)+"soul not same."; break } - if(!(states = tmp[state_])){ err = ERR+cut(soul)+"no state."; break } - for(var key in node){ // double loop uncool, but have to support old format. - if(node_ === key){ continue } + ctx.msg = msg; + ctx.stun = 1; + var nl = Object.keys(put), ni = 0, nj, kl, soul, node, states, err, tmp; + (function pop(o){ + if(nj != ni){ nj = ni; + if(!(soul = nl[ni])){ + ctx.stun--; + console.log('done!', ctx.stun); + fire(ctx); + return; + } + if(!(node = put[soul])){ err = ERR+cut(soul)+"no node." } else + if(!(tmp = node._)){ err = ERR+cut(soul)+"no meta." } else + if(soul !== tmp['#']){ err = ERR+cut(soul)+"soul not same." } else + if(!(states = tmp['>'])){ err = ERR+cut(soul)+"no state." } + kl = Object.keys(node||{}); + } + if(err){ + console.log("handle error!") // handle! + return; + } + var i = 0, key; o = o || 0; + while(o++ < 9 && (key = kl[i++])){ + if('_' === key){ continue } var val = node[key], state = states[key]; if(u === state){ err = ERR+cut(key)+"on"+cut(soul)+"no state."; break } - if(!val_is(val)){ err = ERR+cut(key)+"on"+cut(soul)+"bad "+(typeof val)+cut(val); break } + if(!valid(val)){ err = ERR+cut(key)+"on"+cut(soul)+"bad "+(typeof val)+cut(val); break } ham(val, key, soul, state, msg); } - if(err){ break } - } - //console.log("mix:", +new Date - S); - DBG && (DBG.pe = +new Date); + if((kl = kl.slice(i)).length){ turn(pop); return } + ++ni; kl = null; pop(o); + }()); + + /*DBG && (DBG.pe = +new Date); if(console.STAT){ console.STAT(S, +new Date - S, 'mix');console.STAT(S, ctx.lot.s, 'mix #') } if(ctx.err = err){ root.on('in', {'@': id, err: Gun.log(err)}); return } if(!(--ctx.lot.more)){ fire(ctx) } // if synchronous. - if(!ctx.stun && !msg['@']){ root.on('in', {'@': id, ok: -1}) } // in case no diff sent to storage, etc., still ack. + if(!ctx.stun && !msg['@']){ root.on('in', {'@': id, ok: -1}) } // in case no diff sent to storage, etc., still ack.*/ } Gun.on.put = put; function ham(val, key, soul, state, msg){ var ctx = msg._||'', root = ctx.root, graph = root.graph, lot; var vertex = graph[soul] || empty, was = state_is(vertex, key, 1), known = vertex[key]; - var machine = State(), is = HAM(machine, state, was, val, known), u; - if(!is.incoming){ + var now = State(),u; + if(state > now){ setTo; return } + if(state < was){ old; return } + if(state === was && (val === known || L(val) <= L(known))){ same; return } + /*if(!is.incoming){ if(is.defer){ var to = state - machine; - if(to > (1000 * 3)){ // allow 0.5s tolerance for now. - setTimeout(function(){ - ham(val, key, soul, state, msg); - }, to > MD? MD : to); // setTimeout Max Defer 32bit :( - if(!ctx.to){ root.on('in', {'@': msg['#'], err: to}) } ctx.to = 1; - return to; - } - } else { - return; + setTimeout(function(){ + ham(val, key, soul, state, msg); + }, to > MD? MD : to); // setTimeout Max Defer 32bit :( + if(!ctx.to){ root.on('in', {'@': msg['#'], err: to}) } ctx.to = 1; + return to; } - } - (lot = ctx.lot||'').s++; lot.more++; - (ctx.stun || (ctx.stun = {}))[soul+key] = 1; + return; + }*/ + //(lot = ctx.lot||'').s++; lot.more++; + //(ctx.stun || (ctx.stun = {}))[soul+key] = 1; + ctx.stun++; var DBG = ctx.DBG; DBG && (DBG.ph = DBG.ph || +new Date); root.on('put', {'#': msg['#'], '@': msg['@'], put: {'#': soul, '.': key, ':': val, '>': state}, _: ctx}); } @@ -801,7 +371,8 @@ if((tmp = ctx.out) && (tmp = tmp.put)){ tmp[soul] = state_ify(tmp[soul], key, state, val, soul); // TODO: Hacky, fix & come back later, for actual pushing messages. } - if(!(--ctx.lot.more)){ fire(ctx) } + //if(!(--ctx.lot.more)){ fire(ctx) } + if(!(--ctx.stun)){ fire(ctx) } eve.to.next(msg); } function chain(ctx, soul, key,val, state){ @@ -823,108 +394,29 @@ tmp.on('in', {$: tmp.$, get: soul, put: node}); root.stop = null; // temporary fix till a better solution? } + //console.log("fire:", +new Date - S); console.STAT && console.STAT(S, +new Date - S, 'fire'); ctx.DBG && (ctx.DBG.f = +new Date); - if(!(tmp = ctx.out)){ return } + if(!(tmp = ctx.msg)){ return } tmp.out = universe; + //console.log("PUT:", +new Date - ctx.START, ctx.lot); ctx.START = null; + console.log('out', tmp); root.on('out', tmp); } var ERR = "Error: Invalid graph!"; var cut = function(s){ return " '"+(''+s).slice(0,9)+"...' " } - var HAM = Gun.HAM, MD = 2147483647, State = Gun.state; + var L = JSON.stringify, MD = 2147483647, State = Gun.state; }()); ;(function(){ - Gun.on._put = function(msg, gun){ - var at = gun._, ctx = {$: gun, graph: at.graph, put: {}, map: {}, souls: {}, machine: Gun.state(), ack: msg['@'], cat: at, stop: {}}; - if(!Gun.obj.map(msg.put, perf, ctx)){ return } // HNPERF: performance test, not core code, do not port. - if(!Gun.graph.is(msg.put, null, verify, ctx)){ ctx.err = "Error: Invalid graph!" } - if(ctx.err){ return at.on('in', {'@': msg['#'], err: Gun.log(ctx.err) }) } - obj_map(ctx.put, merge, ctx); - if(!ctx.async){ obj_map(ctx.map, map, ctx) } - if(u !== ctx.defer){ - var to = ctx.defer - ctx.machine; - setTimeout(function(){ - Gun.on._put(msg, gun); - }, to > MD? MD : to ); // setTimeout Max Defer 32bit :( - } - if(!ctx.diff){ return } - at.on('put', obj_to(msg, {put: ctx.diff})); - }; - function verify(val, key, node, soul){ var ctx = this; - var state = Gun.state.is(node, key), tmp; - if(!state){ return ctx.err = "Error: No state on '"+key+"' in node '"+soul+"'!" } - var vertex = ctx.graph[soul] || empty, was = Gun.state.is(vertex, key, true), known = vertex[key]; - var HAM = Gun.HAM(ctx.machine, state, was, val, known); - if(!HAM.incoming){ - if(HAM.defer){ // pick the lowest - ctx.defer = (state < (ctx.defer || Infinity))? state : ctx.defer; - } - return; - } - ctx.put[soul] = Gun.state.to(node, key, ctx.put[soul]); - (ctx.diff || (ctx.diff = {}))[soul] = Gun.state.to(node, key, ctx.diff[soul]); - ctx.souls[soul] = true; - } - function merge(node, soul){ - var ctx = this, cat = ctx.$._, at = (cat.next || empty)[soul]; - if(!at){ - if(!(cat.opt||empty).super){ - ctx.souls[soul] = false; - return; - } - at = (ctx.$.get(soul)._); - } - var msg = ctx.map[soul] = { - put: node, - get: soul, - $: at.$ - }, as = {ctx: ctx, msg: msg}; - ctx.async = !!cat.tag.node; - if(ctx.ack){ msg['@'] = ctx.ack } - obj_map(node, each, as); - if(!ctx.async){ return } - if(!ctx.and){ - // If it is async, we only need to setup one listener per context (ctx) - cat.on('node', function(m){ - this.to.next(m); // make sure to call other context's listeners. - if(m !== ctx.map[m.get]){ return } // filter out events not from this context! - ctx.souls[m.get] = false; // set our many-async flag - obj_map(m.put, patch, m); // merge into view - if(obj_map(ctx.souls, function(v){ if(v){ return v } })){ return } // if flag still outstanding, keep waiting. - if(ctx.c){ return } ctx.c = 1; // failsafe for only being called once per context. - this.off(); - obj_map(ctx.map, map, ctx); // all done, trigger chains. - }); - } - ctx.and = true; - cat.on('node', msg); // each node on the current context's graph needs to be emitted though. - } - function each(val, key){ - var ctx = this.ctx, graph = ctx.graph, msg = this.msg, soul = msg.get, node = msg.put, at = (msg.$._), tmp; - graph[soul] = Gun.state.to(node, key, graph[soul]); - if(ctx.async){ return } - at.put = Gun.state.to(node, key, at.put); - } - function patch(val, key){ - var msg = this, node = msg.put, at = (msg.$._); - at.put = Gun.state.to(node, key, at.put); - } - function map(msg, soul){ - if(!msg.$){ return } - this.cat.stop = this.stop; // temporary fix till a better solution? - (msg.$._).on('in', msg); - this.cat.stop = null; // temporary fix till a better solution? - } - function perf(node, soul){ if(node !== this.graph[soul]){ return true } } // HNPERF: do not port! - Gun.on._get = function(msg, gun){ - var root = gun._, get = msg.get, soul = get[_soul], node = root.graph[soul], has = get[_has], tmp; + var root = gun._, get = msg.get, soul = get['#'], node = root.graph[soul], has = get['.'], tmp; var next = root.next || (root.next = {}), at = next[soul]; // queue concurrent GETs? var ctx = msg._||'', DBG = ctx.DBG = msg.DBG; DBG && (DBG.g = +new Date); - if((soul||'').indexOf('hackernoon!stats') === 0){ root.on('in', {'@': msg['#'], err: 'not right now'}); return; } + //console.log("GET", get); + console.only(1, 'GET', node); if(!node){ return root.on('get', msg) } if(has){ if('string' != typeof has || !obj_has(node, has)){ return root.on('get', msg) } @@ -933,12 +425,30 @@ // Maybe... in case the in-memory key we have is a local write // we still need to trigger a pull/merge from peers. } else { - node = Gun.window? Gun.obj.copy(node) : node; // HNPERF: If !browser bump Performance? Is this too dangerous to reference root graph? Copy / shallow copy too expensive for big nodes. Gun.obj.to(node); // 1 layer deep copy // Gun.obj.copy(node); // too slow on big nodes + //node = node;//Gun.window? Gun.obj.copy(node) : node; // HNPERF: If !browser bump Performance? Is this too dangerous to reference root graph? Copy / shallow copy too expensive for big nodes. Gun.obj.to(node); // 1 layer deep copy // Gun.obj.copy(node); // too slow on big nodes + var ack = msg['#'], id = text_rand(9), keys = Object.keys(node||{}), soul = node._['#']; + (function got(){ + var i = 0, k, put = {}; + while(i < 9 && (k = keys[i++])){ + state_ify(put, k, state_is(node, k), node[k], soul); + } + keys = keys.slice(i); + (tmp = {})[soul] = put; put = tmp; + var faith = function(){}; faith.ram = faith.faith = true; // HNPERF: We're testing performance improvement by skipping going through security again, but this should be audited. + tmp = keys.length; + root.on('in', {'@': ack, '#': id, put: put, '%': (tmp? (id = text_rand(9)) : u), ram: 1, $: gun, _: faith}); + if(!tmp){ return } + setTimeout.turn(got); + }()); + //root.on('get', msg); + return; } - node = Gun.graph.node(node); + //console.log("GOT", Object.keys(node).length); + (tmp = {})[node._['#']] = node; node = tmp; tmp = (at||empty).ack; var faith = function(){}; faith.ram = faith.faith = true; // HNPERF: We're testing performance improvement by skipping going through security again, but this should be audited. DBG && (DBG.ga = +new Date); + console.only(2, 'GOT:', msg['#'], node); root.on('in', { '@': msg['#'], put: node, @@ -957,36 +467,28 @@ Gun.chain.opt = function(opt){ opt = opt || {}; var gun = this, at = gun._, tmp = opt.peers || opt; - if(!obj_is(opt)){ opt = {} } - if(!obj_is(at.opt)){ at.opt = opt } - if(text_is(tmp)){ tmp = [tmp] } - if(list_is(tmp)){ - tmp = obj_map(tmp, function(url, i, map){ - i = {}; i.id = i.url = url; map(url, i); - }); - if(!obj_is(at.opt.peers)){ at.opt.peers = {}} - at.opt.peers = obj_to(tmp, at.opt.peers); + if(!Object.plain(opt)){ opt = {} } + if(!Object.plain(at.opt)){ at.opt = opt } + if('string' == typeof tmp){ tmp = [tmp] } + if(tmp instanceof Array){ + if(!Object.plain(at.opt.peers)){ at.opt.peers = {}} + tmp.forEach(function(url){ + var p = {}; p.id = p.url = url; + at.opt.peers[url] = at.opt.peers[url] || p; + }) } at.opt.peers = at.opt.peers || {}; - obj_map(opt, function each(v,k){ - if(!obj_has(this, k) || text.is(v) || obj.empty(v)){ this[k] = v ; return } - if(v && v.constructor !== Object && !list_is(v)){ return } - obj_map(v, each, this[k]); - }, at.opt); + obj_each(opt, function each(k){ var v = this[k]; + if((this && this.hasOwnProperty(k)) || 'string' == typeof v || Object.empty(v)){ this[k] = v; return } + if(v && v.constructor !== Object && !(v instanceof Array)){ return } + obj_each(v, each); + }); Gun.on('opt', at); - //at.opt.uuid = at.opt.uuid || function(){ return state_lex() + text_rand(12) } - Gun.obj.native(); return gun; } }()); - Gun.obj.native = function(){ var p = Object.prototype; for(var i in p){ console.log("Native Object.prototype polluted, reverting", i); delete p[i]; } }; - var list_is = Gun.list.is; - var text = Gun.text, text_is = text.is, text_rand = text.random; - var obj = Gun.obj, obj_empty = obj.empty, obj_is = obj.is, obj_has = obj.has, obj_to = obj.to, obj_map = obj.map, obj_copy = obj.copy; - var state_lex = Gun.state.lex, state_ify = Gun.state.ify, state_is = Gun.state.is, _soul = Gun.val.link._, _has = '.', node_ = Gun.node._, val_is = Gun.val.is, rel_is = Gun.val.link.is, state_ = Gun.state._; - var empty = {}, u; - var C; + var obj_each = function(o,f){ Object.keys(o).forEach(f,o) }, text_rand = String.random, turn = setTimeout.turn, valid = Gun.valid, state_is = Gun.state.is, state_ify = Gun.state.ify, u, empty = {}, C; Gun.log = function(){ return (!Gun.log.off && C.log.apply(C, arguments)), [].slice.call(arguments).join(' ') }; Gun.log.once = function(w,s,o){ return (o = Gun.log.once)[w] = o[w] || 0, o[w]++ || Gun.log(s) }; @@ -995,7 +497,7 @@ try{ if(typeof MODULE !== "undefined"){ MODULE.exports = Gun } }catch(e){} module.exports = Gun; - (Gun.window||'').console = (Gun.window||'').console || {log: function(){}}; + (Gun.window||{}).console = (Gun.window||{}).console || {log: function(){}}; (C = console).only = function(i, s){ return (C.only.i && i === C.only.i && C.only.i++) && (C.log.apply(C, arguments) || s) }; ;"Please do not remove welcome log unless you are paying for a monthly sponsorship, thanks!"; @@ -1035,7 +537,7 @@ && u === (yes = n(tmp, opt))){} return yes; } - if(Gun.num.is(n)){ + if('number' == typeof n){ return (at.back || at).$.back(n - 1); } return this; @@ -1136,6 +638,7 @@ } function input(msg){ + Gun.window == msg && console.log("!!!!!!!", msg); var eve = this, cat = eve.as, root = cat.root, gun = msg.$, at = (gun||empty)._ || empty, change = msg.put, rel, tmp; if(cat.get && msg.get !== cat.get){ msg = obj_to(msg, {get: cat.get}); @@ -1163,11 +666,12 @@ if(cat.soul){ eve.to.next(msg); echo(cat, msg, eve); - if(cat.next){ obj_map(change, map, {msg: msg, cat: cat}) } + if(cat.next){ Object.keys(change).forEach(map, {msg: msg, cat: cat}) } return; } - if(!(rel = Gun.val.link.is(change))){ - if(Gun.val.is(change)){ + //if(!(rel = Gun.val.link.is(change))){ + if('string' != (rel = valid(change))){ + if(valid(change)){ if(cat.has || cat.soul){ not(cat, msg); } else @@ -1240,15 +744,14 @@ function echo(at, msg, ev){ if(!at.echo){ return } // || node_ === at.get ? //if(at.has){ msg = obj_to(msg, {event: ev}) } - obj_map(at.echo, reverb, msg); + Object.keys(at.echo).forEach(function(k){ var to = at.echo[k]; + if(!to || !to.on){ return } + to.on('in', msg); + }) } - function reverb(to){ - if(!to || !to.on){ return } - to.on('in', this); - } - function map(data, key){ // Map over only the changes on every update. - var cat = this.cat, next = cat.next || empty, via = this.msg, chain, at, tmp; - if(node_ === key && !next[key]){ return } + function map(key){ // Map over only the changes on every update. + var cat = this.cat, next = cat.next || empty, via = this.msg, data = via.put[key], chain, at, tmp; + if('_' === key && !next[key]){ return } if(!(at = next[key])){ return; } @@ -1257,14 +760,14 @@ //} if(at.has){ //if(!(data && data[_soul] && Gun.val.link.is(data) === Gun.node.soul(at.put))){ - if(u === at.put || !Gun.val.link.is(data)){ + if(u === at.put || 'string' != typeof valid(data)){ // not a link! at.put = data; } chain = at.$; } else if(tmp = via.$){ tmp = (chain = via.$.get(key))._; - if(u === tmp.put || !Gun.val.link.is(data)){ + if(u === tmp.put || 'string' != typeof valid(data)){ tmp.put = data; } } @@ -1288,13 +791,15 @@ if((!msg['@']) && null === tmp){ return } //obj_del(at, 'pass'); } - if(u === tmp && Gun.val.link.is(at.put)){ return } // This prevents the very first call of a thing from triggering a "clean up" call. // TODO: link.is(at.put) || !val.is(at.put) ? - obj_map(tmp, function(proxy){ + //if(u === tmp && Gun.val.link.is(at.put)){ return } // This prevents the very first call of a thing from triggering a "clean up" call. // TODO: link.is(at.put) || !val.is(at.put) ? + if(u === tmp && 'string' == typeof valid(at.put)){ return } // This prevents the very first call of a thing from triggering a "clean up" call. // TODO: link.is(at.put) || !val.is(at.put) ? + Object.keys(tmp||{}).forEach(function(k){ var proxy = tmp[k]; if(!(proxy = proxy.at)){ return } - obj_del(proxy.echo, at.id); + delete proxy.echo[at.id]; }); tmp = at.put; - obj_map(at.next, function(neat, key){ + //obj_map(at.next, function(neat, key){ + Object.keys(at.next||{}).forEach(function(key){ var neat = at.next[key]; if(u === tmp && u !== at.put){ return true } neat.put = u; if(neat.ack){ @@ -1314,15 +819,16 @@ tmp.on('out', {get: lex}); if(!at.ask){ return } // TODO: PERFORMANCE? More elegant way? } - tmp = at.ask; Gun.obj.del(at, 'ask'); - obj_map(tmp || at.next, function(neat, key){ + tmp = at.ask; delete at['ask']; + Object.keys(tmp = tmp || at.next || {}).forEach(function(neat, key){ var neat = tmp[key]; var lex = neat.lex || {}; lex['#'] = soul; lex['.'] = lex['.'] || key; neat.on('out', {get: lex}); }); - Gun.obj.del(at, 'ask'); // TODO: PERFORMANCE? More elegant way? + delete at['ask']; // TODO: PERFORMANCE? More elegant way? } function ack(msg, ev){ var as = this.as, get = as.get||'', at = as.$._, tmp = (msg.put||'')[get['#']]; + (msg._||{}).me = 1; if(at.ack){ at.ack = (at.ack + 1) || 1; } if(!msg.put || ('string' == typeof get['.'] && !obj_has(tmp, at.get))){ if(at.put !== u){ return } @@ -1334,16 +840,13 @@ }); return; } - if(node_ == get['.']){ // is this a security concern? + if('_' == get['.']){ // is this a security concern? at.on('in', {get: at.get, put: Gun.val.link.ify(get['#']), $: at.$, '@': msg['@']}); return; } Gun.on.put(msg); } - var empty = {}, u; - var obj = Gun.obj, obj_has = obj.has, obj_put = obj.put, obj_del = obj.del, obj_to = obj.to, obj_map = obj.map; - var text_rand = Gun.text.random; - var _soul = Gun.val.link._, node_ = Gun.node._; + var empty = {}, u, text_rand = String.random, valid = Gun.valid, obj_has = function(o, k){ return o && Object.prototype.hasOwnProperty.call(o, k) } })(USE, './chain'); ;USE(function(module){ @@ -1375,13 +878,13 @@ root.now = tmp; return gun; } else - if(num_is(key)){ + if('number' == typeof key){ return this.get(''+key, cb, as); } else if(tmp = rel.is(key)){ return this.get(tmp, cb, as); } else - if(obj.is(key)){ + if(Object.plain(key)){ gun = this; if(tmp = ((tmp = key['#'])||empty)['='] || tmp){ gun = gun.get(tmp) } gun._.lex = key; @@ -1448,16 +951,18 @@ //if((tmp = root.stop) && (tmp = tmp[at.id] || (tmp[at.id] = {})) && tmp[cat.id]){ return } tmp && (tmp[cat.id] = true); if(eve.seen && at.id && eve.seen[at.id]){ return eve.to.next(msg) } //if((tmp = root.stop)){ if(tmp[at.id]){ return } tmp[at.id] = msg.root; } // temporary fix till a better solution? - if((tmp = data) && tmp[rel._] && (tmp = rel.is(tmp))){ + if((tmp = data) && tmp['#'] && 'string' == (tmp = valid(tmp))){ tmp = ((msg.$$ = at.root.$.get(tmp))._); if(u !== tmp.put){ - msg = obj_to(msg, {put: data = tmp.put}); + //msg = obj_to(msg, {put: data = tmp.put}); + var o = {}; Object.keys(msg, function(k){ o[k] = msg[k] }); o.put = data = tmp.put; + msg = o; } } if((tmp = root.mum) && at.id){ // TODO: can we delete mum entirely now? - var id = at.id + (eve.id || (eve.id = Gun.text.random(9))); + var id = at.id + (eve.id || (eve.id = String.random(9))); if(tmp[id]){ return } - if(u !== data && !rel.is(data)){ tmp[id] = true; } + if(u !== data && 'string' != valid(data)){ tmp[id] = true; } } as.use(msg, eve); if(eve.stun){ @@ -1479,16 +984,13 @@ //obj.del(map, at); // TODO: Warning: This unsubscribes ALL of this chain's listeners from this link, not just the one callback event. return; } - var obj = Gun.obj, obj_map = obj.map, obj_has = obj.has, obj_to = Gun.obj.to; - var num_is = Gun.num.is; - var rel = Gun.val.link, node_soul = Gun.node.soul, node_ = Gun.node._; var empty = {}, u; })(USE, './get'); ;USE(function(module){ var Gun = USE('./root'); Gun.chain.put = function(data, cb, as){ - var gun = this, at = (gun._), root = at.root.$, ctx = root._, M = 100, tmp; + var gun = this, at = (gun._), root = at.root.$, ctx = root._, M = 99, tmp; as = as || {}; as.data = data; as.via = as.$ = as.via || as.$ || gun; @@ -1709,9 +1211,7 @@ } as.ref.put(as.data, as.soul, as); } - var obj = Gun.obj, obj_is = obj.is, obj_put = obj.put, obj_map = obj.map, obj_empty = obj.empty; var u, empty = {}, noop = function(){}, iife = function(fn,as){fn.call(as||empty)}; - var node_ = Gun.node._; })(USE, './put'); ;USE(function(module){ @@ -1771,11 +1271,6 @@ opt.ok.call(gun, data, msg.get, msg, ev); } } - - 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){ @@ -1785,11 +1280,11 @@ if(cb){ (opt = opt || {}).ok = cb; opt.at = at; - opt.out = {'#': Gun.text.random(9)}; + opt.out = {'#': String.random(9)}; gun.get(val, {as: opt}); opt.async = true; //opt.async = at.stun? 1 : true; } 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."); + Gun.log.once("valonce", "Chainable once 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._.nix = gun.once(function(){ chain._.on('in', gun._); @@ -1818,7 +1313,8 @@ }, opt.wait || 99); return; } - if(link && u === link.put && (tmp = rel.is(data))){ data = Gun.node.ify({}, tmp) } + //if(link && u === link.put && (tmp = rel.is(data))){ data = Gun.node.ify({}, tmp) } + if(link && u === link.put && 'string' == (tmp = Gun.valid(data))){ (data = {_:{},'>':{}})['#'] = tmp; } eve.rid? eve.rid(msg) : eve.off(); opt.ok.call(gun || opt.$, data, msg.get); } @@ -1860,8 +1356,6 @@ at.on('off', {}); return gun; } - var obj = Gun.obj, obj_map = obj.map, obj_has = obj.has, obj_del = obj.del, obj_to = obj.to; - var rel = Gun.val.link; var empty = {}, noop = function(){}, u; })(USE, './on'); @@ -1888,18 +1382,18 @@ return chain; } function map(msg){ - if(!msg.put || Gun.val.is(msg.put)){ return this.to.next(msg) } + if(!msg.put || Gun.valid(msg.put)){ return this.to.next(msg) } if(this.as.nix){ this.off() } // TODO: Ugly hack! - obj_map(msg.put, each, {at: this.as, msg: msg}); + Object.keys(msg.put).forEach(each, {at: this.as, msg: msg}); this.to.next(msg); } - function each(v,k){ - if(n_ === k){ return } - var msg = this.msg, gun = msg.$, at = gun._, cat = this.at, tmp = at.lex; + function each(k){ + if('_' === k){ return } + var msg = this.msg, v = msg.put[k], gun = msg.$, at = gun._, cat = this.at, tmp = at.lex; if(tmp && !Gun.text.match(k, tmp['.'] || tmp['#'] || tmp)){ return } // review? ((tmp = gun.get(k)._).echo || (tmp.echo = {}))[cat.id] = tmp.echo[cat.id] || cat; } - var obj_map = Gun.obj.map, noop = function(){}, event = {stun: noop, off: noop}, n_ = Gun.node._, u; + var noop = function(){}, event = {stun: noop, off: noop}, u; })(USE, './map'); ;USE(function(module){ @@ -1925,179 +1419,26 @@ })(USE, './set'); ;USE(function(module){ - if(typeof Gun === 'undefined'){ return } // TODO: localStorage is Browser only. But it would be nice if it could somehow plugin into NodeJS compatible localStorage APIs? - - var root, noop = function(){}, store, u; - try{store = (Gun.window||noop).localStorage}catch(e){} - if(!store){ - Gun.log("Warning: No localStorage exists to persist data to!"); - store = {setItem: function(k,v){this[k]=v}, removeItem: function(k){delete this[k]}, getItem: function(k){return this[k]}}; - } - /* - NOTE: Both `lib/file.js` and `lib/memdisk.js` are based on this design! - If you update anything here, consider updating the other adapters as well. - */ - - Gun.on('create', function(root){ - // This code is used to queue offline writes for resync. - // See the next 'opt' code below for actual saving of data. - var ev = this.to, opt = root.opt; - if(root.once){ return ev.next(root) } - if(false === opt.localStorage){ return ev.next(root) } // we want offline resynce queue regardless! // actually, this doesn't help, per @go1dfish 's observation. Disabling for now, will need better solution later. - opt.prefix = opt.file || 'gun/'; - var gap = Gun.obj.ify(store.getItem('gap/'+opt.prefix)) || {}; - var empty = Gun.obj.empty, id, to, go; - // add re-sync command. - if(!empty(gap)){ - var disk = Gun.obj.ify(store.getItem(opt.prefix)) || {}, send = {}; - Gun.obj.map(gap, function(node, soul){ - Gun.obj.map(node, function(val, key){ - send[soul] = Gun.state.to(disk[soul], key, send[soul]); - }); - }); - setTimeout(function(){ - // TODO: Holy Grail dangling by this thread! If gap / offline resync doesn't trigger, it doesn't work. Ouch, and this is a localStorage specific adapter. :( - root.on('out', {put: send, '#': root.ask(ack)}); - },1); - } - - root.on('out', function(msg){ - if(msg.lS){ return } // TODO: for IndexedDB and others, shouldn't send to peers ACKs to our own GETs. // THIS IS BLOCKING BROWSERS REPLYING TO REQUESTS, NO??? CHANGE THIS SOON!! UNDER CONTROLLED CIRCUMSTANCES!! Or maybe in-memory already doe sit? - if(Gun.is(msg.$) && msg.put && !msg['@']){ - id = msg['#']; - Gun.graph.is(msg.put, null, map); - if(!to){ to = setTimeout(flush, opt.wait || 1) } - } - this.to.next(msg); - }); - root.on('ack', ack); - - function ack(ack){ // TODO: This is experimental, not sure if we should keep this type of event hook. - if(ack.err || !ack.ok){ return } - var id = ack['@']; - setTimeout(function(){ - Gun.obj.map(gap, function(node, soul){ - Gun.obj.map(node, function(val, key){ - if(id !== val){ return } - delete node[key]; - }); - if(empty(node)){ - delete gap[soul]; - } - }); - flush(); - }, opt.wait || 1); - }; - ev.next(root); - - var map = function(val, key, node, soul){ - (gap[soul] || (gap[soul] = {}))[key] = id; - } - var flush = function(){ - clearTimeout(to); - to = false; - try{store.setItem('gap/'+opt.prefix, JSON.stringify(gap)); - }catch(e){ Gun.log(err = e || "localStorage failure") } - } - }); - - Gun.on('create', function(root){ - this.to.next(root); - var opt = root.opt; - if(root.once){ return } - if(false === opt.localStorage){ return } - opt.prefix = opt.file || 'gun/'; - var graph = root.graph, acks = {}, count = 0, to; - var disk = Gun.obj.ify(store.getItem(opt.prefix)) || {}; - var lS = function(){}, u; - root.on('localStorage', disk); // NON-STANDARD EVENT! - - root.on('put', function(msg){ - this.to.next(msg); - var put = msg.put, soul = put['#'], key = put['.'], val = put[':'], state = put['>'], tmp; - disk[soul] = Gun.state.ify(disk[soul], key, state, val, soul); - if(!msg['@']){ (acks[msg['#']] = (tmp = (msg._||'').lot || {})).lS = (tmp.lS||0)+1; } // only ack non-acks. - count += 1; - if(count >= (opt.batch || 1000)){ - return flush(); - } - if(to){ return } - to = setTimeout(flush, opt.wait || 1); - }); - - root.on('get', function(msg){ - this.to.next(msg); - var lex = msg.get, soul, data, u; - function to(){ - if(!lex || !(soul = lex['#'])){ return } - //if(0 >= msg.cap){ return } - var has = lex['.']; - data = disk[soul] || u; - if(data && has){ - data = Gun.state.to(data, has); - } - //if(!data && !Gun.obj.empty(opt.peers)){ return } // if data not found, don't ack if there are peers. // Hmm, what if we have peers but we are disconnected? - root.on('in', {'@': msg['#'], put: Gun.graph.node(data), lS:1});// || root.$}); - }; - Gun.debug? setTimeout(to,1) : to(); - }); - - var map = function(val, key, node, soul){ - disk[soul] = Gun.state.to(node, key, disk[soul]); - } - - var flush = function(data){ - var err; - count = 0; - clearTimeout(to); - to = false; - var ack = acks; - acks = {}; - if(data){ disk = data } - try{store.setItem(opt.prefix, JSON.stringify(disk)); - }catch(e){ - Gun.log(err = (e || "localStorage failure") + " Consider using GUN's IndexedDB plugin for RAD for more storage space, https://gun.eco/docs/RAD#install"); - root.on('localStorage:error', {err: err, file: opt.prefix, flush: disk, retry: flush}); - } - if(!err && !Gun.obj.empty(opt.peers)){ return } // only ack if there are no peers. - Gun.obj.map(ack, function(yes, id){ - if(yes){ - if(yes.more){ acks[id] = yes; return } - if(yes.s !== yes.lS){ err = "localStorage batch not same." } - } - root.on('in', { - '@': id, - err: err, - ok: 0 // localStorage isn't reliable, so make its `ok` code be a low number. - }); - }); - } - }); - })(USE, './adapters/localStorage'); - - ;USE(function(module){ - var Type = USE('../type'); + USE('./shim'); function Mesh(root){ var mesh = function(){}; var opt = root.opt || {}; opt.log = opt.log || console.log; opt.gap = opt.gap || opt.wait || 0; - opt.pack = opt.pack || (opt.memory? (opt.memory * 1000 * 1000) : 1399000000) * 0.3; // max_old_space_size defaults to 1400 MB. + opt.pack = opt.pack || (opt.memory? (opt.memory * 999 * 999) : 300000000) * 0.3; opt.puff = opt.puff || 9; // IDEA: do a start/end benchmark, divide ops/result. - var puff = setTimeout.puff || setTimeout; + var puff = setTimeout.turn || setTimeout; var dup = root.dup, dup_check = dup.check, dup_track = dup.track; var hear = mesh.hear = function(raw, peer){ if(!raw){ return } if(opt.pack <= raw.length){ return mesh.say({dam: '!', err: "Message too big!"}, peer) } - var msg, id, hash, tmp = raw[0], DBG; + var msg, id, hash, tmp = raw[0], ash, DBG; if(mesh === this){ hear.d += raw.length||0 ; ++hear.c } // STATS! if('[' === tmp){ - var S = +new Date; try{msg = JSON.parse(raw)}catch(e){opt.log('DAM JSON parse error', e)} - console.STAT && console.STAT(S, +new Date - S, 'dam parsed JSONs'); raw = ''; if(!msg){ return } console.STAT && console.STAT(+new Date, msg.length, '# on hear batch'); @@ -2116,29 +1457,19 @@ return; } if('{' === tmp || ((raw['#'] || obj_is(raw)) && (msg = raw))){ - var S = +new Date; try{msg = msg || JSON.parse(raw); }catch(e){return opt.log('DAM JSON parse error', e)} - //console.log('dam parse:', +new Date - S); - '{' === tmp && console.STAT && console.STAT(S, +new Date - S, 'dam parsed JSON'); if(!msg){ return } if(msg.DBG){ msg.DBG = DBG = {DBG: msg.DBG} } DBG && (DBG.hp = +new Date); - if(!(id = msg['#'])){ id = msg['#'] = Type.text.random(9) } + if(!(id = msg['#'])){ id = msg['#'] = String.random(9) } if(tmp = dup_check(id)){ return } - if(!(hash = msg['##']) && u !== msg.put){ - var S = +new Date; - hash = msg['##'] = Type.obj.hash(msg.put); - console.STAT && console.STAT(S, +new Date - S, 'hash time'); - } - /* - if(hash && (tmp = msg['@'] || (msg.get && id))){ // Reduces backward daisy in case varying hashes at different daisy depths are the same. - if(dup.check(tmp+hash)){ return } - dup.track(tmp+hash, true).it = it(msg); // GUN core also dedups, so `true` is needed. // Does GUN core need to dedup anymore? - } - if(tmp = msg['><']){ (msg._).to = Type.obj.map(tmp.split(','), tomap) } - */ // TOOD: COME BACK TO THIS LATER!!! IMPORTANT MESH STUFF!! + // DAM logic: + if(!(hash = msg['##']) && false && u !== msg.put){ /*hash = msg['##'] = Type.obj.hash(msg.put)*/ } // disable hashing for now // TODO: impose warning/penalty instead (?) + if(hash && (tmp = msg['@'] || (msg.get && id)) && dup.check(ash = tmp+hash)){ return } // Imagine A <-> B <=> (C & D), C & D reply with same ACK but have different IDs, B can use hash to dedup. Or if a GET has a hash already, we shouldn't ACK if same. (msg._ = function(){}).via = mesh.leap = peer; + if((tmp = msg['><']) && 'string' == typeof tmp){ tmp.slice(0,99).split(',').forEach(function(k){ this[k] = 1 }, (msg._).yo = {}) } // Peers already sent to, do not resend. + // DAM ^ if(tmp = msg.dam){ if(tmp = mesh.hear[tmp]){ tmp(msg, peer, root); @@ -2148,13 +1479,14 @@ } var S = +new Date, ST; DBG && (DBG.is = S); - //hear.ECHO = msg.put || hear.ECHO; !(msg.ok !== -3740) && mesh.say({ok: -3740, put: ECHO, '@': msg['#']}, peer); - root.on('in', msg); + root.on('in', mesh.last = msg); + //ECHO = msg.put || ECHO; !(msg.ok !== -3740) && mesh.say({ok: -3740, put: ECHO, '@': msg['#']}, peer); DBG && (DBG.hd = +new Date); console.STAT && (ST = +new Date - S) > 9 && console.STAT(S, ST, 'msg'); // TODO: PERF: caught one > 1.5s on tgif - if(ST > 1000){ require('fs').writeFile('./stats.msg.radata', JSON.stringify(msg, null, 2), noop) } - dup_track(id).via = peer; - mesh.leap = null; // warning! mesh.leap could be buggy. + (tmp = dup_track(id)).via = peer; + if(msg.get){ tmp.it = msg } + if(ash){ dup_track(ash) } //dup.track(tmp+hash, true).it = it(msg); + mesh.leap = mesh.last = null; // warning! mesh.leap could be buggy. } } var tomap = function(k,i,m){m(k,true)}; @@ -2168,33 +1500,33 @@ var say = mesh.say = function(msg, peer){ if(this.to){ this.to.next(msg) } // compatible with middleware adapters. if(!msg){ return false } - var id, hash, tmp, raw; + var id, hash, tmp, raw, ack = msg['@']; var DBG = msg.DBG, S; if(!peer){ S = +new Date ; DBG && (DBG.y = S) } var meta = msg._||(msg._=function(){}); - if(!(id = msg['#'])){ id = msg['#'] = Type.text.random(9) } - //if(!(hash = msg['##']) && u !== msg.put){ hash = msg['##'] = Type.obj.hash(msg.put) } + if(!(id = msg['#'])){ id = msg['#'] = String.random(9) } + !loop && dup_track(id);//.it = it(msg); // track for 9 seconds, default. Earth<->Mars would need more! // always track, maybe move this to the 'after' logic if we split function. + if(!(hash = msg['##']) && u !== msg.put && !meta.via && ack){ say.hash(msg); return } // TODO: Should broadcasts be hashed? if(!(raw = meta.raw)){ raw = mesh.raw(msg); - /*if(hash && (tmp = msg['@'])){ - dup.track(tmp+hash).it = it(msg); - if(tmp = (dup.s[tmp]||ok).it){ + if(hash && ack){ + dup_track(ack+hash);//.it = it(msg); + if((tmp = (dup.s[ack]||{}).it) || ((tmp = mesh.last) && ack === tmp['#'])){ if(hash === tmp['##']){ return false } tmp['##'] = hash; } - }*/ + } } S && console.STAT && console.STAT(S, +new Date - S, 'say prep'); - !loop && dup_track(id);//.it = it(msg); // track for 9 seconds, default. Earth<->Mars would need more! // always track, maybe move this to the 'after' logic if we split function. //console.log("SEND!", JSON.parse(JSON.stringify(msg))); - if(!peer && (tmp = msg['@'])){ peer = ((tmp = dup.s[tmp]) && (tmp.via || ((tmp = tmp.it) && (tmp = tmp._) && tmp.via))) || mesh.leap } // warning! mesh.leap could be buggy! - if(!peer && msg['@']){ + if(!peer && ack){ peer = ((tmp = dup.s[ack]) && (tmp.via || ((tmp = tmp.it) && (tmp = tmp._) && tmp.via))) || mesh.leap } // warning! mesh.leap could be buggy! + if(!peer && ack){ console.STAT && console.STAT(+new Date, ++SMIA, 'total no peer to ack to'); return false; } // TODO: Temporary? If ack via trace has been lost, acks will go to all peers, which trashes browser bandwidth. Not relaying the ack will force sender to ask for ack again. Note, this is technically wrong for mesh behavior. if(!peer && mesh.way){ return mesh.way(msg) } if(!peer || !peer.id){ message = msg; - if(!Type.obj.is(peer || opt.peers)){ return false } - var P = opt.puff, ps = opt.peers, pl = Object.keys(peer || opt.peers || {}); // TODO: BETTER PERF? No object.keys? It is polyfilled by Type.js tho. + if(!Object.plain(peer || opt.peers)){ return false } + var P = opt.puff, ps = opt.peers, pl = Object.keys(peer || opt.peers || {}); ;(function go(){ var S = +new Date; //Type.obj.map(peer || opt.peers, each); // in case peer is a peer list. @@ -2208,7 +1540,7 @@ console.STAT && console.STAT(S, +new Date - S, 'say loop'); if(!pl.length){ return } puff(go, 0); - dup_track(msg['@']); // keep for later + dup_track(ack); // keep for later }()); return; } @@ -2216,18 +1548,18 @@ if(!peer.wire && mesh.wire){ mesh.wire(peer) } if(id === peer.last){ return } peer.last = id; // was it just sent? if(peer === meta.via){ return false } // don't send back to self. - if((tmp = meta.to) && (tmp[peer.url] || tmp[peer.pid] || tmp[peer.id]) /*&& !o*/){ return false } + if((tmp = meta.yo) && (tmp[peer.url] || tmp[peer.pid] || tmp[peer.id]) /*&& !o*/){ return false } if(peer.batch){ peer.tail = (tmp = peer.tail || 0) + raw.length; if(peer.tail <= opt.pack){ //peer.batch.push(raw); - peer.batch += (tmp?',':'')+raw; // TODO: Prevent double JSON! // FOR v1.0 !? + peer.batch += (tmp?',':'')+raw; return; } flush(peer); } //peer.batch = []; - peer.batch = '['; // TODO: Prevent double JSON! + peer.batch = '['; // Prevents double JSON! var S = +new Date, ST; setTimeout(function(){ console.STAT && (ST = +new Date - S) > 9 && console.STAT(S, ST, '0ms TO', id, peer.id); @@ -2285,7 +1617,7 @@ /* // NOTE TO SELF: Switch NTS to DAM now. */ - if(meta && (raw||'').length < (1000 * 100)){ meta.raw = raw } // HNPERF: If string too big, don't keep in memory. + if(meta && (raw||'').length < (999 * 99)){ meta.raw = raw } // HNPERF: If string too big, don't keep in memory. return raw; } var $ = JSON.stringify, _ = ':])([:'; @@ -2297,7 +1629,7 @@ if(peer.id){ opt.peers[peer.url || peer.id] = peer; } else { - tmp = peer.id = peer.id || Type.text.random(9); + tmp = peer.id = peer.id || String.random(9); mesh.say({dam: '?', pid: root.opt.pid}, opt.peers[tmp] = peer); delete dup.s[peer.last]; // IMPORTANT: see https://gun.eco/docs/DAM#self } @@ -2305,10 +1637,10 @@ if(!tmp.hied){ root.on(tmp.hied = 'hi', peer) } // @rogowski I need this here by default for now to fix go1dfish's bug tmp = peer.queue; peer.queue = []; - Type.obj.map(tmp, function(msg){ + (tmp||[]).forEach(function(msg){ send(msg, peer); }); - Type.obj.native && Type.obj.native(); // dirty place to check if other JS polluted. + //Type.obj.native && Type.obj.native(); // dirty place to check if other JS polluted. } mesh.bye = function(peer){ root.on('bye', peer); @@ -2326,7 +1658,7 @@ } root.on('create', function(root){ - root.opt.pid = root.opt.pid || Type.text.random(9); + root.opt.pid = root.opt.pid || String.random(9); this.to.next(root); root.on('out', mesh.say); }); @@ -2335,7 +1667,7 @@ peer = opt.peers[peer.id || peer] || peer; this.to.next(peer); peer.bye? peer.bye() : (tmp = peer.wire) && tmp.close && tmp.close(); - Type.obj.del(opt.peers, peer.id); + delete opt.peers[peer.id]; peer.wire = null; }); @@ -2347,9 +1679,9 @@ root.on('hi', function(peer, tmp){ this.to.next(peer); if(!(tmp = peer.url) || !gets[tmp]){ return } delete gets[tmp]; if(opt.super){ return } // temporary (?) until we have better fix/solution? - Type.obj.map(root.next, function(node, soul){ + Object.keys(root.next).forEach(function(soul){ var node = root.next[soul]; tmp = {}; tmp[soul] = root.graph[soul]; - mesh.say({'##': Type.obj.hash(tmp), get: {'#': soul}}, peer); + mesh.say({'##': String.hash(tmp), get: {'#': soul}}, peer); }) }); @@ -2359,19 +1691,19 @@ ;(function(){ var $ = JSON.stringify, u; - Type.obj.hash = function(obj, hash){ + Object.hash = function(obj, hash){ if(!hash && u === (obj = $(obj, sort))){ return } - return Type.text.hash(hash || obj || ''); + return String.hash(hash || obj || ''); } function sort(k, v){ var tmp; if(!(v instanceof Object)){ return v } var S = +new Date; - Type.obj.map(Object.keys(v).sort(), map, {to: tmp = {}, on: v}); + Object.keys(v).sort().forEach(map, {to: tmp = {}, on: v}); console.STAT && console.STAT(S, +new Date - S, 'sort'); return tmp; } - Type.obj.hash.sort = sort; + Object.hash.sort = sort; function map(k){ this.to[k] = this.on[k]; @@ -2381,11 +1713,10 @@ function it(msg){ return msg || {_: msg._, '##': msg['##']} } // HNPERF: Only need some meta data, not full reference (took up too much memory). // HNPERF: Garrrgh! We add meta data to msg over time, copying the object happens to early. var empty = {}, ok = true, u; - var obj_is = Type.obj.is, obj_map = Type.obj.map; try{ module.exports = Mesh }catch(e){} - })(USE, './adapters/mesh'); + })(USE, './mesh'); ;USE(function(module){ var Gun = USE('../index'); @@ -2393,15 +1724,11 @@ Gun.on('opt', function(root){ this.to.next(root); - var opt = root.opt; if(root.once){ return } + var opt = root.opt; if(false === opt.WebSocket){ return } - var env; - if(typeof window !== "undefined"){ env = window } - if(typeof global !== "undefined"){ env = global } - env = env || {}; - + var env = Gun.window || {}; var websocket = opt.WebSocket || env.WebSocket || env.webkitWebSocket || env.mozWebSocket; if(!websocket){ return } opt.WebSocket = websocket; @@ -2431,9 +1758,9 @@ 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? + setTimeout(function(){ !opt.super && 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; + var wait = 2 * 999; function reconnect(peer){ clearTimeout(peer.defer); if(doc && peer.retry <= 0){ return } peer.retry = (peer.retry || opt.retry || 60) - 1; @@ -2445,6 +1772,6 @@ var doc = 'undefined' !== typeof document && document; }); var noop = function(){}; - })(USE, './adapters/websocket'); + })(USE, './websocket'); }()); \ No newline at end of file diff --git a/lib/meta.js b/lib/meta.js index 23e5fa08..b1fbf5ce 100644 --- a/lib/meta.js +++ b/lib/meta.js @@ -38,8 +38,9 @@ return m.flip(false) } // cancel and close when no action and "meta key" held down (e.g. ctrl+c) if(!eve.fake && key === k.last){ return }; k.last = key; // jussi: polyfilling eve.repeat? - if(!eve.fake && $(eve.target).closest('input, textarea, [contenteditable=true]').length && !$(eve.target).closest('#meta').length){ - if(meta.flip.is() && !withMeta(eve)) eve.preventDefault() + if(!eve.fake && $(eve.target).closest('input, textarea, [contenteditable=true]').length/* && !$(eve.target).closest('#meta').get().length*/){ + return; + //if(meta.flip.is() && !withMeta(eve)) eve.preventDefault() } m.check('on', key, k.at || (k.at = m.edit)); if(k.meta[key]){ m.flip() } @@ -116,7 +117,7 @@ m.list(at.back); })); } - m.ask = function(help, cb){ + m.ask = function(help, cb, opt){ var $ul = $('#meta .meta-menu ul').empty(); var $put = $('').attr('id', 'meta-ask').attr('placeholder', help); var $form = $('
').append($put).on('submit', function(eve){ @@ -126,6 +127,9 @@ //k.wipe(); m.list(k.at); }); + if(opt){ + $form.on('keyup', function(eve){ cb($put.val()) }) + } var $li = $('
  • ').append($form); $ul.append($li); m.flip(true); @@ -235,6 +239,8 @@ opacity: 1 }, '#meta a': {color: 'black'}, + '#meta:hover': {opacity: 1}, + '#meta:hover .meta-menu': {display: 'block'}, '#meta .meta-menu ul:before': { content: "' '", display: 'block', @@ -264,7 +270,7 @@ })(USE, './metaUI'); ;USE(function(module){ var m = meta, k = m.key; - $(window).on('focus', k.wipe.bind(null, false)); // .on('blur', k.wipe.bind(null, false)) + //$(window).on('focus', k.wipe.bind(null, false)); // .on('blur', k.wipe.bind(null, false)) $(document).on('mousedown mousemove mouseup', function(eve){ m.tap.eve = eve; m.tap.x = eve.pageX||0; diff --git a/lib/radisk.js b/lib/radisk.js index 353aae82..a7aa22e0 100644 --- a/lib/radisk.js +++ b/lib/radisk.js @@ -8,7 +8,7 @@ var has = (Radisk.has || (Radisk.has = {}))[opt.file]; if(has){ return has } - opt.pack = opt.pack || (opt.memory? (opt.memory * 1000 * 1000) : 1399000000) * 0.3; // max_old_space_size defaults to 1400 MB. + opt.pack = opt.pack || (opt.memory? (opt.memory * 999 * 999) : 300000000) * 0.3; opt.until = opt.until || opt.wait || 250; opt.batch = opt.batch || (10 * 1000); opt.chunk = opt.chunk || (1024 * 1024 * 1); // 1MB @@ -21,8 +21,7 @@ function atomic(v){ return u !== v && (!v || 'object' != typeof v) } var timediate = (typeof setImmediate === "undefined")? setTimeout : setImmediate; var puff = setTimeout.puff || timediate; - var map = Gun.obj.map; - var obj_empty = Gun.obj.empty; + var map = Radix.object; var ST = 0; if(!opt.store){ @@ -107,7 +106,8 @@ if((tmp = r.disk[f]) && (tmp = tmp.tags) && tmp[tag]){ continue } ack = tag[f]; delete tag[f]; - if(!obj_empty(tag)){ continue } + var ne; for(var k in tag){ if(tag.hasOwnProperty(k)){ ne = true; break } } // is not empty? + if(ne){ continue } //if(!obj_empty(tag)){ continue } delete r.tags[tag]; ack && ack(err, ok); } @@ -493,7 +493,7 @@ dir = dir || rad; dir.file = f; tmp = Q; Q = null; - Gun.list.map(tmp, function(arg){ + map(tmp, function(arg){ r.find(arg[0], arg[1]); }); } diff --git a/lib/radix.js b/lib/radix.js index 1d28b46a..2b484bbd 100644 --- a/lib/radix.js +++ b/lib/radix.js @@ -15,7 +15,7 @@ k += key[++i]; } if(!at){ - if(!map(t, function(r, s){ + if(!each(t, function(r, s){ var ii = 0, kk = ''; if((s||'').length){ while(s[ii] == key[ii]){ kk += s[ii++]; @@ -102,17 +102,17 @@ } }; - Object.keys = Object.keys || function(o){ return map(o, function(v,k,t){t(k)}) } - if(typeof window !== "undefined"){ - var Gun = window.Gun; window.Radix = Radix; } else { - var Gun = require('../gun'); try{ module.exports = Radix }catch(e){} } - - var map = Gun.obj.map, no = {}, u; + var each = Radix.object = function(o, f, r){ + for(var k in o){ + if(!o.hasOwnProperty(k)){ continue } + if((r = f(o[k], k)) !== u){ return r } + } + }, no = {}, u; var _ = String.fromCharCode(24); }()); \ No newline at end of file diff --git a/lib/stats.js b/lib/stats.js index 772bcd26..da00eb4c 100644 --- a/lib/stats.js +++ b/lib/stats.js @@ -19,8 +19,9 @@ Gun.on('opt', function(root){ os.freemem = os.freemem || noop; os.loadavg = os.loadavg || noop; os.cpus = os.cpus || noop; + var obj_ify = function(o){try{o = JSON.parse(o)}catch(e){o={}};return o;} setTimeout(function(){ - root.stats = Gun.obj.ify((fs.existsSync(__dirname+'/../stats.'+root.opt.file) && fs.readFileSync(__dirname+'/../stats.'+root.opt.file).toString())) || {}; + root.stats = obj_ify((fs.existsSync(__dirname+'/../stats.'+root.opt.file) && fs.readFileSync(__dirname+'/../stats.'+root.opt.file).toString())) || {}; root.stats.up = root.stats.up || {}; root.stats.up.start = root.stats.up.start || +(new Date); root.stats.up.count = (root.stats.up.count || 0) + 1; diff --git a/lib/store.js b/lib/store.js index 672f8010..2a07fc3a 100644 --- a/lib/store.js +++ b/lib/store.js @@ -28,7 +28,7 @@ Gun.on('create', function(root){ }, id, DBG && (DBG.r = DBG.r || {})); DBG && (DBG.sps = DBG.sps || +new Date); }); - var count = {}, obj_empty = Gun.obj.empty; + var count = {}, obj_empty = Object.empty; root.on('get', function(msg){ this.to.next(msg); @@ -138,7 +138,7 @@ Gun.on('create', function(root){ (graph = graph || {})[soul] = Gun.state.ify(graph[soul], has, state, val, soul); } }); - var val_is = Gun.val.is + var val_is = Gun.valid; opt.store.stats = {get:{time:{}, count:0}, put: {time:{}, count:0}}; // STATS! var statg = 0, statp = 0; // STATS! }); \ No newline at end of file diff --git a/lib/yson.js b/lib/yson.js index 2f4433b0..946399e6 100644 --- a/lib/yson.js +++ b/lib/yson.js @@ -150,4 +150,4 @@ function value(s){ module.exports = yson; -}()); +}()); \ No newline at end of file diff --git a/sea.js b/sea.js index d721f4e5..28d554c3 100644 --- a/sea.js +++ b/sea.js @@ -1224,7 +1224,7 @@ }).on.on('secure', msg); return; } - var link_is = Gun.val.link.is, state_ify = Gun.state.ify; + var valid = Gun.valid, link_is = function(d,l){ return 'string' == typeof (l = valid(d)) && l }, state_ify = Gun.state.ify; // okay! The security function handles all the heavy lifting. // It needs to deal read and write of input and output of system data, account/public key data, and regular data.