From 86c4017aca86dbc465ad89096d98fbb58db0edba Mon Sep 17 00:00:00 2001 From: Jonathan Dunlap Date: Fri, 13 Jul 2018 12:29:10 -0700 Subject: [PATCH 001/103] new sea test --- package.json | 2 +- test/sea/sea2.js | 60 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 test/sea/sea2.js diff --git a/package.json b/package.json index 7bb359a0..2da08e66 100644 --- a/package.json +++ b/package.json @@ -53,9 +53,9 @@ "devDependencies": { "@trust/webcrypto": "^0.7.1", "aws-sdk": ">=2.153.0", + "concat-map": "^0.0.1", "express": ">=4.15.2", "ip": "^1.1.5", - "concat-map": "^0.0.1", "mocha": ">=3.2.0", "panic-manager": "^1.2.0", "panic-server": "^1.1.1", diff --git a/test/sea/sea2.js b/test/sea/sea2.js new file mode 100644 index 00000000..a920f579 --- /dev/null +++ b/test/sea/sea2.js @@ -0,0 +1,60 @@ +/* global Gun,describe,expect,it,beforeEach */ +/*eslint max-len: ["error", 95, { "ignoreComments": true }]*/ +/*eslint semi: ["error", "always", { "omitLastInOneLineBlock": true}]*/ +/*eslint object-curly-spacing: ["error", "never"]*/ +/*eslint node/no-deprecated-api: [error, {ignoreModuleItems: ["new buffer.Buffer()"]}] */ + +var root; +var Gun; +(function(env){ + root = env.window ? env.window : global; + env.window && root.localStorage && root.localStorage.clear(); + try{ require('fs').unlinkSync('data.json') }catch(e){} + //root.Gun = root.Gun || require('../gun'); + if(root.Gun){ + //Gun = root.Gun = root.Gun; + } else { + var expect = global.expect = require("../expect"); + root.Gun = require('../../gun'); + //Gun.serve = require('../../lib/serve'); + //require('./s3'); + //require('./uws'); + //require('./wsp/server'); + require('../../lib/file'); + require('../../sea.js'); + } +}(this)); + +;(function(){ +Gun = root.Gun +const SEA = Gun.SEA + +if(!SEA){ return } + +describe('SEA', function(){ + var user; + var gun; + it('is instantiable', done => { + gun = Gun({ localStorage: true, radisk: false }) + user = gun.user() + done() + }) + + it('register users', async done => { + user.create('bob', 'test123', err => { + console.log('sea', SEA.err) + expect(err).toHaveProperty('ok') + setTimeout(done, 30) + }) + }) + + it('login users', async done => { + user.auth('bob', 'test123', err => { + expect(err).toHaveProperty('ok') + done() + }) + }) + +}) + +})() From 91babdb221bfe5d09d00d987b5bc41d7d5675261 Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Fri, 13 Jul 2018 12:59:56 -0700 Subject: [PATCH 002/103] progress with @jadbox who setup the tests! --- sea.js | 19 ++++++++++++++++--- test/sea/sea2.js | 11 ++++++----- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/sea.js b/sea.js index 71ee0259..569f777d 100644 --- a/sea.js +++ b/sea.js @@ -382,9 +382,11 @@ var shim = USE('./shim'); var S = USE('./settings'); var sha256hash = USE('./sha256'); + var u; SEA.sign = async (data, pair, cb) => { try { - if(data.slice + // TODO: how should undefined be handled? + if((u !== data) && data.slice && 'SEA{' === data.slice(0,4) && '"m":' === data.slice(4,8)){ // TODO: This would prevent pair2 signing pair1's signature. @@ -1150,13 +1152,24 @@ 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) + var auth = {}; + auth.priv = priv; + if(epriv){ + auth.epriv = epriv; + } + auth = await SEA.encrypt(auth, 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 user = {}; + user.alias = alias; + user.pub = pub; + user.auth = auth; + if(epub){ + user.epub = epub; + } const tmp = '~'+pairs.pub; // awesome, now we can actually save the user with their public key as their ID. try{ diff --git a/test/sea/sea2.js b/test/sea/sea2.js index a920f579..6a7cbdfa 100644 --- a/test/sea/sea2.js +++ b/test/sea/sea2.js @@ -41,16 +41,17 @@ describe('SEA', function(){ }) it('register users', async done => { - user.create('bob', 'test123', err => { - console.log('sea', SEA.err) - expect(err).toHaveProperty('ok') + user.create('bob', 'test123', ack => { + expect(ack.err).to.not.be.ok(); setTimeout(done, 30) }) }) it('login users', async done => { - user.auth('bob', 'test123', err => { - expect(err).toHaveProperty('ok') + console.log("------------------"); + user.auth('bob', 'test123', ack => { + console.log("?????", ack, SEA.err); + expect(ack.err).to.not.be.ok(); done() }) }) From 4ecbf443176452341bc9218baef17fd2bbe328c7 Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Tue, 7 Aug 2018 15:37:03 -0700 Subject: [PATCH 003/103] start doc refactor? --- README.md | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 81bb0b3b..4790bbce 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,4 @@ -

- - -

+

[![npm](https://img.shields.io/npm/dm/gun.svg)](https://www.npmjs.com/package/gun) [![Travis](https://img.shields.io/travis/amark/gun/master.svg)](https://travis-ci.org/amark/gun) @@ -9,7 +6,9 @@ [![Gitter](https://img.shields.io/gitter/room/amark/gun.js.svg)](https://gitter.im/amark/gun) [![](https://data.jsdelivr.com/v1/package/npm/gun/badge?style=rounded)](https://www.jsdelivr.com/package/npm/gun) -GUN is a realtime, distributed, offline-first, graph database engine. Doing **[20M+](https://github.com/amark/gun/wiki/100000-ops-sec-in-IE6-on-2GB-Atom-CPU) ops/sec** in just **~9KB** gzipped. +**GUN** is an _ecosystem_ of tools that let you build tomorrow's dApps, today. + +Decentralized alternatives to [Reddit](https://notabug.io/), [YouTube](https://d.tube/), [Wikipedia](https://news.ycombinator.com/item?id=17685682), etc. are already pushing terabytes of daily P2P traffic on GUN. We are a [friendly community](https://gitter.im/amark/gun) creating a free fun future for freedom: @@ -22,18 +21,10 @@ GUN is a realtime, distributed, offline-first, graph database engine. Doing **[2
- - - - -
+The ecosystem is one nice stack of technologies that looks like this: -## Why? - - - **Realtime** - You might use Socket.IO for realtime updates, but what happens if you reload the page? GUN solves *state synchronization* for you, no matter what, on reloads, across all your users, and even on conflicting updates. - - **Distributed** - GUN is peer-to-peer by design, meaning you have no centralized database server to maintain or that could crash. This lets you sleep through the night without worrying about database DevOps - we call it "NoDB". From there, you can build decentralized, federated, or centralized apps. - - **Offline-first** - GUN works even if your internet or cell reception doesn't. Users can still plug away and save data as normal, and then when the network comes back online GUN will automatically synchronize all the changes and handle any conflicts for you. - - **Graph** - Most databases force you to bend over backwards to match their storage constraints. But graphs are different, they let you have any data structure you want. Whether that be traditional tables with relations, document oriented trees, or tons of circular references. You choose. + + ## Quickstart From 2fe093287d490f4ae0d456567e43edc8cf017362 Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Tue, 7 Aug 2018 15:39:23 -0700 Subject: [PATCH 004/103] Update README.md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4790bbce..d28dfbf1 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,10 @@ Decentralized alternatives to [Reddit](https://notabug.io/), [YouTube](https://d The ecosystem is one nice stack of technologies that looks like this: - - +
+
+ +For now, it is best to start with GUN and learn the basics: ## Quickstart From 0a81c84877c7c278f885d82dc0f2cb00292bebb1 Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Tue, 14 Aug 2018 09:27:46 -0700 Subject: [PATCH 005/103] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d28dfbf1..29f9abba 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,7 @@ Thanks to:
This would not be possible without **community contributors**, big shout out to: -**[anywhichway](https://github.com/anywhichway) ([Block Storage](https://github.com/anywhichway/gun-block))**; **[beebase](https://github.com/beebase) ([Quasar](https://github.com/beebase/gun-vuex-quasar))**; **[BrockAtkinson](https://github.com/BrockAtkinson) ([brunch config](https://github.com/BrockAtkinson/brunch-gun))**; **[Brysgo](https://github.com/brysgo) ([GraphQL](https://github.com/brysgo/graphql-gun))**; **[d3x0r](https://github.com/d3x0r) ([SQLite](https://github.com/d3x0r/gun-db))**; **[forrestjt](https://github.com/forrestjt) ([file.js](https://github.com/amark/gun/blob/master/lib/file.js))**; **[hillct](https://github.com/hillct) (Docker)**; **[JosePedroDias](https://github.com/josepedrodias) ([graph visualizer](http://acor.sl.pt:9966))**; **[JuniperChicago](https://github.com/JuniperChicago) ([cycle.js bindings](https://github.com/JuniperChicago/cycle-gun))**; **[jveres](https://github.com/jveres) ([todoMVC](https://github.com/jveres/todomvc))**; **[kristianmandrup](https://github.com/kristianmandrup) ([edge](https://github.com/kristianmandrup/gun-edge))**; **[lmangani](https://github.com/lmangani) ([Cytoscape Visualizer](https://github.com/lmangani/gun-scape), [Cassandra](https://github.com/lmangani/gun-cassandra), [Fastify](https://github.com/lmangani/fastify-gundb), [LetsEncrypt](https://github.com/lmangani/polyGun-letsencrypt))**; **[mhelander](https://github.com/mhelander) ([SEA](https://github.com/amark/gun/blob/master/sea.js))**; [omarzion](https://github.com/omarzion) ([Sticky Note App](https://github.com/omarzion/stickies)); [PsychoLlama](https://github.com/PsychoLlama) ([LevelDB](https://github.com/PsychoLlama/gun-level)); **[RangerMauve](https://github.com/RangerMauve) ([schema](https://github.com/gundb/gun-schema))**; **[robertheessels](https://github.com/swifty) ([gun-p2p-auth](https://github.com/swifty/gun-p2p-auth))**; [sbeleidy](https://github.com/sbeleidy); **[Sean Matheson](https://github.com/ctrlplusb) ([Observable/RxJS/Most.js bindings](https://github.com/ctrlplusb/gun-most))**; **[Stefdv](https://github.com/stefdv) (Polymer/web components)**; **[sjones6](https://github.com/sjones6) ([Flint](https://github.com/sjones6/gun-flint))**; **[zrrrzzt](https://github.com/zrrrzzt) ([JWT Auth](https://gist.github.com/zrrrzzt/6f88dc3cedee4ee18588236756d2cfce))**; **[88dev](https://github.com/88dev) ([Database Viewer](https://github.com/88dev/gun-show))**; +**[anywhichway](https://github.com/anywhichway) ([Block Storage](https://github.com/anywhichway/gun-block))**; **[beebase](https://github.com/beebase) ([Quasar](https://github.com/beebase/gun-vuex-quasar))**; **[BrockAtkinson](https://github.com/BrockAtkinson) ([brunch config](https://github.com/BrockAtkinson/brunch-gun))**; **[Brysgo](https://github.com/brysgo) ([GraphQL](https://github.com/brysgo/graphql-gun))**; **[d3x0r](https://github.com/d3x0r) ([SQLite](https://github.com/d3x0r/gun-db))**; **[forrestjt](https://github.com/forrestjt) ([file.js](https://github.com/amark/gun/blob/master/lib/file.js))**; **[hillct](https://github.com/hillct) (Docker)**; **[JosePedroDias](https://github.com/josepedrodias) ([graph visualizer](http://acor.sl.pt:9966))**; **[JuniperChicago](https://github.com/JuniperChicago) ([cycle.js bindings](https://github.com/JuniperChicago/cycle-gun))**; **[jveres](https://github.com/jveres) ([todoMVC](https://github.com/jveres/todomvc))**; **[kristianmandrup](https://github.com/kristianmandrup) ([edge](https://github.com/kristianmandrup/gun-edge))**; **[Lightnet](https://github.com/Lightnet)** ([User Kitchen Sink Playground](https://gdb-auth-vue-node.glitch.me/)); **[lmangani](https://github.com/lmangani) ([Cytoscape Visualizer](https://github.com/lmangani/gun-scape), [Cassandra](https://github.com/lmangani/gun-cassandra), [Fastify](https://github.com/lmangani/fastify-gundb), [LetsEncrypt](https://github.com/lmangani/polyGun-letsencrypt))**; **[mhelander](https://github.com/mhelander) ([SEA](https://github.com/amark/gun/blob/master/sea.js))**; [omarzion](https://github.com/omarzion) ([Sticky Note App](https://github.com/omarzion/stickies)); [PsychoLlama](https://github.com/PsychoLlama) ([LevelDB](https://github.com/PsychoLlama/gun-level)); **[RangerMauve](https://github.com/RangerMauve) ([schema](https://github.com/gundb/gun-schema))**; **[robertheessels](https://github.com/swifty) ([gun-p2p-auth](https://github.com/swifty/gun-p2p-auth))**; [sbeleidy](https://github.com/sbeleidy); **[Sean Matheson](https://github.com/ctrlplusb) ([Observable/RxJS/Most.js bindings](https://github.com/ctrlplusb/gun-most))**; **[Stefdv](https://github.com/stefdv) (Polymer/web components)**; **[sjones6](https://github.com/sjones6) ([Flint](https://github.com/sjones6/gun-flint))**; **[zrrrzzt](https://github.com/zrrrzzt) ([JWT Auth](https://gist.github.com/zrrrzzt/6f88dc3cedee4ee18588236756d2cfce))**; **[88dev](https://github.com/88dev) ([Database Viewer](https://github.com/88dev/gun-show))**; I am missing many others, apologies, will be adding them soon! From 8b482b0ac07cf0b3c52a7c12384386cd9619f00a Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Wed, 15 Aug 2018 16:25:17 -0700 Subject: [PATCH 006/103] start space, sea leave fix, * test --- gun.js | 18 +++++++++++- lib/space.js | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++ sea.js | 5 ++++ 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 lib/space.js diff --git a/gun.js b/gun.js index b56991d5..70dcd0a5 100644 --- a/gun.js +++ b/gun.js @@ -802,8 +802,24 @@ } Gun.on.get = function(msg, gun){ - var root = gun._, soul = msg.get[_soul], node = root.graph[soul], has = msg.get[_has], tmp; + var root = gun._, get = msg.get, soul = get[_soul], node = root.graph[soul], has = get[_has], tmp; var next = root.next || (root.next = {}), at = next[soul]; + if(get['*']){ // TEMPORARY HACK FOR MARTTI, TESTING + var graph = {}; + Gun.obj.map(root.graph, function(node, soul){ + if(Gun.text.match(soul, get)){ + graph[soul] = Gun.obj.copy(node); + } + }); + if(!Gun.obj.empty(graph)){ + root.on('in', { + '@': msg['#'], + how: '*', + put: graph, + $: gun + }); + } + } // TEMPORARY HACK FOR MARTTI, TESTING if(!node || !at){ return root.on('get', msg) } if(has){ if(!obj_has(node, has)){ return root.on('get', msg) } diff --git a/lib/space.js b/lib/space.js new file mode 100644 index 00000000..a36ed7a0 --- /dev/null +++ b/lib/space.js @@ -0,0 +1,78 @@ +;(function(){ + var Gun = (typeof window !== "undefined")? window.Gun : require('../gun'); + var ify = Gun.node.ify, empty = {}, u; + Gun.chain.space = function(key, data, opt){ + if(data instanceof Function){ + return travel(key, data, opt, this); + } + var gun = this; + if(Gun.is(data)){ + data.get(function(soul){ + if(!soul){ + return cb && cb({err: "Indexspace cannot link `undefined`!"}); + } + gun.space(key, Gun.val.link.ify(soul), opt); + }, true); + return gun; + } + var cb = (opt instanceof Function && opt), rank = (opt||empty).rank || opt, tmp; + gun.get(function(soul){ + if(!soul){ + soul = (gun.back('opt.uuid') || Gun.text.random)(9); + } + var space = ify({}, soul), sub = space, l = 0, tmp; + Gun.list.map(index(0, key.length), function(i){ + sub[(tmp = key.slice(l, i))+'"'] = data; + sub = sub[tmp] = ify({}, soul+'"'+key.slice(0,i)); + l = i; + }); + tmp = {}; tmp[key] = data; tmp = ify(tmp, soul+'"'); + sub[key.slice(l, key.length)] = tmp; + //console.log(space); + gun.put(space, cb); + },true); + return gun; + } + function travel(key, cb, opt, ref){ + var root = ref.back(-1), tmp; + opt = opt || {}; + ref.get(function(soul){ + root.get(soul+'"').get(key).get(function(msg, eve){ + if(u !== msg.put){ + eve.off(); + cb(msg.put, msg.get, msg, eve); + return; + } + opt.soul = soul; + opt.start = soul+'"'; + opt.key = key; + opt.top = index(0, opt.find); + opt.low = opt.top.reverse(); + find(opt, cb, root); + }); + }, true) + } + function find(o, cb, root){ + var id = o.start+o.key.slice(0,o.low[0]); + root.get(id).get(function(msg, eve){ + eve.off(); + console.log("oh my!", msg.put, o.start); + }); + } + function index(n, m, l, k){ + l = l || []; + if(!m){ return l } + k = Math.ceil((n||1) / 10); + if((n+k) >= m){ return l } + l.push(n + k); + return index(n + k, m, l); + } +}()); + +/* +gun.user('google').space('martti', "testing 123!"); +gun.user('google').get('search').space('ma', function(){ + // tree & index + // UNFINISHED API! +}); +*/ diff --git a/sea.js b/sea.js index d49165d3..79e3dced 100644 --- a/sea.js +++ b/sea.js @@ -1269,6 +1269,11 @@ delete user._.is; delete user._.sea; } + if(typeof window !== 'undefined'){ + var tmp = window.sessionStorage; + delete tmp.alias; + delete tmp.tmp; + } return await authLeave(this.back(-1)) } // If authenticated user wants to delete his/her account, let's support it! From 3996e3cf48d8f9a73ea690597996ea6053777907 Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Sat, 18 Aug 2018 16:06:45 -0700 Subject: [PATCH 007/103] Index space alpha! --- gun.js | 7 ++-- lib/space.js | 86 +++++++++++++++++++++++++++++++++------------ test/tmp/space.html | 49 ++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 25 deletions(-) create mode 100644 test/tmp/space.html diff --git a/gun.js b/gun.js index 70dcd0a5..f105582e 100644 --- a/gun.js +++ b/gun.js @@ -475,6 +475,7 @@ if(env.soul){ at.rel = Val.rel.ify(env.soul); } + env.shell = as.shell; env.graph = env.graph || {}; env.seen = env.seen || []; env.as = env.as || as; @@ -487,8 +488,10 @@ at.env = env; at.soul = soul; if(Node.ify(at.obj, map, at)){ - //at.rel = at.rel || Val.rel.ify(Node.soul(at.node)); - env.graph[Val.rel.is(at.rel)] = at.node; + at.rel = at.rel || Val.rel.ify(Node.soul(at.node)); + if(at.obj !== env.shell){ + env.graph[Val.rel.is(at.rel)] = at.node; + } } return at; } diff --git a/lib/space.js b/lib/space.js index a36ed7a0..bb7e1a82 100644 --- a/lib/space.js +++ b/lib/space.js @@ -1,6 +1,7 @@ ;(function(){ var Gun = (typeof window !== "undefined")? window.Gun : require('../gun'); var ify = Gun.node.ify, empty = {}, u; + console.log("Index space is beta, API may change!"); Gun.chain.space = function(key, data, opt){ if(data instanceof Function){ return travel(key, data, opt, this); @@ -15,48 +16,87 @@ }, true); return gun; } - var cb = (opt instanceof Function && opt), rank = (opt||empty).rank || opt, tmp; + var cb = (opt instanceof Function && opt), rank = (opt||empty).rank || opt, root = gun.back(-1), tmp; gun.get(function(soul){ if(!soul){ soul = (gun.back('opt.uuid') || Gun.text.random)(9); } - var space = ify({}, soul), sub = space, l = 0, tmp; - Gun.list.map(index(0, key.length), function(i){ - sub[(tmp = key.slice(l, i))+'"'] = data; - sub = sub[tmp] = ify({}, soul+'"'+key.slice(0,i)); + /*var space = ify({}, soul), sub = space, l = 0, tmp; + var atom = Gun.text.ify({get: key, put: data}); + Gun.list.map(index(0, key.length), function(i){ + sub[(tmp = key.slice(l, i))+'"'] = atom; + sub = sub[tmp] = ify({}, soul+'"'+key.slice(0,i)); + l = i; + }); + tmp = {}; tmp[key] = atom.put; tmp = ify(tmp, soul+'"'); + sub[key.slice(l, key.length)] = tmp; + console.log('????', space);*/ + var shell = {}, l = 0, tmp; + var atom = Gun.text.ify({get: key, put: data}); + tmp = {}; tmp[key] = data; + shell.$ = ify(tmp, soul); + tmp = {}; tmp[key.slice(0,l = 1)] = atom; + shell[0] = ify(tmp, soul+'"'); + Gun.list.map(index(1, key.length), function(i){ + tmp = {}; tmp[key.slice(l,i)] = atom; + shell[i] = ify(tmp, soul+'"'+key.slice(0,l)); l = i; }); - tmp = {}; tmp[key] = data; tmp = ify(tmp, soul+'"'); - sub[key.slice(l, key.length)] = tmp; - //console.log(space); - gun.put(space, cb); + tmp = {}; tmp[key.slice(l, key.length)] = atom; + shell[l+1] = ify(tmp, soul+'"'+key.slice(0,l)); + //tmp = {}; tmp[key.slice(l, key.length)] = Gun.val.link.ify(soul); shell[l+1] = ify(tmp, soul+'"'+key.slice(0,l)); + //console.log('???', shell); + gun.put(shell, cb, {soul: soul, shell: shell}); },true); return gun; } function travel(key, cb, opt, ref){ var root = ref.back(-1), tmp; opt = opt || {}; + opt.ack = opt.ack || {}; ref.get(function(soul){ - root.get(soul+'"').get(key).get(function(msg, eve){ - if(u !== msg.put){ - eve.off(); - cb(msg.put, msg.get, msg, eve); - return; - } - opt.soul = soul; - opt.start = soul+'"'; - opt.key = key; - opt.top = index(0, opt.find); - opt.low = opt.top.reverse(); - find(opt, cb, root); + ref.get(key).get(function(msg, eve){ + eve.off(); + opt.exact = true; + opt.ack.key = key; + opt.ack.data = msg.put; + if(opt.match){ cb(opt.ack, key, msg, eve) } }); - }, true) + //if(u !== msg.put){ + // cb(msg.put, msg.get, msg, eve); + // return; + //} + opt.soul = soul; + opt.start = soul+'"'; + opt.key = key; + opt.top = index(0, opt.find); + opt.low = opt.top.reverse(); + find(opt, cb, root); + }, true); } function find(o, cb, root){ var id = o.start+o.key.slice(0,o.low[0]); root.get(id).get(function(msg, eve){ eve.off(); - console.log("oh my!", msg.put, o.start); + o.ack.tree = {}; + if(u === msg.put){ + if(!o.exact){ return o.match = true } + cb(o.ack, id, msg, eve); + return; + o.low = o.low.slice(1); + if(!o.low.length){ + cb(u, o.key, msg, eve); + return; + } + find(o, cb, root); + return; + } + Gun.node.is(msg.put, function(v,k){ + if(!(k = Gun.obj.ify(v) || empty).get){ return } + o.ack.tree[k.get] = k.put; + }); + if(!o.exact){ return o.match = true } + cb(o.ack, id, msg, eve); }); } function index(n, m, l, k){ diff --git a/test/tmp/space.html b/test/tmp/space.html new file mode 100644 index 00000000..c083ed23 --- /dev/null +++ b/test/tmp/space.html @@ -0,0 +1,49 @@ +

Search

+ +
+ +
+ +
+
    + + + + + + + + \ No newline at end of file From 443be3b222fef7618b67808c0e382d366e7c25c5 Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Sat, 18 Aug 2018 16:19:23 -0700 Subject: [PATCH 008/103] Index space in alpha! --- test/tmp/space.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/tmp/space.html b/test/tmp/space.html index c083ed23..72a7f15e 100644 --- a/test/tmp/space.html +++ b/test/tmp/space.html @@ -7,10 +7,11 @@
      +Note: No data is indexed by default, you need to add some! + - ``` -- Or try something mind blowing, like saving circular references to a table of documents! ([play](http://jsbin.com/wefozepume/edit?js,console)) +- Or try something **mind blowing**, like saving circular references to a table of documents! ([play](http://jsbin.com/wefozepume/edit?js,console)) ```javascript var cat = {name: "Fluffy", species: "kitty"}; var mark = {boss: cat}; @@ -65,7 +65,7 @@ cat.slave = mark; gun.get('mark').put(mark); // access the data as if it is a document. -gun.get('mark').get('boss').get('name').val(function(data, key){ +gun.get('mark').get('boss').get('name').once(function(data, key){ // `val` grabs the data once, no subscriptions. console.log("Mark's boss is", data); }); @@ -88,7 +88,15 @@ gun.get('list').map().once(function(data, key){ gun.get('list').set({type: "cucumber", goal: "scare cat"}); ``` -## Support +Want to keep building more? **Jump to [THE DOCUMENTATION](#documentation)!** + +# What is GUN? + +First & foremost, GUN is **a community of the nicest and most helpful people** out there. So [I want to invite you](https://gitter.im/amark/gun) to come tell us about what **you** are working on & wanting to build (new or old school alike! Just be nice as well.) and ask us your questions directly. :) + +On that note, let's get some official shout outs covered first: + +### Support

      Thanks to:
      @@ -109,6 +117,19 @@ Thanks to:
      - Found a bug? Report at: https://github.com/amark/gun/issues ; - **Need help**? Chat with us: https://gitter.im/amark/gun . +### History + +[GUN](https://gun.eco) was created by [Mark Nadal](https://twitter.com/marknadal) in 2014 after he had spent 4 years trying to get his collaborative web app to scale up with traditional databases. + + After he realized [Master-Slave database architecture causes one big bottleneck](https://gun.eco/distributed/matters.html), he (as a complete newbie outsider) naively decided **to question the status quo** and shake things up with controversial, heretical, and contrarian experiments: + +**The NoDB** - no master, no servers, no "single source of truth", not built with a real programming language or real hardware, no DevOps, no locking, not *just* SQL or NoSQL but both (**all** - graphs, documents, tables, key/value). + +The goal was to build a P2P database that could survive living inside **any** browser, and could correctly sync data between **any** device after assuming **any** offline-first activity. + + + +Technically, **GUN is a graph synchronization protocol** with a *lightweight embedded engine*, capable of doing *[20M+ API ops/sec](https://gun.eco/docs/100000-ops-sec-in-IE6-on-2GB-Atom-CPU)* in **just ~9KB gzipped size**. ## Documentation From 9781877db822e7cfc52d0cdbd6a4ded7b54ac571 Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Thu, 23 Aug 2018 23:49:25 -0700 Subject: [PATCH 012/103] fix 1 of @mmalmi 's found bug. --- test/tmp/li.html => examples/simple/show.html | 0 {test/tmp => examples/simple}/space.html | 0 {test/tmp => examples/simple}/tables.html | 0 {test/tmp => examples/simple}/user.html | 0 gun.js | 14 +++-------- package.json | 2 +- sea.js | 5 ++-- test/common.js | 24 +++++++++++++++++++ 8 files changed, 30 insertions(+), 15 deletions(-) rename test/tmp/li.html => examples/simple/show.html (100%) rename {test/tmp => examples/simple}/space.html (100%) rename {test/tmp => examples/simple}/tables.html (100%) rename {test/tmp => examples/simple}/user.html (100%) diff --git a/test/tmp/li.html b/examples/simple/show.html similarity index 100% rename from test/tmp/li.html rename to examples/simple/show.html diff --git a/test/tmp/space.html b/examples/simple/space.html similarity index 100% rename from test/tmp/space.html rename to examples/simple/space.html diff --git a/test/tmp/tables.html b/examples/simple/tables.html similarity index 100% rename from test/tmp/tables.html rename to examples/simple/tables.html diff --git a/test/tmp/user.html b/examples/simple/user.html similarity index 100% rename from test/tmp/user.html rename to examples/simple/user.html diff --git a/gun.js b/gun.js index d467fdfb..f395a6d0 100644 --- a/gun.js +++ b/gun.js @@ -967,7 +967,6 @@ at.on('in', at); return; }*/ - //console.log("out!", at.get, get); if(get['#'] || at.soul){ get['#'] = get['#'] || at.soul; msg['#'] || (msg['#'] = text_rand(9)); @@ -1107,13 +1106,12 @@ not(at, msg); } tmp = from.id? ((at.map || (at.map = {}))[from.id] = at.map[from.id] || {at: from}) : {}; - //console.log("REL?", at.id, at.get, rel === tmp.link, tmp.pass || at.pass); if(rel === tmp.link){ if(!(tmp.pass || at.pass)){ return; } } - if(at.pass){ + if(at.pass){ Gun.obj.map(at.map, function(tmp){ tmp.pass = true }) obj_del(at, 'pass'); } @@ -1730,10 +1728,8 @@ gun.map().on(function(data, key, at, ev){ var next = (cb||noop).call(this, data, key, at, ev); if(u === next){ return } - if(data === next || Gun.is(next)){ - chain._.on('in', next._); - return; - } + if(data === next){ return chain._.on('in', at) } + if(Gun.is(next)){ return chain._.on('in', next._) } chain._.on('in', {get: key, put: next}); }); return chain; @@ -2024,11 +2020,7 @@ var wire = peer.wire; try{ if(wire.send){ - if(wire.readyState === wire.OPEN){ wire.send(raw); - } else { - (peer.queue = peer.queue || []).push(raw); - } } else if(peer.say){ peer.say(raw); diff --git a/package.json b/package.json index f121ee42..30df1026 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gun", - "version": "0.9.9998", + "version": "0.9.9999", "description": "A realtime, decentralized, offline-first, graph data synchronization engine.", "main": "index.js", "browser": "gun.min.js", diff --git a/sea.js b/sea.js index 79e3dced..fc83ff5c 100644 --- a/sea.js +++ b/sea.js @@ -1067,9 +1067,8 @@ var Gun = SEA.Gun; var then = USE('./then'); - function User(){ - this._ = {$: this} - Gun.call() + function User(root){ + this._ = {$: this}; } User.prototype = (function(){ function F(){}; F.prototype = Gun.chain; return new F() }()) // Object.create polyfill User.prototype.constructor = User; diff --git a/test/common.js b/test/common.js index 49a3a491..d933b469 100644 --- a/test/common.js +++ b/test/common.js @@ -3366,6 +3366,30 @@ describe('Gun', function(){ list.set({name: 'dave', age: 25}); }); + it('once map function once', function(done){ + var gun = Gun(), s = 'o/mf/o', u; + var app = gun.get(s); + var list = app.get('list'); + + var check = {}; + gun.get('user').get('alice').put({name:'Alice', email:'alice@example.com'}) + gun.get('user').get('bob').put({name:'Bob', email:'bob@example.com'}) + gun.get('user').get('carl').put({name:'Carl', email:'carl@example.com'}) + + gun.get('user').once().map(v => { + //console.log('this gets called', v); + return v + }).once((v, k) => { + //console.log('this is never called', k, v); + check[k] = (check[k] || 0) + 1; + if(1 === check.alice && 1 === check.bob && 1 === check.carl){ + if(done.c){return}done.c=1; + done(); + } + }); + + }); + it('val and then map', function(done){ var gun = Gun(), s = 'val/then/map', u; var list = gun.get(s); From 84a000f83907a6c03037d8eb99f12d5806124d1b Mon Sep 17 00:00:00 2001 From: Robert Heessels Date: Fri, 24 Aug 2018 10:45:44 +0200 Subject: [PATCH 013/103] First version of RAD IndexedDB. --- lib/rindexed.js | 87 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 lib/rindexed.js diff --git a/lib/rindexed.js b/lib/rindexed.js new file mode 100644 index 00000000..5bd340db --- /dev/null +++ b/lib/rindexed.js @@ -0,0 +1,87 @@ +function Store(opt){ + opt = opt || {}; + opt.file = String(opt.file || 'radata'); + var db = null; + + // Initialize indexedDB. Version 1. + const request = window.indexedDB.open(opt.file, 1) + + // Create schema. onupgradeneeded is called only when DB is first created or when the DB version increases. + request.onupgradeneeded = function(event){ + const db = event.target.result; + db.createObjectStore(opt.file); + } + + // onsuccess is called when the DB is ready. + request.onsuccess = function(){ + db = request.result; + } + + request.onerror = function(event){ + console.log('ERROR: RAD IndexedDB generic error:', event); + }; + + var store = function Store(){}, u; + + store.put = function(file, data, cb){ + cb = cb || function(){}; + if(!db){ + const es = 'ERROR: RAD IndexedDB not yet ready.' + console.log(es); + cb(es, undefined); + } else { + // Start a transaction. The transaction will be automaticallt closed when the last success/error handler took no new action. + const transaction = db.transaction([opt.file], 'readwrite'); + + // Add or update data. + const radStore = transaction.objectStore(opt.file); + const putRequest = radStore.put(data, file); + putRequest.onsuccess = radStore.onsuccess = transaction.onsuccess = function(){ + console.log('RAD IndexedDB put transaction was succesful.'); + cb(null, 1); + }; + putRequest.onabort = radStore.onabort = transaction.onabort = function(){ + const es = 'ERROR: RAD IndexedDB put transaction was aborted.'; + console.log(es); + cb(es, undefined); + }; + putRequest.onerror = radStore.onerror = transaction.onerror = function(event){ + const es = 'ERROR: RAD IndexedDB put transaction was in error: ' + JSON.stringify(event) + console.log(es); + cb(es, undefined); + }; + } + }; + + store.get = function(file, cb){ + cb = cb || function(){}; + if(!db){ + const es = 'ERROR: RAD IndexedDB not yet ready.'; + console.log(es); + cb(es, undefined); + } else { + // Start a transaction. The transaction will be automaticallt closed when the last success/error handler took no new action. + const transaction = db.transaction([opt.file], 'readwrite'); + + // Read data. + const radStore = transaction.objectStore(opt.file); + const getRequest = radStore.get(file); + getRequest.onsuccess = function(){ + console.log('RAD IndexedDB get transaction was succesful.'); + cb(null, getRequest.result); + }; + getRequest.onabort = function(){ + const es = 'ERROR: RAD IndexedDB get transaction was aborted.'; + console.log(es); + cb(es, undefined); + }; + getRequest.onerror = function(event){ + const es = 'ERROR: RAD IndexedDB get transaction was in error: ' + JSON.stringify(event) + console.log(es); + cb(es, undefined); + }; + } + }; + + return store; +} From 90feaa53972ce86c208327de9d198794e10da800 Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Fri, 24 Aug 2018 02:04:58 -0700 Subject: [PATCH 014/103] SEA tweaks --- sea.js | 19 ++++++++++--------- test/panic/who.js | 20 ++++++++------------ 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/sea.js b/sea.js index fc83ff5c..2a5e907f 100644 --- a/sea.js +++ b/sea.js @@ -24,7 +24,7 @@ // THIS IS AN EARLY ALPHA! function SEA(){} - if(typeof window !== "undefined"){ SEA.window = window } + if(typeof window !== "undefined"){ (SEA.window = window).SEA = SEA } module.exports = SEA; })(USE, './root'); @@ -174,7 +174,7 @@ }); try{ const WebCrypto = require('node-webcrypto-ossl') - api.ossl = new WebCrypto({directory: 'key_storage'}).subtle // ECDH + api.ossl = new WebCrypto({directory: 'ossl'}).subtle // ECDH }catch(e){ console.log("node-webcrypto-ossl is optionally needed for ECDH, please install if needed."); } @@ -240,16 +240,14 @@ })(USE, './parse'); ;USE(function(module){ - const { - subtle, ossl = subtle, random: getRandomBytes, TextEncoder, TextDecoder - } = USE('./shim') + const shim = USE('./shim'); const Buffer = USE('./buffer') const parse = USE('./parse') const { pbkdf2 } = USE('./settings') // This internal func returns SHA-256 hashed data for signing const sha256hash = async (mm) => { const m = parse(mm) - const hash = await ossl.digest({name: pbkdf2.hash}, new TextEncoder().encode(m)) + const hash = await shim.subtle.digest({name: pbkdf2.hash}, new shim.TextEncoder().encode(m)) return Buffer.from(hash) } module.exports = sha256hash @@ -368,7 +366,8 @@ const r = { pub: sa.pub, priv: sa.priv, /* pubId, */ epub: dh.epub, epriv: dh.epriv } if(cb){ try{ cb(r) }catch(e){console.log(e)} } return r; - } catch(e) { + } catch(e) { + console.log(e); SEA.err = e; if(cb){ cb() } return; @@ -404,7 +403,8 @@ if(cb){ try{ cb(r) }catch(e){console.log(e)} } return r; - } catch(e) { + } catch(e) { + console.log(e); SEA.err = e; if(cb){ cb() } return; @@ -441,7 +441,8 @@ if(cb){ try{ cb(r) }catch(e){console.log(e)} } return r; - } catch(e) { + } catch(e) { + console.log(e); SEA.err = e; if(cb){ cb() } return; diff --git a/test/panic/who.js b/test/panic/who.js index a6f9e3e3..1deaa5d5 100644 --- a/test/panic/who.js +++ b/test/panic/who.js @@ -7,7 +7,6 @@ var config = { '/': __dirname + '/index.html', '/gun.js': __dirname + '/../../gun.js', '/jquery.js': __dirname + '/../../examples/jquery.js', - '/cryptomodules.js': __dirname + '/../../lib/cryptomodules.js', '/sea.js': __dirname + '/../../sea.js' } } @@ -57,6 +56,7 @@ describe("Make sure SEA syncs correctly", function(){ res.end("I am "+ env.i +"!"); }); var Gun = require('gun'); + require('gun/sea'); var gun = Gun({file: env.i+'data', web: server}); server.listen(port, function(){ test.done(); @@ -80,10 +80,8 @@ describe("Make sure SEA syncs correctly", function(){ script.onload = cb; script.src = src; document.head.appendChild(script); } - load('cryptomodules.js', function(){ - load('sea.js', function(){ - test.done(); - }); + load('sea.js', function(){ + test.done(); }); }, {i: i += 1, config: config})); }); @@ -140,8 +138,8 @@ describe("Make sure SEA syncs correctly", function(){ return bob.run(function(test){ test.async(); - window.gun.get('alias/alice').map().val(function(data){ - window.ref = gun.get('pub/'+data.pub); + window.gun.get('~@alice').map().once(function(data){ + window.ref = gun.get('~'+data.pub); test.done(); }); }); @@ -152,7 +150,7 @@ describe("Make sure SEA syncs correctly", function(){ test.async(); window.count = []; - ref.get('who').get('said').map().val(function(data){ + ref.get('who').get('said').map().once(function(data){ console.log("read...", data); window.count.push(data); if(window.count.length - 1){ return } @@ -181,10 +179,8 @@ describe("Make sure SEA syncs correctly", function(){ script.onload = cb; script.src = src; document.head.appendChild(script); } - load('cryptomodules.js', function(){ - load('sea.js', function(){ - test.done(); - }); + load('sea.js', function(){ + test.done(); }); }, {i: 1, config: config}); }); From aee20ebbc3aba93bf0564ff4436fdce524ca8a24 Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Fri, 24 Aug 2018 03:06:12 -0700 Subject: [PATCH 015/103] RAD indexedDB! --- gun.js | 35 ++++---- lib/rindexed.js | 180 ++++++++++++++++++++++------------------ test/tmp/indexedDB.html | 25 ++++++ 3 files changed, 139 insertions(+), 101 deletions(-) create mode 100644 test/tmp/indexedDB.html diff --git a/gun.js b/gun.js index f395a6d0..67791109 100644 --- a/gun.js +++ b/gun.js @@ -883,7 +883,7 @@ 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 } + if(typeof window !== "undefined"){ (window.Gun = Gun).window = window } try{ if(typeof common !== "undefined"){ common.exports = Gun } }catch(e){} module.exports = Gun; @@ -1787,24 +1787,21 @@ // See the next 'opt' code below for actual saving of data. var ev = this.to, opt = root.opt; if(root.once){ return ev.next(root) } - if(false === opt.localStorage){ return ev.next(root) } - opt.file = opt.file || 'gun/'; - var gap = Gun.obj.ify(store.getItem('gap/'+opt.file)) || {}; + //if(false === opt.localStorage){ return ev.next(root) } // we want offline resynce queue regardless! + opt.prefix = opt.file || 'gun/'; + var gap = Gun.obj.ify(store.getItem('gap/'+opt.prefix)) || {}; var empty = Gun.obj.empty, id, to, go; // add re-sync command. if(!empty(gap)){ - root.on('localStorage', function(disk){ - this.off(); - var send = {} - Gun.obj.map(gap, function(node, soul){ - Gun.obj.map(node, function(val, key){ - send[soul] = Gun.state.to(disk[soul], key, send[soul]); - }); + var disk = Gun.obj.ify(store.getItem(opt.prefix)) || {}, send = {}; + Gun.obj.map(gap, function(node, soul){ + Gun.obj.map(node, function(val, key){ + send[soul] = Gun.state.to(disk[soul], key, send[soul]); }); - setTimeout(function(){ - root.on('out', {put: send, '#': root.ask(ack), I: root.$}); - },10); }); + setTimeout(function(){ + root.on('out', {put: send, '#': root.ask(ack), I: root.$}); + },10); } root.on('out', function(msg){ @@ -1842,7 +1839,7 @@ var flush = function(){ clearTimeout(to); to = false; - try{store.setItem('gap/'+opt.file, JSON.stringify(gap)); + try{store.setItem('gap/'+opt.prefix, JSON.stringify(gap)); }catch(e){ Gun.log(err = e || "localStorage failure") } } }); @@ -1852,9 +1849,9 @@ var opt = root.opt; if(root.once){ return } if(false === opt.localStorage){ return } - opt.file = opt.file || opt.prefix || 'gun/'; // support old option name. + opt.prefix = opt.file || 'gun/'; var graph = root.graph, acks = {}, count = 0, to; - var disk = Gun.obj.ify(store.getItem(opt.file)) || {}; + var disk = Gun.obj.ify(store.getItem(opt.prefix)) || {}; var lS = function(){}, u; root.on('localStorage', disk); // NON-STANDARD EVENT! @@ -1902,10 +1899,10 @@ var ack = acks; acks = {}; if(data){ disk = data } - try{store.setItem(opt.file, JSON.stringify(disk)); + try{store.setItem(opt.prefix, JSON.stringify(disk)); }catch(e){ Gun.log(err = e || "localStorage failure"); - root.on('localStorage:error', {err: err, file: opt.file, flush: disk, retry: flush}); + root.on('localStorage:error', {err: err, file: opt.prefix, flush: disk, retry: flush}); } if(!err && !Gun.obj.empty(opt.peers)){ return } // only ack if there are no peers. Gun.obj.map(ack, function(yes, id){ diff --git a/lib/rindexed.js b/lib/rindexed.js index 5bd340db..671dccb8 100644 --- a/lib/rindexed.js +++ b/lib/rindexed.js @@ -1,87 +1,103 @@ -function Store(opt){ - opt = opt || {}; - opt.file = String(opt.file || 'radata'); - var db = null; +;(function(){ + var Gun = (typeof window !== "undefined")? window.Gun : require('../gun'); - // Initialize indexedDB. Version 1. - const request = window.indexedDB.open(opt.file, 1) + Gun.on('create', function(root){ + this.to.next(root); + root.opt.store = root.opt.store || Store(root.opt); + }); - // Create schema. onupgradeneeded is called only when DB is first created or when the DB version increases. - request.onupgradeneeded = function(event){ - const db = event.target.result; - db.createObjectStore(opt.file); + function Store(opt){ + opt = opt || {}; + opt.file = String(opt.file || 'radata'); + var db = null; + + opt.indexedDB = opt.indexedDB || window.indexedDB; + // Initialize indexedDB. Version 1. + var request = opt.indexedDB.open(opt.file, 1) + + // Create schema. onupgradeneeded is called only when DB is first created or when the DB version increases. + request.onupgradeneeded = function(event){ + var db = event.target.result; + db.createObjectStore(opt.file); + } + + // onsuccess is called when the DB is ready. + request.onsuccess = function(){ + db = request.result; + } + + request.onerror = function(event){ + console.log('ERROR: RAD IndexedDB generic error:', event); + }; + + var store = function Store(){}, u; + + store.put = function(file, data, cb){ + cb = cb || function(){}; + if(!db){ + var es = 'ERROR: RAD IndexedDB not yet ready.' + console.log(es); + cb(es, undefined); + } else { + // Start a transaction. The transaction will be automaticallt closed when the last success/error handler took no new action. + var transaction = db.transaction([opt.file], 'readwrite'); + + // Add or update data. + var radStore = transaction.objectStore(opt.file); + var putRequest = radStore.put(data, file); + putRequest.onsuccess = radStore.onsuccess = transaction.onsuccess = function(){ + //console.log('RAD IndexedDB put transaction was succesful.'); + cb(null, 1); + }; + putRequest.onabort = radStore.onabort = transaction.onabort = function(){ + var es = 'ERROR: RAD IndexedDB put transaction was aborted.'; + console.log(es); + cb(es, undefined); + }; + putRequest.onerror = radStore.onerror = transaction.onerror = function(event){ + var es = 'ERROR: RAD IndexedDB put transaction was in error: ' + JSON.stringify(event) + console.log(es); + cb(es, undefined); + }; + } + }; + + store.get = function(file, cb){ + cb = cb || function(){}; + if(!db){ + var es = 'ERROR: RAD IndexedDB not yet ready.'; + console.log(es); + cb(es, undefined); + } else { + // Start a transaction. The transaction will be automaticallt closed when the last success/error handler took no new action. + var transaction = db.transaction([opt.file], 'readwrite'); + + // Read data. + var radStore = transaction.objectStore(opt.file); + var getRequest = radStore.get(file); + getRequest.onsuccess = function(){ + //console.log('RAD IndexedDB get transaction was succesful.'); + cb(null, getRequest.result); + }; + getRequest.onabort = function(){ + var es = 'ERROR: RAD IndexedDB get transaction was aborted.'; + console.log(es); + cb(es, undefined); + }; + getRequest.onerror = function(event){ + var es = 'ERROR: RAD IndexedDB get transaction was in error: ' + JSON.stringify(event) + console.log(es); + cb(es, undefined); + }; + } + }; + + return store; } - // onsuccess is called when the DB is ready. - request.onsuccess = function(){ - db = request.result; + if(Gun.window){ + Gun.window.RindexedDB = Store; + } else { + module.exports = Store; } - - request.onerror = function(event){ - console.log('ERROR: RAD IndexedDB generic error:', event); - }; - - var store = function Store(){}, u; - - store.put = function(file, data, cb){ - cb = cb || function(){}; - if(!db){ - const es = 'ERROR: RAD IndexedDB not yet ready.' - console.log(es); - cb(es, undefined); - } else { - // Start a transaction. The transaction will be automaticallt closed when the last success/error handler took no new action. - const transaction = db.transaction([opt.file], 'readwrite'); - - // Add or update data. - const radStore = transaction.objectStore(opt.file); - const putRequest = radStore.put(data, file); - putRequest.onsuccess = radStore.onsuccess = transaction.onsuccess = function(){ - console.log('RAD IndexedDB put transaction was succesful.'); - cb(null, 1); - }; - putRequest.onabort = radStore.onabort = transaction.onabort = function(){ - const es = 'ERROR: RAD IndexedDB put transaction was aborted.'; - console.log(es); - cb(es, undefined); - }; - putRequest.onerror = radStore.onerror = transaction.onerror = function(event){ - const es = 'ERROR: RAD IndexedDB put transaction was in error: ' + JSON.stringify(event) - console.log(es); - cb(es, undefined); - }; - } - }; - - store.get = function(file, cb){ - cb = cb || function(){}; - if(!db){ - const es = 'ERROR: RAD IndexedDB not yet ready.'; - console.log(es); - cb(es, undefined); - } else { - // Start a transaction. The transaction will be automaticallt closed when the last success/error handler took no new action. - const transaction = db.transaction([opt.file], 'readwrite'); - - // Read data. - const radStore = transaction.objectStore(opt.file); - const getRequest = radStore.get(file); - getRequest.onsuccess = function(){ - console.log('RAD IndexedDB get transaction was succesful.'); - cb(null, getRequest.result); - }; - getRequest.onabort = function(){ - const es = 'ERROR: RAD IndexedDB get transaction was aborted.'; - console.log(es); - cb(es, undefined); - }; - getRequest.onerror = function(event){ - const es = 'ERROR: RAD IndexedDB get transaction was in error: ' + JSON.stringify(event) - console.log(es); - cb(es, undefined); - }; - } - }; - - return store; -} +}()); \ No newline at end of file diff --git a/test/tmp/indexedDB.html b/test/tmp/indexedDB.html new file mode 100644 index 00000000..53d9afd2 --- /dev/null +++ b/test/tmp/indexedDB.html @@ -0,0 +1,25 @@ +

      RindexedDB

      + + + + + + + + + + +

      + + + + \ No newline at end of file From 80a9a67d0b344c093d887f5093c8885e4642b432 Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Fri, 24 Aug 2018 03:07:56 -0700 Subject: [PATCH 016/103] unbuild --- gun.min.js | 2 +- sea/authenticate.js | 6 +++--- sea/create.js | 16 ++++++++++++++-- sea/pair.js | 3 ++- sea/root.js | 2 +- sea/sha256.js | 6 ++---- sea/shim.js | 2 +- sea/sign.js | 3 ++- sea/user.js | 5 ++--- sea/verify.js | 3 ++- src/adapters/localStorage.js | 33 +++++++++++++++------------------ src/adapters/mesh.js | 4 ---- src/chain.js | 4 +--- src/graph.js | 7 +++++-- src/map.js | 6 ++---- src/root.js | 20 ++++++++++++++++++-- 16 files changed, 71 insertions(+), 51 deletions(-) diff --git a/gun.min.js b/gun.min.js index ba252adf..d80b0399 100644 --- a/gun.min.js +++ b/gun.min.js @@ -1 +1 @@ -!function(){var t;"undefined"!=typeof window&&(t=window),"undefined"!=typeof global&&(t=global);var b=(t=t||{}).console||{log:function(){}};function _(o){return o.slice?_[e(o)]:function(t,n){o(t={exports:{}}),_[e(n)]=t.exports};function e(t){return t.split("/").slice(-1).toString().replace(".js","")}}if("undefined"!=typeof module)var f=module;_(function(t){var p={fn:{is:function(t){return!!t&&"function"==typeof t}}};p.bi={is:function(t){return t instanceof Boolean||"boolean"==typeof t}},p.num={is:function(t){return!d(t)&&(0<=t-parseFloat(t)+1||1/0===t||-1/0===t)}},p.text={is:function(t){return"string"==typeof t}},p.text.ify=function(t){return p.text.is(t)?t:"undefined"!=typeof JSON?JSON.stringify(t):t&&t.toString?t.toString():t},p.text.random=function(t,n){var o="";for(t=t||24,n=n||"0123456789ABCDEFGHIJKLMNOPQRSTUVWXZabcdefghijklmnopqrstuvwxyz";0")){if(!(n>t[">"]))return!1;o=!0}if(p.obj.has(t,"<")){if(!(n",s.drift=0,s.is=function(t,n,o){var e=n&&t&&t[m]&&t[m][s._]||o;if(e)return g(e=e[n])?e:-1/0},s.lex=function(){return s().toString(36).replace(".","")},s.ify=function(t,n,o,e,i){if(!t||!t[m]){if(!i)return;t=a.soul.ify(t,i)}var r=c(t[m],s._);return void 0!==n&&n!==m&&(g(o)&&(r[n]=o),void 0!==e&&(t[n]=e)),t},s.to=function(t,n,o){var e=t[n];return p(e)&&(e=d(e)),s.ify(o,n,s.is(t,n),e,a.soul(t))},function(){function u(t,n){m!==n&&s.ify(this.o,n,this.s)}s.map=function(i,r,a){var t=p(t=i||r)?t:null;return i=v(i=i||r)?i:null,t&&!i?(r=g(r)?r:s(),t[m]=t[m]||{},h(t,u,{o:t,s:r}),t):(a=a||p(r)?r:void 0,r=g(r)?r:s(),function(t,n,o,e){if(!i)return u.call({o:o,s:r},t,n),t;i.call(a||this||{},t,n,o,e),l(o,n)&&void 0===o[n]||u.call({o:o,s:r},t,n)})}}();var f=n.obj,c=f.as,l=f.has,p=f.is,h=f.map,d=f.copy,g=n.num.is,v=n.fn.is,m=a._;t.exports=s})(_,"./state"),_(function(t){var a=_("./type"),f=_("./val"),c=_("./node"),r={};!function(){function i(t,n){if(!t||n!==c.soul(t)||!c.is(t,this.fn,this.as))return!0;this.cb&&(o.n=t,o.as=this.as,this.cb.call(o.as,t,n,o))}function o(t){t&&c.is(o.n,t,o.as)}r.is=function(t,n,o,e){return!(!t||!l(t)||u(t))&&!s(t,i,{cb:n,fn:o,as:e})}}(),function(){function u(t,n){var o;return(o=function(t,n){var o,e=t.seen,i=e.length;for(;i--;)if(o=e[i],n.obj===o.obj)return o;e.push(n)}(t,n))?o:(n.env=t,n.soul=i,c.ify(n.obj,e,n)&&(t.graph[f.rel.is(n.rel)]=n.node),n)}function e(t,n,o){var e,i,r=this,a=r.env;if(c._===n&&h(t,f.rel._))return o._;if(e=s(t,n,o,r,a)){if(n||(r.node=r.node||o||{},h(t,c._)&&c.soul(t)&&(r.node._=d(t._)),r.node=c.soul.ify(r.node,f.rel.is(r.rel)),r.rel=r.rel||f.rel.ify(c.soul(r.node))),(i=a.map)&&(i.call(a.as||{},t,n,o,r),h(o,n))){if(void 0===(t=o[n]))return void p(o,n);if(!(e=s(t,n,o,r,a)))return}if(!n)return r.node;if(!0===e)return t;if((i=u(a,{obj:t,path:r.path.concat(n)})).node)return i.rel}}function i(t){var n=this,o=f.link.is(n.rel),e=n.env.graph;n.rel=n.rel||f.rel.ify(t),n.rel[f.rel._]=t,n.node&&n.node[c._]&&(n.node[c._][f.rel._]=t),h(e,o)&&(e[t]=e[o],p(e,o))}function s(t,n,o,e,i){var r;return!!f.is(t)||(l(t)?1:(r=i.invalid)?s(t=r.call(i.as||{},t,n,o),n,o,e,i):(i.err="Invalid value at '"+e.path.concat(n).join(".")+"'!",void(a.list.is(t)&&(i.err+=" Use `.set(item)` instead of an Array."))))}r.ify=function(t,n,o){var e={path:[],obj:t};return n?"string"==typeof n?n={soul:n}:n instanceof Function&&(n.map=n):n={},n.soul&&(e.rel=f.rel.ify(n.soul)),n.graph=n.graph||{},n.seen=n.seen||[],n.as=n.as||o,u(n,e),n.root=e.node,n.graph}}(),r.node=function(t){var n=c.soul(t);if(n)return o({},n,t)},function(){function i(t,n){var o,e;if(c._!==n)(o=f.rel.is(t))?(e=this.opt.seen[o])?this.obj[n]=e:this.obj[n]=this.opt.seen[o]=r.to(this.graph,o,this.opt):this.obj[n]=t;else{if(u(t,f.rel._))return;this.obj[n]=d(t)}}r.to=function(t,n,o){if(t){var e={};return o=o||{seen:{}},s(t[n],i,{obj:e,graph:t,opt:o}),e}}}();a.fn.is;var n=a.obj,l=n.is,p=n.del,h=n.has,u=n.empty,o=n.put,s=n.map,d=n.copy;t.exports=r})(_,"./graph"),_(function(t){_("./onto"),t.exports=function(t,n){if(this.on){if(!(t instanceof Function)){if(!t||!n)return;var o=t["#"]||t,e=(this.tag||empty)[o];if(!e)return;return e=this.on(o,n),clearTimeout(e.err),!0}o=n&&n["#"]||Math.random().toString(36).slice(2);if(!t)return o;var i=this.on(o,t,n);return i.err=i.err||setTimeout(function(){i.next({err:"Error: No ACK received yet.",lack:!0}),i.off()},(this.opt||{}).lack||9e3),o}}})(_,"./ask"),_(function(t){var r=_("./type");var a=r.time.is;t.exports=function(e){var i={s:{}};return e=e||{max:1e3,age:9e3},i.check=function(t){var n;return!!(n=i.s[t])&&(n.pass?n.pass=!1:i.track(t))},i.track=function(t,n){var o=i.s[t]||(i.s[t]={});return o.was=a(),n&&(o.pass=!0),i.to||(i.to=setTimeout(function(){var o=a();r.obj.map(i.s,function(t,n){e.age>o-t.was||r.obj.del(i.s,n)}),i.to=null},e.age+9)),o},i}})(_,"./dup"),_(function(t){function c(t){return t instanceof c?(this._={gun:this,$:this}).$:this instanceof c?c.create(this._={gun:this,$:this,opt:t}):new c(t)}c.is=function(t){return t instanceof c||t&&t._&&t===t._.$||!1},c.version=.9,(c.chain=c.prototype).toJSON=function(){};var n=_("./type");n.obj.to(n,c),c.HAM=_("./HAM"),c.val=_("./val"),c.node=_("./node"),c.state=_("./state"),c.graph=_("./graph"),c.on=_("./onto"),c.ask=_("./ask"),c.dup=_("./dup"),function(){function a(t){var n,o,e=this.as,i=e.at||e,r=i.$;(o=t["#"])||(o=t["#"]=u(9)),(n=i.dup).check(o)?e.out===t.out&&(t.out=void 0,this.to.next(t)):(n.track(o),i.ask(t["@"],t)||(t.get&&c.on.get(t,r),t.put&&c.on.put(t,r)),this.to.next(t),e.out||(t.out=a,i.on("out",t)))}c.create=function(t){t.root=t.root||t,t.graph=t.graph||{},t.on=t.on||c.on,t.ask=t.ask||c.ask,t.dup=t.dup||c.dup();var n=t.$.opt(t.opt);return t.once||(t.on("in",a,t),t.on("out",a,{at:t,out:a}),c.on("create",t),t.on("create",t)),t.once=1,n}}(),function(){function i(t,n,o,e){var i=this,r=c.state.is(o,n);if(!r)return i.err="Error: No state on '"+n+"' in node '"+e+"'!";var a=i.graph[e]||v,u=c.state.is(a,n,!0),s=a[n],f=c.HAM(i.machine,r,u,t,s);f.incoming?(i.put[e]=c.state.to(o,n,i.put[e]),(i.diff||(i.diff={}))[e]=c.state.to(o,n,i.diff[e]),i.souls[e]=!0):f.defer&&(i.defer=r<(i.defer||1/0)?r:i.defer)}function r(t,n){var o=this,e=o.$._,i=(e.next||v)[n];if(!i){if(!(e.opt||v).super)return void(o.souls[n]=!1);i=o.$.get(n)._}var r=o.map[n]={put:t,get:n,$:i.$},a={ctx:o,msg:r};o.async=!!e.tag.node,o.ack&&(r["@"]=o.ack),h(t,u,a),o.async&&(o.and||e.on("node",function(t){this.to.next(t),t===o.map[t.get]&&(o.souls[t.get]=!1,h(t.put,s,t),h(o.souls,function(t){if(t)return t})||o.c||(o.c=1,this.off(),h(o.map,f,o)))}),o.and=!0,e.on("node",r))}function u(t,n){var o=this.ctx,e=o.graph,i=this.msg,r=i.get,a=i.put,u=i.$._;e[r]=c.state.to(a,n,e[r]),o.async||(u.put=c.state.to(a,n,u.put))}function s(t,n){var o=this.put,e=this.$._;e.put=c.state.to(o,n,e.put)}function f(t,n){t.$&&(this.cat.stop=this.stop,t.$._.on("in",t),this.cat.stop=null)}c.on.put=function(t,n){var o=n._,e={$:n,graph:o.graph,put:{},map:{},souls:{},machine:c.state(),ack:t["@"],cat:o,stop:{}};if(c.graph.is(t.put,null,i,e)||(e.err="Error: Invalid graph!"),e.err)return o.on("in",{"@":t["#"],err:c.log(e.err)});h(e.put,r,e),e.async||h(e.map,f,e),void 0!==e.defer&&setTimeout(function(){c.on.put(t,n)},e.defer-e.machine),e.diff&&o.on("put",p(t,{put:e.diff}))},c.on.get=function(t,n){var o=n._,e=t.get[d],i=o.graph[e],r=t.get[g],a=(o.next||(o.next={}))[e];if(!i||!a)return o.on("get",t);if(r){if(!l(i,r))return o.on("get",t);i=c.state.to(i,r)}else i=c.obj.copy(i);i=c.graph.node(i),a.ack,o.on("in",{"@":t["#"],how:"mem",put:i,$:n}),o.on("get",t)}}(),c.chain.opt=function(t){t=t||{};var n=this._,o=t.peers||t;return a(t)||(t={}),a(n.opt)||(n.opt=t),i(o)&&(o=[o]),e(o)&&(o=h(o,function(t,n,o){o(t,{url:t})}),a(n.opt.peers)||(n.opt.peers={}),n.opt.peers=p(o,n.opt.peers)),n.opt.peers=n.opt.peers||{},p(t,n.opt),c.on("opt",n),n.opt.uuid=n.opt.uuid||function(){return s()+u(12)},this};var e=c.list.is,o=c.text,i=o.is,u=o.random,r=c.obj,a=r.is,l=r.has,p=r.to,h=r.map,s=(r.copy,c.state.lex),d=c.val.rel._,g=".",v=(c.node._,c.val.link.is,{});b.debug=function(t,n){return b.debug.i&&t===b.debug.i&&b.debug.i++&&(b.log.apply(b,arguments)||n)},(c.log=function(){return!c.log.off&&b.log.apply(b,arguments),[].slice.call(arguments).join(" ")}).once=function(t,n,o){return(o=c.log.once)[t]=o[t]||0,o[t]++||c.log(n)},c.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'!"),"undefined"!=typeof window&&(window.Gun=c);try{void 0!==f&&(f.exports=c)}catch(t){}t.exports=c})(_,"./root"),_(function(t){var u=_("./root");u.chain.back=function(t,n){if(-1===(t=t||1)||1/0===t)return this._.root.$;if(1===t)return(this._.back||this._).$;var o=this._;if("string"==typeof t&&(t=t.split(".")),t instanceof Array){for(var e=0,i=t.length,r=o;e .once, apologies unexpected."),this.once(t,n)},a.chain.once=function(t,n){var o=this,e=o._,i=e.put;if(0=(a.batch||1e3))return f();e||(e=setTimeout(f,a.wait||1))}),r.on("get",function(n){this.to.next(n);var o,e,i=n.get;function t(){if(i&&(o=i["#"])){var t=i["."];(e=s[o]||void 0)&&t&&(e=Gun.state.to(e,t)),(e||Gun.obj.empty(a.peers))&&r.on("in",{"@":n["#"],put:Gun.graph.node(e),how:"lS",lS:n.I})}}Gun.debug?setTimeout(t,1):t()});var n=function(t,n,o,e){s[e]=Gun.state.to(o,n,s[e])},f=function(t){var o;u=0,clearTimeout(e),e=!1;var n=i;i={},t&&(s=t);try{c.setItem(a.file,JSON.stringify(s))}catch(t){Gun.log(o=t||"localStorage failure"),r.on("localStorage:error",{err:o,file:a.file,flush:s,retry:f})}(o||Gun.obj.empty(a.peers))&&Gun.obj.map(n,function(t,n){r.on("in",{"@":n,err:o,ok:0})})}}})}})(_,"./adapters/localStorage"),_(function(t){var d=_("../type");function o(p){var h=function(){};return h.out=function(t){var n;if(this.to&&this.to.next(t),(n=t["@"])&&(n=p.dup.s[n])&&(n=n.it)&&n.mesh)return h.say(t,n.mesh.via),void(n["##"]=t["##"]);h.say(t)},h.hear=function(t,n){if(t){var o,e,i,r=p.dup,a=t[0];try{i=JSON.parse(t)}catch(t){}if("{"===a){if(!i)return;if(r.check(o=i["#"]))return;if((a=(r.track(o,!0).it=i)["@"])&&i.put&&(a+=e=i["##"]||(i["##"]=h.hash(i)))!=o){if(r.check(a))return;(a=r.s)[e]=a[o]}return(i.mesh=function(){}).via=n,(a=i["><"])&&(i.mesh.to=d.obj.map(a.split(","),function(t,n,o){o(t,!0)})),void p.on("in",i)}if("["!==a);else{if(!i)return;for(var u,s=0;u=i[s++];)h.hear(u,n)}}},function(){function r(n,o){var t=o.wire;try{t.send?t.readyState===t.OPEN?t.send(n):(o.queue=o.queue||[]).push(n):o.say&&o.say(n)}catch(t){(o.queue=o.queue||[]).push(n)}}h.say=function(n,o){var t,e,i;o?(o.wire||p.opt.wire&&p.opt.wire(o))&&(e=n.mesh||a,o!==e.via&&((i=e.raw)||(i=h.raw(n)),(t=n["@"])&&(t=p.dup.s[t])&&(t=t.it)&&t.get&&t["##"]&&t["##"]===n["##"]||(t=e.to)&&(t[o.url]||t[o.id])||(o.batch?o.batch.push(i):(o.batch=[],setTimeout(function(){var t=o.batch;t&&(o.batch=null,t.length&&r(JSON.stringify(t),o))},p.opt.gap||p.opt.wait||1),r(i,o))))):d.obj.map(p.opt.peers,function(t){h.say(n,t)})}}(),function(){function f(t,n){var o;return n instanceof Object?(d.obj.map(Object.keys(n).sort(),e,{to:o={},on:n}),o):n}function e(t){this.to[t]=this.on[t]}h.raw=function(t){if(!t)return"";var n,o,e,i=p.dup,r=t.mesh||{};if(e=r.raw)return e;if("string"==typeof t)return t;t["@"]&&(e=t.put)&&((o=t["##"])||(n=c(e,f)||"",o=h.hash(t,n),t["##"]=o),(e=i.s)[o=t["@"]+o]=e[t["#"]],t["#"]=o||t["#"],n&&((t=d.obj.to(t)).put=l));var a=0,u=[];d.obj.map(p.opt.peers,function(t){if(u.push(t.url||t.id),9<++a)return!0}),t["><"]=u.join();var s=c(t);return g!==n&&(s=s.replace('"'+l+'"',n)),r&&(r.raw=s),s},h.hash=function(t,n){return o.hash(n||c(t.put,f)||"")||t["#"]||d.text.random(9)};var c=JSON.stringify,l=":])([:"}(),h.hi=function(n){p.on("hi",n);var t=n.queue;n.queue=[],d.obj.map(t,function(t){h.say(t,n)})},h}o.hash=function(t){if("string"!=typeof t)return{err:1};var n=0;if(!t.length)return n;for(var o=0,e=t.length;o=0||1/0===t||-(1/0)===t)}},n.text={is:function(t){return"string"==typeof t}},n.text.ify=function(t){return n.text.is(t)?t:"undefined"!=typeof JSON?JSON.stringify(t):t&&t.toString?t.toString():t},n.text.random=function(t,n){var o="";for(t=t||24,n=n||"0123456789ABCDEFGHIJKLMNOPQRSTUVWXZabcdefghijklmnopqrstuvwxyz";t>0;)o+=n.charAt(Math.floor(Math.random()*n.length)),t--;return o},n.text.match=function(t,o){function e(t,n){for(var o,e=-1,i=0;o=n[i++];)if(!~(e=t.indexOf(o,e+1)))return!1;return!0}var i=!1;if(t=t||"",o=n.text.is(o)?{"=":o}:o||{},n.obj.has(o,"~")&&(t=t.toLowerCase(),o["="]=(o["="]||o["~"]).toLowerCase()),n.obj.has(o,"="))return t===o["="];if(n.obj.has(o,"*")){if(t.slice(0,o["*"].length)!==o["*"])return!1;i=!0,t=t.slice(o["*"].length)}if(n.obj.has(o,"!")){if(t.slice(-o["!"].length)!==o["!"])return!1;i=!0}if(n.obj.has(o,"+")&&n.list.map(n.list.is(o["+"])?o["+"]:[o["+"]],function(n){return t.indexOf(n)>=0?void(i=!0):!0}))return!1;if(n.obj.has(o,"-")&&n.list.map(n.list.is(o["-"])?o["-"]:[o["-"]],function(n){return t.indexOf(n)<0?void(i=!0):!0}))return!1;if(n.obj.has(o,">")){if(!(t>o[">"]))return!1;i=!0}if(n.obj.has(o,"<")){if(!(tn?-1:n>o?1:0):0}},n.list.map=function(t,n,o){return a(t,n,o)},n.list.index=1,n.obj={is:function(t){return t?t instanceof Object&&t.constructor===Object||"Object"===Object.prototype.toString.call(t).match(/^\[object (\w+)\]$/)[1]:!1}},n.obj.put=function(t,n,o){return(t||{})[n]=o,t},n.obj.has=function(t,n){return t&&Object.prototype.hasOwnProperty.call(t,n)},n.obj.del=function(t,n){return t?(t[n]=null,delete t[n],t):void 0},n.obj.as=function(t,n,o,e){return t[n]=t[n]||(e===o?{}:o)},n.obj.ify=function(t){if(r(t))return t;try{t=JSON.parse(t)}catch(n){t={}}return t},function(){function t(t,n){u(this,n)&&o!==this[n]||(this[n]=t)}var o;n.obj.to=function(n,o){return o=o||{},a(n,t,o),o}}(),n.obj.copy=function(t){return t?JSON.parse(JSON.stringify(t)):t},function(){function t(t,n){var o=this.n;if(!o||!(n===o||r(o)&&u(o,n)))return n?!0:void 0}n.obj.empty=function(n,o){return n&&a(n,t,{n:o})?!1:!0}}(),function(){function t(n,o){return 2===arguments.length?(t.r=t.r||{},void(t.r[n]=o)):(t.r=t.r||[],void t.r.push(n))}var i=Object.keys;n.obj.map=function(a,s,f){var c,l,p,h,d,v=0,g=o(s);if(t.r=null,i&&r(a)&&(h=i(a),d=!0),e(a)||h)for(l=(h||a).length;l>v;v++){var m=v+n.list.index;if(g){if(p=d?s.call(f||this,a[h[v]],h[v],t):s.call(f||this,a[v],m,t),p!==c)return p}else if(s===a[d?h[v]:v])return h?h[v]:m}else for(v in a)if(g){if(u(a,v)&&(p=f?s.call(f,a[v],v,t):s(a[v],v,t),p!==c))return p}else if(s===a[v])return v;return g?t.r:n.list.index?0:-1}}(),n.time={},n.time.is=function(t){return t?t instanceof Date:+(new Date).getTime()};var o=n.fn.is,e=n.list.is,i=n.obj,r=i.is,u=i.has,a=i.map;t.exports=n})(t,"./type"),t(function(t){t.exports=function n(t,o,e){if(!t)return{to:n};var i,t=(this.tag||(this.tag={}))[t]||(this.tag[t]={tag:t,to:n._={next:function(t){var n;(n=this.to)&&n.next(t)}}});if(o instanceof Function){var r={off:n.off||(n.off=function(){return this.next===n._.next?!0:(this===this.the.last&&(this.the.last=this.back),this.to.back=this.back,this.next=n._.next,this.back.to=this.to,void(this.the.last===this.the&&delete this.on.tag[this.the.tag]))}),to:n._,next:o,the:t,on:this,as:e};return(r.back=t.last||t).to=r,t.last=r}return(t=t.to)&&i!==o&&t.next(o),t}})(t,"./onto"),t(function(t){function n(t,n,e,i,r){if(n>t)return{defer:!0};if(e>n)return{historical:!0};if(n>e)return{converge:!0,incoming:!0};if(n===e){if(i=o(i)||"",r=o(r)||"",i===r)return{state:!0};if(r>i)return{converge:!0,current:!0};if(i>r)return{converge:!0,incoming:!0}}return{err:"Invalid CRDT Data: "+i+" to "+r+" at "+n+" to "+e+"!"}}if("undefined"==typeof JSON)throw new Error("JSON is not included in this browser. Please load it first: ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js");var o=JSON.stringify;t.exports=n})(t,"./HAM"),t(function(n){var o=t("./type"),e={};e.is=function(t){return t===i?!1:null===t?!0:t===1/0?!1:s(t)||u(t)||a(t)?!0:e.rel.is(t)||!1},e.link=e.rel={_:"#"},function(){function t(t,n){var o=this;return o.id?o.id=!1:n==r&&s(t)?void(o.id=t):o.id=!1}e.rel.is=function(n){if(n&&n[r]&&!n._&&c(n)){var o={};if(p(n,t,o),o.id)return o.id}return!1}}(),e.rel.ify=function(t){return l({},r,t)},o.obj.has._=".";var i,r=e.link._,u=o.bi.is,a=o.num.is,s=o.text.is,f=o.obj,c=f.is,l=f.put,p=f.map;n.exports=e})(t,"./val"),t(function(n){var o=t("./type"),e=t("./val"),i={_:"_"};i.soul=function(t,n){return t&&t._&&t._[n||p]},i.soul.ify=function(t,n){return n="string"==typeof n?{soul:n}:n||{},t=t||{},t._=t._||{},t._[p]=n.soul||t._[p]||l(),t},i.soul._=e.link._,function(){function t(t,n){return n!==i._?e.is(t)?void(this.cb&&this.cb.call(this.as,t,n,this.n,this.s)):!0:void 0}i.is=function(n,o,e){var r;return a(n)&&(r=i.soul(n))?!f(n,t,{as:e,cb:o,s:r,n:n}):!1}}(),function(){function t(t,n){var o,i,r=this.o;return r.map?(o=r.map.call(this.as,t,""+n,r.node),void(i===o?s(r.node,n):r.node&&(r.node[n]=o))):void(e.is(t)&&(r.node[n]=t))}i.ify=function(n,o,e){return o?"string"==typeof o?o={soul:o}:o instanceof Function&&(o={map:o}):o={},o.map&&(o.node=o.map.call(e,n,r,o.node||{})),(o.node=i.soul.ify(o.node||{},o))&&f(n,t,{o:o,as:e}),o.node}}();var r,u=o.obj,a=u.is,s=u.del,f=u.map,c=o.text,l=c.random,p=i.soul._;n.exports=i})(t,"./node"),t(function(n){function o(){var t;return t=r(),t>u?(a=0,u=t+o.drift):u=t+(a+=1)/s+o.drift}{var e=t("./type"),i=t("./node"),r=e.time.is,u=-(1/0),a=0,s=1e3,f="undefined"!=typeof performance?performance.timing&&performance:!1;f&&f.timing&&f.timing.navigationStart||(f=!1)}o._=">",o.drift=0,o.is=function(t,n,e){var i=n&&t&&t[_]&&t[_][o._]||e;if(i)return b(i=i[n])?i:-(1/0)},o.lex=function(){return o().toString(36).replace(".","")},o.ify=function(t,n,e,r,u){if(!t||!t[_]){if(!u)return;t=i.soul.ify(t,u)}var a=p(t[_],o._);return c!==n&&n!==_&&(b(e)&&(a[n]=e),c!==r&&(t[n]=r)),t},o.to=function(t,n,e){var r=t[n];return d(r)&&(r=g(r)),o.ify(e,n,o.is(t,n),r,i.soul(t))},function(){function t(t,n){_!==n&&o.ify(this.o,n,this.s)}o.map=function(n,e,i){var r,u=d(u=n||e)?u:null;return n=y(n=n||e)?n:null,u&&!n?(e=b(e)?e:o(),u[_]=u[_]||{},v(u,t,{o:u,s:e}),u):(i=i||d(e)?e:r,e=b(e)?e:o(),function(o,u,a,s){return n?(n.call(i||this||{},o,u,a,s),void(h(a,u)&&r===a[u]||t.call({o:a,s:e},o,u))):(t.call({o:a,s:e},o,u),o)})}}();var c,l=e.obj,p=l.as,h=l.has,d=l.is,v=l.map,g=l.copy,m=e.num,b=m.is,k=e.fn,y=k.is,_=i._;n.exports=o})(t,"./state"),t(function(n){var o=t("./type"),e=t("./val"),i=t("./node"),r={};!function(){function t(t,o){return t&&o===i.soul(t)&&i.is(t,this.fn,this.as)?void(this.cb&&(n.n=t,n.as=this.as,this.cb.call(n.as,t,o,n))):!0}function n(t){t&&i.is(n.n,t,n.as)}r.is=function(n,o,e,i){return n&&s(n)&&!l(n)?!h(n,t,{cb:o,fn:e,as:i}):!1}}(),function(){function t(t,o){var r;return(r=p(t,o))?r:(o.env=t,o.soul=a,i.ify(o.obj,n,o)&&(o.rel=o.rel||e.rel.ify(i.soul(o.node)),o.obj!==t.shell&&(t.graph[e.rel.is(o.rel)]=o.node)),o)}function n(n,o,r){var a,s,p=this,h=p.env;if(i._===o&&c(n,e.rel._))return r._;if(a=l(n,o,r,p,h)){if(o||(p.node=p.node||r||{},c(n,i._)&&i.soul(n)&&(p.node._=d(n._)),p.node=i.soul.ify(p.node,e.rel.is(p.rel)),p.rel=p.rel||e.rel.ify(i.soul(p.node))),(s=h.map)&&(s.call(h.as||{},n,o,r,p),c(r,o))){if(n=r[o],u===n)return void f(r,o);if(!(a=l(n,o,r,p,h)))return}if(!o)return p.node;if(!0===a)return n;if(s=t(h,{obj:n,path:p.path.concat(o)}),s.node)return s.rel}}function a(t){var n=this,o=e.link.is(n.rel),r=n.env.graph;n.rel=n.rel||e.rel.ify(t),n.rel[e.rel._]=t,n.node&&n.node[i._]&&(n.node[i._][e.rel._]=t),c(r,o)&&(r[t]=r[o],f(r,o))}function l(t,n,i,r,u){var a;return e.is(t)?!0:s(t)?1:(a=u.invalid)?(t=a.call(u.as||{},t,n,i),l(t,n,i,r,u)):(u.err="Invalid value at '"+r.path.concat(n).join(".")+"'!",void(o.list.is(t)&&(u.err+=" Use `.set(item)` instead of an Array.")))}function p(t,n){for(var o,e=t.seen,i=e.length;i--;)if(o=e[i],n.obj===o.obj)return o;e.push(n)}r.ify=function(n,o,i){var r={path:[],obj:n};return o?"string"==typeof o?o={soul:o}:o instanceof Function&&(o.map=o):o={},o.soul&&(r.rel=e.rel.ify(o.soul)),o.shell=(i||{}).shell,o.graph=o.graph||{},o.seen=o.seen||[],o.as=o.as||i,t(o,r),o.root=r.node,o.graph}}(),r.node=function(t){var n=i.soul(t);if(n)return p({},n,t)},function(){function t(t,n){var o,u;if(i._===n){if(l(t,e.rel._))return;return void(this.obj[n]=d(t))}return(o=e.rel.is(t))?(u=this.opt.seen[o])?void(this.obj[n]=u):void(this.obj[n]=this.opt.seen[o]=r.to(this.graph,o,this.opt)):void(this.obj[n]=t)}r.to=function(n,o,e){if(n){var i={};return e=e||{seen:{}},h(n[o],t,{obj:i,graph:n,opt:e}),i}}}();var u,a=(o.fn.is,o.obj),s=a.is,f=a.del,c=a.has,l=a.empty,p=a.put,h=a.map,d=a.copy;n.exports=r})(t,"./graph"),t(function(n){t("./onto"),n.exports=function(t,n){if(this.on){if(!(t instanceof Function)){if(!t||!n)return;var o=t["#"]||t,e=(this.tag||empty)[o];if(!e)return;return e=this.on(o,n),clearTimeout(e.err),!0}var o=n&&n["#"]||Math.random().toString(36).slice(2);if(!t)return o;var i=this.on(o,t,n);return i.err=i.err||setTimeout(function(){i.next({err:"Error: No ACK received yet.",lack:!0}),i.off()},(this.opt||{}).lack||9e3),o}}})(t,"./ask"),t(function(n){function o(t){var n={s:{}};return t=t||{max:1e3,age:9e3},n.check=function(t){var o;return(o=n.s[t])?o.pass?o.pass=!1:n.track(t):!1},n.track=function(o,r){var u=n.s[o]||(n.s[o]={});return u.was=i(),r&&(u.pass=!0),n.to||(n.to=setTimeout(function(){var o=i();e.obj.map(n.s,function(i,r){t.age>o-i.was||e.obj.del(n.s,r)}),n.to=null},t.age+9)),u},n}var e=t("./type"),i=e.time.is;n.exports=o})(t,"./dup"),t(function(n){function i(t){return t instanceof i?(this._={gun:this,$:this}).$:this instanceof i?i.create(this._={gun:this,$:this,opt:t}):new i(t)}i.is=function(t){return t instanceof i||t&&t._&&t===t._.$||!1},i.version=.9,i.chain=i.prototype,i.chain.toJSON=function(){};var r=t("./type");r.obj.to(r,i),i.HAM=t("./HAM"),i.val=t("./val"),i.node=t("./node"),i.state=t("./state"),i.graph=t("./graph"),i.on=t("./onto"),i.ask=t("./ask"),i.dup=t("./dup"),function(){function t(n){var o,e,r=this,a=r.as,s=a.at||a,f=s.$;return(e=n["#"])||(e=n["#"]=c(9)),(o=s.dup).check(e)?void(a.out===n.out&&(n.out=u,r.to.next(n))):(o.track(e),s.ask(n["@"],n)||(n.get&&i.on.get(n,f),n.put&&i.on.put(n,f)),r.to.next(n),void(a.out||(n.out=t,s.on("out",n))))}i.create=function(n){n.root=n.root||n,n.graph=n.graph||{},n.on=n.on||i.on,n.ask=n.ask||i.ask,n.dup=n.dup||i.dup();var o=n.$.opt(n.opt);return n.once||(n.on("in",t,n),n.on("out",t,{at:n,out:t}),i.on("create",n),n.on("create",n)),n.once=1,o}}(),function(){function t(t,n,o,e){var r=this,u=i.state.is(o,n);if(!u)return r.err="Error: No state on '"+n+"' in node '"+e+"'!";var a=r.graph[e]||k,s=i.state.is(a,n,!0),f=a[n],c=i.HAM(r.machine,u,s,t,f);return c.incoming?(r.put[e]=i.state.to(o,n,r.put[e]),(r.diff||(r.diff={}))[e]=i.state.to(o,n,r.diff[e]),void(r.souls[e]=!0)):void(c.defer&&(r.defer=u<(r.defer||1/0)?u:r.defer))}function n(t,n){var i=this,u=i.$._,a=(u.next||k)[n];if(!a){if(!(u.opt||k)["super"])return void(i.souls[n]=!1);a=i.$.get(n)._}var s=i.map[n]={put:t,get:n,$:a.$},f={ctx:i,msg:s};i.async=!!u.tag.node,i.ack&&(s["@"]=i.ack),v(t,o,f),i.async&&(i.and||u.on("node",function(t){this.to.next(t),t===i.map[t.get]&&(i.souls[t.get]=!1,v(t.put,e,t),v(i.souls,function(t){return t?t:void 0})||i.c||(i.c=1,this.off(),v(i.map,r,i)))}),i.and=!0,u.on("node",s))}function o(t,n){var o=this.ctx,e=o.graph,r=this.msg,u=r.get,a=r.put,s=r.$._;e[u]=i.state.to(a,n,e[u]),o.async||(s.put=i.state.to(a,n,s.put))}function e(t,n){var o=this,e=o.put,r=o.$._;r.put=i.state.to(e,n,r.put)}function r(t){t.$&&(this.cat.stop=this.stop,t.$._.on("in",t),this.cat.stop=null)}i.on.put=function(o,e){var a=e._,s={$:e,graph:a.graph,put:{},map:{},souls:{},machine:i.state(),ack:o["@"],cat:a,stop:{}};return i.graph.is(o.put,null,t,s)||(s.err="Error: Invalid graph!"),s.err?a.on("in",{"@":o["#"],err:i.log(s.err)}):(v(s.put,n,s),s.async||v(s.map,r,s),u!==s.defer&&setTimeout(function(){i.on.put(o,e)},s.defer-s.machine),void(s.diff&&a.on("put",d(o,{put:s.diff}))))},i.on.get=function(t,n){var o,e=n._,r=t.get,u=r[m],a=e.graph[u],s=r[b],f=e.next||(e.next={}),c=f[u];if(r["*"]){var l={};i.obj.map(e.graph,function(t,n){i.text.match(n,r)&&(l[n]=i.obj.copy(t))}),i.obj.empty(l)||e.on("in",{"@":t["#"],how:"*",put:l,$:n})}if(!a||!c)return e.on("get",t);if(s){if(!h(a,s))return e.on("get",t);a=i.state.to(a,s)}else a=i.obj.copy(a);a=i.graph.node(a),o=c.ack,e.on("in",{"@":t["#"],how:"mem",put:a,$:n}),e.on("get",t)}}(),function(){i.chain.opt=function(t){t=t||{};var n=this,o=n._,e=t.peers||t;return p(t)||(t={}),p(o.opt)||(o.opt=t),f(e)&&(e=[e]),a(e)&&(e=v(e,function(t,n,o){o(t,{url:t})}),p(o.opt.peers)||(o.opt.peers={}),o.opt.peers=d(e,o.opt.peers)),o.opt.peers=o.opt.peers||{},d(t,o.opt),i.on("opt",o),o.opt.uuid=o.opt.uuid||function(){return g()+c(12)},n}}();var u,a=i.list.is,s=i.text,f=s.is,c=s.random,l=i.obj,p=l.is,h=l.has,d=l.to,v=l.map,g=(l.copy,i.state.lex),m=i.val.rel._,b=".",k=(i.node._,i.val.link.is,{});o.debug=function(t,n){return o.debug.i&&t===o.debug.i&&o.debug.i++&&(o.log.apply(o,arguments)||n)},i.log=function(){return!i.log.off&&o.log.apply(o,arguments),[].slice.call(arguments).join(" ")},i.log.once=function(t,n,o){return(o=i.log.once)[t]=o[t]||0,o[t]++||i.log(n)},i.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'!"),"undefined"!=typeof window&&((window.Gun=i).window=window);try{"undefined"!=typeof e&&(e.exports=i)}catch(y){}n.exports=i})(t,"./root"),t(function(){var n=t("./root");n.chain.back=function(t,i){var r;if(t=t||1,-1===t||1/0===t)return this._.root.$;if(1===t)return(this._.back||this._).$;var u=this,a=u._;if("string"==typeof t&&(t=t.split(".")),!(t instanceof Array)){if(t instanceof Function){for(var s,r={back:a};(r=r.back)&&o===(s=t(r,i)););return s}return n.num.is(t)?(a.back||a).$.back(t-1):this}var f=0,c=t.length,r=a;for(f;c>f;f++)r=(r||e)[t[f]];return o!==r?i?u:r:(r=a.back)?r.$.back(t,i):void 0};var o,e={}})(t,"./back"),t(function(){function n(t){var n,o,e,i=this.as,r=i.back,u=i.root;if(t.I||(t.I=i.$),t.$||(t.$=i.$),this.to.next(t),o=t.get){if(o["#"]||i.soul){if(o["#"]=o["#"]||i.soul,t["#"]||(t["#"]=b(9)),r=u.$.get(o["#"])._,o=o["."]){if(d(r.put,o)&&(n=r.$.get(o)._,(e=n.ack)||(n.ack=-1),r.on("in",{$:r.$,put:c.state.to(r.put,o),get:r.get}),e))return}else{if(e=r.ack,e||(r.ack=-1),d(r,"put")&&r.on("in",r),e)return;t.$=r.$}return u.ask(f,t),u.on("in",t)}if(u.now&&(u.now[i.id]=u.now[i.id]||!0,i.pass={}),o["."])return i.get?(t={get:{".":i.get},$:i.$},r.ask||(r.ask={}),r.ask[i.get]=t.$._,r.on("out",t)):(t={get:{},$:i.$},r.on("out",t));if(i.ack=i.ack||-1,i.get)return t.$=i.$,o["."]=i.get,(r.ask||(r.ask={}))[i.get]=t.$._,r.on("out",t)}return r.on("out",t)}function o(t){var n,o,r=this,s=r.as,f=s.root,h=t.$,b=(h||p)._||p,k=t.put;if(s.get&&t.get!==s.get&&(t=g(t,{get:s.get})),s.has&&b!==s&&(t=g(t,{$:s.$}),b.ack&&(s.ack=b.ack)),l===k){if(o=b.put,r.to.next(t),s.soul)return;if(l===o&&l!==b.put)return;return i(s,t,r),s.has&&a(s,t),v(b.echo,s.id),void v(s.map,b.id)}if(s.soul)return r.to.next(t),i(s,t,r),void(s.next&&m(k,u,{msg:t,cat:s}));if(!(n=c.val.link.is(k)))return c.val.is(k)?(s.has||s.soul?a(s,t):(b.has||b.soul)&&((b.echo||(b.echo={}))[s.id]=b.echo[b.id]||s,(s.map||(s.map={}))[b.id]=s.map[b.id]||{at:b}),r.to.next(t),void i(s,t,r)):(s.has&&b!==s&&d(b,"put")&&(s.put=b.put),(n=c.node.soul(k))&&b.has&&(b.put=s.root.$.get(n)._.put),o=(f.stop||{})[b.id],r.to.next(t),e(s,t,b,n),i(s,t,r),void(s.next&&m(k,u,{msg:t,cat:s})));f.stop;o=f.stop||{},o=o[b.id]||(o[b.id]={}),o.is=o.is||b.put,o[s.id]=b.put||!0,r.to.next(t),e(s,t,b,n),i(s,t,r)}function e(t,n,o,i){if(i&&k!==t.get){var r=t.root.$.get(i)._;t.has?o=r:o.has&&e(o,n,o,i),o!==t&&(o.$||(o={}),(o.echo||(o.echo={}))[t.id]=o.echo[t.id]||t,t.has&&!(t.map||p)[o.id]&&a(t,n),r=o.id?(t.map||(t.map={}))[o.id]=t.map[o.id]||{at:o}:{},(i!==r.link||r.pass||t.pass)&&(t.pass&&(c.obj.map(t.map,function(t){t.pass=!0}),v(t,"pass")),r.pass&&v(r,"pass"),t.has&&(t.link=i),s(t,r.link=i)))}}function i(t,n){t.echo&&m(t.echo,r,n)}function r(t){t&&t.on&&t.on("in",this)}function u(t,n){var o,e,i,r=this.cat,u=r.next||p,a=this.msg;(k!==n||u[n])&&(e=u[n])&&(e.has?(l!==e.put&&c.val.link.is(t)||(e.put=t),o=e.$):(i=a.$)&&(i=(o=a.$.get(n))._,l!==i.put&&c.val.link.is(t)||(i.put=t)),e.on("in",{put:t,get:n,$:o,via:a}))}function a(t,n){if(t.has||t.soul){{var o=t.map;t.root}t.map=null,t.has&&(t.link=null),(t.pass||n["@"]||null!==o)&&(l===o&&c.val.link.is(t.put)||(m(o,function(n){(n=n.at)&&v(n.echo,t.id)}),o=t.put,m(t.next,function(n,e){return l===o&&l!==t.put?!0:(n.put=l,n.ack&&(n.ack=-1),void n.on("in",{get:e,$:n.$,put:l}))})))}}function s(t,n){var o=t.root.$.get(n)._;(!t.ack||(o.on("out",{get:{"#":n}}),t.ask))&&(o=t.ask,c.obj.del(t,"ask"),m(o||t.next,function(t,o){t.on("out",{get:{"#":n,".":o}})}),c.obj.del(t,"ask"))}function f(t){var n=this.as,o=n.get||p,e=n.$._,i=(t.put||p)[o["#"]];if(e.ack&&(e.ack=e.ack+1||1),!t.put||o["."]&&!d(i,e.get)){if(e.put!==l)return;return void e.on("in",{get:e.get,put:e.put=l,$:e.$,"@":t["@"]})}return k==o["."]?void e.on("in",{get:e.get,put:c.val.link.ify(o["#"]),$:e.$,"@":t["@"]}):(t.$=e.root.$,void c.on.put(t,e.root.$))}var c=t("./root");c.chain.chain=function(t){var e,i=this,r=i._,u=new(t||i).constructor(i),a=u._;return a.root=e=r.root,a.id=++e.once,a.back=i._,a.on=c.on,a.on("in",o,a),a.on("out",n,a),u};var l,p={},h=c.obj,d=h.has,v=(h.put,h.del),g=h.to,m=h.map,b=c.text.random,k=(c.val.rel._,c.node._)})(t,"./chain"),t(function(){function n(t,n){var o=n._,e=o.next,i=n.chain(),r=i._;return e||(e=o.next={}),e[r.get=t]=r,n===o.root.$?r.soul=t:(o.soul||o.has)&&(r.has=t),r}function o(t,n,o,e){var i,r=t._;return(i=r.soul)?(n(i,e,r),t):(i=r.link)?(n(i,e,r),t):(t.get(function(t,o){o.rid(t);var r=(r=t.$)&&r._||{};i=r.link||r.soul||c.is(t.put)||l(t.put),n(i,e,t,o)},{out:{get:{".":!0}}}),t)}function e(t){var n,o=this,e=o.as,i=e.at,r=i.root,a=t.$,f=(a||{})._||{},l=t.put||f.put;if((n=r.now)&&o!==n[e.now])return o.to.next(t);if(o.seen&&f.id&&o.seen[f.id])return o.to.next(t);if((n=l)&&n[c._]&&(n=c.is(n))&&(n=(t.$$=f.root.gun.get(n))._,u!==n.put&&(t=s(t,{put:l=n.put}))),(n=r.mum)&&f.id){if(n[f.id])return;u===l||c.is(l)||(n[f.id]=!0)}return e.use(t,o),o.stun?void(o.stun=null):void o.to.next(t)}function i(t){var n=this.on;if(!t||n.soul||n.has)return this.off();if(t=(t=(t=t.$||t)._||t).id){{var o,e;n.map}return(o=(e=this.seen||(this.seen={}))[t])?!0:void(e[t]=!0)}}var r=t("./root");r.chain.get=function(t,u,a){var s,l;if("string"!=typeof t){if(t instanceof Function){if(!0===u)return o(this,t,u,a);s=this;var h,d=s._,v=d.root,l=v.now;a=u||{},a.at=d,a.use=t,a.out=a.out||{},a.out.get=a.out.get||{},(h=d.on("in",e,a)).rid=i,(v.now={$:1})[a.now=d.id]=h;var g=v.mum;return v.mum={},d.on("out",a.out),v.mum=g,v.now=l,s}return f(t)?this.get(""+t,u,a):(l=c.is(t))?this.get(l,u,a):((a=this.chain())._.err={err:r.log("Invalid get request!",t)},u&&u.call(a,a._.err),a)}var m=this,b=m._,k=b.next||p;return(s=k[t])||(s=n(t,m)),s=s.$,(l=b.stun)&&(s._.stun=s._.stun||l),u&&u instanceof Function&&s.get(u,a),s};var u,a=r.obj,s=(a.has,r.obj.to),f=r.num.is,c=r.val.link,l=r.node.soul,p=(r.node._,{})})(t,"./get"),t(function(){function n(t){t.batch=i;var n=t.opt||{},o=t.env=c.state.map(u,n.state);return o.soul=t.soul,t.graph=c.graph.ify(t.data,o,t),o.err?((t.ack||m).call(t,t.out={err:c.log(o.err)}),void(t.res&&t.res())):void t.batch()}function e(t){return void(t&&t())}function i(){var t=this;t.graph&&!v(t.stun,r)&&(t.res=t.res||function(t){t&&t()},t.res(function(){var n=t.$.back(-1)._,o=n.ask(function(o){n.root.on("ack",o),o.err&&c.log(o),o.lack||this.off(),t.ack&&t.ack(o,this)},t.opt),e=n.root.now;p.del(n.root,"now");var i=n.root.mum;n.root.mum={},t.ref._.on("out",{$:t.ref,put:t.out=t.env.graph,opt:t.opt,"#":o}),n.root.mum=i?p.to(i,n.root.mum):i,n.root.now=e},t),t.res&&t.res())}function r(t){return t?!0:void 0}function u(t,n,o,e){var i=this,r=c.is(t);!n&&e.path.length&&(i.res||b)(function(){var n=e.path,o=i.ref,u=(i.opt,0),s=n.length;for(u;s>u;u++)o=o.get(n[u]);r&&(o=t);var f=o._.dub;return f||(f=c.node.soul(e.obj))?(o.back(-1).get(f),void e.soul(f)):((i.stun=i.stun||{})[n]=!0,void o.get(a,!0,{as:{at:e,as:i,p:n}}))},{as:i,at:e})}function a(t,n,o,e){var n=n.as,i=n.at;n=n.as;var r=((o||{}).$||{})._||{};return t=r.dub=r.dub||t||c.node.soul(i.obj)||c.node.soul(o.put||r.put)||c.val.rel.is(o.put||r.put)||(n.via.back("opt.uuid")||c.text.random)(),e&&(e.stun=!0),t?void s(r,r.dub=t,i,n):void r.via.back("opt.uuid")(function(t,o){return t?c.log(t):void s(r,r.dub=r.dub||o,i,n)})}function s(t,n,o,e){t.$.back(-1).get(n),o.soul(n),e.stun[o.path]=!1,e.batch()}function f(t,n,e,i){if(n=n.as,e.$&&e.$._){if(e.err)return void o.log("Please report this as an issue! Put.any.err");var r,u=e.$._,a=u.put,s=n.opt||{};if(!(r=n.ref)||!r._.now){if(i&&(i.stun=!0),n.ref!==n.$){if(r=n.$._.get||u.get,!r)return void o.log("Please report this as an issue! Put.no.get");n.data=d({},r,n.data),r=null}if(l===a){if(!u.get)return;t||(r=u.$.back(function(t){return t.link||t.soul?t.link||t.soul:void(n.data=d({},t.get,n.data))})),r=r||u.get,u=u.root.$.get(r)._,n.soul=r,a=n.data}return n.not||(n.soul=n.soul||t)||(n.path&&h(n.data)?n.soul=(s.uuid||n.via.back("opt.uuid")||c.text.random)():(k==u.get&&(n.soul=(u.put||g)["#"]||u.dub),n.soul=n.soul||u.soul||u.soul||(s.uuid||n.via.back("opt.uuid")||c.text.random)()),n.soul)?void n.ref.put(n.data,n.soul,n):void n.via.back("opt.uuid")(function(t,o){return t?c.log(t):void n.ref.put(n.data,n.soul=o,n)})}}}var c=t("./root");c.chain.put=function(t,o,i){var r,u=this,a=u._,s=a.root.$;return i=i||{},i.data=t,i.via=i.$=i.via||i.$||u,"string"==typeof o?i.soul=o:i.ack=i.ack||o,a.soul&&(i.soul=a.soul),i.soul||s===u?h(i.data)?(i.soul=i.soul||(i.not=c.node.soul(i.data)||(i.via.back("opt.uuid")||c.text.random)()),i.soul?(i.$=u=s.get(i.soul),i.ref=i.$,n(i),u):(i.via.back("opt.uuid")(function(t,n){return t?c.log(t):void(i.ref||i.$).put(i.data,i.soul=n,i)}),u)):((i.ack||m).call(i,i.out={err:c.log("Data saved to the root level of the graph must be a node (an object), not a",typeof i.data,'of "'+i.data+'"!')}),i.res&&i.res(),u):c.is(t)?(t.get(function(t,n,e){return!t&&c.val.is(e.put)?c.log("The reference you are saving is a",typeof e.put,'"'+e.put+'", not a node (object)!'):void u.put(c.val.rel.ify(t),o,i)},!0),u):(i.ref=i.ref||s._===(r=a.back)?u:r.$,i.ref._.soul&&c.val.is(i.data)&&a.get?(i.data=d({},a.get,i.data),i.ref.put(i.data,i.soul,i),u):(i.ref.get(f,!0,{as:i}),i.out||(i.res=i.res||e,i.$._.stun=i.ref._.stun),u))};var l,p=c.obj,h=p.is,d=p.put,v=p.map,g={},m=function(){},b=function(t,n){t.call(n||g)},k=c.node._})(t,"./put"),t(function(n){var o=t("./root");t("./chain"),t("./back"),t("./put"),t("./get"),n.exports=o})(t,"./index"),t(function(){function n(t,n){{var o,e=this,r=t.$,u=(r||{})._||{},a=u.put||t.put;e.at}if(i!==a){if(o=t.$$){if(o=t.$$._,i===o.put)return;a=o.put}e.change&&(a=t.put),e.as?e.ok.call(e.as,t,n):e.ok.call(r,a,t.get,t,n)}}function o(t,n,e){var r,u,a=this.as,s=(a.at,t.$),f=s._,c=f.put||t.put;if(u=t.$$){if(r=u=t.$$._,i===u.put)return;c=u.put}return(u=n.wait)&&(u=u[f.id])&&clearTimeout(u),e||i!==c&&!f.soul&&!f.link&&(!r||0 .once, apologies unexpected."),this.once(t,n)},e.chain.once=function(t,n){var r=this,u=r._,a=u.put;if(0=(n.batch||1e3)?s():void(e||(e=setTimeout(s,n.wait||1)))}),t.on("get",function(o){function e(){if(s&&(i=s["#"])){var e=s["."];r=u[i]||a,r&&e&&(r=Gun.state.to(r,e)),(r||Gun.obj.empty(n.peers))&&t.on("in",{"@":o["#"],put:Gun.graph.node(r),how:"lS",lS:o.I})}}this.to.next(o);var i,r,a,s=o.get;Gun.debug?setTimeout(e,1):e()});var a=function(t,n,o,e){u[e]=Gun.state.to(o,n,u[e])},s=function(a){var f;r=0,clearTimeout(e),e=!1;var c=i;i={},a&&(u=a);try{o.setItem(n.prefix,JSON.stringify(u))}catch(l){Gun.log(f=l||"localStorage failure"),t.on("localStorage:error",{err:f,file:n.prefix,flush:u,retry:s})}(f||Gun.obj.empty(n.peers))&&Gun.obj.map(c,function(n,o){t.on("in",{"@":o,err:f,ok:0})})}}})}})(t,"./adapters/localStorage"),t(function(n){function o(t){var n=function(){};return n.out=function(o){var e;return this.to&&this.to.next(o),(e=o["@"])&&(e=t.dup.s[e])&&(e=e.it)&&e.mesh?(n.say(o,e.mesh.via),void(e["##"]=o["##"])):void n.say(o)},n.hear=function(o,i){if(o){var r,u,a,s=t.dup,f=o[0];try{a=JSON.parse(o)}catch(c){}if("{"===f){if(!a)return;if(s.check(r=a["#"]))return;if(s.track(r,!0).it=a,(f=a["@"])&&a.put&&(u=a["##"]||(a["##"]=n.hash(a)),(f+=u)!=r)){if(s.check(f))return;(f=s.s)[u]=f[r]}return(a.mesh=function(){}).via=i,(f=a["><"])&&(a.mesh.to=e.obj.map(f.split(","),function(t,n,o){o(t,!0)})),void t.on("in",a)}if("["!==f);else{if(!a)return;for(var l,p=0;l=a[p++];)n.hear(l,i)}}},function(){function o(t,n){var o=n.wire;try{o.send?o.send(t):n.say&&n.say(t)}catch(e){(n.queue=n.queue||[]).push(t)}}n.say=function(i,u){if(!u)return void e.obj.map(t.opt.peers,function(t){n.say(i,t)});var a,s,f,c=u.wire||t.opt.wire&&t.opt.wire(u);if(c&&(s=i.mesh||r,u!==s.via&&((f=s.raw)||(f=n.raw(i)),!((a=i["@"])&&(a=t.dup.s[a])&&(a=a.it)&&a.get&&a["##"]&&a["##"]===i["##"]||(a=s.to)&&(a[u.url]||a[u.id]))))){if(u.batch)return void u.batch.push(f);u.batch=[],setTimeout(function(){var t=u.batch;t&&(u.batch=null,t.length&&o(JSON.stringify(t),u))},t.opt.gap||t.opt.wait||1),o(f,u)}}}(),function(){function r(t,n){var o;return n instanceof Object?(e.obj.map(Object.keys(n).sort(),u,{to:o={},on:n}),o):n}function u(t){this.to[t]=this.on[t]}n.raw=function(o){if(!o)return"";var u,f,c,l=t.dup,p=o.mesh||{};if(c=p.raw)return c;if("string"==typeof o)return o;o["@"]&&(c=o.put)&&((f=o["##"])||(u=a(c,r)||"",f=n.hash(o,u),o["##"]=f),(c=l.s)[f=o["@"]+f]=c[o["#"]],o["#"]=f||o["#"],u&&((o=e.obj.to(o)).put=s));var h=0,d=[];e.obj.map(t.opt.peers,function(t){return d.push(t.url||t.id),++h>9?!0:void 0}),o["><"]=d.join();var v=a(o);return i!==u&&(v=v.replace('"'+s+'"',u)),p&&(p.raw=v),v},n.hash=function(t,n){return o.hash(n||a(t.put,r)||"")||t["#"]||e.text.random(9)};var a=JSON.stringify,s=":])([:"}(),n.hi=function(o){t.on("hi",o);var i=o.queue;o.queue=[],e.obj.map(i,function(t){n.say(t,o)})},n}var e=t("../type");o.hash=function(t){if("string"!=typeof t)return{err:1};var n=0;if(!t.length)return n;for(var o,e=0,i=t.length;i>e;++e)o=t.charCodeAt(e),n=(n<<5)-n+o,n|=0;return n};var i,r={};Object.keys=Object.keys||function(t){return map(t,function(t,n,o){o(n)})};try{n.exports=o}catch(u){}})(t,"./adapters/mesh"),t(function(){var n=t("../index");n.Mesh=t("./mesh"),n.on("opt",function(t){function o(n){if(n&&n.url){var o=n.url.replace("http","ws"),u=n.wire=new i.WebSocket(o);return u.onclose=function(){t.on("bye",n),e(n)},u.onerror=function(t){e(n),t&&"ECONNREFUSED"===t.code},u.onopen=function(){a.hi(n)},u.onmessage=function(t){t&&(r.inLength=(r.inLength||0)+(t.data||t).length,a.hear(t.data||t,n))},u}}function e(t){clearTimeout(t.defer),t.defer=setTimeout(function(){o(t)},2e3)}this.to.next(t);var i=t.opt;if(!t.once&&!1!==i.WebSocket){var r;"undefined"!=typeof window&&(r=window),"undefined"!=typeof global&&(r=global),r=r||{};var u=i.WebSocket||r.WebSocket||r.webkitWebSocket||r.mozWebSocket;if(u){i.WebSocket=u;var a=i.mesh=i.mesh||n.Mesh(t);t.on("create",function(n){this.to.next(n),t.on("out",a.out)}),i.wire=i.wire||o}}})})(t,"./adapters/websocket")}(); \ No newline at end of file diff --git a/sea/authenticate.js b/sea/authenticate.js index 06c2eb26..a4ce10ff 100644 --- a/sea/authenticate.js +++ b/sea/authenticate.js @@ -15,7 +15,7 @@ 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 [ user ] = await Promise.all(aliases.map(async ({ at: at, pub: pub }) => { + const users = await Promise.all(aliases.map(async ({ at: at, pub: pub }, i) => { // attempt to PBKDF2 extend the password with the salt. (Verifying the signature gives us the plain text salt.) const auth = parseProps(at.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. @@ -30,7 +30,7 @@ const salt = auth.salt const sea = await SEA.decrypt(auth.ek, proof) if (!sea) { - err = 'Failed to decrypt secret!' + err = 'Failed to decrypt secret! ' + i +'/'+aliases.length; return } // now we have AES decrypted the private key, from when we encrypted it with the proof at registration. @@ -53,7 +53,7 @@ throw { err } } })) - + var user = Gun.list.map(users, function(acc){ if(acc){ return acc } }) if (!user) { throw { err: err || 'Public key does not exist!' } } diff --git a/sea/create.js b/sea/create.js index ac14b49d..a3b6f64e 100644 --- a/sea/create.js +++ b/sea/create.js @@ -14,7 +14,7 @@ var u; // Well first we have to actually create a user. That is what this function does. - User.prototype.create = function(username, pass, cb){ + User.prototype.create = function(username, pass, cb, opt){ // TODO: Needs to be cleaned up!!! const gunRoot = this.back(-1) var gun = this, cat = (gun._); @@ -24,12 +24,13 @@ 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) { + if (at.put && !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) @@ -165,6 +166,17 @@ return user._.sea; } User.prototype.leave = async function(){ + 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; + } return await authLeave(this.back(-1)) } // If authenticated user wants to delete his/her account, let's support it! diff --git a/sea/pair.js b/sea/pair.js index 793083eb..2946c9bd 100644 --- a/sea/pair.js +++ b/sea/pair.js @@ -51,7 +51,8 @@ const r = { pub: sa.pub, priv: sa.priv, /* pubId, */ epub: dh.epub, epriv: dh.epriv } if(cb){ try{ cb(r) }catch(e){console.log(e)} } return r; - } catch(e) { + } catch(e) { + console.log(e); SEA.err = e; if(cb){ cb() } return; diff --git a/sea/root.js b/sea/root.js index 23cd2bcc..00fbf22b 100644 --- a/sea/root.js +++ b/sea/root.js @@ -4,7 +4,7 @@ // THIS IS AN EARLY ALPHA! function SEA(){} - if(typeof window !== "undefined"){ SEA.window = window } + if(typeof window !== "undefined"){ (SEA.window = window).SEA = SEA } module.exports = SEA; \ No newline at end of file diff --git a/sea/sha256.js b/sea/sha256.js index 43f97dc9..a3ff728a 100644 --- a/sea/sha256.js +++ b/sea/sha256.js @@ -1,14 +1,12 @@ - const { - subtle, ossl = subtle, random: getRandomBytes, TextEncoder, TextDecoder - } = require('./shim') + const shim = require('./shim'); const Buffer = require('./buffer') const parse = require('./parse') const { pbkdf2 } = require('./settings') // This internal func returns SHA-256 hashed data for signing const sha256hash = async (mm) => { const m = parse(mm) - const hash = await ossl.digest({name: pbkdf2.hash}, new TextEncoder().encode(m)) + const hash = await shim.subtle.digest({name: pbkdf2.hash}, new shim.TextEncoder().encode(m)) return Buffer.from(hash) } module.exports = sha256hash diff --git a/sea/shim.js b/sea/shim.js index 83e52389..379d042f 100644 --- a/sea/shim.js +++ b/sea/shim.js @@ -28,7 +28,7 @@ }); try{ const WebCrypto = require('node-webcrypto-ossl') - api.ossl = new WebCrypto({directory: 'key_storage'}).subtle // ECDH + api.ossl = new WebCrypto({directory: 'ossl'}).subtle // ECDH }catch(e){ console.log("node-webcrypto-ossl is optionally needed for ECDH, please install if needed."); } diff --git a/sea/sign.js b/sea/sign.js index fe4ed994..fdb8dde0 100644 --- a/sea/sign.js +++ b/sea/sign.js @@ -25,7 +25,8 @@ if(cb){ try{ cb(r) }catch(e){console.log(e)} } return r; - } catch(e) { + } catch(e) { + console.log(e); SEA.err = e; if(cb){ cb() } return; diff --git a/sea/user.js b/sea/user.js index 05a91f1c..7c0fadcd 100644 --- a/sea/user.js +++ b/sea/user.js @@ -3,9 +3,8 @@ var Gun = SEA.Gun; var then = require('./then'); - function User(){ - this._ = {$: this} - Gun.call() + function User(root){ + this._ = {$: this}; } User.prototype = (function(){ function F(){}; F.prototype = Gun.chain; return new F() }()) // Object.create polyfill User.prototype.constructor = User; diff --git a/sea/verify.js b/sea/verify.js index 1ce4347a..adfa29e4 100644 --- a/sea/verify.js +++ b/sea/verify.js @@ -26,7 +26,8 @@ if(cb){ try{ cb(r) }catch(e){console.log(e)} } return r; - } catch(e) { + } catch(e) { + console.log(e); SEA.err = e; if(cb){ cb() } return; diff --git a/src/adapters/localStorage.js b/src/adapters/localStorage.js index c3672909..d81e71e4 100644 --- a/src/adapters/localStorage.js +++ b/src/adapters/localStorage.js @@ -15,24 +15,21 @@ Gun.on('create', function(root){ // See the next 'opt' code below for actual saving of data. var ev = this.to, opt = root.opt; if(root.once){ return ev.next(root) } - if(false === opt.localStorage){ return ev.next(root) } - opt.file = opt.file || 'gun/'; - var gap = Gun.obj.ify(store.getItem('gap/'+opt.file)) || {}; + //if(false === opt.localStorage){ return ev.next(root) } // we want offline resynce queue regardless! + opt.prefix = opt.file || 'gun/'; + var gap = Gun.obj.ify(store.getItem('gap/'+opt.prefix)) || {}; var empty = Gun.obj.empty, id, to, go; // add re-sync command. if(!empty(gap)){ - root.on('localStorage', function(disk){ - this.off(); - var send = {} - Gun.obj.map(gap, function(node, soul){ - Gun.obj.map(node, function(val, key){ - send[soul] = Gun.state.to(disk[soul], key, send[soul]); - }); + var disk = Gun.obj.ify(store.getItem(opt.prefix)) || {}, send = {}; + Gun.obj.map(gap, function(node, soul){ + Gun.obj.map(node, function(val, key){ + send[soul] = Gun.state.to(disk[soul], key, send[soul]); }); - setTimeout(function(){ - root.on('out', {put: send, '#': root.ask(ack), I: root.$}); - },10); }); + setTimeout(function(){ + root.on('out', {put: send, '#': root.ask(ack), I: root.$}); + },10); } root.on('out', function(msg){ @@ -70,7 +67,7 @@ Gun.on('create', function(root){ var flush = function(){ clearTimeout(to); to = false; - try{store.setItem('gap/'+opt.file, JSON.stringify(gap)); + try{store.setItem('gap/'+opt.prefix, JSON.stringify(gap)); }catch(e){ Gun.log(err = e || "localStorage failure") } } }); @@ -80,9 +77,9 @@ Gun.on('create', function(root){ var opt = root.opt; if(root.once){ return } if(false === opt.localStorage){ return } - opt.file = opt.file || opt.prefix || 'gun/'; // support old option name. + opt.prefix = opt.file || 'gun/'; var graph = root.graph, acks = {}, count = 0, to; - var disk = Gun.obj.ify(store.getItem(opt.file)) || {}; + var disk = Gun.obj.ify(store.getItem(opt.prefix)) || {}; var lS = function(){}, u; root.on('localStorage', disk); // NON-STANDARD EVENT! @@ -130,10 +127,10 @@ Gun.on('create', function(root){ var ack = acks; acks = {}; if(data){ disk = data } - try{store.setItem(opt.file, JSON.stringify(disk)); + try{store.setItem(opt.prefix, JSON.stringify(disk)); }catch(e){ Gun.log(err = e || "localStorage failure"); - root.on('localStorage:error', {err: err, file: opt.file, flush: disk, retry: flush}); + root.on('localStorage:error', {err: err, file: opt.prefix, flush: disk, retry: flush}); } if(!err && !Gun.obj.empty(opt.peers)){ return } // only ack if there are no peers. Gun.obj.map(ack, function(yes, id){ diff --git a/src/adapters/mesh.js b/src/adapters/mesh.js index 8c5d4fb8..96149ccb 100644 --- a/src/adapters/mesh.js +++ b/src/adapters/mesh.js @@ -99,11 +99,7 @@ function Mesh(ctx){ var wire = peer.wire; try{ if(wire.send){ - if(wire.readyState === wire.OPEN){ wire.send(raw); - } else { - (peer.queue = peer.queue || []).push(raw); - } } else if(peer.say){ peer.say(raw); diff --git a/src/chain.js b/src/chain.js index f2c5d8b0..c2a1d20d 100644 --- a/src/chain.js +++ b/src/chain.js @@ -24,7 +24,6 @@ function output(msg){ at.on('in', at); return; }*/ - //console.log("out!", at.get, get); if(get['#'] || at.soul){ get['#'] = get['#'] || at.soul; msg['#'] || (msg['#'] = text_rand(9)); @@ -164,13 +163,12 @@ function relate(at, msg, from, rel){ not(at, msg); } tmp = from.id? ((at.map || (at.map = {}))[from.id] = at.map[from.id] || {at: from}) : {}; - //console.log("REL?", at.id, at.get, rel === tmp.link, tmp.pass || at.pass); if(rel === tmp.link){ if(!(tmp.pass || at.pass)){ return; } } - if(at.pass){ + if(at.pass){ Gun.obj.map(at.map, function(tmp){ tmp.pass = true }) obj_del(at, 'pass'); } diff --git a/src/graph.js b/src/graph.js index 49b305ed..a7f6f682 100644 --- a/src/graph.js +++ b/src/graph.js @@ -33,6 +33,7 @@ var Graph = {}; if(env.soul){ at.rel = Val.rel.ify(env.soul); } + env.shell = (as||{}).shell; env.graph = env.graph || {}; env.seen = env.seen || []; env.as = env.as || as; @@ -45,8 +46,10 @@ var Graph = {}; at.env = env; at.soul = soul; if(Node.ify(at.obj, map, at)){ - //at.rel = at.rel || Val.rel.ify(Node.soul(at.node)); - env.graph[Val.rel.is(at.rel)] = at.node; + at.rel = at.rel || Val.rel.ify(Node.soul(at.node)); + if(at.obj !== env.shell){ + env.graph[Val.rel.is(at.rel)] = at.node; + } } return at; } diff --git a/src/map.js b/src/map.js index 04cc4518..b3072500 100644 --- a/src/map.js +++ b/src/map.js @@ -14,10 +14,8 @@ Gun.chain.map = function(cb, opt, t){ gun.map().on(function(data, key, at, ev){ var next = (cb||noop).call(this, data, key, at, ev); if(u === next){ return } - if(data === next || Gun.is(next)){ - chain._.on('in', next._); - return; - } + if(data === next){ return chain._.on('in', at) } + if(Gun.is(next)){ return chain._.on('in', next._) } chain._.on('in', {get: key, put: next}); }); return chain; diff --git a/src/root.js b/src/root.js index e362dd34..24022317 100644 --- a/src/root.js +++ b/src/root.js @@ -151,8 +151,24 @@ Gun.dup = require('./dup'); } Gun.on.get = function(msg, gun){ - var root = gun._, soul = msg.get[_soul], node = root.graph[soul], has = msg.get[_has], tmp; + var root = gun._, get = msg.get, soul = get[_soul], node = root.graph[soul], has = get[_has], tmp; var next = root.next || (root.next = {}), at = next[soul]; + if(get['*']){ // TEMPORARY HACK FOR MARTTI, TESTING + var graph = {}; + Gun.obj.map(root.graph, function(node, soul){ + if(Gun.text.match(soul, get)){ + graph[soul] = Gun.obj.copy(node); + } + }); + if(!Gun.obj.empty(graph)){ + root.on('in', { + '@': msg['#'], + how: '*', + put: graph, + $: gun + }); + } + } // TEMPORARY HACK FOR MARTTI, TESTING if(!node || !at){ return root.on('get', msg) } if(has){ if(!obj_has(node, has)){ return root.on('get', msg) } @@ -213,7 +229,7 @@ Gun.log.once = function(w,s,o){ return (o = Gun.log.once)[w] = o[w] || 0, o[w]++ 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 } +if(typeof window !== "undefined"){ (window.Gun = Gun).window = window } try{ if(typeof common !== "undefined"){ common.exports = Gun } }catch(e){} module.exports = Gun; From 5b624376b79cd0e90b77cf5e7b4dc12004882cff Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Fri, 24 Aug 2018 03:24:29 -0700 Subject: [PATCH 017/103] indexedDB!!! Thanks @robertheessels --- test/tmp/indexedDB.html | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/tmp/indexedDB.html b/test/tmp/indexedDB.html index 53d9afd2..6097fe09 100644 --- a/test/tmp/indexedDB.html +++ b/test/tmp/indexedDB.html @@ -15,11 +15,10 @@ \ No newline at end of file From 2630cc8223a9d857142638ff35f178d982e26313 Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Fri, 24 Aug 2018 16:36:29 -0700 Subject: [PATCH 018/103] Not swearing: DAM @$$ Heisenbug fixed! https://github.com/amark/gun/wiki/@$$ --- gun.js | 15 +++++++++------ package.json | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/gun.js b/gun.js index 67791109..168ef767 100644 --- a/gun.js +++ b/gun.js @@ -823,7 +823,7 @@ }); } } // TEMPORARY HACK FOR MARTTI, TESTING - if(!node || !at){ return root.on('get', msg) } + if(!node){ return root.on('get', msg) } if(has){ if(!obj_has(node, has)){ return root.on('get', msg) } node = Gun.state.to(node, has); @@ -834,7 +834,7 @@ node = Gun.obj.copy(node); } node = Gun.graph.node(node); - tmp = at.ack; + tmp = (at||empty).ack; root.on('in', { '@': msg['#'], how: 'mem', @@ -1941,7 +1941,8 @@ if(!raw){ return } var dup = ctx.dup, id, hash, msg, tmp = raw[0]; try{msg = JSON.parse(raw); - }catch(e){} + }catch(e){console.log('DAM JSON parse error', e)} + console.log("hear:", msg); if('{' === tmp){ if(!msg){ return } if(dup.check(id = msg['#'])){ return } @@ -2017,7 +2018,7 @@ var wire = peer.wire; try{ if(wire.send){ - wire.send(raw); + wire.send(raw); } else if(peer.say){ peer.say(raw); @@ -2028,7 +2029,7 @@ } }()); - + ;(function(){ mesh.raw = function(msg){ @@ -2051,7 +2052,9 @@ }); msg['><'] = to.join(); var raw = $(msg); if(u !== put){ - raw = raw.replace('"'+ _ +'"', put); + tmp = raw.indexOf(_, raw.indexOf('put')); + raw = raw.slice(0, tmp-1) + put + raw.slice(tmp + _.length + 1); + //raw = raw.replace('"'+ _ +'"', put); // https://github.com/amark/gun/wiki/@$$ Heisenbug } if(msh){ msh.raw = raw; diff --git a/package.json b/package.json index 30df1026..3c7b04eb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gun", - "version": "0.9.9999", + "version": "0.9.99991", "description": "A realtime, decentralized, offline-first, graph data synchronization engine.", "main": "index.js", "browser": "gun.min.js", From 001eb6f15d5bf24bc3a4d8e3266be5e2837ff017 Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Fri, 24 Aug 2018 16:49:57 -0700 Subject: [PATCH 019/103] Not swearing: DAM @$$ Heisenbug fixed! --- gun.js | 1 - package-lock.json | 2 +- test/panic/holy-grail.js | 1 + 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gun.js b/gun.js index 168ef767..8188b3b7 100644 --- a/gun.js +++ b/gun.js @@ -1942,7 +1942,6 @@ var dup = ctx.dup, id, hash, msg, tmp = raw[0]; try{msg = JSON.parse(raw); }catch(e){console.log('DAM JSON parse error', e)} - console.log("hear:", msg); if('{' === tmp){ if(!msg){ return } if(dup.check(id = msg['#'])){ return } diff --git a/package-lock.json b/package-lock.json index 414e3295..635c6288 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gun", - "version": "0.9.9998", + "version": "0.9.99991", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/test/panic/holy-grail.js b/test/panic/holy-grail.js index d96dfa2a..188d0499 100644 --- a/test/panic/holy-grail.js +++ b/test/panic/holy-grail.js @@ -45,6 +45,7 @@ describe("The Holy Grail Test!", function(){ }); it("GUN started!", function(){ + console.log("I must have screwed up my NPM installs, server won't start properly but Holy-Grail passes if I do it manually. Will fix later."); return server.run(function(test){ var env = test.props; test.async(); From 8fff5e9a1fe4af76151b913dad51f3847c902718 Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Wed, 29 Aug 2018 15:03:27 -0700 Subject: [PATCH 020/103] SEA & DAM fix --- examples/game/space.html | 25 ++++++++++++- gun.js | 72 ++++++++++++++++++++++++------------ gun.min.js | 3 +- lib/webrtc.js | 27 +++++--------- lib/wire.js | 30 ++++++--------- nts.js | 7 ++-- package.json | 3 +- sea.js | 13 ++++--- sea/shim.js | 9 +++-- sea/verify.js | 4 +- src/adapters/localStorage.js | 2 +- src/adapters/mesh.js | 64 ++++++++++++++++++++++++-------- src/adapters/websocket.js | 16 +++----- src/root.js | 4 +- test/tmp/seanode.js | 13 +++++++ test/tmp/space.html | 50 +++++++++++++++++++++++++ 16 files changed, 236 insertions(+), 106 deletions(-) create mode 100644 test/tmp/seanode.js create mode 100644 test/tmp/space.html diff --git a/examples/game/space.html b/examples/game/space.html index edfdae50..f393be5b 100644 --- a/examples/game/space.html +++ b/examples/game/space.html @@ -7,9 +7,11 @@
      +

      +

      Hello world!

      - - + - - -

      - - - \ No newline at end of file + From 6126f4be65144dfee2775d2041a2422398eb09d5 Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Fri, 21 Sep 2018 16:06:36 -0700 Subject: [PATCH 029/103] Update indexedDB.html --- test/tmp/indexedDB.html | 1 - 1 file changed, 1 deletion(-) diff --git a/test/tmp/indexedDB.html b/test/tmp/indexedDB.html index 284dcf7d..159fcd2e 100644 --- a/test/tmp/indexedDB.html +++ b/test/tmp/indexedDB.html @@ -1,6 +1,5 @@

      RindexedDB

      - From 5289126d1356c8f6f2d929f5491610103f6df251 Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Sun, 23 Sep 2018 01:23:21 -0700 Subject: [PATCH 030/103] fix NodeJS SEA auth w/ PBKDF2 --- examples/basic/user.html | 8 +-- gun.js | 3 +- package-lock.json | 41 ++++++++-------- package.json | 4 +- sea.js | 102 ++++++++++++++++++--------------------- 5 files changed, 77 insertions(+), 81 deletions(-) diff --git a/examples/basic/user.html b/examples/basic/user.html index 62a782aa..c736c67b 100644 --- a/examples/basic/user.html +++ b/examples/basic/user.html @@ -11,7 +11,7 @@
      - +
      @@ -19,8 +19,8 @@ - - - - \ No newline at end of file diff --git a/test/tmp/bigsync.js b/test/tmp/bigsync.js index 5119d726..10f5cba7 100644 --- a/test/tmp/bigsync.js +++ b/test/tmp/bigsync.js @@ -1,17 +1,25 @@ var Gun = require('../../'); -var data = require('fs').readFileSync('/Users/mark/Downloads/raddataformat.txt'); +/*var data = ''; +var a = [], b = Gun.text.random(1000 * 1000 * 10), c; +for(var i = 0; i <= 7; i++){ + data += b; +} +*/ +data = 1; -data = data.toString(); -data += data + data; - -console.log(data.length); - -var gun = Gun('http://localhost:8080/gun'); +var gun = Gun('http://localhost:8765/gun'); +//var gun = Gun(); setTimeout(function(){ + + /*console.log("READ!"); + gun.get('bigsync').get('raw').on(function(a,b){ + console.log('yay!', b, (a && a.slice && a.slice(0,20)) || a, a.length); + }); + return;*/ console.log("SEND!"); - gun.get('bigsync').get('raw').put(data); + gun.get('bigsync').get('raw').put(data, function(ack){console.log(ack)}); /*var req = require('http').request({ host: 'localhost' diff --git a/test/tmp/nab.html b/test/tmp/nab.html deleted file mode 100644 index a5c4b368..00000000 --- a/test/tmp/nab.html +++ /dev/null @@ -1,101 +0,0 @@ -

      notabug

      - - - - - - - -

      homepage

      -
        -
      - - - - - - - - \ No newline at end of file From d269419c96e3bde38c06c8b119f44e658407629e Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Tue, 25 Sep 2018 15:46:50 -0700 Subject: [PATCH 034/103] unbuild Stabilized against V8 Fatalities. --- gun.min.js | 4 ++-- package-lock.json | 2 +- package.json | 2 +- src/adapters/localStorage.js | 2 +- src/adapters/mesh.js | 34 ++++++++++++++++++++++------------ src/dup.js | 2 +- 6 files changed, 28 insertions(+), 18 deletions(-) diff --git a/gun.min.js b/gun.min.js index adfd5edc..61285cd0 100644 --- a/gun.min.js +++ b/gun.min.js @@ -1,2 +1,2 @@ -!function(){function t(n,o){function e(t){return t.split("/").slice(-1).toString().replace(".js","")}return o?require(n):n.slice?t[e(n)]:function(o,i){n(o={exports:{}}),t[e(i)]=o.exports}}var n;"undefined"!=typeof window&&(n=window),"undefined"!=typeof global&&(n=global),n=n||{};var o=n.console||{log:function(){}};if("undefined"!=typeof module)var e=module;t(function(t){var n={};n.fn={is:function(t){return!!t&&"function"==typeof t}},n.bi={is:function(t){return t instanceof Boolean||"boolean"==typeof t}},n.num={is:function(t){return!e(t)&&(t-parseFloat(t)+1>=0||1/0===t||-(1/0)===t)}},n.text={is:function(t){return"string"==typeof t}},n.text.ify=function(t){return n.text.is(t)?t:"undefined"!=typeof JSON?JSON.stringify(t):t&&t.toString?t.toString():t},n.text.random=function(t,n){var o="";for(t=t||24,n=n||"0123456789ABCDEFGHIJKLMNOPQRSTUVWXZabcdefghijklmnopqrstuvwxyz";t>0;)o+=n.charAt(Math.floor(Math.random()*n.length)),t--;return o},n.text.match=function(t,o){function e(t,n){for(var o,e=-1,i=0;o=n[i++];)if(!~(e=t.indexOf(o,e+1)))return!1;return!0}var i=!1;if(t=t||"",o=n.text.is(o)?{"=":o}:o||{},n.obj.has(o,"~")&&(t=t.toLowerCase(),o["="]=(o["="]||o["~"]).toLowerCase()),n.obj.has(o,"="))return t===o["="];if(n.obj.has(o,"*")){if(t.slice(0,o["*"].length)!==o["*"])return!1;i=!0,t=t.slice(o["*"].length)}if(n.obj.has(o,"!")){if(t.slice(-o["!"].length)!==o["!"])return!1;i=!0}if(n.obj.has(o,"+")&&n.list.map(n.list.is(o["+"])?o["+"]:[o["+"]],function(n){return t.indexOf(n)>=0?void(i=!0):!0}))return!1;if(n.obj.has(o,"-")&&n.list.map(n.list.is(o["-"])?o["-"]:[o["-"]],function(n){return t.indexOf(n)<0?void(i=!0):!0}))return!1;if(n.obj.has(o,">")){if(!(t>o[">"]))return!1;i=!0}if(n.obj.has(o,"<")){if(!(tn?-1:n>o?1:0):0}},n.list.map=function(t,n,o){return a(t,n,o)},n.list.index=1,n.obj={is:function(t){return t?t instanceof Object&&t.constructor===Object||"Object"===Object.prototype.toString.call(t).match(/^\[object (\w+)\]$/)[1]:!1}},n.obj.put=function(t,n,o){return(t||{})[n]=o,t},n.obj.has=function(t,n){return t&&Object.prototype.hasOwnProperty.call(t,n)},n.obj.del=function(t,n){return t?(t[n]=null,delete t[n],t):void 0},n.obj.as=function(t,n,o,e){return t[n]=t[n]||(e===o?{}:o)},n.obj.ify=function(t){if(r(t))return t;try{t=JSON.parse(t)}catch(n){t={}}return t},function(){function t(t,n){u(this,n)&&o!==this[n]||(this[n]=t)}var o;n.obj.to=function(n,o){return o=o||{},a(n,t,o),o}}(),n.obj.copy=function(t){return t?JSON.parse(JSON.stringify(t)):t},function(){function t(t,n){var o=this.n;if(!o||!(n===o||r(o)&&u(o,n)))return n?!0:void 0}n.obj.empty=function(n,o){return n&&a(n,t,{n:o})?!1:!0}}(),function(){function t(n,o){return 2===arguments.length?(t.r=t.r||{},void(t.r[n]=o)):(t.r=t.r||[],void t.r.push(n))}var i=Object.keys;n.obj.map=function(a,s,f){var c,l,p,d,h,v=0,g=o(s);if(t.r=null,i&&r(a)&&(d=i(a),h=!0),e(a)||d)for(l=(d||a).length;l>v;v++){var m=v+n.list.index;if(g){if(p=h?s.call(f||this,a[d[v]],d[v],t):s.call(f||this,a[v],m,t),p!==c)return p}else if(s===a[h?d[v]:v])return d?d[v]:m}else for(v in a)if(g){if(u(a,v)&&(p=f?s.call(f,a[v],v,t):s(a[v],v,t),p!==c))return p}else if(s===a[v])return v;return g?t.r:n.list.index?0:-1}}(),n.time={},n.time.is=function(t){return t?t instanceof Date:+(new Date).getTime()};var o=n.fn.is,e=n.list.is,i=n.obj,r=i.is,u=i.has,a=i.map;t.exports=n})(t,"./type"),t(function(t){t.exports=function n(t,o,e){if(!t)return{to:n};var i,t=(this.tag||(this.tag={}))[t]||(this.tag[t]={tag:t,to:n._={next:function(t){var n;(n=this.to)&&n.next(t)}}});if(o instanceof Function){var r={off:n.off||(n.off=function(){return this.next===n._.next?!0:(this===this.the.last&&(this.the.last=this.back),this.to.back=this.back,this.next=n._.next,this.back.to=this.to,void(this.the.last===this.the&&delete this.on.tag[this.the.tag]))}),to:n._,next:o,the:t,on:this,as:e};return(r.back=t.last||t).to=r,t.last=r}return(t=t.to)&&i!==o&&t.next(o),t}})(t,"./onto"),t(function(t){function n(t,n,e,i,r){if(n>t)return{defer:!0};if(e>n)return{historical:!0};if(n>e)return{converge:!0,incoming:!0};if(n===e){if(i=o(i)||"",r=o(r)||"",i===r)return{state:!0};if(r>i)return{converge:!0,current:!0};if(i>r)return{converge:!0,incoming:!0}}return{err:"Invalid CRDT Data: "+i+" to "+r+" at "+n+" to "+e+"!"}}if("undefined"==typeof JSON)throw new Error("JSON is not included in this browser. Please load it first: ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js");var o=JSON.stringify;t.exports=n})(t,"./HAM"),t(function(n){var o=t("./type"),e={};e.is=function(t){return t===i?!1:null===t?!0:t===1/0?!1:s(t)||u(t)||a(t)?!0:e.rel.is(t)||!1},e.link=e.rel={_:"#"},function(){function t(t,n){var o=this;return o.id?o.id=!1:n==r&&s(t)?void(o.id=t):o.id=!1}e.rel.is=function(n){if(n&&n[r]&&!n._&&c(n)){var o={};if(p(n,t,o),o.id)return o.id}return!1}}(),e.rel.ify=function(t){return l({},r,t)},o.obj.has._=".";var i,r=e.link._,u=o.bi.is,a=o.num.is,s=o.text.is,f=o.obj,c=f.is,l=f.put,p=f.map;n.exports=e})(t,"./val"),t(function(n){var o=t("./type"),e=t("./val"),i={_:"_"};i.soul=function(t,n){return t&&t._&&t._[n||p]},i.soul.ify=function(t,n){return n="string"==typeof n?{soul:n}:n||{},t=t||{},t._=t._||{},t._[p]=n.soul||t._[p]||l(),t},i.soul._=e.link._,function(){function t(t,n){return n!==i._?e.is(t)?void(this.cb&&this.cb.call(this.as,t,n,this.n,this.s)):!0:void 0}i.is=function(n,o,e){var r;return a(n)&&(r=i.soul(n))?!f(n,t,{as:e,cb:o,s:r,n:n}):!1}}(),function(){function t(t,n){var o,i,r=this.o;return r.map?(o=r.map.call(this.as,t,""+n,r.node),void(i===o?s(r.node,n):r.node&&(r.node[n]=o))):void(e.is(t)&&(r.node[n]=t))}i.ify=function(n,o,e){return o?"string"==typeof o?o={soul:o}:o instanceof Function&&(o={map:o}):o={},o.map&&(o.node=o.map.call(e,n,r,o.node||{})),(o.node=i.soul.ify(o.node||{},o))&&f(n,t,{o:o,as:e}),o.node}}();var r,u=o.obj,a=u.is,s=u.del,f=u.map,c=o.text,l=c.random,p=i.soul._;n.exports=i})(t,"./node"),t(function(n){function o(){var t;return t=r(),t>u?(a=0,u=t+o.drift):u=t+(a+=1)/s+o.drift}{var e=t("./type"),i=t("./node"),r=e.time.is,u=-(1/0),a=0,s=1e3,f="undefined"!=typeof performance?performance.timing&&performance:!1;f&&f.timing&&f.timing.navigationStart||(f=!1)}o._=">",o.drift=0,o.is=function(t,n,e){var i=n&&t&&t[_]&&t[_][o._]||e;if(i)return b(i=i[n])?i:-(1/0)},o.lex=function(){return o().toString(36).replace(".","")},o.ify=function(t,n,e,r,u){if(!t||!t[_]){if(!u)return;t=i.soul.ify(t,u)}var a=p(t[_],o._);return c!==n&&n!==_&&(b(e)&&(a[n]=e),c!==r&&(t[n]=r)),t},o.to=function(t,n,e){var r=t[n];return h(r)&&(r=g(r)),o.ify(e,n,o.is(t,n),r,i.soul(t))},function(){function t(t,n){_!==n&&o.ify(this.o,n,this.s)}o.map=function(n,e,i){var r,u=h(u=n||e)?u:null;return n=y(n=n||e)?n:null,u&&!n?(e=b(e)?e:o(),u[_]=u[_]||{},v(u,t,{o:u,s:e}),u):(i=i||h(e)?e:r,e=b(e)?e:o(),function(o,u,a,s){return n?(n.call(i||this||{},o,u,a,s),void(d(a,u)&&r===a[u]||t.call({o:a,s:e},o,u))):(t.call({o:a,s:e},o,u),o)})}}();var c,l=e.obj,p=l.as,d=l.has,h=l.is,v=l.map,g=l.copy,m=e.num,b=m.is,k=e.fn,y=k.is,_=i._;n.exports=o})(t,"./state"),t(function(n){var o=t("./type"),e=t("./val"),i=t("./node"),r={};!function(){function t(t,o){return t&&o===i.soul(t)&&i.is(t,this.fn,this.as)?void(this.cb&&(n.n=t,n.as=this.as,this.cb.call(n.as,t,o,n))):!0}function n(t){t&&i.is(n.n,t,n.as)}r.is=function(n,o,e,i){return n&&s(n)&&!l(n)?!d(n,t,{cb:o,fn:e,as:i}):!1}}(),function(){function t(t,o){var r;return(r=p(t,o))?r:(o.env=t,o.soul=a,i.ify(o.obj,n,o)&&(o.rel=o.rel||e.rel.ify(i.soul(o.node)),o.obj!==t.shell&&(t.graph[e.rel.is(o.rel)]=o.node)),o)}function n(n,o,r){var a,s,p=this,d=p.env;if(i._===o&&c(n,e.rel._))return r._;if(a=l(n,o,r,p,d)){if(o||(p.node=p.node||r||{},c(n,i._)&&i.soul(n)&&(p.node._=h(n._)),p.node=i.soul.ify(p.node,e.rel.is(p.rel)),p.rel=p.rel||e.rel.ify(i.soul(p.node))),(s=d.map)&&(s.call(d.as||{},n,o,r,p),c(r,o))){if(n=r[o],u===n)return void f(r,o);if(!(a=l(n,o,r,p,d)))return}if(!o)return p.node;if(!0===a)return n;if(s=t(d,{obj:n,path:p.path.concat(o)}),s.node)return s.rel}}function a(t){var n=this,o=e.link.is(n.rel),r=n.env.graph;n.rel=n.rel||e.rel.ify(t),n.rel[e.rel._]=t,n.node&&n.node[i._]&&(n.node[i._][e.rel._]=t),c(r,o)&&(r[t]=r[o],f(r,o))}function l(t,n,i,r,u){var a;return e.is(t)?!0:s(t)?1:(a=u.invalid)?(t=a.call(u.as||{},t,n,i),l(t,n,i,r,u)):(u.err="Invalid value at '"+r.path.concat(n).join(".")+"'!",void(o.list.is(t)&&(u.err+=" Use `.set(item)` instead of an Array.")))}function p(t,n){for(var o,e=t.seen,i=e.length;i--;)if(o=e[i],n.obj===o.obj)return o;e.push(n)}r.ify=function(n,o,i){var r={path:[],obj:n};return o?"string"==typeof o?o={soul:o}:o instanceof Function&&(o.map=o):o={},o.soul&&(r.rel=e.rel.ify(o.soul)),o.shell=(i||{}).shell,o.graph=o.graph||{},o.seen=o.seen||[],o.as=o.as||i,t(o,r),o.root=r.node,o.graph}}(),r.node=function(t){var n=i.soul(t);if(n)return p({},n,t)},function(){function t(t,n){var o,u;if(i._===n){if(l(t,e.rel._))return;return void(this.obj[n]=h(t))}return(o=e.rel.is(t))?(u=this.opt.seen[o])?void(this.obj[n]=u):void(this.obj[n]=this.opt.seen[o]=r.to(this.graph,o,this.opt)):void(this.obj[n]=t)}r.to=function(n,o,e){if(n){var i={};return e=e||{seen:{}},d(n[o],t,{obj:i,graph:n,opt:e}),i}}}();var u,a=(o.fn.is,o.obj),s=a.is,f=a.del,c=a.has,l=a.empty,p=a.put,d=a.map,h=a.copy;n.exports=r})(t,"./graph"),t(function(n){t("./onto"),n.exports=function(t,n){if(this.on){if(!(t instanceof Function)){if(!t||!n)return;var o=t["#"]||t,e=(this.tag||empty)[o];if(!e)return;return e=this.on(o,n),clearTimeout(e.err),!0}var o=n&&n["#"]||Math.random().toString(36).slice(2);if(!t)return o;var i=this.on(o,t,n);return i.err=i.err||setTimeout(function(){i.next({err:"Error: No ACK received yet.",lack:!0}),i.off()},(this.opt||{}).lack||9e3),o}}})(t,"./ask"),t(function(n){function o(t){var n={s:{}};return t=t||{max:1e3,age:9e3},n.check=function(t){var o;return(o=n.s[t])?o.pass?o.pass=!1:n.track(t):!1},n.track=function(o,r){var u=n.s[o]||(n.s[o]={});return u.was=i(),r&&(u.pass=!0),n.to||(n.to=setTimeout(function(){var o=i();e.obj.map(n.s,function(i,r){t.age>o-i.was||e.obj.del(n.s,r)}),n.to=null},t.age+9)),u},n}var e=t("./type"),i=e.time.is;n.exports=o})(t,"./dup"),t(function(n){function i(t){return t instanceof i?(this._={gun:this,$:this}).$:this instanceof i?i.create(this._={gun:this,$:this,opt:t}):new i(t)}i.is=function(t){return t instanceof i||t&&t._&&t===t._.$||!1},i.version=.9,i.chain=i.prototype,i.chain.toJSON=function(){};var r=t("./type");r.obj.to(r,i),i.HAM=t("./HAM"),i.val=t("./val"),i.node=t("./node"),i.state=t("./state"),i.graph=t("./graph"),i.on=t("./onto"),i.ask=t("./ask"),i.dup=t("./dup"),function(){function t(n){var o,e,r=this,a=r.as,s=a.at||a,f=s.$;return(e=n["#"])||(e=n["#"]=c(9)),(o=s.dup).check(e)?void(a.out===n.out&&(n.out=u,r.to.next(n))):(o.track(e),s.ask(n["@"],n)||(n.get&&i.on.get(n,f),n.put&&i.on.put(n,f)),r.to.next(n),void(a.out||(n.out=t,s.on("out",n))))}i.create=function(n){n.root=n.root||n,n.graph=n.graph||{},n.on=n.on||i.on,n.ask=n.ask||i.ask,n.dup=n.dup||i.dup();var o=n.$.opt(n.opt);return n.once||(n.on("in",t,n),n.on("out",t,{at:n,out:t}),i.on("create",n),n.on("create",n)),n.once=1,o}}(),function(){function t(t,n,o,e){var r=this,u=i.state.is(o,n);if(!u)return r.err="Error: No state on '"+n+"' in node '"+e+"'!";var a=r.graph[e]||k,s=i.state.is(a,n,!0),f=a[n],c=i.HAM(r.machine,u,s,t,f);return c.incoming?(r.put[e]=i.state.to(o,n,r.put[e]),(r.diff||(r.diff={}))[e]=i.state.to(o,n,r.diff[e]),void(r.souls[e]=!0)):void(c.defer&&(r.defer=u<(r.defer||1/0)?u:r.defer))}function n(t,n){var i=this,u=i.$._,a=(u.next||k)[n];if(!a){if(!(u.opt||k)["super"])return void(i.souls[n]=!1);a=i.$.get(n)._}var s=i.map[n]={put:t,get:n,$:a.$},f={ctx:i,msg:s};i.async=!!u.tag.node,i.ack&&(s["@"]=i.ack),v(t,o,f),i.async&&(i.and||u.on("node",function(t){this.to.next(t),t===i.map[t.get]&&(i.souls[t.get]=!1,v(t.put,e,t),v(i.souls,function(t){return t?t:void 0})||i.c||(i.c=1,this.off(),v(i.map,r,i)))}),i.and=!0,u.on("node",s))}function o(t,n){var o=this.ctx,e=o.graph,r=this.msg,u=r.get,a=r.put,s=r.$._;e[u]=i.state.to(a,n,e[u]),o.async||(s.put=i.state.to(a,n,s.put))}function e(t,n){var o=this,e=o.put,r=o.$._;r.put=i.state.to(e,n,r.put)}function r(t){t.$&&(this.cat.stop=this.stop,t.$._.on("in",t),this.cat.stop=null)}i.on.put=function(o,e){var a=e._,s={$:e,graph:a.graph,put:{},map:{},souls:{},machine:i.state(),ack:o["@"],cat:a,stop:{}};return i.graph.is(o.put,null,t,s)||(s.err="Error: Invalid graph!"),s.err?a.on("in",{"@":o["#"],err:i.log(s.err)}):(v(s.put,n,s),s.async||v(s.map,r,s),u!==s.defer&&setTimeout(function(){i.on.put(o,e)},s.defer-s.machine),void(s.diff&&a.on("put",h(o,{put:s.diff}))))},i.on.get=function(t,n){var o,e=n._,r=t.get,u=r[m],a=e.graph[u],s=r[b],f=e.next||(e.next={}),c=f[u];if(d(u,"*")){var l={};i.obj.map(e.graph,function(t,n){i.text.match(n,u)&&(l[n]=i.obj.copy(t))}),i.obj.empty(l)||e.on("in",{"@":t["#"],how:"*",put:l,$:n})}if(!a)return e.on("get",t);if(s){if(!d(a,s))return e.on("get",t);a=i.state.to(a,s)}else a=i.obj.copy(a);a=i.graph.node(a),o=(c||k).ack,e.on("in",{"@":t["#"],how:"mem",put:a,$:n}),e.on("get",t)}}(),function(){i.chain.opt=function(t){t=t||{};var n=this,o=n._,e=t.peers||t;return p(t)||(t={}),p(o.opt)||(o.opt=t),f(e)&&(e=[e]),a(e)&&(e=v(e,function(t,n,o){o(t,{url:t})}),p(o.opt.peers)||(o.opt.peers={}),o.opt.peers=h(e,o.opt.peers)),o.opt.peers=o.opt.peers||{},h(t,o.opt),i.on("opt",o),o.opt.uuid=o.opt.uuid||function(){return g()+c(12)},n}}();var u,a=i.list.is,s=i.text,f=s.is,c=s.random,l=i.obj,p=l.is,d=l.has,h=l.to,v=l.map,g=(l.copy,i.state.lex),m=i.val.rel._,b=".",k=(i.node._,i.val.link.is,{});o.debug=function(t,n){return o.debug.i&&t===o.debug.i&&o.debug.i++&&(o.log.apply(o,arguments)||n)},i.log=function(){return!i.log.off&&o.log.apply(o,arguments),[].slice.call(arguments).join(" ")},i.log.once=function(t,n,o){return(o=i.log.once)[t]=o[t]||0,o[t]++||i.log(n)},i.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'!"),"undefined"!=typeof window&&((window.Gun=i).window=window);try{"undefined"!=typeof e&&(e.exports=i)}catch(y){}n.exports=i})(t,"./root"),t(function(){var n=t("./root");n.chain.back=function(t,i){var r;if(t=t||1,-1===t||1/0===t)return this._.root.$;if(1===t)return(this._.back||this._).$;var u=this,a=u._;if("string"==typeof t&&(t=t.split(".")),!(t instanceof Array)){if(t instanceof Function){for(var s,r={back:a};(r=r.back)&&o===(s=t(r,i)););return s}return n.num.is(t)?(a.back||a).$.back(t-1):this}var f=0,c=t.length,r=a;for(f;c>f;f++)r=(r||e)[t[f]];return o!==r?i?u:r:(r=a.back)?r.$.back(t,i):void 0};var o,e={}})(t,"./back"),t(function(){function n(t){var n,o,e,i=this.as,r=i.back,u=i.root;if(t.I||(t.I=i.$),t.$||(t.$=i.$),this.to.next(t),o=t.get){if(o["#"]||i.soul){if(o["#"]=o["#"]||i.soul,t["#"]||(t["#"]=b(9)),r=u.$.get(o["#"])._,o=o["."]){if(h(r.put,o)&&(n=r.$.get(o)._,(e=n.ack)||(n.ack=-1),r.on("in",{$:r.$,put:c.state.to(r.put,o),get:r.get}),e))return}else{if(e=r.ack,e||(r.ack=-1),h(r,"put")&&r.on("in",r),e)return;t.$=r.$}return u.ask(f,t),u.on("in",t)}if(u.now&&(u.now[i.id]=u.now[i.id]||!0,i.pass={}),o["."])return i.get?(t={get:{".":i.get},$:i.$},r.ask||(r.ask={}),r.ask[i.get]=t.$._,r.on("out",t)):(t={get:{},$:i.$},r.on("out",t));if(i.ack=i.ack||-1,i.get)return t.$=i.$,o["."]=i.get,(r.ask||(r.ask={}))[i.get]=t.$._,r.on("out",t)}return r.on("out",t)}function o(t){var n,o,r=this,s=r.as,f=s.root,d=t.$,b=(d||p)._||p,k=t.put;if(s.get&&t.get!==s.get&&(t=g(t,{get:s.get})),s.has&&b!==s&&(t=g(t,{$:s.$}),b.ack&&(s.ack=b.ack)),l===k){if(o=b.put,r.to.next(t),s.soul)return;if(l===o&&l!==b.put)return;return i(s,t,r),s.has&&a(s,t),v(b.echo,s.id),void v(s.map,b.id)}if(s.soul)return r.to.next(t),i(s,t,r),void(s.next&&m(k,u,{msg:t,cat:s}));if(!(n=c.val.link.is(k)))return c.val.is(k)?(s.has||s.soul?a(s,t):(b.has||b.soul)&&((b.echo||(b.echo={}))[s.id]=b.echo[b.id]||s,(s.map||(s.map={}))[b.id]=s.map[b.id]||{at:b}),r.to.next(t),void i(s,t,r)):(s.has&&b!==s&&h(b,"put")&&(s.put=b.put),(n=c.node.soul(k))&&b.has&&(b.put=s.root.$.get(n)._.put),o=(f.stop||{})[b.id],r.to.next(t),e(s,t,b,n),i(s,t,r),void(s.next&&m(k,u,{msg:t,cat:s})));f.stop;o=f.stop||{},o=o[b.id]||(o[b.id]={}),o.is=o.is||b.put,o[s.id]=b.put||!0,r.to.next(t),e(s,t,b,n),i(s,t,r)}function e(t,n,o,i){if(i&&k!==t.get){var r=t.root.$.get(i)._;t.has?o=r:o.has&&e(o,n,o,i),o!==t&&(o.$||(o={}),(o.echo||(o.echo={}))[t.id]=o.echo[t.id]||t,t.has&&!(t.map||p)[o.id]&&a(t,n),r=o.id?(t.map||(t.map={}))[o.id]=t.map[o.id]||{at:o}:{},(i!==r.link||r.pass||t.pass)&&(t.pass&&(c.obj.map(t.map,function(t){t.pass=!0}),v(t,"pass")),r.pass&&v(r,"pass"),t.has&&(t.link=i),s(t,r.link=i)))}}function i(t,n){t.echo&&m(t.echo,r,n)}function r(t){t&&t.on&&t.on("in",this)}function u(t,n){var o,e,i,r=this.cat,u=r.next||p,a=this.msg;(k!==n||u[n])&&(e=u[n])&&(e.has?(l!==e.put&&c.val.link.is(t)||(e.put=t),o=e.$):(i=a.$)&&(i=(o=a.$.get(n))._,l!==i.put&&c.val.link.is(t)||(i.put=t)),e.on("in",{put:t,get:n,$:o,via:a}))}function a(t,n){if(t.has||t.soul){{var o=t.map;t.root}t.map=null,t.has&&(t.link=null),(t.pass||n["@"]||null!==o)&&(l===o&&c.val.link.is(t.put)||(m(o,function(n){(n=n.at)&&v(n.echo,t.id)}),o=t.put,m(t.next,function(n,e){return l===o&&l!==t.put?!0:(n.put=l,n.ack&&(n.ack=-1),void n.on("in",{get:e,$:n.$,put:l}))})))}}function s(t,n){var o=t.root.$.get(n)._;(!t.ack||(o.on("out",{get:{"#":n}}),t.ask))&&(o=t.ask,c.obj.del(t,"ask"),m(o||t.next,function(t,o){t.on("out",{get:{"#":n,".":o}})}),c.obj.del(t,"ask"))}function f(t){var n=this.as,o=n.get||p,e=n.$._,i=(t.put||p)[o["#"]];if(e.ack&&(e.ack=e.ack+1||1),!t.put||o["."]&&!h(i,e.get)){if(e.put!==l)return;return void e.on("in",{get:e.get,put:e.put=l,$:e.$,"@":t["@"]})}return k==o["."]?void e.on("in",{get:e.get,put:c.val.link.ify(o["#"]),$:e.$,"@":t["@"]}):(t.$=e.root.$,void c.on.put(t,e.root.$))}var c=t("./root");c.chain.chain=function(t){var e,i=this,r=i._,u=new(t||i).constructor(i),a=u._;return a.root=e=r.root,a.id=++e.once,a.back=i._,a.on=c.on,a.on("in",o,a),a.on("out",n,a),u};var l,p={},d=c.obj,h=d.has,v=(d.put,d.del),g=d.to,m=d.map,b=c.text.random,k=(c.val.rel._,c.node._)})(t,"./chain"),t(function(){function n(t,n){var o=n._,e=o.next,i=n.chain(),r=i._;return e||(e=o.next={}),e[r.get=t]=r,n===o.root.$?r.soul=t:(o.soul||o.has)&&(r.has=t),r}function o(t,n,o,e){var i,r=t._;return(i=r.soul)?(n(i,e,r),t):(i=r.link)?(n(i,e,r),t):(t.get(function(t,o){o.rid(t);var r=(r=t.$)&&r._||{};i=r.link||r.soul||c.is(t.put)||l(t.put),n(i,e,t,o)},{out:{get:{".":!0}}}),t)}function e(t){var n,o=this,e=o.as,i=e.at,r=i.root,a=t.$,f=(a||{})._||{},l=t.put||f.put;if((n=r.now)&&o!==n[e.now])return o.to.next(t);if(o.seen&&f.id&&o.seen[f.id])return o.to.next(t);if((n=l)&&n[c._]&&(n=c.is(n))&&(n=(t.$$=f.root.gun.get(n))._,u!==n.put&&(t=s(t,{put:l=n.put}))),(n=r.mum)&&f.id){if(n[f.id])return;u===l||c.is(l)||(n[f.id]=!0)}return e.use(t,o),o.stun?void(o.stun=null):void o.to.next(t)}function i(t){var n=this.on;if(!t||n.soul||n.has)return this.off();if(t=(t=(t=t.$||t)._||t).id){{var o,e;n.map}return(o=(e=this.seen||(this.seen={}))[t])?!0:void(e[t]=!0)}}var r=t("./root");r.chain.get=function(t,u,a){var s,l;if("string"!=typeof t){if(t instanceof Function){if(!0===u)return o(this,t,u,a);s=this;var d,h=s._,v=h.root,l=v.now;a=u||{},a.at=h,a.use=t,a.out=a.out||{},a.out.get=a.out.get||{},(d=h.on("in",e,a)).rid=i,(v.now={$:1})[a.now=h.id]=d;var g=v.mum;return v.mum={},h.on("out",a.out),v.mum=g,v.now=l,s}return f(t)?this.get(""+t,u,a):(l=c.is(t))?this.get(l,u,a):((a=this.chain())._.err={err:r.log("Invalid get request!",t)},u&&u.call(a,a._.err),a)}var m=this,b=m._,k=b.next||p;return(s=k[t])||(s=n(t,m)),s=s.$,(l=b.stun)&&(s._.stun=s._.stun||l),u&&u instanceof Function&&s.get(u,a),s};var u,a=r.obj,s=(a.has,r.obj.to),f=r.num.is,c=r.val.link,l=r.node.soul,p=(r.node._,{})})(t,"./get"),t(function(){function n(t){t.batch=i;var n=t.opt||{},o=t.env=c.state.map(u,n.state);return o.soul=t.soul,t.graph=c.graph.ify(t.data,o,t),o.err?((t.ack||m).call(t,t.out={err:c.log(o.err)}),void(t.res&&t.res())):void t.batch()}function e(t){return void(t&&t())}function i(){var t=this;t.graph&&!v(t.stun,r)&&(t.res=t.res||function(t){t&&t()},t.res(function(){var n=t.$.back(-1)._,o=n.ask(function(o){n.root.on("ack",o),o.err&&c.log(o),o.lack||this.off(),t.ack&&t.ack(o,this)},t.opt),e=n.root.now;p.del(n.root,"now");var i=n.root.mum;n.root.mum={},t.ref._.on("out",{$:t.ref,put:t.out=t.env.graph,opt:t.opt,"#":o}),n.root.mum=i?p.to(i,n.root.mum):i,n.root.now=e},t),t.res&&t.res())}function r(t){return t?!0:void 0}function u(t,n,o,e){var i=this,r=c.is(t);!n&&e.path.length&&(i.res||b)(function(){var n=e.path,o=i.ref,u=(i.opt,0),s=n.length;for(u;s>u;u++)o=o.get(n[u]);r&&(o=t);var f=o._.dub;return f||(f=c.node.soul(e.obj))?(o.back(-1).get(f),void e.soul(f)):((i.stun=i.stun||{})[n]=!0,void o.get(a,!0,{as:{at:e,as:i,p:n}}))},{as:i,at:e})}function a(t,n,o,e){var n=n.as,i=n.at;n=n.as;var r=((o||{}).$||{})._||{};return t=r.dub=r.dub||t||c.node.soul(i.obj)||c.node.soul(o.put||r.put)||c.val.rel.is(o.put||r.put)||(n.via.back("opt.uuid")||c.text.random)(),e&&(e.stun=!0),t?void s(r,r.dub=t,i,n):void r.via.back("opt.uuid")(function(t,o){return t?c.log(t):void s(r,r.dub=r.dub||o,i,n)})}function s(t,n,o,e){t.$.back(-1).get(n),o.soul(n),e.stun[o.path]=!1,e.batch()}function f(t,n,e,i){if(n=n.as,e.$&&e.$._){if(e.err)return void o.log("Please report this as an issue! Put.any.err");var r,u=e.$._,a=u.put,s=n.opt||{};if(!(r=n.ref)||!r._.now){if(i&&(i.stun=!0),n.ref!==n.$){if(r=n.$._.get||u.get,!r)return void o.log("Please report this as an issue! Put.no.get");n.data=h({},r,n.data),r=null}if(l===a){if(!u.get)return;t||(r=u.$.back(function(t){return t.link||t.soul?t.link||t.soul:void(n.data=h({},t.get,n.data))})),r=r||u.get,u=u.root.$.get(r)._,n.soul=r,a=n.data}return n.not||(n.soul=n.soul||t)||(n.path&&d(n.data)?n.soul=(s.uuid||n.via.back("opt.uuid")||c.text.random)():(k==u.get&&(n.soul=(u.put||g)["#"]||u.dub),n.soul=n.soul||u.soul||u.soul||(s.uuid||n.via.back("opt.uuid")||c.text.random)()),n.soul)?void n.ref.put(n.data,n.soul,n):void n.via.back("opt.uuid")(function(t,o){return t?c.log(t):void n.ref.put(n.data,n.soul=o,n)})}}}var c=t("./root");c.chain.put=function(t,o,i){var r,u=this,a=u._,s=a.root.$;return i=i||{},i.data=t,i.via=i.$=i.via||i.$||u,"string"==typeof o?i.soul=o:i.ack=i.ack||o,a.soul&&(i.soul=a.soul),i.soul||s===u?d(i.data)?(i.soul=i.soul||(i.not=c.node.soul(i.data)||(i.via.back("opt.uuid")||c.text.random)()),i.soul?(i.$=u=s.get(i.soul),i.ref=i.$,n(i),u):(i.via.back("opt.uuid")(function(t,n){return t?c.log(t):void(i.ref||i.$).put(i.data,i.soul=n,i)}),u)):((i.ack||m).call(i,i.out={err:c.log("Data saved to the root level of the graph must be a node (an object), not a",typeof i.data,'of "'+i.data+'"!')}),i.res&&i.res(),u):c.is(t)?(t.get(function(t,n,e){return!t&&c.val.is(e.put)?c.log("The reference you are saving is a",typeof e.put,'"'+e.put+'", not a node (object)!'):void u.put(c.val.rel.ify(t),o,i)},!0),u):(i.ref=i.ref||s._===(r=a.back)?u:r.$,i.ref._.soul&&c.val.is(i.data)&&a.get?(i.data=h({},a.get,i.data),i.ref.put(i.data,i.soul,i),u):(i.ref.get(f,!0,{as:i}),i.out||(i.res=i.res||e,i.$._.stun=i.ref._.stun),u))};var l,p=c.obj,d=p.is,h=p.put,v=p.map,g={},m=function(){},b=function(t,n){t.call(n||g)},k=c.node._})(t,"./put"),t(function(n){var o=t("./root");t("./chain"),t("./back"),t("./put"),t("./get"),n.exports=o})(t,"./index"),t(function(){function n(t,n){{var o,e=this,r=t.$,u=(r||{})._||{},a=u.put||t.put;e.at}if(i!==a){if(o=t.$$){if(o=t.$$._,i===o.put)return;a=o.put}e.change&&(a=t.put),e.as?e.ok.call(e.as,t,n):e.ok.call(r,a,t.get,t,n)}}function o(t,n,e){var r,a,s=this.as,f=(s.at,t.$),c=f._,l=c.put||t.put;if(a=t.$$){if(r=a=t.$$._,i===a.put)return;l=a.put}return(a=n.wait)&&(a=a[c.id])&&clearTimeout(a),!e&&(i===l||c.soul||c.link||r&&!(0 .once, apologies unexpected."),this.once(t,n)},e.chain.once=function(t,n){var r=this,u=r._,a=u.put;if(0=(n.batch||1e3)?s():void(e||(e=setTimeout(s,n.wait||1)))}),t.on("get",function(o){function e(){if(s&&(i=s["#"])){var e=s["."];r=u[i]||a,r&&e&&(r=Gun.state.to(r,e)),(r||Gun.obj.empty(n.peers))&&t.on("in",{"@":o["#"],put:Gun.graph.node(r),how:"lS",lS:o.I})}}this.to.next(o);var i,r,a,s=o.get;Gun.debug?setTimeout(e,1):e()});var a=function(t,n,o,e){u[e]=Gun.state.to(o,n,u[e])},s=function(a){var f;r=0,clearTimeout(e),e=!1;var c=i;i={},a&&(u=a);try{o.setItem(n.prefix,JSON.stringify(u))}catch(l){Gun.log(f=l||"localStorage failure"),t.on("localStorage:error",{err:f,file:n.prefix,flush:u,retry:s})}(f||Gun.obj.empty(n.peers))&&Gun.obj.map(c,function(n,o){t.on("in",{"@":o,err:f,ok:0})})}}})}})(t,"./adapters/localStorage"),t(function(n){function e(t){var n=function(){},a=t.opt;return n.out=function(o){var e;return this.to&&this.to.next(o),(e=o["@"])&&(e=t.dup.s[e])&&(e=e.it)&&e.mesh?(n.say(o,e.mesh.via,1),void(e["##"]=o["##"])):void n.say(o)},t.on("create",function(o){o.opt.pid=o.opt.pid||i.text.random(9),this.to.next(o),t.on("out",n.out)}),n.hear=function(e,r){if(e){var u,a,s,f=t.dup,c=e[0];try{s=JSON.parse(e)}catch(l){o.log("DAM JSON parse error",l)}if("{"===c){if(!s)return;if(f.check(u=s["#"]))return;if(f.track(u,!0).it=s,(c=s["@"])&&s.put&&(a=s["##"]||(s["##"]=n.hash(s)),(c+=a)!=u)){if(f.check(c))return;(c=f.s)[a]=c[u]}return(s.mesh=function(){}).via=r,(c=s["><"])&&(s.mesh.to=i.obj.map(c.split(","),function(t,n,o){o(t,!0)})),s.dam?void((c=n.hear[s.dam])&&c(s,r,t)):void t.on("in",s)}if("["!==c);else{if(!s)return;for(var p,d=0;p=s[d++];)n.hear(p,r)}}},function(){function o(t,n){var o=n.wire;try{o.send?o.send(t):n.say&&n.say(t)}catch(e){(n.queue=n.queue||[]).push(t)}}n.say=function(e,r,s){if(!r)return void i.obj.map(a.peers,function(t){n.say(e,t)});var f,c,l,p=r.wire||a.wire&&a.wire(r);if(p&&(c=e.mesh||u,r!==c.via&&((l=c.raw)||(l=n.raw(e)),!((f=e["@"])&&(f=t.dup.s[f])&&(f=f.it)&&f.get&&f["##"]&&f["##"]===e["##"]||(f=c.to)&&(f[r.url]||f[r.id])&&!s)))){if(r.batch)return void r.batch.push(l);r.batch=[],setTimeout(function(){var t=r.batch;t&&(r.batch=null,t.length&&o(JSON.stringify(t),r))},a.gap||a.wait||1),o(l,r)}}}(),function(){function o(t,n){var o;return n instanceof Object?(i.obj.map(Object.keys(n).sort(),u,{to:o={},on:n}),o):n}function u(t){this.to[t]=this.on[t]}n.raw=function(e){if(!e)return"";var u,c,l,p=t.dup,d=e.mesh||{};if(l=d.raw)return l;if("string"==typeof e)return e;e["@"]&&(l=e.put)&&((c=e["##"])||(u=s(l,o)||"",c=n.hash(e,u),e["##"]=c),(l=p.s)[c=e["@"]+c]=l[e["#"]],e["#"]=c||e["#"],u&&((e=i.obj.to(e)).put=f));var h=0,v=[];i.obj.map(a.peers,function(t){return v.push(t.url||t.id),++h>9?!0:void 0}),e["><"]=v.join();var g=s(e);return r!==u&&(l=g.indexOf(f,g.indexOf("put")),g=g.slice(0,l-1)+u+g.slice(l+f.length+1)),d&&(d.raw=g),g},n.hash=function(t,n){return e.hash(n||s(t.put,o)||"")||t["#"]||i.text.random(9)};var s=JSON.stringify,f=":])([:"}(),n.hi=function(o){var e=o.wire||{};o.id||o.url?(a.peers[o.url||o.id]=o,i.obj.del(a.peers,e.id)):(e=e.id=e.id||i.text.random(9),n.say({dam:"?"},a.peers[e]=o)),e.hied||t.on(e.hied="hi",o),e=o.queue,o.queue=[],i.obj.map(e,function(t){n.say(t,o)})},n.bye=function(n){i.obj.del(a.peers,n.id),t.on("bye",n)},n.hear["?"]=function(t,o){return t.pid?(o.id=o.id||t.pid,void n.hi(o)):n.say({dam:"?",pid:a.pid,"@":t["#"]},o)},n}var i=t("../type");e.hash=function(t){if("string"!=typeof t)return{err:1};var n=0;if(!t.length)return n;for(var o,e=0,i=t.length;i>e;++e)o=t.charCodeAt(e),n=(n<<5)-n+o,n|=0;return n};var r,u={};Object.keys=Object.keys||function(t){return map(t,function(t,n,o){o(n)})};try{n.exports=e}catch(a){}})(t,"./adapters/mesh"),t(function(){var n=t("../index");n.Mesh=t("./mesh"),n.on("opt",function(t){function o(t){try{if(!t||!t.url)return o&&o(t);var n=t.url.replace("http","ws"),o=t.wire=new i.WebSocket(n);return o.onclose=function(){i.mesh.bye(t),e(t)},o.onerror=function(n){e(t),n&&"ECONNREFUSED"===n.code},o.onopen=function(){i.mesh.hi(t)},o.onmessage=function(n){ -n&&i.mesh.hear(n.data||n,t)},o}catch(r){}}function e(t){clearTimeout(t.defer),t.defer=setTimeout(function(){o(t)},2e3)}this.to.next(t);var i=t.opt;if(!t.once&&!1!==i.WebSocket){var r;"undefined"!=typeof window&&(r=window),"undefined"!=typeof global&&(r=global),r=r||{};var u=i.WebSocket||r.WebSocket||r.webkitWebSocket||r.mozWebSocket;if(u){i.WebSocket=u;{i.mesh=i.mesh||n.Mesh(t),i.wire}i.wire=o}}})})(t,"./adapters/websocket")}(); \ No newline at end of file +!function(){function t(n,o){function e(t){return t.split("/").slice(-1).toString().replace(".js","")}return o?require(n):n.slice?t[e(n)]:function(o,i){n(o={exports:{}}),t[e(i)]=o.exports}}var n;"undefined"!=typeof window&&(n=window),"undefined"!=typeof global&&(n=global),n=n||{};var o=n.console||{log:function(){}};if("undefined"!=typeof module)var e=module;t(function(t){var n={};n.fn={is:function(t){return!!t&&"function"==typeof t}},n.bi={is:function(t){return t instanceof Boolean||"boolean"==typeof t}},n.num={is:function(t){return!e(t)&&(t-parseFloat(t)+1>=0||1/0===t||-(1/0)===t)}},n.text={is:function(t){return"string"==typeof t}},n.text.ify=function(t){return n.text.is(t)?t:"undefined"!=typeof JSON?JSON.stringify(t):t&&t.toString?t.toString():t},n.text.random=function(t,n){var o="";for(t=t||24,n=n||"0123456789ABCDEFGHIJKLMNOPQRSTUVWXZabcdefghijklmnopqrstuvwxyz";t>0;)o+=n.charAt(Math.floor(Math.random()*n.length)),t--;return o},n.text.match=function(t,o){function e(t,n){for(var o,e=-1,i=0;o=n[i++];)if(!~(e=t.indexOf(o,e+1)))return!1;return!0}var i=!1;if(t=t||"",o=n.text.is(o)?{"=":o}:o||{},n.obj.has(o,"~")&&(t=t.toLowerCase(),o["="]=(o["="]||o["~"]).toLowerCase()),n.obj.has(o,"="))return t===o["="];if(n.obj.has(o,"*")){if(t.slice(0,o["*"].length)!==o["*"])return!1;i=!0,t=t.slice(o["*"].length)}if(n.obj.has(o,"!")){if(t.slice(-o["!"].length)!==o["!"])return!1;i=!0}if(n.obj.has(o,"+")&&n.list.map(n.list.is(o["+"])?o["+"]:[o["+"]],function(n){return t.indexOf(n)>=0?void(i=!0):!0}))return!1;if(n.obj.has(o,"-")&&n.list.map(n.list.is(o["-"])?o["-"]:[o["-"]],function(n){return t.indexOf(n)<0?void(i=!0):!0}))return!1;if(n.obj.has(o,">")){if(!(t>o[">"]))return!1;i=!0}if(n.obj.has(o,"<")){if(!(tn?-1:n>o?1:0):0}},n.list.map=function(t,n,o){return u(t,n,o)},n.list.index=1,n.obj={is:function(t){return t?t instanceof Object&&t.constructor===Object||"Object"===Object.prototype.toString.call(t).match(/^\[object (\w+)\]$/)[1]:!1}},n.obj.put=function(t,n,o){return(t||{})[n]=o,t},n.obj.has=function(t,n){return t&&Object.prototype.hasOwnProperty.call(t,n)},n.obj.del=function(t,n){return t?(t[n]=null,delete t[n],t):void 0},n.obj.as=function(t,n,o,e){return t[n]=t[n]||(e===o?{}:o)},n.obj.ify=function(t){if(r(t))return t;try{t=JSON.parse(t)}catch(n){t={}}return t},function(){function t(t,n){a(this,n)&&o!==this[n]||(this[n]=t)}var o;n.obj.to=function(n,o){return o=o||{},u(n,t,o),o}}(),n.obj.copy=function(t){return t?JSON.parse(JSON.stringify(t)):t},function(){function t(t,n){var o=this.n;if(!o||!(n===o||r(o)&&a(o,n)))return n?!0:void 0}n.obj.empty=function(n,o){return n&&u(n,t,{n:o})?!1:!0}}(),function(){function t(n,o){return 2===arguments.length?(t.r=t.r||{},void(t.r[n]=o)):(t.r=t.r||[],void t.r.push(n))}var i=Object.keys;n.obj.map=function(u,s,f){var c,l,p,d,h,v=0,g=o(s);if(t.r=null,i&&r(u)&&(d=i(u),h=!0),e(u)||d)for(l=(d||u).length;l>v;v++){var m=v+n.list.index;if(g){if(p=h?s.call(f||this,u[d[v]],d[v],t):s.call(f||this,u[v],m,t),p!==c)return p}else if(s===u[h?d[v]:v])return d?d[v]:m}else for(v in u)if(g){if(a(u,v)&&(p=f?s.call(f,u[v],v,t):s(u[v],v,t),p!==c))return p}else if(s===u[v])return v;return g?t.r:n.list.index?0:-1}}(),n.time={},n.time.is=function(t){return t?t instanceof Date:+(new Date).getTime()};var o=n.fn.is,e=n.list.is,i=n.obj,r=i.is,a=i.has,u=i.map;t.exports=n})(t,"./type"),t(function(t){t.exports=function n(t,o,e){if(!t)return{to:n};var i,t=(this.tag||(this.tag={}))[t]||(this.tag[t]={tag:t,to:n._={next:function(t){var n;(n=this.to)&&n.next(t)}}});if(o instanceof Function){var r={off:n.off||(n.off=function(){return this.next===n._.next?!0:(this===this.the.last&&(this.the.last=this.back),this.to.back=this.back,this.next=n._.next,this.back.to=this.to,void(this.the.last===this.the&&delete this.on.tag[this.the.tag]))}),to:n._,next:o,the:t,on:this,as:e};return(r.back=t.last||t).to=r,t.last=r}return(t=t.to)&&i!==o&&t.next(o),t}})(t,"./onto"),t(function(t){function n(t,n,e,i,r){if(n>t)return{defer:!0};if(e>n)return{historical:!0};if(n>e)return{converge:!0,incoming:!0};if(n===e){if(i=o(i)||"",r=o(r)||"",i===r)return{state:!0};if(r>i)return{converge:!0,current:!0};if(i>r)return{converge:!0,incoming:!0}}return{err:"Invalid CRDT Data: "+i+" to "+r+" at "+n+" to "+e+"!"}}if("undefined"==typeof JSON)throw new Error("JSON is not included in this browser. Please load it first: ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js");var o=JSON.stringify;t.exports=n})(t,"./HAM"),t(function(n){var o=t("./type"),e={};e.is=function(t){return t===i?!1:null===t?!0:t===1/0?!1:s(t)||a(t)||u(t)?!0:e.rel.is(t)||!1},e.link=e.rel={_:"#"},function(){function t(t,n){var o=this;return o.id?o.id=!1:n==r&&s(t)?void(o.id=t):o.id=!1}e.rel.is=function(n){if(n&&n[r]&&!n._&&c(n)){var o={};if(p(n,t,o),o.id)return o.id}return!1}}(),e.rel.ify=function(t){return l({},r,t)},o.obj.has._=".";var i,r=e.link._,a=o.bi.is,u=o.num.is,s=o.text.is,f=o.obj,c=f.is,l=f.put,p=f.map;n.exports=e})(t,"./val"),t(function(n){var o=t("./type"),e=t("./val"),i={_:"_"};i.soul=function(t,n){return t&&t._&&t._[n||p]},i.soul.ify=function(t,n){return n="string"==typeof n?{soul:n}:n||{},t=t||{},t._=t._||{},t._[p]=n.soul||t._[p]||l(),t},i.soul._=e.link._,function(){function t(t,n){return n!==i._?e.is(t)?void(this.cb&&this.cb.call(this.as,t,n,this.n,this.s)):!0:void 0}i.is=function(n,o,e){var r;return u(n)&&(r=i.soul(n))?!f(n,t,{as:e,cb:o,s:r,n:n}):!1}}(),function(){function t(t,n){var o,i,r=this.o;return r.map?(o=r.map.call(this.as,t,""+n,r.node),void(i===o?s(r.node,n):r.node&&(r.node[n]=o))):void(e.is(t)&&(r.node[n]=t))}i.ify=function(n,o,e){return o?"string"==typeof o?o={soul:o}:o instanceof Function&&(o={map:o}):o={},o.map&&(o.node=o.map.call(e,n,r,o.node||{})),(o.node=i.soul.ify(o.node||{},o))&&f(n,t,{o:o,as:e}),o.node}}();var r,a=o.obj,u=a.is,s=a.del,f=a.map,c=o.text,l=c.random,p=i.soul._;n.exports=i})(t,"./node"),t(function(n){function o(){var t;return t=r(),t>a?(u=0,a=t+o.drift):a=t+(u+=1)/s+o.drift}{var e=t("./type"),i=t("./node"),r=e.time.is,a=-(1/0),u=0,s=1e3,f="undefined"!=typeof performance?performance.timing&&performance:!1;f&&f.timing&&f.timing.navigationStart||(f=!1)}o._=">",o.drift=0,o.is=function(t,n,e){var i=n&&t&&t[x]&&t[x][o._]||e;if(i)return b(i=i[n])?i:-(1/0)},o.lex=function(){return o().toString(36).replace(".","")},o.ify=function(t,n,e,r,a){if(!t||!t[x]){if(!a)return;t=i.soul.ify(t,a)}var u=p(t[x],o._);return c!==n&&n!==x&&(b(e)&&(u[n]=e),c!==r&&(t[n]=r)),t},o.to=function(t,n,e){var r=t[n];return h(r)&&(r=g(r)),o.ify(e,n,o.is(t,n),r,i.soul(t))},function(){function t(t,n){x!==n&&o.ify(this.o,n,this.s)}o.map=function(n,e,i){var r,a=h(a=n||e)?a:null;return n=y(n=n||e)?n:null,a&&!n?(e=b(e)?e:o(),a[x]=a[x]||{},v(a,t,{o:a,s:e}),a):(i=i||h(e)?e:r,e=b(e)?e:o(),function(o,a,u,s){return n?(n.call(i||this||{},o,a,u,s),void(d(u,a)&&r===u[a]||t.call({o:u,s:e},o,a))):(t.call({o:u,s:e},o,a),o)})}}();var c,l=e.obj,p=l.as,d=l.has,h=l.is,v=l.map,g=l.copy,m=e.num,b=m.is,k=e.fn,y=k.is,x=i._;n.exports=o})(t,"./state"),t(function(n){var o=t("./type"),e=t("./val"),i=t("./node"),r={};!function(){function t(t,o){return t&&o===i.soul(t)&&i.is(t,this.fn,this.as)?void(this.cb&&(n.n=t,n.as=this.as,this.cb.call(n.as,t,o,n))):!0}function n(t){t&&i.is(n.n,t,n.as)}r.is=function(n,o,e,i){return n&&s(n)&&!l(n)?!d(n,t,{cb:o,fn:e,as:i}):!1}}(),function(){function t(t,o){var r;return(r=p(t,o))?r:(o.env=t,o.soul=u,i.ify(o.obj,n,o)&&(o.rel=o.rel||e.rel.ify(i.soul(o.node)),o.obj!==t.shell&&(t.graph[e.rel.is(o.rel)]=o.node)),o)}function n(n,o,r){var u,s,p=this,d=p.env;if(i._===o&&c(n,e.rel._))return r._;if(u=l(n,o,r,p,d)){if(o||(p.node=p.node||r||{},c(n,i._)&&i.soul(n)&&(p.node._=h(n._)),p.node=i.soul.ify(p.node,e.rel.is(p.rel)),p.rel=p.rel||e.rel.ify(i.soul(p.node))),(s=d.map)&&(s.call(d.as||{},n,o,r,p),c(r,o))){if(n=r[o],a===n)return void f(r,o);if(!(u=l(n,o,r,p,d)))return}if(!o)return p.node;if(!0===u)return n;if(s=t(d,{obj:n,path:p.path.concat(o)}),s.node)return s.rel}}function u(t){var n=this,o=e.link.is(n.rel),r=n.env.graph;n.rel=n.rel||e.rel.ify(t),n.rel[e.rel._]=t,n.node&&n.node[i._]&&(n.node[i._][e.rel._]=t),c(r,o)&&(r[t]=r[o],f(r,o))}function l(t,n,i,r,a){var u;return e.is(t)?!0:s(t)?1:(u=a.invalid)?(t=u.call(a.as||{},t,n,i),l(t,n,i,r,a)):(a.err="Invalid value at '"+r.path.concat(n).join(".")+"'!",void(o.list.is(t)&&(a.err+=" Use `.set(item)` instead of an Array.")))}function p(t,n){for(var o,e=t.seen,i=e.length;i--;)if(o=e[i],n.obj===o.obj)return o;e.push(n)}r.ify=function(n,o,i){var r={path:[],obj:n};return o?"string"==typeof o?o={soul:o}:o instanceof Function&&(o.map=o):o={},o.soul&&(r.rel=e.rel.ify(o.soul)),o.shell=(i||{}).shell,o.graph=o.graph||{},o.seen=o.seen||[],o.as=o.as||i,t(o,r),o.root=r.node,o.graph}}(),r.node=function(t){var n=i.soul(t);if(n)return p({},n,t)},function(){function t(t,n){var o,a;if(i._===n){if(l(t,e.rel._))return;return void(this.obj[n]=h(t))}return(o=e.rel.is(t))?(a=this.opt.seen[o])?void(this.obj[n]=a):void(this.obj[n]=this.opt.seen[o]=r.to(this.graph,o,this.opt)):void(this.obj[n]=t)}r.to=function(n,o,e){if(n){var i={};return e=e||{seen:{}},d(n[o],t,{obj:i,graph:n,opt:e}),i}}}();var a,u=(o.fn.is,o.obj),s=u.is,f=u.del,c=u.has,l=u.empty,p=u.put,d=u.map,h=u.copy;n.exports=r})(t,"./graph"),t(function(n){t("./onto"),n.exports=function(t,n){if(this.on){if(!(t instanceof Function)){if(!t||!n)return;var o=t["#"]||t,e=(this.tag||empty)[o];if(!e)return;return e=this.on(o,n),clearTimeout(e.err),!0}var o=n&&n["#"]||Math.random().toString(36).slice(2);if(!t)return o;var i=this.on(o,t,n);return i.err=i.err||setTimeout(function(){i.next({err:"Error: No ACK received yet.",lack:!0}),i.off()},(this.opt||{}).lack||9e3),o}}})(t,"./ask"),t(function(n){function o(t){var n={s:{}};return t=t||{max:1e3,age:9e3},n.check=function(t){var o;return(o=n.s[t])?o.pass?o.pass=!1:n.track(t):!1},n.track=function(o,r){var a=n.s[o]||(n.s[o]={});return a.was=i(),r&&(a.pass=!0),n.to||(n.to=setTimeout(function(){var o=i();e.obj.map(n.s,function(i,r){i&&t.age>o-i.was||e.obj.del(n.s,r)}),n.to=null},t.age+9)),a},n}var e=t("./type"),i=e.time.is;n.exports=o})(t,"./dup"),t(function(n){function i(t){return t instanceof i?(this._={gun:this,$:this}).$:this instanceof i?i.create(this._={gun:this,$:this,opt:t}):new i(t)}i.is=function(t){return t instanceof i||t&&t._&&t===t._.$||!1},i.version=.9,i.chain=i.prototype,i.chain.toJSON=function(){};var r=t("./type");r.obj.to(r,i),i.HAM=t("./HAM"),i.val=t("./val"),i.node=t("./node"),i.state=t("./state"),i.graph=t("./graph"),i.on=t("./onto"),i.ask=t("./ask"),i.dup=t("./dup"),function(){function t(n){var o,e,r=this,u=r.as,s=u.at||u,f=s.$;return(e=n["#"])||(e=n["#"]=c(9)),(o=s.dup).check(e)?void(u.out===n.out&&(n.out=a,r.to.next(n))):(o.track(e),s.ask(n["@"],n)||(n.get&&i.on.get(n,f),n.put&&i.on.put(n,f)),r.to.next(n),void(u.out||(n.out=t,s.on("out",n))))}i.create=function(n){n.root=n.root||n,n.graph=n.graph||{},n.on=n.on||i.on,n.ask=n.ask||i.ask,n.dup=n.dup||i.dup();var o=n.$.opt(n.opt);return n.once||(n.on("in",t,n),n.on("out",t,{at:n,out:t}),i.on("create",n),n.on("create",n)),n.once=1,o}}(),function(){function t(t,n,o,e){var r=this,a=i.state.is(o,n);if(!a)return r.err="Error: No state on '"+n+"' in node '"+e+"'!";var u=r.graph[e]||k,s=i.state.is(u,n,!0),f=u[n],c=i.HAM(r.machine,a,s,t,f);return c.incoming?(r.put[e]=i.state.to(o,n,r.put[e]),(r.diff||(r.diff={}))[e]=i.state.to(o,n,r.diff[e]),void(r.souls[e]=!0)):void(c.defer&&(r.defer=a<(r.defer||1/0)?a:r.defer))}function n(t,n){var i=this,a=i.$._,u=(a.next||k)[n];if(!u){if(!(a.opt||k)["super"])return void(i.souls[n]=!1);u=i.$.get(n)._}var s=i.map[n]={put:t,get:n,$:u.$},f={ctx:i,msg:s};i.async=!!a.tag.node,i.ack&&(s["@"]=i.ack),v(t,o,f),i.async&&(i.and||a.on("node",function(t){this.to.next(t),t===i.map[t.get]&&(i.souls[t.get]=!1,v(t.put,e,t),v(i.souls,function(t){return t?t:void 0})||i.c||(i.c=1,this.off(),v(i.map,r,i)))}),i.and=!0,a.on("node",s))}function o(t,n){var o=this.ctx,e=o.graph,r=this.msg,a=r.get,u=r.put,s=r.$._;e[a]=i.state.to(u,n,e[a]),o.async||(s.put=i.state.to(u,n,s.put))}function e(t,n){var o=this,e=o.put,r=o.$._;r.put=i.state.to(e,n,r.put)}function r(t){t.$&&(this.cat.stop=this.stop,t.$._.on("in",t),this.cat.stop=null)}i.on.put=function(o,e){var u=e._,s={$:e,graph:u.graph,put:{},map:{},souls:{},machine:i.state(),ack:o["@"],cat:u,stop:{}};return i.graph.is(o.put,null,t,s)||(s.err="Error: Invalid graph!"),s.err?u.on("in",{"@":o["#"],err:i.log(s.err)}):(v(s.put,n,s),s.async||v(s.map,r,s),a!==s.defer&&setTimeout(function(){i.on.put(o,e)},s.defer-s.machine),void(s.diff&&u.on("put",h(o,{put:s.diff}))))},i.on.get=function(t,n){var o,e=n._,r=t.get,a=r[m],u=e.graph[a],s=r[b],f=e.next||(e.next={}),c=f[a];if(d(a,"*")){var l={};i.obj.map(e.graph,function(t,n){i.text.match(n,a)&&(l[n]=i.obj.copy(t))}),i.obj.empty(l)||e.on("in",{"@":t["#"],how:"*",put:l,$:n})}if(!u)return e.on("get",t);if(s){if(!d(u,s))return e.on("get",t);u=i.state.to(u,s)}else u=i.obj.copy(u);u=i.graph.node(u),o=(c||k).ack,e.on("in",{"@":t["#"],how:"mem",put:u,$:n}),e.on("get",t)}}(),function(){i.chain.opt=function(t){t=t||{};var n=this,o=n._,e=t.peers||t;return p(t)||(t={}),p(o.opt)||(o.opt=t),f(e)&&(e=[e]),u(e)&&(e=v(e,function(t,n,o){o(t,{url:t})}),p(o.opt.peers)||(o.opt.peers={}),o.opt.peers=h(e,o.opt.peers)),o.opt.peers=o.opt.peers||{},h(t,o.opt),i.on("opt",o),o.opt.uuid=o.opt.uuid||function(){return g()+c(12)},n}}();var a,u=i.list.is,s=i.text,f=s.is,c=s.random,l=i.obj,p=l.is,d=l.has,h=l.to,v=l.map,g=(l.copy,i.state.lex),m=i.val.rel._,b=".",k=(i.node._,i.val.link.is,{});o.debug=function(t,n){return o.debug.i&&t===o.debug.i&&o.debug.i++&&(o.log.apply(o,arguments)||n)},i.log=function(){return!i.log.off&&o.log.apply(o,arguments),[].slice.call(arguments).join(" ")},i.log.once=function(t,n,o){return(o=i.log.once)[t]=o[t]||0,o[t]++||i.log(n)},i.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'!"),"undefined"!=typeof window&&((window.Gun=i).window=window);try{"undefined"!=typeof e&&(e.exports=i)}catch(y){}n.exports=i})(t,"./root"),t(function(){var n=t("./root");n.chain.back=function(t,i){var r;if(t=t||1,-1===t||1/0===t)return this._.root.$;if(1===t)return(this._.back||this._).$;var a=this,u=a._;if("string"==typeof t&&(t=t.split(".")),!(t instanceof Array)){if(t instanceof Function){for(var s,r={back:u};(r=r.back)&&o===(s=t(r,i)););return s}return n.num.is(t)?(u.back||u).$.back(t-1):this}var f=0,c=t.length,r=u;for(f;c>f;f++)r=(r||e)[t[f]];return o!==r?i?a:r:(r=u.back)?r.$.back(t,i):void 0};var o,e={}})(t,"./back"),t(function(){function n(t){var n,o,e,i=this.as,r=i.back,a=i.root;if(t.I||(t.I=i.$),t.$||(t.$=i.$),this.to.next(t),o=t.get){if(o["#"]||i.soul){if(o["#"]=o["#"]||i.soul,t["#"]||(t["#"]=b(9)),r=a.$.get(o["#"])._,o=o["."]){if(h(r.put,o)&&(n=r.$.get(o)._,(e=n.ack)||(n.ack=-1),r.on("in",{$:r.$,put:c.state.to(r.put,o),get:r.get}),e))return}else{if(e=r.ack,e||(r.ack=-1),h(r,"put")&&r.on("in",r),e)return;t.$=r.$}return a.ask(f,t),a.on("in",t)}if(a.now&&(a.now[i.id]=a.now[i.id]||!0,i.pass={}),o["."])return i.get?(t={get:{".":i.get},$:i.$},r.ask||(r.ask={}),r.ask[i.get]=t.$._,r.on("out",t)):(t={get:{},$:i.$},r.on("out",t));if(i.ack=i.ack||-1,i.get)return t.$=i.$,o["."]=i.get,(r.ask||(r.ask={}))[i.get]=t.$._,r.on("out",t)}return r.on("out",t)}function o(t){var n,o,r=this,s=r.as,f=s.root,d=t.$,b=(d||p)._||p,k=t.put;if(s.get&&t.get!==s.get&&(t=g(t,{get:s.get})),s.has&&b!==s&&(t=g(t,{$:s.$}),b.ack&&(s.ack=b.ack)),l===k){if(o=b.put,r.to.next(t),s.soul)return;if(l===o&&l!==b.put)return;return i(s,t,r),s.has&&u(s,t),v(b.echo,s.id),void v(s.map,b.id)}if(s.soul)return r.to.next(t),i(s,t,r),void(s.next&&m(k,a,{msg:t,cat:s}));if(!(n=c.val.link.is(k)))return c.val.is(k)?(s.has||s.soul?u(s,t):(b.has||b.soul)&&((b.echo||(b.echo={}))[s.id]=b.echo[b.id]||s,(s.map||(s.map={}))[b.id]=s.map[b.id]||{at:b}),r.to.next(t),void i(s,t,r)):(s.has&&b!==s&&h(b,"put")&&(s.put=b.put),(n=c.node.soul(k))&&b.has&&(b.put=s.root.$.get(n)._.put),o=(f.stop||{})[b.id],r.to.next(t),e(s,t,b,n),i(s,t,r),void(s.next&&m(k,a,{msg:t,cat:s})));f.stop;o=f.stop||{},o=o[b.id]||(o[b.id]={}),o.is=o.is||b.put,o[s.id]=b.put||!0,r.to.next(t),e(s,t,b,n),i(s,t,r)}function e(t,n,o,i){if(i&&k!==t.get){var r=t.root.$.get(i)._;t.has?o=r:o.has&&e(o,n,o,i),o!==t&&(o.$||(o={}),(o.echo||(o.echo={}))[t.id]=o.echo[t.id]||t,t.has&&!(t.map||p)[o.id]&&u(t,n),r=o.id?(t.map||(t.map={}))[o.id]=t.map[o.id]||{at:o}:{},(i!==r.link||r.pass||t.pass)&&(t.pass&&(c.obj.map(t.map,function(t){t.pass=!0}),v(t,"pass")),r.pass&&v(r,"pass"),t.has&&(t.link=i),s(t,r.link=i)))}}function i(t,n){t.echo&&m(t.echo,r,n)}function r(t){t&&t.on&&t.on("in",this)}function a(t,n){var o,e,i,r=this.cat,a=r.next||p,u=this.msg;(k!==n||a[n])&&(e=a[n])&&(e.has?(l!==e.put&&c.val.link.is(t)||(e.put=t),o=e.$):(i=u.$)&&(i=(o=u.$.get(n))._,l!==i.put&&c.val.link.is(t)||(i.put=t)),e.on("in",{put:t,get:n,$:o,via:u}))}function u(t,n){if(t.has||t.soul){{var o=t.map;t.root}t.map=null,t.has&&(t.link=null),(t.pass||n["@"]||null!==o)&&(l===o&&c.val.link.is(t.put)||(m(o,function(n){(n=n.at)&&v(n.echo,t.id)}),o=t.put,m(t.next,function(n,e){return l===o&&l!==t.put?!0:(n.put=l,n.ack&&(n.ack=-1),void n.on("in",{get:e,$:n.$,put:l}))})))}}function s(t,n){var o=t.root.$.get(n)._;(!t.ack||(o.on("out",{get:{"#":n}}),t.ask))&&(o=t.ask,c.obj.del(t,"ask"),m(o||t.next,function(t,o){t.on("out",{get:{"#":n,".":o}})}),c.obj.del(t,"ask"))}function f(t){var n=this.as,o=n.get||p,e=n.$._,i=(t.put||p)[o["#"]];if(e.ack&&(e.ack=e.ack+1||1),!t.put||o["."]&&!h(i,e.get)){if(e.put!==l)return;return void e.on("in",{get:e.get,put:e.put=l,$:e.$,"@":t["@"]})}return k==o["."]?void e.on("in",{get:e.get,put:c.val.link.ify(o["#"]),$:e.$,"@":t["@"]}):(t.$=e.root.$,void c.on.put(t,e.root.$))}var c=t("./root");c.chain.chain=function(t){var e,i=this,r=i._,a=new(t||i).constructor(i),u=a._;return u.root=e=r.root,u.id=++e.once,u.back=i._,u.on=c.on,u.on("in",o,u),u.on("out",n,u),a};var l,p={},d=c.obj,h=d.has,v=(d.put,d.del),g=d.to,m=d.map,b=c.text.random,k=(c.val.rel._,c.node._)})(t,"./chain"),t(function(){function n(t,n){var o=n._,e=o.next,i=n.chain(),r=i._;return e||(e=o.next={}),e[r.get=t]=r,n===o.root.$?r.soul=t:(o.soul||o.has)&&(r.has=t),r}function o(t,n,o,e){var i,r=t._;return(i=r.soul)?(n(i,e,r),t):(i=r.link)?(n(i,e,r),t):(t.get(function(t,o){o.rid(t);var r=(r=t.$)&&r._||{};i=r.link||r.soul||c.is(t.put)||l(t.put),n(i,e,t,o)},{out:{get:{".":!0}}}),t)}function e(t){var n,o=this,e=o.as,i=e.at,r=i.root,u=t.$,f=(u||{})._||{},l=t.put||f.put;if((n=r.now)&&o!==n[e.now])return o.to.next(t);if(o.seen&&f.id&&o.seen[f.id])return o.to.next(t);if((n=l)&&n[c._]&&(n=c.is(n))&&(n=(t.$$=f.root.gun.get(n))._,a!==n.put&&(t=s(t,{put:l=n.put}))),(n=r.mum)&&f.id){if(n[f.id])return;a===l||c.is(l)||(n[f.id]=!0)}return e.use(t,o),o.stun?void(o.stun=null):void o.to.next(t)}function i(t){var n=this.on;if(!t||n.soul||n.has)return this.off();if(t=(t=(t=t.$||t)._||t).id){{var o,e;n.map}return(o=(e=this.seen||(this.seen={}))[t])?!0:void(e[t]=!0)}}var r=t("./root");r.chain.get=function(t,a,u){var s,l;if("string"!=typeof t){if(t instanceof Function){if(!0===a)return o(this,t,a,u);s=this;var d,h=s._,v=h.root,l=v.now;u=a||{},u.at=h,u.use=t,u.out=u.out||{},u.out.get=u.out.get||{},(d=h.on("in",e,u)).rid=i,(v.now={$:1})[u.now=h.id]=d;var g=v.mum;return v.mum={},h.on("out",u.out),v.mum=g,v.now=l,s}return f(t)?this.get(""+t,a,u):(l=c.is(t))?this.get(l,a,u):((u=this.chain())._.err={err:r.log("Invalid get request!",t)},a&&a.call(u,u._.err),u)}var m=this,b=m._,k=b.next||p;return(s=k[t])||(s=n(t,m)),s=s.$,(l=b.stun)&&(s._.stun=s._.stun||l),a&&a instanceof Function&&s.get(a,u),s};var a,u=r.obj,s=(u.has,r.obj.to),f=r.num.is,c=r.val.link,l=r.node.soul,p=(r.node._,{})})(t,"./get"),t(function(){function n(t){t.batch=i;var n=t.opt||{},o=t.env=c.state.map(a,n.state);return o.soul=t.soul,t.graph=c.graph.ify(t.data,o,t),o.err?((t.ack||m).call(t,t.out={err:c.log(o.err)}),void(t.res&&t.res())):void t.batch()}function e(t){return void(t&&t())}function i(){var t=this;t.graph&&!v(t.stun,r)&&(t.res=t.res||function(t){t&&t()},t.res(function(){var n=t.$.back(-1)._,o=n.ask(function(o){n.root.on("ack",o),o.err&&c.log(o),o.lack||this.off(),t.ack&&t.ack(o,this)},t.opt),e=n.root.now;p.del(n.root,"now");var i=n.root.mum;n.root.mum={},t.ref._.on("out",{$:t.ref,put:t.out=t.env.graph,opt:t.opt,"#":o}),n.root.mum=i?p.to(i,n.root.mum):i,n.root.now=e},t),t.res&&t.res())}function r(t){return t?!0:void 0}function a(t,n,o,e){var i=this,r=c.is(t);!n&&e.path.length&&(i.res||b)(function(){var n=e.path,o=i.ref,a=(i.opt,0),s=n.length;for(a;s>a;a++)o=o.get(n[a]);r&&(o=t);var f=o._.dub;return f||(f=c.node.soul(e.obj))?(o.back(-1).get(f),void e.soul(f)):((i.stun=i.stun||{})[n]=!0,void o.get(u,!0,{as:{at:e,as:i,p:n}}))},{as:i,at:e})}function u(t,n,o,e){var n=n.as,i=n.at;n=n.as;var r=((o||{}).$||{})._||{};return t=r.dub=r.dub||t||c.node.soul(i.obj)||c.node.soul(o.put||r.put)||c.val.rel.is(o.put||r.put)||(n.via.back("opt.uuid")||c.text.random)(),e&&(e.stun=!0),t?void s(r,r.dub=t,i,n):void r.via.back("opt.uuid")(function(t,o){return t?c.log(t):void s(r,r.dub=r.dub||o,i,n)})}function s(t,n,o,e){t.$.back(-1).get(n),o.soul(n),e.stun[o.path]=!1,e.batch()}function f(t,n,e,i){if(n=n.as,e.$&&e.$._){if(e.err)return void o.log("Please report this as an issue! Put.any.err");var r,a=e.$._,u=a.put,s=n.opt||{};if(!(r=n.ref)||!r._.now){if(i&&(i.stun=!0),n.ref!==n.$){if(r=n.$._.get||a.get,!r)return void o.log("Please report this as an issue! Put.no.get");n.data=h({},r,n.data),r=null}if(l===u){if(!a.get)return;t||(r=a.$.back(function(t){return t.link||t.soul?t.link||t.soul:void(n.data=h({},t.get,n.data))})),r=r||a.get,a=a.root.$.get(r)._,n.soul=r,u=n.data}return n.not||(n.soul=n.soul||t)||(n.path&&d(n.data)?n.soul=(s.uuid||n.via.back("opt.uuid")||c.text.random)():(k==a.get&&(n.soul=(a.put||g)["#"]||a.dub),n.soul=n.soul||a.soul||a.soul||(s.uuid||n.via.back("opt.uuid")||c.text.random)()),n.soul)?void n.ref.put(n.data,n.soul,n):void n.via.back("opt.uuid")(function(t,o){return t?c.log(t):void n.ref.put(n.data,n.soul=o,n)})}}}var c=t("./root");c.chain.put=function(t,o,i){var r,a=this,u=a._,s=u.root.$;return i=i||{},i.data=t,i.via=i.$=i.via||i.$||a,"string"==typeof o?i.soul=o:i.ack=i.ack||o,u.soul&&(i.soul=u.soul),i.soul||s===a?d(i.data)?(i.soul=i.soul||(i.not=c.node.soul(i.data)||(i.via.back("opt.uuid")||c.text.random)()),i.soul?(i.$=a=s.get(i.soul),i.ref=i.$,n(i),a):(i.via.back("opt.uuid")(function(t,n){return t?c.log(t):void(i.ref||i.$).put(i.data,i.soul=n,i)}),a)):((i.ack||m).call(i,i.out={err:c.log("Data saved to the root level of the graph must be a node (an object), not a",typeof i.data,'of "'+i.data+'"!')}),i.res&&i.res(),a):c.is(t)?(t.get(function(t,n,e){return!t&&c.val.is(e.put)?c.log("The reference you are saving is a",typeof e.put,'"'+e.put+'", not a node (object)!'):void a.put(c.val.rel.ify(t),o,i)},!0),a):(i.ref=i.ref||s._===(r=u.back)?a:r.$,i.ref._.soul&&c.val.is(i.data)&&u.get?(i.data=h({},u.get,i.data),i.ref.put(i.data,i.soul,i),a):(i.ref.get(f,!0,{as:i}),i.out||(i.res=i.res||e,i.$._.stun=i.ref._.stun),a))};var l,p=c.obj,d=p.is,h=p.put,v=p.map,g={},m=function(){},b=function(t,n){t.call(n||g)},k=c.node._})(t,"./put"),t(function(n){var o=t("./root");t("./chain"),t("./back"),t("./put"),t("./get"),n.exports=o})(t,"./index"),t(function(){function n(t,n){{var o,e=this,r=t.$,a=(r||{})._||{},u=a.put||t.put;e.at}if(i!==u){if(o=t.$$){if(o=t.$$._,i===o.put)return;u=o.put}e.change&&(u=t.put),e.as?e.ok.call(e.as,t,n):e.ok.call(r,u,t.get,t,n)}}function o(t,n,e){var r,u,s=this.as,f=(s.at,t.$),c=f._,l=c.put||t.put;if(u=t.$$){if(r=u=t.$$._,i===u.put)return;l=u.put}return(u=n.wait)&&(u=u[c.id])&&clearTimeout(u),!e&&(i===l||c.soul||c.link||r&&!(0 .once, apologies unexpected."),this.once(t,n)},e.chain.once=function(t,n){var r=this,a=r._,u=a.put;if(0=(n.batch||1e3)?s():void(e||(e=setTimeout(s,n.wait||1)))}),t.on("get",function(o){function e(){if(s&&(i=s["#"])){var e=s["."];r=a[i]||u,r&&e&&(r=Gun.state.to(r,e)),(r||Gun.obj.empty(n.peers))&&t.on("in",{"@":o["#"],put:Gun.graph.node(r),how:"lS",lS:o.I})}}this.to.next(o);var i,r,u,s=o.get;Gun.debug?setTimeout(e,1):e()});var u=function(t,n,o,e){a[e]=Gun.state.to(o,n,a[e])},s=function(u){var f;r=0,clearTimeout(e),e=!1;var c=i;i={},u&&(a=u);try{o.setItem(n.prefix,JSON.stringify(a))}catch(l){Gun.log(f=(l||"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 ."),t.on("localStorage:error",{err:f,file:n.prefix,flush:a,retry:s})}(f||Gun.obj.empty(n.peers))&&Gun.obj.map(c,function(n,o){t.on("in",{"@":o,err:f,ok:0})})}}})}})(t,"./adapters/localStorage"),t(function(n){function e(t){var n=function(){},u=t.opt||{};return u.log=u.log||o.log,u.gap=u.gap||u.wait||1,u.pack=u.pack||.3*(u.memory?1e3*u.memory*1e3:1399e6),n.out=function(o){var e;return this.to&&this.to.next(o),(e=o["@"])&&(e=t.dup.s[e])&&(e=e.it)&&e.mesh?(n.say(o,e.mesh.via,1),void(e["##"]=o["##"])):void n.say(o)},t.on("create",function(o){o.opt.pid=o.opt.pid||i.text.random(9),this.to.next(o),t.on("out",n.out)}),n.hear=function(o,e){if(o){var r,a,s,f=t.dup,c=o[0];if(u.pack<=o.length)return n.say({dam:"!",err:"Message too big!"},e);try{s=JSON.parse(o)}catch(l){u.log("DAM JSON parse error",l)}if("{"===c){if(!s)return;if(f.check(r=s["#"]))return;if(f.track(r,!0).it=s,(c=s["@"])&&s.put&&(a=s["##"]||(s["##"]=n.hash(s)),(c+=a)!=r)){if(f.check(c))return;(c=f.s)[a]=c[r]}return(s.mesh=function(){}).via=e,(c=s["><"])&&(s.mesh.to=i.obj.map(c.split(","),function(t,n,o){o(t,!0)})),s.dam?void((c=n.hear[s.dam])&&c(s,e,t)):void t.on("in",s)}if("["!==c);else{if(!s)return;for(var p,d=0;p=s[d++];)n.hear(p,e)}}},function(){function o(t){var n=t.batch;if(n&&(t.batch=t.tail=null,n.length))try{e(JSON.stringify(n),t)}catch(o){u.log("DAM JSON stringify error",o)}}function e(t,n){var o=n.wire;try{o.send?o.send(t):n.say&&n.say(t)}catch(e){(n.queue=n.queue||[]).push(t)}}n.say=function(r,s,f){if(!s)return void i.obj.map(u.peers,function(t){n.say(r,t)});var c,l,p,d=s.wire||u.wire&&u.wire(s);if(d&&(l=r.mesh||a,s!==l.via&&((p=l.raw)||(p=n.raw(r)),!((c=r["@"])&&(c=t.dup.s[c])&&(c=c.it)&&c.get&&c["##"]&&c["##"]===r["##"]||(c=l.to)&&(c[s.url]||c[s.id])&&!f)))){if(s.batch){if(s.tail=(s.tail||0)+p.length,s.tail<=u.pack)return void s.batch.push(p);o(s)}s.batch=[],setTimeout(function(){o(s)},u.gap),e(p,s)}}}(),function(){function o(t,n){var o;return n instanceof Object?(i.obj.map(Object.keys(n).sort(),a,{to:o={},on:n}),o):n}function a(t){this.to[t]=this.on[t]}n.raw=function(e){if(!e)return"";var a,c,l,p=t.dup,d=e.mesh||{};if(l=d.raw)return l;if("string"==typeof e)return e;e["@"]&&(l=e.put)&&((c=e["##"])||(a=s(l,o)||"",c=n.hash(e,a),e["##"]=c),(l=p.s)[c=e["@"]+c]=l[e["#"]],e["#"]=c||e["#"],a&&((e=i.obj.to(e)).put=f));var h=0,v=[];i.obj.map(u.peers,function(t){return v.push(t.url||t.id),++h>9?!0:void 0}),e["><"]=v.join();var g=s(e);return r!==a&&(l=g.indexOf(f,g.indexOf("put")),g=g.slice(0,l-1)+a+g.slice(l+f.length+1)),d&&(d.raw=g),g},n.hash=function(t,n){return e.hash(n||s(t.put,o)||"")||t["#"]||i.text.random(9)};var s=JSON.stringify,f=":])([:"}(),n.hi=function(o){var e=o.wire||{};o.id||o.url?(u.peers[o.url||o.id]=o,i.obj.del(u.peers,e.id)):(e=e.id=e.id||i.text.random(9),n.say({dam:"?"},u.peers[e]=o)),e.hied||t.on(e.hied="hi",o),e=o.queue,o.queue=[],i.obj.map(e,function(t){n.say(t,o)})},n.bye=function(n){i.obj.del(u.peers,n.id),t.on("bye",n)},n.hear["!"]=function(t){u.log("Error:",t.err)},n.hear["?"]=function(t,o){return t.pid?(o.id=o.id||t.pid,void n.hi(o)):n.say({dam:"?",pid:u.pid,"@":t["#"]},o)},n}var i=t("../type");e.hash=function(t){if("string"!=typeof t)return{err:1};var n=0;if(!t.length)return n;for(var o,e=0,i=t.length;i>e;++e)o=t.charCodeAt(e), +n=(n<<5)-n+o,n|=0;return n};var r,a={};Object.keys=Object.keys||function(t){return map(t,function(t,n,o){o(n)})};try{n.exports=e}catch(u){}})(t,"./adapters/mesh"),t(function(){var n=t("../index");n.Mesh=t("./mesh"),n.on("opt",function(t){function o(t){try{if(!t||!t.url)return o&&o(t);var n=t.url.replace("http","ws"),o=t.wire=new i.WebSocket(n);return o.onclose=function(){i.mesh.bye(t),e(t)},o.onerror=function(n){e(t),n&&"ECONNREFUSED"===n.code},o.onopen=function(){i.mesh.hi(t)},o.onmessage=function(n){n&&i.mesh.hear(n.data||n,t)},o}catch(r){}}function e(t){clearTimeout(t.defer),t.defer=setTimeout(function(){o(t)},2e3)}this.to.next(t);var i=t.opt;if(!t.once&&!1!==i.WebSocket){var r;"undefined"!=typeof window&&(r=window),"undefined"!=typeof global&&(r=global),r=r||{};var a=i.WebSocket||r.WebSocket||r.webkitWebSocket||r.mozWebSocket;if(a){i.WebSocket=a;{i.mesh=i.mesh||n.Mesh(t),i.wire}i.wire=o}}})})(t,"./adapters/websocket")}(); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index bbfbd78f..8f356c89 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gun", - "version": "0.9.99994", + "version": "0.9.99995", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 3d9b4e28..0dcca8db 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gun", - "version": "0.9.99994", + "version": "0.9.99995", "description": "A realtime, decentralized, offline-first, graph data synchronization engine.", "main": "index.js", "browser": "gun.min.js", diff --git a/src/adapters/localStorage.js b/src/adapters/localStorage.js index 932d7164..33361ab5 100644 --- a/src/adapters/localStorage.js +++ b/src/adapters/localStorage.js @@ -129,7 +129,7 @@ Gun.on('create', function(root){ if(data){ disk = data } try{store.setItem(opt.prefix, JSON.stringify(disk)); }catch(e){ - Gun.log(err = e || "localStorage failure"); + Gun.log(err = (e || "localStorage failure") + " Consider using GUN's IndexedDB plugin for RAD for more storage space, temporary example at https://github.com/amark/gun/blob/master/test/tmp/indexedDB.html ."); root.on('localStorage:error', {err: err, file: opt.prefix, flush: disk, retry: flush}); } if(!err && !Gun.obj.empty(opt.peers)){ return } // only ack if there are no peers. diff --git a/src/adapters/mesh.js b/src/adapters/mesh.js index d3a77929..6c99a6e8 100644 --- a/src/adapters/mesh.js +++ b/src/adapters/mesh.js @@ -3,7 +3,10 @@ var Type = require('../type'); function Mesh(ctx){ var mesh = function(){}; - var opt = ctx.opt; + var opt = ctx.opt || {}; + opt.log = opt.log || console.log; + opt.gap = opt.gap || opt.wait || 1; + opt.pack = opt.pack || (opt.memory? (opt.memory * 1000 * 1000) : 1399000000) * 0.3; // max_old_space_size defaults to 1400 MB. mesh.out = function(msg){ var tmp; if(this.to){ this.to.next(msg) } @@ -29,8 +32,9 @@ function Mesh(ctx){ mesh.hear = function(raw, peer){ if(!raw){ return } var dup = ctx.dup, id, hash, msg, tmp = raw[0]; + if(opt.pack <= raw.length){ return mesh.say({dam: '!', err: "Message too big!"}, peer) } try{msg = JSON.parse(raw); - }catch(e){console.log('DAM JSON parse error', e)} + }catch(e){opt.log('DAM JSON parse error', e)} if('{' === tmp){ if(!msg){ return } if(dup.check(id = msg['#'])){ return } @@ -94,20 +98,25 @@ function Mesh(ctx){ } if((tmp = msh.to) && (tmp[peer.url] || tmp[peer.id]) && !o){ return } // TODO: still needs to be tested if(peer.batch){ - peer.batch.push(raw); - return; + peer.tail = (peer.tail || 0) + raw.length; + if(peer.tail <= opt.pack){ + peer.batch.push(raw); + return; + } + flush(peer); } peer.batch = []; - setTimeout(function(){ - var tmp = peer.batch; - if(!tmp){ return } - peer.batch = null; - if(!tmp.length){ return } - send(JSON.stringify(tmp), peer); - }, opt.gap || opt.wait || 1); + setTimeout(function(){flush(peer)}, opt.gap); send(raw, peer); } - + function flush(peer){ + var tmp = peer.batch; + if(!tmp){ return } + peer.batch = peer.tail = null; + if(!tmp.length){ return } + try{send(JSON.stringify(tmp), peer); + }catch(e){opt.log('DAM JSON stringify error', e)} + } function send(raw, peer){ var wire = peer.wire; try{ @@ -193,6 +202,7 @@ function Mesh(ctx){ ctx.on('bye', peer); } + mesh.hear['!'] = function(msg, peer){ opt.log('Error:', msg.err) } mesh.hear['?'] = function(msg, peer){ if(!msg.pid){ return mesh.say({dam: '?', pid: opt.pid, '@': msg['#']}, peer) } peer.id = peer.id || msg.pid; diff --git a/src/dup.js b/src/dup.js index 0d99434b..040089ac 100644 --- a/src/dup.js +++ b/src/dup.js @@ -16,7 +16,7 @@ function Dup(opt){ dup.to = setTimeout(function(){ var now = time_is(); Type.obj.map(dup.s, function(it, id){ - if(opt.age > (now - it.was)){ return } + if(it && opt.age > (now - it.was)){ return } Type.obj.del(dup.s, id); }); dup.to = null; From e59bece17ed06041db5d43e3ccac577051231eca Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Tue, 25 Sep 2018 16:00:57 -0700 Subject: [PATCH 035/103] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f019fd71..502cd122 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,7 @@ On that note, let's get some official shout outs covered first:

      Thanks to:
      Lorenzo Mangani, +NLnet Foundation, Sam Liu, Daniel Dombrowsky, Vincent Woo, From 5b442293a8fb86efd62da7a0fba538769145cbfb Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Tue, 25 Sep 2018 16:06:51 -0700 Subject: [PATCH 036/103] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 502cd122..febd3ffb 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,7 @@ On that note, let's get some official shout outs covered first:

      Thanks to:
      +Robert Heessels, Lorenzo Mangani, NLnet Foundation, Sam Liu, From 5cfa492d7dfead3b6425472e85cce50bc6976601 Mon Sep 17 00:00:00 2001 From: masterex1000 Date: Wed, 26 Sep 2018 17:32:22 -0600 Subject: [PATCH 037/103] Add les.js (Memory Evict) --- lib/les.js | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 lib/les.js diff --git a/lib/les.js b/lib/les.js new file mode 100644 index 00000000..e6f3cb16 --- /dev/null +++ b/lib/les.js @@ -0,0 +1,121 @@ +; +(function() { + + // _ _____ ____ _ + // | | | ____/ ___| (_)___ + // | | | _| \___ \ | / __| + // | |___| |___ ___) | | \__ \ + // |_____|_____|____(_)/ |___/ + // ---------------------------- + // LES.js (Last rEcently uSed) + // ---------------------------- + // A Small, lightweight, queue-based + // Garbage Collector for Gun + // Originally By: Collin Conrad (@masterex1000) + + //NOTE: set to false is running from file in YOUR code + var USELOCALGUN = true; + + //NOTE: adds some debug messages + var DEBUG = false; + + + var Gun = (typeof window !== "undefined") ? window.Gun : (USELOCALGUN ? require('../gun') : require("gun")); + var ev = {}; + var empty = {}; + + Gun.on('opt', function(root) { + this.to.next(root); + if (root.once) + return; + if (typeof process == 'undefined') + return + var mem = process.memoryUsage; + + if (!mem) //exit because we are in the browser + return; + + //Figure out the most amount of memory we can use. TODO: make configurable? + ev.max = parseFloat(root.opt.memory || process.env.WEB_MEMORY || 512) * 0.8; + + var nodes = {}; //checks if the node already exists + var nodesArray = []; //used to easily sort everything and store info about the nodes + var memoryUpdate = 0; // last time we printed the current memory stats + + var check = function() { + ev.used = mem().rss / 1024 / 1024; //Contains the amt. of used ram in MB + setTimeout(function() { // So we can handle requests etc. before we start collecting + GC(ev.used / ev.max); // Calculate the memory ratio, and execute the garbage collector + }, 1); + } + + setInterval(check, 1000); // set the garbage collector to run every second, TODO: make configurable + + //Executed every time a node gets modifyed + root.on("put", function(e) { + var ctime = Date.now(); + var souls = Object.keys(e.put || empty); + for (var i = 0; i < souls.length; i++) { + enqueueNode(souls[i], ctime); + } + }); + + //Adds a soul the garbage collectors "freeing" queue + function enqueueNode(soul, ctime) { + if (nodes[soul] == true) { //The node already exists in the queue + var index = nodesArray.findIndex(function(e) { + return e[0] === soul; + }); + if (index == -1) { + console.err("Something happened and the node '" + soul + "' won't get garbage collection unless the value is updated agian"); + return; + } else { + nodesArray.splice(index, 1); // remove the existing ref. + nodesArray.push([soul, ctime]); // push the new instance + } + } else { + nodesArray.push([soul, ctime]); + nodes[soul] = true; + } + } + + //The main garbage collecting routine + function GC(memRatio) { + var curTime = Date.now(); // get the current time + + if (curTime - memoryUpdate >= 5000) { + console.log("|GC| %s | Current Memory Ratio: %d | Current Ram Usage %sMB | Nodes in Memory %s", new Date().toLocaleString(), round(memRatio, 2), round(ev.used, 2), Object.keys(root.graph || empty).length); + memoryUpdate = curTime; + } + + var freed = 0; + + while (nodesArray.length > 0) { + var soul = nodesArray[0][0]; + var nts = nodesArray[0][1]; + if (DEBUG) + console.log("Soul: " + soul + " | Remove Importance: " + calcRemoveImportance(nts, curTime, memRatio) + + " | Memory Ratio: " + memRatio + " | Time Existed: " + (curTime - nts) / 1000); + if (calcRemoveImportance(nodesArray[0][1], curTime, memRatio) >= 100) { + root.gun.get(nodesArray[0][0]).off(); //Remove the node + delete nodes[nodesArray[0][0]]; // remove the lookup value + nodesArray.splice(0, 1); + freed++; + } else + break; + } + if (freed > 0) + console.log("|GC| Removed %s nodes in %s seconds-----------------------------------------------------------------", freed, (Date.now() - curTime) * 0.001); + } + + //Generates a number that, after it hits a threshold, the node gets removed + function calcRemoveImportance(timestamp, ctime, memoryUsageRatio) { + var time = (ctime - timestamp) * 0.001; + return time * 10 * (memoryUsageRatio * memoryUsageRatio) + } + + function round(value, decimals) { //a basic rounding function + return Number(Math.round(value + 'e' + decimals) + 'e-' + decimals); + } + }); +}()); \ No newline at end of file From 4e5b98af4cf902627061f67c589bf7b105743828 Mon Sep 17 00:00:00 2001 From: masterex1000 Date: Wed, 26 Sep 2018 17:47:01 -0600 Subject: [PATCH 038/103] Add les.js (Memory Evict) --- lib/les.js | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 lib/les.js diff --git a/lib/les.js b/lib/les.js new file mode 100644 index 00000000..e6f3cb16 --- /dev/null +++ b/lib/les.js @@ -0,0 +1,121 @@ +; +(function() { + + // _ _____ ____ _ + // | | | ____/ ___| (_)___ + // | | | _| \___ \ | / __| + // | |___| |___ ___) | | \__ \ + // |_____|_____|____(_)/ |___/ + // ---------------------------- + // LES.js (Last rEcently uSed) + // ---------------------------- + // A Small, lightweight, queue-based + // Garbage Collector for Gun + // Originally By: Collin Conrad (@masterex1000) + + //NOTE: set to false is running from file in YOUR code + var USELOCALGUN = true; + + //NOTE: adds some debug messages + var DEBUG = false; + + + var Gun = (typeof window !== "undefined") ? window.Gun : (USELOCALGUN ? require('../gun') : require("gun")); + var ev = {}; + var empty = {}; + + Gun.on('opt', function(root) { + this.to.next(root); + if (root.once) + return; + if (typeof process == 'undefined') + return + var mem = process.memoryUsage; + + if (!mem) //exit because we are in the browser + return; + + //Figure out the most amount of memory we can use. TODO: make configurable? + ev.max = parseFloat(root.opt.memory || process.env.WEB_MEMORY || 512) * 0.8; + + var nodes = {}; //checks if the node already exists + var nodesArray = []; //used to easily sort everything and store info about the nodes + var memoryUpdate = 0; // last time we printed the current memory stats + + var check = function() { + ev.used = mem().rss / 1024 / 1024; //Contains the amt. of used ram in MB + setTimeout(function() { // So we can handle requests etc. before we start collecting + GC(ev.used / ev.max); // Calculate the memory ratio, and execute the garbage collector + }, 1); + } + + setInterval(check, 1000); // set the garbage collector to run every second, TODO: make configurable + + //Executed every time a node gets modifyed + root.on("put", function(e) { + var ctime = Date.now(); + var souls = Object.keys(e.put || empty); + for (var i = 0; i < souls.length; i++) { + enqueueNode(souls[i], ctime); + } + }); + + //Adds a soul the garbage collectors "freeing" queue + function enqueueNode(soul, ctime) { + if (nodes[soul] == true) { //The node already exists in the queue + var index = nodesArray.findIndex(function(e) { + return e[0] === soul; + }); + if (index == -1) { + console.err("Something happened and the node '" + soul + "' won't get garbage collection unless the value is updated agian"); + return; + } else { + nodesArray.splice(index, 1); // remove the existing ref. + nodesArray.push([soul, ctime]); // push the new instance + } + } else { + nodesArray.push([soul, ctime]); + nodes[soul] = true; + } + } + + //The main garbage collecting routine + function GC(memRatio) { + var curTime = Date.now(); // get the current time + + if (curTime - memoryUpdate >= 5000) { + console.log("|GC| %s | Current Memory Ratio: %d | Current Ram Usage %sMB | Nodes in Memory %s", new Date().toLocaleString(), round(memRatio, 2), round(ev.used, 2), Object.keys(root.graph || empty).length); + memoryUpdate = curTime; + } + + var freed = 0; + + while (nodesArray.length > 0) { + var soul = nodesArray[0][0]; + var nts = nodesArray[0][1]; + if (DEBUG) + console.log("Soul: " + soul + " | Remove Importance: " + calcRemoveImportance(nts, curTime, memRatio) + + " | Memory Ratio: " + memRatio + " | Time Existed: " + (curTime - nts) / 1000); + if (calcRemoveImportance(nodesArray[0][1], curTime, memRatio) >= 100) { + root.gun.get(nodesArray[0][0]).off(); //Remove the node + delete nodes[nodesArray[0][0]]; // remove the lookup value + nodesArray.splice(0, 1); + freed++; + } else + break; + } + if (freed > 0) + console.log("|GC| Removed %s nodes in %s seconds-----------------------------------------------------------------", freed, (Date.now() - curTime) * 0.001); + } + + //Generates a number that, after it hits a threshold, the node gets removed + function calcRemoveImportance(timestamp, ctime, memoryUsageRatio) { + var time = (ctime - timestamp) * 0.001; + return time * 10 * (memoryUsageRatio * memoryUsageRatio) + } + + function round(value, decimals) { //a basic rounding function + return Number(Math.round(value + 'e' + decimals) + 'e-' + decimals); + } + }); +}()); \ No newline at end of file From f1ff3b005cf3348aefb51e9b243a45ec2c9c456c Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Thu, 27 Sep 2018 16:30:56 -0700 Subject: [PATCH 039/103] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index febd3ffb..336915e8 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ **GUN** is an _ecosystem_ of tools that let you build tomorrow's dApps, today. -Decentralized alternatives to [Reddit](https://notabug.io/), [YouTube](https://d.tube/), [Wikipedia](https://news.ycombinator.com/item?id=17685682), etc. are already pushing terabytes of daily P2P traffic on GUN. We are a [friendly community](https://gitter.im/amark/gun) creating a free fun future for freedom: +Decentralized alternatives to [Reddit](https://notabug.io/t/whatever/comments/36588a16b9008da4e3f15663c2225e949eca4a15/gpu-bot-test), [YouTube](https://d.tube/), [Wikipedia](https://news.ycombinator.com/item?id=17685682), etc. are already pushing terabytes of daily P2P traffic on GUN. We are a [friendly community](https://gitter.im/amark/gun) creating a free fun future for freedom: From 41482d25cc7f944e83b519cbc2e8848ade577bb7 Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Mon, 1 Oct 2018 15:51:15 -0700 Subject: [PATCH 040/103] SEA User.auth(pair for @mmalmi --- examples/game/space.html | 8 ++++---- gun.js | 4 ++-- package.json | 2 +- sea.js | 31 ++++++++++++++++++++++--------- 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/examples/game/space.html b/examples/game/space.html index f393be5b..cd93483d 100644 --- a/examples/game/space.html +++ b/examples/game/space.html @@ -9,13 +9,13 @@

      - - - + + + - - + + + + + +
      +
      Share
      + + + + + + + + + \ No newline at end of file diff --git a/gun.min.js b/gun.min.js index d30b4be2..d701be45 100644 --- a/gun.min.js +++ b/gun.min.js @@ -1,2 +1,2 @@ -!function(){function t(n,o){function e(t){return t.split("/").slice(-1).toString().replace(".js","")}return o?require(n):n.slice?t[e(n)]:function(o,i){n(o={exports:{}}),t[e(i)]=o.exports}}var n;"undefined"!=typeof window&&(n=window),"undefined"!=typeof global&&(n=global),n=n||{};var o=n.console||{log:function(){}};if("undefined"!=typeof module)var e=module;t(function(t){var n={};n.fn={is:function(t){return!!t&&"function"==typeof t}},n.bi={is:function(t){return t instanceof Boolean||"boolean"==typeof t}},n.num={is:function(t){return!e(t)&&(t-parseFloat(t)+1>=0||1/0===t||-(1/0)===t)}},n.text={is:function(t){return"string"==typeof t}},n.text.ify=function(t){return n.text.is(t)?t:"undefined"!=typeof JSON?JSON.stringify(t):t&&t.toString?t.toString():t},n.text.random=function(t,n){var o="";for(t=t||24,n=n||"0123456789ABCDEFGHIJKLMNOPQRSTUVWXZabcdefghijklmnopqrstuvwxyz";t>0;)o+=n.charAt(Math.floor(Math.random()*n.length)),t--;return o},n.text.match=function(t,o){function e(t,n){for(var o,e=-1,i=0;o=n[i++];)if(!~(e=t.indexOf(o,e+1)))return!1;return!0}var i=!1;if(t=t||"",o=n.text.is(o)?{"=":o}:o||{},n.obj.has(o,"~")&&(t=t.toLowerCase(),o["="]=(o["="]||o["~"]).toLowerCase()),n.obj.has(o,"="))return t===o["="];if(n.obj.has(o,"*")){if(t.slice(0,o["*"].length)!==o["*"])return!1;i=!0,t=t.slice(o["*"].length)}if(n.obj.has(o,"!")){if(t.slice(-o["!"].length)!==o["!"])return!1;i=!0}if(n.obj.has(o,"+")&&n.list.map(n.list.is(o["+"])?o["+"]:[o["+"]],function(n){return t.indexOf(n)>=0?void(i=!0):!0}))return!1;if(n.obj.has(o,"-")&&n.list.map(n.list.is(o["-"])?o["-"]:[o["-"]],function(n){return t.indexOf(n)<0?void(i=!0):!0}))return!1;if(n.obj.has(o,">")){if(!(t>o[">"]))return!1;i=!0}if(n.obj.has(o,"<")){if(!(tn?-1:n>o?1:0):0}},n.list.map=function(t,n,o){return u(t,n,o)},n.list.index=1,n.obj={is:function(t){return t?t instanceof Object&&t.constructor===Object||"Object"===Object.prototype.toString.call(t).match(/^\[object (\w+)\]$/)[1]:!1}},n.obj.put=function(t,n,o){return(t||{})[n]=o,t},n.obj.has=function(t,n){return t&&Object.prototype.hasOwnProperty.call(t,n)},n.obj.del=function(t,n){return t?(t[n]=null,delete t[n],t):void 0},n.obj.as=function(t,n,o,e){return t[n]=t[n]||(e===o?{}:o)},n.obj.ify=function(t){if(r(t))return t;try{t=JSON.parse(t)}catch(n){t={}}return t},function(){function t(t,n){a(this,n)&&o!==this[n]||(this[n]=t)}var o;n.obj.to=function(n,o){return o=o||{},u(n,t,o),o}}(),n.obj.copy=function(t){return t?JSON.parse(JSON.stringify(t)):t},function(){function t(t,n){var o=this.n;if(!o||!(n===o||r(o)&&a(o,n)))return n?!0:void 0}n.obj.empty=function(n,o){return n&&u(n,t,{n:o})?!1:!0}}(),function(){function t(n,o){return 2===arguments.length?(t.r=t.r||{},void(t.r[n]=o)):(t.r=t.r||[],void t.r.push(n))}var i=Object.keys;n.obj.map=function(u,s,f){var c,l,p,d,h,v=0,g=o(s);if(t.r=null,i&&r(u)&&(d=i(u),h=!0),e(u)||d)for(l=(d||u).length;l>v;v++){var m=v+n.list.index;if(g){if(p=h?s.call(f||this,u[d[v]],d[v],t):s.call(f||this,u[v],m,t),p!==c)return p}else if(s===u[h?d[v]:v])return d?d[v]:m}else for(v in u)if(g){if(a(u,v)&&(p=f?s.call(f,u[v],v,t):s(u[v],v,t),p!==c))return p}else if(s===u[v])return v;return g?t.r:n.list.index?0:-1}}(),n.time={},n.time.is=function(t){return t?t instanceof Date:+(new Date).getTime()};var o=n.fn.is,e=n.list.is,i=n.obj,r=i.is,a=i.has,u=i.map;t.exports=n})(t,"./type"),t(function(t){t.exports=function n(t,o,e){if(!t)return{to:n};var i,t=(this.tag||(this.tag={}))[t]||(this.tag[t]={tag:t,to:n._={next:function(t){var n;(n=this.to)&&n.next(t)}}});if(o instanceof Function){var r={off:n.off||(n.off=function(){return this.next===n._.next?!0:(this===this.the.last&&(this.the.last=this.back),this.to.back=this.back,this.next=n._.next,this.back.to=this.to,void(this.the.last===this.the&&delete this.on.tag[this.the.tag]))}),to:n._,next:o,the:t,on:this,as:e};return(r.back=t.last||t).to=r,t.last=r}return(t=t.to)&&i!==o&&t.next(o),t}})(t,"./onto"),t(function(t){function n(t,n,e,i,r){if(n>t)return{defer:!0};if(e>n)return{historical:!0};if(n>e)return{converge:!0,incoming:!0};if(n===e){if(i=o(i)||"",r=o(r)||"",i===r)return{state:!0};if(r>i)return{converge:!0,current:!0};if(i>r)return{converge:!0,incoming:!0}}return{err:"Invalid CRDT Data: "+i+" to "+r+" at "+n+" to "+e+"!"}}if("undefined"==typeof JSON)throw new Error("JSON is not included in this browser. Please load it first: ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js");var o=JSON.stringify;t.exports=n})(t,"./HAM"),t(function(n){var o=t("./type"),e={};e.is=function(t){return t===i?!1:null===t?!0:t===1/0?!1:s(t)||a(t)||u(t)?!0:e.rel.is(t)||!1},e.link=e.rel={_:"#"},function(){function t(t,n){var o=this;return o.id?o.id=!1:n==r&&s(t)?void(o.id=t):o.id=!1}e.rel.is=function(n){if(n&&n[r]&&!n._&&c(n)){var o={};if(p(n,t,o),o.id)return o.id}return!1}}(),e.rel.ify=function(t){return l({},r,t)},o.obj.has._=".";var i,r=e.link._,a=o.bi.is,u=o.num.is,s=o.text.is,f=o.obj,c=f.is,l=f.put,p=f.map;n.exports=e})(t,"./val"),t(function(n){var o=t("./type"),e=t("./val"),i={_:"_"};i.soul=function(t,n){return t&&t._&&t._[n||p]},i.soul.ify=function(t,n){return n="string"==typeof n?{soul:n}:n||{},t=t||{},t._=t._||{},t._[p]=n.soul||t._[p]||l(),t},i.soul._=e.link._,function(){function t(t,n){return n!==i._?e.is(t)?void(this.cb&&this.cb.call(this.as,t,n,this.n,this.s)):!0:void 0}i.is=function(n,o,e){var r;return u(n)&&(r=i.soul(n))?!f(n,t,{as:e,cb:o,s:r,n:n}):!1}}(),function(){function t(t,n){var o,i,r=this.o;return r.map?(o=r.map.call(this.as,t,""+n,r.node),void(i===o?s(r.node,n):r.node&&(r.node[n]=o))):void(e.is(t)&&(r.node[n]=t))}i.ify=function(n,o,e){return o?"string"==typeof o?o={soul:o}:o instanceof Function&&(o={map:o}):o={},o.map&&(o.node=o.map.call(e,n,r,o.node||{})),(o.node=i.soul.ify(o.node||{},o))&&f(n,t,{o:o,as:e}),o.node}}();var r,a=o.obj,u=a.is,s=a.del,f=a.map,c=o.text,l=c.random,p=i.soul._;n.exports=i})(t,"./node"),t(function(n){function o(){var t;return t=r(),t>a?(u=0,a=t+o.drift):a=t+(u+=1)/s+o.drift}{var e=t("./type"),i=t("./node"),r=e.time.is,a=-(1/0),u=0,s=1e3,f="undefined"!=typeof performance?performance.timing&&performance:!1;f&&f.timing&&f.timing.navigationStart||(f=!1)}o._=">",o.drift=0,o.is=function(t,n,e){var i=n&&t&&t[x]&&t[x][o._]||e;if(i)return b(i=i[n])?i:-(1/0)},o.lex=function(){return o().toString(36).replace(".","")},o.ify=function(t,n,e,r,a){if(!t||!t[x]){if(!a)return;t=i.soul.ify(t,a)}var u=p(t[x],o._);return c!==n&&n!==x&&(b(e)&&(u[n]=e),c!==r&&(t[n]=r)),t},o.to=function(t,n,e){var r=t[n];return h(r)&&(r=g(r)),o.ify(e,n,o.is(t,n),r,i.soul(t))},function(){function t(t,n){x!==n&&o.ify(this.o,n,this.s)}o.map=function(n,e,i){var r,a=h(a=n||e)?a:null;return n=y(n=n||e)?n:null,a&&!n?(e=b(e)?e:o(),a[x]=a[x]||{},v(a,t,{o:a,s:e}),a):(i=i||h(e)?e:r,e=b(e)?e:o(),function(o,a,u,s){return n?(n.call(i||this||{},o,a,u,s),void(d(u,a)&&r===u[a]||t.call({o:u,s:e},o,a))):(t.call({o:u,s:e},o,a),o)})}}();var c,l=e.obj,p=l.as,d=l.has,h=l.is,v=l.map,g=l.copy,m=e.num,b=m.is,k=e.fn,y=k.is,x=i._;n.exports=o})(t,"./state"),t(function(n){var o=t("./type"),e=t("./val"),i=t("./node"),r={};!function(){function t(t,o){return t&&o===i.soul(t)&&i.is(t,this.fn,this.as)?void(this.cb&&(n.n=t,n.as=this.as,this.cb.call(n.as,t,o,n))):!0}function n(t){t&&i.is(n.n,t,n.as)}r.is=function(n,o,e,i){return n&&s(n)&&!l(n)?!d(n,t,{cb:o,fn:e,as:i}):!1}}(),function(){function t(t,o){var r;return(r=p(t,o))?r:(o.env=t,o.soul=u,i.ify(o.obj,n,o)&&(o.rel=o.rel||e.rel.ify(i.soul(o.node)),o.obj!==t.shell&&(t.graph[e.rel.is(o.rel)]=o.node)),o)}function n(n,o,r){var u,s,p=this,d=p.env;if(i._===o&&c(n,e.rel._))return r._;if(u=l(n,o,r,p,d)){if(o||(p.node=p.node||r||{},c(n,i._)&&i.soul(n)&&(p.node._=h(n._)),p.node=i.soul.ify(p.node,e.rel.is(p.rel)),p.rel=p.rel||e.rel.ify(i.soul(p.node))),(s=d.map)&&(s.call(d.as||{},n,o,r,p),c(r,o))){if(n=r[o],a===n)return void f(r,o);if(!(u=l(n,o,r,p,d)))return}if(!o)return p.node;if(!0===u)return n;if(s=t(d,{obj:n,path:p.path.concat(o)}),s.node)return s.rel}}function u(t){var n=this,o=e.link.is(n.rel),r=n.env.graph;n.rel=n.rel||e.rel.ify(t),n.rel[e.rel._]=t,n.node&&n.node[i._]&&(n.node[i._][e.rel._]=t),c(r,o)&&(r[t]=r[o],f(r,o))}function l(t,n,i,r,a){var u;return e.is(t)?!0:s(t)?1:(u=a.invalid)?(t=u.call(a.as||{},t,n,i),l(t,n,i,r,a)):(a.err="Invalid value at '"+r.path.concat(n).join(".")+"'!",void(o.list.is(t)&&(a.err+=" Use `.set(item)` instead of an Array.")))}function p(t,n){for(var o,e=t.seen,i=e.length;i--;)if(o=e[i],n.obj===o.obj)return o;e.push(n)}r.ify=function(n,o,i){var r={path:[],obj:n};return o?"string"==typeof o?o={soul:o}:o instanceof Function&&(o.map=o):o={},o.soul&&(r.rel=e.rel.ify(o.soul)),o.shell=(i||{}).shell,o.graph=o.graph||{},o.seen=o.seen||[],o.as=o.as||i,t(o,r),o.root=r.node,o.graph}}(),r.node=function(t){var n=i.soul(t);if(n)return p({},n,t)},function(){function t(t,n){var o,a;if(i._===n){if(l(t,e.rel._))return;return void(this.obj[n]=h(t))}return(o=e.rel.is(t))?(a=this.opt.seen[o])?void(this.obj[n]=a):void(this.obj[n]=this.opt.seen[o]=r.to(this.graph,o,this.opt)):void(this.obj[n]=t)}r.to=function(n,o,e){if(n){var i={};return e=e||{seen:{}},d(n[o],t,{obj:i,graph:n,opt:e}),i}}}();var a,u=(o.fn.is,o.obj),s=u.is,f=u.del,c=u.has,l=u.empty,p=u.put,d=u.map,h=u.copy;n.exports=r})(t,"./graph"),t(function(n){t("./onto"),n.exports=function(t,n){if(this.on){if(!(t instanceof Function)){if(!t||!n)return;var o=t["#"]||t,e=(this.tag||empty)[o];if(!e)return;return e=this.on(o,n),clearTimeout(e.err),!0}var o=n&&n["#"]||Math.random().toString(36).slice(2);if(!t)return o;var i=this.on(o,t,n);return i.err=i.err||setTimeout(function(){i.next({err:"Error: No ACK received yet.",lack:!0}),i.off()},(this.opt||{}).lack||9e3),o}}})(t,"./ask"),t(function(n){function o(t){var n={s:{}};return t=t||{max:1e3,age:9e3},n.check=function(t){var o;return(o=n.s[t])?o.pass?o.pass=!1:n.track(t):!1},n.track=function(o,r){var a=n.s[o]||(n.s[o]={});return a.was=i(),r&&(a.pass=!0),n.to||(n.to=setTimeout(function(){var o=i();e.obj.map(n.s,function(i,r){i&&t.age>o-i.was||e.obj.del(n.s,r)}),n.to=null},t.age+9)),a},n}var e=t("./type"),i=e.time.is;n.exports=o})(t,"./dup"),t(function(n){function i(t){return t instanceof i?(this._={gun:this,$:this}).$:this instanceof i?i.create(this._={gun:this,$:this,opt:t}):new i(t)}i.is=function(t){return t instanceof i||t&&t._&&t===t._.$||!1},i.version=.9,i.chain=i.prototype,i.chain.toJSON=function(){};var r=t("./type");r.obj.to(r,i),i.HAM=t("./HAM"),i.val=t("./val"),i.node=t("./node"),i.state=t("./state"),i.graph=t("./graph"),i.on=t("./onto"),i.ask=t("./ask"),i.dup=t("./dup"),function(){function t(n){var o,e,r=this,u=r.as,s=u.at||u,f=s.$;return(e=n["#"])||(e=n["#"]=c(9)),(o=s.dup).check(e)?void(u.out===n.out&&(n.out=a,r.to.next(n))):(o.track(e),s.ask(n["@"],n)||(n.get&&i.on.get(n,f),n.put&&i.on.put(n,f)),r.to.next(n),void(u.out||(n.out=t,s.on("out",n))))}i.create=function(n){n.root=n.root||n,n.graph=n.graph||{},n.on=n.on||i.on,n.ask=n.ask||i.ask,n.dup=n.dup||i.dup();var o=n.$.opt(n.opt);return n.once||(n.on("in",t,n),n.on("out",t,{at:n,out:t}),i.on("create",n),n.on("create",n)),n.once=1,o}}(),function(){function t(t,n,o,e){var r=this,a=i.state.is(o,n);if(!a)return r.err="Error: No state on '"+n+"' in node '"+e+"'!";var u=r.graph[e]||k,s=i.state.is(u,n,!0),f=u[n],c=i.HAM(r.machine,a,s,t,f);return c.incoming?(r.put[e]=i.state.to(o,n,r.put[e]),(r.diff||(r.diff={}))[e]=i.state.to(o,n,r.diff[e]),void(r.souls[e]=!0)):void(c.defer&&(r.defer=a<(r.defer||1/0)?a:r.defer))}function n(t,n){var i=this,a=i.$._,u=(a.next||k)[n];if(!u){if(!(a.opt||k)["super"])return void(i.souls[n]=!1);u=i.$.get(n)._}var s=i.map[n]={put:t,get:n,$:u.$},f={ctx:i,msg:s};i.async=!!a.tag.node,i.ack&&(s["@"]=i.ack),v(t,o,f),i.async&&(i.and||a.on("node",function(t){this.to.next(t),t===i.map[t.get]&&(i.souls[t.get]=!1,v(t.put,e,t),v(i.souls,function(t){return t?t:void 0})||i.c||(i.c=1,this.off(),v(i.map,r,i)))}),i.and=!0,a.on("node",s))}function o(t,n){var o=this.ctx,e=o.graph,r=this.msg,a=r.get,u=r.put,s=r.$._;e[a]=i.state.to(u,n,e[a]),o.async||(s.put=i.state.to(u,n,s.put))}function e(t,n){var o=this,e=o.put,r=o.$._;r.put=i.state.to(e,n,r.put)}function r(t){t.$&&(this.cat.stop=this.stop,t.$._.on("in",t),this.cat.stop=null)}i.on.put=function(o,e){var u=e._,s={$:e,graph:u.graph,put:{},map:{},souls:{},machine:i.state(),ack:o["@"],cat:u,stop:{}};return i.graph.is(o.put,null,t,s)||(s.err="Error: Invalid graph!"),s.err?u.on("in",{"@":o["#"],err:i.log(s.err)}):(v(s.put,n,s),s.async||v(s.map,r,s),a!==s.defer&&setTimeout(function(){i.on.put(o,e)},s.defer-s.machine),void(s.diff&&u.on("put",h(o,{put:s.diff}))))},i.on.get=function(t,n){var o,e=n._,r=t.get,a=r[m],u=e.graph[a],s=r[b],f=e.next||(e.next={}),c=f[a];if(d(a,"*")){var l={};i.obj.map(e.graph,function(t,n){i.text.match(n,a)&&(l[n]=i.obj.copy(t))}),i.obj.empty(l)||e.on("in",{"@":t["#"],how:"*",put:l,$:n})}if(!u)return e.on("get",t);if(s){if(!d(u,s))return e.on("get",t);u=i.state.to(u,s)}else u=i.obj.copy(u);u=i.graph.node(u),o=(c||k).ack,e.on("in",{"@":t["#"],how:"mem",put:u,$:n}),e.on("get",t)}}(),function(){i.chain.opt=function(t){t=t||{};var n=this,o=n._,e=t.peers||t;return p(t)||(t={}),p(o.opt)||(o.opt=t),f(e)&&(e=[e]),u(e)&&(e=v(e,function(t,n,o){o(t,{url:t})}),p(o.opt.peers)||(o.opt.peers={}),o.opt.peers=h(e,o.opt.peers)),o.opt.peers=o.opt.peers||{},h(t,o.opt),i.on("opt",o),o.opt.uuid=o.opt.uuid||function(){return g()+c(12)},n}}();var a,u=i.list.is,s=i.text,f=s.is,c=s.random,l=i.obj,p=l.is,d=l.has,h=l.to,v=l.map,g=(l.copy,i.state.lex),m=i.val.rel._,b=".",k=(i.node._,i.val.link.is,{});o.debug=function(t,n){return o.debug.i&&t===o.debug.i&&o.debug.i++&&(o.log.apply(o,arguments)||n)},i.log=function(){return!i.log.off&&o.log.apply(o,arguments),[].slice.call(arguments).join(" ")},i.log.once=function(t,n,o){return(o=i.log.once)[t]=o[t]||0,o[t]++||i.log(n)},i.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'!"),"undefined"!=typeof window&&((window.GUN=window.Gun=i).window=window);try{"undefined"!=typeof e&&(e.exports=i)}catch(y){}n.exports=i})(t,"./root"),t(function(){var n=t("./root");n.chain.back=function(t,i){var r;if(t=t||1,-1===t||1/0===t)return this._.root.$;if(1===t)return(this._.back||this._).$;var a=this,u=a._;if("string"==typeof t&&(t=t.split(".")),!(t instanceof Array)){if(t instanceof Function){for(var s,r={back:u};(r=r.back)&&o===(s=t(r,i)););return s}return n.num.is(t)?(u.back||u).$.back(t-1):this}var f=0,c=t.length,r=u;for(f;c>f;f++)r=(r||e)[t[f]];return o!==r?i?a:r:(r=u.back)?r.$.back(t,i):void 0};var o,e={}})(t,"./back"),t(function(){function n(t){var n,o,e,i=this.as,r=i.back,a=i.root;if(t.I||(t.I=i.$),t.$||(t.$=i.$),this.to.next(t),o=t.get){if(o["#"]||i.soul){if(o["#"]=o["#"]||i.soul,t["#"]||(t["#"]=b(9)),r=a.$.get(o["#"])._,o=o["."]){if(h(r.put,o)&&(n=r.$.get(o)._,(e=n.ack)||(n.ack=-1),r.on("in",{$:r.$,put:c.state.to(r.put,o),get:r.get}),e))return}else{if(e=r.ack,e||(r.ack=-1),h(r,"put")&&r.on("in",r),e)return;t.$=r.$}return a.ask(f,t),a.on("in",t)}if(a.now&&(a.now[i.id]=a.now[i.id]||!0,i.pass={}),o["."])return i.get?(t={get:{".":i.get},$:i.$},r.ask||(r.ask={}),r.ask[i.get]=t.$._,r.on("out",t)):(t={get:{},$:i.$},r.on("out",t));if(i.ack=i.ack||-1,i.get)return t.$=i.$,o["."]=i.get,(r.ask||(r.ask={}))[i.get]=t.$._,r.on("out",t)}return r.on("out",t)}function o(t){var n,o,r=this,s=r.as,f=s.root,d=t.$,b=(d||p)._||p,k=t.put;if(s.get&&t.get!==s.get&&(t=g(t,{get:s.get})),s.has&&b!==s&&(t=g(t,{$:s.$}),b.ack&&(s.ack=b.ack)),l===k){if(o=b.put,r.to.next(t),s.soul)return;if(l===o&&l!==b.put)return;return i(s,t,r),s.has&&u(s,t),v(b.echo,s.id),void v(s.map,b.id)}if(s.soul)return r.to.next(t),i(s,t,r),void(s.next&&m(k,a,{msg:t,cat:s}));if(!(n=c.val.link.is(k)))return c.val.is(k)?(s.has||s.soul?u(s,t):(b.has||b.soul)&&((b.echo||(b.echo={}))[s.id]=b.echo[b.id]||s,(s.map||(s.map={}))[b.id]=s.map[b.id]||{at:b}),r.to.next(t),void i(s,t,r)):(s.has&&b!==s&&h(b,"put")&&(s.put=b.put),(n=c.node.soul(k))&&b.has&&(b.put=s.root.$.get(n)._.put),o=(f.stop||{})[b.id],r.to.next(t),e(s,t,b,n),i(s,t,r),void(s.next&&m(k,a,{msg:t,cat:s})));f.stop;o=f.stop||{},o=o[b.id]||(o[b.id]={}),o.is=o.is||b.put,o[s.id]=b.put||!0,r.to.next(t),e(s,t,b,n),i(s,t,r)}function e(t,n,o,i){if(i&&k!==t.get){var r=t.root.$.get(i)._;t.has?o=r:o.has&&e(o,n,o,i),o!==t&&(o.$||(o={}),(o.echo||(o.echo={}))[t.id]=o.echo[t.id]||t,t.has&&!(t.map||p)[o.id]&&u(t,n),r=o.id?(t.map||(t.map={}))[o.id]=t.map[o.id]||{at:o}:{},(i!==r.link||r.pass||t.pass)&&(t.pass&&(c.obj.map(t.map,function(t){t.pass=!0}),v(t,"pass")),r.pass&&v(r,"pass"),t.has&&(t.link=i),s(t,r.link=i)))}}function i(t,n){t.echo&&m(t.echo,r,n)}function r(t){t&&t.on&&t.on("in",this)}function a(t,n){var o,e,i,r=this.cat,a=r.next||p,u=this.msg;(k!==n||a[n])&&(e=a[n])&&(e.has?(l!==e.put&&c.val.link.is(t)||(e.put=t),o=e.$):(i=u.$)&&(i=(o=u.$.get(n))._,l!==i.put&&c.val.link.is(t)||(i.put=t)),e.on("in",{put:t,get:n,$:o,via:u}))}function u(t,n){if(t.has||t.soul){{var o=t.map;t.root}t.map=null,t.has&&(t.link=null),(t.pass||n["@"]||null!==o)&&(l===o&&c.val.link.is(t.put)||(m(o,function(n){(n=n.at)&&v(n.echo,t.id)}),o=t.put,m(t.next,function(n,e){return l===o&&l!==t.put?!0:(n.put=l,n.ack&&(n.ack=-1),void n.on("in",{get:e,$:n.$,put:l}))})))}}function s(t,n){var o=t.root.$.get(n)._;(!t.ack||(o.on("out",{get:{"#":n}}),t.ask))&&(o=t.ask,c.obj.del(t,"ask"),m(o||t.next,function(t,o){t.on("out",{get:{"#":n,".":o}})}),c.obj.del(t,"ask"))}function f(t){var n=this.as,o=n.get||p,e=n.$._,i=(t.put||p)[o["#"]];if(e.ack&&(e.ack=e.ack+1||1),!t.put||o["."]&&!h(i,e.get)){if(e.put!==l)return;return void e.on("in",{get:e.get,put:e.put=l,$:e.$,"@":t["@"]})}return k==o["."]?void e.on("in",{get:e.get,put:c.val.link.ify(o["#"]),$:e.$,"@":t["@"]}):(t.$=e.root.$,void c.on.put(t,e.root.$))}var c=t("./root");c.chain.chain=function(t){var e,i=this,r=i._,a=new(t||i).constructor(i),u=a._;return u.root=e=r.root,u.id=++e.once,u.back=i._,u.on=c.on,u.on("in",o,u),u.on("out",n,u),a};var l,p={},d=c.obj,h=d.has,v=(d.put,d.del),g=d.to,m=d.map,b=c.text.random,k=(c.val.rel._,c.node._)})(t,"./chain"),t(function(){function n(t,n){var o=n._,e=o.next,i=n.chain(),r=i._;return e||(e=o.next={}),e[r.get=t]=r,n===o.root.$?r.soul=t:(o.soul||o.has)&&(r.has=t),r}function o(t,n,o,e){var i,r=t._;return(i=r.soul)?(n(i,e,r),t):(i=r.link)?(n(i,e,r),t):(t.get(function(t,o){o.rid(t);var r=(r=t.$)&&r._||{};i=r.link||r.soul||c.is(t.put)||l(t.put),n(i,e,t,o)},{out:{get:{".":!0}}}),t)}function e(t){var n,o=this,e=o.as,i=e.at,r=i.root,u=t.$,f=(u||{})._||{},l=t.put||f.put;if((n=r.now)&&o!==n[e.now])return o.to.next(t);if(o.seen&&f.id&&o.seen[f.id])return o.to.next(t);if((n=l)&&n[c._]&&(n=c.is(n))&&(n=(t.$$=f.root.gun.get(n))._,a!==n.put&&(t=s(t,{put:l=n.put}))),(n=r.mum)&&f.id){if(n[f.id])return;a===l||c.is(l)||(n[f.id]=!0)}return e.use(t,o),o.stun?void(o.stun=null):void o.to.next(t)}function i(t){var n=this.on;if(!t||n.soul||n.has)return this.off();if(t=(t=(t=t.$||t)._||t).id){{var o,e;n.map}return(o=(e=this.seen||(this.seen={}))[t])?!0:void(e[t]=!0)}}var r=t("./root");r.chain.get=function(t,a,u){var s,l;if("string"!=typeof t){if(t instanceof Function){if(!0===a)return o(this,t,a,u);s=this;var d,h=s._,v=h.root,l=v.now;u=a||{},u.at=h,u.use=t,u.out=u.out||{},u.out.get=u.out.get||{},(d=h.on("in",e,u)).rid=i,(v.now={$:1})[u.now=h.id]=d;var g=v.mum;return v.mum={},h.on("out",u.out),v.mum=g,v.now=l,s}return f(t)?this.get(""+t,a,u):(l=c.is(t))?this.get(l,a,u):((u=this.chain())._.err={err:r.log("Invalid get request!",t)},a&&a.call(u,u._.err),u)}var m=this,b=m._,k=b.next||p;return(s=k[t])||(s=n(t,m)),s=s.$,(l=b.stun)&&(s._.stun=s._.stun||l),a&&a instanceof Function&&s.get(a,u),s};var a,u=r.obj,s=(u.has,r.obj.to),f=r.num.is,c=r.val.link,l=r.node.soul,p=(r.node._,{})})(t,"./get"),t(function(){function n(t){t.batch=i;var n=t.opt||{},o=t.env=c.state.map(a,n.state);return o.soul=t.soul,t.graph=c.graph.ify(t.data,o,t),o.err?((t.ack||m).call(t,t.out={err:c.log(o.err)}),void(t.res&&t.res())):void t.batch()}function e(t){return void(t&&t())}function i(){var t=this;t.graph&&!v(t.stun,r)&&(t.res=t.res||function(t){t&&t()},t.res(function(){var n=t.$.back(-1)._,o=n.ask(function(o){n.root.on("ack",o),o.err&&c.log(o),o.lack||this.off(),t.ack&&t.ack(o,this)},t.opt),e=n.root.now;p.del(n.root,"now");var i=n.root.mum;n.root.mum={},t.ref._.on("out",{$:t.ref,put:t.out=t.env.graph,opt:t.opt,"#":o}),n.root.mum=i?p.to(i,n.root.mum):i,n.root.now=e},t),t.res&&t.res())}function r(t){return t?!0:void 0}function a(t,n,o,e){var i=this,r=c.is(t);!n&&e.path.length&&(i.res||b)(function(){var n=e.path,o=i.ref,a=(i.opt,0),s=n.length;for(a;s>a;a++)o=o.get(n[a]);r&&(o=t);var f=o._.dub;return f||(f=c.node.soul(e.obj))?(o.back(-1).get(f),void e.soul(f)):((i.stun=i.stun||{})[n]=!0,void o.get(u,!0,{as:{at:e,as:i,p:n}}))},{as:i,at:e})}function u(t,n,o,e){var n=n.as,i=n.at;n=n.as;var r=((o||{}).$||{})._||{};return t=r.dub=r.dub||t||c.node.soul(i.obj)||c.node.soul(o.put||r.put)||c.val.rel.is(o.put||r.put)||(n.via.back("opt.uuid")||c.text.random)(),e&&(e.stun=!0),t?void s(r,r.dub=t,i,n):void r.via.back("opt.uuid")(function(t,o){return t?c.log(t):void s(r,r.dub=r.dub||o,i,n)})}function s(t,n,o,e){t.$.back(-1).get(n),o.soul(n),e.stun[o.path]=!1,e.batch()}function f(t,n,e,i){if(n=n.as,e.$&&e.$._){if(e.err)return void o.log("Please report this as an issue! Put.any.err");var r,a=e.$._,u=a.put,s=n.opt||{};if(!(r=n.ref)||!r._.now){if(i&&(i.stun=!0),n.ref!==n.$){if(r=n.$._.get||a.get,!r)return void o.log("Please report this as an issue! Put.no.get");n.data=h({},r,n.data),r=null}if(l===u){if(!a.get)return;t||(r=a.$.back(function(t){return t.link||t.soul?t.link||t.soul:void(n.data=h({},t.get,n.data))})),r=r||a.get,a=a.root.$.get(r)._,n.soul=r,u=n.data}return n.not||(n.soul=n.soul||t)||(n.path&&d(n.data)?n.soul=(s.uuid||n.via.back("opt.uuid")||c.text.random)():(k==a.get&&(n.soul=(a.put||g)["#"]||a.dub),n.soul=n.soul||a.soul||a.soul||(s.uuid||n.via.back("opt.uuid")||c.text.random)()),n.soul)?void n.ref.put(n.data,n.soul,n):void n.via.back("opt.uuid")(function(t,o){return t?c.log(t):void n.ref.put(n.data,n.soul=o,n)})}}}var c=t("./root");c.chain.put=function(t,o,i){var r,a=this,u=a._,s=u.root.$;return i=i||{},i.data=t,i.via=i.$=i.via||i.$||a,"string"==typeof o?i.soul=o:i.ack=i.ack||o,u.soul&&(i.soul=u.soul),i.soul||s===a?d(i.data)?(i.soul=i.soul||(i.not=c.node.soul(i.data)||(i.via.back("opt.uuid")||c.text.random)()),i.soul?(i.$=a=s.get(i.soul),i.ref=i.$,n(i),a):(i.via.back("opt.uuid")(function(t,n){return t?c.log(t):void(i.ref||i.$).put(i.data,i.soul=n,i)}),a)):((i.ack||m).call(i,i.out={err:c.log("Data saved to the root level of the graph must be a node (an object), not a",typeof i.data,'of "'+i.data+'"!')}),i.res&&i.res(),a):c.is(t)?(t.get(function(t,n,e){return!t&&c.val.is(e.put)?c.log("The reference you are saving is a",typeof e.put,'"'+e.put+'", not a node (object)!'):void a.put(c.val.rel.ify(t),o,i)},!0),a):(i.ref=i.ref||s._===(r=u.back)?a:r.$,i.ref._.soul&&c.val.is(i.data)&&u.get?(i.data=h({},u.get,i.data),i.ref.put(i.data,i.soul,i),a):(i.ref.get(f,!0,{as:i}),i.out||(i.res=i.res||e,i.$._.stun=i.ref._.stun),a))};var l,p=c.obj,d=p.is,h=p.put,v=p.map,g={},m=function(){},b=function(t,n){t.call(n||g)},k=c.node._})(t,"./put"),t(function(n){var o=t("./root");t("./chain"),t("./back"),t("./put"),t("./get"),n.exports=o})(t,"./index"),t(function(){function n(t,n){{var o,e=this,r=t.$,a=(r||{})._||{},u=a.put||t.put;e.at}if(i!==u){if(o=t.$$){if(o=t.$$._,i===o.put)return;u=o.put}e.change&&(u=t.put),e.as?e.ok.call(e.as,t,n):e.ok.call(r,u,t.get,t,n)}}function o(t,n,e){var r,u,s=this.as,f=(s.at,t.$),c=f._,l=c.put||t.put;if(u=t.$$){if(r=u=t.$$._,i===u.put)return;l=u.put}return(u=n.wait)&&(u=u[c.id])&&clearTimeout(u),!e&&(i===l||c.soul||c.link||r&&!(0 .once, apologies unexpected."),this.once(t,n)},e.chain.once=function(t,n){var r=this,a=r._,u=a.put;if(0=(n.batch||1e3)?s():void(e||(e=setTimeout(s,n.wait||1)))}),t.on("get",function(o){function e(){if(s&&(i=s["#"])){var e=s["."];r=a[i]||u,r&&e&&(r=Gun.state.to(r,e)),(r||Gun.obj.empty(n.peers))&&t.on("in",{"@":o["#"],put:Gun.graph.node(r),how:"lS",lS:o.I})}}this.to.next(o);var i,r,u,s=o.get;Gun.debug?setTimeout(e,1):e()});var u=function(t,n,o,e){a[e]=Gun.state.to(o,n,a[e])},s=function(u){var f;r=0,clearTimeout(e),e=!1;var c=i;i={},u&&(a=u);try{o.setItem(n.prefix,JSON.stringify(a))}catch(l){Gun.log(f=(l||"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 ."),t.on("localStorage:error",{err:f,file:n.prefix,flush:a,retry:s})}(f||Gun.obj.empty(n.peers))&&Gun.obj.map(c,function(n,o){t.on("in",{"@":o,err:f,ok:0})})}}})}})(t,"./adapters/localStorage"),t(function(n){function e(t){var n=function(){},u=t.opt||{};return u.log=u.log||o.log,u.gap=u.gap||u.wait||1,u.pack=u.pack||.3*(u.memory?1e3*u.memory*1e3:1399e6),n.out=function(o){var e;return this.to&&this.to.next(o),(e=o["@"])&&(e=t.dup.s[e])&&(e=e.it)&&e.mesh?(n.say(o,e.mesh.via,1),void(e["##"]=o["##"])):void n.say(o)},t.on("create",function(o){o.opt.pid=o.opt.pid||i.text.random(9),this.to.next(o),t.on("out",n.out)}),n.hear=function(o,e){if(o){var r,a,s,f=t.dup,c=o[0];if(u.pack<=o.length)return n.say({dam:"!",err:"Message too big!"},e);try{s=JSON.parse(o)}catch(l){u.log("DAM JSON parse error",l)}if("{"===c){if(!s)return;if(f.check(r=s["#"]))return;if(f.track(r,!0).it=s,(c=s["@"])&&s.put&&(a=s["##"]||(s["##"]=n.hash(s)),(c+=a)!=r)){if(f.check(c))return;(c=f.s)[a]=c[r]}return(s.mesh=function(){}).via=e,(c=s["><"])&&(s.mesh.to=i.obj.map(c.split(","),function(t,n,o){o(t,!0)})),s.dam?void((c=n.hear[s.dam])&&c(s,e,t)):void t.on("in",s)}if("["!==c);else{if(!s)return;for(var p,d=0;p=s[d++];)n.hear(p,e)}}},function(){function o(t){var n=t.batch;if(n&&(t.batch=t.tail=null,n.length))try{e(JSON.stringify(n),t)}catch(o){u.log("DAM JSON stringify error",o)}}function e(t,n){var o=n.wire;try{o.send?o.send(t):n.say&&n.say(t)}catch(e){(n.queue=n.queue||[]).push(t)}}n.say=function(r,s,f){if(!s)return void i.obj.map(u.peers,function(t){n.say(r,t)});var c,l,p,d=s.wire||u.wire&&u.wire(s);if(d&&(l=r.mesh||a,s!==l.via&&((p=l.raw)||(p=n.raw(r)),!((c=r["@"])&&(c=t.dup.s[c])&&(c=c.it)&&c.get&&c["##"]&&c["##"]===r["##"]||(c=l.to)&&(c[s.url]||c[s.id])&&!f)))){if(s.batch){if(s.tail=(s.tail||0)+p.length,s.tail<=u.pack)return void s.batch.push(p);o(s)}s.batch=[],setTimeout(function(){o(s)},u.gap),e(p,s)}}}(),function(){function o(t,n){var o;return n instanceof Object?(i.obj.map(Object.keys(n).sort(),a,{to:o={},on:n}),o):n}function a(t){this.to[t]=this.on[t]}n.raw=function(e){if(!e)return"";var a,c,l,p=t.dup,d=e.mesh||{};if(l=d.raw)return l;if("string"==typeof e)return e;e["@"]&&(l=e.put)&&((c=e["##"])||(a=s(l,o)||"",c=n.hash(e,a),e["##"]=c),(l=p.s)[c=e["@"]+c]=l[e["#"]],e["#"]=c||e["#"],a&&((e=i.obj.to(e)).put=f));var h=0,v=[];i.obj.map(u.peers,function(t){return v.push(t.url||t.id),++h>9?!0:void 0}),e["><"]=v.join();var g=s(e);return r!==a&&(l=g.indexOf(f,g.indexOf("put")),g=g.slice(0,l-1)+a+g.slice(l+f.length+1)),d&&(d.raw=g),g},n.hash=function(t,n){return e.hash(n||s(t.put,o)||"")||t["#"]||i.text.random(9)};var s=JSON.stringify,f=":])([:"}(),n.hi=function(o){var e=o.wire||{};o.id||o.url?(u.peers[o.url||o.id]=o,i.obj.del(u.peers,e.id)):(e=e.id=e.id||i.text.random(9),n.say({dam:"?"},u.peers[e]=o)),e.hied||t.on(e.hied="hi",o),e=o.queue,o.queue=[],i.obj.map(e,function(t){n.say(t,o)})},n.bye=function(n){i.obj.del(u.peers,n.id),t.on("bye",n)},n.hear["!"]=function(t){u.log("Error:",t.err)},n.hear["?"]=function(t,o){return t.pid?(o.id=o.id||t.pid,void n.hi(o)):n.say({dam:"?",pid:u.pid,"@":t["#"]},o)},n}var i=t("../type");e.hash=function(t){if("string"!=typeof t)return{err:1};var n=0;if(!t.length)return n;for(var o,e=0,i=t.length;i>e;++e)o=t.charCodeAt(e), -n=(n<<5)-n+o,n|=0;return n};var r,a={};Object.keys=Object.keys||function(t){return map(t,function(t,n,o){o(n)})};try{n.exports=e}catch(u){}})(t,"./adapters/mesh"),t(function(){var n=t("../index");n.Mesh=t("./mesh"),n.on("opt",function(t){function o(t){try{if(!t||!t.url)return o&&o(t);var n=t.url.replace("http","ws"),o=t.wire=new i.WebSocket(n);return o.onclose=function(){i.mesh.bye(t),e(t)},o.onerror=function(n){e(t),n&&"ECONNREFUSED"===n.code},o.onopen=function(){i.mesh.hi(t)},o.onmessage=function(n){n&&i.mesh.hear(n.data||n,t)},o}catch(r){}}function e(t){clearTimeout(t.defer),t.defer=setTimeout(function(){o(t)},2e3)}this.to.next(t);var i=t.opt;if(!t.once&&!1!==i.WebSocket){var r;"undefined"!=typeof window&&(r=window),"undefined"!=typeof global&&(r=global),r=r||{};var a=i.WebSocket||r.WebSocket||r.webkitWebSocket||r.mozWebSocket;if(a){i.WebSocket=a;{i.mesh=i.mesh||n.Mesh(t),i.wire}i.wire=o}}})})(t,"./adapters/websocket")}(); \ No newline at end of file +!function(){function t(n,o){function e(t){return t.split("/").slice(-1).toString().replace(".js","")}return o?require(n):n.slice?t[e(n)]:function(o,i){n(o={exports:{}}),t[e(i)]=o.exports}}var n;"undefined"!=typeof window&&(n=window),"undefined"!=typeof global&&(n=global),n=n||{};var o=n.console||{log:function(){}};if("undefined"!=typeof module)var e=module;t(function(t){var n={};n.fn={is:function(t){return!!t&&"function"==typeof t}},n.bi={is:function(t){return t instanceof Boolean||"boolean"==typeof t}},n.num={is:function(t){return!e(t)&&(t-parseFloat(t)+1>=0||1/0===t||-(1/0)===t)}},n.text={is:function(t){return"string"==typeof t}},n.text.ify=function(t){return n.text.is(t)?t:"undefined"!=typeof JSON?JSON.stringify(t):t&&t.toString?t.toString():t},n.text.random=function(t,n){var o="";for(t=t||24,n=n||"0123456789ABCDEFGHIJKLMNOPQRSTUVWXZabcdefghijklmnopqrstuvwxyz";t>0;)o+=n.charAt(Math.floor(Math.random()*n.length)),t--;return o},n.text.match=function(t,o){function e(t,n){for(var o,e=-1,i=0;o=n[i++];)if(!~(e=t.indexOf(o,e+1)))return!1;return!0}var i=!1;if(t=t||"",o=n.text.is(o)?{"=":o}:o||{},n.obj.has(o,"~")&&(t=t.toLowerCase(),o["="]=(o["="]||o["~"]).toLowerCase()),n.obj.has(o,"="))return t===o["="];if(n.obj.has(o,"*")){if(t.slice(0,o["*"].length)!==o["*"])return!1;i=!0,t=t.slice(o["*"].length)}if(n.obj.has(o,"!")){if(t.slice(-o["!"].length)!==o["!"])return!1;i=!0}if(n.obj.has(o,"+")&&n.list.map(n.list.is(o["+"])?o["+"]:[o["+"]],function(n){return t.indexOf(n)>=0?void(i=!0):!0}))return!1;if(n.obj.has(o,"-")&&n.list.map(n.list.is(o["-"])?o["-"]:[o["-"]],function(n){return t.indexOf(n)<0?void(i=!0):!0}))return!1;if(n.obj.has(o,">")){if(!(t>o[">"]))return!1;i=!0}if(n.obj.has(o,"<")){if(!(tn?-1:n>o?1:0):0}},n.list.map=function(t,n,o){return u(t,n,o)},n.list.index=1,n.obj={is:function(t){return t?t instanceof Object&&t.constructor===Object||"Object"===Object.prototype.toString.call(t).match(/^\[object (\w+)\]$/)[1]:!1}},n.obj.put=function(t,n,o){return(t||{})[n]=o,t},n.obj.has=function(t,n){return t&&Object.prototype.hasOwnProperty.call(t,n)},n.obj.del=function(t,n){return t?(t[n]=null,delete t[n],t):void 0},n.obj.as=function(t,n,o,e){return t[n]=t[n]||(e===o?{}:o)},n.obj.ify=function(t){if(r(t))return t;try{t=JSON.parse(t)}catch(n){t={}}return t},function(){function t(t,n){a(this,n)&&o!==this[n]||(this[n]=t)}var o;n.obj.to=function(n,o){return o=o||{},u(n,t,o),o}}(),n.obj.copy=function(t){return t?JSON.parse(JSON.stringify(t)):t},function(){function t(t,n){var o=this.n;if(!o||!(n===o||r(o)&&a(o,n)))return n?!0:void 0}n.obj.empty=function(n,o){return n&&u(n,t,{n:o})?!1:!0}}(),function(){function t(n,o){return 2===arguments.length?(t.r=t.r||{},void(t.r[n]=o)):(t.r=t.r||[],void t.r.push(n))}var i=Object.keys;n.obj.map=function(u,s,f){var c,l,p,d,h,v=0,g=o(s);if(t.r=null,i&&r(u)&&(d=i(u),h=!0),e(u)||d)for(l=(d||u).length;l>v;v++){var m=v+n.list.index;if(g){if(p=h?s.call(f||this,u[d[v]],d[v],t):s.call(f||this,u[v],m,t),p!==c)return p}else if(s===u[h?d[v]:v])return d?d[v]:m}else for(v in u)if(g){if(a(u,v)&&(p=f?s.call(f,u[v],v,t):s(u[v],v,t),p!==c))return p}else if(s===u[v])return v;return g?t.r:n.list.index?0:-1}}(),n.time={},n.time.is=function(t){return t?t instanceof Date:+(new Date).getTime()};var o=n.fn.is,e=n.list.is,i=n.obj,r=i.is,a=i.has,u=i.map;t.exports=n})(t,"./type"),t(function(t){t.exports=function n(t,o,e){if(!t)return{to:n};var i,t=(this.tag||(this.tag={}))[t]||(this.tag[t]={tag:t,to:n._={next:function(t){var n;(n=this.to)&&n.next(t)}}});if(o instanceof Function){var r={off:n.off||(n.off=function(){return this.next===n._.next?!0:(this===this.the.last&&(this.the.last=this.back),this.to.back=this.back,this.next=n._.next,this.back.to=this.to,void(this.the.last===this.the&&delete this.on.tag[this.the.tag]))}),to:n._,next:o,the:t,on:this,as:e};return(r.back=t.last||t).to=r,t.last=r}return(t=t.to)&&i!==o&&t.next(o),t}})(t,"./onto"),t(function(t){function n(t,n,e,i,r){if(n>t)return{defer:!0};if(e>n)return{historical:!0};if(n>e)return{converge:!0,incoming:!0};if(n===e){if(i=o(i)||"",r=o(r)||"",i===r)return{state:!0};if(r>i)return{converge:!0,current:!0};if(i>r)return{converge:!0,incoming:!0}}return{err:"Invalid CRDT Data: "+i+" to "+r+" at "+n+" to "+e+"!"}}if("undefined"==typeof JSON)throw new Error("JSON is not included in this browser. Please load it first: ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js");var o=JSON.stringify;t.exports=n})(t,"./HAM"),t(function(n){var o=t("./type"),e={};e.is=function(t){return t===i?!1:null===t?!0:t===1/0?!1:s(t)||a(t)||u(t)?!0:e.rel.is(t)||!1},e.link=e.rel={_:"#"},function(){function t(t,n){var o=this;return o.id?o.id=!1:n==r&&s(t)?void(o.id=t):o.id=!1}e.rel.is=function(n){if(n&&n[r]&&!n._&&c(n)){var o={};if(p(n,t,o),o.id)return o.id}return!1}}(),e.rel.ify=function(t){return l({},r,t)},o.obj.has._=".";var i,r=e.link._,a=o.bi.is,u=o.num.is,s=o.text.is,f=o.obj,c=f.is,l=f.put,p=f.map;n.exports=e})(t,"./val"),t(function(n){var o=t("./type"),e=t("./val"),i={_:"_"};i.soul=function(t,n){return t&&t._&&t._[n||p]},i.soul.ify=function(t,n){return n="string"==typeof n?{soul:n}:n||{},t=t||{},t._=t._||{},t._[p]=n.soul||t._[p]||l(),t},i.soul._=e.link._,function(){function t(t,n){return n!==i._?e.is(t)?void(this.cb&&this.cb.call(this.as,t,n,this.n,this.s)):!0:void 0}i.is=function(n,o,e){var r;return u(n)&&(r=i.soul(n))?!f(n,t,{as:e,cb:o,s:r,n:n}):!1}}(),function(){function t(t,n){var o,i,r=this.o;return r.map?(o=r.map.call(this.as,t,""+n,r.node),void(i===o?s(r.node,n):r.node&&(r.node[n]=o))):void(e.is(t)&&(r.node[n]=t))}i.ify=function(n,o,e){return o?"string"==typeof o?o={soul:o}:o instanceof Function&&(o={map:o}):o={},o.map&&(o.node=o.map.call(e,n,r,o.node||{})),(o.node=i.soul.ify(o.node||{},o))&&f(n,t,{o:o,as:e}),o.node}}();var r,a=o.obj,u=a.is,s=a.del,f=a.map,c=o.text,l=c.random,p=i.soul._;n.exports=i})(t,"./node"),t(function(n){function o(){var t;return t=r(),t>a?(u=0,a=t+o.drift):a=t+(u+=1)/s+o.drift}{var e=t("./type"),i=t("./node"),r=e.time.is,a=-(1/0),u=0,s=1e3,f="undefined"!=typeof performance?performance.timing&&performance:!1;f&&f.timing&&f.timing.navigationStart||(f=!1)}o._=">",o.drift=0,o.is=function(t,n,e){var i=n&&t&&t[x]&&t[x][o._]||e;if(i)return b(i=i[n])?i:-(1/0)},o.lex=function(){return o().toString(36).replace(".","")},o.ify=function(t,n,e,r,a){if(!t||!t[x]){if(!a)return;t=i.soul.ify(t,a)}var u=p(t[x],o._);return c!==n&&n!==x&&(b(e)&&(u[n]=e),c!==r&&(t[n]=r)),t},o.to=function(t,n,e){var r=t[n];return h(r)&&(r=g(r)),o.ify(e,n,o.is(t,n),r,i.soul(t))},function(){function t(t,n){x!==n&&o.ify(this.o,n,this.s)}o.map=function(n,e,i){var r,a=h(a=n||e)?a:null;return n=y(n=n||e)?n:null,a&&!n?(e=b(e)?e:o(),a[x]=a[x]||{},v(a,t,{o:a,s:e}),a):(i=i||h(e)?e:r,e=b(e)?e:o(),function(o,a,u,s){return n?(n.call(i||this||{},o,a,u,s),void(d(u,a)&&r===u[a]||t.call({o:u,s:e},o,a))):(t.call({o:u,s:e},o,a),o)})}}();var c,l=e.obj,p=l.as,d=l.has,h=l.is,v=l.map,g=l.copy,m=e.num,b=m.is,k=e.fn,y=k.is,x=i._;n.exports=o})(t,"./state"),t(function(n){var o=t("./type"),e=t("./val"),i=t("./node"),r={};!function(){function t(t,o){return t&&o===i.soul(t)&&i.is(t,this.fn,this.as)?void(this.cb&&(n.n=t,n.as=this.as,this.cb.call(n.as,t,o,n))):!0}function n(t){t&&i.is(n.n,t,n.as)}r.is=function(n,o,e,i){return n&&s(n)&&!l(n)?!d(n,t,{cb:o,fn:e,as:i}):!1}}(),function(){function t(t,o){var r;return(r=p(t,o))?r:(o.env=t,o.soul=u,i.ify(o.obj,n,o)&&(o.rel=o.rel||e.rel.ify(i.soul(o.node)),o.obj!==t.shell&&(t.graph[e.rel.is(o.rel)]=o.node)),o)}function n(n,o,r){var u,s,p=this,d=p.env;if(i._===o&&c(n,e.rel._))return r._;if(u=l(n,o,r,p,d)){if(o||(p.node=p.node||r||{},c(n,i._)&&i.soul(n)&&(p.node._=h(n._)),p.node=i.soul.ify(p.node,e.rel.is(p.rel)),p.rel=p.rel||e.rel.ify(i.soul(p.node))),(s=d.map)&&(s.call(d.as||{},n,o,r,p),c(r,o))){if(n=r[o],a===n)return void f(r,o);if(!(u=l(n,o,r,p,d)))return}if(!o)return p.node;if(!0===u)return n;if(s=t(d,{obj:n,path:p.path.concat(o)}),s.node)return s.rel}}function u(t){var n=this,o=e.link.is(n.rel),r=n.env.graph;n.rel=n.rel||e.rel.ify(t),n.rel[e.rel._]=t,n.node&&n.node[i._]&&(n.node[i._][e.rel._]=t),c(r,o)&&(r[t]=r[o],f(r,o))}function l(t,n,i,r,a){var u;return e.is(t)?!0:s(t)?1:(u=a.invalid)?(t=u.call(a.as||{},t,n,i),l(t,n,i,r,a)):(a.err="Invalid value at '"+r.path.concat(n).join(".")+"'!",void(o.list.is(t)&&(a.err+=" Use `.set(item)` instead of an Array.")))}function p(t,n){for(var o,e=t.seen,i=e.length;i--;)if(o=e[i],n.obj===o.obj)return o;e.push(n)}r.ify=function(n,o,i){var r={path:[],obj:n};return o?"string"==typeof o?o={soul:o}:o instanceof Function&&(o.map=o):o={},o.soul&&(r.rel=e.rel.ify(o.soul)),o.shell=(i||{}).shell,o.graph=o.graph||{},o.seen=o.seen||[],o.as=o.as||i,t(o,r),o.root=r.node,o.graph}}(),r.node=function(t){var n=i.soul(t);if(n)return p({},n,t)},function(){function t(t,n){var o,a;if(i._===n){if(l(t,e.rel._))return;return void(this.obj[n]=h(t))}return(o=e.rel.is(t))?(a=this.opt.seen[o])?void(this.obj[n]=a):void(this.obj[n]=this.opt.seen[o]=r.to(this.graph,o,this.opt)):void(this.obj[n]=t)}r.to=function(n,o,e){if(n){var i={};return e=e||{seen:{}},d(n[o],t,{obj:i,graph:n,opt:e}),i}}}();var a,u=(o.fn.is,o.obj),s=u.is,f=u.del,c=u.has,l=u.empty,p=u.put,d=u.map,h=u.copy;n.exports=r})(t,"./graph"),t(function(n){t("./onto"),n.exports=function(t,n){if(this.on){if(!(t instanceof Function)){if(!t||!n)return;var o=t["#"]||t,e=(this.tag||empty)[o];if(!e)return;return e=this.on(o,n),clearTimeout(e.err),!0}var o=n&&n["#"]||Math.random().toString(36).slice(2);if(!t)return o;var i=this.on(o,t,n);return i.err=i.err||setTimeout(function(){i.next({err:"Error: No ACK received yet.",lack:!0}),i.off()},(this.opt||{}).lack||9e3),o}}})(t,"./ask"),t(function(n){function o(t){var n={s:{}};return t=t||{max:1e3,age:9e3},n.check=function(t){var o;return(o=n.s[t])?o.pass?o.pass=!1:n.track(t):!1},n.track=function(o,r){var a=n.s[o]||(n.s[o]={});return a.was=i(),r&&(a.pass=!0),n.to||(n.to=setTimeout(function(){var o=i();e.obj.map(n.s,function(i,r){i&&t.age>o-i.was||e.obj.del(n.s,r)}),n.to=null},t.age+9)),a},n}var e=t("./type"),i=e.time.is;n.exports=o})(t,"./dup"),t(function(n){function i(t){return t instanceof i?(this._={gun:this,$:this}).$:this instanceof i?i.create(this._={gun:this,$:this,opt:t}):new i(t)}i.is=function(t){return t instanceof i||t&&t._&&t===t._.$||!1},i.version=.9,i.chain=i.prototype,i.chain.toJSON=function(){};var r=t("./type");r.obj.to(r,i),i.HAM=t("./HAM"),i.val=t("./val"),i.node=t("./node"),i.state=t("./state"),i.graph=t("./graph"),i.on=t("./onto"),i.ask=t("./ask"),i.dup=t("./dup"),function(){function t(n){var o,e,r=this,u=r.as,s=u.at||u,f=s.$;return(e=n["#"])||(e=n["#"]=c(9)),(o=s.dup).check(e)?void(u.out===n.out&&(n.out=a,r.to.next(n))):(o.track(e),s.ask(n["@"],n)||(n.get&&i.on.get(n,f),n.put&&i.on.put(n,f)),r.to.next(n),void(u.out||(n.out=t,s.on("out",n))))}i.create=function(n){n.root=n.root||n,n.graph=n.graph||{},n.on=n.on||i.on,n.ask=n.ask||i.ask,n.dup=n.dup||i.dup();var o=n.$.opt(n.opt);return n.once||(n.on("in",t,n),n.on("out",t,{at:n,out:t}),i.on("create",n),n.on("create",n)),n.once=1,o}}(),function(){function t(t,n,o,e){var r=this,a=i.state.is(o,n);if(!a)return r.err="Error: No state on '"+n+"' in node '"+e+"'!";var u=r.graph[e]||k,s=i.state.is(u,n,!0),f=u[n],c=i.HAM(r.machine,a,s,t,f);return c.incoming?(r.put[e]=i.state.to(o,n,r.put[e]),(r.diff||(r.diff={}))[e]=i.state.to(o,n,r.diff[e]),void(r.souls[e]=!0)):void(c.defer&&(r.defer=a<(r.defer||1/0)?a:r.defer))}function n(t,n){var i=this,a=i.$._,u=(a.next||k)[n];if(!u){if(!(a.opt||k)["super"])return void(i.souls[n]=!1);u=i.$.get(n)._}var s=i.map[n]={put:t,get:n,$:u.$},f={ctx:i,msg:s};i.async=!!a.tag.node,i.ack&&(s["@"]=i.ack),v(t,o,f),i.async&&(i.and||a.on("node",function(t){this.to.next(t),t===i.map[t.get]&&(i.souls[t.get]=!1,v(t.put,e,t),v(i.souls,function(t){return t?t:void 0})||i.c||(i.c=1,this.off(),v(i.map,r,i)))}),i.and=!0,a.on("node",s))}function o(t,n){var o=this.ctx,e=o.graph,r=this.msg,a=r.get,u=r.put,s=r.$._;e[a]=i.state.to(u,n,e[a]),o.async||(s.put=i.state.to(u,n,s.put))}function e(t,n){var o=this,e=o.put,r=o.$._;r.put=i.state.to(e,n,r.put)}function r(t){t.$&&(this.cat.stop=this.stop,t.$._.on("in",t),this.cat.stop=null)}i.on.put=function(o,e){var u=e._,s={$:e,graph:u.graph,put:{},map:{},souls:{},machine:i.state(),ack:o["@"],cat:u,stop:{}};return i.graph.is(o.put,null,t,s)||(s.err="Error: Invalid graph!"),s.err?u.on("in",{"@":o["#"],err:i.log(s.err)}):(v(s.put,n,s),s.async||v(s.map,r,s),a!==s.defer&&setTimeout(function(){i.on.put(o,e)},s.defer-s.machine),void(s.diff&&u.on("put",h(o,{put:s.diff}))))},i.on.get=function(t,n){var o,e=n._,r=t.get,a=r[m],u=e.graph[a],s=r[b],f=e.next||(e.next={}),c=f[a];if(d(a,"*")){var l={};i.obj.map(e.graph,function(t,n){i.text.match(n,a)&&(l[n]=i.obj.copy(t))}),i.obj.empty(l)||e.on("in",{"@":t["#"],how:"*",put:l,$:n})}if(!u)return e.on("get",t);if(s){if(!d(u,s))return e.on("get",t);u=i.state.to(u,s)}else u=i.obj.copy(u);u=i.graph.node(u),o=(c||k).ack,e.on("in",{"@":t["#"],how:"mem",put:u,$:n}),e.on("get",t)}}(),function(){i.chain.opt=function(t){t=t||{};var n=this,o=n._,e=t.peers||t;return p(t)||(t={}),p(o.opt)||(o.opt=t),f(e)&&(e=[e]),u(e)&&(e=v(e,function(t,n,o){o(t,{url:t})}),p(o.opt.peers)||(o.opt.peers={}),o.opt.peers=h(e,o.opt.peers)),o.opt.peers=o.opt.peers||{},h(t,o.opt),i.on("opt",o),o.opt.uuid=o.opt.uuid||function(){return g()+c(12)},n}}();var a,u=i.list.is,s=i.text,f=s.is,c=s.random,l=i.obj,p=l.is,d=l.has,h=l.to,v=l.map,g=(l.copy,i.state.lex),m=i.val.rel._,b=".",k=(i.node._,i.val.link.is,{});o.debug=function(t,n){return o.debug.i&&t===o.debug.i&&o.debug.i++&&(o.log.apply(o,arguments)||n)},i.log=function(){return!i.log.off&&o.log.apply(o,arguments),[].slice.call(arguments).join(" ")},i.log.once=function(t,n,o){return(o=i.log.once)[t]=o[t]||0,o[t]++||i.log(n)},i.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'!"),"undefined"!=typeof window&&((window.GUN=window.Gun=i).window=window);try{"undefined"!=typeof e&&(e.exports=i)}catch(y){}n.exports=i})(t,"./root"),t(function(){var n=t("./root");n.chain.back=function(t,i){var r;if(t=t||1,-1===t||1/0===t)return this._.root.$;if(1===t)return(this._.back||this._).$;var a=this,u=a._;if("string"==typeof t&&(t=t.split(".")),!(t instanceof Array)){if(t instanceof Function){for(var s,r={back:u};(r=r.back)&&o===(s=t(r,i)););return s}return n.num.is(t)?(u.back||u).$.back(t-1):this}var f=0,c=t.length,r=u;for(f;c>f;f++)r=(r||e)[t[f]];return o!==r?i?a:r:(r=u.back)?r.$.back(t,i):void 0};var o,e={}})(t,"./back"),t(function(){function n(t){var n,o,e,i=this.as,r=i.back,a=i.root;if(t.I||(t.I=i.$),t.$||(t.$=i.$),this.to.next(t),o=t.get){if(o["#"]||i.soul){if(o["#"]=o["#"]||i.soul,t["#"]||(t["#"]=b(9)),r=a.$.get(o["#"])._,o=o["."]){if(h(r.put,o)&&(n=r.$.get(o)._,(e=n.ack)||(n.ack=-1),r.on("in",{$:r.$,put:c.state.to(r.put,o),get:r.get}),e))return}else{if(e=r.ack,e||(r.ack=-1),h(r,"put")&&r.on("in",r),e)return;t.$=r.$}return a.ask(f,t),a.on("in",t)}if(a.now&&(a.now[i.id]=a.now[i.id]||!0,i.pass={}),o["."])return i.get?(t={get:{".":i.get},$:i.$},r.ask||(r.ask={}),r.ask[i.get]=t.$._,r.on("out",t)):(t={get:{},$:i.$},r.on("out",t));if(i.ack=i.ack||-1,i.get)return t.$=i.$,o["."]=i.get,(r.ask||(r.ask={}))[i.get]=t.$._,r.on("out",t)}return r.on("out",t)}function o(t){var n,o,r=this,s=r.as,f=s.root,d=t.$,b=(d||p)._||p,k=t.put;if(s.get&&t.get!==s.get&&(t=g(t,{get:s.get})),s.has&&b!==s&&(t=g(t,{$:s.$}),b.ack&&(s.ack=b.ack)),l===k){if(o=b.put,r.to.next(t),s.soul)return;if(l===o&&l!==b.put)return;return i(s,t,r),s.has&&u(s,t),v(b.echo,s.id),void v(s.map,b.id)}if(s.soul)return r.to.next(t),i(s,t,r),void(s.next&&m(k,a,{msg:t,cat:s}));if(!(n=c.val.link.is(k)))return c.val.is(k)?(s.has||s.soul?u(s,t):(b.has||b.soul)&&((b.echo||(b.echo={}))[s.id]=b.echo[b.id]||s,(s.map||(s.map={}))[b.id]=s.map[b.id]||{at:b}),r.to.next(t),void i(s,t,r)):(s.has&&b!==s&&h(b,"put")&&(s.put=b.put),(n=c.node.soul(k))&&b.has&&(b.put=s.root.$.get(n)._.put),o=(f.stop||{})[b.id],r.to.next(t),e(s,t,b,n),i(s,t,r),void(s.next&&m(k,a,{msg:t,cat:s})));f.stop;o=f.stop||{},o=o[b.id]||(o[b.id]={}),o.is=o.is||b.put,o[s.id]=b.put||!0,r.to.next(t),e(s,t,b,n),i(s,t,r)}function e(t,n,o,i){if(i&&k!==t.get){var r=t.root.$.get(i)._;t.has?o=r:o.has&&e(o,n,o,i),o!==t&&(o.$||(o={}),(o.echo||(o.echo={}))[t.id]=o.echo[t.id]||t,t.has&&!(t.map||p)[o.id]&&u(t,n),r=o.id?(t.map||(t.map={}))[o.id]=t.map[o.id]||{at:o}:{},(i!==r.link||r.pass||t.pass)&&(t.pass&&(c.obj.map(t.map,function(t){t.pass=!0}),v(t,"pass")),r.pass&&v(r,"pass"),t.has&&(t.link=i),s(t,r.link=i)))}}function i(t,n){t.echo&&m(t.echo,r,n)}function r(t){t&&t.on&&t.on("in",this)}function a(t,n){var o,e,i,r=this.cat,a=r.next||p,u=this.msg;(k!==n||a[n])&&(e=a[n])&&(e.has?(l!==e.put&&c.val.link.is(t)||(e.put=t),o=e.$):(i=u.$)&&(i=(o=u.$.get(n))._,l!==i.put&&c.val.link.is(t)||(i.put=t)),e.on("in",{put:t,get:n,$:o,via:u}))}function u(t,n){if(t.has||t.soul){{var o=t.map;t.root}t.map=null,t.has&&(t.link=null),(t.pass||n["@"]||null!==o)&&(l===o&&c.val.link.is(t.put)||(m(o,function(n){(n=n.at)&&v(n.echo,t.id)}),o=t.put,m(t.next,function(n,e){return l===o&&l!==t.put?!0:(n.put=l,n.ack&&(n.ack=-1),void n.on("in",{get:e,$:n.$,put:l}))})))}}function s(t,n){var o=t.root.$.get(n)._;(!t.ack||(o.on("out",{get:{"#":n}}),t.ask))&&(o=t.ask,c.obj.del(t,"ask"),m(o||t.next,function(t,o){t.on("out",{get:{"#":n,".":o}})}),c.obj.del(t,"ask"))}function f(t){var n=this.as,o=n.get||p,e=n.$._,i=(t.put||p)[o["#"]];if(e.ack&&(e.ack=e.ack+1||1),!t.put||o["."]&&!h(i,e.get)){if(e.put!==l)return;return void e.on("in",{get:e.get,put:e.put=l,$:e.$,"@":t["@"]})}return k==o["."]?void e.on("in",{get:e.get,put:c.val.link.ify(o["#"]),$:e.$,"@":t["@"]}):(t.$=e.root.$,void c.on.put(t,e.root.$))}var c=t("./root");c.chain.chain=function(t){var e,i=this,r=i._,a=new(t||i).constructor(i),u=a._;return u.root=e=r.root,u.id=++e.once,u.back=i._,u.on=c.on,u.on("in",o,u),u.on("out",n,u),a};var l,p={},d=c.obj,h=d.has,v=(d.put,d.del),g=d.to,m=d.map,b=c.text.random,k=(c.val.rel._,c.node._)})(t,"./chain"),t(function(){function n(t,n){var o=n._,e=o.next,i=n.chain(),r=i._;return e||(e=o.next={}),e[r.get=t]=r,n===o.root.$?r.soul=t:(o.soul||o.has)&&(r.has=t),r}function o(t,n,o,e){var i,r=t._;return(i=r.soul)?(n(i,e,r),t):(i=r.link)?(n(i,e,r),t):(t.get(function(t,o){o.rid(t);var r=(r=t.$)&&r._||{};i=r.link||r.soul||c.is(t.put)||l(t.put),n(i,e,t,o)},{out:{get:{".":!0}}}),t)}function e(t){var n,o=this,e=o.as,i=e.at,r=i.root,u=t.$,f=(u||{})._||{},l=t.put||f.put;if((n=r.now)&&o!==n[e.now])return o.to.next(t);if(o.seen&&f.id&&o.seen[f.id])return o.to.next(t);if((n=l)&&n[c._]&&(n=c.is(n))&&(n=(t.$$=f.root.gun.get(n))._,a!==n.put&&(t=s(t,{put:l=n.put}))),(n=r.mum)&&f.id){if(n[f.id])return;a===l||c.is(l)||(n[f.id]=!0)}return e.use(t,o),o.stun?void(o.stun=null):void o.to.next(t)}function i(t){var n=this.on;if(!t||n.soul||n.has)return this.off();if(t=(t=(t=t.$||t)._||t).id){{var o,e;n.map}return(o=(e=this.seen||(this.seen={}))[t])?!0:void(e[t]=!0)}}var r=t("./root");r.chain.get=function(t,a,u){var s,l;if("string"!=typeof t){if(t instanceof Function){if(!0===a)return o(this,t,a,u);s=this;var d,h=s._,v=h.root,l=v.now;u=a||{},u.at=h,u.use=t,u.out=u.out||{},u.out.get=u.out.get||{},(d=h.on("in",e,u)).rid=i,(v.now={$:1})[u.now=h.id]=d;var g=v.mum;return v.mum={},h.on("out",u.out),v.mum=g,v.now=l,s}return f(t)?this.get(""+t,a,u):(l=c.is(t))?this.get(l,a,u):((u=this.chain())._.err={err:r.log("Invalid get request!",t)},a&&a.call(u,u._.err),u)}var m=this,b=m._,k=b.next||p;return(s=k[t])||(s=n(t,m)),s=s.$,(l=b.stun)&&(s._.stun=s._.stun||l),a&&a instanceof Function&&s.get(a,u),s};var a,u=r.obj,s=(u.has,r.obj.to),f=r.num.is,c=r.val.link,l=r.node.soul,p=(r.node._,{})})(t,"./get"),t(function(){function n(t){t.batch=i;var n=t.opt||{},o=t.env=c.state.map(a,n.state);return o.soul=t.soul,t.graph=c.graph.ify(t.data,o,t),o.err?((t.ack||m).call(t,t.out={err:c.log(o.err)}),void(t.res&&t.res())):void t.batch()}function e(t){return void(t&&t())}function i(){var t=this;t.graph&&!v(t.stun,r)&&(t.res=t.res||function(t){t&&t()},t.res(function(){var n=t.$.back(-1)._,o=n.ask(function(o){n.root.on("ack",o),o.err&&c.log(o),o.lack||this.off(),t.ack&&t.ack(o,this)},t.opt),e=n.root.now;p.del(n.root,"now");var i=n.root.mum;n.root.mum={},t.ref._.on("out",{$:t.ref,put:t.out=t.env.graph,opt:t.opt,"#":o}),n.root.mum=i?p.to(i,n.root.mum):i,n.root.now=e},t),t.res&&t.res())}function r(t){return t?!0:void 0}function a(t,n,o,e){var i=this,r=c.is(t);!n&&e.path.length&&(i.res||b)(function(){var n=e.path,o=i.ref,a=(i.opt,0),s=n.length;for(a;s>a;a++)o=o.get(n[a]);r&&(o=t);var f=o._.dub;return f||(f=c.node.soul(e.obj))?(o.back(-1).get(f),void e.soul(f)):((i.stun=i.stun||{})[n]=!0,void o.get(u,!0,{as:{at:e,as:i,p:n}}))},{as:i,at:e})}function u(t,n,o,e){var n=n.as,i=n.at;n=n.as;var r=((o||{}).$||{})._||{};return t=r.dub=r.dub||t||c.node.soul(i.obj)||c.node.soul(o.put||r.put)||c.val.rel.is(o.put||r.put)||(n.via.back("opt.uuid")||c.text.random)(),e&&(e.stun=!0),t?void s(r,r.dub=t,i,n):void r.via.back("opt.uuid")(function(t,o){return t?c.log(t):void s(r,r.dub=r.dub||o,i,n)})}function s(t,n,o,e){t.$.back(-1).get(n),o.soul(n),e.stun[o.path]=!1,e.batch()}function f(t,n,e,i){if(n=n.as,e.$&&e.$._){if(e.err)return void o.log("Please report this as an issue! Put.any.err");var r,a=e.$._,u=a.put,s=n.opt||{};if(!(r=n.ref)||!r._.now){if(i&&(i.stun=!0),n.ref!==n.$){if(r=n.$._.get||a.get,!r)return void o.log("Please report this as an issue! Put.no.get");n.data=h({},r,n.data),r=null}if(l===u){if(!a.get)return;t||(r=a.$.back(function(t){return t.link||t.soul?t.link||t.soul:void(n.data=h({},t.get,n.data))})),r=r||a.get,a=a.root.$.get(r)._,n.soul=r,u=n.data}return n.not||(n.soul=n.soul||t)||(n.path&&d(n.data)?n.soul=(s.uuid||n.via.back("opt.uuid")||c.text.random)():(k==a.get&&(n.soul=(a.put||g)["#"]||a.dub),n.soul=n.soul||a.soul||a.soul||(s.uuid||n.via.back("opt.uuid")||c.text.random)()),n.soul)?void n.ref.put(n.data,n.soul,n):void n.via.back("opt.uuid")(function(t,o){return t?c.log(t):void n.ref.put(n.data,n.soul=o,n)})}}}var c=t("./root");c.chain.put=function(t,o,i){var r,a=this,u=a._,s=u.root.$;return i=i||{},i.data=t,i.via=i.$=i.via||i.$||a,"string"==typeof o?i.soul=o:i.ack=i.ack||o,u.soul&&(i.soul=u.soul),i.soul||s===a?d(i.data)?(i.soul=i.soul||(i.not=c.node.soul(i.data)||(i.via.back("opt.uuid")||c.text.random)()),i.soul?(i.$=a=s.get(i.soul),i.ref=i.$,n(i),a):(i.via.back("opt.uuid")(function(t,n){return t?c.log(t):void(i.ref||i.$).put(i.data,i.soul=n,i)}),a)):((i.ack||m).call(i,i.out={err:c.log("Data saved to the root level of the graph must be a node (an object), not a",typeof i.data,'of "'+i.data+'"!')}),i.res&&i.res(),a):c.is(t)?(t.get(function(t,n,e){return!t&&c.val.is(e.put)?c.log("The reference you are saving is a",typeof e.put,'"'+e.put+'", not a node (object)!'):void a.put(c.val.rel.ify(t),o,i)},!0),a):(i.ref=i.ref||s._===(r=u.back)?a:r.$,i.ref._.soul&&c.val.is(i.data)&&u.get?(i.data=h({},u.get,i.data),i.ref.put(i.data,i.soul,i),a):(i.ref.get(f,!0,{as:i}),i.out||(i.res=i.res||e,i.$._.stun=i.ref._.stun),a))};var l,p=c.obj,d=p.is,h=p.put,v=p.map,g={},m=function(){},b=function(t,n){t.call(n||g)},k=c.node._})(t,"./put"),t(function(n){var o=t("./root");t("./chain"),t("./back"),t("./put"),t("./get"),n.exports=o})(t,"./index"),t(function(){function n(t,n){{var o,e=this,r=t.$,a=(r||{})._||{},u=a.put||t.put;e.at}if(i!==u){if(o=t.$$){if(o=t.$$._,i===o.put)return;u=o.put}e.change&&(u=t.put),e.as?e.ok.call(e.as,t,n):e.ok.call(r,u,t.get,t,n)}}function o(t,n,r){var u,f,c=this.as,l=(c.at,t.$),p=l._,d=p.put||t.put;return(f=t.$$)&&(u=f=t.$$._,i!==u.put&&(d=u.put)),(f=n.wait)&&(f=f[p.id])&&clearTimeout(f),!r&&(i===d||p.soul||p.link||u&&!(0 .once, apologies unexpected."),this.once(t,n)},e.chain.once=function(t,n){var r=this,a=r._,u=a.put;if(0=(o.batch||1e3)?s():void(e||(e=setTimeout(s,o.wait||1)))}),n.on("get",function(t){function e(){if(s&&(i=s["#"])){var e=s["."];r=a[i]||u,r&&e&&(r=Gun.state.to(r,e)),(r||Gun.obj.empty(o.peers))&&n.on("in",{"@":t["#"],put:Gun.graph.node(r),how:"lS",lS:t.I})}}this.to.next(t);var i,r,u,s=t.get;Gun.debug?setTimeout(e,1):e()});var u=function(t,n,o,e){a[e]=Gun.state.to(o,n,a[e])},s=function(u){var f;r=0,clearTimeout(e),e=!1;var c=i;i={},u&&(a=u);try{t.setItem(o.prefix,JSON.stringify(a))}catch(l){Gun.log(f=(l||"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 ."),n.on("localStorage:error",{err:f,file:o.prefix,flush:a,retry:s})}(f||Gun.obj.empty(o.peers))&&Gun.obj.map(c,function(t,o){n.on("in",{"@":o,err:f,ok:0})})}}})}})(t,"./adapters/localStorage"),t(function(n){function e(t){var n=function(){},u=t.opt||{};return u.log=u.log||o.log,u.gap=u.gap||u.wait||1,u.pack=u.pack||.3*(u.memory?1e3*u.memory*1e3:1399e6),n.out=function(o){var e;return this.to&&this.to.next(o),(e=o["@"])&&(e=t.dup.s[e])&&(e=e.it)&&e.mesh?(n.say(o,e.mesh.via,1),void(e["##"]=o["##"])):void n.say(o)},t.on("create",function(o){o.opt.pid=o.opt.pid||i.text.random(9),this.to.next(o),t.on("out",n.out)}),n.hear=function(o,e){if(o){var r,a,s,f=t.dup,c=o[0];if(u.pack<=o.length)return n.say({dam:"!",err:"Message too big!"},e);try{s=JSON.parse(o)}catch(l){u.log("DAM JSON parse error",l)}if("{"===c){if(!s)return;if(f.check(r=s["#"]))return;if(f.track(r,!0).it=s,(c=s["@"])&&s.put&&(a=s["##"]||(s["##"]=n.hash(s)),(c+=a)!=r)){if(f.check(c))return;(c=f.s)[a]=c[r]}return(s.mesh=function(){}).via=e,(c=s["><"])&&(s.mesh.to=i.obj.map(c.split(","),function(t,n,o){o(t,!0)})),s.dam?void((c=n.hear[s.dam])&&c(s,e,t)):void t.on("in",s)}if("["!==c);else{if(!s)return;for(var p,d=0;p=s[d++];)n.hear(p,e)}}},function(){function o(t){var n=t.batch;if(n&&(t.batch=t.tail=null,n.length))try{e(JSON.stringify(n),t)}catch(o){u.log("DAM JSON stringify error",o)}}function e(t,n){var o=n.wire;try{o.send?o.send(t):n.say&&n.say(t)}catch(e){(n.queue=n.queue||[]).push(t)}}n.say=function(r,s,f){if(!s)return void i.obj.map(u.peers,function(t){n.say(r,t)});var c,l,p,d=s.wire||u.wire&&u.wire(s);if(d&&(l=r.mesh||a,s!==l.via&&((p=l.raw)||(p=n.raw(r)),!((c=r["@"])&&(c=t.dup.s[c])&&(c=c.it)&&c.get&&c["##"]&&c["##"]===r["##"]||(c=l.to)&&(c[s.url]||c[s.id])&&!f)))){if(s.batch){if(s.tail=(s.tail||0)+p.length,s.tail<=u.pack)return void s.batch.push(p);o(s)}s.batch=[],setTimeout(function(){o(s)},u.gap),e(p,s)}}}(),function(){function o(t,n){var o;return n instanceof Object?(i.obj.map(Object.keys(n).sort(),a,{to:o={},on:n}),o):n}function a(t){this.to[t]=this.on[t]}n.raw=function(e){if(!e)return"";var a,c,l,p=t.dup,d=e.mesh||{};if(l=d.raw)return l;if("string"==typeof e)return e;e["@"]&&(l=e.put)&&((c=e["##"])||(a=s(l,o)||"",c=n.hash(e,a),e["##"]=c),(l=p.s)[c=e["@"]+c]=l[e["#"]],e["#"]=c||e["#"],a&&((e=i.obj.to(e)).put=f));var h=0,v=[];i.obj.map(u.peers,function(t){return v.push(t.url||t.id),++h>9?!0:void 0}),e["><"]=v.join();var g=s(e);return r!==a&&(l=g.indexOf(f,g.indexOf("put")),g=g.slice(0,l-1)+a+g.slice(l+f.length+1)),d&&(d.raw=g),g},n.hash=function(t,n){return e.hash(n||s(t.put,o)||"")||t["#"]||i.text.random(9)};var s=JSON.stringify,f=":])([:"}(),n.hi=function(o){var e=o.wire||{};o.id||o.url?(u.peers[o.url||o.id]=o,i.obj.del(u.peers,e.id)):(e=e.id=e.id||i.text.random(9),n.say({dam:"?"},u.peers[e]=o)),e.hied||t.on(e.hied="hi",o),e=o.queue,o.queue=[],i.obj.map(e,function(t){n.say(t,o)})},n.bye=function(n){i.obj.del(u.peers,n.id),t.on("bye",n)},n.hear["!"]=function(t){u.log("Error:",t.err)},n.hear["?"]=function(t,o){return t.pid?(o.id=o.id||t.pid,void n.hi(o)):n.say({dam:"?",pid:u.pid,"@":t["#"]},o)},n}var i=t("../type");e.hash=function(t){ +if("string"!=typeof t)return{err:1};var n=0;if(!t.length)return n;for(var o,e=0,i=t.length;i>e;++e)o=t.charCodeAt(e),n=(n<<5)-n+o,n|=0;return n};var r,a={};Object.keys=Object.keys||function(t){return map(t,function(t,n,o){o(n)})};try{n.exports=e}catch(u){}})(t,"./adapters/mesh"),t(function(){var n=t("../index");n.Mesh=t("./mesh"),n.on("opt",function(t){function o(t){try{if(!t||!t.url)return o&&o(t);var n=t.url.replace("http","ws"),o=t.wire=new i.WebSocket(n);return o.onclose=function(){i.mesh.bye(t),e(t)},o.onerror=function(n){e(t),n&&"ECONNREFUSED"===n.code},o.onopen=function(){i.mesh.hi(t)},o.onmessage=function(n){n&&i.mesh.hear(n.data||n,t)},o}catch(r){}}function e(t){clearTimeout(t.defer),t.defer=setTimeout(function(){o(t)},2e3)}this.to.next(t);var i=t.opt;if(!t.once&&!1!==i.WebSocket){var r;"undefined"!=typeof window&&(r=window),"undefined"!=typeof global&&(r=global),r=r||{};var a=i.WebSocket||r.WebSocket||r.webkitWebSocket||r.mozWebSocket;if(a){i.WebSocket=a;{i.mesh=i.mesh||n.Mesh(t),i.wire}i.wire=o}}})})(t,"./adapters/websocket")}(); \ No newline at end of file diff --git a/lib/normalize.js b/lib/normalize.js new file mode 100644 index 00000000..901f1671 --- /dev/null +++ b/lib/normalize.js @@ -0,0 +1,139 @@ +;(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); + }); + }) + }; + 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 = t.toLowerCase()) +'>').append(a.$.contents())); + h.attr(a.$, a.attr, a.attrs); + } + ,function(a, tmp){ // lookahead + if((tmp = n.joint(a.$,1)) && (t = t.contents()).length === 1 && a.tag === n.tag(t = t.first())){ + a.$.append(t.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){ // exclude + var t; + if(!n.get(a.opt,'tags.' + a.tag) + || ((t = n.get(a.opt,'tags.'+ a.tag +'.exclude')) + && a.$.parents($.map(t,function(i,v){return v})+' ').length) + ){ + a.$.replaceWith(a.$.contents()); + } + } + ,function(a){ // prior + var t; + if((t = n.joint(a.$)).length && a.tag === n.tag(t)){ + t.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; +}()); \ No newline at end of file diff --git a/lib/reboot.js b/lib/reboot.js new file mode 100644 index 00000000..9a2ba612 --- /dev/null +++ b/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()); + +}()); \ No newline at end of file diff --git a/lib/server.js b/lib/server.js index a893322e..0c698be1 100644 --- a/lib/server.js +++ b/lib/server.js @@ -3,15 +3,16 @@ 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('../axe');}catch(e){} require('./file'); require('./evict'); if('debug' === process.env.GUN_ENV){ require('./debug') } diff --git a/package.json b/package.json index 47c7d85f..a8f8ebfa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gun", - "version": "0.9.99997", + "version": "0.9.99998", "description": "A realtime, decentralized, offline-first, graph data synchronization engine.", "main": "index.js", "browser": "gun.min.js", diff --git a/sea.js b/sea.js index 7c08fc81..ca47037a 100644 --- a/sea.js +++ b/sea.js @@ -27,15 +27,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; @@ -320,8 +314,18 @@ var S = USE('./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(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... @@ -383,7 +387,7 @@ var S = USE('./settings'); var sha256hash = USE('./sha256'); - SEA.sign = SEA.sign || (async (data, pair, cb) => { try { + SEA.sign = SEA.sign || (async (data, pair, cb, opt) => { try { if(data && data.slice && 'SEA{' === data.slice(0,4) && '"m":' === data.slice(4,8)){ @@ -393,6 +397,10 @@ if(cb){ try{ cb(data) }catch(e){console.log(e)} } return data; } + 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) @@ -422,7 +430,7 @@ var parse = USE('./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)? @@ -431,6 +439,8 @@ 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) @@ -474,8 +484,12 @@ var aeskey = USE('./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) @@ -507,8 +521,12 @@ var parse = USE('./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) .then((aes) => (/*shim.ossl ||*/ shim.subtle).decrypt({ // Keeping aesKey scope as private as possible... @@ -530,8 +548,12 @@ var SEA = USE('./root'); var shim = USE('./shim'); var S = USE('./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 @@ -576,6 +598,7 @@ ;USE(function(module){ // Old Code... + try{ const __gky10 = USE('./shim') const crypto = __gky10.crypto const subtle = __gky10.subtle @@ -594,8 +617,8 @@ const keysToEcdsaJwk = __gky11.jwk const sha1hash = USE('./sha1') const sha256hash = USE('./sha256') - const recallCryptoKey = USE('./remember') const parseProps = USE('./parse') + }catch(e){} // Practical examples about usage found from ./test/common.js const SEA = USE('./root'); @@ -609,7 +632,7 @@ // This is easy way to use IndexedDB, all methods are Promises // Note: Not all SEA interfaces have to support this. - SEA.EasyIndexedDB = EasyIndexedDB; + try{SEA.EasyIndexedDB = EasyIndexedDB;}catch(e){} // This is Buffer used in SEA and usable from Gun/SEA application also. // For documentation see https://nodejs.org/api/buffer.html @@ -656,419 +679,6 @@ module.exports = SEA })(USE, './sea'); - ;USE(function(module){ - var SEA = USE('./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 - })(USE, './query'); - - ;USE(function(module){ - var SEA = USE('./sea'); - var Gun = SEA.Gun; - const queryGunAliases = USE('./query') - const parseProps = USE('./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; try{tmp = window.sessionStorage}catch(e){} - if(tmp && gunRoot._.opt.remember){ // TODO: Bug! This needs to be moved to finalize? - tmp.alias = alias; - tmp.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; - })(USE, './authenticate'); - - ;USE(function(module){ - const authsettings = USE('./settings') - const SEA = USE('./sea'); - const Gun = SEA.Gun; - //const { scope: seaIndexedDb } = USE('./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 - })(USE, './update'); - - ;USE(function(module){ - const SEA = USE('./sea'); - const Gun = SEA.Gun; - const Buffer = USE('./buffer') - const authsettings = USE('./settings') - const updateStorage = USE('./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 - })(USE, './persist'); - - ;USE(function(module){ - const authPersist = USE('./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 - })(USE, './login'); - - ;USE(function(module){ - const Buffer = USE('./buffer') - const authsettings = USE('./settings') - //const { scope: seaIndexedDb } = USE('./indexed') - const queryGunAliases = USE('./query') - const parseProps = USE('./parse') - const updateStorage = USE('./update') - const SEA = USE('./sea') - const Gun = SEA.Gun; - const finalizeLogin = USE('./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 - })(USE, './recall'); - - ;USE(function(module){ - const authPersist = USE('./persist') - const authsettings = USE('./settings') - //const { scope: seaIndexedDb } = USE('./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 - })(USE, './leave'); - ;USE(function(module){ var Gun = USE('./sea').Gun; Gun.chain.then = function(cb){ @@ -1100,7 +710,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; @@ -1115,22 +725,75 @@ // TODO: This needs to be split into all separate functions. // Not just everything thrown into 'create'. - const SEA = USE('./sea') - const User = USE('./user') - const authRecall = USE('./recall') - const authsettings = USE('./settings') - const authenticate = USE('./authenticate') - const finalizeLogin = USE('./login') - const authLeave = USE('./leave') - const _initial_authsettings = USE('./settings').recall - const Gun = SEA.Gun; + var SEA = USE('./sea'); + var User = USE('./user'); + var authsettings = USE('./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._); + 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 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. + var ack = {err: Gun.log('User already created!')}; + cat.ing = false; + cb(ack); + gun.leave(); + return; + } + 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){ + act.pair = pair || {}; + // the user's public key doesn't need to be signed. But everything else needs to be signed with it! + act.data = {pub: pair.pub}; + SEA.sign(alias, pair, act.d); + } + act.d = function(alias){ + act.data.alias = alias; + SEA.sign(act.pair.epub, act.pair, act.e); + } + act.e = function(epub){ + act.data.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 = auth; + SEA.sign({ek: auth, s: act.salt}, act.pair, act.g); + } + act.g = function(auth){ var tmp; + 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){ + var gun = this, cat = (gun._), root = gun.back(-1); cb = cb || function(){}; if(cat.ing){ cb({err: Gun.log("User is already being created or authenticated!"), wait: true}); @@ -1138,160 +801,116 @@ } 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) { - // 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) - cat.ing = false; - gun.leave(); - return reject({ err: err }) + var pair = (alias.pub || alias.epub)? alias : (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); } - 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) + if(act.name){ return act.f(data) } + act.c((act.data = data).auth); + } + 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.') } - }) - return gun; // gun chain commands must return gun chains! - } - // 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._); - if(cat.ing){ - cb({err: "User is already being created or authenticated!", wait: true}); - return gun; + root.get(get).once(act.a); } - cat.ing = true; - - 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.c = function(auth){ + if(u === auth){ return act.b() } + SEA.work(pass, (act.auth = auth).s, act.d); // the proof of work is evidence that we've spent some time/effort trying to log in, this slows brute force. } - - var key = (alias.pub || alias.epub)? alias : (pass.pub || pass.epub)? pass : null; - if(key){ - (async function(){ try { - alias = (typeof alias === 'string')? alias : null; - const login = finalizeLogin(alias, key, gunRoot, { pin: pin }) - login.catch(putErr('Finalizing login failed!')) - return cat.ing = false, cb(await login), gun; - } catch(e){ - return cat.ing = false, gun.leave(), cb(e), gun; - }}()) - return gun; + act.d = function(proof){ + if(u === proof){ return act.b() } + SEA.decrypt(act.auth.ek, proof, act.e); } - - 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; + act.e = function(half){ + if(u === half){ return act.b() } + act.half = half; + act.f(act.data); } - - (async function(){ try { - const keys = await authenticate(alias, pass, gunRoot) - if (!keys) { - return putErr('Auth attempt failed!')({ message: 'No keys' }) + 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; + opt.change? act.z() : cb(at); + if(SEA.window && ((gun.back('user')._).opt||opt).remember){ + // TODO: this needs to be modular. + var sS = {}; try{sS = window.sessionStorage}catch(e){} + sS.recall = true; + sS.alias = alias; + sS.tmp = pass; } - 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; + 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); } - } catch (e) { - return putErr('Auth attempt failed!')(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){ + SEA.sign({ek: auth, s: act.salt}, act.pair, act.w); + } + act.w = function(auth){ + 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; @@ -1299,86 +918,52 @@ delete user._.sea; } if(SEA.window){ - var tmp; try{tmp = window.sessionStorage}catch(e){}; tmp = tmp || {}; - delete tmp.alias; - delete tmp.tmp; + var sS = {}; try{sS = window.sessionStorage}catch(e){}; + delete sS.alias; + delete sS.tmp; + delete sS.recall; } - 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, tmp; - if(o && o.sessionStorage){ + User.prototype.recall = function(opt, cb){ + var gun = this, root = gun.back(-1), tmp; + opt = opt || {}; + if(opt && opt.sessionStorage){ if(SEA.window){ - try{tmp = window.sessionStorage}catch(e){} - if(tmp){ - gunRoot._.opt.remember = true; - if(tmp.alias && tmp.tmp){ - gunRoot.user().auth(tmp.alias, tmp.tmp); + var sS = {}; try{sS = window.sessionStorage}catch(e){} + 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); } } } 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) @@ -1404,7 +989,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); @@ -1425,7 +1010,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); @@ -1555,9 +1140,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(val, (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; @@ -1590,7 +1175,7 @@ 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; @@ -1631,20 +1216,19 @@ //}); return; } - var pub = tmp; - if(pub !== user.pub){ + if((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(val, (user._).sea, function(data){ if(u === data){ return each.end({err: 'My signature fail.'}) } node[key] = data; check['any'+soul+key] = 0; @@ -1669,6 +1253,7 @@ } to.next(msg); // pass forward any data we do not know how to handle or process (this allows custom security protocols). } + var noop = {}; })(USE, './index'); }()); \ No newline at end of file diff --git a/sea/authenticate.js b/sea/authenticate.js deleted file mode 100644 index 91bc0d8e..00000000 --- a/sea/authenticate.js +++ /dev/null @@ -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; - \ No newline at end of file diff --git a/sea/buffer.js b/sea/buffer.js index c17702b7..1854bd1c 100644 --- a/sea/buffer.js +++ b/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 diff --git a/sea/create.js b/sea/create.js index a6c00cfd..96048918 100644 --- a/sea/create.js +++ b/sea/create.js @@ -2,22 +2,75 @@ // 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._); + 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 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. + var ack = {err: Gun.log('User already created!')}; + cat.ing = false; + cb(ack); + gun.leave(); + return; + } + 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){ + act.pair = pair || {}; + // the user's public key doesn't need to be signed. But everything else needs to be signed with it! + act.data = {pub: pair.pub}; + SEA.sign(alias, pair, act.d); + } + act.d = function(alias){ + act.data.alias = alias; + SEA.sign(act.pair.epub, act.pair, act.e); + } + act.e = function(epub){ + act.data.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 = auth; + SEA.sign({ek: auth, s: act.salt}, act.pair, act.g); + } + act.g = function(auth){ var tmp; + 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){ + var gun = this, cat = (gun._), root = gun.back(-1); cb = cb || function(){}; if(cat.ing){ cb({err: Gun.log("User is already being created or authenticated!"), wait: true}); @@ -25,234 +78,169 @@ } 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) { - // 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) - cat.ing = false; - gun.leave(); - return reject({ err: err }) + var pair = (alias.pub || alias.epub)? alias : (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); } - 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) - } - }) - return gun; // gun chain commands must return gun chains! - } - // 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._); - if(cat.ing){ - cb({err: "User is already being created or authenticated!", wait: true}); - return gun; + if(act.name){ return act.f(data) } + act.c((act.data = data).auth); } - 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; - } - - 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; - } - - (async function(){ try { - const keys = await authenticate(alias, pass, gunRoot) - if (!keys) { - return putErr('Auth attempt failed!')({ message: 'No keys' }) + 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.') } - 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; + root.get(get).once(act.a); + } + act.c = function(auth){ + if(u === auth){ return act.b() } + SEA.work(pass, (act.auth = auth).s, act.d); // 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){ + if(u === proof){ return act.b() } + SEA.decrypt(act.auth.ek, proof, act.e); + } + act.e = function(half){ + if(u === half){ return act.b() } + 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; + opt.change? act.z() : cb(at); + if(SEA.window && ((gun.back('user')._).opt||opt).remember){ + // TODO: this needs to be modular. + var sS = {}; try{sS = window.sessionStorage}catch(e){} + sS.recall = true; + sS.alias = alias; + sS.tmp = pass; } - } 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){ + SEA.sign({ek: auth, s: act.salt}, act.pair, act.w); + } + act.w = function(auth){ + 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){ + var sS = {}; try{sS = window.sessionStorage}catch(e){}; + delete sS.alias; + delete sS.tmp; + delete sS.recall; } - 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){ + var sS = {}; try{sS = window.sessionStorage}catch(e){} + 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); } } } 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 +266,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 +287,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); diff --git a/sea/decrypt.js b/sea/decrypt.js index d82a7f9f..94b5cdf5 100644 --- a/sea/decrypt.js +++ b/sea/decrypt.js @@ -6,8 +6,12 @@ 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) .then((aes) => (/*shim.ossl ||*/ shim.subtle).decrypt({ // Keeping aesKey scope as private as possible... diff --git a/sea/encrypt.js b/sea/encrypt.js index 6816efb3..c03a037b 100644 --- a/sea/encrypt.js +++ b/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) diff --git a/sea/index.js b/sea/index.js index 80a5d26f..a70f45e5 100644 --- a/sea/index.js +++ b/sea/index.js @@ -62,7 +62,7 @@ // if there is a request to read data from us, then... var soul = msg.get['#']; if(soul){ // for now, only allow direct IDs to be read. - if(typeof soul !== 'string'){ return to.next(msg) } // do not handle lexical cursors. + if(soul !== 'string'){ return to.next(msg) } // do not handle lexical cursors. if('alias' === soul){ // Allow reading the list of usernames/aliases in the system? return to.next(msg); // yes. } else @@ -111,9 +111,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(val, (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; @@ -146,7 +146,7 @@ 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; @@ -187,20 +187,19 @@ //}); return; } - var pub = tmp; - if(pub !== user.pub){ + if((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(val, (user._).sea, function(data){ if(u === data){ return each.end({err: 'My signature fail.'}) } node[key] = data; check['any'+soul+key] = 0; @@ -210,7 +209,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 +224,6 @@ } to.next(msg); // pass forward any data we do not know how to handle or process (this allows custom security protocols). } + var noop = {}; - + \ No newline at end of file diff --git a/sea/leave.js b/sea/leave.js deleted file mode 100644 index df497b8f..00000000 --- a/sea/leave.js +++ /dev/null @@ -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 - \ No newline at end of file diff --git a/sea/login.js b/sea/login.js deleted file mode 100644 index 1288aebf..00000000 --- a/sea/login.js +++ /dev/null @@ -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 - \ No newline at end of file diff --git a/sea/pair.js b/sea/pair.js index 9ff4651e..dd02e9d6 100644 --- a/sea/pair.js +++ b/sea/pair.js @@ -4,8 +4,18 @@ 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(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... diff --git a/sea/persist.js b/sea/persist.js deleted file mode 100644 index 511decc8..00000000 --- a/sea/persist.js +++ /dev/null @@ -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 - \ No newline at end of file diff --git a/sea/query.js b/sea/query.js deleted file mode 100644 index f256c8e0..00000000 --- a/sea/query.js +++ /dev/null @@ -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 - \ No newline at end of file diff --git a/sea/recall.js b/sea/recall.js deleted file mode 100644 index 998f39de..00000000 --- a/sea/recall.js +++ /dev/null @@ -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 - \ No newline at end of file diff --git a/sea/root.js b/sea/root.js index 7095ad34..7b99eb97 100644 --- a/sea/root.js +++ b/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; diff --git a/sea/sea.js b/sea/sea.js index 77032638..7eab76a2 100644 --- a/sea/sea.js +++ b/sea/sea.js @@ -1,5 +1,6 @@ // Old Code... + try{ const __gky10 = require('./shim') const crypto = __gky10.crypto const subtle = __gky10.subtle @@ -18,8 +19,8 @@ const keysToEcdsaJwk = __gky11.jwk const sha1hash = require('./sha1') const sha256hash = require('./sha256') - const recallCryptoKey = require('./remember') const parseProps = require('./parse') + }catch(e){} // Practical examples about usage found from ./test/common.js const SEA = require('./root'); @@ -75,7 +76,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 \ No newline at end of file diff --git a/sea/secret.js b/sea/secret.js index 3d38ba18..cf832004 100644 --- a/sea/secret.js +++ b/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 diff --git a/sea/shim.js b/sea/shim.js index 82dccd8a..28594a8b 100644 --- a/sea/shim.js +++ b/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 \ No newline at end of file diff --git a/sea/sign.js b/sea/sign.js index 399df922..05688cce 100644 --- a/sea/sign.js +++ b/sea/sign.js @@ -4,7 +4,7 @@ var S = require('./settings'); var sha256hash = require('./sha256'); - SEA.sign = SEA.sign || (async (data, pair, cb) => { try { + SEA.sign = SEA.sign || (async (data, pair, cb, opt) => { try { if(data && data.slice && 'SEA{' === data.slice(0,4) && '"m":' === data.slice(4,8)){ @@ -14,6 +14,10 @@ if(cb){ try{ cb(data) }catch(e){console.log(e)} } return data; } + 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) diff --git a/sea/update.js b/sea/update.js deleted file mode 100644 index a312d63f..00000000 --- a/sea/update.js +++ /dev/null @@ -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 - \ No newline at end of file diff --git a/sea/user.js b/sea/user.js index 7c0fadcd..daab6da3 100644 --- a/sea/user.js +++ b/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; diff --git a/sea/verify.js b/sea/verify.js index 128aa928..1946f6b5 100644 --- a/sea/verify.js +++ b/sea/verify.js @@ -6,7 +6,7 @@ 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)? @@ -15,6 +15,9 @@ 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']) @@ -27,7 +30,7 @@ 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(cb){ cb() } return; diff --git a/src/adapters/localStorage.js b/src/adapters/localStorage.js index 33361ab5..38aac62b 100644 --- a/src/adapters/localStorage.js +++ b/src/adapters/localStorage.js @@ -1,10 +1,12 @@ 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: noop, removeItem: noop, getItem: noop}; +} /* 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. diff --git a/src/on.js b/src/on.js index 4121852e..f9b3998a 100644 --- a/src/on.js +++ b/src/on.js @@ -78,19 +78,19 @@ function val(msg, eve, to){ var opt = this.as, cat = opt.at, gun = msg.$, at = gun._, data = at.put || msg.put, link, tmp; if(tmp = msg.$$){ link = tmp = (msg.$$._); - if(u === 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); } From 4896085d3a880e9186880bf90361f0bb45b28162 Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Sat, 17 Nov 2018 17:12:39 -0800 Subject: [PATCH 048/103] quick fix, republish ...999... --- package.json | 2 +- sea.js | 34 ++++------------------------------ 2 files changed, 5 insertions(+), 31 deletions(-) diff --git a/package.json b/package.json index a8f8ebfa..e89ff43a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gun", - "version": "0.9.99998", + "version": "0.9.99999", "description": "A realtime, decentralized, offline-first, graph data synchronization engine.", "main": "index.js", "browser": "gun.min.js", diff --git a/sea.js b/sea.js index ca47037a..40957b4f 100644 --- a/sea.js +++ b/sea.js @@ -597,46 +597,20 @@ })(USE, './secret'); ;USE(function(module){ - // Old Code... - try{ - const __gky10 = USE('./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 = USE('./indexed') - const Buffer = USE('./buffer') - var settings = USE('./settings'); - const __gky11 = USE('./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 = USE('./sha1') - const sha256hash = USE('./sha256') - const parseProps = USE('./parse') - }catch(e){} - + var shim = USE('./shim'); // Practical examples about usage found from ./test/common.js - const SEA = USE('./root'); + var SEA = USE('./root'); SEA.work = USE('./work'); SEA.sign = USE('./sign'); SEA.verify = USE('./verify'); SEA.encrypt = USE('./encrypt'); SEA.decrypt = USE('./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. - try{SEA.EasyIndexedDB = EasyIndexedDB;}catch(e){} + 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 || USE('./buffer'); // These SEA functions support now ony Promises or // async/await (compatible) code, use those like Promises. From 0ad2c74b794ef33a7adf3177f91effd0c7f55841 Mon Sep 17 00:00:00 2001 From: Jake Date: Wed, 21 Nov 2018 05:27:11 +0200 Subject: [PATCH 049/103] added react-native demo --- examples/react-native/.babelrc | 3 + examples/react-native/.buckconfig | 6 + examples/react-native/.flowconfig | 70 + examples/react-native/.gitattributes | 1 + examples/react-native/.gitignore | 56 + examples/react-native/.watchmanconfig | 1 + examples/react-native/README.md | 0 examples/react-native/android/app/BUCK | 65 + .../react-native/android/app/build.gradle | 151 ++ .../android/app/proguard-rules.pro | 17 + .../android/app/src/main/AndroidManifest.xml | 26 + .../app/src/main/assets/html/blank.html | 0 .../main/java/com/gundemo/MainActivity.java | 15 + .../java/com/gundemo/MainApplication.java | 47 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3056 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 5024 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2096 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 2858 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4569 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 7098 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 6464 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 10676 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 9250 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 15523 bytes .../app/src/main/res/values/strings.xml | 3 + .../app/src/main/res/values/styles.xml | 8 + examples/react-native/android/build.gradle | 39 + .../react-native/android/gradle.properties | 18 + .../android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54329 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + examples/react-native/android/gradlew | 172 ++ examples/react-native/android/gradlew.bat | 84 + examples/react-native/android/keystores/BUCK | 8 + .../keystores/debug.keystore.properties | 4 + examples/react-native/android/settings.gradle | 5 + examples/react-native/app.json | 4 + examples/react-native/index.js | 8 + .../react-native/ios/GunDemo-tvOS/Info.plist | 54 + .../ios/GunDemo-tvOSTests/Info.plist | 24 + .../ios/GunDemo.xcodeproj/project.pbxproj | 1590 ++++++++++++ .../react-native/ios/GunDemo/AppDelegate.h | 14 + .../react-native/ios/GunDemo/AppDelegate.m | 35 + .../ios/GunDemo/Base.lproj/LaunchScreen.xib | 42 + .../AppIcon.appiconset/Contents.json | 38 + .../ios/GunDemo/Images.xcassets/Contents.json | 6 + examples/react-native/ios/GunDemo/Info.plist | 60 + examples/react-native/ios/GunDemo/main.m | 16 + .../ios/GunDemoTests/GunDemoTests.m | 68 + .../react-native/ios/GunDemoTests/Info.plist | 24 + examples/react-native/package.json | 30 + examples/react-native/shim.js | 20 + examples/react-native/src/App/Demo.js | 152 ++ .../react-native/src/App/PolyFillCrypto.js | 92 + examples/react-native/src/App/app.js | 26 + examples/react-native/src/App/index.js | 1 + .../src/extensions/asyncStorageAdapter.js | 75 + examples/react-native/src/extensions/sea.js | 1215 ++++++++++ .../src/webview-crypto/MainWorker.d.ts | 14 + .../src/webview-crypto/MainWorker.js | 170 ++ .../src/webview-crypto/MainWorker.js.map | 1 + .../src/webview-crypto/WebViewWorker.d.ts | 6 + .../src/webview-crypto/WebViewWorker.js | 75 + .../src/webview-crypto/WebViewWorker.js.map | 1 + .../src/webview-crypto/asyncSerialize.d.ts | 8 + .../src/webview-crypto/asyncSerialize.js | 56 + .../src/webview-crypto/asyncSerialize.js.map | 1 + .../src/webview-crypto/compat.d.ts | 1 + .../react-native/src/webview-crypto/compat.js | 4 + .../src/webview-crypto/compat.js.map | 1 + .../src/webview-crypto/index.d.ts | 3 + .../react-native/src/webview-crypto/index.js | 4 + .../src/webview-crypto/index.js.map | 1 + .../src/webview-crypto/serializeBinary.d.ts | 5 + .../src/webview-crypto/serializeBinary.js | 160 ++ .../src/webview-crypto/serializeBinary.js.map | 1 + .../webview-crypto/webViewWorkerString.d.ts | 3 + .../src/webview-crypto/webViewWorkerString.js | 2135 +++++++++++++++++ .../webview-crypto/webViewWorkerString.js.map | 1 + 78 files changed, 7049 insertions(+) create mode 100644 examples/react-native/.babelrc create mode 100644 examples/react-native/.buckconfig create mode 100644 examples/react-native/.flowconfig create mode 100644 examples/react-native/.gitattributes create mode 100644 examples/react-native/.gitignore create mode 100644 examples/react-native/.watchmanconfig create mode 100644 examples/react-native/README.md create mode 100644 examples/react-native/android/app/BUCK create mode 100644 examples/react-native/android/app/build.gradle create mode 100644 examples/react-native/android/app/proguard-rules.pro create mode 100644 examples/react-native/android/app/src/main/AndroidManifest.xml create mode 100644 examples/react-native/android/app/src/main/assets/html/blank.html create mode 100644 examples/react-native/android/app/src/main/java/com/gundemo/MainActivity.java create mode 100644 examples/react-native/android/app/src/main/java/com/gundemo/MainApplication.java create mode 100644 examples/react-native/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 examples/react-native/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 examples/react-native/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 examples/react-native/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 examples/react-native/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 examples/react-native/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 examples/react-native/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 examples/react-native/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 examples/react-native/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 examples/react-native/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 examples/react-native/android/app/src/main/res/values/strings.xml create mode 100644 examples/react-native/android/app/src/main/res/values/styles.xml create mode 100644 examples/react-native/android/build.gradle create mode 100644 examples/react-native/android/gradle.properties create mode 100644 examples/react-native/android/gradle/wrapper/gradle-wrapper.jar create mode 100644 examples/react-native/android/gradle/wrapper/gradle-wrapper.properties create mode 100755 examples/react-native/android/gradlew create mode 100644 examples/react-native/android/gradlew.bat create mode 100644 examples/react-native/android/keystores/BUCK create mode 100644 examples/react-native/android/keystores/debug.keystore.properties create mode 100644 examples/react-native/android/settings.gradle create mode 100644 examples/react-native/app.json create mode 100644 examples/react-native/index.js create mode 100644 examples/react-native/ios/GunDemo-tvOS/Info.plist create mode 100644 examples/react-native/ios/GunDemo-tvOSTests/Info.plist create mode 100644 examples/react-native/ios/GunDemo.xcodeproj/project.pbxproj create mode 100644 examples/react-native/ios/GunDemo/AppDelegate.h create mode 100644 examples/react-native/ios/GunDemo/AppDelegate.m create mode 100644 examples/react-native/ios/GunDemo/Base.lproj/LaunchScreen.xib create mode 100644 examples/react-native/ios/GunDemo/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 examples/react-native/ios/GunDemo/Images.xcassets/Contents.json create mode 100644 examples/react-native/ios/GunDemo/Info.plist create mode 100644 examples/react-native/ios/GunDemo/main.m create mode 100644 examples/react-native/ios/GunDemoTests/GunDemoTests.m create mode 100644 examples/react-native/ios/GunDemoTests/Info.plist create mode 100644 examples/react-native/package.json create mode 100644 examples/react-native/shim.js create mode 100644 examples/react-native/src/App/Demo.js create mode 100644 examples/react-native/src/App/PolyFillCrypto.js create mode 100644 examples/react-native/src/App/app.js create mode 100644 examples/react-native/src/App/index.js create mode 100644 examples/react-native/src/extensions/asyncStorageAdapter.js create mode 100644 examples/react-native/src/extensions/sea.js create mode 100644 examples/react-native/src/webview-crypto/MainWorker.d.ts create mode 100644 examples/react-native/src/webview-crypto/MainWorker.js create mode 100644 examples/react-native/src/webview-crypto/MainWorker.js.map create mode 100644 examples/react-native/src/webview-crypto/WebViewWorker.d.ts create mode 100644 examples/react-native/src/webview-crypto/WebViewWorker.js create mode 100644 examples/react-native/src/webview-crypto/WebViewWorker.js.map create mode 100644 examples/react-native/src/webview-crypto/asyncSerialize.d.ts create mode 100644 examples/react-native/src/webview-crypto/asyncSerialize.js create mode 100644 examples/react-native/src/webview-crypto/asyncSerialize.js.map create mode 100644 examples/react-native/src/webview-crypto/compat.d.ts create mode 100644 examples/react-native/src/webview-crypto/compat.js create mode 100644 examples/react-native/src/webview-crypto/compat.js.map create mode 100644 examples/react-native/src/webview-crypto/index.d.ts create mode 100644 examples/react-native/src/webview-crypto/index.js create mode 100644 examples/react-native/src/webview-crypto/index.js.map create mode 100644 examples/react-native/src/webview-crypto/serializeBinary.d.ts create mode 100644 examples/react-native/src/webview-crypto/serializeBinary.js create mode 100644 examples/react-native/src/webview-crypto/serializeBinary.js.map create mode 100644 examples/react-native/src/webview-crypto/webViewWorkerString.d.ts create mode 100644 examples/react-native/src/webview-crypto/webViewWorkerString.js create mode 100644 examples/react-native/src/webview-crypto/webViewWorkerString.js.map diff --git a/examples/react-native/.babelrc b/examples/react-native/.babelrc new file mode 100644 index 00000000..d4b74b5b --- /dev/null +++ b/examples/react-native/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["module:metro-react-native-babel-preset"] +} diff --git a/examples/react-native/.buckconfig b/examples/react-native/.buckconfig new file mode 100644 index 00000000..934256cb --- /dev/null +++ b/examples/react-native/.buckconfig @@ -0,0 +1,6 @@ + +[android] + target = Google Inc.:Google APIs:23 + +[maven_repositories] + central = https://repo1.maven.org/maven2 diff --git a/examples/react-native/.flowconfig b/examples/react-native/.flowconfig new file mode 100644 index 00000000..1043c82d --- /dev/null +++ b/examples/react-native/.flowconfig @@ -0,0 +1,70 @@ +[ignore] +; We fork some components by platform +.*/*[.]android.js + +; Ignore "BUCK" generated dirs +/\.buckd/ + +; Ignore unexpected extra "@providesModule" +.*/node_modules/.*/node_modules/fbjs/.* + +; Ignore duplicate module providers +; For RN Apps installed via npm, "Libraries" folder is inside +; "node_modules/react-native" but in the source repo it is in the root +.*/Libraries/react-native/React.js + +; Ignore polyfills +.*/Libraries/polyfills/.* + +; Ignore metro +.*/node_modules/metro/.* + +[include] + +[libs] +node_modules/react-native/Libraries/react-native/react-native-interface.js +node_modules/react-native/flow/ +node_modules/react-native/flow-github/ + +[options] +emoji=true + +esproposal.optional_chaining=enable +esproposal.nullish_coalescing=enable + +module.system=haste +module.system.haste.use_name_reducers=true +# get basename +module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1' +# strip .js or .js.flow suffix +module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1' +# strip .ios suffix +module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1' +module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1' +module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1' +module.system.haste.paths.blacklist=.*/__tests__/.* +module.system.haste.paths.blacklist=.*/__mocks__/.* +module.system.haste.paths.blacklist=/node_modules/react-native/Libraries/Animated/src/polyfills/.* +module.system.haste.paths.whitelist=/node_modules/react-native/Libraries/.* + +munge_underscores=true + +module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' + +module.file_ext=.js +module.file_ext=.jsx +module.file_ext=.json +module.file_ext=.native.js + +suppress_type=$FlowIssue +suppress_type=$FlowFixMe +suppress_type=$FlowFixMeProps +suppress_type=$FlowFixMeState + +suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) +suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ +suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy +suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError + +[version] +^0.78.0 diff --git a/examples/react-native/.gitattributes b/examples/react-native/.gitattributes new file mode 100644 index 00000000..d42ff183 --- /dev/null +++ b/examples/react-native/.gitattributes @@ -0,0 +1 @@ +*.pbxproj -text diff --git a/examples/react-native/.gitignore b/examples/react-native/.gitignore new file mode 100644 index 00000000..5d647565 --- /dev/null +++ b/examples/react-native/.gitignore @@ -0,0 +1,56 @@ +# OSX +# +.DS_Store + +# Xcode +# +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +*.moved-aside +DerivedData +*.hmap +*.ipa +*.xcuserstate +project.xcworkspace + +# Android/IntelliJ +# +build/ +.idea +.gradle +local.properties +*.iml + +# node.js +# +node_modules/ +npm-debug.log +yarn-error.log + +# BUCK +buck-out/ +\.buckd/ +*.keystore + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/ + +*/fastlane/report.xml +*/fastlane/Preview.html +*/fastlane/screenshots + +# Bundle artifact +*.jsbundle diff --git a/examples/react-native/.watchmanconfig b/examples/react-native/.watchmanconfig new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/examples/react-native/.watchmanconfig @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/examples/react-native/README.md b/examples/react-native/README.md new file mode 100644 index 00000000..e69de29b diff --git a/examples/react-native/android/app/BUCK b/examples/react-native/android/app/BUCK new file mode 100644 index 00000000..5d9a8b3f --- /dev/null +++ b/examples/react-native/android/app/BUCK @@ -0,0 +1,65 @@ +# To learn about Buck see [Docs](https://buckbuild.com/). +# To run your application with Buck: +# - install Buck +# - `npm start` - to start the packager +# - `cd android` +# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` +# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck +# - `buck install -r android/app` - compile, install and run application +# + +lib_deps = [] + +for jarfile in glob(['libs/*.jar']): + name = 'jars__' + jarfile[jarfile.rindex('/') + 1: jarfile.rindex('.jar')] + lib_deps.append(':' + name) + prebuilt_jar( + name = name, + binary_jar = jarfile, + ) + +for aarfile in glob(['libs/*.aar']): + name = 'aars__' + aarfile[aarfile.rindex('/') + 1: aarfile.rindex('.aar')] + lib_deps.append(':' + name) + android_prebuilt_aar( + name = name, + aar = aarfile, + ) + +android_library( + name = "all-libs", + exported_deps = lib_deps, +) + +android_library( + name = "app-code", + srcs = glob([ + "src/main/java/**/*.java", + ]), + deps = [ + ":all-libs", + ":build_config", + ":res", + ], +) + +android_build_config( + name = "build_config", + package = "com.gundemo", +) + +android_resource( + name = "res", + package = "com.gundemo", + res = "src/main/res", +) + +android_binary( + name = "app", + keystore = "//android/keystores:debug", + manifest = "src/main/AndroidManifest.xml", + package_type = "debug", + deps = [ + ":app-code", + ], +) diff --git a/examples/react-native/android/app/build.gradle b/examples/react-native/android/app/build.gradle new file mode 100644 index 00000000..134d4fd1 --- /dev/null +++ b/examples/react-native/android/app/build.gradle @@ -0,0 +1,151 @@ +apply plugin: "com.android.application" + +import com.android.build.OutputFile + +/** + * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets + * and bundleReleaseJsAndAssets). + * These basically call `react-native bundle` with the correct arguments during the Android build + * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the + * bundle directly from the development server. Below you can see all the possible configurations + * and their defaults. If you decide to add a configuration block, make sure to add it before the + * `apply from: "../../node_modules/react-native/react.gradle"` line. + * + * project.ext.react = [ + * // the name of the generated asset file containing your JS bundle + * bundleAssetName: "index.android.bundle", + * + * // the entry file for bundle generation + * entryFile: "index.android.js", + * + * // whether to bundle JS and assets in debug mode + * bundleInDebug: false, + * + * // whether to bundle JS and assets in release mode + * bundleInRelease: true, + * + * // whether to bundle JS and assets in another build variant (if configured). + * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants + * // The configuration property can be in the following formats + * // 'bundleIn${productFlavor}${buildType}' + * // 'bundleIn${buildType}' + * // bundleInFreeDebug: true, + * // bundleInPaidRelease: true, + * // bundleInBeta: true, + * + * // whether to disable dev mode in custom build variants (by default only disabled in release) + * // for example: to disable dev mode in the staging build type (if configured) + * devDisabledInStaging: true, + * // The configuration property can be in the following formats + * // 'devDisabledIn${productFlavor}${buildType}' + * // 'devDisabledIn${buildType}' + * + * // the root of your project, i.e. where "package.json" lives + * root: "../../", + * + * // where to put the JS bundle asset in debug mode + * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", + * + * // where to put the JS bundle asset in release mode + * jsBundleDirRelease: "$buildDir/intermediates/assets/release", + * + * // where to put drawable resources / React Native assets, e.g. the ones you use via + * // require('./image.png')), in debug mode + * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", + * + * // where to put drawable resources / React Native assets, e.g. the ones you use via + * // require('./image.png')), in release mode + * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", + * + * // by default the gradle tasks are skipped if none of the JS files or assets change; this means + * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to + * // date; if you have any other folders that you want to ignore for performance reasons (gradle + * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ + * // for example, you might want to remove it from here. + * inputExcludes: ["android/**", "ios/**"], + * + * // override which node gets called and with what additional arguments + * nodeExecutableAndArgs: ["node"], + * + * // supply additional arguments to the packager + * extraPackagerArgs: [] + * ] + */ + +project.ext.react = [ + entryFile: "index.js" +] + +apply from: "../../node_modules/react-native/react.gradle" + +/** + * Set this to true to create two separate APKs instead of one: + * - An APK that only works on ARM devices + * - An APK that only works on x86 devices + * The advantage is the size of the APK is reduced by about 4MB. + * Upload all the APKs to the Play Store and people will download + * the correct one based on the CPU architecture of their device. + */ +def enableSeparateBuildPerCPUArchitecture = false + +/** + * Run Proguard to shrink the Java bytecode in release builds. + */ +def enableProguardInReleaseBuilds = false + +android { + compileSdkVersion rootProject.ext.compileSdkVersion + buildToolsVersion rootProject.ext.buildToolsVersion + + defaultConfig { + applicationId "com.gundemo" + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion + versionCode 1 + versionName "1.0" + ndk { + abiFilters "armeabi-v7a", "x86" + } + } + splits { + abi { + reset() + enable enableSeparateBuildPerCPUArchitecture + universalApk false // If true, also generate a universal APK + include "armeabi-v7a", "x86" + } + } + buildTypes { + release { + minifyEnabled enableProguardInReleaseBuilds + proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" + } + } + // applicationVariants are e.g. debug, release + applicationVariants.all { variant -> + variant.outputs.each { output -> + // For each separate APK per architecture, set a unique version code as described here: + // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits + def versionCodes = ["armeabi-v7a":1, "x86":2] + def abi = output.getFilter(OutputFile.ABI) + if (abi != null) { // null for the universal-debug, universal-release variants + output.versionCodeOverride = + versionCodes.get(abi) * 1048576 + defaultConfig.versionCode + } + } + } +} + +dependencies { + compile project(':react-native-webview-bridge') + implementation fileTree(dir: "libs", include: ["*.jar"]) + implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}" + implementation "com.facebook.react:react-native:+" // From node_modules +} + +// Run this once to be able to run the application with BUCK +// puts all compile dependencies into folder libs for BUCK to use +task copyDownloadableDepsToLibs(type: Copy) { + from configurations.compile + into 'libs' +} diff --git a/examples/react-native/android/app/proguard-rules.pro b/examples/react-native/android/app/proguard-rules.pro new file mode 100644 index 00000000..a92fa177 --- /dev/null +++ b/examples/react-native/android/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/examples/react-native/android/app/src/main/AndroidManifest.xml b/examples/react-native/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..5fde7ad3 --- /dev/null +++ b/examples/react-native/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + diff --git a/examples/react-native/android/app/src/main/assets/html/blank.html b/examples/react-native/android/app/src/main/assets/html/blank.html new file mode 100644 index 00000000..e69de29b diff --git a/examples/react-native/android/app/src/main/java/com/gundemo/MainActivity.java b/examples/react-native/android/app/src/main/java/com/gundemo/MainActivity.java new file mode 100644 index 00000000..de2d8c2f --- /dev/null +++ b/examples/react-native/android/app/src/main/java/com/gundemo/MainActivity.java @@ -0,0 +1,15 @@ +package com.gundemo; + +import com.facebook.react.ReactActivity; + +public class MainActivity extends ReactActivity { + + /** + * Returns the name of the main component registered from JavaScript. + * This is used to schedule rendering of the component. + */ + @Override + protected String getMainComponentName() { + return "GunDemo"; + } +} diff --git a/examples/react-native/android/app/src/main/java/com/gundemo/MainApplication.java b/examples/react-native/android/app/src/main/java/com/gundemo/MainApplication.java new file mode 100644 index 00000000..cd09d780 --- /dev/null +++ b/examples/react-native/android/app/src/main/java/com/gundemo/MainApplication.java @@ -0,0 +1,47 @@ +package com.gundemo; + +import android.app.Application; + +import com.facebook.react.ReactApplication; +import com.github.alinz.reactnativewebviewbridge.WebViewBridgePackage; +import com.facebook.react.ReactNativeHost; +import com.facebook.react.ReactPackage; +import com.facebook.react.shell.MainReactPackage; +import com.facebook.soloader.SoLoader; + +import java.util.Arrays; +import java.util.List; + +public class MainApplication extends Application implements ReactApplication { + + private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { + @Override + public boolean getUseDeveloperSupport() { + return BuildConfig.DEBUG; + } + + @Override + protected List getPackages() { + return Arrays.asList( + new MainReactPackage(), + new WebViewBridgePackage() + ); + } + + @Override + protected String getJSMainModuleName() { + return "index"; + } + }; + + @Override + public ReactNativeHost getReactNativeHost() { + return mReactNativeHost; + } + + @Override + public void onCreate() { + super.onCreate(); + SoLoader.init(this, /* native exopackage */ false); + } +} diff --git a/examples/react-native/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/examples/react-native/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..a2f5908281d070150700378b64a84c7db1f97aa1 GIT binary patch literal 3056 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 axPAxe%7rDusX+w*00006jj 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⁣ zwiG+x#%j}{0gU~J8;<|!B1@-XaB@{KORFwrYg_8rOv({b0EO#DbeQRm;B6_9=mXGf z-x|VL{zd`)#@yN}HkCSJbjbNlE|zL3Wm9Q8HY`sV)}3%pgN>cL^67{Z;PPL(*wT8N zUjXU{@|*hvm}({wsAC=x0^ok0%UAz0;sogW{B!nDqk|JJ5x~4NfTDgP49^zeu`csl?5mY@JdQdISc zFs!E{^grmkLnUk9 zny~m)1vws@5BFI<-0Tuo2JWX(0v`W|t(wg;s--L47WTvTMz-8l#TL^=OJNRS2?_Qj z3AKT+gvbyBi#H*-tJ%tWD|>EV3wy|8qxfzS!5RW;Jpl5*zo&^UBU=fG#2}UvRyNkK zA06Dy9;K1ca@r2T>yThYgI!ont$(G{6q#2QT+00r_x0(b)gsE`lBB?2gr55gq^D3Fi&p%E(p9>U%bv zkg1Jco(RbyTX7FDHOnl7-O@ zI$AaIl?9NJKPm(WiBP`1-#CB1QzU>&hKm)fpa5DKE{2$X0hGz-0uZ?cyTk(YC!Y&| zL=1VrNERSA5NA2jq7FACfX4JfPyj5XXl1yv0>~s;eF7L2$>&oMqeTFT2m$y7FlkON z_yurD1yIOvA;5C6016pyxBznGUt0kJ&k5r#;&>Jow`r)sp9R~PmK~lz$3xH%LT*1U zJdOyABZ3!FvNoR*vN$5ykHS8f`jA4zV+|L}i1C4`B2c{R0;UdYxaU|H)2avz@ z=mEYc|2S<+(B2Tj+FkX+2D+yFI!k9lWMA61DJ{)e;lum$(;O87?vGJJe!KtK04+N_ zI*P~t@dUb>9Xh{dbyl{-ZQ(UMgz7$|QfL5XSPkskt^NgctYC#;4WcZB1@%@wy@2t3 z2z0DI7&%b$*Aw~abe?GxE`ez@+6hOh-6*8fHRV{1os$EL@}uUZeG4h1&Be`98q*7j z=3-v+lhIjfWVo12!<>%V^a6lTgW3+_#W6n|p*~==zOH7z$0{LSZk(Tpd7EaD04hnA zL;#fxS0aD{`5^&D`}>0Uq?byDD-l2=!wm_bLcUl4gc(% za1p|itVANvFF>hghAS07Im1;IK;|b*W)}VDyI;BIp2=K*yu2a)j?B|f<44NI$NbmJ z#dE0>jI$fMr&@>4kN8MLFb4&2O9fEKaQg%(QO$4_1rVQywG^CmBLh#}_7gKW3vd?| z2?1^&KWq8}8I^_S0|)MowU_pw$q@nl@Nkn$z>BQq_KA^9yaR`(R3u{{Ig;cwt z@AJ^{ODQCm^neroM9nKNUAXi9RCK`OsP_LuR0PUR(YZCCX5dNF6VzcoK&=b^r`W?ltt|*F zpkoae%ZT{C1h~EcFui~b7fF`vb<<~j_VquuUA$}QqIKYELPp#;{u?q8Dz}WAG-(3; zjrm$i%7UbyZMM(Y{>!uJ#vNB?R~B{6Htp=>e*<{fQQ5W7V(1coCWlOON!MzZxhum| ztZBQpGR z;~#ur^&PockKdV{Q6R>o`Pl{0x!DEbpZ7y9Y;*ZvE!*gU`V1W3znva{f=?WO5I&>B z&hw6}tjECtaghm5z|C#%M;Yf_*pI^};h}Vl=^r9EN=tVDj86D;C$jIJ?K7VP+00000NkvXXu0mjf D5i!M* literal 0 HcmV?d00001 diff --git a/examples/react-native/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/examples/react-native/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..459ca609d3ae0d3943ab44cdc27feef9256dc6d7 GIT binary patch literal 7098 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 literal 0 HcmV?d00001 diff --git a/examples/react-native/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/examples/react-native/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..8ca12fe024be86e868d14e91120a6902f8e88ac6 GIT binary patch literal 6464 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 B0*L?s literal 0 HcmV?d00001 diff --git a/examples/react-native/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/examples/react-native/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..8e19b410a1b15ff180f3dacac19395fe3046cdec GIT binary patch literal 10676 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 literal 0 HcmV?d00001 diff --git a/examples/react-native/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/examples/react-native/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..4c19a13c239cb67b8a2134ddd5f325db1d2d5bee GIT binary patch literal 15523 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 BIP(Ai literal 0 HcmV?d00001 diff --git a/examples/react-native/android/app/src/main/res/values/strings.xml b/examples/react-native/android/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..532e430a --- /dev/null +++ b/examples/react-native/android/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + GunDemo + diff --git a/examples/react-native/android/app/src/main/res/values/styles.xml b/examples/react-native/android/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..319eb0ca --- /dev/null +++ b/examples/react-native/android/app/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/examples/react-native/android/build.gradle b/examples/react-native/android/build.gradle new file mode 100644 index 00000000..a1e80854 --- /dev/null +++ b/examples/react-native/android/build.gradle @@ -0,0 +1,39 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + ext { + buildToolsVersion = "27.0.3" + minSdkVersion = 16 + compileSdkVersion = 27 + targetSdkVersion = 26 + supportLibVersion = "27.1.1" + } + repositories { + google() + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.1.4' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + mavenLocal() + google() + jcenter() + maven { + // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm + url "$rootDir/../node_modules/react-native/android" + } + } +} + + +task wrapper(type: Wrapper) { + gradleVersion = '4.4' + distributionUrl = distributionUrl.replace("bin", "all") +} diff --git a/examples/react-native/android/gradle.properties b/examples/react-native/android/gradle.properties new file mode 100644 index 00000000..89e0d99e --- /dev/null +++ b/examples/react-native/android/gradle.properties @@ -0,0 +1,18 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx10248m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true diff --git a/examples/react-native/android/gradle/wrapper/gradle-wrapper.jar b/examples/react-native/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..01b8bf6b1f99cad9213fc495b33ad5bbab8efd20 GIT binary patch literal 54329 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

      <5G2M1WZP5 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

    • -
      +
    • @@ -547,23 +549,33 @@ }); window.user = S.user; $('#speak').on('submit', (e) => { - /*var say = normalize($('#speak .draft')); - console.log(1, say.html()); - return;*/ - var say = $('#speak .draft').text(); + var say = $('#speak .draft').text(); //.text(); // NO NO NO NO NO if(!say){ return } - var ref = S.user.get('who').get('all').set({what: say}); - ref.get('by').put(S.user.get('who')); - S.user.get('who').get('said').time(ref); - S.gun.get('@').time(ref); + console.log('save!', say); + var ref = S.user.get('who').get('all').set({what: say, when: Gun.state()}); + //ref.get('by').put(S.user.get('who')); + //S.user.get('who').get('said').time(ref); + S.user.get('who').get('said').set(ref); + //S.gun.get('@').time(ref); $('#speak .draft').text(''); }); - S.gun.get('@').time(async (data, key, time) => { - var ref = S.gun.get(data), tmp; - var said = await ref.then(); + //S.gun.get('@').time(async (data, key, time) => { + S.user.get('who').get('said').map().once(async (data, key, time) => { + //var ref = S.gun.get(data), tmp; + //var said = await ref.then(); + key = key.replace(/[^A-Za-z]/ig,''); + var tmp, said = data, time = said.when; var $li = $($('#'+key)[0] || $('#draft .model .spoke').clone(true,true).attr('id', key)[(tmp = $.as.sort(time, $('#draft ul').children('li').first()))[0]?'insertBefore':'appendTo'](tmp[0] || '#draft ul')); - $li.find('.what').text(said.what); - var by = ref.get('by'); + tmp = said.what; + if(tmp && tmp.ct){ + tmp = JSON.stringify(tmp); + setTimeout(async function(){ + tmp = await SEA.decrypt(said.what, S.user._.sea); + $li.find('.what').text(tmp); + }, 750); + } + $li.find('.what').text(tmp); // NORMALIAZE!!! + var by = S.user.get('who');// ref.get('by'); by.get('face').get('small').on(data => { $li.find('.face').attr('src', data).removeClass('none'); }); @@ -579,7 +591,7 @@ if(face){ $li.find('.face').attr('src', face).removeClass('none'); } - }, 10); + }); $(document).on('click', '#speak .act.face', (eve) => { }); @@ -587,61 +599,80 @@ -
      +
      - + -
      @@ -673,6 +704,7 @@

      Hello world!

      + - + + + \ No newline at end of file diff --git a/lib/upload.js b/lib/upload.js index 6cdadd21..aaacba29 100644 --- a/lib/upload.js +++ b/lib/upload.js @@ -25,9 +25,10 @@ if(!e){ return cb && cb({err: "No file!"}) } if(e.err){ return } var file = (((e.event || e).target || e).result || e), img = new Image(); + img.crossOrigin = "Anonymous"; img.src = file; img.onload = function(){ - if(img.width < w && img.height < (h||Infinity)){ + if(img.width < (w = w || 1000) && img.height < (h||Infinity) && "data:" == file.slice(0,5)){ e.base64 = file; return cb(e || file); } From 2aae735960f35aa39f3efd7205d333bee9a33f01 Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Thu, 31 Jan 2019 04:57:44 -0800 Subject: [PATCH 102/103] bump --- test/sea/sea.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/sea/sea.js b/test/sea/sea.js index 52dbbcb2..4f7fe245 100644 --- a/test/sea/sea.js +++ b/test/sea/sea.js @@ -334,6 +334,7 @@ describe('SEA', function(){ }); it('set user ref null override', function(done){ + this.timeout(9000); var gun = Gun(); var user = gun.user(); var msg = {what: 'hello world'}; From c243d71e7140098074912a3b455f55a0111b5374 Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Thu, 31 Jan 2019 05:37:49 -0800 Subject: [PATCH 103/103] link and normalize --- gun.js | 44 ++--- lib/normalize.js | 422 +++++++++++++++++++++++++++++++---------------- 2 files changed, 306 insertions(+), 160 deletions(-) diff --git a/gun.js b/gun.js index 0e8e5508..e01b93ce 100644 --- a/gun.js +++ b/gun.js @@ -264,11 +264,11 @@ || 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. + return Val.link.is(v) || false; // is the value a soul relation? Then it is valid and return it. If not, everything else remaining is an invalid data type. Custom extensions can be built on top of these primitives to support other types. } Val.link = Val.rel = {_: '#'}; ;(function(){ - Val.rel.is = function(v){ // this defines whether an object is a soul relation or not, they look like this: {'#': 'UUID'} + Val.link.is = function(v){ // this defines whether an object is a soul relation or not, they look like this: {'#': 'UUID'} if(v && v[rel_] && !v._ && obj_is(v)){ // must be an object. var o = {}; obj_map(v, map, o); @@ -287,7 +287,7 @@ } } }()); - Val.rel.ify = function(t){ return obj_put({}, rel_, t) } // convert a soul into a relation and return it. + Val.link.ify = function(t){ return obj_put({}, rel_, t) } // convert a soul into a relation and return it. Type.obj.has._ = '.'; var rel_ = Val.link._, u; var bi_is = Type.bi.is; @@ -473,7 +473,7 @@ env.map = env; } if(env.soul){ - at.rel = Val.rel.ify(env.soul); + at.link = Val.link.ify(env.soul); } env.shell = (as||{}).shell; env.graph = env.graph || {}; @@ -488,16 +488,16 @@ at.env = env; at.soul = soul; if(Node.ify(at.obj, map, at)){ - at.rel = at.rel || Val.rel.ify(Node.soul(at.node)); + at.link = at.link || Val.link.ify(Node.soul(at.node)); if(at.obj !== env.shell){ - env.graph[Val.rel.is(at.rel)] = at.node; + env.graph[Val.link.is(at.link)] = at.node; } } return at; } function map(v,k,n){ var at = this, env = at.env, is, tmp; - if(Node._ === k && obj_has(v,Val.rel._)){ + if(Node._ === k && obj_has(v,Val.link._)){ return n._; // TODO: Bug? } if(!(is = valid(v,k,n, at,env))){ return } @@ -506,8 +506,8 @@ if(obj_has(v, Node._) && Node.soul(v)){ // ? for safety ? at.node._ = obj_copy(v._); } - at.node = Node.soul.ify(at.node, Val.rel.is(at.rel)); - at.rel = at.rel || Val.rel.ify(Node.soul(at.node)); + at.node = Node.soul.ify(at.node, Val.link.is(at.link)); + at.link = at.link || Val.link.ify(Node.soul(at.node)); } if(tmp = env.map){ tmp.call(env.as || {}, v,k,n, at); @@ -526,14 +526,14 @@ } tmp = node(env, {obj: v, path: at.path.concat(k)}); if(!tmp.node){ return } - return tmp.rel; //{'#': Node.soul(tmp.node)}; + return tmp.link; //{'#': Node.soul(tmp.node)}; } function soul(id){ var at = this; - var prev = Val.link.is(at.rel), graph = at.env.graph; - at.rel = at.rel || Val.rel.ify(id); - at.rel[Val.rel._] = id; + var prev = Val.link.is(at.link), graph = at.env.graph; + at.link = at.link || Val.link.ify(id); + at.link[Val.link._] = id; if(at.node && at.node[Node._]){ - at.node[Node._][Val.rel._] = id; + at.node[Node._][Val.link._] = id; } if(obj_has(graph, prev)){ graph[id] = graph[prev]; @@ -573,13 +573,13 @@ } function map(v,k){ var tmp, obj; if(Node._ === k){ - if(obj_empty(v, Val.rel._)){ + if(obj_empty(v, Val.link._)){ return; } this.obj[k] = obj_copy(v); return; } - if(!(tmp = Val.rel.is(v))){ + if(!(tmp = Val.link.is(v))){ this.obj[k] = v; return; } @@ -871,7 +871,7 @@ var list_is = Gun.list.is; var text = Gun.text, text_is = text.is, text_rand = text.random; var obj = Gun.obj, obj_is = obj.is, obj_has = obj.has, obj_to = obj.to, obj_map = obj.map, obj_copy = obj.copy; - var state_lex = Gun.state.lex, _soul = Gun.val.rel._, _has = '.', node_ = Gun.node._, rel_is = Gun.val.link.is; + var state_lex = Gun.state.lex, _soul = Gun.val.link._, _has = '.', node_ = Gun.node._, rel_is = Gun.val.link.is; var empty = {}, u; console.debug = function(i, s){ return (console.debug.i && i === console.debug.i && console.debug.i++) && (console.log.apply(console, arguments) || s) }; @@ -1133,11 +1133,11 @@ if(!(at = next[key])){ return; } - //if(data && data[_soul] && (tmp = Gun.val.rel.is(data)) && (tmp = (cat.root.$.get(tmp)._)) && obj_has(tmp, 'put')){ + //if(data && data[_soul] && (tmp = Gun.val.link.is(data)) && (tmp = (cat.root.$.get(tmp)._)) && obj_has(tmp, 'put')){ // data = tmp.put; //} if(at.has){ - //if(!(data && data[_soul] && Gun.val.rel.is(data) === Gun.node.soul(at.put))){ + //if(!(data && data[_soul] && Gun.val.link.is(data) === Gun.node.soul(at.put))){ if(u === at.put || !Gun.val.link.is(data)){ at.put = data; } @@ -1222,7 +1222,7 @@ var empty = {}, u; var obj = Gun.obj, obj_has = obj.has, obj_put = obj.put, obj_del = obj.del, obj_to = obj.to, obj_map = obj.map; var text_rand = Gun.text.random; - var _soul = Gun.val.rel._, node_ = Gun.node._; + var _soul = Gun.val.link._, node_ = Gun.node._; })(USE, './chain'); ;USE(function(module){ @@ -1395,7 +1395,7 @@ if(!soul && Gun.val.is(msg.put)){ return Gun.log("The reference you are saving is a", typeof msg.put, '"'+ msg.put +'", not a node (object)!'); } - gun.put(Gun.val.rel.ify(soul), cb, as); + gun.put(Gun.val.link.ify(soul), cb, as); }, true); return gun; } @@ -1495,7 +1495,7 @@ function soul(id, as, msg, eve){ var as = as.as, cat = as.at; as = as.as; var at = ((msg || {}).$ || {})._ || {}; - id = at.dub = at.dub || id || Gun.node.soul(cat.obj) || Gun.node.soul(msg.put || at.put) || Gun.val.rel.is(msg.put || at.put) || (as.via.back('opt.uuid') || Gun.text.random)(); // TODO: BUG!? Do we really want the soul of the object given to us? Could that be dangerous? + id = at.dub = at.dub || id || Gun.node.soul(cat.obj) || Gun.node.soul(msg.put || at.put) || Gun.val.link.is(msg.put || at.put) || (as.via.back('opt.uuid') || Gun.text.random)(); // TODO: BUG!? Do we really want the soul of the object given to us? Could that be dangerous? if(eve){ eve.stun = true } if(!id){ // polyfill async uuid for SEA at.via.back('opt.uuid')(function(err, id){ // TODO: improve perf without anonymous callback diff --git a/lib/normalize.js b/lib/normalize.js index 9abe21f6..871ec6cf 100644 --- a/lib/normalize.js +++ b/lib/normalize.js @@ -1,138 +1,284 @@ -;(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; -}()); \ No newline at end of file +(function(){ + + $.normalize = function(html, customOpt){ + var html, root$, wrapped, opt; + opt = html.opt || (customOpt ? prepareOptTags($.extend(true, baseOpt, customOpt)) + : defaultOpt); + if(!html.opt){ + // first call + unstableList.length = 0; // drop state from previous run (in case there has been error) + root$ = $('
      '+html+'
      '); + } + // initial recursion + (html.$ || root$).contents().each(function(){ + if(this.nodeType === this.TEXT_NODE) { + this.textContent = this.textContent.replace(/^[ \n]+|[ \n]+$/g, ' '); + return; + } + var a = {$: $(this), opt: opt}; + initTag(a); + $.normalize(a); + }); + if(root$){ + stateMachine(); + return root$.html(); + } + } + + var baseOpt = { + hierarchy: ['div', 'pre', 'ol', 'ul', 'li', + 'h1', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'a', // block + 'b', 'code', 'i', 'span', 's', 'sub', 'sup', 'u', // inline + 'br'] // empty + ,tags: { + 'a': {attrs:{'href':1}, exclude:{'a':1}}, + 'b': {exclude:{'b':1,'p':1}}, + 'br': {empty: 1}, + 'i': {exclude:{'i':1,'p':1}}, + 'span': {exclude:{'p':1,'ul':1,'ol':1,'li':1,'br':1}}, + 's': {space:1}, + 'u': {exclude:{'u':1,'p':1},space:1}, + } + ,convert: { + 'em': 'i', 'strong': 'b', 'strike': 's', + } + ,attrs: { + 'id':1 + ,'class':1 + ,'style':1 + } + ,blockTag: function(a){ + return a.opt.tags[a.tag].order < a.opt.tags.a.order; + } + ,mutate: [exclude, moveSpaceUp, next, parentOrderWrap] + } + + var defaultOpt = prepareOptTags($.extend(true, {}, baseOpt)); + + var unstableList = []; + + function addUnstable(a) { // NOT ES5 + if(!a.tag) { throw Error("not tag in ", a) } + if(a.unstable) return; + unstableList.push(a); + a.unstable = true; + } + + function initTag(a) { + // initial handling (container, convert, attributes): + a.tag = tag(a.$); + if(empty(a)) { + return; + } + parseAndRemoveAttrs(a); + convert(a); + setAttrs(a); + a.$[0].a = a; // link from dom element back to a + // state machine init + unstableList.push(a); + a.unstable = true; + return a; + } + + function stateMachine() { + if(unstableList.length===0) + return; + var a, i = -1; + while (a = unstableList.pop()) { // PERF: running index is probably faster than shift (mutates array) + a.unstable = false; + $(a.opt.mutate).each(function(i,fn){ + return fn && fn(a, addUnstable); + }); + } + } + + function prepareOptTags(opt) { + var name, tag, tags = opt.tags; + for(name in tags) { + if(opt.hierarchy.indexOf(name)===-1) + throw Error('tag "'+name+'" is missing hierachy definition'); + } + opt.hierarchy.forEach(function(name){ + if(!tags[name]){ + tags[name] = {attrs: opt.attrs}; + } + (tag=tags[name]).attrs = $.extend(tag.attrs||{}, opt.attrs); + tag.name = name; // not used, debug help (REMOVE later?) + // order + tag.order = opt.hierarchy.indexOf(name) + if(tag.order === -1) { + throw Error("Order of '"+name+"' not defined in hierarchy"); + } + }); + return opt; + } + + // GENERAL UTILS + + function get(o, args){ // path argments as separate string parameters + if(typeof args === 'string') + return o[args[0]]; + var i = 0, l = args.length, u; + while((o = o[args[i++]]) != null && i < l){}; + return i < l ? u : o; + } + + function has(obj,prop){ + return Object.prototype.hasOwnProperty.call(obj, prop); + } + + // ELEMENT UTILS + + function tag(e){ + return (($(e)[0]||{}).nodeName||'').toLowerCase(); + } + + function joint(e, d){ + d = (d? 'next' : 'previous') + 'Sibling'; + return $(($(e)[0]||{})[d]); + } + + // create key val attributes object from elements attributes + function attrsAsObj(e, filterCb){ + var attrObj = {}; + (e = $(e)) && e.length && $(e[0].attributes||[]).each(function(value,name){ + name = name.nodeName||name.name; + value = e.attr(name); + value = filterCb? filterCb(value,name,e) : value; + if(value !== undefined && value !== false) + attrObj[name] = value; + }); + return attrObj; + } + + // TODO: PERF testing - for loop to compare through? + function sameAttrs(a, b) { + return JSON.stringify(a.attr) === JSON.stringify(b.attr); + } + + // INITIAL MUTATORS + + function parseAndRemoveAttrs(a) { + a.attrs = []; + var tag = a.opt.convert[a.tag] || a.tag, + tOpt = a.opt.tags[tag]; + a.attr = tOpt && attrsAsObj(a.$, function(value,name){ + a.$.removeAttr(name); + if(tOpt.attrs[name.toLowerCase()]){ + a.attrs.push(name) + return value; + } + }); + } + + function setAttrs(a){ + var l = function(ind,name){ + var t = name; + name = a.attrs? name : ind; + var value = a.attrs? a.attr[name.toLowerCase()] : t; + a.$.attr(name, value); + } + a.attrs? $(a.attrs.sort()).each(l) : $.each(a.attr,l); + } + + function convert(a){ + var t; + if(t = a.opt.convert[a.tag]){ + a.$.replaceWith(a.$ = $('<'+ (a.tag = t.toLowerCase()) +'>').append(a.$.contents())); + } + } + + // LOOPING (STATE MACHINE) MUTATORS + + function exclude(a, addUnstable){ + var t = get(a.opt, ['tags', a.tag]), + pt = get(a.opt, ['tags', tag(a.$.parent())]); + if(!t || (pt && get(pt, ['exclude', a.tag]))){ + var c = a.$.contents(); + a.$.replaceWith(c); + c.length===1 && c[0].a && addUnstable(c[0].a); + return false; + } + } + + function moveSpaceUp(a, addUnstable){ + var n = a.$[0]; + if(moveSpace(n, true) + moveSpace(n, false)) { + // either front, back or both spaces moved + var c; + if(n.textContent==='') { + empty(a); + } else if((c = a.$.contents()[0]) && c.a) { + parentOrderWrap(c.a, addUnstable) + } + } + } + + function moveSpace(n, bef) { + var childRe = bef? /^ / : / $/, + parentRe = bef? / $/ : /^ /, + c = bef? 'firstChild' : 'lastChild', + s = bef? 'previousSibling' : 'nextSibling'; + sAdd = bef? 'after' : 'before'; + pAdd = bef? 'prepend' : 'append'; + if(!n || !n[c] || n[c].nodeType !== n.TEXT_NODE || !n[c].wholeText.match(childRe)) { + return 0; + } + if((n2 = n[s]) && !n.a.opt.blockTag(n.a)) { + if(n2.nodeType === 3 && !n2.textContent.match(parentRe)) { + n2.textContent = (bef?'':' ') + n2.textContent + (bef?' ':''); + } else if(n2.nodeType === 1) { + $(n2)[sAdd](' '); + } + } else if((n2 = n.parentNode) && !n.a.opt.blockTag(n.a)) { + $(n2)[pAdd](' '); + } else { + return 0; + } + n[c].textContent = n[c].wholeText.replace(childRe, ''); + if(!n[c].wholeText.length) + $(n[c]).remove(); + return 1; + } + + function next(a, addUnstable, t){ + var t = t || joint(a.$, true), sm; + if(!t.length || a.opt.blockTag(a)) + return; + if(a.opt.spaceMerge && t.length===1 && t[0].nodeType === 3 && t[0].wholeText===' '){ + if(!(t2 = joint(t, true)).length || a.opt.blockTag(t2[0].a)) + return; + t.remove(); + t2.prepend(' '); + return next(a, addUnstable, t2); + } + if(!t[0].a || a.tag !== t[0].a.tag || !sameAttrs(a, t[0].a)) + return; + t.prepend(a.$.contents()); + empty(a); + addUnstable(t[0].a); + (t = t.children(":first")).length && addUnstable(t[0].a); + } + + function empty(a){ + var t = a.opt.tags[a.tag]; + if((!t || !t.empty) && !a.$.contents().length && !a.$[0].attributes.length){ + a.$.remove(); + return true; // NOTE true/false - different API than in exclude + } + } + + function parentOrderWrap(a, addUnstable){ + var parent = a.$.parent(), children = parent.contents(), + tags = a.opt.tags, ptag; + + if(children.length===1 && children[0] === a.$[0] + && (ptag=tags[tag(parent)]) && ptag.order > tags[a.tag].order){ + parent.after(a.$); + parent.append(a.$.contents()); + a.$.append(parent); + addUnstable(parent[0].a); + addUnstable(a); + } + } +})(); \ No newline at end of file