mirror of
https://github.com/amark/gun.git
synced 2025-03-30 15:08:33 +00:00

* tmp for hn issue * log top to stats * test for guntest peer * try big messages * parse time? * what bin/node is 11ms? * be normal for hnoon * tolerate 0.5s * try 3s for hnoon? * stop empty gets * tmp for guntest * back to normal * check hash time * back to normal in hear * screen / upload / play / pause * merge latest npm release into manhattan * merge master 0.2020.421 into manhattan manually * WIP * manually merge from master * gatling * Update upload.html * work in progress... * yson panic chat basic * after `.put(` walk * restructure acks * messy but 2 units passing! * put recursive once on map * basics * have NTS use DAM + fix other utils * Rewrote nts * Allow passing test cli args. Before, no CLI args would be passed when running `npm test`. Keeping the `mocha` at the end of the test script allows passing CLI args to Mocha. * put back scan & once tweak * PANIC user paste OK * manhattan sea * stub out nts for now * AXE tweak * tweak for quick first prod testing * tweak for first in-prod testing * tweak * tweak * sketchy in-prod debug attempt * caught it? maybe? now restore * Create download-log.html * stub out yson test prod? * ugh, gotta see what is going on * move dl * gonna stop doing commit messages for in-prod test/tweaks/debugging * a * p * squelch * console stats * stats * stop travis * restore yson * ahhh no file access without sudo * mem * no stub * fix axe * bump * back to in-prod testing, isolate/stub out code * stub all out for 17K ? CPU ? test * stub dup gc * ugh main stub * does this stop url format blocking? * re-add dup * no top :( * will this work? * get ack stats? * a map chain may ask for data not a root soul chain * move proper logic into .get( * how 2019 compat? * a couple more! * more tests passing! :D :) * even more! SO EXCITING :D * Am I alive? * wow I can't believe it works like this * THANK YOU @rogowski !!!!!! * Create trace.html Adding tracing to debuging. * @rogowski is a super star :) :) :) * Update trace.js Change `Gun.logs` to `Gun.traces` and `Gun._log` to `Gun._trace`. * Update trace.html Change `Gun.logs` to `Gun.traces` and `Gun._log` to `Gun._trace`. Overload get,put,on,map * @rogowski approved of these trace changes :) * Update trace.html More decoupled. * Update trace.js More decoupled * 2 steps backwards, 1 step forward? * back where we ( @rogowski ) started :P * YAYAYAYAYAYAYAY past where we started at! * safer to have it here * slight tweak? Let's see how long it lasts. * merge checks we left out during consolidation * ugly common.js for @rogowski * slightly better * amazing map discovery + don't clear on not found if data exists * onto next test... * all caught up!!! Now update tests from graphify to statedisk * Update common.js Tests updated from graphify to statedisk. * easy to debug & fix thanks to @rogowski 's test upgrades & trace! * hmm, IDK if we should support this anymore? * support once chaining? * check if listener order is earlier than write * in-process message passing needs to clean itself of flags for now * ack to chains that can't be reached * call sub chains on clear/empty WIP * call sub chains clear/empty OK! * into unlink. Clean/refactor later. * oh that was nice * self check not needed? * test was poorly constructed? * refactor unlink to cleaner logic * Will you blame me for this? Special case, maybe later move to cleaner place? * use stun's run id instead. * cleaner unlink logic * better map, link, and unlink logic. * unstub relay * refactor unlink * invert * if prev value would have caused unlink, do not unlink again. * w000h00! Best unlink so far. * woops, fix unlinking nested * unsubscribe nested maps (working, tho possible perf regression? check) * put check soul * add default uuid * improved browser peer retry logic, let devices sleep, etc. * Chaining API unit tests passing! * merge new panic tests into here to test * add panic utils * fix long streaming timeout/expiry issue, update examples * yield generating test data * yeah, adapter errors (like out of storage) should not affect sync/networking logic, that was a bad experiment * git glitch? * some mid debugging fixes but maybe scary changes, hopefully safe to revert here except dub * SEA unit tests passing!!! Needed quite a few fixes on async write queue stuff. * optionally make auth async * revise/correct set * Fix reverse boundary check * Add extra tests, catch bad guy, obliterate bug. * chat app with emoji examples * handle empty string keyed objects * starting lex support * tweak for lex * woops! lexical alphabetical oopsies. That was bad. * upload either way * debug * start * fix * fix * clean + feature * update dependencies in package.json (#1086) * rad lex once map once * axe polyfill for now * oops log * oops maybe without this it crashed the peer * what on earth happened to my browser/OS? "unplug & plug it back in" restart seemed to fix it. * oh, don't memory leak req/res asks. :/ duh! * no accidental #soul."" * ugh, still have to sort :(, really should polyfill weakmap then * oops, pluck needs new object to go into * oops, make sure soul is passed * updating deprecated functions * begin AXE. Next: load balance! * Update sea.js * keys are dangerous! * AXE round robin load balance * better ash hash checking * lS reuse in-mem reply chunking * state machine!!! * RAD needs to pass cache misses. * updating deprecated functions (#1088) * update dependencies in package.json * updating deprecated functions * remove where.gundb.io * Bring SEA.certify into manhattan branch (#1092) Co-authored-by: Radu Cioienaru <radu@projectmanager.com> * fix rad, make get() hookable * rad browser tests seem to be passing! * reverse user random side, add err, update styles, + more * fix pack/max, update dom * paste! * of course it'll dedup cause it just called track on hear, fix * 📦 Adding the hub feature to this branch & improvements. (#1102) * 📦 Adding the hub feature to this branch. * 🗑 Removed the container for speed improvement ! * 📝 I added some comments to the code. Co-authored-by: Hector <fairfairytotor@gmail.com> Co-authored-by: Hector <pro.hector.kub@gmail.com> * Update axe.js * 🦅 Wrap everything in a try & catch for error handling… (#1105) * 🦅 Wrap everything in a try & catch for error handling & speed improvement. * 📦 Finally here : opt.file for the hub feature ! * 📦 Finally here : opt.file for the hub feature ! And also : fixed indentation 😋 Co-authored-by: noctisatrae <pro.hector.kub@gmail.com> * probs better this way, safer * moved test/axe tests to test/panic/axe. * New test: axe load balance. * axe test: webrtc data balance(fix paths and file renamed). * test axe: renaming webrtc file. * axe test: separating webrtc test for data_balance. * axe test: test only with the relay(without webrtc). * Update sea.js Same as https://github.com/amark/gun/pull/1062 * Update gun.js var tmp * Update upload.js * merge, update stun * SEA.certify wire logic + unit tests (#1110) * SEA.certify wire logic + unit tests * picking white hair * ack err * axe tests using puppeteer. * change stun system * ~20lines * put use parent soul link if need * handle errors * finally seems fixed * cb not to * relay * nasty bug! Don't crash, tho need to find what causes it * undo local changes/notes to self * deprecation warnings * "old" data to test against * oops, forgot I played with ascii * debug * in-prod check: sites * in-prod isolate * gotta find this, by stubbing out * where? * will this work? * clearly not, lol what's the point then? maybe like this * and again * must we? * USE THIS MANHATTAN VERSION * clean * better panic hints Co-authored-by: Robin Bron <finwo@pm.me> Co-authored-by: Pavel Diatchenko <diatche@users.noreply.github.com> Co-authored-by: rogowski <163828+rogowski@users.noreply.github.com> Co-authored-by: I001962 <i001962@gmail.com> Co-authored-by: Adriano Rogowski <rogowski.adriano@gmail.com> Co-authored-by: Radu <cetatuie@gmail.com> Co-authored-by: Radu Cioienaru <radu@projectmanager.com> Co-authored-by: Hector <46224745+noctisatrae@users.noreply.github.com> Co-authored-by: Hector <fairfairytotor@gmail.com> Co-authored-by: Hector <pro.hector.kub@gmail.com> Co-authored-by: Martti Malmi <sirius@iki.fi> Co-authored-by: mimiza <dev@mimiza.com>
451 lines
16 KiB
JavaScript
451 lines
16 KiB
JavaScript
;(function(){
|
|
var u;
|
|
if(''+u == typeof Gun){ return }
|
|
var DEP = function(n){ console.log("Warning! Deprecated internal utility will break in next version:", n) }
|
|
// Generic javascript utilities.
|
|
var Type = Gun;
|
|
//Type.fns = Type.fn = {is: function(fn){ return (!!fn && fn instanceof Function) }}
|
|
Type.fn = Type.fn || {is: function(fn){ DEP('fn'); return (!!fn && 'function' == typeof fn) }}
|
|
Type.bi = Type.bi || {is: function(b){ DEP('bi');return (b instanceof Boolean || typeof b == 'boolean') }}
|
|
Type.num = Type.num || {is: function(n){ DEP('num'); return !list_is(n) && ((n - parseFloat(n) + 1) >= 0 || Infinity === n || -Infinity === n) }}
|
|
Type.text = Type.text || {is: function(t){ DEP('text'); return (typeof t == 'string') }}
|
|
Type.text.ify = Type.text.ify || function(t){ DEP('text.ify');
|
|
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 = Type.text.random || function(l, c){ DEP('text.random');
|
|
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-- }
|
|
return s;
|
|
}
|
|
Type.text.match = Type.text.match || function(t, o){ var tmp, u; DEP('text.match');
|
|
if('string' !== typeof t){ return false }
|
|
if('string' == typeof o){ o = {'=': o} }
|
|
o = o || {};
|
|
tmp = (o['='] || o['*'] || o['>'] || o['<']);
|
|
if(t === tmp){ return true }
|
|
if(u !== o['=']){ return false }
|
|
tmp = (o['*'] || o['>'] || o['<']);
|
|
if(t.slice(0, (tmp||'').length) === tmp){ return true }
|
|
if(u !== o['*']){ return false }
|
|
if(u !== o['>'] && u !== o['<']){
|
|
return (t >= o['>'] && t <= o['<'])? true : false;
|
|
}
|
|
if(u !== o['>'] && t >= o['>']){ return true }
|
|
if(u !== o['<'] && t <= o['<']){ return true }
|
|
return false;
|
|
}
|
|
Type.text.hash = Type.text.hash || function(s, c){ // via SO
|
|
DEP('text.hash');
|
|
if(typeof s !== 'string'){ return }
|
|
c = c || 0;
|
|
if(!s.length){ return c }
|
|
for(var i=0,l=s.length,n; i<l; ++i){
|
|
n = s.charCodeAt(i);
|
|
c = ((c<<5)-c)+n;
|
|
c |= 0;
|
|
}
|
|
return c;
|
|
}
|
|
Type.list = Type.list || {is: function(l){ DEP('list'); return (l instanceof Array) }}
|
|
Type.list.slit = Type.list.slit || Array.prototype.slice;
|
|
Type.list.sort = Type.list.sort || function(k){ // creates a new sort function based off some key
|
|
DEP('list.sort');
|
|
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 }
|
|
}
|
|
}
|
|
Type.list.map = Type.list.map || function(l, c, _){ DEP('list.map'); 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 = Type.boj || {is: function(o){ DEP('obj'); return o? (o instanceof Object && o.constructor === Object) || Object.prototype.toString.call(o).match(/^\[object (\w+)\]$/)[1] === 'Object' : false }}
|
|
Type.obj.put = Type.obj.put || function(o, k, v){ DEP('obj.put'); return (o||{})[k] = v, o }
|
|
Type.obj.has = Type.obj.has || function(o, k){ DEP('obj.has'); return o && Object.prototype.hasOwnProperty.call(o, k) }
|
|
Type.obj.del = Type.obj.del || function(o, k){ DEP('obj.del');
|
|
if(!o){ return }
|
|
o[k] = null;
|
|
delete o[k];
|
|
return o;
|
|
}
|
|
Type.obj.as = Type.obj.as || function(o, k, v, u){ DEP('obj.as'); return o[k] = o[k] || (u === v? {} : v) }
|
|
Type.obj.ify = Type.obj.ify || function(o){ DEP('obj.ify');
|
|
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 = Type.obj.to || function(from, to){ DEP('obj.to');
|
|
to = to || {};
|
|
obj_map(from, map, to);
|
|
return to;
|
|
}
|
|
}());
|
|
Type.obj.copy = Type.obj.copy || function(o){ DEP('obj.copy'); // 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 = Type.obj.empty || function(o, n){ DEP('obj.empty');
|
|
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 = Type.obj.map || function(l, c, _){ DEP('obj.map');
|
|
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;
|
|
}
|
|
_ = _ || {};
|
|
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;
|
|
}
|
|
}());
|
|
Type.time = Type.time || {};
|
|
Type.time.is = Type.time.is || function(t){ DEP('time'); 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;
|
|
|
|
var Val = {};
|
|
Val.is = function(v){ DEP('val.is'); // 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 }
|
|
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.
|
|
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.
|
|
}
|
|
Val.link = Val.rel = {_: '#'};
|
|
;(function(){
|
|
Val.link.is = function(v){ DEP('val.link.is'); // 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){ DEP('val.link.ify'); 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;
|
|
|
|
Type.val = Type.val || Val;
|
|
|
|
var Node = {_: '_'};
|
|
Node.soul = function(n, o){ DEP('node.soul'); 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){ DEP('node.soul.ify'); // 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){ DEP('node.is'); 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){ DEP('node.ify'); // 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;
|
|
Type.node = Type.node || Node;
|
|
|
|
var State = Type.state;
|
|
State.lex = function(){ DEP('state.lex'); return State().toString(36).replace('.','') }
|
|
State.to = function(from, k, to){ DEP('state.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){ DEP('state.map'); 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;
|
|
|
|
var Graph = {};
|
|
;(function(){
|
|
Graph.is = function(g, cb, fn, as){ DEP('graph.is'); // 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.
|
|
}
|
|
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);
|
|
}
|
|
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.
|
|
}
|
|
}());
|
|
;(function(){
|
|
Graph.ify = function(obj, env, as){ DEP('graph.ify');
|
|
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){ DEP('graph.node');
|
|
var soul = Node.soul(node);
|
|
if(!soul){ return }
|
|
return obj_put({}, soul, node);
|
|
}
|
|
;(function(){
|
|
Graph.to = function(graph, root, opt){ DEP('graph.to');
|
|
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;
|
|
Type.graph = Type.graph || Graph;
|
|
}()); |