build/unbuild

This commit is contained in:
Mark Nadal 2020-05-01 19:13:13 -07:00
parent 51be588a74
commit 985cfa2b4d
27 changed files with 493 additions and 238 deletions

2
gun.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,9 +1,9 @@
if(typeof btoa === "undefined"){
if(typeof Buffer === "undefined") {
root.Buffer = require("buffer").Buffer
global.Buffer = require("buffer").Buffer
}
root.btoa = function (data) { return Buffer.from(data, "binary").toString("base64"); };
root.atob = function (data) { return Buffer.from(data, "base64").toString("binary"); };
global.btoa = function (data) { return Buffer.from(data, "binary").toString("base64"); };
global.atob = function (data) { return Buffer.from(data, "base64").toString("binary"); };
}

View File

@ -226,6 +226,7 @@
}
// If authenticated user wants to delete his/her account, let's support it!
User.prototype.delete = async function(alias, pass, cb){
console.log("user.delete() IS DEPRECATED AND WILL BE MOVED TO A MODULE!!!");
var gun = this, root = gun.back(-1), user = gun.back('user');
try {
user.auth(alias, pass, function(ack){
@ -267,6 +268,7 @@
return gun;
}
User.prototype.alive = async function(){
console.log("user.alive() IS DEPRECATED!!!");
const gunRoot = this.back(-1)
try {
// All is good. Should we do something more with actual recalled data?
@ -286,25 +288,32 @@
console.log(ctx, ev)
})
}
user.get('trust').get(path).put(theirPubkey);
// do a lookup on this gun chain directly (that gets bob's copy of the data)
// do a lookup on the metadata trust table for this path (that gets all the pubkeys allowed to write on this path)
// do a lookup on each of those pubKeys ON the path (to get the collab data "layers")
// THEN you perform Jachen's mix operation
// and return the result of that to...
}
User.prototype.grant = function(to, cb){
console.log("`.grant` API MAY BE DELETED OR CHANGED OR RENAMED, DO NOT USE!");
var gun = this, user = gun.back(-1).user(), pair = user.pair(), path = '';
var gun = this, user = gun.back(-1).user(), pair = user._.sea, path = '';
gun.back(function(at){ if(at.is){ return } path += (at.get||'') });
(async function(){
var enc, sec = await user.get('trust').get(pair.pub).get(path).then();
var enc, sec = await user.get('grant').get(pair.pub).get(path).then();
sec = await SEA.decrypt(sec, pair);
if(!sec){
sec = SEA.random(16).toString();
enc = await SEA.encrypt(sec, pair);
user.get('trust').get(pair.pub).get(path).put(enc);
user.get('grant').get(pair.pub).get(path).put(enc);
}
var pub = to.get('pub').then();
var epub = to.get('epub').then();
pub = await pub; epub = await epub;
var dh = await SEA.secret(epub, pair);
enc = await SEA.encrypt(sec, dh);
user.get('trust').get(pub).get(path).put(enc, cb);
user.get('grant').get(pub).get(path).put(enc, cb);
}());
return gun;
}

View File

@ -1,15 +1,15 @@
const SEA = require('./sea')
const Gun = SEA.Gun;
var SEA = require('./sea')
var Gun = SEA.Gun;
// After we have a GUN extension to make user registration/login easy, we then need to handle everything else.
// We do this with a GUN adapter, we first listen to when a gun instance is created (and when its options change)
Gun.on('opt', function(at){
if(!at.sea){ // only add SEA once per instance, on the "at" context.
at.sea = {own: {}};
at.on('in', security, at); // now listen to all input data, acting as a firewall.
at.on('out', signature, at); // and output listeners, to encrypt outgoing data.
at.on('node', each, at);
//at.on('in', security, at); // now listen to all input data, acting as a firewall.
//at.on('out', signature, at); // and output listeners, to encrypt outgoing data.
at.on('put', check, at);
}
this.to.next(at); // make sure to call the "next" middleware adapter.
});
@ -58,6 +58,94 @@
security.call(this, msg);
}
var u;
function check(msg){ // REVISE / IMPROVE, NO NEED TO PASS MSG/EVE EACH SUB?
var eve = this, at = eve.as, put = msg.put, soul = put['#'], key = put['.'], val = put[':'], state = put['>'], id = msg['#'], tmp;
if(!soul || !key){ return }
if((msg._||'').faith && (at.opt||'').faith && 'function' == typeof msg._){
SEA.verify(SEA.opt.pack(put), false, function(data){ // this is synchronous if false
put['='] = SEA.opt.unpack(data);
eve.to.next(msg);
});
return
}
var no = function(why){ at.on('in', {'@': id, err: why}) };
//var no = function(why){ msg.ack(why) };
(msg._||'').DBG && ((msg._||'').DBG.c = +new Date);
if(0 <= soul.indexOf('<?')){ // special case for "do not sync data X old"
// 'a~pub.key/b<?9'
tmp = parseFloat(soul.split('<?')[1]||'');
if(tmp && (state < (Gun.state() - (tmp * 1000)))){ // sec to ms
(tmp = msg._) && (tmp = tmp.lot) && (tmp.more--); // THIS IS BAD CODE! It assumes GUN internals do something that will probably change in future, but hacking in now.
return; // omit!
}
}
if('~@' === soul){ // special case for shared system data, the list of aliases.
check.alias(eve, msg, val, key, soul, at, no); return;
}
if('~@' === soul.slice(0,2)){ // special case for shared system data, the list of public keys for an alias.
check.pubs(eve, msg, val, key, soul, at, no); return;
}
//if('~' === soul.slice(0,1) && 2 === (tmp = soul.slice(1)).split('.').length){ // special case, account data for a public key.
if(tmp = SEA.opt.pub(soul)){ // special case, account data for a public key.
check.pub(eve, msg, val, key, soul, at, no, at.user||'', tmp); return;
}
if(0 <= soul.indexOf('#')){ // special case for content addressing immutable hashed data.
check.hash(eve, msg, val, key, soul, at, no); return;
}
check.any(eve, msg, val, key, soul, at, no, at.user||''); return;
eve.to.next(msg); // not handled
}
check.hash = function(eve, msg, val, key, soul, at, no){
SEA.work(val, null, function(data){
if(data && data === key.split('#').slice(-1)[0]){ return eve.to.next(msg) }
no("Data hash not same as hash!");
}, {name: 'SHA-256'});
}
check.alias = function(eve, msg, val, key, soul, at, no){ // Example: {_:#~@, ~@alice: {#~@alice}}
if(!val){ return no("Data must exist!") } // data MUST exist
if('~@'+key === link_is(val)){ return eve.to.next(msg) } // in fact, it must be EXACTLY equal to itself
no("Alias not same!"); // if it isn't, reject.
};
check.pubs = function(eve, msg, val, key, soul, at, no){ // Example: {_:#~@alice, ~asdf: {#~asdf}}
if(!val){ return no("Alias must exist!") } // data MUST exist
if(key === link_is(val)){ return eve.to.next(msg) } // and the ID must be EXACTLY equal to its property
no("Alias not same!"); // that way nobody can tamper with the list of public keys.
};
check.pub = function(eve, msg, val, key, soul, at, no, user, pub){ var tmp; // Example: {_:#~asdf, hello:'world'~fdsa}}
if('pub' === key && '~'+pub === soul){
if(val === pub){ return eve.to.next(msg) } // the account MUST match `pub` property that equals the ID of the public key.
return no("Account not same!");
}
if((tmp = user.is) && pub === tmp.pub){
SEA.sign(SEA.opt.pack(msg.put), (user._).sea, function(data){
if(u === data){ return no(SEA.err || 'Signature fail.') }
if(tmp = link_is(val)){ (at.sea.own[tmp] = at.sea.own[tmp] || {})[pub] = 1 }
msg.put[':'] = JSON.stringify({':': tmp = SEA.opt.unpack(data.m), '~': data.s});
msg.put['='] = tmp;
eve.to.next(msg);
}, {raw: 1});
return;
}
SEA.verify(SEA.opt.pack(msg.put), pub, function(data){ var tmp;
data = SEA.opt.unpack(data);
if(u === data){ return no("Unverified data.") } // make sure the signature matches the account it claims to be on. // reject any updates that are signed with a mismatched account.
if((tmp = link_is(data)) && pub === SEA.opt.pub(tmp)){ (at.sea.own[tmp] = at.sea.own[tmp] || {})[pub] = 1 }
msg.put['='] = data;
eve.to.next(msg);
});
};
check.any = function(eve, msg, val, key, soul, at, no, user){ var tmp, pub;
if(at.opt.secure){ return no("Soul missing public key at '" + key + "'.") }
// TODO: Ask community if should auto-sign non user-graph data.
at.on('secure', function(msg){ this.off();
if(!at.opt.secure){ return eve.to.next(msg) }
no("Data cannot be changed.");
}).on.on('secure', msg);
return;
}
var link_is = Gun.val.link.is, 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.
// This is broken down into some pretty clear edge cases, let's go over them:
@ -83,6 +171,11 @@
}
}
if(msg.put){
/*
NOTICE: THIS IS OLD AND GETTING DEPRECATED.
ANY SECURITY CHANGES SHOULD HAPPEN ABOVE FIRST
THEN PORTED TO HERE.
*/
// potentially parallel async operations!!!
var check = {}, each = {}, u;
each.node = function(node, soul){
@ -207,12 +300,14 @@
}
to.next(msg); // pass forward any data we do not know how to handle or process (this allows custom security protocols).
}
var pubcut = /[^\w_-]/; // anything not alphanumeric or _ -
SEA.opt.pub = function(s){
if(!s){ return }
s = s.split('~');
if(!s || !(s = s[1])){ return }
s = s.split('.');
if(!s || 2 > s.length){ return }
s = s.split(pubcut).slice(0,2);
if(!s || 2 != s.length){ return }
if('@' === (s[0]||'')[0]){ return }
s = s.slice(0,2).join('.');
return s;
}
@ -221,16 +316,18 @@
}
SEA.opt.pack = function(d,k, n,s){ // pack for verifying
if(SEA.opt.check(d)){ return d }
var meta = (Gun.obj.ify(d)||noop), sig = meta['~'];
return sig? {m: {'#':s,'.':k,':':meta[':'],'>':Gun.state.is(n, k)}, s: sig} : d;
var meta = (Gun.obj.ify((d && d[':'])||d)||''), sig = meta['~'];
return sig? {m: {'#':s||d['#'],'.':k||d['.'],':':meta[':'],'>':d['>']||Gun.state.is(n, k)}, s: sig} : d;
}
var O = SEA.opt;
SEA.opt.unpack = function(d, k, n){ var tmp;
if(u === d){ return }
if(d && (u !== (tmp = d[':']))){ return tmp }
k = k || O.fall_key; if(!n && O.fall_val){ n = {}; n[k] = O.fall_val }
if(!k || !n){ return }
if(d === n[k]){ return d }
if(!SEA.opt.check(n[k])){ return d }
var soul = Gun.node.soul(n), s = Gun.state.is(n, k);
var soul = Gun.node.soul(n) || O.fall_soul, s = Gun.state.is(n, k) || O.fall_state;
if(d && 4 === d.length && soul === d[0] && k === d[1] && fl(s) === fl(d[3])){
return d[2];
}
@ -242,6 +339,7 @@
var noop = function(){}, u;
var fl = Math.floor; // TODO: Still need to fix inconsistent state issue.
var rel_is = Gun.val.rel.is;
var obj_ify = Gun.obj.ify;
// TODO: Potential bug? If pub/priv key starts with `-`? IDK how possible.

View File

@ -11,6 +11,6 @@
if(SEA.window = module.window){ SEA.window.SEA = SEA }
try{ if(typeof common !== "undefined"){ common.exports = SEA } }catch(e){}
try{ if(typeof MODULE !== "undefined"){ MODULE.exports = SEA } }catch(e){}
module.exports = SEA;

View File

@ -1,13 +1,13 @@
var shim = require('./shim');
// Practical examples about usage found from ./test/common.js
// Practical examples about usage found in tests.
var SEA = require('./root');
SEA.work = require('./work');
SEA.sign = require('./sign');
SEA.verify = require('./verify');
SEA.encrypt = require('./encrypt');
SEA.decrypt = require('./decrypt');
SEA.aeskey = require('./aeskey');
SEA.opt.aeskey = require('./aeskey'); // not official!
SEA.random = SEA.random || shim.random;
@ -49,7 +49,7 @@
// But all other behavior needs to be equally easy, like opinionated ways of
// Adding friends (trusted public keys), sending private messages, etc.
// Cheers! Tell me what you think.
var Gun = (SEA.window||{}).Gun || require((typeof common == "undefined"?'.':'')+'./gun', 1);
var Gun = (SEA.window||{}).Gun || require((typeof MODULE == "undefined"?'.':'')+'./gun', 1);
Gun.SEA = SEA;
SEA.GUN = SEA.Gun = Gun;

View File

@ -26,8 +26,8 @@
const isocrypto = require('isomorphic-webcrypto');
api.ossl = api.subtle = isocrypto.subtle;
}catch(e){
console.log("node-webcrypto-ossl and text-encoding may not be included by default, please add it to your package.json!");
OSSL_WEBCRYPTO_OR_TEXT_ENCODING_NOT_INSTALLED;
console.log("text-encoding and @peculiar/webcrypto may not be included by default, please add it to your package.json!");
TEXT_ENCODING_OR_PECULIAR_WEBCRYPTO_NOT_INSTALLED;
}}
module.exports = api

View File

@ -20,7 +20,7 @@
at.opt.uuid = function(cb){
var id = uuid(), pub = root.user;
if(!pub || !(pub = pub.is) || !(pub = pub.pub)){ return id }
id = id + '~' + pub + '.';
id = id + '~' + pub + '/';
if(cb && cb.call){ cb(null, id) }
return id;
}

View File

@ -15,7 +15,7 @@
opt = opt || {};
// SEA.I // verify is free! Requires no user permission.
var pub = pair.pub || pair;
var key = SEA.opt.slow_leak? await SEA.opt.slow_leak(pub) : await (shim.ossl || shim.subtle).importKey('jwk', jwk, {name: 'ECDSA', namedCurve: 'P-256'}, false, ['verify']);
var key = SEA.opt.slow_leak? await SEA.opt.slow_leak(pub) : await (shim.ossl || shim.subtle).importKey('jwk', S.jwk(pub), {name: 'ECDSA', namedCurve: 'P-256'}, false, ['verify']);
var hash = await sha(json.m);
var buf, sig, check, tmp; try{
buf = shim.Buffer.from(json.s, opt.encode || 'base64'); // NEW DEFAULT!
@ -50,9 +50,11 @@
return knownKeys[pair];
};
var O = SEA.opt;
SEA.opt.fall_verify = async function(data, pair, cb, opt, f){
if(f === SEA.opt.fallback){ throw "Signature did not match" } f = f || 1;
var tmp = data||'';
data = SEA.opt.unpack(data) || data;
var json = S.parse(data), pub = pair.pub || pair, key = await SEA.opt.slow_leak(pub);
var hash = (f <= SEA.opt.fallback)? shim.Buffer.from(await shim.subtle.digest({name: 'SHA-256'}, new shim.TextEncoder().encode(S.parse(json.m)))) : await sha(json.m); // this line is old bad buggy code but necessary for old compatibility.
var buf; var sig; var check; try{
@ -67,6 +69,7 @@
if(!check){ throw "Signature did not match." }
}
var r = check? S.parse(json.m) : u;
O.fall_soul = tmp['#']; O.fall_key = tmp['.']; O.fall_val = data; O.fall_state = tmp['>'];
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
return r;
}

View File

@ -12,13 +12,13 @@
cb = salt;
salt = u;
}
salt = salt || shim.random(9);
data = (typeof data == 'string')? data : JSON.stringify(data);
if('sha' === (opt.name||'').toLowerCase().slice(0,3)){
var rsha = shim.Buffer.from(await sha(data, opt.name), 'binary').toString(opt.encode || 'base64')
if(cb){ try{ cb(rsha) }catch(e){console.log(e)} }
return rsha;
}
salt = salt || shim.random(9);
var key = await (shim.ossl || shim.subtle).importKey('raw', new shim.TextEncoder().encode(data), {name: opt.name || 'PBKDF2'}, false, ['deriveBits']);
var work = await (shim.ossl || shim.subtle).deriveBits({
name: opt.name || 'PBKDF2',

View File

@ -36,7 +36,7 @@ Gun.on('create', function(root){
}
root.on('out', function(msg){
if(msg.lS){ return } // TODO: for IndexedDB and others, shouldn't send to peers ACKs to our own GETs.
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);
@ -86,10 +86,11 @@ Gun.on('create', function(root){
var lS = function(){}, u;
root.on('localStorage', disk); // NON-STANDARD EVENT!
root.on('put', function(at){
this.to.next(at);
Gun.graph.is(at.put, null, map);
if(!at['@']){ acks[at['#']] = true; } // only ack non-acks.
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();
@ -110,7 +111,7 @@ Gun.on('create', function(root){
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), how: 'lS', lS: msg.$});// || root.$});
root.on('in', {'@': msg['#'], put: Gun.graph.node(data), lS:1});// || root.$});
};
Gun.debug? setTimeout(to,1) : to();
});
@ -134,6 +135,10 @@ Gun.on('create', function(root){
}
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,

View File

@ -1,147 +1,175 @@
var Type = require('../type');
var puff = (typeof setImmediate !== "undefined")? setImmediate : setTimeout;
function Mesh(root){
var mesh = function(){};
var opt = root.opt || {};
opt.log = opt.log || console.log;
opt.gap = opt.gap || opt.wait || 1;
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.puff = opt.puff || 9; // IDEA: do a start/end benchmark, divide ops/result.
var puff = setTimeout.puff || setTimeout;
var dup = root.dup;
var dup = root.dup, dup_check = dup.check, dup_track = dup.track;
// TODO: somewhere in here caused a out-of-memory crash! How? It is inbound!
mesh.hear = function(raw, peer){
var hear = mesh.hear = function(raw, peer){
if(!raw){ return }
var msg, id, hash, tmp = raw[0];
if(opt.pack <= raw.length){ return mesh.say({dam: '!', err: "Message too big!"}, peer) }
if('{' != raw[2]){ mesh.hear.d += raw.length||0; ++mesh.hear.c; } // STATS! // ugh, stupid double JSON encoding
var msg, id, hash, tmp = raw[0], DBG;
if(mesh === this){ hear.d += raw.length||0 ; ++hear.c } // STATS!
if('[' === tmp){
try{msg = JSON.parse(raw);}catch(e){opt.log('DAM JSON parse error', e)}
try{msg = JSON.parse(raw)}catch(e){opt.log('DAM JSON parse error', e)}
raw = '';
if(!msg){ return }
LOG && opt.log(+new Date, msg.length, 'in hear batch');
console.STAT && console.STAT(+new Date, msg.length, '# on hear batch');
var P = opt.puff;
(function go(){
var S; LOG && (S = +new Date); // STATS!
var m, c = 100; // hardcoded for now?
while(c-- && (m = msg.shift())){
mesh.hear(m, peer);
}
LOG && opt.log(S, +new Date - S, 'batch heard');
var S = +new Date;
//var P = peer.puff || opt.puff, s = +new Date; // TODO: For future, but in mix?
var i = 0, m; while(i < P && (m = msg[i++])){ hear(m, peer) }
//peer.puff = Math.ceil((+new Date - s)? P * 1.1 : P * 0.9);
msg = msg.slice(i); // slicing after is faster than shifting during.
console.STAT && console.STAT(S, +new Date - S, 'hear loop');
flush(peer); // force send all synchronously batched acks.
if(!msg.length){ return }
puff(go, 0);
}());
return;
}
if('{' === tmp || (Type.obj.is(raw) && (msg = raw))){
if('{' === tmp || ((raw['#'] || obj_is(raw)) && (msg = raw))){
try{msg = msg || JSON.parse(raw);
}catch(e){return opt.log('DAM JSON parse error', e)}
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(msg.DBG_s){ opt.log(+new Date - msg.DBG_s, 'to hear', id) }
if(dup.check(id)){ return }
dup.track(id, true).it = it(msg); // GUN core also dedups, so `true` is needed. // Does GUN core need to dedup anymore?
if(!(hash = msg['##']) && u !== msg.put){ hash = msg['##'] = Type.obj.hash(msg.put) }
if(tmp = dup_check(id)){ return }
/*if(!(hash = msg['##']) && u !== msg.put){ hash = msg['##'] = Type.obj.hash(msg.put) }
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?
}
(msg._ = function(){}).via = peer;
if(tmp = msg['><']){ (msg._).to = Type.obj.map(tmp.split(','), tomap) }
if(msg.dam){
if(tmp = mesh.hear[msg.dam]){
*/ // TOOD: COME BACK TO THIS LATER!!! IMPORTANT MESH STUFF!!
(msg._ = function(){}).via = mesh.leap = peer;
if(tmp = msg.dam){
if(tmp = mesh.hear[tmp]){
tmp(msg, peer, root);
}
dup_track(id);
return;
}
var S, ST; LOG && (S = +new Date);
var S = +new Date, ST;
DBG && (DBG.is = S);
root.on('in', msg);
LOG && !msg.nts && (ST = +new Date - S) > 9 && opt.log(S, ST, 'msg', msg['#']);
return;
//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
dup_track(id).via = peer;
mesh.leap = null; // warning! mesh.leap could be buggy.
}
}
var tomap = function(k,i,m){m(k,true)};
mesh.hear.c = mesh.hear.d = 0;
var noop = function(){};
hear.c = hear.d = 0;
;(function(){
var SMIA = 0;
var message;
var message, loop;
function each(peer){ mesh.say(message, peer) }
mesh.say = function(msg, peer){
if(this.to){ this.to.next(msg) } // compatible with middleware adapters.
var say = mesh.say = function(msg, peer){
if(this && this.to){ this.to.next(msg) } // compatible with middleware adapters.
if(!msg){ return false }
var id, hash, tmp, raw;
var S; LOG && (S = +new Date); //msg.DBG_s = msg.DBG_s || +new Date;
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(!(hash = msg['##']) && u !== msg.put){ hash = msg['##'] = Type.obj.hash(msg.put) }
if(!(raw = meta.raw)){
raw = mesh.raw(msg);
if(hash && (tmp = msg['@'])){
/*if(hash && (tmp = msg['@'])){
dup.track(tmp+hash).it = it(msg);
if(tmp = (dup.s[tmp]||ok).it){
if(hash === tmp['##']){ return false }
tmp['##'] = hash;
}
}
}*/
}
//LOG && opt.log(S, +new Date - S, 'say prep');
dup.track(id).it = it(msg); // track for 9 seconds, default. Earth<->Mars would need more!
if(!peer){ peer = (tmp = dup.s[msg['@']]) && (tmp = tmp.it) && (tmp = tmp._) && (tmp = tmp.via) }
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['@']){
LOG && opt.log(+new Date, ++SMIA, 'total no peer to ack to');
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 S; LOG && (S = +new Date);
Type.obj.map(peer || opt.peers, each); // in case peer is a peer list.
//LOG && opt.log(S, +new Date - S, 'say loop');
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.
;(function go(){
var S = +new Date;
//Type.obj.map(peer || opt.peers, each); // in case peer is a peer list.
loop = 1; var wr = meta.raw; meta.raw = raw; // quick perf hack
var i = 0, p; while(i < 9 && (p = (pl||'')[i++])){
if(!(p = ps[p])){ continue }
say(msg, p);
}
meta.raw = wr; loop = 0;
pl = pl.slice(i); // slicing after is faster than shifting during.
console.STAT && console.STAT(S, +new Date - S, 'say loop');
if(!pl.length){ return }
puff(go, 0);
dup_track(msg['@']); // keep for later
}());
return;
}
// TODO: PERF: consider splitting function here, so say loops do less work.
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 }
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(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.push(raw);
peer.batch += (tmp?',':'')+raw; // TODO: Prevent double JSON! // FOR v1.0 !?
return;
}
flush(peer);
}
peer.batch = []; // peer.batch = '['; // TODO: Prevent double JSON!
setTimeout(function(){flush(peer)}, opt.gap);
//peer.batch = [];
peer.batch = '['; // TODO: Prevent 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);
flush(peer);
}, opt.gap);
send(raw, peer);
}
function flush(peer){
var tmp = peer.batch; // var tmp = peer.batch + ']'; // TODO: Prevent double JSON!
peer.batch = peer.tail = null;
if(!tmp){ return }
if(!tmp.length){ return } // if(3 > tmp.length){ return } // TODO: ^
var S; LOG && (S = +new Date);
try{tmp = (1 === tmp.length? tmp[0] : JSON.stringify(tmp));
}catch(e){return opt.log('DAM JSON stringify error', e)}
LOG && opt.log(S, +new Date - S, 'say stringify', tmp.length);
if(!tmp){ return }
send(tmp, peer);
}
mesh.say.c = mesh.say.d = 0;
}());
function flush(peer){
var tmp = peer.batch, t = 'string' == typeof tmp, l;
if(t){ tmp += ']' }// TODO: Prevent double JSON!
peer.batch = peer.tail = null;
if(!tmp){ return }
if(t? 3 > tmp.length : !tmp.length){ return } // TODO: ^
if(!t){try{tmp = (1 === tmp.length? tmp[0] : JSON.stringify(tmp));
}catch(e){return opt.log('DAM JSON stringify error', e)}}
if(!tmp){ return }
send(tmp, peer);
}
// for now - find better place later.
function send(raw, peer){ try{
var wire = peer.wire;
var S, ST; LOG && (S = +new Date);
if(peer.say){
peer.say(raw);
} else
if(wire.send){
wire.send(raw);
}
LOG && (ST = +new Date - S) > 9 && opt.log(S, ST, 'wire send', raw.length);
mesh.say.d += raw.length||0; ++mesh.say.c; // STATS!
}catch(e){
(peer.queue = peer.queue || []).push(raw);
@ -153,18 +181,22 @@ function Mesh(root){
if(!msg){ return '' }
var meta = (msg._) || {}, put, hash, tmp;
if(tmp = meta.raw){ return tmp }
if(typeof msg === 'string'){ return msg }
if(!msg.dam){
if('string' == typeof msg){ return msg }
/*if(!msg.dam){ // TOOD: COME BACK TO THIS LATER!!! IMPORTANT MESH STUFF!!
var i = 0, to = []; Type.obj.map(opt.peers, function(p){
to.push(p.url || p.pid || p.id); if(++i > 3){ return true } // limit server, fast fix, improve later! // For "tower" peer, MUST include 6 surrounding ids. // REDUCED THIS TO 3 for temporary relay peer performance, towers still should list neighbors.
}); if(i > 1){ msg['><'] = to.join() }
}
}*/ // TOOD: COME BACK TO THIS LATER!!! IMPORTANT MESH STUFF!!
var raw = $(msg); // optimize by reusing put = the JSON.stringify from .hash?
/*if(u !== put){
tmp = raw.indexOf(_, raw.indexOf('put'));
raw = raw.slice(0, tmp-1) + put + raw.slice(tmp + _.length + 1);
//raw = raw.replace('"'+ _ +'"', put); // NEVER USE THIS! ALSO NEVER DELETE IT TO NOT MAKE SAME MISTAKE! https://github.com/amark/gun/wiki/@$$ Heisenbug
}*/
// TODO: PERF: tgif, CPU way too much on re-JSONifying ^ it.
/*
// 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.
return raw;
}
@ -188,12 +220,12 @@ function Mesh(root){
Type.obj.map(tmp, function(msg){
send(msg, peer);
});
Type.obj.native && Type.obj.native(); // dirty place to check if other JS polluted.
}
mesh.bye = function(peer){
root.on('bye', peer);
var tmp = +(new Date); tmp = (tmp - (peer.met||tmp));
mesh.bye.time = ((mesh.bye.time || tmp) + tmp) / 2;
LOG = console.LOG; // dirty place to cheaply update LOG settings over time.
}
mesh.hear['!'] = function(msg, peer){ opt.log('Error:', msg.err) }
mesh.hear['?'] = function(msg, peer){
@ -212,7 +244,7 @@ function Mesh(root){
});
root.on('bye', function(peer, tmp){
peer = opt.peers[peer.id || peer] || peer;
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);
@ -226,6 +258,7 @@ function Mesh(root){
});
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){
tmp = {}; tmp[soul] = root.graph[soul];
mesh.say({'##': Type.obj.hash(tmp), get: {'#': soul}}, peer);
@ -236,18 +269,6 @@ function Mesh(root){
}
;(function(){
Type.text.hash = function(s){ // via SO
if(typeof s !== 'string'){ return {err: 1} }
var c = 0;
if(!s.length){ return c }
for(var i=0,l=s.length,n; i<l; ++i){
n = s.charCodeAt(i);
c = ((c<<5)-c)+n;
c |= 0;
}
return c; // Math.abs(c);
}
var $ = JSON.stringify, u;
Type.obj.hash = function(obj, hash){
@ -257,7 +278,9 @@ function Mesh(root){
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});
console.STAT && console.STAT(S, +new Date - S, 'sort');
return tmp;
}
Type.obj.hash.sort = sort;
@ -270,7 +293,7 @@ function Mesh(root){
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 LOG = console.LOG;
var obj_is = Type.obj.is, obj_map = Type.obj.map;
try{ module.exports = Mesh }catch(e){}

View File

@ -3,9 +3,9 @@
require('./onto'); // depends upon onto!
module.exports = function ask(cb, as){
if(!this.on){ return }
if(!(cb instanceof Function)){
if(!('function' == typeof cb)){
if(!cb || !as){ return }
var id = cb['#'] || cb, tmp = (this.tag||empty)[id];
var id = cb['#'] || cb, tmp = (this.tag||'')[id];
if(!tmp){ return }
tmp = this.on(id, as);
clearTimeout(tmp.err);
@ -15,7 +15,7 @@ module.exports = function ask(cb, as){
if(!cb){ return id }
var to = this.on(id, cb, as);
to.err = to.err || setTimeout(function(){
to.next({err: "Error: No ACK received yet.", lack: true});
to.next({err: "Error: No ACK yet.", lack: true});
to.off();
}, (this.opt||{}).lack || 9000);
return id;

View File

@ -25,7 +25,7 @@ Gun.chain.back = function(n, opt){ var tmp;
}
return;
}
if(n instanceof Function){
if('function' == typeof n){
var yes, tmp = {back: at};
while((tmp = tmp.back)
&& u === (yes = n(tmp, opt))){}

View File

@ -57,6 +57,12 @@ function output(msg){
put._ = meta;
back.on('in', {$: back.$, put: put, get: back.get})
}
if(tmp = at.lex){
tmp = (tmp._) || (tmp._ = function(){});
if(back.ack < tmp.ask){ tmp.ask = back.ack }
if(tmp.ask){ return }
tmp.ask = 1;
}
}
root.ask(ack, msg);
return root.on('in', msg);
@ -271,7 +277,7 @@ function ask(at, soul){
Gun.obj.del(at, 'ask'); // TODO: PERFORMANCE? More elegant way?
}
function ack(msg, ev){
var as = this.as, get = as.get || empty, at = as.$._, tmp = (msg.put||empty)[get['#']];
var as = this.as, get = as.get||'', at = as.$._, tmp = (msg.put||'')[get['#']];
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 }
@ -287,7 +293,7 @@ function ack(msg, ev){
at.on('in', {get: at.get, put: Gun.val.link.ify(get['#']), $: at.$, '@': msg['@']});
return;
}
Gun.on.put(msg, at.root.$);
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;

View File

@ -1,30 +1,28 @@
var Type = require('./type');
function Dup(opt){
var dup = {s:{}};
var dup = {s:{}}, s = dup.s;
opt = opt || {max: 1000, age: /*1000 * 9};//*/ 1000 * 9 * 3};
dup.check = function(id){ var tmp;
if(!(tmp = dup.s[id])){ return false }
if(tmp.pass){ return tmp.pass = false }
return dup.track(id);
dup.check = function(id){
if(!s[id]){ return false }
return dt(id);
}
dup.track = function(id, pass){
var it = dup.s[id] || (dup.s[id] = {});
it.was = time_is();
if(pass){ it.pass = true }
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 = time_is();
Type.obj.map(dup.s, function(it, id){
var now = +new Date;
Type.obj.map(s, function(it, id){
if(it && (age || opt.age) > (now - it.was)){ return }
Type.obj.del(dup.s, id);
delete s[id];
});
dup.to = null;
console.STAT && (age = +new Date - now) > 9 && console.STAT(now, age, 'dup drop');
}
return dup;
}
var time_is = Type.time.is;
module.exports = Dup;

View File

@ -10,7 +10,7 @@ Gun.chain.get = function(key, cb, as){
}
gun = gun.$;
} else
if(key instanceof Function){
if('function' == typeof key){
if(true === cb){ return soul(this, key, cb, as), this }
gun = this;
var at = gun._, root = at.root, tmp = root.now, ev;
@ -46,7 +46,7 @@ Gun.chain.get = function(key, cb, as){
if(tmp = this._.stun){ // TODO: Refactor?
gun._.stun = gun._.stun || tmp;
}
if(cb && cb instanceof Function){
if(cb && 'function' == typeof cb){
gun.get(cb, as);
}
return gun;
@ -72,7 +72,7 @@ function soul(gun, cb, opt, as){
if(cat.jam){ return cat.jam.push([cb, as]) }
cat.jam = [[cb,as]];
gun.get(function go(msg, eve){
if(u === msg.put && (tmp = Object.keys(cat.root.opt.peers).length) && ++acks < tmp){
if(u === msg.put && (tmp = Object.keys(cat.root.opt.peers).length) && ++acks <= tmp){
return;
}
eve.rid(msg);
@ -101,7 +101,7 @@ function use(msg){
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))){
tmp = ((msg.$$ = at.root.gun.get(tmp))._);
tmp = ((msg.$$ = at.root.$.get(tmp))._);
if(u !== tmp.put){
msg = obj_to(msg, {put: data = tmp.put});
}

View File

@ -27,9 +27,13 @@ var Graph = {};
if(typeof env === 'string'){
env = {soul: env};
} else
if(env instanceof Function){
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);
}

View File

@ -29,7 +29,7 @@ Node.soul._ = Val.link._;
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(o instanceof Function){ o = {map: 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});

View File

@ -9,7 +9,7 @@ module.exports = function onto(tag, arg, as){
tmp.next(arg);
}}
}});
if(arg instanceof Function){
if('function' == typeof arg){
var be = {
off: onto.off ||
(onto.off = function(){

View File

@ -1,9 +1,4 @@
var root;
if(typeof window !== "undefined"){ root = window }
if(typeof global !== "undefined"){ root = global }
root = root || {};
var console = root.console || {log: function(){}};
function USE(arg, req){
return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){
arg(mod = {exports: {}});
@ -13,5 +8,5 @@
return p.split('/').slice(-1).toString().replace('.js','');
}
}
if(typeof module !== "undefined"){ var common = module }
if(typeof module !== "undefined"){ var MODULE = module }

13
src/puff.js Normal file
View File

@ -0,0 +1,13 @@
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;

View File

@ -1,21 +1,7 @@
var Gun = require('./root');
Gun.chain.put = function(data, cb, as){
// #soul.has=value>state
// ~who#where.where=what>when@was
// TODO: BUG! Put probably cannot handle plural chains! `!as` is quickfix test.
var gun = this, at = (gun._), root = at.root.$, ctx = root._, M = 100, tmp;
/*if(!ctx.puta && !as){ if(tmp = ctx.puts){ if(tmp > M){ // without this, when synchronous, writes to a 'not found' pile up, when 'not found' resolves it recursively calls `put` which incrementally resolves each write. Stack overflow limits can be as low as 10K, so this limit is hardcoded to 1% of 10K.
(ctx.stack || (ctx.stack = [])).push([gun, data, cb, as]);
if(ctx.puto){ return }
ctx.puto = setTimeout(function drain(){
var d = ctx.stack.splice(0,M), i = 0, at; ctx.puta = true;
while(at = d[i++]){ at[0].put(at[1], at[2], at[3]) } delete ctx.puta;
if(ctx.stack.length){ return ctx.puto = setTimeout(drain, 0) }
ctx.stack = ctx.puts = ctx.puto = null;
}, 0);
return gun;
} ++ctx.puts } else { ctx.puts = 1 } }*/
as = as || {};
as.data = data;
as.via = as.$ = as.via || as.$ || gun;
@ -34,6 +20,7 @@ Gun.chain.put = function(data, cb, as){
return gun;
}
as.soul = as.soul || (as.not = Gun.node.soul(as.data) || (as.via.back('opt.uuid') || Gun.text.random)());
as.via._.stun = {};
if(!as.soul){ // polyfill async uuid for SEA
as.via.back('opt.uuid')(function(err, soul){ // TODO: improve perf without anonymous callback
if(err){ return Gun.log(err) } // TODO: Handle error!
@ -46,9 +33,11 @@ Gun.chain.put = function(data, cb, as){
ify(as);
return gun;
}
as.via._.stun = {};
if(Gun.is(data)){
data.get(function(soul, o, msg){
if(!soul){
delete as.via._.stun;
return Gun.log("The reference you are saving is a", typeof msg.put, '"'+ msg.put +'", not a node (object)!');
}
gun.put(Gun.val.link.ify(soul), cb, as);
@ -70,6 +59,14 @@ Gun.chain.put = function(data, cb, as){
}
return gun;
};
/*Gun.chain.put = function(data, cb, as){ // don't rewrite! :(
var gun = this, at = gun._;
as = as || {};
as.soul || (as.soul = at.soul || ('string' == typeof cb && cb));
if(!as.soul){ return get(data, cb, as) }
return gun;
}*/
function ify(as){
as.batch = batch;
@ -101,9 +98,10 @@ function stun(cb){
}
function batch(){ var as = this;
if(!as.graph || obj_map(as.stun, no)){ return }
if(!as.graph || !obj_empty(as.stun)){ return }
as.res = as.res || function(cb){ if(cb){ cb() } };
as.res(function(){
delete as.via._.stun;
var cat = (as.$.back(-1)._), ask = cat.ask(function(ack){
cat.root.on('ack', ack);
if(ack.err){ Gun.log(ack) }
@ -124,6 +122,7 @@ function batch(){ var as = this;
});
cat.root.mum = mum? obj.to(mum, cat.root.mum) : mum;
cat.root.now = tmp;
as.via._.on('res', {}); delete as.via._.tag.res; // emitting causes mem leak?
}, as);
if(as.res){ as.res() }
} function no(v,k){ if(v){ return true } }
@ -145,16 +144,20 @@ function map(v,k,n, at){ var as = this;
at.soul(id);
return;
}
(as.stun = as.stun || {})[path] = true;
ref.get(soul, true, {as: {at: at, as: as, p:path}});
(as.stun = as.stun || {})[path] = 1;
ref.get(soul, true, {as: {at: at, as: as, p:path, ref: ref}});
}, {as: as, at: at});
//if(is){ return {} }
}
var G = String.fromCharCode(31);
function soul(id, as, msg, eve){
var as = as.as, cat = as.at; as = as.as;
var as = as.as, path = as.p, ref = as.ref, cat = as.at; as = as.as;
var sat = ref.back(function(at){ return sat = at.soul || at.link || at.dub });
var pat = [sat || as.soul].concat(ref._.has || ref._.get || path)
var at = ((msg || {}).$ || {})._ || {};
id = at.dub = at.dub || id || Gun.node.soul(cat.obj) || Gun.node.soul(msg.put || at.put) || Gun.val.link.is(msg.put || at.put) || (as.via.back('opt.uuid') || Gun.text.random)(); // TODO: BUG!? Do we really want the soul of the object given to us? Could that be dangerous?
id = at.dub = at.dub || id || Gun.node.soul(cat.obj) || Gun.node.soul(msg.put || at.put) || Gun.val.link.is(msg.put || at.put) || pat.join('/') /* || (function(){
return (as.soul+'.')+Gun.text.hash(path.join(G)).toString(32);
})(); // TODO: BUG!? Do we really want the soul of the object given to us? Could that be dangerous? What about copy operations? */
if(eve){ eve.stun = true }
if(!id){ // polyfill async uuid for SEA
as.via.back('opt.uuid')(function(err, id){ // TODO: improve perf without anonymous callback
@ -169,7 +172,7 @@ function soul(id, as, msg, eve){
function solve(at, id, cat, as){
at.$.back(-1).get(id);
cat.soul(id);
as.stun[cat.path] = false;
delete as.stun[cat.path];
as.batch();
}
@ -186,6 +189,7 @@ function any(soul, as, msg, eve){
if(as.ref !== as.$){
tmp = (as.$._).get || at.get;
if(!tmp){ // TODO: Handle
delete as.via._.stun;
Gun.log("Please report this as an issue! Put.no.get"); // TODO: BUG!??
return;
}
@ -193,7 +197,7 @@ function any(soul, as, msg, eve){
tmp = null;
}
if(u === data){
if(!at.get){ return } // TODO: Handle
if(!at.get){ delete as.via._.stun; return } // TODO: Handle
if(!soul){
tmp = at.$.back(function(at){
if(at.link || at.soul){ return at.link || at.soul }
@ -218,7 +222,7 @@ function any(soul, as, msg, eve){
}
if(!as.soul){ // polyfill async uuid for SEA
as.via.back('opt.uuid')(function(err, soul){ // TODO: improve perf without anonymous callback
if(err){ return Gun.log(err) } // Handle error.
if(err){ delete as.via._.stun; return Gun.log(err) } // Handle error.
as.ref.put(as.data, as.soul = soul, as);
});
return;
@ -226,7 +230,7 @@ function any(soul, as, msg, eve){
}
as.ref.put(as.data, as.soul, as);
}
var obj = Gun.obj, obj_is = obj.is, obj_put = obj.put, obj_map = obj.map;
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._;

View File

@ -1,14 +1,14 @@
function Gun(o){
if(o instanceof Gun){ return (this._ = {gun: this, $: this}).$ }
if(o instanceof Gun){ return (this._ = {$: this}).$ }
if(!(this instanceof Gun)){ return new Gun(o) }
return Gun.create(this._ = {gun: this, $: this, opt: o});
return Gun.create(this._ = {$: this, opt: o});
}
Gun.is = function($){ return ($ instanceof Gun) || ($ && $._ && ($ === $._.$)) || false }
Gun.version = 0.9;
Gun.version = 0.2020;
Gun.chain = Gun.prototype;
Gun.chain.toJSON = function(){};
@ -23,6 +23,7 @@ Gun.graph = require('./graph');
Gun.on = require('./onto');
Gun.ask = require('./ask');
Gun.dup = require('./dup');
Gun.puff = require('./puff');
;(function(){
Gun.create = function(at){
@ -33,45 +34,131 @@ Gun.dup = require('./dup');
at.dup = at.dup || Gun.dup();
var gun = at.$.opt(at.opt);
if(!at.once){
at.on('in', root, at);
at.on('out', root, {at: at, out: root});
at.on('in', universe, at);
at.on('out', universe, at);
at.on('put', map, at);
Gun.on('create', at);
at.on('create', at);
}
at.once = 1;
return gun;
}
function root(msg){
//add to.next(at); // TODO: MISSING FEATURE!!!
var ev = this, as = ev.as, at = as.at || as, gun = at.$, dup, tmp;
if(!(tmp = msg['#'])){ tmp = msg['#'] = text_rand(9) }
if((dup = at.dup).check(tmp)){
if(as.out === msg.out){
msg.out = u;
ev.to.next(msg);
function universe(msg){
if(!msg){ return }
if(msg.out === universe){ this.to.next(msg); return }
var eve = this, as = eve.as, at = as.at || as, gun = at.$, dup = at.dup, tmp, DBG = msg.DBG;
(tmp = msg['#']) || (tmp = msg['#'] = text_rand(9));
if(dup.check(tmp)){ return } dup.track(tmp);
tmp = msg._; msg._ = ('function' == typeof tmp)? tmp : function(){};
(msg.$ && (msg.$ === (msg.$._||'').$)) || (msg.$ = gun);
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) }
if(msg.put){ put(msg); return }
}
DBG && (DBG.uc = +new Date);
eve.to.next(msg);
DBG && (DBG.ua = +new Date);
msg.out = universe; at.on('out', msg);
DBG && (DBG.ue = +new Date);
}
function put(msg){
if(!msg){ return }
var ctx = msg._||'', root = ctx.root = ((msg.$||'')._||'').root;
var put = msg.put, id = msg['#'], err, tmp;
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.
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 }
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 }
ham(val, key, soul, state, msg);
}
if(err){ break }
}
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.
} 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){
if(is.defer){
var to = state - machine;
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;
}
return;
}
dup.track(tmp);
if(tmp = msg['@']){ dup.track(tmp) } // HNPERF: Bump original request's liveliness.
if(!at.ask(tmp, msg)){
if(msg.get){
Gun.on.get(msg, gun); //at.on('get', get(msg));
}
if(msg.put){
Gun.on.put(msg, gun); //at.on('put', put(msg));
}
}
ev.to.next(msg);
if(!as.out){
msg.out = root;
at.on('out', msg);
}
(lot = ctx.lot||'').s++; lot.more++;
(ctx.stun || (ctx.stun = {}))[soul+key] = 1;
var DBG = ctx.DBG; DBG && (DBG.ph = DBG.ph || +new Date);
root.on('put', {'#': msg['#'], '@': msg['@'], put: {'#': soul, '.': key, ':': val, '>': state}, _: ctx});
}
function map(msg){
var DBG; if(DBG = (msg._||'').DBG){ DBG.pa = +new Date; DBG.pm = DBG.pm || +new Date}
var eve = this, root = eve.as, graph = root.graph, ctx = msg._, put = msg.put, soul = put['#'], key = put['.'], val = put[':'], state = put['>'], id = msg['#'], tmp;
graph[soul] = state_ify(graph[soul], key, state, val, soul); // TODO: Only put in graph if subscribed? Relays vs Browsers?
chain(ctx, soul, key, (u !== (tmp = put['=']))? tmp : val, state); // TODO: This should NOT be how the API works, this should be done at an extension layer, but hacky solution to migrate with old code for now.
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) } // TODO: 'forget' feature in SEA tied to this, bad approach, but hacked in for now. Any changes here must update there.
eve.to.next(msg);
}
function chain(ctx, soul, key,val, state){
var root = ctx.root, put, tmp;
(root.opt||'').super && root.$.get(soul); // I think we need super for now, but since we are rewriting, should consider getting rid of it.
if(!root || !(tmp = root.next) || !(tmp = tmp[soul]) || !tmp.$){ return }
(put = ctx.put || (ctx.put = {}))[soul] = state_ify(put[soul], key, state, val, soul);
tmp.put = state_ify(tmp.put, key, state, val, soul);
}
function fire(ctx){
if(ctx.err){ return }
var stop = {};
var root = ctx.root, next = root.next||'', put = ctx.put, tmp;
var S = +new Date;
//Gun.graph.is(put, function(node, soul){
for(var soul in put){ var node = put[soul]; // Gun.obj.native() makes this safe.
if(!(tmp = next[soul]) || !tmp.$){ continue }
root.stop = stop; // temporary fix till a better solution?
tmp.on('in', {$: tmp.$, get: soul, put: node});
root.stop = null; // temporary fix till a better solution?
}
console.STAT && console.STAT(S, +new Date - S, 'fire');
ctx.DBG && (ctx.DBG.f = +new Date);
if(!(tmp = ctx.out)){ return }
tmp.out = universe;
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;
}());
;(function(){
Gun.on.put = function(msg, gun){
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!" }
@ -81,13 +168,12 @@ Gun.dup = require('./dup');
if(u !== ctx.defer){
var to = ctx.defer - ctx.machine;
setTimeout(function(){
Gun.on.put(msg, gun);
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}));
};
var MD = 2147483647;
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+"'!" }
@ -155,10 +241,12 @@ Gun.dup = require('./dup');
}
function perf(node, soul){ if(node !== this.graph[soul]){ return true } } // HNPERF: do not port!
Gun.on.get = function(msg, gun){
Gun.on._get = function(msg, gun){
var root = gun._, get = msg.get, soul = get[_soul], node = root.graph[soul], has = get[_has], 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(!node){ return root.on('get', msg) }
if(has){
if('string' != typeof has || !obj_has(node, has)){ return root.on('get', msg) }
@ -171,16 +259,19 @@ Gun.dup = require('./dup');
}
node = Gun.graph.node(node);
tmp = (at||empty).ack;
var faith = function(){}; faith.faith = true; // HNPERF: We're testing performance improvement by skipping going through security again, but this should be audited.
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);
root.on('in', {
'@': msg['#'],
how: 'mem',
put: node,
ram: 1,
$: gun,
_: faith
});
DBG && (DBG.gm = +new Date);
//if(0 < tmp){ return }
root.on('get', msg);
DBG && (DBG.gd = +new Date);
}
}());
@ -205,39 +296,30 @@ Gun.dup = require('./dup');
obj_map(v, each, this[k]);
}, at.opt);
Gun.on('opt', at);
at.opt.uuid = at.opt.uuid || function(){ return state_lex() + text_rand(12) }
//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_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, _soul = Gun.val.link._, _has = '.', node_ = Gun.node._, rel_is = Gun.val.link.is;
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;
console.only = function(i, s){ return (console.only.i && i === console.only.i && console.only.i++) && (console.log.apply(console, arguments) || s) };
Gun.log = function(){ return (!Gun.log.off && console.log.apply(console, 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) }
;"Please do not remove these messages unless you are paying for a monthly sponsorship, thanks!";
Gun.log.once("welcome", "Hello wonderful person! :) Thanks for using GUN, feel free to ask for help on https://gitter.im/amark/gun and ask StackOverflow questions tagged with 'gun'!");
;"Please do not remove these messages unless you are paying for a monthly sponsorship, thanks!";
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) };
if(typeof window !== "undefined"){ (window.GUN = window.Gun = Gun).window = window }
try{ if(typeof common !== "undefined"){ common.exports = Gun } }catch(e){}
try{ if(typeof MODULE !== "undefined"){ MODULE.exports = Gun } }catch(e){}
module.exports = Gun;
/*Gun.on('opt', function(ctx){ // FOR TESTING PURPOSES
this.to.next(ctx);
if(ctx.once){ return }
ctx.on('node', function(msg){
var to = this.to;
//Gun.node.is(msg.put, function(v,k){ msg.put[k] = v + v });
setTimeout(function(){
to.next(msg);
},1);
});
});*/
(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!";
Gun.log.once("welcome", "Hello wonderful person! :) Thanks for using GUN, please ask for help on http://chat.gun.eco if anything takes you longer than 5min to figure out!");

View File

@ -6,15 +6,18 @@ Gun.chain.set = function(item, cb, opt){
opt = opt || {}; opt.item = opt.item || item;
if(soul = Gun.node.soul(item)){ item = Gun.obj.put({}, soul, Gun.val.link.ify(soul)) }
if(!Gun.is(item)){
if(Gun.obj.is(item)){;
item = gun.back(-1).get(soul = soul || Gun.node.soul(item) || gun.back('opt.uuid')()).put(item);
if(Gun.obj.is(item)){
//item = gun.back(-1).get(soul = soul || Gun.node.soul(item) || (gun.back('opt.uuid') || uuid)()).put(item);
soul = soul || Gun.node.soul(item) || uuid(); // this just key now, not a soul.
}
return gun.get(soul || (Gun.state.lex() + Gun.text.random(7))).put(item, cb, opt);
return gun.get(soul || uuid()).put(item, cb, opt);
}
item.get(function(soul, o, msg){
if(!soul && item._.stun){ item._.on('res', function(){ this.off(); gun.set(item, cb, opt) }); return }
if(!soul){ return cb.call(gun, {err: Gun.log('Only a node can be linked! Not "' + msg.put + '"!')}) }
gun.put(Gun.obj.put({}, soul, Gun.val.link.ify(soul)), cb, opt);
},true);
return item;
}
function uuid(){ return Gun.state.lex() + Gun.text.random(7) }

View File

@ -6,7 +6,7 @@ function State(){
/*if(perf){
t = start + perf.now(); // Danger: Accuracy decays significantly over time, even if precise.
} else {*/
t = time();
t = +new Date;
//}
if(last < t){
return N = 0, last = t + State.drift;
@ -15,10 +15,10 @@ function State(){
}
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);
State._ = '>';
var S_ = State._ = '>';
State.drift = 0;
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_][State._]) || o;
var tmp = (k && n && n[N_] && n[N_][S_]) || o;
if(!tmp){ return }
return num_is(tmp = tmp[k])? tmp : -Infinity;
}
@ -30,7 +30,7 @@ State.ify = function(n, k, s, v, soul){ // put a key's state on a node.
}
n = Node.soul.ify(n, soul); // then make it so!
}
var tmp = obj_as(n[N_], State._); // grab the states data.
var tmp = obj_as(n[N_], S_); // grab the states data.
if(u !== k && k !== N_){
if(num_is(s)){
tmp[k] = s; // add the valid state.

View File

@ -35,6 +35,17 @@ Type.text.match = function(t, o){ var tmp, u;
if(u !== o['<'] && t <= o['<']){ return true }
return false;
}
Type.text.hash = function(s, c){ // via SO
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 = {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
@ -77,9 +88,9 @@ Type.obj.copy = function(o){ // because http://web.archive.org/web/2014032822402
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;
function empty(v,i){ var n = this.n, u;
if(n && (i === n || (obj_is(n) && obj_has(n, i)))){ return }
if(i){ return true }
if(u !== i){ return true }
}
Type.obj.empty = function(o, n){
if(!o){ return true }
@ -95,20 +106,21 @@ Type.obj.copy = function(o){ // because http://web.archive.org/web/2014032822402
} t.r = t.r || [];
t.r.push(k);
};
var keys = Object.keys, map;
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 = fn_is(c);
t.r = null;
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(_ || this, l[ll[i]], ll[i], t) : c.call(_ || this, l[i], ii, t);
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!