Browse Source

update gundb

Nikolay Suslov 6 years ago
parent
commit
06fc0859e1
39 changed files with 1491 additions and 1639 deletions
  1. 5 7
      public/app.js
  2. 99 0
      public/lib/gundb/axe.js
  3. 45 38
      public/lib/gundb/gun.js
  4. 0 0
      public/lib/gundb/gun.min.js
  5. 4 4
      public/lib/gundb/lib/bye.js
  6. 10 0
      public/lib/gundb/lib/fun.js
  7. 38 0
      public/lib/gundb/lib/metae.js
  8. 230 0
      public/lib/gundb/lib/monotype.js
  9. 138 0
      public/lib/gundb/lib/normalize.js
  10. 19 0
      public/lib/gundb/lib/reboot.js
  11. 2 2
      public/lib/gundb/lib/rfs.js
  12. 5 3
      public/lib/gundb/lib/server.js
  13. 29 0
      public/lib/gundb/lib/super.js
  14. 329 721
      public/lib/gundb/sea.js
  15. 0 63
      public/lib/gundb/sea/authenticate.js
  16. 1 1
      public/lib/gundb/sea/buffer.js
  17. 217 204
      public/lib/gundb/sea/create.js
  18. 18 5
      public/lib/gundb/sea/decrypt.js
  19. 10 5
      public/lib/gundb/sea/encrypt.js
  20. 2 2
      public/lib/gundb/sea/https.js
  21. 24 9
      public/lib/gundb/sea/index.js
  22. 0 21
      public/lib/gundb/sea/leave.js
  23. 0 49
      public/lib/gundb/sea/login.js
  24. 13 1
      public/lib/gundb/sea/pair.js
  25. 0 41
      public/lib/gundb/sea/persist.js
  26. 0 43
      public/lib/gundb/sea/query.js
  27. 0 141
      public/lib/gundb/sea/recall.js
  28. 2 8
      public/lib/gundb/sea/root.js
  29. 5 30
      public/lib/gundb/sea/sea.js
  30. 7 2
      public/lib/gundb/sea/secret.js
  31. 26 31
      public/lib/gundb/sea/shim.js
  32. 8 12
      public/lib/gundb/sea/sign.js
  33. 0 48
      public/lib/gundb/sea/update.js
  34. 1 1
      public/lib/gundb/sea/user.js
  35. 18 6
      public/lib/gundb/sea/verify.js
  36. 3 2
      public/lib/gundb/sea/work.js
  37. 176 131
      public/vwf.js
  38. 4 5
      public/vwf/model/aframe.js
  39. 3 3
      public/web/index-app.js

+ 5 - 7
public/app.js

@@ -546,7 +546,7 @@ class App {
 
     _LCSDB.on('auth',
       async function (ack) {
-        if (ack.pub) {
+        if (ack.sea.pub) {
           document.querySelector("#profile")._status = "User: " + _LCSUSER.is.alias //+' pub: ' + _LCSUSER.is.pub;
           document.querySelector("#profile").$update();
         }
@@ -912,13 +912,11 @@ class App {
     //       console.log('world files loaded');
     //       vwf.ready( vwf.application, loadObj)
     //     }
-    //   }, {wait: 200});
+    //   });
     // }
-    // }, {wait: 200});
-
+    // });
 
     return loadObj
-
   }
 
   // LookupSaveRevisions takes the public path and the name of a save, and provides
@@ -1630,7 +1628,7 @@ class App {
 
 
     db.get('worlds').once().map().once((val, index)=>{
-      db.get('documents').get(index).once().map().once((res, datI)=>{
+      db.get('documents').get(index).once().map().load((res, datI)=>{
 
         var doc = {};
 
@@ -1713,7 +1711,7 @@ class App {
     }
 
     db.get('worlds').once().map().once((val, index)=>{
-      db.get('worlds').get(index).get('info_json').once(res=>{
+      db.get('worlds').get(index).get('info_json').load(res=>{
         
        var doc = {};
 

+ 99 - 0
public/lib/gundb/axe.js

@@ -0,0 +1,99 @@
+;(function(){
+
+  /* UNBUILD */
+  var root;
+  if(typeof window !== "undefined"){ root = window }
+  if(typeof global !== "undefined"){ root = global }
+  root = root || {};
+  var console = root.console || {log: function(){}};
+  function USE(arg, req){
+    return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){
+      arg(mod = {exports: {}});
+      USE[R(path)] = mod.exports;
+    }
+    function R(p){
+      return p.split('/').slice(-1).toString().replace('.js','');
+    }
+  }
+  if(typeof module !== "undefined"){ var common = module }
+  /* UNBUILD */
+
+  ;USE(function(module){
+    if(typeof window !== "undefined"){ module.window = window }
+    var tmp = module.window || module;
+    var AXE = tmp.AXE || function(){};
+
+    if(AXE.window = module.window){ try{
+      AXE.window.AXE = AXE;
+      tmp = document.createEvent('CustomEvent');
+      tmp.initCustomEvent('extension', false, false, {type: "AXE"});
+      (window.dispatchEvent || window.fireEvent)(tmp);
+      window.postMessage({type: "AXE"}, '*');
+    } catch(e){} }
+
+    try{ if(typeof common !== "undefined"){ common.exports = AXE } }catch(e){}
+    module.exports = AXE;
+  })(USE, './root');
+  
+  ;USE(function(module){
+
+    var AXE = USE('./root'), Gun = (AXE.window||{}).Gun || USE('./gun', 1);
+    (Gun.AXE = AXE).GUN = AXE.Gun = Gun;
+
+    Gun.on('opt', function(at){
+      if(!at.axe){
+        at.axe = {};
+        var p = at.opt.peers, tmp;
+        // 1. If any remembered peers or from last cache or extension
+        // 2. Fallback to use hard coded peers from dApp
+        // 3. Or any offered peers.
+        //if(Gun.obj.empty(p)){
+        //  Gun.obj.map(['http://localhost:8765/gun'/*, 'https://guntest.herokuapp.com/gun'*/], function(url){
+        //    p[url] = {url: url, axe: {}};
+        //  });
+        //}
+        // Our current hypothesis is that it is most optimal
+        // to take peers in a common network, and align
+        // them in a line, where you only have left and right
+        // peers, so messages propagate left and right in
+        // a linear manner with reduced overlap, and
+        // with one common superpeer (with ready failovers)
+        // in case the p2p linear latency is high.
+        // Or there could be plenty of other better options.
+        console.log("axe", at.opt);
+        if(at.opt.super){
+          function verify(msg, send, at) {
+            var peers = Object.keys(p), puts = Object.keys(msg.put), i, j, peer;
+            var soul = puts[0]; /// TODO: verify all souls in puts. Copy the msg only with subscribed souls?
+            for (i=0; i < peers.length; ++i) {
+              peer = p[peers[i]];
+              //if (peer.url) {console.log('AXE do not reject superpeers'); send(msg, peer); continue;} /// always send to superpeers?
+              if (!peer.id) {console.log('AXE peer without id: ', peer); continue;}
+              if (!Gun.subscribe[soul] || !Gun.subscribe[soul][peer.id]) { console.log('AXE SAY reject msg to peer: %s, soul: %s', peer.id, soul); continue; }
+              send(msg, peer);
+            }
+          }
+          AXE.say = function(msg, send, at) {
+            if (!msg.put) { send(msg); return; }
+            console.log('AXE HOOK!! ', msg);
+            verify(msg, send, at);
+          };
+          /// TODO: remove peer from all Gun.subscribe. On `mesh.bye` event?
+        }
+        if(at.opt.super){
+          at.on('in', USE('./lib/super', 1), at);
+        } else {
+          //at.on('in', input, at);
+        }
+      }
+      this.to.next(at); // make sure to call the "next" middleware adapter.
+    });
+
+    function input(msg){
+      var at = this.as, to = this.to;
+    }
+
+    module.exports = AXE;
+  })(USE, './axe');
+
+}());

+ 45 - 38
public/lib/gundb/gun.js

@@ -172,13 +172,13 @@
 			var u, tag = (this.tag || (this.tag = {}))[tag] ||
 			(this.tag[tag] = {tag: tag, to: onto._ = {
 				next: function(arg){ var tmp;
-					if((tmp = this.to)){ 
+					if((tmp = this.to)){
 						tmp.next(arg);
 				}}
 			}});
 			if(arg instanceof Function){
 				var be = {
-					off: onto.off || 
+					off: onto.off ||
 					(onto.off = function(){
 						if(this.next === onto._.next){ return !0 }
 						if(this === this.the.last){
@@ -261,7 +261,7 @@
 			if(v === Infinity){ return false } // we want this to be, but JSON does not support it, sad face.
 			if(text_is(v) // by "text" we mean strings.
 			|| bi_is(v) // by "binary" we mean boolean.
-			|| num_is(v)){ // by "number" we mean integers or decimals. 
+			|| num_is(v)){ // by "number" we mean integers or decimals.
 				return true; // simple values are valid.
 			}
 			return Val.rel.is(v) || false; // is the value a soul relation? Then it is valid and return it. If not, everything else remaining is an invalid data type. Custom extensions can be built on top of these primitives to support other types.
@@ -384,10 +384,10 @@
 		State.ify = function(n, k, s, v, soul){ // put a key's state on a node.
 			if(!n || !n[N_]){ // reject if it is not node-like.
 				if(!soul){ // unless they passed a soul
-					return; 
+					return;
 				}
 				n = Node.soul.ify(n, soul); // then make it so!
-			} 
+			}
 			var tmp = obj_as(n[N_], State._); // grab the states data.
 			if(u !== k && k !== N_){
 				if(num_is(s)){
@@ -400,7 +400,7 @@
 			return n;
 		}
 		State.to = function(from, k, to){
-			var val = from[k]; // BUGGY!
+			var val = (from||{})[k];
 			if(obj_is(val)){
 				val = obj_copy(val);
 			}
@@ -758,7 +758,7 @@
 				if(!at){
 					if(!(cat.opt||empty).super){
 						ctx.souls[soul] = false;
-						return; 
+						return;
 					}
 					at = (ctx.$.get(soul)._);
 				}
@@ -882,8 +882,8 @@
 		;"Please do not remove these messages unless you are paying for a monthly sponsorship, thanks!";
 		Gun.log.once("welcome", "Hello wonderful person! :) Thanks for using GUN, feel free to ask for help on https://gitter.im/amark/gun and ask StackOverflow questions tagged with 'gun'!");
 		;"Please do not remove these messages unless you are paying for a monthly sponsorship, thanks!";
-		
-		if(typeof window !== "undefined"){ (window.Gun = Gun).window = window }
+
+		if(typeof window !== "undefined"){ (window.GUN = window.Gun = Gun).window = window }
 		try{ if(typeof common !== "undefined"){ common.exports = Gun } }catch(e){}
 		module.exports = Gun;
 
@@ -1082,7 +1082,7 @@
 			//if(tmp[cat.id]){ return }
 			tmp.is = tmp.is || at.put;
 			tmp[cat.id] = at.put || true;
-			//if(root.stop){ 
+			//if(root.stop){
 				eve.to.next(msg)
 			//}
 			relate(cat, msg, at, rel);
@@ -1095,7 +1095,7 @@
 			var tmp = (at.root.$.get(rel)._);
 			if(at.has){
 				from = tmp;
-			} else 
+			} else
 			if(from.has){
 				relate(from, msg, from, rel);
 			}
@@ -1147,7 +1147,7 @@
 			if(tmp = via.$){
 				tmp = (chain = via.$.get(key))._;
 				if(u === tmp.put || !Gun.val.link.is(data)){
-					tmp.put = data; 
+					tmp.put = data;
 				}
 			}
 			at.on('in', {
@@ -1287,13 +1287,16 @@
 			return at;
 		}
 		function soul(gun, cb, opt, as){
-			var cat = gun._, tmp;
+			var cat = gun._, acks = 0, tmp;
 			if(tmp = cat.soul){ return cb(tmp, as, cat), gun }
 			if(tmp = cat.link){ return cb(tmp, as, cat), gun }
 			gun.get(function(msg, ev){
+				if(u === msg.put && (tmp = (obj_map(cat.root.opt.peers, function(v,k,t){t(k)})||[]).length) && acks++ <= tmp){
+					return;
+				}
 				ev.rid(msg);
 				var at = ((at = msg.$) && at._) || {};
-				tmp = at.link || at.soul || rel.is(msg.put) || node_soul(msg.put);
+				tmp = at.link || at.soul || rel.is(msg.put) || node_soul(msg.put) || at.dub;
 				cb(tmp, as, msg, ev);
 			}, {out: {get: {'.':true}}});
 			return gun;
@@ -1311,7 +1314,7 @@
 
 			//root.stop && (root.stop.ID = root.stop.ID || Gun.text.random(2));
 			//if((tmp = root.stop) && (tmp = tmp[at.id] || (tmp[at.id] = {})) && tmp[cat.id]){ return } tmp && (tmp[cat.id] = true);
-			if(eve.seen && at.id && eve.seen[at.id]){ return eve.to.next(msg) }	
+			if(eve.seen && at.id && eve.seen[at.id]){ return eve.to.next(msg) }
 			//if((tmp = root.stop)){ if(tmp[at.id]){ return } tmp[at.id] = msg.root; } // temporary fix till a better solution?
 			if((tmp = data) && tmp[rel._] && (tmp = rel.is(tmp))){
 				tmp = ((msg.$$ = at.root.gun.get(tmp))._);
@@ -1319,9 +1322,10 @@
 					msg = obj_to(msg, {put: data = tmp.put});
 				}
 			}
-			if((tmp = root.mum) && at.id){
-				if(tmp[at.id]){ return }
-				if(u !== data && !rel.is(data)){ tmp[at.id] = true; }
+			if((tmp = root.mum) && at.id){ // TODO: can we delete mum entirely now?
+				var id = at.id + (eve.id || (eve.id = Gun.text.random(9)));
+				if(tmp[id]){ return }
+				if(u !== data && !rel.is(data)){ tmp[id] = true; }
 			}
 			as.use(msg, eve);
 			if(eve.stun){
@@ -1343,7 +1347,7 @@
 			//obj.del(map, at); // TODO: Warning: This unsubscribes ALL of this chain's listeners from this link, not just the one callback event.
 			return;
 		}
-		var obj = Gun.obj, obj_has = obj.has, obj_to = Gun.obj.to;
+		var obj = Gun.obj, obj_map = obj.map, obj_has = obj.has, obj_to = Gun.obj.to;
 		var num_is = Gun.num.is;
 		var rel = Gun.val.link, node_soul = Gun.node.soul, node_ = Gun.node._;
 		var empty = {}, u;
@@ -1381,7 +1385,7 @@
 					});
 					return gun;
 				}
-				as.$ = gun = root.get(as.soul);
+				as.$ = root.get(as.soul);
 				as.ref = as.$;
 				ify(as);
 				return gun;
@@ -1536,8 +1540,8 @@
 						as.data = obj_put({}, at.get, as.data);
 					});
 				}
-				tmp = tmp || at.get;
-				at = (at.root.$.get(tmp)._);
+				tmp = tmp || at.soul || at.link || at.dub;// || at.get;
+				at = tmp? (at.root.$.get(tmp)._) : at;
 				as.soul = tmp;
 				data = as.data;
 			}
@@ -1655,19 +1659,19 @@
 			var opt = this.as, cat = opt.at, gun = msg.$, at = gun._, data = at.put || msg.put, link, tmp;
 			if(tmp = msg.$$){
 				link = tmp = (msg.$$._);
-				if(u === tmp.put){
-					return;
+				if(u !== link.put){
+					data = link.put;
 				}
-				data = tmp.put;
 			}
 			if((tmp = eve.wait) && (tmp = tmp[at.id])){ clearTimeout(tmp) }
 			if((!to && (u === data || at.soul || at.link || (link && !(0 < link.ack))))
-			|| (u === data && (tmp = (obj_map(at.root.opt.peers, function(v,k,t){t(k)})||[]).length) && (link||at).ack <= tmp)){
+			|| (u === data && (tmp = (obj_map(at.root.opt.peers, function(v,k,t){t(k)})||[]).length) && (!to && (link||at).ack <= tmp))){
 				tmp = (eve.wait = {})[at.id] = setTimeout(function(){
 					val.call({as:opt}, msg, eve, tmp || 1);
 				}, opt.wait || 99);
 				return;
 			}
+			if(link && u === link.put && (tmp = rel.is(data))){ data = Gun.node.ify({}, tmp) }
 			eve.rid(msg);
 			opt.ok.call(gun || opt.$, data, msg.get);
 		}
@@ -1755,13 +1759,12 @@
 			var gun = this, soul;
 			cb = cb || function(){};
 			opt = opt || {}; opt.item = opt.item || item;
-			if(soul = Gun.node.soul(item)){ return gun.set(gun.back(-1).get(soul), cb, opt) }
+			if(soul = Gun.node.soul(item)){ item = Gun.obj.put({}, soul, Gun.val.link.ify(soul)) }
 			if(!Gun.is(item)){
-				var id = gun.back('opt.uuid')();
-				if(id && Gun.obj.is(item)){
-					return gun.set(gun._.root.$.put(item, id), cb, opt);
+				if(Gun.obj.is(item)){;
+					item = gun.back(-1).get(soul = soul || Gun.node.soul(item) || gun.back('opt.uuid')()).put(item);
 				}
-				return gun.get((Gun.state.lex() + Gun.text.random(7))).put(item, cb, opt);
+				return gun.get(soul || (Gun.state.lex() + Gun.text.random(7))).put(item, cb, opt);
 			}
 			item.get(function(soul, o, msg){
 				if(!soul){ return cb.call(gun, {err: Gun.log('Only a node can be linked! Not "' + msg.put + '"!')}) }
@@ -1774,10 +1777,12 @@
 	;USE(function(module){
 		if(typeof Gun === 'undefined'){ return } // TODO: localStorage is Browser only. But it would be nice if it could somehow plugin into NodeJS compatible localStorage APIs?
 
-		var root, noop = function(){}, u;
-		if(typeof window !== 'undefined'){ root = window }
-		var store = root.localStorage || {setItem: noop, removeItem: noop, getItem: noop};
-
+		var root, noop = function(){}, store, u;
+		try{store = (Gun.window||noop).localStorage}catch(e){}
+		if(!store){
+			console.log("Warning: No localStorage exists to persist data to!");
+			store = {setItem: function(k,v){this[k]=v}, removeItem: function(k){delete this[k]}, getItem: function(k){return this[k]}};
+		}
 		/*
 			NOTE: Both `lib/file.js` and `lib/memdisk.js` are based on this design!
 			If you update anything here, consider updating the other adapters as well.
@@ -1855,7 +1860,7 @@
 			var disk = Gun.obj.ify(store.getItem(opt.prefix)) || {};
 			var lS = function(){}, u;
 			root.on('localStorage', disk); // NON-STANDARD EVENT!
-			
+
 			root.on('put', function(at){
 				this.to.next(at);
 				Gun.graph.is(at.put, null, map);
@@ -1901,7 +1906,7 @@
 				acks = {};
 				if(data){ disk = data }
 				try{store.setItem(opt.prefix, JSON.stringify(disk));
-				}catch(e){ 
+				}catch(e){
 					Gun.log(err = (e || "localStorage failure") + " Consider using GUN's IndexedDB plugin for RAD for more storage space, temporary example at https://github.com/amark/gun/blob/master/test/tmp/indexedDB.html .");
 					root.on('localStorage:error', {err: err, file: opt.prefix, flush: disk, retry: flush});
 				}
@@ -1939,6 +1944,7 @@
 					return;
 				}
 				// add hook for AXE?
+				//if (Gun.AXE && opt && opt.super) { Gun.AXE.say(msg, mesh.say, this); return; } // rogowski
 				mesh.say(msg);
 			}
 
@@ -1975,8 +1981,9 @@
 						}
 						return;
 					}
+          
 					ctx.on('in', msg);
-					
+
 					return;
 				} else
 				if('[' === tmp){

File diff suppressed because it is too large
+ 0 - 0
public/lib/gundb/gun.min.js


+ 4 - 4
public/lib/gundb/lib/bye.js

@@ -3,10 +3,10 @@ var Gun = (typeof window !== "undefined")? window.Gun : require('../gun');
 Gun.on('opt', function(root){
 	this.to.next(root);
 	if(root.once){ return }
-	console.log("WARNING: `lib/bye` is out of date!");
 	root.on('in', function(msg){
-		if(!msg.peer || !msg.BYE){ return this.to.next(msg) }
-		var peer = msg.peer();
+		//Msg did not have a peer property saved before, so nothing ever went further
+		if(!msg.mesh || !msg.BYE){ return this.to.next(msg) }
+		var peer = msg.mesh.via;
 		(peer.bye = peer.bye || []).push(msg.BYE);
 	})
 	root.on('bye', function(peer){
@@ -34,4 +34,4 @@ Gun.chain.bye = function(){
 		return gun;
 	}
 	return bye;
-}
+}

+ 10 - 0
public/lib/gundb/lib/fun.js

@@ -0,0 +1,10 @@
+window.fun = function fun(e){ setTimeout(() => {
+	e = e || {}; var $img = $('<div class="joy"></div>')
+		.css({position: 'fixed', width: 100,
+			top: (e.y || e.clientY || (Math.random() * $(window).height()))-50,
+			left: (e.x || e.clientX || e.pageX || (Math.random() * $(window).width()))-50,
+			transform: 'rotate('+(Math.random() * 360)+'deg)'
+		}).appendTo('body');
+		setTimeout(() => { $img.remove() },800);
+},10)};
+$(document).on('keyup', fun).on('touchstart', fun).on('mousedown', fun);

+ 38 - 0
public/lib/gundb/lib/metae.js

@@ -0,0 +1,38 @@
+$(function(){
+	var m = window.meta = {edit:[], os:{}}, ua = '';
+	try{ua = navigator.userAgent.toLowerCase()}catch(e){}
+	m.os.is = {
+		win: (ua.search("win") >= 0)? "windows":false,
+		lin: (ua.search("linux") >= 0)? "linux":false,
+		mac: (ua.search("mac") >= 0)? "macintosh":false,
+		and: (ua.search("android") >= 0)? "android":false,
+		ios: (ua.search('ipod') >= 0 
+			|| ua.search('iphone') >= 0 
+			|| ua.search('ipad') >= 0)? "ios":false
+	}
+	m.key = {ctrl: 17, cmd: 91};
+	m.key.meta = (m.os.is.win||m.os.is.lin||m.os.is.and)? m.key.ctrl : m.key.cmd;
+	m.key.on = {};
+	$(document).on('keydown', function(e){
+		m.e = e;
+		console.log('keydown', e.keyCode);
+		m.key.on[e.code = e.keyCode] = !0;
+	}).on('keyup', function(e){
+		m.e = e;
+		delete m.key.on[e.code = e.keyCode];
+	}).on('keydown', '[contenteditable=true]', function(e){
+		return;
+		var r = monotype();
+		console.log("keys down", Gun.obj.copy(m.key.on));
+		$.each(m.edit, function(i,edit){ var tmp = true;
+			$.each(edit.keys||[''], function(i,k){
+				if(!m.key.on[k.length? k.charCodeAt(0) : k]){ tmp = false }
+			});
+			console.log(tmp, edit);
+		})
+		r.restore();
+	});
+	m.edit.push({keys: ['B'], on: function(){
+		console.log('hi!');
+	}})
+});

+ 230 - 0
public/lib/gundb/lib/monotype.js

@@ -0,0 +1,230 @@
+;var monotype = monotype || (function(monotype){
+	monotype.range = function(n){
+		var R, s, t, n = n || 0, win = monotype.win || window, doc = win.document;
+		if(!arguments.length) return doc.createRange();
+		if(!(win.Range && R instanceof Range)){
+			s = win.getSelection? win.getSelection() : {};
+			if(s.rangeCount){ 
+				R = s.getRangeAt(n);
+			} else {
+				if(doc.createRange){
+					R = doc.createRange();
+					R.setStart(doc.body, 0);
+				} else 
+				if (doc.selection){ // <IE9
+					R = doc.selection.createRange();
+					R = R.getBookmark();
+				}
+			}
+			s.end = (s.extentNode || s.focusNode || R.startContainer);
+			if(s.anchorNode === s.end){
+				R.direction = s.anchorOffset <= (s.extentOffset || s.focusOffset || 0)? 1 : -1;
+			} else
+			if($.contains(s.anchorNode||{}, s.end||{})){
+				s.end = $(s.anchorNode).contents().filter(s.end).length? s.end : $(s.end).parentsUntil(s.anchorNode).last()[0];
+				R.direction = s.anchorOffset < $(s.anchorNode).contents().index(s.end)? 1 : -1; // Compare immediate descendants to see which comes first.
+			} else {
+				R.direction = s.anchorNode === R.endContainer? -1 : 1; // Checking against startContainer fails going backward.
+			}
+		}
+		return R;
+	}
+	monotype.restore = function(R){
+		var win = monotype.win, doc = win.document;
+		if(R.R && R.restore){ 
+			R.restore();
+			return;
+		}
+		if(win.getSelection){
+			var s = win.getSelection();
+			s.removeAllRanges();
+			if(s.extend && R.direction < 0){
+				R.esC = R.startContainer;
+				R.esO = R.startOffset;
+				R.setStart(R.endContainer, R.endOffset);
+			}
+			s.addRange(R);
+			R.esC && s.extend(R.esC, R.esO);
+		} else {
+			if(doc.body.createTextRange) { // <IE9
+				var ier = doc.body.createTextRange();
+				ier.moveToBookmark(R);
+				ier.select();
+			}
+		}
+	}
+	monotype.text = function(n){
+		return !n? false : (n.nodeType == 3 || n.nodeType == Node.TEXT_NODE);
+	}
+	monotype.prev = function(n,c,d){
+		return !n? null : n === c? null
+		: n[(d?'next':'previous')+'Sibling']?
+			monotype.deep(n[(d?'next':'previous')+'Sibling'],d?-1:Infinity).container
+		: monotype.prev($(n).parent()[0],c,d);
+	}; monotype.next = function(n,c){ return monotype.prev(n,c,1) }
+	monotype.deep = function(n, o, c, i){
+		return i = (o === Infinity? $(n).contents().length-1 : o),
+		i = (i === -1? 0 : i),
+		(c = $(n).contents()).length?
+			monotype.deep(c = c[i < c.length? i : c.length - 1], monotype.text(c)? 0 : o)
+		: {
+			container: n
+			,offset: $(n).text() && o !== -1? (o === Infinity? $(n).text().length : o) : 0
+		};
+	}
+	monotype.count = function(n, o, c){
+		var g = monotype.deep(n, o)
+		, m = g.container
+		, i = g.offset || 0;
+		while(m = monotype.prev(m,c)){
+			i += $(m).text().length;
+		}
+		return i;
+	}
+	monotype.hint = function(n, o, c){
+		var g = monotype.deep(n, o)
+		, m = g.container
+		, i = g.offset || 0
+		, h = [], t;
+		while(m){
+			h.push({
+				t: t = $(m).text()
+				,n: t? 'TEXT' : m.nodeName
+			});
+			m = t? null : monotype.prev(m,c);
+		}
+		if(h.length == 1 && h[0].t){
+			return [];
+		}
+		if((t = $(n).contents()).length && o == t.length){
+			h.push(1); // Indicate that the selection is after the last element.
+		}
+		return h;
+	}
+	monotype.reach = function(i, c, o){
+		o = o || {};
+		o.i = o.i || o.offset || 0;
+		o.$ = o.$? o.$.jquery? o.$ : $(o.$) 
+		: o.container? $(o.container) : $(c);
+		var n = monotype.deep(o.$[0], -1).container, t;
+		while(n){
+			t = $(n).text().length;
+			if(i <= o.i + t){
+				o.$ = $(n);
+				o.i = i - o.i;
+				n = null;
+			} else {
+				o.i += t;
+			}
+			n = monotype.next(n,c);
+		}
+		return o;
+	}
+	return monotype;
+})(function(r,opt){
+	r = r || {};
+	opt = opt || {};
+	monotype.win = opt.win || window;
+	r = r.jquery || monotype.text(r)? {root: $(r)} : r;
+	r.root = $(r.root || monotype.win.document.body);
+	var t, m = monotype;
+	//console.log('_______________________');
+	r.R = m.range(0);
+	r.H = {};
+	r.H.R = $.extend({}, r.R);
+	r.d = r.R.direction || 1;
+	r.t = r.R.toString();
+	r.H.s = m.hint(r.R.startContainer, r.R.startOffset, r.root[0]);
+	r.s = m.count(r.R.startContainer, r.R.startOffset, r.root[0]);
+	t = m.deep(r.R.startContainer, r.R.startOffset);
+	(!t.offset && !r.H.s.length) && (r.s += 0.1); // At the beginning of a text, not at the end of a text.
+	r.H.e = m.hint(r.R.endContainer, r.R.endOffset, r.root[0]);
+	r.e = (function(n, o, c, t){
+		if(r.R.collapsed
+		|| (o === r.R.startOffset 
+		&& n === r.R.startContainer)){
+			return r.s;
+		} c = m.count(n, o, r.root[0]);
+		t = m.deep(n, o);
+		(!t.offset && !r.H.e.length) && (c += 0.1); // Same as above.
+		return c;
+	})(r.R.endContainer, r.R.endOffset);
+	//console.log(r.s, r.R.startOffset, r.H.s, 'M',r.d,'E', r.H.e, r.R.endOffset, r.e);
+	t = r.root.text();
+	r.L = t.length;
+	r.T = {
+		s: t.slice(r.s - 9, r.s)
+		,e: t.slice(r.e, r.e + 9)
+		,t: function(){ return r.T.s + r.T.e }
+	}
+	r.range = function(){
+		//console.log('----');
+		r.H = r.H || {};
+		var s = m.reach(r.s, r.root[0])
+		, st = s.$.text()
+		, e = m.reach(r.e, r.root[0])
+		, et = e.$.text()
+		, R = m.range()
+		, p = function(g, c){ // TODO: BUG! Backtracking in non-Chrome and non-IE9+ browsers. IE9 doesn't like end selections.
+			if(!c || !c.length){
+				return g;
+			}
+			var n = g.$[0], f = [], i = 0, t;
+			while((n = m.next(n,r.root[0])) && ++i < c.length){
+				t = $(n).text();
+				if(t){
+					n = null;
+				} else {
+					f.push(n);
+				}
+			}
+			n = $(f[f.length-1] || g.$);
+			t = n.parent();
+			if(c[c.length-1] === 1 || (i && f.length === i 
+			&& (f.length < c.length-1))){ // tests pass with this condition, yet failed without
+				return {
+					i: t.contents().length
+					,$: t
+				}
+			}
+			if(f.length < c.length - 1){ // despite above's addition, this still gets activated.
+				f = t.contents().slice(n = t.contents().index(n));
+				i = f.map(function(j){ return $(this).text()? (n+j+1) : null})[0] || t.contents().length;
+				f = f.slice(0, i - n);
+				n = f.last()[0];
+				if(g.$[0] === n){
+					return g;
+				}
+				return {
+					$: t
+					,i: t.contents().index(n)
+				}
+			}
+			return {
+				i: 0
+				,$: n
+			};
+		}
+		s = p(s, r.H.s);
+		e = p(e, r.H.e);
+		//console.log("START", parseInt(s.i), 'in """',(s.$[0]),'""" with hint of', r.H.s, 'from original', r.s);
+		//console.log("END", parseInt(e.i), 'in """',(e.$[0]),'""" hint clue of', r.H.e, 'from original', r.e);
+		R.setStart(s.$[0], parseInt(s.i));
+		R.setEnd(e.$[0], parseInt(e.i));
+		return R;
+	}
+	r.restore = function(R){
+		if(r.R.startOffset !== r.H.R.startOffset
+		|| r.R.endOffset !== r.H.R.endOffset
+		|| r.R.startContainer !== r.H.R.startContainer
+		|| r.R.endContainer !== r.H.R.endContainer){
+			r.R = R = r.range();
+		} else {
+			R = r.R;
+		}
+		R.direction = r.d;
+		m.restore(R);
+		return r;
+	}
+	return r;
+});

+ 138 - 0
public/lib/gundb/lib/normalize.js

@@ -0,0 +1,138 @@
+;(function(){
+	function normalize(opt){
+		var el = $(this);
+		opt = opt || $.extend(true, normalize.opt, opt||{});
+		el.children().each(function(){
+			var a = {$: $(this), opt: opt};
+			a.tag = normalize.tag(a.$);
+			$(a.opt.mutate).each(function(i,fn){
+				fn && fn(a);
+			});
+		})
+		return el;
+	};
+	var n = normalize, u;
+	n.get = function(o, p){
+		p = p.split('.');
+		var i = 0, l = p.length, u;
+		while((o = o[p[i++]]) != null && i < l){};
+		return i < l ? u : o;
+	}
+	n.has = function(o,p){
+		return Object.prototype.hasOwnProperty.call(o, p); 
+	}
+	n.tag = function(e){
+		return (($(e)[0]||{}).nodeName||'').toLowerCase();
+	}
+	n.attrs = function(e, cb){
+	  var attr = {};
+	  (e = $(e)) && e.length && $(e[0].attributes||[]).each(function(v,n){
+	  	n = n.nodeName||n.name;
+	    v = e.attr(n);
+			v = cb? cb(v,n,e) : v;
+	    if(v !== u && v !== false){ attr[n] = v }
+	  });
+	  return attr;
+	}
+	n.joint = function(e, d){
+		d = (d? 'next' : 'previous') + 'Sibling'
+		return $(($(e)[0]||{})[d]);
+	}
+	var h = {
+		attr: function(a$, av, al){
+			var l = function(i,v){
+				var t = v;
+				i = al? v : i;
+				v = al? av[v.toLowerCase()] : t;
+				a$.attr(i, v);
+			}
+			al? $(al.sort()).each(l) : $.each(av,l);
+		}
+	}
+	n.opt = { // some reasonable defaults, limited to content alone.
+		tags: {
+			'a': {attrs:{'src':1}, exclude:{'a':1}}, 
+			'b': {exclude:{'b':1}},
+			//'blockquote':1, 
+			'br': {empty: 1},
+			'div': 1,
+			//'code':1, 
+			'i': {exclude:{'i':1}},
+			'img': {attrs:{'src':1}, empty: 1},
+			'li':1, 'ol':1,
+			'p': {exclude:{'p':1,'div':1}},
+			//'pre':1,
+			's': {exclude:{'s':1}},
+			'sub':1, 'sup':1, 
+			'span': {exclude:{'p':1,'ul':1,'ol':1,'li':1,'br':1}}, 
+			'u': {exclude:{'u':1,'p':1}},
+			'ul':1
+		}
+		// a, audio, b, br, div, i, img, li, ol, p, s, span, sub, sup, u, ul, video
+		// button, canvas, embed, form, iframe, input, style, svg, table,
+		// Text: bold, italics, underline, align, bullet, list, 
+		,convert: {
+			'em': 'i', 'strong': 'b'
+		}
+		,attrs: {
+			'id':1
+			,'class':1
+			,'style':1
+		}
+		,mutate: [
+			function(a){ // attr
+				a.attrs = [];
+				a.attr = $.extend(a.opt.attrs, n.get(a.opt,'tags.'+ a.tag +'attrs'));
+				a.attr = n.attrs(a.$, function(v,i){
+					a.$.removeAttr(i);
+					if(a.attr[i.toLowerCase()]){
+						a.attrs.push(i)
+						return v;
+					}
+				});
+				// if this tag is gonna get converted, wait to add attr back till after the convert
+				if(a.attrs && !n.get(a.opt, 'convert.' + a.tag)){
+					h.attr(a.$, a.attr, a.attrs);
+				}
+			}
+			,function(a, tmp){ // convert
+				if(!(tmp = n.get(a.opt,'convert.' + a.tag))){ return }
+				a.attr = a.attr || n.attrs(a.$);
+				a.$.replaceWith(a.$ = $('<'+ (a.tag = tmp.toLowerCase()) +'>').append(a.$.contents()));
+				h.attr(a.$, a.attr, a.attrs);
+			}
+			,function(a, tmp){ // lookahead
+				if((tmp = n.joint(a.$,1)) && (tmp = tmp.contents()).length === 1 && a.tag === n.tag(tmp = tmp.first())){
+					a.$.append(tmp.parent()); // no need to unwrap the child, since the recursion will do it for us
+				}
+			}
+			,function(a){ // recurse
+				// this needs to precede the exclusion and empty.
+				normalize(a);
+			}
+			,function(a, tmp){ // exclude
+				if(!n.get(a.opt,'tags.' + a.tag) 
+				|| ((tmp = n.get(a.opt,'tags.'+ a.tag +'.exclude')) 
+				&& a.$.parents($.map(tmp,function(i,v){return v})+' ').length)
+				){ 
+					a.$.replaceWith(a.$.contents());
+				}
+			}
+			,function(a, tmp){ // prior
+				if((tmp = n.joint(a.$)).length && a.tag === n.tag(tmp)){
+					tmp.append(a.$.contents());
+				}
+			}
+			,function(a){ // empty
+				// should always go last, since the element will be removed!
+				if(a.opt.empty || !n.has(a.opt,'empty')){
+					if(!n.get(a.opt,'tags.'+ a.tag +'.empty') 
+					&& !a.$.contents().length){
+						a.$.remove();
+					}
+				}
+			}
+		]
+	}
+	$.fn.normalize = normalize;
+}());

+ 19 - 0
public/lib/gundb/lib/reboot.js

@@ -0,0 +1,19 @@
+;(function(){
+	var exec = require('child_process').execSync;
+	var dir = __dirname, tmp;
+
+	try{exec("crontab -l");
+	}catch(e){tmp = e}
+	if(0 > tmp.toString().indexOf('no')){ return }
+
+	try{tmp = exec('which node').toString();
+	}catch(e){console.log(e);return}
+
+	try{tmp = exec('echo "@reboot '+tmp+' '+dir+'/../examples/http.js" > '+dir+'/reboot.cron');
+	}catch(e){console.log(e);return}
+
+	try{tmp = exec('crontab '+dir+'/reboot.cron');
+	}catch(e){console.log(e);return}
+	console.log(tmp.toString());
+
+}());

+ 2 - 2
public/lib/gundb/lib/rfs.js

@@ -77,7 +77,7 @@ function Mem(opt){
       cb(null, tmp);
     }, 1);
   };
-  store.list = function(cb, match){
+  store.list = function(cb, match){ // supporting this is no longer needed! Optional.
     setTimeout(function(){
       Gun.obj.map(Object.keys(storage), cb) || cb();
     }, 1);
@@ -85,4 +85,4 @@ function Mem(opt){
   return store;
 }
 
-module.exports = Store;//Gun.TESTING? Mem : Store;
+module.exports = Store;//Gun.TESTING? Mem : Store;

+ 5 - 3
public/lib/gundb/lib/server.js

@@ -3,15 +3,17 @@
 	Gun.serve = require('./serve');
 	//process.env.GUN_ENV = process.env.GUN_ENV || 'debug';
 	Gun.on('opt', function(root){
+		if(u === root.opt.super){
+			root.opt.super = true;
+		}
 		this.to.next(root);
-		if(root.once){ return }
-		if(u !== root.opt.super){ return }
-		root.opt.super = true;
 	})
 	require('../nts');
 	require('./store');
 	require('./rs3');
 	require('./wire');
+	try{require('../sea');}catch(e){}
+	//try{require('../axe');}catch(e){}
 	require('./file');
 	require('./evict');
 	if('debug' === process.env.GUN_ENV){ require('./debug') }

+ 29 - 0
public/lib/gundb/lib/super.js

@@ -0,0 +1,29 @@
+;(function(){
+	var Gun = (typeof window !== "undefined")? window.Gun : require('../gun');
+	var Rad = (Gun.window||{}).Radix || require('./radix');
+	function input(msg){
+		var at = this.as, to = this.to, peer = (msg.mesh||empty).via;
+		var get = msg.get, soul, key;
+		if(!peer || !get){ return to.next(msg) }
+		console.log("super", msg);
+		if(soul = get['#']){
+			if(key = get['.']){
+
+			} else {
+
+			}
+			subscribe(soul, peer, msg);
+		}
+		to.next(msg);
+	}
+	/// Store the subscribes
+	Gun.subscribe = {}; /// TODO: use Rad instead of plain object?
+	function subscribe(soul, peer, msg) {
+		if (!peer.id) { console.log('super jump peer without id: ', peer, msg); return; } /// TODO: this occurs in first subscription. Use peer reference or peer.wire.id?
+		Gun.subscribe[soul] = Gun.subscribe[soul] || {};
+		Gun.subscribe[soul][peer.id] = 1;
+	}
+	var empty = {}, u;
+	if(Gun.window){ return }
+	try{module.exports = input}catch(e){}
+}());

File diff suppressed because it is too large
+ 329 - 721
public/lib/gundb/sea.js


+ 0 - 63
public/lib/gundb/sea/authenticate.js

@@ -1,63 +0,0 @@
-
-    var SEA = require('./sea');
-    var Gun = SEA.Gun;
-    const queryGunAliases = require('./query')
-    const parseProps = require('./parse')
-    // This is internal User authentication func.
-    const authenticate = async (alias, pass, gunRoot) => {
-      // load all public keys associated with the username alias we want to log in with.
-      const aliases = (await queryGunAliases(alias, gunRoot))
-      .filter(a => !!a.pub && !!a.put)
-      // Got any?
-      if (!aliases.length) {
-        throw { err: 'Public key does not exist!' }
-      }
-      let err
-      // then attempt to log into each one until we find ours!
-      // (if two users have the same username AND the same password... that would be bad)
-      const users = await Promise.all(aliases.map(async (a, i) => {
-        // attempt to PBKDF2 extend the password with the salt. (Verifying the signature gives us the plain text salt.)
-        const auth = parseProps(a.put.auth)
-      // NOTE: aliasquery uses `gun.get` which internally SEA.read verifies the data for us, so we do not need to re-verify it here.
-      // SEA.verify(at.put.auth, pub).then(function(auth){
-        try {
-          const proof = await SEA.work(pass, auth.s)
-          //const props = { pub: pub, proof: proof, at: at }
-          // the proof of work is evidence that we've spent some time/effort trying to log in, this slows brute force.
-          /*
-          MARK TO @mhelander : pub vs epub!???
-          */
-          const salt = auth.salt
-          const sea = await SEA.decrypt(auth.ek, proof)
-          if (!sea) {
-            err = 'Failed to decrypt secret! ' + (i+1) +'/'+aliases.length;
-            return
-          }
-          // now we have AES decrypted the private key, from when we encrypted it with the proof at registration.
-          // if we were successful, then that meanswe're logged in!
-          const priv = sea.priv
-          const epriv = sea.epriv
-          const epub = a.put.epub
-          // TODO: 'salt' needed?
-          err = null
-          if(SEA.window){
-            var tmp = SEA.window.sessionStorage;
-            if(tmp && gunRoot._.opt.remember){
-              SEA.window.sessionStorage.alias = alias;
-              SEA.window.sessionStorage.tmp = pass;
-            }
-          }
-          return {priv: priv, pub: a.put.pub, salt: salt, epub: epub, epriv: epriv };
-        } catch (e) {
-          err = 'Failed to decrypt secret!'
-          throw { err }
-        }
-      }))
-      var user = Gun.list.map(users, function(acc){ if(acc){ return acc } })
-      if (!user) {
-        throw { err: err || 'Public key does not exist!' }
-      }
-      return user
-    }
-    module.exports = authenticate;
-  

+ 1 - 1
public/lib/gundb/sea/buffer.js

@@ -45,7 +45,7 @@
           }
           return buf
         }
-        const byteLength = input.byteLength
+        const byteLength = input.byteLength // what is going on here? FOR MARTTI
         const length = input.byteLength ? input.byteLength : input.length
         if (length) {
           let buf

+ 217 - 204
public/lib/gundb/sea/create.js

@@ -2,257 +2,270 @@
     // TODO: This needs to be split into all separate functions.
     // Not just everything thrown into 'create'.
 
-    const SEA = require('./sea')
-    const User = require('./user')
-    const authRecall = require('./recall')
-    const authsettings = require('./settings')
-    const authenticate = require('./authenticate')
-    const finalizeLogin = require('./login')
-    const authLeave = require('./leave')
-    const _initial_authsettings = require('./settings').recall
-    const Gun = SEA.Gun;
+    var SEA = require('./sea');
+    var User = require('./user');
+    var authsettings = require('./settings');
+    var Gun = SEA.Gun;
+
+    var noop = function(){};
 
-    var u;
     // Well first we have to actually create a user. That is what this function does.
-    User.prototype.create = function(username, pass, cb, opt){
-      // TODO: Needs to be cleaned up!!!
-      const gunRoot = this.back(-1)
-      var gun = this, cat = (gun._);
-      cb = cb || function(){};
+    User.prototype.create = function(alias, pass, cb, opt){
+      var gun = this, cat = (gun._), root = gun.back(-1);
+      cb = cb || noop;
       if(cat.ing){
         cb({err: Gun.log("User is already being created or authenticated!"), wait: true});
         return gun;
       }
       cat.ing = true;
       opt = opt || {};
-      var resolve = function(){}, reject = resolve;
-      // Because more than 1 user might have the same username, we treat the alias as a list of those users.
-      if(cb){ resolve = reject = cb }
-      gunRoot.get('~@'+username).get(async (at, ev) => {
-        ev.off()
-        if (at.put && !opt.already) {
+      var act = {}, u;
+      act.a = function(pubs){
+        act.pubs = pubs;
+        if(pubs && !opt.already){
           // If we can enforce that a user name is already taken, it might be nice to try, but this is not guaranteed.
-          const err = 'User already created!'
-          Gun.log(err)
+          var ack = {err: Gun.log('User already created!')};
           cat.ing = false;
+          cb(ack);
           gun.leave();
-          return reject({ err: err })
+          return;
         }
-        const salt = Gun.text.random(64)
-        // pseudo-randomly create a salt, then use CryptoJS's PBKDF2 function to extend the password with it.
-        try {
-          const proof = await SEA.work(pass, salt)
-          // this will take some short amount of time to produce a proof, which slows brute force attacks.
-          const pairs = await SEA.pair()
-          // now we have generated a brand new ECDSA key pair for the user account.
-          const pub = pairs.pub
-          const priv = pairs.priv
-          const epriv = pairs.epriv
-          // the user's public key doesn't need to be signed. But everything else needs to be signed with it!
-          const alias = await SEA.sign(username, pairs)
-          if(u === alias){ throw SEA.err }
-          const epub = await SEA.sign(pairs.epub, pairs)
-          if(u === epub){ throw SEA.err }
-          // to keep the private key safe, we AES encrypt it with the proof of work!
-          const auth = await SEA.encrypt({ priv: priv, epriv: epriv }, proof)
-          .then((auth) => // TODO: So signedsalt isn't needed?
-          // SEA.sign(salt, pairs).then((signedsalt) =>
-            SEA.sign({ek: auth, s: salt}, pairs)
-          // )
-          ).catch((e) => { Gun.log('SEA.en or SEA.write calls failed!'); cat.ing = false; gun.leave(); reject(e) })
-          const user = { alias: alias, pub: pub, epub: epub, auth: auth }
-          const tmp = '~'+pairs.pub;
-          // awesome, now we can actually save the user with their public key as their ID.
-          try{
-
-          gunRoot.get(tmp).put(user)
-        }catch(e){console.log(e)}
-          // next up, we want to associate the alias with the public key. So we add it to the alias list.
-          gunRoot.get('~@'+username).put(Gun.obj.put({}, tmp, Gun.val.link.ify(tmp)))
-          // callback that the user has been created. (Note: ok = 0 because we didn't wait for disk to ack)
-          setTimeout(() => { cat.ing = false; resolve({ ok: 0, pub: pairs.pub}) }, 10) // TODO: BUG! If `.auth` happens synchronously after `create` finishes, auth won't work. This setTimeout is a temporary hack until we can properly fix it.
-        } catch (e) {
-          Gun.log('SEA.create failed!')
-          cat.ing = false;
-          gun.leave();
-          reject(e)
+        act.salt = Gun.text.random(64); // pseudo-randomly create a salt, then use PBKDF2 function to extend the password with it.
+        SEA.work(pass, act.salt, act.b); // this will take some short amount of time to produce a proof, which slows brute force attacks.
+      }
+      act.b = function(proof){
+        act.proof = proof;
+        SEA.pair(act.c); // now we have generated a brand new ECDSA key pair for the user account.
+      }
+      act.c = function(pair){ var tmp;
+        act.pair = pair || {};
+        if(tmp = cat.root.user){
+          tmp._.sea = pair;
+          tmp.is = {pub: pair.pub, epub: pair.epub, alias: alias};
         }
-      })
-      return gun;  // gun chain commands must return gun chains!
+        // the user's public key doesn't need to be signed. But everything else needs to be signed with it! // we have now automated it! clean up these extra steps now!
+        act.data = {pub: pair.pub};
+        SEA.sign(alias, pair, act.d); 
+      }
+      act.d = function(sig){
+        act.data.alias = alias || sig;
+        SEA.sign(act.pair.epub, act.pair, act.e);
+      }
+      act.e = function(epub){
+        act.data.epub = act.pair.epub || epub; 
+        SEA.encrypt({priv: act.pair.priv, epriv: act.pair.epriv}, act.proof, act.f); // to keep the private key safe, we AES encrypt it with the proof of work!
+      }
+      act.f = function(auth){
+        act.data.auth = JSON.stringify({ek: auth, s: act.salt}); 
+        SEA.sign({ek: auth, s: act.salt}, act.pair, act.g);
+      }
+      act.g = function(auth){ var tmp;
+        act.data.auth = act.data.auth || auth;
+        root.get(tmp = '~'+act.pair.pub).put(act.data); // awesome, now we can actually save the user with their public key as their ID.
+        root.get('~@'+alias).put(Gun.obj.put({}, tmp, Gun.val.link.ify(tmp))); // next up, we want to associate the alias with the public key. So we add it to the alias list.
+        setTimeout(function(){ // we should be able to delete this now, right?
+        cat.ing = false;
+        cb({ok: 0, pub: act.pair.pub}); // callback that the user has been created. (Note: ok = 0 because we didn't wait for disk to ack)
+        if(noop === cb){ gun.auth(alias, pass) } // if no callback is passed, auto-login after signing up.
+        },10);
+      }
+      root.get('~@'+alias).once(act.a);
+      return gun;
     }
     // now that we have created a user, we want to authenticate them!
     User.prototype.auth = function(alias, pass, cb, opt){
-      // TODO: Needs to be cleaned up!!!!
-      const opts = opt || (typeof cb !== 'function' && cb)
-      let pin = opts && opts.pin
-      let newpass = opts && opts.newpass
-      const gunRoot = this.back(-1)
-      cb = typeof cb === 'function' ? cb : () => {}
-      newpass = newpass || (opts||{}).change;
-      var gun = this, cat = (gun._);
+      var gun = this, cat = (gun._), root = gun.back(-1);
+      cb = cb || function(){};
       if(cat.ing){
-        cb({err: "User is already being created or authenticated!", wait: true});
+        cb({err: Gun.log("User is already being created or authenticated!"), wait: true});
         return gun;
       }
       cat.ing = true;
-
-      if (!pass && pin) { (async function(){
-        try {
-          var r = await authRecall(gunRoot, { alias: alias, pin: pin })
-          return cat.ing = false, cb(r), gun;
-        } catch (e) {
-          var err = { err: 'Auth attempt failed! Reason: No session data for alias & PIN' }
-          return cat.ing = false, gun.leave(), cb(err), gun;
-        }}())
-        return gun;
+      opt = opt || {};
+      var pair = (alias && (alias.pub || alias.epub))? alias : (pass && (pass.pub || pass.epub))? pass : null;
+      var act = {}, u;
+      act.a = function(data){
+        if(!data){ return act.b() }
+        if(!data.pub){
+          var tmp = [];
+          Gun.node.is(data, function(v){ tmp.push(v) })
+          return act.b(tmp);
+        }
+        if(act.name){ return act.f(data) }
+        act.c((act.data = data).auth);
       }
-
-      const putErr = (msg) => (e) => {
-        const { message, err = message || '' } = e
-        Gun.log(msg)
-        var error = { err: msg+' Reason: '+err }
-        return cat.ing = false, gun.leave(), cb(error), gun;
+      act.b = function(list){
+        var get = (act.list = (act.list||[]).concat(list||[])).shift();
+        if(u === get){
+          if(act.name){ return act.err('Your user account is not published for dApps to access, please consider syncing it online, or allowing local access by adding your device as a peer.') }
+          return act.err('Wrong user or password.') 
+        }
+        root.get(get).once(act.a);
       }
-
-      (async function(){ try {
-        const keys = await authenticate(alias, pass, gunRoot)
-        if (!keys) {
-          return putErr('Auth attempt failed!')({ message: 'No keys' })
+      act.c = function(auth){
+        if(u === auth){ return act.b() }
+        if(Gun.text.is(auth)){ return act.c(Gun.obj.ify(auth)) } // new format
+        SEA.work(pass, (act.auth = auth).s, act.d, act.enc); // the proof of work is evidence that we've spent some time/effort trying to log in, this slows brute force.
+      }
+      act.d = function(proof){
+        SEA.decrypt(act.auth.ek, proof, act.e, act.enc);
+      }
+      act.e = function(half){
+        if(u === half){
+          if(!act.enc){ // try old format
+            act.enc = {encode: 'utf8'};
+            return act.c(act.auth);
+          } act.enc = null; // end backwards
+          return act.b();
         }
-        const pub = keys.pub
-        const priv = keys.priv
-        const epub = keys.epub
-        const epriv = keys.epriv
-        // we're logged in!
-        if (newpass) {
-          // password update so encrypt private key using new pwd + salt
-          try {
-            const salt = Gun.text.random(64);
-            const encSigAuth = await SEA.work(newpass, salt)
-            .then((key) =>
-              SEA.encrypt({ priv: priv, epriv: epriv }, key)
-              .then((auth) => SEA.sign({ek: auth, s: salt}, keys))
-            )
-            const signedEpub = await SEA.sign(epub, keys)
-            const signedAlias = await SEA.sign(alias, keys)
-            const user = {
-              pub: pub,
-              alias: signedAlias,
-              auth: encSigAuth,
-              epub: signedEpub
-            }
-            // awesome, now we can update the user using public key ID.
-            gunRoot.get('~'+user.pub).put(user)
-            // then we're done
-            const login = finalizeLogin(alias, keys, gunRoot, { pin })
-            login.catch(putErr('Failed to finalize login with new password!'))
-            return cat.ing = false, cb(await login), gun
-          } catch (e) {
-            return putErr('Password set attempt failed!')(e)
-          }
-        } else {
-          const login = finalizeLogin(alias, keys, gunRoot, { pin: pin })
-          login.catch(putErr('Finalizing login failed!'))
-          return cat.ing = false, cb(await login), gun;
+        act.half = half;
+        act.f(act.data);
+      }
+      act.f = function(data){
+        if(!data || !data.pub){ return act.b() }
+        var tmp = act.half || {};
+        act.g({pub: data.pub, epub: data.epub, priv: tmp.priv, epriv: tmp.epriv});
+      }
+      act.g = function(pair){
+        act.pair = pair;
+        var user = (root._).user, at = (user._);
+        var tmp = at.tag;
+        var upt = at.opt;
+        at = user._ = root.get('~'+pair.pub)._;
+        at.opt = upt;
+        // add our credentials in-memory only to our root user instance
+        user.is = {pub: pair.pub, epub: pair.epub, alias: alias};
+        at.sea = act.pair;
+        cat.ing = false;
+        if(pass && !Gun.text.is(act.data.auth)){ opt.shuffle = opt.change = pass; } // migrate UTF8 + Shuffle! Test against NAB alias test_sea_shuffle + passw0rd
+        opt.change? act.z() : cb(at);
+        if(SEA.window && ((gun.back('user')._).opt||opt).remember){
+          // TODO: this needs to be modular.
+          try{var sS = {};
+          sS = window.sessionStorage;
+          sS.recall = true;
+          sS.alias = alias;
+          sS.tmp = pass;
+          }catch(e){}
         }
-      } catch (e) {
-        return putErr('Auth attempt failed!')(e)
-      } }());
+        try{
+          (root._).on('auth', at) // TODO: Deprecate this, emit on user instead! Update docs when you do.
+          //at.on('auth', at) // Arrgh, this doesn't work without event "merge" code, but "merge" code causes stack overflow and crashes after logging in & trying to write data.
+        }catch(e){
+          Gun.log("Your 'auth' callback crashed with:", e);
+        }
+      }
+      act.z = function(){
+        // password update so encrypt private key using new pwd + salt
+        act.salt = Gun.text.random(64); // pseudo-random
+        SEA.work(opt.change, act.salt, act.y);
+      }
+      act.y = function(proof){
+        SEA.encrypt({priv: act.pair.priv, epriv: act.pair.epriv}, proof, act.x);
+      }
+      act.x = function(auth){
+        act.w(JSON.stringify({ek: auth, s: act.salt}));
+        //SEA.sign({ek: auth, s: act.salt}, act.pair, act.w);
+      }
+      act.w = function(auth){
+        if(opt.shuffle){ // delete in future!
+          var tmp = Gun.obj.to(act.data);
+          Gun.obj.del(tmp, '_');
+          tmp.auth = auth;
+          console.log('migrate core account from UTF8 & shuffle', tmp);
+          root.get('~'+act.pair.pub).put(tmp);
+        } // end delete
+        root.get('~'+act.pair.pub).get('auth').put(auth, cb);
+      }
+      act.err = function(e){
+        var ack = {err: Gun.log(e || 'User cannot be found!')};
+        cat.ing = false;
+        cb(ack);
+      }
+      act.plugin = function(name){
+        if(!(act.name = name)){ return act.err() }
+        var tmp = [name];
+        if('~' !== name[0]){
+          tmp[1] = '~'+name;
+          tmp[2] = '~@'+name;
+        }
+        act.b(tmp);
+      }
+      if(pair){
+        act.g(pair);
+      } else
+      if(alias){
+        root.get('~@'+alias).once(act.a);
+      } else
+      if(!alias && !pass){
+        SEA.name(act.plugin);
+      }
       return gun;
     }
     User.prototype.pair = function(){
+      console.log("user.pair() IS DEPRECATED AND WILL BE DELETED!!!");
       var user = this;
       if(!user.is){ return false }
       return user._.sea;
     }
-    User.prototype.leave = async function(){
+    User.prototype.leave = function(opt, cb){
       var gun = this, user = (gun.back(-1)._).user;
       if(user){
         delete user.is;
         delete user._.is;
         delete user._.sea;
       }
-      if(typeof window !== 'undefined'){
-        var tmp = window.sessionStorage;
-        delete tmp.alias;
-        delete tmp.tmp;
+      if(SEA.window){
+        try{var sS = {};
+        sS = window.sessionStorage;
+        delete sS.alias;
+        delete sS.tmp;
+        delete sS.recall;
+        }catch(e){};
       }
-      return await authLeave(this.back(-1))
+      return gun;
     }
     // If authenticated user wants to delete his/her account, let's support it!
-    User.prototype.delete = async function(alias, pass){
-      const gunRoot = this.back(-1)
+    User.prototype.delete = async function(alias, pass, cb){
+      var gun = this, root = gun.back(-1), user = gun.back('user');
       try {
-        const __gky40 = await authenticate(alias, pass, gunRoot)
-        const pub = __gky40.pub
-        await authLeave(gunRoot, alias)
-        // Delete user data
-        gunRoot.get('~'+pub).put(null)
-        // Wipe user data from memory
-        const { user = { _: {} } } = gunRoot._;
-        // TODO: is this correct way to 'logout' user from Gun.User ?
-        [ 'alias', 'sea', 'pub' ].map((key) => delete user._[key])
-        user._.is = user.is = {}
-        gunRoot.user()
-        return { ok: 0 }  // TODO: proper return codes???
+        user.auth(alias, pass, function(ack){
+          var pub = (user.is||{}).pub;
+          // Delete user data
+          user.map().once(function(){ this.put(null) });
+          // Wipe user data from memory
+          user.leave();
+          (cb || noop)({ok: 0});
+        });
       } catch (e) {
-        Gun.log('User.delete failed! Error:', e)
-        throw e // TODO: proper error codes???
+        Gun.log('User.delete failed! Error:', e);
       }
+      return gun;
     }
-    // If authentication is to be remembered over reloads or browser closing,
-    // set validity time in minutes.
-    User.prototype.recall = function(setvalidity, options){
-      var gun = this;
-      const gunRoot = this.back(-1)
-
-      let validity
-      let opts
-      
-      var o = setvalidity;
-      if(o && o.sessionStorage){
-        if(typeof window !== 'undefined'){
-          var tmp = window.sessionStorage;
-          if(tmp){
-            gunRoot._.opt.remember = true;
-            if(tmp.alias && tmp.tmp){
-              gunRoot.user().auth(tmp.alias, tmp.tmp);
+    User.prototype.recall = function(opt, cb){
+      var gun = this, root = gun.back(-1), tmp;
+      opt = opt || {};
+      if(opt && opt.sessionStorage){
+        if(SEA.window){
+          try{var sS = {};
+          sS = window.sessionStorage;
+          if(sS){
+            (root._).opt.remember = true;
+            ((gun.back('user')._).opt||opt).remember = true;
+            if(sS.recall || (sS.alias && sS.tmp)){
+              root.user().auth(sS.alias, sS.tmp, cb);
             }
           }
+          }catch(e){}
         }
         return gun;
       }
-
-      if (!Gun.val.is(setvalidity)) {
-        opts = setvalidity
-        validity = _initial_authsettings.validity
-      } else {
-        opts = options
-        validity = setvalidity * 60 // minutes to seconds
-      }
-
-      try {
-        // opts = { hook: function({ iat, exp, alias, proof }) }
-        // iat == Date.now() when issued, exp == seconds to expire from iat
-        // How this works:
-        // called when app bootstraps, with wanted options
-        // IF authsettings.validity === 0 THEN no remember-me, ever
-        // IF PIN then signed 'remember' to window.sessionStorage and 'auth' to IndexedDB
-        authsettings.validity = typeof validity !== 'undefined'
-        ? validity : _initial_authsettings.validity
-        authsettings.hook = (Gun.obj.has(opts, 'hook') && typeof opts.hook === 'function')
-        ? opts.hook : _initial_authsettings.hook
-        // All is good. Should we do something more with actual recalled data?
-        (async function(){ await authRecall(gunRoot) }());
-        return gun;
-      } catch (e) {
-        const err = 'No session!'
-        Gun.log(err)
-        // NOTE! It's fine to resolve recall with reason why not successful
-        // instead of rejecting...
-        //return { err: (e && e.err) || err }
-        return gun;
-      }
+      /*
+        TODO: copy mhelander's expiry code back in.
+        Although, we should check with community,
+        should expiry be core or a plugin?
+      */
+      return gun;
     }
     User.prototype.alive = async function(){
       const gunRoot = this.back(-1)
@@ -278,7 +291,7 @@
     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 = '';
-      gun.back(function(at){ if(at.pub){ return } path += (at.get||'') });
+      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();
       sec = await SEA.decrypt(sec, pair);
@@ -299,7 +312,7 @@
     User.prototype.secret = function(data, cb){
       console.log("`.secret` API MAY BE DELETED OR CHANGED OR RENAMED, DO NOT USE!");
       var gun = this, user = gun.back(-1).user(), pair = user.pair(), path = '';
-      gun.back(function(at){ if(at.pub){ return } path += (at.get||'') });
+      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();
       sec = await SEA.decrypt(sec, pair);

+ 18 - 5
public/lib/gundb/sea/decrypt.js

@@ -6,18 +6,31 @@
     var parse = require('./parse');
 
     SEA.decrypt = SEA.decrypt || (async (data, pair, cb, opt) => { try {
-      var opt = opt || {};
-      const key = pair.epriv || pair;
+      opt = opt || {};
+      var key = (pair||opt).epriv || pair;
+      if(!key){
+        pair = await SEA.I(null, {what: data, how: 'decrypt', why: opt.why});
+        key = pair.epriv || pair;
+      }
       const json = parse(data)
-      const ct = await aeskey(key, shim.Buffer.from(json.s, 'utf8'), opt)
+
+      var buf; try{buf = shim.Buffer.from(json.s, opt.encode || 'base64') // NEW DEFAULT!
+      }catch(e){buf = shim.Buffer.from(json.s, 'utf8')} // AUTO BACKWARD OLD UTF8 DATA!
+      var bufiv; try{bufiv = shim.Buffer.from(json.iv, opt.encode || 'base64') // NEW DEFAULT!
+      }catch(e){bufiv = shim.Buffer.from(json.iv, 'utf8')} // AUTO BACKWARD OLD UTF8 DATA!
+      var bufct; try{bufct = shim.Buffer.from(json.ct, opt.encode || 'base64') // NEW DEFAULT!
+      }catch(e){bufct = shim.Buffer.from(json.ct, 'utf8')} // AUTO BACKWARD OLD UTF8 DATA!
+
+      const ct = await aeskey(key, buf, opt)
       .then((aes) => (/*shim.ossl ||*/ shim.subtle).decrypt({  // Keeping aesKey scope as private as possible...
-        name: opt.name || 'AES-GCM', iv: new Uint8Array(shim.Buffer.from(json.iv, 'utf8'))
-      }, aes, new Uint8Array(shim.Buffer.from(json.ct, 'utf8'))))
+        name: opt.name || 'AES-GCM', iv: new Uint8Array(bufiv)
+      }, aes, new Uint8Array(bufct)))
       const r = parse(new shim.TextDecoder('utf8').decode(ct))
       if(cb){ try{ cb(r) }catch(e){console.log(e)} }
       return r;
     } catch(e) { 
       SEA.err = e;
+      if(SEA.throw){ throw e }
       if(cb){ cb() }
       return;
     }});

+ 10 - 5
public/lib/gundb/sea/encrypt.js

@@ -5,8 +5,12 @@
     var aeskey = require('./aeskey');
 
     SEA.encrypt = SEA.encrypt || (async (data, pair, cb, opt) => { try {
-      var opt = opt || {};
-      const key = pair.epriv || pair;
+      opt = opt || {};
+      var key = (pair||opt).epriv || pair;
+      if(!key){
+        pair = await SEA.I(null, {what: data, how: 'encrypt', why: opt.why});
+        key = pair.epriv || pair;
+      }
       const msg = JSON.stringify(data)
       const rand = {s: shim.random(8), iv: shim.random(16)};
       const ct = await aeskey(key, rand.s, opt)
@@ -14,15 +18,16 @@
         name: opt.name || 'AES-GCM', iv: new Uint8Array(rand.iv)
       }, aes, new shim.TextEncoder().encode(msg)))
       const r = 'SEA'+JSON.stringify({
-        ct: shim.Buffer.from(ct, 'binary').toString('utf8'),
-        iv: rand.iv.toString('utf8'),
-        s: rand.s.toString('utf8')
+        ct: shim.Buffer.from(ct, 'binary').toString(opt.encode || 'base64'),
+        iv: rand.iv.toString(opt.encode || 'base64'),
+        s: rand.s.toString(opt.encode || 'base64')
       });
 
       if(cb){ try{ cb(r) }catch(e){console.log(e)} }
       return r;
     } catch(e) { 
       SEA.err = e;
+      if(SEA.throw){ throw e }
       if(cb){ cb() }
       return;
     }});

+ 2 - 2
public/lib/gundb/sea/https.js

@@ -1,10 +1,10 @@
 
     var SEA = require('./root');
-    if(SEA.window){
+    try{ if(SEA.window){
       if(location.protocol.indexOf('s') < 0
       && location.host.indexOf('localhost') < 0
       && location.protocol.indexOf('file:') < 0){
         location.protocol = 'https:'; // WebCrypto does NOT work without HTTPS!
       }
-    }
+    } }catch(e){}
   

+ 24 - 9
public/lib/gundb/sea/index.js

@@ -34,6 +34,8 @@
       Gun.node.is(msg.put, function(val, key, node){ c++; // for each property on the node
         // TODO: consider async/await use here...
         SEA.verify(val, false, function(data){ c--; // false just extracts the plain data.
+          var tmp = data;
+          data = SEA.opt.unpack(data, key, node);
           node[key] = val = data; // transform to plain value.
           if(d && !c && (c = -1)){ to.next(msg) }
         });
@@ -111,9 +113,9 @@
             return each.end({err: "Account must match!"});
           }
           check['user'+soul+key] = 1;
-          if(user && (user = user._) && user.sea && pub === user.pub){
+          if(user && user.is && pub === user.is.pub){
             //var id = Gun.text.random(3);
-            SEA.sign(val, user.sea, function(data){ var rel;
+            SEA.sign([soul, key, val, Gun.state.is(node, key)], (user._).sea, function(data){ var rel;
               if(u === data){ return each.end({err: SEA.err || 'Pub signature fail.'}) }
               if(rel = Gun.val.link.is(val)){
                 (at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true;
@@ -126,6 +128,7 @@
             return;
           }
           SEA.verify(val, pub, function(data){ var rel, tmp;
+            data = SEA.opt.unpack(data, key, node);
             if(u === data){ // make sure the signature matches the account it claims to be on.
               return each.end({err: "Unverified data."}); // reject any updates that are signed with a mismatched account.
             }
@@ -146,10 +149,11 @@
           return s;
         }
         each.any = function(val, key, node, soul, user){ var tmp, pub;
-          if(!user || !(user = user._) || !(user = user.sea)){
+          if(!user || !user.is){
             if(tmp = relpub(soul)){
               check['any'+soul+key] = 1;
               SEA.verify(val, pub = tmp, function(data){ var rel;
+                data = SEA.opt.unpack(data, key, node);
                 if(u === data){ return each.end({err: "Mismatched owner on '" + key + "'."}) } // thanks @rogowski !
                 if((rel = Gun.val.link.is(data)) && pub === relpub(rel)){
                   (at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true;
@@ -187,20 +191,19 @@
             //});
             return;
           }
-          var pub = tmp;
-          if(pub !== user.pub){
+          if(!msg.I || (pub = tmp) !== (user.is||noop).pub){
             each.any(val, key, node, soul);
             return;
           }
           /*var other = Gun.obj.map(at.sea.own[soul], function(v, p){
-            if(user.pub !== p){ return p }
+            if((user.is||{}).pub !== p){ return p }
           });
           if(other){
             each.any(val, key, node, soul);
             return;
           }*/
           check['any'+soul+key] = 1;
-          SEA.sign(val, user, function(data){
+          SEA.sign([soul, key, val, Gun.state.is(node, key)], (user._).sea, function(data){
             if(u === data){ return each.end({err: 'My signature fail.'}) }
             node[key] = data;
             check['any'+soul+key] = 0;
@@ -210,7 +213,7 @@
         each.end = function(ctx){ // TODO: Can't you just switch this to each.end = cb?
           if(each.err){ return }
           if((each.err = ctx.err) || ctx.no){
-            console.log('NO!', each.err, msg.put);
+            console.log('NO!', each.err, msg.put); // 451 mistmached data FOR MARTTI
             return;
           }
           if(!each.end.ed){ return }
@@ -225,5 +228,17 @@
       }
       to.next(msg); // pass forward any data we do not know how to handle or process (this allows custom security protocols).
     }
+    SEA.opt.unpack = function(data, key, node){
+      if(u === data){ return }
+      var tmp = data, soul = Gun.node.soul(node), s = Gun.state.is(node, key);
+      if(tmp && 4 === tmp.length && soul === tmp[0] && key === tmp[1] && s === tmp[3]){
+        return tmp[2];
+      }
+      if(s < SEA.opt.shuffle_attack){
+        return data;
+      }
+    }
+    SEA.opt.shuffle_attack = 1546329600000; // Jan 1, 2019
+    var noop = {}, u;
 
-  
+  

+ 0 - 21
public/lib/gundb/sea/leave.js

@@ -1,21 +0,0 @@
-
-    const authPersist = require('./persist')
-    const authsettings = require('./settings')
-    //const { scope: seaIndexedDb } = require('./indexed')
-    // This internal func executes logout actions
-    const authLeave = async (gunRoot, alias = gunRoot._.user._.alias) => {
-      var user = gunRoot._.user._ || {};
-      [ 'get', 'soul', 'ack', 'put', 'is', 'alias', 'pub', 'epub', 'sea' ].map((key) => delete user[key])
-      if(user.$){
-        delete user.$.is;
-      }
-      // Let's use default
-      gunRoot.user();
-      // Removes persisted authentication & CryptoKeys
-      try {
-        await authPersist({ alias: alias })
-      } catch (e) {}  //eslint-disable-line no-empty
-      return { ok: 0 }
-    }
-    module.exports = authLeave
-  

+ 0 - 49
public/lib/gundb/sea/login.js

@@ -1,49 +0,0 @@
-
-    const authPersist = require('./persist')
-    // This internal func finalizes User authentication
-    const finalizeLogin = async (alias, key, gunRoot, opts) => {
-      const user = gunRoot._.user
-      // add our credentials in-memory only to our root gun instance
-      var tmp = user._.tag;
-      var opt = user._.opt;
-      user._ = gunRoot.get('~'+key.pub)._;
-      user._.opt = opt;
-      var tags = user._.tag;
-      /*Object.values && Object.values(tmp).forEach(function(tag){
-        // TODO: This is ugly & buggy code, it needs to be refactored & tested into a event "merge" utility.
-        var t = tags[tag.tag];
-        console.log("hm??", tag, t);
-        if(!t){
-          tags[tag.tag] = tag;
-          return;
-        }
-        if(tag.last){
-          tag.last.to = t.to;
-          t.last = tag.last = t.last || tag.last;
-        }
-        t.to = tag.to;
-      })*/
-      //user._.tag = tmp || user._.tag;
-      // so that way we can use the credentials to encrypt/decrypt data
-      // that is input/output through gun (see below)
-      const pub = key.pub
-      const priv = key.priv
-      const epub = key.epub
-      const epriv = key.epriv
-      user._.is = user.is = {alias: alias, pub: pub};
-      Object.assign(user._, { alias: alias, pub: pub, epub: epub, sea: { pub: pub, priv: priv, epub: epub, epriv: epriv } })
-      //console.log("authorized", user._);
-      // persist authentication
-      //await authPersist(user._, key.proof, opts) // temporarily disabled
-      // emit an auth event, useful for page redirects and stuff.  
-      try {
-        gunRoot._.on('auth', user._) // TODO: Deprecate this, emit on user instead! Update docs when you do.
-        //user._.on('auth', user._) // Arrgh, this doesn't work without event "merge" code, but "merge" code causes stack overflow and crashes after logging in & trying to write data.
-      } catch (e) {
-        console.log('Your \'auth\' callback crashed with:', e)
-      }
-      // returns success with the user data credentials.
-      return user._
-    }
-    module.exports = finalizeLogin
-  

+ 13 - 1
public/lib/gundb/sea/pair.js

@@ -4,8 +4,19 @@
     var S = require('./settings');
     var Buff = (typeof Buffer !== 'undefined')? Buffer : shim.Buffer;
 
+    SEA.name = SEA.name || (async (cb, opt) => { try {
+      if(cb){ try{ cb() }catch(e){console.log(e)} }
+      return;
+    } catch(e) {
+      console.log(e);
+      SEA.err = e;
+      if(SEA.throw){ throw e }
+      if(cb){ cb() }
+      return;
+    }});
+
     //SEA.pair = async (data, proof, cb) => { try {
-    SEA.pair = SEA.pair || (async (cb) => { try {
+    SEA.pair = SEA.pair || (async (cb, opt) => { try {
 
       const ecdhSubtle = shim.ossl || shim.subtle
       // First: ECDSA keys for signing/verifying...
@@ -54,6 +65,7 @@
     } catch(e) {
       console.log(e);
       SEA.err = e;
+      if(SEA.throw){ throw e }
       if(cb){ cb() }
       return;
     }});

+ 0 - 41
public/lib/gundb/sea/persist.js

@@ -1,41 +0,0 @@
-
-    const SEA = require('./sea');
-    const Gun = SEA.Gun;
-    const Buffer = require('./buffer')
-    const authsettings = require('./settings')
-    const updateStorage = require('./update')
-    // This internal func persists User authentication if so configured
-    const authPersist = async (user, proof, opts) => {
-      // opts = { pin: 'string' }
-      // no opts.pin then uses random PIN
-      // How this works:
-      // called when app bootstraps, with wanted options
-      // IF authsettings.validity === 0 THEN no remember-me, ever
-      // IF PIN then signed 'remember' to window.sessionStorage and 'auth' to IndexedDB
-      const pin = Buffer.from(
-        (Gun.obj.has(opts, 'pin') && opts.pin) || Gun.text.random(10),
-        'utf8'
-      ).toString('base64')
-
-      const alias = user.alias
-      const exp = authsettings.validity      // seconds // @mhelander what is `exp`???
-
-      if (proof && alias && exp) {
-        const iat = Math.ceil(Date.now() / 1000)  // seconds
-        const remember = Gun.obj.has(opts, 'pin') || undefined  // for hook - not stored
-        const props = authsettings.hook({ alias: alias, iat: iat, exp: exp, remember: remember })
-        const pub = user.pub
-        const epub = user.epub
-        const priv = user.sea.priv
-        const epriv = user.sea.epriv
-        const key = { pub: pub, priv: priv, epub: epub, epriv: epriv }
-        if (props instanceof Promise) {
-          const asyncProps = await props.then()
-          return await updateStorage(proof, key, pin)(asyncProps)
-        }
-        return await updateStorage(proof, key, pin)(props)
-      }
-      return await updateStorage()({ alias: 'delete' })
-    }
-    module.exports = authPersist
-  

+ 0 - 43
public/lib/gundb/sea/query.js

@@ -1,43 +0,0 @@
-
-    var SEA = require('./sea');
-    var Gun = SEA.Gun;
-    // This is internal func queries public key(s) for alias.
-    const queryGunAliases = (alias, gunRoot) => new Promise((resolve, reject) => {
-      // load all public keys associated with the username alias we want to log in with.
-      gunRoot.get('~@'+alias).once((data, key) => {
-        //rev.off();
-        if (!data) {
-          // if no user, don't do anything.
-          const err = 'No user!'
-          Gun.log(err)
-          return reject({ err })
-        }
-        // then figuring out all possible candidates having matching username
-        const aliases = []
-        let c = 0
-        // TODO: how about having real chainable map without callback ?
-        Gun.obj.map(data, (at, pub) => {
-          if (!pub.slice || '~' !== pub.slice(0, 1)) {
-            // TODO: ... this would then be .filter((at, pub))
-            return
-          }
-          ++c
-          // grab the account associated with this public key.
-          gunRoot.get(pub).once(data => {
-            pub = pub.slice(1)
-            --c
-            if (data){
-              aliases.push({ pub, put: data })
-            }
-            if (!c && (c = -1)) {
-              resolve(aliases)
-            }
-          })
-        })
-        if (!c) {
-          reject({ err: 'Public key does not exist!' })
-        }
-      })
-    })
-    module.exports = queryGunAliases
-  

+ 0 - 141
public/lib/gundb/sea/recall.js

@@ -1,141 +0,0 @@
-
-    const Buffer = require('./buffer')
-    const authsettings = require('./settings')
-    //const { scope: seaIndexedDb } = require('./indexed')
-    const queryGunAliases = require('./query')
-    const parseProps = require('./parse')
-    const updateStorage = require('./update')
-    const SEA = require('./sea')
-    const Gun = SEA.Gun;
-    const finalizeLogin = require('./login')
-
-    // This internal func recalls persisted User authentication if so configured
-    const authRecall = async (gunRoot, authprops) => {
-      // window.sessionStorage only holds signed { alias, pin } !!!
-      const remember = authprops || sessionStorage.getItem('remember')
-      const { alias = sessionStorage.getItem('user'), pin: pIn } = authprops || {} // @mhelander what is pIn?
-      const pin = pIn && Buffer.from(pIn, 'utf8').toString('base64')
-      // Checks for existing proof, matching alias and expiration:
-      const checkRememberData = async ({ proof, alias: aLias, iat, exp, remember }) => {
-        if (!!proof && alias === aLias) {
-          const checkNotExpired = (args) => {
-            if (Math.floor(Date.now() / 1000) < (iat + args.exp)) {
-              // No way hook to update 'iat'
-              return Object.assign(args, { iat: iat, proof: proof })
-            } else {
-              Gun.log('Authentication expired!')
-            }
-          }
-          // We're not gonna give proof to hook!
-          const hooked = authsettings.hook({ alias: alias, iat: iat, exp: exp, remember: remember })
-          return ((hooked instanceof Promise)
-          && await hooked.then(checkNotExpired)) || checkNotExpired(hooked)
-        }
-      }
-      const readAndDecrypt = async (data, pub, key) =>
-        parseProps(await SEA.decrypt(await SEA.verify(data, pub), key))
-
-      // Already authenticated?
-      if (gunRoot._.user
-      && Gun.obj.has(gunRoot._.user._, 'pub')
-      && Gun.obj.has(gunRoot._.user._, 'sea')) {
-        return gunRoot._.user._  // Yes, we're done here.
-      }
-      // No, got persisted 'alias'?
-      if (!alias) {
-        throw { err: 'No authentication session found!' }
-      }
-      // Yes, got persisted 'remember'?
-      if (!remember) {
-        throw {  // And return proof if for matching alias
-          err: (await seaIndexedDb.get(alias, 'auth') && authsettings.validity
-          && 'Missing PIN and alias!') || 'No authentication session found!'
-        }
-      }
-      // Yes, let's get (all?) matching aliases
-      const aliases = (await queryGunAliases(alias, gunRoot))
-      .filter(({ pub } = {}) => !!pub)
-      // Got any?
-      if (!aliases.length) {
-        throw { err: 'Public key does not exist!' }
-      }
-      let err
-      // Yes, then attempt to log into each one until we find ours!
-      // (if two users have the same username AND the same password... that would be bad)
-      const [ { key, at, proof, pin: newPin } = {} ] = await Promise
-      .all(aliases.filter(({ at: { put } = {} }) => !!put)
-      .map(async ({ at: at, pub: pub }) => {
-        const readStorageData = async (args) => {
-          const props = args || parseProps(await SEA.verify(remember, pub, true))
-          let pin = props.pin
-          let aLias = props.alias
-
-          const data = (!pin && alias === aLias)
-          // No PIN, let's try short-term proof if for matching alias
-          ? await checkRememberData(props)
-          // Got PIN so get IndexedDB secret if signature is ok
-          : await checkRememberData(await readAndDecrypt(await seaIndexedDb.get(alias, 'auth'), pub, pin))
-          pin = pin || data.pin
-          delete data.pin
-          return { pin: pin, data: data }
-        }
-        // got pub, try auth with pin & alias :: or unwrap Storage data...
-        const __gky20 = await readStorageData(pin && { pin, alias })
-        const data = __gky20.data
-        const newPin = __gky20.pin
-        const proof = data.proof
-
-        if (!proof) {
-          if (!data) {
-            err = 'No valid authentication session found!'
-            return
-          }
-          try { // Wipes IndexedDB silently
-            await updateStorage()(data)
-          } catch (e) {}  //eslint-disable-line no-empty
-          err = 'Expired session!'
-          return
-        }
-
-        try { // auth parsing or decryption fails or returns empty - silently done
-          const auth= at.put.auth.auth
-          const sea = await SEA.decrypt(auth, proof)
-          if (!sea) {
-            err = 'Failed to decrypt private key!'
-            return
-          }
-          const priv = sea.priv
-          const epriv = sea.epriv
-          const epub = at.put.epub
-          // Success! we've found our private data!
-          err = null
-          return { proof: proof, at: at, pin: newPin, key: { pub: pub, priv: priv, epriv: epriv, epub: epub } }
-        } catch (e) {
-          err = 'Failed to decrypt private key!'
-          return
-        }
-      }).filter((props) => !!props))
-
-      if (!key) {
-        throw { err: err || 'Public key does not exist!' }
-      }
-
-      // now we have AES decrypted the private key,
-      // if we were successful, then that means we're logged in!
-      try {
-        await updateStorage(proof, key, newPin || pin)(key)
-
-        const user = Object.assign(key, { at: at, proof: proof })
-        const pIN = newPin || pin
-
-        const pinProp = pIN && { pin: Buffer.from(pIN, 'base64').toString('utf8') }
-
-        return await finalizeLogin(alias, user, gunRoot, pinProp)
-      } catch (e) { // TODO: right log message ?
-        Gun.log('Failed to finalize login with new password!')
-        const { err = '' } = e || {}
-        throw { err: 'Finalizing new password login failed! Reason: '+err }
-      }
-    }
-    module.exports = authRecall
-  

+ 2 - 8
public/lib/gundb/sea/root.js

@@ -7,15 +7,9 @@
     if(typeof window !== "undefined"){ module.window = window }
 
     var tmp = module.window || module;
-    var SEA = tmp.SEA || function(){};
+    var SEA = tmp.SEA || {};
 
-    if(SEA.window = module.window){ try{
-      SEA.window.SEA = SEA;
-      tmp = document.createEvent('CustomEvent');
-      tmp.initCustomEvent('extension', false, false, {type: "SEA"});
-      (window.dispatchEvent || window.fireEvent)(tmp);
-      window.postMessage({type: "SEA"}, '*');
-    } catch(e){} }
+    if(SEA.window = module.window){ SEA.window.SEA = SEA }
 
     try{ if(typeof common !== "undefined"){ common.exports = SEA } }catch(e){}
     module.exports = SEA;

+ 5 - 30
public/lib/gundb/sea/sea.js

@@ -1,43 +1,18 @@
 
-    // Old Code...
-    const __gky10 = require('./shim')
-    const crypto = __gky10.crypto
-    const subtle = __gky10.subtle
-    const ossl = __gky10.ossl
-    const TextEncoder = __gky10.TextEncoder
-    const TextDecoder = __gky10.TextDecoder
-    const getRandomBytes = __gky10.random
-    const EasyIndexedDB = require('./indexed')
-    const Buffer = require('./buffer')
-    var settings = require('./settings');
-    const __gky11 = require('./settings')
-    const pbKdf2 = __gky11.pbkdf2
-    const ecdsaKeyProps = __gky11.ecdsa.pair
-    const ecdsaSignProps = __gky11.ecdsa.sign
-    const ecdhKeyProps = __gky11.ecdh
-    const keysToEcdsaJwk = __gky11.jwk
-    const sha1hash = require('./sha1')
-    const sha256hash = require('./sha256')
-    const recallCryptoKey = require('./remember')
-    const parseProps = require('./parse')
-
+    var shim = require('./shim');
     // Practical examples about usage found from ./test/common.js
-    const SEA = require('./root');
+    var SEA = require('./root');
     SEA.work = require('./work');
     SEA.sign = require('./sign');
     SEA.verify = require('./verify');
     SEA.encrypt = require('./encrypt');
     SEA.decrypt = require('./decrypt');
 
-    SEA.random = SEA.random || getRandomBytes;
-
-    // This is easy way to use IndexedDB, all methods are Promises
-    // Note: Not all SEA interfaces have to support this.
-    SEA.EasyIndexedDB = EasyIndexedDB;
+    SEA.random = SEA.random || shim.random;
 
     // This is Buffer used in SEA and usable from Gun/SEA application also.
     // For documentation see https://nodejs.org/api/buffer.html
-    SEA.Buffer = SEA.Buffer || Buffer;
+    SEA.Buffer = SEA.Buffer || require('./buffer');
 
     // These SEA functions support now ony Promises or
     // async/await (compatible) code, use those like Promises.
@@ -75,7 +50,7 @@
     // Cheers! Tell me what you think.
     var Gun = (SEA.window||{}).Gun || require('./gun', 1);
     Gun.SEA = SEA;
-    SEA.Gun = Gun;
+    SEA.GUN = SEA.Gun = Gun;
 
     module.exports = SEA
   

+ 7 - 2
public/lib/gundb/sea/secret.js

@@ -2,8 +2,12 @@
     var SEA = require('./root');
     var shim = require('./shim');
     var S = require('./settings');
-    // Derive shared secret from other's pub and my epub/epriv
-    SEA.secret = SEA.secret || (async (key, pair, cb) => { try {
+    // Derive shared secret from other's pub and my epub/epriv 
+    SEA.secret = SEA.secret || (async (key, pair, cb, opt) => { try {
+      opt = opt || {};
+      if(!pair || !pair.epriv || !pair.epub){
+        pair = await SEA.I(null, {what: key, how: 'secret', why: opt.why});
+      }
       const pub = key.epub || key
       const epub = pair.epub
       const epriv = pair.epriv
@@ -25,6 +29,7 @@
       return r;
     } catch(e) { 
       SEA.err = e;
+      if(SEA.throw){ throw e }
       if(cb){ cb() }
       return;
     }});

+ 26 - 31
public/lib/gundb/sea/shim.js

@@ -1,43 +1,38 @@
 
+    const SEA = require('./root')
     const Buffer = require('./buffer')
     const api = {Buffer: Buffer}
+    var o = {};
 
-    if (typeof window !== 'undefined') {
-      var crypto = window.crypto || window.msCrypto;
-      var subtle = crypto.subtle || crypto.webkitSubtle;
-      const TextEncoder = window.TextEncoder
-      const TextDecoder = window.TextDecoder
+    if(SEA.window){
+      api.crypto = window.crypto || window.msCrypto;
+      api.subtle = (api.crypto||o).subtle || (api.crypto||o).webkitSubtle;
+      api.TextEncoder = window.TextEncoder;
+      api.TextDecoder = window.TextDecoder;
+      api.random = (len) => Buffer.from(api.crypto.getRandomValues(new Uint8Array(Buffer.alloc(len))))
+    }
+    if(!api.crypto){try{
+      var crypto = require('crypto', 1);
+      const { subtle } = require('@trust/webcrypto', 1)             // All but ECDH
+      const { TextEncoder, TextDecoder } = require('text-encoding', 1)
       Object.assign(api, {
         crypto,
         subtle,
         TextEncoder,
         TextDecoder,
-        random: (len) => Buffer.from(crypto.getRandomValues(new Uint8Array(Buffer.alloc(len))))
-      })
-    } else {
-      try{
-        var crypto = require('crypto', 1);
-        const { subtle } = require('@trust/webcrypto', 1)             // All but ECDH
-        const { TextEncoder, TextDecoder } = require('text-encoding', 1)
-        Object.assign(api, {
-          crypto,
-          subtle,
-          TextEncoder,
-          TextDecoder,
-          random: (len) => Buffer.from(crypto.randomBytes(len))
-        });
-        //try{
-          const WebCrypto = require('node-webcrypto-ossl', 1)
-          api.ossl = new WebCrypto({directory: 'ossl'}).subtle // ECDH
-        //}catch(e){
-          //console.log("node-webcrypto-ossl is optionally needed for ECDH, please install if needed.");
-        //}
-      }catch(e){
-        console.log("@trust/webcrypto and text-encoding are not included by default, you must add it to your package.json!");
-        console.log("node-webcrypto-ossl is temporarily needed for ECDSA signature verification, and optionally needed for ECDH, please install if needed (currently necessary so add them to your package.json for now).");
-        TRUST_WEBCRYPTO_OR_TEXT_ENCODING_NOT_INSTALLED;
-      }
-    }
+        random: (len) => Buffer.from(crypto.randomBytes(len))
+      });
+      //try{
+        const WebCrypto = require('node-webcrypto-ossl', 1)
+        api.ossl = new WebCrypto({directory: 'ossl'}).subtle // ECDH
+      //}catch(e){
+        //console.log("node-webcrypto-ossl is optionally needed for ECDH, please install if needed.");
+      //}
+    }catch(e){
+      console.log("@trust/webcrypto and text-encoding are not included by default, you must add it to your package.json!");
+      console.log("node-webcrypto-ossl is temporarily needed for ECDSA signature verification, and optionally needed for ECDH, please install if needed (currently necessary so add them to your package.json for now).");
+      TRUST_WEBCRYPTO_OR_TEXT_ENCODING_NOT_INSTALLED;
+    }}
 
     module.exports = api
   

+ 8 - 12
public/lib/gundb/sea/sign.js

@@ -3,31 +3,27 @@
     var shim = require('./shim');
     var S = require('./settings');
     var sha256hash = require('./sha256');
+    var u;
 
-    SEA.sign = SEA.sign || (async (data, pair, cb) => { try {
-      if(data && data.slice
-      && 'SEA{' === data.slice(0,4)
-      && '"m":' === data.slice(4,8)){
-        // TODO: This would prevent pair2 signing pair1's signature.
-        // So we may want to change this in the future.
-        // but for now, we want to prevent duplicate double signature.
-        if(cb){ try{ cb(data) }catch(e){console.log(e)} }
-        return data;
+    SEA.sign = SEA.sign || (async (data, pair, cb, opt) => { try {
+      opt = opt || {};
+      if(!(pair||opt).priv){
+        pair = await SEA.I(null, {what: data, how: 'sign', why: opt.why});
       }
       const pub = pair.pub
       const priv = pair.priv
       const jwk = S.jwk(pub, priv)
-      const msg = JSON.stringify(data)
-      const hash = await sha256hash(msg)
+      const hash = await sha256hash(JSON.stringify(data))
       const sig = await (shim.ossl || shim.subtle).importKey('jwk', jwk, S.ecdsa.pair, false, ['sign'])
       .then((key) => (shim.ossl || shim.subtle).sign(S.ecdsa.sign, key, new Uint8Array(hash))) // privateKey scope doesn't leak out from here!
-      const r = 'SEA'+JSON.stringify({m: msg, s: shim.Buffer.from(sig, 'binary').toString('utf8')});
+      const r = 'SEA'+JSON.stringify({m: data, s: shim.Buffer.from(sig, 'binary').toString(opt.encode || 'base64')});
 
       if(cb){ try{ cb(r) }catch(e){console.log(e)} }
       return r;
     } catch(e) {
       console.log(e);
       SEA.err = e;
+      if(SEA.throw){ throw e }
       if(cb){ cb() }
       return;
     }});

+ 0 - 48
public/lib/gundb/sea/update.js

@@ -1,48 +0,0 @@
-
-    const authsettings = require('./settings')
-    const SEA = require('./sea');
-    const Gun = SEA.Gun;
-    //const { scope: seaIndexedDb } = require('./indexed')
-    // This updates sessionStorage & IndexedDB to persist authenticated "session"
-    const updateStorage = (proof, key, pin) => async (props) => {
-      if (!Gun.obj.has(props, 'alias')) {
-        return  // No 'alias' - we're done.
-      }
-      if (authsettings.validity && proof && Gun.obj.has(props, 'iat')) {
-        props.proof = proof
-        delete props.remember   // Not stored if present
-
-        const alias = props.alias
-        const id = props.alias
-        const remember = { alias: alias, pin: pin }
-
-        try {
-          const signed = await SEA.sign(JSON.stringify(remember), key)
-
-          sessionStorage.setItem('user', alias)
-          sessionStorage.setItem('remember', signed)
-
-          const encrypted = await SEA.encrypt(props, pin)
-
-          if (encrypted) {
-            const auth = await SEA.sign(encrypted, key)
-            await seaIndexedDb.wipe() // NO! Do not do this. It ruins other people's sessionStorage code. This is bad/wrong, commenting it out.
-            await seaIndexedDb.put(id, { auth: auth })
-          }
-
-          return props
-        } catch (err) {
-          throw { err: 'Session persisting failed!' }
-        }
-      }
-
-      // Wiping IndexedDB completely when using random PIN
-      await seaIndexedDb.wipe() // NO! Do not do this. It ruins other people's sessionStorage code. This is bad/wrong, commenting it out.
-      // And remove sessionStorage data
-      sessionStorage.removeItem('user')
-      sessionStorage.removeItem('remember')
-
-      return props
-    }
-    module.exports = updateStorage
-  

+ 1 - 1
public/lib/gundb/sea/user.js

@@ -19,7 +19,7 @@
       (at = (user = at.user = gun.chain(new User))._).opt = {};
       at.opt.uuid = function(cb){
         var id = uuid(), pub = root.user;
-        if(!pub || !(pub = (pub._).sea) || !(pub = pub.pub)){ return id }
+        if(!pub || !(pub = pub.is) || !(pub = pub.pub)){ return id }
         id = id + '~' + pub + '.';
         if(cb && cb.call){ cb(null, id) }
         return id;

+ 18 - 6
public/lib/gundb/sea/verify.js

@@ -6,29 +6,41 @@
     var parse = require('./parse');
     var u;
 
-    SEA.verify = SEA.verify || (async (data, pair, cb) => { try {
+    SEA.verify = SEA.verify || (async (data, pair, cb, opt) => { try {
       const json = parse(data)
       if(false === pair){ // don't verify!
-        const raw = (json !== data)? 
+        const raw = (json !== data)?
           (json.s && json.m)? parse(json.m) : data
         : json;
         if(cb){ try{ cb(raw) }catch(e){console.log(e)} }
         return raw;
       }
+      opt = opt || {};
+      // SEA.I // verify is free! Requires no user permission.
+      if(json === data){ throw "No signature on data." }
       const pub = pair.pub || pair
       const jwk = S.jwk(pub)
       const key = await (shim.ossl || shim.subtle).importKey('jwk', jwk, S.ecdsa.pair, false, ['verify'])
       const hash = await sha256hash(json.m)
-      const sig = new Uint8Array(shim.Buffer.from(json.s, 'utf8'))
-      const check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash))
-      if(!check){ throw "Signature did not match." }
+      var buf; var sig; var check; try{
+        buf = shim.Buffer.from(json.s, opt.encode || 'base64') // NEW DEFAULT!
+        sig = new Uint8Array(buf)
+        check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash))
+        if(!check){ throw "Signature did not match." }
+      }catch(e){
+        buf = shim.Buffer.from(json.s, 'utf8') // AUTO BACKWARD OLD UTF8 DATA!
+        sig = new Uint8Array(buf)
+        check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash))
+        if(!check){ throw "Signature did not match." }
+      }
       const r = check? parse(json.m) : u;
 
       if(cb){ try{ cb(r) }catch(e){console.log(e)} }
       return r;
     } catch(e) {
-      console.log(e);
+      console.log(e); // mismatched owner FOR MARTTI
       SEA.err = e;
+      if(SEA.throw){ throw e }
       if(cb){ cb() }
       return;
     }});

+ 3 - 2
public/lib/gundb/sea/work.js

@@ -14,7 +14,7 @@
       }
       salt = salt || shim.random(9);
       if('SHA-256' === opt.name){
-        var rsha = shim.Buffer.from(await sha(data), 'binary').toString('utf8')
+        var rsha = shim.Buffer.from(await sha(data), 'binary').toString(opt.encode || 'base64')
         if(cb){ try{ cb(rsha) }catch(e){console.log(e)} }
         return rsha;
       }
@@ -28,11 +28,12 @@
         hash: opt.hash || S.pbkdf2.hash,
       }, key, opt.length || (S.pbkdf2.ks * 8))
       data = shim.random(data.length)  // Erase data in case of passphrase
-      const r = shim.Buffer.from(result, 'binary').toString('utf8')
+      const r = shim.Buffer.from(result, 'binary').toString(opt.encode || 'base64')
       if(cb){ try{ cb(r) }catch(e){console.log(e)} }
       return r;
     } catch(e) { 
       SEA.err = e;
+      if(SEA.throw){ throw e }
       if(cb){ cb() }
       return;
     }});

+ 176 - 131
public/vwf.js

@@ -469,120 +469,133 @@ Copyright (c) 2014-2018 Nikolai Suslov and the Krestianstvo.org project contribu
             let appName = JSON.parse(localStorage.getItem('lcs_app')).path.application.split(".").join("_");
             let dbPath = appName + '_config_yaml';
            
-            _LCS_WORLD_USER.get('worlds').get(path.slice(1)).get(dbPath).once().then(res => {
+            _LCS_WORLD_USER.get('worlds').get(path.slice(1)).get(dbPath).get('file').load(res => {
+                
+                var conf = "";
+
                 if (res) {
-                    let config = YAML.parse(res.file);
-                    return config
-                } else {
-                    return ""
-                }
-            })
-            .then(function(configLibraries) {
-                if(configLibraries && typeof configLibraries == "object") {
-                    if (typeof configLibraries.configuration == "object") {
-                        applicationConfig = configLibraries.configuration;
-                    }
-                    Object.keys(configLibraries).forEach(function(libraryType) {
-                        if(libraryType == 'info' && configLibraries[libraryType]["title"])
-                        {
-                            //jQuery('title').html(configLibraries[libraryType]["title"]);
-                            document.querySelector('title').innerHTML = configLibraries[libraryType]["title"]
-                        }
-                        if(!userLibraries[libraryType]) {
-                            userLibraries[libraryType] = {};
+                    let config = YAML.parse(res);
+                    conf = config
+                } 
+
+                let confPromise = new Promise((resolve, reject) => {
+                      resolve(conf); 
+                  });
+                  
+                  confPromise.then(function(configLibraries) {
+                    if(configLibraries && typeof configLibraries == "object") {
+                        if (typeof configLibraries.configuration == "object") {
+                            applicationConfig = configLibraries.configuration;
                         }
-                        // Merge libraries from config file and URL together. Check for incompatible
-                        // libraries, and disable them.
-                        Object.keys(configLibraries[libraryType]).forEach(function(libraryName) {
-                            var disabled = false;
-                            if(requireArray[libraryName] && requireArray[libraryName].disabledBy) {
-                                for(var i=0; i<requireArray[libraryName].disabledBy.length; i++) {
-                                    Object.keys(userLibraries).forEach(function(userLibraryType) {
-                                        Object.keys(userLibraries[userLibraryType]).forEach(function(userLibraryName) {
-                                            if(requireArray[libraryName].disabledBy[i] == userLibraryName) {
-                                                disabled = true;
-                                            }
-                                        })
-                                    })
-                                }
+                        Object.keys(configLibraries).forEach(function(libraryType) {
+                            if(libraryType == 'info' && configLibraries[libraryType]["title"])
+                            {
+                                //jQuery('title').html(configLibraries[libraryType]["title"]);
+                                document.querySelector('title').innerHTML = configLibraries[libraryType]["title"]
                             }
-                            if(!disabled) {
-                                if(userLibraries[libraryType][libraryName] == undefined) {
-                                    userLibraries[libraryType][libraryName] = configLibraries[libraryType][libraryName];
-                                }
-                                else if(typeof userLibraries[libraryType][libraryName] == "object" && typeof configLibraries[libraryType][libraryName] == "object") {
-                                    userLibraries[libraryType][libraryName] = Object.assign({}, configLibraries[libraryType][libraryName], userLibraries[libraryType][libraryName]);
-                                    // userLibraries[libraryType][libraryName] = jQuery.extend({}, configLibraries[libraryType][libraryName], userLibraries[libraryType][libraryName]);
-                                }
+                            if(!userLibraries[libraryType]) {
+                                userLibraries[libraryType] = {};
                             }
-                        });
-                    });
-                }
-            }).then(function(){
-                Object.keys(userLibraries).forEach(function(libraryType) {
-                    if(initializers[libraryType]) {
-                        Object.keys(userLibraries[libraryType]).forEach(function(libraryName) {
-                            if(requireArray[libraryName]) {
-                                requireArray[libraryName].active = true;
-                                initializers[libraryType][libraryName].active = true;
-                                if(userLibraries[libraryType][libraryName] && userLibraries[libraryType][libraryName] != "") {
-                                    if(typeof initializers[libraryType][libraryName].parameters == "object") {
-                                        
-                                        initializers[libraryType][libraryName].parameters = Object.assign({}, initializers[libraryType][libraryName].parameters, userLibraries[libraryType][libraryName]);
-                                        // initializers[libraryType][libraryName].parameters = jQuery.extend({}, initializers[libraryType][libraryName].parameters,
-                                        //     userLibraries[libraryType][libraryName]);
-                                    }
-                                    else {
-                                        initializers[libraryType][libraryName].parameters = userLibraries[libraryType][libraryName];
+                            // Merge libraries from config file and URL together. Check for incompatible
+                            // libraries, and disable them.
+                            Object.keys(configLibraries[libraryType]).forEach(function(libraryName) {
+                                var disabled = false;
+                                if(requireArray[libraryName] && requireArray[libraryName].disabledBy) {
+                                    for(var i=0; i<requireArray[libraryName].disabledBy.length; i++) {
+                                        Object.keys(userLibraries).forEach(function(userLibraryType) {
+                                            Object.keys(userLibraries[userLibraryType]).forEach(function(userLibraryName) {
+                                                if(requireArray[libraryName].disabledBy[i] == userLibraryName) {
+                                                    disabled = true;
+                                                }
+                                            })
+                                        })
                                     }
                                 }
-                                if(requireArray[libraryName].linkedLibraries) {
-                                    for(var i=0; i<requireArray[libraryName].linkedLibraries.length; i++) {
-                                        requireArray[requireArray[libraryName].linkedLibraries[i]].active = true;
+                                if(!disabled) {
+                                    if(userLibraries[libraryType][libraryName] == undefined) {
+                                        userLibraries[libraryType][libraryName] = configLibraries[libraryType][libraryName];
+                                    }
+                                    else if(typeof userLibraries[libraryType][libraryName] == "object" && typeof configLibraries[libraryType][libraryName] == "object") {
+                                        userLibraries[libraryType][libraryName] = Object.assign({}, configLibraries[libraryType][libraryName], userLibraries[libraryType][libraryName]);
+                                        // userLibraries[libraryType][libraryName] = jQuery.extend({}, configLibraries[libraryType][libraryName], userLibraries[libraryType][libraryName]);
                                     }
                                 }
-                            }
+                            });
                         });
                     }
-                });
-
-                // Load default renderer if no other librarys specified
-                if(Object.keys(userLibraries["model"]).length == 0 && Object.keys(userLibraries["view"]).length == 0) {
-                    // requireArray["vwf/model/threejs"].active = true;
-                    // requireArray["vwf/view/threejs"].active = true;
-                    // requireArray["vwf/model/threejs/three"].active = true;
-                    // requireArray["vwf/model/threejs/js/loaders/ColladaLoader"].active = true;
-                    // requireArray["vwf/model/threejs/js/loaders/gltf/glTF-parser"].active = true;
-                    // requireArray["vwf/model/threejs/js/loaders/gltf/glTFLoader"].active = true;
-                    // requireArray["vwf/model/threejs/js/loaders/gltf/glTFAnimation"].active = true;
-                    // requireArray["vwf/model/threejs/js/loaders/gltf/glTFLoaderUtils"].active = true;
-                    // requireArray["vwf/model/threejs/js/stereo/DeviceOrientationControls"].active = true;
-                    // requireArray["vwf/model/threejs/js/stereo/OrbitControls"].active = true;
-                    // requireArray["vwf/model/threejs/js/stereo/StereoEffect"].active = true;
-                    // initializers["model"]["vwf/model/threejs"].active = true;
-                    // initializers["view"]["vwf/view/threejs"].active = true;
-                }
-
-                require( requireConfig, getActiveLibraries(requireArray, false), function( ready ) {
-
-                    ready( async function() {
-
-                        // Merge any application configuration settings into the configuration
-                        // object.
+                }).then(function(){
+                    Object.keys(userLibraries).forEach(function(libraryType) {
+                        if(initializers[libraryType]) {
+                            Object.keys(userLibraries[libraryType]).forEach(function(libraryName) {
+                                if(requireArray[libraryName]) {
+                                    requireArray[libraryName].active = true;
+                                    initializers[libraryType][libraryName].active = true;
+                                    if(userLibraries[libraryType][libraryName] && userLibraries[libraryType][libraryName] != "") {
+                                        if(typeof initializers[libraryType][libraryName].parameters == "object") {
+                                            
+                                            initializers[libraryType][libraryName].parameters = Object.assign({}, initializers[libraryType][libraryName].parameters, userLibraries[libraryType][libraryName]);
+                                            // initializers[libraryType][libraryName].parameters = jQuery.extend({}, initializers[libraryType][libraryName].parameters,
+                                            //     userLibraries[libraryType][libraryName]);
+                                        }
+                                        else {
+                                            initializers[libraryType][libraryName].parameters = userLibraries[libraryType][libraryName];
+                                        }
+                                    }
+                                    if(requireArray[libraryName].linkedLibraries) {
+                                        for(var i=0; i<requireArray[libraryName].linkedLibraries.length; i++) {
+                                            requireArray[requireArray[libraryName].linkedLibraries[i]].active = true;
+                                        }
+                                    }
+                                }
+                            });
+                        }
+                    });
+    
+                    // Load default renderer if no other librarys specified
+                    if(Object.keys(userLibraries["model"]).length == 0 && Object.keys(userLibraries["view"]).length == 0) {
+                        // requireArray["vwf/model/threejs"].active = true;
+                        // requireArray["vwf/view/threejs"].active = true;
+                        // requireArray["vwf/model/threejs/three"].active = true;
+                        // requireArray["vwf/model/threejs/js/loaders/ColladaLoader"].active = true;
+                        // requireArray["vwf/model/threejs/js/loaders/gltf/glTF-parser"].active = true;
+                        // requireArray["vwf/model/threejs/js/loaders/gltf/glTFLoader"].active = true;
+                        // requireArray["vwf/model/threejs/js/loaders/gltf/glTFAnimation"].active = true;
+                        // requireArray["vwf/model/threejs/js/loaders/gltf/glTFLoaderUtils"].active = true;
+                        // requireArray["vwf/model/threejs/js/stereo/DeviceOrientationControls"].active = true;
+                        // requireArray["vwf/model/threejs/js/stereo/OrbitControls"].active = true;
+                        // requireArray["vwf/model/threejs/js/stereo/StereoEffect"].active = true;
+                        // initializers["model"]["vwf/model/threejs"].active = true;
+                        // initializers["view"]["vwf/view/threejs"].active = true;
+                    }
+    
+                    require( requireConfig, getActiveLibraries(requireArray, false), function( ready ) {
+    
+                        ready( async function() {
+    
+                            // Merge any application configuration settings into the configuration
+                            // object.
+    
+                            require( "vwf/configuration" ).instance = require( "vwf/utility" ).merge(
+                                {}, require( "vwf/configuration" ).instance, applicationConfig );
+    
+                            // With the scripts loaded, we must initialize the framework. vwf.initialize()
+                            // accepts three parameters: a world specification, model configuration parameters,
+                            // and view configuration parameters.
+    
+                            await vwf.initialize(application, getActiveLibraries(initializers["model"], true), getActiveLibraries(initializers["view"], true), callback);
+    
+                        } );
+    
+                    } );
+                })
 
-                        require( "vwf/configuration" ).instance = require( "vwf/utility" ).merge(
-                            {}, require( "vwf/configuration" ).instance, applicationConfig );
 
-                        // With the scripts loaded, we must initialize the framework. vwf.initialize()
-                        // accepts three parameters: a world specification, model configuration parameters,
-                        // and view configuration parameters.
+                  
 
-                        await vwf.initialize(application, getActiveLibraries(initializers["model"], true), getActiveLibraries(initializers["view"], true), callback);
+            },{wait: 200})
 
-                    } );
 
-                } );
-            })
+            
 
             // jQuery.getJSON("admin/config", function(configLibraries) {
             // }).always(function(jqXHR, textStatus) { 
@@ -818,7 +831,7 @@ Copyright (c) 2014-2018 Nikolai Suslov and the Krestianstvo.org project contribu
 
             //await _app.getApplicationState();
             await _app.getApplicationState()
-                .then(res => self.ready( application, res))
+                .then(res => {self.ready( application, res)})
 
         };
 
@@ -4831,22 +4844,9 @@ if ( ! childComponent.source ) {
                 let fetchUrl =  remappedURI( require( "vwf/utility" ).resolveURI( nodeURI, baseURI ) );
                 let dbName = fetchUrl.replace(window.location.origin + '/', "").split(".").join("_") + '_yaml';
 
-                let worldName = dbName.split('/')[0];
-                var userDB = window._LCS_WORLD_USER.get('worlds').get(worldName);
+                const parseComp = async function(f) {
 
-                var fileName = "";
-
-                if(dbName.includes("vwf_example_com")){
-                    userDB = window._LCS_SYS_USER.get('proxy');
-                    fileName = dbName;
-                } else {
-                   fileName = dbName.replace(worldName + '/', "");
-                }
-      
-
-                userDB.get(fileName).once(async function(res) {
-
-                    let result = YAML.parse(res.file);
+                    let result = YAML.parse(f);
 
                     let nativeObject = result;
                     // console.log(nativeObject);
@@ -4864,7 +4864,39 @@ if ( ! childComponent.source ) {
  
                      }
 
-                }, {wait: 200})
+                }
+
+
+            var fileName = "";
+
+                if(dbName.includes("vwf_example_com")){
+                    //userDB = await window._LCS_SYS_USER.get('proxy').then();
+                   fileName = dbName;
+                   window._LCS_SYS_USER.get('proxy').get(fileName).get('file').load(r=>{
+                       //console.log(r);
+                       parseComp(r);
+
+                   },{wait:200});
+
+                } else {
+                    let worldName = dbName.split('/')[0];
+                    //userDB = await window._LCS_WORLD_USER.get('worlds').path(worldName).then();
+                   fileName = dbName.replace(worldName + '/', "");
+                   window._LCS_WORLD_USER.get('worlds').path(worldName).get(fileName).get('file').load(r=>{
+                    //console.log(r);
+                    parseComp(r);
+
+                },{wait:200});
+                }
+
+                //console.log(source);
+
+                //userDB.get(fileName).once(async function(res) {
+
+            
+                    
+
+               // }, {wait: 1000})
             }
 
         };
@@ -4890,21 +4922,9 @@ if ( ! childComponent.source ) {
                 let fetchUrl = remappedURI( require( "vwf/utility" ).resolveURI( scriptURI, baseURI ) );
                 let dbName = fetchUrl.replace(window.location.origin + '/', "").split(".").join("_");
 
+                const parseComp = async function(res) {
 
-                let worldName = dbName.split('/')[0];
-                var userDB = window._LCS_WORLD_USER.get('worlds').get(worldName);
-                var fileName = "";
-
-                if(dbName.includes("vwf_example_com")){
-                    userDB = window._LCS_SYS_USER.get('proxy');
-                    fileName = dbName
-                } else {
-                    fileName = dbName.replace(worldName + '/', "");
-                }
-
-                userDB.get(fileName).once(async function(res) {
-
-                    let scriptText = res.file;
+                    let scriptText = res;
 
                     try {
                         await callback_async( scriptText );
@@ -4916,7 +4936,32 @@ if ( ! childComponent.source ) {
                         errback_async( error );
                         queue.resume( "after loading " + scriptURI ); // resume the queue; may invoke dispatch(), so call last before returning to the host
                     }
-                }, {wait: 200})
+                }
+
+
+
+                let worldName = dbName.split('/')[0];
+                //var userDB = window._LCS_WORLD_USER.get('worlds').get(worldName);
+                var fileName = "";
+
+                if(dbName.includes("vwf_example_com")){
+                    //userDB = window._LCS_SYS_USER.get('proxy');
+                    fileName = dbName;
+                    window._LCS_SYS_USER.get('proxy').get(fileName).get('file').load(r=>{
+                        //console.log(r);
+                        parseComp(r);
+                    },{wait: 200});
+ 
+                } else {
+                    fileName = dbName.replace(worldName + '/', "");
+                    window._LCS_WORLD_USER.get('worlds').path(worldName).get(fileName).get('file').load(r=>{
+                        //console.log(r);
+                        parseComp(r);
+    
+                    },{wait: 200});
+                }
+
+               // userDB.get(fileName).once(, {wait: 1000})
             }
 
         };

+ 4 - 5
public/vwf/model/aframe.js

@@ -597,11 +597,10 @@ define(["module", "vwf/model", "vwf/utility"], function (module, model, utility)
                                 let dbPath = propertyValue.split(".").join("_");
 
 
-                                _LCS_WORLD_USER.get('worlds').get(worldName).get(dbPath).once().then(response => {
+                                _LCS_WORLD_USER.get('worlds').get(worldName).get(dbPath).get('file').load(response => {
                                     if (response) {
-
-                                        console.log(JSON.parse(response.file));
-                                        let assets = JSON.parse(response.file);
+                                        console.log(JSON.parse(response));
+                                        let assets = JSON.parse(response);
                                         for (var prop in assets) {
                                             var elm = document.createElement(assets[prop].tag);
                                             elm.setAttribute('id', prop);
@@ -612,7 +611,7 @@ define(["module", "vwf/model", "vwf/utility"], function (module, model, utility)
 
 
                                     }
-                                });
+                                },{wait: 200});
 
 
                                 // httpGetJson(propertyValue).then(function (response) {

+ 3 - 3
public/web/index-app.js

@@ -282,7 +282,7 @@ class IndexApp {
 
         _LCSDB.on('auth', (ack) => {
 
-            if (ack.pub) {
+            if (ack.sea.pub) {
 
                 let alias = _LCSUSER.is.alias;
                 let userEl = document.querySelector('#userGUI');
@@ -394,7 +394,7 @@ class IndexApp {
                                 "label": 'Sign OUT',
                                 "onclick": function (e) {
                                     _LCSUSER.leave().then(ack => {
-                                        if (ack.ok == 0) {
+                                        if (ack.pub) {
                                             window.sessionStorage.removeItem('alias');
                                             window.sessionStorage.removeItem('tmp');
                                             window.location.reload(true);
@@ -560,7 +560,7 @@ class IndexApp {
                                                                 console.log(ack.err)
                                                                 return ack.err
                                                             };
-                                                            if (ack.pub) {
+                                                            if (ack.sea.pub) {
                                                                 _LCSUSER.auth(alias, pass);
                                                                 _LCSDB.get('users').get(alias).put({
                                                                     'alias': alias,

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