put recursive once on map

This commit is contained in:
Mark Nadal 2020-09-10 17:51:02 -07:00
parent 69c476e74b
commit 921000adc2

218
gun.js
View File

@ -279,7 +279,6 @@
if(dup.check(tmp)){ return } dup.track(tmp); if(dup.check(tmp)){ return } dup.track(tmp);
tmp = msg._; msg._ = ('function' == typeof tmp)? tmp : function(){}; tmp = msg._; msg._ = ('function' == typeof tmp)? tmp : function(){};
(msg.$ && (msg.$ === (msg.$._||'').$)) || (msg.$ = gun); (msg.$ && (msg.$ === (msg.$._||'').$)) || (msg.$ = gun);
console.only(2, 'uni', msg);
if(msg['@'] && !msg.put){ ack(msg) } if(msg['@'] && !msg.put){ ack(msg) }
if(!at.ask(msg['@'], msg)){ // is this machine listening for an ack? if(!at.ask(msg['@'], msg)){ // is this machine listening for an ack?
DBG && (DBG.u = +new Date); DBG && (DBG.u = +new Date);
@ -302,7 +301,7 @@
}*/ }*/
var put = msg.put; var put = msg.put;
var DBG = ctx.DBG = msg.DBG; var DBG = ctx.DBG = msg.DBG;
if(put['#'] && put['.']){ root.on('put', msg); return } // TODO: BUG! This needs to call HAM instead. if(put['#'] && put['.']){ root && root.on('put', msg); return } // TODO: BUG! This needs to call HAM instead.
DBG && (DBG.p = S); DBG && (DBG.p = S);
ctx['#'] = msg['#']; ctx['#'] = msg['#'];
ctx.msg = msg; ctx.msg = msg;
@ -314,7 +313,8 @@
if(nj != ni){ nj = ni; if(nj != ni){ nj = ni;
if(!(soul = nl[ni])){ if(!(soul = nl[ni])){
ctx.stun--; // TODO: 'forget' feature in SEA tied to this, bad approach, but hacked in for now. Any changes here must update there. ctx.stun--; // TODO: 'forget' feature in SEA tied to this, bad approach, but hacked in for now. Any changes here must update there.
fire(ctx); ctx.hatch && ctx.hatch(); // TODO: rename/rework how put & this interact.
//fire(ctx); // ABC
return; return;
} }
if(!(node = put[soul])){ err = ERR+cut(soul)+"no node." } else if(!(node = put[soul])){ err = ERR+cut(soul)+"no node." } else
@ -681,25 +681,37 @@
function input(msg){ function input(msg){
// manhattan: // manhattan:
var eve = this, cat = eve.as, root = cat.root, gun = msg.$ || (msg.$ = cat.$), at = (gun||'')._ || empty, tmp = msg.put||'', soul = tmp['#'], key = tmp['.'], change = tmp['=']||tmp[':'], state = tmp['>'] || -Infinity, link, sat; var eve = this, cat = eve.as, root = cat.root, gun = msg.$ || (msg.$ = cat.$), at = (gun||'')._ || empty, tmp = msg.put||'', soul = tmp['#'], key = tmp['.'], change = tmp['=']||tmp[':'], state = tmp['>'] || -Infinity, link, sat;
if(tmp && tmp._ && tmp._['#']){ // convert from old format if(tmp && tmp._ && tmp._['#']){ // convert from old format
return setTimeout.each(Object.keys(tmp).sort(), function(k){ return setTimeout.each(Object.keys(tmp).sort(), function(k){
if('_' == k || !(state = state_is(tmp, k))){ return } if('_' == k || !(state = state_is(tmp, k))){ return }
cat.on('in', {put: {'#': tmp._['#'], '.': k, ':': tmp[k], '>': state}}); cat.on('in', {put: {'#': tmp._['#'], '.': k, ':': tmp[k], '>': state}});
}); });
} }
//console.log("howza?", cat.soul, cat.has, cat.id, (msg.seen||'')[cat.id]);
if((msg.seen||'')[cat.id]){ return } (msg.seen || (msg.seen = function(){}))[cat.id] = cat; if((msg.seen||'')[cat.id]){ return } (msg.seen || (msg.seen = function(){}))[cat.id] = cat;
if(cat.ask && key){ if(cat.ask && key){
if(cat.soul && state >= state_is(root.graph[soul], key)){ // faster than HAM if(cat.soul && state >= state_is(root.graph[soul], key)){ // faster than HAM
cat.put = state_ify(cat.put, key, state, change, soul); cat.put = state_ify(cat.put, key, state, change, soul);
} }
if(cat.has && state >= state_is(root.graph[soul], key)){ // faster than HAM if(cat.has && at.soul){
cat.put = at.put;
} else
if(cat.has && at === cat && state >= state_is(root.graph[soul], key)){ // faster than HAM
cat.put = change; cat.put = change;
} }
} }
if(cat.soul && at === cat){ // (1) we're a root node chain.
} else
if(cat.has && at === cat){ // (2)
} else
if(cat.has && at.soul){ // (3) we're a proxy for a root node.
Object.keys(msg).forEach(function(k){ tmp[k] = msg[k] }, tmp = {});
tmp.get = cat.has; tmp.$ = cat.$; tmp.$$ = at; msg = tmp;
} else {}
eve.to.next(msg); // 1st API job is to call all the listeners. eve.to.next(msg); // 1st API job is to call all the listeners.
//if(cat !== at && !(at = at.next[key])){ atnextkey; return }
//if(!(state < at.state)){ at.state = state } // invert logic cause NaN falses. // TODO: BUG: prevent async errors
setTimeout.each(Object.keys(cat.act||''), function(act){ // 1st API job is to call all chain listeners. setTimeout.each(Object.keys(cat.act||''), function(act){ // 1st API job is to call all chain listeners.
(act = cat.act[act]) && act(msg); (act = cat.act[act]) && act(msg);
},0,99); },0,99);
@ -707,6 +719,7 @@
if(!(lat = cat.echo[lat])){ return } if(!(lat = cat.echo[lat])){ return }
lat.on('in', msg); lat.on('in', msg);
},0,99); },0,99);
if(u === change){ // 1st edge case: If we have a brand new database, no data will be found. if(u === change){ // 1st edge case: If we have a brand new database, no data will be found.
cat.put = u; // empty out the cache if, for example, alice's car's color no longer exists (relative to alice) if alice no longer has a car. cat.put = u; // empty out the cache if, for example, alice's car's color no longer exists (relative to alice) if alice no longer has a car.
delete cat.link; // TODO: Empty out links, maps, echos, acks/asks, etc.? delete cat.link; // TODO: Empty out links, maps, echos, acks/asks, etc.?
@ -716,7 +729,6 @@
},0,99); },0,99);
return; return;
} }
if(at.soul){ if(at.soul){
if((sat = cat.next) && (sat = sat[key])){ if((sat = cat.next) && (sat = sat[key])){
tmp = {}; Object.keys(msg).forEach(function(k){ tmp[k] = msg[k] }); tmp.$ = sat.$; tmp = {}; Object.keys(msg).forEach(function(k){ tmp[k] = msg[k] }); tmp.$ = sat.$;
@ -724,7 +736,7 @@
} }
} }
if('string' == typeof (link = valid(change))){ if('string' == typeof (link = valid(change))){
if(cat.has){ if((cat.has && at === cat)){
sat = root.$.get(cat.link = link)._; // grab what we're linking to. sat = root.$.get(cat.link = link)._; // grab what we're linking to.
if((sat.echo || (sat.echo = {}))[cat.id]){ return } // we've already added ourself. if((sat.echo || (sat.echo = {}))[cat.id]){ return } // we've already added ourself.
sat.echo[cat.id] = cat; // add ourselves to it. sat.echo[cat.id] = cat; // add ourselves to it.
@ -960,8 +972,8 @@
} }
function ack(msg, ev){ function ack(msg, ev){
// manhattan: // manhattan:
var as = this.as, at = as.$._; var as = this.as, at = as.$._, get = as.get||'', tmp = (msg.put||'')[get['#']]||'';
if(Object.empty(msg.put, '_')){ if(!msg.put || ('string' == typeof get['.'] && u === tmp[at.get])){
if(at.put !== u){ return } if(at.put !== u){ return }
at.on('in', { at.on('in', {
get: at.get, get: at.get,
@ -1139,44 +1151,47 @@
;USE(function(module){ ;USE(function(module){
var Gun = USE('./root'); var Gun = USE('./root');
Gun.chain.put = function(data, cb, as){ // I rewrote it :) Gun.chain.put = function(data, cb, as){ // I rewrote it :)
var gun = this, at = gun._; var gun = this, at = gun._, root = at.root;
as = as || {}; as = as || {};
(root.stun = root.stun || {})[at.id] = (as.stun = as.stun || {});
as.ack = as.ack || cb; as.ack = as.ack || cb;
as.via = as.via || gun; as.via = as.via || gun;
as.data = as.data || data; as.data = as.data || data;
as.soul || (as.soul = at.soul || ('string' == typeof cb && cb)); as.soul || (as.soul = at.soul || ('string' == typeof cb && cb));
var s = as.state = as.state || Gun.state(); var s = as.state = as.state || Gun.state();
if(!as.soul){ return get(as), gun } if(!as.soul){ return get(as), gun }
as.$ = at.root.$.get(as.soul); as.$ = root.$.get(as.soul);
as.todo = [{it: as.data, ref: as.$}]; as.todo = [{it: as.data, ref: as.$}];
as.turn = as.turn || turn; as.turn = as.turn || turn;
as.ran = as.ran || ran; as.ran = as.ran || ran;
root.stun._ = (root.stun._ || 0) + 1;
var S = +new Date; var S = +new Date;
(function walk(){ (function walk(){
var to = as.todo, at = to.pop(), d = at.it, v, k, cat, tmp; var to = as.todo, at = to.pop(), d = at.it, v, k, cat, tmp, g;
(tmp = at.ref) && (root.stun[tmp._.id] = as.stun); // stun
if(tmp = at.todo){ if(tmp = at.todo){
k = tmp.pop(); d = d[k]; k = tmp.pop(); d = d[k];
if(tmp.length){ to.push(at) } if(tmp.length){ to.push(at) }
} }
if(!(v = valid(d))){ if(!(v = valid(d)) && !(g = Gun.is(d))){
if(!Object.plain(d)){ (as.ack||noop).call(as, as.out = {err: Gun.log("Invalid data, " + typeof d + " at " + (as.path||[]).join('.'))}); return } if(!Object.plain(d)){ (as.ack||noop).call(as, as.out = {err: Gun.log("Invalid data, " + typeof d + " at " + (as.path||[]).join('.'))}); return }
var seen = as.seen || (as.seen = []), i = seen.length; var seen = as.seen || (as.seen = []), i = seen.length;
while(i--){ if(d === (tmp = seen[i]).it){ v = d = tmp.link; break } } while(i--){ if(d === (tmp = seen[i]).it){ v = d = tmp.link; break } }
} }
if(k && v){ at.node = state_ify(at.node, k, s, d) } // handle soul later. if(k && v){ at.node = state_ify(at.node, k, s, d) } // handle soul later.
else { else {
as.seen.push(cat = {it: d, link: {}, todo: Object.keys(d).sort().reverse()}); as.seen.push(cat = {it: d, link: {}, todo: g? [] : Object.keys(d).sort().reverse()});
at.node = state_ify(at.node, k, s, cat.link); at.node = state_ify(at.node, k, s, cat.link);
to.push(cat); !g && to.push(cat);
// --------------- // ---------------
var id = seen.length; var id = as.seen.length;
(as.wait || (as.wait = {}))[id] = ''; (as.wait || (as.wait = {}))[id] = '';
tmp = (cat.ref = (k? at.ref.get(k) : at.ref))._; tmp = (cat.ref = (g? d : k? at.ref.get(k) : at.ref))._;
(tmp = tmp.soul || tmp.link || tmp.dub)? resolve({soul: tmp}) : cat.ref.get(resolve); (tmp = tmp.soul || tmp.link || tmp.dub)? resolve({soul: tmp}) : cat.ref.get(resolve, {stun: 0});
function resolve(msg, eve){ function resolve(msg, eve){
if(eve){ eve.off(); eve.rid(msg) } // TODO: Too early! Check all peers ack not found. if(eve){ eve.off(); eve.rid(msg) } // TODO: Too early! Check all peers ack not found.
console.log("PUT get soul::::::::::::::", msg.soul || msg.get, msg.put, msg, msg.$ && msg.$._.id);
var soul = msg.soul || ((tmp = msg.put) && (tmp = tmp._) && (tmp = tmp['#'])) || ((tmp = msg.put) && (tmp = tmp['='] || tmp[':']) && tmp['#']) || ((tmp = msg.put) && tmp['#']); var soul = msg.soul || ((tmp = msg.put) && (tmp = tmp._) && (tmp = tmp['#'])) || ((tmp = msg.put) && (tmp = tmp['='] || tmp[':']) && tmp['#']) || ((tmp = msg.put) && tmp['#']);
(tmp = msg.$) && (root.stun[tmp._.id] = as.stun); // stun
if(!soul){ if(!soul){
soul = []; soul = [];
msg.$.back(function(at){ msg.$.back(function(at){
@ -1185,7 +1200,8 @@
}); });
soul = msg.$._.dub = soul.reverse().join('/'); soul = msg.$._.dub = soul.reverse().join('/');
} }
((as.graph || (as.graph = {}))[cat.link['#'] = soul] = (cat.node || (cat.node = {_:{}})))._['#'] = soul; cat.link['#'] = soul;
!g && (((as.graph || (as.graph = {}))[soul] = (cat.node || (cat.node = {_:{}})))._['#'] = soul);
delete as.wait[id]; delete as.wait[id];
as.ran(as); as.ran(as);
}; };
@ -1199,15 +1215,18 @@
function ran(as){ function ran(as){
if(as.todo.length || !Object.empty(as.wait)){ return } if(as.todo.length || !Object.empty(as.wait)){ return }
delete as.via._.stun;
var cat = (as.$.back(-1)._), ask = cat.ask(function(ack){ var cat = (as.$.back(-1)._), ask = cat.ask(function(ack){
cat.root.on('ack', ack); cat.root.on('ack', ack);
if(ack.err){ Gun.log(ack) } if(ack.err){ Gun.log(ack) }
if(++acks > (as.acks || 0)){ this.off() } // Adjustable ACKs! Only 1 by default. if(++acks > (as.acks || 0)){ this.off() } // Adjustable ACKs! Only 1 by default.
if(!as.ack){ return } if(!as.ack){ return }
as.ack(ack, this); as.ack(ack, this);
}, as.opt), acks = 0; }, as.opt), acks = 0, stun = as.stun;
(as.via._).on('out', {put: as.out = as.graph, opt: as.opt, '#': ask}); if((tmp = cat.root.stun) && --tmp._ === 0){ delete cat.root.stun }
(tmp = function(){ // this is not official yet, but quick solution to hack in for now.
setTimeout.each(Object.keys(stun||''), function(cb){if(cb = stun[cb]){cb()}});
}).hatch = tmp; // this is not official yet ^
(as.via._).on('out', {put: as.out = as.graph, opt: as.opt, '#': ask, _: tmp});
} }
function get(as){ function get(as){
@ -1236,26 +1255,8 @@
;USE(function(module){ ;USE(function(module){
var Gun = USE('./index'); var Gun = USE('./index');
Gun.chain.on = function(tag, arg, eas, as){
var gun = this, at = gun._, tmp, act, off;
if(typeof tag === 'string'){
if(!arg){ return at.on(tag) }
act = at.on(tag, arg, eas || at, as);
if(eas && eas.$){
(eas.subs || (eas.subs = [])).push(act);
}
return gun;
}
var opt = arg;
opt = (true === opt)? {change: true} : opt || {};
opt.at = at;
opt.ok = tag;
//opt.last = {};
gun.get(ok, opt); // TODO: PERF! Event listener leak!!!?
return gun;
}
Gun.chain.on = function(tag, arg, eas, as){ // don't rewrite! Gun.chain.on = function(tag, arg, eas, as){ // don't rewrite!
var gun = this, cat = gun._, act, off, id, tmp; var gun = this, cat = gun._, root = cat.root, act, off, id, tmp;
if(typeof tag === 'string'){ if(typeof tag === 'string'){
if(!arg){ return cat.on(tag) } if(!arg){ return cat.on(tag) }
act = cat.on(tag, arg, eas || cat, as); act = cat.on(tag, arg, eas || cat, as);
@ -1270,71 +1271,30 @@
opt.ok = tag; opt.ok = tag;
//opt.last = {}; //opt.last = {};
//gun.get(ok, opt); // TODO: PERF! Event listener leak!!!? //gun.get(ok, opt); // TODO: PERF! Event listener leak!!!?
console.only(1, '.on(');
((cat.act||(cat.act={}))[id = tmp||String.random(7)] = function one(msg, eve){ ((cat.act||(cat.act={}))[id = tmp||String.random(7)] = function one(msg, eve){
console.log("(((ON", msg); var at = msg.$._, data = at.put, tmp;
if(opt.stun===u && (tmp = root.stun) && (tmp = tmp[at.id] || tmp[at.back.id])){ // Remember! If you port this into `.get(cb` make sure you allow stun:0 skip option for `.put(`.
tmp[id] = function(){one(msg,eve)};
return;
}
// call:
if(opt.as){
opt.ok.call(opt.as, msg, eve);
} else {
opt.ok.call(at.$, data, msg.get || at.get, msg, eve);
}
}).at = cat; }).at = cat;
cat.on('out', {get: {}}); cat.on('out', {get: {}});
return gun; return gun;
} }
function ok(msg, ev){ var opt = this;
var gun = msg.$, at = (gun||{})._ || {}, data = at.put || msg.put, cat = opt.at, tmp;
if(u === data){
return;
}
if(tmp = msg.$$){
tmp = (msg.$$._);
if(u === tmp.put){
return;
}
data = tmp.put;
}
if(opt.change){ // TODO: BUG? Move above the undef checks?
data = msg.put;
}
// DEDUPLICATE // TODO: NEEDS WORK! BAD PROTOTYPE
//if(tmp.put === data && tmp.get === id && !Gun.node.soul(data)){ return }
//tmp.put = data;
//tmp.get = id;
// DEDUPLICATE // TODO: NEEDS WORK! BAD PROTOTYPE
//at.last = data;
if(opt.as){
opt.ok.call(opt.as, msg, ev);
} else {
opt.ok.call(gun, data, msg.get, msg, ev);
}
}
// Rules: // Rules:
// 1. If cached, should be fast, but not read while write. // 1. If cached, should be fast, but not read while write.
// 2. Should not retrigger other listeners, should get triggered even if nothing found. // 2. Should not retrigger other listeners, should get triggered even if nothing found.
// 3. If the same callback passed to many different once chains, each should resolve - an unsubscribe from the same callback should not effect the state of the other resolving chains, if you do want to cancel them all early you should mutate the callback itself with a flag & check for it at top of callback // 3. If the same callback passed to many different once chains, each should resolve - an unsubscribe from the same callback should not effect the state of the other resolving chains, if you do want to cancel them all early you should mutate the callback itself with a flag & check for it at top of callback
Gun.chain.once = function(cb, opt){
var gun = this, at = gun._, data = at.put;
if(0 < at.ack && u !== data){
(cb || noop).call(gun, data, at.get);
return gun;
}
if(cb){
(opt = opt || {}).ok = cb;
opt.at = at;
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 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._);
});
return chain;
}
return gun;
}
Gun.chain.once = function(cb, opt){ opt = opt || {}; // avoid rewriting Gun.chain.once = function(cb, opt){ opt = opt || {}; // avoid rewriting
var gun = this, cat = gun._, root = cat.root, data = cat.put, id, tmp; var gun = this, cat = gun._, root = cat.root, data = cat.put, id, one, tmp;
//(root.act[tmp = ++root.acts] = cat.act[tmp] = function(msg, eve, to){ //(root.act[tmp = ++root.acts] = cat.act[tmp] = function(msg, eve, to){
((cat.act||(cat.act={}))[id = tmp||String.random(7)] = function one(msg, eve, to){ ((cat.act||(cat.act={}))[id = tmp||String.random(7)] = function(msg, eve, to){
if(!(tmp = msg.put)){ if(!(tmp = msg.put)){
console.log('not found'); console.log('not found');
return; return;
@ -1343,54 +1303,20 @@
// probably already getting it. // probably already getting it.
return; return;
} }
var at = msg.$._; var at = msg.$._, one = (at.one||(at.one={}));
if(!(tmp = at.soul || at.link)){ if('' === one[id]){ return }
delete cat.act[id]; // TODO: BUG? What about map? if(!(tmp = at.soul || at.link)){ once(); return }
cb.call(at.$, at.put, console.log("fix once key!")) clearTimeout(one[id]); one[id] = setTimeout(once, opt.wait||99);
return; function once(){
one[id] = ''; if(cat.soul || cat.has){ delete cat.act[id] }
if(u === (tmp = ((msg.$||'')._||'').put)){ tmp = ((msg.$$||'')._||'').put }
cb.call(at.$, tmp, msg.get || at.get);
} }
clearTimeout(one.to);
one.to = setTimeout(function(){
delete cat.act[id]; // TODO: BUG? What about map?
cb.call(at.$, at.put, console.log("fix once key"));
}, opt.wait||99);
}).at = cat; }).at = cat;
cat.on('out', {get: {}}); cat.on('out', {get: {}});
return gun; return gun;
} }
// You have a list, it contains Bob's uncle and Sally's husband.
// How many times should your callback fire? Once, or twice? Sally's husband is Bob's uncle.
function val(msg, eve, to){
if(!msg.$){ eve.off(); return }
var opt = this.as, cat = opt.at, gun = msg.$, at = gun._, data = at.put || msg.put, link, tmp;
if(at.has && !at.link && 'string' != typeof Gun.valid(data) && !((data||'')._||'')['#']){
//console.log(".once primitive!", at.link, data);
}
if(tmp = msg.$$){
link = tmp = (msg.$$._);
if(u !== link.put){
data = link.put;
}
}
if((tmp = eve.wait) && (tmp = tmp[at.id])){ clearTimeout(tmp) }
eve.ack = (eve.ack||0)+1;
// TODO: super should not be in core code, bring AXE up into core instead to fix?
if(!to && u === data && !at.root.opt.super && eve.ack <= (opt.acks || Object.keys(at.root.opt.peers).length)){ return }
if((!to && (u === data || at.soul || at.link || (link && !(0 < link.ack))))
|| (u === data && !at.root.opt.super && (tmp = Object.keys(at.root.opt.peers).length) && (!to && (link||at).ack < tmp))){
tmp = (eve.wait = {})[at.id] = setTimeout(function(){
console.log("TIME!!!!");
val.call({as:opt}, msg, eve, tmp || 1);
}, opt.wait || 99);
return;
}
//if(link && u === link.put && (tmp = rel.is(data))){ data = Gun.node.ify({}, tmp) }
if(link && u === link.put && 'string' == typeof (tmp = Gun.valid(data))){ (data = {_:{},'>':{}})['#'] = tmp; }
eve.rid? eve.rid(msg) : eve.off();
opt.ok.call(gun || opt.$, data, msg.get);
}
Gun.chain.off = function(){ Gun.chain.off = function(){
// make off more aggressive. Warning, it might backfire! // make off more aggressive. Warning, it might backfire!
var gun = this, at = gun._, tmp; var gun = this, at = gun._, tmp;
@ -1439,7 +1365,7 @@
if(chain = cat.each){ return chain } if(chain = cat.each){ return chain }
cat.each = chain = gun.chain(); cat.each = chain = gun.chain();
chain._.nix = gun.back('nix'); chain._.nix = gun.back('nix');
gun.on('in', map, chain._); gun.on(map, {as: chain._});
return chain; return chain;
} }
Gun.log.once("mapfn", "Map functions are experimental, their behavior and API may change moving forward. Please play with it and report bugs and ideas on how to improve it."); Gun.log.once("mapfn", "Map functions are experimental, their behavior and API may change moving forward. Please play with it and report bugs and ideas on how to improve it.");
@ -1454,11 +1380,19 @@
return chain; return chain;
} }
function map(msg){ function map(msg){
if(!msg.put || Gun.valid(msg.put)){ return this.to.next(msg) } if(!msg.put || Gun.valid(msg.put)){ this.to.next(msg); return }
if(this.as.nix){ this.off() } // TODO: Ugly hack! if(this.as.nix){ this.off() } // TODO: Ugly hack!
Object.keys(msg.put).forEach(each, {at: this.as, msg: msg}); Object.keys(msg.put).forEach(each, {at: this.as, msg: msg});
this.to.next(msg); this.to.next(msg);
} }
function map(msg){
var cat = this, gun = msg.$, put = msg.put;
if(!put){ return }
var soul = put['#'], k = put['.'], val = put['=']||put[':'];
// TODO: lex match!
//console.log("let's go!!!!!!!!!!!!!!!!!", k, gun._.id, gun._.get);
((tmp = gun.get(k)._).echo || (tmp.echo = {}))[cat.id] = tmp.echo[cat.id] || cat;
}
function each(k){ function each(k){
if('_' === k){ return } if('_' === k){ return }
var msg = this.msg, v = msg.put[k], gun = msg.$, at = gun._, cat = this.at, tmp = at.lex; var msg = this.msg, v = msg.put[k], gun = msg.$, at = gun._, cat = this.at, tmp = at.lex;