Merge branch 'rnsupport' of https://github.com/gooddollar/gun into rnsupport

This commit is contained in:
Hadar Rottenberg 2020-01-07 15:32:30 +02:00
commit 0c27b81993
45 changed files with 4223 additions and 2444 deletions

2
.gitignore vendored
View File

@ -10,4 +10,4 @@ yarn.lock
*.DS_store
.esm-cache
.sessionStorage
.localStorage
.localStorage

View File

@ -116,7 +116,8 @@ Thanks to:<br/>
<a href="https://github.com/dfreire">Dário Freire</a>,
<a href="http://github.com/velua">John Williamson</a>,
<a href="http://github.com/finwo">Robin Bron</a>,
<a href="http://github.com/ElieMakhoul">Elie Makhoul</a>
<a href="http://github.com/ElieMakhoul">Elie Makhoul</a>,
<a href="http://github.com/mikestaub">Mike Staub</a>
</p>
- Join others in sponsoring code: https://www.patreon.com/gunDB !
@ -193,7 +194,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 text-encoding node-webcrypto-ossl --save`
`npm install text-encoding @peculiar/webcrypto --save`
You will need to require it too (it will be automatically added to the Gun object):

52
axe.js
View File

@ -60,6 +60,20 @@
// with one common superpeer (with ready failovers)
// in case the p2p linear latency is high.
// Or there could be plenty of other better options.
/*
AXE should have a couple of threshold items...
let's pretend there is a variable max peers connected
mob = 10000
if we get more peers than that...
we should start sending those peers a remote command
that they should connect to this or that other peer
and then once they (or before they do?) drop them from us.
sake of the test... gonna set that peer number to 1.
The mob threshold might be determined by other factors,
like how much RAM or CPU stress we have.
*/
opt.mob = opt.mob || Infinity;
var mesh = opt.mesh = opt.mesh || Gun.Mesh(at);
console.log("AXE enabled.");
@ -256,7 +270,7 @@
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){
@ -266,6 +280,42 @@
}
});
});
// handle rebalancing a mob of peers:
at.on('hi', function(peer){
this.to.next(peer);
if(peer.url){ return } // I am assuming that if we are wanting to make an outbound connection to them, that we don't ever want to drop them unless our actual config settings change.
var count = Object.keys(opt.peers).length;
if(opt.mob >= count){ return } // TODO: Make dynamic based on RAM/CPU also. Or possibly even weird stuff like opt.mob / axe.up length?
var peers = Object.keys(axe.up);
if(!peers.length){ return }
mesh.say({dam: 'mob', mob: count, peers: peers}, peer);
//setTimeout(function(){ mesh.bye(peer) }, 9); // something with better perf? // UNCOMMENT WHEN WE ACTIVATE THIS FEATURE
});
at.on('bye', function(peer){
this.to.next(peer);
});
at.on('hi', function(peer){
this.to.next(peer);
// this code handles disconnecting from self & duplicates
setTimeout(function(){ // must wait
if(peer.pid !== opt.pid){
// this extra logic checks for duplicate connections between 2 peers.
if(!Gun.obj.map(axe.up, function(p){
if(peer.pid === p.pid && peer !== p){
return yes = true;
}
})){ return }
}
mesh.say({dam: '-'}, peer);
delete at.dup.s[peer.last];
}, Math.random() * 100);
});
mesh.hear['-'] = function(msg, peer){
mesh.bye(peer);
peer.url = '';
}
}
function joindht(dht, soul, pids) {

22
examples/basic/paste.html Normal file
View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<style>html, body, textarea { width: 100%; height: 100%; padding: 0; margin: 0; }</style>
<textarea placeholder="paste here!"></textarea>
<script src="../jquery.js"></script>
<script src="../../../gun/gun.js"></script>
<script>
var gun = Gun(location.origin + '/gun');
var to, paste = gun.get('test').get('paste').on(function(data){
$('textarea').val(data);
})
$('textarea').on('input change blur keyup mouseup touchend', function(){
clearTimeout(to); // debounce
to = setTimeout(function(){
paste.put($('textarea').val());
}, 100);
})
</script>

View File

@ -1,4 +1,4 @@
<html><body>
<!DOCTYPE html>
<style>
html, body {
background: rgb(245, 245, 245);
@ -136,5 +136,4 @@ $('#search').on('blur', function(e){
});
});
});
</script>
</body></html>
</script>

View File

@ -5,6 +5,7 @@
<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">

View File

@ -0,0 +1,59 @@
<!DOCTYPE html>
<video id="video" width="100%"></video>
<center>
<button id="record">Record</button>
<button id="play">Play</button>
</center>
<script src="../jquery.js"></script>
<script src="../../../gun/gun.js"></script>
<script>
var gun = Gun(location.origin + '/gun');
var record = {recorder: null, recording: false};
$('#record').on('click', ()=>{
if(!record.ing){ return record.stream() }
$('#record').text("Record");
if(record.ing.stop){ record.ing.stop() }
record.ing = false;
})
record.stream = function(){
navigator.mediaDevices.getDisplayMedia({ video: true }).then(stream => {
var chunks = []; // we have a stream, we can record it
record.ing = new MediaRecorder(stream);
record.ing.ondataavailable = eve => chunks.push(eve.data);
record.ing.onstop = eve => record.save(new Blob(chunks));
record.ing.start()
$('#record').text("End");
}, err => { console.log(err) });
}
record.save = function(data){
record.file = record.file || new FileReader();
record.file.readAsDataURL(data);
record.file.onloadend = function(){
var b64 = record.file.result;
b64 = "data:video/webm" + b64.slice(b64.indexOf(';'));
gun.get('test').get('screen').put(b64);
}
}
$('#play').on('click', ()=>{
if(record.playing){
$('#play').text("Play")
$('#video').get(0).stop();
record.playing = false;
return;
}
$('#play').text("Stop");
record.playing = true;
gun.get('test').get('screen').once((data)=>{
if(!data){ return }
$('#video').get(0).src = data;
$('#video').get(0).play()
})
})
</script>

View File

@ -1,50 +0,0 @@
<h1>Search</h1>
<form id="ask">
<input id="search" placeholder="search..." autocomplete="off">
</form>
<div id="answer"></div>
<ul></ul>
<small>Note: No data is indexed by default, you need to add some!</small>
<script src="../../examples/jquery.js"></script>
<script src="../../gun.js"></script>
<script src="../../sea.js"></script>
<script src="../../lib/space.js"></script>
<script>
var gun = Gun();
var ask = {};
$('#search').on('keyup', function(e){
ask.now = (this.value||'').toLowerCase().replace(/[\W_]+/g,"");
if(ask.last === ask.now){ return }
ask.last = ask.now;
clearTimeout(ask.to);
ask.to = setTimeout(search, 20);
});
function search(){
var key = ask.now;
gun.get('Q').space(key, function(ack){
if(!ack || key !== ask.now){ return }
UI(ack)
});
}
function UI(ack){
$('#answer').text(ack.data || '');
var $ul = $('ul').empty(), tree = ack.tree;
Gun.obj.map(tree, function(v,k){
$('<li>').text(k +' - ' + v).appendTo($ul);
});
};
function load(DATA){
Gun.obj.map(DATA, function(v,k){
gun.get('Q').space(k, v);
});
}
</script>

View File

@ -1,3 +1,5 @@
<!DOCTYPE html>
<h1>Tables</h1>
<form id="sign">

View File

@ -1,3 +1,5 @@
<!DOCTYPE html>
<h1>User</h1>
<form id="sign">
@ -11,8 +13,8 @@
<ul></ul>
<form id="said">
<input id="say">
<input id="speak" type="submit" value="speak">
<input id="say">
<input id="speak" type="submit" value="speak">
</form>
<script src="../jquery.js"></script>
@ -27,19 +29,19 @@ $('#up').on('click', function(e){
user.create($('#alias').val(), $('#pass').val(), login);
});
function login(e){
user.auth($('#alias').val(), $('#pass').val());
user.auth($('#alias').val(), $('#pass').val());
return false; // e.preventDefault();
};
$('#sign').on('submit', login);
$('#mask').on('click', login);
gun.on('auth', function(){
$('#sign').hide();
user.get('said').map().on(UI);
$('#sign').hide();
user.get('said').map().on(UI);
});
$('#said').on('submit', function(e){
e.preventDefault();
e.preventDefault();
//if(!user.is){ return }
user.get('said').set($('#say').val());
$('#say').val("");

241
examples/docs.html Normal file
View File

@ -0,0 +1,241 @@
<!DOCTYPE html>
<html>
<head>
<!-- always start with these two lines to set a clean baseline for different devices -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="style.css">
<script src="jquery.js"></script>
<title>Docs</title>
</head>
<body class="black whitet">
<style>
/*
Choose white text on a black background so you can add color in.
Pick your favorite font and choose a font size.
*/
@import url('https://fonts.googleapis.com/css?family=Oxygen');
html, body {
font-family: "Oxygen", sans-serif;
}
[contenteditable]:focus {
outline: none;
}
</style>
<div class="hold full hue2">
<div id="page" class="max focus gap" style="padding-top: 9%;"></div>
</div>
<script src="../../gun/gun.js"></script>
<script src="../../gun/lib/monotype.js"></script>
<script src="../../gun/lib/meta.js"></script>
<script src="../../gun/lib/normalize.js"></script>
<script async src="../../gun/lib/fun.js"></script>
<script async src="../../gun/lib/wave.js"></script>
<!-- script async src="https://edide.io/music.lib"></script -->
<script>
var gun = Gun(['https://guntest.herokuapp.com/gun', 'http://localhost:8765/gun']);
;(window.onhashchange = function(){
var file = (location.hash||'').slice(1);
var S = +new Date;
$('#page').empty().attr('contenteditable', 'false');
gun.get('test/gun/docs/'+file).get('what').map().on(function render(data, i){
if(window.LOCK){ return }
var p = $('#page').children().get(i);
if(!p){
$('#page').append('<p>');
setTimeout(function(){ render(data, i) },0);
return;
}
var r = monotype(p);
var safe = $.normalize(data);
p.outerHTML = safe;
r.restore();
});
})();
document.execCommand('defaultParagraphSeparator', false, 'p');
meta.edit({
name: "Edit",
combo: ['E'],
use: function(eve){
console.log('on');
}, on: function(eve){
if($(eve.target).closest('p').length){ return }
var edit = this;
setTimeout(function(){ meta.flip(false) },1);
edit.init();
$(document).on('keydown.tmp', '[contenteditable]', function(eve){
if(eve.which != 13){ return }
eve.preventDefault();
var r = window.getSelection().getRangeAt(0);
var c = r.commonAncestorContainer, p;
r.deleteContents();
var p = c.splitText? $(c.splitText(r.startOffset)).parent() : $(c);
var n = $("<"+p.get(0).tagName+">"), f;
p.contents().each(function(){
if(this === c){ return f = true }
if(!f){ return }
n.append(this);
});
p.after(n);
edit.select(n.get(0));
// make sure we re-save & sync each changed paragraph.
edit.save(p);
p.nextAll().each(function(){
edit.save(this);
});
}).on('keyup.tmp', '[contenteditable]', function(eve){
//$('#debug').val(doc.html());
var p = $(window.getSelection().anchorNode).closest('p');
var r = monotype(p);
var html = p.html() || '';
if(!html && !p.prev().length && !p.next().length && !$('#page').html()){
edit.init();
}
var safe = $.normalize(html);
p.html(safe);
r.restore();
edit.save(p);
});
},
up: function(){
console.log("UP");
$('[contenteditable=true]').off('.tmp');
},
init: function(){
var edit = this;
var doc = $('#page').attr('contenteditable', 'true');
if(!doc.text()){
doc.html('<p class="loud crack"></p>');
}
edit.select(doc.children().first().get(0));
},
save: function(p){
p = $(p);
var i = p.index();// = Array.prototype.indexOf.call(parent.children, child);
var file = (location.hash||'').slice(1);
var data = (p.get(0)||{}).outerHTML||'';
window.LOCK = true;
gun.get('test/gun/docs/'+file).get('what').get(i).put(data);
window.LOCK = false;
},
select: function(p){
var s = window.getSelection(),
r = document.createRange();
if(p.innerHTML){
r.setStart(p, 0);
r.collapse(true);
s.removeAllRanges();
s.addRange(r);
return;
}
p.innerHTML = '\u00a0';
r.selectNodeContents(p);
s.removeAllRanges();
s.addRange(r);
document.execCommand('delete', false, null);
}
});
;(function(){
meta.edit({name: "Layout", combo: ['L']});
meta.edit({name: "Fill", combo: ['L','F'],
use: function(eve){},
on: function(eve){
var on = meta.tap();
meta.ask('Color name, code, or URL?', function(color){
on.css('background', color);
});
},
up: function(eve){}
});
meta.edit({name: "Add", combo: ['L','A']});
meta.edit({name: "Row", combo: ['L','A', 'R'],
on: function(eve){
meta.tap().append('<div style="min-height: 9em; padding: 2%;">');
}
});
meta.edit({name: "Columns", combo: ['L','A','C'],
on: function(eve){
var on = meta.tap().addClass('center'), 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)+'%');
})
}
});
meta.edit({name: "Text", combo: ['L','A','T'],
on: function(eve){
var tag = $('<p>text</p>');
meta.tap().append(tag);
tag.focus();
}
});
}());
;(function(){
var song = {};
// TODO:
// 1. Manually OR automatically load music.js API, dependencies, and modules. - FINE for now
// 2. only export music API, not meta, not dom, not mouselock system, not UI/html, etc. better module isolation and export.
// 3. `var wave = Wave('a').play()` // also on `Music.now`
// defaults... instrument: pure tones, volume curve: |\_ , speed curve: 0.5
// 4. `wave.blur(0.5).itch(0.5);`
// 5. wave.long(2); // how long in seconds each note plays, optionally: wave.pace(60) is bpm
// 6. wave.loud(0.5); // 0% to 100% volume loudness of device output.
// 7. wave.vary(0.5); // slows down or speeds up wiggle per harmonic
// 8:
// wave structure, does ToneJS allow us to change the sine wave smoothness/type continuously or is it a pre-fixed type?
// wave structure: /\/\/, |_|, /|/, \|\| do some research with ToneJS whether these are dynamic or fixed
// wave.itch(); // changes the shape of the wiggle from smooth sine to square or triangle
// wave.blur(220hz); // blur may not apply/work on pure notes other than filtering them.
meta.edit({name: "Music", combo: ['M']});
meta.edit({name: "Play", combo: ['M','P'],
on: function(eve){
// TODO: We still need to add to meta API ability to change name.
if(song.play){
music.stop();
song.play = false;
return;
}
song.play = true;
music.stop();
setTimeout(function(){
song.now = wave($('#page').text()).play();
},250);
}
});
meta.edit({name: "Blur", combo: ['M','B'],
on: function(eve){
$(document).on('mousemove.tmp', function(eve){
var x = eve.pageX;
song.now.loud(x/$('body').innerWidth());
});
},
up: function(){
$(document).off('.tmp');
}
});
$(document).on('keydown', function(eve){
music.play(String.fromCharCode(eve.which));
});
}());
</script>
</body>
</html>

439
examples/game/furball.html Normal file
View File

@ -0,0 +1,439 @@
<!DOCTYPE html>
<html>
<head>
<!-- always start with these two lines to set a clean baseline for different devices -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="../style.css">
<!-- link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/gun/examples/style.css" -->
<script src="https://cdn.jsdelivr.net/npm/gun/examples/jquery.js"></script>
<title>Furball</title>
</head>
<body class="black whitet">
<style>
/*
Choose white text on a black background so you can add color in.
Pick your favorite font and choose a font size.
*/
@import url('https://fonts.googleapis.com/css?family=Mali');
html, body {
font-family: "Mali", sans-serif;
}
.huef {
background: #4D79D8;
-webkit-animation: huef 9s infinite;
animation: huef 9s infinite;
} @keyframes huef {
0% {background-color: #4D79D8;}
25% {background-color: #33cc33;}
50% {background-color: #f2b919;}
75% {background-color: #ea3224;}
100% {background-color: #4D79D8;}
} @-webkit-keyframes huef {
0% {background-color: #4D79D8;}
25% {background-color: #33cc33;}
50% {background-color: #f2b919;}
75% {background-color: #ea3224;}
100% {background-color: #4D79D8;}
}
button, input {
padding: 1em;
background: transparent;
border: 1px solid white;
border-radius: 1.5em;
color: white;
margin: 0.5em;
margin-bottom: 0;
cursor: pointer;
}
button:hover, input:hover {
background: white;
color: black;
transform: scale(1.1);
}
.air { padding-top: 9%; }
.yak button { font-size: 80%; }
.wag {
-webkit-animation: wag 3s infinite;
animation: wag 3s infinite;
} @keyframes wag {
0% {transform: rotate(0deg);}
50% {transform: rotate(-1deg);}
100% {transform: rotate(0deg);}
}
@keyframes print {
0% { overflow: hidden; height: 0vh; }
99% { overflow: hidden; height: 100vh; }
100% { overflow: visible; height: auto; }
}
input {
outline: none;
}
</style>
</style>
<!-- for educational sites, consider starting with a nice full screen welcome message -->
<div class="home hold full huef center air">
<div class="focus row">
<p><i>Neon ERA presents</i></p>
<p class="shout wag">Furball Forces</p>
<!-- just like in real life, say who you are and give a concise reason why you add value to someone's life and then make a call to action, if they want to learn more they can always scroll to learn more -->
<div>
<!-- a class="unit hold" href="#fullscreen"><button>WATCH TRAILER</button></a -->
<a class="unit yak" href="#choose"><button>PLAY GAME</button></a>
</div>
</div>
<div class="focus center row leak">
<!-- just like in real life, looking pretty attracts attention, so show off and look glamorous! -->
<img class="unit blink" src="file:///Users/mark/Downloads/supercatdog.png" style="min-width: 10em; width: 80%;">
</div>
<script>location.hash = ''</script>
<script src="https://cdn.jsdelivr.net/npm/gun/lib/fun.js"></script>
<script>;(function(){
// OPTIONAL MUSIC:
$('.home button').on('click', function(){
if(window.screen.height > window.screen.width){ return }
$('body').append("<div id='audio' onclick='$(this).remove();'><iframe width='0' height='0' src='https://www.youtube-nocookie.com/embed/LLPoZGX0qZk?autoplay=1' frameborder='0'></iframe></div>");
})
}());
</script>
<style>#audio { padding: 0.5em; position: fixed; bottom: 0; left: 0; } #audio:before { content: '\25BA'; } #audio:hover:before { content: '\25FC'; }</style>
</div>
<div id="choose" class="hold full hue4 center air">
<div class="focus row">
<p class="shout wag fur">Choose Team:</p>
<div>
<a class="unit yak" href="#automecha"><button style="background: white; color: black;">#AutoMecha</button></a>
<a class="unit yak" href="#cyberninjas"><button style="background: black; color: white; border-color: black;">#CyberNinjas</button></a>
</div>
</div>
<div class="focus center row leak">
<img class="unit blink" src="file:///Users/mark/Downloads/supercatdog.png" style="transform: scaleX(-1); filter: invert(1); min-width: 10em; width: 80%;">
</div>
</div>
<div id="cyberninjas" class="hold full black">
<style>
#cyberninjas:target .story {
animation: print 3s steps(50, end);
}
</style>
<div class="story pad">
<p class="loud crack">Episode 1: Waking</p>
<p>"How long until they're online?"</p>
<p>"We're copying the soul files, almost done."</p>
<p>"Monsters are on the bridge, we do not have time!"</p>
<p>"The new body is printing now, it'll be able to outrun them all, just hold on."</p>
<p>"It won't know where to run! We're risking ruining the whole resistance, I need to talk to it now."</p>
<p>"95% done." The voice behind the glass turns to the soul in the body, "My cub, can you hear me?"</p>
<p>...</p>
<a class="unit yak" href="#cyberninjas2"><button>Reply "Yes, Mom?"</button></a>
</div>
<script>
;(function(){
$('#cyberninjas a').on('click', function(){
$('#hud .life').removeClass('down');
});
}());
</script>
</div>
<div id="cyberninjas2" class="hold full red">
<style>
#cyberninjas2:target .story {
animation: print 3s steps(50, end);
}
#hud {
opacity: 0.4;
font-family: 'Audiowide', cursive;
z-index: 999999999999;
transition: all 3s;
}
#hud .life {
position: fixed;
left: 50%;
bottom: 0px;
padding: 0.25em 1em 0.1em;
border-radius: 0.5em 0.5em 0 0;
transform: translateX(-50%);
background: black;
text-shadow: 0em -0.125em 0.75em white;
}
#hud .score {
position: fixed;
left: 50%;
top: 0px;
padding: 0.1em 1em 0.25em;
border-radius: 0 0 0.5em 0.5em;
transform: translateX(-50%);
background: black;
text-shadow: 0em 0.1em 0.75em white;
}
#hud .down {
bottom: -2em !important;
}
#hud .up {
top: -2em !important;
}
</style>
<div id="hud">
<div class="score shade up">
SCORE: <span id="hudscore">0</span>%
</div>
<div class="life shade down">
LIFE: <span id="hudlife">50</span>%
</div>
</div>
<div class="story pad">
<p>A fire explodes in the room behind the glass as an AutoMecha blows the door open.</p>
<p>The floor shakes and the bed crashes through the wall, flying out of the building.</p>
<p class="center">"Mom!!!"</p>
<p>There is a total free fall from 10 levels up, water down below.</p>
<p>...</p>
<a class="unit yak" href="#cyberninjas3"><button>Dive or Die</button></a>
</div>
<script>
;(function(){
$('#cyberninjas2 a').on('click', function(){
$('#hudlife').text($('#hudlife').data().is = 50);
});
}());
</script>
</div>
<div id="cyberninjas3" class="hold full blue">
<style>
@import url('https://fonts.googleapis.com/css?family=Audiowide');
#cyberninjas3:target .story {
animation: print 3s steps(50, end);
}
</style>
<div class="story pad">
<p>The water splashes, swelling and swirling all around.</p>
<p>...</p>
<p class="center">Rapidly tap to swim up to air:</p>
<a class="unit yak"><button>Swim</button></a>
</div>
<script>
;(function(){
var go, life = $('#hudlife').data();
$('#cyberninjas3 a').on('click', function(){
$('#hudlife').text(life.is += 5);
if(100 <= life.is){
location.hash = 'cyberninjas4';
clearInterval(go);
go = false;
return;
}
if(go){ return }
go = setInterval(function(){
if(0 >= life.is){
location.hash = 'cyberninjas2';
$('#hudlife').text(life.is = 50);
clearInterval(go);
go = false;
return;
}
$('#hudlife').text(life.is -= 5);
}, 1000); // 1 second
});
}());
</script>
</div>
<div id="cyberninjas4" class="hold full black">
<style>
#cyberninjas4:target .story {
animation: print 3s steps(50, end);
}
</style>
<div class="story pad">
<p class="loud crack">Episode 2: Who Am I?</p>
<p>"Grab on!" A voice calls out from the darkness.</p>
<p>A life vest hits the water and floats within arm's distance.</p>
<p>The shivering body is pulled up onto the boat.</p>
<p>"Wow, you're heavier than you look. Are you OK? What's your name?"</p>
<p>...</p>
<p class="center">Write your reply & hit enter:</p>
<form class="center">
<input class="loud" style="width: 60%;">
</form>
</div>
<script>
;(function(){
$('form').on('submit', function(eve){ eve.preventDefault() });
$('#cyberninjas4').on('submit', function(){
var name = $(this).find('input').val();
if(!name.length){ return }
$('.story-name').text(' '+(window.NAME = name));
$('#hud .score').removeClass('up');
location.hash = 'cyberninjas5';
})
}());
</script>
</div>
<div id="cyberninjas5" class="hold full green">
<style>
#cyberninjas5:target .story {
animation: print 3s steps(50, end);
}
</style>
<div class="story pad">
<p>"Well<span class="story-name"></span>, it's a miracle you did not die in the building explosion or from that fall."</p>
<p>"What is going on? What happened?"</p>
<p>"You can't remember? Your brain must be knocked up pretty hard."</p>
<p>"No, I was mid copy into this body and now my memories are glitching."</p>
<p>"Woah, you're one of those pro elite AREION revolutionaries? All flesh & blood! Dense, too. I would've assumed they were stealing AutoMecha tech for that instead."</p>
<p>"I was about to be told vital data for the resistance, but then they blew up the build--"</p>
<p>...</p>
<p>"Hey, what's the matter?"</p>
<p>"My mom. She was in there. I need to go back. Please, help me and tell me everything you know."</p>
<p>"I'm so sorry. I can only get so close with the boat, you're gonna have to jump over a lot of broken bits. You ready?"</p>
<p>...</p>
<a class="unit yak" href="#cyberninjas6"><button>GO!</button></a>
</div>
</div>
<div id="cyberninjas6" class="hold full green">
<style>
#cyberninjas6:target .story {
animation: print 3s steps(50, end);
}
</style>
<div class="story pad">
<p class="center">... to be continued ...</p>
<div id="player" style="position: fixed; width: 1em; height: 1em; background: white; left: 50%; top: 50%; border-radius: 100%;"></div>
<!-- jumping game ? like offline dinosaur ? -->
</div>
<script src="../../../gun/lib/meta.js"></script>
<script>
;(function(){
var p = $('#player');
p.x = 50;
p.y = 50;
meta.edit({
name: "Up",
combo: ["W"],
on: function(){
console.log("up");
this.to = this.to || setInterval(this.on, 100);
$("html, body").stop().animate({ scrollTop: $(window).scrollTop()-100 }, 100);
p.css({top: --p.y +'%'});
},
use: function(){},
up: function(){ clearTimeout(this.to); this.to = 0 }
});
meta.edit({
name: "Left",
combo: ["A"],
on: function(){
console.log("left");
this.to = this.to || setInterval(this.on, 100);
p.css({left: --p.x +'%'});
},
use: function(){},
up: function(){ clearTimeout(this.to); this.to = 0 }
});
meta.edit({
name: "Down",
combo: ["S"],
on: function on(){
console.log("down");
this.to = this.to || setInterval(this.on, 100);
$("html, body").stop().animate({ scrollTop: $(window).scrollTop()+100 }, 100);
p.css({top: ++p.y +'%'});
},
use: function(){},
up: function(){ clearTimeout(this.to); this.to = 0 }
});
meta.edit({
name: "Right",
combo: ["D"],
on: function(){
console.log("right");
this.to = this.to || setInterval(this.on, 100);
p.css({left: ++p.x +'%'});
},
use: function(){},
up: function(){ clearTimeout(this.to); this.to = 0 }
});
meta.edit({
name: "Jump",
combo: [32],
on: function(){ console.log("jump") },
use: function(){},
up: function(){}
});
meta.edit({
name: "Crouch",
combo: [16],
on: function(){ console.log("crouch") },
use: function(){},
up: function(){}
});
meta.edit({
name: "Use",
combo: ["E"],
on: function(){ console.log("use") },
use: function(){},
up: function(){}
});
meta.edit({
name: "Fire",
combo: ["F"],
on: function(){ console.log("fire") },
use: function(){},
up: function(){}
});
meta.edit({
name: "Switch",
combo: [9],
on: function(){ console.log("Switch") },
use: function(){},
up: function(){}
});
window.requestAnimationFrame = window.requestAnimationFrame || setTimeout;
window.requestAnimationFrame(function frame(){
window.requestAnimationFrame(frame, 16);
}, 16);
}());
</script>
</div>
<div id="automecha" class="hold full white blackt">
<style>
#automecha:target .story {
animation: print 3s steps(50, end);
}
</style>
<div class="story pad">
<p class="loud crack">Episode 1: Training</p>
<p>...</p>
</div>
</div>
<div class="hold black center">
<div class="pad">
<div class="left">
<p class="loud">For <i>You</i>,</p>
<p>Crafted with love, <span class="redt"></span> by ERA.</p>
</div>
</div>
<div>
<img src="https://era.eco/media/world.png" class="row">
</div>
</div>
</body>
</html>

View File

@ -1,18 +0,0 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
/node_modules
# testing
/coverage
# production
/build
# misc
.DS_Store
.env
npm-debug.log*
yarn-debug.log*
yarn-error.log*

File diff suppressed because it is too large Load Diff

View File

@ -1,22 +0,0 @@
{
"name": "gun-react-examples",
"version": "1.1.0",
"private": true,
"dependencies": {
"gun": "file:../..",
"react": "^15.5.4",
"react-dom": "^15.5.4"
},
"devDependencies": {
"express": "^4.15.2",
"express-http-proxy": "^0.11.0",
"react-scripts": "0.9.5"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"server": "PORT=8081 node ./server.js"
}
}

View File

@ -1,21 +0,0 @@
console.log("If modules not found, run `npm install` in /example folder!");
var port = process.env.OPENSHIFT_NODEJS_PORT || process.env.VCAP_APP_PORT || process.env.PORT || process.argv[2] || 8765;
var host = process.env.OPENSHIFT_NODEJS_HOST || process.env.VCAP_APP_HOST || process.env.HOST || 'localhost';
var express = require('express');
var proxy = require('express-http-proxy');
var http = require('http');
var app = express();
var server = http.createServer(app);
var Gun = require('gun');
var gun = Gun({
file: 'data.json',
web: server
});
app.use(Gun.serve);
app.use(proxy(host + ':8765'));
server.listen(port);
console.log('Server started on port ' + port + ' with /gun');

39
examples/react/todo.html Normal file
View File

@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<body>
<div id="app"></div>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
<script type="text/babel">
const gun = Gun();
const App = () => {
const newTodo = React.useRef()
const [todos, setTodos] = React.useState({})
React.useEffect(() => {
gun
.get("todos")
.map()
.on((todo, id) => setTodos(todos => ({...todos, [id]: todo })));
}, [])
return (
<div>
<title>TODOs</title>
<ul>{Object.values(todos).map(({title}, i) => <li key={i}>{title}</li>)}</ul>
<form onSubmit={e => {
e.preventDefault();
gun.get("todos").set({ title: newTodo.current.value });
ref.current.value = ''
}}>
<input ref={newTodo} placeholder="new todo"/>
</form>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("app"));
</script>
</body>
</html>

1104
examples/smoothie.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2,161 +2,97 @@
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="./style.css">
<link rel="stylesheet" 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;
body {
font-family: helvetica;
background-color: rgb(25,25,25);
color: rgb(80,135,25) !important;
text-shadow: 1px 1px 20px rgb(80,150,25);
}
.ct-series-b .ct-line,
.ct-series-b .ct-point {
stroke: green !important;
}
.label {
position: absolute;
left: 0.5em;
top: 1.75em;
}
.tall { height: 10em; }
.input {
height: 30px;
padding:10px;
background-color: rgb(50,50,50);
color: rgb(250,50,50);
}
.tall { height: 5em; }
</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>
<input id="url" class="center input crack" placeholder="enter peer stats source url">
<div class="center row charts">
</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 class="model none">
<div class="chart"><span class="label"></span><canvas class="tall row"></canvas></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-daml ct-chart tall"></div>
</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 src="./smoothie.js" charset="utf-8"></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));
var fetchData = async function(){
// fetch the data from server
var data = await (await fetch(url.value||(location.origin+'/gun/stats.radata'), {method: 'GET',mode: 'cors'})).json();
$('#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('memory').line.append(+new Date, data.memory.heapTotal / 1024 / 1024);
try{ Stats('dam # in').line.append(+new Date, data.dam.in.count); }catch(e){}
try{ Stats('dam in MB').line.append(+new Date, data.dam.in.done / 1024 / 1024); }catch(e){}
try{ Stats('dam # out').line.append(+new Date, data.dam.out.count); }catch(e){}
try{ Stats('dam out MB').line.append(+new Date, data.dam.out.done / 1024 / 1024); }catch(e){}
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' }
}});
console.log('data',data);
//fetch keys in all, these may be dynamically changing
//for each key, check if we already have created a time series, if not, create it and add it
// to the chart corredsponding to the unit of measure
$.each(data.all, function(key, arr){
var chart = Stats(key);
// get data and append to line
// get the arrays inside the key
//for each array append the data to the line
for(var i in arr) {
// append data [timestamp], [data]
chart.line.append(arr[i][0], arr[i][1]);
}
})
}
setInterval(fetchData, 15 * 1000);
fetchData();
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){}
try{
stats.daml = Object.values(data.dam['in'].long).map(function(n){ return n });
new Chartist.Line('.ct-daml', {
// 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.daml]
}, {fullWidth: true, axisY: {
labelInterpolationFnc: function(v) { return v+'ms' }
}});
}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();
function Stats(key, chart){
// if we have already created, get data to append to it.
if(chart = Stats[key]){
return chart;
}
// create a new Series for this key
// add it into the map
chart = Stats[key] = new SmoothieChart({responsive: true, minValue: 0, grid:{strokeStyle:'rgba(100%,100%,100%,0.2)'},labels:{fontSize:20}});
chart.line = new TimeSeries();
chart.addTimeSeries(chart.line,{ strokeStyle:'rgb('+Math.random()*255+', '+Math.random()*255+','+Math.random()*255+')', lineWidth:5 });
chart.canvas = $('.model').find('.chart').clone(true).appendTo('.charts');
chart.canvas.find('span').text(key);
chart.streamTo(chart.canvas.find('canvas').get(0), 15 * 1000);
// check first two characters of key to determine other charts to add this in
// tbd later
return chart;
}
</script>
</body>
</html>

66
gun.js
View File

@ -811,20 +811,16 @@
// Maybe... in case the in-memory key we have is a local write
// we still need to trigger a pull/merge from peers.
} else {
//var S = +new Date;
node = Gun.obj.copy(node);
//console.log(+new Date - S, 'copy node');
}
node = Gun.graph.node(node);
tmp = (at||empty).ack;
//var S = +new Date;
root.on('in', {
'@': msg['#'],
how: 'mem',
put: node,
$: gun
});
//console.log(+new Date - S, 'root got send');
//if(0 < tmp){ return }
root.on('get', msg);
}
@ -965,7 +961,7 @@
if(obj_has(back, 'put')){
back.on('in', back);
}
if(tmp){ return }
if(tmp && u !== back.put){ return } //if(tmp){ return }
msg.$ = back.$;
} else
if(obj_has(back.put, get)){ // TODO: support #LEX !
@ -1178,7 +1174,7 @@
if(u === tmp && u !== at.put){ return true }
neat.put = u;
if(neat.ack){
neat.ack = -1; // TODO: BUG? Should this be 0?
neat.ack = -1; // Shouldn't this be reset to 0? If we do that, SEA test `set user ref should be found` fails, odd.
}
neat.on('in', {
get: key,
@ -1317,7 +1313,6 @@
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);
@ -1545,7 +1540,7 @@
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");
Gun.log("Please report this as an issue! Put.any.err");
return;
}
var at = (msg.$._), data = at.put, opt = as.opt||{}, root, tmp;
@ -1554,7 +1549,7 @@
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!??
Gun.log("Please report this as an issue! Put.no.get"); // TODO: BUG!??
return;
}
as.data = obj_put({}, tmp, as.data);
@ -1814,7 +1809,7 @@
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!");
Gun.log("Warning: No localStorage exists to persist data to!");
store = {setItem: function(k,v){this[k]=v}, removeItem: function(k){delete this[k]}, getItem: function(k){return this[k]}};
}
/*
@ -1920,7 +1915,6 @@
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();
@ -1976,15 +1970,14 @@
if('[' === tmp){
try{msg = JSON.parse(raw);}catch(e){opt.log('DAM JSON parse error', e)}
if(!msg){ return }
//console.log('hear batch length of', msg.length);
LOG && opt.log(+new Date, msg.length, 'in hear batch');
(function go(){
var S = +new Date; // STATS!
var S; LOG && (S = +new Date); // STATS!
var m, c = 100; // hardcoded for now?
while(c-- && (m = msg.shift())){
mesh.hear(m, peer);
}
//console.log(+new Date - S, 'hear batch');
(mesh.hear.long || (mesh.hear.long = [])).push(+new Date - S);
LOG && opt.log(S, +new Date - S, 'batch heard');
if(!msg.length){ return }
puff(go, 0);
}());
@ -1995,7 +1988,7 @@
}catch(e){return opt.log('DAM JSON parse error', e)}
if(!msg){ return }
if(!(id = msg['#'])){ id = msg['#'] = Type.text.random(9) }
if(msg.DBG_s){ console.log(+new Date - msg.DBG_s, 'to hear', id) }
if(msg.DBG_s){ opt.log(+new Date - msg.DBG_s, 'to hear', id) }
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) }
@ -2011,9 +2004,9 @@
}
return;
}
//var S = +new Date;
var S; LOG && (S = +new Date);
root.on('in', msg);
//!msg.nts && console.log(+new Date - S, 'msg', msg['#']);
LOG && !msg.nts && opt.log(S, +new Date - S, 'msg', msg['#']);
return;
}
}
@ -2027,7 +2020,7 @@
if(this.to){ this.to.next(msg) } // compatible with middleware adapters.
if(!msg){ return false }
var id, hash, tmp, raw;
//var S = +new Date; //msg.DBG_s = msg.DBG_s || +new Date;
var S; LOG && (S = +new Date); //msg.DBG_s = msg.DBG_s || +new Date;
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) }
@ -2041,15 +2034,15 @@
}
}
}
//console.log(+new Date - S, 'mesh say prep');
LOG && opt.log(S, +new Date - S, 'say prep');
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 }
//var S = +new Date;
var S; LOG && (S = +new Date);
Type.obj.map(peer || opt.peers, each); // in case peer is a peer list.
//console.log(+new Date - S, 'mesh say loop');
LOG && opt.log(S, +new Date - S, 'say loop');
return;
}
if(!peer.wire && mesh.wire){ mesh.wire(peer) }
@ -2073,10 +2066,10 @@
peer.batch = peer.tail = null;
if(!tmp){ return }
if(!tmp.length){ return } // if(3 > tmp.length){ return } // TODO: ^
//var S = +new Date;
var S; LOG && (S = +new Date);
try{tmp = (1 === tmp.length? tmp[0] : JSON.stringify(tmp));
}catch(e){return opt.log('DAM JSON stringify error', e)}
//console.log(+new Date - S, 'mesh flush', tmp.length);
LOG && opt.log(S, +new Date - S, 'say stringify', tmp.length);
if(!tmp){ return }
send(tmp, peer);
}
@ -2086,14 +2079,14 @@
// for now - find better place later.
function send(raw, peer){ try{
var wire = peer.wire;
//var S = +new Date;
var S; LOG && (S = +new Date);
if(peer.say){
peer.say(raw);
} else
if(wire.send){
wire.send(raw);
}
//console.log(+new Date - S, 'wire send', raw.length);
LOG && opt.log(S, +new Date - S, 'wire send', raw.length);
mesh.say.d += raw.length||0; ++mesh.say.c; // STATS!
}catch(e){
(peer.queue = peer.queue || []).push(raw);
@ -2129,7 +2122,8 @@
opt.peers[peer.url || peer.id] = peer;
} else {
tmp = peer.id = peer.id || Type.text.random(9);
mesh.say({dam: '?'}, opt.peers[tmp] = peer);
mesh.say({dam: '?', pid: root.opt.pid}, opt.peers[tmp] = peer);
delete dup.s[peer.last]; // IMPORTANT: see https://gun.eco/docs/DAM#self
}
peer.met = peer.met || +(new Date);
if(!tmp.hied){ root.on(tmp.hied = 'hi', peer) }
@ -2143,21 +2137,16 @@
root.on('bye', peer);
var tmp = +(new Date); tmp = (tmp - (peer.met||tmp));
mesh.bye.time = ((mesh.bye.time || tmp) + tmp) / 2;
LOG = console.LOG; // dirty place to cheaply update LOG settings over time.
}
mesh.hear['!'] = function(msg, peer){ opt.log('Error:', msg.err) }
mesh.hear['?'] = function(msg, peer){
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(msg.pid){
if(!peer.pid){ peer.pid = msg.pid }
if(msg['@']){ return }
}
if(peer.pid){ return }
peer.pid = msg.pid;
mesh.say({dam: '?', pid: opt.pid, '@': msg['#']}, peer);
delete dup.s[peer.last]; // IMPORTANT: see https://gun.eco/docs/DAM#self
}
root.on('create', function(root){
@ -2223,6 +2212,7 @@
}());
var empty = {}, ok = true, u;
var LOG = console.LOG;
try{ module.exports = Mesh }catch(e){}

2
gun.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,155 +1,190 @@
;(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.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() }
}
}
k.up = function(eve){ var tmp;
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){
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){
var noop = function(){}, u;
$.fn.or = function(s){ return this.length ? this : $(s||'body') };
var m = window.meta = {edit:[]};
var k = m.key = {};
k.meta = {17:17, 91:17, 93:17, 224:17}; // ctrl met
function withMeta(eve){ return eve.metaKey || eve.ctrlKey }
k.down = function(eve){
if(eve.repeat){ return }
var key = (k.eve = m.eve = eve).which = eve.which || eve.fake || eve.keyCode;
// ADDED
//if(!m.flip.is() && !k.meta[key]){ return } // cancel non-open events when closed TODO make optional
if(!k.meta[key] && withMeta(eve) && !k.at[key]) { return m.flip(false) } // cancel and close when no action and "meta key" held down (e.g. ctrl+c)
if(!eve.fake && key === k.last){ return }; k.last = key; // jussi: polyfilling eve.repeat?
if(!eve.fake && $(eve.target).closest('input, textarea, [contenteditable=true]').length && !$(eve.target).closest('#meta').length){
if(!meta.flip.is() && !withMeta(eve)){ // cancel meta/hud during text input UNLESS hud is open OR cmd key is held down.
return;
}
//if(k.meta[key]){ k.down.meta = key = -1 }
//if(!k.down.meta){ return }
// hmmm?
//if(!k.meta[key] && !meta.flip.is()) return // aserwed
}
m.check('on', key, k.at || (k.at = m.edit));
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(){
return $('#meta .meta-menu').is(':visible');
}
m.flip.wait = 500;
m.check = function(how, key, at){
at = k.at || m.edit;
var edit = at[key];
if(!edit){ return }
var tmp = k.eve || noop;
if(tmp.preventDefault){ tmp.preventDefault() }
if(edit[how]){
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() }
}*/
// m.list(k.at.back || m.edit);
// if(k.at){ m.flip() } // && !k.at.back
m.flip()
}
}
if('up' != how){ return }
if(at != edit){ edit.back = at }
m.list(edit, true);
}
m.list = function(at, opt){
if(!at){ return m.flip(false) }
var l = [];
$.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){
a = a.combo.slice(-1)[0] || 0;
if(a.length){ a = a.toUpperCase().charCodeAt(0) }
b = b.combo.slice(-1)[0] || 0;
if(b.length){ b = b.toUpperCase().charCodeAt(0) }
return (a < b)? -1 : 1;
});
var $ul = $('#meta .meta-menu ul')
$ul.children('li').addClass('meta-none').hide(); setTimeout(function(){ $ul.children('.meta-none').remove() },250); // necessary fix for weird bug glitch
$.each(l, function(i, k){
$ul.append($('<li>').text(k.name));
});
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();
var $put = $('<input>').attr('id', 'meta-ask').attr('placeholder', help);
var $form = $('<form>').append($put).on('submit', function(eve){
eve.preventDefault();
cb($put.val());
$li.remove();
k.wipe();
});
var $li = $('<li>').append($form);
$ul.append($li);
m.flip(true);
$put.focus();
}
k.wipe = function(opt){
k.down.meta = false;
k.combo = [];
if(!opt){ m.flip(false) }
m.list(k.at = m.edit);
};
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('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 }
if(!(eve.fake = eve.which = (($(this).text().match(/[A-Z]/)||{})[0]||'').toUpperCase().charCodeAt(0))){ return }
eve.tap = true;
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){ if('number' == typeof k){ tmp.push(k) } return }
tmp.push(k.toUpperCase().charCodeAt(0));
});
var at = meta.edit, l = edit.combo.length;
$.each(tmp, function(i,k){ at = at[k] = (++i >= l)? edit : at[k] || {} });
edit.combow = edit.combow.join(',');
m.list(meta.edit);
}
$.fn.or = function(s){ return this.length ? this : $(s||'body') };
;(function(){try{
k.up = function(eve){ var tmp;
var key = (k.eve = m.eve = eve).which = eve.which || eve.fake || eve.keyCode;
//if(!m.flip.is() && !k.meta[key]){ return } // ADDED cancel non-open events when closed TODO make optional
// 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(27 === eve.which){ k.wipe() } // -1 === key ||
}
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(){
return $('#meta .meta-menu').is(':visible');
}
m.flip.wait = 500;
m.check = function(how, key, at){
at = k.at || m.edit;
var next = at[key];
if(!next){ return }
var tmp = k.eve || noop;
if(tmp.preventDefault){ tmp.preventDefault()} // prevent typing (etc) when action found
if(next[how]){
//if(tmp.fake && !next.fake){
//m.tap.next = next;
//} else {
next[how](m.eve);
meta.ui.blink()
/*if(k.at !== m.next && 'up' === how){
if(k.down.meta){ m.list(k.at = m.next) }
else { k.wipe() }
}*/
//}
}
if('up' == how){ return }
if(at != next){ next.back = at }
(k.combo || (k.combo = [])).push(key);
m.list(next, true);
}
m.list = function(at, opt){
if(!at){ return m.flip(false) }
// m.ui.depth(m.key.combo ? m.key.combo.length : 0)
var l = [];
$.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){
a = a.combo.slice(-1)[0] || 0;
if(a.length){ a = a.toUpperCase().charCodeAt(0) }
b = b.combo.slice(-1)[0] || 0;
if(b.length){ b = b.toUpperCase().charCodeAt(0) }
return (a < b)? -1 : 1;
});
var $ul = $('#meta .meta-menu ul')
$ul.children('li').addClass('meta-none').hide(); setTimeout(function(){ $ul.children('.meta-none').remove() },250); // necessary fix for weird bug glitch
$.each(l, function(i, k){
$ul.append($('<li>').text(k.name).data(k));
});
if(opt){ m.flip(true) }
$ul.append($('<li>').html('&larr;').on('click', function(){
// m.key.combo.pop()
m.list(at.back);
}));
}
m.ask = function(help, cb){
var $ul = $('#meta .meta-menu ul').empty();
var $put = $('<input>').attr('id', 'meta-ask').attr('placeholder', help);
var $form = $('<form>').append($put).on('submit', function(eve){
eve.preventDefault();
cb($put.val());
$li.remove();
//k.wipe();
m.list(k.at);
});
var $li = $('<li>').append($form);
$ul.append($li);
m.flip(true);
$put.focus();
}
k.wipe = function(opt){
// k.down.meta = false;
k.combo = [];
if(!opt){ m.flip(false) }
m.list(k.at = m.edit);
};
m.tap = function(){
var on = $('.meta-on')
.or($($(document.querySelectorAll(':hover')).get().reverse()).first())
.or($(document.elementFromPoint(meta.tap.x||0, meta.tap.y||0)));
return on;
}
meta.edit = function(edit){
var tmp = edit.combow = [];
$.each(edit.combo || (edit.combo = []), function(i,k){
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;
$.each(tmp, function(i,k){ at = at[k] = (++i >= l)? edit : at[k] || {} });
edit.combow = edit.combow.join(',');
m.list(k.at || meta.edit);
}
})(USE, './metaCore');
;USE(function(module){
/* UI */
if(meta.css){ return }
meta.ui = {
blink: function(){ // hint visually that action has happened
$('#meta').css('transition', 'none').css('background', 'none')
setTimeout(function(){
$('#meta')[0].style.transition = null
$('#meta')[0].style.background = null
})
},
depth: function(n){
if (n) {
$('#meta').css('background', 'hsl(60, 100%,'+(85-(n*10))+'%)');
} else {
$('#meta')[0].style.background = null
}
}
}
var $m = $('<div>').attr('id', 'meta');
$m.append($('<span>').text('+').addClass('meta-start'));
$m.append($('<span>').html('&#9776;').addClass('meta-start'));
$m.append($('<div>').addClass('meta-menu meta-none').append('<ul>'));
$(document.body).append($m);
css({
@ -161,7 +196,7 @@
background: 'white',
'font-size': '18pt',
'font-family': 'Tahoma, arial',
'box-shadow': '0px 0px 1px #000044',
//'box-shadow': '0px 0px 1px #000044',
'border-radius': '1em',
'text-align': 'center',
'z-index': 999999,
@ -202,25 +237,26 @@
'border-radius': '1em',
'margin-left': '0.25em',
'margin-top': '0.25em',
'float': 'right'
'float': 'right',
'cursor': 'pointer'
},
'#meta a': {color: 'black'},
'#meta:hover': {opacity: 1},
// '#meta:hover': {opacity: 1},
'#meta .meta-menu ul:before': {
content: "' '",
display: 'block',
'min-height': '15em',
height: '50vh'
},
'#meta li': {
background: 'white',
padding: '0.5em 1em',
'border-radius': '1em',
'margin-left': '0.25em',
'margin-top': '0.25em',
'float': 'right'
},
'#meta:hover .meta-menu': {display: 'block'}
// '#meta li': {
// background: 'white',
// padding: '0.5em 1em',
// 'border-radius': '1em',
// 'margin-left': '0.25em',
// 'margin-top': '0.25em',
// 'float': 'right'
// },
// '#meta:hover .meta-menu': {display: 'block'}
});
function css(css){
var tmp = '';
@ -233,10 +269,14 @@
});
var tag = document.createElement('style');
tag.innerHTML = tmp;
document.body.appendChild(tag);
$m.append(tag)
}
}catch(e){}}());
;(function(){
//}catch(e){}
})(USE, './metaUI');
;USE(function(module){
// include basic text editing by default.
var monotype = window.monotype || function(){console.log("monotype needed")};
var m = meta;
@ -256,7 +296,6 @@
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};
@ -266,7 +305,7 @@
r.restore();
if(document.execCommand(cmd, null, as||null)){
if(m.text.range){ m.text.range = monotype() }
return;
return meta.flip(false); // ADDED meta.flip
}
}
if(!opt.tag){ return }
@ -379,5 +418,54 @@
on: function(eve){ meta.text.editor('fontSize', 7) },
up: function(){}
});
}());
})(USE, './metaText');
;USE(function(module){
var m = meta, k = m.key;
$(window).on('focus', k.wipe.bind(null, false)); // .on('blur', k.wipe.bind(null, false))
$(document).on('mousedown mousemove mouseup', function(eve){
m.tap.eve = eve;
m.tap.x = eve.pageX||0;
m.tap.y = eve.pageY||0;
m.tap.on = $(eve.target);
})
// Setting m.tap.edit has been commented, so should never end up here?
//.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 });
var [start, end] = 'ontouchstart' in window
? ['touchstart', 'touchend']
: ['mousedown', 'mouseup']
$(document).on(start, '#meta .meta-menu li', function(eve){
var combo = $(this).data().combo;
eve.fake = eve.which = combo && combo.slice(-1)[0].charCodeAt(0);
eve.tap = true;
k.down(eve);
$(document).one(end, () => k.up(eve))
return;
// if(m.tap.stun){ return m.tap.stun = false }
// if(!(eve.fake = eve.which = (($(this).text().match(/[A-Z]/)||{})[0]||'').toUpperCase().charCodeAt(0))){ return }
// eve.tap = true;
// k.down(eve);
// k.up(eve);
});
$(document).on('keydown', k.down).on('keyup', k.up);
$('#meta').on('click', function(ev) {
if (ev.target.tagName == 'LI' || ev.target.tagName == 'UL') return
meta.flip()
})
//$(document).on('select contextmenu keyup mouseup', '[contenteditable=true]', m.text.on);
})(USE, './metaEvents');
}());

View File

@ -22,7 +22,7 @@ Gun.on('create', function(root){
socket.bind({port: udp.port, exclusive: true}, function(){
socket.setBroadcast(true);
socket.setMulticastTTL(128);
socket.addMembership(udp.address);
try{ socket.addMembership(udp.address); }catch(e){}
});
socket.on("listening", function(){

View File

@ -1,7 +1,8 @@
(function(){
$.normalize = function(html, customOpt){
var html, root$, wrapped, opt;
html = html || '';
var root$, wrapped, opt;
opt = html.opt || (customOpt ? prepareOptTags($.extend(true, baseOpt, customOpt))
: defaultOpt);
if(!html.opt){

View File

@ -5,6 +5,7 @@ Gun.chain.open = function(cb, opt, at){
opt.doc = opt.doc || {};
opt.ids = opt.ids || {};
opt.any = opt.any || cb;
opt.meta = opt.meta || false;
opt.ev = opt.ev || {off: function(){
Gun.obj.map(opt.ev.s, function(e){
if(e){ e.off() }
@ -12,7 +13,9 @@ Gun.chain.open = function(cb, opt, at){
opt.ev.s = {};
}, s:{}}
return this.on(function(data, key, ctx, ev){
delete ((data = Gun.obj.copy(data))||{})._;
if(opt.meta !== true){
delete ((data = Gun.obj.copy(data))||{})._;
}
clearTimeout(opt.to);
opt.to = setTimeout(function(){
if(!opt.any){ return }

View File

@ -11,6 +11,10 @@
* Author: Jachen Duschletta / 2019
*/
// Get window or node Gun instance
var Gun = (typeof window !== "undefined")? window.Gun : require('../gun');
/*
* Function promOnce
* @param limit - due to promises resolving too fast if we do not set a timer

View File

@ -19,7 +19,7 @@
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;
var LOG = console.LOG;
if(!opt.store){
return opt.log("ERROR: Radisk needs `opt.store` interface with `{get: fn, put: fn (, list: fn)}`!");
@ -46,7 +46,7 @@
cb = val;
var S; LOG && (S = +new Date);
val = r.batch(key);
LOG && console.log(+new Date - S, 'rad mem');
LOG && opt.log(S, +new Date - S, 'rad mem');
if(u !== val){
cb(u, r.range(val, o), o);
if(atomic(val)){ return }
@ -77,6 +77,7 @@
r.thrash = function(){
var thrash = r.thrash;
if(thrash.ing){ return thrash.more = true }
LOG = console.LOG; // dirty place to cheaply update LOG settings over time.
thrash.more = false;
thrash.ing = true;
var batch = thrash.at = r.batch, i = 0;
@ -174,9 +175,9 @@
}
f.write = function(){
var tmp = ename(file);
var start; LOG && (start = +new Date); // comment this out!
var S; LOG && (S = +new Date);
opt.store.put(tmp, f.text, function(err){
LOG && console.log("wrote to disk in", (+new Date) - start, tmp); // comment this out!
LOG && opt.log(S, +new Date - S, "wrote disk", tmp);
if(err){ return cb(err) }
r.list.add(tmp, cb);
});
@ -205,10 +206,10 @@
r.write.jsonify = function(f, file, rad, cb, o){
var raw;
var start; LOG && (start = +new Date); // comment this out!
var S; LOG && (S = +new Date);
try{raw = JSON.stringify(rad.$);
}catch(e){ return cb("Record too big!") }
LOG && console.log("stringified JSON in", +new Date - start); // comment this out!
LOG && opt.log(S, +new Date - S, "rad stringified JSON");
if(opt.chunk < raw.length && !o.force){
if(Radix.map(rad, f.each, true)){ return }
}
@ -234,7 +235,7 @@
if(RAD && !o.next){ // cache
var S; LOG && (S = +new Date);
var val = RAD(key);
LOG && console.log(+new Date - S, 'rad cached');
LOG && opt.log(S, +new Date - S, 'rad cached');
//if(u !== val){
//cb(u, val, o);
if(atomic(val)){ cb(u, val, o); return }
@ -247,7 +248,7 @@
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)){
LOG && console.log(+new Date - S, 'rad read lex'); S = +new Date;
LOG && opt.log(S, +new Date - S, 'rad read lex'); S = +new Date;
if(o.next || o.reverse){ g.file = file }
if(tmp = Q[g.file]){
tmp.push({key: key, ack: cb, file: g.file, opt: o});
@ -268,9 +269,9 @@
g.info = info;
if(disk){ RAD = g.disk = disk }
disk = Q[g.file]; delete Q[g.file];
LOG && console.log(+new Date - S, 'rad read it in, now ack to:', disk.length); S = +new Date;
LOG && opt.log(S, +new Date - S, 'rad read in, ack', disk.length); S = +new Date;
map(disk, g.ack);
LOG && console.log(+new Date - S, 'rad read acked');
LOG && opt.log(S, +new Date - S, 'rad read acked');
}
g.ack = function(as){
if(!as.ack){ return }
@ -315,7 +316,7 @@
var p = function Parse(){}, info = {};
p.disk = Radix();
p.read = function(err, data){ var tmp;
LOG && console.log('read disk in', +new Date - S, ename(file)); // keep this commented out in
LOG && opt.log(S, +new Date - S, 'read disk', ename(file));
delete Q[file];
if((p.err = err) || (p.not = !data)){
return map(q, p.ack);
@ -332,12 +333,12 @@
}
info.parsed = data.length;
LOG && (S = +new Date); // keep this commented out in production!
LOG && (S = +new Date);
if(opt.jsonify || '{' === data[0]){ // temporary testing idea
try{
var json = JSON.parse(data);
p.disk.$ = json;
LOG && console.log('parsed JSON in', +new Date - S); // keep this commented out in production!
LOG && opt.log(S, +new Date - S, 'rad parsed JSON');
map(q, p.ack);
return;
}catch(e){ tmp = e }
@ -346,7 +347,7 @@
return map(q, p.ack);
}
}
LOG && (S = +new Date); // keep this commented out in production!
LOG && (S = +new Date);
var tmp = p.split(data), pre = [], i, k, v;
if(!tmp || 0 !== tmp[1]){
p.err = "File '"+file+"' does not have root radix! ";
@ -369,7 +370,7 @@
if(u !== k && u !== v){ p.disk(pre.join(''), v) }
tmp = p.split(tmp[2]);
}
LOG && console.log('parsed RAD in', +new Date - S); // keep this commented out in production!
LOG && opt.log(S, +new Date - S, 'parsed RAD');
//cb(err, p.disk);
map(q, p.ack);
};
@ -389,7 +390,7 @@
if(p.err || p.not){ return cb(p.err, u, info) }
cb(u, p.disk, info);
}
var S; LOG && (S = +new Date); // keep this commented out in production!
var S; LOG && (S = +new Date);
if(raw){ return p.read(null, raw) }
opt.store.get(ename(file), p.read);
}

View File

@ -2,10 +2,12 @@
var Gun = require('../gun'), u;
Gun.serve = require('./serve');
//process.env.GUN_ENV = process.env.GUN_ENV || 'debug';
//console.LOG = true; // only temporarily, REVERT THIS IN FUTURE!
Gun.on('opt', function(root){
if(u === root.opt.super){
root.opt.super = true;
}
root.opt.log = root.opt.log || Gun.log;
this.to.next(root);
})
require('../nts');

View File

@ -5,6 +5,7 @@ Gun.on('opt', function(root){
if(root.once){ return }
if(typeof process === 'undefined'){ return }
if(typeof require === 'undefined'){ return }
if(false === root.opt.stats){ return }
var noop = function(){};
var os = require('os') || {};
var fs = require('fs') || {};
@ -39,6 +40,7 @@ Gun.on('opt', function(root){
stats.peers.count = Object.keys(root.opt.peers||{}).length;
stats.node = {};
stats.node.count = Object.keys(root.graph||{}).length;
stats.all = all;
var dam = root.opt.mesh;
if(dam){
stats.dam = {'in': {count: dam.hear.c, done: dam.hear.d, long: dam.hear.long}, 'out': {count: dam.say.c, done: dam.say.d}};
@ -57,3 +59,14 @@ Gun.on('opt', function(root){
}, 1000 * 15);
Object.keys = Object.keys || function(o){ return Gun.obj.map(o, function(v,k,t){t(k)}) }
});
var log = Gun.log, all = {}, max = 1000;
Gun.log = function(a,b,c,d){
if('number' == typeof a && 'number' == typeof b && 'string' == typeof c){
var tmp = (all[c] || (all[c] = []));
if(max < tmp.push([a,b])){ all[c] = [] } // reset
//return;
}
return log.apply(Gun, arguments);
}

View File

@ -1,5 +1,5 @@
var Gun = (typeof window !== "undefined")? window.Gun : require('../gun');
Gun.on('create', function(root){
if(Gun.TESTING){ root.opt.file = 'radatatest' }
this.to.next(root);
@ -7,6 +7,7 @@ Gun.on('create', function(root){
if(false === opt.radisk){ return }
var Radisk = (Gun.window && Gun.window.Radisk) || require('./radisk');
var Radix = Radisk.Radix;
var LOG = console.LOG;
opt.store = opt.store || (!Gun.window && require('./rfs')(opt));
var rad = Radisk(opt), esc = String.fromCharCode(27);
@ -30,12 +31,12 @@ Gun.on('create', function(root){
val = Radisk.encode(val, null, esc)+'>'+Radisk.encode(Gun.state.is(node, key), null, esc);
rad(soul+esc+key, val, (track? ack : u));
});
//console.log(+new Date - S, 'put loop');
LOG && Gun.log(S, +new Date - S, 'put loop');
function ack(err, ok){
acks--;
if(ack.err){ return }
if(ack.err = err){
try{opt.store.stats.put.err = err}catch(e){} // STATS!
//Gun.log(); //try{opt.store.stats.put.err = err}catch(e){} // STATS!
root.on('in', {'@': id, err: err});
return;
}
@ -45,7 +46,7 @@ Gun.on('create', function(root){
}catch(e){} // STATS!
//console.log(+new Date - S, 'put'); S = +new Date;
root.on('in', {'@': id, ok: 1});
//console.log(+new Date - S, 'put sent');
//console.log(S, +new Date - S, 'put sent');
}
});
@ -77,10 +78,14 @@ Gun.on('create', function(root){
o.limit = (tmp <= (o.pack || (1000 * 100)))? tmp : 1;
}
if(has['-'] || (soul||{})['-']){ o.reverse = true }
if(tmp = (root.next||empty)[soul]){
if(tmp && tmp.rad){ return }
if(o.atom){ tmp = (tmp.next||empty)[o.atom] }
if(tmp && tmp.rad){ return }
if((tmp = (root.next||empty)[soul]) && tmp.put){
if(o.atom){
tmp = (tmp.next||empty)[o.atom] ;
if(tmp && tmp.rad){ return }
} else
if(tmp && tmp.rad){
return;
}
}
var S = (+new Date); // STATS!
rad(key||'', function(err, data, o){
@ -89,7 +94,7 @@ Gun.on('create', function(root){
if(err){ opt.store.stats.get.err = err }
}catch(e){} // STATS!
//if(u === data && o.chunks > 1){ return } // if we already sent a chunk, ignore ending empty responses. // this causes tests to fail.
//console.log(+new Date - S, 'got'); S = +new Date;
LOG && Gun.log(S, +new Date - S, 'got'); S = +new Date; // MARK RETURN HERE!!!! Gun.log will always log unless off :/ switch to something like LOG && whatever?
if(data){
if(typeof data !== 'string'){
if(o.atom){
@ -100,11 +105,10 @@ Gun.on('create', function(root){
}
if(!graph && data){ each(data, '') }
}
//console.log(+new Date - S, 'got prep'); S = +new Date;
LOG && Gun.log(S, +new Date - S, 'got prep');
root.on('in', {'@': id, put: graph, '%': o.more? 1 : u, err: err? err : u, _: each});
//console.log(+new Date - S, 'got sent');
}, o);
//console.log(+new Date - S, 'get call');
LOG && Gun.log(S, +new Date - S, 'get call');
function each(val, has, a,b){
if(!val){ return }
has = (key+has).split(esc);
@ -118,6 +122,7 @@ Gun.on('create', function(root){
if(o.limit && o.limit <= o.count){ return true }
}
each.rad = get;
LOG = console.LOG;
});
opt.store.stats = {get:{time:{}, count:0}, put: {time:{}, count:0}}; // STATS!
var statg = 0, statp = 0; // STATS!

View File

@ -1,272 +0,0 @@
(function(){
if(!this.Gun){ return }
function Test(o){
var test = this;
if(!(test instanceof Test)){ return new Test(o) }
test._ = {};
test._.stack = [];
return test;
}
Test.chain = Test.prototype;
Test.chain.run = function(fn){
var test = this;
var e = test._.i, i = 0;
var stack = test._.stack;
stack.push(fn);
//var to = setInterval(function(){ if(++i >= e){ return clearTimeout(to) }
var start = Gun.time.is();
while(++i <= e){
Gun.list.map(stack, function(fn){ (fn||function(){})(i) })
}
console.log((Gun.time.is() - start) / 1000);
//},0);
return test;
}
Test.chain.it = function(i){
var test = this;
test._.i = i || 1000;
return test;
}
Test.chain.gen = function(fn){
var test = this;
test._.stack.push(fn);
return test;
}
var gun = window.gun = Gun();
window.SPAM = function(read){ // TODO: BUG? gun-sid in transport layer not correct?
//localStorage.clear();
var start = Gun.time.is();
/*var mark = {
name: "Mark Nadal"
};
var amber = {
name: "Amber Nadal",
spouse: mark
}
mark.spouse = amber;
amber.pet = {
name: "Fluffy",
slave: mark
}
Test().it(read).gen(function(i){
Gun.ify(mark);
}).run();return;*/
Test().it(read).gen(function(i){
gun.get('users').path(i).path('where').put({lat: Math.random(), lng: Math.random(),i:i});
}).run();return;
Test().it(read === true? 1 : read || 1000).gen(function(i){
if(read === true){
gun.get('users').map().path('where').on(function(node){
console.log("node:", node);
if(node.i === (1000)){
console.log("total:", Gun.time.is() - start);
start = Gun.time.is();
}
});
return;
}
// PUT, GET, PATH, ON
var now = Gun.time.is();
/*var obj = {'i': i, 'v': Gun.text.random(100)};
gun.put(obj, function(err, ok){
//console.log("put done", i, 'in', Gun.time.is() - now);//, i % 1000);
if(i % (1000) === 0){
console.log("total:", Gun.time.is() - start);
start = Gun.time.is();
}
});return;*/
gun.get('users').path(i).path('where').put({
lat: Math.random(),lng: Math.random(),i: i
//lol: i / 2
}, function(err, ok){
console.log("put done", i, 'in', Gun.time.is() - now);//, i % 1000);
if(i % (1000) === 0){
console.log("total:", Gun.time.is() - start);
start = Gun.time.is();
}
});return;
}).run(function(){});
}
//window.SPAM(1000000);
}());
/* EXTRA GUN UTILITY FUNCTIONS I MIGHT WANT TO KEEP
(function(){
Gun().get('chat').path('messages').since(Gun().get('me').path('last')).map().val(function(msg){
});
Gun().get('chat').path('messages').last(100).map().val(function(msg){
});
var peers = [
peer1,
peer2
];
Gun.on('put').event(function(graph, cb, opt){
Gun.is.graph(graph, function(node, soul){
localStorage[soul] = node;
});
});
Gun.on('put').event(function(graph, cb, opt){
Peers(opt.peers).send({
id: MsgID,
value: data,
from: myPeerID
}, cb);
});
Gun.on('get').event(function(lex, cb, opt){
Peers(opt.peers || peers).send({
'#': MsgID,
'$': lex,
'~': myPeerID
}, cb);
});
Peers.server(function(req, res){
if(Msg.IDs[req.id]){ return } // throttle
// auth
Peers(peers).send(req); // relay
// auth
if(req.rid){ return } // ignore
if(req.put && opt.everything || graph[for soul in req.body]){ // process
Gun.put(gun, req.body, REPLY);
}
});
// TODO: MARK / JESSE need to solve infinite circular loop on get flushing and put flushing.
GUN = {'#': 'soul', '.': 'field', '=': 'value', '>': 'state'}
MSG = {'#': 'id', '$': 'body', '@': 'to'}
Gun.wire = function(data){
}
Gun.get.wire = function(lex, cb, opt){ return Gun.text.is(lex)? Gun.get.wire.from(lex, cb, opt) : Gun.get.wire.to(lex, cb, opt) }
Gun.get.wire.to = function(lex, cb, opt){
var t = '';
Gun.obj.map(lex, function(v,f){
if(!v){ return }
Gun.list.map(Gun.list.is(v)? v : [v], function(v){
t += f + "'" + Gun.put.wire.ify(v) + "'";
});
});
return t + '?';
}
Gun.get.wire.from = function(t, cb, opt){
if(!t){ return null }
var a = Gun.put.wire.from.parse(t), lex = {};
Gun.list.map([Gun._.soul, Gun._.field, Gun._.value, Gun._.state], function(sym, i){
if(!(i = a.indexOf(sym) + 1)){ return }
lex[sym] = Gun.put.wire.type(a[i]);
});
return lex;
}
// #soul.field
// "#soul.field=value>state"
// #messages>>1234567890 //{soul: 'messages', state: {'>': 1234567890}}
// #id$"msg"~who@to
Gun.put.wire = function(n, cb, opt){ return Gun.text.is(n)? Gun.put.wire.from(n, cb, opt) : Gun.put.wire.to(n, cb, opt) }
Gun.put.wire.ify = function(s){ var tmp;
if(Infinity === s || -Infinity === s){ return s }
if(tmp = Gun.is.rel(s)){ return '#' + JSON.stringify(tmp) }
return JSON.stringify(s)
}
Gun.put.wire.type = function(s){ var tmp;
if(Gun._.soul === s.charAt(0)){ return Gun.is.rel.ify(JSON.parse(s.slice(1))) }
if(String(Infinity) === s){ return Infinity }
if(String(-Infinity) === s){ return -Infinity }
return JSON.parse(s)
}
Gun.put.wire.to = function(n, cb, opt){ var t, b;
if(!n || !(t = Gun.is.node.soul(n))){ return null }
cb = cb || function(){};
t = (b = "#'" + Gun.put.wire.ify(t) + "'");
var val = function(v,f, nv,nf){
var w = '', s = Gun.is.node.state(n,f), sw = '';
if(!s){ return }
w += ".'" + Gun.put.wire.ify(f) + "'";
console.log("yeah value?", v, Gun.put.wire.ify(v));
w += "='" + Gun.put.wire.ify(v) + "'";
if(s !== Gun.is.node.state(n,nf)){
w += ">'" + Gun.put.wire.ify(s) + "'";
} else {
sw = ">'" + Gun.put.wire.ify(s) + "'";
}
t += w;
w = b + w + sw;
cb(null, w);
}
var next = function(v,f){ // TODO: BUG! Missing adding meta data.
if(Gun._.meta === f){ return }
if(next.f){
val(next.v, next.f, v,f);
}
next.f = f;
next.v = v;
}
Gun.obj.map(n, next);
next();
return t;
}
Gun.put.wire.from = function(t, cb, opt){
if(!t){ return null }
var a = Gun.put.wire.from.parse(t);
Gun.list.map(a, function(v, i){
if(Gun._.soul === v){
Gun.is.node.soul.ify(n, Gun.put.wire.type(a[i]));
return;
}
if(Gun._.field === v){
var val = a.indexOf(Gun._.value,i), state = a.indexOf(Gun._.state,i);
Gun.is.node.state.ify([n], Gun.put.wire.type(a[i]), Gun.put.wire.type(a[val+1]), Gun.put.wire.type(a[state+1]));
return;
}
})
return n;
}
Gun.put.wire.from.parse = function(t){
var a = [], s = -1, e = 0, end = 1, n = {};
while((e = t.indexOf("'", s + 1)) >= 0){
if(s === e || '\\' === t.charAt(e-1)){}else{
a.push(t.slice(s + 1,e));
s = e;
}
}
return a;
}
}());
*/
;(function(){ // make as separate module!
function SQL(){}
SQL.select = function(sel){
this._.sql.select = sel;
return this;
}
SQL.from = function(from){
this._.sql.from = from;
//this.get(from).map();
return this;
}
SQL.where = function(where){
this._.sql.where = where;
return this;
}
Gun.chain.sql = function(sql){
var gun = this;//.chain();
sql = gun._.sql = sql || {};
gun.select = SQL.select;
gun.from = SQL.from;
gun.where = SQL.where;
return gun;
}
Gun.on('chain').event(function(gun, at){
console.log("sql stuff?", gun._, at.node);
var query = gun._.sql;
if(!query){ return }
var node = at.node;
});
}());

1242
lib/wave.js Normal file

File diff suppressed because it is too large Load Diff

13
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "gun",
"version": "0.2019.915",
"version": "0.2019.1211",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -40,7 +40,8 @@
"@types/node": {
"version": "10.14.18",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.18.tgz",
"integrity": "sha512-ryO3Q3++yZC/+b8j8BdKd/dn9JlzlHBPdm80656xwYUdmPkpTGTjkAdt6BByiNupGPE8w0FhBgvYy/fX9hRNGQ=="
"integrity": "sha512-ryO3Q3++yZC/+b8j8BdKd/dn9JlzlHBPdm80656xwYUdmPkpTGTjkAdt6BByiNupGPE8w0FhBgvYy/fX9hRNGQ==",
"optional": true
},
"@unimodules/core": {
"version": "3.0.2",
@ -120,6 +121,7 @@
"version": "2.0.26",
"resolved": "https://registry.npmjs.org/asn1js/-/asn1js-2.0.26.tgz",
"integrity": "sha512-yG89F0j9B4B0MKIcFyWWxnpZPLaNTjCj4tkE3fjbAoo0qmpGw0PYYqSbX/4ebnd9Icn8ZgK4K1fvDyEtW1JYtQ==",
"optional": true,
"requires": {
"pvutils": "^1.0.17"
}
@ -1347,6 +1349,7 @@
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.0.6.tgz",
"integrity": "sha512-0yNrOdJyLE7FZzmeEHTKanwBr5XbmDAd020cKa4ZiTYuGMBYBZmq7vHOhcOqhVllh6gghDBbaz1lnVdOqiB7cw==",
"optional": true,
"requires": {
"@types/node": "^10.14.17",
"tslib": "^1.10.0"
@ -1355,7 +1358,8 @@
"pvutils": {
"version": "1.0.17",
"resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.0.17.tgz",
"integrity": "sha512-wLHYUQxWaXVQvKnwIDWFVKDJku9XDCvyhhxoq8dc5MFdIlRenyPI9eSfEtcvgHgD7FlvCyGAlWgOzRnZD99GZQ=="
"integrity": "sha512-wLHYUQxWaXVQvKnwIDWFVKDJku9XDCvyhhxoq8dc5MFdIlRenyPI9eSfEtcvgHgD7FlvCyGAlWgOzRnZD99GZQ==",
"optional": true
},
"querystring": {
"version": "0.2.0",
@ -1664,7 +1668,8 @@
"tslib": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==",
"optional": true
},
"uglify-js": {
"version": "3.6.0",

View File

@ -1,6 +1,6 @@
{
"name": "gun",
"version": "0.2019.930",
"version": "0.2019.1228",
"description": "A realtime, decentralized, offline-first, graph data synchronization engine.",
"main": "index.js",
"browser": "browser.js",
@ -16,7 +16,8 @@
"e2e": "mocha e2e/distributed.js",
"docker": "hooks/build",
"minify": "uglifyjs gun.js -o gun.min.js -c -m",
"unbuild": "node lib/unbuild.js & npm run minify"
"unbuild": "node lib/unbuild.js & npm run minify",
"unbuildMeta": "node lib/unbuild.js lib/meta"
},
"repository": {
"type": "git",

21
sea.js
View File

@ -191,8 +191,8 @@
const isocrypto = require('isomorphic-webcrypto');
api.ossl = api.subtle = isocrypto.subtle;
}catch(e){
console.log("node-webcrypto-ossl and text-encoding may not be included by default, please add it to your package.json!");
OSSL_WEBCRYPTO_OR_TEXT_ENCODING_NOT_INSTALLED;
console.log("text-encoding and @peculiar/webcrypto may not be included by default, please add it to your package.json!");
TEXT_ENCODING_OR_PECULIAR_WEBCRYPTO_NOT_INSTALLED;
}}
module.exports = api
@ -978,6 +978,7 @@
}
// If authenticated user wants to delete his/her account, let's support it!
User.prototype.delete = async function(alias, pass, cb){
console.log("user.delete() IS DEPRECATED AND WILL BE MOVED TO A MODULE!!!");
var gun = this, root = gun.back(-1), user = gun.back('user');
try {
user.auth(alias, pass, function(ack){
@ -1019,6 +1020,7 @@
return gun;
}
User.prototype.alive = async function(){
console.log("user.alive() IS DEPRECATED!!!");
const gunRoot = this.back(-1)
try {
// All is good. Should we do something more with actual recalled data?
@ -1038,25 +1040,32 @@
console.log(ctx, ev)
})
}
user.get('trust').get(path).put(theirPubkey);
// do a lookup on this gun chain directly (that gets bob's copy of the data)
// do a lookup on the metadata trust table for this path (that gets all the pubkeys allowed to write on this path)
// do a lookup on each of those pubKeys ON the path (to get the collab data "layers")
// THEN you perform Jachen's mix operation
// and return the result of that to...
}
User.prototype.grant = function(to, cb){
console.log("`.grant` API MAY BE DELETED OR CHANGED OR RENAMED, DO NOT USE!");
var gun = this, user = gun.back(-1).user(), pair = user.pair(), path = '';
var gun = this, user = gun.back(-1).user(), pair = user._.sea, path = '';
gun.back(function(at){ if(at.is){ return } path += (at.get||'') });
(async function(){
var enc, sec = await user.get('trust').get(pair.pub).get(path).then();
var enc, sec = await user.get('grant').get(pair.pub).get(path).then();
sec = await SEA.decrypt(sec, pair);
if(!sec){
sec = SEA.random(16).toString();
enc = await SEA.encrypt(sec, pair);
user.get('trust').get(pair.pub).get(path).put(enc);
user.get('grant').get(pair.pub).get(path).put(enc);
}
var pub = to.get('pub').then();
var epub = to.get('epub').then();
pub = await pub; epub = await epub;
var dh = await SEA.secret(epub, pair);
enc = await SEA.encrypt(sec, dh);
user.get('trust').get(pub).get(path).put(enc, cb);
user.get('grant').get(pub).get(path).put(enc, cb);
}());
return gun;
}

View File

@ -4,7 +4,7 @@ if(typeof Gun === 'undefined'){ return } // TODO: localStorage is Browser only.
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!");
Gun.log("Warning: No localStorage exists to persist data to!");
store = {setItem: function(k,v){this[k]=v}, removeItem: function(k){delete this[k]}, getItem: function(k){return this[k]}};
}
/*
@ -110,7 +110,6 @@ Gun.on('create', function(root){
data = Gun.state.to(data, has);
}
//if(!data && !Gun.obj.empty(opt.peers)){ return } // if data not found, don't ack if there are peers. // Hmm, what if we have peers but we are disconnected?
//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();

View File

@ -19,15 +19,14 @@ function Mesh(root){
if('[' === tmp){
try{msg = JSON.parse(raw);}catch(e){opt.log('DAM JSON parse error', e)}
if(!msg){ return }
//console.log('hear batch length of', msg.length);
LOG && opt.log(+new Date, msg.length, 'in hear batch');
(function go(){
var S = +new Date; // STATS!
var S; LOG && (S = +new Date); // STATS!
var m, c = 100; // hardcoded for now?
while(c-- && (m = msg.shift())){
mesh.hear(m, peer);
}
//console.log(+new Date - S, 'hear batch');
(mesh.hear.long || (mesh.hear.long = [])).push(+new Date - S);
LOG && opt.log(S, +new Date - S, 'batch heard');
if(!msg.length){ return }
puff(go, 0);
}());
@ -38,7 +37,7 @@ function Mesh(root){
}catch(e){return opt.log('DAM JSON parse error', e)}
if(!msg){ return }
if(!(id = msg['#'])){ id = msg['#'] = Type.text.random(9) }
if(msg.DBG_s){ console.log(+new Date - msg.DBG_s, 'to hear', id) }
if(msg.DBG_s){ opt.log(+new Date - msg.DBG_s, 'to hear', id) }
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) }
@ -54,9 +53,9 @@ function Mesh(root){
}
return;
}
//var S = +new Date;
var S; LOG && (S = +new Date);
root.on('in', msg);
//!msg.nts && console.log(+new Date - S, 'msg', msg['#']);
LOG && !msg.nts && opt.log(S, +new Date - S, 'msg', msg['#']);
return;
}
}
@ -70,7 +69,7 @@ function Mesh(root){
if(this.to){ this.to.next(msg) } // compatible with middleware adapters.
if(!msg){ return false }
var id, hash, tmp, raw;
//var S = +new Date; //msg.DBG_s = msg.DBG_s || +new Date;
var S; LOG && (S = +new Date); //msg.DBG_s = msg.DBG_s || +new Date;
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) }
@ -84,15 +83,15 @@ function Mesh(root){
}
}
}
//console.log(+new Date - S, 'mesh say prep');
LOG && opt.log(S, +new Date - S, 'say prep');
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 }
//var S = +new Date;
var S; LOG && (S = +new Date);
Type.obj.map(peer || opt.peers, each); // in case peer is a peer list.
//console.log(+new Date - S, 'mesh say loop');
LOG && opt.log(S, +new Date - S, 'say loop');
return;
}
if(!peer.wire && mesh.wire){ mesh.wire(peer) }
@ -116,10 +115,10 @@ function Mesh(root){
peer.batch = peer.tail = null;
if(!tmp){ return }
if(!tmp.length){ return } // if(3 > tmp.length){ return } // TODO: ^
//var S = +new Date;
var S; LOG && (S = +new Date);
try{tmp = (1 === tmp.length? tmp[0] : JSON.stringify(tmp));
}catch(e){return opt.log('DAM JSON stringify error', e)}
//console.log(+new Date - S, 'mesh flush', tmp.length);
LOG && opt.log(S, +new Date - S, 'say stringify', tmp.length);
if(!tmp){ return }
send(tmp, peer);
}
@ -129,14 +128,14 @@ function Mesh(root){
// for now - find better place later.
function send(raw, peer){ try{
var wire = peer.wire;
//var S = +new Date;
var S; LOG && (S = +new Date);
if(peer.say){
peer.say(raw);
} else
if(wire.send){
wire.send(raw);
}
//console.log(+new Date - S, 'wire send', raw.length);
LOG && opt.log(S, +new Date - S, 'wire send', raw.length);
mesh.say.d += raw.length||0; ++mesh.say.c; // STATS!
}catch(e){
(peer.queue = peer.queue || []).push(raw);
@ -172,7 +171,8 @@ function Mesh(root){
opt.peers[peer.url || peer.id] = peer;
} else {
tmp = peer.id = peer.id || Type.text.random(9);
mesh.say({dam: '?'}, opt.peers[tmp] = peer);
mesh.say({dam: '?', pid: root.opt.pid}, opt.peers[tmp] = peer);
delete dup.s[peer.last]; // IMPORTANT: see https://gun.eco/docs/DAM#self
}
peer.met = peer.met || +(new Date);
if(!tmp.hied){ root.on(tmp.hied = 'hi', peer) }
@ -186,21 +186,16 @@ function Mesh(root){
root.on('bye', peer);
var tmp = +(new Date); tmp = (tmp - (peer.met||tmp));
mesh.bye.time = ((mesh.bye.time || tmp) + tmp) / 2;
LOG = console.LOG; // dirty place to cheaply update LOG settings over time.
}
mesh.hear['!'] = function(msg, peer){ opt.log('Error:', msg.err) }
mesh.hear['?'] = function(msg, peer){
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(msg.pid){
if(!peer.pid){ peer.pid = msg.pid }
if(msg['@']){ return }
}
if(peer.pid){ return }
peer.pid = msg.pid;
mesh.say({dam: '?', pid: opt.pid, '@': msg['#']}, peer);
delete dup.s[peer.last]; // IMPORTANT: see https://gun.eco/docs/DAM#self
}
root.on('create', function(root){
@ -266,6 +261,7 @@ function Mesh(root){
}());
var empty = {}, ok = true, u;
var LOG = console.LOG;
try{ module.exports = Mesh }catch(e){}

View File

@ -34,7 +34,7 @@ function output(msg){
if(obj_has(back, 'put')){
back.on('in', back);
}
if(tmp){ return }
if(tmp && u !== back.put){ return } //if(tmp){ return }
msg.$ = back.$;
} else
if(obj_has(back.put, get)){ // TODO: support #LEX !
@ -247,7 +247,7 @@ function not(at, msg){
if(u === tmp && u !== at.put){ return true }
neat.put = u;
if(neat.ack){
neat.ack = -1; // TODO: BUG? Should this be 0?
neat.ack = -1; // Shouldn't this be reset to 0? If we do that, SEA test `set user ref should be found` fails, odd.
}
neat.on('in', {
get: key,

View File

@ -89,7 +89,6 @@ function soul(gun, cb, opt, as){
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);

View File

@ -177,7 +177,7 @@ 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");
Gun.log("Please report this as an issue! Put.any.err");
return;
}
var at = (msg.$._), data = at.put, opt = as.opt||{}, root, tmp;
@ -186,7 +186,7 @@ function any(soul, as, msg, eve){
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!??
Gun.log("Please report this as an issue! Put.no.get"); // TODO: BUG!??
return;
}
as.data = obj_put({}, tmp, as.data);

View File

@ -162,20 +162,16 @@ Gun.dup = require('./dup');
// Maybe... in case the in-memory key we have is a local write
// we still need to trigger a pull/merge from peers.
} else {
//var S = +new Date;
node = Gun.obj.copy(node);
//console.log(+new Date - S, 'copy node');
}
node = Gun.graph.node(node);
tmp = (at||empty).ack;
//var S = +new Date;
root.on('in', {
'@': msg['#'],
how: 'mem',
put: node,
$: gun
});
//console.log(+new Date - S, 'root got send');
//if(0 < tmp){ return }
root.on('get', msg);
}

View File

@ -3774,6 +3774,24 @@ describe('Gun', function(){
done();
})
});
/*describe('talk to live server tests', function(){
this.timeout(1000 * 9);
it.only('Second once on undefined should call', function(done){ // this test is passing when it fails by hand?
var gun = Gun('https://gunjs.herokuapp.com/gun');
gun.get('~@O8H2BJa4pNfecWamWN7efd888Pg1@hackernoon').once(function(data){
console.log(1, data);
expect(data).to.not.be.ok();
setTimeout(function(){
gun.get('~@O8H2BJa4pNfecWamWN7efd888Pg1@hackernoon').once(function(data){
console.log(2, data);
expect(data).to.not.be.ok();
done();
});
}, 3000);
});
});
});*/
return;
it('Nested listener should be called', function(done){

191
test/panic/4dht.js Normal file
View File

@ -0,0 +1,191 @@
/*
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.
Alice: [Bob]
Bob: [Carl]
Carl: [Bob]
Dave: [Carl]
Ed: [?]
100,000 browsers
1 relay peer
50,000 browsers on Bob
50,000 browsers on Carl
//var gun = Gun(['https://gunjs.herokuapp.com/gun', 'https://guntest.herokuapp.com/gun']);
pretend we have 3TB of data.
300K browsers.
suppose we have 3 nodejs peers that are shards
var superpeer1 = Gun(AXE({shard: 'a~m'}));
var superpeer2 = Gun(AXE({shard: 'n~r'}));
var superpeer3 = Gun(AXE({shard: 's~z'}));
300K browsers join a popular app and they have to do this
via the browser, so they all go to superpeer1.com
then 2/3 of them should get sharded to superpeer2 & superpeer3
first all connect to:
..s1-----s2--s3
./\.\.
b1.b2.b3
then be load balanced to:
..s1--s2--s3
./....|....\.
b1....b2....b3
*/
var config = {
IP: require('ip').address(),
port: 8765,
servers: 3,
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 s1 = servers.pluck(1);
var s2 = servers.excluding(s1).pluck(1);
var s3 = servers.excluding([s1,s2]).pluck(1);
var browsers = clients.excluding(servers);
var b1 = browsers.pluck(1);
var b2 = servers.excluding(b1).pluck(1);
var b3 = servers.excluding([b1,b2]).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, mob: 4});
global.gun = gun;
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(browser, id){
tests.push(browser.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');
var mesh = gun.back('opt.mesh');
mesh.hear['mob'] = function(msg, peer){
// TODO: NOTE, code AXE DHT to aggressively drop new peers AFTER superpeer sends this rebalance/disconnect message that contains some other superpeers.
if(!msg.peers){ return }
var one = msg.peers[Math.floor(Math.random()*msg.peers.length)];
console.log("CONNECT TO THIS PEER:", one);
gun.opt(one);
mesh.bye(peer);
if(test.c){ return }
test.c = 1;
test.done();
}
console.log("connected to who superpeer(s)?", gun._.opt.peers);
window.gun = gun;
window.ref = gun.get('test');
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
});
it("Got Load Balanced to Different Peer", function(){
var tests = [], i = 0;
browsers.each(function(browser, id){
tests.push(browser.run(function(test){
console.log("...", gun._.opt.peers);
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
});
it("All finished!", function(done){
console.log("Done! Cleaning things up...");
setTimeout(function(){
done();
//},1000);
},1000 * 60);
});
after("Everything shut down.", function(){
browsers.run(function(){
//location.reload();
//setTimeout(function(){
//}, 15 * 1000);
});
return servers.run(function(){
process.exit();
});
});
});

137
test/panic/bulkimport.js Normal file
View File

@ -0,0 +1,137 @@
/*
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: 2,
puts: 1000,
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);
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('test');
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
});
it("Puts", function(){
return alice.run(function(test){
console.log("I AM ALICE");
test.async();
var i = test.props.puts, d = 0;
while(i--){ go(i) }
function go(i){
ref.get(i).put({hello: 'world'}, function(ack){
if(ack.err){ put_failed }
if(++d !== test.props.puts){ return }
console.log("all success", d);
test.done();
});
}
}, {puts: config.puts});
});
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();
});
});
});

192
test/panic/livestream.js Normal file
View File

@ -0,0 +1,192 @@
/*
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: 2,
route: {
'/': __dirname + '/index.html',
'/gun.js': __dirname + '/../../gun.js',
'/jquery.js': __dirname + '/../../examples/jquery.js',
'/livestream.html': __dirname + '/livestream.html',
}
}
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 others = browsers.excluding(alice);
describe("Broadcast Video", 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 load QVDev's streaming!", function(){
var tests = [], i = 0;
browsers.each(function(client, id){
tests.push(client.run(function(test){
test.async();
console.log("load?");
function load(src, cb){
var script = document.createElement('script');
script.onload = cb; script.src = src;
document.head.appendChild(script);
}
load('https://cdn.jsdelivr.net/gh/QVDev/GunStreamer/js/GunRecorder.js', function(){
load('https://cdn.jsdelivr.net/gh/QVDev/GunStreamer/js/GunStreamer.js', function(){
load('https://cdn.jsdelivr.net/gh/QVDev/GunStreamer/js/GunViewer.js', function(){
load('https://cdn.jsdelivr.net/gh/QVDev/GunStreamer/js/mediabuffer.js', function(){
test.done();
});
});
});
});
$('body').append('<video id="video" width="100%" autoplay></video>');
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
});
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;
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
});
it("Stream", function(){
return alice.run(function(test){
console.log("I AM ALICE");
test.async();
var stream = window.stream = new GunStreamer({
url: "https://cdn.jsdelivr.net/gh/QVDev/GunStreamer/js/parser_worker.js",
gun: gun,
streamId: 'livestream',
dbRecord: 'streams'
});
var record = window.record = new GunRecorder({
mimeType: 'video/webm; codecs="opus,vp8"',
//audioBitsPerSecond: 6000,//Audio bits per second this is the lowest quality
//videoBitsPerSecond: 100000,//Video bits per second this is the lowest quality
cameraOptions: {video:{width: 1280, height: 720, facingMode: "environment", frameRate: 60}, audio: false},
video_id: "video",
recordInterval: 1000, // how long each chunk?
onRecordStateChange: function(state){ /* change play/pause buttons */ },
onDataAvailable: function(data){ stream.onDataAvailable(data) } // pass recorded data to streamer
});
record.startCamera();
$('#video').on('playing', function(eve){
record.record();
test.done();
});
console.log("start recording!");
}, {acks: config.servers});
});
it("View", function(){
return others.run(function(test){
console.log("I AM A VIEWER!");
test.async();
var view = new GunViewer({
//mimeType: 'video/webm; codecs="opus,vp8"', // NEED THIS ONE FOR AUDIO+VIDEO
mimeType: 'video/webm; codecs="vp8"',
streamerId: "video",
debug: true,//For debug logs
});
gun.get('livestream').on(function(data){
window.data = data;
view.onStreamerData(data);
});
}, {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();
});
});
});