Merge to gun/master 0.9.94 conflicts resolved

This commit is contained in:
mhelander 2018-02-16 18:57:52 +02:00
commit b7aa61f57e
16 changed files with 529 additions and 129 deletions

View File

@ -1,6 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<title>Converse</title>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
<link rel="stylesheet" type="text/css" href="/style.css">
<link href='https://fonts.googleapis.com/css?family=Poiret+One' rel='stylesheet' type='text/css'>
@ -49,7 +50,7 @@
margin-bottom: 0.5em;
}
.send {
.say {
margin: 0 0 0.4em 0.4em;
padding: 0.2em 0.5em;
}
@ -75,7 +76,7 @@
<form class="white huet2 box">
<div>
<div class="send hue2 right whitet box act">send</div>
<div class="say hue2 right whitet box act">say</div>
<b id="name-input" class="jot left who" contenteditable="true" data-text="Name"></b>
<p id="message-input" class="jot left what" contenteditable="true" data-text="Write a message..."></p>
</div>
@ -98,75 +99,62 @@
<script src="/gun/nts.js"></script>
<script>
var gun = Gun(location.origin + '/gun');
var chat = gun.get('converse');
var chat = gun.get('converse/' + location.hash.slice(1));
chat.map()
.val(mapChatToUI);
$("form .send").on('click', submit);
$("form .what").on('keydown', handleEnterPressed);
function mapChatToUI (msg, field) {
var ul = $('ul'),
last = sort(field, ul.children('li').last());
// Grab message to update or model
var msgField = $("#msg-" + field)[0];
if (!msgField) {
msgField = $('.model li')
.clone(true)
.attr('id', 'msg-' + field)
.insertAfter(last);
}
// Merge message data to UI model
var li = $(msgField);
li.find('.who').text(msg.who);
li.find('.what').text(msg.what);
li.find('.sort').text(field);
var time = new Date(msg.when);
li.find('.when').text(time.toDateString() + ', ' + time.toLocaleTimeString());
$('html, body').stop(true, true)
.animate({
scrollTop: ul.height(),
});
$("form .say").on('click', submit);
$("form .what").on('keydown', enter);
function enter(e){
if(e.which !== 13){ return }
submit(e);
}
function submit (e) {
function submit(e){
e.preventDefault();
var msg = {
when: Gun.time.is(),
};
var msg = {when: Gun.time.is()};
msg.who = $('form .who').text();
if (!msg.who) {
if(!msg.who){
msg.who = 'user' + Gun.text.random(3);
$('form .who').text(msg.who);
}
msg.what = $('form .what').text();
if (!msg.what) {
return;
}
if(!msg.what){ return }
chat.get(msg.when + 'r' + Gun.text.random(3)).put(msg);
chat.set(msg);
$('form .what').text('');
}
function handleEnterPressed (e) {
if (e.which == 13) {
submit(e);
}
}
chat.map().val(function(msg, id){
var ul = $('ul');
var last = sort(msg.when, ul.children('li').last());
function sort(fieldId, element) {
var numericId = parseFloat(fieldId);
var idFromText = $(element).find('.sort').text() || -Infinity;
var currentId = numericId >= parseFloat(idFromText);
return currentId ? element : sort(fieldId, element.prev());
var li = $("#msg-" + id)[0]; // grab if exists
if(!li){
li = $('.model li').clone(true) // else create it
.attr('id', 'msg-' + id)
.insertAfter(last);
}
// bind the message data into the UI
li = $(li);
li.find('.who').text(msg.who);
li.find('.what').text(msg.what);
li.find('.sort').text(msg.when);
var time = new Date(msg.when);
li.find('.when').text(time.toDateString() + ', ' + time.toLocaleTimeString());
$('html, body').stop(true, true)
.animate({scrollTop: ul.height()});
});
function sort(id, li){
var num = parseFloat(id);
var id = $(li).find('.sort').text() || -Infinity;
var at = num >= parseFloat(id);
return at ? li : sort(id, li.prev());
}
</script>
</body>
</html>
</html>

View File

@ -4,12 +4,10 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="../style.css">
</head>
<body class="whitet black">
<body>
<div id="sign" class="hue page">
<style>
input, button {
font-size: 1em;
margin: 1em 0;
padding: 1em;
}
@ -17,15 +15,6 @@
display: inline-block;
width: 15%;
}
.loading {
animation: pulse 2s infinite;
}
@keyframes pulse
{
0% {opacity: 1;}
50% {opacity: 0.5;}
100% {opacity: 1;}
}
</style>
<form id="inup" class="sign pad center">
<div class="loud">Welcome,</div>
@ -212,10 +201,10 @@
c.tell("Passphrase needs to be longer than 9 characters.");
return;
}
but.addClass('loading');
but.addClass('pulse');
data.alias = data.alias.toLowerCase();
user.create(data.alias, data.pass, function(ack){
if(!ack.wait){ but.removeClass('loading') }
if(!ack.wait){ but.removeClass('pulse') }
if(ack.err){ c.tell(ack.err); return }
if(ack.pub){
gun.get('users').get(data.alias).put(gun.get('alias/'+data.alias));
@ -237,10 +226,10 @@
return;
}
var but = form.find('button:first');
but.addClass('loading');
but.addClass('pulse');
data.alias = data.alias.toLowerCase();
user.auth(data.alias, data.pass, function(ack){
if(!ack.wait){ but.removeClass('loading') }
if(!ack.wait){ but.removeClass('pulse') }
if(ack.err){ c.tell(ack.err); return }
session(data);
});

View File

@ -5,24 +5,33 @@ html, body {
padding: 0;
font-family: 'Oxygen', 'Trebuchet MS', arial;
position: relative;
background: black;
color: white;
}
body {
font-size: 18pt;
}
div, ul, li, form, p, span, button, input, textarea {
div, ul, li, p, span, form, button, input, textarea {
margin: 0;
padding: 0;
position: relative;
overflow: hidden;
font-size: 1em;
line-height: 1.5em;
-webkit-transition: all 0.3s;
transition: all 0.3s;
box-sizing: border-box;
}
button, input, textarea {
background: white;
border: none;
color: black;
}
input, textarea {
border: none;
width: 100%;
}
@ -132,6 +141,17 @@ ul, li {
color: white;
}
.pulse {
animation: pulse 2s infinite;
}
@keyframes pulse
{
0% {opacity: 1;}
50% {opacity: 0.5;}
100% {opacity: 1;}
}
.hue {
background: #4D79D8;
-webkit-animation: hue 900s infinite;

View File

@ -1,6 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<title>Think</title>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
<link rel="stylesheet" type="text/css" href="/style.css">
</head>
@ -50,7 +51,7 @@
// for how to build a simplified version
// of this example: https://scrimba.com/c/cW2Vsa
var gun = Gun(location.origin+'/gun');
var think = gun.get('think');
var think = gun.get('think/' + location.hash.slice(1));
var typing, throttle;
$('.add').on('click', function(){
$('<li>').attr('contenteditable', true).prependTo('ul');

View File

@ -1,11 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>GPS by GUN</title>
<title>Where</title>
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport" />
<meta charset="utf-8">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css"/>
<script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet@1.0.3/dist/leaflet.css"/>
<script src="https://cdn.jsdelivr.net/npm/leaflet@1.0.3/dist/leaflet.js"></script>
</head>
<body>
<div id="map"></div>
@ -68,7 +68,7 @@
word-wrap: break-word;
}
</style>
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script src="/jquery.js"></script>
<script src="/gun.js"></script>
<script>
function Where(opt, cb){
@ -96,7 +96,7 @@
where.map.on('zoomlevelschange', where.opt.zoom.ing, where.opt.err);
where.update = function(latlng){
if(new Date().getTime() - where.update.last < 1000){
if((+new Date) - where.update.last < 400){
clearTimeout(where.update.to);
where.update.to = setTimeout(function(){
where.update(latlng);
@ -106,7 +106,7 @@
where.map.setView(latlng, where.opt.zoom.level, {animate: true});
where.marker = where.marker || L.marker().setLatLng(latlng).addTo(where.map);
where.marker.setLatLng(latlng).update();
where.update.last = new Date().getTime();
where.update.last = (+new Date);
}
if(where.opt.track){
@ -149,7 +149,7 @@
//$('#debug').value = 'track ' + JSON.stringify(latlng);
gps.where.update(latlng);
});
$('#share').toggleClass("hide");
$('#share').addClass("hide");
} else {
document.cookie = 'gps=' + (gps.track = (document.cookie.match(/gps\=(.*?)(\&|$|\;)/i)||[])[1] || Gun.text.random(5)); // trick with cookies!
gps.ref = gun.get('gps/' + gps.track);
@ -165,8 +165,8 @@
//$('#debug').value = JSON.stringify(gps.last);
}
gps.where = gps.where || Where(gps.opt);
$('#follow').text(("gps.gunDB.io/" || (location.origin + location.pathname)) + '#' + gps.track);
$('#share').toggleClass("hide");
$('#follow').text(("where.gunDB.io/" || (location.origin + location.pathname)) + '#' + gps.track);
$('#share').removeClass("hide");
$('#share').on('click', function(){
$('#link').toggleClass("hide");
});

31
gun.js
View File

@ -288,6 +288,7 @@
}
}());
Val.rel.ify = function(t){ return obj_put({}, rel_, t) } // convert a soul into a relation and return it.
Type.obj.has._ = '.';
var rel_ = Val.rel._, u;
var bi_is = Type.bi.is;
var num_is = Type.num.is;
@ -711,7 +712,10 @@
if(!Gun.graph.is(msg.put, null, verify, ctx)){ ctx.err = "Error: Invalid graph!" }
if(ctx.err){ return at.on('in', {'@': msg['#'], err: Gun.log(ctx.err) }) }
obj_map(ctx.put, merge, ctx);
if(!ctx.async){ obj_map(ctx.map, map, ctx) }
if(!ctx.async){
at.stop = {}; // temporary fix till a better solution?
obj_map(ctx.map, map, ctx)
}
if(u !== ctx.defer){
setTimeout(function(){
Gun.on.put(msg, gun);
@ -760,6 +764,7 @@
if(obj_map(ctx.souls, function(v){ if(v){ return v } })){ return } // if flag still outstanding, keep waiting.
if(ctx.c){ return } ctx.c = 1; // failsafe for only being called once per context.
this.off();
cat.stop = {}; // temporary fix till a better solution?
obj_map(ctx.map, map, ctx); // all done, trigger chains.
});
}
@ -778,10 +783,8 @@
}
function map(msg, soul){
if(!msg.gun){ return }
msg.gun._.root._.stop = {};
//console.log('map ->', soul, msg.put);
(msg.gun._).on('in', msg);
msg.gun._.root._.stop = {};
}
Gun.on.get = function(msg, gun){
@ -944,9 +947,7 @@
//if(u !== back.put){
back.on('in', back);
}
if(back.ack){
return;
}
if(back.ack){ return }
msg.gun = back.gun;
back.ack = -1;
} else
@ -1002,7 +1003,7 @@
}
if(u === change){
ev.to.next(msg);
if(cat.soul){ return }
if(cat.soul){ return } // TODO: BUG, I believe the fresh input refactor caught an edge case that a `gun.get('soul').get('key')` that points to a soul that doesn't exist will not trigger val/get etc.
echo(cat, msg, ev);
if(cat.has){
not(cat, msg);
@ -1017,13 +1018,6 @@
obj_map(change, map, {at: msg, cat: cat});
return;
}
/*if(rel = Gun.val.rel.is(change)){
if(tmp = (gun.back(-1).get(rel)._).put){
change = tmp; // this will cause performance to turn to mush, maybe use `.now` check?
}
//if(tmp.put){ change = tmp.put; }
}
if(!rel || tmp){*/
if(!(rel = Gun.val.rel.is(change))){
if(Gun.val.is(change)){
if(cat.has || cat.soul){
@ -1078,10 +1072,11 @@
// neither of these are ideal, need to be fixed without hacks,
// but for now, this works for current tests. :/
if(!now){
var stop = at.root._.stop;
return;
/*var stop = at.root._.stop;
if(!stop){ return }
if(stop[at.id] === rel){ return }
stop[at.id] = rel;
stop[at.id] = rel;*/
} else {
if(u === now[at.id]){ return }
if((now._ || (now._ = {}))[at.id] === rel){ return }
@ -1151,7 +1146,6 @@
function ask(at, soul){
var tmp = (at.root.get(soul)._);
if(at.ack){
//tmp.ack = tmp.ack || -1;
tmp.on('out', {get: {'#': soul}});
if(!at.ask){ return } // TODO: PERFORMANCE? More elegant way?
}
@ -1371,13 +1365,14 @@
// and STOP is a hack to get async behavior to correctly call.
// neither of these are ideal, need to be fixed without hacks,
// but for now, this works for current tests. :/
var tmp = cat.root._.now; obj.del(cat.root._, 'now');
var tmp = cat.root._.now; obj.del(cat.root._, 'now'); cat.root._.PUT = true;
var tmp2 = cat.root._.stop;
(as.ref._).now = true;
(as.ref._).on('out', {
gun: as.ref, put: as.out = as.env.graph, opt: as.opt, '#': ask
});
obj.del((as.ref._), 'now');
obj.del((cat.root._), 'PUT');
cat.root._.now = tmp;
cat.root._.stop = tmp2;
}, as);

2
gun.min.js vendored

File diff suppressed because one or more lines are too long

50
lib/untitled.js Normal file
View File

@ -0,0 +1,50 @@
function input(msg){
var ev = this, cat = this.as, gun = msg.gun, at = gun._, change = msg.put, rel, tmp;
// dispatch to chain listeners.
// dispatch to children.
// dispatch to echoes.
if(u === change){
// Here are the possible options:
// 1. We think the data does not exist because peers/disk cannot find it.
// 2. We know the data does not exist because a parent is or was changed to a primitive.
// Souls can only (1) because they have no parent.
// Has can be (1) or (2).
// Gets and chains can be (1) or (2).
if(cat.soul || cat.has){
// a soul can never become undefined.
// a soul can only not be found.
if(cat.soul && u !== cat.put){
return;
}
// a key may sometimes might not be found.
// a key on a soul can not become undefined,
// but the chain might be on a chain that
// does not exist, and therefore can be undefined.
ev.to.next(msg); // ex, notify val and stuff.
echo(cat, msg, ev); // ex, notify a sub-object pointer like `mark.pet`! // TODO: BUG ON VAL, it will still not get called because it checks !node when it should also check ack.
if(cat.soul){ return }
obj_map(cat.next, unknown); // notify children.
}
if(cat.has){
if()
}
return;
}
if(cat.soul){
return;
}
if(cat.has){
return;
}
if(cat.get){
return;
}
ev.to.next(msg);
}
function unknown(ref, key){
(ref = (ref._)).put = u;
ref.on('in', {get: key, put: u, gun: ref.gun});
}
gun.get('users').map().map().get('who').get('say').map().on(cb);

40
lib/upload.js Normal file
View File

@ -0,0 +1,40 @@
;(function(){
function upload(cb, opt){
var el = $(this); cb = cb || function(){}; opt = opt || {};
el.on('drop', function(e){
e.preventDefault();
upload.drop(((e.originalEvent||{}).dataTransfer||{}).files, 0);
}).on('dragover', function(e){
e.preventDefault();
});
upload.drop = function(files,i){
if(opt.max && (files[i].fileSize > opt.max || files[i].size > opt.max)){
cb({err: "File size is too large.", file: file[i]}, upload);
if(files[++i]){ upload.drop(files,i) }
return false;
}
reader = new FileReader();
reader.onload = function(e){
cb({file: files[i], event: e, id: i}, upload);
if(files[++i]){ upload.drop(files,i) }
};
if(files[i]){ reader.readAsDataURL(files[i]) }
}
}
upload.shrink = function(e, cb, w, h){ // via stackoverflow
if(!e){ return cb && cb({err: "No file!"}) }
if(e.err){ return }
var file = (((e.event || e).target || e).result || e), img = new Image();
img.src = file;
img.onload = function(){
if(!h && img.width > w){ h = img.height * (w / img.width) }
var canvas = document.createElement('canvas'), ctx = canvas.getContext('2d');
canvas.width = w;
canvas.height = h;
ctx.drawImage(img, 0, 0, w, h); // draw source image to canvas.
var b64 = e.base64 = canvas.toDataURL(); // base64 the compressed image.
cb((e.base64 && e) || b64);
};
}
$.fn.upload = upload;
}());

View File

@ -1,6 +1,6 @@
{
"name": "gun",
"version": "0.9.93",
"version": "0.9.94",
"description": "A realtime, decentralized, offline-first, graph data synchronization engine.",
"main": "index.js",
"browser": "gun.min.js",

View File

@ -32,9 +32,7 @@ function output(msg){
//if(u !== back.put){
back.on('in', back);
}
if(back.ack){
return;
}
if(back.ack){ return }
msg.gun = back.gun;
back.ack = -1;
} else
@ -90,7 +88,7 @@ function input(msg){
}
if(u === change){
ev.to.next(msg);
if(cat.soul){ return }
if(cat.soul){ return } // TODO: BUG, I believe the fresh input refactor caught an edge case that a `gun.get('soul').get('key')` that points to a soul that doesn't exist will not trigger val/get etc.
echo(cat, msg, ev);
if(cat.has){
not(cat, msg);
@ -105,13 +103,6 @@ function input(msg){
obj_map(change, map, {at: msg, cat: cat});
return;
}
/*if(rel = Gun.val.rel.is(change)){
if(tmp = (gun.back(-1).get(rel)._).put){
change = tmp; // this will cause performance to turn to mush, maybe use `.now` check?
}
//if(tmp.put){ change = tmp.put; }
}
if(!rel || tmp){*/
if(!(rel = Gun.val.rel.is(change))){
if(Gun.val.is(change)){
if(cat.has || cat.soul){
@ -166,10 +157,11 @@ function relate(at, msg, from, rel){
// neither of these are ideal, need to be fixed without hacks,
// but for now, this works for current tests. :/
if(!now){
var stop = at.root._.stop;
return;
/*var stop = at.root._.stop;
if(!stop){ return }
if(stop[at.id] === rel){ return }
stop[at.id] = rel;
stop[at.id] = rel;*/
} else {
if(u === now[at.id]){ return }
if((now._ || (now._ = {}))[at.id] === rel){ return }
@ -239,7 +231,6 @@ function not(at, msg){
function ask(at, soul){
var tmp = (at.root.get(soul)._);
if(at.ack){
//tmp.ack = tmp.ack || -1;
tmp.on('out', {get: {'#': soul}});
if(!at.ask){ return } // TODO: PERFORMANCE? More elegant way?
}

View File

@ -101,13 +101,14 @@ function batch(){ var as = this;
// and STOP is a hack to get async behavior to correctly call.
// neither of these are ideal, need to be fixed without hacks,
// but for now, this works for current tests. :/
var tmp = cat.root._.now; obj.del(cat.root._, 'now');
var tmp = cat.root._.now; obj.del(cat.root._, 'now'); cat.root._.PUT = true;
var tmp2 = cat.root._.stop;
(as.ref._).now = true;
(as.ref._).on('out', {
gun: as.ref, put: as.out = as.env.graph, opt: as.opt, '#': ask
});
obj.del((as.ref._), 'now');
obj.del((cat.root._), 'PUT');
cat.root._.now = tmp;
cat.root._.stop = tmp2;
}, as);

View File

@ -67,7 +67,10 @@ Gun.dup = require('./dup');
if(!Gun.graph.is(msg.put, null, verify, ctx)){ ctx.err = "Error: Invalid graph!" }
if(ctx.err){ return at.on('in', {'@': msg['#'], err: Gun.log(ctx.err) }) }
obj_map(ctx.put, merge, ctx);
if(!ctx.async){ obj_map(ctx.map, map, ctx) }
if(!ctx.async){
at.stop = {}; // temporary fix till a better solution?
obj_map(ctx.map, map, ctx)
}
if(u !== ctx.defer){
setTimeout(function(){
Gun.on.put(msg, gun);
@ -116,6 +119,7 @@ Gun.dup = require('./dup');
if(obj_map(ctx.souls, function(v){ if(v){ return v } })){ return } // if flag still outstanding, keep waiting.
if(ctx.c){ return } ctx.c = 1; // failsafe for only being called once per context.
this.off();
cat.stop = {}; // temporary fix till a better solution?
obj_map(ctx.map, map, ctx); // all done, trigger chains.
});
}
@ -134,10 +138,8 @@ Gun.dup = require('./dup');
}
function map(msg, soul){
if(!msg.gun){ return }
msg.gun._.root._.stop = {};
//console.log('map ->', soul, msg.put);
(msg.gun._).on('in', msg);
msg.gun._.root._.stop = {};
}
Gun.on.get = function(msg, gun){

View File

@ -34,6 +34,7 @@ Val.rel = {_: '#'};
}
}());
Val.rel.ify = function(t){ return obj_put({}, rel_, t) } // convert a soul into a relation and return it.
Type.obj.has._ = '.';
var rel_ = Val.rel._, u;
var bi_is = Type.bi.is;
var num_is = Type.num.is;

View File

@ -3753,18 +3753,18 @@ describe('Gun', function(){
}, 1);
});
return;
it.only('get map should not slowdown', function(done){
it('get map should not slowdown', function(done){
this.timeout(5000);
var gun = (window.gun = Gun()).get('g/m/no/slow');
var gun = Gun().get('g/m/no/slow');
//console.log("---------- setup data done -----------");
var prev, diff, max = 25, total = 2, largest = -1, gone = {};
var prev, diff, max = 25, total = 9, largest = -1, gone = {};
//var prev, diff, max = Infinity, total = 10000, largest = -1, gone = {};
// TODO: It would be nice if we could change these numbers for different platforms/versions of javascript interpreters so we can squeeze as much out of them.
gun.get('history').map().on(function(time, index){
console.log(">>>", index, time);
//console.log(">>>", index, time);
diff = Gun.time.is() - time;
return;
//return;
expect(gone[index]).to.not.be.ok();
gone[index] = diff;
largest = (largest < diff)? diff : largest;
@ -3783,8 +3783,8 @@ describe('Gun', function(){
prev = Gun.time.is();
var put = {}; put[turns += 1] = prev;
//console.log("put", put);
console.log("------", turns, "-------");
2 === turns && (console.debug.i = 1);
//console.log("------", turns, "-------");
//3 === turns && (console.debug.i = 1);
console.debug(1, 'save', {history: put});
gun.put({history: put});
}, 1);

322
test/panic/speak.js Normal file
View File

@ -0,0 +1,322 @@
var config = {
IP: require('ip').address(),
port: 8080,
servers: 1,
browsers: 2,
each: 250,
burst: 1, // do not go below 1!
wait: 1,
route: {
'/': __dirname + '/index.html',
'/gun.js': __dirname + '/../../gun.js',
'/jquery.js': __dirname + '/../../examples/jquery.js',
'/cryptomodules.js': __dirname + '/../../lib/cryptomodules.js',
'/sea.js': __dirname + '/../../sea.js'
}
}
var fs = require('fs');
var server = require('https').createServer({
key: fs.readFileSync(__dirname+'/../https/server.key'),
cert: fs.readFileSync(__dirname+'/../https/server.crt'),
ca: fs.readFileSync(__dirname+'/../https/ca.crt'),
requestCert: true,
rejectUnauthorized: false
});
var panic = require('panic-server');
panic.server(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: 'https://' + config.IP + ':' + config.port
});
// Now lets divide our clients into "servers" and "browsers".
var servers = clients.filter('Node.js');
var browsers = clients.excluding(servers);
// Sweet! Now we can start the tests.
// PANIC works with Mocha and other testing libraries!
// So it is easy to use PANIC.
describe("Stress test GUN with SEA users causing PANIC!", function(){
this.timeout(10 * 60 * 1000);
it("Servers have joined!", function(){
return servers.atLeast(config.servers);
});
it("GUN has spawned!", function(){
// Once they are, we need to actually spin up the gun server.
var tests = [], i = 0;
servers.each(function(client){
// for each server peer, tell it to run this code:
tests.push(client.run(function(test){
// NOTE: Despite the fact this LOOKS like we're in a closure...
// it is not! This code is actually getting run
// in a DIFFERENT machine or process!
var env = test.props;
// As a result, we have to manually pass it scope.
test.async();
//setInterval(function(){ var u, t; u = process.memoryUsage().heapUsed; t = require('os').totalmem(); console.log((u/t).toFixed(2)) }, 1000)
// Clean up from previous test.
try{ require('fs').unlinkSync(env.i+'data') }catch(e){ console.log("!!! WARNING !!!! MUST MANUALLY REMOVE OLD DATA!!!!, e") }
var purl = 'https://'+env.config.IP+':'+env.config.port;
require('gun/test/https/test')(env.config.port + env.i, env.i+'data', function(){
// This server peer is now done with the test!
// It has successfully launched.
test.done();
}, function(file){
file = file.toString();
if(0 >= file.indexOf('<script src="/gun.js"></script>')){ return }
file = file.replace('<script src="/gun.js"></script>',
"<script src='"+purl+"/panic.js'></script><script>panic.server('"+purl+"')</script><script src='/gun.js'></script><script>localStorage.clear();sessionStorage.clear();</script>");
return file;
});
}, {i: i += 1, config: config}));
});
// NOW, this is very important:
// Do not proceed to the next test until
// every single server (in different machines/processes)
// have ALL successfully launched.
return Promise.all(tests);
});
it(config.browsers +" browser(s) have joined!", function(){
console.log("PLEASE OPEN https://"+ config.IP +":"+ (config.port) +" IN "+ config.browsers +" BROWSER(S)!");
return browsers.atLeast(config.browsers);
});
it("Browsers load SEA!", 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('sea.js', function(){
test.done();
});
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
});
it("Browsers initialized gun!", function(){
var tests = [], ids = {}, i = 0;
// Let us create a list of all the browsers IDs connected.
// This will later let each browser check against every other browser.
browsers.each(function(client, id){
ids[id] = 1;
});
browsers.each(function(client, id){
tests.push(client.run(function(test){
localStorage.clear();
var env = window.env = test.props;
var peers = [], i = env.config.servers;
while(i--){
// For the total number of servers listed in the configuration
// Add their URL into an array.
peers.push('https://'+ env.config.IP + ':' + (env.config.port + (i + 1)) + '/gun');
}
var gun = window.gun = Gun(peers);
var user = window.user = gun.user();
var go = window.go = {num: 0, total: 0, users: {}, pub: {}};
window.ID = env.id;
go.check = Gun.obj.map(env.ids, function(v,id,t){
// for each browser ID
// they will be saving X number of messages each.
go.users[id] = true; // set an outstanding flag to check against.
var i = env.config.each;
while(i--){
// So add a deterministic key we can check against.
t(id + (i + 1), 1);
// And count up the total number of messages we expect for all.
go.total += 1;
}
});
console.log(peers, go);
}, {i: i += 1, id: id, ids: ids, config: config}));
});
return Promise.all(tests);
});
it("All users created!", function(){
var tests = [], ids = {}, i = 0;
browsers.each(function(client, id){
ids[id] = 1;
});
browsers.each(function(client, id){
tests.push(client.run(function(test){
test.async();
gun.on('secure', function(at){
/* enforce some rules about shared app level data */
if(!at.put || !at.put.users){ return }
var no;
Gun.node.is(at.put.users, function(val, key){
Gun.SEA.read(val, false, function(val){
if('alias/'+key === Gun.val.rel.is(val)){ return }
no = true;
})
if(no){ return no }
});
if(no){ return }
this.to.next(at);
});
var unsafepassword = 'asdf'+ID;
console.log("sign in and up:", ID);
window.user.create(ID, unsafepassword, function(ack){
if(ack.err || !ack.pub){ return }
window.pub = ack.pub;
gun.get('users').get(ID).put(gun.get('alias/'+ID));
console.log("signed up", ack.pub);
console.debug.j = 1;
window.user.auth(ID, unsafepassword, function(ack){
console.debug.j = 0;
console.log("signed in", ack);
if(ack.err || !ack.pub){ return }
test.done();
});
});
}, {i: i += 1, id: id, ids: ids, config: config}));
});
return Promise.all(tests);
});
it("Start reading and sending messages!", function(){
var tests = [], ids = {}, i = 0;
browsers.each(function(client, id){
ids[id] = 1;
});
browsers.each(function(client, id){
tests.push(client.run(function(test){
test.async();
gun.get('users').map().map()
.get('who').get('said').map().on(function(msg){
check(msg);
});
var said = user.get('who').get('said');
function run(i){
var what = i +"|||"+ "Hello world!" +"|||"+ pub +"|||"+ ID;
said.set({
what: what
});/*, function(ack){
if(ack.err){ return }
test.done();
});*/
}
/* TODO: sometimes sign in hangs */
console.log("<<<<< START >>>>>");
var i = 0, to = setInterval(function frame(a, b){
if(!b && 2 <= (b = env.config.burst)){
while(--b){
frame(i, true);
}
return;
}
if(env.config.each <= i){
clearTimeout(to);
return;
}
run(i += 1);
}, env.config.wait || 1);
var col = $("<div>").css({width: 250, position: 'relative', float: 'left', border: 'solid 1px black'}), cols = {};
var report = $("<div>").css({position: 'fixed', top: 0, right: 0, background: 'white', padding: 10}).text(" / "+ go.total +" Verified").prependTo('body');
var reportc = $('<span>').text(0).prependTo(report);
var last = $("<div>").text("Processing: ").css({border: "solid 1px black"}).appendTo("body");
last = $("<span>").text(" ").appendTo(last);
function check(data){
data = data.what.split("|||");
var msg = {num: data[0], what: data[0] +' '+ data[1], who: data[2], id: data[3]};
var who;
if(!go.pub[msg.who]){
go.pub[msg.who] = msg.id;
go.users[msg.id] = false;
//who = cols[msg.id] = col.clone(true).appendTo('body');
//who.prepend("<input value='"+ msg.who +"'>");
//who.prepend("<input value='"+ msg.id +"'>");
}
if(!go.check[msg.id + msg.num]){
return;
}
go.check[msg.id + msg.num] = false;
clearTimeout(end.to); end.to = setTimeout(end, 9);
reportc.text(++go.num);
last.text(msg.what);
//who = cols[msg.id];
//$("<div>").css({border: 'solid 1px blue'}).text(msg.what).appendTo(who);
}
function end(){
var wait = Gun.obj.map(go.users, function(v){ if(v){ return true }});
if(wait){ return }
var more = Gun.obj.map(go.check, function(v){ if(v){ return true }});
if(more){ return }
test.done();
}
}, {i: i += 1, id: id, ids: ids, config: config}));
});
return Promise.all(tests);
});
/* MODEL TEST
it("Browsers initialized gun!", function(){
var tests = [], ids = {}, i = 0;
browsers.each(function(client, id){
ids[id] = 1;
});
browsers.each(function(client, id){
tests.push(client.run(function(test){
// code here
}, {i: i += 1, id: id, ids: ids, config: config}));
});
return Promise.all(tests);
});
*/
it("All finished!", function(done){
console.log("Done! Cleaning things up...");
setTimeout(function(){
done();
}, 2000);
});
after("Everything shut down.", function(){
browsers.run(function(){
//location.reload();
//setTimeout(function(){
//}, 15 * 1000);
});
return servers.run(function(){
process.exit();
});
});
});