Merge pull request #818 from amark/master

merge master INTO dev
This commit is contained in:
Mark Nadal 2019-09-16 18:12:55 -07:00 committed by GitHub
commit 3953c17130
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
103 changed files with 53977 additions and 5913 deletions

View File

@ -3,8 +3,7 @@ branches:
except:
- debug
node_js:
- 8
- 10
cache:
directories:
- node_modules
- node_modules

View File

@ -1,10 +1,9 @@
<p><a href="https://gun.eco/"><img width="40%" src="https://cldup.com/TEy9yGh45l.svg"/></a><img width="50%" align="right" vspace="25" src="https://gun.eco/see/demo.gif"/></p>
[![npm](https://img.shields.io/npm/dm/gun.svg)](https://www.npmjs.com/package/gun)
[![](https://data.jsdelivr.com/v1/package/npm/gun/badge?style=rounded)](https://www.jsdelivr.com/package/npm/gun)
[![Travis](https://img.shields.io/travis/amark/gun/master.svg)](https://travis-ci.org/amark/gun)
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Famark%2Fgun.svg?size=shield)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Famark%2Fgun?ref=badge_shield)
[![Gitter](https://img.shields.io/gitter/room/amark/gun.js.svg)](https://gitter.im/amark/gun)
[![](https://data.jsdelivr.com/v1/package/npm/gun/badge?style=rounded)](https://www.jsdelivr.com/package/npm/gun)
**GUN** is an _ecosystem_ of tools that let you <u>build tomorrow's dApps, today</u>.
@ -12,7 +11,7 @@ Decentralized alternatives to [Reddit](https://notabug.io/t/whatever/comments/36
<table>
<tr>
<a href=""><img width="31%" align="left" src="https://gun.eco/see/3dvr.gif" title="3D VR"/></a>
<a href="https://youtu.be/s_m16-w6bBI"><img width="31%" align="left" src="https://gun.eco/see/3dvr.gif" title="3D VR"/></a>
<a href="https://github.com/cstefanache/cstefanache.github.io/blob/master/_posts/2016-08-02-gun-db-artificial-knowledge-sharing.md#gundb"><img width="31%" align="left" src="https://gun.eco/see/aiml.gif" title="AI/ML"/></a>
<a href="http://gps.gunDB.io/"><img width="31%" align="left" src="https://gun.eco/see/gps.gif" title="GPS"/></a>
<a href="https://github.com/lmangani/gun-scape#gun-scape"><img width="31%" align="left" src="https://gun.eco/see/dataviz.gif" title="Data Viz"/></a>
@ -20,8 +19,9 @@ Decentralized alternatives to [Reddit](https://notabug.io/t/whatever/comments/36
<a href="https://github.com/Stefdv/gun-ui-lcd#okay-what-about-gundb-"><img width="31%" align="left" src="https://gun.eco/see/iot.gif" title="IoT"/></a>
</tr>
</table>
<center><a href="https://youtu.be/1ASrmQ-CwX4"><img src="https://gun.eco/see/ar.gif" title="AR"/></a></center>
The ecosystem is one nice stack of technologies that looks like this:
The ecosystem is one nice stack of technologies that looks like this: (names -> use case)
<div><img width="48%" src="https://gun.eco/see/stack.png"/>
<img width="48%" align="right" src="https://gun.eco/see/layers.png"/></div>
@ -167,7 +167,7 @@ Technically, **GUN is a graph synchronization protocol** with a *lightweight emb
This would not be possible without **community contributors**, big shout out to:
**[ajmeyghani](https://github.com/ajmeyghani) ([Learn GUN Basics with Diagrams](https://medium.com/@ajmeyghani/gundb-a-graph-database-in-javascript-3860a08d873c))**; **[anywhichway](https://github.com/anywhichway) ([Block Storage](https://github.com/anywhichway/gun-block))**; **[beebase](https://github.com/beebase) ([Quasar](https://github.com/beebase/gun-vuex-quasar))**; **[BrockAtkinson](https://github.com/BrockAtkinson) ([brunch config](https://github.com/BrockAtkinson/brunch-gun))**; **[Brysgo](https://github.com/brysgo) ([GraphQL](https://github.com/brysgo/graphql-gun))**; **[d3x0r](https://github.com/d3x0r) ([SQLite](https://github.com/d3x0r/gun-db))**; **[forrestjt](https://github.com/forrestjt) ([file.js](https://github.com/amark/gun/blob/master/lib/file.js))**; **[hillct](https://github.com/hillct) (Docker)**; **[JosePedroDias](https://github.com/josepedrodias) ([graph visualizer](http://acor.sl.pt:9966))**; **[JuniperChicago](https://github.com/JuniperChicago) ([cycle.js bindings](https://github.com/JuniperChicago/cycle-gun))**; **[jveres](https://github.com/jveres) ([todoMVC](https://github.com/jveres/todomvc))**; **[kristianmandrup](https://github.com/kristianmandrup) ([edge](https://github.com/kristianmandrup/gun-edge))**; **[Lightnet](https://github.com/Lightnet)** ([Awesome Vue User Examples](https://glitch.com/edit/#!/jsvuegunui?path=README.md:1:0) & [User Kitchen Sink Playground](https://gdb-auth-vue-node.glitch.me/)); **[lmangani](https://github.com/lmangani) ([Cytoscape Visualizer](https://github.com/lmangani/gun-scape), [Cassandra](https://github.com/lmangani/gun-cassandra), [Fastify](https://github.com/lmangani/fastify-gundb), [LetsEncrypt](https://github.com/lmangani/polyGun-letsencrypt))**; **[mhelander](https://github.com/mhelander) ([SEA](https://github.com/amark/gun/blob/master/sea.js))**; [omarzion](https://github.com/omarzion) ([Sticky Note App](https://github.com/omarzion/stickies)); [PsychoLlama](https://github.com/PsychoLlama) ([LevelDB](https://github.com/PsychoLlama/gun-level)); **[RangerMauve](https://github.com/RangerMauve) ([schema](https://github.com/gundb/gun-schema))**; **[robertheessels](https://github.com/swifty) ([gun-p2p-auth](https://github.com/swifty/gun-p2p-auth))**; **[rogowski](https://github.com/rogowski) (AXE)**; [sbeleidy](https://github.com/sbeleidy); **[sbiaudet](https://github.com/sbiaudet) ([C# Port](https://github.com/sbiaudet/cs-gun))**; **[Sean Matheson](https://github.com/ctrlplusb) ([Observable/RxJS/Most.js bindings](https://github.com/ctrlplusb/gun-most))**; **[Shadyzpop](https://github.com/Shadyzpop) ([React Native example](https://github.com/amark/gun/tree/master/examples/react-native))**; **[sjones6](https://github.com/sjones6) ([Flint](https://github.com/sjones6/gun-flint))**; **[Stefdv](https://github.com/stefdv) (Polymer/web components)**; **[zrrrzzt](https://github.com/zrrrzzt) ([JWT Auth](https://gist.github.com/zrrrzzt/6f88dc3cedee4ee18588236756d2cfce))**; **[xmonader](https://github.com/xmonader) ([Python Port](https://github.com/xmonader/pygundb))**; **[88dev](https://github.com/88dev) ([Database Viewer](https://github.com/88dev/gun-show))**;
**[ajmeyghani](https://github.com/ajmeyghani) ([Learn GUN Basics with Diagrams](https://medium.com/@ajmeyghani/gundb-a-graph-database-in-javascript-3860a08d873c))**; **[anywhichway](https://github.com/anywhichway) ([Block Storage](https://github.com/anywhichway/gun-block))**; **[beebase](https://github.com/beebase) ([Quasar](https://github.com/beebase/gun-vuex-quasar))**; **[BrockAtkinson](https://github.com/BrockAtkinson) ([brunch config](https://github.com/BrockAtkinson/brunch-gun))**; **[Brysgo](https://github.com/brysgo) ([GraphQL](https://github.com/brysgo/graphql-gun))**; **[d3x0r](https://github.com/d3x0r) ([SQLite](https://github.com/d3x0r/gun-db))**; **[forrestjt](https://github.com/forrestjt) ([file.js](https://github.com/amark/gun/blob/master/lib/file.js))**; **[hillct](https://github.com/hillct) (Docker)**; **[JosePedroDias](https://github.com/josepedrodias) ([graph visualizer](http://acor.sl.pt:9966))**; **[JuniperChicago](https://github.com/JuniperChicago) ([cycle.js bindings](https://github.com/JuniperChicago/cycle-gun))**; **[jveres](https://github.com/jveres) ([todoMVC](https://github.com/jveres/todomvc))**; **[kristianmandrup](https://github.com/kristianmandrup) ([edge](https://github.com/kristianmandrup/gun-edge))**; **[Lightnet](https://github.com/Lightnet)** ([Awesome Vue User Examples](https://glitch.com/edit/#!/jsvuegunui?path=README.md:1:0) & [User Kitchen Sink Playground](https://gdb-auth-vue-node.glitch.me/)); **[lmangani](https://github.com/lmangani) ([Cytoscape Visualizer](https://github.com/lmangani/gun-scape), [Cassandra](https://github.com/lmangani/gun-cassandra), [Fastify](https://github.com/lmangani/fastify-gundb), [LetsEncrypt](https://github.com/lmangani/polyGun-letsencrypt))**; **[mhelander](https://github.com/mhelander) ([SEA](https://github.com/amark/gun/blob/master/sea.js))**; [omarzion](https://github.com/omarzion) ([Sticky Note App](https://github.com/omarzion/stickies)); [PsychoLlama](https://github.com/PsychoLlama) ([LevelDB](https://github.com/PsychoLlama/gun-level)); **[RangerMauve](https://github.com/RangerMauve) ([schema](https://github.com/gundb/gun-schema))**; **[robertheessels](https://github.com/swifty) ([gun-p2p-auth](https://github.com/swifty/gun-p2p-auth))**; **[rogowski](https://github.com/rogowski) (AXE)**; [sbeleidy](https://github.com/sbeleidy); **[sbiaudet](https://github.com/sbiaudet) ([C# Port](https://github.com/sbiaudet/cs-gun))**; **[Sean Matheson](https://github.com/ctrlplusb) ([Observable/RxJS/Most.js bindings](https://github.com/ctrlplusb/gun-most))**; **[Shadyzpop](https://github.com/Shadyzpop) ([React Native example](https://github.com/amark/gun/tree/master/examples/react-native))**; **[sjones6](https://github.com/sjones6) ([Flint](https://github.com/sjones6/gun-flint))**; **[Stefdv](https://github.com/stefdv) (Polymer/web components)**; **[zrrrzzt](https://github.com/zrrrzzt) ([JWT Auth](https://gist.github.com/zrrrzzt/6f88dc3cedee4ee18588236756d2cfce))**; **[xmonader](https://github.com/xmonader) ([Python Port](https://github.com/xmonader/pygundb))**;
I am missing many others, apologies, will be adding them soon!
@ -190,7 +190,7 @@ var Gun = require('gun/gun');
If you also need to install SEA for user auth and crypto, also install some of its dependencies like this:
`npm install @trust/crypto text-encoding node-webcrypto-ossl --save`
`npm install text-encoding node-webcrypto-ossl --save`
You will need to require it too (it will be automatically added to the Gun object):
@ -208,6 +208,8 @@ To quickly spin up a Gun test server for your development team, utilize either [
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/amark/gun)
> Heroku deletes your data every 15 minutes, one way to fix this is by adding [cheap storage](https://gun.eco/docs/Using-Amazon-S3-for-Storage).
Or:
```bash

View File

@ -1,6 +1,7 @@
{
"name": "gun-server",
"website": "http://gun.js.org",
"stack": "heroku-18",
"website": "http://gun.eco/",
"repository": "https://github.com/amark/gun",
"logo": "https://avatars3.githubusercontent.com/u/8811914",
"keywords": ["node", "gun", "gunDB", "database","graph","offline-first"],

110
as.js
View File

@ -1,7 +1,73 @@
;(function(){
function as(el, gun, cb){
function as(el, gun, cb, opt){
el = $(el);
if(gun === as.gui && as.el && as.el.is(el)){ return }
opt = opt || {};
opt.match = opt.match || '{{ ';
opt.end = opt.end || ' }}';
;(function(){ // experimental
function nest(t, s,e, r, i,tmp,u){
if(r && !r.length){ return t||'' }
if(!t){ return [] }
e = e || s;
i = t.indexOf(s, i||0);
if(0 > i){ return [] }
tmp = t.indexOf(e, i+1);
if(!r){ return [t.slice(i+s.length, tmp)].concat(nest(t, s,e, r, tmp,tmp,u)) }
return t.slice(0,i)+r[0]+nest(t.slice(tmp+e.length), s,e, r.slice(1), 0,tmp,u);
}
/* experimental */
function template(tag, attr){
var html = (tag = $(tag))[0].outerHTML, sub, tmp;
if(html && (0 > html.indexOf(opt.match))){ return }
if(!attr){
$.each(tag[0].attributes, function(i,v){
if(!v){ return }
if(!nest(v.value, opt.match, opt.end).length){ return }
template(tag, v.name)
});
if((sub = tag.children()).length){
return sub.each(function(){ template(this) });
}
}
var data = [], plate = attr? tag.attr(attr) : tag.html();
tmp = nest(plate, opt.match, opt.end);
if(!tmp.length){ return }
$.each(tmp, function(pos, match){
var expr = match.split(' ');
var path = (expr[0]).split('.');
if(expr = expr.slice(1).join(' ')){
expr = new Function("_", "b", "return (_)" + expr);
}
var val = (expr && expr('')) || '';
data.push(val);
if(!attr){ tag.text(val) }
var ref = gun, sup = [], tmp;
if(tmp = tag.attr('name')){ sup.push(tmp) }
tag.parents("[name]").each(function(){
sup.push($(this).attr('name'));
});
$.each(path = sup.reverse().concat(path), function(i,v){
ref = ref.get(v);
});
ref.on(function(v){
v = data[pos] = expr? expr(v) : v;
var tmp = nest(plate, opt.match, opt.end, data);
if(attr){
tag.attr(attr, tmp);
} else {
tag.text(tmp);
}
});
});
}
template(el);
}());
as.gui = gun;
as.el = el;
if(el.data('as')){
@ -59,10 +125,12 @@
if(many && ui.is('.sort')){
var up = ui.closest("[name='#']");
var tmp = as.sort(data, up.parent().children().last());
up.insertAfter(tmp);
tmp? up.insertAfter(tmp) : up.prependTo(up.parent());
}
if(as.lock === gui){ return }
(ui[0] && u === ui[0].value)? ui.text(data) : ui.val(data);
if(!(data && data instanceof Object)){
(ui[0] && u === ui[0].value)? ui.text(data) : ui.val(data);
}
ui.data('was', data);
if(cb){
cb(data, key, ui);
@ -80,12 +148,7 @@
}, wait || 200);
}
}
as.sort = function sort(id, li){
var num = parseFloat(id);
var id = $(li).find('.sort').text() || -Infinity;
var at = num >= parseFloat(id);
return at ? li : sort(id, li.prev());
}
as.sort = function sort(num, li){ return parseFloat(num) >= parseFloat($(li).find('.sort').text() || -Infinity)? li : sort(num, li.prev()) }
$(document).on('keyup', 'input, textarea, [contenteditable]', as.wait(function(){
var el = $(this);
var data = (el[0] && u === el[0].value)? el.text() : el.val();
@ -94,7 +157,7 @@
as.lock = g;
g.put(data);
}, 99));
$(document).on('submit', 'form', function(e){ e.preventDefault() });
//$(document).on('submit', 'form', function(e){ e.preventDefault() });
var u;
window.as = as;
$.as = as;
@ -146,4 +209,31 @@
;$(function(){
$('.page').not(':first').hide();
$.as.route(location.hash.slice(1));
$(JOY.start = JOY.start || function(){ $.as(document, gun, null, JOY.opt) });
if($('body').attr('peers')){ (console.warn || console.log)('Warning: Please upgrade <body peers=""> to https://github.com/eraeco/joydb#peers !') }
});
;(function(){ // need to isolate into separate module!
var joy = window.JOY = function(){};
joy.auth = function(a,b,cb,o){
if(!o){ o = cb ; cb = 0 }
if(o === true){
gun.user().create(a, b);
return;
}
gun.user().auth(a,b, cb,o);
}
var opt = joy.opt = window.CONFIG || {}, peers;
$('link[type=peer]').each(function(){ (peers || (peers = [])).push($(this).attr('href')) });
!window.gun && (opt.peers = opt.peers || peers || (function(){
(console.warn || console.log)('Warning: No peer provided, defaulting to DEMO peer. Do not run in production, or your data will be regularly wiped, reset, or deleted. For more info, check https://github.com/eraeco/joydb#peers !');
return ['https://gunjs.herokuapp.com/gun'];
}()));
window.gun = window.gun || Gun(opt);
gun.on('auth', function(ack){
console.log("Your namespace is publicly available at", ack.soul);
});
}());

356
axe.js
View File

@ -1,99 +1,287 @@
;(function(){
/* UNBUILD */
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: {}});
USE[R(path)] = mod.exports;
}
function R(p){
return p.split('/').slice(-1).toString().replace('.js','');
}
}
if(typeof module !== "undefined"){ var common = module }
/* UNBUILD */
/* UNBUILD */
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: {}});
USE[R(path)] = mod.exports;
}
function R(p){
return p.split('/').slice(-1).toString().replace('.js','');
}
}
if(typeof module !== "undefined"){ var common = module }
/* UNBUILD */
;USE(function(module){
;USE(function(module){
if(typeof window !== "undefined"){ module.window = window }
var tmp = module.window || module;
var AXE = tmp.AXE || function(){};
if(AXE.window = module.window){ try{
AXE.window.AXE = AXE;
tmp = document.createEvent('CustomEvent');
tmp.initCustomEvent('extension', false, false, {type: "AXE"});
(window.dispatchEvent || window.fireEvent)(tmp);
window.postMessage({type: "AXE"}, '*');
} catch(e){} }
var AXE = tmp.AXE || function(){};
if(AXE.window = module.window){ AXE.window.AXE = AXE }
try{ if(typeof common !== "undefined"){ common.exports = AXE } }catch(e){}
module.exports = AXE;
})(USE, './root');
})(USE, './root');
;USE(function(module){
;USE(function(module){
var AXE = USE('./root'), Gun = (AXE.window||{}).Gun || USE('./gun', 1);
(Gun.AXE = AXE).GUN = AXE.Gun = Gun;
var AXE = USE('./root'), Gun = (AXE.window||{}).Gun || USE('./gun', 1);
(Gun.AXE = AXE).GUN = AXE.Gun = Gun;
Gun.on('opt', function(at){
if(!at.axe){
at.axe = {};
var p = at.opt.peers, tmp;
// 1. If any remembered peers or from last cache or extension
// 2. Fallback to use hard coded peers from dApp
// 3. Or any offered peers.
//if(Gun.obj.empty(p)){
// Gun.obj.map(['http://localhost:8765/gun'/*, 'https://guntest.herokuapp.com/gun'*/], function(url){
// p[url] = {url: url, axe: {}};
// });
//}
// Our current hypothesis is that it is most optimal
// to take peers in a common network, and align
// them in a line, where you only have left and right
// peers, so messages propagate left and right in
// a linear manner with reduced overlap, and
// with one common superpeer (with ready failovers)
// in case the p2p linear latency is high.
// Or there could be plenty of other better options.
console.log("axe", at.opt);
if(at.opt.super){
function verify(msg, send, at) {
var peers = Object.keys(p), puts = Object.keys(msg.put), i, j, peer;
var soul = puts[0]; /// TODO: verify all souls in puts. Copy the msg only with subscribed souls?
for (i=0; i < peers.length; ++i) {
peer = p[peers[i]];
//if (peer.url) {console.log('AXE do not reject superpeers'); send(msg, peer); continue;} /// always send to superpeers?
if (!peer.id) {console.log('AXE peer without id: ', peer); continue;}
if (!Gun.subscribe[soul] || !Gun.subscribe[soul][peer.id]) { console.log('AXE SAY reject msg to peer: %s, soul: %s', peer.id, soul); continue; }
send(msg, peer);
}
}
AXE.say = function(msg, send, at) {
if (!msg.put) { send(msg); return; }
console.log('AXE HOOK!! ', msg);
verify(msg, send, at);
};
/// TODO: remove peer from all Gun.subscribe. On `mesh.bye` event?
}
if(at.opt.super){
at.on('in', USE('./lib/super', 1), at);
} else {
//at.on('in', input, at);
}
}
this.to.next(at); // make sure to call the "next" middleware adapter.
});
Gun.on('opt', function(at){
start(at);
this.to.next(at); // make sure to call the "next" middleware adapter.
});
function input(msg){
var at = this.as, to = this.to;
}
function start(at){
if(at.axe){ return }
var opt = at.opt, peers = opt.peers;
if(false === opt.axe){ return }
if((typeof process !== "undefined") && 'false' === ''+(process.env||{}).AXE){ return }
var axe = at.axe = {}, tmp;
// 1. If any remembered peers or from last cache or extension
// 2. Fallback to use hard coded peers from dApp
// 3. Or any offered peers.
//if(Gun.obj.empty(p)){
// Gun.obj.map(['http://localhost:8765/gun'/*, 'https://guntest.herokuapp.com/gun'*/], function(url){
// p[url] = {url: url, axe: {}};
// });
//}
// Our current hypothesis is that it is most optimal
// to take peers in a common network, and align
// them in a line, where you only have left and right
// peers, so messages propagate left and right in
// a linear manner with reduced overlap, and
// with one common superpeer (with ready failovers)
// in case the p2p linear latency is high.
// Or there could be plenty of other better options.
var mesh = opt.mesh = opt.mesh || Gun.Mesh(at);
console.log("AXE enabled.");
module.exports = AXE;
})(USE, './axe');
function verify(dht, msg) {
var puts = Object.keys(msg.put);
var soul = puts[0]; /// TODO: verify all souls in puts. Copy the msg only with subscribed souls?
var subs = dht(soul);
if (!subs) { return; }
var tmp = [];
Gun.obj.map(subs.split(','), function(pid) {
if (pid in peers) {
tmp.push(pid);
mesh.say(msg, peers[pid]);
}
});
/// Only connected peers in the tmp array.
if (opt.super) {
dht(soul, tmp.join(','));
}
}
function route(get){ var tmp;
if(!get){ return }
if('string' != typeof (tmp = get['#'])){ return }
return tmp;
}
var Rad = (Gun.window||{}).Radix || USE('./lib/radix', 1);
at.opt.dht = Rad();
at.on('in', function input(msg){
var to = this.to, peer = (msg._||{}).via;
var dht = opt.dht;
var routes = axe.routes || (axe.routes = {}); // USE RAD INSTEAD! TMP TESTING!
var get = msg.get, hash, tmp;
//if(get && opt.super && peer){
if(get && opt.super && peer && (tmp = route(get))){
hash = tmp; //Gun.obj.hash(get); // USE RAD INSTEAD!
(routes[hash] || (routes[hash] = {}))[peer.id] = peer;
(peer.routes || (peer.routes = {}))[hash] = routes[hash];
/*if(soul = get['#']){ // SWITCH BACK TO USING DHT!
if(key = get['.']){
} else {
}
if (!peer.id) {console.log('[*** WARN] no peer.id %s', soul);}
var pids = joindht(dht, soul, peer.id);
if (pids) {
var dht = {};
dht[soul] = pids;
mesh.say({dht:dht}, opt.peers[peer.id]);
}
}*/
}
if((tmp = msg['@']) && (tmp = at.dup.s[tmp]) && (tmp = tmp.it)){
(tmp = (tmp._||ok)).ack = (tmp.ack || 0) + 1; // count remote ACKs to GET.
}
to.next(msg);
if (opt.rtc && msg.dht) {
Gun.obj.map(msg.dht, function(pids, soul) {
dht(soul, pids);
Gun.obj.map(pids.split(','), function(pid) {
/// TODO: here we can put an algorithm of who must connect?
if (!pid || pid in opt.peers || pid === opt.pid || opt.announce[pid]) { return; }
opt.announce[pid] = true; /// To try only one connection to the same peer.
opt.announce(pid);
});
});
}
});
//try{console.log(req.connection.remoteAddress)}catch(e){};
mesh.hear['opt'] = function(msg, peer){
if(msg.ok){ return opt.log(msg) }
var tmp = msg.opt;
if(!tmp){ return }
tmp = tmp.peers;
if(!tmp || !Gun.text.is(tmp)){ return }
if(axe.up[tmp] || 6 <= Object.keys(axe.up).length){ return }
var o = tmp; //{peers: tmp};
at.$.opt(o);
o = peers[tmp];
if(!o){ return }
o.retry = 9;
mesh.wire(o);
if(peer){ mesh.say({dam: 'opt', ok: 1, '@': msg['#']}, peer) }
}
setInterval(function(tmp){
if(!(tmp = at.stats && at.stats.stay)){ return }
(tmp.axe = tmp.axe || {}).up = Object.keys(axe.up||{});
},1000 * 60)
setTimeout(function(tmp){
if(!(tmp = at.stats && at.stats.stay)){ return }
Gun.obj.map((tmp.axe||{}).up, function(url){ mesh.hear.opt({opt: {peers: url}}) })
},1000);
if(at.opt.super){
var rotate = 0;
mesh.way = function(msg) {
if (msg.rtc) {
if (msg.rtc.to) {
/// Send announce to one peer only if the msg have 'to' attr
var peer = (peers) ? peers[msg.rtc.to] : null;
if (peer) { mesh.say(msg, peer); }
return;
}
}
if(msg.get){ mesh.say(msg, axe.up) } // always send gets up!
if(msg.get && (tmp = route(msg.get))){
var hash = tmp; //Gun.obj.hash(msg.get);
var routes = axe.routes || (axe.routes = {}); // USE RAD INSTEAD! TMP TESTING!
var peers = routes[hash];
function chat(peers, old){ // what about optimizing for directed peers?
if(!peers){ return chat(opt.peers) }
var ids = Object.keys(peers); // TODO: BUG! THIS IS BAD PERFORMANCE!!!!
var meta = (msg._||yes);
clearTimeout(meta.lack);
var id, peer, c = 1; // opt. ?redundancy?
while((id = ids[meta.turn || 0]) && c--){ // TODO: This hits peers in order, not necessarily best for load balancing. And what about optimizing for directed peers?
peer = peers[id];
meta.turn = (meta.turn || 0) + 1;
if((old && old[id]) || false === mesh.say(msg, peer)){ ++c }
}
//console.log("AXE:", Gun.obj.copy(msg), meta.turn, c, ids, opt.peers === peers);
if(0 < c){
if(peers === opt.peers){ return } // prevent infinite lack loop.
return meta.turn = 0, chat(opt.peers, peers)
}
var hash = msg['##'], ack = meta.ack;
meta.lack = setTimeout(function(){
if(ack && hash && hash === msg['##']){ return }
if(meta.turn >= (axe.turns || 3)){ return } // variable for later! Also consider ACK based turn limit.
//console.log(msg['#'], "CONTINUE:", ack, hash, msg['##']);
chat(peers, old); // keep asking for data if there is mismatching hashes.
}, 25);
}
return chat(peers);
}
// TODO: PUTs need to only go to subs!
if(msg.put){
var routes = axe.routes || (axe.routes = {}); // USE RAD INSTEAD! TMP TESTING!
var peers = {};
Gun.obj.map(msg.put, function(node, soul){
var hash = soul; //Gun.obj.hash({'#': soul});
var to = routes[hash];
if(!to){ return }
Gun.obj.to(to, peers);
});
mesh.say(msg, peers);
return;
}
mesh.say(msg, opt.peers); return; // TODO: DISABLE THIS!!! USE DHT!
if (!msg.put) { mesh.say(msg); return; }
//console.log('AXE HOOK!! ', msg);
verify(opt.dht, msg);
};
} else {
mesh.route = function(msg) {
if (msg.rtc) {
}
if (!msg.put) { mesh.say(msg); return; }
verify(opt.dht, msg);
/// Always send to superpeers?
Gun.obj.map(peers, function(peer) {
if (peer.url) {
mesh.say(msg, peer);
}
});
};
/*var connections = 0; // THIS HAS BEEN MOVED TO CORE NOW!
at.on('hi', function(opt) {
this.to.next(opt);
//console.log('AXE PEER [HI]', new Date(), opt);
connections++;
/// The first connection don't need to resubscribe the nodes.
if (connections === 1) { return; }
/// Resubscribe all nodes.
setTimeout(function() {
var souls = Object.keys(at.graph);
for (var i=0; i < souls.length; ++i) {
//at.gun.get(souls[i]).off();
at.next[souls[i]].ack = 0;
at.gun.get(souls[i]).once(function(){});
}
//location.reload();
}, 500);
}, at);*/
}
axe.up = {};
at.on('hi', function(peer){
this.to.next(peer);
if(!peer.url){ return }
axe.up[peer.id] = peer;
})
at.on('bye', function(peer){ this.to.next(peer);
if(peer.url){ delete axe.up[peer.id] }
Gun.obj.map(peer.routes, function(route, hash){
delete route[peer.id];
if(Gun.obj.empty(route)){
delete axe.routes[hash];
}
});
});
}
function joindht(dht, soul, pids) {
if (!pids || !soul || !dht) { return; }
var subs = dht(soul);
var tmp = subs ? subs.split(',') : [];
Gun.obj.map(pids.split(','), function(pid) {
if (pid && tmp.indexOf(pid) === -1) { tmp.push(pid); }
});
tmp = tmp.join(',');
dht(soul, tmp);
return tmp;
}
var empty = {}, yes = true, u;
module.exports = AXE;
})(USE, './axe');
}());

View File

@ -8,8 +8,11 @@
</head>
<body>
<h3 id="pid"></h3>
<script src="../gun.js"></script>
<!-- <script src="../axe.js"></script> -->
<script src="../gun/axe.js"></script>
<script src="../gun/lib/radix.js"></script>
<script src="../gun/lib/webrtc.js"></script>
<!-- <script src="../sea.js"></script> -->
<script>
var pid = location.hash.slice(1);
@ -23,14 +26,17 @@
Gun.on('opt', function(ctx) {
this.to.next(ctx);
ctx.on('hi', function(opt) {
console.log('HI!! PEER', new Date(), opt.pid);
// console.log('HI!! PEER', new Date(), opt.pid);
setTimeout(function() {
document.getElementById('pid').innerHTML = gun._.opt.pid;
});
});
if (pid) {
ctx.on('out', function(msg) {
msg.pid = pid;
this.to.next(msg);
});
}
// if (pid) {
// ctx.on('out', function(msg) {
// msg.pid = pid;
// this.to.next(msg);
// });
// }
});
var gun = Gun(opt);

View File

@ -0,0 +1,120 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
<link href='https://fonts.googleapis.com/css?family=Poiret+One' rel='stylesheet' type='text/css'>
</head>
<body>
<h1><button id="left">&larr;</button> <span id="date"></span> Schedule <button id="right">&rarr;</button></h1>
<form id="add">
<style>input[type="number"]{ width: 4em; }</style>
<input id="what" placeholder="What?">
<input id="where" placeholder="Where?">
<input type="number" id="hour"><script>hour.value = new Date().getHours() % 12 || 12</script> :
<input type="number" id="min" value="0">
<select id="ampm">
<option value="">am</option>
<option value="1">pm</option>
<script>ampm.children[new Date().getHours() < 12? 0 : 1].selected='selected'</script>
</select>
<input id="id" type="hidden">
<input id="go" type="submit" value="add">
<div id="err"></div>
</form>
<style>
.none { display: none; }
p, ul, li { list-style-type: none; margin: 0; padding: 0; }
</style>
<ul></ul>
<div class="model none">
<li>
<b class="when"></b>
<span class="what"></span>
<u class="where"></u>
<span class="sort none">0</span>
<button class="edit"><</button>
</li>
</div>
<script src="../jquery.js"></script>
<script src="../../../gun/gun.js"></script>
<script src="../../../gun/nts.js"></script>
<script src="../../../gun/lib/webrtc.js"></script>
<script>
var name = 'schedule/' + location.hash.slice(1);
var gun = Gun(location.origin + '/gun');
//var gun = Gun('http://localhost:8765/gun');
//var gun = Gun();
$('#add').on('submit', function(event){
event.preventDefault();
event = {};
if(!schedule.on){ return err.innerText = "No date!" }
event.when = new Date(schedule.on.getFullYear(), schedule.on.getMonth(), schedule.on.getDate(), hour.value % 12 + (ampm.value? 12 : 0), min.value).getTime();
if(!(event.what = what.value)){ return err.innerText = "No description!" }
if(!(event.where = where.value)){ return err.innerText = "No location!" }
var day = gun.get(name+now(event.when));
day.get(id.value || Gun.text.random(9)).put(event);
what.value = where.value = id.value = err.innerText = '';
go.value = 'add';
schedule(event.when);
});
function schedule(ms){
var day = new Date(ms);
if(schedule.on && schedule.on.toLocaleDateString() === day.toLocaleDateString()){ return } schedule.on = day;
$('#date').text(day.getFullYear()+' '+ day.toString().split(' ')[1] +' '+day.getDate());
day = gun.get(name+now(ms));
$('ul').empty();
day.map().on(UI);
}
schedule(+new Date());
$('#left').on('click', function(){ schedule(+new Date(schedule.on.getFullYear(), schedule.on.getMonth(), schedule.on.getDate() - 1)) });
$('#right').on('click', function(){ schedule(+new Date(schedule.on.getFullYear(), schedule.on.getMonth(), schedule.on.getDate() + 1)) });
function UI(event, id){
if(!event){ return }
var when = new Date(event.when);
if(schedule.on && when.toLocaleDateString() !== schedule.on.toLocaleDateString()){ return }
var ul = $('ul')
var li = $("#cal-" + id)[0]; // grab if exists
if(!li){
li = $('.model li').clone(true) // else create it
.attr('id', 'cal-' + id);
}
li = (UI.last = sort(event.when, ul.children('li').last())[0])? $(li).insertAfter(UI.last) : $(li).prependTo(ul);
li.find('.what').text(event.what);
li.find('.where').text(event.where);
li.find('.sort').text(event.when);
li.find('.edit').val(id);
var time = when.toLocaleTimeString();
li.find('.when').text(time.split(':').slice(0,2).join(':') + time.slice(-2));
};
$(document).on('click', '.edit', function(){
go.value = 'update';
id.value = this.value;
what.value = $(this).parent().find('.what').text();
where.value = $(this).parent().find('.where').text();
var when = new Date(parseFloat($(this).parent().find('.sort').text()));
hour.value = when.getHours() % 12 || 12;
min.value = when.getMinutes();
ampm.value = when.getHours() < 12? '' : 1;
what.focus();
});
function now(t){
return new Date(t || Gun.state()).toLocaleDateString().split('/').reverse().join('/')
}
function sort(num, li){ return parseFloat(num) >= parseFloat($(li).find('.sort').text() || -Infinity)? li : sort(num, li.prev()) }
</script>
</body>
</html>

View File

@ -1,161 +1,213 @@
<!DOCTYPE html>
<html>
<head>
<title>Converse</title>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
<link rel="stylesheet" type="text/css" href="/style.css">
<link href='https://fonts.googleapis.com/css?family=Poiret+One' rel='stylesheet' type='text/css'>
<style>
#converse {
font-size: 16pt;
}
#converse .box {
margin-bottom: 0.2em;
padding: 1em;
border-radius: 0.1em;
}
#converse b:after {
content: " ";
}
#converse li .when {
position: absolute;
top: 0;
right: -2em;
padding: 0.5em 1em;
background: rgba(100%,100%,100%,0.9);
opacity: 0;
}
#converse li:hover .when {
opacity: 1;
right: 0em;
}
.poiret {
font-family: 'Poiret One', sans-serif;
}
.large {
font-size: 200%;
}
#converse .what, #converse .who {
cursor: text;
outline: none;
display: inline;
min-width: 1em;
padding-left: 1px;
}
[contentEditable=true]:empty:not(:focus):before{
content:attr(data-text)
}
#title {
margin-bottom: 0.5em;
}
<head>
<title>Converse</title>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
<link rel="stylesheet" type="text/css" href="/style.css">
<link href='https://fonts.googleapis.com/css?family=Poiret+One' rel='stylesheet' type='text/css'>
<style>
.chat__heading {
position: fixed;
text-align: center;
z-index: 1;
width: 100%;
margin-top: 0;
margin-bottom: 0;
}
.say {
margin: 0 0 0.4em 0.4em;
padding: 0.2em 0.5em;
}
.chat__form-container {
display: flex;
justify-content: center;
width: 100%;
padding: 10px 20px;
position: fixed;
z-index: 1;
bottom: 0;
}
#name-input {
margin-top: 0.5em;
margin-right: 0.5em;
}
.chat__form {
display: flex;
justify-content: center;
height: 50px;
background-color: white;
border: 2px solid white;
max-width: 900px;
width: 100%;
border-radius: 5px;
}
#message-input {
margin-top: 0.5em;
}
</style>
</head>
<body>
<div id="converse" class="hue2 page">
<div class="pad">
<div id='title' class="poiret large rubric whitet">Have a Conversation...</div>
<div>
<ul>
<li class="none"></li>
</ul>
.chat__name-input {
flex: 1;
padding: 10px;
}
<form class="white huet2 box">
<div>
<div class="say hue2 right whitet box act">say</div>
<b id="name-input" class="jot left who" contenteditable="true" data-text="Name"></b>
<p id="message-input" class="jot left what" contenteditable="true" data-text="Write a message..."></p>
</div>
</form>
.chat__message-input {
flex: 5;
padding: 10px;
}
<div class="model">
<li class="white huet2 box">
<b class="who"></b>
<p class="what"></p>
<span class="sort none">0</span>
<div class="when"></div>
</li>
</div>
</div>
</div>
</div>
.chat__submit {
padding: 10px;
color: white;
border-radius: 5px;
}
<script src="/jquery.js"></script>
<script src="/gun.js"></script>
<script src="/gun/nts.js"></script>
<script>
var gun = Gun(location.origin + '/gun');
var chat = gun.get('converse/' + location.hash.slice(1));
.chat__submit:hover::after {
background-color: rgba(0,0,0,0.2);
}
$("form .say").on('click', submit);
$("form .what").on('keydown', enter);
function enter(e){
if(e.which !== 13){ return }
submit(e);
}
function submit(e){
e.preventDefault();
.chat__submit:focus::after {
background-color: rgba(0,0,0,0.2);
}
var msg = {when: Gun.time.is()};
.chat__submit::after {
content: '';
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
border-radius: 5px;
transition: background-color 0.3s;
background-color: rgba(0,0,0,0);
}
msg.who = $('form .who').text();
if(!msg.who){
msg.who = 'user' + Gun.text.random(3);
$('form .who').text(msg.who);
}
.chat__message-list {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
overflow-y: auto;
padding: 60px 20px;
width: 100%;
background-color: rgba(0, 0, 0, 0.2);
min-height: 100vh;
}
msg.what = $('form .what').text();
if(!msg.what){ return }
.chat__message {
display: flex;
flex-wrap: wrap;
margin-bottom: 10px;
padding: 10px;
border-radius: 5px;
width: 100%;
position: relative;
max-width: 900px;
}
chat.set(msg);
$('form .what').text('');
}
.chat__name {
margin-right: 20px;
}
chat.map().val(function(msg, id){
if(!msg){ return }
var ul = $('ul');
var last = sort(msg.when, ul.children('li').last());
.chat__when {
position: absolute;
top: 0;
right: 2em;
padding: 10px;
background: rgba(100%, 100%, 100%, 0.9);
opacity: 0;
border-radius: 5px;
}
var li = $("#msg-" + id)[0]; // grab if exists
if(!li){
li = $('.model li').clone(true) // else create it
.attr('id', 'msg-' + id)
.insertAfter(last);
}
.chat__message:hover .chat__when {
opacity: 1;
right: 0em;
}
// bind the message data into the UI
li = $(li);
li.find('.who').text(msg.who);
li.find('.what').text(msg.what);
li.find('.sort').text(msg.when);
@media (max-width: 567px) {
.chat__heading {
font-size: 30px;
}
}
</style>
</head>
var time = new Date(msg.when);
li.find('.when').text(time.toDateString() + ', ' + time.toLocaleTimeString());
<body>
<div class="chat hue2 page">
<h2 id='title' class="chat__heading hue2 whitet">Have a Conversation...</h2>
<ul class="chat__message-list">
<li class="none"></li>
</ul>
$('html, body').stop(true, true)
.animate({scrollTop: ul.height()});
});
<div class="chat__form-container hue2">
<form class="chat__form">
<label for="name-input" class="visually-hidden">Name</label>
<input id="name-input" class="chat__name-input" placeholder="Name"></input>
<label for="message-input" class="visually-hidden">Message</label>
<input id="message-input" class="chat__message-input" placeholder="Write a message..."></input>
<button class="chat__submit say hue2">say</button>
</form>
</div>
function sort(id, li){
var num = parseFloat(id);
var id = $(li).find('.sort').text() || -Infinity;
var at = num >= parseFloat(id);
return at ? li : sort(id, li.prev());
}
</script>
</body>
</html>
<div class="model">
<li class="chat__message white huet2 box">
<b class="chat__name"></b>
<p class="chat__message-text"></p>
<span class="sort none">0</span>
<div class="chat__when"></div>
</li>
</div>
</div>
<script src="/jquery.js"></script>
<script src="/gun.js"></script>
<script src="/gun/nts.js"></script>
<script>
var gun = Gun(location.origin + '/gun');
var chat = gun.get('converse/' + location.hash.slice(1));
$(".chat__submit").on('click', submit);
$(".chat_form").on('keydown', enter);
function enter(e) {
if (e.which !== 13) { return }
submit(e);
}
function submit(e) {
e.preventDefault();
var msg = { when: Gun.time.is() };
msg.who = $('.chat__name-input').val();
if (!msg.who) {
msg.who = 'user' + Gun.text.random(3);
$('.chat__name-input').val(msg.who);
}
msg.what = $('.chat__message-input').val();
if (!msg.what) { return }
chat.set(msg);
$('.chat__message-input').val('').focus();
}
chat.map().val(function (msg, id) {
if (!msg) { return }
var messageList = $('.chat__message-list');
var last = sort(msg.when, messageList.children('li').last());
var li = $("#msg-" + id)[0]; // grab if exists
if (!li) {
li = $('.model li').clone(true) // else create it
.attr('id', 'msg-' + id)
.insertAfter(last);
}
// bind the message data into the UI
li = $(li);
li.find('.chat__name').text(msg.who);
li.find('.chat__message-text').text(msg.what);
li.find('.sort').text(msg.when);
var time = new Date(msg.when);
li.find('.chat__when').text(time.toDateString() + ', ' + time.toLocaleTimeString());
$('html, body').stop(true, true)
.animate({ scrollTop: messageList.height() });
});
function sort(num, li) { return parseFloat(num) >= parseFloat($(li).find('.sort').text() || -Infinity) ? li : sort(num, li.prev()) }
</script>
</body>
</html>

View File

@ -3,7 +3,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body style="text-align: center;">
<h1 id="when" style="font-size: 7vw; margin-top: 43vh;"></h1>
<h1 id="when" style="font-size: 7vw; margin-top: 43vh; font-family: monospace;"></h1>
</body>
<script src="/gun.js"></script>
<script src="/gun/nts.js"></script>
@ -16,4 +16,4 @@
when.innerHTML = print;
});
</script>
</html>
</html>

View File

@ -221,7 +221,7 @@
});
function send(raw){
if(!raw){ return }
if(raw.indexOf('webrtc') >= 0){
if(raw.indexOf('rtc') >= 0){
if(!this._send){ return }
return this._send(raw);
}

View File

@ -1,21 +1,23 @@
;(function(){
var cluster = require('cluster');
if(cluster.isMaster){
return cluster.fork() && cluster.on('exit', function(){ cluster.fork() });
}
var fs = require('fs');
var config = { port: process.env.OPENSHIFT_NODEJS_PORT || process.env.VCAP_APP_PORT || process.env.PORT || process.argv[2] || 8765 };
var Gun = require('../'); // require('gun')
if(process.env.HTTPS_KEY){
config.key = fs.readFileSync(process.env.HTTPS_KEY);
config.cert = fs.readFileSync(process.env.HTTPS_CERT);
config.server = require('https').createServer(config, Gun.serve(__dirname));
} else {
config.server = require('http').createServer(Gun.serve(__dirname));
}
var gun = Gun({web: config.server.listen(config.port) });
console.log('Relay peer started on port ' + config.port + ' with /gun');
;(function(){
var cluster = require('cluster');
if(cluster.isMaster){
return cluster.fork() && cluster.on('exit', function(){ cluster.fork() });
}
var fs = require('fs');
var config = { port: process.env.OPENSHIFT_NODEJS_PORT || process.env.VCAP_APP_PORT || process.env.PORT || process.argv[2] || 8765 };
var Gun = require('../'); // require('gun')
if(process.env.HTTPS_KEY){
config.key = fs.readFileSync(process.env.HTTPS_KEY);
config.cert = fs.readFileSync(process.env.HTTPS_CERT);
config.server = require('https').createServer(config, Gun.serve(__dirname));
} else {
config.server = require('http').createServer(Gun.serve(__dirname));
}
var gun = Gun({web: config.server.listen(config.port)});
console.log('Relay peer started on port ' + config.port + ' with /gun');
module.exports = gun;
}());

View File

@ -8,6 +8,8 @@
# an installer that will automatically do it for you.
#debian/ubuntu
su -
apt-get install sudo -y
sudo apt-get update -y
sudo apt-get install curl git git-core -y
#fedora/openSUSE

View File

@ -4,7 +4,7 @@
<title>Party by Neon ERA</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="./style.css">
<link href="https://fonts.googleapis.com/css?family=Alegreya+Sans:300italic" rel="stylesheet" type="text/css">
<link href="https://fonts.googleapis.com/css?family=Raleway:100" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Caveat" rel="stylesheet">
<style>
.write {
@ -69,13 +69,30 @@
#hi .faces img {
w-idth: 5%;
width: 3em;
width: 7vh;
}
#hi .ton {
border-radius: 1em;
font-size: 150%;
font-size: 2.25vmax;
margin: 0 0.5em 0 0.5em;
background: transparent;
border: 1px solid white;
color: white;
font-family: 'Raleway', sans-serif;
}
#hi .ton:hover {
background: white;
color: black;
}
</style>
<div class="loud write shout rim">Party by NEON ERA.</div>
<div class="loud write shout rim">Join the Private Party!</div>
<div id="faces" class="flush faces">
<div class="right" style="max-width: 20em;">
<input id="halias" class="write jot sap" placeholder="username">
<input id="hpass" type="password" class="write jot sap" placeholder="passphrase">
<div class="right" style="max-width: 30em;">
<a href="chrome://extensions" target="_blank"><button class="ton">Install Now</button></a>
<a href="#"><button class="ton">How It Works</button></a>
<!-- input id="halias" class="write jot sap" placeholder="username">
<input id="hpass" type="password" class="write jot sap" placeholder="passphrase" -->
<script>
$.as.route.page('hi', () => {
$('#hpass').on('focus', () => {
@ -136,6 +153,14 @@
return faces;
});
</script>
<div class="pad ditch">
<p class="loud"><i>Your friend has invited you to add a privacy extension to your browser:</i></p>
<p> - Decrypts your friends' messages across any site!</p>
<p> - Stops tech monopolies from selling your private data to advertisers.</p>
<p> - Gives you ownership and control over all your data online.</p>
<p> - Creates a searchable history of your posts, friends, and more!</p>
</div>
<div id="faces2" class="flush faces"></div>
<div class="pad ditch">
<p class="loud"><i>Express your thoughts & connect with the world around you!</i></p>
<p> - Discover new relationships.</p>
@ -143,12 +168,10 @@
<p> - Watch fun videos and photos from people who share.</p>
<p> - But this time, you own it: fully decentralized.</p>
</div>
<div id="faces2" class="flush faces">
</div>
<div class="pad ditch" style="margin-top: 1em;">
<p><span class="loud write shout">Welcome</span><span class="write loud">, you are currently connected to <b id="peers" class="huet4">2</b> peers. <b>Why not try to sign up or log in?</b></span></p>
<p><span class="loud write shout">Welcome,</span><!-- span class="write loud">, you are currently connected to <b id="peers" class="huet4">2</b> peers. <b>Why not try to sign up or log in?</b></span --></p>
<p> - Your identity is created here, by you. Not on a server.</p>
<p> - It uses secure <a href="https://gun.eco/explainers/data/security.html">cryptographic</a> methods to protect you.</p>
<p> - It uses secure <a href="https://gun.eco/docs/Cartoon-Cryptography">cryptographic</a> methods to protect you.</p>
<p> - Only you have access to it, meaning even we cannot reset your password!</p>
<p> - For added security, you can freely <a href="https://github.com/amark/gun">download</a> and run it on your own computer.</p>
</div>
@ -718,7 +741,7 @@
if(e.err){ return }
var m = $($("#d"+e.id)[0] || $('#d0').clone(true,true).attr('id', 'd'+e.id).css('backgroundImage', '').appendTo('#draft')).addClass('pulse');
if(up){ return up.shrink(e, resize, 1000) }
console.log(e.id, e.base64);
//console.log(e.id, e.base64);
m.removeClass('pulse').css({
backgroundImage: 'url(' + e.base64 + ')',
backgroundRepeat: 'no-repeat',
@ -727,7 +750,7 @@
});
});
</script>
<!-- script async src="../../gun/lib/fun.js"></script -->
<script async src="../../gun/lib/fun.js"></script>
<script async src="../../gun/lib/normalize.js"></script>
<script async src="../../gun/lib/monotype.js"></script>
<script async src="../../gun/lib/meta.js"></script>

22
examples/start.js.html Normal file
View File

@ -0,0 +1,22 @@
/*<!DOCTYPE html>
<html>
<head></head>
<body></body>
<script>// */
;(function(){try{
if(typeof window == "undefined"){ return }
var url = location.hash.slice(1);
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(e){
if(4 != xhr.readyState){ return }
var d = document, doc = d.createElement("html");
doc.innerHTML = xhr.responseText;
var head = doc.getElementsByTagName('head')[0] || doc.appendChild(d.createElement('head'));
var base = doc.getElementsByTagName('base')[0] || head.appendChild(d.createElement('base'));
base.href = url;
document.write(doc.outerHTML);
};
xhr.open('GET', url, true);
xhr.send(null);
}catch(e){document.write(''+e)}}());
//</script></html>

146
examples/stats.html Normal file
View File

@ -0,0 +1,146 @@
<!DOCTYPE HTML>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="./style.css">
</head>
<body>
<style>
@import url('https://fonts.googleapis.com/css?family=Oxygen');
html, body {
font-family: "Oxygen", sans-serif;
}
svg, .ct-chart * {
overflow: visible;
}
.ct-series-a .ct-line,
.ct-series-a .ct-point {
str-oke: blue !important;
}
.ct-series-b .ct-line,
.ct-series-b .ct-point {
stroke: green !important;
}
.tall { height: 10em; }
</style>
<input id="url" class="center none" placeholder="enter peer stats source url">
<div class="center"><span class="shout" id="peers">0</span> peers <span class="shout" id="time">0</span> min <span class="shout" id="nodes">0</span> nodes <span class="shout" id="hours">0</span> hours</div>
<div class="leak" style="padding: 0 2em;">
<div class="leak ct-mem ct-chart ct-perfect-fourth tall"></div>
</div>
<div class="center leak" style="padding: 0 2em;">
<div class="unit col leak ct-damc ct-chart tall" style="width: 49%;"></div>
<div class="unit col leak ct-damd ct-chart tall" style="width: 49%;"></div>
</div>
<div class="center leak" style="padding: 0 2em;">
<div class="unit col leak ct-radc ct-chart tall" style="width: 49%;"></div>
<div class="unit col leak ct-radt ct-chart tall" style="width: 49%;"></div>
</div>
<div class="center"><span id="rerr"></span></div>
<div class="center leak" style="padding: 0 2em;">
<div class="leak ct-cpu ct-chart ct-perfect-fourth tall"></div>
</div>
<script src="./jquery.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/chartist.js/latest/chartist.min.css">
<script src="https://cdn.jsdelivr.net/chartist.js/latest/chartist.min.js"></script>
<script>
var stats = {slide: [0,0,0,0,0], din: [0,0,0,0,0], dout: [0,0,0,0,0], dind: [0,0,0,0,0], doutd: [0,0,0,0,0], rgetc: [0,0,0,0,0], rputc: [0,0,0,0,0]};
setInterval(function(){
stats.show();
}, 1000 * 15);
stats.show = async function(data){ //$.getJSON(url.value||(location.origin+'/gun/stats.radata'), function(data){ console.log(data);
data = await (await fetch(url.value||(location.origin+'/gun/stats.radata'), {method: 'GET',mode: 'cors'})).json();
console.log(data);
$('#peers').text(data.peers.count);
$('#time').text((data.peers.time / 1000 / 60).toFixed(0));
$('#nodes').text(data.node.count);
$('#hours').text((data.up.time / 60 / 60).toFixed(0));
$('#dinc').text(data.dam.in.count);
$('#dind').text((data.dam.in.done / 1024 / 1024).toFixed(1));
$('#doutc').text(data.dam.out.count);
$('#doutd').text((data.dam.out.done / 1024 / 1024).toFixed(1));
stats.slide.push(data.memory.heapTotal / 1024 / 1024); stats.slide = stats.slide.slice(1);
new Chartist.Line('.ct-mem', {
// A labels array that can contain any sort of values
labels: ['-1min', '-45s', '-30s', '-15s', '0'],
// Our series array that contains series objects or in this case series data arrays
series: [stats.slide]
}, {fullWidth: true, low: 0, axisY: {
labelInterpolationFnc: function(v) { return v+'MB' }
}});
stats.din.push(data.dam['in'].count); stats.din = stats.din.slice(1);
stats.dout.push(data.dam.out.count); stats.dout = stats.dout.slice(1);
new Chartist.Line('.ct-damc', {
// A labels array that can contain any sort of values
labels: ['-1min', '-45s', '-30s', '-15s', '0'],
// Our series array that contains series objects or in this case series data arrays
series: [stats.dout, stats.din]
}, {fullWidth: true, axisY: {
labelInterpolationFnc: function(v) { return v+'msgs' }
}});
stats.dind.push(data.dam['in'].done / 1024 / 1024); stats.dind = stats.dind.slice(1);
stats.doutd.push(data.dam.out.done / 1024 / 1024); stats.doutd = stats.doutd.slice(1);
new Chartist.Line('.ct-damd', {
// A labels array that can contain any sort of values
labels: ['-1min', '-45s', '-30s', '-15s', '0'],
// Our series array that contains series objects or in this case series data arrays
series: [stats.doutd, stats.dind]
}, {fullWidth: true, axisY: {
labelInterpolationFnc: function(v) { return v+'MB' }
}});
try{ $('#rerr').text(data.rad.put.err || data.rad.get.err) }catch(e){}
try{
stats.rgetc.push(data.rad.get.count); stats.rgetc = stats.rgetc.slice(1);
stats.rputc.push(data.rad.put.count); stats.rputc = stats.rputc.slice(1);
new Chartist.Line('.ct-radc', {
// A labels array that can contain any sort of values
labels: ['-1min', '-45s', '-30s', '-15s', '0'],
// Our series array that contains series objects or in this case series data arrays
series: [stats.rputc, stats.rgetc]
}, {fullWidth: true, axisY: {
labelInterpolationFnc: function(v) { return v+'io' }
}});
}catch(e){}
try{
stats.radgt = Object.values(data.rad.get.time).map(function(n){ return n/1000 });
stats.radpt = Object.values(data.rad.put.time).map(function(n){ return n/1000 });
new Chartist.Line('.ct-radt', {
// A labels array that can contain any sort of values
//labels: ['-1min', '-45s', '-30s', '-15s', '0'],
// Our series array that contains series objects or in this case series data arrays
series: [stats.radpt, stats.radgt]
}, {fullWidth: true, axisY: {
labelInterpolationFnc: function(v) { return v+'sec' }
}});
}catch(e){}
new Chartist.Line('.ct-cpu', {
// A labels array that can contain any sort of values
labels: ['-15min', '-5min', '1min'],
// Our series array that contains series objects or in this case series data arrays
series: [data.cpu.loadavg.reverse()]
}, {fullWidth: true, low: 0, axisY: {
labelInterpolationFnc: function(v) { return v+'cpu' }
}});
//})
}
stats.show();
</script>
</body>
</html>

View File

@ -1,38 +1,26 @@
@import url(https://fonts.googleapis.com/css?family=Oxygen);
html, body {
margin: 0;
padding: 0;
font-family: 'Oxygen', 'Trebuchet MS', arial;
position: relative;
background: black;
color: white;
line-height: 1.5;
font-size: 18pt;
}
body {
font-size: 18pt;
}
div, ul, ol, li, p, span, form, button, input, textarea {
div, ul, ol, li, p, span, form, button, input, textarea, img {
margin: 0;
padding: 0;
position: relative;
overflow: hidden;
font-size: 1em;
line-height: 1.5em;
vertical-align: inherit;
-webkit-transition: all 0.3s;
transition: all 0.3s;
box-sizing: border-box;
}
button, input, textarea {
background: white;
border: none;
color: black;
}
a {
color: white;
a, button, input, textarea {
background: inherit;
border: inherit;
color: inherit;
text-decoration: inherit;
}
input, textarea {
@ -43,6 +31,10 @@ ul, li {
list-style: none;
}
[contenteditable=true]:empty:before {
content: attr(placeholder);
}
.model, .none { display: none }
.hide {
opacity: 0;
@ -50,48 +42,75 @@ ul, li {
transition: all 2s;
}
.page {
.full, .page {
width: 100%;
min-height: 100vh;
}
.max {
max-width: 48em;
}
.min {
min-width: 12em;
}
.pad {
margin: 5% auto;
min-width: 250px;
width: 95%;
max-width: 50em;
margin: 5% auto;
max-width: 48em;
min-width: 12em;
}
.right {
float: right;
text-align: right;
}
.left {
float: left;
text-align: left;
}
.center {
text-align: center;
vertical-align: middle;
margin-left: auto;
margin-right: auto;
}
.mid {
margin-left: auto;
margin-right: auto;
}
.flush {
line-height: 0em;
}
.rim {
margin: 2%;
}
.gap {
padding: 3%;
}
.gully {
margin-bottom: 1%;
.top {
vertical-align: top;
}
.low {
vertical-align: bottom;
}
.rim { margin: 2%; }
.gap { padding: 3%; }
.stack { line-height: 0; }
.crack { margin-bottom: 1%; }
.sit { margin-bottom: 0; }
.row { width: 100%; }
.col { max-width: 33em; }
.col {
max-width: 24em;
min-width: 12em;
}
.focus {
margin-left: auto;
margin-right: auto;
float: none;
clear: both;
}
.unit, .symbol {
display: inline-block;
vertical-align: inherit;
}
.leak { overflow: visible; }
.hold { overflow: hidden; }
.act {
display: block;
font-weight: normal;
@ -100,102 +119,68 @@ ul, li {
transition: all 0.3s;
cursor: pointer;
}
.symbol {
display: inline-block;
}
.sap { border-radius: 0.1em; }
.jot { border-bottom: 1px dashed #95B2CA; }
.loud {
font-size: 150%;
}
.jot {
border-bottom: 1px dashed #95B2CA;
}
.sap {
border-radius: 0.1em;
.shout {
font-size: 36pt;
font-size: 6.5vmax;
}
.red {
background: #ea3224;
}
.green {
background: #33cc33;
}
.blue {
background: #4D79D8;
}
.yellow {
background: #f2b919;
}
.black {
background: black;
}
.white {
background: white;
}
.red { background: #ea3224; }
.green { background: #33cc33; }
.blue { background: #4D79D8; }
.yellow { background: #d3a438; }
.black { background: black; }
.white { background: white; }
.shade {
background: rgba(0%, 0%, 0%, 0.1);
}
.tint {
background: rgba(100%, 100%, 100%, 0.1);
}
.shade { background: rgba(0%, 0%, 0%, 0.1); }
.tint { background: rgba(100%, 100%, 100%, 0.1); }
.redt {
color: #ea3224;
}
.greent {
color: #33cc33;
}
.bluet {
color: #4D79D8;
}
.yellowt {
color: #f2b919;
}
.blackt {
color: black;
}
.whitet {
color: white;
}
.redt { color: #ea3224; }
.greent { color: #33cc33; }
.bluet { color: #4D79D8; }
.yellowt { color: #d3a438; }
.blackt { color: black; }
.whitet { color: white; }
.hue {
background: #4D79D8;
-webkit-animation: hue 900s infinite;
animation: hue 900s infinite;
}
@keyframes hue {
} @keyframes hue {
0% {background-color: #4D79D8;}
25% {background-color: #33cc33;}
50% {background-color: #f2b919;}
50% {background-color: #d3a438;}
75% {background-color: #ea3224;}
100% {background-color: #4D79D8;}
} @-webkit-keyframes hue {
0% {background-color: #4D79D8;}
25% {background-color: #33cc33;}
50% {background-color: #f2b919;}
50% {background-color: #d3a438;}
75% {background-color: #ea3224;}
100% {background-color: #4D79D8;}
}
.huet {
color: #4D79D8;
color: #4D79D8;
-webkit-animation: huet 900s infinite;
animation: huet 900s infinite;
}
@keyframes huet {
} @keyframes huet {
0% {color: #4D79D8;}
25% {color: #33cc33;}
50% {color: #f2b919;}
50% {color: #d3a438;}
75% {color: #ea3224;}
100% {color: #4D79D8;}
} @-webkit-keyframes huet {
0% {color: #4D79D8;}
25% {color: #33cc33;}
50% {color: #f2b919;}
50% {color: #d3a438;}
75% {color: #ea3224;}
100% {color: #4D79D8;}
}
@ -204,19 +189,17 @@ ul, li {
background: #ea3224;
-webkit-animation: hue2 900s infinite;
animation: hue2 900s infinite;
}
@keyframes hue2 {
} @keyframes hue2 {
0% {background-color: #ea3224;}
25% {background-color: #4D79D8;}
50% {background-color: #33cc33;}
75% {background-color: #f2b919;}
75% {background-color: #d3a438;}
100% {background-color: #ea3224;}
} @-webkit-keyframes hue2 {
0% {background-color: #ea3224;}
25% {background-color: #4D79D8;}
50% {background-color: #33cc33;}
75% {background-color: #f2b919;}
75% {background-color: #d3a438;}
100% {background-color: #ea3224;}
}
@ -224,19 +207,17 @@ ul, li {
color: #ea3224;
-webkit-animation: huet2 900s infinite;
animation: huet2 900s infinite;
}
@keyframes huet2 {
} @keyframes huet2 {
0% {color: #ea3224;}
25% {color: #4D79D8;}
50% {color: #33cc33;}
75% {color: #f2b919;}
75% {color: #d3a438;}
100% {color: #ea3224;}
} @-webkit-keyframes huet2 {
0% {color: #ea3224;}
25% {color: #4D79D8;}
50% {color: #33cc33;}
75% {color: #f2b919;}
75% {color: #d3a438;}
100% {color: #ea3224;}
}
@ -244,17 +225,15 @@ ul, li {
background: #33cc33;
-webkit-animation: hue3 900s infinite;
animation: hue3 900s infinite;
}
@keyframes hue3 {
} @keyframes hue3 {
0% {background-color: #33cc33;}
25% {background-color: #f2b919;}
25% {background-color: #d3a438;}
50% {background-color: #ea3224;}
75% {background-color: #4D79D8;}
100% {background-color: #33cc33;}
} @-webkit-keyframes hue3 {
0% {background-color: #33cc33;}
25% {background-color: #f2b919;}
25% {background-color: #d3a438;}
50% {background-color: #ea3224;}
75% {background-color: #4D79D8;}
100% {background-color: #33cc33;}
@ -264,74 +243,64 @@ ul, li {
color: #33cc33;
-webkit-animation: huet3 900s infinite;
animation: huet3 900s infinite;
}
@keyframes huet3 {
} @keyframes huet3 {
0% {color: #33cc33;}
25% {color: #f2b919;}
25% {color: #d3a438;}
50% {color: #ea3224;}
75% {color: #4D79D8;}
100% {color: #33cc33;}
} @-webkit-keyframes huet3 {
0% {color: #33cc33;}
25% {color: #f2b919;}
25% {color: #d3a438;}
50% {color: #ea3224;}
75% {color: #4D79D8;}
100% {color: #33cc33;}
}
.hue4 {
background: #f2b919;
background: #d3a438;
-webkit-animation: hue4 900s infinite;
animation: hue4 900s infinite;
}
@keyframes hue4 {
0% {background-color: #f2b919;}
} @keyframes hue4 {
0% {background-color: #d3a438;}
25% {background-color: #ea3224;}
50% {background-color: #4D79D8;}
75% {background-color: #33cc33;}
100% {background-color: #f2b919;}
100% {background-color: #d3a438;}
} @-webkit-keyframes hue4 {
0% {background-color: #f2b919;}
0% {background-color: #d3a438;}
25% {background-color: #ea3224;}
50% {background-color: #4D79D8;}
75% {background-color: #33cc33;}
100% {background-color: #f2b919;}
100% {background-color: #d3a438;}
}
.huet4 {
color: #f2b919;
color: #d3a438;
-webkit-animation: huet4 900s infinite;
animation: huet4 900s infinite;
}
@keyframes huet4 {
0% {color: #f2b919;}
} @keyframes huet4 {
0% {color: #d3a438;}
25% {color: #ea3224;}
50% {color: #4D79D8;}
75% {color: #33cc33;}
100% {color: #f2b919;}
100% {color: #d3a438;}
} @-webkit-keyframes huet4 {
0% {color: #f2b919;}
0% {color: #d3a438;}
25% {color: #ea3224;}
50% {color: #4D79D8;}
75% {color: #33cc33;}
100% {color: #f2b919;}
100% {color: #d3a438;}
}
.pulse {
animation: pulse 2s infinite;
}
@keyframes pulse
{
} @keyframes pulse {
0% {opacity: 1;}
50% {opacity: 0.5;}
100% {opacity: 1;}
}
.joy {
width: 100px;
height: 100px;
@ -341,12 +310,18 @@ ul, li {
pointer-events: none;
z-index: 999999999;
animation: joy 1s steps(28);
} @keyframes joy {
0% {background-position: 0 0;}
100% {background-position: -2800px 0;}
}
.visually-hidden {
border: 0;
clip: rect(1px, 1px, 1px, 1px);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
@keyframes joy {
0% {
background-position: 0 0;
}
100% {
background-position: -2800px 0;
}
}

View File

@ -1,83 +1,151 @@
<!DOCTYPE html>
<html>
<head>
<title>Think</title>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
<link rel="stylesheet" type="text/css" href="/style.css">
</head>
<body>
<div id="think" class="hue page">
<link href='https://fonts.googleapis.com/css?family=Alegreya+Sans:300italic' rel='stylesheet' type='text/css'>
<style>
#think {
font-size: 24pt;
font-size: 6vmin;
font-family: 'Alegreya Sans', sans-serif;
color: white;
}
#think li {
width: 90%;
margin-top: 0.3em;
border-bottom: 1px dashed white;
}
#think .add {
width: 1em;
height: 1em;
line-height: 1em;
padding: 0.5em;
font-family: Tahoma, arial;
text-align: center;
border-radius: 50%;
}
#think ul, #think li {
list-style-type: circle;
margin-left: 0.5em;
}
</style>
<div class="pad whitet" style="width: 75%;">
<div style="margin-top: 2%;">
<div class="rubric left center">Add a Thought...</div>
<a href="#" class="right huet white act add">+</a>
</div>
<ul>
</ul>
</div>
<script src="/jquery.js"></script>
<script src="/gun.js"></script>
<script src="/gun/nts.js"></script>
<script>
// Check out the interactive tutorial
// for how to build a simplified version
// of this example: https://scrimba.com/c/cW2Vsa
var gun = Gun(location.origin+'/gun');
var think = gun.get('think/' + location.hash.slice(1));
var typing, throttle;
$('.add').on('click', function(){
$('<li>').attr('contenteditable', true).prependTo('ul');
});
$(document).on('keyup', "[contenteditable=true]", function(){
var li = $(this), id = li.attr('id');
if(!id){
li.attr('id', id = Gun.text.random());
}
typing = id;
clearTimeout(throttle);
throttle = setTimeout(function(){
think.get(id).put(li.text());
typing = false;
},10);
});
think.map().on(function(thought, id){
var li = $('#'+id)[0] || $('<li>').attr('id', id).attr('contenteditable', true).prependTo('ul');
if(thought){
if(id === typing){ return }
$(li).text(thought);
} else {
$(li).hide();
}
});
</script>
</div>
</div>
</body>
<head>
<title>Think</title>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
<link rel="stylesheet" type="text/css" href="/style.css">
<link href='https://fonts.googleapis.com/css?family=Alegreya+Sans:300italic' rel='stylesheet' type='text/css'>
<style>
.thought {
font-family: 'Alegreya Sans', sans-serif;
}
.thought__heading {
text-align: center;
margin-top: 0;
margin-bottom: 0;
color: white;
}
.thought__form-container {
position: fixed;
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 10px 20px;
top: 0;
z-index: 1;
}
.thought__item {
width: 100%;
max-width: 900px;
}
.thought__input {
flex: 1;
font-family: 'Alegreya Sans', sans-serif;
font-size: 25px;
font-weight: 500;
padding: 15px;
width: 100%;
margin-bottom: 10px;
background-color: white;
border-radius: 5px;
}
.thought__add {
width: 30px;
height: 30px;
font-family: Tahoma, arial;
text-align: center;
border-radius: 50%;
background-color: white;
font-size: 25px;
font-weight: 700;
}
.thought__add:hover::after {
background-color: rgba(0,0,0,0.2);
}
.thought__add:focus::after {
background-color: rgba(0,0,0,0.2);
}
.thought__add::after {
content: '';
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
border-radius: 50%;
transition: background-color 0.3s;
background-color: rgba(0,0,0,0);
}
.thought__list {
list-style-type: circle;
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
overflow-y: auto;
padding: 90px 20px;
width: 100%;
background-color: rgba(0, 0, 0, 0.2);
min-height: 100vh;
}
@media (max-width: 567px) {
.thought__heading {
font-size: 30px;
}
}
</style>
</head>
<body>
<div class="thought hue page">
<div class="thought__form-container hue">
<h2 id='title' class="thought__heading hue">Add a thought...</h2>
<button class="thought__add say huet">
<span aria-hidden="true">+</span>
<span class="visually-hidden">Add thought</span>
</button>
</div>
<ul class="thought__list">
</ul>
</div>
<script src="/jquery.js"></script>
<script src="/gun.js"></script>
<script src="/gun/nts.js"></script>
<script>
// Check out the interactive tutorial
// for how to build a simplified version
// of this example: https://scrimba.com/c/cW2Vsa
var gun = Gun(location.origin + '/gun');
var think = gun.get('think/' + location.hash.slice(1));
var thoughtItemStr = function(id) { return '<li class="thought__item"><label class="visually-hidden" for="' + id + '">Thought</label><input id="' + id + '" class="thought__input huet"><li/>'}
var typing, throttle;
$('.thought__add').on('click', function () {
$(thoughtItemStr('')).prependTo('.thought__list').find('.thought__input').focus();
});
$(document).on('keyup', '.thought__input', function () {
var input = $(this), id = input.attr('id');
if (!id) {
input.attr('id', id = Gun.text.random());
}
typing = id;
clearTimeout(throttle);
throttle = setTimeout(function () {
think.get(id).put(input.val());
typing = false;
}, 10);
});
think.map().on(function (thought, id) {
var li = $('#' + id).parent()[0] || $(thoughtItemStr(id)).prependTo('.thought__list');
if (thought) {
if (id === typing) { return }
$(li).find('.thought__input').val(thought);
} else {
$(li).hide();
}
});
</script>
</div>
</body>

4482
gun.js

File diff suppressed because it is too large Load Diff

2
gun.min.js vendored

File diff suppressed because one or more lines are too long

13
lib/afore.js Normal file
View File

@ -0,0 +1,13 @@
function afore(tag, hear){
if(!tag){ return }
tag = tag.the; // grab the linked list root
var tmp = tag.to; // grab first listener
hear = tmp.on.on(tag.tag, hear); // add us to end
hear.to = tmp || hear.to; // make our next be current first
hear.back.to = hear.to; // make our back point to our next
tag.last = hear.back; // make last be same as before
hear.back = tag; // make our back be the start
tag.to = hear; // make the start be us
return hear;
}
if(typeof module !== "undefined"){ module.exports = afore } // afore(gun._.on('in'), function(){ })

View File

@ -5,8 +5,8 @@ Gun.on('opt', function(root){
if(root.once){ return }
root.on('in', function(msg){
//Msg did not have a peer property saved before, so nothing ever went further
if(!msg.mesh || !msg.BYE){ return this.to.next(msg) }
var peer = msg.mesh.via;
if(!msg._ || !msg.BYE){ return this.to.next(msg) }
var peer = msg._.via;
(peer.bye = peer.bye || []).push(msg.BYE);
})
root.on('bye', function(peer){

44
lib/doll.js Normal file
View File

@ -0,0 +1,44 @@
;(function(){ // jQuery shim
if(window.$){ return }
(($ = window.$ = function(q, tag){
if(!(this instanceof $)){ return new $(q, tag) }
this.tags = (q && q.tags) || (('string' != typeof q)?
(q?[q]:[]) : (tag||document).querySelectorAll(q));
return this;
}).fn = $.prototype).each = function(cb){ return $.each(this.tags, cb), this }
$.each = function(o, cb){ Object.keys(o).forEach(function(k){ cb(k, o[k]) }) }
$.fn.get = function(i, l, u){ return l = this.tags, (i === u)? l : l[i] }
$.fn.on = function(eve, cb){ return this.each(function(i, tag){ tag.addEventListener(eve, cb) })}
$.fn.is = function(q, b){ return this.each(function(i, tag){ b = b || tag.matches(q) }), b }
$.fn.css = function(obj){ return this.each(function(i, tag){ $.each(obj, function(k,v){ tag.style[k] = v }) })}
$.fn.text = function(text, key, f, u){
text = (text === u)? '' : (f = 1) && text;
key = key || 'textContent';
this.each(function(i, tag){
if(f){ tag[key] = text }
else { text += (tag[key]||'') }
});
return f? this : text;
}
$.fn.html = function(html){ return this.text(html, 'innerHTML') }
$.fn.find = function(q){
var I = $(), l = I.tags;
this.each(function(i, tag){
$(q, tag).each(function(i, tag){
if(0 > l.indexOf(tag)){ l.push(tag) }
});
});
return I;
}
$.fn.add = function(html, div){
(div = document.createElement('div')).innerHTML = html;
this.tags = [].slice.call(this.tags).concat([].slice.call(div.childNodes));
return this;
}
$.fn.append = function(html, op, f){ return this.each(function(i, tag){
(('<' === html[0])? $().add(html) : $(html)).each(function(i, node){
(f? node : tag)[op || 'appendChild'](f? tag : node);
})
})}
$.fn.appendTo = function(html){ return this.append(html, 0, 1) }
}());

12
lib/email.js Normal file
View File

@ -0,0 +1,12 @@
;(function(){
var email, fail = {send: function(opt, cb){ cb && cb("You do not have email installed.") } };
if(!process.env.EMAIL){ return module.exports = fail }
try{ email = require('emailjs') }catch(e){};
if(!email){ return module.exports = fail }
return module.exports = email.server.connect({
user: process.env.EMAIL,
password: process.env.EMAIL_KEY,
host: process.env.EMAIL_HOST || "smtp.gmail.com",
ssl: process.env.EMAIL_SSL || true
});
}());

18
lib/fsrm.js Normal file
View File

@ -0,0 +1,18 @@
var fs = require('fs');
var nodePath = require('path');
var dir = __dirname + '/../';
module.exports = function rm(path, full) {
path = full || nodePath.join(dir, path);
if(!fs.existsSync(path)){ return }
fs.readdirSync(path).forEach(function(file,index){
var curPath = path + "/" + file;
if(fs.lstatSync(curPath).isDirectory()) { // recurse
rm(null, curPath);
} else { // delete file
fs.unlinkSync(curPath);
}
});
fs.rmdirSync(path);
};

95
lib/hot.js Normal file
View File

@ -0,0 +1,95 @@
;(function(){
// on fires when shortcut keydowns or on touch after command selected and then touchdown
var m = meta;
m.edit({name: "Add", combo: ['A']});
m.edit({name: "Row", combo: ['A', 'R'],
on: function(eve){
m.tap().append('<div class="hold center" style="min-height: 9em; padding: 2%;">');
}
});
m.edit({name: "Columns", combo: ['A','C'],
on: function(eve){
var on = m.tap(), tmp, c;
var html = '<div class="unit col" style="min-height: 9em; padding: 2%;"></div>';
if(!on.children('.col').length){ html += html }
c = (tmp = on.append(html).children('.col')).length;
tmp.each(function(){
$(this).css('width', (100/c)+'%');
})
}
});
m.edit({name: "Text", combo: ['A','T'],
on: function(eve){
m.tap().append('<p contenteditable="true">Text</p>');
}
});
m.edit({name: "Drag", combo: ['D']});
;(function(){
$(document).on('click', function(){
var tmp = $('.m-on');
if(!tmp.length){ return }
tmp.removeClass('m-on');
})
m.edit({combo: [38], // up
on: function(eve){
var on = m.tap().removeClass('m-on');
on = on.prev().or(on.parent()).or(on);
on.addClass('m-on');
}, up: function(){
}
});
m.edit({combo: [40], // down
on: function(eve){
var on = m.tap().removeClass('m-on');
on = on.next().or(on.children().first()).or(on);
on.addClass('m-on');
}, up: function(){
}
});
m.edit({combo: [39], // right
on: function(eve){
var on = m.tap().removeClass('m-on');
on = on.children().first().or(on.next()).or(on.parent()).or(on);
on.addClass('m-on');
}, up: function(){
}
});
m.edit({combo: [37], // left
on: function(eve){
var on = m.tap().removeClass('m-on');
on = on.parent().or(on);
on.addClass('m-on');
}, up: function(){
}
});
}());
m.edit({name: "Turn", combo: ['T']});
m.edit({name: "Size", combo: ['S']});
m.edit({name: "X", combo: ['S','X'],
on: function(eve){
var on = m.tap(), was = on.width();
$(document).on('mousemove.tmp', function(eve){
var be = was + ((eve.pageX||0) - was);
on.css({'max-width': be, width: '100%'});
})
}, up: function(){ $(document).off('mousemove.tmp') }
});
m.edit({name: "Y", combo: ['S','Y'],
on: function(eve){
var on = m.tap(), was = on.height();
$(document).on('mousemove.tmp', function(eve){
var be = was + ((eve.pageY||0) - was);
on.css({'min-height': be});
})
}, up: function(){ $(document).off('mousemove.tmp') }
});
m.edit({name: "Fill", combo: ['F'],
on: function(eve){
var on = m.tap();
m.ask('Color name, code, or URL?', function(color){
var css = on.closest('p').length? 'color' : 'background';
on.css(css, color);
});
}
});
}());

5
lib/hub.js Normal file
View File

@ -0,0 +1,5 @@
var fs = require('fs');
fs.watch('.', {persistent: false, recursive: true}, function(eve, name){
console.log("changed!", eve, name);
})

46
lib/ipfs.js Normal file
View File

@ -0,0 +1,46 @@
console.log("IPFS PLUGIN NOT OFFICIALLY MAINTAINED! PROBABLY WON'T WORK! USE AT YOUR OWN RISK! PLEASE CONTRIBUTE FIXES!");
var opt = gun._.opt, u;
if (u === opt.ipfs.directory) {
opt.ipfs.directory = '/gun';
}
opt.store = {};
opt.store.put = function(file, data, cb){
var uri = opt.ipfs.directory + '/' + file;
opt.ipfs.instance.files.write(uri, Buffer.from(JSON.stringify(data)), {create:true})
.then(res => {
console.log('File written to IPFS directory', uri, res);
return opt.ipfs.instance.files.stat(opt.ipfs.directory, {hash:true});
}).then(res => {
console.log('Directory hash:', res.hash);
return opt.ipfs.instance.name.publish(res.hash);
// currently throws "This command must be run in online mode. Try running 'ipfs daemon' first." for some reason, maybe js-ipfs IPNS not ready yet
}).then(res => {
console.log('IPFS put request successful:', res);
cb(undefined, 1);
}).catch(error => {
console.error('IPFS put request failed', error);
});
}
opt.store.get = function(file, cb){
var uri = opt.ipfs.directory + '/' + file;
opt.ipfs.instance.files.read(uri, {})
.then(res => {
var data = JSON.parse(res.toString());
console.log(uri + ' was loaded from ipfs:', data);
cb(data);
});
}
opt.store.list = function(cb){
var stream = opt.ipfs.files.lsReadableStream(opt.ipfs.directory);
stream.on('data', (file) => {
console.log('ls', file.name);
if (cb(file.name)) {
stream.destroy();
}
});
stream.on('finish', () => {
cb();
});
}

View File

@ -71,7 +71,7 @@
const gc_enable = root.opt.gc_enable ? root.opt.gc_enable : true;
const gc_delay = root.opt.gc_delay ? root.opt.gc_delay : 1000;
const gc_info_enable = root.opt.gc_info_enable ? root.opt.gc_info_enable : true;
const gc_info_enable = ("gc_info_enable" in root.opt) ? root.opt.gc_info_enable : true;
const gc_info = root.opt.gc_info ? root.opt.gc_info : 5000;
const gc_info_mini = root.opt.gc_info_mini ? root.opt.gc_info_mini : false;
@ -127,6 +127,7 @@
//Executed every time a node gets modified
root.on("put", function(e) {
this.to.next(e);
var ctime = Date.now();
var souls = Object.keys(e.put || empty); // get all of the nodes in the update
for (var i = 0; i < souls.length; i++) { // iterate over them and add them
@ -219,4 +220,4 @@
return Number(Math.round(value + 'e' + decimals) + 'e-' + decimals);
}
});
}());
}());

25
lib/match.js Normal file
View File

@ -0,0 +1,25 @@
var Type = require('../src/type');
function match(t, o){ var r = false;
t = t || '';
o = Type.text.is(o)? {'=': o} : o || {}; // {'~', '=', '*', '<', '>', '+', '-', '?', '!'} // ignore case, exactly equal, anything after, lexically larger, lexically lesser, added in, subtacted from, questionable fuzzy match, and ends with.
if(Type.obj.has(o,'~')){ t = t.toLowerCase(); o['='] = (o['='] || o['~']).toLowerCase() }
if(Type.obj.has(o,'=')){ return t === o['='] }
if(Type.obj.has(o,'*')){ if(t.slice(0, o['*'].length) === o['*']){ r = true; t = t.slice(o['*'].length) } else { return false }}
if(Type.obj.has(o,'!')){ if(t.slice(-o['!'].length) === o['!']){ r = true } else { return false }}
if(Type.obj.has(o,'+')){
if(Type.list.map(Type.list.is(o['+'])? o['+'] : [o['+']], function(m){
if(t.indexOf(m) >= 0){ r = true } else { return true }
})){ return false }
}
if(Type.obj.has(o,'-')){
if(Type.list.map(Type.list.is(o['-'])? o['-'] : [o['-']], function(m){
if(t.indexOf(m) < 0){ r = true } else { return true }
})){ return false }
}
if(Type.obj.has(o,'>')){ if(t > o['>']){ r = true } else { return false }}
if(Type.obj.has(o,'<')){ if(t < o['<']){ r = true } else { return false }}
function fuzzy(t,f){ var n = -1, i = 0, c; for(;c = f[i++];){ if(!~(n = t.indexOf(c, n+1))){ return false }} return true } // via http://stackoverflow.com/questions/9206013/javascript-fuzzy-search
if(Type.obj.has(o,'?')){ if(fuzzy(t, o['?'])){ r = true } else { return false }} // change name!
return r;
}
module.exports = match;

View File

@ -1,73 +1,72 @@
$(function(){
var m = window.meta = {edit:[], os:{}}, ua = '', u;
try{ua = navigator.userAgent.toLowerCase()}catch(e){}
m.os.is = {
win: (ua.search("win") >= 0)? "windows":false,
lin: (ua.search("linux") >= 0)? "linux":false,
mac: (ua.search("mac") >= 0)? "macintosh":false,
and: (ua.search("android") >= 0)? "android":false,
ios: (ua.search('ipod') >= 0
|| ua.search('iphone') >= 0
|| ua.search('ipad') >= 0)? "ios":false
}
var k = m.key = {ctrl: 17, cmd: 91};
k.meta = (m.os.is.win||m.os.is.lin||m.os.is.and)? k.ctrl : k.cmd;
;(function(){
var noop = function(){}, u;
var m = window.meta = {edit:[]};
var k = m.key = {};
k.meta = {17:17, 91:17, 93:17, 224:17};
k.down = function(eve){
if($(eve.target).is('input') || eve.repeat){ return }
(k.eve = m.eve = eve).which = eve.which || eve.fake || eve.keyCode;
if(!eve.fake && eve.which === k.last){ return }
if(k.meta === (k.last = eve.which)){ k.down.meta = m.flip(k.wipe()) || true }
if(m.flip.is()){
(k.combo || (k.combo = [])).push(eve.which);
m.check('on', eve.which, k.at || (k.at = m.edit));
if(eve.repeat){ return }
var key = (k.eve = m.eve = eve).which = eve.which || eve.fake || eve.keyCode;
if(!eve.fake && key === k.last){ return } k.last = key;
if(!eve.fake && $(eve.target).closest('input, textarea, [contenteditable=true]').length){
if(k.meta[key]){ k.down.meta = key = -1 }
if(!k.down.meta){ return }
}
(k.combo || (k.combo = [])).push(key);
m.check('on', key, k.at || (k.at = m.edit));
if(k.meta[key]){
m.list(k.at.back || m.edit);
if(k.at && !k.at.back){ m.flip() }
}
if(eve.metaKey && (k.meta !== eve.which)){ k.up(eve) } // on some systems, meta hijacks keyup
}
k.up = function(eve){ var tmp;
if($(eve.target).is('input')){ return }
k.eve = m.eve = eve;
k.last = null;
eve.which = eve.which || eve.fake || eve.keyCode;
if(m.flip.is()){ m.check('up', eve.which) }
if(tmp = (k.meta === eve.which)){ k.down.meta = false }
if(tmp && k.at === m.edit){ k.wipe() }
if(27 === eve.which){ return m.flip(false) }
}
m.flip = function(tmp, aid){
if(aid){
m.flip.aid = true;
setTimeout(function(){$(document).one('click',function(eve){m.flip(m.flip.aid = false)})},250); // ugly but important for visual aid.
var key = (k.eve = m.eve = eve).which = eve.which || eve.fake || eve.keyCode;
if(!eve.fake && $(eve.target).closest('input, textarea, [contenteditable=true]').length){
if(k.meta[key]){
k.down.meta = null;
key = -1;
} else
if(!k.down.meta){ return }
}
k.last = null;
if($(':focus').closest('#meta').length){ return }
m.check('up', key);
if(-1 === key || 27 === eve.which){ k.wipe() }
}
m.flip = function(tmp){
var board = $('#meta .meta-menu');
((tmp === false) || (!tmp && board.is(':visible')))?
board.addClass('meta-none')
: board.removeClass('meta-none');
}
m.flip.is = function(){
if(m.flip.aid && ((m.eve||{}).fake || k.at !== m.edit)){ m.flip.aid = false }
return !m.flip.aid && $('#meta .meta-menu').is(':visible');
return $('#meta .meta-menu').is(':visible');
}
m.flip.wait = 500;
m.check = function(how, key, at){
at = k.at || m.edit;
//m.list(at);
var edit = at[key], tmp;
var edit = at[key];
if(!edit){ return }
if(k.eve && k.eve.preventDefault){ k.eve.preventDefault() }
var tmp = k.eve || noop;
if(tmp.preventDefault){ tmp.preventDefault() }
if(edit[how]){
edit[how](m.eve);
if(k.at !== m.edit && 'up' === how){
if(k.down.meta){ m.list(k.at = m.edit) }
else { k.wipe() }
if(tmp.fake && !edit.fake){
m.tap.edit = edit;
} else {
edit[how](m.eve);
/*if(k.at !== m.edit && 'up' === how){
if(k.down.meta){ m.list(k.at = m.edit) }
else { k.wipe() }
}*/
}
}
if('up' != how){ return }
edit.back = at;
m.list(edit, at);
if(at != edit){ edit.back = at }
m.list(edit, true);
}
m.list = function(at){
m.list = function(at, opt){
if(!at){ return m.flip(false) }
var l = [];
$.each(at, function(i,k){ 'back' != i && k.combo && l.push(k) });
$.each(at, function(i,k){ 'back' != i && k.combo && k.name && l.push(k) });
if(!l.length){ return }
k.at = at;
l = l.sort(function(a,b){
@ -82,8 +81,10 @@ $(function(){
$.each(l, function(i, k){
$ul.append($('<li>').text(k.name));
});
if(!at.back){ return }
$ul.append($('<li>').html('&larr;').one('click', function(){ m.list(k.at = at.back) }));
if(opt){ m.flip(true) }
$ul.append($('<li>').html('&larr;').one('click', function(){
m.list(k.at = at.back);
}));
}
m.ask = function(help, cb){
var $ul = $('#meta .meta-menu ul').empty();
@ -96,34 +97,33 @@ $(function(){
});
var $li = $('<li>').append($form);
$ul.append($li);
m.flip(true);
$put.focus();
}
k.wipe = function(){
k.wipe = function(opt){
k.down.meta = false;
k.combo = [];
m.flip(false);
m.flip.aid = false;
if(!opt){ m.flip(false) }
m.list(k.at = m.edit);
};
$(document).on('keydown', k.down).on('keyup', k.up);
m.tap = {};
m.tap.select = function(eve){
m.tap.range = null;
if(!(m.tap.text()||'').trim()){
if(m.tap.was){
m.tap.was = null;
m.flip(false);
}
return;
}
m.flip(m.tap.range = monotype((eve||{}).target), m.tap.was = true);
}
m.tap.text = function(tmp){
return ((tmp = window.getSelection) && tmp().toString()) ||
((tmp = document.selection) && tmp.createRange().text) || '';
m.tap = function(){
var on = $('.meta-on')
.or($($(document.querySelectorAll(':hover')).get().reverse()).first())
.or($(document.elementFromPoint(meta.tap.x, meta.tap.y)));
return on;
}
$(window).on('blur', k.wipe).on('focus', k.wipe);
$(document).on('select contextmenu keyup mouseup', '[contenteditable=true]', m.tap.select);
//.on('keydown', '[contenteditable=true]', function(e){});
$(document).on('mousedown mousemove mouseup', function(eve){
m.tap.eve = eve;
m.tap.x = eve.pageX||0;
m.tap.y = eve.pageY||0;
m.tap.on = $(eve.target);
}).on('mousedown touchstart', function(eve){
var tmp = m.tap.edit;
if(!tmp || !tmp.on){ return }
tmp.on(eve);
m.tap.edit = null;
});
$(document).on('touchstart', '#meta .meta-start', function(eve){ m.tap.stun = true });
$(document).on('click', '#meta .meta-menu li', function(eve){
if(m.tap.stun){ return m.tap.stun = false }
@ -132,10 +132,11 @@ $(function(){
k.down(eve);
k.up(eve);
});
$(document).on('keydown', k.down).on('keyup', k.up);
meta.edit = function(edit){
var tmp = edit.combow = [];
$.each(edit.combo || (edit.combo = []), function(i,k){
if(!k || !k.length){ return }
if(!k || !k.length){ if('number' == typeof k){ tmp.push(k) } return }
tmp.push(k.toUpperCase().charCodeAt(0));
});
var at = meta.edit, l = edit.combo.length;
@ -143,37 +144,7 @@ $(function(){
edit.combow = edit.combow.join(',');
m.list(meta.edit);
}
meta.text = {zws: '&#8203;'};
meta.text.editor = function(opt, as){ var tmp;
if(!opt){ return }
opt = (typeof opt == 'string')? {edit: opt} : opt.tag? opt : {tag: opt};
var r = opt.range = opt.range || m.tap.range || monotype(), cmd = opt.edit;
as = opt.as = opt.as || as;
if(cmd && document.execCommand){
r.restore();
if(document.execCommand(cmd, null, as||null)){ return }
}
if(!opt.tag){ return }
opt.tag = $(opt.tag);
opt.name = opt.name || opt.tag.prop('tagName');
if((tmp = $(r.get()).closest(opt.name)).length){
if(r.s === r.e){
tmp.after(meta.text.zws);
r = r.select(monotype.next(tmp[0]),1);
} else {
tmp.contents().unwrap(opt.name);
}
} else
if(r.s === r.e){
r.insert(opt.tag);
r = r.select(opt.tag);
} else {
r.wrap(opt.tag);
}
r.restore();
opt.range = null;
if(m.tap.range){ m.tap.range = monotype() }
}
$.fn.or = function(s){ return this.length ? this : $(s||'body') };
;(function(){try{
/* UI */
if(meta.css){ return }
@ -199,10 +170,12 @@ $(function(){
width: '2em',
height: '2em',
opacity: 0.7,
outline: 'none',
color: '#000044',
overflow: 'visible',
transition: 'all 0.2s ease-in'
},
'#meta *': {outline: 'none'},
'#meta .meta-none': {display: 'none'},
'#meta span': {'line-height': '2em'},
'#meta .meta-menu': {
@ -258,103 +231,153 @@ $(function(){
});
tmp += '}\n';
});
(node = document.createElement('style')).innerHTML = tmp;
document.body.appendChild(node);
var tag = document.createElement('style');
tag.innerHTML = tmp;
document.body.appendChild(tag);
}
}catch(e){}}());
;(function(){
// on fires when shortcut keydowns or on touch after command selected and then touchdown
meta.edit({
name: "Bold",
combo: ['B'],
on: function(e){
meta.text.editor('bold');
},
up: function(){}
});
meta.edit({
name: "Italic",
combo: ['I'],
on: function(e){
meta.text.editor('italic');
},
up: function(){}
});
meta.edit({
name: "Underline",
combo: ['U'],
on: function(e){
meta.text.editor('underline');
},
up: function(){}
});
meta.edit({
name: "linK",
combo: ['K'],
up: function(e){
var range = meta.tap.range || monotype();
meta.ask('Paste or type link...', function(url){
meta.text.editor({tag: $('<a href="'+url+'">link</a>'), edit: url? 'createLink' : 'unlink', as: url, range: range});
})
},
on: function(){}
});
meta.edit({name: "aliGn", combo: ['G']});
meta.edit({
name: "Left",
combo: ['G','L'],
on: function(e){ meta.text.editor('justifyLeft') },
up: function(){}
});
meta.edit({
name: "Right",
combo: ['G','R'],
on: function(e){ meta.text.editor('justifyRight') },
up: function(){ }
});
meta.edit({
name: "Middle",
combo: ['G','M'],
on: function(e){ meta.text.editor('justifyCenter') },
up: function(){ }
});
meta.edit({
name: "Justify",
combo: ['G','J'],
on: function(e){ meta.text.editor('justifyFull') },
up: function(){}
});
// Align Number
// Align Points
// Align Strike
meta.edit({name: "Size", combo: ['S']});
meta.edit({
name: "Small",
combo: ['S','S'],
on: function(e){ meta.text.editor('fontSize', 2) },
up: function(){ }
});
meta.edit({
name: "Normal",
combo: ['S','N'],
on: function(e){ meta.text.editor('fontSize', 5) },
up: function(){}
});
meta.edit({
name: "Header",
combo: ['S','H'],
on: function(e){ meta.text.editor('fontSize', 6) },
up: function(){}
});
meta.edit({
name: "Title",
combo: ['S','T'],
on: function(e){ meta.text.editor('fontSize', 7) },
up: function(){}
});
// Size Spacing
// Size Super
// Size Sub
meta.edit({name: "Edit", combo: ['E']});
// include basic text editing by default.
var monotype = window.monotype || function(){console.log("monotype needed")};
var m = meta;
m.text = {zws: '&#8203;'};
m.text.on = function(eve){ var tmp;
if($((eve||{}).target).closest('#meta').length){ return }
m.text.range = null;
if(!(m.text.copy()||'').trim()){
m.flip(false);
m.list(m.text.it);
return;
}
m.text.range = monotype((eve||{}).target);
m.text.it.on(eve);
}
m.text.copy = function(tmp){
return ((tmp = window.getSelection) && tmp().toString()) ||
((tmp = document.selection) && tmp.createRange().text) || '';
}
$(document).on('select contextmenu keyup mouseup', '[contenteditable=true]', m.text.on);
m.text.editor = function(opt, as){ var tmp;
if(!opt){ return }
opt = (typeof opt == 'string')? {edit: opt} : opt.tag? opt : {tag: opt};
var r = opt.range = opt.range || m.text.range || monotype(), cmd = opt.edit;
as = opt.as = opt.as || as;
if(cmd && document.execCommand){
r.restore();
if(document.execCommand(cmd, null, as||null)){
if(m.text.range){ m.text.range = monotype() }
return;
}
}
if(!opt.tag){ return }
opt.tag = $(opt.tag);
opt.name = opt.name || opt.tag.prop('tagName');
if((tmp = $(r.get()).closest(opt.name)).length){
if(r.s === r.e){
tmp.after(m.text.zws);
r = r.select(monotype.next(tmp[0]),1);
} else {
tmp.contents().unwrap(opt.name);
}
} else
if(r.s === r.e){
r.insert(opt.tag);
r = r.select(opt.tag);
} else {
r.wrap(opt.tag);
}
r.restore();
opt.range = null;
if(m.text.range){ m.text.range = monotype() }
}
meta.edit(meta.text.it = {combo: [-1], on: function(){ m.list(this, true) }, back: meta.edit}); // -1 is key for typing.
meta.text.it[-1] = meta.text.it;
meta.edit({
name: "Bold",
combo: [-1,'B'], fake: -1,
on: function(eve){
meta.text.editor('bold');
},
up: function(){}
});
meta.edit({
name: "Italic",
combo: [-1,'I'], fake: -1,
on: function(eve){
meta.text.editor('italic');
},
up: function(){}
});
/*meta.edit({
name: "Underline",
combo: [-1,'U'], fake: -1,
on: function(eve){
meta.text.editor('underline');
},
up: function(){}
});*/
meta.edit({
name: "linK",
combo: [-1,'K'], fake: -1,
on: function(eve){
var range = meta.text.range || monotype();
meta.ask('Paste or type link...', function(url){
meta.text.editor({tag: $('<a href="'+url+'">link</a>'), edit: url? 'createLink' : 'unlink', as: url, range: range});
})
}
});
//meta.edit({name: "aliGn", combo: [-1,'G']}); // MOVE TO ADVANCED MENu!
meta.edit({
name: "Left",
combo: [-1,'G','L'], fake: -1,
on: function(eve){ meta.text.editor('justifyLeft') },
up: function(){}
});
meta.edit({
name: "Right",
combo: [-1,'G','R'], fake: -1,
on: function(eve){ meta.text.editor('justifyRight') },
up: function(){ }
});
meta.edit({
name: "Middle",
combo: [-1,'G','M'], fake: -1,
on: function(eve){ meta.text.editor('justifyCenter') },
up: function(){ }
});
meta.edit({
name: "Justify",
combo: [-1,'G','J'], fake: -1,
on: function(eve){ meta.text.editor('justifyFull') },
up: function(){}
});
// Align Number
// Align Points
// Align Strike
meta.edit({name: "Size", combo: [-1,'S'], on: function(){ m.list(this, true) }});
meta.edit({
name: "Small",
combo: [-1,'S','S'], fake: -1,
on: function(eve){ meta.text.editor('fontSize', 2) },
up: function(){ }
});
meta.edit({
name: "Normal",
combo: [-1,'S','N'], fake: -1,
on: function(eve){ meta.text.editor('fontSize', 5) },
up: function(){}
});
meta.edit({
name: "Header",
combo: [-1,'S','H'], fake: -1,
on: function(eve){ meta.text.editor('fontSize', 6) },
up: function(){}
});
meta.edit({
name: "Title",
combo: [-1,'S','T'], fake: -1,
on: function(eve){ meta.text.editor('fontSize', 7) },
up: function(){}
});
}());
});
}());

17
lib/mix.js Normal file
View File

@ -0,0 +1,17 @@
;(function(){
var Gun = (typeof window !== "undefined")? window.Gun : require('../gun');
Gun.state.node = function(node, vertex, opt){
opt = opt || {};
opt.state = opt.state || Gun.state();
var now = Gun.obj.copy(vertex);
Gun.node.is(node, function(val, key){
var ham = Gun.HAM(opt.state, Gun.state.is(node, key), Gun.state.is(vertex, key), val, vertex[key]);
if(!ham.incoming){
// if(ham.defer){}
return;
}
now = Gun.state.to(node, key, now);
});
return now;
}
}());

88
lib/multicast.js Normal file
View File

@ -0,0 +1,88 @@
var Gun = (typeof window !== "undefined")? window.Gun : require('../gun');
Gun.on('create', function(root){
this.to.next(root);
var opt = root.opt;
if(false === opt.multicast){ return }
if((typeof process !== "undefined") && 'false' === ''+(process.env||{}).MULTICAST){ return }
//if(true !== opt.multicast){ return } // disable multicast by default for now.
var udp = opt.multicast = opt.multicast || {};
udp.address = udp.address || '233.255.255.255';
udp.pack = udp.pack || 50000; // UDP messages limited to 65KB.
udp.port = udp.port || 8765;
var noop = function(){}, u;
var pid = '2'+Math.random().toString().slice(-8);
var mesh = opt.mesh = opt.mesh || Gun.Mesh(root);
var dgram;
try{ dgram = require("dgram") }catch(e){ return }
var socket = dgram.createSocket({type: "udp4", reuseAddr: true});
socket.bind(udp.port);
socket.on("listening", function(){
try { socket.addMembership(udp.address) }catch(e){ return }
udp.peer = {id: udp.address + ':' + udp.port, wire: socket};
udp.peer.say = function(raw){
var buf = Buffer.from(raw, 'utf8');
if(udp.pack <= buf.length){ // message too big!!!
return;
}
socket.send(buf, 0, buf.length, udp.port, udp.address, noop);
}
console.log('Multicast on', udp.peer.id);
return; // below code only needed for when WebSocket connections desired!
setInterval(function broadcast(){
port = port || (opt.web && opt.web.address()||{}).port;
if(!port){ return }
udp.peer.say(JSON.stringify({id: opt.pid || (opt.pid = Math.random().toString(36).slice(2)), port: port}));
}, 1000);
});
socket.on("message", function(raw, info) { try {
if(!raw){ return }
raw = raw.toString('utf8');
if('2'===raw[0]){ return check(raw, info) }
opt.mesh.hear(raw, udp.peer);
return; // below code only needed for when WebSocket connections desired!
var message;
message = JSON.parse(raw.toString('utf8'));
if(opt.pid === message.id){ return } // ignore self
var url = 'http://' + info.address + ':' + (port || (opt.web && opt.web.address()||{}).port) + '/gun';
if(root.opt.peers[url]){ return }
//console.log('discovered', url, message, info);
root.$.opt(url);
} catch(e){
//console.log('multicast error', e, raw);
return;
} });
function say(msg){
this.to.next(msg);
if(!udp.peer){ return }
mesh.say(msg, udp.peer);
}
function check(id, info){ var tmp;
if(!udp.peer){ return }
if(!id){
id = check.id = check.id || Buffer.from(pid, 'utf8');
socket.send(id, 0, id.length, udp.port, udp.address, noop);
return;
}
if((tmp = root.stats) && (tmp = tmp.gap) && info){ (tmp.near || (tmp.near = {}))[info.address] = info.port || 1 } // STATS!
if(check.on || id === pid){ return }
root.on('out', check.on = say);
}
setInterval(check, 1000 * 1);
});

View File

@ -29,12 +29,13 @@
hierarchy: ['div', 'pre', 'ol', 'ul', 'li',
'h1', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'a', // block
'b', 'code', 'i', 'span', 's', 'sub', 'sup', 'u', // inline
'br'] // empty
'br', 'img'] // empty
,tags: {
'a': {attrs:{'href':1}, exclude:{'a':1}},
'b': {exclude:{'b':1,'p':1}},
'br': {empty: 1},
'i': {exclude:{'i':1,'p':1}},
'img': {attrs:{'src':1}, empty: 1},
'span': {exclude:{'p':1,'ul':1,'ol':1,'li':1,'br':1}},
's': {space:1},
'u': {exclude:{'u':1,'p':1},space:1},
@ -138,12 +139,17 @@
return $(($(e)[0]||{})[d]);
}
var xssattr = /[^a-z:]/ig, xssjs = /javascript:/ig;
// url("javascript: // and all permutations
// stylesheets can apparently have XSS?
// create key val attributes object from elements attributes
function attrsAsObj(e, filterCb){
var attrObj = {};
(e = $(e)) && e.length && $(e[0].attributes||[]).each(function(value,name){
name = name.nodeName||name.name;
value = e.attr(name);
if(value.replace(xssattr,'').match(xssjs)){ e.removeAttr(name); return }
value = filterCb? filterCb(value,name,e) : value;
if(value !== undefined && value !== false)
attrObj[name] = value;

View File

@ -35,15 +35,19 @@ Gun.chain.open = function(cb, opt, at){
}
var tmp = this, id;
Gun.obj.map(data, function(val, key){
var doc = at || opt.doc;
if (!doc) {
return;
}
if(!(id = Gun.val.link.is(val))){
(at || opt.doc)[key] = val;
doc[key] = val;
return;
}
if(opt.ids[id]){
(at || opt.doc)[key] = opt.ids[id];
doc[key] = opt.ids[id];
return;
}
tmp.get(key).open(opt.any, opt, opt.ids[id] = (at || opt.doc)[key] = {});
tmp.get(key).open(opt.any, opt, opt.ids[id] = doc[key] = {});
});
})
}
}

124
lib/promise.js Normal file
View File

@ -0,0 +1,124 @@
/* Promise Library v1.0 for GUN DB
* Turn any part of a gun chain into a promise, that you can then use
* .then().catch() pattern.
* In normal gun doing var item = gun.get('someKey'), gun returns a reference
* to the someKey synchroneously. Using a reference is quite helpful in making
* graph structures, so I have chosen to follow the following paradigm.
* Whenever a promise is resolved, gun will return an object of data, I will
* wrap that data in an object together with the reference like so:
* {ref: gunRef, data: data}.
* This code is freely given in the spirit of open source MIT license.
* Author: Jachen Duschletta / 2019
*/
/*
* Function promOnce
* @param limit - due to promises resolving too fast if we do not set a timer
* we will not be able receive any data back from gun before returning the promise
* works both following a Chain.get and a Chain.map (limit only applies to map)
* If no limit is chosen, defaults to 100 ms (quite sufficient to fetch about 2000 nodes or more)
* @param opt - option object
* @return {ref: gunReference, data: object / string (data), key: string (soulOfData)}
*/
Gun.chain.promOnce = async function (limit, opt) {
var gun = this, cat = gun._;
if(!limit){limit = 100}
if(cat.subs){
var array = [];
gun.map().once((data, key)=>{
var gun = this;
array.push(new Promise((res, rej)=>{
res({ref: gun, data:data, key:key});
})
)
}, opt);
await sleep(limit);
return Promise.all(array)
} else {
return (new Promise((res, rej)=>{
gun.once(function (data, key) {
var gun = this;
res({ref:gun,data:data,key:key});
}, opt);
}))
}
var chain = gun.chain();
return chain;
}
function sleep (limit) {
return (new Promise((res, rej)=>{
setTimeout(res, limit);
}))
}
/*
* Function promPut
* @param item (string / object) - item to be put to that key in the chain
* @param opt - option object
* @return object - Returns an object with the ref to that node that was just
* created as well as the 'ack' which acknowledges the put was succesful
* object {ref: gunReference, ack: acknowledgmentObject}
* If put had an error we can catch the return via .catch
*/
Gun.chain.promPut = async function (item, opt) {
var gun = this;
return (new Promise((res, rej)=>{
gun.put(item, function(ack) {
if(ack.err){rej(ack.err)}
res({ref:gun, ack:ack});
}, opt);
}))
}
/*
* Function promSet
* @param item (string / object) - item to be set into a list at this key
* @param opt - option object
* @return object - Returns object with the ref to that node that was just
* created as well as the 'ack' which acknowledges the set was succesful
* object {ref: gunReference, ack: acknowledgmentObject}
* If set had an error we can catch the return via .catch
*/
Gun.chain.promSet = async function(item, opt){
var gun = this, soul;
var cb = cb || function(){};
opt = opt || {}; opt.item = opt.item || item;
return (new Promise(async function (res,rej) {
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 = await gun.back(-1).get(soul = soul || Gun.node.soul(item) || gun.back('opt.uuid')()).promPut(item);
item = item.ref;
}
res(gun.get(soul || (Gun.state.lex() + Gun.text.random(7))).promPut(item));
}
item.get(function(soul, o, msg){
var ack = {};
if(!soul){ rej({ack:{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);
res({ref:item, ack:{ok:0}});
}))
}
/*
* Function promOn
* @param callback (function) - function to be called upon changes to data
* @param option (object) - {change: true} only allow changes to trigger the callback
* @return - data and key
* subscribes callback to data
*/
Gun.chain.promOn = async function (callback, option) {
var gun = this;
return (new Promise((res, rej)=>{
gun.on(function (data, key){
callback(data, key);
res(data, key);
}, option);
}));
}

View File

@ -5,15 +5,21 @@
opt = opt || {};
opt.log = opt.log || console.log;
opt.file = String(opt.file || 'radata');
var has = (Radisk.has || (Radisk.has = {}))[opt.file];
if(has){ return has }
opt.pack = opt.pack || (opt.memory? (opt.memory * 1000 * 1000) : 1399000000) * 0.3; // max_old_space_size defaults to 1400 MB.
opt.until = opt.until || opt.wait || 9;
opt.batch = opt.batch || 10 * 1000;
opt.until = opt.until || opt.wait || 250;
opt.batch = opt.batch || (10 * 1000);
opt.chunk = opt.chunk || (1024 * 1024 * 10); // 10MB
opt.code = opt.code || {};
opt.code.from = opt.code.from || '!';
//opt.jsonify = true; if(opt.jsonify){ console.log("JSON RAD!!!") } // TODO: REMOVE!!!!
function ename(t){ return encodeURIComponent(t).replace(/\*/g, '%2A') }
function atomic(v){ return u !== v && (!v || 'object' != typeof v) }
var map = Gun.obj.map;
var LOG = false;//true;
if(!opt.store){
return opt.log("ERROR: Radisk needs `opt.store` interface with `{get: fn, put: fn (, list: fn)}`!");
@ -36,25 +42,29 @@
var r = function(key, val, cb){
key = ''+key;
if(val instanceof Function){
var o = cb || {};
cb = val;
val = r.batch(key);
if(u !== val){
cb(u, r.range(val, o), o);
if(atomic(val)){ return }
// if a node is requested and some of it is cached... the other parts might not be.
return cb(u, val);
}
if(r.thrash.at){
val = r.thrash.at(key);
if(u !== val){
cb(u, r.range(val, o), o);
if(atomic(val)){ cb(u, val, o); return }
// if a node is requested and some of it is cached... the other parts might not be.
return cb(u, val);
}
}
return r.read(key, cb);
return r.read(key, cb, o);
}
r.batch(key, val);
if(cb){ r.batch.acks.push(cb) }
if(++r.batch.ed >= opt.batch){ return r.thrash() } // (2)
clearTimeout(r.batch.to); // (1)
if(r.batch.to){ return }
//clearTimeout(r.batch.to); // (1) // THIS LINE IS EVIL! NEVER USE IT! ALSO NEVER DELETE THIS SO WE NEVER MAKE THE SAME MISTAKE AGAIN!
r.batch.to = setTimeout(r.thrash, opt.until || 1);
}
@ -73,10 +83,13 @@
r.batch = Radix();
r.batch.acks = [];
r.batch.ed = 0;
//console.debug(99); var ID = Gun.text.random(2), S = (+new Date); console.log("[[[[[[[[", ID, batch.acks.length);
r.save(batch, function(err, ok){
if(++i > 1){ return }
if(++i > 1){ opt.log('RAD ERR: Radisk has callbacked multiple times, please report this as a BUG at github.com/amark/gun/issues ! ' + i); return }
if(err){ opt.log('err', err) }
//console.debug(99); var TMP; console.log("]]]]]]]]", ID, batch.acks.length, (TMP = +new Date) - S, 'more?', thrash.more);
map(batch.acks, function(cb){ cb(err, ok) });
//console.log("][", +new Date - TMP, thrash.more);
thrash.at = null;
thrash.ing = false;
if(thrash.more){ thrash() }
@ -90,7 +103,7 @@
4. Read the previous file to that into memory
5. Scan through the in memory radix for all values lexically less than the limit.
6. Merge and write all of those to the in-memory file and back to disk.
7. If file to large, split. More details needed here.
7. If file too large, split. More details needed here.
*/
r.save = function(rad, cb){
var s = function Span(){};
@ -136,28 +149,32 @@
Therefore it is unavoidable that a read will have to happen,
the question is just how long you delay it.
*/
r.write = function(file, rad, cb, force){
r.write = function(file, rad, cb, o){
o = ('object' == typeof o)? o : {force: o};
var f = function Fractal(){};
f.text = '';
f.count = 0;
f.file = file;
f.each = function(val, key, k, pre){
//console.log("RAD:::", JSON.stringify([val, key, k, pre]));
if(u !== val){ f.count++ }
if(opt.pack <= (val||'').length){ return cb("Record too big!"), true }
var enc = Radisk.encode(pre.length) +'#'+ Radisk.encode(k) + (u === val? '' : ':'+ Radisk.encode(val)) +'\n';
if((opt.chunk < f.text.length + enc.length) && (1 < f.count) && !force){
if((opt.chunk < f.text.length + enc.length) && (1 < f.count) && !o.force){
f.text = '';
f.limit = Math.ceil(f.count/2);
f.count = 0;
f.sub = Radix();
Radix.map(rad, f.slice)
Radix.map(rad, f.slice);
return true;
}
f.text += enc;
}
f.write = function(){
var tmp = ename(file);
var start; LOG && (start = +new Date); // comment this out!
opt.store.put(tmp, f.text, function(err){
LOG && console.log("wrote to disk in", (+new Date) - start, tmp); // comment this out!
if(err){ return cb(err) }
r.list.add(tmp, cb);
});
@ -168,7 +185,7 @@
var name = f.file;
f.file = key;
f.count = 0;
r.write(name, f.sub, f.next, force);
r.write(name, f.sub, f.next, o);
return true;
}
f.sub(key, val);
@ -177,54 +194,98 @@
if(err){ return cb(err) }
f.sub = Radix();
if(!Radix.map(rad, f.slice)){
r.write(f.file, f.sub, cb, force);
r.write(f.file, f.sub, cb, o);
}
}
if(opt.jsonify){ return r.write.jsonify(f, file, rad, cb, o) } // temporary testing idea
if(!Radix.map(rad, f.each, true)){ f.write() }
}
r.write.jsonify = function(f, file, rad, cb, o){
var raw;
var start; LOG && (start = +new Date); // comment this out!
try{raw = JSON.stringify(rad.$);
}catch(e){ return cb("Record too big!") }
LOG && console.log("stringified JSON in", +new Date - start); // comment this out!
if(opt.chunk < raw.length && !o.force){
if(Radix.map(rad, f.each, true)){ return }
}
f.text = raw;
f.write();
}
r.range = function(tree, o){
if(!tree || !o){ return }
if(u === o.start && u === o.end){ return tree }
if(atomic(tree)){ return tree }
var sub = Radix();
Radix.map(tree, function(v,k){
sub(k,v);
}, o);
return sub('');
}
;(function(){
var Q = {};
r.read = function(key, cb, next){
if(RAD && !next){ // cache
r.read = function(key, cb, o){
o = o || {};
if(RAD && !o.next){ // cache
var val = RAD(key);
if(u !== val){
//if(u !== val){
//cb(u, val, o);
if(atomic(val)){ cb(u, val, o); return }
// if a node is requested and some of it is cached... the other parts might not be.
return cb(u, val);
}
//}
}
var g = function Get(){}, tmp;
g.lex = function(file){
o.span = (u !== o.start) || (u !== o.end);
var g = function Get(){};
g.lex = function(file){ var tmp;
file = (u === file)? u : decodeURIComponent(file);
if(!file || file > (next || key)){
if(next){ g.file = file }
tmp = o.next || key || (o.reverse? o.end || '\uffff' : o.start || '');
if(!file || (o.reverse? file < tmp : file > tmp)){
if(o.next || o.reverse){ g.file = file }
if(tmp = Q[g.file]){
tmp.push({key: key, ack: cb, file: g.file});
tmp.push({key: key, ack: cb, file: g.file, opt: o});
return true;
}
Q[g.file] = [{key: key, ack: cb, file: g.file}];
Q[g.file] = [{key: key, ack: cb, file: g.file, opt: o}];
if(!g.file){
g.it(null, u, {});
return true;
}
r.parse(g.file, g.it);
return true;
}
g.file = file;
}
g.it = function(err, disk){
g.it = function(err, disk, info){
if(g.err = err){ opt.log('err', err) }
g.info = info;
if(disk){ RAD = g.disk = disk }
disk = Q[g.file]; delete Q[g.file];
map(disk, g.ack);
}
g.ack = function(as){
if(!as.ack){ return }
var tmp = as.key, rad = g.disk || noop, data = rad(tmp), last = rad.last;
if(data){ as.ack(g.err, data) }
else if(!as.file){ return as.ack(g.err, u) }
if(!last || last === tmp){ return as.ack(g.err, u) } // is this correct?
if(last > tmp && 0 > last.indexOf(tmp)){ return as.ack(g.err, u) }
r.read(tmp, as.ack, as.file);
var tmp = as.key, o = as.opt, info = g.info, rad = g.disk || noop, data = r.range(rad(tmp), o), last = rad.last || Radix.map(rad, rev, revo);
o.parsed = (o.parsed || 0) + (info.parsed||0);
o.chunks = (o.chunks || 0) + 1;
if(!o.some){ o.some = (u !== data) }
if(u !== data){ as.ack(g.err, data, o) }
else if(!as.file){ !o.some && as.ack(g.err, u, o); return }
if(!o.span){
if(/*!last || */last === tmp){ !o.some && as.ack(g.err, u, o); return }
if(last && last > tmp && 0 != last.indexOf(tmp)){ !o.some && as.ack(g.err, u, o); return }
}
if(o.some && o.parsed >= o.limit){ return }
o.next = as.file;
r.read(tmp, as.ack, o);
}
if(o.reverse){ g.lex.reverse = true }
r.list(g.lex);
}
function rev(a,b){ return b }
var revo = {reverse: true};
}());
;(function(){
@ -236,14 +297,14 @@
Then we can work on the harder problem of being multi-process.
*/
var Q = {}, s = String.fromCharCode(31);
r.parse = function(file, cb){ var q;
r.parse = function(file, cb, raw){ var q;
if(q = Q[file]){ return q.push(cb) } q = Q[file] = [cb];
var p = function Parse(){};
var p = function Parse(){}, info = {};
p.disk = Radix();
p.read = function(err, data){ var tmp;
LOG && console.log('read disk in', +new Date - start, ename(file)); // keep this commented out in
delete Q[file];
if((p.err = err) || (p.not = !data)){
//return cb(err, u);//map(q, p.ack);
return map(q, p.ack);
}
if(typeof data !== 'string'){
@ -251,12 +312,33 @@
if(opt.pack <= data.length){
p.err = "Chunk too big!";
} else {
data = data.toString();
data = data.toString(); // If it crashes, it crashes here. How!?? We check size first!
}
}catch(e){ p.err = e }
if(p.err){ return map(q, p.ack) }
}
info.parsed = data.length;
LOG && (start = +new Date); // keep this commented out in production!
if(opt.jsonify){ // temporary testing idea
try{
var json = JSON.parse(data);
p.disk.$ = json;
LOG && console.log('parsed JSON in', +new Date - start); // keep this commented out in production!
map(q, p.ack);
return;
}catch(e){ tmp = e }
if('{' === data[0]){
p.err = tmp || "JSON error!";
return map(q, p.ack);
}
}
LOG && (start = +new Date); // keep this commented out in production!
var tmp = p.split(data), pre = [], i, k, v;
if(!tmp || 0 !== tmp[1]){
p.err = "File '"+file+"' does not have root radix! ";
return map(q, p.ack);
}
while(tmp){
k = v = u;
i = tmp[1];
@ -274,6 +356,7 @@
if(u !== k && u !== v){ p.disk(pre.join(''), v) }
tmp = p.split(tmp[2]);
}
LOG && console.log('parsed RAD in', +new Date - start); // keep this commented out in production!
//cb(err, p.disk);
map(q, p.ack);
};
@ -290,9 +373,11 @@
}
p.ack = function(cb){
if(!cb){ return }
if(p.err || p.not){ return cb(p.err, u) }
cb(u, p.disk);
if(p.err || p.not){ return cb(p.err, u, info) }
cb(u, p.disk, info);
}
var start; LOG && (start = +new Date); // keep this commented out in production!
if(raw){ return p.read(null, raw) }
opt.store.get(ename(file), p.read);
}
}());
@ -301,9 +386,10 @@
var dir, q, f = String.fromCharCode(28), ef = ename(f);
r.list = function(cb){
if(dir){
var tmp = {reverse: (cb.reverse)? 1 : 0};
Radix.map(dir, function(val, key){
return cb(key);
}) || cb();
}, tmp) || cb();
return;
}
if(q){ return q.push(cb) } q = [cb];
@ -315,8 +401,11 @@
return cb(u, 1);
}
dir(file, true);
cb.listed = (cb.listed || 0) + 1;
r.write(f, dir, function(err, ok){
if(err){ return cb(err) }
cb.listed = (cb.listed || 0) - 1;
if(cb.listed !== 0){ return }
cb(u, 1);
}, true);
}
@ -345,14 +434,13 @@
r.list.dir = dir = rad;
tmp = q; q = null;
Gun.list.map(tmp, function(cb){
Radix.map(dir, function(val, key){
return cb(key);
}) || cb();
r.list(cb);
});
}
}());
var noop = function(){}, RAD, u;
Radisk.has[opt.file] = r;
return r;
}
@ -421,6 +509,7 @@
} else {
var Gun = require('../gun');
var Radix = require('./radix');
//var Radix = require('./radix2'); Radisk = require('./radisk2');
try{ module.exports = Radisk }catch(e){}
}

525
lib/radisk2.js Normal file
View File

@ -0,0 +1,525 @@
;(function(){
console.log("RADISK 2!!!!");
function Radisk(opt){
opt = opt || {};
opt.log = opt.log || console.log;
opt.file = String(opt.file || 'radata');
var has = (Radisk.has || (Radisk.has = {}))[opt.file];
if(has){ return has }
opt.pack = opt.pack || (opt.memory? (opt.memory * 1000 * 1000) : 1399000000) * 0.3; // max_old_space_size defaults to 1400 MB.
opt.until = opt.until || opt.wait || 250;
opt.batch = opt.batch || (10 * 1000);
opt.chunk = opt.chunk || (1024 * 1024 * 10); // 10MB
opt.code = opt.code || {};
opt.code.from = opt.code.from || '!';
//opt.jsonify = true; // TODO: REMOVE!!!!
function ename(t){ return encodeURIComponent(t).replace(/\*/g, '%2A') }
function atomic(v){ return u !== v && (!v || 'object' != typeof v) }
var map = Gun.obj.map;
var LOG = false;
if(!opt.store){
return opt.log("ERROR: Radisk needs `opt.store` interface with `{get: fn, put: fn (, list: fn)}`!");
}
if(!opt.store.put){
return opt.log("ERROR: Radisk needs `store.put` interface with `(file, data, cb)`!");
}
if(!opt.store.get){
return opt.log("ERROR: Radisk needs `store.get` interface with `(file, cb)`!");
}
if(!opt.store.list){
//opt.log("WARNING: `store.list` interface might be needed!");
}
/*
Any and all storage adapters should...
1. Because writing to disk takes time, we should batch data to disk. This improves performance, and reduces potential disk corruption.
2. If a batch exceeds a certain number of writes, we should immediately write to disk when physically possible. This caps total performance, but reduces potential loss.
*/
var r = function(key, val, cb){
key = ''+key;
if(val instanceof Function){
var o = cb || {};
cb = val;
val = r.batch(key);
if(u !== val){
cb(u, r.range(val, o), o);
if(atomic(val)){ return }
// if a node is requested and some of it is cached... the other parts might not be.
}
if(r.thrash.at){
val = r.thrash.at(key);
if(u !== val){
cb(u, r.range(val, o), o);
if(atomic(val)){ cb(u, val, o); return }
// if a node is requested and some of it is cached... the other parts might not be.
}
}
return r.read(key, cb, o);
}
r.batch(key, val);
if(cb){ r.batch.acks.push(cb) }
if(++r.batch.ed >= opt.batch){ return r.thrash() } // (2)
if(r.batch.to){ return }
//clearTimeout(r.batch.to); // (1) // THIS LINE IS EVIL! NEVER USE IT! ALSO NEVER DELETE THIS SO WE NEVER MAKE THE SAME MISTAKE AGAIN!
r.batch.to = setTimeout(r.thrash, opt.until || 1);
}
r.batch = Radix();
r.batch.acks = [];
r.batch.ed = 0;
r.thrash = function(){
var thrash = r.thrash;
if(thrash.ing){ return thrash.more = true }
thrash.more = false;
thrash.ing = true;
var batch = thrash.at = r.batch, i = 0;
clearTimeout(r.batch.to);
r.batch = null;
r.batch = Radix();
r.batch.acks = [];
r.batch.ed = 0;
//var id = Gun.text.random(2), S = (+new Date); console.log("<<<<<<<<<<<<", id);
r.save(batch, function(err, ok){
if(++i > 1){ opt.log('RAD ERR: Radisk has callbacked multiple times, please report this as a BUG at github.com/amark/gun/issues ! ' + i); return }
if(err){ opt.log('err', err) }
//console.log(">>>>>>>>>>>>", id, ((+new Date) - S), batch.acks.length);
map(batch.acks, function(cb){ cb(err, ok) });
thrash.at = null;
thrash.ing = false;
if(thrash.more){ thrash() }
});
}
/*
1. Find the first radix item in memory.
2. Use that as the starting index in the directory of files.
3. Find the first file that is lexically larger than it,
4. Read the previous file to that into memory
5. Scan through the in memory radix for all values lexically less than the limit.
6. Merge and write all of those to the in-memory file and back to disk.
7. If file too large, split. More details needed here.
*/
r.save = function(rad, cb){
var s = function Span(){};
s.find = function(tree, key){
if(key < s.start){ return }
s.start = key;
r.list(s.lex);
return true;
}
s.lex = function(file){
file = (u === file)? u : decodeURIComponent(file);
if(!file || file > s.start){
s.mix(s.file || opt.code.from, s.start, s.end = file);
return true;
}
s.file = file;
}
s.mix = function(file, start, end){
s.start = s.end = s.file = u;
r.parse(file, function(err, disk){
if(err){ return cb(err) }
disk = disk || Radix();
Radix.map(rad, function(val, key){
if(key < start){ return }
if(end && end < key){ return s.start = key }
// PLUGIN: consider adding HAM as an extra layer of protection
disk(key, val); // merge batch[key] -> disk[key]
});
r.write(file, disk, s.next);
});
}
s.next = function(err, ok){
if(s.err = err){ return cb(err) }
if(s.start){ return Radix.map(rad, s.find) }
cb(err, ok);
}
Radix.map(rad, s.find);
}
/*
Any storage engine at some point will have to do a read in order to write.
This is true of even systems that use an append only log, if they support updates.
Therefore it is unavoidable that a read will have to happen,
the question is just how long you delay it.
*/
r.write = function(file, rad, cb, o){
o = ('object' == typeof o)? o : {force: o};
var f = function Fractal(){};
f.text = '';
f.count = 0;
f.file = file;
f.each = function(val, key, k, pre){
//console.log("RAD:::", JSON.stringify([val, key, k, pre]));
if(u !== val){ f.count++ }
if(opt.pack <= (val||'').length){ return cb("Record too big!"), true }
var enc = Radisk.encode(pre.length) +'#'+ Radisk.encode(k) + (u === val? '' : ':'+ Radisk.encode(val)) +'\n';
if((opt.chunk < f.text.length + enc.length) && (1 < f.count) && !o.force){
f.text = '';
f.limit = Math.ceil(f.count/2);
f.count = 0;
f.sub = Radix();
Radix.map(rad, f.slice);
return true;
}
f.text += enc;
}
f.write = function(){
var tmp = ename(file);
var start; LOG && (start = (+new Date)); // comment this out!
opt.store.put(tmp, f.text, function(err){
LOG && console.log("wrote JSON in", (+new Date) - start); // comment this out!
if(err){ return cb(err) }
r.list.add(tmp, cb);
});
}
f.slice = function(val, key){
if(key < f.file){ return }
if(f.limit < (++f.count)){
var name = f.file;
f.file = key;
f.count = 0;
r.write(name, f.sub, f.next, o);
return true;
}
f.sub(key, val);
}
f.next = function(err){
if(err){ return cb(err) }
f.sub = Radix();
if(!Radix.map(rad, f.slice)){
r.write(f.file, f.sub, cb, o);
}
}
if(opt.jsonify){ return r.write.jsonify(f, file, rad, cb, o) } // temporary testing idea
if(!Radix.map(rad, f.each, true)){ f.write() }
}
r.write.jsonify = function(f, file, rad, cb, o){
var raw;
var start; LOG && (start = (+new Date)); // comment this out!
try{raw = JSON.stringify(rad.$);
}catch(e){ return cb("Record too big!") }
LOG && console.log("stringified JSON in", (+new Date) - start); // comment this out!
if(opt.chunk < raw.length && !o.force){
if(Radix.map(rad, f.each, true)){ return }
}
f.text = raw;
f.write();
}
r.range = function(tree, o){
if(!tree || !o){ return }
if(u === o.start && u === o.end){ return tree }
if(atomic(tree)){ return tree }
var sub = Radix();
Radix.map(tree, function(v,k){
sub(k,v);
}, o)
return sub('');
}
;(function(){
var Q = {};
r.read = function(key, cb, o){
o = o || {};
if(RAD && !o.next){ // cache
var val = RAD(key);
//if(u !== val){
//cb(u, val, o);
if(atomic(val)){ cb(u, val, o); return }
// if a node is requested and some of it is cached... the other parts might not be.
//}
}
o.span = (u !== o.start) || (u !== o.end);
var g = function Get(){};
g.lex = function(file){ var tmp;
file = (u === file)? u : decodeURIComponent(file);
tmp = o.next || key || (o.reverse? o.end || '\uffff' : o.start || '');
if(!file || (o.reverse? file < tmp : file > tmp)){
if(o.next || o.reverse){ g.file = file }
if(tmp = Q[g.file]){
tmp.push({key: key, ack: cb, file: g.file, opt: o});
return true;
}
Q[g.file] = [{key: key, ack: cb, file: g.file, opt: o}];
if(!g.file){
g.it(null, u, {});
return true;
}
r.parse(g.file, g.it);
return true;
}
g.file = file;
}
g.it = function(err, disk, info){
if(g.err = err){ opt.log('err', err) }
g.info = info;
if(disk){ RAD = g.disk = disk }
disk = Q[g.file]; delete Q[g.file];
map(disk, g.ack);
}
g.ack = function(as){
if(!as.ack){ return }
var tmp = as.key, o = as.opt, info = g.info, rad = g.disk || noop, data = r.range(rad(tmp), o), last = rad.last;
o.parsed = (o.parsed || 0) + (info.parsed||0);
o.chunks = (o.chunks || 0) + 1;
if(!o.some){ o.some = (u !== data) }
if(u !== data){ as.ack(g.err, data, o) }
else if(!as.file){ !o.some && as.ack(g.err, u, o); return }
if(!o.span){
if(/*!last || */last === tmp){ !o.some && as.ack(g.err, u, o); return }
if(last && last > tmp && 0 != last.indexOf(tmp)){ !o.some && as.ack(g.err, u, o); return }
}
if(o.some && o.parsed >= o.limit){ return }
o.next = as.file;
r.read(tmp, as.ack, o);
}
if(o.reverse){ g.lex.reverse = true }
r.list(g.lex);
}
}());
;(function(){
/*
Let us start by assuming we are the only process that is
changing the directory or bucket. Not because we do not want
to be multi-process/machine, but because we want to experiment
with how much performance and scale we can get out of only one.
Then we can work on the harder problem of being multi-process.
*/
var Q = {}, s = String.fromCharCode(31);
r.parse = function(file, cb, raw){ var q;
if(q = Q[file]){ return q.push(cb) } q = Q[file] = [cb];
var p = function Parse(){}, info = {};
p.disk = Radix();
p.read = function(err, data){ var tmp;
delete Q[file];
if((p.err = err) || (p.not = !data)){
return map(q, p.ack);
}
if(typeof data !== 'string'){
try{
if(opt.pack <= data.length){
p.err = "Chunk too big!";
} else {
data = data.toString(); // If it crashes, it crashes here. How!?? We check size first!
}
}catch(e){ p.err = e }
if(p.err){ return map(q, p.ack) }
}
info.parsed = data.length;
var start; LOG && (start = (+new Date)); // keep this commented out in production!
if(opt.jsonify){ // temporary testing idea
try{
var json = JSON.parse(data);
p.disk.$ = json;
LOG && console.log('parsed JSON in', (+new Date) - start); // keep this commented out in production!
map(q, p.ack);
return;
}catch(e){ tmp = e }
if('{' === data[0]){
p.err = tmp || "JSON error!";
return map(q, p.ack);
}
}
var start; LOG && (start = (+new Date)); // keep this commented out in production!
var tmp = p.split(data), pre = [], i, k, v, at, ats=[];
if(!tmp || 0 !== tmp[1]){
p.err = "File '"+file+"' does not have root radix! ";
return map(q, p.ack);
}
while(tmp){
k = v = u;
i = tmp[1];
tmp = p.split(tmp[2])||'';
if('#' == tmp[0]){
k = tmp[1];
pre = pre.slice(0,i);
if(i <= pre.length){
pre.push(k);
}
}
tmp = p.split(tmp[2])||'';
if('\n' == tmp[0]){
at = ats[i] || p.disk.at;
p.disk(k, u, at);
ats[i] = p.disk.at;
ats[i+1] = p.disk.at[k] || (p.disk.at[k]={});
continue;
}
if('=' == tmp[0] || ':' == tmp[0]){ v = tmp[1] }
if(u !== k && u !== v){
// p.disk(pre.join(''), v)// mark's code
at = ats[i];// || p.disk.at;
p.disk(k, v, at);
ats[i] = p.disk.at;
ats[i+1] = p.disk.at[k];
}
tmp = p.split(tmp[2]);
}
LOG && console.log('parsed JSON in', (+new Date) - start); // keep this commented out in production!
//cb(err, p.disk);
map(q, p.ack);
};
p.split = function(t){
if(!t){ return }
var l = [], o = {}, i = -1, a = '', b, c;
i = t.indexOf(s);
if(!t[i]){ return }
a = t.slice(0, i);
l[0] = a;
l[1] = b = Radisk.decode(t.slice(i), o);
l[2] = t.slice(i + o.i);
return l;
}
p.ack = function(cb){
if(!cb){ return }
if(p.err || p.not){ return cb(p.err, u, info) }
cb(u, p.disk, info);
}
if(raw){ return p.read(null, raw) }
opt.store.get(ename(file), p.read);
}
}());
;(function(){
var dir, q, f = String.fromCharCode(28), ef = ename(f);
r.list = function(cb){
if(dir){
var tmp = {reverse: (cb.reverse)? 1 : 0};
Radix.map(dir, function(val, key){
return cb(key);
}, tmp) || cb();
return;
}
if(q){ return q.push(cb) } q = [cb];
r.parse(f, r.list.init);
}
r.list.add = function(file, cb){
var has = dir(file);
if(has || file === ef){
return cb(u, 1);
}
dir(file, true);
cb.listed = (cb.listed || 0) + 1;
r.write(f, dir, function(err, ok){
if(err){ return cb(err) }
cb.listed = (cb.listed || 0) - 1;
if(cb.listed !== 0){ return }
cb(u, 1);
}, true);
}
r.list.init = function(err, disk){
if(err){
opt.log('list', err);
setTimeout(function(){ r.parse(f, r.list.init) }, 1000);
return;
}
if(disk){
r.list.drain(disk);
return;
}
if(!opt.store.list){
r.list.drain(Radix());
return;
}
// import directory.
opt.store.list(function(file){
dir = dir || Radix();
if(!file){ return r.list.drain(dir) }
r.list.add(file, noop);
});
}
r.list.drain = function(rad, tmp){
r.list.dir = dir = rad;
tmp = q; q = null;
Gun.list.map(tmp, function(cb){
r.list(cb);
});
}
}());
var noop = function(){}, RAD, u;
Radisk.has[opt.file] = r;
return r;
}
;(function(){
var _ = String.fromCharCode(31), u;
Radisk.encode = function(d, o, s){ s = s || _;
var t = s, tmp;
if(typeof d == 'string'){
var i = d.indexOf(s);
while(i != -1){ t += s; i = d.indexOf(s, i+1) }
return t + '"' + d + s;
} else
if(d && d['#'] && (tmp = Gun.val.link.is(d))){
return t + '#' + tmp + t;
} else
if(Gun.num.is(d)){
return t + '+' + (d||0) + t;
} else
if(null === d){
return t + ' ' + t;
} else
if(true === d){
return t + '+' + t;
} else
if(false === d){
return t + '-' + t;
}// else
//if(binary){}
}
Radisk.decode = function(t, o, s){ s = s || _;
var d = '', i = -1, n = 0, c, p;
if(s !== t[0]){ return }
while(s === t[++i]){ ++n }
p = t[c = n] || true;
while(--n >= 0){ i = t.indexOf(s, i+1) }
if(i == -1){ i = t.length }
d = t.slice(c+1, i);
if(o){ o.i = i+1 }
if('"' === p){
return d;
} else
if('#' === p){
return Gun.val.link.ify(d);
} else
if('+' === p){
if(0 === d.length){
return true;
}
return parseFloat(d);
} else
if(' ' === p){
return null;
} else
if('-' === p){
return false;
}
}
}());
if(typeof window !== "undefined"){
var Gun = window.Gun;
var Radix = window.Radix;
window.Radisk = Radisk;
} else {
var Gun = require('../gun');
var Radix = require('./radix2');
try{ module.exports = Radisk }catch(e){}
}
Radisk.Radix = Radix;
}());

View File

@ -5,9 +5,10 @@
key = ''+key;
if(!t && u !== val){
radix.last = (key < radix.last)? radix.last : key;
radix.sort = null;
delete (radix.$||{})[_];
}
t = t || radix[_] || (radix[_] = {});
t = t || radix.$ || (radix.$ = {});
if(!key && Object.keys(t).length){ return t }
var i = 0, l = key.length-1, k = key[i], at, tmp;
while(!(at = t[k]) && i < l){
k += key[++i];
@ -15,9 +16,9 @@
if(!at){
if(!map(t, function(r, s){
var ii = 0, kk = '';
while(s[ii] == key[ii]){
if((s||'').length){ while(s[ii] == key[ii]){
kk += s[ii++];
}
} }
if(kk){
if(u === val){
if(ii <= l){ return }
@ -25,47 +26,64 @@
}
var __ = {};
__[s.slice(ii)] = r;
(__[key.slice(ii)] = {})[$] = val;
(t[kk] = {})[_] = __;
ii = key.slice(ii);
('' === ii)? (__[''] = val) : ((__[ii] = {})[''] = val);
t[kk] = __;
delete t[s];
return true;
}
})){
if(u === val){ return; }
(t[k] || (t[k] = {}))[$] = val;
(t[k] || (t[k] = {}))[''] = val;
}
if(u === val){
return tmp;
}
} else
if(i == l){
if(u === val){ return (u === (tmp = at[$]))? at[_] : tmp }
at[$] = val;
if(u === val){ return (u === (tmp = at['']))? at : tmp }
at[''] = val;
} else {
if(u !== val){ at.sort = null }
return radix(key.slice(++i), val, at[_] || (at[_] = {}));
if(u !== val){ delete at[_] }
return radix(key.slice(++i), val, at || (at = {}));
}
}
return radix;
};
Radix.map = function map(radix, cb, opt, pre){ pre = pre || [];
var t = radix[_] || radix, keys = radix.sort || (radix.sort = Object.keys(t).sort()), i = 0, l = keys.length;
for(;i < l; i++){ var key = keys[i], tree = t[key], tmp;
if(u !== (tmp = tree[$])){
tmp = cb(tmp, pre.join('') + key, key, pre);
var t = ('function' == typeof radix)? radix.$ || {} : radix;
if(!t){ return }
var keys = (t[_]||no).sort || (t[_] = function $(){ $.sort = Object.keys(t).sort(); return $ }()).sort, rev;
//var keys = Object.keys(t).sort();
opt = (true === opt)? {branch: true} : (opt || {});
if(rev = opt.reverse){ keys = keys.slice().reverse() }
var start = opt.start, end = opt.end;
var i = 0, l = keys.length;
for(;i < l; i++){ var key = keys[i], tree = t[key], tmp, p, pt;
if(!tree || '' === key || _ === key){ continue }
p = pre.slice(); p.push(key);
pt = p.join('');
if(u !== start && pt < (start||'').slice(0,pt.length)){ continue }
if(u !== end && (end || '\uffff') < pt){ continue }
if(rev){ // children must be checked first when going in reverse.
tmp = map(tree, cb, opt, p);
if(u !== tmp){ return tmp }
} else
if(opt){
cb(u, pre.join(''), key, pre);
}
if(tmp = tree[_]){
pre.push(key);
if(u !== (tmp = tree[''])){
tmp = cb(tmp, pt, key, pre);
if(u !== tmp){ return tmp }
} else
if(opt.branch){
tmp = cb(u, pt, key, pre);
if(u !== tmp){ return tmp }
}
pre = p;
if(!rev){
tmp = map(tree, cb, opt, pre);
//tmp = map(tmp, cb, opt, pre);
if(u !== tmp){ return tmp }
pre.pop();
}
pre.pop();
}
};
@ -80,6 +98,6 @@
}
var map = Gun.obj.map, no = {}, u;
var $ = String.fromCharCode(30), _ = String.fromCharCode(29);
var _ = String.fromCharCode(24);
}());

98
lib/radix2.js Normal file
View File

@ -0,0 +1,98 @@
;(function(){
function Radix(){
var radix = function(key, val, t){
key = ''+key;
if(!t && u !== val){
radix.last = (key < radix.last)? radix.last : key;
delete (radix.$||{})[_];
}
t = t || radix.$ || (radix.$ = {});
if(!key && Object.keys(t).length){ return t }
var i = 0, l = key.length-1, k = key[i], at, tmp;
while(!(at = t[k]) && i < l){
k += key[++i];
}
radix.at = t; /// caching to external access.
if(!at){
if(!map(t, function(r, s){
var ii = 0, kk = '';
if((s||'').length){ while(s[ii] == key[ii]){
kk += s[ii++];
} }
if(kk){
if(u === val){
if(ii <= l){ return }
return (tmp || (tmp = {}))[s.slice(ii)] = r;
}
var __ = {};
__[s.slice(ii)] = r;
ii = key.slice(ii);
('' === ii)? (__[''] = val) : ((__[ii] = {})[''] = val);
t[kk] = __;
delete t[s];
return true;
}
})){
if(u === val){ return; }
(t[k] || (t[k] = {}))[''] = val;
}
if(u === val){
return tmp;
}
} else
if(i == l){
if(u === val){ return (u === (tmp = at['']))? at : tmp }
at[''] = val;
} else {
if(u !== val){ delete at[_] }
return radix(key.slice(++i), val, at || (at = {}));
}
}
return radix;
};
Radix.map = function map(radix, cb, opt, pre){ pre = pre || [];
var t = ('function' == typeof radix)? radix.$ || {} : radix;
if(!t){ return }
var keys = (t[_]||no).sort || (t[_] = function $(){ $.sort = Object.keys(t).sort(); return $ }()).sort;
//var keys = Object.keys(t).sort();
opt = (true === opt)? {branch: true} : (opt || {});
if(opt.reverse){ keys = keys.slice().reverse() }
var start = opt.start, end = opt.end;
var i = 0, l = keys.length;
for(;i < l; i++){ var key = keys[i], tree = t[key], tmp, p, pt;
if(!tree || '' === key || _ === key){ continue }
p = pre.slice(); p.push(key);
pt = p.join('');
if(u !== start && pt < (start||'').slice(0,pt.length)){ continue }
if(u !== end && (end || '\uffff') < pt){ continue }
if(u !== (tmp = tree[''])){
tmp = cb(tmp, pt, key, pre);
if(u !== tmp){ return tmp }
} else
if(opt.branch){
tmp = cb(u, pt, key, pre);
if(u !== tmp){ return tmp }
}
pre = p;
tmp = map(tree, cb, opt, pre);
if(u !== tmp){ return tmp }
pre.pop();
}
};
Object.keys = Object.keys || function(o){ return map(o, function(v,k,t){t(k)}) }
if(typeof window !== "undefined"){
var Gun = window.Gun;
window.Radix = Radix;
} else {
var Gun = require('../gun');
try{ module.exports = Radix }catch(e){}
}
var map = Gun.obj.map, no = {}, u;
var _ = String.fromCharCode(24);
}());

View File

@ -1,10 +1,15 @@
var Gun = (typeof window !== "undefined")? window.Gun : require('../gun');
function Store(opt){
opt = opt || {};
opt.log = opt.log || console.log;
opt.file = String(opt.file || 'radata');
var fs = require('fs'), u;
var store = function Store(){};
if(Store[opt.file]){
console.log("Warning: reusing same fs store and options as 1st.");
return Store[opt.file];
}
Store[opt.file] = store;
store.put = function(file, data, cb){
var random = Math.random().toString(36).slice(-3);
@ -20,18 +25,13 @@ function Store(opt){
if('ENOENT' === (err.code||'').toUpperCase()){
return cb(null);
}
Gun.log("ERROR:", err)
opt.log("ERROR:", err)
}
cb(err, data);
});
};
store.list = function(cb, match){
fs.readdir(opt.file, function(err, dir){
Gun.obj.map(dir, cb) || cb(); // Stream interface requires a final call to know when to be done.
});
};
if(!fs.existsSync(opt.file)){ fs.mkdirSync(opt.file) }
//store.list(function(){ return true });
function move(oldPath, newPath, cb) {
fs.rename(oldPath, newPath, function (err) {
@ -60,29 +60,4 @@ function Store(opt){
return store;
}
function Mem(opt){
opt = opt || {};
opt.file = String(opt.file || 'radata');
var storage = Mem.storage || (Mem.storage = {});
var store = function Store(){}, u;
store.put = function(file, data, cb){
setTimeout(function(){
storage[file] = data;
cb(null, 1);
}, 1);
};
store.get = function(file, cb){
setTimeout(function(){
var tmp = storage[file] || u;
cb(null, tmp);
}, 1);
};
store.list = function(cb, match){ // supporting this is no longer needed! Optional.
setTimeout(function(){
Gun.obj.map(Object.keys(storage), cb) || cb();
}, 1);
};
return store;
}
module.exports = Store;//Gun.TESTING? Mem : Store;
module.exports = Store;

View File

@ -1,129 +1,69 @@
;(function(){
var Gun = (typeof window !== "undefined")? window.Gun : require('../gun');
Gun.on('create', function(root){
this.to.next(root);
root.opt.store = root.opt.store || Store(root.opt);
});
function Store(opt){
opt = opt || {};
opt.file = String(opt.file || 'radata');
var db = null;
opt.chunk = opt.chunk || (1024 * 1024); // 1MB
var db = null, u;
opt.indexedDB = opt.indexedDB || window.indexedDB;
// Initialize indexedDB. Version 1.
var request = opt.indexedDB.open(opt.file, 1)
try{opt.indexedDB = opt.indexedDB || indexedDB}catch(e){}
try{if(!opt.indexedDB || 'file:' == location.protocol){
var store = {}, s = {};
store.put = function(f, d, cb){ s[f] = d; cb(null, 1) };
store.get = function(f, cb){ cb(null, s[f] || u) };
console.log('Warning: No indexedDB exists to persist data to!');
return store;
}}catch(e){}
var store = function Store(){};
if(Store[opt.file]){
console.log("Warning: reusing same IndexedDB store and options as 1st.");
return Store[opt.file];
}
Store[opt.file] = store;
// Create schema. onupgradeneeded is called only when DB is first created or when the DB version increases.
request.onupgradeneeded = function(event){
var db = event.target.result;
db.createObjectStore(opt.file);
store.start = function(){
var o = indexedDB.open(opt.file, 1);
o.onupgradeneeded = function(eve){ (eve.target.result).createObjectStore(opt.file) }
o.onsuccess = function(){ db = o.result }
o.onerror = function(eve){ console.log(eve||1); }
}; store.start();
store.put = function(key, data, cb){
if(!db){ setTimeout(function(){ store.put(key, data, cb) },1); return }
var tx = db.transaction([opt.file], 'readwrite');
var obj = tx.objectStore(opt.file);
var req = obj.put(data, ''+key);
req.onsuccess = obj.onsuccess = tx.onsuccess = function(){ cb(null, 1) }
req.onabort = obj.onabort = tx.onabort = function(eve){ cb(eve||'put.tx.abort') }
req.onerror = obj.onerror = tx.onerror = function(eve){ cb(eve||'put.tx.error') }
}
// onsuccess is called when the DB is ready.
request.onsuccess = function(){
db = request.result;
store.get = function(key, cb){
if(!db){ setTimeout(function(){ store.get(key, cb) },9); return }
var tx = db.transaction([opt.file], 'readonly');
var obj = tx.objectStore(opt.file);
var req = obj.get(''+key);
req.onsuccess = function(){ cb(null, req.result) }
req.onabort = function(eve){ cb(eve||4) }
req.onerror = function(eve){ cb(eve||5) }
}
request.onerror = function(event){
console.log('ERROR: RAD IndexedDB generic error:', event);
};
var store = function Store(){}, u;
store.put = function(file, data, cb){
cb = cb || function(){};
var doPut = function(){
// Start a transaction. The transaction will be automaticallt closed when the last success/error handler took no new action.
var transaction = db.transaction([opt.file], 'readwrite');
// Add or update data.
var radStore = transaction.objectStore(opt.file);
var putRequest = radStore.put(data, file);
putRequest.onsuccess = radStore.onsuccess = transaction.onsuccess = function(){
//console.log('RAD IndexedDB put transaction was succesful.');
cb(null, 1);
};
putRequest.onabort = radStore.onabort = transaction.onabort = function(){
var es = 'ERROR: RAD IndexedDB put transaction was aborted.';
console.log(es);
cb(es, undefined);
};
putRequest.onerror = radStore.onerror = transaction.onerror = function(event){
var es = 'ERROR: RAD IndexedDB put transaction was in error: ' + JSON.stringify(event)
console.log(es);
cb(es, undefined);
};
}
if(!db){
waitDbReady(doPut, 100, function(){
var es = 'ERROR: Timeout: RAD IndexedDB not ready.';
console.log(es);
cb(es, undefined);
}, 10)
} else {
doPut();
}
};
store.get = function(file, cb){
cb = cb || function(){};
var doGet = function(){
// Start a transaction. The transaction will be automaticallt closed when the last success/error handler took no new action.
var transaction = db.transaction([opt.file], 'readwrite');
// Read data.
var radStore = transaction.objectStore(opt.file);
var getRequest = radStore.get(file);
getRequest.onsuccess = function(){
//console.log('RAD IndexedDB get transaction was succesful.');
cb(null, getRequest.result);
};
getRequest.onabort = function(){
var es = 'ERROR: RAD IndexedDB get transaction was aborted.';
console.log(es);
cb(es, undefined);
};
getRequest.onerror = function(event){
var es = 'ERROR: RAD IndexedDB get transaction was in error: ' + JSON.stringify(event)
console.log(es);
cb(es, undefined);
};
}
if(!db){
waitDbReady(doGet, 100, function(){
var es = 'ERROR: Timeout: RAD IndexedDB not ready.';
console.log(es);
cb(es, undefined);
}, 10)
} else {
doGet();
}
};
var waitDbReady = function(readyFunc, checkInterval, timeoutFunc, timeoutSecs){
var startTime = new Date();
var checkFunc = function(){
if(db){
readyFunc();
} else {
if((new Date() - startTime) / 1000 >= timeoutSecs){
timeoutFunc();
} else {
setTimeout(checkFunc, checkInterval);
}
}
};
checkFunc();
};
setInterval(function(){ db && db.close(); db = null; store.start() }, 1000 * 15); // reset webkit bug?
return store;
}
if(Gun.window){
Gun.window.RindexedDB = Store;
if(typeof window !== "undefined"){
(Store.window = window).RindexedDB = Store;
} else {
module.exports = Store;
try{ module.exports = Store }catch(e){}
}
}());
try{
var Gun = Store.window.Gun || require('../gun');
Gun.on('create', function(root){
this.to.next(root);
root.opt.store = root.opt.store || Store(root.opt);
});
}catch(e){}
}());

29
lib/rls.js Normal file
View File

@ -0,0 +1,29 @@
;(function(){
function Store(opt){
opt = opt || {};
opt.file = String(opt.file || 'radata');
var store = function Store(){};
var ls = localStorage;
store.put = function(key, data, cb){ ls[''+key] = data; cb(null, 1) }
store.get = function(key, cb){ cb(null, ls[''+key]) }
return store;
}
if(typeof window !== "undefined"){
(Store.window = window).RlocalStorage = Store;
} else {
try{ module.exports = Store }catch(e){}
}
try{
var Gun = Store.window.Gun || require('../gun');
Gun.on('create', function(root){
this.to.next(root);
root.opt.store = root.opt.store || Store(root.opt);
});
}catch(e){}
}());

22
lib/rmem.js Normal file
View File

@ -0,0 +1,22 @@
function Rmem(){
var opt = {}, store = {}, u;
opt.put = function(file, data, cb){
//setTimeout(function(){ // make async
store[file] = data;
cb(null, 1);
//}, 1);
};
opt.get = function(file, cb){
//setTimeout(function(){ // make async
var tmp = store[file] || u;
cb(null, tmp);
//}, 1);
};
return opt;
}
if(typeof window !== "undefined"){
window.Rmem = Rmem;
} else {
try{ module.exports = Rmem }catch(e){}
}

View File

@ -7,9 +7,9 @@ var u, AWS;
Gun.on('create', function(root){
this.to.next(root);
var opt = root.opt;
if(!process.env.AWS_S3_BUCKET){ return }
opt.batch = opt.batch || (1000 * 1);
opt.until = opt.until || (1000 * 15);
if(!opt.s3 && !process.env.AWS_S3_BUCKET){ return }
opt.batch = opt.batch || (1000 * 10);
opt.until = opt.until || (1000 * 3);
opt.chunk = opt.chunk || (1024 * 1024 * 10); // 10MB
try{AWS = require('aws-sdk');
@ -41,8 +41,14 @@ function Store(opt){
opt.file = String(opt.file || 'radata');
var opts = opt.s3, s3 = opts.s3;
var c = {p: {}, g: {}, l: {}};
var store = function Store(){};
if(Store[opt.file]){
console.log("Warning: reusing same S3 store and options as 1st.");
return Store[opt.file];
}
Store[opt.file] = store;
store.put = function(file, data, cb){
var params = {Bucket: opts.bucket, Key: file, Body: data};
//console.log("RS3 PUT ---->", (data||"").slice(0,20));
@ -95,4 +101,4 @@ function Store(opt){
return store;
}
module.exports = Store;
module.exports = Store;

View File

@ -1,22 +1,26 @@
var fs = require('fs');
var path = require('path');
var dot = /\.\.+/g;
var slash = /\/\/+/g;
function CDN(dir){
return function(req, res){
req.url = (req.url||'').replace(dot,'').replace(slash,'/');
if(serve(req, res)){ return } // filters GUN requests!
fs.createReadStream(path.join(dir, req.url)).on('error',function(tmp){ // static files!
try{ tmp = fs.readFileSync(path.join(dir, 'index.html')) }catch(e){}
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(tmp+''); // or default to index
try{ res.writeHead(200, {'Content-Type': 'text/html'});
res.end(tmp+''); }catch(e){} // or default to index
}).pipe(res); // stream
}
}
function serve(req, res, next){
function serve(req, res, next){ var tmp;
if(typeof req === 'string'){ return CDN(req) }
if(!req || !res){ return false }
next = next || serve;
if(!req.url){ return next() }
if(res.setHeader){ res.setHeader('Access-Control-Allow-Origin', '*') }
if(0 <= req.url.indexOf('gun.js')){
res.writeHead(200, {'Content-Type': 'text/javascript'});
res.end(serve.js = serve.js || require('fs').readFileSync(__dirname + '/../gun.js'));
@ -31,6 +35,11 @@ function serve(req, res, next){
return true;
}
}
if((tmp = req.socket) && (tmp = tmp.server) && (tmp = tmp.route)){ var url;
if(tmp = tmp[(((req.url||'').slice(1)).split('/')[0]||'').split('.')[0]]){
try{ return tmp(req, res, next) }catch(e){ console.log(req.url+' crashed with '+e) }
}
}
return next();
}

View File

@ -13,9 +13,11 @@
require('./rs3');
require('./wire');
try{require('../sea');}catch(e){}
//try{require('../axe');}catch(e){}
try{require('../axe');}catch(e){}
require('./file');
require('./evict');
require('./multicast');
require('./stats');
if('debug' === process.env.GUN_ENV){ require('./debug') }
module.exports = Gun;
}());
}());

58
lib/stats.js Normal file
View File

@ -0,0 +1,58 @@
var Gun = (typeof window !== "undefined")? window.Gun : require('../gun');
Gun.on('opt', function(root){
this.to.next(root);
if(root.once){ return }
if(typeof process === 'undefined'){ return }
if(typeof require === 'undefined'){ return }
var noop = function(){};
var os = require('os') || {};
var fs = require('fs') || {};
fs.existsSync = fs.existsSync || require('path').existsSync;
if(!fs.existsSync){ return }
if(!process){ return }
process.uptime = process.uptime || noop;
process.cpuUsage = process.cpuUsage || noop;
process.memoryUsage = process.memoryUsage || noop;
os.totalmem = os.totalmem || noop;
os.freemem = os.freemem || noop;
os.loadavg = os.loadavg || noop;
os.cpus = os.cpus || noop;
setTimeout(function(){
root.stats = Gun.obj.ify((fs.existsSync(__dirname+'/../stats.'+root.opt.file) && fs.readFileSync(__dirname+'/../stats.'+root.opt.file).toString())) || {};
root.stats.up = root.stats.up || {};
root.stats.up.start = root.stats.up.start || +(new Date);
root.stats.up.count = (root.stats.up.count || 0) + 1;
root.stats.stay = root.stats.stay || {};
root.stats.gap = {};
},1);
setInterval(function(){
if(!root.stats){ root.stats = {} }
var stats = root.stats, tmp;
(stats.up||{}).time = process.uptime();
stats.memory = process.memoryUsage() || {};
stats.memory.totalmem = os.totalmem();
stats.memory.freemem = os.freemem();
stats.cpu = process.cpuUsage() || {};
stats.cpu.loadavg = os.loadavg();
stats.peers = {};
stats.peers.count = Object.keys(root.opt.peers||{}).length;
stats.node = {};
stats.node.count = Object.keys(root.graph||{}).length;
var dam = root.opt.mesh;
if(dam){
stats.dam = {'in': {count: dam.hear.c, done: dam.hear.d}, 'out': {count: dam.say.c, done: dam.say.d}};
dam.hear.c = dam.hear.d = dam.say.c = dam.say.d = 0; // reset
stats.peers.time = dam.bye.time || 0;
}
var rad = root.opt.store; rad = rad && rad.stats;
if(rad){
stats.rad = rad;
root.opt.store.stats = {get:{time:{}, count:0}, put: {time:{}, count:0}}; // reset
}
fs.writeFile(__dirname+'/../stats.'+root.opt.file, JSON.stringify(stats, null, 2), function(err){});
stats.gap = {};
}, 1000 * 15);
Object.keys = Object.keys || function(o){ return Gun.obj.map(o, function(v,k,t){t(k)}) }
});

View File

@ -1,58 +1,103 @@
var Gun = (typeof window !== "undefined")? window.Gun : require('../gun');
Gun.on('create', function(root){
this.to.next(root);
var opt = root.opt, u;
if(false === opt.radisk){ return }
var Radisk = (Gun.window && Gun.window.Radisk) || require('./radisk');
var Radix = Radisk.Radix;
opt.store = opt.store || (!Gun.window && require('./rfs')(opt));
var rad = Radisk(opt), esc = String.fromCharCode(27);
root.on('put', function(msg){
this.to.next(msg);
var id = msg['#'], track = !msg['@'], acks = track? 0 : u; // only ack non-acks.
if(msg.rad && !track){ return } // don't save our own acks
Gun.graph.is(msg.put, null, function(val, key, node, soul){
if(track){ ++acks }
val = Radisk.encode(val, null, esc)+'>'+Radisk.encode(Gun.state.is(node, key), null, esc);
rad(soul+'.'+key, val, (track? ack : u));
});
function ack(err, ok){
acks--;
if(ack.err){ return }
if(ack.err = err){
root.on('in', {'@': id, err: err});
return;
}
if(acks){ return }
root.on('in', {'@': id, ok: 1});
}
});
root.on('get', function(msg){
this.to.next(msg);
var id = msg['#'], soul = msg.get['#'], key = msg.get['.']||'', tmp = soul+'.'+key, node;
rad(tmp, function(err, val){
if(val){
if(val && typeof val !== 'string'){
if(key){
val = u;
} else {
Radix.map(val, each)
}
}
if(!node && val){ each(val, key) }
}
root.on('in', {'@': id, put: Gun.graph.node(node), err: err? err : u, rad: Radix});
});
function each(val, key){
tmp = val.lastIndexOf('>');
var state = Radisk.decode(val.slice(tmp+1), null, esc);
val = Radisk.decode(val.slice(0,tmp), null, esc);
node = Gun.state.ify(node, key, state, val, soul);
}
});
if(Gun.TESTING){ root.opt.file = 'radatatest' }
this.to.next(root);
var opt = root.opt, u;
if(false === opt.radisk){ return }
var Radisk = (Gun.window && Gun.window.Radisk) || require('./radisk');
var Radix = Radisk.Radix;
opt.store = opt.store || (!Gun.window && require('./rfs')(opt));
var rad = Radisk(opt), esc = String.fromCharCode(27);
root.on('put', function(msg){
this.to.next(msg);
var id = msg['#'] || Gun.text.random(3), track = !msg['@'], acks = track? 0 : u; // only ack non-acks.
if(msg.rad && !track){ return } // don't save our own acks
var start = (+new Date); // STATS!
Gun.graph.is(msg.put, null, function(val, key, node, soul){
if(track){ ++acks }
//console.log('put:', soul, key, val);
val = Radisk.encode(val, null, esc)+'>'+Radisk.encode(Gun.state.is(node, key), null, esc);
rad(soul+esc+key, val, (track? ack : u));
});
function ack(err, ok){
acks--;
if(ack.err){ return }
if(ack.err = err){
try{opt.store.stats.put.err = err}catch(e){} // STATS!
root.on('in', {'@': id, err: err});
return;
}
if(acks){ return }
try{opt.store.stats.put.time[statp % 50] = (+new Date) - start; ++statp;
opt.store.stats.put.count++;
}catch(e){} // STATS!
//console.log("PAT!", id);
root.on('in', {'@': id, ok: 1});
}
});
root.on('get', function(msg){
this.to.next(msg);
var id = msg['#'], get = msg.get, soul = msg.get['#'], has = msg.get['.']||'', o = {}, graph, lex, key, tmp, force;
if('string' == typeof soul){
key = soul;
} else
if(soul){
if(u !== (tmp = soul['*'])){ o.limit = force = 1 }
if(u !== soul['>']){ o.start = soul['>'] }
if(u !== soul['<']){ o.end = soul['<'] }
key = force? (''+tmp) : tmp || soul['='];
force = null;
}
if(key && !o.limit){ // a soul.has must be on a soul, and not during soul*
if('string' == typeof has){
key = key+esc+(o.atom = has);
} else
if(has){
if(u !== has['>']){ o.start = has['>']; o.limit = 1 }
if(u !== has['<']){ o.end = has['<']; o.limit = 1 }
if(u !== (tmp = has['*'])){ o.limit = force = 1 }
if(key){ key = key+esc + (force? (''+(tmp||'')) : tmp || (o.atom = has['='] || '')) }
}
}
if((tmp = get['%']) || o.limit){
o.limit = (tmp <= (o.pack || (1000 * 100)))? tmp : 1;
}
if(has['-'] || (soul||{})['-']){ o.reverse = true }
var start = (+new Date); // STATS! // console.log("GET!", id, JSON.stringify(key));
rad(key||'', function(err, data, o){
try{opt.store.stats.get.time[statg % 50] = (+new Date) - start; ++statg;
opt.store.stats.get.count++;
if(err){ opt.store.stats.get.err = err }
}catch(e){} // STATS!
if(data){
if(typeof data !== 'string'){
if(o.atom){
data = u;
} else {
Radix.map(data, each)
}
}
if(!graph && data){ each(data, '') }
}
root.on('in', {'@': id, put: graph, err: err? err : u, rad: Radix});
}, o);
function each(val, has, a,b){
if(!val){ return }
has = (key+has).split(esc);
var soul = has.slice(0,1)[0];
has = has.slice(-1)[0];
o.count = (o.count || 0) + val.length;
tmp = val.lastIndexOf('>');
var state = Radisk.decode(val.slice(tmp+1), null, esc);
val = Radisk.decode(val.slice(0,tmp), null, esc);
(graph = graph || {})[soul] = Gun.state.ify(graph[soul], has, state, val, soul);
if(o.limit && o.limit <= o.count){ return true }
}
});
opt.store.stats = {get:{time:{}, count:0}, put: {time:{}, count:0}}; // STATS!
var statg = 0, statp = 0; // STATS!
});

View File

@ -1,28 +1,36 @@
;(function(){
var Gun = (typeof window !== "undefined")? window.Gun : require('../gun');
var Rad = (Gun.window||{}).Radix || require('./radix');
/// Store the subscribes
Gun.subs = Rad();
function input(msg){
var at = this.as, to = this.to, peer = (msg.mesh||empty).via;
var at = this.as, to = this.to, peer = (msg._||empty).via;
var get = msg.get, soul, key;
if(!peer || !get){ return to.next(msg) }
console.log("super", msg);
// console.log("super", msg);
if(soul = get['#']){
if(key = get['.']){
} else {
}
subscribe(soul, peer, msg);
if (!peer.id) {console.log('[*** WARN] no peer.id %s', soul);}
var subs = Gun.subs(soul) || null;
var tmp = subs ? subs.split(',') : [], p = at.opt.peers;
if (subs) {
Gun.obj.map(subs.split(','), function(peerid) {
if (peerid in p) { tmp.push(peerid); }
});
}
if (tmp.indexOf(peer.id) === -1) { tmp.push(peer.id);}
tmp = tmp.join(',');
Gun.subs(soul, tmp);
var dht = {};
dht[soul] = tmp;
at.opt.mesh.say({dht:dht}, peer);
}
to.next(msg);
}
/// Store the subscribes
Gun.subscribe = {}; /// TODO: use Rad instead of plain object?
function subscribe(soul, peer, msg) {
if (!peer.id) { console.log('super jump peer without id: ', peer, msg); return; } /// TODO: this occurs in first subscription. Use peer reference or peer.wire.id?
Gun.subscribe[soul] = Gun.subscribe[soul] || {};
Gun.subscribe[soul][peer.id] = 1;
}
var empty = {}, u;
if(Gun.window){ return }
try{module.exports = input}catch(e){}

View File

@ -1,16 +1,21 @@
var Gun = (typeof window !== "undefined")? window.Gun : require('../gun');
// Returns a gun reference in a promise and then calls a callback if specified
Gun.chain.promise = function(cb) {
var gun = this, cb = cb || function(ctx) { return ctx };
return (new Promise(function(res, rej) {
gun.once(function(data, key){
res({put: data, get: key, gun: this});
res({put: data, get: key, gun: this}); // gun reference is returned by promise
});
})).then(cb);
})).then(cb); //calling callback with resolved data
};
Gun.chain.then = function(cb) {
return this.promise(function(res){
return cb? cb(res.put) : res.put;
});
};
// Returns a promise for the data, key of the gun call
Gun.chain.then = function() {
var gun = this;
return (new Promise((res, rej)=>{
gun.once(function (data, key) {
res(data, key); //call resolve when data is returned
})
}))
};

View File

@ -11,19 +11,7 @@ var write = function(path, data){
return fs.writeFileSync(nodePath.join(dir, path), data);
}
var rm = function(path, full) {
path = full || nodePath.join(dir, path);
if(!fs.existsSync(path)){ return }
fs.readdirSync(path).forEach(function(file,index){
var curPath = path + "/" + file;
if(fs.lstatSync(curPath).isDirectory()) { // recurse
rm(null, curPath);
} else { // delete file
fs.unlinkSync(curPath);
}
});
fs.rmdirSync(path);
};
var rm = require('./fsrm');
var mk = function(path){
path = nodePath.join(dir, path);

View File

@ -30,11 +30,13 @@
]};
opt.rtc.dataChannel = opt.rtc.dataChannel || {ordered: false, maxRetransmits: 2};
opt.rtc.sdp = opt.rtc.sdp || {mandatory: {OfferToReceiveAudio: false, OfferToReceiveVideo: false}};
opt.announce = function(to){
root.on('out', {rtc: {id: opt.pid, to:to}}); // announce ourself
};
var mesh = opt.mesh = opt.mesh || Gun.Mesh(root);
root.on('create', function(at){
this.to.next(at);
setTimeout(function(){ root.on('out', {rtc: {id: opt.pid}}) },1); // announce ourself
setTimeout(opt.announce, 1);
});
root.on('in', function(msg){
if(msg.rtc){ open(msg) }
@ -44,31 +46,28 @@
function open(msg){
var rtc = msg.rtc, peer, tmp;
if(!rtc || !rtc.id){ return }
delete opt.announce[rtc.id]; /// remove after connect
if(tmp = rtc.answer){
if(!(peer = opt.peers[rtc.id]) || peer.remoteSet){ return }
if(!(peer = opt.peers[rtc.id] || open[rtc.id]) || peer.remoteSet){ return }
return peer.setRemoteDescription(peer.remoteSet = new opt.RTCSessionDescription(tmp));
}
if(tmp = rtc.candidate){
peer = opt.peers[rtc.id] || open({rtc: {id: rtc.id}});
peer = opt.peers[rtc.id] || open[rtc.id] || open({rtc: {id: rtc.id}});
return peer.addIceCandidate(new opt.RTCIceCandidate(tmp));
}
if(opt.peers[rtc.id]){ return }
//if(opt.peers[rtc.id]){ return }
if(open[rtc.id]){ return }
(peer = new opt.RTCPeerConnection(opt.rtc)).id = rtc.id;
var wire = peer.wire = peer.createDataChannel('dc', opt.rtc.dataChannel);
mesh.hi(peer);
open[rtc.id] = peer;
wire.onclose = function(){
delete open[rtc.id];
mesh.bye(peer);
peer.wire = null;
//reconnect(peer);
};
wire.onerror = function(error){
//reconnect(peer); // placement?
if(!error){ return }
if(error.code === 'ECONNREFUSED'){
//reconnect(peer, as);
}
};
wire.onerror = function(err){};
wire.onopen = function(e){
//delete open[rtc.id];
mesh.hi(peer);
}
wire.onmessage = function(msg){
@ -101,4 +100,4 @@
}
});
var noop = function(){};
}());
}());

View File

@ -58,7 +58,7 @@ Gun.on('opt', function(root){
var url = require('url');
opt.WebSocket = opt.WebSocket || require('ws');
var ws = opt.ws || {};
var ws = opt.ws = opt.ws || {};
ws.server = ws.server || opt.web;
if(ws.server && !ws.web){
@ -78,6 +78,7 @@ Gun.on('opt', function(root){
opt.mesh.bye(peer);
});
wire.on('error', function(e){});
setTimeout(function heart(){ if(!opt.peers[peer.id]){ return } try{ wire.send("[]"); setTimeout(heart, 1000 * 20) }catch(e){} }, 1000 * 20); // Some systems, like Heroku, require heartbeats to not time out. // TODO: Make this configurable?
});
}

1282
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,20 @@
{
"name": "gun",
"version": "0.9.9999991",
"version": "0.2019.915",
"description": "A realtime, decentralized, offline-first, graph data synchronization engine.",
"main": "index.js",
"browser": "gun.min.js",
"browser": "gun.js",
"scripts": {
"start": "node examples/http.js 8765",
"start": "node examples/http.js",
"https": "HTTPS_KEY=test/https/server.key HTTPS_CERT=test/https/server.crt npm start",
"prepublishOnly": "npm run unbuild",
"test": "mocha",
"testsea": "mocha test/sea.js",
"testaxe": "mocha test/axe/holy-grail.js",
"e2e": "mocha e2e/distributed.js",
"docker": "hooks/build",
"unbuild": "node lib/unbuild.js && uglifyjs gun.js -o gun.min.js -c -m"
"minify": "uglifyjs gun.js -o gun.min.js -c -m",
"unbuild": "node lib/unbuild.js & npm run minify"
},
"repository": {
"type": "git",
@ -49,18 +51,17 @@
"node": ">=0.8.4"
},
"dependencies": {
"ws": "~>5.2.0"
"ws": "~>7.1.0"
},
"optionalDependencies": {
"text-encoding": "^0.7.0",
"node-webcrypto-ossl": "^1.0.39"
"@peculiar/webcrypto": "^1.0.19",
"emailjs": "^2.2.0",
"text-encoding": "^0.7.0"
},
"devDependencies": {
"aws-sdk": ">=2.153.0",
"concat-map": "^0.0.1",
"express": ">=4.15.2",
"ip": "^1.1.5",
"mocha": "^5.2.0",
"mocha": "^6.2.0",
"panic-manager": "^1.2.0",
"panic-server": "^1.1.1",
"uglify-js": ">=2.8.22"

22
sea.js
View File

@ -47,6 +47,15 @@
})(USE, './https');
;USE(function(module){
if(typeof global !== "undefined"){
var g = global;
g.btoa = function (data) { return Buffer.from(data, "binary").toString("base64"); };
g.atob = function (data) { return Buffer.from(data, "base64").toString("binary"); };
}
})(USE, './base64');
;USE(function(module){
USE('./base64');
// This is Array extended to have .toString(['utf8'|'hex'|'base64'])
function SeaArray() {}
Object.assign(SeaArray, { from: Array.from })
@ -72,6 +81,7 @@
})(USE, './array');
;USE(function(module){
USE('./base64');
// This is Buffer implementation used in SEA. Functionality is mostly
// compatible with NodeJS 'safe-buffer' and is used for encoding conversions
// between binary and 'hex' | 'utf8' | 'base64'
@ -174,7 +184,7 @@
random: (len) => Buffer.from(crypto.randomBytes(len))
});
//try{
const WebCrypto = USE('node-webcrypto-ossl', 1);
const { Crypto: WebCrypto } = USE('@peculiar/webcrypto', 1);
api.ossl = api.subtle = new WebCrypto({directory: 'ossl'}).subtle // ECDH
//}catch(e){
//console.log("node-webcrypto-ossl is optionally needed for ECDH, please install if needed.");
@ -510,7 +520,7 @@
key = pair.epriv || pair;
}
var msg = (typeof data == 'string')? data : JSON.stringify(data);
var rand = {s: shim.random(8), iv: shim.random(16)};
var rand = {s: shim.random(9), iv: shim.random(15)}; // consider making this 9 and 15 or 18 or 12 to reduce == padding.
var ct = await aeskey(key, rand.s, opt).then((aes) => (/*shim.ossl ||*/ shim.subtle).encrypt({ // Keeping the AES key scope as private as possible...
name: opt.name || 'AES-GCM', iv: new Uint8Array(rand.iv)
}, aes, new shim.TextEncoder().encode(msg)));
@ -591,7 +601,7 @@
var epriv = pair.epriv;
var ecdhSubtle = shim.ossl || shim.subtle;
var pubKeyData = keysToEcdhJwk(pub);
var props = Object.assign(S.ecdh, { public: await ecdhSubtle.importKey(...pubKeyData, true, []) });
var props = Object.assign({ public: await ecdhSubtle.importKey(...pubKeyData, true, []) },S.ecdh); // Thanks to @sirpy !
var privKeyData = keysToEcdhJwk(epub, epriv);
var derived = await ecdhSubtle.importKey(...privKeyData, false, ['deriveKey']).then(async (privKey) => {
// privateKey scope doesn't leak out from here!
@ -677,7 +687,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 || USE('./gun', 1);
var Gun = (SEA.window||{}).Gun || USE((typeof common == "undefined"?'.':'')+'./gun', 1);
Gun.SEA = SEA;
SEA.GUN = SEA.Gun = Gun;
@ -1121,6 +1131,10 @@
// This is broken down into some pretty clear edge cases, let's go over them:
function security(msg){
var at = this.as, sea = at.sea, to = this.to;
if(at.opt.faith && (msg._||noop).faith){ // you probably shouldn't have faith in this!
this.to.next(msg); // why do we allow skipping security? I'm very scared about it actually.
return; // but so that way storage adapters that already verified something can get performance boost. This was a community requested feature. If anybody finds an exploit with it, please report immediately. It should only be exploitable if you have XSS control anyways, which if you do, you can bypass security regardless of this.
}
if(msg.get){
// if there is a request to read data from us, then...
var soul = msg.get['#'];

View File

@ -1,4 +1,6 @@
function btoa(b) {
return new Buffer(b).toString('base64');
};
// This is Array extended to have .toString(['utf8'|'hex'|'base64'])
function SeaArray() {}
Object.assign(SeaArray, { from: Array.from })

View File

@ -1,4 +1,6 @@
function atob(a) {
return new Buffer(a, 'base64').toString('binary');
};
// This is Buffer implementation used in SEA. Functionality is mostly
// compatible with NodeJS 'safe-buffer' and is used for encoding conversions
// between binary and 'hex' | 'utf8' | 'base64'

View File

@ -14,7 +14,7 @@
key = pair.epriv || pair;
}
var msg = (typeof data == 'string')? data : JSON.stringify(data);
var rand = {s: shim.random(8), iv: shim.random(16)};
var rand = {s: shim.random(9), iv: shim.random(15)}; // consider making this 9 and 15 or 18 or 12 to reduce == padding.
var ct = await aeskey(key, rand.s, opt).then((aes) => (/*shim.ossl ||*/ shim.subtle).encrypt({ // Keeping the AES key scope as private as possible...
name: opt.name || 'AES-GCM', iv: new Uint8Array(rand.iv)
}, aes, new shim.TextEncoder().encode(msg)));

View File

@ -63,6 +63,10 @@
// This is broken down into some pretty clear edge cases, let's go over them:
function security(msg){
var at = this.as, sea = at.sea, to = this.to;
if(at.opt.faith && (msg._||noop).faith){ // you probably shouldn't have faith in this!
this.to.next(msg); // why do we allow skipping security? I'm very scared about it actually.
return; // but so that way storage adapters that already verified something can get performance boost. This was a community requested feature. If anybody finds an exploit with it, please report immediately. It should only be exploitable if you have XSS control anyways, which if you do, you can bypass security regardless of this.
}
if(msg.get){
// if there is a request to read data from us, then...
var soul = msg.get['#'];

View File

@ -48,7 +48,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('./gun', 1);
var Gun = (SEA.window||{}).Gun || require((typeof common == "undefined"?'.':'')+'./gun', 1);
Gun.SEA = SEA;
SEA.GUN = SEA.Gun = Gun;

View File

@ -13,7 +13,7 @@
var epriv = pair.epriv;
var ecdhSubtle = shim.ossl || shim.subtle;
var pubKeyData = keysToEcdhJwk(pub);
var props = Object.assign(S.ecdh, { public: await ecdhSubtle.importKey(...pubKeyData, true, []) });
var props = Object.assign({ public: await ecdhSubtle.importKey(...pubKeyData, true, []) },S.ecdh);
var privKeyData = keysToEcdhJwk(epub, epriv);
var derived = await ecdhSubtle.importKey(...privKeyData, false, ['deriveKey']).then(async (privKey) => {
// privateKey scope doesn't leak out from here!
@ -47,4 +47,4 @@
}
module.exports = SEA.secret;

View File

@ -22,7 +22,7 @@
random: (len) => Buffer.from(crypto.randomBytes(len))
});
//try{
const WebCrypto = require('node-webcrypto-ossl', 1);
const { Crypto: WebCrypto } = USE('@peculiar/webcrypto', 1);
api.ossl = api.subtle = new WebCrypto({directory: 'ossl'}).subtle // ECDH
//}catch(e){
//console.log("node-webcrypto-ossl is optionally needed for ECDH, please install if needed.");

View File

@ -1,46 +1,46 @@
/* Based on the Hypothetical Amnesia Machine thought experiment */
function HAM(machineState, incomingState, currentState, incomingValue, currentValue){
if(machineState < incomingState){
return {defer: true}; // the incoming value is outside the boundary of the machine's state, it must be reprocessed in another state.
}
if(incomingState < currentState){
return {historical: true}; // the incoming value is within the boundary of the machine's state, but not within the range.
}
if(currentState < incomingState){
return {converge: true, incoming: true}; // the incoming value is within both the boundary and the range of the machine's state.
}
if(incomingState === currentState){
incomingValue = Lexical(incomingValue) || "";
currentValue = Lexical(currentValue) || "";
if(incomingValue === currentValue){ // Note: while these are practically the same, the deltas could be technically different
return {state: true};
}
/*
The following is a naive implementation, but will always work.
Never change it unless you have specific needs that absolutely require it.
If changed, your data will diverge unless you guarantee every peer's algorithm has also been changed to be the same.
As a result, it is highly discouraged to modify despite the fact that it is naive,
because convergence (data integrity) is generally more important.
Any difference in this algorithm must be given a new and different name.
*/
if(incomingValue < currentValue){ // Lexical only works on simple value types!
return {converge: true, current: true};
}
if(currentValue < incomingValue){ // Lexical only works on simple value types!
return {converge: true, incoming: true};
}
}
return {err: "Invalid CRDT Data: "+ incomingValue +" to "+ currentValue +" at "+ incomingState +" to "+ currentState +"!"};
}
if(typeof JSON === 'undefined'){
throw new Error(
'JSON is not included in this browser. Please load it first: ' +
'ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js'
);
}
var Lexical = JSON.stringify, undefined;
module.exports = HAM;
/* Based on the Hypothetical Amnesia Machine thought experiment */
function HAM(machineState, incomingState, currentState, incomingValue, currentValue){
if(machineState < incomingState){
return {defer: true}; // the incoming value is outside the boundary of the machine's state, it must be reprocessed in another state.
}
if(incomingState < currentState){
return {historical: true}; // the incoming value is within the boundary of the machine's state, but not within the range.
}
if(currentState < incomingState){
return {converge: true, incoming: true}; // the incoming value is within both the boundary and the range of the machine's state.
}
if(incomingState === currentState){
incomingValue = Lexical(incomingValue) || "";
currentValue = Lexical(currentValue) || "";
if(incomingValue === currentValue){ // Note: while these are practically the same, the deltas could be technically different
return {state: true};
}
/*
The following is a naive implementation, but will always work.
Never change it unless you have specific needs that absolutely require it.
If changed, your data will diverge unless you guarantee every peer's algorithm has also been changed to be the same.
As a result, it is highly discouraged to modify despite the fact that it is naive,
because convergence (data integrity) is generally more important.
Any difference in this algorithm must be given a new and different name.
*/
if(incomingValue < currentValue){ // Lexical only works on simple value types!
return {converge: true, current: true};
}
if(currentValue < incomingValue){ // Lexical only works on simple value types!
return {converge: true, incoming: true};
}
}
return {err: "Invalid CRDT Data: "+ incomingValue +" to "+ currentValue +" at "+ incomingState +" to "+ currentState +"!"};
}
if(typeof JSON === 'undefined'){
throw new Error(
'JSON is not included in this browser. Please load it first: ' +
'ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js'
);
}
var Lexical = JSON.stringify, undefined;
module.exports = HAM;

View File

@ -1,147 +1,146 @@
if(typeof Gun === 'undefined'){ return } // TODO: localStorage is Browser only. But it would be nice if it could somehow plugin into NodeJS compatible localStorage APIs?
var root, noop = function(){}, store, u;
try{store = (Gun.window||noop).localStorage}catch(e){}
if(!store){
console.log("Warning: No localStorage exists to persist data to!");
store = {setItem: function(k,v){this[k]=v}, removeItem: function(k){delete this[k]}, getItem: function(k){return this[k]}};
}
/*
NOTE: Both `lib/file.js` and `lib/memdisk.js` are based on this design!
If you update anything here, consider updating the other adapters as well.
*/
Gun.on('create', function(root){
// This code is used to queue offline writes for resync.
// See the next 'opt' code below for actual saving of data.
var ev = this.to, opt = root.opt;
if(root.once){ return ev.next(root) }
//if(false === opt.localStorage){ return ev.next(root) } // we want offline resynce queue regardless!
opt.prefix = opt.file || 'gun/';
var gap = Gun.obj.ify(store.getItem('gap/'+opt.prefix)) || {};
var empty = Gun.obj.empty, id, to, go;
// add re-sync command.
if(!empty(gap)){
var disk = Gun.obj.ify(store.getItem(opt.prefix)) || {}, send = {};
Gun.obj.map(gap, function(node, soul){
Gun.obj.map(node, function(val, key){
send[soul] = Gun.state.to(disk[soul], key, send[soul]);
});
});
setTimeout(function(){
root.on('out', {put: send, '#': root.ask(ack)});
},1);
}
root.on('out', function(msg){
if(msg.lS){ return }
if(Gun.is(msg.$) && msg.put && !msg['@'] && !empty(opt.peers)){
id = msg['#'];
Gun.graph.is(msg.put, null, map);
if(!to){ to = setTimeout(flush, opt.wait || 1) }
}
this.to.next(msg);
});
root.on('ack', ack);
function ack(ack){ // TODO: This is experimental, not sure if we should keep this type of event hook.
if(ack.err || !ack.ok){ return }
var id = ack['@'];
setTimeout(function(){
Gun.obj.map(gap, function(node, soul){
Gun.obj.map(node, function(val, key){
if(id !== val){ return }
delete node[key];
});
if(empty(node)){
delete gap[soul];
}
});
flush();
}, opt.wait || 1);
};
ev.next(root);
var map = function(val, key, node, soul){
(gap[soul] || (gap[soul] = {}))[key] = id;
}
var flush = function(){
clearTimeout(to);
to = false;
try{store.setItem('gap/'+opt.prefix, JSON.stringify(gap));
}catch(e){ Gun.log(err = e || "localStorage failure") }
}
});
Gun.on('create', function(root){
this.to.next(root);
var opt = root.opt;
if(root.once){ return }
if(false === opt.localStorage){ return }
opt.prefix = opt.file || 'gun/';
var graph = root.graph, acks = {}, count = 0, to;
var disk = Gun.obj.ify(store.getItem(opt.prefix)) || {};
var lS = function(){}, u;
root.on('localStorage', disk); // NON-STANDARD EVENT!
root.on('put', function(at){
this.to.next(at);
Gun.graph.is(at.put, null, map);
if(!at['@']){ acks[at['#']] = true; } // only ack non-acks.
count += 1;
if(count >= (opt.batch || 1000)){
return flush();
}
if(to){ return }
to = setTimeout(flush, opt.wait || 1);
});
root.on('get', function(msg){
this.to.next(msg);
var lex = msg.get, soul, data, u;
function to(){
if(!lex || !(soul = lex['#'])){ return }
//if(0 >= msg.cap){ return }
var has = lex['.'];
data = disk[soul] || u;
if(data && has){
data = Gun.state.to(data, has);
}
if(!data && !Gun.obj.empty(opt.peers)){ // if data not found, don't ack if there are peers.
return; // Hmm, what if we have peers but we are disconnected?
}
//console.log("lS get", lex, data);
root.on('in', {'@': msg['#'], put: Gun.graph.node(data), how: 'lS', lS: msg.$ || root.$});
};
Gun.debug? setTimeout(to,1) : to();
});
var map = function(val, key, node, soul){
disk[soul] = Gun.state.to(node, key, disk[soul]);
}
var flush = function(data){
var err;
count = 0;
clearTimeout(to);
to = false;
var ack = acks;
acks = {};
if(data){ disk = data }
try{store.setItem(opt.prefix, JSON.stringify(disk));
}catch(e){
Gun.log(err = (e || "localStorage failure") + " Consider using GUN's IndexedDB plugin for RAD for more storage space, temporary example at https://github.com/amark/gun/blob/master/test/tmp/indexedDB.html .");
root.on('localStorage:error', {err: err, file: opt.prefix, flush: disk, retry: flush});
}
if(!err && !Gun.obj.empty(opt.peers)){ return } // only ack if there are no peers.
Gun.obj.map(ack, function(yes, id){
root.on('in', {
'@': id,
err: err,
ok: 0 // localStorage isn't reliable, so make its `ok` code be a low number.
});
});
}
});
if(typeof Gun === 'undefined'){ return } // TODO: localStorage is Browser only. But it would be nice if it could somehow plugin into NodeJS compatible localStorage APIs?
var root, noop = function(){}, store, u;
try{store = (Gun.window||noop).localStorage}catch(e){}
if(!store){
console.log("Warning: No localStorage exists to persist data to!");
store = {setItem: function(k,v){this[k]=v}, removeItem: function(k){delete this[k]}, getItem: function(k){return this[k]}};
}
/*
NOTE: Both `lib/file.js` and `lib/memdisk.js` are based on this design!
If you update anything here, consider updating the other adapters as well.
*/
Gun.on('create', function(root){
// This code is used to queue offline writes for resync.
// See the next 'opt' code below for actual saving of data.
var ev = this.to, opt = root.opt;
if(root.once){ return ev.next(root) }
//if(false === opt.localStorage){ return ev.next(root) } // we want offline resynce queue regardless!
opt.prefix = opt.file || 'gun/';
var gap = Gun.obj.ify(store.getItem('gap/'+opt.prefix)) || {};
var empty = Gun.obj.empty, id, to, go;
// add re-sync command.
if(!empty(gap)){
var disk = Gun.obj.ify(store.getItem(opt.prefix)) || {}, send = {};
Gun.obj.map(gap, function(node, soul){
Gun.obj.map(node, function(val, key){
send[soul] = Gun.state.to(disk[soul], key, send[soul]);
});
});
setTimeout(function(){
// TODO: Holy Grail dangling by this thread! If gap / offline resync doesn't trigger, it doesn't work. Ouch, and this is a localStorage specific adapter. :(
root.on('out', {put: send, '#': root.ask(ack)});
},1);
}
root.on('out', function(msg){
if(msg.lS){ return } // TODO: for IndexedDB and others, shouldn't send to peers ACKs to our own GETs.
if(Gun.is(msg.$) && msg.put && !msg['@']){
id = msg['#'];
Gun.graph.is(msg.put, null, map);
if(!to){ to = setTimeout(flush, opt.wait || 1) }
}
this.to.next(msg);
});
root.on('ack', ack);
function ack(ack){ // TODO: This is experimental, not sure if we should keep this type of event hook.
if(ack.err || !ack.ok){ return }
var id = ack['@'];
setTimeout(function(){
Gun.obj.map(gap, function(node, soul){
Gun.obj.map(node, function(val, key){
if(id !== val){ return }
delete node[key];
});
if(empty(node)){
delete gap[soul];
}
});
flush();
}, opt.wait || 1);
};
ev.next(root);
var map = function(val, key, node, soul){
(gap[soul] || (gap[soul] = {}))[key] = id;
}
var flush = function(){
clearTimeout(to);
to = false;
try{store.setItem('gap/'+opt.prefix, JSON.stringify(gap));
}catch(e){ Gun.log(err = e || "localStorage failure") }
}
});
Gun.on('create', function(root){
this.to.next(root);
var opt = root.opt;
if(root.once){ return }
if(false === opt.localStorage){ return }
opt.prefix = opt.file || 'gun/';
var graph = root.graph, acks = {}, count = 0, to;
var disk = Gun.obj.ify(store.getItem(opt.prefix)) || {};
var lS = function(){}, u;
root.on('localStorage', disk); // NON-STANDARD EVENT!
root.on('put', function(at){
this.to.next(at);
Gun.graph.is(at.put, null, map);
if(!at['@']){ acks[at['#']] = true; } // only ack non-acks.
count += 1;
if(count >= (opt.batch || 1000)){
return flush();
}
if(to){ return }
to = setTimeout(flush, opt.wait || 1);
});
root.on('get', function(msg){
this.to.next(msg);
var lex = msg.get, soul, data, u;
function to(){
if(!lex || !(soul = lex['#'])){ return }
//if(0 >= msg.cap){ return }
var has = lex['.'];
data = disk[soul] || u;
if(data && has){
data = Gun.state.to(data, has);
}
//if(!data && !Gun.obj.empty(opt.peers)){ return } // if data not found, don't ack if there are peers. // Hmm, what if we have peers but we are disconnected?
//console.log("lS get", lex, data);
root.on('in', {'@': msg['#'], put: Gun.graph.node(data), how: 'lS', lS: msg.$});// || root.$});
};
Gun.debug? setTimeout(to,1) : to();
});
var map = function(val, key, node, soul){
disk[soul] = Gun.state.to(node, key, disk[soul]);
}
var flush = function(data){
var err;
count = 0;
clearTimeout(to);
to = false;
var ack = acks;
acks = {};
if(data){ disk = data }
try{store.setItem(opt.prefix, JSON.stringify(disk));
}catch(e){
Gun.log(err = (e || "localStorage failure") + " Consider using GUN's IndexedDB plugin for RAD for more storage space, https://gun.eco/docs/RAD#install");
root.on('localStorage:error', {err: err, file: opt.prefix, flush: disk, retry: flush});
}
if(!err && !Gun.obj.empty(opt.peers)){ return } // only ack if there are no peers.
Gun.obj.map(ack, function(yes, id){
root.on('in', {
'@': id,
err: err,
ok: 0 // localStorage isn't reliable, so make its `ok` code be a low number.
});
});
}
});

View File

@ -1,233 +1,252 @@
var Type = require('../type');
function Mesh(ctx){
var mesh = function(){};
var opt = ctx.opt || {};
opt.log = opt.log || console.log;
opt.gap = opt.gap || opt.wait || 1;
opt.pack = opt.pack || (opt.memory? (opt.memory * 1000 * 1000) : 1399000000) * 0.3; // max_old_space_size defaults to 1400 MB.
mesh.out = function(msg){ var tmp;
if(this.to){ this.to.next(msg) }
//if(mesh.last != msg['#']){ return mesh.last = msg['#'], this.to.next(msg) }
if((tmp = msg['@'])
&& (tmp = ctx.dup.s[tmp])
&& (tmp = tmp.it)
&& tmp._){
mesh.say(msg, (tmp._).via, 1);
tmp['##'] = msg['##'];
return;
}
// add hook for AXE?
//if (Gun.AXE && opt && opt.super) { Gun.AXE.say(msg, mesh.say, this); return; } // rogowski
mesh.say(msg);
}
ctx.on('create', function(root){
root.opt.pid = root.opt.pid || Type.text.random(9);
this.to.next(root);
ctx.on('out', mesh.out);
});
mesh.hear = function(raw, peer){
if(!raw){ return }
var dup = ctx.dup, id, hash, msg, tmp = raw[0];
if(opt.pack <= raw.length){ return mesh.say({dam: '!', err: "Message too big!"}, peer) }
try{msg = JSON.parse(raw);
}catch(e){opt.log('DAM JSON parse error', e)}
if('{' === tmp){
if(!msg){ return }
if(dup.check(id = msg['#'])){ return }
dup.track(id, true).it = msg; // GUN core also dedups, so `true` is needed.
if((tmp = msg['@']) && msg.put){
hash = msg['##'] || (msg['##'] = mesh.hash(msg));
if((tmp = tmp + hash) != id){
if(dup.check(tmp)){ return }
(tmp = dup.s)[hash] = tmp[id];
}
}
(msg._ = function(){}).via = peer;
if((tmp = msg['><'])){
(msg._).to = Type.obj.map(tmp.split(','), function(k,i,m){m(k,true)});
}
if(msg.dam){
if(tmp = mesh.hear[msg.dam]){
tmp(msg, peer, ctx);
}
return;
}
ctx.on('in', msg);
return;
} else
if('[' === tmp){
if(!msg){ return }
var i = 0, m;
while(m = msg[i++]){
mesh.hear(m, peer);
}
return;
}
}
;(function(){
mesh.say = function(msg, peer, o){
/*
TODO: Plenty of performance optimizations
that can be made just based off of ordering,
and reducing function calls for cached writes.
*/
if(!peer){
Type.obj.map(opt.peers, function(peer){
mesh.say(msg, peer);
});
return;
}
var tmp, wire = peer.wire || ((opt.wire) && opt.wire(peer)), msh, raw;// || open(peer, ctx); // TODO: Reopen!
if(!wire){ return }
msh = (msg._) || empty;
if(peer === msh.via){ return }
if(!(raw = msh.raw)){ raw = mesh.raw(msg) }
if((tmp = msg['@'])
&& (tmp = ctx.dup.s[tmp])
&& (tmp = tmp.it)){
if(tmp.get && tmp['##'] && tmp['##'] === msg['##']){ // PERF: move this condition outside say?
return; // TODO: this still needs to be tested in the browser!
}
}
if((tmp = msh.to) && (tmp[peer.url] || tmp[peer.id]) && !o){ return } // TODO: still needs to be tested
if(peer.batch){
peer.tail = (peer.tail || 0) + raw.length;
if(peer.tail <= opt.pack){
peer.batch.push(raw);
return;
}
flush(peer);
}
peer.batch = [];
setTimeout(function(){flush(peer)}, opt.gap);
send(raw, peer);
}
function flush(peer){
var tmp = peer.batch;
if(!tmp){ return }
peer.batch = peer.tail = null;
if(!tmp.length){ return }
try{send(JSON.stringify(tmp), peer);
}catch(e){opt.log('DAM JSON stringify error', e)}
}
function send(raw, peer){
var wire = peer.wire;
try{
if(wire.send){
wire.send(raw);
} else
if(peer.say){
peer.say(raw);
}
}catch(e){
(peer.queue = peer.queue || []).push(raw);
}
}
}());
;(function(){
mesh.raw = function(msg){
if(!msg){ return '' }
var dup = ctx.dup, msh = (msg._) || {}, put, hash, tmp;
if(tmp = msh.raw){ return tmp }
if(typeof msg === 'string'){ return msg }
if(msg['@'] && (tmp = msg.put)){
if(!(hash = msg['##'])){
put = $(tmp, sort) || '';
hash = mesh.hash(msg, put);
msg['##'] = hash;
}
(tmp = dup.s)[hash = msg['@']+hash] = tmp[msg['#']];
msg['#'] = hash || msg['#'];
if(put){ (msg = Type.obj.to(msg)).put = _ }
}
var i = 0, to = []; Type.obj.map(opt.peers, function(p){
to.push(p.url || p.id); if(++i > 9){ return true } // limit server, fast fix, improve later!
}); msg['><'] = to.join();
var raw = $(msg);
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); // https://github.com/amark/gun/wiki/@$$ Heisenbug
}
if(msh){
msh.raw = raw;
}
return raw;
}
mesh.hash = function(msg, hash){
return Mesh.hash(hash || $(msg.put, sort) || '') || msg['#'] || Type.text.random(9);
}
function sort(k, v){ var tmp;
if(!(v instanceof Object)){ return v }
Type.obj.map(Object.keys(v).sort(), map, {to: tmp = {}, on: v});
return tmp;
}
function map(k){
this.to[k] = this.on[k];
}
var $ = JSON.stringify, _ = ':])([:';
}());
mesh.hi = function(peer){
var tmp = peer.wire || {};
if(peer.id || peer.url){
opt.peers[peer.url || peer.id] = peer;
Type.obj.del(opt.peers, tmp.id);
} else {
tmp = tmp.id = tmp.id || Type.text.random(9);
mesh.say({dam: '?'}, opt.peers[tmp] = peer);
}
if(!tmp.hied){ ctx.on(tmp.hied = 'hi', peer) }
tmp = peer.queue; peer.queue = [];
Type.obj.map(tmp, function(msg){
mesh.say(msg, peer);
});
}
mesh.bye = function(peer){
Type.obj.del(opt.peers, peer.id); // assume if peer.url then reconnect
ctx.on('bye', peer);
}
mesh.hear['!'] = function(msg, peer){ opt.log('Error:', msg.err) }
mesh.hear['?'] = function(msg, peer){
if(!msg.pid){ return mesh.say({dam: '?', pid: opt.pid, '@': msg['#']}, peer) }
peer.id = peer.id || msg.pid;
mesh.hi(peer);
}
return mesh;
}
Mesh.hash = function(s){ // via SO
if(typeof s !== 'string'){ return {err: 1} }
var c = 0;
if(!s.length){ return c }
for(var i=0,l=s.length,n; i<l; ++i){
n = s.charCodeAt(i);
c = ((c<<5)-c)+n;
c |= 0;
}
return c; // Math.abs(c);
}
var empty = {}, u;
Object.keys = Object.keys || function(o){ return map(o, function(v,k,t){t(k)}) }
try{ module.exports = Mesh }catch(e){}
var Type = require('../type');
function Mesh(root){
var mesh = function(){};
var opt = root.opt || {};
opt.log = opt.log || console.log;
opt.gap = opt.gap || opt.wait || 1;
opt.pack = opt.pack || (opt.memory? (opt.memory * 1000 * 1000) : 1399000000) * 0.3; // max_old_space_size defaults to 1400 MB.
var dup = root.dup;
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
if('[' === tmp){
try{msg = JSON.parse(raw);}catch(e){opt.log('DAM JSON parse error', e)}
if(!msg){ return }
var i = 0, m;
while(m = msg[i++]){
mesh.hear(m, peer);
}
return;
}
if('{' === tmp || (Type.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(!(id = msg['#'])){ id = msg['#'] = Type.text.random(9) }
if(dup.check(id)){ return }
dup.track(id, true).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(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 = 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]){
tmp(msg, peer, root);
}
return;
}
root.on('in', msg);
return;
}
}
var tomap = function(k,i,m){m(k,true)};
mesh.hear.c = mesh.hear.d = 0;
;(function(){
var message;
function each(peer){ mesh.say(message, peer) }
mesh.say = function(msg, peer){
if(this.to){ this.to.next(msg) } // compatible with middleware adapters.
if(!msg){ return false }
var id, hash, tmp, raw;
var 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(!(raw = meta.raw)){
raw = meta.raw = mesh.raw(msg);
if(hash && (tmp = msg['@'])){
dup.track(tmp+hash).it = msg;
if(tmp = (dup.s[tmp]||ok).it){
if(hash === tmp['##']){ return false }
tmp['##'] = hash;
}
}
}
dup.track(id).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) }
if(!peer && mesh.way){ return mesh.way(msg) }
if(!peer || !peer.id){ message = msg;
if(!Type.obj.is(peer || opt.peers)){ return false }
Type.obj.map(peer || opt.peers, each); // in case peer is a peer list.
return;
}
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((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 !?
return;
}
flush(peer);
}
peer.batch = []; // peer.batch = '['; // TODO: Prevent double JSON!
setTimeout(function(){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: ^
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);
}
mesh.say.c = mesh.say.d = 0;
}());
// for now - find better place later.
function send(raw, peer){ try{
var wire = peer.wire;
if(peer.say){
peer.say(raw);
} else
if(wire.send){
wire.send(raw);
}
mesh.say.d += raw.length||0; ++mesh.say.c; // STATS!
}catch(e){
(peer.queue = peer.queue || []).push(raw);
}}
;(function(){
mesh.raw = function(msg){ // TODO: Clean this up / delete it / move logic out!
if(!msg){ return '' }
var meta = (msg._) || {}, put, hash, tmp;
if(tmp = meta.raw){ return tmp }
if(typeof msg === 'string'){ return msg }
if(!msg.dam){
var i = 0, to = []; Type.obj.map(opt.peers, function(p){
to.push(p.url || p.pid || p.id); if(++i > 9){ return true } // limit server, fast fix, improve later! // For "tower" peer, MUST include 6 surrounding ids.
}); if(i > 1){ msg['><'] = to.join() }
}
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
}*/
if(meta){ meta.raw = raw }
return raw;
}
var $ = JSON.stringify, _ = ':])([:';
}());
mesh.hi = function(peer){
var tmp = peer.wire || {};
if(peer.id){
opt.peers[peer.url || peer.id] = peer;
} else {
tmp = peer.id = peer.id || Type.text.random(9);
mesh.say({dam: '?'}, opt.peers[tmp] = peer);
}
peer.met = peer.met || +(new Date);
if(!tmp.hied){ root.on(tmp.hied = 'hi', peer) }
// @rogowski I need this here by default for now to fix go1dfish's bug
tmp = peer.queue; peer.queue = [];
Type.obj.map(tmp, function(msg){
send(msg, peer);
});
}
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;
}
mesh.hear['!'] = function(msg, peer){ opt.log('Error:', msg.err) }
mesh.hear['?'] = function(msg, peer){
if(!msg.pid){
mesh.say({dam: '?', pid: opt.pid, '@': msg['#']}, peer);
// @rogowski I want to re-enable this AXE logic with some fix/merge later.
/* var tmp = peer.queue; peer.queue = [];
Type.obj.map(tmp, function(msg){
mesh.say(msg, peer);
}); */
// @rogowski 2: I think with my PID fix we can delete this and use the original.
return;
}
if(peer.pid){ return }
peer.pid = msg.pid;
}
root.on('create', function(root){
root.opt.pid = root.opt.pid || Type.text.random(9);
this.to.next(root);
root.on('out', mesh.say);
});
root.on('bye', function(peer, tmp){
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);
peer.wire = null;
});
var gets = {};
root.on('bye', function(peer, tmp){ this.to.next(peer);
if(!(tmp = peer.url)){ return } gets[tmp] = true;
setTimeout(function(){ delete gets[tmp] },opt.lack || 9000);
});
root.on('hi', function(peer, tmp){ this.to.next(peer);
if(!(tmp = peer.url) || !gets[tmp]){ return } delete gets[tmp];
Type.obj.map(root.next, function(node, soul){
tmp = {}; tmp[soul] = root.graph[soul];
mesh.say({'##': Type.obj.hash(tmp), get: {'#': soul}}, peer);
})
});
return mesh;
}
;(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){
if(!hash && u === (obj = $(obj, sort))){ return }
return Type.text.hash(hash || obj || '');
}
function sort(k, v){ var tmp;
if(!(v instanceof Object)){ return v }
Type.obj.map(Object.keys(v).sort(), map, {to: tmp = {}, on: v});
return tmp;
}
Type.obj.hash.sort = sort;
function map(k){
this.to[k] = this.on[k];
}
}());
var empty = {}, ok = true, u;
try{ module.exports = Mesh }catch(e){}

View File

@ -1,57 +1,57 @@
var Gun = require('../index');
Gun.Mesh = require('./mesh');
Gun.on('opt', function(root){
this.to.next(root);
var opt = root.opt;
if(root.once){ return }
if(false === opt.WebSocket){ return }
var env;
if(typeof window !== "undefined"){ env = window }
if(typeof global !== "undefined"){ env = global }
env = env || {};
var websocket = opt.WebSocket || env.WebSocket || env.webkitWebSocket || env.mozWebSocket;
if(!websocket){ return }
opt.WebSocket = websocket;
var mesh = opt.mesh = opt.mesh || Gun.Mesh(root);
var wire = opt.wire;
opt.wire = open;
function open(peer){ try{
if(!peer || !peer.url){ return wire && wire(peer) }
var url = peer.url.replace('http', 'ws');
var wire = peer.wire = new opt.WebSocket(url);
wire.onclose = function(){
opt.mesh.bye(peer);
reconnect(peer);
};
wire.onerror = function(error){
reconnect(peer); // placement?
if(!error){ return }
if(error.code === 'ECONNREFUSED'){
//reconnect(peer, as);
}
};
wire.onopen = function(){
opt.mesh.hi(peer);
}
wire.onmessage = function(msg){
if(!msg){ return }
opt.mesh.hear(msg.data || msg, peer);
};
return wire;
}catch(e){}}
function reconnect(peer){
clearTimeout(peer.defer);
peer.defer = setTimeout(function(){
open(peer);
}, 2 * 1000);
}
});
var noop = function(){};
var Gun = require('../index');
Gun.Mesh = require('./mesh');
Gun.on('opt', function(root){
this.to.next(root);
var opt = root.opt;
if(root.once){ return }
if(false === opt.WebSocket){ return }
var env;
if(typeof window !== "undefined"){ env = window }
if(typeof global !== "undefined"){ env = global }
env = env || {};
var websocket = opt.WebSocket || env.WebSocket || env.webkitWebSocket || env.mozWebSocket;
if(!websocket){ return }
opt.WebSocket = websocket;
var mesh = opt.mesh = opt.mesh || Gun.Mesh(root);
var wire = mesh.wire || opt.wire;
mesh.wire = opt.wire = open;
function open(peer){ try{
if(!peer || !peer.url){ return wire && wire(peer) }
var url = peer.url.replace('http', 'ws');
var wire = peer.wire = new opt.WebSocket(url);
wire.onclose = function(){
opt.mesh.bye(peer);
reconnect(peer);
};
wire.onerror = function(error){
reconnect(peer);
};
wire.onopen = function(){
opt.mesh.hi(peer);
}
wire.onmessage = function(msg){
if(!msg){ return }
opt.mesh.hear(msg.data || msg, peer);
};
return wire;
}catch(e){}}
var wait = 2 * 1000;
function reconnect(peer){
clearTimeout(peer.defer);
if(doc && peer.retry <= 0){ return } peer.retry = (peer.retry || opt.retry || 60) - 1;
peer.defer = setTimeout(function to(){
if(doc && doc.hidden){ return setTimeout(to,wait) }
open(peer);
}, wait);
}
var doc = 'undefined' !== typeof document && document;
});
var noop = function(){};

View File

@ -1,23 +1,23 @@
// request / response module, for asking and acking messages.
require('./onto'); // depends upon onto!
module.exports = function ask(cb, as){
if(!this.on){ return }
if(!(cb instanceof Function)){
if(!cb || !as){ return }
var id = cb['#'] || cb, tmp = (this.tag||empty)[id];
if(!tmp){ return }
tmp = this.on(id, as);
clearTimeout(tmp.err);
return true;
}
var id = (as && as['#']) || Math.random().toString(36).slice(2);
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.off();
}, (this.opt||{}).lack || 9000);
return id;
}
// request / response module, for asking and acking messages.
require('./onto'); // depends upon onto!
module.exports = function ask(cb, as){
if(!this.on){ return }
if(!(cb instanceof Function)){
if(!cb || !as){ return }
var id = cb['#'] || cb, tmp = (this.tag||empty)[id];
if(!tmp){ return }
tmp = this.on(id, as);
clearTimeout(tmp.err);
return true;
}
var id = (as && as['#']) || Math.random().toString(36).slice(2);
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.off();
}, (this.opt||{}).lack || 9000);
return id;
}

View File

@ -1,40 +1,40 @@
var Gun = require('./root');
Gun.chain.back = function(n, opt){ var tmp;
n = n || 1;
if(-1 === n || Infinity === n){
return this._.root.$;
} else
if(1 === n){
return (this._.back || this._).$;
}
var gun = this, at = gun._;
if(typeof n === 'string'){
n = n.split('.');
}
if(n instanceof Array){
var i = 0, l = n.length, tmp = at;
for(i; i < l; i++){
tmp = (tmp||empty)[n[i]];
}
if(u !== tmp){
return opt? gun : tmp;
} else
if((tmp = at.back)){
return tmp.$.back(n, opt);
}
return;
}
if(n instanceof Function){
var yes, tmp = {back: at};
while((tmp = tmp.back)
&& u === (yes = n(tmp, opt))){}
return yes;
}
if(Gun.num.is(n)){
return (at.back || at).$.back(n - 1);
}
return this;
}
var empty = {}, u;
var Gun = require('./root');
Gun.chain.back = function(n, opt){ var tmp;
n = n || 1;
if(-1 === n || Infinity === n){
return this._.root.$;
} else
if(1 === n){
return (this._.back || this._).$;
}
var gun = this, at = gun._;
if(typeof n === 'string'){
n = n.split('.');
}
if(n instanceof Array){
var i = 0, l = n.length, tmp = at;
for(i; i < l; i++){
tmp = (tmp||empty)[n[i]];
}
if(u !== tmp){
return opt? gun : tmp;
} else
if((tmp = at.back)){
return tmp.$.back(n, opt);
}
return;
}
if(n instanceof Function){
var yes, tmp = {back: at};
while((tmp = tmp.back)
&& u === (yes = n(tmp, opt))){}
return yes;
}
if(Gun.num.is(n)){
return (at.back || at).$.back(n - 1);
}
return this;
}
var empty = {}, u;

View File

@ -1,280 +1,296 @@
// WARNING: GUN is very simple, but the JavaScript chaining API around GUN
// is complicated and was extremely hard to build. If you port GUN to another
// language, consider implementing an easier API to build.
var Gun = require('./root');
Gun.chain.chain = function(sub){
var gun = this, at = gun._, chain = new (sub || gun).constructor(gun), cat = chain._, root;
cat.root = root = at.root;
cat.id = ++root.once;
cat.back = gun._;
cat.on = Gun.on;
cat.on('in', input, cat); // For 'in' if I add my own listeners to each then I MUST do it before in gets called. If I listen globally for all incoming data instead though, regardless of individual listeners, I can transform the data there and then as well.
cat.on('out', output, cat); // However for output, there isn't really the global option. I must listen by adding my own listener individually BEFORE this one is ever called.
return chain;
}
function output(msg){
var put, get, at = this.as, back = at.back, root = at.root, tmp;
if(!msg.$){ msg.$ = at.$ }
this.to.next(msg);
if(get = msg.get){
/*if(u !== at.put){
at.on('in', at);
return;
}*/
if(get['#'] || at.soul){
get['#'] = get['#'] || at.soul;
msg['#'] || (msg['#'] = text_rand(9));
back = (root.$.get(get['#'])._);
if(!(get = get['.'])){
tmp = back.ack;
if(!tmp){ back.ack = -1 }
if(obj_has(back, 'put')){
back.on('in', back);
}
if(tmp){ return }
msg.$ = back.$;
} else
if(obj_has(back.put, get)){
put = (back.$.get(get)._);
if(!(tmp = put.ack)){ put.ack = -1 }
back.on('in', {
$: back.$,
put: Gun.state.to(back.put, get),
get: back.get
});
if(tmp){ return }
}
root.ask(ack, msg);
return root.on('in', msg);
}
if(root.now){ root.now[at.id] = root.now[at.id] || true; at.pass = {} }
if(get['.']){
if(at.get){
msg = {get: {'.': at.get}, $: at.$};
//if(back.ask || (back.ask = {})[at.get]){ return }
(back.ask || (back.ask = {}));
back.ask[at.get] = msg.$._; // TODO: PERFORMANCE? More elegant way?
return back.on('out', msg);
}
msg = {get: {}, $: at.$};
return back.on('out', msg);
}
at.ack = at.ack || -1;
if(at.get){
msg.$ = at.$;
get['.'] = at.get;
(back.ask || (back.ask = {}))[at.get] = msg.$._; // TODO: PERFORMANCE? More elegant way?
return back.on('out', msg);
}
}
return back.on('out', msg);
}
function input(msg){
var eve = this, cat = eve.as, root = cat.root, gun = msg.$, at = (gun||empty)._ || empty, change = msg.put, rel, tmp;
if(cat.get && msg.get !== cat.get){
msg = obj_to(msg, {get: cat.get});
}
if(cat.has && at !== cat){
msg = obj_to(msg, {$: cat.$});
if(at.ack){
cat.ack = at.ack;
//cat.ack = cat.ack || at.ack;
}
}
if(u === change){
tmp = at.put;
eve.to.next(msg);
if(cat.soul){ return } // TODO: BUG, I believee the fresh input refactor caught an edge case that a `gun.get('soul').get('key')` that points to a soul that doesn't exist will not trigger val/get etc.
if(u === tmp && u !== at.put){ return }
echo(cat, msg, eve);
if(cat.has){
not(cat, msg);
}
obj_del(at.echo, cat.id);
obj_del(cat.map, at.id);
return;
}
if(cat.soul){
eve.to.next(msg);
echo(cat, msg, eve);
if(cat.next){ obj_map(change, map, {msg: msg, cat: cat}) }
return;
}
if(!(rel = Gun.val.link.is(change))){
if(Gun.val.is(change)){
if(cat.has || cat.soul){
not(cat, msg);
} else
if(at.has || at.soul){
(at.echo || (at.echo = {}))[cat.id] = at.echo[at.id] || cat;
(cat.map || (cat.map = {}))[at.id] = cat.map[at.id] || {at: at};
//if(u === at.put){ return } // Not necessary but improves performance. If we have it but at does not, that means we got things out of order and at will get it. Once at gets it, it will tell us again.
}
eve.to.next(msg);
echo(cat, msg, eve);
return;
}
if(cat.has && at !== cat && obj_has(at, 'put')){
cat.put = at.put;
};
if((rel = Gun.node.soul(change)) && at.has){
at.put = (cat.root.$.get(rel)._).put;
}
tmp = (root.stop || {})[at.id];
//if(tmp && tmp[cat.id]){ } else {
eve.to.next(msg);
//}
relate(cat, msg, at, rel);
echo(cat, msg, eve);
if(cat.next){ obj_map(change, map, {msg: msg, cat: cat}) }
return;
}
var was = root.stop;
tmp = root.stop || {};
tmp = tmp[at.id] || (tmp[at.id] = {});
//if(tmp[cat.id]){ return }
tmp.is = tmp.is || at.put;
tmp[cat.id] = at.put || true;
//if(root.stop){
eve.to.next(msg)
//}
relate(cat, msg, at, rel);
echo(cat, msg, eve);
}
var C = 0;
function relate(at, msg, from, rel){
if(!rel || node_ === at.get){ return }
var tmp = (at.root.$.get(rel)._);
if(at.has){
from = tmp;
} else
if(from.has){
relate(from, msg, from, rel);
}
if(from === at){ return }
if(!from.$){ from = {} }
(from.echo || (from.echo = {}))[at.id] = from.echo[at.id] || at;
if(at.has && !(at.map||empty)[from.id]){ // if we haven't seen this before.
not(at, msg);
}
tmp = from.id? ((at.map || (at.map = {}))[from.id] = at.map[from.id] || {at: from}) : {};
if(rel === tmp.link){
if(!(tmp.pass || at.pass)){
return;
}
}
if(at.pass){
Gun.obj.map(at.map, function(tmp){ tmp.pass = true })
obj_del(at, 'pass');
}
if(tmp.pass){ obj_del(tmp, 'pass') }
if(at.has){ at.link = rel }
ask(at, tmp.link = rel);
}
function echo(at, msg, ev){
if(!at.echo){ return } // || node_ === at.get ?
//if(at.has){ msg = obj_to(msg, {event: ev}) }
obj_map(at.echo, reverb, msg);
}
function reverb(to){
if(!to || !to.on){ return }
to.on('in', this);
}
function map(data, key){ // Map over only the changes on every update.
var cat = this.cat, next = cat.next || empty, via = this.msg, chain, at, tmp;
if(node_ === key && !next[key]){ return }
if(!(at = next[key])){
return;
}
//if(data && data[_soul] && (tmp = Gun.val.rel.is(data)) && (tmp = (cat.root.$.get(tmp)._)) && obj_has(tmp, 'put')){
// data = tmp.put;
//}
if(at.has){
//if(!(data && data[_soul] && Gun.val.rel.is(data) === Gun.node.soul(at.put))){
if(u === at.put || !Gun.val.link.is(data)){
at.put = data;
}
chain = at.$;
} else
if(tmp = via.$){
tmp = (chain = via.$.get(key))._;
if(u === tmp.put || !Gun.val.link.is(data)){
tmp.put = data;
}
}
at.on('in', {
put: data,
get: key,
$: chain,
via: via
});
}
function not(at, msg){
if(!(at.has || at.soul)){ return }
var tmp = at.map, root = at.root;
at.map = null;
if(at.has){ at.link = null }
//if(!root.now || !root.now[at.id]){
if(!at.pass){
if((!msg['@']) && null === tmp){ return }
//obj_del(at, 'pass');
}
if(u === tmp && Gun.val.link.is(at.put)){ return } // This prevents the very first call of a thing from triggering a "clean up" call. // TODO: link.is(at.put) || !val.is(at.put) ?
obj_map(tmp, function(proxy){
if(!(proxy = proxy.at)){ return }
obj_del(proxy.echo, at.id);
});
tmp = at.put;
obj_map(at.next, function(neat, key){
if(u === tmp && u !== at.put){ return true }
neat.put = u;
if(neat.ack){
neat.ack = -1;
}
neat.on('in', {
get: key,
$: neat.$,
put: u
});
});
}
function ask(at, soul){
var tmp = (at.root.$.get(soul)._);
if(at.ack){
tmp.on('out', {get: {'#': soul}});
if(!at.ask){ return } // TODO: PERFORMANCE? More elegant way?
}
tmp = at.ask; Gun.obj.del(at, 'ask');
obj_map(tmp || at.next, function(neat, key){
neat.on('out', {get: {'#': soul, '.': key}});
});
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['#']];
if(at.ack){ at.ack = (at.ack + 1) || 1; }
if(!msg.put || (get['.'] && !obj_has(tmp, at.get))){
if(at.put !== u){ return }
at.on('in', {
get: at.get,
put: at.put = u,
$: at.$,
'@': msg['@']
});
return;
}
if(node_ == get['.']){ // is this a security concern?
at.on('in', {get: at.get, put: Gun.val.link.ify(get['#']), $: at.$, '@': msg['@']});
return;
}
Gun.on.put(msg, at.root.$);
}
var empty = {}, u;
var obj = Gun.obj, obj_has = obj.has, obj_put = obj.put, obj_del = obj.del, obj_to = obj.to, obj_map = obj.map;
var text_rand = Gun.text.random;
var _soul = Gun.val.rel._, node_ = Gun.node._;
// WARNING: GUN is very simple, but the JavaScript chaining API around GUN
// is complicated and was extremely hard to build. If you port GUN to another
// language, consider implementing an easier API to build.
var Gun = require('./root');
Gun.chain.chain = function(sub){
var gun = this, at = gun._, chain = new (sub || gun).constructor(gun), cat = chain._, root;
cat.root = root = at.root;
cat.id = ++root.once;
cat.back = gun._;
cat.on = Gun.on;
cat.on('in', input, cat); // For 'in' if I add my own listeners to each then I MUST do it before in gets called. If I listen globally for all incoming data instead though, regardless of individual listeners, I can transform the data there and then as well.
cat.on('out', output, cat); // However for output, there isn't really the global option. I must listen by adding my own listener individually BEFORE this one is ever called.
return chain;
}
function output(msg){
var put, get, at = this.as, back = at.back, root = at.root, tmp;
if(!msg.$){ msg.$ = at.$ }
this.to.next(msg);
if(get = msg.get){
/*if(u !== at.put){
at.on('in', at);
return;
}*/
if(at.lex){ msg.get = obj_to(at.lex, msg.get) }
if(get['#'] || at.soul){
get['#'] = get['#'] || at.soul;
msg['#'] || (msg['#'] = text_rand(9));
back = (root.$.get(get['#'])._);
if(!(get = get['.'])){
tmp = back.ack;
if(!tmp){ back.ack = -1 }
if(obj_has(back, 'put')){
back.on('in', back);
}
if(tmp){ return }
msg.$ = back.$;
} else
if(obj_has(back.put, get)){ // TODO: support #LEX !
put = (back.$.get(get)._);
if(!(tmp = put.ack)){ put.ack = -1 }
back.on('in', {
$: back.$,
put: Gun.state.to(back.put, get),
get: back.get
});
if(tmp){ return }
} else
if('string' != typeof get){
var put = {}, meta = (back.put||{})._;
Gun.obj.map(back.put, function(v,k){
if(!Gun.text.match(k, get)){ return }
put[k] = v;
})
if(!Gun.obj.empty(put)){
put._ = meta;
back.on('in', {$: back.$, put: put, get: back.get})
}
}
root.ask(ack, msg);
return root.on('in', msg);
}
if(root.now){ root.now[at.id] = root.now[at.id] || true; at.pass = {} }
if(get['.']){
if(at.get){
msg = {get: {'.': at.get}, $: at.$};
//if(back.ask || (back.ask = {})[at.get]){ return }
(back.ask || (back.ask = {}));
back.ask[at.get] = msg.$._; // TODO: PERFORMANCE? More elegant way?
return back.on('out', msg);
}
msg = {get: {}, $: at.$};
return back.on('out', msg);
}
at.ack = at.ack || -1;
if(at.get){
msg.$ = at.$;
get['.'] = at.get;
(back.ask || (back.ask = {}))[at.get] = msg.$._; // TODO: PERFORMANCE? More elegant way?
return back.on('out', msg);
}
}
return back.on('out', msg);
}
function input(msg){
var eve = this, cat = eve.as, root = cat.root, gun = msg.$, at = (gun||empty)._ || empty, change = msg.put, rel, tmp;
if(cat.get && msg.get !== cat.get){
msg = obj_to(msg, {get: cat.get});
}
if(cat.has && at !== cat){
msg = obj_to(msg, {$: cat.$});
if(at.ack){
cat.ack = at.ack;
//cat.ack = cat.ack || at.ack;
}
}
if(u === change){
tmp = at.put;
eve.to.next(msg);
if(cat.soul){ return } // TODO: BUG, I believee the fresh input refactor caught an edge case that a `gun.get('soul').get('key')` that points to a soul that doesn't exist will not trigger val/get etc.
if(u === tmp && u !== at.put){ return }
echo(cat, msg, eve);
if(cat.has){
not(cat, msg);
}
obj_del(at.echo, cat.id);
obj_del(cat.map, at.id);
return;
}
if(cat.soul){
eve.to.next(msg);
echo(cat, msg, eve);
if(cat.next){ obj_map(change, map, {msg: msg, cat: cat}) }
return;
}
if(!(rel = Gun.val.link.is(change))){
if(Gun.val.is(change)){
if(cat.has || cat.soul){
not(cat, msg);
} else
if(at.has || at.soul){
(at.echo || (at.echo = {}))[cat.id] = at.echo[at.id] || cat;
(cat.map || (cat.map = {}))[at.id] = cat.map[at.id] || {at: at};
//if(u === at.put){ return } // Not necessary but improves performance. If we have it but at does not, that means we got things out of order and at will get it. Once at gets it, it will tell us again.
}
eve.to.next(msg);
echo(cat, msg, eve);
return;
}
if(cat.has && at !== cat && obj_has(at, 'put')){
cat.put = at.put;
};
if((rel = Gun.node.soul(change)) && at.has){
at.put = (cat.root.$.get(rel)._).put;
}
tmp = (root.stop || {})[at.id];
//if(tmp && tmp[cat.id]){ } else {
eve.to.next(msg);
//}
relate(cat, msg, at, rel);
echo(cat, msg, eve);
if(cat.next){ obj_map(change, map, {msg: msg, cat: cat}) }
return;
}
var was = root.stop;
tmp = root.stop || {};
tmp = tmp[at.id] || (tmp[at.id] = {});
//if(tmp[cat.id]){ return }
tmp.is = tmp.is || at.put;
tmp[cat.id] = at.put || true;
//if(root.stop){
eve.to.next(msg)
//}
relate(cat, msg, at, rel);
echo(cat, msg, eve);
}
function relate(at, msg, from, rel){
if(!rel || node_ === at.get){ return }
var tmp = (at.root.$.get(rel)._);
if(at.has){
from = tmp;
} else
if(from.has){
relate(from, msg, from, rel);
}
if(from === at){ return }
if(!from.$){ from = {} }
(from.echo || (from.echo = {}))[at.id] = from.echo[at.id] || at;
if(at.has && !(at.map||empty)[from.id]){ // if we haven't seen this before.
not(at, msg);
}
tmp = from.id? ((at.map || (at.map = {}))[from.id] = at.map[from.id] || {at: from}) : {};
if(rel === tmp.link){
if(!(tmp.pass || at.pass)){
return;
}
}
if(at.pass){
Gun.obj.map(at.map, function(tmp){ tmp.pass = true })
obj_del(at, 'pass');
}
if(tmp.pass){ obj_del(tmp, 'pass') }
if(at.has){ at.link = rel }
ask(at, tmp.link = rel);
}
function echo(at, msg, ev){
if(!at.echo){ return } // || node_ === at.get ?
//if(at.has){ msg = obj_to(msg, {event: ev}) }
obj_map(at.echo, reverb, msg);
}
function reverb(to){
if(!to || !to.on){ return }
to.on('in', this);
}
function map(data, key){ // Map over only the changes on every update.
var cat = this.cat, next = cat.next || empty, via = this.msg, chain, at, tmp;
if(node_ === key && !next[key]){ return }
if(!(at = next[key])){
return;
}
//if(data && data[_soul] && (tmp = Gun.val.link.is(data)) && (tmp = (cat.root.$.get(tmp)._)) && obj_has(tmp, 'put')){
// data = tmp.put;
//}
if(at.has){
//if(!(data && data[_soul] && Gun.val.link.is(data) === Gun.node.soul(at.put))){
if(u === at.put || !Gun.val.link.is(data)){
at.put = data;
}
chain = at.$;
} else
if(tmp = via.$){
tmp = (chain = via.$.get(key))._;
if(u === tmp.put || !Gun.val.link.is(data)){
tmp.put = data;
}
}
at.on('in', {
put: data,
get: key,
$: chain,
via: via
});
}
function not(at, msg){
if(!(at.has || at.soul)){ return }
var tmp = at.map, root = at.root;
at.map = null;
if(at.has){
if(at.dub && at.root.stop){ at.dub = null }
at.link = null;
}
//if(!root.now || !root.now[at.id]){
if(!at.pass){
if((!msg['@']) && null === tmp){ return }
//obj_del(at, 'pass');
}
if(u === tmp && Gun.val.link.is(at.put)){ return } // This prevents the very first call of a thing from triggering a "clean up" call. // TODO: link.is(at.put) || !val.is(at.put) ?
obj_map(tmp, function(proxy){
if(!(proxy = proxy.at)){ return }
obj_del(proxy.echo, at.id);
});
tmp = at.put;
obj_map(at.next, function(neat, key){
if(u === tmp && u !== at.put){ return true }
neat.put = u;
if(neat.ack){
neat.ack = -1; // TODO: BUG? Should this be 0?
}
neat.on('in', {
get: key,
$: neat.$,
put: u
});
});
}
function ask(at, soul){
var tmp = (at.root.$.get(soul)._), lex = at.lex;
if(at.ack || lex){
(lex = lex||{})['#'] = soul;
tmp.on('out', {get: lex});
if(!at.ask){ return } // TODO: PERFORMANCE? More elegant way?
}
tmp = at.ask; Gun.obj.del(at, 'ask');
obj_map(tmp || at.next, function(neat, key){
var lex = neat.lex || {}; lex['#'] = soul; lex['.'] = lex['.'] || key;
neat.on('out', {get: lex});
});
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['#']];
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 }
at.on('in', {
get: at.get,
put: at.put = u,
$: at.$,
'@': msg['@']
});
return;
}
if(node_ == get['.']){ // is this a security concern?
at.on('in', {get: at.get, put: Gun.val.link.ify(get['#']), $: at.$, '@': msg['@']});
return;
}
Gun.on.put(msg, at.root.$);
}
var empty = {}, u;
var obj = Gun.obj, obj_has = obj.has, obj_put = obj.put, obj_del = obj.del, obj_to = obj.to, obj_map = obj.map;
var text_rand = Gun.text.random;
var _soul = Gun.val.link._, node_ = Gun.node._;

View File

@ -1,31 +1,31 @@
var Type = require('./type');
function Dup(opt){
var dup = {s:{}};
opt = opt || {max: 1000, age: 1000 * 9};//1000 * 60 * 2};
dup.check = function(id){ var tmp;
if(!(tmp = dup.s[id])){ return false }
if(tmp.pass){ return tmp.pass = false }
return dup.track(id);
}
dup.track = function(id, pass){
var it = dup.s[id] || (dup.s[id] = {});
it.was = time_is();
if(pass){ it.pass = true }
if(!dup.to){
dup.to = setTimeout(function(){
var now = time_is();
Type.obj.map(dup.s, function(it, id){
if(it && opt.age > (now - it.was)){ return }
Type.obj.del(dup.s, id);
});
dup.to = null;
}, opt.age + 9);
}
return it;
}
return dup;
}
var time_is = Type.time.is;
module.exports = Dup;
var Type = require('./type');
function Dup(opt){
var dup = {s:{}};
opt = opt || {max: 1000, age: 1000 * 9};//1000 * 60 * 2};
dup.check = function(id){ var tmp;
if(!(tmp = dup.s[id])){ return false }
if(tmp.pass){ return tmp.pass = false }
return dup.track(id);
}
dup.track = function(id, pass){
var it = dup.s[id] || (dup.s[id] = {});
it.was = time_is();
if(pass){ it.pass = true }
if(!dup.to){
dup.to = setTimeout(function(){
var now = time_is();
Type.obj.map(dup.s, function(it, id){
if(it && opt.age > (now - it.was)){ return }
Type.obj.del(dup.s, id);
});
dup.to = null;
}, opt.age + 9);
}
return it;
}
return dup;
}
var time_is = Type.time.is;
module.exports = Dup;

View File

@ -1,128 +1,133 @@
var Gun = require('./root');
Gun.chain.get = function(key, cb, as){
var gun, tmp;
if(typeof key === 'string'){
var back = this, cat = back._;
var next = cat.next || empty;
if(!(gun = next[key])){
gun = cache(key, back);
}
gun = gun.$;
} else
if(key instanceof Function){
if(true === cb){ return soul(this, key, cb, as) }
gun = this;
var at = gun._, root = at.root, tmp = root.now, ev;
as = cb || {};
as.at = at;
as.use = key;
as.out = as.out || {};
as.out.get = as.out.get || {};
(ev = at.on('in', use, as)).rid = rid;
(root.now = {$:1})[as.now = at.id] = ev;
var mum = root.mum; root.mum = {};
at.on('out', as.out);
root.mum = mum;
root.now = tmp;
return gun;
} else
if(num_is(key)){
return this.get(''+key, cb, as);
} else
if(tmp = rel.is(key)){
return this.get(tmp, cb, as);
} else {
(as = this.chain())._.err = {err: Gun.log('Invalid get request!', key)}; // CLEAN UP
if(cb){ cb.call(as, as._.err) }
return as;
}
if(tmp = cat.stun){ // TODO: Refactor?
gun._.stun = gun._.stun || tmp;
}
if(cb && cb instanceof Function){
gun.get(cb, as);
}
return gun;
}
function cache(key, back){
var cat = back._, next = cat.next, gun = back.chain(), at = gun._;
if(!next){ next = cat.next = {} }
next[at.get = key] = at;
if(back === cat.root.$){
at.soul = key;
} else
if(cat.soul || cat.has){
at.has = key;
//if(obj_has(cat.put, key)){
//at.put = cat.put[key];
//}
}
return at;
}
function soul(gun, cb, opt, as){
var cat = gun._, acks = 0, tmp;
if(tmp = cat.soul){ return cb(tmp, as, cat), gun }
if(tmp = cat.link){ return cb(tmp, as, cat), gun }
gun.get(function(msg, ev){
if(u === msg.put && (tmp = (obj_map(cat.root.opt.peers, function(v,k,t){t(k)})||[]).length) && ++acks < tmp){
return;
}
ev.rid(msg);
var at = ((at = msg.$) && at._) || {};
tmp = at.link || at.soul || rel.is(msg.put) || node_soul(msg.put) || at.dub;
cb(tmp, as, msg, ev);
}, {out: {get: {'.':true}}});
return gun;
}
function use(msg){
var eve = this, as = eve.as, cat = as.at, root = cat.root, gun = msg.$, at = (gun||{})._ || {}, data = msg.put || at.put, tmp;
if((tmp = root.now) && eve !== tmp[as.now]){ return eve.to.next(msg) }
//console.log("USE:", cat.id, cat.soul, cat.has, cat.get, msg, root.mum);
//if(at.async && msg.root){ return }
//if(at.async === 1 && cat.async !== true){ return }
//if(root.stop && root.stop[at.id]){ return } root.stop && (root.stop[at.id] = true);
//if(!at.async && !cat.async && at.put && msg.put === at.put){ return }
//else if(!cat.async && msg.put !== at.put && root.stop && root.stop[at.id]){ return } root.stop && (root.stop[at.id] = true);
//root.stop && (root.stop.id = root.stop.id || Gun.text.random(2));
//if((tmp = root.stop) && (tmp = tmp[at.id] || (tmp[at.id] = {})) && tmp[cat.id]){ return } tmp && (tmp[cat.id] = true);
if(eve.seen && at.id && eve.seen[at.id]){ return eve.to.next(msg) }
//if((tmp = root.stop)){ if(tmp[at.id]){ return } tmp[at.id] = msg.root; } // temporary fix till a better solution?
if((tmp = data) && tmp[rel._] && (tmp = rel.is(tmp))){
tmp = ((msg.$$ = at.root.gun.get(tmp))._);
if(u !== tmp.put){
msg = obj_to(msg, {put: data = tmp.put});
}
}
if((tmp = root.mum) && at.id){ // TODO: can we delete mum entirely now?
var id = at.id + (eve.id || (eve.id = Gun.text.random(9)));
if(tmp[id]){ return }
if(u !== data && !rel.is(data)){ tmp[id] = true; }
}
as.use(msg, eve);
if(eve.stun){
eve.stun = null;
return;
}
eve.to.next(msg);
}
function rid(at){
var cat = this.on;
if(!at || cat.soul || cat.has){ return this.off() }
if(!(at = (at = (at = at.$ || at)._ || at).id)){ return }
var map = cat.map, tmp, seen;
//if(!map || !(tmp = map[at]) || !(tmp = tmp.at)){ return }
if(tmp = (seen = this.seen || (this.seen = {}))[at]){ return true }
seen[at] = true;
return;
//tmp.echo[cat.id] = {}; // TODO: Warning: This unsubscribes ALL of this chain's listeners from this link, not just the one callback event.
//obj.del(map, at); // TODO: Warning: This unsubscribes ALL of this chain's listeners from this link, not just the one callback event.
return;
}
var obj = Gun.obj, obj_map = obj.map, obj_has = obj.has, obj_to = Gun.obj.to;
var num_is = Gun.num.is;
var rel = Gun.val.link, node_soul = Gun.node.soul, node_ = Gun.node._;
var empty = {}, u;
var Gun = require('./root');
Gun.chain.get = function(key, cb, as){
var gun, tmp;
if(typeof key === 'string'){
var back = this, cat = back._;
var next = cat.next || empty;
if(!(gun = next[key])){
gun = cache(key, back);
}
gun = gun.$;
} else
if(key instanceof Function){
if(true === cb){ return soul(this, key, cb, as) }
gun = this;
var at = gun._, root = at.root, tmp = root.now, ev;
as = cb || {};
as.at = at;
as.use = key;
as.out = as.out || {};
as.out.get = as.out.get || {};
(ev = at.on('in', use, as)).rid = rid;
(root.now = {$:1})[as.now = at.id] = ev;
var mum = root.mum; root.mum = {};
at.on('out', as.out);
root.mum = mum;
root.now = tmp;
return gun;
} else
if(num_is(key)){
return this.get(''+key, cb, as);
} else
if(tmp = rel.is(key)){
return this.get(tmp, cb, as);
} else
if(obj.is(key)){
gun = this;
if(tmp = ((tmp = key['#'])||empty)['='] || tmp){ gun = gun.get(tmp) }
gun._.lex = key;
return gun;
} else {
(as = this.chain())._.err = {err: Gun.log('Invalid get request!', key)}; // CLEAN UP
if(cb){ cb.call(as, as._.err) }
return as;
}
if(tmp = this._.stun){ // TODO: Refactor?
gun._.stun = gun._.stun || tmp;
}
if(cb && cb instanceof Function){
gun.get(cb, as);
}
return gun;
}
function cache(key, back){
var cat = back._, next = cat.next, gun = back.chain(), at = gun._;
if(!next){ next = cat.next = {} }
next[at.get = key] = at;
if(back === cat.root.$){
at.soul = key;
} else
if(cat.soul || cat.has){
at.has = key;
//if(obj_has(cat.put, key)){
//at.put = cat.put[key];
//}
}
return at;
}
function soul(gun, cb, opt, as){
var cat = gun._, acks = 0, tmp;
if(tmp = cat.soul || cat.link || cat.dub){ return cb(tmp, as, cat), gun }
gun.get(function(msg, ev){
if(u === msg.put && (tmp = Object.keys(cat.root.opt.peers).length) && ++acks < tmp){
return;
}
ev.rid(msg);
var at = ((at = msg.$) && at._) || {};
tmp = at.link || at.soul || rel.is(msg.put) || node_soul(msg.put) || at.dub;
cb(tmp, as, msg, ev);
}, {out: {get: {'.':true}}});
return gun;
}
function use(msg){
var eve = this, as = eve.as, cat = as.at, root = cat.root, gun = msg.$, at = (gun||{})._ || {}, data = msg.put || at.put, tmp;
if((tmp = root.now) && eve !== tmp[as.now]){ return eve.to.next(msg) }
//console.log("USE:", cat.id, cat.soul, cat.has, cat.get, msg, root.mum);
//if(at.async && msg.root){ return }
//if(at.async === 1 && cat.async !== true){ return }
//if(root.stop && root.stop[at.id]){ return } root.stop && (root.stop[at.id] = true);
//if(!at.async && !cat.async && at.put && msg.put === at.put){ return }
//else if(!cat.async && msg.put !== at.put && root.stop && root.stop[at.id]){ return } root.stop && (root.stop[at.id] = true);
//root.stop && (root.stop.id = root.stop.id || Gun.text.random(2));
//if((tmp = root.stop) && (tmp = tmp[at.id] || (tmp[at.id] = {})) && tmp[cat.id]){ return } tmp && (tmp[cat.id] = true);
if(eve.seen && at.id && eve.seen[at.id]){ return eve.to.next(msg) }
//if((tmp = root.stop)){ if(tmp[at.id]){ return } tmp[at.id] = msg.root; } // temporary fix till a better solution?
if((tmp = data) && tmp[rel._] && (tmp = rel.is(tmp))){
tmp = ((msg.$$ = at.root.gun.get(tmp))._);
if(u !== tmp.put){
msg = obj_to(msg, {put: data = tmp.put});
}
}
if((tmp = root.mum) && at.id){ // TODO: can we delete mum entirely now?
var id = at.id + (eve.id || (eve.id = Gun.text.random(9)));
if(tmp[id]){ return }
if(u !== data && !rel.is(data)){ tmp[id] = true; }
}
as.use(msg, eve);
if(eve.stun){
eve.stun = null;
return;
}
eve.to.next(msg);
}
function rid(at){
var cat = this.on;
if(!at || cat.soul || cat.has){ return this.off() }
if(!(at = (at = (at = at.$ || at)._ || at).id)){ return }
var map = cat.map, tmp, seen;
//if(!map || !(tmp = map[at]) || !(tmp = tmp.at)){ return }
if(tmp = (seen = this.seen || (this.seen = {}))[at]){ return true }
seen[at] = true;
return;
//tmp.echo[cat.id] = {}; // TODO: Warning: This unsubscribes ALL of this chain's listeners from this link, not just the one callback event.
//obj.del(map, at); // TODO: Warning: This unsubscribes ALL of this chain's listeners from this link, not just the one callback event.
return;
}
var obj = Gun.obj, obj_map = obj.map, obj_has = obj.has, obj_to = Gun.obj.to;
var num_is = Gun.num.is;
var rel = Gun.val.link, node_soul = Gun.node.soul, node_ = Gun.node._;
var empty = {}, u;

View File

@ -1,155 +1,155 @@
var Type = require('./type');
var Val = require('./val');
var Node = require('./node');
var Graph = {};
;(function(){
Graph.is = function(g, cb, fn, as){ // checks to see if an object is a valid graph.
if(!g || !obj_is(g) || obj_empty(g)){ return false } // must be an object.
return !obj_map(g, map, {cb:cb,fn:fn,as:as}); // makes sure it wasn't an empty object.
}
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){
var at = {path: [], obj: obj};
if(!env){
env = {};
} else
if(typeof env === 'string'){
env = {soul: env};
} else
if(env instanceof Function){
env.map = env;
}
if(env.soul){
at.rel = Val.rel.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.rel = at.rel || Val.rel.ify(Node.soul(at.node));
if(at.obj !== env.shell){
env.graph[Val.rel.is(at.rel)] = at.node;
}
}
return at;
}
function map(v,k,n){
var at = this, env = at.env, is, tmp;
if(Node._ === k && obj_has(v,Val.rel._)){
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.rel.is(at.rel));
at.rel = at.rel || Val.rel.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.rel; //{'#': Node.soul(tmp.node)};
}
function soul(id){ var at = this;
var prev = Val.link.is(at.rel), graph = at.env.graph;
at.rel = at.rel || Val.rel.ify(id);
at.rel[Val.rel._] = id;
if(at.node && at.node[Node._]){
at.node[Node._][Val.rel._] = id;
}
if(obj_has(graph, prev)){
graph[id] = graph[prev];
obj_del(graph, prev);
}
}
function valid(v,k,n, at,env){ var tmp;
if(Val.is(v)){ return true }
if(obj_is(v)){ return 1 }
if(tmp = env.invalid){
v = tmp.call(env.as || {}, v,k,n);
return valid(v,k,n, at,env);
}
env.err = "Invalid value at '" + at.path.concat(k).join('.') + "'!";
if(Type.list.is(v)){ env.err += " Use `.set(item)` instead of an Array." }
}
function seen(env, at){
var arr = env.seen, i = arr.length, has;
while(i--){ has = arr[i];
if(at.obj === has.obj){ return has }
}
arr.push(at);
}
}());
Graph.node = function(node){
var soul = Node.soul(node);
if(!soul){ return }
return obj_put({}, soul, node);
}
;(function(){
Graph.to = function(graph, root, opt){
if(!graph){ return }
var obj = {};
opt = opt || {seen: {}};
obj_map(graph[root], map, {obj:obj, graph: graph, opt: opt});
return obj;
}
function map(v,k){ var tmp, obj;
if(Node._ === k){
if(obj_empty(v, Val.rel._)){
return;
}
this.obj[k] = obj_copy(v);
return;
}
if(!(tmp = Val.rel.is(v))){
this.obj[k] = v;
return;
}
if(obj = this.opt.seen[tmp]){
this.obj[k] = obj;
return;
}
this.obj[k] = this.opt.seen[tmp] = Graph.to(this.graph, tmp, this.opt);
}
}());
var fn_is = Type.fn.is;
var obj = Type.obj, obj_is = obj.is, obj_del = obj.del, obj_has = obj.has, obj_empty = obj.empty, obj_put = obj.put, obj_map = obj.map, obj_copy = obj.copy;
var u;
module.exports = Graph;
var Type = require('./type');
var Val = require('./val');
var Node = require('./node');
var Graph = {};
;(function(){
Graph.is = function(g, cb, fn, as){ // checks to see if an object is a valid graph.
if(!g || !obj_is(g) || obj_empty(g)){ return false } // must be an object.
return !obj_map(g, map, {cb:cb,fn:fn,as:as}); // makes sure it wasn't an empty object.
}
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){
var at = {path: [], obj: obj};
if(!env){
env = {};
} else
if(typeof env === 'string'){
env = {soul: env};
} else
if(env instanceof Function){
env.map = env;
}
if(env.soul){
at.link = Val.link.ify(env.soul);
}
env.shell = (as||{}).shell;
env.graph = env.graph || {};
env.seen = env.seen || [];
env.as = env.as || as;
node(env, at);
env.root = at.node;
return env.graph;
}
function node(env, at){ var tmp;
if(tmp = seen(env, at)){ return tmp }
at.env = env;
at.soul = soul;
if(Node.ify(at.obj, map, at)){
at.link = at.link || Val.link.ify(Node.soul(at.node));
if(at.obj !== env.shell){
env.graph[Val.link.is(at.link)] = at.node;
}
}
return at;
}
function map(v,k,n){
var at = this, env = at.env, is, tmp;
if(Node._ === k && obj_has(v,Val.link._)){
return n._; // TODO: Bug?
}
if(!(is = valid(v,k,n, at,env))){ return }
if(!k){
at.node = at.node || n || {};
if(obj_has(v, Node._) && Node.soul(v)){ // ? for safety ?
at.node._ = obj_copy(v._);
}
at.node = Node.soul.ify(at.node, Val.link.is(at.link));
at.link = at.link || Val.link.ify(Node.soul(at.node));
}
if(tmp = env.map){
tmp.call(env.as || {}, v,k,n, at);
if(obj_has(n,k)){
v = n[k];
if(u === v){
obj_del(n, k);
return;
}
if(!(is = valid(v,k,n, at,env))){ return }
}
}
if(!k){ return at.node }
if(true === is){
return v;
}
tmp = node(env, {obj: v, path: at.path.concat(k)});
if(!tmp.node){ return }
return tmp.link; //{'#': Node.soul(tmp.node)};
}
function soul(id){ var at = this;
var prev = Val.link.is(at.link), graph = at.env.graph;
at.link = at.link || Val.link.ify(id);
at.link[Val.link._] = id;
if(at.node && at.node[Node._]){
at.node[Node._][Val.link._] = id;
}
if(obj_has(graph, prev)){
graph[id] = graph[prev];
obj_del(graph, prev);
}
}
function valid(v,k,n, at,env){ var tmp;
if(Val.is(v)){ return true }
if(obj_is(v)){ return 1 }
if(tmp = env.invalid){
v = tmp.call(env.as || {}, v,k,n);
return valid(v,k,n, at,env);
}
env.err = "Invalid value at '" + at.path.concat(k).join('.') + "'!";
if(Type.list.is(v)){ env.err += " Use `.set(item)` instead of an Array." }
}
function seen(env, at){
var arr = env.seen, i = arr.length, has;
while(i--){ has = arr[i];
if(at.obj === has.obj){ return has }
}
arr.push(at);
}
}());
Graph.node = function(node){
var soul = Node.soul(node);
if(!soul){ return }
return obj_put({}, soul, node);
}
;(function(){
Graph.to = function(graph, root, opt){
if(!graph){ return }
var obj = {};
opt = opt || {seen: {}};
obj_map(graph[root], map, {obj:obj, graph: graph, opt: opt});
return obj;
}
function map(v,k){ var tmp, obj;
if(Node._ === k){
if(obj_empty(v, Val.link._)){
return;
}
this.obj[k] = obj_copy(v);
return;
}
if(!(tmp = Val.link.is(v))){
this.obj[k] = v;
return;
}
if(obj = this.opt.seen[tmp]){
this.obj[k] = obj;
return;
}
this.obj[k] = this.opt.seen[tmp] = Graph.to(this.graph, tmp, this.opt);
}
}());
var fn_is = Type.fn.is;
var obj = Type.obj, obj_is = obj.is, obj_del = obj.del, obj_has = obj.has, obj_empty = obj.empty, obj_put = obj.put, obj_map = obj.map, obj_copy = obj.copy;
var u;
module.exports = Graph;

View File

@ -1,8 +1,8 @@
var Gun = require('./root');
require('./chain');
require('./back');
require('./put');
require('./get');
module.exports = Gun;
var Gun = require('./root');
require('./chain');
require('./back');
require('./put');
require('./get');
module.exports = Gun;

View File

@ -1,35 +1,36 @@
var Gun = require('./index');
Gun.chain.map = function(cb, opt, t){
var gun = this, cat = gun._, chain;
if(!cb){
if(chain = cat.each){ return chain }
cat.each = chain = gun.chain();
chain._.nix = gun.back('nix');
gun.on('in', map, 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.");
chain = gun.chain();
gun.map().on(function(data, key, at, ev){
var next = (cb||noop).call(this, data, key, at, ev);
if(u === next){ return }
if(data === next){ return chain._.on('in', at) }
if(Gun.is(next)){ return chain._.on('in', next._) }
chain._.on('in', {get: key, put: next});
});
return chain;
}
function map(msg){
if(!msg.put || Gun.val.is(msg.put)){ return this.to.next(msg) }
if(this.as.nix){ this.off() } // TODO: Ugly hack!
obj_map(msg.put, each, {at: this.as, msg: msg});
this.to.next(msg);
}
function each(v,k){
if(n_ === k){ return }
var msg = this.msg, gun = msg.$, at = this.at, tmp = (gun.get(k)._);
(tmp.echo || (tmp.echo = {}))[at.id] = tmp.echo[at.id] || at;
}
var obj_map = Gun.obj.map, noop = function(){}, event = {stun: noop, off: noop}, n_ = Gun.node._, u;
var Gun = require('./index');
Gun.chain.map = function(cb, opt, t){
var gun = this, cat = gun._, chain;
if(!cb){
if(chain = cat.each){ return chain }
cat.each = chain = gun.chain();
chain._.nix = gun.back('nix');
gun.on('in', map, 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.");
chain = gun.chain();
gun.map().on(function(data, key, at, ev){
var next = (cb||noop).call(this, data, key, at, ev);
if(u === next){ return }
if(data === next){ return chain._.on('in', at) }
if(Gun.is(next)){ return chain._.on('in', next._) }
chain._.on('in', {get: key, put: next});
});
return chain;
}
function map(msg){
if(!msg.put || Gun.val.is(msg.put)){ return this.to.next(msg) }
if(this.as.nix){ this.off() } // TODO: Ugly hack!
obj_map(msg.put, each, {at: this.as, msg: msg});
this.to.next(msg);
}
function each(v,k){
if(n_ === k){ return }
var msg = this.msg, gun = msg.$, at = gun._, cat = this.at, tmp = at.lex;
if(tmp && !Gun.text.match(k, tmp['.'] || tmp['#'] || tmp)){ return } // review?
((tmp = gun.get(k)._).echo || (tmp.echo = {}))[cat.id] = tmp.echo[cat.id] || cat;
}
var obj_map = Gun.obj.map, noop = function(){}, event = {stun: noop, off: noop}, n_ = Gun.node._, u;

View File

@ -1,58 +1,58 @@
var Type = require('./type');
var Val = require('./val');
var Node = {_: '_'};
Node.soul = function(n, o){ return (n && n._ && n._[o || soul_]) } // convenience function to check to see if there is a soul on a node and return it.
Node.soul.ify = function(n, o){ // put a soul on an object.
o = (typeof o === 'string')? {soul: o} : o || {};
n = n || {}; // make sure it exists.
n._ = n._ || {}; // make sure meta exists.
n._[soul_] = o.soul || n._[soul_] || text_random(); // put the soul on it.
return n;
}
Node.soul._ = Val.link._;
;(function(){
Node.is = function(n, cb, as){ var s; // checks to see if an object is a valid node.
if(!obj_is(n)){ return false } // must be an object.
if(s = Node.soul(n)){ // must have a soul on it.
return !obj_map(n, map, {as:as,cb:cb,s:s,n:n});
}
return false; // nope! This was not a valid node.
}
function map(v, k){ // we invert this because the way we check for this is via a negation.
if(k === Node._){ return } // skip over the metadata.
if(!Val.is(v)){ return true } // it is true that this is an invalid node.
if(this.cb){ this.cb.call(this.as, v, k, this.n, this.s) } // optionally callback each key/value.
}
}());
;(function(){
Node.ify = function(obj, o, as){ // returns a node from a shallow object.
if(!o){ o = {} }
else if(typeof o === 'string'){ o = {soul: o} }
else if(o instanceof Function){ o = {map: o} }
if(o.map){ o.node = o.map.call(as, obj, u, o.node || {}) }
if(o.node = Node.soul.ify(o.node || {}, o)){
obj_map(obj, map, {o:o,as:as});
}
return o.node; // This will only be a valid node if the object wasn't already deep!
}
function map(v, k){ var o = this.o, tmp, u; // iterate over each key/value.
if(o.map){
tmp = o.map.call(this.as, v, ''+k, o.node);
if(u === tmp){
obj_del(o.node, k);
} else
if(o.node){ o.node[k] = tmp }
return;
}
if(Val.is(v)){
o.node[k] = v;
}
}
}());
var obj = Type.obj, obj_is = obj.is, obj_del = obj.del, obj_map = obj.map;
var text = Type.text, text_random = text.random;
var soul_ = Node.soul._;
var u;
module.exports = Node;
var Type = require('./type');
var Val = require('./val');
var Node = {_: '_'};
Node.soul = function(n, o){ return (n && n._ && n._[o || soul_]) } // convenience function to check to see if there is a soul on a node and return it.
Node.soul.ify = function(n, o){ // put a soul on an object.
o = (typeof o === 'string')? {soul: o} : o || {};
n = n || {}; // make sure it exists.
n._ = n._ || {}; // make sure meta exists.
n._[soul_] = o.soul || n._[soul_] || text_random(); // put the soul on it.
return n;
}
Node.soul._ = Val.link._;
;(function(){
Node.is = function(n, cb, as){ var s; // checks to see if an object is a valid node.
if(!obj_is(n)){ return false } // must be an object.
if(s = Node.soul(n)){ // must have a soul on it.
return !obj_map(n, map, {as:as,cb:cb,s:s,n:n});
}
return false; // nope! This was not a valid node.
}
function map(v, k){ // we invert this because the way we check for this is via a negation.
if(k === Node._){ return } // skip over the metadata.
if(!Val.is(v)){ return true } // it is true that this is an invalid node.
if(this.cb){ this.cb.call(this.as, v, k, this.n, this.s) } // optionally callback each key/value.
}
}());
;(function(){
Node.ify = function(obj, o, as){ // returns a node from a shallow object.
if(!o){ o = {} }
else if(typeof o === 'string'){ o = {soul: o} }
else if(o instanceof Function){ o = {map: o} }
if(o.map){ o.node = o.map.call(as, obj, u, o.node || {}) }
if(o.node = Node.soul.ify(o.node || {}, o)){
obj_map(obj, map, {o:o,as:as});
}
return o.node; // This will only be a valid node if the object wasn't already deep!
}
function map(v, k){ var o = this.o, tmp, u; // iterate over each key/value.
if(o.map){
tmp = o.map.call(this.as, v, ''+k, o.node);
if(u === tmp){
obj_del(o.node, k);
} else
if(o.node){ o.node[k] = tmp }
return;
}
if(Val.is(v)){
o.node[k] = v;
}
}
}());
var obj = Type.obj, obj_is = obj.is, obj_del = obj.del, obj_map = obj.map;
var text = Type.text, text_random = text.random;
var soul_ = Node.soul._;
var u;
module.exports = Node;

276
src/on.js
View File

@ -1,137 +1,141 @@
var Gun = require('./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;
}
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);
}
}
Gun.chain.val = function(cb, opt){
Gun.log.once("onceval", "Future Breaking API Change: .val -> .once, apologies unexpected.");
return this.once(cb, opt);
}
Gun.chain.once = function(cb, opt){
var gun = this, at = gun._, data = at.put;
if(0 < at.ack && u !== data){
(cb || noop).call(gun, data, at.get);
return gun;
}
if(cb){
(opt = opt || {}).ok = cb;
opt.at = at;
opt.out = {'#': Gun.text.random(9)};
gun.get(val, {as: opt});
opt.async = true; //opt.async = at.stun? 1 : true;
} else {
Gun.log.once("valonce", "Chainable val is experimental, its behavior and API may change moving forward. Please play with it and report bugs and ideas on how to improve it.");
var chain = gun.chain();
chain._.nix = gun.once(function(){
chain._.on('in', gun._);
});
return chain;
}
return gun;
}
function val(msg, eve, to){
var opt = this.as, cat = opt.at, gun = msg.$, at = gun._, data = at.put || msg.put, link, tmp;
if(tmp = msg.$$){
link = tmp = (msg.$$._);
if(u !== link.put){
data = link.put;
}
}
if((tmp = eve.wait) && (tmp = tmp[at.id])){ clearTimeout(tmp) }
if((!to && (u === data || at.soul || at.link || (link && !(0 < link.ack))))
|| (u === data && (tmp = (obj_map(at.root.opt.peers, function(v,k,t){t(k)})||[]).length) && (!to && (link||at).ack <= tmp))){
tmp = (eve.wait = {})[at.id] = setTimeout(function(){
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) }
eve.rid(msg);
opt.ok.call(gun || opt.$, data, msg.get);
}
Gun.chain.off = function(){
// make off more aggressive. Warning, it might backfire!
var gun = this, at = gun._, tmp;
var cat = at.back;
if(!cat){ return }
if(tmp = cat.next){
if(tmp[at.get]){
obj_del(tmp, at.get);
} else {
}
}
if(tmp = cat.ask){
obj_del(tmp, at.get);
}
if(tmp = cat.put){
obj_del(tmp, at.get);
}
if(tmp = at.soul){
obj_del(cat.root.graph, tmp);
}
if(tmp = at.map){
obj_map(tmp, function(at){
if(at.link){
cat.root.$.get(at.link).off();
}
});
}
if(tmp = at.next){
obj_map(tmp, function(neat){
neat.$.off();
});
}
at.on('off', {});
return gun;
}
var obj = Gun.obj, obj_map = obj.map, obj_has = obj.has, obj_del = obj.del, obj_to = obj.to;
var rel = Gun.val.link;
var empty = {}, noop = function(){}, u;
var Gun = require('./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;
}
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);
}
}
Gun.chain.val = function(cb, opt){
Gun.log.once("onceval", "Future Breaking API Change: .val -> .once, apologies unexpected.");
return this.once(cb, opt);
}
Gun.chain.once = function(cb, opt){
var gun = this, at = gun._, data = at.put;
if(0 < at.ack && u !== data){
(cb || noop).call(gun, data, at.get);
return gun;
}
if(cb){
(opt = opt || {}).ok = cb;
opt.at = at;
opt.out = {'#': Gun.text.random(9)};
gun.get(val, {as: opt});
opt.async = true; //opt.async = at.stun? 1 : true;
} else {
Gun.log.once("valonce", "Chainable val is experimental, its behavior and API may change moving forward. Please play with it and report bugs and ideas on how to improve it.");
var chain = gun.chain();
chain._.nix = gun.once(function(){
chain._.on('in', gun._);
});
return chain;
}
return gun;
}
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(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;
if(!to && u === data && 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 && (tmp = Object.keys(at.root.opt.peers).length) && (!to && (link||at).ack < tmp))){
tmp = (eve.wait = {})[at.id] = setTimeout(function(){
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) }
eve.rid(msg);
opt.ok.call(gun || opt.$, data, msg.get);
}
Gun.chain.off = function(){
// make off more aggressive. Warning, it might backfire!
var gun = this, at = gun._, tmp;
var cat = at.back;
if(!cat){ return }
at.ack = 0; // so can resubscribe.
if(tmp = cat.next){
if(tmp[at.get]){
obj_del(tmp, at.get);
} else {
}
}
if(tmp = cat.ask){
obj_del(tmp, at.get);
}
if(tmp = cat.put){
obj_del(tmp, at.get);
}
if(tmp = at.soul){
obj_del(cat.root.graph, tmp);
}
if(tmp = at.map){
obj_map(tmp, function(at){
if(at.link){
cat.root.$.get(at.link).off();
}
});
}
if(tmp = at.next){
obj_map(tmp, function(neat){
neat.$.off();
});
}
at.on('off', {});
return gun;
}
var obj = Gun.obj, obj_map = obj.map, obj_has = obj.has, obj_del = obj.del, obj_to = obj.to;
var rel = Gun.val.link;
var empty = {}, noop = function(){}, u;

View File

@ -1,39 +1,39 @@
// On event emitter generic javascript utility.
module.exports = function onto(tag, arg, as){
if(!tag){ return {to: onto} }
var u, tag = (this.tag || (this.tag = {}))[tag] ||
(this.tag[tag] = {tag: tag, to: onto._ = {
next: function(arg){ var tmp;
if((tmp = this.to)){
tmp.next(arg);
}}
}});
if(arg instanceof Function){
var be = {
off: onto.off ||
(onto.off = function(){
if(this.next === onto._.next){ return !0 }
if(this === this.the.last){
this.the.last = this.back;
}
this.to.back = this.back;
this.next = onto._.next;
this.back.to = this.to;
if(this.the.last === this.the){
delete this.on.tag[this.the.tag];
}
}),
to: onto._,
next: arg,
the: tag,
on: this,
as: as,
};
(be.back = tag.last || tag).to = be;
return tag.last = be;
}
if((tag = tag.to) && u !== arg){ tag.next(arg) }
return tag;
};
// On event emitter generic javascript utility.
module.exports = function onto(tag, arg, as){
if(!tag){ return {to: onto} }
var u, tag = (this.tag || (this.tag = {}))[tag] ||
(this.tag[tag] = {tag: tag, to: onto._ = {
next: function(arg){ var tmp;
if((tmp = this.to)){
tmp.next(arg);
}}
}});
if(arg instanceof Function){
var be = {
off: onto.off ||
(onto.off = function(){
if(this.next === onto._.next){ return !0 }
if(this === this.the.last){
this.the.last = this.back;
}
this.to.back = this.back;
this.next = onto._.next;
this.back.to = this.to;
if(this.the.last === this.the){
delete this.on.tag[this.the.tag];
}
}),
to: onto._,
next: arg,
the: tag,
on: this,
as: as,
};
(be.back = tag.last || tag).to = be;
return tag.last = be;
}
if((tag = tag.to) && u !== arg){ tag.next(arg) }
return tag;
};

View File

@ -1,17 +1,17 @@
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: {}});
USE[R(path)] = mod.exports;
}
function R(p){
return p.split('/').slice(-1).toString().replace('.js','');
}
}
if(typeof module !== "undefined"){ var common = module }
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: {}});
USE[R(path)] = mod.exports;
}
function R(p){
return p.split('/').slice(-1).toString().replace('.js','');
}
}
if(typeof module !== "undefined"){ var common = module }

View File

@ -1,216 +1,230 @@
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!
var gun = this, at = (gun._), root = at.root.$, tmp;
as = as || {};
as.data = data;
as.via = as.$ = as.via || as.$ || gun;
if(typeof cb === 'string'){
as.soul = cb;
} else {
as.ack = as.ack || cb;
}
if(at.soul){
as.soul = at.soul;
}
if(as.soul || root === gun){
if(!obj_is(as.data)){
(as.ack||noop).call(as, as.out = {err: Gun.log("Data saved to the root level of the graph must be a node (an object), not a", (typeof as.data), 'of "' + as.data + '"!')});
if(as.res){ as.res() }
return gun;
}
as.soul = as.soul || (as.not = Gun.node.soul(as.data) || (as.via.back('opt.uuid') || Gun.text.random)());
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!
(as.ref||as.$).put(as.data, as.soul = soul, as);
});
return gun;
}
as.$ = root.get(as.soul);
as.ref = as.$;
ify(as);
return gun;
}
if(Gun.is(data)){
data.get(function(soul, o, msg){
if(!soul && Gun.val.is(msg.put)){
return Gun.log("The reference you are saving is a", typeof msg.put, '"'+ msg.put +'", not a node (object)!');
}
gun.put(Gun.val.rel.ify(soul), cb, as);
}, true);
return gun;
}
as.ref = as.ref || (root._ === (tmp = at.back))? gun : tmp.$;
if(as.ref._.soul && Gun.val.is(as.data) && at.get){
as.data = obj_put({}, at.get, as.data);
as.ref.put(as.data, as.soul, as);
return gun;
}
as.ref.get(any, true, {as: as});
if(!as.out){
// TODO: Perf idea! Make a global lock, that blocks everything while it is on, but if it is on the lock it does the expensive lookup to see if it is a dependent write or not and if not then it proceeds full speed. Meh? For write heavy async apps that would be terrible.
as.res = as.res || stun; // Gun.on.stun(as.ref); // TODO: BUG! Deal with locking?
as.$._.stun = as.ref._.stun;
}
return gun;
};
function ify(as){
as.batch = batch;
var opt = as.opt||{}, env = as.env = Gun.state.map(map, opt.state);
env.soul = as.soul;
as.graph = Gun.graph.ify(as.data, env, as);
if(env.err){
(as.ack||noop).call(as, as.out = {err: Gun.log(env.err)});
if(as.res){ as.res() }
return;
}
as.batch();
}
function stun(cb){
if(cb){ cb() }
return;
var as = this;
if(!as.ref){ return }
if(cb){
as.after = as.ref._.tag;
as.now = as.ref._.tag = {};
cb();
return;
}
if(as.after){
as.ref._.tag = as.after;
}
}
function batch(){ var as = this;
if(!as.graph || obj_map(as.stun, no)){ return }
as.res = as.res || function(cb){ if(cb){ cb() } };
as.res(function(){
var cat = (as.$.back(-1)._), ask = cat.ask(function(ack){
cat.root.on('ack', ack);
if(ack.err){ Gun.log(ack) }
if(!ack.lack){ this.off() } // One response is good enough for us currently. Later we may want to adjust this.
if(!as.ack){ return }
as.ack(ack, this);
}, as.opt);
// NOW is a hack to get synchronous replies to correctly call.
// and STOP is a hack to get async behavior to correctly call.
// neither of these are ideal, need to be fixed without hacks,
// but for now, this works for current tests. :/
var tmp = cat.root.now; obj.del(cat.root, 'now');
var mum = cat.root.mum; cat.root.mum = {};
(as.ref._).on('out', {
$: as.ref, put: as.out = as.env.graph, opt: as.opt, '#': ask
});
cat.root.mum = mum? obj.to(mum, cat.root.mum) : mum;
cat.root.now = tmp;
}, as);
if(as.res){ as.res() }
} function no(v,k){ if(v){ return true } }
function map(v,k,n, at){ var as = this;
var is = Gun.is(v);
if(k || !at.path.length){ return }
(as.res||iife)(function(){
var path = at.path, ref = as.ref, opt = as.opt;
var i = 0, l = path.length;
for(i; i < l; i++){
ref = ref.get(path[i]);
}
if(is){ ref = v }
var id = (ref._).dub;
if(id || (id = Gun.node.soul(at.obj))){
ref.back(-1).get(id);
at.soul(id);
return;
}
(as.stun = as.stun || {})[path] = true;
ref.get(soul, true, {as: {at: at, as: as, p:path}});
}, {as: as, at: at});
//if(is){ return {} }
}
function soul(id, as, msg, eve){
var as = as.as, cat = as.at; as = as.as;
var at = ((msg || {}).$ || {})._ || {};
id = at.dub = at.dub || id || Gun.node.soul(cat.obj) || Gun.node.soul(msg.put || at.put) || Gun.val.rel.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?
if(eve){ eve.stun = true }
if(!id){ // polyfill async uuid for SEA
at.via.back('opt.uuid')(function(err, id){ // TODO: improve perf without anonymous callback
if(err){ return Gun.log(err) } // TODO: Handle error.
solve(at, at.dub = at.dub || id, cat, as);
});
return;
}
solve(at, at.dub = id, cat, as);
}
function solve(at, id, cat, as){
at.$.back(-1).get(id);
cat.soul(id);
as.stun[cat.path] = false;
as.batch();
}
function any(soul, as, msg, eve){
as = as.as;
if(!msg.$ || !msg.$._){ return } // TODO: Handle
if(msg.err){ // TODO: Handle
console.log("Please report this as an issue! Put.any.err");
return;
}
var at = (msg.$._), data = at.put, opt = as.opt||{}, root, tmp;
if((tmp = as.ref) && tmp._.now){ return }
if(eve){ eve.stun = true }
if(as.ref !== as.$){
tmp = (as.$._).get || at.get;
if(!tmp){ // TODO: Handle
console.log("Please report this as an issue! Put.no.get"); // TODO: BUG!??
return;
}
as.data = obj_put({}, tmp, as.data);
tmp = null;
}
if(u === data){
if(!at.get){ return } // TODO: Handle
if(!soul){
tmp = at.$.back(function(at){
if(at.link || at.soul){ return at.link || at.soul }
as.data = obj_put({}, at.get, as.data);
});
}
tmp = tmp || at.soul || at.link || at.dub;// || at.get;
at = tmp? (at.root.$.get(tmp)._) : at;
as.soul = tmp;
data = as.data;
}
if(!as.not && !(as.soul = as.soul || soul)){
if(as.path && obj_is(as.data)){
as.soul = (opt.uuid || as.via.back('opt.uuid') || Gun.text.random)();
} else {
//as.data = obj_put({}, as.$._.get, as.data);
if(node_ == at.get){
as.soul = (at.put||empty)['#'] || at.dub;
}
as.soul = as.soul || at.soul || at.soul || (opt.uuid || as.via.back('opt.uuid') || Gun.text.random)();
}
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.
as.ref.put(as.data, as.soul = soul, as);
});
return;
}
}
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 u, empty = {}, noop = function(){}, iife = function(fn,as){fn.call(as||empty)};
var node_ = Gun.node._;
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!
var gun = this, at = (gun._), root = at.root.$, ctx = root._, M = 100, tmp;
if(!ctx.puta){ 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;
if(typeof cb === 'string'){
as.soul = cb;
} else {
as.ack = as.ack || cb;
}
if(at.soul){
as.soul = at.soul;
}
if(as.soul || root === gun){
if(!obj_is(as.data)){
(as.ack||noop).call(as, as.out = {err: Gun.log("Data saved to the root level of the graph must be a node (an object), not a", (typeof as.data), 'of "' + as.data + '"!')});
if(as.res){ as.res() }
return gun;
}
as.soul = as.soul || (as.not = Gun.node.soul(as.data) || (as.via.back('opt.uuid') || Gun.text.random)());
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!
(as.ref||as.$).put(as.data, as.soul = soul, as);
});
return gun;
}
as.$ = root.get(as.soul);
as.ref = as.$;
ify(as);
return gun;
}
if(Gun.is(data)){
data.get(function(soul, o, msg){
if(!soul){
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);
}, true);
return gun;
}
if(at.has && (tmp = Gun.val.link.is(data))){ at.dub = tmp }
as.ref = as.ref || (root._ === (tmp = at.back))? gun : tmp.$;
if(as.ref._.soul && Gun.val.is(as.data) && at.get){
as.data = obj_put({}, at.get, as.data);
as.ref.put(as.data, as.soul, as);
return gun;
}
as.ref.get(any, true, {as: as});
if(!as.out){
// TODO: Perf idea! Make a global lock, that blocks everything while it is on, but if it is on the lock it does the expensive lookup to see if it is a dependent write or not and if not then it proceeds full speed. Meh? For write heavy async apps that would be terrible.
as.res = as.res || stun; // Gun.on.stun(as.ref); // TODO: BUG! Deal with locking?
as.$._.stun = as.ref._.stun;
}
return gun;
};
function ify(as){
as.batch = batch;
var opt = as.opt||{}, env = as.env = Gun.state.map(map, opt.state);
env.soul = as.soul;
as.graph = Gun.graph.ify(as.data, env, as);
if(env.err){
(as.ack||noop).call(as, as.out = {err: Gun.log(env.err)});
if(as.res){ as.res() }
return;
}
as.batch();
}
function stun(cb){
if(cb){ cb() }
return;
var as = this;
if(!as.ref){ return }
if(cb){
as.after = as.ref._.tag;
as.now = as.ref._.tag = {};
cb();
return;
}
if(as.after){
as.ref._.tag = as.after;
}
}
function batch(){ var as = this;
if(!as.graph || obj_map(as.stun, no)){ return }
as.res = as.res || function(cb){ if(cb){ cb() } };
as.res(function(){
var cat = (as.$.back(-1)._), ask = cat.ask(function(ack){
cat.root.on('ack', ack);
if(ack.err){ Gun.log(ack) }
if(++acks > (as.acks || 0)){ this.off() } // Adjustable ACKs! Only 1 by default.
if(!as.ack){ return }
as.ack(ack, this);
//--C;
}, as.opt), acks = 0;
//C++;
// NOW is a hack to get synchronous replies to correctly call.
// and STOP is a hack to get async behavior to correctly call.
// neither of these are ideal, need to be fixed without hacks,
// but for now, this works for current tests. :/
var tmp = cat.root.now; obj.del(cat.root, 'now');
var mum = cat.root.mum; cat.root.mum = {};
(as.ref._).on('out', {
$: as.ref, put: as.out = as.env.graph, opt: as.opt, '#': ask
});
cat.root.mum = mum? obj.to(mum, cat.root.mum) : mum;
cat.root.now = tmp;
}, as);
if(as.res){ as.res() }
} function no(v,k){ if(v){ return true } }
function map(v,k,n, at){ var as = this;
var is = Gun.is(v);
if(k || !at.path.length){ return }
(as.res||iife)(function(){
var path = at.path, ref = as.ref, opt = as.opt;
var i = 0, l = path.length;
for(i; i < l; i++){
ref = ref.get(path[i]);
}
if(is){ ref = v }
var id = (ref._).dub;
if(id || (id = Gun.node.soul(at.obj))){
ref.back(-1).get(id);
at.soul(id);
return;
}
(as.stun = as.stun || {})[path] = true;
ref.get(soul, true, {as: {at: at, as: as, p:path}});
}, {as: as, at: at});
//if(is){ return {} }
}
function soul(id, as, msg, eve){
var as = as.as, cat = as.at; as = as.as;
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?
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
if(err){ return Gun.log(err) } // TODO: Handle error.
solve(at, at.dub = at.dub || id, cat, as);
});
return;
}
solve(at, at.dub = id, cat, as);
}
function solve(at, id, cat, as){
at.$.back(-1).get(id);
cat.soul(id);
as.stun[cat.path] = false;
as.batch();
}
function any(soul, as, msg, eve){
as = as.as;
if(!msg.$ || !msg.$._){ return } // TODO: Handle
if(msg.err){ // TODO: Handle
console.log("Please report this as an issue! Put.any.err");
return;
}
var at = (msg.$._), data = at.put, opt = as.opt||{}, root, tmp;
if((tmp = as.ref) && tmp._.now){ return }
if(eve){ eve.stun = true }
if(as.ref !== as.$){
tmp = (as.$._).get || at.get;
if(!tmp){ // TODO: Handle
console.log("Please report this as an issue! Put.no.get"); // TODO: BUG!??
return;
}
as.data = obj_put({}, tmp, as.data);
tmp = null;
}
if(u === data){
if(!at.get){ return } // TODO: Handle
if(!soul){
tmp = at.$.back(function(at){
if(at.link || at.soul){ return at.link || at.soul }
as.data = obj_put({}, at.get, as.data);
});
}
tmp = tmp || at.soul || at.link || at.dub;// || at.get;
at = tmp? (at.root.$.get(tmp)._) : at;
as.soul = tmp;
data = as.data;
}
if(!as.not && !(as.soul = as.soul || soul)){
if(as.path && obj_is(as.data)){
as.soul = (opt.uuid || as.via.back('opt.uuid') || Gun.text.random)();
} else {
//as.data = obj_put({}, as.$._.get, as.data);
if(node_ == at.get){
as.soul = (at.put||empty)['#'] || at.dub;
}
as.soul = as.soul || at.soul || at.link || (opt.uuid || as.via.back('opt.uuid') || Gun.text.random)();
}
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.
as.ref.put(as.data, as.soul = soul, as);
});
return;
}
}
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 u, empty = {}, noop = function(){}, iife = function(fn,as){fn.call(as||empty)};
var node_ = Gun.node._;

View File

@ -1,247 +1,236 @@
function Gun(o){
if(o instanceof Gun){ return (this._ = {gun: this, $: this}).$ }
if(!(this instanceof Gun)){ return new Gun(o) }
return Gun.create(this._ = {gun: this, $: this, opt: o});
}
Gun.is = function($){ return ($ instanceof Gun) || ($ && $._ && ($ === $._.$)) || false }
Gun.version = 0.9;
Gun.chain = Gun.prototype;
Gun.chain.toJSON = function(){};
var Type = require('./type');
Type.obj.to(Type, Gun);
Gun.HAM = require('./HAM');
Gun.val = require('./val');
Gun.node = require('./node');
Gun.state = require('./state');
Gun.graph = require('./graph');
Gun.on = require('./onto');
Gun.ask = require('./ask');
Gun.dup = require('./dup');
;(function(){
Gun.create = function(at){
at.root = at.root || at;
at.graph = at.graph || {};
at.on = at.on || Gun.on;
at.ask = at.ask || Gun.ask;
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});
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);
}
return;
}
dup.track(tmp);
if(!at.ask(msg['@'], 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);
}
}
}());
;(function(){
Gun.on.put = function(msg, gun){
var at = gun._, ctx = {$: gun, graph: at.graph, put: {}, map: {}, souls: {}, machine: Gun.state(), ack: msg['@'], cat: at, stop: {}};
if(!Gun.graph.is(msg.put, null, verify, ctx)){ ctx.err = "Error: Invalid graph!" }
if(ctx.err){ return at.on('in', {'@': msg['#'], err: Gun.log(ctx.err) }) }
obj_map(ctx.put, merge, ctx);
if(!ctx.async){ obj_map(ctx.map, map, ctx) }
if(u !== ctx.defer){
setTimeout(function(){
Gun.on.put(msg, gun);
}, ctx.defer - ctx.machine);
}
if(!ctx.diff){ return }
at.on('put', obj_to(msg, {put: ctx.diff}));
};
function verify(val, key, node, soul){ var ctx = this;
var state = Gun.state.is(node, key), tmp;
if(!state){ return ctx.err = "Error: No state on '"+key+"' in node '"+soul+"'!" }
var vertex = ctx.graph[soul] || empty, was = Gun.state.is(vertex, key, true), known = vertex[key];
var HAM = Gun.HAM(ctx.machine, state, was, val, known);
if(!HAM.incoming){
if(HAM.defer){ // pick the lowest
ctx.defer = (state < (ctx.defer || Infinity))? state : ctx.defer;
}
return;
}
ctx.put[soul] = Gun.state.to(node, key, ctx.put[soul]);
(ctx.diff || (ctx.diff = {}))[soul] = Gun.state.to(node, key, ctx.diff[soul]);
ctx.souls[soul] = true;
}
function merge(node, soul){
var ctx = this, cat = ctx.$._, at = (cat.next || empty)[soul];
if(!at){
if(!(cat.opt||empty).super){
ctx.souls[soul] = false;
return;
}
at = (ctx.$.get(soul)._);
}
var msg = ctx.map[soul] = {
put: node,
get: soul,
$: at.$
}, as = {ctx: ctx, msg: msg};
ctx.async = !!cat.tag.node;
if(ctx.ack){ msg['@'] = ctx.ack }
obj_map(node, each, as);
if(!ctx.async){ return }
if(!ctx.and){
// If it is async, we only need to setup one listener per context (ctx)
cat.on('node', function(m){
this.to.next(m); // make sure to call other context's listeners.
if(m !== ctx.map[m.get]){ return } // filter out events not from this context!
ctx.souls[m.get] = false; // set our many-async flag
obj_map(m.put, patch, m); // merge into view
if(obj_map(ctx.souls, function(v){ if(v){ return v } })){ return } // if flag still outstanding, keep waiting.
if(ctx.c){ return } ctx.c = 1; // failsafe for only being called once per context.
this.off();
obj_map(ctx.map, map, ctx); // all done, trigger chains.
});
}
ctx.and = true;
cat.on('node', msg); // each node on the current context's graph needs to be emitted though.
}
function each(val, key){
var ctx = this.ctx, graph = ctx.graph, msg = this.msg, soul = msg.get, node = msg.put, at = (msg.$._), tmp;
graph[soul] = Gun.state.to(node, key, graph[soul]);
if(ctx.async){ return }
at.put = Gun.state.to(node, key, at.put);
}
function patch(val, key){
var msg = this, node = msg.put, at = (msg.$._);
at.put = Gun.state.to(node, key, at.put);
}
function map(msg, soul){
if(!msg.$){ return }
this.cat.stop = this.stop; // temporary fix till a better solution?
(msg.$._).on('in', msg);
this.cat.stop = null; // temporary fix till a better solution?
}
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];
if(obj_has(soul, '*')){ // TEMPORARY HACK FOR MARTTI, TESTING
var graph = {};
Gun.obj.map(root.graph, function(node, s){
if(Gun.text.match(s, soul)){
graph[s] = Gun.obj.copy(node);
}
});
if(!Gun.obj.empty(graph)){
root.on('in', {
'@': msg['#'],
how: '*',
put: graph,
$: gun
});
}
} // TEMPORARY HACK FOR MARTTI, TESTING
if(!node){ return root.on('get', msg) }
if(has){
if(!obj_has(node, has)){ return root.on('get', msg) }
node = Gun.state.to(node, has);
// If we have a key in-memory, do we really need to fetch?
// Maybe... in case the in-memory key we have is a local write
// we still need to trigger a pull/merge from peers.
} else {
node = Gun.obj.copy(node);
}
node = Gun.graph.node(node);
tmp = (at||empty).ack;
root.on('in', {
'@': msg['#'],
how: 'mem',
put: node,
$: gun
});
//if(0 < tmp){ return }
root.on('get', msg);
}
}());
;(function(){
Gun.chain.opt = function(opt){
opt = opt || {};
var gun = this, at = gun._, tmp = opt.peers || opt;
if(!obj_is(opt)){ opt = {} }
if(!obj_is(at.opt)){ at.opt = opt }
if(text_is(tmp)){ tmp = [tmp] }
if(list_is(tmp)){
tmp = obj_map(tmp, function(url, i, map){
map(url, {url: url});
});
if(!obj_is(at.opt.peers)){ at.opt.peers = {}}
at.opt.peers = obj_to(tmp, at.opt.peers);
}
at.opt.peers = at.opt.peers || {};
obj_to(opt, at.opt); // copies options on to `at.opt` only if not already taken.
Gun.on('opt', at);
at.opt.uuid = at.opt.uuid || function(){ return state_lex() + text_rand(12) }
return gun;
}
}());
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.rel._, _has = '.', node_ = Gun.node._, rel_is = Gun.val.link.is;
var empty = {}, u;
console.debug = function(i, s){ return (console.debug.i && i === console.debug.i && console.debug.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!";
if(typeof window !== "undefined"){ (window.GUN = window.Gun = Gun).window = window }
try{ if(typeof common !== "undefined"){ common.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);
});
});*/
function Gun(o){
if(o instanceof Gun){ return (this._ = {gun: this, $: this}).$ }
if(!(this instanceof Gun)){ return new Gun(o) }
return Gun.create(this._ = {gun: this, $: this, opt: o});
}
Gun.is = function($){ return ($ instanceof Gun) || ($ && $._ && ($ === $._.$)) || false }
Gun.version = 0.9;
Gun.chain = Gun.prototype;
Gun.chain.toJSON = function(){};
var Type = require('./type');
Type.obj.to(Type, Gun);
Gun.HAM = require('./HAM');
Gun.val = require('./val');
Gun.node = require('./node');
Gun.state = require('./state');
Gun.graph = require('./graph');
Gun.on = require('./onto');
Gun.ask = require('./ask');
Gun.dup = require('./dup');
;(function(){
Gun.create = function(at){
at.root = at.root || at;
at.graph = at.graph || {};
at.on = at.on || Gun.on;
at.ask = at.ask || Gun.ask;
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});
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);
}
return;
}
dup.track(tmp);
if(!at.ask(msg['@'], 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);
}
}
}());
;(function(){
Gun.on.put = function(msg, gun){
var at = gun._, ctx = {$: gun, graph: at.graph, put: {}, map: {}, souls: {}, machine: Gun.state(), ack: msg['@'], cat: at, stop: {}};
if(!Gun.graph.is(msg.put, null, verify, ctx)){ ctx.err = "Error: Invalid graph!" }
if(ctx.err){ return at.on('in', {'@': msg['#'], err: Gun.log(ctx.err) }) }
obj_map(ctx.put, merge, ctx);
if(!ctx.async){ obj_map(ctx.map, map, ctx) }
if(u !== ctx.defer){
setTimeout(function(){
Gun.on.put(msg, gun);
}, ctx.defer - ctx.machine);
}
if(!ctx.diff){ return }
at.on('put', obj_to(msg, {put: ctx.diff}));
};
function verify(val, key, node, soul){ var ctx = this;
var state = Gun.state.is(node, key), tmp;
if(!state){ return ctx.err = "Error: No state on '"+key+"' in node '"+soul+"'!" }
var vertex = ctx.graph[soul] || empty, was = Gun.state.is(vertex, key, true), known = vertex[key];
var HAM = Gun.HAM(ctx.machine, state, was, val, known);
if(!HAM.incoming){
if(HAM.defer){ // pick the lowest
ctx.defer = (state < (ctx.defer || Infinity))? state : ctx.defer;
}
return;
}
ctx.put[soul] = Gun.state.to(node, key, ctx.put[soul]);
(ctx.diff || (ctx.diff = {}))[soul] = Gun.state.to(node, key, ctx.diff[soul]);
ctx.souls[soul] = true;
}
function merge(node, soul){
var ctx = this, cat = ctx.$._, at = (cat.next || empty)[soul];
if(!at){
if(!(cat.opt||empty).super){
ctx.souls[soul] = false;
return;
}
at = (ctx.$.get(soul)._);
}
var msg = ctx.map[soul] = {
put: node,
get: soul,
$: at.$
}, as = {ctx: ctx, msg: msg};
ctx.async = !!cat.tag.node;
if(ctx.ack){ msg['@'] = ctx.ack }
obj_map(node, each, as);
if(!ctx.async){ return }
if(!ctx.and){
// If it is async, we only need to setup one listener per context (ctx)
cat.on('node', function(m){
this.to.next(m); // make sure to call other context's listeners.
if(m !== ctx.map[m.get]){ return } // filter out events not from this context!
ctx.souls[m.get] = false; // set our many-async flag
obj_map(m.put, patch, m); // merge into view
if(obj_map(ctx.souls, function(v){ if(v){ return v } })){ return } // if flag still outstanding, keep waiting.
if(ctx.c){ return } ctx.c = 1; // failsafe for only being called once per context.
this.off();
obj_map(ctx.map, map, ctx); // all done, trigger chains.
});
}
ctx.and = true;
cat.on('node', msg); // each node on the current context's graph needs to be emitted though.
}
function each(val, key){
var ctx = this.ctx, graph = ctx.graph, msg = this.msg, soul = msg.get, node = msg.put, at = (msg.$._), tmp;
graph[soul] = Gun.state.to(node, key, graph[soul]);
if(ctx.async){ return }
at.put = Gun.state.to(node, key, at.put);
}
function patch(val, key){
var msg = this, node = msg.put, at = (msg.$._);
at.put = Gun.state.to(node, key, at.put);
}
function map(msg, soul){
if(!msg.$){ return }
this.cat.stop = this.stop; // temporary fix till a better solution?
(msg.$._).on('in', msg);
this.cat.stop = null; // temporary fix till a better solution?
}
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?
if(!node){ return root.on('get', msg) }
if(has){
if('string' != typeof has || !obj_has(node, has)){ return root.on('get', msg) }
node = Gun.state.to(node, has);
// If we have a key in-memory, do we really need to fetch?
// Maybe... in case the in-memory key we have is a local write
// we still need to trigger a pull/merge from peers.
} else {
node = Gun.obj.copy(node);
}
node = Gun.graph.node(node);
tmp = (at||empty).ack;
root.on('in', {
'@': msg['#'],
how: 'mem',
put: node,
$: gun
});
//if(0 < tmp){ return }
root.on('get', msg);
}
}());
;(function(){
Gun.chain.opt = function(opt){
opt = opt || {};
var gun = this, at = gun._, tmp = opt.peers || opt;
if(!obj_is(opt)){ opt = {} }
if(!obj_is(at.opt)){ at.opt = opt }
if(text_is(tmp)){ tmp = [tmp] }
if(list_is(tmp)){
tmp = obj_map(tmp, function(url, i, map){
i = {}; i.id = i.url = url; map(url, i);
});
if(!obj_is(at.opt.peers)){ at.opt.peers = {}}
at.opt.peers = obj_to(tmp, at.opt.peers);
}
at.opt.peers = at.opt.peers || {};
obj_map(opt, function each(v,k){
if(!obj_has(this, k) || text.is(v) || obj.empty(v)){ this[k] = v ; return }
if(v && v.constructor !== Object && !list_is(v)){ return }
obj_map(v, each, this[k]);
}, at.opt);
Gun.on('opt', at);
at.opt.uuid = at.opt.uuid || function(){ return state_lex() + text_rand(12) }
return gun;
}
}());
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 empty = {}, u;
console.debug = function(i, s){ return (console.debug.i && i === console.debug.i && console.debug.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!";
if(typeof window !== "undefined"){ (window.GUN = window.Gun = Gun).window = window }
try{ if(typeof common !== "undefined"){ common.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);
});
});*/

View File

@ -1,20 +1,20 @@
var Gun = require('./index');
Gun.chain.set = function(item, cb, opt){
var gun = this, soul;
cb = cb || function(){};
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);
}
return gun.get(soul || (Gun.state.lex() + Gun.text.random(7))).put(item, cb, opt);
}
item.get(function(soul, o, msg){
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;
}
var Gun = require('./index');
Gun.chain.set = function(item, cb, opt){
var gun = this, soul;
cb = cb || function(){};
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);
}
return gun.get(soul || (Gun.state.lex() + Gun.text.random(7))).put(item, cb, opt);
}
item.get(function(soul, o, msg){
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;
}

View File

@ -1,83 +1,83 @@
var Type = require('./type');
var Node = require('./node');
function State(){
var t;
/*if(perf){
t = start + perf.now(); // Danger: Accuracy decays significantly over time, even if precise.
} else {*/
t = time();
//}
if(last < t){
return N = 0, last = t + State.drift;
}
return last = t + ((N += 1) / D) + State.drift;
}
var time = Type.time.is, last = -Infinity, N = 0, D = 1000; // WARNING! In the future, on machines that are D times faster than 2016AD machines, you will want to increase D by another several orders of magnitude so the processing speed never out paces the decimal resolution (increasing an integer effects the state accuracy).
var perf = (typeof performance !== 'undefined')? (performance.timing && performance) : false, start = (perf && perf.timing && perf.timing.navigationStart) || (perf = false);
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;
if(!tmp){ return }
return num_is(tmp = tmp[k])? tmp : -Infinity;
}
State.lex = function(){ return State().toString(36).replace('.','') }
State.ify = function(n, k, s, v, soul){ // put a key's state on a node.
if(!n || !n[N_]){ // reject if it is not node-like.
if(!soul){ // unless they passed a soul
return;
}
n = Node.soul.ify(n, soul); // then make it so!
}
var tmp = obj_as(n[N_], State._); // grab the states data.
if(u !== k && k !== N_){
if(num_is(s)){
tmp[k] = s; // add the valid state.
}
if(u !== v){ // Note: Not its job to check for valid values!
n[k] = v;
}
}
return n;
}
State.to = function(from, k, to){
var val = (from||{})[k];
if(obj_is(val)){
val = obj_copy(val);
}
return State.ify(to, k, State.is(from, k), val, Node.soul(from));
}
;(function(){
State.map = function(cb, s, as){ var u; // for use with Node.ify
var o = obj_is(o = cb || s)? o : null;
cb = fn_is(cb = cb || s)? cb : null;
if(o && !cb){
s = num_is(s)? s : State();
o[N_] = o[N_] || {};
obj_map(o, map, {o:o,s:s});
return o;
}
as = as || obj_is(s)? s : u;
s = num_is(s)? s : State();
return function(v, k, o, opt){
if(!cb){
map.call({o: o, s: s}, v,k);
return v;
}
cb.call(as || this || {}, v, k, o, opt);
if(obj_has(o,k) && u === o[k]){ return }
map.call({o: o, s: s}, v,k);
}
}
function map(v,k){
if(N_ === k){ return }
State.ify(this.o, k, this.s) ;
}
}());
var obj = Type.obj, obj_as = obj.as, obj_has = obj.has, obj_is = obj.is, obj_map = obj.map, obj_copy = obj.copy;
var num = Type.num, num_is = num.is;
var fn = Type.fn, fn_is = fn.is;
var N_ = Node._, u;
module.exports = State;
var Type = require('./type');
var Node = require('./node');
function State(){
var t;
/*if(perf){
t = start + perf.now(); // Danger: Accuracy decays significantly over time, even if precise.
} else {*/
t = time();
//}
if(last < t){
return N = 0, last = t + State.drift;
}
return last = t + ((N += 1) / D) + State.drift;
}
var time = Type.time.is, last = -Infinity, N = 0, D = 1000; // WARNING! In the future, on machines that are D times faster than 2016AD machines, you will want to increase D by another several orders of magnitude so the processing speed never out paces the decimal resolution (increasing an integer effects the state accuracy).
var perf = (typeof performance !== 'undefined')? (performance.timing && performance) : false, start = (perf && perf.timing && perf.timing.navigationStart) || (perf = false);
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;
if(!tmp){ return }
return num_is(tmp = tmp[k])? tmp : -Infinity;
}
State.lex = function(){ return State().toString(36).replace('.','') }
State.ify = function(n, k, s, v, soul){ // put a key's state on a node.
if(!n || !n[N_]){ // reject if it is not node-like.
if(!soul){ // unless they passed a soul
return;
}
n = Node.soul.ify(n, soul); // then make it so!
}
var tmp = obj_as(n[N_], State._); // grab the states data.
if(u !== k && k !== N_){
if(num_is(s)){
tmp[k] = s; // add the valid state.
}
if(u !== v){ // Note: Not its job to check for valid values!
n[k] = v;
}
}
return n;
}
State.to = function(from, k, to){
var val = (from||{})[k];
if(obj_is(val)){
val = obj_copy(val);
}
return State.ify(to, k, State.is(from, k), val, Node.soul(from));
}
;(function(){
State.map = function(cb, s, as){ var u; // for use with Node.ify
var o = obj_is(o = cb || s)? o : null;
cb = fn_is(cb = cb || s)? cb : null;
if(o && !cb){
s = num_is(s)? s : State();
o[N_] = o[N_] || {};
obj_map(o, map, {o:o,s:s});
return o;
}
as = as || obj_is(s)? s : u;
s = num_is(s)? s : State();
return function(v, k, o, opt){
if(!cb){
map.call({o: o, s: s}, v,k);
return v;
}
cb.call(as || this || {}, v, k, o, opt);
if(obj_has(o,k) && u === o[k]){ return }
map.call({o: o, s: s}, v,k);
}
}
function map(v,k){
if(N_ === k){ return }
State.ify(this.o, k, this.s) ;
}
}());
var obj = Type.obj, obj_as = obj.as, obj_has = obj.has, obj_is = obj.is, obj_map = obj.map, obj_copy = obj.copy;
var num = Type.num, num_is = num.is;
var fn = Type.fn, fn_is = fn.is;
var N_ = Node._, u;
module.exports = State;

View File

@ -1,146 +1,141 @@
// Generic javascript utilities.
var Type = {};
//Type.fns = Type.fn = {is: function(fn){ return (!!fn && fn instanceof Function) }}
Type.fn = {is: function(fn){ return (!!fn && 'function' == typeof fn) }}
Type.bi = {is: function(b){ return (b instanceof Boolean || typeof b == 'boolean') }}
Type.num = {is: function(n){ return !list_is(n) && ((n - parseFloat(n) + 1) >= 0 || Infinity === n || -Infinity === n) }}
Type.text = {is: function(t){ return (typeof t == 'string') }}
Type.text.ify = function(t){
if(Type.text.is(t)){ return t }
if(typeof JSON !== "undefined"){ return JSON.stringify(t) }
return (t && t.toString)? t.toString() : t;
}
Type.text.random = function(l, c){
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 = function(t, o){ var r = false;
t = t || '';
o = Type.text.is(o)? {'=': o} : o || {}; // {'~', '=', '*', '<', '>', '+', '-', '?', '!'} // ignore case, exactly equal, anything after, lexically larger, lexically lesser, added in, subtacted from, questionable fuzzy match, and ends with.
if(Type.obj.has(o,'~')){ t = t.toLowerCase(); o['='] = (o['='] || o['~']).toLowerCase() }
if(Type.obj.has(o,'=')){ return t === o['='] }
if(Type.obj.has(o,'*')){ if(t.slice(0, o['*'].length) === o['*']){ r = true; t = t.slice(o['*'].length) } else { return false }}
if(Type.obj.has(o,'!')){ if(t.slice(-o['!'].length) === o['!']){ r = true } else { return false }}
if(Type.obj.has(o,'+')){
if(Type.list.map(Type.list.is(o['+'])? o['+'] : [o['+']], function(m){
if(t.indexOf(m) >= 0){ r = true } else { return true }
})){ return false }
}
if(Type.obj.has(o,'-')){
if(Type.list.map(Type.list.is(o['-'])? o['-'] : [o['-']], function(m){
if(t.indexOf(m) < 0){ r = true } else { return true }
})){ return false }
}
if(Type.obj.has(o,'>')){ if(t > o['>']){ r = true } else { return false }}
if(Type.obj.has(o,'<')){ if(t < o['<']){ r = true } else { return false }}
function fuzzy(t,f){ var n = -1, i = 0, c; for(;c = f[i++];){ if(!~(n = t.indexOf(c, n+1))){ return false }} return true } // via http://stackoverflow.com/questions/9206013/javascript-fuzzy-search
if(Type.obj.has(o,'?')){ if(fuzzy(t, o['?'])){ r = true } else { return false }} // change name!
return r;
}
Type.list = {is: function(l){ return (l instanceof Array) }}
Type.list.slit = Array.prototype.slice;
Type.list.sort = function(k){ // creates a new sort function based off some key
return function(A,B){
if(!A || !B){ return 0 } A = A[k]; B = B[k];
if(A < B){ return -1 }else if(A > B){ return 1 }
else { return 0 }
}
}
Type.list.map = function(l, c, _){ return obj_map(l, c, _) }
Type.list.index = 1; // change this to 0 if you want non-logical, non-mathematical, non-matrix, non-convenient array notation
Type.obj = {is: function(o){ return o? (o instanceof Object && o.constructor === Object) || Object.prototype.toString.call(o).match(/^\[object (\w+)\]$/)[1] === 'Object' : false }}
Type.obj.put = function(o, k, v){ return (o||{})[k] = v, o }
Type.obj.has = function(o, k){ return o && Object.prototype.hasOwnProperty.call(o, k) }
Type.obj.del = function(o, k){
if(!o){ return }
o[k] = null;
delete o[k];
return o;
}
Type.obj.as = function(o, k, v, u){ return o[k] = o[k] || (u === v? {} : v) }
Type.obj.ify = function(o){
if(obj_is(o)){ return o }
try{o = JSON.parse(o);
}catch(e){o={}};
return o;
}
;(function(){ var u;
function map(v,k){
if(obj_has(this,k) && u !== this[k]){ return }
this[k] = v;
}
Type.obj.to = function(from, to){
to = to || {};
obj_map(from, map, to);
return to;
}
}());
Type.obj.copy = function(o){ // because http://web.archive.org/web/20140328224025/http://jsperf.com/cloning-an-object/2
return !o? o : JSON.parse(JSON.stringify(o)); // is shockingly faster than anything else, and our data has to be a subset of JSON anyways!
}
;(function(){
function empty(v,i){ var n = this.n;
if(n && (i === n || (obj_is(n) && obj_has(n, i)))){ return }
if(i){ return true }
}
Type.obj.empty = function(o, n){
if(!o){ return true }
return obj_map(o,empty,{n:n})? false : true;
}
}());
;(function(){
function t(k,v){
if(2 === arguments.length){
t.r = t.r || {};
t.r[k] = v;
return;
} t.r = t.r || [];
t.r.push(k);
};
var keys = Object.keys;
Type.obj.map = function(l, c, _){
var u, i = 0, x, r, ll, lle, f = fn_is(c);
t.r = null;
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);
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.is = function(t){ return t? t instanceof Date : (+new Date().getTime()) }
var fn_is = Type.fn.is;
var list_is = Type.list.is;
var obj = Type.obj, obj_is = obj.is, obj_has = obj.has, obj_map = obj.map;
module.exports = Type;
// Generic javascript utilities.
var Type = {};
//Type.fns = Type.fn = {is: function(fn){ return (!!fn && fn instanceof Function) }}
Type.fn = {is: function(fn){ return (!!fn && 'function' == typeof fn) }}
Type.bi = {is: function(b){ return (b instanceof Boolean || typeof b == 'boolean') }}
Type.num = {is: function(n){ return !list_is(n) && ((n - parseFloat(n) + 1) >= 0 || Infinity === n || -Infinity === n) }}
Type.text = {is: function(t){ return (typeof t == 'string') }}
Type.text.ify = function(t){
if(Type.text.is(t)){ return t }
if(typeof JSON !== "undefined"){ return JSON.stringify(t) }
return (t && t.toString)? t.toString() : t;
}
Type.text.random = function(l, c){
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 = function(t, o){ var tmp, u;
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.list = {is: function(l){ return (l instanceof Array) }}
Type.list.slit = Array.prototype.slice;
Type.list.sort = function(k){ // creates a new sort function based off some key
return function(A,B){
if(!A || !B){ return 0 } A = A[k]; B = B[k];
if(A < B){ return -1 }else if(A > B){ return 1 }
else { return 0 }
}
}
Type.list.map = function(l, c, _){ return obj_map(l, c, _) }
Type.list.index = 1; // change this to 0 if you want non-logical, non-mathematical, non-matrix, non-convenient array notation
Type.obj = {is: function(o){ return o? (o instanceof Object && o.constructor === Object) || Object.prototype.toString.call(o).match(/^\[object (\w+)\]$/)[1] === 'Object' : false }}
Type.obj.put = function(o, k, v){ return (o||{})[k] = v, o }
Type.obj.has = function(o, k){ return o && Object.prototype.hasOwnProperty.call(o, k) }
Type.obj.del = function(o, k){
if(!o){ return }
o[k] = null;
delete o[k];
return o;
}
Type.obj.as = function(o, k, v, u){ return o[k] = o[k] || (u === v? {} : v) }
Type.obj.ify = function(o){
if(obj_is(o)){ return o }
try{o = JSON.parse(o);
}catch(e){o={}};
return o;
}
;(function(){ var u;
function map(v,k){
if(obj_has(this,k) && u !== this[k]){ return }
this[k] = v;
}
Type.obj.to = function(from, to){
to = to || {};
obj_map(from, map, to);
return to;
}
}());
Type.obj.copy = function(o){ // because http://web.archive.org/web/20140328224025/http://jsperf.com/cloning-an-object/2
return !o? o : JSON.parse(JSON.stringify(o)); // is shockingly faster than anything else, and our data has to be a subset of JSON anyways!
}
;(function(){
function empty(v,i){ var n = this.n;
if(n && (i === n || (obj_is(n) && obj_has(n, i)))){ return }
if(i){ return true }
}
Type.obj.empty = function(o, n){
if(!o){ return true }
return obj_map(o,empty,{n:n})? false : true;
}
}());
;(function(){
function t(k,v){
if(2 === arguments.length){
t.r = t.r || {};
t.r[k] = v;
return;
} t.r = t.r || [];
t.r.push(k);
};
var keys = Object.keys, map;
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;
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);
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.is = function(t){ return t? t instanceof Date : (+new Date().getTime()) }
var fn_is = Type.fn.is;
var list_is = Type.list.is;
var obj = Type.obj, obj_is = obj.is, obj_has = obj.has, obj_map = obj.map;
module.exports = Type;

View File

@ -1,44 +1,44 @@
var Type = require('./type');
var Val = {};
Val.is = function(v){ // Valid values are a subset of JSON: null, binary, number (!Infinity), text, or a soul relation. Arrays need special algorithms to handle concurrency, so they are not supported directly. Use an extension that supports them if needed but research their problems first.
if(v === u){ return false }
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.rel.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.rel.is = function(v){ // this defines whether an object is a soul relation or not, they look like this: {'#': 'UUID'}
if(v && v[rel_] && !v._ && obj_is(v)){ // must be an object.
var o = {};
obj_map(v, map, o);
if(o.id){ // a valid id was found.
return o.id; // yay! Return it.
}
}
return false; // the value was not a valid soul relation.
}
function map(s, k){ var o = this; // map over the object...
if(o.id){ return o.id = false } // if ID is already defined AND we're still looping through the object, it is considered invalid.
if(k == rel_ && text_is(s)){ // the key should be '#' and have a text value.
o.id = s; // we found the soul!
} else {
return o.id = false; // if there exists anything else on the object that isn't the soul, then it is considered invalid.
}
}
}());
Val.rel.ify = function(t){ return obj_put({}, rel_, t) } // convert a soul into a relation and return it.
Type.obj.has._ = '.';
var rel_ = Val.link._, u;
var bi_is = Type.bi.is;
var num_is = Type.num.is;
var text_is = Type.text.is;
var obj = Type.obj, obj_is = obj.is, obj_put = obj.put, obj_map = obj.map;
module.exports = Val;
var Type = require('./type');
var Val = {};
Val.is = function(v){ // Valid values are a subset of JSON: null, binary, number (!Infinity), text, or a soul relation. Arrays need special algorithms to handle concurrency, so they are not supported directly. Use an extension that supports them if needed but research their problems first.
if(v === u){ return false }
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){ // this defines whether an object is a soul relation or not, they look like this: {'#': 'UUID'}
if(v && v[rel_] && !v._ && obj_is(v)){ // must be an object.
var o = {};
obj_map(v, map, o);
if(o.id){ // a valid id was found.
return o.id; // yay! Return it.
}
}
return false; // the value was not a valid soul relation.
}
function map(s, k){ var o = this; // map over the object...
if(o.id){ return o.id = false } // if ID is already defined AND we're still looping through the object, it is considered invalid.
if(k == rel_ && text_is(s)){ // the key should be '#' and have a text value.
o.id = s; // we found the soul!
} else {
return o.id = false; // if there exists anything else on the object that isn't the soul, then it is considered invalid.
}
}
}());
Val.link.ify = function(t){ return obj_put({}, rel_, t) } // convert a soul into a relation and return it.
Type.obj.has._ = '.';
var rel_ = Val.link._, u;
var bi_is = Type.bi.is;
var num_is = Type.num.is;
var text_is = Type.text.is;
var obj = Type.obj, obj_is = obj.is, obj_put = obj.put, obj_map = obj.map;
module.exports = Val;

425
test/axe/holy-grail.js Normal file
View File

@ -0,0 +1,425 @@
/**
* AXE test 1
* What we want here: (1) Superpeer and (n) peers
* - The peers receives only the requested data.
* - If the Superpeer crash, after restart, must recreate all subscriptions and update the peers.
* - If some peer crash or go offline, must receive the changes via RTC.
*
* Tip: to run this `npm run testaxe`
* Tip 2: if you clone the gun repo, you need to create a link do gun package. Do `npm install && cd node_modules && ln -s ../ gun`
* Tip 3: If you not in localhost, run the browsers in anonymous mode because of domain security policies. https://superuser.com/questions/565409/how-to-stop-an-automatic-redirect-from-http-to-https-in-chrome
*/
var config = {
IP: require('ip').address(),
port: 8765,
servers: 2,
browsers: 3,
route: {
'/': __dirname + '/index.html',
'/gun.js': __dirname + '/../../gun.js',
'/gun/axe.js': __dirname + '/../../axe.js',
'/gun/lib/radix.js': __dirname + '/../../lib/radix.js',
'/gun/lib/webrtc.js': __dirname + '/../../lib/webrtc.js',
'/jquery.js': __dirname + '/../../examples/jquery.js'
}
}
var panic = require('panic-server');
panic.server().on('request', function(req, res){
config.route[req.url] && require('fs').createReadStream(config.route[req.url]).pipe(res);
}).listen(config.port);
var clients = panic.clients;
var manager = require('panic-manager')();
manager.start({
clients: Array(config.servers).fill().map(function(u, i){
return {
type: 'node',
port: config.port + (i + 1)
}
}),
panic: 'http://' + config.IP + ':' + config.port
});
var servers = clients.filter('Node.js');
var server = servers.pluck(1);
var server2 = servers.excluding(server).pluck(1);
var browsers = clients.excluding(servers);
var alice = browsers.pluck(1);
var bob = browsers.excluding(alice).pluck(1);
var john = browsers.excluding(alice).excluding(bob).pluck(1);
var again = {};
describe("The Holy Grail AXE Test!", function(){
this.timeout(5 * 60 * 1000);
// this.timeout(10 * 60 * 1000);
it("Servers have joined!", function(){
return servers.atLeast(config.servers);
});
it("GUN started!", function(){
return server.run(function(test){
var env = test.props;
test.async();
try{ require('fs').unlinkSync(env.i+'dataaxe') }catch(e){}
try{ require('fs').unlinkSync((env.i+1)+'dataaxe') }catch(e){}
var port = env.config.port + env.i;
var server = require('http').createServer(function(req, res){
res.end("I am "+ env.i +"!");
});
var Gun = require('gun');
require('gun/axe');
var gun = Gun({
file: env.i+'dataaxe',
web: server
});
server.listen(port, function(){
test.done();
});
}, {i: 1, config: config});
});
it(config.browsers +" browser(s) have joined!", function(){
console.log("PLEASE OPEN http://"+ config.IP +":"+ config.port +" IN "+ config.browsers +" BROWSER(S)!");
return browsers.atLeast(config.browsers);
});
it("Browsers initialized gun!", function(){
var tests = [], i = 0;
browsers.each(function(client, id){
tests.push(client.run(function(test){
localStorage.clear(); console.log('Clear localStorage!!!');
var env = test.props;
var opt = {peers:['http://'+ env.config.IP + ':' + (env.config.port + 1) + '/gun'], wait: 1000};
var pid = location.hash.slice(1);
if (pid) { opt.pid = pid; }
Gun.on('opt', function(ctx) {
this.to.next(ctx);
ctx.on('hi', function(opt) {
document.getElementById('pid').innerHTML = (document.getElementById('pid').innerHTML || "-") + ', ' + this.on.opt.pid;
});
});
var gun = window.gun = Gun(opt);
window.ref = gun.get('holy').get('grail');
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
});
it("Wait for Alice, Bob and John...", function(done){
setTimeout(done, 1000);
});
it("Alice Write: Hi Bob!", function(){
return alice.run(function(test){
console.log("I AM ALICE");
$('#name').text('Alice');
test.async();
ref.once(function() { // TODO: Need `.once` first for subscription. If Alice do a `.put` before a `.once`, Alice will get old data from localStorage if Bob update
ref.put('Hi Bob!', function(ack) {
console.log(ack);
setTimeout(test.done, 2000);
});
});
});
});
it("Bob receive ONCE from Alice: Hi Bob!", function(){
return bob.run(function(test){
console.log("I AM BOB");
$('#name').text('Bob');
test.async();
ref.once(function(data){
if('Hi Bob!' === data){
console.log('[OK] Bob receive the question: ', data);
return test.done();
} else {
var err = '[FAIL] Bob MUST receive: Hi Bob! but receive: ' + data + ' Storage: ' + localStorage.getItem('gun/');
console.log(err);
return test.fail(err);
}
})
})
});
it("Bob Write response: Hi Alice!", function(){
return bob.run(function(test){
test.async();
ref.put('Hi Alice!', function(ack) {
console.log('[OK] Bob Write response: Hi Alice!', ack);
setTimeout(test.done, 2000);
});
});
});
it("Alice Read response from Bob: Hi Alice!", function(){
return alice.run(function(test){
test.async();
ref.once(function(data){
if('Hi Alice!' === data){
console.log('[OK] Alice receive the response: ', data);
return test.done();
} else {
//TODO: aqui em duvida.. está pegando do localStorage, mas Bob alterou o dado.
var err = '[FAIL] Alice receive wrong response: "' + data + '" and must be "Hi Alice!"';
console.log(err);
return test.fail(err);
}
})
})
});
it("John Read what Bob say to Alice: Hi Alice!", function(){
return john.run(function(test){
test.async();
console.log("I AM JOHN");
$('#name').text('John');
ref.once(function(data){
if('Hi Alice!' === data){
console.log('[OK] John receive the data: ', data);
return test.done();
} else {
//TODO: aqui em duvida.. está pegando do localStorage, mas Bob alterou o dado.
var err = '[FAIL] John receive wrong data: "' + data + '" and must be "Hi Alice!"';
console.log(err);
return test.fail(err);
}
})
})
});
it("Bob Write in some data, Alice not subscribed", function(){
return bob.run(function(test){
test.async();
gun.get('bob').get('mine').put('Alice dont want this data now!', function() {
setTimeout(test.done, 2000);
});
});
});
it("Alice not subscribed. Must NOT receive data from Bob", function(){
return alice.run(function(test){
test.async();
/// This must be empty, because alice don't make a subscription to this node.
var bobdata = JSON.parse(localStorage.getItem('gun/')).bob;
if (bobdata) {
var err = '[FAIL] Alice receive not subscribed data in localStorage: ' + JSON.stringify(bobdata);
console.log(err);
return test.fail(err);
}
if (gun._.graph.bob) {
var err = '[FAIL] Alice receive not subscribed data in in graph: ' + JSON.stringify(gun._.graph.bob);
console.log(err);
return test.fail(err);
}
console.log('[OK] Alice Read must NOT receive data from Bob: ', bobdata);
return test.done();
})
});
it("Alice subscription Bob data with ONCE, MUST receive", function(){
return alice.run(function(test){
test.async();
gun.get('bob').once(function(data){
if(data){
console.log('[OK] Alice receive the value: ', data);
return test.done();
} else {
var err = '[FAIL] Alice receive the value: ' + data;
console.log(err);
return test.fail(err);
}
})
})
});
it("Bob Write in some data. Now Alice is subscribed.", function(){
return bob.run(function(test){
test.async();
gun.get('bob').get('mine').put('Alice WANT this data now!', function() {
setTimeout(test.done, 5000);
});
});
});
it("Alice must receive 'Alice WANT this data now!' from Bob node", function(){
return alice.run(function(test){
test.async();
if (gun._.graph.bob && gun._.graph.bob.mine === 'Alice WANT this data now!') {
console.log('[OK] GRAPH: ', gun._.graph.bob);
test.done();
} else {
var err = '[FAIL] GRAPH: ' + JSON.stringify(gun._.graph.bob);
console.log(err);
test.fail(err);
}
})
});
it("Server has crashed!", function(){
return server.run(function(test){
// var env = test.props;
// try{ require('fs').unlinkSync(env.i+'data'); }catch(e){}
process.exit(0);
}, {i: 1, config: config})
});
it("Wait...", function(done){
setTimeout(done, 2000);
});
it("Alice change the data (superpeer crashed yet).", function(){
return alice.run(function(test){
var env = test.props;
if(window.WebSocket){
var err;
try{ new WebSocket('http://'+ env.config.IP + ':' + (env.config.port + 2) + '/gun') }catch(e){ err = e }
if(!err){
test.fail("Server did not crash.");
}
}
test.async()
ref.put("Superpeer? Where are you?", function() {
setTimeout(test.done, 1000);
});
}, {config: config});
});
it("Bob receive what Alice change via WebRTC.", function(){
return bob.run(function(test){
test.async();
ref.once(function(data){
if('Superpeer? Where are you?' === data){
console.log('[OK] Bob received data via WebRTC: ', data);
return test.done();
} else {
var err = '[FAIL] Bob MUST not receive: "Superpeer? Where are you?", but receive: ' + data;
console.log(err);
return test.fail(err);
}
})
})
});
it("Bob change the data again (superpeer crashed yet).", function(){
return alice.run(function(test){
var env = test.props;
if(window.WebSocket){
var err;
try{ new WebSocket('http://'+ env.config.IP + ':' + (env.config.port + 2) + '/gun') }catch(e){ err = e }
if(!err){
test.fail("Server did not crash.");
}
}
test.async()
ref.put("Alice, can you hear me?", function() {
setTimeout(test.done, 1000);
});
}, {config: config});
});
it("Alice MUST receive 'Alice, can you hear me?' via WebRTC.", function(){
return bob.run(function(test){
test.async();
ref.once(function(data){
if('Alice, can you hear me?' === data){
console.log('[OK] Alice received data via WebRTC: ', data);
return test.done();
} else {
var err = '[FAIL] Alice MUST not receive: "Superpeer? Where are you?", but receive: ' + data;
console.log(err);
return test.fail(err);
}
})
})
});
it("Superpeer come started again!", function(){
return server2.run(function(test){
var env = test.props;
test.async();
// try{ require('fs').unlinkSync(env.i+'dataaxe') }catch(e){}
// try{ require('fs').unlinkSync((env.i+1)+'dataaxe') }catch(e){}
var port = env.config.port + env.i;
var server = require('http').createServer(function(req, res){
res.end("I am "+ env.i +"!");
});
var Gun = require('gun');
require('gun/axe');
var gun = Gun({
file: env.i+'dataaxe',
web: server
});
server.listen(port, function(){
test.done();
});
}, {i: 1, config: config});
});
it("Wait sync...", function(done){
setTimeout(done, 5000);
});
it("Alice change the data again (superpeer is UP!).", function(){
return alice.run(function(test){
var env = test.props;
test.async()
ref.put("Yes Bob! Thanks for asking!", function() {
setTimeout(test.done, 1000);
});
}, {config: config});
});
it("Bob MUST receive 'Yes Bob! Thanks for asking!'", function(){
return bob.run(function(test){
test.async();
ref.once(function(data){
if('Yes Bob! Thanks for asking!' === data){
console.log('[OK] Bob received the data change: ', data);
return test.done();
} else {
var err = '[FAIL] Bob MUST not receive: "Yes Bob! Thanks for asking!", but receive: ' + data;
console.log(err);
return test.fail(err);
}
})
})
});
it("John dont want to know what Bob say in his node!", function(){
return john.run(function(test){
test.async();
/// This must be empty, because John don't make a subscription to this node.
var bobdata = JSON.parse(localStorage.getItem('gun/')).bob;
if (bobdata) {
var err = '[FAIL] John receive not subscribed data: ' + JSON.stringify(bobdata);
console.log(err);
return test.fail(err);
}
if (gun._.graph.bob) {
var err = '[FAIL] John receive not subscribed data in in graph: ' + JSON.stringify(gun._.graph.bob);
console.log(err);
return test.fail(err);
}
console.log('[OK] John Read must NOT receive data from Bob: ', bobdata, gun._.graph.bob);
return test.done();
})
});
it("All finished!", function(done){
console.log("Done! Cleaning things up...");
setTimeout(function(){
done();
},1000);
});
after("Everything shut down.", function(){
browsers.run(function(){
//location.reload();
//setTimeout(function(){
//}, 15 * 1000);
});
return servers.run(function(){
process.exit();
});
});
});

13
test/axe/index.html Normal file
View File

@ -0,0 +1,13 @@
<script src='panic.js'></script>
<script>panic.server(location.origin)</script>
<script src='gun.js'></script>
<script src='gun/axe.js'></script>
<script src="gun/lib/radix.js"></script>
<script src='gun/lib/webrtc.js'></script>
<script src='jquery.js'></script>
<meta charset="utf-8"/>
<h1>Running AXE Tests</h1>
<div><span>Name: </span><span id="name"></span></div>
<div><span>PID: </span><span id="pid"></span></div>
<div id="log"></div>
<!-- <textarea id="print" style="width: 100%; height: 90%; border: 0;"></textarea> -->

View File

@ -1,5 +1,4 @@
describe('Gun', function(){
var root;
(function(){
var env;
@ -7,7 +6,10 @@ describe('Gun', function(){
if(typeof window !== 'undefined'){ env = window }
root = env.window? env.window : global;
try{ env.window && root.localStorage && root.localStorage.clear() }catch(e){}
try{ localStorage.clear() }catch(e){}
try{ indexedDB.deleteDatabase('radatatest') }catch(e){}
try{ require('fs').unlinkSync('data.json') }catch(e){}
try{ require('../lib/fsrm')('radatatest') }catch(e){}
//root.Gun = root.Gun || require('../gun');
if(root.Gun){
root.Gun = root.Gun;
@ -19,6 +21,7 @@ describe('Gun', function(){
//require('../lib/file');
require('../lib/store');
require('../lib/rfs');
require('./rad/rad.js');
require('./sea/sea.js');
}
}(this));
@ -185,13 +188,13 @@ describe('Gun', function(){
it('match',function(){
expect(Gun.text.match("user/mark", 'user/mark')).to.be.ok();
expect(Gun.text.match("user/mark/nadal", {'=': 'user/mark'})).to.not.be.ok();
expect(Gun.text.match("user/mark", {'~': 'user/Mark'})).to.be.ok();
expect(Gun.text.match("user/mark/nadal", {'*': 'user/'})).to.be.ok();
expect(Gun.text.match("email/mark@gunDB.io", {'*': 'user/'})).to.not.be.ok();
expect(Gun.text.match("user/mark/nadal", {'*': 'user/', '>': 'j', '<': 'o'})).to.be.ok();
expect(Gun.text.match("user/amber/nadal", {'*': 'user/', '>': 'j', '<': 'o'})).to.not.be.ok();
expect(Gun.text.match("user/amber/nadal", {'*': 'user/', '>': 'a', '<': 'c'})).to.be.ok();
expect(Gun.text.match("user/mark/nadal", {'*': 'user/', '>': 'a', '<': 'c'})).to.not.be.ok();
expect(Gun.text.match("user/mark/nadal", {'>': 'user/j', '<': 'user/o'})).to.be.ok();
expect(Gun.text.match("user/amber/nadal", {'>': 'user/j', '<': 'user/o'})).to.not.be.ok();
expect(Gun.text.match("user/amber/nadal", {'>': 'user/a', '<': 'user/c'})).to.be.ok();
expect(Gun.text.match("user/mark/nadal", {'>': 'user/a', '<': 'user/c'})).to.not.be.ok();
return; // below is OLD bloat, still available in lib/match.js
expect(Gun.text.match("user/mark/nadal", {'*': 'user/', '>': 'j', '<': 'o', '?': 'm/n'})).to.be.ok();
expect(Gun.text.match("user/amber/cazzell", {'*': 'user/', '?': 'm/n'})).to.not.be.ok();
expect(Gun.text.match("user/mark/nadal", {'*': 'user/', '-': 'mad'})).to.be.ok();
@ -207,6 +210,7 @@ describe('Gun', function(){
expect(Gun.text.match("user/mark/rachel/timothy/cazzell", {'*': 'user/', '+': ['mark', 'cazzell'], '-': ['amber', 'timothy']})).to.not.be.ok();
expect(Gun.text.match("photo/kitten.jpg", {'*': 'photo/', '!': '.jpg'})).to.be.ok();
expect(Gun.text.match("photo/kittens.gif", {'*': 'photo/', '!': '.jpg'})).to.not.be.ok();
expect(Gun.text.match("user/mark", {'~': 'user/Mark'})).to.be.ok();
});
});
describe('List', function(){
@ -613,6 +617,7 @@ describe('Gun', function(){
});
describe('Gun Safety', function(){
/* WARNING NOTE: Internal API has significant breaking changes! */
var gun = Gun();
it('is',function(){
expect(Gun.is(gun)).to.be(true);
@ -3048,7 +3053,7 @@ describe('Gun', function(){
});
it('get put get get put reload get get then get', function(done){
this.timeout(6000);
this.timeout(9000);
var gun = Gun();
gun.get('stef').put({name:'Stef'});
@ -3081,7 +3086,7 @@ describe('Gun', function(){
if(done.c){ return } done.c = 1;
done();
});
},5000);
},1200);
});
it('get get get any parallel', function(done){
@ -3677,22 +3682,22 @@ describe('Gun', function(){
this.timeout(5000);
var gun = Gun({test_no_peer:true}).get('g/m/no/slow');
//console.log("---------- setup data done -----------");
var prev, diff, max = 25, total = 9, largest = -1, gone = {};
var prev, diff, max = 25, total = 9, largest = -1, gone = {}, u;
//var prev, diff, max = Infinity, total = 10000, largest = -1, gone = {};
// TODO: It would be nice if we could change these numbers for different platforms/versions of javascript interpreters so we can squeeze as much out of them.
gun.get('history').map().on(function(time, index){
//console.log(">>>", index, time);
diff = Gun.time.is() - time;
//console.log(">>>", index, time, diff);
//return;
expect(gone[index]).to.not.be.ok();
gone[index] = diff;
largest = (largest < diff)? diff : largest;
//console.log(diff, '<', max);
expect(diff > max).to.not.be.ok();
});
var turns = 0;
var many = setInterval(function(){
if(turns > total || (diff || 0) > (max + 5)){
if(u === diff){ return }
clearTimeout(many);
expect(Gun.num.is(diff)).to.be.ok();
if(done.c){ return } done.c = 1;
@ -3762,9 +3767,9 @@ describe('Gun', function(){
var msg = {what: 'hello world'};
//var ref = user.get('who').get('all').set(msg);
//user.get('who').get('said').set(ref);
var ref = gun.get('who').get('all').set(msg);
gun.get('who').get('said').set(ref);
gun.get('who').get('said').map().once(function(data){
var ref = gun.get('s/r/who').get('all').set(msg);
gun.get('s/r/who').get('said').set(ref);
gun.get('s/r/who').get('said').map().once(function(data){
expect(data.what).to.be.ok();
done();
})

View File

@ -23,8 +23,15 @@
<script
src="../sea.js">
</script>
<script src="./common.js"></script>
<script src="../lib/radix.js"></script>
<script src="../lib/radisk.js"></script>
<script src="../lib/store.js"></script>
<script src="../lib/rindexed.js"></script>
<script src="./rad/rad.js"></script>
<script src="./sea/sea.js"></script>
<script src="./common.js"></script>
<script>
if(location.search){
Gun.debug = true;

View File

@ -0,0 +1,71 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../../../gun/examples/jquery.js"></script>
<script async src="../../../gun/lib/monotype.js"></script>
<script async src="../../../gun/lib/meta.js"></script>
</head>
<body>
<div id="edit" contenteditable='true'>the world is a beautiful place.</div>
<div id="out">The world is a beautiful place.</div>
<div id="test">
<button id="render">render</button>
<textarea id="before"></textarea>
<textarea id="after"></textarea>
<script src="../../../gun/lib/normalize.js"></script>
</div>
<script>
$('#render').on('click', check);
$('#edit').on('keyup', check).focus();
function check(){
var a = $('#edit').html();
$('#before').val(a);
var opt = {};
opt.hierarchy = ['div', 'ol', 'ul', 'li', 'p', 'a', 'b', 'i', 'span', 's', 'sub', 'sup', 'u', 'br'];
opt.convert = {'em': 'i', 'strong': 'b', 'strike': 's', 'font': 'span'};
var b = $.normalize(a);
$('#after').val(b);
$('#out').html(b);
}
</script>
<script>
var $xss = $('<div id="xss">').appendTo('body');
$.each([
'javascript:',
'JaVaScRiPt:',
'java script:',
'java\nscript:',
'java\tscript:',
'java\0script:',
'jav&#x09;ascript:',
'jav&#x0A;ascript:',
'jav&#x0D;ascript:',
' &#14; javascript:',
'&#106;avascript:',
'&#0000106avascript:',
'&#x6A;avascript:',
'\u006Aavascript:',
'&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74:',
'&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x0003A;',
'&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;:',
'&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;',
'&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116:',
'&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058'
], function(i,v){
//console.log(v);
var s = "<div><a href='"+v+"alert(1)'>xss</a></div>";
var html = $.normalize(s);
if(html.match(/href/ig)){ alert('xss') }
$xss.append(html);
console.log(html);
});
// url("javascript: // and all permutations
// stylesheets can apparently have XSS?
</script>
<style>
button { width: 100%; }
textarea { width: 45%; height: 20em; font-size: 18pt; }
</style>
</body>
</html>

198
test/panic/1putackget.js Normal file
View File

@ -0,0 +1,198 @@
/*
This is the first in a series of basic networking correctness tests.
Each test itself might be dumb and simple, but built up together,
they prove desired end goals for behavior at scale.
1. (this file) Is a browser write is confirmed as save by multiple peers even if by daisy chain.
2.
*/
var config = {
IP: require('ip').address(),
port: 8765,
servers: 2,
browsers: 2,
route: {
'/': __dirname + '/index.html',
'/gun.js': __dirname + '/../../gun.js',
'/jquery.js': __dirname + '/../../examples/jquery.js'
}
}
var panic = require('panic-server');
panic.server().on('request', function(req, res){
config.route[req.url] && require('fs').createReadStream(config.route[req.url]).pipe(res);
}).listen(config.port);
var clients = panic.clients;
var manager = require('panic-manager')();
manager.start({
clients: Array(config.servers).fill().map(function(u, i){
return {
type: 'node',
port: config.port + (i + 1)
}
}),
panic: 'http://' + config.IP + ':' + config.port
});
var servers = clients.filter('Node.js');
var bob = servers.pluck(1);
var carl = servers.excluding(bob).pluck(1);
var browsers = clients.excluding(servers);
var alice = browsers.pluck(1);
var dave = browsers.excluding(alice).pluck(1);
describe("Put ACK", function(){
//this.timeout(5 * 60 * 1000);
this.timeout(10 * 60 * 1000);
it("Servers have joined!", function(){
return servers.atLeast(config.servers);
});
it("GUN started!", function(){
var tests = [], i = 0;
servers.each(function(client){
tests.push(client.run(function(test){
var env = test.props;
test.async();
try{ require('fs').unlinkSync(env.i+'data') }catch(e){}
try{ require('gun/lib/fsrm')(env.i+'data') }catch(e){}
var server = require('http').createServer(function(req, res){
res.end("I am "+ env.i +"!");
});
var port = env.config.port + env.i;
var Gun = require('gun');
var peers = [], i = env.config.servers;
while(i--){
var tmp = (env.config.port + (i + 1));
if(port != tmp){ // ignore ourselves
peers.push('http://'+ env.config.IP + ':' + tmp + '/gun');
}
}
console.log(port, " connect to ", peers);
var gun = Gun({file: env.i+'data', peers: peers, web: server});
server.listen(port, function(){
test.done();
});
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
});
it(config.browsers +" browser(s) have joined!", function(){
console.log("PLEASE OPEN http://"+ config.IP +":"+ config.port +" IN "+ config.browsers +" BROWSER(S)!");
return browsers.atLeast(config.browsers);
});
it("Browsers initialized gun!", function(){
var tests = [], i = 0;
browsers.each(function(client, id){
tests.push(client.run(function(test){
try{ localStorage.clear() }catch(e){}
try{ indexedDB.deleteDatabase('radata') }catch(e){}
var env = test.props;
var gun = Gun('http://'+ env.config.IP + ':' + (env.config.port + 1) + '/gun');
window.ref = gun.get('a');
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
});
it("Put", function(){
return alice.run(function(test){
console.log("I AM ALICE");
test.async();
var c = test.props.acks, acks = {};
c = c < 2? 2 : c;
ref.put({hello: 'world'}, function(ack){
//console.log("acks:", ack, c);
acks[ack['#']] = 1;
if(Object.keys(acks).length == c){
wire();
return test.done();
}
}, {acks: c});
function wire(){
var hear = ref._.root.opt.mesh.hear;
ref._.root.opt.mesh.hear = function(raw, peer){
var msg = Gun.obj.ify(raw);
console.log('hear:', msg);
hear(raw, peer);
(ref.hear || (ref.hear = [])).push(msg);
}
var say = ref._.root.opt.mesh.say;
ref._.root.opt.mesh.say = function(raw, peer){
var yes = say(raw, peer);
if(yes === false){ return }
console.log("say:", msg, yes);
(ref.say || (ref.say = [])).push(Gun.obj.ify(msg));
}
}
}, {acks: config.servers});
});
it("Get", function(){
/*
Here is the recursive rule for GET, keep replying while hashes mismatch.
1. Receive a GET message.
2. If it has a hash, and if you have a thing matching the GET, then see if the hashes are the same, if they are then don't ACK, don't relay, end.
3. If you would have the thing but do not, then ACK that YOU have nothing.
4. If you have a thing matching the GET or an ACK for the GET's message, add the hash to the GET message, and ACK with the thing or ideally the remaining difference.
5. Pick ?3? OTHER peers preferably by priority that they have got the thing, send them the GET, plus all "up" peers.
6. If no ACKs you are done, end.
7. If you get ACKs back to the GET with things and different hashes, optionally merge into the thing you have GOT and update the hash.
8. Go to 4.
*/
return dave.run(function(test){
console.log("I AM DAVE");
test.async();
var c = 0, to;
var hear = ref._.root.opt.mesh.hear;
ref._.root.opt.mesh.hear = function(raw, peer){
var msg = Gun.obj.ify(raw);
console.log('hear:', msg);
hear(raw, peer);
(ref.hear || (ref.hear = [])).push(msg);
if(msg.put){ ++c }
}
ref.get(function(ack){
if(!ack.put || ack.put.hello !== 'world'){ return }
if(c > 1){ too_many_acks }
clearTimeout(to);
to = setTimeout(test.done, 1000);
});
}, {acks: config.servers});
});
it("DAM", function(){
return alice.run(function(test){
test.async();
if(ref.say){ said_too_much }
if(ref.hear.length > 1){ heard_to_much }
test.done()
}, {acks: config.servers});
});
it("All finished!", function(done){
console.log("Done! Cleaning things up...");
setTimeout(function(){
done();
},1000);
});
after("Everything shut down.", function(){
browsers.run(function(){
//location.reload();
//setTimeout(function(){
//}, 15 * 1000);
});
return servers.run(function(){
process.exit();
});
});
});

165
test/panic/2getget.js Normal file
View File

@ -0,0 +1,165 @@
/*
This is the first in a series of basic networking correctness tests.
Each test itself might be dumb and simple, but built up together,
they prove desired end goals for behavior at scale.
1. (this file) Is a browser write is confirmed as save by multiple peers even if by daisy chain.
2.
*/
var config = {
IP: require('ip').address(),
port: 8765,
servers: 1,
browsers: 3,
route: {
'/': __dirname + '/index.html',
'/gun.js': __dirname + '/../../gun.js',
'/jquery.js': __dirname + '/../../examples/jquery.js'
}
}
var panic = require('panic-server');
panic.server().on('request', function(req, res){
config.route[req.url] && require('fs').createReadStream(config.route[req.url]).pipe(res);
}).listen(config.port);
var clients = panic.clients;
var manager = require('panic-manager')();
manager.start({
clients: Array(config.servers).fill().map(function(u, i){
return {
type: 'node',
port: config.port + (i + 1)
}
}),
panic: 'http://' + config.IP + ':' + config.port
});
var servers = clients.filter('Node.js');
var bob = servers.pluck(1);
var browsers = clients.excluding(servers);
var alice = browsers.pluck(1);
var carl = browsers.excluding(alice).pluck(1);
var dave = browsers.excluding([alice, carl]).pluck(1);
var cd = new panic.ClientList([carl, dave]);
describe("Put ACK", function(){
//this.timeout(5 * 60 * 1000);
this.timeout(10 * 60 * 1000);
it("Servers have joined!", function(){
return servers.atLeast(config.servers);
});
it("GUN started!", function(){
var tests = [], i = 0;
servers.each(function(client){
tests.push(client.run(function(test){
var env = test.props;
test.async();
try{ require('fs').unlinkSync(env.i+'data') }catch(e){}
try{ require('gun/lib/fsrm')(env.i+'data') }catch(e){}
var server = require('http').createServer(function(req, res){
res.end("I am "+ env.i +"!");
});
var port = env.config.port + env.i;
var Gun = require('gun');
var peers = [], i = env.config.servers;
while(i--){
var tmp = (env.config.port + (i + 1));
if(port != tmp){ // ignore ourselves
peers.push('http://'+ env.config.IP + ':' + tmp + '/gun');
}
}
console.log(port, " connect to ", peers);
var gun = Gun({file: env.i+'data', peers: peers, web: server});
server.listen(port, function(){
test.done();
});
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
});
it(config.browsers +" browser(s) have joined!", function(){
console.log("PLEASE OPEN http://"+ config.IP +":"+ config.port +" IN "+ config.browsers +" BROWSER(S)!");
return browsers.atLeast(config.browsers);
});
it("Browsers initialized gun!", function(){
var tests = [], i = 0;
browsers.each(function(client, id){
tests.push(client.run(function(test){
try{ localStorage.clear() }catch(e){}
try{ indexedDB.deleteDatabase('radata') }catch(e){}
var env = test.props;
var gun = Gun('http://'+ env.config.IP + ':' + (env.config.port + 1) + '/gun');
window.gun = gun;
window.ref = gun.get('a');
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
});
it("connect", function(){
return alice.run(function(test){
console.log("I AM ALICE");
test.async();
gun.get('random').get(function(ack){
setTimeout(function(){
test.done();
}, 1000);
})
});
});
it("Put", function(){
return alice.run(function(test){
test.async();
var say = ref._.root.opt.mesh.say;
ref._.root.opt.mesh.say = function(){}; // prevent from syncing
var c = 0;
ref.put({hello: 'world'}, function(ack){ ++c });
setTimeout(function(){
ref._.root.opt.mesh.say = say;
if(c){ should_not_have_ack }
test.done();
}, 1000);
});
});
it("Get", function(){
return cd.run(function(test){
test.async();
console.log("I am Carl or Dave");
ref.get(function(ack){
console.log('ack', ack);
if(ack.put){
test.done();
}
});
});
});
it("All finished!", function(done){
console.log("Done! Cleaning things up...");
setTimeout(function(){
done();
},1000);
});
after("Everything shut down.", function(){
browsers.run(function(){
//location.reload();
//setTimeout(function(){
//}, 15 * 1000);
});
return servers.run(function(){
process.exit();
});
});
});

View File

@ -50,6 +50,8 @@ describe("The Holy Grail Test!", function(){
test.async();
try{ require('fs').unlinkSync(env.i+'data') }catch(e){}
try{ require('fs').unlinkSync((env.i+1)+'data') }catch(e){}
try{ require('gun/lib/fsrm')(env.i+'data') }catch(e){}
try{ require('gun/lib/fsrm')((env.i+1)+'data') }catch(e){}
var port = env.config.port + env.i;
var server = require('http').createServer(function(req, res){
res.end("I am "+ env.i +"!");
@ -104,7 +106,8 @@ describe("The Holy Grail Test!", function(){
return server.run(function(test){
console.log(3);
var env = test.props;
try{ require('fs').unlinkSync(env.i+'data'); }catch(e){}
try{ require('fs').unlinkSync(env.i+'data') }catch(e){}
try{ require('gun/lib/fsrm')(env.i+'data') }catch(e){}
process.exit(0);
}, {i: 1, config: config})
});
@ -176,6 +179,7 @@ describe("The Holy Grail Test!", function(){
var env = test.props;
test.async();
try{ require('fs').unlinkSync(env.i+'data') }catch(e){}
try{ require('gun/lib/fsrm')(env.i+'data') }catch(e){}
var port = env.config.port + env.i;
var server = require('http').createServer(function(req, res){
res.end("I am "+ env.i +"!");

View File

@ -3,9 +3,9 @@ var config = {
port: 8765,
servers: 2,
browsers: 3,
each: 1000000,
burst: 25,
wait: 25,
each: 100000, //1000000,
burst: 1,
wait: 1,
dir: __dirname,
chunk: 1024 * 1024 * 10,
notrad: false,

View File

@ -4,7 +4,7 @@ var config = {
servers: 1,
browsers: 2,
each: 2500,
burst: 25, // do not go below 1!
burst: 1, // do not go below 1!
wait: 1,
route: {
'/': __dirname + '/index.html',

102
test/rad/browser.html Normal file
View File

@ -0,0 +1,102 @@
<html>
<body>
<h1>RindexedDB</h1>
<script src="../../../gun/gun.js"></script>
<script src="../../../gun/lib/radix.js"></script>
<script src="../../../gun/lib/radisk.js"></script>
<script src="../../../gun/lib/store.js"></script>
<script src="../../../gun/lib/rindexed.js"></script>
<!-- script src="../../../gun/lib/rls.js"></script -->
<button onclick="spam()">spam</button>
<button onclick="read()">read</button>
<input id="wait" placeholder="wait" type="number">
<input id="burst" placeholder="burst" type="number">
<div id='debugs'></div>
<div id='debug'></div>
<script>
Gun.TESTING = true;
try{localStorage.clear()}catch(e){}
indexedDB.deleteDatabase('radatatest');
var opt = {localStorage: false};
//opt.store = RindexedDB(opt);
var gun = Gun(opt);
</script>
<script>
wait.onchange = function(){ spam.wait = this.value }
burst.onchange = function(){ spam.burst = this.value }
//setTimeout(spam, 1);
function spam(){
//spam.max = 100000; spam.left = spam.max; spam.wait = 1; spam.burst = 250; spam.c = 0; spam.s = (+new Date);
//spam.max = 100000; spam.left = spam.max; spam.wait = 1; spam.burst = 100; spam.c = 0; spam.s = (+new Date);
spam.max = 1000000; spam.left = spam.max; spam.wait = 1; spam.burst = 250; spam.c = 0; spam.s = (+new Date);
//spam.max = 100; spam.left = spam.max; spam.wait = 1; spam.burst = 1; spam.c = 0; spam.s = (+new Date);
var to = setInterval(function(){ var b = spam.burst;
if(spam.c >= spam.max){ clearTimeout(to); return; }
if(!b){ b = burst = 1 }
while(b--){ go(++spam.c) }
function go(i){ var d = 0;
//loc.put(i, {test: i}, function(err, ok){ var ack = {err: err, ok: ok};
//ind.put(i, {test: i}, function(err, ok){ var ack = {err: err, ok: ok};
var ref = gun.get(i).put({test: i}, function(ack){
if(ack.err){ console.log(ack); }
if(d++){ return }
if(--spam.left){ return }
spam.end = (+new Date) - spam.s;
console.log('DONE!\n', spam.max, 'in', spam.end/1000, 'seconds\n', Math.round(spam.max / (spam.end/1000)), 'per second.');
document.body.style.backgroundColor = 'lime';
});
}
},wait);
setInterval(function(){
if(spam.end === true){ return }
if(spam.end){ spam.end = true }
var t = (+new Date) - spam.s, tmp, sec;
var status = 'saved\n'+ (tmp = (spam.max - spam.left)) +' in '+ (sec = (t/1000)) +' seconds\n'+ Math.round(tmp / sec) +' per second';
debugs.innerText = status;
}, 500);
}
</script>
<script>
;(function(){
var f = 'index';
indexedDB.deleteDatabase(f);
var o = indexedDB.open(f, 1), ind = {}, db;
o.onupgradeneeded = function(eve){ (eve.target.result).createObjectStore(f) }
o.onsuccess = function(){ db = o.result }
o.onerror = function(eve){ console.log(eve||1); }
ind.put = function(key, data, cb){
if(!db){ setTimeout(function(){ ind.put(key, data, cb) },9); return }
var tx = db.transaction([f], 'readwrite');
var obj = tx.objectStore(f);
var req = obj.put(data, ''+key);
req.onsuccess = obj.onsuccess = tx.onsuccess = function(){ cb(null, 1) }
req.onabort = obj.onabort = tx.onabort = function(eve){ cb(eve||2) }
req.onerror = obj.onerror = tx.onerror = function(eve){ cb(eve||3) }
}
ind.get = function(key, cb){
if(!db){ setTimeout(function(){ ind.get(key, cb) },9); return }
var tx = db.transaction([f], 'readwrite');
var obj = tx.objectStore(f);
var req = obj.get(''+key);
req.onsuccess = function(){ cb(null, req.result) }
req.onabort = function(eve){ cb(eve||4) }
req.onerror = function(eve){ cb(eve||5) }
}
window.ind = ind;
}());
</script>
<script>
;(function(){
localStorage.clear();
var ls = localStorage, loc = {};
loc.put = function(key, data, cb){ ls[''+key] = data; cb(null, 1) }
loc.get = function(key, cb){ cb(null, ls[''+key]) }
window.loc = loc;
}());
</script>
</body>
</html>

10
test/rad/parse.rad Normal file
View File

@ -0,0 +1,10 @@
+1#"age:"+29>+1549776205172
+1#"name:""Bob!>+1549776205172
+1#"pet:"#XAqxAKkRa6lTsfAElEjDweqt>+1549776205172
+0#"u/m
+1#"
+2#"alice:"#dlgw87rue6oVQhsvc3XFLrOu>+1549776205172
+2#"bob:"#nuTAd2Tn4S5SiDVA7nxNBbZt>+1549776205172
+1#"/p
+2#"alice:"#USw3Dp7hTD7VMBLnd8dVBR4s>+1549776205200
+2#"bob:"#1VwZRUw7vQ1hX8gspN1ZrHVj>+1549776205200

349
test/rad/rad.js Normal file
View File

@ -0,0 +1,349 @@
var root;
var Gun;
(function(){
var env;
if(typeof global !== 'undefined'){ env = global }
if(typeof window !== 'undefined'){ env = window }
root = env.window? env.window : global;
try{ env.window && root.localStorage && root.localStorage.clear() }catch(e){}
try{ indexedDB.deleteDatabase('radatatest') }catch(e){}
if(root.Gun){
root.Gun = root.Gun;
root.Gun.TESTING = true;
} else {
try{ require('fs').unlinkSync('data.json') }catch(e){}
try{ require('../../lib/fsrm')('radatatest') }catch(e){}
root.Gun = require('../../gun');
root.Gun.TESTING = true;
//require('../lib/file');
require('../../lib/store');
require('../../lib/rfs');
}
try{ var expect = global.expect = require("../expect") }catch(e){}
}(this));
;(function(){
Gun = root.Gun
if(Gun.window && !Gun.window.RindexedDB){ return }
var opt = {};
opt.file = 'radatatest';
var Radisk = (Gun.window && Gun.window.Radisk) || require('../../lib/radisk');
opt.store = ((Gun.window && Gun.window.RindexedDB) || require('../../lib/rfs'))(opt);
opt.chunk = 1000;
var Radix = Radisk.Radix;
var rad = Radisk(opt), esc = String.fromCharCode(27);
describe('RAD', function(){
var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammamaria","Andy","Anselme","Ardeen","Armand","Ashelman","Aube","Averyl","Baker","Barger","Baten","Bee","Benia","Bernat","Bevers","Bittner","Bobbe","Bonny","Boyce","Breech","Brittaney","Bryn","Burkitt","Cadmann","Campagna","Carlee","Carver","Cavallaro","Chainey","Chaunce","Ching","Cianca","Claudina","Clyve","Colon","Cooke","Corrina","Crawley","Cullie","Dacy","Daniela","Daryn","Deedee","Denie","Devland","Dimitri","Dolphin","Dorinda","Dream","Dunham","Eachelle","Edina","Eisenstark","Elish","Elvis","Eng","Erland","Ethan","Evelyn","Fairman","Faus","Fenner","Fillander","Flip","Foskett","Fredette","Fullerton","Gamali","Gaspar","Gemina","Germana","Gilberto","Giuditta","Goer","Gotcher","Greenstein","Grosvenor","Guthrey","Haldane","Hankins","Harriette","Hayman","Heise","Hepsiba","Hewie","Hiroshi","Holtorf","Howlond","Hurless","Ieso","Ingold","Isidora","Jacoba","Janelle","Jaye","Jennee","Jillana","Johnson","Josy","Justinian","Kannan","Kast","Keeley","Kennett","Kho","Kiran","Knowles","Koser","Kroll","LaMori","Lanctot","Lasky","Laverna","Leff","Leonanie","Lewert","Lilybel","Lissak","Longerich","Lou","Ludeman","Lyman","Madai","Maia","Malvina","Marcy","Maris","Martens","Mathilda","Maye","McLain","Melamie","Meras","Micco","Millburn","Mittel","Montfort","Moth","Mutz","Nananne","Nazler","Nesta","Nicolina","Noellyn","Nuli","Ody","Olympie","Orlena","Other","Pain","Parry","Paynter","Pentheas","Pettifer","Phyllida","Plath","Posehn","Proulx","Quinlan","Raimes","Ras","Redmer","Renelle","Ricard","Rior","Rocky","Ron","Rosetta","Rubia","Ruttger","Salbu","Sandy","Saw","Scholz","Secor","September","Shanleigh","Shenan","Sholes","Sig","Sisely","Soble","Spanos","Stanwinn","Stevie","Stu","Suzanne","Tacy","Tanney","Tekla","Thackeray","Thomasin","Tilla","Tomas","Tracay","Tristis","Ty","Urana","Valdis","Vasta","Vezza","Vitoria","Wait","Warring","Weissmann","Whetstone","Williamson","Wittenburg","Wymore","Yoho","Zamir","Zimmermann"];
//console.log("HYPER TEST");var z = 10000; while(--z){ names.push(Gun.text.random(7)) }this.timeout(9000);
describe('Radix', function(){
var radix = Radix();
it('radix write read', function(done){
var all = {};
names.forEach(function(v,i){
v = v.toLowerCase();
all[v] = v;
radix(v, i)
});
expect(Gun.obj.empty(all)).to.not.be.ok();
Radix.map(radix, function(v,k){
delete all[k];
});
expect(Gun.obj.empty(all)).to.be.ok();
done();
});
it('radix write read again', function(done){
var all = {};
names.forEach(function(v,i){
v = v.toLowerCase();
all[v] = v;
//rad(v, i)
});
expect(Gun.obj.empty(all)).to.not.be.ok();
Radix.map(radix, function(v,k){
delete all[k];
});
expect(Gun.obj.empty(all)).to.be.ok();
done();
});
it('radix read start end', function(done){
var all = {}, start = 'Warring'.toLowerCase(), end = 'Zamir'.toLowerCase();
names.forEach(function(v,i){
v = v.toLowerCase();
if(v < start){ return }
if(end < v){ return }
all[v] = v;
//rad(v, i)
});
expect(Gun.obj.empty(all)).to.not.be.ok();
Radix.map(radix, function(v,k, a,b){
//if(!all[k]){ throw "out of range!" }
delete all[k];
}, {start: start, end: end});
expect(Gun.obj.empty(all)).to.be.ok();
done();
});
it('radix read start- end+', function(done){
var all = {}, start = 'Warrinf'.toLowerCase(), end = 'Zamis'.toLowerCase();
names.forEach(function(v,i){
v = v.toLowerCase();
if(v < start){ return }
if(end < v){ return }
all[v] = v;
//rad(v, i)
});
expect(Gun.obj.empty(all)).to.not.be.ok();
Radix.map(radix, function(v,k, a,b){
//if(!all[k]){ throw "out of range!" }
delete all[k];
}, {start: start, end: end});
expect(Gun.obj.empty(all)).to.be.ok();
done();
});
it('radix reverse', function(done){
var r = Radix(), tmp;
r('alice', 1);r('bob', 2);r('carl', 3);r('carlo',4);
r('dave', 5);r('zach',6);r('zachary',7);
var by = ['alice','bob','carl','carlo','dave','zach','zachary'];
Gun.obj.map(by, function(k,i){
r(k,i);
});
Radix.map(r, function(v,k, a,b){
expect(by.pop()).to.be(k);
tmp = v;
}, {reverse: 1});
expect(tmp).to.be(1);
expect(by.length).to.be(0);
Radix.map(r, function(v,k, a,b){
tmp = v;
});
expect(tmp).to.be(7);
done();
});
});
describe('Radisk', function(){
/*it('parse', function(done){
this.timeout(60000);
if(Gun.window){ return done() }
var raw = require('fs').readFileSync(__dirname + '/parse.rad').toString();
rad.parse('!', function(err, disk){
console.log("!!!!", err);
}, raw);
return;
});*/
it('write contacts', function(done){
var all = {}, to, start;
names.forEach(function(v,i){
v = v.toLowerCase();
all[v] = true;
rad(v, i, function(err, ok){
expect(err).to.not.be.ok();
delete all[v];
if(!Gun.obj.empty(all)){ return }
done();
})
})
});
/*it('read contacts reverse', function(done){
var opt = {};
opt.reverse = true;
opt.end = 'nothing';
opt.start = 'marcy';
var first, last;
rad('', function(err, data){
console.log("???", err, data);
return;
Radix.map(data, function(v,k){
console.log(k, v);
//delete all[find+k];
});
//if(!Gun.obj.empty(all)){ return }
//done();
}, opt);
});
console.log("UNDO THIS RETURN!!!");return;*/
it('read contacts start end', function(done){
var opt = {};
opt.start = 'Warring'.toLowerCase();
opt.end = 'Zamir'.toLowerCase();
var all = {}, find = '';
names.forEach(function(v){
v = v.toLowerCase();
if(v < opt.start){ return }
if(opt.end < v){ return }
if(v.indexOf(find) == 0){ all[v] = true }
});
rad(find, function(err, data){
Radix.map(data, function(v,k){
//console.log(find+k, v);
delete all[find+k];
});
if(!Gun.obj.empty(all)){ return }
done();
}, opt);
});
it('read contacts', function(done){
var all = {}, find = 'a';
names.forEach(function(v){
v = v.toLowerCase();
if(v.indexOf(find) == 0){ all[v] = true }
});
rad(find, function(err, data){
//console.log(">>>>>>>>> KUNG FOO PANDA <<<<<<<<<<<");
//console.debug.i=1;console.log(data);
Radix.map(data, function(v,k){
delete all[find+k];
});
if(!Gun.obj.empty(all)){ return }
done();
});
});
it('read again', function(done){
var all = {}, find = 'm';
names.forEach(function(v){
v = v.toLowerCase();
if(v.indexOf(find) == 0){ all[v] = true }
});
rad(find, function(err, data, info){
Radix.map(data, function(v,k){
delete all[find+k];
});
if(!Gun.obj.empty(all)){ return }
done();
});
});
it('read bytes', function(done){
var all = {}, find = 'm', to;
names.forEach(function(v){
v = v.toLowerCase();
if(v.indexOf(find) == 0){ all[v] = true }
});
rad(find, function(err, data, info){
Radix.map(data, function(v,k){
delete all[find+k];
});
clearTimeout(to);
to = setTimeout(function(){
expect(Gun.obj.empty(all)).to.not.be.ok();
done();
},100);
}, {limit: 1});
});
});
var ntmp = names;
describe('RAD + GUN', function(){
var ochunk = 1000;
var gun = Gun({chunk: ochunk});
it('write same', function(done){
var all = {}, to, start, tmp;
var names = [], c = 285;
while(--c){ names.push('bob') }
names.forEach(function(v,i){
all[++i] = true;
tmp = v.toLowerCase();
gun.get('names').get(tmp).put({name: v, age: i}, function(ack){
expect(ack.err).to.not.be.ok();
delete all[i];
if(!Gun.obj.empty(all)){ return }
done();
})
});
});
it('write contacts', function(done){
var all = {}, to, start, tmp;
names.forEach(function(v,i){
all[++i] = true;
tmp = v.toLowerCase();
gun.get('names').get(tmp).put({name: v, age: i}, function(ack){
expect(ack.err).to.not.be.ok();
delete all[i];
if(!Gun.obj.empty(all)){ return }
done();
})
})
});
it('read contacts', function(done){
var all = {}, find = 'm', to;
names.forEach(function(v){
v = v.toLowerCase();
if(v.indexOf(find) == 0){ all[v] = true }
});
gun.get('names').get({'.': {'*': find}, '%': 1000 * 100}).once().map().once(function(data, key){
expect(data.name).to.be.ok();
expect(data.age).to.be.ok();
delete all[key];
clearTimeout(to);
to = setTimeout(function(){
expect(Gun.obj.empty(all)).to.be.ok();
done();
},100);
});
});
it('read contacts again', function(done){
var all = {}, find = 'a', to;
names.forEach(function(v){
v = v.toLowerCase();
if(v.indexOf(find) == 0){ all[v] = true }
});
gun.get('names').get({'.': {'*': find}, '%': 1000 * 100}).once().map().once(function(data, key){
expect(data.name).to.be.ok();
expect(data.age).to.be.ok();
delete all[key];
clearTimeout(to);
to = setTimeout(function(){
expect(Gun.obj.empty(all)).to.be.ok();
done();
},100);
});
});
it('read contacts fresh', function(done){
var gun = Gun({chunk: ochunk});
var all = {}, find = 'b', to;
names.forEach(function(v){
v = v.toLowerCase();
if(v.indexOf(find) == 0){ all[v] = true }
});
gun.get('names').get({'.': {'*': find}, '%': 1000 * 100}).once().map().once(function(data, key){
expect(data.name).to.be.ok();
expect(data.age).to.be.ok();
delete all[key];
clearTimeout(to);
to = setTimeout(function(){
expect(Gun.obj.empty(all)).to.be.ok();
done();
},100);
});
});
});
});
}());

View File

@ -10,7 +10,7 @@ describe('Radix', function(){
rad('ablah', 'cool');
rad('node/circle.bob', 'awesome');
expect(rad('asdf.')).to.be.eql({pub: {'\u001e': 'yum'}});
expect(rad('asdf.')).to.be.eql({pub: {'': 'yum'}});
expect(rad('nv/foo.bar')).to.be(undefined);
});
});

View File

@ -6,23 +6,23 @@ var Gun;
if(typeof window !== 'undefined'){ env = window }
root = env.window? env.window : global;
try{ env.window && root.localStorage && root.localStorage.clear() }catch(e){}
try{ require('fs').unlinkSync('data.json') }catch(e){}
//root.Gun = root.Gun || require('../gun');
try{ indexedDB.deleteDatabase('radatatest') }catch(e){}
if(root.Gun){
root.Gun = root.Gun;
root.Gun.TESTING = true;
} else {
try{ require('fs').unlinkSync('data.json') }catch(e){}
try{ require('../../lib/fsrm')('radatatest') }catch(e){}
root.Gun = require('../../gun');
root.Gun.TESTING = true;
//require('../lib/file');
require('../lib/store');
require('../lib/rfs');
require('../../lib/store');
require('../../lib/rfs');
}
if(root.Gun.SEA){
//Gun = root.Gun = root.Gun;
} else {
var expect = global.expect = require("../expect");
try{ var expect = global.expect = require("../expect") }catch(e){}
if(!root.Gun.SEA){
require('../../sea.js');
}
}(this));
@ -296,12 +296,15 @@ describe('SEA', function(){
})
it('refresh login', function(done){
gun = Gun();
user = gun.user();
user.auth('carl', 'test123', function(ack){
expect(ack.err).to.not.be.ok();
done()
})
this.timeout(9000);
setTimeout(function(){
gun = Gun();
user = gun.user();
user.auth('carl', 'test123', function(ack){
expect(ack.err).to.not.be.ok();
done()
})
}, 800);
})
it('gun put JSON', function(done){
@ -361,4 +364,4 @@ describe('SEA', function(){
})
})()
}());

Some files were not shown because too many files have changed in this diff Show More