diff --git a/.travis.yml b/.travis.yml index 3b2b98a4..4cf6f163 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,9 +3,8 @@ branches: except: - debug node_js: - - 4.0 - - 4.2 - - 5.0 - - 6.8 - - 7.9 - - 8.6 + - 8 + - 10 +cache: + directories: + - node_modules \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index be214ded..7eb3a7cd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:edge +FROM alpine:latest # Build-time metadata as defined at http://label-schema.org ARG BUILD_DATE ARG VCS_REF @@ -22,4 +22,5 @@ RUN apk update && apk upgrade \ && npm install \ && apk del .build-dependencies && rm -rf /var/cache/* /tmp/npm* EXPOSE 8080 +EXPOSE 8765 CMD ["npm","start"] diff --git a/Procfile b/Procfile index a5ffa97a..875fe822 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: node --optimize_for_size --max_old_space_size=460 --gc_interval=100 examples/http.js \ No newline at end of file +web: node --optimize_for_size --gc_interval=100 examples/http.js diff --git a/README.md b/README.md index a0b470dc..c2438c08 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/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: @@ -22,22 +21,16 @@ 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 socketio 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. +For now, it is best to start with GUN and _just use it_ to learn the basics, since it is _**so easy**_: (**or** want to read more? Skip ahead to the "[What is GUN?](#what-is-gun)" section.) ## Quickstart - - Try the [interactive tutorial](https://gun.eco/think.html) in the browser (**5min** ~ average developer). + - Try the [interactive tutorial](https://gun.eco/docs/Todo-Dapp) in the browser (**5min** ~ average developer). - Or `npm install gun` and run the examples with `cd node_modules/gun && npm start` (**5min** ~ average developer). > **Note:** If you don't have [node](http://nodejs.org/) or [npm](https://www.npmjs.com/), read [this](https://github.com/amark/gun/blob/master/examples/install.sh) first. @@ -62,7 +55,7 @@ gun.get('mark').on(function(data, key){ }); ``` -- 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}; @@ -72,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); }); @@ -95,20 +88,32 @@ 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:
-Lorenzo Mangani, -Sam Liu, -Daniel Dombrowsky, -Vincent Woo, -AJ ONeal, +Robert Heessels, +Lorenzo Mangani, +NLnet Foundation, +Sam Liu, +Daniel Dombrowsky, +Vincent Woo, +AJ ONeal, Bill Ottman, -Sean Matheson, +Mike Lange, +Sean Matheson, Alan Mimms, Dário Freire, -John Williamson +John Williamson, +Robin Bron

- Join others in sponsoring code: https://www.patreon.com/gunDB ! @@ -116,19 +121,32 @@ 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/Performance)* in **just ~9KB gzipped size**. ## Documentation - - + + - + @@ -136,23 +154,52 @@ Thanks to:
- + - + - - - + + +

API reference

Tutorials

API reference

Tutorials

Examples

GraphQL

Electron

Native

React Native

Vue

Webcomponents

CAP Theorem Tradeoffs

CAP Theorem Tradeoffs

How Data Sync Works

How GUN is Built

How GUN is Built

Crypto Auth

Modules

Roadmap

Crypto Auth

Modules

Roadmap

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))**; +**[ajmeyghani](https://github.com/ajmeyghani) ([Learn GUN Basics with Diagrams](https://medium.com/@ajmeyghani/gundb-a-graph-database-in-javascript-3860a08d873c))**; **[anywhichway](https://github.com/anywhichway) ([Block Storage](https://github.com/anywhichway/gun-block))**; **[beebase](https://github.com/beebase) ([Quasar](https://github.com/beebase/gun-vuex-quasar))**; **[BrockAtkinson](https://github.com/BrockAtkinson) ([brunch config](https://github.com/BrockAtkinson/brunch-gun))**; **[Brysgo](https://github.com/brysgo) ([GraphQL](https://github.com/brysgo/graphql-gun))**; **[d3x0r](https://github.com/d3x0r) ([SQLite](https://github.com/d3x0r/gun-db))**; **[forrestjt](https://github.com/forrestjt) ([file.js](https://github.com/amark/gun/blob/master/lib/file.js))**; **[hillct](https://github.com/hillct) (Docker)**; **[JosePedroDias](https://github.com/josepedrodias) ([graph visualizer](http://acor.sl.pt:9966))**; **[JuniperChicago](https://github.com/JuniperChicago) ([cycle.js bindings](https://github.com/JuniperChicago/cycle-gun))**; **[jveres](https://github.com/jveres) ([todoMVC](https://github.com/jveres/todomvc))**; **[kristianmandrup](https://github.com/kristianmandrup) ([edge](https://github.com/kristianmandrup/gun-edge))**; **[Lightnet](https://github.com/Lightnet)** ([Awesome Vue User Examples](https://glitch.com/edit/#!/jsvuegunui?path=README.md:1:0) & [User Kitchen Sink Playground](https://gdb-auth-vue-node.glitch.me/)); **[lmangani](https://github.com/lmangani) ([Cytoscape Visualizer](https://github.com/lmangani/gun-scape), [Cassandra](https://github.com/lmangani/gun-cassandra), [Fastify](https://github.com/lmangani/fastify-gundb), [LetsEncrypt](https://github.com/lmangani/polyGun-letsencrypt))**; **[mhelander](https://github.com/mhelander) ([SEA](https://github.com/amark/gun/blob/master/sea.js))**; [omarzion](https://github.com/omarzion) ([Sticky Note App](https://github.com/omarzion/stickies)); [PsychoLlama](https://github.com/PsychoLlama) ([LevelDB](https://github.com/PsychoLlama/gun-level)); **[RangerMauve](https://github.com/RangerMauve) ([schema](https://github.com/gundb/gun-schema))**; **[robertheessels](https://github.com/swifty) ([gun-p2p-auth](https://github.com/swifty/gun-p2p-auth))**; **[rogowski](https://github.com/rogowski) (AXE)**; [sbeleidy](https://github.com/sbeleidy); **[sbiaudet](https://github.com/sbiaudet) ([C# Port](https://github.com/sbiaudet/cs-gun))**; **[Sean Matheson](https://github.com/ctrlplusb) ([Observable/RxJS/Most.js bindings](https://github.com/ctrlplusb/gun-most))**; **[Shadyzpop](https://github.com/Shadyzpop) ([React Native example](https://github.com/amark/gun/tree/master/examples/react-native))**; **[sjones6](https://github.com/sjones6) ([Flint](https://github.com/sjones6/gun-flint))**; **[Stefdv](https://github.com/stefdv) (Polymer/web components)**; **[zrrrzzt](https://github.com/zrrrzzt) ([JWT Auth](https://gist.github.com/zrrrzzt/6f88dc3cedee4ee18588236756d2cfce))**; **[xmonader](https://github.com/xmonader) ([Python Port](https://github.com/xmonader/pygundb))**; **[88dev](https://github.com/88dev) ([Database Viewer](https://github.com/88dev/gun-show))**; I am missing many others, apologies, will be adding them soon! +## Testing + +Tests may be run with `npm test`. Tests will trigger persistent writes to the DB, so subsequent runs of the test will fail. You must clear the DB before running the tests again. This can be done by running the following command in the project directory. + +```bash +rm -rf *data* +``` + +### Additional Cryptography Libraries + +To install with npm, first install `npm install gun -S`. +For just the networking layer, import Gun: + +```javascript +var Gun = require('gun/gun'); +``` + +If you also need to install SEA for user auth and crypto, also install some of its dependencies like this: + +`npm install @trust/crypto text-encoding node-webcrypto-ossl --save` + +You will need to require it too (it will be automatically added to the Gun object): + +```javascript +var Gun = require('gun/gun'); +var Sea = require('gun/sea'); +``` + + ## Deploy To quickly spin up a Gun test server for your development team, utilize either [Heroku](http://heroku.com) or [Docker](http://docker.com) or any variant thereof [Dokku](http://dokku.viewdocs.io/dokku/), [Flynn.io](http://flynn.io), [now.sh](https://zeit.co/now), etc. ! @@ -188,7 +235,7 @@ Then visit the URL in the output of the 'now --npm' step, in your browser. Pull from the [Docker Hub](https://hub.docker.com/r/gundb/gun/) [![](https://images.microbadger.com/badges/commit/gundb/gun.svg)](https://microbadger.com/images/gundb/gun). Or: ```bash -docker run -p 8080:8080 gundb/gun +docker run -p 8765:8765 gundb/gun ``` Or build the [Docker](https://docs.docker.com/engine/installation/) image locally: @@ -197,17 +244,17 @@ Or build the [Docker](https://docs.docker.com/engine/installation/) image locall git clone https://github.com/amark/gun.git cd gun docker build -t myrepo/gundb:v1 . -docker run -p 8080:8080 myrepo/gundb:v1 +docker run -p 8765:8765 myrepo/gundb:v1 ``` Or, if you prefer your Docker image with metadata labels (Linux/Mac only): ```bash npm run docker -docker run -p 8080:8080 usenameHere/gun:git +docker run -p 8765:8765 username/gun:git ``` -Then visit [http://localhost:8080](http://localhost:8080) in your browser. +Then visit [http://localhost:8765](http://localhost:8765) in your browser. ## License diff --git a/as.js b/as.js index fc869469..f6ac4f37 100644 --- a/as.js +++ b/as.js @@ -40,7 +40,7 @@ } ref.get(function(at){ - var data = at.put, key = at.get, gui = at.gun, ui = name, back; + var data = at.put, key = at.get, gui = at.gun || at.$, ui = name, back; if(model){ ui = model.has[(gui._).id]; if(!ui){ @@ -80,12 +80,11 @@ }, wait || 200); } } - as.sort = function(sort, el) { - var vs = $(el).find('.sort'); - vs = (vs[0] && u === vs[0].value)? vs.text() : vs.val(); - var id = sort; - var test = id >= vs; - return test ? el : as.sort(sort, el.prev()); + as.sort = function sort(id, li){ + var num = parseFloat(id); + var id = $(li).find('.sort').text() || -Infinity; + var at = num >= parseFloat(id); + return at ? li : sort(id, li.prev()); } $(document).on('keyup', 'input, textarea, [contenteditable]', as.wait(function(){ var el = $(this); @@ -147,4 +146,4 @@ ;$(function(){ $('.page').not(':first').hide(); $.as.route(location.hash.slice(1)); -}); \ No newline at end of file +}); diff --git a/axe.js b/axe.js new file mode 100644 index 00000000..dbcb9c0f --- /dev/null +++ b/axe.js @@ -0,0 +1,193 @@ +;(function(){ + + /* UNBUILD */ + var root; + if(typeof window !== "undefined"){ root = window } + if(typeof global !== "undefined"){ root = global } + root = root || {}; + var console = root.console || {log: function(){}}; + function USE(arg, req){ + return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){ + arg(mod = {exports: {}}); + USE[R(path)] = mod.exports; + } + function R(p){ + return p.split('/').slice(-1).toString().replace('.js',''); + } + } + if(typeof module !== "undefined"){ var common = module } + /* UNBUILD */ + + ;USE(function(module){ + if(typeof window !== "undefined"){ module.window = window } + var tmp = module.window || module; + var AXE = tmp.AXE || function(){}; + + if(AXE.window = module.window){ try{ + AXE.window.AXE = AXE; + tmp = document.createEvent('CustomEvent'); + tmp.initCustomEvent('extension', false, false, {type: "AXE"}); + (window.dispatchEvent || window.fireEvent)(tmp); + window.postMessage({type: "AXE"}, '*'); + } catch(e){} } + + try{ if(typeof common !== "undefined"){ common.exports = AXE } }catch(e){} + module.exports = AXE; + })(USE, './root'); + + ;USE(function(module){ + + var AXE = USE('./root'), Gun = (AXE.window||{}).Gun || USE('./gun', 1); + (Gun.AXE = AXE).GUN = AXE.Gun = Gun; + Gun.on('opt', function(at){ + if(!at.axe){ + at.axe = {}; + var peers = at.opt.peers, tmp; + // 1. If any remembered peers or from last cache or extension + // 2. Fallback to use hard coded peers from dApp + // 3. Or any offered peers. + //if(Gun.obj.empty(p)){ + // Gun.obj.map(['http://localhost:8765/gun'/*, 'https://guntest.herokuapp.com/gun'*/], function(url){ + // p[url] = {url: url, axe: {}}; + // }); + //} + // Our current hypothesis is that it is most optimal + // to take peers in a common network, and align + // them in a line, where you only have left and right + // peers, so messages propagate left and right in + // a linear manner with reduced overlap, and + // with one common superpeer (with ready failovers) + // in case the p2p linear latency is high. + // Or there could be plenty of other better options. + console.log("axe"); + + function verify(dht, msg, send, at) { + var puts = Object.keys(msg.put); + var soul = puts[0]; /// TODO: verify all souls in puts. Copy the msg only with subscribed souls? + var subs = dht(soul); +// console.log('[AXE] VERIFY soul: %s, subs: %s, Peers: %s, msg: ', soul, subs, Object.keys(peers), msg); + if (!subs) { return; } + var tmp = []; + Gun.obj.map(subs.split(','), function(pid) { + if (pid in peers) { + tmp.push(pid); +// console.log('[AXE] SEND TO >>>>> ', pid, msg.put.bob || msg.put); + send(msg, peers[pid]); + } + }); + /// Only connected peers in the tmp array. + if (at.on.opt.super) { + dht(soul, tmp.join(',')); + } + } + + var Rad = (Gun.window||{}).Radix || USE('./lib/radix', 1); + at.opt.dht = Rad(); + at.on('in', input/*USE('./lib/super', 1)*/, at); +// at.on('out', function(msg, a) { +// this.to.next(msg); +// console.log('[AXE] out:', msg, a); +// }, at); + if(at.opt.super){ + AXE.say = function(msg, send, at) { + if (msg.rtc) { +// console.log('[AXE] MSG WEBRTC: ', msg.rtc); + if (msg.rtc.to) { + /// Send announce to one peer only if the msg have 'to' attr + var peer = (at.on.opt.peers) ? at.on.opt.peers[msg.rtc.to] : null; +// if (peer) { at.on.opt.mesh.say(msg, peer); } + if (peer) { send(msg, peer); } + return; + } + } + if (!msg.put) { send(msg); return; } + //console.log('AXE HOOK!! ', msg); + verify(at.on.opt.dht, msg, send, at); + }; + } else { + AXE.say = function(msg, send, at) { + if (msg.rtc) { +// console.log('[AXE] MSG WEBRTC: ', msg.rtc); + } + if (!msg.put) { send(msg); return; } + verify(at.on.opt.dht, msg, send, at); + /// Always send to superpeers? + Gun.obj.map(at.on.opt.peers, function(peer) { + if (peer.url) { +// console.log('SEND TO SUPERPEER', msg); + send(msg, peer); + } + }); + }; + var connections = 0; + at.on('hi', function(opt) { + this.to.next(opt); + //console.log('AXE PEER [HI]', new Date(), opt); + connections++; + /// The first connection don't need to resubscribe the nodes. + if (connections === 1) { return; } + /// Resubscribe all nodes. + setTimeout(function() { + var souls = Object.keys(at.graph); + for (var i=0; i < souls.length; ++i) { + //at.gun.get(souls[i]).off(); + at.next[souls[i]].ack = 0; + at.gun.get(souls[i]).once(function(){}); + } + //location.reload(); + }, 500); + }, at); + } + } + this.to.next(at); // make sure to call the "next" middleware adapter. + }); + function joindht(dht, soul, pids) { + if (!pids || !soul || !dht) { return; } + var subs = dht(soul); + var tmp = subs ? subs.split(',') : []; + Gun.obj.map(pids.split(','), function(pid) { + if (pid && tmp.indexOf(pid) === -1) { tmp.push(pid); } + }); + tmp = tmp.join(','); + dht(soul, tmp); + return tmp; + } + function input(msg){ +// console.log('[AXE] input: ', msg); + var at = this.as, to = this.to, peer = (msg._||{}).via; + var opt = at.opt; + var dht = opt.dht; + var get = msg.get, soul, key; + if(peer && get){ + if(soul = get['#']){ + if(key = get['.']){ + + } else { + + } + if (!peer.id) {console.log('[*** WARN] no peer.id %s', soul);} + var pids = joindht(dht, soul, peer.id); + if (pids) { + var dht = {}; + dht[soul] = pids; + at.opt.mesh.say({dht:dht}, opt.peers[peer.id]); + } + } + } + to.next(msg); + + if (opt.rtc && msg.dht) { + Gun.obj.map(msg.dht, function(pids, soul) { + dht(soul, pids); + Gun.obj.map(pids.split(','), function(pid) { + /// TODO: here we can put an algorithm of who must connect? + if (!pid || pid in opt.peers || pid === opt.pid || opt.announce[pid]) { return; } + opt.announce[pid] = true; /// To try only one connection to the same peer. + opt.announce(pid); + }); + }); + } + } + module.exports = AXE; + })(USE, './axe'); +}()); diff --git a/examples/angular/server.js b/examples/angular/server.js index f1d1c55e..f8a1978b 100644 --- a/examples/angular/server.js +++ b/examples/angular/server.js @@ -1,4 +1,4 @@ -var port = process.env.OPENSHIFT_NODEJS_PORT || process.env.VCAP_APP_PORT || process.env.PORT || process.argv[2] || 8080; +var port = process.env.OPENSHIFT_NODEJS_PORT || process.env.VCAP_APP_PORT || process.env.PORT || process.argv[2] || 8765; var host = process.env.OPENSHIFT_NODEJS_HOST || process.env.VCAP_APP_HOST || process.env.HOST || 'localhost'; var express = require('express'); diff --git a/examples/axe.html b/examples/axe.html new file mode 100644 index 00000000..5c2caa09 --- /dev/null +++ b/examples/axe.html @@ -0,0 +1,46 @@ + + + + + + + Testing AXE + + + +

+ + + + + + + + diff --git a/examples/basic/private.html b/examples/basic/private.html new file mode 100644 index 00000000..24325c8c --- /dev/null +++ b/examples/basic/private.html @@ -0,0 +1,140 @@ + + +
+
+ +
+
+ +
+
+
+
+

Login

+ + + + +
+ + +
+

Profile

+

Data is privately encrypted by default. "+" to grant access, "x" to revoke access.

+
+
+
+
+
+
+
+ +
+ Public Key: +
+ + + + + + + \ No newline at end of file diff --git a/examples/basic/space.html b/examples/basic/space.html new file mode 100644 index 00000000..72a7f15e --- /dev/null +++ b/examples/basic/space.html @@ -0,0 +1,50 @@ +

Search

+ +
+ +
+ +
+ + +Note: No data is indexed by default, you need to add some! + + + + + + + \ No newline at end of file diff --git a/examples/basic/tables.html b/examples/basic/tables.html new file mode 100644 index 00000000..dae5a90a --- /dev/null +++ b/examples/basic/tables.html @@ -0,0 +1,130 @@ +

Tables

+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/test/user.html b/examples/basic/user.html similarity index 52% rename from test/user.html rename to examples/basic/user.html index 4837bf66..873d517c 100644 --- a/test/user.html +++ b/examples/basic/user.html @@ -5,41 +5,42 @@ +
- +
- - - - + + + - - + + + + + + + + + \ No newline at end of file diff --git a/examples/party.html b/examples/party.html new file mode 100644 index 00000000..40783099 --- /dev/null +++ b/examples/party.html @@ -0,0 +1,758 @@ + + + + Party by Neon ERA + + + + + + + + + + + + + + + + +
+ +
Join the Private Party!
+
+
+ + + + +
+
+ +
+

Your friend has invited you to add a privacy extension to your browser:

+

- Decrypts your friends' messages across any site!

+

- Stops tech monopolies from selling your private data to advertisers.

+

- Gives you ownership and control over all your data online.

+

- Creates a searchable history of your posts, friends, and more!

+
+
+
+

Express your thoughts & connect with the world around you!

+

- Discover new relationships.

+

- Keep up with friends and news in realtime.

+

- Watch fun videos and photos from people who share.

+

- But this time, you own it: fully decentralized.

+
+
+

Welcome,

+

- Your identity is created here, by you. Not on a server.

+

- It uses secure cryptographic methods to protect you.

+

- Only you have access to it, meaning even we cannot reset your password!

+

- For added security, you can freely download and run it on your own computer.

+
+
+
+
Crafted with by era.
+
+
+
+ +
+ +
+
Welcome,
+
+ +
+
+ +
+
+ + +
+ By logging in or up, you agree to the Terms of Service. +

Forgot password?

+
+ +
+ +
+ + +
+

Forgot Password

+
+
    +
  • +
  • +

    What was your first pet's name, the type of animal they were, and your favorite quirk about them? +

    +
  • +
  • +

    Where was your first kiss? Describe what they looked like. +

    +
  • +
  • +
  • +
+
+ +
+
+ +
+ + +
+

Settings

+

Your username is .

+

+ Drop in a new profile photo: +

+
+

Because no server can reset your password, you must fill out a "forgot password" recovery form. + We recommend you use simple, grammatically correct, memorable sentences for your answers. + Keep each sentence less than 50 letters so that it will be easy for you to reconstruct - + the length of your reply will be stored as a hint to help.

+
    +
  1. +

    What was your first pet's name, the type of animal they were, and your favorite quirk about them? +

    +
  2. +
  3. +

    Where was your first kiss? Describe what they looked like. +

    +
  4. +
  5. +

    The reminder of what your password is:

    +

    +
  6. +
  7. + +
  8. +
+
+
+ +

We recommend you use a memorable and grammatically correct sentence as your passphrase. + Then write it down on paper and store it some place safe that will be easy for you to find. Here are some tips:

+
    +
  • - What gives you purpose and meaning in your life?
  • +
  • - Say something nice about an important person in your life.
  • +
  • - If you could work on your hobby full time, what goals would you have?
  • +
  • - Try to combine all of them into a rhyme, or something funny that makes you laugh.
  • +
  • + + +
  • +
  • - Ex, "Uncle Ben taught me to fight for what is right, I might have a spider bite."
  • +
  • - Ex, "I am quite better than the Dark Knight, despite any Kryptonite."
  • +
  • - Ex, "Give me a cause for these claws, I have no paws and no laws."
  • +
  • - Ex, "My suite is made of red supplements, with hidden jet compartments."
  • +
+
+

You are currently connected to 2 peers.

+ +
+ +
+ +
+ +
+ +
+ +

Welcome!

+ +
+
+ +
+ +
+
  • +
    +
  • +
    +
    + +
    + +
    + +

    Contacts

    + +
    +
  • +
    + + +
    +
    + FB ID: +
    +
  • +
    +
    + +
    + +
    + +

    Hello world!

    +
    + + + + + + + + + \ No newline at end of file diff --git a/examples/pop.png b/examples/pop.png new file mode 100644 index 00000000..96bdfb80 Binary files /dev/null and b/examples/pop.png differ 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..7331c712 --- /dev/null +++ b/examples/react-native/README.md @@ -0,0 +1,18 @@ +# Gun on react-native! +--- +### running the demo +1. do `yarn install` on the directory of the demo `examples/react-native` +2. run the demo with `react-native run-ios` or `react-native run-android` + +### debugging +i would recommend using [react-native-debugger](https://github.com/facebook/react-devtools/tree/master/packages/react-devtools) but you can use chrome's debugger as well + +- ios: `cmd+D` then `Debug JS Remotely` +- android: `cmd+M` then `Debug JS Remotely` + +now you have access to the gun globals on the console which are +`gun` -> the root gun +`user` -> the gun user +--- +# how it all of this is done +since react-native doesnt provide the crypto module that we desire the most and all of the packages are incompatible with react-native/sea, and so to get `sea.js` working we use a webview(react-native browser) and bridge the crypto module from that browser to the global `window` and thats exactly what `webview-crypto` does, thanks to [webview-crypto repo](https://github.com/saulshanabrook/webview-crypto), the webview-crypto provided in this repo is somewhat the same but modified to get it working and mostly compatible with sea/react-native (even though there is a polyfiller for that but it just doesnt work ;/). \ No newline at end of file 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 00000000..a2f59082 Binary files /dev/null and b/examples/react-native/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/examples/react-native/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/examples/react-native/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 00000000..1b523998 Binary files /dev/null and b/examples/react-native/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/examples/react-native/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/examples/react-native/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000..ff10afd6 Binary files /dev/null and b/examples/react-native/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/examples/react-native/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/examples/react-native/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 00000000..115a4c76 Binary files /dev/null and b/examples/react-native/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/examples/react-native/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/examples/react-native/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000..dcd3cd80 Binary files /dev/null and b/examples/react-native/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ 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 00000000..459ca609 Binary files /dev/null and b/examples/react-native/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ 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 00000000..8ca12fe0 Binary files /dev/null and b/examples/react-native/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ 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 00000000..8e19b410 Binary files /dev/null and b/examples/react-native/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/examples/react-native/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/examples/react-native/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 00000000..b824ebdd Binary files /dev/null and b/examples/react-native/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ 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 00000000..4c19a13c Binary files /dev/null and b/examples/react-native/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ 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 00000000..01b8bf6b Binary files /dev/null and b/examples/react-native/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/examples/react-native/android/gradle/wrapper/gradle-wrapper.properties b/examples/react-native/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..b6517bb1 --- /dev/null +++ b/examples/react-native/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip diff --git a/examples/react-native/android/gradlew b/examples/react-native/android/gradlew new file mode 100755 index 00000000..cccdd3d5 --- /dev/null +++ b/examples/react-native/android/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/examples/react-native/android/gradlew.bat b/examples/react-native/android/gradlew.bat new file mode 100644 index 00000000..e95643d6 --- /dev/null +++ b/examples/react-native/android/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/examples/react-native/android/keystores/BUCK b/examples/react-native/android/keystores/BUCK new file mode 100644 index 00000000..88e4c31b --- /dev/null +++ b/examples/react-native/android/keystores/BUCK @@ -0,0 +1,8 @@ +keystore( + name = "debug", + properties = "debug.keystore.properties", + store = "debug.keystore", + visibility = [ + "PUBLIC", + ], +) diff --git a/examples/react-native/android/keystores/debug.keystore.properties b/examples/react-native/android/keystores/debug.keystore.properties new file mode 100644 index 00000000..121bfb49 --- /dev/null +++ b/examples/react-native/android/keystores/debug.keystore.properties @@ -0,0 +1,4 @@ +key.store=debug.keystore +key.alias=androiddebugkey +key.store.password=android +key.alias.password=android diff --git a/examples/react-native/android/settings.gradle b/examples/react-native/android/settings.gradle new file mode 100644 index 00000000..78376f3d --- /dev/null +++ b/examples/react-native/android/settings.gradle @@ -0,0 +1,5 @@ +rootProject.name = 'GunDemo' +include ':react-native-webview-bridge' +project(':react-native-webview-bridge').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview-bridge/android') + +include ':app' diff --git a/examples/react-native/app.json b/examples/react-native/app.json new file mode 100644 index 00000000..0cb2cb60 --- /dev/null +++ b/examples/react-native/app.json @@ -0,0 +1,4 @@ +{ + "name": "GunDemo", + "displayName": "GunDemo" +} \ No newline at end of file diff --git a/examples/react-native/index.js b/examples/react-native/index.js new file mode 100644 index 00000000..9e370d8c --- /dev/null +++ b/examples/react-native/index.js @@ -0,0 +1,8 @@ +/** @format */ +import './shim'; +import {AppRegistry} from 'react-native'; +import App from './src/App'; +import {name as appName} from './app.json'; + +AppRegistry.registerComponent(appName, () => App); +console.disableYellowBox = true; diff --git a/examples/react-native/ios/GunDemo-tvOS/Info.plist b/examples/react-native/ios/GunDemo-tvOS/Info.plist new file mode 100644 index 00000000..2fb6a11c --- /dev/null +++ b/examples/react-native/ios/GunDemo-tvOS/Info.plist @@ -0,0 +1,54 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + NSLocationWhenInUseUsageDescription + + NSAppTransportSecurity + + + NSExceptionDomains + + localhost + + NSExceptionAllowsInsecureHTTPLoads + + + + + + diff --git a/examples/react-native/ios/GunDemo-tvOSTests/Info.plist b/examples/react-native/ios/GunDemo-tvOSTests/Info.plist new file mode 100644 index 00000000..886825cc --- /dev/null +++ b/examples/react-native/ios/GunDemo-tvOSTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/examples/react-native/ios/GunDemo.xcodeproj/project.pbxproj b/examples/react-native/ios/GunDemo.xcodeproj/project.pbxproj new file mode 100644 index 00000000..b07184e6 --- /dev/null +++ b/examples/react-native/ios/GunDemo.xcodeproj/project.pbxproj @@ -0,0 +1,1590 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; }; + 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; }; + 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; }; + 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; }; + 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; }; + 00E356F31AD99517003FC87E /* GunDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* GunDemoTests.m */; }; + 11D1A2F320CAFA9E000508D9 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */; }; + 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; }; + 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; }; + 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; }; + 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; + 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; + 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; + 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; + 140ED2AC1D01E1AD002B40FF /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; + 146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; + 2D02E4BC1E0B4A80006451C7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; + 2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; + 2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; + 2D02E4C21E0B4AEC006451C7 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation.a */; }; + 2D02E4C31E0B4AEC006451C7 /* libRCTImage-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */; }; + 2D02E4C41E0B4AEC006451C7 /* libRCTLinking-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E881DF850E9000B6D8A /* libRCTLinking-tvOS.a */; }; + 2D02E4C51E0B4AEC006451C7 /* libRCTNetwork-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E8C1DF850E9000B6D8A /* libRCTNetwork-tvOS.a */; }; + 2D02E4C61E0B4AEC006451C7 /* libRCTSettings-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E901DF850E9000B6D8A /* libRCTSettings-tvOS.a */; }; + 2D02E4C71E0B4AEC006451C7 /* libRCTText-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E941DF850E9000B6D8A /* libRCTText-tvOS.a */; }; + 2D02E4C81E0B4AEC006451C7 /* libRCTWebSocket-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */; }; + 2D16E6881FA4F8E400B85C8A /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D16E6891FA4F8E400B85C8A /* libReact.a */; }; + 2DCD954D1E0B4F2C00145EB5 /* GunDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* GunDemoTests.m */; }; + 2DF0FFEE2056DD460020B375 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3EA31DF850E9000B6D8A /* libReact.a */; }; + 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; + 99082B67837B4425B90A62F2 /* libReact-Native-Webview-Bridge.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 929405EFCB05436C99DFC544 /* libReact-Native-Webview-Bridge.a */; }; + ADBDB9381DFEBF1600ED6528 /* libRCTBlob.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ADBDB9271DFEBF0700ED6528 /* libRCTBlob.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTActionSheet; + }; + 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTGeolocation; + }; + 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 58B5115D1A9E6B3D00147676; + remoteInfo = RCTImage; + }; + 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 58B511DB1A9E6C8500147676; + remoteInfo = RCTNetwork; + }; + 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 832C81801AAF6DEF007FA2F7; + remoteInfo = RCTVibration; + }; + 00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 13B07F861A680F5B00A75B9A; + remoteInfo = GunDemo; + }; + 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTSettings; + }; + 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3C86DF461ADF2C930047B81A; + remoteInfo = RCTWebSocket; + }; + 146834031AC3E56700842450 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192; + remoteInfo = React; + }; + 2344B40F21A4F53000763E72 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5188B081163B45E68CD4AE92 /* React-Native-Webview-Bridge.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 4114DC4C1C187C3A003CD988; + remoteInfo = "React-Native-Webview-Bridge"; + }; + 2D02E4911E0B4A5D006451C7 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2D02E47A1E0B4A5D006451C7; + remoteInfo = "GunDemo-tvOS"; + }; + 2D16E6711FA4F8DC00B85C8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = ADD01A681E09402E00F6D226; + remoteInfo = "RCTBlob-tvOS"; + }; + 2D16E6831FA4F8DC00B85C8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3DBE0D001F3B181A0099AA32; + remoteInfo = fishhook; + }; + 2D16E6851FA4F8DC00B85C8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3DBE0D0D1F3B181C0099AA32; + remoteInfo = "fishhook-tvOS"; + }; + 2DF0FFDE2056DD460020B375 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = EBF21BDC1FC498900052F4D5; + remoteInfo = jsinspector; + }; + 2DF0FFE02056DD460020B375 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = EBF21BFA1FC4989A0052F4D5; + remoteInfo = "jsinspector-tvOS"; + }; + 2DF0FFE22056DD460020B375 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 139D7ECE1E25DB7D00323FB7; + remoteInfo = "third-party"; + }; + 2DF0FFE42056DD460020B375 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3D383D3C1EBD27B6005632C8; + remoteInfo = "third-party-tvOS"; + }; + 2DF0FFE62056DD460020B375 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 139D7E881E25C6D100323FB7; + remoteInfo = "double-conversion"; + }; + 2DF0FFE82056DD460020B375 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3D383D621EBD27B9005632C8; + remoteInfo = "double-conversion-tvOS"; + }; + 2DF0FFEA2056DD460020B375 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9936F3131F5F2E4B0010BF04; + remoteInfo = privatedata; + }; + 2DF0FFEC2056DD460020B375 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9936F32F1F5F2E5B0010BF04; + remoteInfo = "privatedata-tvOS"; + }; + 3DAD3E831DF850E9000B6D8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 2D2A283A1D9B042B00D4039D; + remoteInfo = "RCTImage-tvOS"; + }; + 3DAD3E871DF850E9000B6D8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 2D2A28471D9B043800D4039D; + remoteInfo = "RCTLinking-tvOS"; + }; + 3DAD3E8B1DF850E9000B6D8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 2D2A28541D9B044C00D4039D; + remoteInfo = "RCTNetwork-tvOS"; + }; + 3DAD3E8F1DF850E9000B6D8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 2D2A28611D9B046600D4039D; + remoteInfo = "RCTSettings-tvOS"; + }; + 3DAD3E931DF850E9000B6D8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 2D2A287B1D9B048500D4039D; + remoteInfo = "RCTText-tvOS"; + }; + 3DAD3E981DF850E9000B6D8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 2D2A28881D9B049200D4039D; + remoteInfo = "RCTWebSocket-tvOS"; + }; + 3DAD3EA21DF850E9000B6D8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 2D2A28131D9B038B00D4039D; + remoteInfo = "React-tvOS"; + }; + 3DAD3EA41DF850E9000B6D8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3D3C059A1DE3340900C268FA; + remoteInfo = yoga; + }; + 3DAD3EA61DF850E9000B6D8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3D3C06751DE3340C00C268FA; + remoteInfo = "yoga-tvOS"; + }; + 3DAD3EA81DF850E9000B6D8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3D3CD9251DE5FBEC00167DC4; + remoteInfo = cxxreact; + }; + 3DAD3EAA1DF850E9000B6D8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3D3CD9321DE5FBEE00167DC4; + remoteInfo = "cxxreact-tvOS"; + }; + 3DAD3EAC1DF850E9000B6D8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3D3CD90B1DE5FBD600167DC4; + remoteInfo = jschelpers; + }; + 3DAD3EAE1DF850E9000B6D8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3D3CD9181DE5FBD800167DC4; + remoteInfo = "jschelpers-tvOS"; + }; + 5E9157321DD0AC6500FF2AA8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTAnimation; + }; + 5E9157341DD0AC6500FF2AA8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 2D2A28201D9B03D100D4039D; + remoteInfo = "RCTAnimation-tvOS"; + }; + 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTLinking; + }; + 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 58B5119B1A9E6C1200147676; + remoteInfo = RCTText; + }; + ADBDB9261DFEBF0700ED6528 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 358F4ED71D1E81A9004DF814; + remoteInfo = RCTBlob; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = ""; }; + 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = "../node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj"; sourceTree = ""; }; + 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = "../node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj"; sourceTree = ""; }; + 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = "../node_modules/react-native/Libraries/Image/RCTImage.xcodeproj"; sourceTree = ""; }; + 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = ""; }; + 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = ""; }; + 00E356EE1AD99517003FC87E /* GunDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GunDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 00E356F21AD99517003FC87E /* GunDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GunDemoTests.m; sourceTree = ""; }; + 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = ""; }; + 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = ""; }; + 13B07F961A680F5B00A75B9A /* GunDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GunDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = GunDemo/AppDelegate.h; sourceTree = ""; }; + 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = GunDemo/AppDelegate.m; sourceTree = ""; }; + 13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; + 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = GunDemo/Images.xcassets; sourceTree = ""; }; + 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = GunDemo/Info.plist; sourceTree = ""; }; + 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = GunDemo/main.m; sourceTree = ""; }; + 146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = ""; }; + 2D02E47B1E0B4A5D006451C7 /* GunDemo-tvOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "GunDemo-tvOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 2D02E4901E0B4A5D006451C7 /* GunDemo-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "GunDemo-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 2D16E6891FA4F8E400B85C8A /* libReact.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libReact.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 5188B081163B45E68CD4AE92 /* React-Native-Webview-Bridge.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = "React-Native-Webview-Bridge.xcodeproj"; path = "../node_modules/react-native-webview-bridge/ios/React-Native-Webview-Bridge.xcodeproj"; sourceTree = ""; }; + 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = ""; }; + 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; }; + 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; }; + 929405EFCB05436C99DFC544 /* libReact-Native-Webview-Bridge.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = "libReact-Native-Webview-Bridge.a"; sourceTree = ""; }; + ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTBlob.xcodeproj; path = "../node_modules/react-native/Libraries/Blob/RCTBlob.xcodeproj"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 00E356EB1AD99517003FC87E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 140ED2AC1D01E1AD002B40FF /* libReact.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ADBDB9381DFEBF1600ED6528 /* libRCTBlob.a in Frameworks */, + 11D1A2F320CAFA9E000508D9 /* libRCTAnimation.a in Frameworks */, + 146834051AC3E58100842450 /* libReact.a in Frameworks */, + 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */, + 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */, + 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */, + 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */, + 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */, + 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */, + 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */, + 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */, + 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */, + 99082B67837B4425B90A62F2 /* libReact-Native-Webview-Bridge.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2D02E4781E0B4A5D006451C7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2D16E6881FA4F8E400B85C8A /* libReact.a in Frameworks */, + 2D02E4C21E0B4AEC006451C7 /* libRCTAnimation.a in Frameworks */, + 2D02E4C31E0B4AEC006451C7 /* libRCTImage-tvOS.a in Frameworks */, + 2D02E4C41E0B4AEC006451C7 /* libRCTLinking-tvOS.a in Frameworks */, + 2D02E4C51E0B4AEC006451C7 /* libRCTNetwork-tvOS.a in Frameworks */, + 2D02E4C61E0B4AEC006451C7 /* libRCTSettings-tvOS.a in Frameworks */, + 2D02E4C71E0B4AEC006451C7 /* libRCTText-tvOS.a in Frameworks */, + 2D02E4C81E0B4AEC006451C7 /* libRCTWebSocket-tvOS.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2D02E48D1E0B4A5D006451C7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2DF0FFEE2056DD460020B375 /* libReact.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 00C302A81ABCB8CE00DB3ED1 /* Products */ = { + isa = PBXGroup; + children = ( + 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */, + ); + name = Products; + sourceTree = ""; + }; + 00C302B61ABCB90400DB3ED1 /* Products */ = { + isa = PBXGroup; + children = ( + 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */, + ); + name = Products; + sourceTree = ""; + }; + 00C302BC1ABCB91800DB3ED1 /* Products */ = { + isa = PBXGroup; + children = ( + 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */, + 3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */, + ); + name = Products; + sourceTree = ""; + }; + 00C302D41ABCB9D200DB3ED1 /* Products */ = { + isa = PBXGroup; + children = ( + 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */, + 3DAD3E8C1DF850E9000B6D8A /* libRCTNetwork-tvOS.a */, + ); + name = Products; + sourceTree = ""; + }; + 00C302E01ABCB9EE00DB3ED1 /* Products */ = { + isa = PBXGroup; + children = ( + 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */, + ); + name = Products; + sourceTree = ""; + }; + 00E356EF1AD99517003FC87E /* GunDemoTests */ = { + isa = PBXGroup; + children = ( + 00E356F21AD99517003FC87E /* GunDemoTests.m */, + 00E356F01AD99517003FC87E /* Supporting Files */, + ); + path = GunDemoTests; + sourceTree = ""; + }; + 00E356F01AD99517003FC87E /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 00E356F11AD99517003FC87E /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 139105B71AF99BAD00B5F7CC /* Products */ = { + isa = PBXGroup; + children = ( + 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */, + 3DAD3E901DF850E9000B6D8A /* libRCTSettings-tvOS.a */, + ); + name = Products; + sourceTree = ""; + }; + 139FDEE71B06529A00C62182 /* Products */ = { + isa = PBXGroup; + children = ( + 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */, + 3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */, + 2D16E6841FA4F8DC00B85C8A /* libfishhook.a */, + 2D16E6861FA4F8DC00B85C8A /* libfishhook-tvOS.a */, + ); + name = Products; + sourceTree = ""; + }; + 13B07FAE1A68108700A75B9A /* GunDemo */ = { + isa = PBXGroup; + children = ( + 008F07F21AC5B25A0029DE68 /* main.jsbundle */, + 13B07FAF1A68108700A75B9A /* AppDelegate.h */, + 13B07FB01A68108700A75B9A /* AppDelegate.m */, + 13B07FB51A68108700A75B9A /* Images.xcassets */, + 13B07FB61A68108700A75B9A /* Info.plist */, + 13B07FB11A68108700A75B9A /* LaunchScreen.xib */, + 13B07FB71A68108700A75B9A /* main.m */, + ); + name = GunDemo; + sourceTree = ""; + }; + 146834001AC3E56700842450 /* Products */ = { + isa = PBXGroup; + children = ( + 146834041AC3E56700842450 /* libReact.a */, + 3DAD3EA31DF850E9000B6D8A /* libReact.a */, + 3DAD3EA51DF850E9000B6D8A /* libyoga.a */, + 3DAD3EA71DF850E9000B6D8A /* libyoga.a */, + 3DAD3EA91DF850E9000B6D8A /* libcxxreact.a */, + 3DAD3EAB1DF850E9000B6D8A /* libcxxreact.a */, + 3DAD3EAD1DF850E9000B6D8A /* libjschelpers.a */, + 3DAD3EAF1DF850E9000B6D8A /* libjschelpers.a */, + 2DF0FFDF2056DD460020B375 /* libjsinspector.a */, + 2DF0FFE12056DD460020B375 /* libjsinspector-tvOS.a */, + 2DF0FFE32056DD460020B375 /* libthird-party.a */, + 2DF0FFE52056DD460020B375 /* libthird-party.a */, + 2DF0FFE72056DD460020B375 /* libdouble-conversion.a */, + 2DF0FFE92056DD460020B375 /* libdouble-conversion.a */, + 2DF0FFEB2056DD460020B375 /* libprivatedata.a */, + 2DF0FFED2056DD460020B375 /* libprivatedata-tvOS.a */, + ); + name = Products; + sourceTree = ""; + }; + 2344B3E621A4F52C00763E72 /* Recovered References */ = { + isa = PBXGroup; + children = ( + 929405EFCB05436C99DFC544 /* libReact-Native-Webview-Bridge.a */, + ); + name = "Recovered References"; + sourceTree = ""; + }; + 2344B40C21A4F53000763E72 /* Products */ = { + isa = PBXGroup; + children = ( + 2344B41021A4F53000763E72 /* libReact-Native-Webview-Bridge.a */, + ); + name = Products; + sourceTree = ""; + }; + 2D16E6871FA4F8E400B85C8A /* Frameworks */ = { + isa = PBXGroup; + children = ( + 2D16E6891FA4F8E400B85C8A /* libReact.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + 5E91572E1DD0AC6500FF2AA8 /* Products */ = { + isa = PBXGroup; + children = ( + 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */, + 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation.a */, + ); + name = Products; + sourceTree = ""; + }; + 78C398B11ACF4ADC00677621 /* Products */ = { + isa = PBXGroup; + children = ( + 78C398B91ACF4ADC00677621 /* libRCTLinking.a */, + 3DAD3E881DF850E9000B6D8A /* libRCTLinking-tvOS.a */, + ); + name = Products; + sourceTree = ""; + }; + 832341AE1AAA6A7D00B99B32 /* Libraries */ = { + isa = PBXGroup; + children = ( + 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */, + 146833FF1AC3E56700842450 /* React.xcodeproj */, + 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */, + ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */, + 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */, + 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */, + 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */, + 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */, + 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */, + 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */, + 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */, + 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */, + 5188B081163B45E68CD4AE92 /* React-Native-Webview-Bridge.xcodeproj */, + ); + name = Libraries; + sourceTree = ""; + }; + 832341B11AAA6A8300B99B32 /* Products */ = { + isa = PBXGroup; + children = ( + 832341B51AAA6A8300B99B32 /* libRCTText.a */, + 3DAD3E941DF850E9000B6D8A /* libRCTText-tvOS.a */, + ); + name = Products; + sourceTree = ""; + }; + 83CBB9F61A601CBA00E9B192 = { + isa = PBXGroup; + children = ( + 13B07FAE1A68108700A75B9A /* GunDemo */, + 832341AE1AAA6A7D00B99B32 /* Libraries */, + 00E356EF1AD99517003FC87E /* GunDemoTests */, + 83CBBA001A601CBA00E9B192 /* Products */, + 2D16E6871FA4F8E400B85C8A /* Frameworks */, + 2344B3E621A4F52C00763E72 /* Recovered References */, + ); + indentWidth = 2; + sourceTree = ""; + tabWidth = 2; + usesTabs = 0; + }; + 83CBBA001A601CBA00E9B192 /* Products */ = { + isa = PBXGroup; + children = ( + 13B07F961A680F5B00A75B9A /* GunDemo.app */, + 00E356EE1AD99517003FC87E /* GunDemoTests.xctest */, + 2D02E47B1E0B4A5D006451C7 /* GunDemo-tvOS.app */, + 2D02E4901E0B4A5D006451C7 /* GunDemo-tvOSTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + ADBDB9201DFEBF0600ED6528 /* Products */ = { + isa = PBXGroup; + children = ( + ADBDB9271DFEBF0700ED6528 /* libRCTBlob.a */, + 2D16E6721FA4F8DC00B85C8A /* libRCTBlob-tvOS.a */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 00E356ED1AD99517003FC87E /* GunDemoTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "GunDemoTests" */; + buildPhases = ( + 00E356EA1AD99517003FC87E /* Sources */, + 00E356EB1AD99517003FC87E /* Frameworks */, + 00E356EC1AD99517003FC87E /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 00E356F51AD99517003FC87E /* PBXTargetDependency */, + ); + name = GunDemoTests; + productName = GunDemoTests; + productReference = 00E356EE1AD99517003FC87E /* GunDemoTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 13B07F861A680F5B00A75B9A /* GunDemo */ = { + isa = PBXNativeTarget; + buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "GunDemo" */; + buildPhases = ( + 13B07F871A680F5B00A75B9A /* Sources */, + 13B07F8C1A680F5B00A75B9A /* Frameworks */, + 13B07F8E1A680F5B00A75B9A /* Resources */, + 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = GunDemo; + productName = "Hello World"; + productReference = 13B07F961A680F5B00A75B9A /* GunDemo.app */; + productType = "com.apple.product-type.application"; + }; + 2D02E47A1E0B4A5D006451C7 /* GunDemo-tvOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2D02E4BA1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "GunDemo-tvOS" */; + buildPhases = ( + 2D02E4771E0B4A5D006451C7 /* Sources */, + 2D02E4781E0B4A5D006451C7 /* Frameworks */, + 2D02E4791E0B4A5D006451C7 /* Resources */, + 2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "GunDemo-tvOS"; + productName = "GunDemo-tvOS"; + productReference = 2D02E47B1E0B4A5D006451C7 /* GunDemo-tvOS.app */; + productType = "com.apple.product-type.application"; + }; + 2D02E48F1E0B4A5D006451C7 /* GunDemo-tvOSTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2D02E4BB1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "GunDemo-tvOSTests" */; + buildPhases = ( + 2D02E48C1E0B4A5D006451C7 /* Sources */, + 2D02E48D1E0B4A5D006451C7 /* Frameworks */, + 2D02E48E1E0B4A5D006451C7 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 2D02E4921E0B4A5D006451C7 /* PBXTargetDependency */, + ); + name = "GunDemo-tvOSTests"; + productName = "GunDemo-tvOSTests"; + productReference = 2D02E4901E0B4A5D006451C7 /* GunDemo-tvOSTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 83CBB9F71A601CBA00E9B192 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 940; + ORGANIZATIONNAME = Facebook; + TargetAttributes = { + 00E356ED1AD99517003FC87E = { + CreatedOnToolsVersion = 6.2; + TestTargetID = 13B07F861A680F5B00A75B9A; + }; + 2D02E47A1E0B4A5D006451C7 = { + CreatedOnToolsVersion = 8.2.1; + ProvisioningStyle = Automatic; + }; + 2D02E48F1E0B4A5D006451C7 = { + CreatedOnToolsVersion = 8.2.1; + ProvisioningStyle = Automatic; + TestTargetID = 2D02E47A1E0B4A5D006451C7; + }; + }; + }; + buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "GunDemo" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 83CBB9F61A601CBA00E9B192; + productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */; + ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; + }, + { + ProductGroup = 5E91572E1DD0AC6500FF2AA8 /* Products */; + ProjectRef = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */; + }, + { + ProductGroup = ADBDB9201DFEBF0600ED6528 /* Products */; + ProjectRef = ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */; + }, + { + ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */; + ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; + }, + { + ProductGroup = 00C302BC1ABCB91800DB3ED1 /* Products */; + ProjectRef = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; + }, + { + ProductGroup = 78C398B11ACF4ADC00677621 /* Products */; + ProjectRef = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; + }, + { + ProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */; + ProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; + }, + { + ProductGroup = 139105B71AF99BAD00B5F7CC /* Products */; + ProjectRef = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; + }, + { + ProductGroup = 832341B11AAA6A8300B99B32 /* Products */; + ProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; + }, + { + ProductGroup = 00C302E01ABCB9EE00DB3ED1 /* Products */; + ProjectRef = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; + }, + { + ProductGroup = 139FDEE71B06529A00C62182 /* Products */; + ProjectRef = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; + }, + { + ProductGroup = 2344B40C21A4F53000763E72 /* Products */; + ProjectRef = 5188B081163B45E68CD4AE92 /* React-Native-Webview-Bridge.xcodeproj */; + }, + { + ProductGroup = 146834001AC3E56700842450 /* Products */; + ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 13B07F861A680F5B00A75B9A /* GunDemo */, + 00E356ED1AD99517003FC87E /* GunDemoTests */, + 2D02E47A1E0B4A5D006451C7 /* GunDemo-tvOS */, + 2D02E48F1E0B4A5D006451C7 /* GunDemo-tvOSTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTActionSheet.a; + remoteRef = 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTGeolocation.a; + remoteRef = 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTImage.a; + remoteRef = 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTNetwork.a; + remoteRef = 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTVibration.a; + remoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTSettings.a; + remoteRef = 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTWebSocket.a; + remoteRef = 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 146834041AC3E56700842450 /* libReact.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libReact.a; + remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 2344B41021A4F53000763E72 /* libReact-Native-Webview-Bridge.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libReact-Native-Webview-Bridge.a"; + remoteRef = 2344B40F21A4F53000763E72 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 2D16E6721FA4F8DC00B85C8A /* libRCTBlob-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libRCTBlob-tvOS.a"; + remoteRef = 2D16E6711FA4F8DC00B85C8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 2D16E6841FA4F8DC00B85C8A /* libfishhook.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libfishhook.a; + remoteRef = 2D16E6831FA4F8DC00B85C8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 2D16E6861FA4F8DC00B85C8A /* libfishhook-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libfishhook-tvOS.a"; + remoteRef = 2D16E6851FA4F8DC00B85C8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 2DF0FFDF2056DD460020B375 /* libjsinspector.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libjsinspector.a; + remoteRef = 2DF0FFDE2056DD460020B375 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 2DF0FFE12056DD460020B375 /* libjsinspector-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libjsinspector-tvOS.a"; + remoteRef = 2DF0FFE02056DD460020B375 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 2DF0FFE32056DD460020B375 /* libthird-party.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libthird-party.a"; + remoteRef = 2DF0FFE22056DD460020B375 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 2DF0FFE52056DD460020B375 /* libthird-party.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libthird-party.a"; + remoteRef = 2DF0FFE42056DD460020B375 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 2DF0FFE72056DD460020B375 /* libdouble-conversion.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libdouble-conversion.a"; + remoteRef = 2DF0FFE62056DD460020B375 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 2DF0FFE92056DD460020B375 /* libdouble-conversion.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libdouble-conversion.a"; + remoteRef = 2DF0FFE82056DD460020B375 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 2DF0FFEB2056DD460020B375 /* libprivatedata.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libprivatedata.a; + remoteRef = 2DF0FFEA2056DD460020B375 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 2DF0FFED2056DD460020B375 /* libprivatedata-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libprivatedata-tvOS.a"; + remoteRef = 2DF0FFEC2056DD460020B375 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libRCTImage-tvOS.a"; + remoteRef = 3DAD3E831DF850E9000B6D8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3DAD3E881DF850E9000B6D8A /* libRCTLinking-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libRCTLinking-tvOS.a"; + remoteRef = 3DAD3E871DF850E9000B6D8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3DAD3E8C1DF850E9000B6D8A /* libRCTNetwork-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libRCTNetwork-tvOS.a"; + remoteRef = 3DAD3E8B1DF850E9000B6D8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3DAD3E901DF850E9000B6D8A /* libRCTSettings-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libRCTSettings-tvOS.a"; + remoteRef = 3DAD3E8F1DF850E9000B6D8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3DAD3E941DF850E9000B6D8A /* libRCTText-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libRCTText-tvOS.a"; + remoteRef = 3DAD3E931DF850E9000B6D8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libRCTWebSocket-tvOS.a"; + remoteRef = 3DAD3E981DF850E9000B6D8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3DAD3EA31DF850E9000B6D8A /* libReact.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libReact.a; + remoteRef = 3DAD3EA21DF850E9000B6D8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3DAD3EA51DF850E9000B6D8A /* libyoga.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libyoga.a; + remoteRef = 3DAD3EA41DF850E9000B6D8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3DAD3EA71DF850E9000B6D8A /* libyoga.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libyoga.a; + remoteRef = 3DAD3EA61DF850E9000B6D8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3DAD3EA91DF850E9000B6D8A /* libcxxreact.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libcxxreact.a; + remoteRef = 3DAD3EA81DF850E9000B6D8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3DAD3EAB1DF850E9000B6D8A /* libcxxreact.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libcxxreact.a; + remoteRef = 3DAD3EAA1DF850E9000B6D8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3DAD3EAD1DF850E9000B6D8A /* libjschelpers.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libjschelpers.a; + remoteRef = 3DAD3EAC1DF850E9000B6D8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3DAD3EAF1DF850E9000B6D8A /* libjschelpers.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libjschelpers.a; + remoteRef = 3DAD3EAE1DF850E9000B6D8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTAnimation.a; + remoteRef = 5E9157321DD0AC6500FF2AA8 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTAnimation.a; + remoteRef = 5E9157341DD0AC6500FF2AA8 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 78C398B91ACF4ADC00677621 /* libRCTLinking.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTLinking.a; + remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 832341B51AAA6A8300B99B32 /* libRCTText.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTText.a; + remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + ADBDB9271DFEBF0700ED6528 /* libRCTBlob.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTBlob.a; + remoteRef = ADBDB9261DFEBF0700ED6528 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + 00E356EC1AD99517003FC87E /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 13B07F8E1A680F5B00A75B9A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, + 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2D02E4791E0B4A5D006451C7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2D02E48E1E0B4A5D006451C7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Bundle React Native code and images"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh"; + }; + 2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Bundle React Native Code And Images"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 00E356EA1AD99517003FC87E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 00E356F31AD99517003FC87E /* GunDemoTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 13B07F871A680F5B00A75B9A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, + 13B07FC11A68108700A75B9A /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2D02E4771E0B4A5D006451C7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */, + 2D02E4BC1E0B4A80006451C7 /* AppDelegate.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2D02E48C1E0B4A5D006451C7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2DCD954D1E0B4F2C00145EB5 /* GunDemoTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 00E356F51AD99517003FC87E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 13B07F861A680F5B00A75B9A /* GunDemo */; + targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */; + }; + 2D02E4921E0B4A5D006451C7 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 2D02E47A1E0B4A5D006451C7 /* GunDemo-tvOS */; + targetProxy = 2D02E4911E0B4A5D006451C7 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = { + isa = PBXVariantGroup; + children = ( + 13B07FB21A68108700A75B9A /* Base */, + ); + name = LaunchScreen.xib; + path = GunDemo; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 00E356F61AD99517003FC87E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../node_modules/react-native-webview-bridge/ios", + ); + INFOPLIST_FILE = GunDemoTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + ); + OTHER_LDFLAGS = ( + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GunDemo.app/GunDemo"; + }; + name = Debug; + }; + 00E356F71AD99517003FC87E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + COPY_PHASE_STRIP = NO; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../node_modules/react-native-webview-bridge/ios", + ); + INFOPLIST_FILE = GunDemoTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + ); + OTHER_LDFLAGS = ( + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GunDemo.app/GunDemo"; + }; + name = Release; + }; + 13B07F941A680F5B00A75B9A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = NO; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../node_modules/react-native-webview-bridge/ios", + ); + INFOPLIST_FILE = GunDemo/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = GunDemo; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 13B07F951A680F5B00A75B9A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CURRENT_PROJECT_VERSION = 1; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../node_modules/react-native-webview-bridge/ios", + ); + INFOPLIST_FILE = GunDemo/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = GunDemo; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + 2D02E4971E0B4A5E006451C7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_TESTABILITY = YES; + GCC_NO_COMMON_BLOCKS = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../node_modules/react-native-webview-bridge/ios", + ); + INFOPLIST_FILE = "GunDemo-tvOS/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + ); + OTHER_LDFLAGS = ( + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.GunDemo-tvOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 9.2; + }; + name = Debug; + }; + 2D02E4981E0B4A5E006451C7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_NO_COMMON_BLOCKS = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../node_modules/react-native-webview-bridge/ios", + ); + INFOPLIST_FILE = "GunDemo-tvOS/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + ); + OTHER_LDFLAGS = ( + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.GunDemo-tvOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 9.2; + }; + name = Release; + }; + 2D02E4991E0B4A5E006451C7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_TESTABILITY = YES; + GCC_NO_COMMON_BLOCKS = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../node_modules/react-native-webview-bridge/ios", + ); + INFOPLIST_FILE = "GunDemo-tvOSTests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + ); + OTHER_LDFLAGS = ( + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.GunDemo-tvOSTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GunDemo-tvOS.app/GunDemo-tvOS"; + TVOS_DEPLOYMENT_TARGET = 10.1; + }; + name = Debug; + }; + 2D02E49A1E0B4A5E006451C7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_NO_COMMON_BLOCKS = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../node_modules/react-native-webview-bridge/ios", + ); + INFOPLIST_FILE = "GunDemo-tvOSTests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + ); + OTHER_LDFLAGS = ( + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.GunDemo-tvOSTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GunDemo-tvOS.app/GunDemo-tvOS"; + TVOS_DEPLOYMENT_TARGET = 10.1; + }; + name = Release; + }; + 83CBBA201A601CBA00E9B192 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 83CBBA211A601CBA00E9B192 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "GunDemoTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 00E356F61AD99517003FC87E /* Debug */, + 00E356F71AD99517003FC87E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "GunDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 13B07F941A680F5B00A75B9A /* Debug */, + 13B07F951A680F5B00A75B9A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2D02E4BA1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "GunDemo-tvOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2D02E4971E0B4A5E006451C7 /* Debug */, + 2D02E4981E0B4A5E006451C7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2D02E4BB1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "GunDemo-tvOSTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2D02E4991E0B4A5E006451C7 /* Debug */, + 2D02E49A1E0B4A5E006451C7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "GunDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83CBBA201A601CBA00E9B192 /* Debug */, + 83CBBA211A601CBA00E9B192 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; +} diff --git a/examples/react-native/ios/GunDemo/AppDelegate.h b/examples/react-native/ios/GunDemo/AppDelegate.h new file mode 100644 index 00000000..d4f2580b --- /dev/null +++ b/examples/react-native/ios/GunDemo/AppDelegate.h @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import + +@interface AppDelegate : UIResponder + +@property (nonatomic, strong) UIWindow *window; + +@end diff --git a/examples/react-native/ios/GunDemo/AppDelegate.m b/examples/react-native/ios/GunDemo/AppDelegate.m new file mode 100644 index 00000000..df255a6d --- /dev/null +++ b/examples/react-native/ios/GunDemo/AppDelegate.m @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "AppDelegate.h" + +#import +#import + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + NSURL *jsCodeLocation; + + jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; + + RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation + moduleName:@"GunDemo" + initialProperties:nil + launchOptions:launchOptions]; + rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; + + self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; + UIViewController *rootViewController = [UIViewController new]; + rootViewController.view = rootView; + self.window.rootViewController = rootViewController; + [self.window makeKeyAndVisible]; + return YES; +} + +@end diff --git a/examples/react-native/ios/GunDemo/Base.lproj/LaunchScreen.xib b/examples/react-native/ios/GunDemo/Base.lproj/LaunchScreen.xib new file mode 100644 index 00000000..3c66dc85 --- /dev/null +++ b/examples/react-native/ios/GunDemo/Base.lproj/LaunchScreen.xib @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/react-native/ios/GunDemo/Images.xcassets/AppIcon.appiconset/Contents.json b/examples/react-native/ios/GunDemo/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..118c98f7 --- /dev/null +++ b/examples/react-native/ios/GunDemo/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,38 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/react-native/ios/GunDemo/Images.xcassets/Contents.json b/examples/react-native/ios/GunDemo/Images.xcassets/Contents.json new file mode 100644 index 00000000..2d92bd53 --- /dev/null +++ b/examples/react-native/ios/GunDemo/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/examples/react-native/ios/GunDemo/Info.plist b/examples/react-native/ios/GunDemo/Info.plist new file mode 100644 index 00000000..7fd5d8ea --- /dev/null +++ b/examples/react-native/ios/GunDemo/Info.plist @@ -0,0 +1,60 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + GunDemo + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + NSLocationWhenInUseUsageDescription + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + NSLocationWhenInUseUsageDescription + + NSAppTransportSecurity + + + NSAllowsArbitraryLoads + + NSExceptionDomains + + localhost + + NSExceptionAllowsInsecureHTTPLoads + + + + + + diff --git a/examples/react-native/ios/GunDemo/main.m b/examples/react-native/ios/GunDemo/main.m new file mode 100644 index 00000000..c73e0062 --- /dev/null +++ b/examples/react-native/ios/GunDemo/main.m @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import + +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/examples/react-native/ios/GunDemoTests/GunDemoTests.m b/examples/react-native/ios/GunDemoTests/GunDemoTests.m new file mode 100644 index 00000000..ee464a0f --- /dev/null +++ b/examples/react-native/ios/GunDemoTests/GunDemoTests.m @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import +#import + +#import +#import + +#define TIMEOUT_SECONDS 600 +#define TEXT_TO_LOOK_FOR @"Welcome to React Native!" + +@interface GunDemoTests : XCTestCase + +@end + +@implementation GunDemoTests + +- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test +{ + if (test(view)) { + return YES; + } + for (UIView *subview in [view subviews]) { + if ([self findSubviewInView:subview matching:test]) { + return YES; + } + } + return NO; +} + +- (void)testRendersWelcomeScreen +{ + UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; + NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; + BOOL foundElement = NO; + + __block NSString *redboxError = nil; + RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { + if (level >= RCTLogLevelError) { + redboxError = message; + } + }); + + while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { + [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + + foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { + if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { + return YES; + } + return NO; + }]; + } + + RCTSetLogFunction(RCTDefaultLogFunction); + + XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); + XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); +} + + +@end diff --git a/examples/react-native/ios/GunDemoTests/Info.plist b/examples/react-native/ios/GunDemoTests/Info.plist new file mode 100644 index 00000000..ba72822e --- /dev/null +++ b/examples/react-native/ios/GunDemoTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/examples/react-native/package.json b/examples/react-native/package.json new file mode 100644 index 00000000..0d42b8ba --- /dev/null +++ b/examples/react-native/package.json @@ -0,0 +1,30 @@ +{ + "name": "GunDemo", + "version": "0.0.1", + "private": true, + "scripts": { + "start": "node node_modules/react-native/local-cli/cli.js start", + "test": "jest" + }, + "dependencies": { + "buffer": "^5.2.1", + "encode-utf8": "^1.0.2", + "fast-base64-encode": "^1.0.0", + "gun": "^0.9.99999", + "lodash": "^4.17.11", + "react": "16.6.1", + "react-native": "0.57.5", + "react-native-webview-bridge": "^0.40.1", + "serialize-error": "^3.0.0", + "text-encoding": "^0.7.0" + }, + "devDependencies": { + "babel-jest": "23.6.0", + "jest": "23.6.0", + "metro-react-native-babel-preset": "0.49.2", + "react-test-renderer": "16.6.1" + }, + "jest": { + "preset": "react-native" + } +} diff --git a/examples/react-native/shim.js b/examples/react-native/shim.js new file mode 100644 index 00000000..0e3e7e26 --- /dev/null +++ b/examples/react-native/shim.js @@ -0,0 +1,20 @@ +if (typeof __dirname === 'undefined') global.__dirname = '/' +if (typeof __filename === 'undefined') global.__filename = '' + +if (typeof Buffer === 'undefined') global.Buffer = require('buffer').Buffer + +const isDev = typeof __DEV__ === 'boolean' && __DEV__ +process.env['NODE_ENV'] = isDev ? 'development' : 'production' +if (typeof localStorage !== 'undefined') { + localStorage.debug = isDev ? '*' : '' +} + +global.location = { + protocol: 'file:', + host: '', +}; + +const { TextEncoder, TextDecoder } = require('text-encoding'); + +global.TextDecoder = TextDecoder; +global.TextEncoder = TextEncoder; \ No newline at end of file diff --git a/examples/react-native/src/App/Demo.js b/examples/react-native/src/App/Demo.js new file mode 100644 index 00000000..d86f1174 --- /dev/null +++ b/examples/react-native/src/App/Demo.js @@ -0,0 +1,152 @@ +import * as React from 'react'; +import {View, StyleSheet, TextInput, Text, TouchableOpacity, AsyncStorage} from 'react-native'; + +import Gun from 'gun/gun'; +import 'gun/lib/open'; +import '../extensions/sea'; + +import adapter from '../extensions/asyncStorageAdapter'; + +Gun.on('create', function(db) { + this.to.next(db); + const pluginInterop = function(middleware) { + return function(request) { + this.to.next(request); + return middleware(request, db); + }; + } + + // Register the adapter + db.on('get', pluginInterop(adapter.read)); + db.on('put', pluginInterop(adapter.write)); +}); + +export class Demo extends React.Component { + constructor() { + super(); + + this.gun = new Gun(); + this.user = this.gun.user(); + + window.gun = this.gun; + window.user = this.user; + + this.state = { + authenticated: false, + list: [], + listText: '', + username: '', + password: '', + } + } + + hookUserList = () => { + this.user.get('list').open((list) => { + const userList = Object.keys(list).reduce((newList, key) => { + if (!!Object.keys(list[key]).length) { + return [...newList, {text: list[key].text, key}]; + }; + }, []); + this.setState({ + list: userList || [], + }); + }); + } + + addToList = () => { + this.user.get('list').set({text: this.state.listText}); + } + + doSignin = () => { + this.user.auth(this.state.username, this.state.password, (d) => { + if (d.err) { + console.log('err', d.err); + return; + } + + this.setState({authenticated: true}); + this.hookUserList(); + }); + } + + doSignup = () => { + this.user.create(this.state.username, this.state.password, () => { + this.doSignin(); + }); + } + + loginScreen = () => { + return ( + + this.setState({username})} value={this.state.username} style={styles.input} /> + this.setState({password})} value={this.state.password} style={styles.input} /> + + Sign in + + + Sign up + + + ) + } + + userListScreen = () => { + return ( + + { + !!this.state.list.length && this.state.list.map((item) => * {item.text}) + } + + this.setState({listText})} value={this.state.listText} style={styles.input} /> + + Add to list + + + ) + } + + render() { + return ( + + { + this.state.authenticated ? + this.userListScreen() : this.loginScreen() + } + + ) + } +} + +const styles = StyleSheet.create({ + container: { + justifyContent: 'center', + alignItems: 'center', + width: '100%', + height: '100%', + }, + sub: { + height: '50%', + width: '60%', + justifyContent: 'space-between', + padding: 4, + }, + button: { + backgroundColor: 'rgba(0, 0, 0, 0.3)', + borderWidth: 1, + borderRadius: 5, + alignItems: 'center', + justifyContent: 'center', + width: '100%', + height: 50, + }, + input: { + borderColor: 'black', + borderWidth: 1, + borderRadius: 5, + width: '100%', + height: 50, + alignItems: 'center', + justifyContent: 'center', + paddingHorizontal: '32%', + }, +}); diff --git a/examples/react-native/src/App/PolyFillCrypto.js b/examples/react-native/src/App/PolyFillCrypto.js new file mode 100644 index 00000000..4ac624c7 --- /dev/null +++ b/examples/react-native/src/App/PolyFillCrypto.js @@ -0,0 +1,92 @@ +import * as React from 'react'; +import { Platform, StyleSheet, View } from 'react-native'; + +import WebViewBridge from 'react-native-webview-bridge'; + +import { MainWorker, webViewWorkerString } from '../webview-crypto'; + +import encodeUtf8 from 'encode-utf8'; +import encodeBase64 from 'fast-base64-encode'; + +const base64EncodeString = (input) => { + return encodeBase64(new Uint8Array(encodeUtf8(input))); +}; + +const internalLibIOS = ` +${webViewWorkerString} +(function () { + var wvw = new WebViewWorker(WebViewBridge.send.bind(WebViewBridge)); + WebViewBridge.onMessage = wvw.onMainMessage.bind(wvw); +}()); +`; + +const intermediateLib = ` +${webViewWorkerString} +(function () { + var wvw = new WebViewWorker(WebViewBridge.send.bind(WebViewBridge)); + WebViewBridge.onMessage = wvw.onMainMessage.bind(wvw); +}()); +`; + +const internalLibAndroid = `eval(window.atob('${base64EncodeString(intermediateLib)}'))`; + +export default class PolyfillCrypto extends React.Component { + shouldComponentUpdate() { + return false; + } + + render() { + let worker; + const uri = 'file:///android_asset/html/blank.html'; + return ( + + { + if (c && !worker) { + worker = new MainWorker(c.sendToBridge, this.props.debug); + + if (window.crypto) { + // we are in chrome debugger + // this means overridng the crypto object itself won't + // work, so we have to override all of it's methods + for (const name in worker.crypto.subtle) { + window.crypto.subtle[name] = worker.crypto.subtle[name]; + } + window.crypto.fake = true; + } else { + window.crypto = worker.crypto; + } + window.crypto.loaded = true; + console.log('*** poly injected', window.crypto); + } + }} + onBridgeMessage={ + // can't refer to this.state.onBridgeMessage directly + // because it is not defined when this component is first + // started, only set in `ref` + (message) => { + worker.onWebViewMessage(message); + } + } + injectedJavaScript={ + Platform.OS === 'android' ? internalLibAndroid : internalLibIOS + } + onError={(error) => { + console.warn('Error creating webview: ', error); + }} + javaScriptEnabled={true} + source={{ + uri: Platform.OS === 'android' ? uri : 'about:blank', + }} + /> + + ); + } +} + +const styles = StyleSheet.create({ + hidden: { + height: 0, + opacity: 0, + }, +}); diff --git a/examples/react-native/src/App/app.js b/examples/react-native/src/App/app.js new file mode 100644 index 00000000..440e9ba1 --- /dev/null +++ b/examples/react-native/src/App/app.js @@ -0,0 +1,26 @@ +/** + * Sample React Native App + * https://github.com/facebook/react-native + * + * @format + * @flow + */ + +import React, {Component} from 'react'; +import {View} from 'react-native'; + +import {Demo} from './Demo'; + +import PolyFillCrypto from './PolyFillCrypto'; + + +export class App extends Component { + render() { + return ( + + + + + ); + } +} diff --git a/examples/react-native/src/App/index.js b/examples/react-native/src/App/index.js new file mode 100644 index 00000000..5cb8af0c --- /dev/null +++ b/examples/react-native/src/App/index.js @@ -0,0 +1 @@ +export {App as default} from './app'; diff --git a/examples/react-native/src/extensions/asyncStorageAdapter.js b/examples/react-native/src/extensions/asyncStorageAdapter.js new file mode 100644 index 00000000..8c9740e3 --- /dev/null +++ b/examples/react-native/src/extensions/asyncStorageAdapter.js @@ -0,0 +1,75 @@ +import * as Gun from 'gun'; +import { AsyncStorage } from 'react-native'; + +const readNode = (key, cb) => { + AsyncStorage.getItem(key || '', cb); +}; + +const read = (request, db) => { + const { get } = request; + + const dedupid = request['#']; + const key = get['#']; + const field = get['.']; + + const done = (err, data) => { + if (!data && !err) { + db.on('in', { + '@': dedupid, + put: null, + err: null, + }); + } else { + db.on('in', { + '@': dedupid, + put: Gun.graph.node(data), + err, + }); + } + }; + + const acknowledgeRet = (err, result) => { + if (err) { + done(err); + } else if (result === null) { + // Nothing found + done(null); + } else { + const temp = JSON.parse(result); + if (field) { + done(null, temp[field] || null); + } else { + done(null, temp); + } + } + }; + + readNode(key || '', acknowledgeRet); +}; + +const write = (request, db) => { + const { put: graph } = request; + const keys = Object.keys(graph); + const dedupid = graph['#']; + + const instructions = keys.map((key) => { + return [key, JSON.stringify(graph[key] || {})]; + }); + + AsyncStorage.multiMerge(instructions, (err) => { + db.on('in', { + '#': dedupid, + ok: !err || err.length === 0, + err, + }); + }); +}; + +// This returns a promise, it can be awaited! +const reset = () => AsyncStorage.clear(); + +export default { + read, + write, + reset, +}; diff --git a/examples/react-native/src/extensions/sea.js b/examples/react-native/src/extensions/sea.js new file mode 100644 index 00000000..b99397d9 --- /dev/null +++ b/examples/react-native/src/extensions/sea.js @@ -0,0 +1,1215 @@ +;(function(){ + + /* UNBUILD */ + var root; + if(typeof window !== "undefined"){ root = window } + if(typeof global !== "undefined"){ root = global } + root = root || {}; + var console = root.console || {log: function(){}}; + function USE(arg, req){ + return req? () => {} : arg.slice? USE[R(arg)] : function(mod, path){ + arg(mod = {exports: {}}); + USE[R(path)] = mod.exports; + } + function R(p){ + return p.split('/').slice(-1).toString().replace('.js',''); + } + } + if(typeof module !== "undefined"){ var common = module } + /* UNBUILD */ + + ;USE(function(module){ + // Security, Encryption, and Authorization: SEA.js + // MANDATORY READING: https://gun.eco/explainers/data/security.html + // IT IS IMPLEMENTED IN A POLYFILL/SHIM APPROACH. + // THIS IS AN EARLY ALPHA! + + if(typeof window !== "undefined"){ module.window = window } + + var tmp = module.window || module; + var SEA = tmp.SEA || {}; + + if(SEA.window = module.window){ SEA.window.SEA = SEA } + + try{ if(typeof common !== "undefined"){ common.exports = SEA } }catch(e){} + module.exports = SEA; + })(USE, './root'); + + ;USE(function(module){ + var SEA = USE('./root'); + if(SEA.window){ + if(location.protocol.indexOf('s') < 0 + && location.host.indexOf('localhost') < 0 + && location.protocol.indexOf('file:') < 0){ + location.protocol = 'https:'; // WebCrypto does NOT work without HTTPS! + } + } + })(USE, './https'); + + ;USE(function(module){ + // This is Array extended to have .toString(['utf8'|'hex'|'base64']) + function SeaArray() {} + Object.assign(SeaArray, { from: Array.from }) + SeaArray.prototype = Object.create(Array.prototype) + SeaArray.prototype.toString = function(enc, start, end) { enc = enc || 'utf8'; start = start || 0; + const length = this.length + if (enc === 'hex') { + const buf = new Uint8Array(this) + return [ ...Array(((end && (end + 1)) || length) - start).keys()] + .map((i) => buf[ i + start ].toString(16).padStart(2, '0')).join('') + } + if (enc === 'utf8') { + return Array.from( + { length: (end || length) - start }, + (_, i) => String.fromCharCode(this[ i + start]) + ).join('') + } + if (enc === 'base64') { + return btoa(this) + } + } + module.exports = SeaArray; + })(USE, './array'); + + ;USE(function(module){ + // This is Buffer implementation used in SEA. Functionality is mostly + // compatible with NodeJS 'safe-buffer' and is used for encoding conversions + // between binary and 'hex' | 'utf8' | 'base64' + // See documentation and validation for safe implementation in: + // https://github.com/feross/safe-buffer#update + var SeaArray = USE('./array'); + function SafeBuffer(...props) { + console.warn('new SafeBuffer() is depreciated, please use SafeBuffer.from()') + return SafeBuffer.from(...props) + } + SafeBuffer.prototype = Object.create(Array.prototype) + Object.assign(SafeBuffer, { + // (data, enc) where typeof data === 'string' then enc === 'utf8'|'hex'|'base64' + from() { + if (!Object.keys(arguments).length) { + throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.') + } + const input = arguments[0] + let buf + if (typeof input === 'string') { + const enc = arguments[1] || 'utf8' + if (enc === 'hex') { + const bytes = input.match(/([\da-fA-F]{2})/g) + .map((byte) => parseInt(byte, 16)) + if (!bytes || !bytes.length) { + throw new TypeError('Invalid first argument for type \'hex\'.') + } + buf = SeaArray.from(bytes) + } else if (enc === 'utf8') { + const length = input.length + const words = new Uint16Array(length) + Array.from({ length: length }, (_, i) => words[i] = input.charCodeAt(i)) + buf = SeaArray.from(words) + } else if (enc === 'base64') { + const dec = atob(input) + const length = dec.length + const bytes = new Uint8Array(length) + Array.from({ length: length }, (_, i) => bytes[i] = dec.charCodeAt(i)) + buf = SeaArray.from(bytes) + } else if (enc === 'binary') { + buf = SeaArray.from(input) + } else { + console.info('SafeBuffer.from unknown encoding: '+enc) + } + return buf + } + const byteLength = input.byteLength // what is going on here? FOR MARTTI + const length = input.byteLength ? input.byteLength : input.length + if (length) { + let buf + if (input instanceof ArrayBuffer) { + buf = new Uint8Array(input) + } + return SeaArray.from(buf || input) + } + }, + // This is 'safe-buffer.alloc' sans encoding support + alloc(length, fill = 0 /*, enc*/ ) { + return SeaArray.from(new Uint8Array(Array.from({ length: length }, () => fill))) + }, + // This is normal UNSAFE 'buffer.alloc' or 'new Buffer(length)' - don't use! + allocUnsafe(length) { + return SeaArray.from(new Uint8Array(Array.from({ length : length }))) + }, + // This puts together array of array like members + concat(arr) { // octet array + if (!Array.isArray(arr)) { + throw new TypeError('First argument must be Array containing ArrayBuffer or Uint8Array instances.') + } + return SeaArray.from(arr.reduce((ret, item) => ret.concat(Array.from(item)), [])) + } + }) + SafeBuffer.prototype.from = SafeBuffer.from + SafeBuffer.prototype.toString = SeaArray.prototype.toString + + module.exports = SafeBuffer; + })(USE, './buffer'); + + ;USE(function(module){ + const SEA = USE('./root') + const Buffer = USE('./buffer') + const api = {Buffer: Buffer} + var o = {}; + + const interval = setInterval(() => { + if (window.crypto.loaded) { + api.crypto = window.crypto; + api.subtle = api.crypto.subtle; + api.TextEncoder = window.TextEncoder; + api.TextDecoder = window.TextDecoder; + api.random = (len) => Buffer.from(api.crypto.getRandomValues(new Uint8Array(Buffer.alloc(len)))) + console.log('*** gun sea crypto set', api); + clearInterval(interval); + } + }, 500); + + module.exports = api + })(USE, './shim'); + + ;USE(function(module){ + const SEA = USE('./root'); + const Buffer = USE('./buffer') + const settings = {} + // Encryption parameters + const pbkdf2 = { hash: 'SHA-256', iter: 100000, ks: 64 } + + const ecdsaSignProps = { name: 'ECDSA', hash: { name: 'SHA-256' } } + const ecdsaKeyProps = { name: 'ECDSA', namedCurve: 'P-256' } + const ecdhKeyProps = { name: 'ECDH', namedCurve: 'P-256' } + + const _initial_authsettings = { + validity: 12 * 60 * 60, // internally in seconds : 12 hours + hook: (props) => props // { iat, exp, alias, remember } + // or return new Promise((resolve, reject) => resolve(props) + } + // These are used to persist user's authentication "session" + const authsettings = Object.assign({}, _initial_authsettings) + // This creates Web Cryptography API compliant JWK for sign/verify purposes + const keysToEcdsaJwk = (pub, d) => { // d === priv + //const [ x, y ] = Buffer.from(pub, 'base64').toString('utf8').split(':') // old + const [ x, y ] = pub.split('.') // new + var jwk = { kty: "EC", crv: "P-256", x: x, y: y, ext: true } + jwk.key_ops = d ? ['sign'] : ['verify']; + if(d){ jwk.d = d } + return jwk; + } + + Object.assign(settings, { + pbkdf2: pbkdf2, + ecdsa: { + pair: ecdsaKeyProps, + sign: ecdsaSignProps + }, + ecdh: ecdhKeyProps, + jwk: keysToEcdsaJwk, + recall: authsettings + }) + SEA.opt = settings; + module.exports = settings + })(USE, './settings'); + + ;USE(function(module){ + module.exports = (props) => { + try { + if(props.slice && 'SEA{' === props.slice(0,4)){ + props = props.slice(3); + } + return props.slice ? JSON.parse(props) : props + } catch (e) {} //eslint-disable-line no-empty + return props + } + })(USE, './parse'); + + ;USE(function(module){ + 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 shim.subtle.digest({name: pbkdf2.hash}, new shim.TextEncoder().encode(m)) + return Buffer.from(hash) + } + module.exports = sha256hash + })(USE, './sha256'); + + ;USE(function(module){ + // This internal func returns SHA-1 hashed data for KeyID generation + const __shim = USE('./shim') + const subtle = __shim.subtle + const ossl = __shim.ossl ? __shim.ossl : subtle + const sha1hash = (b) => ossl.digest({name: 'SHA-1'}, new ArrayBuffer(b)) + module.exports = sha1hash + })(USE, './sha1'); + + ;USE(function(module){ + var SEA = USE('./root'); + var shim = USE('./shim'); + var S = USE('./settings'); + var sha = USE('./sha256'); + var u; + + SEA.work = SEA.work || (async (data, pair, cb, opt) => { try { // used to be named `proof` + var salt = (pair||{}).epub || pair; // epub not recommended, salt should be random! + var opt = opt || {}; + if(salt instanceof Function){ + cb = salt; + salt = u; + } + salt = salt || shim.random(9); + if('SHA-256' === opt.name){ + var rsha = shim.Buffer.from(await sha(data), 'binary').toString('utf8') + if(cb){ try{ cb(rsha) }catch(e){console.log(e)} } + return rsha; + } + const key = await (shim.ossl || shim.subtle).importKey( + 'raw', new shim.TextEncoder().encode(data), { name: opt.name || 'PBKDF2' }, false, ['deriveBits'] + ) + const result = await (shim.ossl || shim.subtle).deriveBits({ + name: opt.name || 'PBKDF2', + iterations: opt.iterations || S.pbkdf2.iter, + salt: new shim.TextEncoder().encode(opt.salt || salt), + hash: opt.hash || S.pbkdf2.hash, + }, key, opt.length || (S.pbkdf2.ks * 8)) + data = shim.random(data.length) // Erase data in case of passphrase + const r = shim.Buffer.from(result, 'binary').toString('utf8') + if(cb){ try{ cb(r) }catch(e){console.log(e)} } + return r; + } catch(e) { + SEA.err = e; + if(cb){ cb() } + return; + }}); + + module.exports = SEA.work; + })(USE, './work'); + + ;USE(function(module){ + var SEA = USE('./root'); + var shim = USE('./shim'); + 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, opt) => { try { + + const ecdhSubtle = shim.ossl || shim.subtle + // First: ECDSA keys for signing/verifying... + var sa = await shim.subtle.generateKey(S.ecdsa.pair, true, [ 'sign', 'verify' ]) + .then(async (keys) => { + // privateKey scope doesn't leak out from here! + //const { d: priv } = await shim.subtle.exportKey('jwk', keys.privateKey) + const key = {}; + key.priv = (await shim.subtle.exportKey('jwk', keys.privateKey)).d; + const pub = await shim.subtle.exportKey('jwk', keys.publicKey) + //const pub = Buff.from([ x, y ].join(':')).toString('base64') // old + key.pub = pub.x+'.'+pub.y // new + // x and y are already base64 + // pub is UTF8 but filename/URL safe (https://www.ietf.org/rfc/rfc3986.txt) + // but split on a non-base64 letter. + return key; + }) + + // To include PGPv4 kind of keyId: + // const pubId = await SEA.keyid(keys.pub) + // Next: ECDH keys for encryption/decryption... + + try{ + var dh = await ecdhSubtle.generateKey(S.ecdh, true, ['deriveKey']) + .then(async (keys) => { + // privateKey scope doesn't leak out from here! + const key = {}; + key.epriv = (await ecdhSubtle.exportKey('jwk', keys.privateKey)).d; + const pub = await ecdhSubtle.exportKey('jwk', keys.publicKey) + //const epub = Buff.from([ ex, ey ].join(':')).toString('base64') // old + key.epub = pub.x+'.'+pub.y // new + // ex and ey are already base64 + // epub is UTF8 but filename/URL safe (https://www.ietf.org/rfc/rfc3986.txt) + // but split on a non-base64 letter. + return key; + }) + }catch(e){ + if(SEA.window){ throw e } + if(e == 'Error: ECDH is not a supported algorithm'){ console.log('Ignoring ECDH...') } + else { throw e } + } dh = dh || {}; + + 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) { + console.log(e); + SEA.err = e; + if(cb){ cb() } + return; + }}); + + module.exports = SEA.pair; + })(USE, './pair'); + + ;USE(function(module){ + var SEA = USE('./root'); + var shim = USE('./shim'); + var S = USE('./settings'); + var sha256hash = USE('./sha256'); + + SEA.sign = SEA.sign || (async (data, pair, cb, opt) => { try { + if(data && data.slice + && 'SEA{' === data.slice(0,4) + && '"m":' === data.slice(4,8)){ + // TODO: This would prevent pair2 signing pair1's signature. + // So we may want to change this in the future. + // but for now, we want to prevent duplicate double signature. + if(cb){ try{ cb(data) }catch(e){console.log(e)} } + return data; + } + opt = opt || {}; + if(!(pair||opt).priv){ + pair = await SEA.I(null, {what: data, how: 'sign', why: opt.why}); + } + const pub = pair.pub + const priv = pair.priv + const jwk = S.jwk(pub, priv) + const msg = JSON.stringify(data) + const hash = await sha256hash(msg) + const sig = await (shim.ossl || shim.subtle).importKey('jwk', jwk, S.ecdsa.pair, false, ['sign']) + .then((key) => (shim.ossl || shim.subtle).sign(S.ecdsa.sign, key, new Uint8Array(hash))) // privateKey scope doesn't leak out from here! + const r = 'SEA'+JSON.stringify({m: msg, s: shim.Buffer.from(sig, 'binary').toString('utf8')}); + + if(cb){ try{ cb(r) }catch(e){console.log(e)} } + return r; + } catch(e) { + console.log(e); + SEA.err = e; + if(cb){ cb() } + return; + }}); + + module.exports = SEA.sign; + })(USE, './sign'); + + ;USE(function(module){ + var SEA = USE('./root'); + var shim = USE('./shim'); + var S = USE('./settings'); + var sha256hash = USE('./sha256'); + var parse = USE('./parse'); + var u; + + SEA.verify = SEA.verify || (async (data, pair, cb, opt) => { try { + const json = parse(data) + if(false === pair){ // don't verify! + const raw = (json !== data)? + (json.s && json.m)? parse(json.m) : data + : json; + if(cb){ try{ cb(raw) }catch(e){console.log(e)} } + return raw; + } + opt = opt || {}; + // SEA.I // verify is free! Requires no user permission. + if(json === data){ throw "No signature on data." } + const pub = pair.pub || pair + const jwk = S.jwk(pub) + const key = await (shim.ossl || shim.subtle).importKey('jwk', jwk, S.ecdsa.pair, false, ['verify']) + const hash = await sha256hash(json.m) + const sig = new Uint8Array(shim.Buffer.from(json.s, 'utf8')) + const check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash)) + if(!check){ throw "Signature did not match." } + const r = check? parse(json.m) : u; + + if(cb){ try{ cb(r) }catch(e){console.log(e)} } + return r; + } catch(e) { + console.log(e); // mismatched owner FOR MARTTI + SEA.err = e; + if(cb){ cb() } + return; + }}); + + module.exports = SEA.verify; + })(USE, './verify'); + + ;USE(function(module){ + var shim = USE('./shim'); + var sha256hash = USE('./sha256'); + + const importGen = async (key, salt, opt) => { + //const combo = shim.Buffer.concat([shim.Buffer.from(key, 'utf8'), salt || shim.random(8)]).toString('utf8') // old + var opt = opt || {}; + const combo = key + (salt || shim.random(8)).toString('utf8'); // new + const hash = shim.Buffer.from(await sha256hash(combo), 'binary') + return await shim.subtle.importKey('raw', new Uint8Array(hash), opt.name || 'AES-GCM', false, ['encrypt', 'decrypt']) + } + module.exports = importGen; + })(USE, './aeskey'); + + ;USE(function(module){ + var SEA = USE('./root'); + var shim = USE('./shim'); + var S = USE('./settings'); + var aeskey = USE('./aeskey'); + + SEA.encrypt = SEA.encrypt || (async (data, pair, cb, opt) => { try { + 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) + .then((aes) => (/*shim.ossl ||*/ shim.subtle).encrypt({ // Keeping the AES key scope as private as possible... + name: opt.name || 'AES-GCM', iv: new Uint8Array(rand.iv) + }, aes, new shim.TextEncoder().encode(msg))) + const r = 'SEA'+JSON.stringify({ + ct: shim.Buffer.from(ct, 'binary').toString('utf8'), + iv: rand.iv.toString('utf8'), + s: rand.s.toString('utf8') + }); + + if(cb){ try{ cb(r) }catch(e){console.log(e)} } + return r; + } catch(e) { + SEA.err = e; + if(cb){ cb() } + return; + }}); + + module.exports = SEA.encrypt; + })(USE, './encrypt'); + + ;USE(function(module){ + var SEA = USE('./root'); + var shim = USE('./shim'); + var S = USE('./settings'); + var aeskey = USE('./aeskey'); + var parse = USE('./parse'); + + SEA.decrypt = SEA.decrypt || (async (data, pair, cb, opt) => { try { + 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... + name: opt.name || 'AES-GCM', iv: new Uint8Array(shim.Buffer.from(json.iv, 'utf8')) + }, aes, new Uint8Array(shim.Buffer.from(json.ct, 'utf8')))) + const r = parse(new shim.TextDecoder('utf8').decode(ct)) + if(cb){ try{ cb(r) }catch(e){console.log(e)} } + return r; + } catch(e) { + SEA.err = e; + if(cb){ cb() } + return; + }}); + + module.exports = SEA.decrypt; + })(USE, './decrypt'); + + ;USE(function(module){ + 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, 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 + const ecdhSubtle = shim.ossl || shim.subtle + const pubKeyData = keysToEcdhJwk(pub) + const props = Object.assign( + S.ecdh, + { public: await ecdhSubtle.importKey(...pubKeyData, true, []) } + ) + const privKeyData = keysToEcdhJwk(epub, epriv) + const derived = await ecdhSubtle.importKey(...privKeyData, false, ['deriveKey']) + .then(async (privKey) => { + // privateKey scope doesn't leak out from here! + const derivedKey = await ecdhSubtle.deriveKey(props, privKey, { name: 'AES-GCM', length: 256 }, true, [ 'encrypt', 'decrypt' ]) + return ecdhSubtle.exportKey('jwk', derivedKey).then(({ k }) => k) + }) + const r = derived; + if(cb){ try{ cb(r) }catch(e){console.log(e)} } + return r; + } catch(e) { + SEA.err = e; + if(cb){ cb() } + return; + }}); + + const keysToEcdhJwk = (pub, d) => { // d === priv + //const [ x, y ] = Buffer.from(pub, 'base64').toString('utf8').split(':') // old + const [ x, y ] = pub.split('.') // new + const jwk = d ? { d: d } : {} + return [ // Use with spread returned value... + 'jwk', + Object.assign( + jwk, + { x: x, y: y, kty: 'EC', crv: 'P-256', ext: true } + ), // ??? refactor + S.ecdh + ] + } + + module.exports = SEA.secret; + })(USE, './secret'); + + ;USE(function(module){ + var shim = USE('./shim'); + // Practical examples about usage found from ./test/common.js + 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 || 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 || USE('./buffer'); + + // These SEA functions support now ony Promises or + // async/await (compatible) code, use those like Promises. + // + // Creates a wrapper library around Web Crypto API + // for various AES, ECDSA, PBKDF2 functions we called above. + // Calculate public key KeyID aka PGPv4 (result: 8 bytes as hex string) + SEA.keyid = SEA.keyid || (async (pub) => { + try { + // base64('base64(x):base64(y)') => Buffer(xy) + const pb = Buffer.concat( + pub.replace(/-/g, '+').replace(/_/g, '/').split('.') + .map((t) => Buffer.from(t, 'base64')) + ) + // id is PGPv4 compliant raw key + const id = Buffer.concat([ + Buffer.from([0x99, pb.length / 0x100, pb.length % 0x100]), pb + ]) + const sha1 = await sha1hash(id) + const hash = Buffer.from(sha1, 'binary') + return hash.toString('hex', hash.length - 8) // 16-bit ID as hex + } catch (e) { + console.log(e) + throw e + } + }); + // all done! + // Obviously it is missing MANY necessary features. This is only an alpha release. + // Please experiment with it, audit what I've done so far, and complain about what needs to be added. + // SEA should be a full suite that is easy and seamless to use. + // Again, scroll naer the top, where I provide an EXAMPLE of how to create a user and sign in. + // Once logged in, the rest of the code you just read handled automatically signing/validating data. + // But all other behavior needs to be equally easy, like opinionated ways of + // Adding friends (trusted public keys), sending private messages, etc. + // Cheers! Tell me what you think. + var Gun = (SEA.window||{}).Gun || USE('./gun', 1); + Gun.SEA = SEA; + SEA.GUN = SEA.Gun = Gun; + + module.exports = SEA + })(USE, './sea'); + + ;USE(function(module){ + var Gun = USE('./sea').Gun; + Gun.chain.then = function(cb){ + var gun = this, p = (new Promise(function(res, rej){ + gun.once(res); + })); + return cb? p.then(cb) : p; + } + })(USE, './then'); + + ;USE(function(module){ + var SEA = USE('./sea'); + var Gun = SEA.Gun; + var then = USE('./then'); + + function User(root){ + this._ = {$: this}; + } + User.prototype = (function(){ function F(){}; F.prototype = Gun.chain; return new F() }()) // Object.create polyfill + User.prototype.constructor = User; + + // let's extend the gun chain with a `user` function. + // only one user can be logged in at a time, per gun instance. + Gun.chain.user = function(pub){ + var gun = this, root = gun.back(-1), user; + if(pub){ return root.get('~'+pub) } + if(user = root.back('user')){ return user } + var root = (root._), at = root, uuid = at.opt.uuid || Gun.state.lex; + (at = (user = at.user = gun.chain(new User))._).opt = {}; + at.opt.uuid = function(cb){ + var id = uuid(), pub = root.user; + if(!pub || !(pub = pub.is) || !(pub = pub.pub)){ return id } + id = id + '~' + pub + '.'; + if(cb && cb.call){ cb(null, id) } + return id; + } + return user; + } + Gun.User = User; + module.exports = User; + })(USE, './user'); + + ;USE(function(module){ + // TODO: This needs to be split into all separate functions. + // Not just everything thrown into 'create'. + + var SEA = USE('./sea'); + var User = USE('./user'); + var authsettings = USE('./settings'); + var Gun = SEA.Gun; + + var noop = function(){}; + + // Well first we have to actually create a user. That is what this function does. + 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}); + return gun; + } + cat.ing = true; + opt = opt || {}; + 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); + } + 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.') + } + 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; + } + 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 = function(opt, cb){ + var gun = this, user = (gun.back(-1)._).user; + if(user){ + delete user.is; + delete user._.is; + delete user._.sea; + } + if(SEA.window){ + var sS = {}; try{sS = window.sessionStorage}catch(e){}; + delete sS.alias; + delete sS.tmp; + delete sS.recall; + } + return gun; + } + // If authenticated user wants to delete his/her account, let's support it! + User.prototype.delete = async function(alias, pass, cb){ + var gun = this, root = gun.back(-1), user = gun.back('user'); + try { + 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); + } + return gun; + } + 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; + } + /* + 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) + try { + // All is good. Should we do something more with actual recalled data? + await authRecall(gunRoot) + return gunRoot._.user._ + } catch (e) { + const err = 'No session!' + Gun.log(err) + throw { err } + } + } + User.prototype.trust = async function(user){ + // TODO: BUG!!! SEA `node` read listener needs to be async, which means core needs to be async too. + //gun.get('alice').get('age').trust(bob); + if (Gun.is(user)) { + user.get('pub').get((ctx, ev) => { + console.log(ctx, ev) + }) + } + } + 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.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); + if(!sec){ + sec = SEA.random(16).toString(); + enc = await SEA.encrypt(sec, pair); + user.get('trust').get(pair.pub).get(path).put(enc); + } + var pub = to.get('pub').then(); + var epub = to.get('epub').then(); + pub = await pub; epub = await epub; + var dh = await SEA.secret(epub, pair); + enc = await SEA.encrypt(sec, dh); + user.get('trust').get(pub).get(path).put(enc, cb); + }()); + return gun; + } + 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.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); + if(!sec){ + sec = SEA.random(16).toString(); + enc = await SEA.encrypt(sec, pair); + user.get('trust').get(pair.pub).get(path).put(enc); + } + enc = await SEA.encrypt(data, sec); + gun.put(enc, cb); + }()); + return gun; + } + module.exports = User + })(USE, './create'); + + ;USE(function(module){ + const SEA = USE('./sea') + const Gun = SEA.Gun; + // After we have a GUN extension to make user registration/login easy, we then need to handle everything else. + + // We do this with a GUN adapter, we first listen to when a gun instance is created (and when its options change) + Gun.on('opt', function(at){ + if(!at.sea){ // only add SEA once per instance, on the "at" context. + at.sea = {own: {}}; + at.on('in', security, at); // now listen to all input data, acting as a firewall. + at.on('out', signature, at); // and output listeners, to encrypt outgoing data. + at.on('node', each, at); + } + this.to.next(at); // make sure to call the "next" middleware adapter. + }); + + // Alright, this next adapter gets run at the per node level in the graph database. + // This will let us verify that every property on a node has a value signed by a public key we trust. + // If the signature does not match, the data is just `undefined` so it doesn't get passed on. + // If it does match, then we transform the in-memory "view" of the data into its plain value (without the signature). + // Now NOTE! Some data is "system" data, not user data. Example: List of public keys, aliases, etc. + // This data is self-enforced (the value can only match its ID), but that is handled in the `security` function. + // From the self-enforced data, we can see all the edges in the graph that belong to a public key. + // Example: ~ASDF is the ID of a node with ASDF as its public key, signed alias and salt, and + // its encrypted private key, but it might also have other signed values on it like `profile = ` edge. + // Using that directed edge's ID, we can then track (in memory) which IDs belong to which keys. + // Here is a problem: Multiple public keys can "claim" any node's ID, so this is dangerous! + // This means we should ONLY trust our "friends" (our key ring) public keys, not any ones. + // I have not yet added that to SEA yet in this alpha release. That is coming soon, but beware in the meanwhile! + function each(msg){ // TODO: Warning: Need to switch to `gun.on('node')`! Do not use `Gun.on('node'` in your apps! + // NOTE: THE SECURITY FUNCTION HAS ALREADY VERIFIED THE DATA!!! + // WE DO NOT NEED TO RE-VERIFY AGAIN, JUST TRANSFORM IT TO PLAINTEXT. + var to = this.to, vertex = (msg.$._).put, c = 0, d; + Gun.node.is(msg.put, function(val, key, node){ c++; // for each property on the node + // TODO: consider async/await use here... + SEA.verify(val, false, function(data){ c--; // false just extracts the plain data. + node[key] = val = data; // transform to plain value. + if(d && !c && (c = -1)){ to.next(msg) } + }); + }); + d = true; + if(d && !c){ to.next(msg) } + return; + } + + // signature handles data output, it is a proxy to the security function. + function signature(msg){ + if(msg.user){ + return this.to.next(msg); + } + var ctx = this.as; + msg.user = ctx.user; + security.call(this, msg); + } + + // okay! The security function handles all the heavy lifting. + // It needs to deal read and write of input and output of system data, account/public key data, and regular data. + // This is broken down into some pretty clear edge cases, let's go over them: + function security(msg){ + var at = this.as, sea = at.sea, to = this.to; + if(msg.get){ + // 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(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 + if('~@' === soul.slice(0,2)){ // Allow reading the list of public keys associated with an alias? + return to.next(msg); // yes. + } else { // Allow reading everything? + return to.next(msg); // yes // TODO: No! Make this a callback/event that people can filter on. + } + } + } + if(msg.put){ + // potentially parallel async operations!!! + var check = {}, each = {}, u; + each.node = function(node, soul){ + if(Gun.obj.empty(node, '_')){ return check['node'+soul] = 0 } // ignore empty updates, don't reject them. + Gun.obj.map(node, each.way, {soul: soul, node: node}); + }; + each.way = function(val, key){ + var soul = this.soul, node = this.node, tmp; + if('_' === key){ return } // ignore meta data + if('~@' === soul){ // special case for shared system data, the list of aliases. + each.alias(val, key, node, soul); return; + } + if('~@' === soul.slice(0,2)){ // special case for shared system data, the list of public keys for an alias. + each.pubs(val, key, node, soul); return; + } + if('~' === soul.slice(0,1) && 2 === (tmp = soul.slice(1)).split('.').length){ // special case, account data for a public key. + each.pub(val, key, node, soul, tmp, msg.user); return; + } + each.any(val, key, node, soul, msg.user); return; + return each.end({err: "No other data allowed!"}); + }; + each.alias = function(val, key, node, soul){ // Example: {_:#~@, ~@alice: {#~@alice}} + if(!val){ return each.end({err: "Data must exist!"}) } // data MUST exist + if('~@'+key === Gun.val.link.is(val)){ return check['alias'+key] = 0 } // in fact, it must be EXACTLY equal to itself + each.end({err: "Mismatching alias."}); // if it isn't, reject. + }; + each.pubs = function(val, key, node, soul){ // Example: {_:#~@alice, ~asdf: {#~asdf}} + if(!val){ return each.end({err: "Alias must exist!"}) } // data MUST exist + if(key === Gun.val.link.is(val)){ return check['pubs'+soul+key] = 0 } // and the ID must be EXACTLY equal to its property + each.end({err: "Alias must match!"}); // that way nobody can tamper with the list of public keys. + }; + each.pub = function(val, key, node, soul, pub, user){ // Example: {_:#~asdf, hello:SEA{'world',fdsa}} + if('pub' === key){ + if(val === pub){ return (check['pub'+soul+key] = 0) } // the account MUST match `pub` property that equals the ID of the public key. + return each.end({err: "Account must match!"}); + } + check['user'+soul+key] = 1; + if(user && user.is && pub === user.is.pub){ + //var id = Gun.text.random(3); + 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; + } + node[key] = data; + check['user'+soul+key] = 0; + each.end({ok: 1}); + }); + // TODO: Handle error!!!! + return; + } + SEA.verify(val, pub, function(data){ var rel, tmp; + if(u === data){ // make sure the signature matches the account it claims to be on. + return each.end({err: "Unverified data."}); // reject any updates that are signed with a mismatched account. + } + if((rel = Gun.val.link.is(data)) && pub === relpub(rel)){ + (at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true; + } + check['user'+soul+key] = 0; + each.end({ok: 1}); + }); + }; + function relpub(s){ + if(!s){ return } + s = s.split('~'); + if(!s || !(s = s[1])){ return } + s = s.split('.'); + if(!s || 2 > s.length){ return } + s = s.slice(0,2).join('.'); + return s; + } + each.any = function(val, key, node, soul, user){ var tmp, pub; + if(!user || !user.is){ + if(tmp = relpub(soul)){ + check['any'+soul+key] = 1; + SEA.verify(val, pub = tmp, function(data){ var rel; + if(u === data){ return each.end({err: "Mismatched owner on '" + key + "'."}) } // thanks @rogowski ! + if((rel = Gun.val.link.is(data)) && pub === relpub(rel)){ + (at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true; + } + check['any'+soul+key] = 0; + each.end({ok: 1}); + }); + return; + } + check['any'+soul+key] = 1; + at.on('secure', function(msg){ this.off(); + check['any'+soul+key] = 0; + if(at.opt.secure){ msg = null } + each.end(msg || {err: "Data cannot be modified."}); + }).on.on('secure', msg); + //each.end({err: "Data cannot be modified."}); + return; + } + if(!(tmp = relpub(soul))){ + if(at.opt.secure){ + each.end({err: "Soul is missing public key at '" + key + "'."}); + return; + } + if(val && val.slice && 'SEA{' === (val).slice(0,4)){ + check['any'+soul+key] = 0; + each.end({ok: 1}); + return; + } + //check['any'+soul+key] = 1; + //SEA.sign(val, user, function(data){ + // if(u === data){ return each.end({err: 'Any signature failed.'}) } + // node[key] = data; + check['any'+soul+key] = 0; + each.end({ok: 1}); + //}); + return; + } + 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.is||{}).pub !== p){ return p } + }); + if(other){ + each.any(val, key, node, soul); + return; + }*/ + check['any'+soul+key] = 1; + 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; + each.end({ok: 1}); + }); + } + 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); // 451 mistmached data FOR MARTTI + return; + } + if(!each.end.ed){ return } + if(Gun.obj.map(check, function(no){ + if(no){ return true } + })){ return } + to.next(msg); + }; + Gun.obj.map(msg.put, each.node); + each.end({end: each.end.ed = true}); + return; // need to manually call next after async. + } + 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/examples/react-native/src/webview-crypto/MainWorker.d.ts b/examples/react-native/src/webview-crypto/MainWorker.d.ts new file mode 100644 index 00000000..dff4ae82 --- /dev/null +++ b/examples/react-native/src/webview-crypto/MainWorker.d.ts @@ -0,0 +1,14 @@ +export default class MainWorker { + private sendToWebView; + private debug; + readonly crypto: Crypto; + private readonly subtle; + private static uuid; + private toSend; + private readyToSend; + private messages; + constructor(sendToWebView: (message: string) => void, debug?: boolean); + onWebViewMessage(message: string): void; + private getRandomValues; + private callMethod; +} diff --git a/examples/react-native/src/webview-crypto/MainWorker.js b/examples/react-native/src/webview-crypto/MainWorker.js new file mode 100644 index 00000000..fe13bc54 --- /dev/null +++ b/examples/react-native/src/webview-crypto/MainWorker.js @@ -0,0 +1,170 @@ +import serializeError from 'serialize-error'; +import { parse, stringify } from './serializeBinary'; +const SUBTLE_METHODS = [ + 'encrypt', + 'decrypt', + 'sign', + 'verify', + 'digest', + 'generateKey', + 'deriveKey', + 'deriveBits', + 'importKey', + 'exportKey', + 'wrapKey', + 'unwrapKey', +]; +/* +MainWorker provides a `crypto` attribute that proxies method calls +to the webview. + +It sends strings to the webview in the format: + + { + id: , + method: getRandomValues | subtle., + args: [] + } + +When the webview succeeds in completeing that method, it gets backs: + + { + id: , + value: + } + +And when it fails: + + { + id: , + reason: , + } + +*/ +export default class MainWorker { + // sendToWebView should take a string and send that message to the webview + constructor(sendToWebView, debug = false) { + this.sendToWebView = sendToWebView; + this.debug = debug; + // hold a queue of messages to send, in case someone calls crypto + // before the webview is initialized + this.toSend = []; + this.readyToSend = false; + // Holds the `resolve` and `reject` function for all the promises + // we are working on + this.messages = {}; + } + get crypto() { + const callMethod = this.callMethod; + return { + subtle: this.subtle, + getRandomValues: this.getRandomValues.bind(this), + fake: true, + }; + } + get subtle() { + const s = {}; + for (const m of SUBTLE_METHODS) { + s[m] = (...args) => { + return this.callMethod(`subtle.${m}`, args, true); + }; + } + return s; + } + // http://stackoverflow.com/a/105074/907060 + static uuid() { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return `${s4()}-${s4()}-${s4()}-${s4()}-${s4()}-${s4()}-${s4()}-${s4()}`; + } + onWebViewMessage(message) { + // first message just tells us the webview is ready + if (!this.readyToSend) { + if (this.debug) { + console.log('[webview-crypto] Got first message; ready to send'); + } + this.readyToSend = true; + for (const m of this.toSend) { + this.sendToWebView(m); + } + return; + } + parse(message) + .then(({ id, value, reason }) => { + if (this.debug) { + console.log('[webview-crypto] Received message:', JSON.stringify({ + id, + value, + reason, + })); + } + if (!id) { + console.warn('[webview-crypto] no ID passed back from message:', JSON.stringify(serializeError(reason))); + return; + } + const { resolve, reject } = this.messages[id]; + if (value) { + resolve(value); + } + else { + reject(reason); + } + delete this.messages[id]; + }) + .catch((reason) => { + console.warn('[webview-crypto] error in `parse` of message:', JSON.stringify(message), 'reason:', JSON.stringify(serializeError(reason))); + }); + } + getRandomValues(array) { + const promise = this.callMethod('getRandomValues', [array], false); + // make the _promise not enumerable so it isn't JSON stringified, + // which could lead to an infinite loop with Angular's zone promises + Object.defineProperty(array, '_promise', { + value: promise, + configurable: true, + enumerable: false, + writable: true, + }); + promise.then((updatedArray) => { + array.set(updatedArray); + }); + return array; + } + callMethod(method, args, waitForArrayBufferView) { + const id = MainWorker.uuid(); + // store this promise, so we can resolve it when we get a message + // back from the web view + const promise = new Promise((resolve, reject) => { + this.messages[id] = { resolve, reject }; + }); + const payloadObject = { method, id, args }; + if (this.debug) { + console.log('[webview-crypto] Sending message:', JSON.stringify({ + method, + args, + payloadObject, + })); + } + stringify(payloadObject, waitForArrayBufferView) + .then((message) => { + if (this.readyToSend) { + this.sendToWebView(message); + } + else { + this.toSend.push(message); + } + }) + .catch((reason) => { + this.messages[id].reject({ + message: `exception in stringify-ing message: ${method} ${id}`, + reason, + }); + delete this.messages[id]; + }); + return promise; + } +} +//# sourceMappingURL=MainWorker.js.map \ No newline at end of file diff --git a/examples/react-native/src/webview-crypto/MainWorker.js.map b/examples/react-native/src/webview-crypto/MainWorker.js.map new file mode 100644 index 00000000..0de8d063 --- /dev/null +++ b/examples/react-native/src/webview-crypto/MainWorker.js.map @@ -0,0 +1 @@ +{"version":3,"file":"MainWorker.js","sourceRoot":"","sources":["../src/MainWorker.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAA+B,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAElF,MAAM,cAAc,GAAG;IACtB,SAAS;IACT,SAAS;IACT,MAAM;IACN,QAAQ;IACR,QAAQ;IACR,aAAa;IACb,WAAW;IACX,YAAY;IACZ,WAAW;IACX,WAAW;IACX,SAAS;IACT,WAAW;CACX,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;EA0BE;AACF,MAAM,CAAC,OAAO,OAAO,UAAU;IA2C9B,0EAA0E;IAC1E,YAAoB,aAAwC,EAAU,QAAQ,KAAK;QAA/D,kBAAa,GAAb,aAAa,CAA2B;QAAU,UAAK,GAAL,KAAK,CAAQ;QAfnF,iEAAiE;QACjE,oCAAoC;QAC5B,WAAM,GAAa,EAAE,CAAC;QACtB,gBAAW,GAAG,KAAK,CAAC;QAE5B,iEAAiE;QACjE,oBAAoB;QACZ,aAAQ,GAKZ,EAAE,CAAC;IAG+E,CAAC;IA3CvF,IAAI,MAAM;QACT,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;QACnC,OAAO;YACN,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,eAAe,EAAE,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC;YAChD,IAAI,EAAE,IAAI;SACH,CAAC;IACV,CAAC;IAED,IAAY,MAAM;QACjB,MAAM,CAAC,GAAQ,EAAE,CAAC;QAClB,KAAK,MAAM,CAAC,IAAI,cAAc,EAAE;YAC/B,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAW,EAAE,EAAE;gBACzB,OAAO,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;YACnD,CAAC,CAAC;SACF;QACD,OAAO,CAAiB,CAAC;IAC1B,CAAC;IAED,2CAA2C;IACnC,MAAM,CAAC,IAAI;QAClB,SAAS,EAAE;YACV,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC;iBAC9C,QAAQ,CAAC,EAAE,CAAC;iBACZ,SAAS,CAAC,CAAC,CAAC,CAAC;QAChB,CAAC;QACD,OAAO,GAAG,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;IAC1E,CAAC;IAkBD,gBAAgB,CAAC,OAAe;QAC/B,mDAAmD;QACnD,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;YACtB,IAAI,IAAI,CAAC,KAAK,EAAE;gBACf,OAAO,CAAC,GAAG,CAAC,mDAAmD,CAAC,CAAC;aACjE;YACD,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;YACxB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE;gBAC5B,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;aACtB;YACD,OAAO;SACP;QACD,KAAK,CAAC,OAAO,CAAC;aACZ,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE;YAC/B,IAAI,IAAI,CAAC,KAAK,EAAE;gBACf,OAAO,CAAC,GAAG,CACV,oCAAoC,EACpC,IAAI,CAAC,SAAS,CAAC;oBACd,EAAE;oBACF,KAAK;oBACL,MAAM;iBACN,CAAC,CACF,CAAC;aACF;YACD,IAAI,CAAC,EAAE,EAAE;gBACR,OAAO,CAAC,IAAI,CACX,kDAAkD,EAClD,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CACtC,CAAC;gBACF,OAAO;aACP;YACD,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YAC9C,IAAI,KAAK,EAAE;gBACV,OAAO,CAAC,KAAK,CAAC,CAAC;aACf;iBAAM;gBACN,MAAM,CAAC,MAAM,CAAC,CAAC;aACf;YACD,OAAO,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC1B,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE;YACjB,OAAO,CAAC,IAAI,CACX,+CAA+C,EAC/C,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EACvB,SAAS,EACT,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CACtC,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,eAAe,CAAC,KAAkC;QACzD,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,iBAAiB,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;QAEnE,iEAAiE;QACjE,oEAAoE;QACpE,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE;YACxC,KAAK,EAAE,OAAO;YACd,YAAY,EAAE,IAAI;YAClB,UAAU,EAAE,KAAK;YACjB,QAAQ,EAAE,IAAI;SACd,CAAC,CAAC;QAEH,OAAO,CAAC,IAAI,CAAC,CAAC,YAA6B,EAAE,EAAE;YAC7C,KAAa,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;QACH,OAAO,KAAK,CAAC;IACd,CAAC;IAEO,UAAU,CAAC,MAAc,EAAE,IAAW,EAAE,sBAA+B;QAC9E,MAAM,EAAE,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC;QAC7B,iEAAiE;QACjE,yBAAyB;QACzB,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC/C,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QACzC,CAAC,CAAC,CAAC;QACH,MAAM,aAAa,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;QAC3C,IAAI,IAAI,CAAC,KAAK,EAAE;YACf,OAAO,CAAC,GAAG,CACV,mCAAmC,EACnC,IAAI,CAAC,SAAS,CAAC;gBACd,MAAM;gBACN,IAAI;gBACJ,aAAa;aACb,CAAC,CACF,CAAC;SACF;QACD,SAAS,CAAC,aAAa,EAAE,sBAAsB,CAAC;aAC9C,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;YACjB,IAAI,IAAI,CAAC,WAAW,EAAE;gBACrB,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;aAC5B;iBAAM;gBACN,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;aAC1B;QACF,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE;YACjB,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC;gBACxB,OAAO,EAAE,uCAAuC,MAAM,IAAI,EAAE,EAAE;gBAC9D,MAAM;aACN,CAAC,CAAC;YACH,OAAO,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;QACJ,OAAO,OAAO,CAAC;IAChB,CAAC;CACD"} \ No newline at end of file diff --git a/examples/react-native/src/webview-crypto/WebViewWorker.d.ts b/examples/react-native/src/webview-crypto/WebViewWorker.d.ts new file mode 100644 index 00000000..ed3c250f --- /dev/null +++ b/examples/react-native/src/webview-crypto/WebViewWorker.d.ts @@ -0,0 +1,6 @@ +export declare class WebViewWorker { + private sendToMain; + constructor(sendToMain: (message: string) => void); + onMainMessage(message: string): Promise; + send(data: any): Promise; +} diff --git a/examples/react-native/src/webview-crypto/WebViewWorker.js b/examples/react-native/src/webview-crypto/WebViewWorker.js new file mode 100644 index 00000000..f2c4846c --- /dev/null +++ b/examples/react-native/src/webview-crypto/WebViewWorker.js @@ -0,0 +1,75 @@ +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +import serializeError from 'serialize-error'; +import { subtle } from './compat'; +import { parse, stringify } from './serializeBinary'; +export class WebViewWorker { + constructor(sendToMain) { + this.sendToMain = sendToMain; + sendToMain('We are ready!'); + } + onMainMessage(message) { + return __awaiter(this, void 0, void 0, function* () { + let id; + let method; + let args; + try { + ({ id, method, args } = yield parse(message)); + } + catch (e) { + yield this.send({ + reason: `Couldn't parse data: ${e}`, + }); + return; + } + let value; + try { + if (method === 'getRandomValues') { + value = crypto.getRandomValues(args[0]); + } + else { + const methodName = method.split('.')[1]; + console.log(methodName, args); + value = yield subtle()[methodName].apply(subtle(), args); + // if we import a crypto key, we want to save how we imported it + // so we can send that back and re-create the key later + if (methodName === 'importKey') { + value._import = { + format: args[0], + keyData: args[1], + }; + } + } + } + catch (e) { + yield this.send({ id, reason: serializeError(e) }); + return; + } + yield this.send({ id, value }); + }); + } + send(data) { + return __awaiter(this, void 0, void 0, function* () { + let message; + try { + message = yield stringify(data); + } + catch (e) { + const newData = { + id: data.id, + reason: `stringify error ${e}`, + }; + this.sendToMain(JSON.stringify(newData)); + return; + } + this.sendToMain(message); + }); + } +} +//# sourceMappingURL=WebViewWorker.js.map \ No newline at end of file diff --git a/examples/react-native/src/webview-crypto/WebViewWorker.js.map b/examples/react-native/src/webview-crypto/WebViewWorker.js.map new file mode 100644 index 00000000..192a46db --- /dev/null +++ b/examples/react-native/src/webview-crypto/WebViewWorker.js.map @@ -0,0 +1 @@ +{"version":3,"file":"WebViewWorker.js","sourceRoot":"","sources":["../src/WebViewWorker.ts"],"names":[],"mappings":";;;;;;;;AAAA,OAAO,cAAc,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,OAAO,aAAa;IACzB,YAAoB,UAAqC;QAArC,eAAU,GAAV,UAAU,CAA2B;QACxD,UAAU,CAAC,eAAe,CAAC,CAAC;IAC7B,CAAC;IAEK,aAAa,CAAC,OAAe;;YAClC,IAAI,EAAU,CAAC;YACf,IAAI,MAAc,CAAC;YACnB,IAAI,IAAW,CAAC;YAChB,IAAI;gBACH,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;aAC9C;YAAC,OAAO,CAAC,EAAE;gBACX,MAAM,IAAI,CAAC,IAAI,CAAC;oBACf,MAAM,EAAE,wBAAwB,CAAC,EAAE;iBACnC,CAAC,CAAC;gBACH,OAAO;aACP;YACD,IAAI,KAAK,CAAC;YAEV,IAAI;gBACH,IAAI,MAAM,KAAK,iBAAiB,EAAE;oBACjC,KAAK,GAAG,MAAM,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;iBACxC;qBAAM;oBACN,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;oBACxC,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;oBAC9B,KAAK,GAAG,MAAO,MAAM,EAAU,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,CAAC;oBAElE,gEAAgE;oBAChE,uDAAuD;oBACvD,IAAI,UAAU,KAAK,WAAW,EAAE;wBAC/B,KAAK,CAAC,OAAO,GAAG;4BACf,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;4BACf,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;yBAChB,CAAC;qBACF;iBACD;aACD;YAAC,OAAO,CAAC,EAAE;gBACX,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,EAAG,cAAsB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBAC5D,OAAO;aACP;YACD,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAChC,CAAC;KAAA;IAEK,IAAI,CAAC,IAAS;;YACnB,IAAI,OAAe,CAAC;YACpB,IAAI;gBACH,OAAO,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC;aAChC;YAAC,OAAO,CAAC,EAAE;gBACX,MAAM,OAAO,GAAG;oBACf,EAAE,EAAE,IAAI,CAAC,EAAE;oBACX,MAAM,EAAE,mBAAmB,CAAC,EAAE;iBAC9B,CAAC;gBACF,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;gBACzC,OAAO;aACP;YACD,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAC1B,CAAC;KAAA;CACD"} \ No newline at end of file diff --git a/examples/react-native/src/webview-crypto/asyncSerialize.d.ts b/examples/react-native/src/webview-crypto/asyncSerialize.d.ts new file mode 100644 index 00000000..da6d9c29 --- /dev/null +++ b/examples/react-native/src/webview-crypto/asyncSerialize.d.ts @@ -0,0 +1,8 @@ +export interface ISerializer { + id: string; + isType: (o: any) => boolean; + toObject?: (t: T) => Promise; + fromObject?: (o: S) => Promise; +} +export declare function toObjects(serializers: Array>, o: any): Promise; +export declare function fromObjects(serializers: Array>, o: any): Promise; diff --git a/examples/react-native/src/webview-crypto/asyncSerialize.js b/examples/react-native/src/webview-crypto/asyncSerialize.js new file mode 100644 index 00000000..1ba93371 --- /dev/null +++ b/examples/react-native/src/webview-crypto/asyncSerialize.js @@ -0,0 +1,56 @@ +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +// tslint:disable +import find from 'lodash/find'; +class Serialized { +} +function isSerialized(object) { + return object.hasOwnProperty('__serializer_id'); +} +export function toObjects(serializers, o) { + return __awaiter(this, void 0, void 0, function* () { + if (typeof o !== 'object') { + return o; + } + const serializer = find(serializers, (s) => s.isType(o)); + if (serializer) { + const value = serializer.toObject ? yield serializer.toObject(o) : o; + return { + __serializer_id: serializer.id, + value: yield toObjects(serializers, value), + }; + } + const newO = o instanceof Array ? [] : {}; + for (const atr in o) { + newO[atr] = yield toObjects(serializers, o[atr]); + } + return newO; + }); +} +export function fromObjects(serializers, o) { + return __awaiter(this, void 0, void 0, function* () { + if (typeof o !== 'object') { + return o; + } + if (isSerialized(o)) { + const value = yield fromObjects(serializers, o.value); + const serializer = find(serializers, ['id', o.__serializer_id]) || {}; + if (serializer.fromObject) { + return serializer.fromObject(value); + } + return value; + } + const newO = o instanceof Array ? [] : {}; + for (const atr in o) { + newO[atr] = yield fromObjects(serializers, o[atr]); + } + return newO; + }); +} +//# sourceMappingURL=asyncSerialize.js.map \ No newline at end of file diff --git a/examples/react-native/src/webview-crypto/asyncSerialize.js.map b/examples/react-native/src/webview-crypto/asyncSerialize.js.map new file mode 100644 index 00000000..02ca803d --- /dev/null +++ b/examples/react-native/src/webview-crypto/asyncSerialize.js.map @@ -0,0 +1 @@ +{"version":3,"file":"asyncSerialize.js","sourceRoot":"","sources":["../src/asyncSerialize.ts"],"names":[],"mappings":";;;;;;;;AAAA,iBAAiB;AACjB,OAAO,IAAI,MAAM,aAAa,CAAC;AAS/B,MAAM,UAAU;CAIf;AAED,SAAS,YAAY,CAAC,MAAW;IAChC,OAAO,MAAM,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAgB,SAAS,CAC9B,WAAyC,EACzC,CAAM;;QAEN,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE;YAC1B,OAAO,CAAC,CAAC;SACT;QAED,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9D,IAAI,UAAU,EAAE;YACf,MAAM,KAAK,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACrE,OAAO;gBACN,eAAe,EAAE,UAAU,CAAC,EAAE;gBAC9B,KAAK,EAAE,MAAM,SAAS,CAAC,WAAW,EAAE,KAAK,CAAC;aAC5B,CAAC;SAChB;QAED,MAAM,IAAI,GAAQ,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/C,KAAK,MAAM,GAAG,IAAI,CAAC,EAAE;YACpB,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;SACjD;QACD,OAAO,IAAI,CAAC;IACb,CAAC;CAAA;AAED,MAAM,UAAgB,WAAW,CAChC,WAAyC,EACzC,CAAM;;QAEN,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE;YAC1B,OAAO,CAAC,CAAC;SACT;QAED,IAAI,YAAY,CAAC,CAAC,CAAC,EAAE;YACpB,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;YACtD,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,eAAe,CAAC,CAAC,IAAI,EAAS,CAAC;YAC7E,IAAI,UAAU,CAAC,UAAU,EAAE;gBAC1B,OAAO,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;aACpC;YACD,OAAO,KAAK,CAAC;SACb;QAED,MAAM,IAAI,GAAQ,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/C,KAAK,MAAM,GAAG,IAAI,CAAC,EAAE;YACpB,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,WAAW,CAAC,WAAW,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;SACnD;QACD,OAAO,IAAI,CAAC;IACb,CAAC;CAAA"} \ No newline at end of file diff --git a/examples/react-native/src/webview-crypto/compat.d.ts b/examples/react-native/src/webview-crypto/compat.d.ts new file mode 100644 index 00000000..6d8b364b --- /dev/null +++ b/examples/react-native/src/webview-crypto/compat.d.ts @@ -0,0 +1 @@ +export declare function subtle(): SubtleCrypto; diff --git a/examples/react-native/src/webview-crypto/compat.js b/examples/react-native/src/webview-crypto/compat.js new file mode 100644 index 00000000..4d6a856c --- /dev/null +++ b/examples/react-native/src/webview-crypto/compat.js @@ -0,0 +1,4 @@ +export function subtle() { + return window.crypto.subtle || window.crypto.webkitSubtle; +} +//# sourceMappingURL=compat.js.map \ No newline at end of file diff --git a/examples/react-native/src/webview-crypto/compat.js.map b/examples/react-native/src/webview-crypto/compat.js.map new file mode 100644 index 00000000..ae7444e6 --- /dev/null +++ b/examples/react-native/src/webview-crypto/compat.js.map @@ -0,0 +1 @@ +{"version":3,"file":"compat.js","sourceRoot":"","sources":["../src/compat.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,MAAM;IACrB,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,IAAK,MAAM,CAAC,MAAc,CAAC,YAAY,CAAC;AACpE,CAAC"} \ No newline at end of file diff --git a/examples/react-native/src/webview-crypto/index.d.ts b/examples/react-native/src/webview-crypto/index.d.ts new file mode 100644 index 00000000..e69657f9 --- /dev/null +++ b/examples/react-native/src/webview-crypto/index.d.ts @@ -0,0 +1,3 @@ +import MainWorker from './MainWorker'; +import webViewWorkerString from './webViewWorkerString'; +export { MainWorker, webViewWorkerString }; diff --git a/examples/react-native/src/webview-crypto/index.js b/examples/react-native/src/webview-crypto/index.js new file mode 100644 index 00000000..3de63b0a --- /dev/null +++ b/examples/react-native/src/webview-crypto/index.js @@ -0,0 +1,4 @@ +import MainWorker from './MainWorker'; +import webViewWorkerString from './webViewWorkerString'; +export { MainWorker, webViewWorkerString }; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/examples/react-native/src/webview-crypto/index.js.map b/examples/react-native/src/webview-crypto/index.js.map new file mode 100644 index 00000000..71f89487 --- /dev/null +++ b/examples/react-native/src/webview-crypto/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,cAAc,CAAC;AACtC,OAAO,mBAAmB,MAAM,uBAAuB,CAAC;AAExD,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,CAAC"} \ No newline at end of file diff --git a/examples/react-native/src/webview-crypto/serializeBinary.d.ts b/examples/react-native/src/webview-crypto/serializeBinary.d.ts new file mode 100644 index 00000000..53e09cfc --- /dev/null +++ b/examples/react-native/src/webview-crypto/serializeBinary.d.ts @@ -0,0 +1,5 @@ +export declare function parse(text: string): Promise; +export declare function stringify(value: any, waitForArrayBufferView?: boolean): Promise; +export interface IArrayBufferViewWithPromise extends ArrayBufferView { + _promise?: Promise; +} diff --git a/examples/react-native/src/webview-crypto/serializeBinary.js b/examples/react-native/src/webview-crypto/serializeBinary.js new file mode 100644 index 00000000..2af3a41d --- /dev/null +++ b/examples/react-native/src/webview-crypto/serializeBinary.js @@ -0,0 +1,160 @@ +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +import { fromObjects, toObjects } from './asyncSerialize'; +import { subtle } from './compat'; +export function parse(text) { + return __awaiter(this, void 0, void 0, function* () { + // need decodeURIComponent so binary strings are transfered properly + const deocodedText = unescape(text); + const objects = JSON.parse(deocodedText); + return fromObjects(serializers(true), objects); + }); +} +export function stringify(value, waitForArrayBufferView = true) { + return __awaiter(this, void 0, void 0, function* () { + const serialized = yield toObjects(serializers(waitForArrayBufferView), value); + // need encodeURIComponent so binary strings are transfered properly + const message = JSON.stringify(serialized); + return escape(message); + }); +} +function serializers(waitForArrayBufferView) { + return [ + ArrayBufferSerializer, + ArrayBufferViewSerializer(waitForArrayBufferView), + CryptoKeySerializer, + ]; +} +const ArrayBufferSerializer = { + id: 'ArrayBuffer', + isType: (o) => o instanceof ArrayBuffer, + // from https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String + // modified to use Int8Array so that we can hold odd number of bytes + toObject: (ab) => __awaiter(this, void 0, void 0, function* () { + return String.fromCharCode.apply(null, new Int8Array(ab)); + }), + fromObject: (data) => __awaiter(this, void 0, void 0, function* () { + const buf = new ArrayBuffer(data.length); + const bufView = new Int8Array(buf); + for (let i = 0, strLen = data.length; i < strLen; i++) { + bufView[i] = data.charCodeAt(i); + } + return buf; + }), +}; +function isArrayBufferViewWithPromise(obj) { + return obj.hasOwnProperty('_promise'); +} +// Normally we could just do `abv.constructor.name`, but in +// JavaScriptCore, this wont work for some weird reason. +// list from https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView +function arrayBufferViewName(abv) { + if (abv instanceof Int8Array) { + return 'Int8Array'; + } + if (abv instanceof Uint8Array) { + return 'Uint8Array'; + } + if (abv instanceof Uint8ClampedArray) { + return 'Uint8ClampedArray'; + } + if (abv instanceof Int16Array) { + return 'Int16Array'; + } + if (abv instanceof Uint16Array) { + return 'Uint16Array'; + } + if (abv instanceof Int32Array) { + return 'Int32Array'; + } + if (abv instanceof Uint32Array) { + return 'Uint32Array'; + } + if (abv instanceof Float32Array) { + return 'Float32Array'; + } + if (abv instanceof Float64Array) { + return 'Float64Array'; + } + if (abv instanceof DataView) { + return 'DataView'; + } + return ''; +} +function ArrayBufferViewSerializer(waitForPromise) { + return { + id: 'ArrayBufferView', + isType: ArrayBuffer.isView, + toObject: (abv) => __awaiter(this, void 0, void 0, function* () { + if (waitForPromise) { + // wait for promise to resolve if the abv was returned from getRandomValues + if (isArrayBufferViewWithPromise(abv)) { + yield abv._promise; + } + } + return { + name: arrayBufferViewName(abv), + buffer: abv.buffer, + }; + }), + fromObject: (abvs) => __awaiter(this, void 0, void 0, function* () { + // tslint:disable-next-line + return eval(`new ${abvs.name}(abvs.buffer)`); + }), + }; +} +function hasData(ck) { + return ck._import !== undefined; +} +const CryptoKeySerializer = { + id: 'CryptoKey', + isType: (o) => { + const localStr = o.toLocaleString(); + // can't use CryptoKey or constructor on WebView iOS + const isCryptoKey = localStr === '[object CryptoKey]' || localStr === '[object Key]'; + const isCryptoKeyWithData = o._import && !o.serialized; + return isCryptoKey || isCryptoKeyWithData; + }, + toObject: (ck) => __awaiter(this, void 0, void 0, function* () { + // if we already have the import serialized, just return that + if (hasData(ck)) { + return { + serialized: true, + _import: ck._import, + type: ck.type, + extractable: ck.extractable, + algorithm: ck.algorithm, + usages: ck.usages, + }; + } + const jwk = yield subtle().exportKey('jwk', ck); + return { + _import: { + format: 'jwk', + keyData: jwk, + }, + serialized: true, + algorithm: ck.algorithm, + extractable: ck.extractable, + usages: ck.usages, + type: ck.type, + }; + }), + fromObject: (cks) => __awaiter(this, void 0, void 0, function* () { + // if we don't have access to to a real crypto implementation, just return + // the serialized crypto key + if (crypto.fake) { + const newCks = Object.assign({}, cks); + delete newCks.serialized; + return newCks; + } + return subtle().importKey(cks._import.format, cks._import.keyData, cks.algorithm, cks.extractable, cks.usages); + }), +}; +//# sourceMappingURL=serializeBinary.js.map \ No newline at end of file diff --git a/examples/react-native/src/webview-crypto/serializeBinary.js.map b/examples/react-native/src/webview-crypto/serializeBinary.js.map new file mode 100644 index 00000000..90f8642f --- /dev/null +++ b/examples/react-native/src/webview-crypto/serializeBinary.js.map @@ -0,0 +1 @@ +{"version":3,"file":"serializeBinary.js","sourceRoot":"","sources":["../src/serializeBinary.ts"],"names":[],"mappings":";;;;;;;;AAAA,OAAO,EAAE,WAAW,EAAe,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAIlC,MAAM,UAAgB,KAAK,CAAC,IAAY;;QACvC,oEAAoE;QACpE,MAAM,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QACzC,OAAO,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;IAChD,CAAC;CAAA;AACD,MAAM,UAAgB,SAAS,CAAC,KAAU,EAAE,sBAAsB,GAAG,IAAI;;QACxE,MAAM,UAAU,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,sBAAsB,CAAC,EAAE,KAAK,CAAC,CAAC;QAC/E,oEAAoE;QACpE,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QAC3C,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC;IACxB,CAAC;CAAA;AAED,SAAS,WAAW,CAAC,sBAA+B;IACnD,OAAO;QACN,qBAAqB;QACrB,yBAAyB,CAAC,sBAAsB,CAAC;QACjD,mBAAmB;KACnB,CAAC;AACH,CAAC;AAED,MAAM,qBAAqB,GAAqC;IAC/D,EAAE,EAAE,aAAa;IACjB,MAAM,EAAE,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,YAAY,WAAW;IAE5C,uGAAuG;IACvG,oEAAoE;IACpE,QAAQ,EAAE,CAAO,EAAe,EAAE,EAAE;QACnC,OAAO,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3D,CAAC,CAAA;IACD,UAAU,EAAE,CAAO,IAAY,EAAE,EAAE;QAClC,MAAM,GAAG,GAAG,IAAI,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACzC,MAAM,OAAO,GAAG,IAAI,SAAS,CAAC,GAAG,CAAC,CAAC;QACnC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE;YACtD,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;SAChC;QACD,OAAO,GAAG,CAAC;IACZ,CAAC,CAAA;CACD,CAAC;AAUF,SAAS,4BAA4B,CAAC,GAAQ;IAC7C,OAAO,GAAG,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;AACvC,CAAC;AAED,2DAA2D;AAC3D,wDAAwD;AACxD,6EAA6E;AAC7E,SAAS,mBAAmB,CAAC,GAAoB;IAChD,IAAI,GAAG,YAAY,SAAS,EAAE;QAC7B,OAAO,WAAW,CAAC;KACnB;IACD,IAAI,GAAG,YAAY,UAAU,EAAE;QAC9B,OAAO,YAAY,CAAC;KACpB;IACD,IAAI,GAAG,YAAY,iBAAiB,EAAE;QACrC,OAAO,mBAAmB,CAAC;KAC3B;IACD,IAAI,GAAG,YAAY,UAAU,EAAE;QAC9B,OAAO,YAAY,CAAC;KACpB;IACD,IAAI,GAAG,YAAY,WAAW,EAAE;QAC/B,OAAO,aAAa,CAAC;KACrB;IACD,IAAI,GAAG,YAAY,UAAU,EAAE;QAC9B,OAAO,YAAY,CAAC;KACpB;IACD,IAAI,GAAG,YAAY,WAAW,EAAE;QAC/B,OAAO,aAAa,CAAC;KACrB;IACD,IAAI,GAAG,YAAY,YAAY,EAAE;QAChC,OAAO,cAAc,CAAC;KACtB;IACD,IAAI,GAAG,YAAY,YAAY,EAAE;QAChC,OAAO,cAAc,CAAC;KACtB;IACD,IAAI,GAAG,YAAY,QAAQ,EAAE;QAC5B,OAAO,UAAU,CAAC;KAClB;IACD,OAAO,EAAE,CAAC;AACX,CAAC;AAED,SAAS,yBAAyB,CACjC,cAAuB;IAEvB,OAAO;QACN,EAAE,EAAE,iBAAiB;QACrB,MAAM,EAAE,WAAW,CAAC,MAAM;QAC1B,QAAQ,EAAE,CAAO,GAAoB,EAAE,EAAE;YACxC,IAAI,cAAc,EAAE;gBACnB,2EAA2E;gBAC3E,IAAI,4BAA4B,CAAC,GAAG,CAAC,EAAE;oBACtC,MAAM,GAAG,CAAC,QAAQ,CAAC;iBACnB;aACD;YACD,OAAO;gBACN,IAAI,EAAE,mBAAmB,CAAC,GAAG,CAAC;gBAC9B,MAAM,EAAE,GAAG,CAAC,MAAM;aACX,CAAC;QACV,CAAC,CAAA;QACD,UAAU,EAAE,CAAO,IAAgC,EAAE,EAAE;YACtD,2BAA2B;YAC3B,OAAO,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,eAAe,CAAC,CAAC;QAC9C,CAAC,CAAA;KACD,CAAC;AACH,CAAC;AASD,SAAS,OAAO,CAAC,EAAkC;IAClD,OAAQ,EAAyB,CAAC,OAAO,KAAK,SAAS,CAAC;AACzD,CAAC;AAMD,MAAM,mBAAmB,GAAsE;IAC9F,EAAE,EAAE,WAAW;IACf,MAAM,EAAE,CAAC,CAAM,EAAE,EAAE;QAClB,MAAM,QAAQ,GAAG,CAAC,CAAC,cAAc,EAAE,CAAC;QACpC,oDAAoD;QACpD,MAAM,WAAW,GAAG,QAAQ,KAAK,oBAAoB,IAAI,QAAQ,KAAK,cAAc,CAAC;QACrF,MAAM,mBAAmB,GAAG,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC;QACvD,OAAO,WAAW,IAAI,mBAAmB,CAAC;IAC3C,CAAC;IACD,QAAQ,EAAE,CAAO,EAAE,EAAE,EAAE;QACtB,6DAA6D;QAC7D,IAAI,OAAO,CAAC,EAAE,CAAC,EAAE;YAChB,OAAO;gBACN,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,EAAE,CAAC,OAAO;gBACnB,IAAI,EAAE,EAAE,CAAC,IAAI;gBACb,WAAW,EAAE,EAAE,CAAC,WAAW;gBAC3B,SAAS,EAAE,EAAE,CAAC,SAAS;gBACvB,MAAM,EAAE,EAAE,CAAC,MAAM;aACjB,CAAC;SACF;QACD,MAAM,GAAG,GAAG,MAAM,MAAM,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAChD,OAAO;YACN,OAAO,EAAE;gBACR,MAAM,EAAE,KAAK;gBACb,OAAO,EAAE,GAAG;aACZ;YACD,UAAU,EAAE,IAAI;YAChB,SAAS,EAAE,EAAE,CAAC,SAAS;YACvB,WAAW,EAAE,EAAE,CAAC,WAAW;YAC3B,MAAM,EAAE,EAAE,CAAC,MAAM;YACjB,IAAI,EAAE,EAAE,CAAC,IAAI;SACb,CAAC;IACH,CAAC,CAAA;IACD,UAAU,EAAE,CAAO,GAAyB,EAAE,EAAE;QAC/C,0EAA0E;QAC1E,4BAA4B;QAC5B,IAAK,MAAc,CAAC,IAAI,EAAE;YACzB,MAAM,MAAM,qBAA8B,GAAG,CAAE,CAAC;YAChD,OAAO,MAAM,CAAC,UAAU,CAAC;YACzB,OAAO,MAAM,CAAC;SACd;QACD,OAAO,MAAM,EAAE,CAAC,SAAS,CACxB,GAAG,CAAC,OAAO,CAAC,MAAM,EAClB,GAAG,CAAC,OAAO,CAAC,OAAc,EAC1B,GAAG,CAAC,SAAgB,EACpB,GAAG,CAAC,WAAW,EACf,GAAG,CAAC,MAAM,CACV,CAAC;IACH,CAAC,CAAA;CACD,CAAC"} \ No newline at end of file diff --git a/examples/react-native/src/webview-crypto/webViewWorkerString.d.ts b/examples/react-native/src/webview-crypto/webViewWorkerString.d.ts new file mode 100644 index 00000000..0d667c32 --- /dev/null +++ b/examples/react-native/src/webview-crypto/webViewWorkerString.d.ts @@ -0,0 +1,3 @@ +export declare const base64InjString = "\n(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined'\n ? module.exports = factory(global)\n : typeof define === 'function' && define.amd\n ? define(factory) : factory(global)\n}((\n typeof self !== 'undefined' ? self\n : typeof window !== 'undefined' ? window\n : typeof global !== 'undefined' ? global\n: this\n), function(global) {\n 'use strict';\n // existing version for noConflict()\n var _Base64 = global.Base64;\n var version = \"2.4.9\";\n // if node.js and NOT React Native, we use Buffer\n var buffer;\n if (typeof module !== 'undefined' && module.exports) {\n try {\n buffer = eval(\"require('buffer').Buffer\");\n } catch (err) {\n buffer = undefined;\n }\n }\n // constants\n var b64chars\n = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n var b64tab = function(bin) {\n var t = {};\n for (var i = 0, l = bin.length; i < l; i++) t[bin.charAt(i)] = i;\n return t;\n }(b64chars);\n var fromCharCode = String.fromCharCode;\n // encoder stuff\n var cb_utob = function(c) {\n if (c.length < 2) {\n var cc = c.charCodeAt(0);\n return cc < 0x80 ? c\n : cc < 0x800 ? (fromCharCode(0xc0 | (cc >>> 6))\n + fromCharCode(0x80 | (cc & 0x3f)))\n : (fromCharCode(0xe0 | ((cc >>> 12) & 0x0f))\n + fromCharCode(0x80 | ((cc >>> 6) & 0x3f))\n + fromCharCode(0x80 | ( cc & 0x3f)));\n } else {\n var cc = 0x10000\n + (c.charCodeAt(0) - 0xD800) * 0x400\n + (c.charCodeAt(1) - 0xDC00);\n return (fromCharCode(0xf0 | ((cc >>> 18) & 0x07))\n + fromCharCode(0x80 | ((cc >>> 12) & 0x3f))\n + fromCharCode(0x80 | ((cc >>> 6) & 0x3f))\n + fromCharCode(0x80 | ( cc & 0x3f)));\n }\n };\n var re_utob = /[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\0-]/g;\n var utob = function(u) {\n return u.replace(re_utob, cb_utob);\n };\n var cb_encode = function(ccc) {\n var padlen = [0, 2, 1][ccc.length % 3],\n ord = ccc.charCodeAt(0) << 16\n | ((ccc.length > 1 ? ccc.charCodeAt(1) : 0) << 8)\n | ((ccc.length > 2 ? ccc.charCodeAt(2) : 0)),\n chars = [\n b64chars.charAt( ord >>> 18),\n b64chars.charAt((ord >>> 12) & 63),\n padlen >= 2 ? '=' : b64chars.charAt((ord >>> 6) & 63),\n padlen >= 1 ? '=' : b64chars.charAt(ord & 63)\n ];\n return chars.join('');\n };\n var btoa = global.btoa ? function(b) {\n return global.btoa(b);\n } : function(b) {\n return b.replace(/[sS]{1,3}/g, cb_encode);\n };\n var _encode = buffer ?\n buffer.from && Uint8Array && buffer.from !== Uint8Array.from\n ? function (u) {\n return (u.constructor === buffer.constructor ? u : buffer.from(u))\n .toString('base64')\n }\n : function (u) {\n return (u.constructor === buffer.constructor ? u : new buffer(u))\n .toString('base64')\n }\n : function (u) { return btoa(utob(u)) }\n ;\n var encode = function(u, urisafe) {\n return !urisafe\n ? _encode(String(u))\n : _encode(String(u)).replace(/[+/]/g, function(m0) {\n return m0 == '+' ? '-' : '_';\n }).replace(/=/g, '');\n };\n var encodeURI = function(u) { return encode(u, true) };\n // decoder stuff\n var re_btou = new RegExp([\n '[\u00C0-\u00DF][\u0080-\u00BF]',\n '[\u00E0-\u00EF][\u0080-\u00BF]{2}',\n '[\u00F0-\u00F7][\u0080-\u00BF]{3}'\n ].join('|'), 'g');\n var cb_btou = function(cccc) {\n switch(cccc.length) {\n case 4:\n var cp = ((0x07 & cccc.charCodeAt(0)) << 18)\n | ((0x3f & cccc.charCodeAt(1)) << 12)\n | ((0x3f & cccc.charCodeAt(2)) << 6)\n | (0x3f & cccc.charCodeAt(3)),\n offset = cp - 0x10000;\n return (fromCharCode((offset >>> 10) + 0xD800)\n + fromCharCode((offset & 0x3FF) + 0xDC00));\n case 3:\n return fromCharCode(\n ((0x0f & cccc.charCodeAt(0)) << 12)\n | ((0x3f & cccc.charCodeAt(1)) << 6)\n | (0x3f & cccc.charCodeAt(2))\n );\n default:\n return fromCharCode(\n ((0x1f & cccc.charCodeAt(0)) << 6)\n | (0x3f & cccc.charCodeAt(1))\n );\n }\n };\n var btou = function(b) {\n return b.replace(re_btou, cb_btou);\n };\n var cb_decode = function(cccc) {\n var len = cccc.length,\n padlen = len % 4,\n n = (len > 0 ? b64tab[cccc.charAt(0)] << 18 : 0)\n | (len > 1 ? b64tab[cccc.charAt(1)] << 12 : 0)\n | (len > 2 ? b64tab[cccc.charAt(2)] << 6 : 0)\n | (len > 3 ? b64tab[cccc.charAt(3)] : 0),\n chars = [\n fromCharCode( n >>> 16),\n fromCharCode((n >>> 8) & 0xff),\n fromCharCode( n & 0xff)\n ];\n chars.length -= [0, 0, 2, 1][padlen];\n return chars.join('');\n };\n var atob = global.atob ? function(a) {\n return global.atob(a);\n } : function(a){\n return a.replace(/[sS]{1,4}/g, cb_decode);\n };\n var _decode = buffer ?\n buffer.from && Uint8Array && buffer.from !== Uint8Array.from\n ? function(a) {\n return (a.constructor === buffer.constructor\n ? a : buffer.from(a, 'base64')).toString();\n }\n : function(a) {\n return (a.constructor === buffer.constructor\n ? a : new buffer(a, 'base64')).toString();\n }\n : function(a) { return btou(atob(a)) };\n var decode = function(a){\n return _decode(\n String(a).replace(/[-_]/g, function(m0) { return m0 == '-' ? '+' : '/' })\n .replace(/[^A-Za-z0-9+/]/g, '')\n );\n };\n var noConflict = function() {\n var Base64 = global.Base64;\n global.Base64 = _Base64;\n return Base64;\n };\n // export Base64\n global.Base64 = {\n VERSION: version,\n atob: atob,\n btoa: btoa,\n fromBase64: decode,\n toBase64: encode,\n utob: utob,\n encode: encode,\n encodeURI: encodeURI,\n btou: btou,\n decode: decode,\n noConflict: noConflict,\n __buffer__: buffer\n };\n // if ES5 is available, make Base64.extendString() available\n if (typeof Object.defineProperty === 'function') {\n var noEnum = function(v){\n return {value:v,enumerable:false,writable:true,configurable:true};\n };\n global.Base64.extendString = function () {\n Object.defineProperty(\n String.prototype, 'fromBase64', noEnum(function () {\n return decode(this)\n }));\n Object.defineProperty(\n String.prototype, 'toBase64', noEnum(function (urisafe) {\n return encode(this, urisafe)\n }));\n Object.defineProperty(\n String.prototype, 'toBase64URI', noEnum(function () {\n return encode(this, true)\n }));\n };\n }\n return {Base64: global.Base64}\n}));\n"; +declare const _default: "\nvar WebViewWorker = function(t) {\n function r(n) {\n if (e[n]) return e[n].exports;\n var o = e[n] = {\n exports: {},\n id: n,\n loaded: !1\n };\n return t[n].call(o.exports, o, o.exports, r), o.loaded = !0, o.exports\n }\n var e = {};\n return r.m = t, r.c = e, r.p = \"\", r(0)\n}([function(t, r, e) {\n t.exports = e(1)\n}, function(t, r, e) {\n \"use strict\";\n var n = this && this.__awaiter || function(t, r, e, n) {\n return new(e || (e = Promise))(function(o, i) {\n function a(t) {\n try {\n c(n.next(t))\n } catch (t) {\n i(t)\n }\n }\n\n function u(t) {\n try {\n c(n.throw(t))\n } catch (t) {\n i(t)\n }\n }\n\n function c(t) {\n t.done ? o(t.value) : new e(function(r) {\n r(t.value)\n }).then(a, u)\n }\n c((n = n.apply(t, r)).next())\n })\n },\n o = this && this.__generator || function(t, r) {\n function e(t) {\n return function(r) {\n return n([t, r])\n }\n }\n\n function n(e) {\n if (o) throw new TypeError(\"Generator is already executing.\");\n for (; u;) try {\n if (o = 1, i && (a = i[2 & e[0] ? \"return\" : e[0] ? \"throw\" : \"next\"]) && !(a = a.call(i, e[1])).done) return a;\n switch (i = 0, a && (e = [0, a.value]), e[0]) {\n case 0:\n case 1:\n a = e;\n break;\n case 4:\n return u.label++, {\n value: e[1],\n done: !1\n };\n case 5:\n u.label++, i = e[1], e = [0];\n continue;\n case 7:\n e = u.ops.pop(), u.trys.pop();\n continue;\n default:\n if (a = u.trys, !(a = a.length > 0 && a[a.length - 1]) && (6 === e[0] || 2 === e[0])) {\n u = 0;\n continue\n }\n if (3 === e[0] && (!a || e[1] > a[0] && e[1] < a[3])) {\n u.label = e[1];\n break\n }\n if (6 === e[0] && u.label < a[1]) {\n u.label = a[1], a = e;\n break\n }\n if (a && u.label < a[2]) {\n u.label = a[2], u.ops.push(e);\n break\n }\n a[2] && u.ops.pop(), u.trys.pop();\n continue\n }\n e = r.call(t, u)\n } catch (t) {\n e = [6, t], i = 0\n } finally {\n o = a = 0\n }\n if (5 & e[0]) throw e[1];\n return {\n value: e[0] ? e[1] : void 0,\n done: !0\n }\n }\n var o, i, a, u = {\n label: 0,\n sent: function() {\n if (1 & a[0]) throw a[1];\n return a[1]\n },\n trys: [],\n ops: []\n };\n return {\n next: e(0),\n throw: e(1),\n return: e(2)\n }\n },\n i = e(2),\n a = e(126),\n u = e(127),\n c = function() {\n function t(t) {\n this.sendToMain = t, t(\"We are ready!\")\n }\n return t.prototype.onMainMessage = function(t) {\n return n(this, void 0, void 0, function() {\n var r, e, n, c, s, f, p, l;\n return o(this, function(o) {\n switch (o.label) {\n case 0:\n return o.trys.push([0, 2, , 4]), [4, i.parse(t)];\n case 1:\n return l = o.sent(), r = l.id, e = l.method, n = l.args, [3, 4];\n case 2:\n return c = o.sent(), [4, this.send({\n reason: \"Couldn't parse data: \" + c\n })];\n case 3:\n return o.sent(), [2];\n case 4:\n return o.trys.push([4, 8, , 10]), \"getRandomValues\" !== e ? [3, 5] : (s = crypto.getRandomValues(n[0]), [3, 7]);\n case 5:\n // console.log(f, n)\n return f = e.split(\".\")[1], [4, a.subtle()[f].apply(a.subtle(), n)];\n case 6:\n s = o.sent(), \"importKey\" === f && (s._import = {\n format: n[0],\n keyData: n[1]\n }), o.label = 7;\n case 7:\n return [3, 10];\n case 8:\n return p = o.sent(), [4, this.send({\n id: r,\n reason: u(p)\n })];\n case 9:\n return o.sent(), [2];\n case 10:\n return [4, this.send({\n id: r,\n value: s\n })];\n case 11:\n return o.sent(), [2]\n }\n })\n })\n }, t.prototype.send = function(t) {\n return n(this, void 0, void 0, function() {\n var r, e, n;\n return o(this, function(o) {\n switch (o.label) {\n case 0:\n return o.trys.push([0, 2, , 3]), [4, i.stringify(t)];\n case 1:\n return r = o.sent(), [3, 3];\n case 2:\n return e = o.sent(), n = {\n id: t.id,\n reason: \"stringify error \" + e\n }, this.sendToMain(JSON.stringify(n)), [2];\n case 3:\n return this.sendToMain(r), [2]\n }\n })\n })\n }, t\n }();\n t.exports = c\n}, function(module, exports, __webpack_require__) {\n \"use strict\";\n\n function parse(t) {\n return __awaiter(this, void 0, void 0, function() {\n var r, e;\n return __generator(this, function(n) {\n switch (n.label) {\n case 0:\n // console.log('*** decoding', t);\n return r = unescape(t), e = JSON.parse(r), [4, asyncSerialize_1.fromObjects(serializers(!0), e)];\n case 1:\n return [2, n.sent()]\n }\n })\n })\n }\n\n function stringify(t, r) {\n return void 0 === r && (r = !0), __awaiter(this, void 0, void 0, function() {\n var e, n;\n return __generator(this, function(o) {\n switch (o.label) {\n case 0:\n return [4, asyncSerialize_1.toObjects(serializers(r), t)];\n case 1:\n // console.log('*** encoding', n);\n return e = o.sent(), n = JSON.stringify(e), [2, escape(n)]\n }\n })\n })\n }\n\n function serializers(t) {\n return [ArrayBufferSerializer, ArrayBufferViewSerializer(t), CryptoKeySerializer]\n }\n\n function isArrayBufferViewWithPromise(t) {\n return t.hasOwnProperty(\"_promise\")\n }\n\n function arrayBufferViewName(t) {\n return t instanceof Int8Array ? \"Int8Array\" : t instanceof Uint8Array ? \"Uint8Array\" : t instanceof Uint8ClampedArray ? \"Uint8ClampedArray\" : t instanceof Int16Array ? \"Int16Array\" : t instanceof Uint16Array ? \"Uint16Array\" : t instanceof Int32Array ? \"Int32Array\" : t instanceof Uint32Array ? \"Uint32Array\" : t instanceof Float32Array ? \"Float32Array\" : t instanceof Float64Array ? \"Float64Array\" : t instanceof DataView ? \"DataView\" : void 0\n }\n\n function ArrayBufferViewSerializer(waitForPromise) {\n var _this = this;\n return {\n id: \"ArrayBufferView\",\n isType: ArrayBuffer.isView,\n toObject: function(t) {\n return __awaiter(_this, void 0, void 0, function() {\n return __generator(this, function(r) {\n switch (r.label) {\n case 0:\n return waitForPromise && isArrayBufferViewWithPromise(t) ? [4, t._promise] : [3, 2];\n case 1:\n r.sent(), r.label = 2;\n case 2:\n return [2, {\n name: arrayBufferViewName(t),\n buffer: t.buffer\n }]\n }\n })\n })\n },\n fromObject: function(abvs) {\n return __awaiter(_this, void 0, void 0, function() {\n return __generator(this, function(_a) {\n return [2, eval(\"new \" + abvs.name + \"(abvs.buffer)\")]\n })\n })\n }\n }\n }\n\n function hasData(t) {\n return void 0 !== t._import\n }\n var __assign = this && this.__assign || Object.assign || function(t) {\n for (var r, e = 1, n = arguments.length; e < n; e++) {\n r = arguments[e];\n for (var o in r) Object.prototype.hasOwnProperty.call(r, o) && (t[o] = r[o])\n }\n return t\n },\n __awaiter = this && this.__awaiter || function(t, r, e, n) {\n return new(e || (e = Promise))(function(o, i) {\n function a(t) {\n try {\n c(n.next(t))\n } catch (t) {\n i(t)\n }\n }\n\n function u(t) {\n try {\n c(n.throw(t))\n } catch (t) {\n i(t)\n }\n }\n\n function c(t) {\n t.done ? o(t.value) : new e(function(r) {\n r(t.value)\n }).then(a, u)\n }\n c((n = n.apply(t, r)).next())\n })\n },\n __generator = this && this.__generator || function(t, r) {\n function e(t) {\n return function(r) {\n return n([t, r])\n }\n }\n\n function n(e) {\n if (o) throw new TypeError(\"Generator is already executing.\");\n for (; u;) try {\n if (o = 1, i && (a = i[2 & e[0] ? \"return\" : e[0] ? \"throw\" : \"next\"]) && !(a = a.call(i, e[1])).done) return a;\n switch (i = 0, a && (e = [0, a.value]), e[0]) {\n case 0:\n case 1:\n a = e;\n break;\n case 4:\n return u.label++, {\n value: e[1],\n done: !1\n };\n case 5:\n u.label++, i = e[1], e = [0];\n continue;\n case 7:\n e = u.ops.pop(), u.trys.pop();\n continue;\n default:\n if (a = u.trys, !(a = a.length > 0 && a[a.length - 1]) && (6 === e[0] || 2 === e[0])) {\n u = 0;\n continue\n }\n if (3 === e[0] && (!a || e[1] > a[0] && e[1] < a[3])) {\n u.label = e[1];\n break\n }\n if (6 === e[0] && u.label < a[1]) {\n u.label = a[1], a = e;\n break\n }\n if (a && u.label < a[2]) {\n u.label = a[2], u.ops.push(e);\n break\n }\n a[2] && u.ops.pop(), u.trys.pop();\n continue\n }\n e = r.call(t, u)\n } catch (t) {\n e = [6, t], i = 0\n } finally {\n o = a = 0\n }\n if (5 & e[0]) throw e[1];\n return {\n value: e[0] ? e[1] : void 0,\n done: !0\n }\n }\n var o, i, a, u = {\n label: 0,\n sent: function() {\n if (1 & a[0]) throw a[1];\n return a[1]\n },\n trys: [],\n ops: []\n };\n return {\n next: e(0),\n throw: e(1),\n return: e(2)\n }\n },\n _this = this,\n asyncSerialize_1 = __webpack_require__(3),\n compat_1 = __webpack_require__(126);\n exports.parse = parse, exports.stringify = stringify;\n var ArrayBufferSerializer = {\n id: \"ArrayBuffer\",\n isType: function(t) {\n return t instanceof ArrayBuffer\n },\n toObject: function(t) {\n return __awaiter(_this, void 0, void 0, function() {\n return __generator(this, function(r) {\n return [2, String.fromCharCode.apply(null, new Int8Array(t))]\n })\n })\n },\n fromObject: function(t) {\n return __awaiter(_this, void 0, void 0, function() {\n var r, e, n, o;\n return __generator(this, function(i) {\n for (r = new ArrayBuffer(t.length), e = new Int8Array(r), n = 0, o = t.length; n < o; n++) e[n] = t.charCodeAt(n);\n return [2, r]\n })\n })\n }\n },\n CryptoKeySerializer = {\n id: \"CryptoKey\",\n isType: function(t) {\n var r = t.toLocaleString(),\n e = \"[object CryptoKey]\" === r || \"[object Key]\" === r,\n n = t._import && !t.serialized;\n return e || n\n },\n toObject: function(t) {\n return __awaiter(_this, void 0, void 0, function() {\n var r;\n return __generator(this, function(e) {\n switch (e.label) {\n case 0:\n return hasData(t) ? [2, {\n serialized: !0,\n _import: t._import,\n type: t.type,\n extractable: t.extractable,\n algorithm: t.algorithm,\n usages: t.usages\n }] : [4, compat_1.subtle().exportKey(\"jwk\", t)];\n case 1:\n return r = e.sent(), [2, {\n _import: {\n format: \"jwk\",\n keyData: r\n },\n serialized: !0,\n algorithm: t.algorithm,\n extractable: t.extractable,\n usages: t.usages,\n type: t.type\n }]\n }\n })\n })\n },\n fromObject: function(t) {\n return __awaiter(_this, void 0, void 0, function() {\n var r;\n return __generator(this, function(e) {\n switch (e.label) {\n case 0:\n return crypto.fake ? (r = __assign({}, t), delete r.serialized, [2, r]) : [4, compat_1.subtle().importKey(t._import.format, t._import.keyData, t.algorithm, t.extractable, t.usages)];\n case 1:\n return [2, e.sent()]\n }\n })\n })\n }\n }\n}, function(t, r, e) {\n \"use strict\";\n\n function n(t) {\n return t.hasOwnProperty(\"__serializer_id\")\n }\n\n function o(t, r) {\n return a(this, void 0, void 0, function() {\n var e, n, i, a, s, f, p, l, v, h, y;\n return u(this, function(u) {\n switch (u.label) {\n case 0:\n return \"object\" != typeof r ? [2, r] : (e = c(t, function(t) {\n return t.isType(r)\n }), e ? e.toObject ? [4, e.toObject(r)] : [3, 2] : [3, 5]);\n case 1:\n return i = u.sent(), [3, 3];\n case 2:\n i = r, u.label = 3;\n case 3:\n return n = i, a = {\n __serializer_id: e.id\n }, [4, o(t, n)];\n case 4:\n return [2, (a.value = u.sent(), a)];\n case 5:\n s = r instanceof Array ? [] : {}, f = [];\n for (p in r) f.push(p);\n l = 0, u.label = 6;\n case 6:\n return l < f.length ? (v = f[l], h = s, y = v, [4, o(t, r[v])]) : [3, 9];\n case 7:\n h[y] = u.sent(), u.label = 8;\n case 8:\n return l++, [3, 6];\n case 9:\n return [2, s]\n }\n })\n })\n }\n\n function i(t, r) {\n return a(this, void 0, void 0, function() {\n var e, o, a, s, f, p, l, v, h;\n return u(this, function(u) {\n switch (u.label) {\n case 0:\n return \"object\" != typeof r ? [2, r] : n(r) ? [4, i(t, r.value)] : [3, 2];\n case 1:\n return e = u.sent(), o = c(t, [\"id\", r.__serializer_id]), o.fromObject ? [2, o.fromObject(e)] : [2, e];\n case 2:\n a = r instanceof Array ? [] : {}, s = [];\n for (f in r) s.push(f);\n p = 0, u.label = 3;\n case 3:\n return p < s.length ? (l = s[p], v = a, h = l, [4, i(t, r[l])]) : [3, 6];\n case 4:\n v[h] = u.sent(), u.label = 5;\n case 5:\n return p++, [3, 3];\n case 6:\n return [2, a]\n }\n })\n })\n }\n var a = this && this.__awaiter || function(t, r, e, n) {\n return new(e || (e = Promise))(function(o, i) {\n function a(t) {\n try {\n c(n.next(t))\n } catch (t) {\n i(t)\n }\n }\n\n function u(t) {\n try {\n c(n.throw(t))\n } catch (t) {\n i(t)\n }\n }\n\n function c(t) {\n t.done ? o(t.value) : new e(function(r) {\n r(t.value)\n }).then(a, u)\n }\n c((n = n.apply(t, r)).next())\n })\n },\n u = this && this.__generator || function(t, r) {\n function e(t) {\n return function(r) {\n return n([t, r])\n }\n }\n\n function n(e) {\n if (o) throw new TypeError(\"Generator is already executing.\");\n for (; u;) try {\n if (o = 1, i && (a = i[2 & e[0] ? \"return\" : e[0] ? \"throw\" : \"next\"]) && !(a = a.call(i, e[1])).done) return a;\n switch (i = 0, a && (e = [0, a.value]), e[0]) {\n case 0:\n case 1:\n a = e;\n break;\n case 4:\n return u.label++, {\n value: e[1],\n done: !1\n };\n case 5:\n u.label++, i = e[1], e = [0];\n continue;\n case 7:\n e = u.ops.pop(), u.trys.pop();\n continue;\n default:\n if (a = u.trys, !(a = a.length > 0 && a[a.length - 1]) && (6 === e[0] || 2 === e[0])) {\n u = 0;\n continue\n }\n if (3 === e[0] && (!a || e[1] > a[0] && e[1] < a[3])) {\n u.label = e[1];\n break\n }\n if (6 === e[0] && u.label < a[1]) {\n u.label = a[1], a = e;\n break\n }\n if (a && u.label < a[2]) {\n u.label = a[2], u.ops.push(e);\n break\n }\n a[2] && u.ops.pop(), u.trys.pop();\n continue\n }\n e = r.call(t, u)\n } catch (t) {\n e = [6, t], i = 0\n } finally {\n o = a = 0\n }\n if (5 & e[0]) throw e[1];\n return {\n value: e[0] ? e[1] : void 0,\n done: !0\n }\n }\n var o, i, a, u = {\n label: 0,\n sent: function() {\n if (1 & a[0]) throw a[1];\n return a[1]\n },\n trys: [],\n ops: []\n };\n return {\n next: e(0),\n throw: e(1),\n return: e(2)\n }\n },\n c = e(4);\n (function() {\n function t() {}\n return t\n })();\n r.toObjects = o, r.fromObjects = i\n}, function(t, r, e) {\n var n = e(5),\n o = e(121),\n i = n(o);\n t.exports = i\n}, function(t, r, e) {\n function n(t) {\n return function(r, e, n) {\n var u = Object(r);\n if (!i(r)) {\n var c = o(e, 3);\n r = a(r), e = function(t) {\n return c(u[t], t, u)\n }\n }\n var s = t(r, e, n);\n return s > -1 ? u[c ? r[s] : s] : void 0\n }\n }\n var o = e(6),\n i = e(92),\n a = e(73);\n t.exports = n\n}, function(t, r, e) {\n function n(t) {\n return \"function\" == typeof t ? t : null == t ? a : \"object\" == typeof t ? u(t) ? i(t[0], t[1]) : o(t) : c(t)\n }\n var o = e(7),\n i = e(101),\n a = e(117),\n u = e(69),\n c = e(118);\n t.exports = n\n}, function(t, r, e) {\n function n(t) {\n var r = i(t);\n return 1 == r.length && r[0][2] ? a(r[0][0], r[0][1]) : function(e) {\n return e === t || o(e, t, r)\n }\n }\n var o = e(8),\n i = e(98),\n a = e(100);\n t.exports = n\n}, function(t, r, e) {\n function n(t, r, e, n) {\n var c = e.length,\n s = c,\n f = !n;\n if (null == t) return !s;\n for (t = Object(t); c--;) {\n var p = e[c];\n if (f && p[2] ? p[1] !== t[p[0]] : !(p[0] in t)) return !1\n }\n for (; ++c < s;) {\n p = e[c];\n var l = p[0],\n v = t[l],\n h = p[1];\n if (f && p[2]) {\n if (void 0 === v && !(l in t)) return !1\n } else {\n var y = new o;\n if (n) var _ = n(v, h, l, t, r, y);\n if (!(void 0 === _ ? i(h, v, a | u, n, y) : _)) return !1\n }\n }\n return !0\n }\n var o = e(9),\n i = e(53),\n a = 1,\n u = 2;\n t.exports = n\n}, function(t, r, e) {\n function n(t) {\n var r = this.__data__ = new o(t);\n this.size = r.size\n }\n var o = e(10),\n i = e(18),\n a = e(19),\n u = e(20),\n c = e(21),\n s = e(22);\n n.prototype.clear = i, n.prototype.delete = a, n.prototype.get = u, n.prototype.has = c, n.prototype.set = s, t.exports = n\n}, function(t, r, e) {\n function n(t) {\n var r = -1,\n e = null == t ? 0 : t.length;\n for (this.clear(); ++r < e;) {\n var n = t[r];\n this.set(n[0], n[1])\n }\n }\n var o = e(11),\n i = e(12),\n a = e(15),\n u = e(16),\n c = e(17);\n n.prototype.clear = o, n.prototype.delete = i, n.prototype.get = a, n.prototype.has = u, n.prototype.set = c, t.exports = n\n}, function(t, r) {\n function e() {\n this.__data__ = [], this.size = 0\n }\n t.exports = e\n}, function(t, r, e) {\n function n(t) {\n var r = this.__data__,\n e = o(r, t);\n if (e < 0) return !1;\n var n = r.length - 1;\n return e == n ? r.pop() : a.call(r, e, 1), --this.size, !0\n }\n var o = e(13),\n i = Array.prototype,\n a = i.splice;\n t.exports = n\n}, function(t, r, e) {\n function n(t, r) {\n for (var e = t.length; e--;)\n if (o(t[e][0], r)) return e;\n return -1\n }\n var o = e(14);\n t.exports = n\n}, function(t, r) {\n function e(t, r) {\n return t === r || t !== t && r !== r\n }\n t.exports = e\n}, function(t, r, e) {\n function n(t) {\n var r = this.__data__,\n e = o(r, t);\n return e < 0 ? void 0 : r[e][1]\n }\n var o = e(13);\n t.exports = n\n}, function(t, r, e) {\n function n(t) {\n return o(this.__data__, t) > -1\n }\n var o = e(13);\n t.exports = n\n}, function(t, r, e) {\n function n(t, r) {\n var e = this.__data__,\n n = o(e, t);\n return n < 0 ? (++this.size, e.push([t, r])) : e[n][1] = r, this\n }\n var o = e(13);\n t.exports = n\n}, function(t, r, e) {\n function n() {\n this.__data__ = new o, this.size = 0\n }\n var o = e(10);\n t.exports = n\n}, function(t, r) {\n function e(t) {\n var r = this.__data__,\n e = r.delete(t);\n return this.size = r.size, e\n }\n t.exports = e\n}, function(t, r) {\n function e(t) {\n return this.__data__.get(t)\n }\n t.exports = e\n}, function(t, r) {\n function e(t) {\n return this.__data__.has(t)\n }\n t.exports = e\n}, function(t, r, e) {\n function n(t, r) {\n var e = this.__data__;\n if (e instanceof o) {\n var n = e.__data__;\n if (!i || n.length < u - 1) return n.push([t, r]), this.size = ++e.size, this;\n e = this.__data__ = new a(n)\n }\n return e.set(t, r), this.size = e.size, this\n }\n var o = e(10),\n i = e(23),\n a = e(38),\n u = 200;\n t.exports = n\n}, function(t, r, e) {\n var n = e(24),\n o = e(29),\n i = n(o, \"Map\");\n t.exports = i\n}, function(t, r, e) {\n function n(t, r) {\n var e = i(t, r);\n return o(e) ? e : void 0\n }\n var o = e(25),\n i = e(37);\n t.exports = n\n}, function(t, r, e) {\n function n(t) {\n if (!a(t) || i(t)) return !1;\n var r = o(t) ? h : s;\n return r.test(u(t))\n }\n var o = e(26),\n i = e(34),\n a = e(33),\n u = e(36),\n c = /[\\\\^$.*+?()[\\]{}|]/g,\n s = /^\\[object .+?Constructor\\]$/,\n f = Function.prototype,\n p = Object.prototype,\n l = f.toString,\n v = p.hasOwnProperty,\n h = RegExp(\"^\" + l.call(v).replace(c, \"\\\\$&\").replace(/hasOwnProperty|(function).*?(?=\\\\\\()| for .+?(?=\\\\\\])/g, \"$1.*?\") + \"$\");\n t.exports = n\n}, function(t, r, e) {\n function n(t) {\n if (!i(t)) return !1;\n var r = o(t);\n return r == u || r == c || r == a || r == s\n }\n var o = e(27),\n i = e(33),\n a = \"[object AsyncFunction]\",\n u = \"[object Function]\",\n c = \"[object GeneratorFunction]\",\n s = \"[object Proxy]\";\n t.exports = n\n}, function(t, r, e) {\n function n(t) {\n return null == t ? void 0 === t ? c : u : s && s in Object(t) ? i(t) : a(t)\n }\n var o = e(28),\n i = e(31),\n a = e(32),\n u = \"[object Null]\",\n c = \"[object Undefined]\",\n s = o ? o.toStringTag : void 0;\n t.exports = n\n}, function(t, r, e) {\n var n = e(29),\n o = n.Symbol;\n t.exports = o\n}, function(t, r, e) {\n var n = e(30),\n o = \"object\" == typeof self && self && self.Object === Object && self,\n i = n || o || Function(\"return this\")();\n t.exports = i\n}, function(t, r) {\n (function(r) {\n var e = \"object\" == typeof r && r && r.Object === Object && r;\n t.exports = e\n }).call(r, function() {\n return this\n }())\n}, function(t, r, e) {\n function n(t) {\n var r = a.call(t, c),\n e = t[c];\n try {\n t[c] = void 0;\n var n = !0\n } catch (t) {}\n var o = u.call(t);\n return n && (r ? t[c] = e : delete t[c]), o\n }\n var o = e(28),\n i = Object.prototype,\n a = i.hasOwnProperty,\n u = i.toString,\n c = o ? o.toStringTag : void 0;\n t.exports = n\n}, function(t, r) {\n function e(t) {\n return o.call(t)\n }\n var n = Object.prototype,\n o = n.toString;\n t.exports = e\n}, function(t, r) {\n function e(t) {\n var r = typeof t;\n return null != t && (\"object\" == r || \"function\" == r)\n }\n t.exports = e\n}, function(t, r, e) {\n function n(t) {\n return !!i && i in t\n }\n var o = e(35),\n i = function() {\n var t = /[^.]+$/.exec(o && o.keys && o.keys.IE_PROTO || \"\");\n return t ? \"Symbol(src)_1.\" + t : \"\"\n }();\n t.exports = n\n}, function(t, r, e) {\n var n = e(29),\n o = n[\"__core-js_shared__\"];\n t.exports = o\n}, function(t, r) {\n function e(t) {\n if (null != t) {\n try {\n return o.call(t)\n } catch (t) {}\n try {\n return t + \"\"\n } catch (t) {}\n }\n return \"\"\n }\n var n = Function.prototype,\n o = n.toString;\n t.exports = e\n}, function(t, r) {\n function e(t, r) {\n return null == t ? void 0 : t[r]\n }\n t.exports = e\n}, function(t, r, e) {\n function n(t) {\n var r = -1,\n e = null == t ? 0 : t.length;\n for (this.clear(); ++r < e;) {\n var n = t[r];\n this.set(n[0], n[1])\n }\n }\n var o = e(39),\n i = e(47),\n a = e(50),\n u = e(51),\n c = e(52);\n n.prototype.clear = o, n.prototype.delete = i, n.prototype.get = a, n.prototype.has = u, n.prototype.set = c, t.exports = n\n}, function(t, r, e) {\n function n() {\n this.size = 0, this.__data__ = {\n hash: new o,\n map: new(a || i),\n string: new o\n }\n }\n var o = e(40),\n i = e(10),\n a = e(23);\n t.exports = n\n}, function(t, r, e) {\n function n(t) {\n var r = -1,\n e = null == t ? 0 : t.length;\n for (this.clear(); ++r < e;) {\n var n = t[r];\n this.set(n[0], n[1])\n }\n }\n var o = e(41),\n i = e(43),\n a = e(44),\n u = e(45),\n c = e(46);\n n.prototype.clear = o, n.prototype.delete = i, n.prototype.get = a, n.prototype.has = u, n.prototype.set = c, t.exports = n\n}, function(t, r, e) {\n function n() {\n this.__data__ = o ? o(null) : {}, this.size = 0\n }\n var o = e(42);\n t.exports = n\n}, function(t, r, e) {\n var n = e(24),\n o = n(Object, \"create\");\n t.exports = o\n}, function(t, r) {\n function e(t) {\n var r = this.has(t) && delete this.__data__[t];\n return this.size -= r ? 1 : 0, r\n }\n t.exports = e\n}, function(t, r, e) {\n function n(t) {\n var r = this.__data__;\n if (o) {\n var e = r[t];\n return e === i ? void 0 : e\n }\n return u.call(r, t) ? r[t] : void 0\n }\n var o = e(42),\n i = \"__lodash_hash_undefined__\",\n a = Object.prototype,\n u = a.hasOwnProperty;\n t.exports = n\n}, function(t, r, e) {\n function n(t) {\n var r = this.__data__;\n return o ? void 0 !== r[t] : a.call(r, t)\n }\n var o = e(42),\n i = Object.prototype,\n a = i.hasOwnProperty;\n t.exports = n\n}, function(t, r, e) {\n function n(t, r) {\n var e = this.__data__;\n return this.size += this.has(t) ? 0 : 1, e[t] = o && void 0 === r ? i : r, this\n }\n var o = e(42),\n i = \"__lodash_hash_undefined__\";\n t.exports = n\n}, function(t, r, e) {\n function n(t) {\n var r = o(this, t).delete(t);\n return this.size -= r ? 1 : 0, r\n }\n var o = e(48);\n t.exports = n\n}, function(t, r, e) {\n function n(t, r) {\n var e = t.__data__;\n return o(r) ? e[\"string\" == typeof r ? \"string\" : \"hash\"] : e.map\n }\n var o = e(49);\n t.exports = n\n}, function(t, r) {\n function e(t) {\n var r = typeof t;\n return \"string\" == r || \"number\" == r || \"symbol\" == r || \"boolean\" == r ? \"__proto__\" !== t : null === t\n }\n t.exports = e\n}, function(t, r, e) {\n function n(t) {\n return o(this, t).get(t)\n }\n var o = e(48);\n t.exports = n\n}, function(t, r, e) {\n function n(t) {\n return o(this, t).has(t)\n }\n var o = e(48);\n t.exports = n\n}, function(t, r, e) {\n function n(t, r) {\n var e = o(this, t),\n n = e.size;\n return e.set(t, r), this.size += e.size == n ? 0 : 1, this\n }\n var o = e(48);\n t.exports = n\n}, function(t, r, e) {\n function n(t, r, e, a, u) {\n return t === r || (null == t || null == r || !i(t) && !i(r) ? t !== t && r !== r : o(t, r, e, a, n, u))\n }\n var o = e(54),\n i = e(78);\n t.exports = n\n}, function(t, r, e) {\n function n(t, r, e, n, _, d) {\n var x = s(t),\n g = s(r),\n w = x ? h : c(t),\n j = g ? h : c(r);\n w = w == v ? y : w, j = j == v ? y : j;\n var m = w == y,\n O = j == y,\n A = w == j;\n if (A && f(t)) {\n if (!f(r)) return !1;\n x = !0, m = !1\n }\n if (A && !m) return d || (d = new o), x || p(t) ? i(t, r, e, n, _, d) : a(t, r, w, e, n, _, d);\n if (!(e & l)) {\n var z = m && b.call(t, \"__wrapped__\"),\n k = O && b.call(r, \"__wrapped__\");\n if (z || k) {\n var S = z ? t.value() : t,\n P = k ? r.value() : r;\n return d || (d = new o), _(S, P, e, n, d)\n }\n }\n return !!A && (d || (d = new o), u(t, r, e, n, _, d))\n }\n var o = e(9),\n i = e(55),\n a = e(61),\n u = e(65),\n c = e(93),\n s = e(69),\n f = e(79),\n p = e(83),\n l = 1,\n v = \"[object Arguments]\",\n h = \"[object Array]\",\n y = \"[object Object]\",\n _ = Object.prototype,\n b = _.hasOwnProperty;\n t.exports = n\n}, function(t, r, e) {\n function n(t, r, e, n, s, f) {\n var p = e & u,\n l = t.length,\n v = r.length;\n if (l != v && !(p && v > l)) return !1;\n var h = f.get(t);\n if (h && f.get(r)) return h == r;\n var y = -1,\n _ = !0,\n b = e & c ? new o : void 0;\n for (f.set(t, r), f.set(r, t); ++y < l;) {\n var d = t[y],\n x = r[y];\n if (n) var g = p ? n(x, d, y, r, t, f) : n(d, x, y, t, r, f);\n if (void 0 !== g) {\n if (g) continue;\n _ = !1;\n break\n }\n if (b) {\n if (!i(r, function(t, r) {\n if (!a(b, r) && (d === t || s(d, t, e, n, f))) return b.push(r)\n })) {\n _ = !1;\n break\n }\n } else if (d !== x && !s(d, x, e, n, f)) {\n _ = !1;\n break\n }\n }\n return f.delete(t), f.delete(r), _\n }\n var o = e(56),\n i = e(59),\n a = e(60),\n u = 1,\n c = 2;\n t.exports = n\n}, function(t, r, e) {\n function n(t) {\n var r = -1,\n e = null == t ? 0 : t.length;\n for (this.__data__ = new o; ++r < e;) this.add(t[r])\n }\n var o = e(38),\n i = e(57),\n a = e(58);\n n.prototype.add = n.prototype.push = i, n.prototype.has = a, t.exports = n\n}, function(t, r) {\n function e(t) {\n return this.__data__.set(t, n), this\n }\n var n = \"__lodash_hash_undefined__\";\n t.exports = e\n}, function(t, r) {\n function e(t) {\n return this.__data__.has(t)\n }\n t.exports = e\n}, function(t, r) {\n function e(t, r) {\n for (var e = -1, n = null == t ? 0 : t.length; ++e < n;)\n if (r(t[e], e, t)) return !0;\n return !1\n }\n t.exports = e\n}, function(t, r) {\n function e(t, r) {\n return t.has(r)\n }\n t.exports = e\n}, function(t, r, e) {\n function n(t, r, e, n, o, m, A) {\n switch (e) {\n case j:\n if (t.byteLength != r.byteLength || t.byteOffset != r.byteOffset) return !1;\n t = t.buffer, r = r.buffer;\n case w:\n return !(t.byteLength != r.byteLength || !m(new i(t), new i(r)));\n case l:\n case v:\n case _:\n return a(+t, +r);\n case h:\n return t.name == r.name && t.message == r.message;\n case b:\n case x:\n return t == r + \"\";\n case y:\n var z = c;\n case d:\n var k = n & f;\n if (z || (z = s), t.size != r.size && !k) return !1;\n var S = A.get(t);\n if (S) return S == r;\n n |= p, A.set(t, r);\n var P = u(z(t), z(r), n, o, m, A);\n return A.delete(t), P;\n case g:\n if (O) return O.call(t) == O.call(r)\n }\n return !1\n }\n var o = e(28),\n i = e(62),\n a = e(14),\n u = e(55),\n c = e(63),\n s = e(64),\n f = 1,\n p = 2,\n l = \"[object Boolean]\",\n v = \"[object Date]\",\n h = \"[object Error]\",\n y = \"[object Map]\",\n _ = \"[object Number]\",\n b = \"[object RegExp]\",\n d = \"[object Set]\",\n x = \"[object String]\",\n g = \"[object Symbol]\",\n w = \"[object ArrayBuffer]\",\n j = \"[object DataView]\",\n m = o ? o.prototype : void 0,\n O = m ? m.valueOf : void 0;\n t.exports = n\n}, function(t, r, e) {\n var n = e(29),\n o = n.Uint8Array;\n t.exports = o\n}, function(t, r) {\n function e(t) {\n var r = -1,\n e = Array(t.size);\n return t.forEach(function(t, n) {\n e[++r] = [n, t]\n }), e\n }\n t.exports = e\n}, function(t, r) {\n function e(t) {\n var r = -1,\n e = Array(t.size);\n return t.forEach(function(t) {\n e[++r] = t\n }), e\n }\n t.exports = e\n}, function(t, r, e) {\n function n(t, r, e, n, a, c) {\n var s = e & i,\n f = o(t),\n p = f.length,\n l = o(r),\n v = l.length;\n if (p != v && !s) return !1;\n for (var h = p; h--;) {\n var y = f[h];\n if (!(s ? y in r : u.call(r, y))) return !1\n }\n var _ = c.get(t);\n if (_ && c.get(r)) return _ == r;\n var b = !0;\n c.set(t, r), c.set(r, t);\n for (var d = s; ++h < p;) {\n y = f[h];\n var x = t[y],\n g = r[y];\n if (n) var w = s ? n(g, x, y, r, t, c) : n(x, g, y, t, r, c);\n if (!(void 0 === w ? x === g || a(x, g, e, n, c) : w)) {\n b = !1;\n break\n }\n d || (d = \"constructor\" == y)\n }\n if (b && !d) {\n var j = t.constructor,\n m = r.constructor;\n j != m && \"constructor\" in t && \"constructor\" in r && !(\"function\" == typeof j && j instanceof j && \"function\" == typeof m && m instanceof m) && (b = !1)\n }\n return c.delete(t), c.delete(r), b\n }\n var o = e(66),\n i = 1,\n a = Object.prototype,\n u = a.hasOwnProperty;\n t.exports = n\n}, function(t, r, e) {\n function n(t) {\n return o(t, a, i)\n }\n var o = e(67),\n i = e(70),\n a = e(73);\n t.exports = n\n}, function(t, r, e) {\n function n(t, r, e) {\n var n = r(t);\n return i(t) ? n : o(n, e(t))\n }\n var o = e(68),\n i = e(69);\n t.exports = n\n}, function(t, r) {\n function e(t, r) {\n for (var e = -1, n = r.length, o = t.length; ++e < n;) t[o + e] = r[e];\n return t\n }\n t.exports = e\n}, function(t, r) {\n var e = Array.isArray;\n t.exports = e\n}, function(t, r, e) {\n var n = e(71),\n o = e(72),\n i = Object.prototype,\n a = i.propertyIsEnumerable,\n u = Object.getOwnPropertySymbols,\n c = u ? function(t) {\n return null == t ? [] : (t = Object(t), n(u(t), function(r) {\n return a.call(t, r)\n }))\n } : o;\n t.exports = c\n}, function(t, r) {\n function e(t, r) {\n for (var e = -1, n = null == t ? 0 : t.length, o = 0, i = []; ++e < n;) {\n var a = t[e];\n r(a, e, t) && (i[o++] = a)\n }\n return i\n }\n t.exports = e\n}, function(t, r) {\n function e() {\n return []\n }\n t.exports = e\n}, function(t, r, e) {\n function n(t) {\n return a(t) ? o(t) : i(t)\n }\n var o = e(74),\n i = e(88),\n a = e(92);\n t.exports = n\n}, function(t, r, e) {\n function n(t, r) {\n var e = a(t),\n n = !e && i(t),\n f = !e && !n && u(t),\n l = !e && !n && !f && s(t),\n v = e || n || f || l,\n h = v ? o(t.length, String) : [],\n y = h.length;\n for (var _ in t) !r && !p.call(t, _) || v && (\"length\" == _ || f && (\"offset\" == _ || \"parent\" == _) || l && (\"buffer\" == _ || \"byteLength\" == _ || \"byteOffset\" == _) || c(_, y)) || h.push(_);\n return h\n }\n var o = e(75),\n i = e(76),\n a = e(69),\n u = e(79),\n c = e(82),\n s = e(83),\n f = Object.prototype,\n p = f.hasOwnProperty;\n t.exports = n\n}, function(t, r) {\n function e(t, r) {\n for (var e = -1, n = Array(t); ++e < t;) n[e] = r(e);\n return n\n }\n t.exports = e\n}, function(t, r, e) {\n var n = e(77),\n o = e(78),\n i = Object.prototype,\n a = i.hasOwnProperty,\n u = i.propertyIsEnumerable,\n c = n(function() {\n return arguments\n }()) ? n : function(t) {\n return o(t) && a.call(t, \"callee\") && !u.call(t, \"callee\")\n };\n t.exports = c\n}, function(t, r, e) {\n function n(t) {\n return i(t) && o(t) == a\n }\n var o = e(27),\n i = e(78),\n a = \"[object Arguments]\";\n t.exports = n\n}, function(t, r) {\n function e(t) {\n return null != t && \"object\" == typeof t\n }\n t.exports = e\n}, function(t, r, e) {\n (function(t) {\n var n = e(29),\n o = e(81),\n i = \"object\" == typeof r && r && !r.nodeType && r,\n a = i && \"object\" == typeof t && t && !t.nodeType && t,\n u = a && a.exports === i,\n c = u ? n.Buffer : void 0,\n s = c ? c.isBuffer : void 0,\n f = s || o;\n t.exports = f\n }).call(r, e(80)(t))\n}, function(t, r) {\n t.exports = function(t) {\n return t.webpackPolyfill || (t.deprecate = function() {}, t.paths = [], t.children = [], t.webpackPolyfill = 1), t\n }\n}, function(t, r) {\n function e() {\n return !1\n }\n t.exports = e\n}, function(t, r) {\n function e(t, r) {\n return r = null == r ? n : r, !!r && (\"number\" == typeof t || o.test(t)) && t > -1 && t % 1 == 0 && t < r\n }\n var n = 9007199254740991,\n o = /^(?:0|[1-9]\\d*)$/;\n t.exports = e\n}, function(t, r, e) {\n var n = e(84),\n o = e(86),\n i = e(87),\n a = i && i.isTypedArray,\n u = a ? o(a) : n;\n t.exports = u\n}, function(t, r, e) {\n function n(t) {\n return a(t) && i(t.length) && !!T[o(t)]\n }\n var o = e(27),\n i = e(85),\n a = e(78),\n u = \"[object Arguments]\",\n c = \"[object Array]\",\n s = \"[object Boolean]\",\n f = \"[object Date]\",\n p = \"[object Error]\",\n l = \"[object Function]\",\n v = \"[object Map]\",\n h = \"[object Number]\",\n y = \"[object Object]\",\n _ = \"[object RegExp]\",\n b = \"[object Set]\",\n d = \"[object String]\",\n x = \"[object WeakMap]\",\n g = \"[object ArrayBuffer]\",\n w = \"[object DataView]\",\n j = \"[object Float32Array]\",\n m = \"[object Float64Array]\",\n O = \"[object Int8Array]\",\n A = \"[object Int16Array]\",\n z = \"[object Int32Array]\",\n k = \"[object Uint8Array]\",\n S = \"[object Uint8ClampedArray]\",\n P = \"[object Uint16Array]\",\n B = \"[object Uint32Array]\",\n T = {};\n T[j] = T[m] = T[O] = T[A] = T[z] = T[k] = T[S] = T[P] = T[B] = !0, T[u] = T[c] = T[g] = T[s] = T[w] = T[f] = T[p] = T[l] = T[v] = T[h] = T[y] = T[_] = T[b] = T[d] = T[x] = !1, t.exports = n\n}, function(t, r) {\n function e(t) {\n return \"number\" == typeof t && t > -1 && t % 1 == 0 && t <= n\n }\n var n = 9007199254740991;\n t.exports = e\n}, function(t, r) {\n function e(t) {\n return function(r) {\n return t(r)\n }\n }\n t.exports = e\n}, function(t, r, e) {\n (function(t) {\n var n = e(30),\n o = \"object\" == typeof r && r && !r.nodeType && r,\n i = o && \"object\" == typeof t && t && !t.nodeType && t,\n a = i && i.exports === o,\n u = a && n.process,\n c = function() {\n try {\n return u && u.binding && u.binding(\"util\")\n } catch (t) {}\n }();\n t.exports = c\n }).call(r, e(80)(t))\n}, function(t, r, e) {\n function n(t) {\n if (!o(t)) return i(t);\n var r = [];\n for (var e in Object(t)) u.call(t, e) && \"constructor\" != e && r.push(e);\n return r\n }\n var o = e(89),\n i = e(90),\n a = Object.prototype,\n u = a.hasOwnProperty;\n t.exports = n\n}, function(t, r) {\n function e(t) {\n var r = t && t.constructor,\n e = \"function\" == typeof r && r.prototype || n;\n return t === e\n }\n var n = Object.prototype;\n t.exports = e\n}, function(t, r, e) {\n var n = e(91),\n o = n(Object.keys, Object);\n t.exports = o\n}, function(t, r) {\n function e(t, r) {\n return function(e) {\n return t(r(e))\n }\n }\n t.exports = e\n}, function(t, r, e) {\n function n(t) {\n return null != t && i(t.length) && !o(t)\n }\n var o = e(26),\n i = e(85);\n t.exports = n\n}, function(t, r, e) {\n var n = e(94),\n o = e(23),\n i = e(95),\n a = e(96),\n u = e(97),\n c = e(27),\n s = e(36),\n f = \"[object Map]\",\n p = \"[object Object]\",\n l = \"[object Promise]\",\n v = \"[object Set]\",\n h = \"[object WeakMap]\",\n y = \"[object DataView]\",\n _ = s(n),\n b = s(o),\n d = s(i),\n x = s(a),\n g = s(u),\n w = c;\n (n && w(new n(new ArrayBuffer(1))) != y || o && w(new o) != f || i && w(i.resolve()) != l || a && w(new a) != v || u && w(new u) != h) && (w = function(t) {\n var r = c(t),\n e = r == p ? t.constructor : void 0,\n n = e ? s(e) : \"\";\n if (n) switch (n) {\n case _:\n return y;\n case b:\n return f;\n case d:\n return l;\n case x:\n return v;\n case g:\n return h\n }\n return r\n }), t.exports = w\n}, function(t, r, e) {\n var n = e(24),\n o = e(29),\n i = n(o, \"DataView\");\n t.exports = i\n}, function(t, r, e) {\n var n = e(24),\n o = e(29),\n i = n(o, \"Promise\");\n t.exports = i\n}, function(t, r, e) {\n var n = e(24),\n o = e(29),\n i = n(o, \"Set\");\n t.exports = i\n}, function(t, r, e) {\n var n = e(24),\n o = e(29),\n i = n(o, \"WeakMap\");\n t.exports = i\n}, function(t, r, e) {\n function n(t) {\n for (var r = i(t), e = r.length; e--;) {\n var n = r[e],\n a = t[n];\n r[e] = [n, a, o(a)]\n }\n return r\n }\n var o = e(99),\n i = e(73);\n t.exports = n\n}, function(t, r, e) {\n function n(t) {\n return t === t && !o(t)\n }\n var o = e(33);\n t.exports = n\n}, function(t, r) {\n function e(t, r) {\n return function(e) {\n return null != e && (e[t] === r && (void 0 !== r || t in Object(e)))\n }\n }\n t.exports = e\n}, function(t, r, e) {\n function n(t, r) {\n return u(t) && c(r) ? s(f(t), r) : function(e) {\n var n = i(e, t);\n return void 0 === n && n === r ? a(e, t) : o(r, n, p | l)\n }\n }\n var o = e(53),\n i = e(102),\n a = e(114),\n u = e(105),\n c = e(99),\n s = e(100),\n f = e(113),\n p = 1,\n l = 2;\n t.exports = n\n}, function(t, r, e) {\n function n(t, r, e) {\n var n = null == t ? void 0 : o(t, r);\n return void 0 === n ? e : n\n }\n var o = e(103);\n t.exports = n\n}, function(t, r, e) {\n function n(t, r) {\n r = o(r, t);\n for (var e = 0, n = r.length; null != t && e < n;) t = t[i(r[e++])];\n return e && e == n ? t : void 0\n }\n var o = e(104),\n i = e(113);\n t.exports = n\n}, function(t, r, e) {\n function n(t, r) {\n return o(t) ? t : i(t, r) ? [t] : a(u(t))\n }\n var o = e(69),\n i = e(105),\n a = e(107),\n u = e(110);\n t.exports = n\n}, function(t, r, e) {\n function n(t, r) {\n if (o(t)) return !1;\n var e = typeof t;\n return !(\"number\" != e && \"symbol\" != e && \"boolean\" != e && null != t && !i(t)) || (u.test(t) || !a.test(t) || null != r && t in Object(r))\n }\n var o = e(69),\n i = e(106),\n a = /\\.|\\[(?:[^[\\]]*|([\"'])(?:(?!\\1)[^\\\\]|\\\\.)*?\\1)\\]/,\n u = /^\\w*$/;\n t.exports = n\n}, function(t, r, e) {\n function n(t) {\n return \"symbol\" == typeof t || i(t) && o(t) == a\n }\n var o = e(27),\n i = e(78),\n a = \"[object Symbol]\";\n t.exports = n\n}, function(t, r, e) {\n var n = e(108),\n o = /^\\./,\n i = /[^.[\\]]+|\\[(?:(-?\\d+(?:\\.\\d+)?)|([\"'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2)\\]|(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))/g,\n a = /\\\\(\\\\)?/g,\n u = n(function(t) {\n var r = [];\n return o.test(t) && r.push(\"\"), t.replace(i, function(t, e, n, o) {\n r.push(n ? o.replace(a, \"$1\") : e || t)\n }), r\n });\n t.exports = u\n}, function(t, r, e) {\n function n(t) {\n var r = o(t, function(t) {\n return e.size === i && e.clear(), t\n }),\n e = r.cache;\n return r\n }\n var o = e(109),\n i = 500;\n t.exports = n\n}, function(t, r, e) {\n function n(t, r) {\n if (\"function\" != typeof t || null != r && \"function\" != typeof r) throw new TypeError(i);\n var e = function() {\n var n = arguments,\n o = r ? r.apply(this, n) : n[0],\n i = e.cache;\n if (i.has(o)) return i.get(o);\n var a = t.apply(this, n);\n return e.cache = i.set(o, a) || i, a\n };\n return e.cache = new(n.Cache || o), e\n }\n var o = e(38),\n i = \"Expected a function\";\n n.Cache = o, t.exports = n\n}, function(t, r, e) {\n function n(t) {\n return null == t ? \"\" : o(t)\n }\n var o = e(111);\n t.exports = n\n}, function(t, r, e) {\n function n(t) {\n if (\"string\" == typeof t) return t;\n if (a(t)) return i(t, n) + \"\";\n if (u(t)) return f ? f.call(t) : \"\";\n var r = t + \"\";\n return \"0\" == r && 1 / t == -c ? \"-0\" : r\n }\n var o = e(28),\n i = e(112),\n a = e(69),\n u = e(106),\n c = 1 / 0,\n s = o ? o.prototype : void 0,\n f = s ? s.toString : void 0;\n t.exports = n\n}, function(t, r) {\n function e(t, r) {\n for (var e = -1, n = null == t ? 0 : t.length, o = Array(n); ++e < n;) o[e] = r(t[e], e, t);\n return o\n }\n t.exports = e\n}, function(t, r, e) {\n function n(t) {\n if (\"string\" == typeof t || o(t)) return t;\n var r = t + \"\";\n return \"0\" == r && 1 / t == -i ? \"-0\" : r\n }\n var o = e(106),\n i = 1 / 0;\n t.exports = n\n}, function(t, r, e) {\n function n(t, r) {\n return null != t && i(t, r, o)\n }\n var o = e(115),\n i = e(116);\n t.exports = n\n}, function(t, r) {\n function e(t, r) {\n return null != t && r in Object(t)\n }\n t.exports = e\n}, function(t, r, e) {\n function n(t, r, e) {\n r = o(r, t);\n for (var n = -1, f = r.length, p = !1; ++n < f;) {\n var l = s(r[n]);\n if (!(p = null != t && e(t, l))) break;\n t = t[l]\n }\n return p || ++n != f ? p : (f = null == t ? 0 : t.length, !!f && c(f) && u(l, f) && (a(t) || i(t)))\n }\n var o = e(104),\n i = e(76),\n a = e(69),\n u = e(82),\n c = e(85),\n s = e(113);\n t.exports = n\n}, function(t, r) {\n function e(t) {\n return t\n }\n t.exports = e\n}, function(t, r, e) {\n function n(t) {\n return a(t) ? o(u(t)) : i(t)\n }\n var o = e(119),\n i = e(120),\n a = e(105),\n u = e(113);\n t.exports = n\n}, function(t, r) {\n function e(t) {\n return function(r) {\n return null == r ? void 0 : r[t]\n }\n }\n t.exports = e\n}, function(t, r, e) {\n function n(t) {\n return function(r) {\n return o(r, t)\n }\n }\n var o = e(103);\n t.exports = n\n}, function(t, r, e) {\n function n(t, r, e) {\n var n = null == t ? 0 : t.length;\n if (!n) return -1;\n var c = null == e ? 0 : a(e);\n return c < 0 && (c = u(n + c, 0)), o(t, i(r, 3), c)\n }\n var o = e(122),\n i = e(6),\n a = e(123),\n u = Math.max;\n t.exports = n\n}, function(t, r) {\n function e(t, r, e, n) {\n for (var o = t.length, i = e + (n ? 1 : -1); n ? i-- : ++i < o;)\n if (r(t[i], i, t)) return i;\n return -1\n }\n t.exports = e\n}, function(t, r, e) {\n function n(t) {\n var r = o(t),\n e = r % 1;\n return r === r ? e ? r - e : r : 0\n }\n var o = e(124);\n t.exports = n\n}, function(t, r, e) {\n function n(t) {\n if (!t) return 0 === t ? t : 0;\n if (t = o(t), t === i || t === -i) {\n var r = t < 0 ? -1 : 1;\n return r * a\n }\n return t === t ? t : 0\n }\n var o = e(125),\n i = 1 / 0,\n a = 1.7976931348623157e308;\n t.exports = n\n}, function(t, r, e) {\n function n(t) {\n if (\"number\" == typeof t) return t;\n if (i(t)) return a;\n if (o(t)) {\n var r = \"function\" == typeof t.valueOf ? t.valueOf() : t;\n t = o(r) ? r + \"\" : r\n }\n if (\"string\" != typeof t) return 0 === t ? t : +t;\n t = t.replace(u, \"\");\n var e = s.test(t);\n return e || f.test(t) ? p(t.slice(2), e ? 2 : 8) : c.test(t) ? a : +t\n }\n var o = e(33),\n i = e(106),\n a = NaN,\n u = /^\\s+|\\s+$/g,\n c = /^[-+]0x[0-9a-f]+$/i,\n s = /^0b[01]+$/i,\n f = /^0o[0-7]+$/i,\n p = parseInt;\n t.exports = n\n}, function(t, r) {\n \"use strict\";\n\n function e() {\n return window.crypto.subtle || window.crypto.webkitSubtle\n }\n r.subtle = e\n}, function(t, r) {\n \"use strict\";\n\n function e(t, r) {\n var n;\n return n = Array.isArray(t) ? [] : {}, r.push(t), Object.keys(t).forEach(function(o) {\n var i = t[o];\n if (\"function\" != typeof i) return i && \"object\" == typeof i ? r.indexOf(t[o]) === -1 ? void(n[o] = e(t[o], r.slice(0))) : void(n[o] = \"[Circular]\") : void(n[o] = i)\n }), \"string\" == typeof t.name && (n.name = t.name), \"string\" == typeof t.message && (n.message = t.message), \"string\" == typeof t.stack && (n.stack = t.stack), n\n }\n t.exports = function(t) {\n return \"object\" == typeof t ? e(t, []) : \"function\" == typeof t ? \"[Function: \" + (t.name || \"anonymous\") + \"]\" : t\n }\n}]);\n"; +export default _default; diff --git a/examples/react-native/src/webview-crypto/webViewWorkerString.js b/examples/react-native/src/webview-crypto/webViewWorkerString.js new file mode 100644 index 00000000..6da7a411 --- /dev/null +++ b/examples/react-native/src/webview-crypto/webViewWorkerString.js @@ -0,0 +1,2135 @@ +export const base64InjString = ` +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + ? module.exports = factory(global) + : typeof define === 'function' && define.amd + ? define(factory) : factory(global) +}(( + typeof self !== 'undefined' ? self + : typeof window !== 'undefined' ? window + : typeof global !== 'undefined' ? global +: this +), function(global) { + 'use strict'; + // existing version for noConflict() + var _Base64 = global.Base64; + var version = "2.4.9"; + // if node.js and NOT React Native, we use Buffer + var buffer; + if (typeof module !== 'undefined' && module.exports) { + try { + buffer = eval("require('buffer').Buffer"); + } catch (err) { + buffer = undefined; + } + } + // constants + var b64chars + = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + var b64tab = function(bin) { + var t = {}; + for (var i = 0, l = bin.length; i < l; i++) t[bin.charAt(i)] = i; + return t; + }(b64chars); + var fromCharCode = String.fromCharCode; + // encoder stuff + var cb_utob = function(c) { + if (c.length < 2) { + var cc = c.charCodeAt(0); + return cc < 0x80 ? c + : cc < 0x800 ? (fromCharCode(0xc0 | (cc >>> 6)) + + fromCharCode(0x80 | (cc & 0x3f))) + : (fromCharCode(0xe0 | ((cc >>> 12) & 0x0f)) + + fromCharCode(0x80 | ((cc >>> 6) & 0x3f)) + + fromCharCode(0x80 | ( cc & 0x3f))); + } else { + var cc = 0x10000 + + (c.charCodeAt(0) - 0xD800) * 0x400 + + (c.charCodeAt(1) - 0xDC00); + return (fromCharCode(0xf0 | ((cc >>> 18) & 0x07)) + + fromCharCode(0x80 | ((cc >>> 12) & 0x3f)) + + fromCharCode(0x80 | ((cc >>> 6) & 0x3f)) + + fromCharCode(0x80 | ( cc & 0x3f))); + } + }; + var re_utob = /[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g; + var utob = function(u) { + return u.replace(re_utob, cb_utob); + }; + var cb_encode = function(ccc) { + var padlen = [0, 2, 1][ccc.length % 3], + ord = ccc.charCodeAt(0) << 16 + | ((ccc.length > 1 ? ccc.charCodeAt(1) : 0) << 8) + | ((ccc.length > 2 ? ccc.charCodeAt(2) : 0)), + chars = [ + b64chars.charAt( ord >>> 18), + b64chars.charAt((ord >>> 12) & 63), + padlen >= 2 ? '=' : b64chars.charAt((ord >>> 6) & 63), + padlen >= 1 ? '=' : b64chars.charAt(ord & 63) + ]; + return chars.join(''); + }; + var btoa = global.btoa ? function(b) { + return global.btoa(b); + } : function(b) { + return b.replace(/[\s\S]{1,3}/g, cb_encode); + }; + var _encode = buffer ? + buffer.from && Uint8Array && buffer.from !== Uint8Array.from + ? function (u) { + return (u.constructor === buffer.constructor ? u : buffer.from(u)) + .toString('base64') + } + : function (u) { + return (u.constructor === buffer.constructor ? u : new buffer(u)) + .toString('base64') + } + : function (u) { return btoa(utob(u)) } + ; + var encode = function(u, urisafe) { + return !urisafe + ? _encode(String(u)) + : _encode(String(u)).replace(/[+\/]/g, function(m0) { + return m0 == '+' ? '-' : '_'; + }).replace(/=/g, ''); + }; + var encodeURI = function(u) { return encode(u, true) }; + // decoder stuff + var re_btou = new RegExp([ + '[\xC0-\xDF][\x80-\xBF]', + '[\xE0-\xEF][\x80-\xBF]{2}', + '[\xF0-\xF7][\x80-\xBF]{3}' + ].join('|'), 'g'); + var cb_btou = function(cccc) { + switch(cccc.length) { + case 4: + var cp = ((0x07 & cccc.charCodeAt(0)) << 18) + | ((0x3f & cccc.charCodeAt(1)) << 12) + | ((0x3f & cccc.charCodeAt(2)) << 6) + | (0x3f & cccc.charCodeAt(3)), + offset = cp - 0x10000; + return (fromCharCode((offset >>> 10) + 0xD800) + + fromCharCode((offset & 0x3FF) + 0xDC00)); + case 3: + return fromCharCode( + ((0x0f & cccc.charCodeAt(0)) << 12) + | ((0x3f & cccc.charCodeAt(1)) << 6) + | (0x3f & cccc.charCodeAt(2)) + ); + default: + return fromCharCode( + ((0x1f & cccc.charCodeAt(0)) << 6) + | (0x3f & cccc.charCodeAt(1)) + ); + } + }; + var btou = function(b) { + return b.replace(re_btou, cb_btou); + }; + var cb_decode = function(cccc) { + var len = cccc.length, + padlen = len % 4, + n = (len > 0 ? b64tab[cccc.charAt(0)] << 18 : 0) + | (len > 1 ? b64tab[cccc.charAt(1)] << 12 : 0) + | (len > 2 ? b64tab[cccc.charAt(2)] << 6 : 0) + | (len > 3 ? b64tab[cccc.charAt(3)] : 0), + chars = [ + fromCharCode( n >>> 16), + fromCharCode((n >>> 8) & 0xff), + fromCharCode( n & 0xff) + ]; + chars.length -= [0, 0, 2, 1][padlen]; + return chars.join(''); + }; + var atob = global.atob ? function(a) { + return global.atob(a); + } : function(a){ + return a.replace(/[\s\S]{1,4}/g, cb_decode); + }; + var _decode = buffer ? + buffer.from && Uint8Array && buffer.from !== Uint8Array.from + ? function(a) { + return (a.constructor === buffer.constructor + ? a : buffer.from(a, 'base64')).toString(); + } + : function(a) { + return (a.constructor === buffer.constructor + ? a : new buffer(a, 'base64')).toString(); + } + : function(a) { return btou(atob(a)) }; + var decode = function(a){ + return _decode( + String(a).replace(/[-_]/g, function(m0) { return m0 == '-' ? '+' : '/' }) + .replace(/[^A-Za-z0-9\+\/]/g, '') + ); + }; + var noConflict = function() { + var Base64 = global.Base64; + global.Base64 = _Base64; + return Base64; + }; + // export Base64 + global.Base64 = { + VERSION: version, + atob: atob, + btoa: btoa, + fromBase64: decode, + toBase64: encode, + utob: utob, + encode: encode, + encodeURI: encodeURI, + btou: btou, + decode: decode, + noConflict: noConflict, + __buffer__: buffer + }; + // if ES5 is available, make Base64.extendString() available + if (typeof Object.defineProperty === 'function') { + var noEnum = function(v){ + return {value:v,enumerable:false,writable:true,configurable:true}; + }; + global.Base64.extendString = function () { + Object.defineProperty( + String.prototype, 'fromBase64', noEnum(function () { + return decode(this) + })); + Object.defineProperty( + String.prototype, 'toBase64', noEnum(function (urisafe) { + return encode(this, urisafe) + })); + Object.defineProperty( + String.prototype, 'toBase64URI', noEnum(function () { + return encode(this, true) + })); + }; + } + return {Base64: global.Base64} +})); +`; +export default ` +var WebViewWorker = function(t) { + function r(n) { + if (e[n]) return e[n].exports; + var o = e[n] = { + exports: {}, + id: n, + loaded: !1 + }; + return t[n].call(o.exports, o, o.exports, r), o.loaded = !0, o.exports + } + var e = {}; + return r.m = t, r.c = e, r.p = "", r(0) +}([function(t, r, e) { + t.exports = e(1) +}, function(t, r, e) { + "use strict"; + var n = this && this.__awaiter || function(t, r, e, n) { + return new(e || (e = Promise))(function(o, i) { + function a(t) { + try { + c(n.next(t)) + } catch (t) { + i(t) + } + } + + function u(t) { + try { + c(n.throw(t)) + } catch (t) { + i(t) + } + } + + function c(t) { + t.done ? o(t.value) : new e(function(r) { + r(t.value) + }).then(a, u) + } + c((n = n.apply(t, r)).next()) + }) + }, + o = this && this.__generator || function(t, r) { + function e(t) { + return function(r) { + return n([t, r]) + } + } + + function n(e) { + if (o) throw new TypeError("Generator is already executing."); + for (; u;) try { + if (o = 1, i && (a = i[2 & e[0] ? "return" : e[0] ? "throw" : "next"]) && !(a = a.call(i, e[1])).done) return a; + switch (i = 0, a && (e = [0, a.value]), e[0]) { + case 0: + case 1: + a = e; + break; + case 4: + return u.label++, { + value: e[1], + done: !1 + }; + case 5: + u.label++, i = e[1], e = [0]; + continue; + case 7: + e = u.ops.pop(), u.trys.pop(); + continue; + default: + if (a = u.trys, !(a = a.length > 0 && a[a.length - 1]) && (6 === e[0] || 2 === e[0])) { + u = 0; + continue + } + if (3 === e[0] && (!a || e[1] > a[0] && e[1] < a[3])) { + u.label = e[1]; + break + } + if (6 === e[0] && u.label < a[1]) { + u.label = a[1], a = e; + break + } + if (a && u.label < a[2]) { + u.label = a[2], u.ops.push(e); + break + } + a[2] && u.ops.pop(), u.trys.pop(); + continue + } + e = r.call(t, u) + } catch (t) { + e = [6, t], i = 0 + } finally { + o = a = 0 + } + if (5 & e[0]) throw e[1]; + return { + value: e[0] ? e[1] : void 0, + done: !0 + } + } + var o, i, a, u = { + label: 0, + sent: function() { + if (1 & a[0]) throw a[1]; + return a[1] + }, + trys: [], + ops: [] + }; + return { + next: e(0), + throw: e(1), + return: e(2) + } + }, + i = e(2), + a = e(126), + u = e(127), + c = function() { + function t(t) { + this.sendToMain = t, t("We are ready!") + } + return t.prototype.onMainMessage = function(t) { + return n(this, void 0, void 0, function() { + var r, e, n, c, s, f, p, l; + return o(this, function(o) { + switch (o.label) { + case 0: + return o.trys.push([0, 2, , 4]), [4, i.parse(t)]; + case 1: + return l = o.sent(), r = l.id, e = l.method, n = l.args, [3, 4]; + case 2: + return c = o.sent(), [4, this.send({ + reason: "Couldn't parse data: " + c + })]; + case 3: + return o.sent(), [2]; + case 4: + return o.trys.push([4, 8, , 10]), "getRandomValues" !== e ? [3, 5] : (s = crypto.getRandomValues(n[0]), [3, 7]); + case 5: + // console.log(f, n) + return f = e.split(".")[1], [4, a.subtle()[f].apply(a.subtle(), n)]; + case 6: + s = o.sent(), "importKey" === f && (s._import = { + format: n[0], + keyData: n[1] + }), o.label = 7; + case 7: + return [3, 10]; + case 8: + return p = o.sent(), [4, this.send({ + id: r, + reason: u(p) + })]; + case 9: + return o.sent(), [2]; + case 10: + return [4, this.send({ + id: r, + value: s + })]; + case 11: + return o.sent(), [2] + } + }) + }) + }, t.prototype.send = function(t) { + return n(this, void 0, void 0, function() { + var r, e, n; + return o(this, function(o) { + switch (o.label) { + case 0: + return o.trys.push([0, 2, , 3]), [4, i.stringify(t)]; + case 1: + return r = o.sent(), [3, 3]; + case 2: + return e = o.sent(), n = { + id: t.id, + reason: "stringify error " + e + }, this.sendToMain(JSON.stringify(n)), [2]; + case 3: + return this.sendToMain(r), [2] + } + }) + }) + }, t + }(); + t.exports = c +}, function(module, exports, __webpack_require__) { + "use strict"; + + function parse(t) { + return __awaiter(this, void 0, void 0, function() { + var r, e; + return __generator(this, function(n) { + switch (n.label) { + case 0: + console.log('*** decoding', t); + return r = unescape(t), e = JSON.parse(r), [4, asyncSerialize_1.fromObjects(serializers(!0), e)]; + case 1: + return [2, n.sent()] + } + }) + }) + } + + function stringify(t, r) { + return void 0 === r && (r = !0), __awaiter(this, void 0, void 0, function() { + var e, n; + return __generator(this, function(o) { + switch (o.label) { + case 0: + return [4, asyncSerialize_1.toObjects(serializers(r), t)]; + case 1: + console.log('*** encoding', n); + return e = o.sent(), n = JSON.stringify(e), [2, escape(n)] + } + }) + }) + } + + function serializers(t) { + return [ArrayBufferSerializer, ArrayBufferViewSerializer(t), CryptoKeySerializer] + } + + function isArrayBufferViewWithPromise(t) { + return t.hasOwnProperty("_promise") + } + + function arrayBufferViewName(t) { + return t instanceof Int8Array ? "Int8Array" : t instanceof Uint8Array ? "Uint8Array" : t instanceof Uint8ClampedArray ? "Uint8ClampedArray" : t instanceof Int16Array ? "Int16Array" : t instanceof Uint16Array ? "Uint16Array" : t instanceof Int32Array ? "Int32Array" : t instanceof Uint32Array ? "Uint32Array" : t instanceof Float32Array ? "Float32Array" : t instanceof Float64Array ? "Float64Array" : t instanceof DataView ? "DataView" : void 0 + } + + function ArrayBufferViewSerializer(waitForPromise) { + var _this = this; + return { + id: "ArrayBufferView", + isType: ArrayBuffer.isView, + toObject: function(t) { + return __awaiter(_this, void 0, void 0, function() { + return __generator(this, function(r) { + switch (r.label) { + case 0: + return waitForPromise && isArrayBufferViewWithPromise(t) ? [4, t._promise] : [3, 2]; + case 1: + r.sent(), r.label = 2; + case 2: + return [2, { + name: arrayBufferViewName(t), + buffer: t.buffer + }] + } + }) + }) + }, + fromObject: function(abvs) { + return __awaiter(_this, void 0, void 0, function() { + return __generator(this, function(_a) { + return [2, eval("new " + abvs.name + "(abvs.buffer)")] + }) + }) + } + } + } + + function hasData(t) { + return void 0 !== t._import + } + var __assign = this && this.__assign || Object.assign || function(t) { + for (var r, e = 1, n = arguments.length; e < n; e++) { + r = arguments[e]; + for (var o in r) Object.prototype.hasOwnProperty.call(r, o) && (t[o] = r[o]) + } + return t + }, + __awaiter = this && this.__awaiter || function(t, r, e, n) { + return new(e || (e = Promise))(function(o, i) { + function a(t) { + try { + c(n.next(t)) + } catch (t) { + i(t) + } + } + + function u(t) { + try { + c(n.throw(t)) + } catch (t) { + i(t) + } + } + + function c(t) { + t.done ? o(t.value) : new e(function(r) { + r(t.value) + }).then(a, u) + } + c((n = n.apply(t, r)).next()) + }) + }, + __generator = this && this.__generator || function(t, r) { + function e(t) { + return function(r) { + return n([t, r]) + } + } + + function n(e) { + if (o) throw new TypeError("Generator is already executing."); + for (; u;) try { + if (o = 1, i && (a = i[2 & e[0] ? "return" : e[0] ? "throw" : "next"]) && !(a = a.call(i, e[1])).done) return a; + switch (i = 0, a && (e = [0, a.value]), e[0]) { + case 0: + case 1: + a = e; + break; + case 4: + return u.label++, { + value: e[1], + done: !1 + }; + case 5: + u.label++, i = e[1], e = [0]; + continue; + case 7: + e = u.ops.pop(), u.trys.pop(); + continue; + default: + if (a = u.trys, !(a = a.length > 0 && a[a.length - 1]) && (6 === e[0] || 2 === e[0])) { + u = 0; + continue + } + if (3 === e[0] && (!a || e[1] > a[0] && e[1] < a[3])) { + u.label = e[1]; + break + } + if (6 === e[0] && u.label < a[1]) { + u.label = a[1], a = e; + break + } + if (a && u.label < a[2]) { + u.label = a[2], u.ops.push(e); + break + } + a[2] && u.ops.pop(), u.trys.pop(); + continue + } + e = r.call(t, u) + } catch (t) { + e = [6, t], i = 0 + } finally { + o = a = 0 + } + if (5 & e[0]) throw e[1]; + return { + value: e[0] ? e[1] : void 0, + done: !0 + } + } + var o, i, a, u = { + label: 0, + sent: function() { + if (1 & a[0]) throw a[1]; + return a[1] + }, + trys: [], + ops: [] + }; + return { + next: e(0), + throw: e(1), + return: e(2) + } + }, + _this = this, + asyncSerialize_1 = __webpack_require__(3), + compat_1 = __webpack_require__(126); + exports.parse = parse, exports.stringify = stringify; + var ArrayBufferSerializer = { + id: "ArrayBuffer", + isType: function(t) { + return t instanceof ArrayBuffer + }, + toObject: function(t) { + return __awaiter(_this, void 0, void 0, function() { + return __generator(this, function(r) { + return [2, String.fromCharCode.apply(null, new Int8Array(t))] + }) + }) + }, + fromObject: function(t) { + return __awaiter(_this, void 0, void 0, function() { + var r, e, n, o; + return __generator(this, function(i) { + for (r = new ArrayBuffer(t.length), e = new Int8Array(r), n = 0, o = t.length; n < o; n++) e[n] = t.charCodeAt(n); + return [2, r] + }) + }) + } + }, + CryptoKeySerializer = { + id: "CryptoKey", + isType: function(t) { + var r = t.toLocaleString(), + e = "[object CryptoKey]" === r || "[object Key]" === r, + n = t._import && !t.serialized; + return e || n + }, + toObject: function(t) { + return __awaiter(_this, void 0, void 0, function() { + var r; + return __generator(this, function(e) { + switch (e.label) { + case 0: + return hasData(t) ? [2, { + serialized: !0, + _import: t._import, + type: t.type, + extractable: t.extractable, + algorithm: t.algorithm, + usages: t.usages + }] : [4, compat_1.subtle().exportKey("jwk", t)]; + case 1: + return r = e.sent(), [2, { + _import: { + format: "jwk", + keyData: r + }, + serialized: !0, + algorithm: t.algorithm, + extractable: t.extractable, + usages: t.usages, + type: t.type + }] + } + }) + }) + }, + fromObject: function(t) { + return __awaiter(_this, void 0, void 0, function() { + var r; + return __generator(this, function(e) { + switch (e.label) { + case 0: + return crypto.fake ? (r = __assign({}, t), delete r.serialized, [2, r]) : [4, compat_1.subtle().importKey(t._import.format, t._import.keyData, t.algorithm, t.extractable, t.usages)]; + case 1: + return [2, e.sent()] + } + }) + }) + } + } +}, function(t, r, e) { + "use strict"; + + function n(t) { + return t.hasOwnProperty("__serializer_id") + } + + function o(t, r) { + return a(this, void 0, void 0, function() { + var e, n, i, a, s, f, p, l, v, h, y; + return u(this, function(u) { + switch (u.label) { + case 0: + return "object" != typeof r ? [2, r] : (e = c(t, function(t) { + return t.isType(r) + }), e ? e.toObject ? [4, e.toObject(r)] : [3, 2] : [3, 5]); + case 1: + return i = u.sent(), [3, 3]; + case 2: + i = r, u.label = 3; + case 3: + return n = i, a = { + __serializer_id: e.id + }, [4, o(t, n)]; + case 4: + return [2, (a.value = u.sent(), a)]; + case 5: + s = r instanceof Array ? [] : {}, f = []; + for (p in r) f.push(p); + l = 0, u.label = 6; + case 6: + return l < f.length ? (v = f[l], h = s, y = v, [4, o(t, r[v])]) : [3, 9]; + case 7: + h[y] = u.sent(), u.label = 8; + case 8: + return l++, [3, 6]; + case 9: + return [2, s] + } + }) + }) + } + + function i(t, r) { + return a(this, void 0, void 0, function() { + var e, o, a, s, f, p, l, v, h; + return u(this, function(u) { + switch (u.label) { + case 0: + return "object" != typeof r ? [2, r] : n(r) ? [4, i(t, r.value)] : [3, 2]; + case 1: + return e = u.sent(), o = c(t, ["id", r.__serializer_id]), o.fromObject ? [2, o.fromObject(e)] : [2, e]; + case 2: + a = r instanceof Array ? [] : {}, s = []; + for (f in r) s.push(f); + p = 0, u.label = 3; + case 3: + return p < s.length ? (l = s[p], v = a, h = l, [4, i(t, r[l])]) : [3, 6]; + case 4: + v[h] = u.sent(), u.label = 5; + case 5: + return p++, [3, 3]; + case 6: + return [2, a] + } + }) + }) + } + var a = this && this.__awaiter || function(t, r, e, n) { + return new(e || (e = Promise))(function(o, i) { + function a(t) { + try { + c(n.next(t)) + } catch (t) { + i(t) + } + } + + function u(t) { + try { + c(n.throw(t)) + } catch (t) { + i(t) + } + } + + function c(t) { + t.done ? o(t.value) : new e(function(r) { + r(t.value) + }).then(a, u) + } + c((n = n.apply(t, r)).next()) + }) + }, + u = this && this.__generator || function(t, r) { + function e(t) { + return function(r) { + return n([t, r]) + } + } + + function n(e) { + if (o) throw new TypeError("Generator is already executing."); + for (; u;) try { + if (o = 1, i && (a = i[2 & e[0] ? "return" : e[0] ? "throw" : "next"]) && !(a = a.call(i, e[1])).done) return a; + switch (i = 0, a && (e = [0, a.value]), e[0]) { + case 0: + case 1: + a = e; + break; + case 4: + return u.label++, { + value: e[1], + done: !1 + }; + case 5: + u.label++, i = e[1], e = [0]; + continue; + case 7: + e = u.ops.pop(), u.trys.pop(); + continue; + default: + if (a = u.trys, !(a = a.length > 0 && a[a.length - 1]) && (6 === e[0] || 2 === e[0])) { + u = 0; + continue + } + if (3 === e[0] && (!a || e[1] > a[0] && e[1] < a[3])) { + u.label = e[1]; + break + } + if (6 === e[0] && u.label < a[1]) { + u.label = a[1], a = e; + break + } + if (a && u.label < a[2]) { + u.label = a[2], u.ops.push(e); + break + } + a[2] && u.ops.pop(), u.trys.pop(); + continue + } + e = r.call(t, u) + } catch (t) { + e = [6, t], i = 0 + } finally { + o = a = 0 + } + if (5 & e[0]) throw e[1]; + return { + value: e[0] ? e[1] : void 0, + done: !0 + } + } + var o, i, a, u = { + label: 0, + sent: function() { + if (1 & a[0]) throw a[1]; + return a[1] + }, + trys: [], + ops: [] + }; + return { + next: e(0), + throw: e(1), + return: e(2) + } + }, + c = e(4); + (function() { + function t() {} + return t + })(); + r.toObjects = o, r.fromObjects = i +}, function(t, r, e) { + var n = e(5), + o = e(121), + i = n(o); + t.exports = i +}, function(t, r, e) { + function n(t) { + return function(r, e, n) { + var u = Object(r); + if (!i(r)) { + var c = o(e, 3); + r = a(r), e = function(t) { + return c(u[t], t, u) + } + } + var s = t(r, e, n); + return s > -1 ? u[c ? r[s] : s] : void 0 + } + } + var o = e(6), + i = e(92), + a = e(73); + t.exports = n +}, function(t, r, e) { + function n(t) { + return "function" == typeof t ? t : null == t ? a : "object" == typeof t ? u(t) ? i(t[0], t[1]) : o(t) : c(t) + } + var o = e(7), + i = e(101), + a = e(117), + u = e(69), + c = e(118); + t.exports = n +}, function(t, r, e) { + function n(t) { + var r = i(t); + return 1 == r.length && r[0][2] ? a(r[0][0], r[0][1]) : function(e) { + return e === t || o(e, t, r) + } + } + var o = e(8), + i = e(98), + a = e(100); + t.exports = n +}, function(t, r, e) { + function n(t, r, e, n) { + var c = e.length, + s = c, + f = !n; + if (null == t) return !s; + for (t = Object(t); c--;) { + var p = e[c]; + if (f && p[2] ? p[1] !== t[p[0]] : !(p[0] in t)) return !1 + } + for (; ++c < s;) { + p = e[c]; + var l = p[0], + v = t[l], + h = p[1]; + if (f && p[2]) { + if (void 0 === v && !(l in t)) return !1 + } else { + var y = new o; + if (n) var _ = n(v, h, l, t, r, y); + if (!(void 0 === _ ? i(h, v, a | u, n, y) : _)) return !1 + } + } + return !0 + } + var o = e(9), + i = e(53), + a = 1, + u = 2; + t.exports = n +}, function(t, r, e) { + function n(t) { + var r = this.__data__ = new o(t); + this.size = r.size + } + var o = e(10), + i = e(18), + a = e(19), + u = e(20), + c = e(21), + s = e(22); + n.prototype.clear = i, n.prototype.delete = a, n.prototype.get = u, n.prototype.has = c, n.prototype.set = s, t.exports = n +}, function(t, r, e) { + function n(t) { + var r = -1, + e = null == t ? 0 : t.length; + for (this.clear(); ++r < e;) { + var n = t[r]; + this.set(n[0], n[1]) + } + } + var o = e(11), + i = e(12), + a = e(15), + u = e(16), + c = e(17); + n.prototype.clear = o, n.prototype.delete = i, n.prototype.get = a, n.prototype.has = u, n.prototype.set = c, t.exports = n +}, function(t, r) { + function e() { + this.__data__ = [], this.size = 0 + } + t.exports = e +}, function(t, r, e) { + function n(t) { + var r = this.__data__, + e = o(r, t); + if (e < 0) return !1; + var n = r.length - 1; + return e == n ? r.pop() : a.call(r, e, 1), --this.size, !0 + } + var o = e(13), + i = Array.prototype, + a = i.splice; + t.exports = n +}, function(t, r, e) { + function n(t, r) { + for (var e = t.length; e--;) + if (o(t[e][0], r)) return e; + return -1 + } + var o = e(14); + t.exports = n +}, function(t, r) { + function e(t, r) { + return t === r || t !== t && r !== r + } + t.exports = e +}, function(t, r, e) { + function n(t) { + var r = this.__data__, + e = o(r, t); + return e < 0 ? void 0 : r[e][1] + } + var o = e(13); + t.exports = n +}, function(t, r, e) { + function n(t) { + return o(this.__data__, t) > -1 + } + var o = e(13); + t.exports = n +}, function(t, r, e) { + function n(t, r) { + var e = this.__data__, + n = o(e, t); + return n < 0 ? (++this.size, e.push([t, r])) : e[n][1] = r, this + } + var o = e(13); + t.exports = n +}, function(t, r, e) { + function n() { + this.__data__ = new o, this.size = 0 + } + var o = e(10); + t.exports = n +}, function(t, r) { + function e(t) { + var r = this.__data__, + e = r.delete(t); + return this.size = r.size, e + } + t.exports = e +}, function(t, r) { + function e(t) { + return this.__data__.get(t) + } + t.exports = e +}, function(t, r) { + function e(t) { + return this.__data__.has(t) + } + t.exports = e +}, function(t, r, e) { + function n(t, r) { + var e = this.__data__; + if (e instanceof o) { + var n = e.__data__; + if (!i || n.length < u - 1) return n.push([t, r]), this.size = ++e.size, this; + e = this.__data__ = new a(n) + } + return e.set(t, r), this.size = e.size, this + } + var o = e(10), + i = e(23), + a = e(38), + u = 200; + t.exports = n +}, function(t, r, e) { + var n = e(24), + o = e(29), + i = n(o, "Map"); + t.exports = i +}, function(t, r, e) { + function n(t, r) { + var e = i(t, r); + return o(e) ? e : void 0 + } + var o = e(25), + i = e(37); + t.exports = n +}, function(t, r, e) { + function n(t) { + if (!a(t) || i(t)) return !1; + var r = o(t) ? h : s; + return r.test(u(t)) + } + var o = e(26), + i = e(34), + a = e(33), + u = e(36), + c = /[\\\\^$.*+?()[\\]{}|]/g, + s = /^\\[object .+?Constructor\\]$/, + f = Function.prototype, + p = Object.prototype, + l = f.toString, + v = p.hasOwnProperty, + h = RegExp("^" + l.call(v).replace(c, "\\\\$&").replace(/hasOwnProperty|(function).*?(?=\\\\\\()| for .+?(?=\\\\\\])/g, "$1.*?") + "$"); + t.exports = n +}, function(t, r, e) { + function n(t) { + if (!i(t)) return !1; + var r = o(t); + return r == u || r == c || r == a || r == s + } + var o = e(27), + i = e(33), + a = "[object AsyncFunction]", + u = "[object Function]", + c = "[object GeneratorFunction]", + s = "[object Proxy]"; + t.exports = n +}, function(t, r, e) { + function n(t) { + return null == t ? void 0 === t ? c : u : s && s in Object(t) ? i(t) : a(t) + } + var o = e(28), + i = e(31), + a = e(32), + u = "[object Null]", + c = "[object Undefined]", + s = o ? o.toStringTag : void 0; + t.exports = n +}, function(t, r, e) { + var n = e(29), + o = n.Symbol; + t.exports = o +}, function(t, r, e) { + var n = e(30), + o = "object" == typeof self && self && self.Object === Object && self, + i = n || o || Function("return this")(); + t.exports = i +}, function(t, r) { + (function(r) { + var e = "object" == typeof r && r && r.Object === Object && r; + t.exports = e + }).call(r, function() { + return this + }()) +}, function(t, r, e) { + function n(t) { + var r = a.call(t, c), + e = t[c]; + try { + t[c] = void 0; + var n = !0 + } catch (t) {} + var o = u.call(t); + return n && (r ? t[c] = e : delete t[c]), o + } + var o = e(28), + i = Object.prototype, + a = i.hasOwnProperty, + u = i.toString, + c = o ? o.toStringTag : void 0; + t.exports = n +}, function(t, r) { + function e(t) { + return o.call(t) + } + var n = Object.prototype, + o = n.toString; + t.exports = e +}, function(t, r) { + function e(t) { + var r = typeof t; + return null != t && ("object" == r || "function" == r) + } + t.exports = e +}, function(t, r, e) { + function n(t) { + return !!i && i in t + } + var o = e(35), + i = function() { + var t = /[^.]+$/.exec(o && o.keys && o.keys.IE_PROTO || ""); + return t ? "Symbol(src)_1." + t : "" + }(); + t.exports = n +}, function(t, r, e) { + var n = e(29), + o = n["__core-js_shared__"]; + t.exports = o +}, function(t, r) { + function e(t) { + if (null != t) { + try { + return o.call(t) + } catch (t) {} + try { + return t + "" + } catch (t) {} + } + return "" + } + var n = Function.prototype, + o = n.toString; + t.exports = e +}, function(t, r) { + function e(t, r) { + return null == t ? void 0 : t[r] + } + t.exports = e +}, function(t, r, e) { + function n(t) { + var r = -1, + e = null == t ? 0 : t.length; + for (this.clear(); ++r < e;) { + var n = t[r]; + this.set(n[0], n[1]) + } + } + var o = e(39), + i = e(47), + a = e(50), + u = e(51), + c = e(52); + n.prototype.clear = o, n.prototype.delete = i, n.prototype.get = a, n.prototype.has = u, n.prototype.set = c, t.exports = n +}, function(t, r, e) { + function n() { + this.size = 0, this.__data__ = { + hash: new o, + map: new(a || i), + string: new o + } + } + var o = e(40), + i = e(10), + a = e(23); + t.exports = n +}, function(t, r, e) { + function n(t) { + var r = -1, + e = null == t ? 0 : t.length; + for (this.clear(); ++r < e;) { + var n = t[r]; + this.set(n[0], n[1]) + } + } + var o = e(41), + i = e(43), + a = e(44), + u = e(45), + c = e(46); + n.prototype.clear = o, n.prototype.delete = i, n.prototype.get = a, n.prototype.has = u, n.prototype.set = c, t.exports = n +}, function(t, r, e) { + function n() { + this.__data__ = o ? o(null) : {}, this.size = 0 + } + var o = e(42); + t.exports = n +}, function(t, r, e) { + var n = e(24), + o = n(Object, "create"); + t.exports = o +}, function(t, r) { + function e(t) { + var r = this.has(t) && delete this.__data__[t]; + return this.size -= r ? 1 : 0, r + } + t.exports = e +}, function(t, r, e) { + function n(t) { + var r = this.__data__; + if (o) { + var e = r[t]; + return e === i ? void 0 : e + } + return u.call(r, t) ? r[t] : void 0 + } + var o = e(42), + i = "__lodash_hash_undefined__", + a = Object.prototype, + u = a.hasOwnProperty; + t.exports = n +}, function(t, r, e) { + function n(t) { + var r = this.__data__; + return o ? void 0 !== r[t] : a.call(r, t) + } + var o = e(42), + i = Object.prototype, + a = i.hasOwnProperty; + t.exports = n +}, function(t, r, e) { + function n(t, r) { + var e = this.__data__; + return this.size += this.has(t) ? 0 : 1, e[t] = o && void 0 === r ? i : r, this + } + var o = e(42), + i = "__lodash_hash_undefined__"; + t.exports = n +}, function(t, r, e) { + function n(t) { + var r = o(this, t).delete(t); + return this.size -= r ? 1 : 0, r + } + var o = e(48); + t.exports = n +}, function(t, r, e) { + function n(t, r) { + var e = t.__data__; + return o(r) ? e["string" == typeof r ? "string" : "hash"] : e.map + } + var o = e(49); + t.exports = n +}, function(t, r) { + function e(t) { + var r = typeof t; + return "string" == r || "number" == r || "symbol" == r || "boolean" == r ? "__proto__" !== t : null === t + } + t.exports = e +}, function(t, r, e) { + function n(t) { + return o(this, t).get(t) + } + var o = e(48); + t.exports = n +}, function(t, r, e) { + function n(t) { + return o(this, t).has(t) + } + var o = e(48); + t.exports = n +}, function(t, r, e) { + function n(t, r) { + var e = o(this, t), + n = e.size; + return e.set(t, r), this.size += e.size == n ? 0 : 1, this + } + var o = e(48); + t.exports = n +}, function(t, r, e) { + function n(t, r, e, a, u) { + return t === r || (null == t || null == r || !i(t) && !i(r) ? t !== t && r !== r : o(t, r, e, a, n, u)) + } + var o = e(54), + i = e(78); + t.exports = n +}, function(t, r, e) { + function n(t, r, e, n, _, d) { + var x = s(t), + g = s(r), + w = x ? h : c(t), + j = g ? h : c(r); + w = w == v ? y : w, j = j == v ? y : j; + var m = w == y, + O = j == y, + A = w == j; + if (A && f(t)) { + if (!f(r)) return !1; + x = !0, m = !1 + } + if (A && !m) return d || (d = new o), x || p(t) ? i(t, r, e, n, _, d) : a(t, r, w, e, n, _, d); + if (!(e & l)) { + var z = m && b.call(t, "__wrapped__"), + k = O && b.call(r, "__wrapped__"); + if (z || k) { + var S = z ? t.value() : t, + P = k ? r.value() : r; + return d || (d = new o), _(S, P, e, n, d) + } + } + return !!A && (d || (d = new o), u(t, r, e, n, _, d)) + } + var o = e(9), + i = e(55), + a = e(61), + u = e(65), + c = e(93), + s = e(69), + f = e(79), + p = e(83), + l = 1, + v = "[object Arguments]", + h = "[object Array]", + y = "[object Object]", + _ = Object.prototype, + b = _.hasOwnProperty; + t.exports = n +}, function(t, r, e) { + function n(t, r, e, n, s, f) { + var p = e & u, + l = t.length, + v = r.length; + if (l != v && !(p && v > l)) return !1; + var h = f.get(t); + if (h && f.get(r)) return h == r; + var y = -1, + _ = !0, + b = e & c ? new o : void 0; + for (f.set(t, r), f.set(r, t); ++y < l;) { + var d = t[y], + x = r[y]; + if (n) var g = p ? n(x, d, y, r, t, f) : n(d, x, y, t, r, f); + if (void 0 !== g) { + if (g) continue; + _ = !1; + break + } + if (b) { + if (!i(r, function(t, r) { + if (!a(b, r) && (d === t || s(d, t, e, n, f))) return b.push(r) + })) { + _ = !1; + break + } + } else if (d !== x && !s(d, x, e, n, f)) { + _ = !1; + break + } + } + return f.delete(t), f.delete(r), _ + } + var o = e(56), + i = e(59), + a = e(60), + u = 1, + c = 2; + t.exports = n +}, function(t, r, e) { + function n(t) { + var r = -1, + e = null == t ? 0 : t.length; + for (this.__data__ = new o; ++r < e;) this.add(t[r]) + } + var o = e(38), + i = e(57), + a = e(58); + n.prototype.add = n.prototype.push = i, n.prototype.has = a, t.exports = n +}, function(t, r) { + function e(t) { + return this.__data__.set(t, n), this + } + var n = "__lodash_hash_undefined__"; + t.exports = e +}, function(t, r) { + function e(t) { + return this.__data__.has(t) + } + t.exports = e +}, function(t, r) { + function e(t, r) { + for (var e = -1, n = null == t ? 0 : t.length; ++e < n;) + if (r(t[e], e, t)) return !0; + return !1 + } + t.exports = e +}, function(t, r) { + function e(t, r) { + return t.has(r) + } + t.exports = e +}, function(t, r, e) { + function n(t, r, e, n, o, m, A) { + switch (e) { + case j: + if (t.byteLength != r.byteLength || t.byteOffset != r.byteOffset) return !1; + t = t.buffer, r = r.buffer; + case w: + return !(t.byteLength != r.byteLength || !m(new i(t), new i(r))); + case l: + case v: + case _: + return a(+t, +r); + case h: + return t.name == r.name && t.message == r.message; + case b: + case x: + return t == r + ""; + case y: + var z = c; + case d: + var k = n & f; + if (z || (z = s), t.size != r.size && !k) return !1; + var S = A.get(t); + if (S) return S == r; + n |= p, A.set(t, r); + var P = u(z(t), z(r), n, o, m, A); + return A.delete(t), P; + case g: + if (O) return O.call(t) == O.call(r) + } + return !1 + } + var o = e(28), + i = e(62), + a = e(14), + u = e(55), + c = e(63), + s = e(64), + f = 1, + p = 2, + l = "[object Boolean]", + v = "[object Date]", + h = "[object Error]", + y = "[object Map]", + _ = "[object Number]", + b = "[object RegExp]", + d = "[object Set]", + x = "[object String]", + g = "[object Symbol]", + w = "[object ArrayBuffer]", + j = "[object DataView]", + m = o ? o.prototype : void 0, + O = m ? m.valueOf : void 0; + t.exports = n +}, function(t, r, e) { + var n = e(29), + o = n.Uint8Array; + t.exports = o +}, function(t, r) { + function e(t) { + var r = -1, + e = Array(t.size); + return t.forEach(function(t, n) { + e[++r] = [n, t] + }), e + } + t.exports = e +}, function(t, r) { + function e(t) { + var r = -1, + e = Array(t.size); + return t.forEach(function(t) { + e[++r] = t + }), e + } + t.exports = e +}, function(t, r, e) { + function n(t, r, e, n, a, c) { + var s = e & i, + f = o(t), + p = f.length, + l = o(r), + v = l.length; + if (p != v && !s) return !1; + for (var h = p; h--;) { + var y = f[h]; + if (!(s ? y in r : u.call(r, y))) return !1 + } + var _ = c.get(t); + if (_ && c.get(r)) return _ == r; + var b = !0; + c.set(t, r), c.set(r, t); + for (var d = s; ++h < p;) { + y = f[h]; + var x = t[y], + g = r[y]; + if (n) var w = s ? n(g, x, y, r, t, c) : n(x, g, y, t, r, c); + if (!(void 0 === w ? x === g || a(x, g, e, n, c) : w)) { + b = !1; + break + } + d || (d = "constructor" == y) + } + if (b && !d) { + var j = t.constructor, + m = r.constructor; + j != m && "constructor" in t && "constructor" in r && !("function" == typeof j && j instanceof j && "function" == typeof m && m instanceof m) && (b = !1) + } + return c.delete(t), c.delete(r), b + } + var o = e(66), + i = 1, + a = Object.prototype, + u = a.hasOwnProperty; + t.exports = n +}, function(t, r, e) { + function n(t) { + return o(t, a, i) + } + var o = e(67), + i = e(70), + a = e(73); + t.exports = n +}, function(t, r, e) { + function n(t, r, e) { + var n = r(t); + return i(t) ? n : o(n, e(t)) + } + var o = e(68), + i = e(69); + t.exports = n +}, function(t, r) { + function e(t, r) { + for (var e = -1, n = r.length, o = t.length; ++e < n;) t[o + e] = r[e]; + return t + } + t.exports = e +}, function(t, r) { + var e = Array.isArray; + t.exports = e +}, function(t, r, e) { + var n = e(71), + o = e(72), + i = Object.prototype, + a = i.propertyIsEnumerable, + u = Object.getOwnPropertySymbols, + c = u ? function(t) { + return null == t ? [] : (t = Object(t), n(u(t), function(r) { + return a.call(t, r) + })) + } : o; + t.exports = c +}, function(t, r) { + function e(t, r) { + for (var e = -1, n = null == t ? 0 : t.length, o = 0, i = []; ++e < n;) { + var a = t[e]; + r(a, e, t) && (i[o++] = a) + } + return i + } + t.exports = e +}, function(t, r) { + function e() { + return [] + } + t.exports = e +}, function(t, r, e) { + function n(t) { + return a(t) ? o(t) : i(t) + } + var o = e(74), + i = e(88), + a = e(92); + t.exports = n +}, function(t, r, e) { + function n(t, r) { + var e = a(t), + n = !e && i(t), + f = !e && !n && u(t), + l = !e && !n && !f && s(t), + v = e || n || f || l, + h = v ? o(t.length, String) : [], + y = h.length; + for (var _ in t) !r && !p.call(t, _) || v && ("length" == _ || f && ("offset" == _ || "parent" == _) || l && ("buffer" == _ || "byteLength" == _ || "byteOffset" == _) || c(_, y)) || h.push(_); + return h + } + var o = e(75), + i = e(76), + a = e(69), + u = e(79), + c = e(82), + s = e(83), + f = Object.prototype, + p = f.hasOwnProperty; + t.exports = n +}, function(t, r) { + function e(t, r) { + for (var e = -1, n = Array(t); ++e < t;) n[e] = r(e); + return n + } + t.exports = e +}, function(t, r, e) { + var n = e(77), + o = e(78), + i = Object.prototype, + a = i.hasOwnProperty, + u = i.propertyIsEnumerable, + c = n(function() { + return arguments + }()) ? n : function(t) { + return o(t) && a.call(t, "callee") && !u.call(t, "callee") + }; + t.exports = c +}, function(t, r, e) { + function n(t) { + return i(t) && o(t) == a + } + var o = e(27), + i = e(78), + a = "[object Arguments]"; + t.exports = n +}, function(t, r) { + function e(t) { + return null != t && "object" == typeof t + } + t.exports = e +}, function(t, r, e) { + (function(t) { + var n = e(29), + o = e(81), + i = "object" == typeof r && r && !r.nodeType && r, + a = i && "object" == typeof t && t && !t.nodeType && t, + u = a && a.exports === i, + c = u ? n.Buffer : void 0, + s = c ? c.isBuffer : void 0, + f = s || o; + t.exports = f + }).call(r, e(80)(t)) +}, function(t, r) { + t.exports = function(t) { + return t.webpackPolyfill || (t.deprecate = function() {}, t.paths = [], t.children = [], t.webpackPolyfill = 1), t + } +}, function(t, r) { + function e() { + return !1 + } + t.exports = e +}, function(t, r) { + function e(t, r) { + return r = null == r ? n : r, !!r && ("number" == typeof t || o.test(t)) && t > -1 && t % 1 == 0 && t < r + } + var n = 9007199254740991, + o = /^(?:0|[1-9]\\d*)$/; + t.exports = e +}, function(t, r, e) { + var n = e(84), + o = e(86), + i = e(87), + a = i && i.isTypedArray, + u = a ? o(a) : n; + t.exports = u +}, function(t, r, e) { + function n(t) { + return a(t) && i(t.length) && !!T[o(t)] + } + var o = e(27), + i = e(85), + a = e(78), + u = "[object Arguments]", + c = "[object Array]", + s = "[object Boolean]", + f = "[object Date]", + p = "[object Error]", + l = "[object Function]", + v = "[object Map]", + h = "[object Number]", + y = "[object Object]", + _ = "[object RegExp]", + b = "[object Set]", + d = "[object String]", + x = "[object WeakMap]", + g = "[object ArrayBuffer]", + w = "[object DataView]", + j = "[object Float32Array]", + m = "[object Float64Array]", + O = "[object Int8Array]", + A = "[object Int16Array]", + z = "[object Int32Array]", + k = "[object Uint8Array]", + S = "[object Uint8ClampedArray]", + P = "[object Uint16Array]", + B = "[object Uint32Array]", + T = {}; + T[j] = T[m] = T[O] = T[A] = T[z] = T[k] = T[S] = T[P] = T[B] = !0, T[u] = T[c] = T[g] = T[s] = T[w] = T[f] = T[p] = T[l] = T[v] = T[h] = T[y] = T[_] = T[b] = T[d] = T[x] = !1, t.exports = n +}, function(t, r) { + function e(t) { + return "number" == typeof t && t > -1 && t % 1 == 0 && t <= n + } + var n = 9007199254740991; + t.exports = e +}, function(t, r) { + function e(t) { + return function(r) { + return t(r) + } + } + t.exports = e +}, function(t, r, e) { + (function(t) { + var n = e(30), + o = "object" == typeof r && r && !r.nodeType && r, + i = o && "object" == typeof t && t && !t.nodeType && t, + a = i && i.exports === o, + u = a && n.process, + c = function() { + try { + return u && u.binding && u.binding("util") + } catch (t) {} + }(); + t.exports = c + }).call(r, e(80)(t)) +}, function(t, r, e) { + function n(t) { + if (!o(t)) return i(t); + var r = []; + for (var e in Object(t)) u.call(t, e) && "constructor" != e && r.push(e); + return r + } + var o = e(89), + i = e(90), + a = Object.prototype, + u = a.hasOwnProperty; + t.exports = n +}, function(t, r) { + function e(t) { + var r = t && t.constructor, + e = "function" == typeof r && r.prototype || n; + return t === e + } + var n = Object.prototype; + t.exports = e +}, function(t, r, e) { + var n = e(91), + o = n(Object.keys, Object); + t.exports = o +}, function(t, r) { + function e(t, r) { + return function(e) { + return t(r(e)) + } + } + t.exports = e +}, function(t, r, e) { + function n(t) { + return null != t && i(t.length) && !o(t) + } + var o = e(26), + i = e(85); + t.exports = n +}, function(t, r, e) { + var n = e(94), + o = e(23), + i = e(95), + a = e(96), + u = e(97), + c = e(27), + s = e(36), + f = "[object Map]", + p = "[object Object]", + l = "[object Promise]", + v = "[object Set]", + h = "[object WeakMap]", + y = "[object DataView]", + _ = s(n), + b = s(o), + d = s(i), + x = s(a), + g = s(u), + w = c; + (n && w(new n(new ArrayBuffer(1))) != y || o && w(new o) != f || i && w(i.resolve()) != l || a && w(new a) != v || u && w(new u) != h) && (w = function(t) { + var r = c(t), + e = r == p ? t.constructor : void 0, + n = e ? s(e) : ""; + if (n) switch (n) { + case _: + return y; + case b: + return f; + case d: + return l; + case x: + return v; + case g: + return h + } + return r + }), t.exports = w +}, function(t, r, e) { + var n = e(24), + o = e(29), + i = n(o, "DataView"); + t.exports = i +}, function(t, r, e) { + var n = e(24), + o = e(29), + i = n(o, "Promise"); + t.exports = i +}, function(t, r, e) { + var n = e(24), + o = e(29), + i = n(o, "Set"); + t.exports = i +}, function(t, r, e) { + var n = e(24), + o = e(29), + i = n(o, "WeakMap"); + t.exports = i +}, function(t, r, e) { + function n(t) { + for (var r = i(t), e = r.length; e--;) { + var n = r[e], + a = t[n]; + r[e] = [n, a, o(a)] + } + return r + } + var o = e(99), + i = e(73); + t.exports = n +}, function(t, r, e) { + function n(t) { + return t === t && !o(t) + } + var o = e(33); + t.exports = n +}, function(t, r) { + function e(t, r) { + return function(e) { + return null != e && (e[t] === r && (void 0 !== r || t in Object(e))) + } + } + t.exports = e +}, function(t, r, e) { + function n(t, r) { + return u(t) && c(r) ? s(f(t), r) : function(e) { + var n = i(e, t); + return void 0 === n && n === r ? a(e, t) : o(r, n, p | l) + } + } + var o = e(53), + i = e(102), + a = e(114), + u = e(105), + c = e(99), + s = e(100), + f = e(113), + p = 1, + l = 2; + t.exports = n +}, function(t, r, e) { + function n(t, r, e) { + var n = null == t ? void 0 : o(t, r); + return void 0 === n ? e : n + } + var o = e(103); + t.exports = n +}, function(t, r, e) { + function n(t, r) { + r = o(r, t); + for (var e = 0, n = r.length; null != t && e < n;) t = t[i(r[e++])]; + return e && e == n ? t : void 0 + } + var o = e(104), + i = e(113); + t.exports = n +}, function(t, r, e) { + function n(t, r) { + return o(t) ? t : i(t, r) ? [t] : a(u(t)) + } + var o = e(69), + i = e(105), + a = e(107), + u = e(110); + t.exports = n +}, function(t, r, e) { + function n(t, r) { + if (o(t)) return !1; + var e = typeof t; + return !("number" != e && "symbol" != e && "boolean" != e && null != t && !i(t)) || (u.test(t) || !a.test(t) || null != r && t in Object(r)) + } + var o = e(69), + i = e(106), + a = /\\.|\\[(?:[^[\\]]*|(["'])(?:(?!\\1)[^\\\\]|\\\\.)*?\\1)\\]/, + u = /^\\w*$/; + t.exports = n +}, function(t, r, e) { + function n(t) { + return "symbol" == typeof t || i(t) && o(t) == a + } + var o = e(27), + i = e(78), + a = "[object Symbol]"; + t.exports = n +}, function(t, r, e) { + var n = e(108), + o = /^\\./, + i = /[^.[\\]]+|\\[(?:(-?\\d+(?:\\.\\d+)?)|(["'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2)\\]|(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))/g, + a = /\\\\(\\\\)?/g, + u = n(function(t) { + var r = []; + return o.test(t) && r.push(""), t.replace(i, function(t, e, n, o) { + r.push(n ? o.replace(a, "$1") : e || t) + }), r + }); + t.exports = u +}, function(t, r, e) { + function n(t) { + var r = o(t, function(t) { + return e.size === i && e.clear(), t + }), + e = r.cache; + return r + } + var o = e(109), + i = 500; + t.exports = n +}, function(t, r, e) { + function n(t, r) { + if ("function" != typeof t || null != r && "function" != typeof r) throw new TypeError(i); + var e = function() { + var n = arguments, + o = r ? r.apply(this, n) : n[0], + i = e.cache; + if (i.has(o)) return i.get(o); + var a = t.apply(this, n); + return e.cache = i.set(o, a) || i, a + }; + return e.cache = new(n.Cache || o), e + } + var o = e(38), + i = "Expected a function"; + n.Cache = o, t.exports = n +}, function(t, r, e) { + function n(t) { + return null == t ? "" : o(t) + } + var o = e(111); + t.exports = n +}, function(t, r, e) { + function n(t) { + if ("string" == typeof t) return t; + if (a(t)) return i(t, n) + ""; + if (u(t)) return f ? f.call(t) : ""; + var r = t + ""; + return "0" == r && 1 / t == -c ? "-0" : r + } + var o = e(28), + i = e(112), + a = e(69), + u = e(106), + c = 1 / 0, + s = o ? o.prototype : void 0, + f = s ? s.toString : void 0; + t.exports = n +}, function(t, r) { + function e(t, r) { + for (var e = -1, n = null == t ? 0 : t.length, o = Array(n); ++e < n;) o[e] = r(t[e], e, t); + return o + } + t.exports = e +}, function(t, r, e) { + function n(t) { + if ("string" == typeof t || o(t)) return t; + var r = t + ""; + return "0" == r && 1 / t == -i ? "-0" : r + } + var o = e(106), + i = 1 / 0; + t.exports = n +}, function(t, r, e) { + function n(t, r) { + return null != t && i(t, r, o) + } + var o = e(115), + i = e(116); + t.exports = n +}, function(t, r) { + function e(t, r) { + return null != t && r in Object(t) + } + t.exports = e +}, function(t, r, e) { + function n(t, r, e) { + r = o(r, t); + for (var n = -1, f = r.length, p = !1; ++n < f;) { + var l = s(r[n]); + if (!(p = null != t && e(t, l))) break; + t = t[l] + } + return p || ++n != f ? p : (f = null == t ? 0 : t.length, !!f && c(f) && u(l, f) && (a(t) || i(t))) + } + var o = e(104), + i = e(76), + a = e(69), + u = e(82), + c = e(85), + s = e(113); + t.exports = n +}, function(t, r) { + function e(t) { + return t + } + t.exports = e +}, function(t, r, e) { + function n(t) { + return a(t) ? o(u(t)) : i(t) + } + var o = e(119), + i = e(120), + a = e(105), + u = e(113); + t.exports = n +}, function(t, r) { + function e(t) { + return function(r) { + return null == r ? void 0 : r[t] + } + } + t.exports = e +}, function(t, r, e) { + function n(t) { + return function(r) { + return o(r, t) + } + } + var o = e(103); + t.exports = n +}, function(t, r, e) { + function n(t, r, e) { + var n = null == t ? 0 : t.length; + if (!n) return -1; + var c = null == e ? 0 : a(e); + return c < 0 && (c = u(n + c, 0)), o(t, i(r, 3), c) + } + var o = e(122), + i = e(6), + a = e(123), + u = Math.max; + t.exports = n +}, function(t, r) { + function e(t, r, e, n) { + for (var o = t.length, i = e + (n ? 1 : -1); n ? i-- : ++i < o;) + if (r(t[i], i, t)) return i; + return -1 + } + t.exports = e +}, function(t, r, e) { + function n(t) { + var r = o(t), + e = r % 1; + return r === r ? e ? r - e : r : 0 + } + var o = e(124); + t.exports = n +}, function(t, r, e) { + function n(t) { + if (!t) return 0 === t ? t : 0; + if (t = o(t), t === i || t === -i) { + var r = t < 0 ? -1 : 1; + return r * a + } + return t === t ? t : 0 + } + var o = e(125), + i = 1 / 0, + a = 1.7976931348623157e308; + t.exports = n +}, function(t, r, e) { + function n(t) { + if ("number" == typeof t) return t; + if (i(t)) return a; + if (o(t)) { + var r = "function" == typeof t.valueOf ? t.valueOf() : t; + t = o(r) ? r + "" : r + } + if ("string" != typeof t) return 0 === t ? t : +t; + t = t.replace(u, ""); + var e = s.test(t); + return e || f.test(t) ? p(t.slice(2), e ? 2 : 8) : c.test(t) ? a : +t + } + var o = e(33), + i = e(106), + a = NaN, + u = /^\\s+|\\s+$/g, + c = /^[-+]0x[0-9a-f]+$/i, + s = /^0b[01]+$/i, + f = /^0o[0-7]+$/i, + p = parseInt; + t.exports = n +}, function(t, r) { + "use strict"; + + function e() { + return window.crypto.subtle || window.crypto.webkitSubtle + } + r.subtle = e +}, function(t, r) { + "use strict"; + + function e(t, r) { + var n; + return n = Array.isArray(t) ? [] : {}, r.push(t), Object.keys(t).forEach(function(o) { + var i = t[o]; + if ("function" != typeof i) return i && "object" == typeof i ? r.indexOf(t[o]) === -1 ? void(n[o] = e(t[o], r.slice(0))) : void(n[o] = "[Circular]") : void(n[o] = i) + }), "string" == typeof t.name && (n.name = t.name), "string" == typeof t.message && (n.message = t.message), "string" == typeof t.stack && (n.stack = t.stack), n + } + t.exports = function(t) { + return "object" == typeof t ? e(t, []) : "function" == typeof t ? "[Function: " + (t.name || "anonymous") + "]" : t + } +}]); +`; +//# sourceMappingURL=webViewWorkerString.js.map \ No newline at end of file diff --git a/examples/react-native/src/webview-crypto/webViewWorkerString.js.map b/examples/react-native/src/webview-crypto/webViewWorkerString.js.map new file mode 100644 index 00000000..7fb99936 --- /dev/null +++ b/examples/react-native/src/webview-crypto/webViewWorkerString.js.map @@ -0,0 +1 @@ +{"version":3,"file":"webViewWorkerString.js","sourceRoot":"","sources":["../src/webViewWorkerString.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,eAAe,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+M9B,CAAC;AAEF,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAq4Dd,CAAC"} \ No newline at end of file diff --git a/examples/react/server.js b/examples/react/server.js index 20f87ad0..60f5ff62 100644 --- a/examples/react/server.js +++ b/examples/react/server.js @@ -1,5 +1,5 @@ console.log("If modules not found, run `npm install` in /example folder!"); -var port = process.env.OPENSHIFT_NODEJS_PORT || process.env.VCAP_APP_PORT || process.env.PORT || process.argv[2] || 8081; +var port = process.env.OPENSHIFT_NODEJS_PORT || process.env.VCAP_APP_PORT || process.env.PORT || process.argv[2] || 8765; var host = process.env.OPENSHIFT_NODEJS_HOST || process.env.VCAP_APP_HOST || process.env.HOST || 'localhost'; var express = require('express'); @@ -15,7 +15,7 @@ var gun = Gun({ }); app.use(Gun.serve); -app.use(proxy(host + ':8080')); +app.use(proxy(host + ':8765')); server.listen(port); console.log('Server started on port ' + port + ' with /gun'); diff --git a/examples/start.js.html b/examples/start.js.html new file mode 100644 index 00000000..0341e360 --- /dev/null +++ b/examples/start.js.html @@ -0,0 +1,17 @@ +/* + + + + \ No newline at end of file diff --git a/examples/style.css b/examples/style.css index 2195529b..bc059405 100644 --- a/examples/style.css +++ b/examples/style.css @@ -1,38 +1,26 @@ -@import url(https://fonts.googleapis.com/css?family=Oxygen); - html, body { margin: 0; padding: 0; - font-family: 'Oxygen', 'Trebuchet MS', arial; position: relative; - background: black; - color: white; + line-height: 1.5; + font-size: 18pt; } -body { - font-size: 18pt; -} - -div, ul, ol, li, p, span, form, button, input, textarea { +div, ul, ol, li, p, span, form, button, input, textarea, img { margin: 0; padding: 0; position: relative; - overflow: hidden; - font-size: 1em; - line-height: 1.5em; + vertical-align: inherit; -webkit-transition: all 0.3s; transition: all 0.3s; box-sizing: border-box; } -button, input, textarea { - background: white; - border: none; - color: black; -} - -a { - color: white; +a, button, input, textarea { + background: inherit; + border: inherit; + color: inherit; + text-decoration: inherit; } input, textarea { @@ -43,51 +31,86 @@ ul, li { list-style: none; } -.model, .none { display: none } +[contenteditable=true]:empty:before { + content: attr(placeholder); +} -.page { +.model, .none { display: none } +.hide { + opacity: 0; + visibility: hidden; + transition: all 2s; +} + +.full, .page { width: 100%; min-height: 100vh; } +.max { + max-width: 48em; +} +.min { + min-width: 12em; +} .pad { - margin: 5% auto; - min-width: 250px; width: 95%; - max-width: 50em; + margin: 5% auto; + max-width: 48em; + min-width: 12em; } .right { float: right; + text-align: right; } .left { float: left; + text-align: left; } .center { text-align: center; + vertical-align: middle; + margin-left: auto; + margin-right: auto; } .mid { margin-left: auto; margin-right: auto; } -.flush { - line-height: 0em; -} - -.rim { - margin: 2%; -} -.gap { - padding: 3%; -} -.ditch { - margin-top: 2em; - margin-bottom: 2em; +.top { + vertical-align: top; +} +.low { + vertical-align: bottom; } +.rim { margin: 2%; } +.gap { padding: 3%; } +.stack { line-height: 0; } +.crack { margin-bottom: 1%; } .sit { margin-bottom: 0; } + .row { width: 100%; } -.col { max-width: 33em; } +.col { + max-width: 24em; + min-width: 12em; +} + +.focus { + margin-left: auto; + margin-right: auto; + float: none; + clear: both; +} +.unit, .symbol { + display: inline-block; + vertical-align: inherit; +} + +.leak { overflow: visible; } +.hold { overflow: hidden; } + .act { display: block; font-weight: normal; @@ -96,73 +119,41 @@ ul, li { transition: all 0.3s; cursor: pointer; } -.symbol { - display: inline-block; -} + +.sap { border-radius: 0.1em; } +.jot { border-bottom: 1px dashed #95B2CA; } .loud { font-size: 150%; } -.jot { - border-bottom: 1px dashed #95B2CA; -} -.sap { - border-radius: 0.1em; +.shout { + font-size: 36pt; + font-size: 6.5vmax; } -.red { - background: #ea3224; -} -.green { - background: #33cc33; -} -.blue { - background: #4D79D8; -} -.yellow { - background: #f2b919; -} -.black { - background: black; -} -.white { - background: white; -} +.red { background: #ea3224; } +.green { background: #33cc33; } +.blue { background: #4D79D8; } +.yellow { background: #f2b919; } +.black { background: black; } +.white { background: white; } -.shade { - background: rgba(0%, 0%, 0%, 0.1); -} -.tint { - background: rgba(100%, 100%, 100%, 0.1); -} +.shade { background: rgba(0%, 0%, 0%, 0.1); } +.tint { background: rgba(100%, 100%, 100%, 0.1); } -.redt { - color: #ea3224; -} -.greent { - color: #33cc33; -} -.bluet { - color: #4D79D8; -} -.yellowt { - color: #f2b919; -} -.blackt { - color: black; -} -.whitet { - color: white; -} +.redt { color: #ea3224; } +.greent { color: #33cc33; } +.bluet { color: #4D79D8; } +.yellowt { color: #f2b919; } +.blackt { color: black; } +.whitet { color: white; } .hue { background: #4D79D8; -webkit-animation: hue 900s infinite; animation: hue 900s infinite; -} - -@keyframes hue { +} @keyframes hue { 0% {background-color: #4D79D8;} 25% {background-color: #33cc33;} 50% {background-color: #f2b919;} @@ -177,12 +168,10 @@ ul, li { } .huet { - color: #4D79D8; + color: #4D79D8; -webkit-animation: huet 900s infinite; animation: huet 900s infinite; -} - -@keyframes huet { +} @keyframes huet { 0% {color: #4D79D8;} 25% {color: #33cc33;} 50% {color: #f2b919;} @@ -200,9 +189,7 @@ ul, li { background: #ea3224; -webkit-animation: hue2 900s infinite; animation: hue2 900s infinite; -} - -@keyframes hue2 { +} @keyframes hue2 { 0% {background-color: #ea3224;} 25% {background-color: #4D79D8;} 50% {background-color: #33cc33;} @@ -220,9 +207,7 @@ ul, li { color: #ea3224; -webkit-animation: huet2 900s infinite; animation: huet2 900s infinite; -} - -@keyframes huet2 { +} @keyframes huet2 { 0% {color: #ea3224;} 25% {color: #4D79D8;} 50% {color: #33cc33;} @@ -240,9 +225,7 @@ ul, li { background: #33cc33; -webkit-animation: hue3 900s infinite; animation: hue3 900s infinite; -} - -@keyframes hue3 { +} @keyframes hue3 { 0% {background-color: #33cc33;} 25% {background-color: #f2b919;} 50% {background-color: #ea3224;} @@ -260,9 +243,7 @@ ul, li { color: #33cc33; -webkit-animation: huet3 900s infinite; animation: huet3 900s infinite; -} - -@keyframes huet3 { +} @keyframes huet3 { 0% {color: #33cc33;} 25% {color: #f2b919;} 50% {color: #ea3224;} @@ -280,9 +261,7 @@ ul, li { background: #f2b919; -webkit-animation: hue4 900s infinite; animation: hue4 900s infinite; -} - -@keyframes hue4 { +} @keyframes hue4 { 0% {background-color: #f2b919;} 25% {background-color: #ea3224;} 50% {background-color: #4D79D8;} @@ -300,9 +279,7 @@ ul, li { color: #f2b919; -webkit-animation: huet4 900s infinite; animation: huet4 900s infinite; -} - -@keyframes huet4 { +} @keyframes huet4 { 0% {color: #f2b919;} 25% {color: #ea3224;} 50% {color: #4D79D8;} @@ -318,11 +295,22 @@ ul, li { .pulse { animation: pulse 2s infinite; -} - -@keyframes pulse -{ +} @keyframes pulse { 0% {opacity: 1;} 50% {opacity: 0.5;} 100% {opacity: 1;} +} + +.joy { + width: 100px; + height: 100px; + position: absolute; + background: url(https://cdn.jsdelivr.net/npm/gun/examples/pop.png) no-repeat; + background-position: -2800px 0; + pointer-events: none; + z-index: 999999999; + animation: joy 1s steps(28); +} @keyframes joy { + 0% {background-position: 0 0;} + 100% {background-position: -2800px 0;} } \ No newline at end of file diff --git a/examples/vue/index.html b/examples/vue/index.html index c00914bd..c7c51c7b 100644 --- a/examples/vue/index.html +++ b/examples/vue/index.html @@ -1,131 +1,178 @@ - - Gun plugin for Vue - - - - - - -
    - This is example of simple Vue plugin. It works exatcly same as the Vue instance data property, but the name is gunData.
    - The cool part is that every property in the gunData is realtime synced via gunDB to every other page viewer!
    - - - - - - -
    - Vue instance data: -
    {{ $data }}
    -
    - test
    - arr[1]
    - obj.a
    - deepObj.level1.prop
    - deepObj.level1.arr[1]
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    - + + + +
    + This is example of simple Vue plugin. It works exatcly same as the Vue instance data property, but the name is gunData.
    + The cool part is that every property in the gunData is realtime synced via gunDB to every other page viewer!
    + + + + + + +
    + Vue instance data: +
    
    +        
    + test
    + arr[1]
    + obj.a
    + deepObj.level1.prop
    + deepObj.level1.arr[1]
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    + - - + + + diff --git a/gun.js b/gun.js index d2b6b076..464202f7 100644 --- a/gun.js +++ b/gun.js @@ -1,28 +1,28 @@ ;(function(){ - /* UNBUILD */ - var root; - if(typeof window !== "undefined"){ root = window } - if(typeof global !== "undefined"){ root = global } - root = root || {}; - var console = root.console || {log: function(){}}; - function USE(arg){ - return arg.slice? USE[R(arg)] : function(mod, path){ - arg(mod = {exports: {}}); - USE[R(path)] = mod.exports; - } - function R(p){ - return p.split('/').slice(-1).toString().replace('.js',''); - } - } - if(typeof module !== "undefined"){ var common = module } - /* UNBUILD */ + /* UNBUILD */ + var root; + if(typeof window !== "undefined"){ root = window } + if(typeof global !== "undefined"){ root = global } + root = root || {}; + var console = root.console || {log: function(){}}; + function USE(arg, req){ + return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){ + arg(mod = {exports: {}}); + USE[R(path)] = mod.exports; + } + function R(p){ + return p.split('/').slice(-1).toString().replace('.js',''); + } + } + if(typeof module !== "undefined"){ var common = module } + /* UNBUILD */ ;USE(function(module){ // Generic javascript utilities. var Type = {}; //Type.fns = Type.fn = {is: function(fn){ return (!!fn && fn instanceof Function) }} - Type.fns = Type.fn = {is: function(fn){ return (!!fn && 'function' == typeof fn) }} + Type.fn = {is: function(fn){ return (!!fn && 'function' == typeof fn) }} Type.bi = {is: function(b){ return (b instanceof Boolean || typeof b == 'boolean') }} Type.num = {is: function(n){ return !list_is(n) && ((n - parseFloat(n) + 1) >= 0 || Infinity === n || -Infinity === n) }} Type.text = {is: function(t){ return (typeof t == 'string') }} @@ -169,16 +169,16 @@ // On event emitter generic javascript utility. module.exports = function onto(tag, arg, as){ if(!tag){ return {to: onto} } - var tag = (this.tag || (this.tag = {}))[tag] || + var u, tag = (this.tag || (this.tag = {}))[tag] || (this.tag[tag] = {tag: tag, to: onto._ = { next: function(arg){ var tmp; - if((tmp = this.to)){ + if((tmp = this.to)){ tmp.next(arg); }} }}); if(arg instanceof Function){ var be = { - off: onto.off || + off: onto.off || (onto.off = function(){ if(this.next === onto._.next){ return !0 } if(this === this.the.last){ @@ -200,7 +200,7 @@ (be.back = tag.last || tag).to = be; return tag.last = be; } - (tag = tag.to).next(arg); + if((tag = tag.to) && u !== arg){ tag.next(arg) } return tag; }; })(USE, './onto'); @@ -261,14 +261,14 @@ if(v === Infinity){ return false } // we want this to be, but JSON does not support it, sad face. if(text_is(v) // by "text" we mean strings. || bi_is(v) // by "binary" we mean boolean. - || num_is(v)){ // by "number" we mean integers or decimals. + || num_is(v)){ // by "number" we mean integers or decimals. return true; // simple values are valid. } - return Val.rel.is(v) || false; // is the value a soul relation? Then it is valid and return it. If not, everything else remaining is an invalid data type. Custom extensions can be built on top of these primitives to support other types. + 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.rel = {_: '#'}; + 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,9 +287,9 @@ } } }()); - 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.rel._, u; + var rel_ = Val.link._, u; var bi_is = Type.bi.is; var num_is = Type.num.is; var text_is = Type.text.is; @@ -309,7 +309,7 @@ n._[soul_] = o.soul || n._[soul_] || text_random(); // put the soul on it. return n; } - Node.soul._ = Val.rel._; + Node.soul._ = Val.link._; ;(function(){ Node.is = function(n, cb, as){ var s; // checks to see if an object is a valid node. if(!obj_is(n)){ return false } // must be an object. @@ -361,11 +361,11 @@ var Node = USE('./node'); function State(){ var t; - if(perf){ - t = start + perf.now(); - } else { + /*if(perf){ + t = start + perf.now(); // Danger: Accuracy decays significantly over time, even if precise. + } else {*/ t = time(); - } + //} if(last < t){ return N = 0, last = t + State.drift; } @@ -384,10 +384,10 @@ State.ify = function(n, k, s, v, soul){ // put a key's state on a node. if(!n || !n[N_]){ // reject if it is not node-like. if(!soul){ // unless they passed a soul - return; + return; } n = Node.soul.ify(n, soul); // then make it so! - } + } var tmp = obj_as(n[N_], State._); // grab the states data. if(u !== k && k !== N_){ if(num_is(s)){ @@ -400,7 +400,7 @@ return n; } State.to = function(from, k, to){ - var val = from[k]; + var val = (from||{})[k]; if(obj_is(val)){ val = obj_copy(val); } @@ -473,8 +473,9 @@ 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 || {}; env.seen = env.seen || []; env.as = env.as || as; @@ -487,24 +488,26 @@ 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.link = at.link || Val.link.ify(Node.soul(at.node)); + if(at.obj !== env.shell){ + env.graph[Val.link.is(at.link)] = at.node; + } } return at; } function map(v,k,n){ var at = this, env = at.env, is, tmp; - if(Node._ === k && obj_has(v,Val.rel._)){ + if(Node._ === k && obj_has(v,Val.link._)){ return n._; // TODO: Bug? } if(!(is = valid(v,k,n, at,env))){ return } if(!k){ at.node = at.node || n || {}; - if(obj_has(v, Node._)){ + 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); @@ -523,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.rel.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]; @@ -570,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; } @@ -610,7 +613,7 @@ if(!cb){ return id } var to = this.on(id, cb, as); to.err = to.err || setTimeout(function(){ - to.next({err: "Error: No ACK received yet."}); + to.next({err: "Error: No ACK received yet.", lack: true}); to.off(); }, (this.opt||{}).lack || 9000); return id; @@ -635,7 +638,7 @@ 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; @@ -652,12 +655,12 @@ ;USE(function(module){ function Gun(o){ - if(o instanceof Gun){ return (this._ = {gun: this}).gun } + if(o instanceof Gun){ return (this._ = {gun: this, $: this}).$ } if(!(this instanceof Gun)){ return new Gun(o) } - return Gun.create(this._ = {gun: this, opt: o}); + return Gun.create(this._ = {gun: this, $: this, opt: o}); } - Gun.is = function(gun){ return (gun instanceof Gun) } + Gun.is = function($){ return ($ instanceof Gun) || ($ && $._ && ($ === $._.$)) || false } Gun.version = 0.9; @@ -682,46 +685,51 @@ at.on = at.on || Gun.on; at.ask = at.ask || Gun.ask; at.dup = at.dup || Gun.dup(); - var gun = at.gun.opt(at.opt); + var gun = at.$.opt(at.opt); if(!at.once){ at.on('in', root, at); - at.on('out', root, at); + at.on('out', root, {at: at, out: root}); + Gun.on('create', at); + at.on('create', at); } at.once = 1; return gun; } function root(msg){ - //console.log("add to.next(at)"); // TODO: MISSING FEATURE!!! - var ev = this, at = ev.as, gun = at.gun, dup, tmp; - //if(!msg.gun){ msg.gun = at.gun } + //add to.next(at); // TODO: MISSING FEATURE!!! + var ev = this, as = ev.as, at = as.at || as, gun = at.$, dup, tmp; if(!(tmp = msg['#'])){ tmp = msg['#'] = text_rand(9) } - if((dup = at.dup).check(tmp)){ return } + if((dup = at.dup).check(tmp)){ + if(as.out === msg.out){ + msg.out = u; + ev.to.next(msg); + } + return; + } dup.track(tmp); - //msg = obj_to(msg);//, {gun: at.gun}); // can we delete this now? if(!at.ask(msg['@'], msg)){ if(msg.get){ - Gun.on.get(msg, gun); - //at.on('get', get(msg)); + Gun.on.get(msg, gun); //at.on('get', get(msg)); } if(msg.put){ - Gun.on.put(msg, gun); - //at.on('put', put(msg)); + Gun.on.put(msg, gun); //at.on('put', put(msg)); } } - at.on('out', msg); + ev.to.next(msg); + if(!as.out){ + msg.out = root; + at.on('out', msg); + } } }()); ;(function(){ Gun.on.put = function(msg, gun){ - var at = gun._, ctx = {gun: gun, graph: at.graph, put: {}, map: {}, souls: {}, machine: Gun.state(), ack: msg['@']}; + var at = gun._, ctx = {$: gun, graph: at.graph, put: {}, map: {}, souls: {}, machine: Gun.state(), ack: msg['@'], cat: at, stop: {}}; if(!Gun.graph.is(msg.put, null, verify, ctx)){ ctx.err = "Error: Invalid graph!" } if(ctx.err){ return at.on('in', {'@': msg['#'], err: Gun.log(ctx.err) }) } obj_map(ctx.put, merge, ctx); - if(!ctx.async){ - at.stop = {}; // temporary fix till a better solution? - obj_map(ctx.map, map, ctx) - } + if(!ctx.async){ obj_map(ctx.map, map, ctx) } if(u !== ctx.defer){ setTimeout(function(){ Gun.on.put(msg, gun); @@ -746,31 +754,33 @@ ctx.souls[soul] = true; } function merge(node, soul){ - var ctx = this, cat = ctx.gun._, at = (cat.next || empty)[soul]; + var ctx = this, cat = ctx.$._, at = (cat.next || empty)[soul]; if(!at){ - ctx.souls[soul] = false; - return + if(!(cat.opt||empty).super){ + ctx.souls[soul] = false; + return; + } + at = (ctx.$.get(soul)._); } var msg = ctx.map[soul] = { put: node, get: soul, - gun: at.gun + $: at.$ }, as = {ctx: ctx, msg: msg}; ctx.async = !!cat.tag.node; if(ctx.ack){ msg['@'] = ctx.ack } obj_map(node, each, as); if(!ctx.async){ return } if(!ctx.and){ - // If it is async, we only need to setup on listener per context (ctx) + // If it is async, we only need to setup one listener per context (ctx) cat.on('node', function(m){ this.to.next(m); // make sure to call other context's listeners. if(m !== ctx.map[m.get]){ return } // filter out events not from this context! ctx.souls[m.get] = false; // set our many-async flag - obj_map(m.put, aeach, m); // merge into view + obj_map(m.put, patch, m); // merge into view if(obj_map(ctx.souls, function(v){ if(v){ return v } })){ return } // if flag still outstanding, keep waiting. if(ctx.c){ return } ctx.c = 1; // failsafe for only being called once per context. this.off(); - cat.stop = {}; // temporary fix till a better solution? obj_map(ctx.map, map, ctx); // all done, trigger chains. }); } @@ -778,27 +788,28 @@ cat.on('node', msg); // each node on the current context's graph needs to be emitted though. } function each(val, key){ - var ctx = this.ctx, graph = ctx.graph, msg = this.msg, soul = msg.get, node = msg.put, at = (msg.gun._), tmp; + var ctx = this.ctx, graph = ctx.graph, msg = this.msg, soul = msg.get, node = msg.put, at = (msg.$._), tmp; graph[soul] = Gun.state.to(node, key, graph[soul]); if(ctx.async){ return } at.put = Gun.state.to(node, key, at.put); } - function aeach(val, key){ - var msg = this, node = msg.put, at = (msg.gun._); + function patch(val, key){ + var msg = this, node = msg.put, at = (msg.$._); at.put = Gun.state.to(node, key, at.put); } function map(msg, soul){ - if(!msg.gun){ return } - //console.log('map ->', soul, msg.put); - (msg.gun._).on('in', msg); + if(!msg.$){ return } + this.cat.stop = this.stop; // temporary fix till a better solution? + (msg.$._).on('in', msg); + this.cat.stop = null; // temporary fix till a better solution? } Gun.on.get = function(msg, gun){ - var root = gun._, 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(!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) } + if('string' != typeof has || !obj_has(node, has)){ return root.on('get', msg) } node = Gun.state.to(node, has); // If we have a key in-memory, do we really need to fetch? // Maybe... in case the in-memory key we have is a local write @@ -807,16 +818,14 @@ node = Gun.obj.copy(node); } node = Gun.graph.node(node); - //tmp = at.ack; + tmp = (at||empty).ack; root.on('in', { '@': msg['#'], how: 'mem', put: node, - gun: gun + $: gun }); - //if(0 < tmp){ - // return; - //} + //if(0 < tmp){ return } root.on('get', msg); } }()); @@ -846,7 +855,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.rel.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) }; @@ -857,8 +866,8 @@ ;"Please do not remove these messages unless you are paying for a monthly sponsorship, thanks!"; Gun.log.once("welcome", "Hello wonderful person! :) Thanks for using GUN, feel free to ask for help on https://gitter.im/amark/gun and ask StackOverflow questions tagged with 'gun'!"); ;"Please do not remove these messages unless you are paying for a monthly sponsorship, thanks!"; - - if(typeof window !== "undefined"){ window.Gun = Gun } + + if(typeof window !== "undefined"){ (window.GUN = window.Gun = Gun).window = window } try{ if(typeof common !== "undefined"){ common.exports = Gun } }catch(e){} module.exports = Gun; @@ -867,10 +876,8 @@ if(ctx.once){ return } ctx.on('node', function(msg){ var to = this.to; - //console.log(">>>", msg.put); //Gun.node.is(msg.put, function(v,k){ msg.put[k] = v + v }); setTimeout(function(){ - //console.log("<<<<<", msg.put); to.next(msg); },1); }); @@ -882,10 +889,10 @@ Gun.chain.back = function(n, opt){ var tmp; n = n || 1; if(-1 === n || Infinity === n){ - return this._.root.gun; + return this._.root.$; } else if(1 === n){ - return (this._.back || this._).gun; + return (this._.back || this._).$; } var gun = this, at = gun._; if(typeof n === 'string'){ @@ -900,18 +907,18 @@ return opt? gun : tmp; } else if((tmp = at.back)){ - return tmp.gun.back(n, opt); + return tmp.$.back(n, opt); } return; } if(n instanceof Function){ var yes, tmp = {back: at}; while((tmp = tmp.back) - && !(yes = n(tmp, opt))){} + && u === (yes = n(tmp, opt))){} return yes; } if(Gun.num.is(n)){ - return (at.back || at).gun.back(n - 1); + return (at.back || at).$.back(n - 1); } return this; } @@ -923,11 +930,11 @@ // is complicated and was extremely hard to build. If you port GUN to another // language, consider implementing an easier API to build. var Gun = USE('./root'); - Gun.chain.chain = function(){ - var at = this._, chain = new this.constructor(this), cat = chain._, root; + Gun.chain.chain = function(sub){ + var gun = this, at = gun._, chain = new (sub || gun).constructor(gun), cat = chain._, root; cat.root = root = at.root; cat.id = ++root.once; - cat.back = this._; + cat.back = gun._; cat.on = Gun.on; cat.on('in', input, cat); // For 'in' if I add my own listeners to each then I MUST do it before in gets called. If I listen globally for all incoming data instead though, regardless of individual listeners, I can transform the data there and then as well. cat.on('out', output, cat); // However for output, there isn't really the global option. I must listen by adding my own listener individually BEFORE this one is ever called. @@ -935,55 +942,69 @@ } function output(msg){ - var put, get, at = this.as, back = at.back, root = at.root; - if(!msg.gun){ msg.gun = at.gun } + var put, get, at = this.as, back = at.back, root = at.root, tmp; + if(!msg.$){ msg.$ = at.$ } this.to.next(msg); if(get = msg.get){ /*if(u !== at.put){ at.on('in', at); return; }*/ + if(at.lex){ msg.get = obj_to(at.lex, msg.get) } if(get['#'] || at.soul){ get['#'] = get['#'] || at.soul; msg['#'] || (msg['#'] = text_rand(9)); - back = (root.gun.get(get['#'])._); + back = (root.$.get(get['#'])._); if(!(get = get['.'])){ + tmp = back.ack; + if(!tmp){ back.ack = -1 } if(obj_has(back, 'put')){ - //if(u !== back.put){ back.on('in', back); } - if(back.ack){ return } - msg.gun = back.gun; - back.ack = -1; + if(tmp){ return } + msg.$ = back.$; } else if(obj_has(back.put, get)){ + put = (back.$.get(get)._); + if(!(tmp = put.ack)){ put.ack = -1 } back.on('in', { - gun: back.gun, + $: back.$, put: Gun.state.to(back.put, get), get: back.get }); - return; + if(tmp){ return } + } else + if('string' != typeof get){ + var put = {}, meta = (back.put||{})._; + Gun.obj.map(back.put, function(v,k){ + if(!Gun.text.match(k, get)){ return } + put[k] = v; + }) + if(!Gun.obj.empty(put)){ + put._ = meta; + back.on('in', {$: back.$, put: put, get: back.get}) + } } root.ask(ack, msg); return root.on('in', msg); } - if(root.now){ - root.now[at.id] = root.now[at.id] || true; - } + if(root.now){ root.now[at.id] = root.now[at.id] || true; at.pass = {} } if(get['.']){ if(at.get){ - msg = {get: {'.': at.get}, gun: at.gun}; - (back.ask || (back.ask = {}))[at.get] = msg.gun._; // TODO: PERFORMANCE? More elegant way? + msg = {get: {'.': at.get}, $: at.$}; + //if(back.ask || (back.ask = {})[at.get]){ return } + (back.ask || (back.ask = {})); + back.ask[at.get] = msg.$._; // TODO: PERFORMANCE? More elegant way? return back.on('out', msg); } - msg = {get: {}, gun: at.gun}; + msg = {get: {}, $: at.$}; return back.on('out', msg); } at.ack = at.ack || -1; if(at.get){ - msg.gun = at.gun; + msg.$ = at.$; get['.'] = at.get; - (back.ask || (back.ask = {}))[at.get] = msg.gun._; // TODO: PERFORMANCE? More elegant way? + (back.ask || (back.ask = {}))[at.get] = msg.$._; // TODO: PERFORMANCE? More elegant way? return back.on('out', msg); } } @@ -991,25 +1012,23 @@ } function input(msg){ - var ev = this, cat = this.as, gun = msg.gun, at = gun._, change = msg.put, rel, tmp; + var eve = this, cat = eve.as, root = cat.root, gun = msg.$, at = (gun||empty)._ || empty, change = msg.put, rel, tmp; if(cat.get && msg.get !== cat.get){ msg = obj_to(msg, {get: cat.get}); } if(cat.has && at !== cat){ - msg = obj_to(msg, {gun: cat.gun}); + msg = obj_to(msg, {$: cat.$}); if(at.ack){ cat.ack = at.ack; //cat.ack = cat.ack || at.ack; } } - if(node_ === cat.get && change && change['#']){ - // TODO: Potential bug? What if (soul.has = pointer) gets changed to (soul.has = primitive), we still need to clear out / wipe /reset (soul.has._) to have _id = nothing, or puts might have false positives (revert back to old soul). - cat._id = change['#']; - } if(u === change){ - ev.to.next(msg); - if(cat.soul){ return } // TODO: BUG, I believe the fresh input refactor caught an edge case that a `gun.get('soul').get('key')` that points to a soul that doesn't exist will not trigger val/get etc. - echo(cat, msg, ev); + tmp = at.put; + eve.to.next(msg); + if(cat.soul){ return } // TODO: BUG, I believee the fresh input refactor caught an edge case that a `gun.get('soul').get('key')` that points to a soul that doesn't exist will not trigger val/get etc. + if(u === tmp && u !== at.put){ return } + echo(cat, msg, eve); if(cat.has){ not(cat, msg); } @@ -1018,107 +1037,117 @@ return; } if(cat.soul){ - ev.to.next(msg); - echo(cat, msg, ev); - obj_map(change, map, {at: msg, cat: cat}); + eve.to.next(msg); + echo(cat, msg, eve); + if(cat.next){ obj_map(change, map, {msg: msg, cat: cat}) } return; } - if(!(rel = Gun.val.rel.is(change))){ + if(!(rel = Gun.val.link.is(change))){ if(Gun.val.is(change)){ if(cat.has || cat.soul){ not(cat, msg); } else if(at.has || at.soul){ - (at.echo || (at.echo = {}))[cat.id] = cat; + (at.echo || (at.echo = {}))[cat.id] = at.echo[at.id] || cat; (cat.map || (cat.map = {}))[at.id] = cat.map[at.id] || {at: at}; //if(u === at.put){ return } // Not necessary but improves performance. If we have it but at does not, that means we got things out of order and at will get it. Once at gets it, it will tell us again. } - ev.to.next(msg); - echo(cat, msg, ev); + eve.to.next(msg); + echo(cat, msg, eve); return; } if(cat.has && at !== cat && obj_has(at, 'put')){ cat.put = at.put; }; if((rel = Gun.node.soul(change)) && at.has){ - at.put = (cat.root.gun.get(rel)._).put; + at.put = (cat.root.$.get(rel)._).put; } - ev.to.next(msg); - echo(cat, msg, ev); + tmp = (root.stop || {})[at.id]; + //if(tmp && tmp[cat.id]){ } else { + eve.to.next(msg); + //} relate(cat, msg, at, rel); - obj_map(change, map, {at: msg, cat: cat}); + echo(cat, msg, eve); + if(cat.next){ obj_map(change, map, {msg: msg, cat: cat}) } return; } + var was = root.stop; + tmp = root.stop || {}; + tmp = tmp[at.id] || (tmp[at.id] = {}); + //if(tmp[cat.id]){ return } + tmp.is = tmp.is || at.put; + tmp[cat.id] = at.put || true; + //if(root.stop){ + eve.to.next(msg) + //} relate(cat, msg, at, rel); - ev.to.next(msg); - echo(cat, msg, ev); + echo(cat, msg, eve); } function relate(at, msg, from, rel){ if(!rel || node_ === at.get){ return } - var tmp = (at.root.gun.get(rel)._); + var tmp = (at.root.$.get(rel)._); if(at.has){ from = tmp; - } else + } else if(from.has){ relate(from, msg, from, rel); } if(from === at){ return } - (from.echo || (from.echo = {}))[at.id] = at; + if(!from.$){ from = {} } + (from.echo || (from.echo = {}))[at.id] = from.echo[at.id] || at; if(at.has && !(at.map||empty)[from.id]){ // if we haven't seen this before. not(at, msg); } - tmp = (at.map || (at.map = {}))[from.id] = at.map[from.id] || {at: from}; - var now = at.root.now; - //now = now || at.root.stop; - if(rel === tmp.rel){ - // NOW is a hack to get synchronous replies to correctly call. - // and STOP is a hack to get async behavior to correctly call. - // neither of these are ideal, need to be fixed without hacks, - // but for now, this works for current tests. :/ - if(!now){ + tmp = from.id? ((at.map || (at.map = {}))[from.id] = at.map[from.id] || {at: from}) : {}; + if(rel === tmp.link){ + if(!(tmp.pass || at.pass)){ return; - /*var stop = at.root.stop; - if(!stop){ return } - if(stop[at.id] === rel){ return } - stop[at.id] = rel;*/ - } else { - if(u === now[at.id]){ return } - if((now._ || (now._ = {}))[at.id] === rel){ return } - now._[at.id] = rel; } } - ask(at, tmp.rel = rel); + if(at.pass){ + Gun.obj.map(at.map, function(tmp){ tmp.pass = true }) + obj_del(at, 'pass'); + } + if(tmp.pass){ obj_del(tmp, 'pass') } + if(at.has){ at.link = rel } + ask(at, tmp.link = rel); } function echo(at, msg, ev){ if(!at.echo){ return } // || node_ === at.get ? - if(at.has){ msg = obj_to(msg, {event: ev}) } + //if(at.has){ msg = obj_to(msg, {event: ev}) } obj_map(at.echo, reverb, msg); } function reverb(to){ + if(!to || !to.on){ return } to.on('in', this); } function map(data, key){ // Map over only the changes on every update. - var cat = this.cat, next = cat.next || empty, via = this.at, chain, at, tmp; + var cat = this.cat, next = cat.next || empty, via = this.msg, chain, at, tmp; if(node_ === key && !next[key]){ return } if(!(at = next[key])){ return; } - //if(data && data[_soul] && (tmp = Gun.val.rel.is(data)) && (tmp = (cat.root.gun.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; } - chain = at.gun; - } else { - chain = via.gun.get(key); + chain = at.$; + } else + if(tmp = via.$){ + tmp = (chain = via.$.get(key))._; + if(u === tmp.put || !Gun.val.link.is(data)){ + tmp.put = data; + } } at.on('in', { put: data, get: key, - gun: chain, + $: chain, via: via }); } @@ -1126,103 +1155,117 @@ if(!(at.has || at.soul)){ return } var tmp = at.map, root = at.root; at.map = null; - if(!root.now || !root.now[at.id]){ - if((!msg['@']) && null === tmp){ return } + if(at.has){ + if(at.dub && at.root.stop){ at.dub = null } + at.link = null; } - if(u === tmp && Gun.val.rel.is(at.put)){ return } // TODO: Bug? Threw second condition in for a particular test, not sure if a counter example is tested though. + //if(!root.now || !root.now[at.id]){ + if(!at.pass){ + if((!msg['@']) && null === tmp){ return } + //obj_del(at, 'pass'); + } + if(u === tmp && Gun.val.link.is(at.put)){ return } // This prevents the very first call of a thing from triggering a "clean up" call. // TODO: link.is(at.put) || !val.is(at.put) ? obj_map(tmp, function(proxy){ if(!(proxy = proxy.at)){ return } obj_del(proxy.echo, at.id); }); + tmp = at.put; obj_map(at.next, function(neat, key){ + if(u === tmp && u !== at.put){ return true } neat.put = u; if(neat.ack){ neat.ack = -1; } neat.on('in', { get: key, - gun: neat.gun, + $: neat.$, put: u }); }); } function ask(at, soul){ - var tmp = (at.root.gun.get(soul)._); + var tmp = (at.root.$.get(soul)._); if(at.ack){ tmp.on('out', {get: {'#': soul}}); if(!at.ask){ return } // TODO: PERFORMANCE? More elegant way? } - obj_map(at.ask || at.next, function(neat, key){ - //(tmp.gun.get(key)._).on('out', {get: {'#': soul, '.': key}}); - //tmp.on('out', {get: {'#': soul, '.': key}}); + tmp = at.ask; Gun.obj.del(at, 'ask'); + obj_map(tmp || at.next, function(neat, key){ neat.on('out', {get: {'#': soul, '.': key}}); - //at.on('out', {get: {'#': soul, '.': key}}); }); Gun.obj.del(at, 'ask'); // TODO: PERFORMANCE? More elegant way? } function ack(msg, ev){ - var as = this.as, get = as.get || empty, at = as.gun._, tmp = (msg.put||empty)[get['#']]; - if(at.ack){ at.ack = (at.ack + 1) || 1 } - if(!msg.put /*|| node_ == get['.']*/ || (get['.'] && !obj_has(tmp, at.get))){ + var as = this.as, get = as.get || empty, at = as.$._, tmp = (msg.put||empty)[get['#']]; + if(at.ack){ at.ack = (at.ack + 1) || 1; } + if(!msg.put || ('string' == typeof get['.'] && !obj_has(tmp, at.get))){ if(at.put !== u){ return } - //at.ack = 0; at.on('in', { get: at.get, put: at.put = u, - gun: at.gun, + $: at.$, '@': msg['@'] - }) + }); return; } if(node_ == get['.']){ // is this a security concern? - at.on('in', {get: at.get, put: tmp[at.get], gun: at.gun, '@': msg['@']}); + at.on('in', {get: at.get, put: Gun.val.link.ify(get['#']), $: at.$, '@': msg['@']}); return; } - //if(/*!msg.gun &&*/ !get['.'] && get['#']){ at.ack = (at.ack + 1) || 1 } - //msg = obj_to(msg); - msg.gun = at.root.gun; - //Gun.on('put', at); - Gun.on.put(msg, at.root.gun); + Gun.on.put(msg, at.root.$); } var empty = {}, u; var obj = Gun.obj, obj_has = obj.has, obj_put = obj.put, obj_del = obj.del, obj_to = obj.to, obj_map = obj.map; var text_rand = Gun.text.random; - var _soul = Gun.val.rel._, node_ = Gun.node._; + var _soul = Gun.val.link._, node_ = Gun.node._; })(USE, './chain'); ;USE(function(module){ var Gun = USE('./root'); Gun.chain.get = function(key, cb, as){ - var gun; + var gun, tmp; if(typeof key === 'string'){ var back = this, cat = back._; - var next = cat.next || empty, tmp; + var next = cat.next || empty; if(!(gun = next[key])){ gun = cache(key, back); } - gun = gun.gun; + gun = gun.$; } else if(key instanceof Function){ + if(true === cb){ return soul(this, key, cb, as) } gun = this; var at = gun._, root = at.root, tmp = root.now, ev; as = cb || {}; + as.at = at; as.use = key; as.out = as.out || {}; as.out.get = as.out.get || {}; - ev = at.on('in', use, as); + (ev = at.on('in', use, as)).rid = rid; (root.now = {$:1})[as.now = at.id] = ev; + var mum = root.mum; root.mum = {}; at.on('out', as.out); + root.mum = mum; root.now = tmp; return gun; } else if(num_is(key)){ return this.get(''+key, cb, as); + } else + if(tmp = rel.is(key)){ + return this.get(tmp, cb, as); + } else + if(obj.is(key)){ + gun = this; + if(tmp = ((tmp = key['#'])||empty)['='] || tmp){ gun = gun.get(tmp) } + gun._.lex = key; + return gun; } else { (as = this.chain())._.err = {err: Gun.log('Invalid get request!', key)}; // CLEAN UP if(cb){ cb.call(as, as._.err) } return as; } - if(tmp = cat.stun){ // TODO: Refactor? + if(tmp = this._.stun){ // TODO: Refactor? gun._.stun = gun._.stun || tmp; } if(cb && cb instanceof Function){ @@ -1234,7 +1277,7 @@ var cat = back._, next = cat.next, gun = back.chain(), at = gun._; if(!next){ next = cat.next = {} } next[at.get = key] = at; - if(back === cat.root.gun){ + if(back === cat.root.$){ at.soul = key; } else if(cat.soul || cat.has){ @@ -1245,26 +1288,69 @@ } return at; } + function soul(gun, cb, opt, as){ + var cat = gun._, acks = 0, tmp; + if(tmp = cat.soul || cat.link || cat.dub){ return cb(tmp, as, cat), gun } + gun.get(function(msg, ev){ + if(u === msg.put && (tmp = (obj_map(cat.root.opt.peers, function(v,k,t){t(k)})||[]).length) && ++acks < tmp){ + return; + } + ev.rid(msg); + var at = ((at = msg.$) && at._) || {}; + tmp = at.link || at.soul || rel.is(msg.put) || node_soul(msg.put) || at.dub; + cb(tmp, as, msg, ev); + }, {out: {get: {'.':true}}}); + return gun; + } function use(msg){ - var ev = this, as = ev.as, gun = msg.gun, at = gun._, root = at.root, data = msg.put, tmp; - if((tmp = root.now) && ev !== tmp[as.now]){ - return ev.to.next(msg); - } - if(u === data){ - data = at.put; - } + var eve = this, as = eve.as, cat = as.at, root = cat.root, gun = msg.$, at = (gun||{})._ || {}, data = msg.put || at.put, tmp; + if((tmp = root.now) && eve !== tmp[as.now]){ return eve.to.next(msg) } + //console.log("USE:", cat.id, cat.soul, cat.has, cat.get, msg, root.mum); + //if(at.async && msg.root){ return } + //if(at.async === 1 && cat.async !== true){ return } + //if(root.stop && root.stop[at.id]){ return } root.stop && (root.stop[at.id] = true); + //if(!at.async && !cat.async && at.put && msg.put === at.put){ return } + //else if(!cat.async && msg.put !== at.put && root.stop && root.stop[at.id]){ return } root.stop && (root.stop[at.id] = true); + + + //root.stop && (root.stop.id = root.stop.id || Gun.text.random(2)); + //if((tmp = root.stop) && (tmp = tmp[at.id] || (tmp[at.id] = {})) && tmp[cat.id]){ return } tmp && (tmp[cat.id] = true); + if(eve.seen && at.id && eve.seen[at.id]){ return eve.to.next(msg) } + //if((tmp = root.stop)){ if(tmp[at.id]){ return } tmp[at.id] = msg.root; } // temporary fix till a better solution? if((tmp = data) && tmp[rel._] && (tmp = rel.is(tmp))){ - tmp = (at.root.gun.get(tmp)._); + tmp = ((msg.$$ = at.root.gun.get(tmp))._); if(u !== tmp.put){ - msg = obj_to(msg, {put: tmp.put}); + msg = obj_to(msg, {put: data = tmp.put}); } } - as.use(msg, msg.event || ev); - ev.to.next(msg); + if((tmp = root.mum) && at.id){ // TODO: can we delete mum entirely now? + var id = at.id + (eve.id || (eve.id = Gun.text.random(9))); + if(tmp[id]){ return } + if(u !== data && !rel.is(data)){ tmp[id] = true; } + } + as.use(msg, eve); + if(eve.stun){ + eve.stun = null; + return; + } + eve.to.next(msg); } - var obj = Gun.obj, obj_has = obj.has, obj_to = Gun.obj.to; + function rid(at){ + var cat = this.on; + if(!at || cat.soul || cat.has){ return this.off() } + if(!(at = (at = (at = at.$ || at)._ || at).id)){ return } + var map = cat.map, tmp, seen; + //if(!map || !(tmp = map[at]) || !(tmp = tmp.at)){ return } + if(tmp = (seen = this.seen || (this.seen = {}))[at]){ return true } + seen[at] = true; + return; + //tmp.echo[cat.id] = {}; // TODO: Warning: This unsubscribes ALL of this chain's listeners from this link, not just the one callback event. + //obj.del(map, at); // TODO: Warning: This unsubscribes ALL of this chain's listeners from this link, not just the one callback event. + return; + } + var obj = Gun.obj, obj_map = obj.map, obj_has = obj.has, obj_to = Gun.obj.to; var num_is = Gun.num.is; - var rel = Gun.val.rel, node_ = Gun.node._; + var rel = Gun.val.link, node_soul = Gun.node.soul, node_ = Gun.node._; var empty = {}, u; })(USE, './get'); @@ -1274,14 +1360,25 @@ // #soul.has=value>state // ~who#where.where=what>when@was // TODO: BUG! Put probably cannot handle plural chains! - var gun = this, at = (gun._), root = at.root.gun, tmp; + var gun = this, at = (gun._), root = at.root.$, ctx = root._, M = 100, tmp; + if(!ctx.puta){ if(tmp = ctx.puts){ if(tmp > M){ // without this, when synchronous, writes to a 'not found' pile up, when 'not found' resolves it recursively calls `put` which incrementally resolves each write. Stack overflow limits can be as low as 10K, so this limit is hardcoded to 1% of 10K. + (ctx.stack || (ctx.stack = [])).push([gun, data, cb, as]); + if(ctx.puto){ return } + ctx.puto = setTimeout(function drain(){ + var d = ctx.stack.splice(0,M), i = 0, at; ctx.puta = true; + while(at = d[i++]){ at[0].put(at[1], at[2], at[3]) } delete ctx.puta; + if(ctx.stack.length){ return ctx.puto = setTimeout(drain, 0) } + ctx.stack = ctx.puts = ctx.puto = null; + }, 0); + return gun; + } ++ctx.puts } else { ctx.puts = 1 } } as = as || {}; as.data = data; - as.gun = as.gun || gun; + as.via = as.$ = as.via || as.$ || gun; if(typeof cb === 'string'){ as.soul = cb; } else { - as.ack = cb; + as.ack = as.ack || cb; } if(at.soul){ as.soul = at.soul; @@ -1292,39 +1389,40 @@ if(as.res){ as.res() } return gun; } - as.soul = as.soul || (as.not = Gun.node.soul(as.data) || ((root._).opt.uuid || Gun.text.random)()); + as.soul = as.soul || (as.not = Gun.node.soul(as.data) || (as.via.back('opt.uuid') || Gun.text.random)()); if(!as.soul){ // polyfill async uuid for SEA - (root._).opt.uuid(function(err, soul){ // TODO: improve perf without anonymous callback + as.via.back('opt.uuid')(function(err, soul){ // TODO: improve perf without anonymous callback if(err){ return Gun.log(err) } // TODO: Handle error! - (as.ref||as.gun).put(as.data, as.soul = soul, as); + (as.ref||as.$).put(as.data, as.soul = soul, as); }); return gun; } - as.gun = gun = root.get(as.soul); - as.ref = as.gun; + as.$ = root.get(as.soul); + as.ref = as.$; ify(as); return gun; } if(Gun.is(data)){ - data.get('_').get(function(at, ev, tmp){ ev.off(); - if(!(tmp = at.gun) || !(tmp = tmp._.back) || !tmp.soul){ - return Gun.log("The reference you are saving is a", typeof at.put, '"'+ as.put +'", not a node (object)!'); + data.get(function(soul, o, msg){ + if(!soul){ + return Gun.log("The reference you are saving is a", typeof msg.put, '"'+ msg.put +'", not a node (object)!'); } - gun.put(Gun.val.rel.ify(tmp.soul), cb, as); - }); + gun.put(Gun.val.link.ify(soul), cb, as); + }, true); return gun; } - as.ref = as.ref || (root._ === (tmp = at.back))? gun : tmp.gun; + if(at.has && (tmp = Gun.val.link.is(data))){ at.dub = tmp } + as.ref = as.ref || (root._ === (tmp = at.back))? gun : tmp.$; if(as.ref._.soul && Gun.val.is(as.data) && at.get){ as.data = obj_put({}, at.get, as.data); as.ref.put(as.data, as.soul, as); return gun; } - as.ref.get('_').get(any, {as: as}); + as.ref.get(any, true, {as: as}); if(!as.out){ // TODO: Perf idea! Make a global lock, that blocks everything while it is on, but if it is on the lock it does the expensive lookup to see if it is a dependent write or not and if not then it proceeds full speed. Meh? For write heavy async apps that would be terrible. as.res = as.res || stun; // Gun.on.stun(as.ref); // TODO: BUG! Deal with locking? - as.gun._.stun = as.ref._.stun; + as.$._.stun = as.ref._.stun; } return gun; }; @@ -1362,30 +1460,33 @@ if(!as.graph || obj_map(as.stun, no)){ return } as.res = as.res || function(cb){ if(cb){ cb() } }; as.res(function(){ - var cat = (as.gun.back(-1)._), ask = cat.ask(function(ack){ - this.off(); // One response is good enough for us currently. Later we may want to adjust this. + var cat = (as.$.back(-1)._), ask = cat.ask(function(ack){ + cat.root.on('ack', ack); + if(ack.err){ Gun.log(ack) } + if(!ack.lack){ this.off() } // One response is good enough for us currently. Later we may want to adjust this. if(!as.ack){ return } as.ack(ack, this); + //--C; }, as.opt); + //C++; // NOW is a hack to get synchronous replies to correctly call. // and STOP is a hack to get async behavior to correctly call. // neither of these are ideal, need to be fixed without hacks, // but for now, this works for current tests. :/ - var tmp = cat.root.now; obj.del(cat.root, 'now'); cat.root.PUT = true; - var tmp2 = cat.root.stop; - (as.ref._).now = true; + var tmp = cat.root.now; obj.del(cat.root, 'now'); + var mum = cat.root.mum; cat.root.mum = {}; (as.ref._).on('out', { - gun: as.ref, put: as.out = as.env.graph, opt: as.opt, '#': ask + $: as.ref, put: as.out = as.env.graph, opt: as.opt, '#': ask }); - obj.del((as.ref._), 'now'); - obj.del((cat.root), 'PUT'); + cat.root.mum = mum? obj.to(mum, cat.root.mum) : mum; cat.root.now = tmp; - cat.root.stop = tmp2; }, as); if(as.res){ as.res() } } function no(v,k){ if(v){ return true } } + //console.debug(999,1); var C = 0; setInterval(function(){ try{ debug.innerHTML = C }catch(e){console.log(e)} }, 500); function map(v,k,n, at){ var as = this; + var is = Gun.is(v); if(k || !at.path.length){ return } (as.res||iife)(function(){ var path = at.path, ref = as.ref, opt = as.opt; @@ -1393,65 +1494,53 @@ for(i; i < l; i++){ ref = ref.get(path[i]); } - if(Gun.node.soul(at.obj)){ - var id = Gun.node.soul(at.obj) || (ref.back('opt.uuid') || Gun.text.random)(); - if(!id){ // polyfill async uuid for SEA - (as.stun = as.stun || {})[path] = true; // make DRY - ref.back('opt.uuid')(function(err, id){ // TODO: improve perf without anonymous callback - if(err){ return Gun.log(err) } // TODO: Handle error. - ref.back(-1).get(id); - at.soul(id); - as.stun[path] = false; - as.batch(); - }); - return; - } + if(is){ ref = v } + var id = (ref._).dub; + if(id || (id = Gun.node.soul(at.obj))){ ref.back(-1).get(id); at.soul(id); return; } (as.stun = as.stun || {})[path] = true; - ref.get('_').get(soul, {as: {at: at, as: as}}); + ref.get(soul, true, {as: {at: at, as: as, p:path}}); }, {as: as, at: at}); + //if(is){ return {} } } - function soul(msg, ev){ var as = this.as, cat = as.at; as = as.as; - //ev.stun(); // TODO: BUG!? - if(!msg.gun || !msg.gun._.back){ return } // TODO: Handle - var at = msg.gun._, at_ = at; - var _id = (msg.put||empty)['#']; - ev.off(); - at = (msg.gun._.back); // go up 1! - var id = id || Gun.node.soul(cat.obj) || Gun.node.soul(at.put) || Gun.val.rel.is(at.put) || _id || at_._id || (as.gun.back('opt.uuid') || Gun.text.random)(); // TODO: BUG!? Do we really want the soul of the object given to us? Could that be dangerous? + function soul(id, as, msg, eve){ + var as = as.as, cat = as.at; as = as.as; + var at = ((msg || {}).$ || {})._ || {}; + id = at.dub = at.dub || id || Gun.node.soul(cat.obj) || Gun.node.soul(msg.put || at.put) || Gun.val.link.is(msg.put || at.put) || (as.via.back('opt.uuid') || Gun.text.random)(); // TODO: BUG!? Do we really want the soul of the object given to us? Could that be dangerous? + if(eve){ eve.stun = true } if(!id){ // polyfill async uuid for SEA - at.gun.back('opt.uuid')(function(err, id){ // TODO: improve perf without anonymous callback + at.via.back('opt.uuid')(function(err, id){ // TODO: improve perf without anonymous callback if(err){ return Gun.log(err) } // TODO: Handle error. - solve(at, at_._id = at_._id || id, cat, as); + solve(at, at.dub = at.dub || id, cat, as); }); return; } - solve(at, at_._id = at_._id || id, cat, as); + solve(at, at.dub = id, cat, as); } function solve(at, id, cat, as){ - at.gun.back(-1).get(id); + at.$.back(-1).get(id); cat.soul(id); as.stun[cat.path] = false; as.batch(); } - function any(at, ev){ - var as = this.as; - if(!at.gun || !at.gun._){ return } // TODO: Handle - if(at.err){ // TODO: Handle + function any(soul, as, msg, eve){ + as = as.as; + if(!msg.$ || !msg.$._){ return } // TODO: Handle + if(msg.err){ // TODO: Handle console.log("Please report this as an issue! Put.any.err"); return; } - var cat = (at.gun._.back), data = cat.put, opt = as.opt||{}, root, tmp; + var at = (msg.$._), data = at.put, opt = as.opt||{}, root, tmp; if((tmp = as.ref) && tmp._.now){ return } - ev.off(); - if(as.ref !== as.gun){ - tmp = (as.gun._).get || cat.get; + if(eve){ eve.stun = true } + if(as.ref !== as.$){ + tmp = (as.$._).get || at.get; if(!tmp){ // TODO: Handle console.log("Please report this as an issue! Put.no.get"); // TODO: BUG!?? return; @@ -1460,30 +1549,30 @@ tmp = null; } if(u === data){ - if(!cat.get){ return } // TODO: Handle - if(!cat.soul){ - tmp = cat.gun.back(function(at){ - if(at.soul){ return at.soul } + if(!at.get){ return } // TODO: Handle + if(!soul){ + tmp = at.$.back(function(at){ + if(at.link || at.soul){ return at.link || at.soul } as.data = obj_put({}, at.get, as.data); }); } - tmp = tmp || cat.get; - cat = (cat.root.gun.get(tmp)._); - as.not = as.soul = tmp; + tmp = tmp || at.soul || at.link || at.dub;// || at.get; + at = tmp? (at.root.$.get(tmp)._) : at; + as.soul = tmp; data = as.data; } - if(!as.not && !(as.soul = Gun.node.soul(data))){ - if(as.path && obj_is(as.data)){ // Apparently necessary - as.soul = (opt.uuid || cat.root.opt.uuid || Gun.text.random)(); + if(!as.not && !(as.soul = as.soul || soul)){ + if(as.path && obj_is(as.data)){ + as.soul = (opt.uuid || as.via.back('opt.uuid') || Gun.text.random)(); } else { - //as.data = obj_put({}, as.gun._.get, as.data); + //as.data = obj_put({}, as.$._.get, as.data); if(node_ == at.get){ - as.soul = (at.put||empty)['#'] || at._id; + as.soul = (at.put||empty)['#'] || at.dub; } - as.soul = as.soul || at.soul || cat.soul || (opt.uuid || cat.root.opt.uuid || Gun.text.random)(); + as.soul = as.soul || at.soul || at.link || (opt.uuid || as.via.back('opt.uuid') || Gun.text.random)(); } if(!as.soul){ // polyfill async uuid for SEA - as.ref.back('opt.uuid')(function(err, soul){ // TODO: improve perf without anonymous callback + as.via.back('opt.uuid')(function(err, soul){ // TODO: improve perf without anonymous callback if(err){ return Gun.log(err) } // Handle error. as.ref.put(as.data, as.soul = soul, as); }); @@ -1513,50 +1602,45 @@ if(typeof tag === 'string'){ if(!arg){ return at.on(tag) } act = at.on(tag, arg, eas || at, as); - if(eas && eas.gun){ + if(eas && eas.$){ (eas.subs || (eas.subs = [])).push(act); } - off = function() { - if (act && act.off) act.off(); - off.off(); - }; - off.off = gun.off.bind(gun) || noop; - gun.off = off; return gun; } var opt = arg; opt = (true === opt)? {change: true} : opt || {}; + opt.at = at; opt.ok = tag; - opt.last = {}; + //opt.last = {}; gun.get(ok, opt); // TODO: PERF! Event listener leak!!!? return gun; } - function ok(at, ev){ var opt = this; - var gun = at.gun, cat = gun._, data = cat.put || at.put, tmp = opt.last, id = cat.id+at.get, tmp; + function ok(msg, ev){ var opt = this; + var gun = msg.$, at = (gun||{})._ || {}, data = at.put || msg.put, cat = opt.at, tmp; if(u === data){ return; } - if(data && data[rel._] && (tmp = rel.is(data))){ - tmp = (cat.root.gun.get(tmp)._); + if(tmp = msg.$$){ + tmp = (msg.$$._); if(u === tmp.put){ return; } data = tmp.put; } if(opt.change){ // TODO: BUG? Move above the undef checks? - data = at.put; + data = msg.put; } // DEDUPLICATE // TODO: NEEDS WORK! BAD PROTOTYPE - if(tmp.put === data && tmp.get === id && !Gun.node.soul(data)){ return } - tmp.put = data; - tmp.get = id; + //if(tmp.put === data && tmp.get === id && !Gun.node.soul(data)){ return } + //tmp.put = data; + //tmp.get = id; // DEDUPLICATE // TODO: NEEDS WORK! BAD PROTOTYPE - cat.last = data; + //at.last = data; if(opt.as){ - opt.ok.call(opt.as, at, ev); + opt.ok.call(opt.as, msg, ev); } else { - opt.ok.call(gun, data, at.get, at, ev); + opt.ok.call(gun, data, msg.get, msg, ev); } } @@ -1572,14 +1656,14 @@ } if(cb){ (opt = opt || {}).ok = cb; - opt.cat = at; + opt.at = at; opt.out = {'#': Gun.text.random(9)}; gun.get(val, {as: opt}); opt.async = true; //opt.async = at.stun? 1 : true; } else { Gun.log.once("valonce", "Chainable val is experimental, its behavior and API may change moving forward. Please play with it and report bugs and ideas on how to improve it."); var chain = gun.chain(); - chain._.val = gun.once(function(){ + chain._.nix = gun.once(function(){ chain._.on('in', gun._); }); return chain; @@ -1587,35 +1671,26 @@ return gun; } - function val(msg, ev, to){ - var opt = this.as, cat = opt.cat, gun = msg.gun, coat = gun._, data = coat.put || msg.put, tmp; - if(u === data){ - //return; - } - //if(coat.soul && !(0 < coat.ack)){ return } - if(tmp = Gun.node.soul(data) || rel.is(data)){ - //if(data && data[rel._] && (tmp = rel.is(data))){ - tmp = (cat.root.gun.get(tmp)._); - if(u === tmp.put){//} || !(0 < tmp.ack)){ - return; + function val(msg, eve, to){ + if(!msg.$){ eve.off(); return } + var opt = this.as, cat = opt.at, gun = msg.$, at = gun._, data = at.put || msg.put, link, tmp; + if(tmp = msg.$$){ + link = tmp = (msg.$$._); + if(u !== link.put){ + data = link.put; } - data = tmp.put; } - if(ev.wait){ clearTimeout(ev.wait) } - //if(!to && (!(0 < coat.ack) || ((true === opt.async) && 0 !== opt.wait))){ - if(!to){ - ev.wait = setTimeout(function(){ - val.call({as:opt}, msg, ev, ev.wait || 1); + if((tmp = eve.wait) && (tmp = tmp[at.id])){ clearTimeout(tmp) } + if((!to && (u === data || at.soul || at.link || (link && !(0 < link.ack)))) + || (u === data && (tmp = (obj_map(at.root.opt.peers, function(v,k,t){t(k)})||[]).length) && (!to && (link||at).ack <= tmp))){ + tmp = (eve.wait = {})[at.id] = setTimeout(function(){ + val.call({as:opt}, msg, eve, tmp || 1); }, opt.wait || 99); return; } - if(cat.has || cat.soul){ - if(ev.off()){ return } // if it is already off, don't call again! - } else { - if((opt.seen = opt.seen || {})[coat.id]){ return } - opt.seen[coat.id] = true; - } - opt.ok.call(msg.gun || opt.gun, data, msg.get); + if(link && u === link.put && (tmp = rel.is(data))){ data = Gun.node.ify({}, tmp) } + eve.rid(msg); + opt.ok.call(gun || opt.$, data, msg.get); } Gun.chain.off = function(){ @@ -1641,21 +1716,21 @@ } if(tmp = at.map){ obj_map(tmp, function(at){ - if(at.rel){ - cat.root.gun.get(at.rel).off(); + if(at.link){ + cat.root.$.get(at.link).off(); } }); } if(tmp = at.next){ obj_map(tmp, function(neat){ - neat.gun.off(); + neat.$.off(); }); } at.on('off', {}); return gun; } var obj = Gun.obj, obj_map = obj.map, obj_has = obj.has, obj_del = obj.del, obj_to = obj.to; - var rel = Gun.val.rel; + var rel = Gun.val.link; var empty = {}, noop = function(){}, u; })(USE, './on'); @@ -1664,10 +1739,9 @@ Gun.chain.map = function(cb, opt, t){ var gun = this, cat = gun._, chain; if(!cb){ - if(chain = cat.fields){ return chain } - chain = cat.fields = gun.chain(); - chain._.val = gun.back('val'); - chain._.MAPOF = cat.soul; + if(chain = cat.each){ return chain } + cat.each = chain = gun.chain(); + chain._.nix = gun.back('nix'); gun.on('in', map, chain._); return chain; } @@ -1676,24 +1750,23 @@ gun.map().on(function(data, key, at, ev){ var next = (cb||noop).call(this, data, key, at, ev); if(u === next){ return } - if(Gun.is(next)){ - chain._.on('in', next._); - return; - } - chain._.on('in', {get: key, put: next, gun: chain}); + if(data === next){ return chain._.on('in', at) } + if(Gun.is(next)){ return chain._.on('in', next._) } + chain._.on('in', {get: key, put: next}); }); return chain; } function map(msg){ - if(!msg.put || Gun.val.is(msg.put)){ return } - if(this.as.val){ this.off() } // TODO: Ugly hack! + if(!msg.put || Gun.val.is(msg.put)){ return this.to.next(msg) } + if(this.as.nix){ this.off() } // TODO: Ugly hack! obj_map(msg.put, each, {at: this.as, msg: msg}); this.to.next(msg); } function each(v,k){ if(n_ === k){ return } - var msg = this.msg, gun = msg.gun, at = this.at, tmp = (gun.get(k)._); - (tmp.echo || (tmp.echo = {}))[at.id] = at; + var msg = this.msg, gun = msg.$, at = gun._, cat = this.at, tmp = at.lex; + if(tmp && !Gun.text.match(k, tmp['.'] || tmp['#'] || tmp)){ return } // TODO: Ugly hack! + ((tmp = gun.get(k)._).echo || (tmp.echo = {}))[cat.id] = tmp.echo[cat.id] || cat; } var obj_map = Gun.obj.map, noop = function(){}, event = {stun: noop, off: noop}, n_ = Gun.node._, u; })(USE, './map'); @@ -1704,24 +1777,17 @@ var gun = this, soul; cb = cb || function(){}; opt = opt || {}; opt.item = opt.item || item; - if(soul = Gun.node.soul(item)){ return gun.set(gun.back(-1).get(soul), cb, opt) } + if(soul = Gun.node.soul(item)){ item = Gun.obj.put({}, soul, Gun.val.link.ify(soul)) } if(!Gun.is(item)){ - var id = gun._.root.opt.uuid(); - if(id && Gun.obj.is(item)){ - return gun.set(gun._.root.gun.put(item, id), cb, opt); + if(Gun.obj.is(item)){; + item = gun.back(-1).get(soul = soul || Gun.node.soul(item) || gun.back('opt.uuid')()).put(item); } - return gun.get(id || (Gun.state.lex() + Gun.text.random(12))).put(item, cb, opt); + return gun.get(soul || (Gun.state.lex() + Gun.text.random(7))).put(item, cb, opt); } - item.get('_').get(function(at, ev){ - if(!at.gun || !at.gun._.back){ return } - ev.off(); - var soul = (at.put||{})['#']; - at = (at.gun._.back); - var put = {}, node = at.put; - soul = at.soul || Gun.node.soul(node) || soul; - if(!soul){ return cb.call(gun, {err: Gun.log('Only a node can be linked! Not "' + node + '"!')}) } - gun.put(Gun.obj.put(put, soul, Gun.val.rel.ify(soul)), cb, opt); - },{wait:0}); + item.get(function(soul, o, msg){ + if(!soul){ return cb.call(gun, {err: Gun.log('Only a node can be linked! Not "' + msg.put + '"!')}) } + gun.put(Gun.obj.put({}, soul, Gun.val.link.ify(soul)), cb, opt); + },true); return item; } })(USE, './set'); @@ -1729,24 +1795,90 @@ ;USE(function(module){ if(typeof Gun === 'undefined'){ return } // TODO: localStorage is Browser only. But it would be nice if it could somehow plugin into NodeJS compatible localStorage APIs? - var root, noop = function(){}, u; - if(typeof window !== 'undefined'){ root = window } - var store = root.localStorage || {setItem: noop, removeItem: noop, getItem: noop}; - + var root, noop = function(){}, store, u; + try{store = (Gun.window||noop).localStorage}catch(e){} + if(!store){ + console.log("Warning: No localStorage exists to persist data to!"); + store = {setItem: function(k,v){this[k]=v}, removeItem: function(k){delete this[k]}, getItem: function(k){return this[k]}}; + } /* NOTE: Both `lib/file.js` and `lib/memdisk.js` are based on this design! If you update anything here, consider updating the other adapters as well. */ - Gun.on('opt', function(root){ + Gun.on('create', function(root){ + // This code is used to queue offline writes for resync. + // See the next 'opt' code below for actual saving of data. + var ev = this.to, opt = root.opt; + if(root.once){ return ev.next(root) } + //if(false === opt.localStorage){ return ev.next(root) } // we want offline resynce queue regardless! + opt.prefix = opt.file || 'gun/'; + var gap = Gun.obj.ify(store.getItem('gap/'+opt.prefix)) || {}; + var empty = Gun.obj.empty, id, to, go; + // add re-sync command. + if(!empty(gap)){ + var disk = Gun.obj.ify(store.getItem(opt.prefix)) || {}, send = {}; + Gun.obj.map(gap, function(node, soul){ + Gun.obj.map(node, function(val, key){ + send[soul] = Gun.state.to(disk[soul], key, send[soul]); + }); + }); + setTimeout(function(){ + root.on('out', {put: send, '#': root.ask(ack)}); + },1); + } + + root.on('out', function(msg){ + if(msg.lS){ return } + if(Gun.is(msg.$) && msg.put && !msg['@'] && !empty(opt.peers)){ + id = msg['#']; + Gun.graph.is(msg.put, null, map); + if(!to){ to = setTimeout(flush, opt.wait || 1) } + } + this.to.next(msg); + }); + root.on('ack', ack); + + function ack(ack){ // TODO: This is experimental, not sure if we should keep this type of event hook. + if(ack.err || !ack.ok){ return } + var id = ack['@']; + setTimeout(function(){ + Gun.obj.map(gap, function(node, soul){ + Gun.obj.map(node, function(val, key){ + if(id !== val){ return } + delete node[key]; + }); + if(empty(node)){ + delete gap[soul]; + } + }); + flush(); + }, opt.wait || 1); + }; + ev.next(root); + + var map = function(val, key, node, soul){ + (gap[soul] || (gap[soul] = {}))[key] = id; + } + var flush = function(){ + clearTimeout(to); + to = false; + try{store.setItem('gap/'+opt.prefix, JSON.stringify(gap)); + }catch(e){ Gun.log(err = e || "localStorage failure") } + } + }); + + Gun.on('create', function(root){ this.to.next(root); var opt = root.opt; if(root.once){ return } if(false === opt.localStorage){ return } - opt.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! + root.on('put', function(at){ this.to.next(at); Gun.graph.is(at.put, null, map); @@ -1759,12 +1891,12 @@ to = setTimeout(flush, opt.wait || 1); }); - root.on('get', function(at){ - this.to.next(at); - var lex = at.get, soul, data, u; - //setTimeout(function(){ + root.on('get', function(msg){ + this.to.next(msg); + var lex = msg.get, soul, data, u; + function to(){ if(!lex || !(soul = lex['#'])){ return } - //if(0 >= at.cap){ return } + //if(0 >= msg.cap){ return } var has = lex['.']; data = disk[soul] || u; if(data && has){ @@ -1773,23 +1905,29 @@ if(!data && !Gun.obj.empty(opt.peers)){ // if data not found, don't ack if there are peers. return; // Hmm, what if we have peers but we are disconnected? } - root.on('in', {'@': at['#'], put: Gun.graph.node(data), how: 'lS'}); - //},1); + //console.log("lS get", lex, data); + root.on('in', {'@': msg['#'], put: Gun.graph.node(data), how: 'lS', lS: msg.$ || root.$}); + }; + Gun.debug? setTimeout(to,1) : to(); }); var map = function(val, key, node, soul){ disk[soul] = Gun.state.to(node, key, disk[soul]); } - var flush = function(){ + var flush = function(data){ var err; count = 0; clearTimeout(to); to = false; var ack = acks; acks = {}; - try{store.setItem(opt.file, JSON.stringify(disk)); - }catch(e){ Gun.log(err = e || "localStorage failure") } + if(data){ disk = data } + try{store.setItem(opt.prefix, JSON.stringify(disk)); + }catch(e){ + Gun.log(err = (e || "localStorage failure") + " Consider using GUN's IndexedDB plugin for RAD for more storage space, temporary example at https://github.com/amark/gun/blob/master/test/tmp/indexedDB.html ."); + root.on('localStorage:error', {err: err, file: opt.prefix, flush: disk, retry: flush}); + } if(!err && !Gun.obj.empty(opt.peers)){ return } // only ack if there are no peers. Gun.obj.map(ack, function(yes, id){ root.on('in', { @@ -1803,32 +1941,45 @@ })(USE, './adapters/localStorage'); ;USE(function(module){ + var Gun = USE('../index'); var Type = USE('../type'); function Mesh(ctx){ var mesh = function(){}; + var opt = ctx.opt || {}; + opt.log = opt.log || console.log; + opt.gap = opt.gap || opt.wait || 1; + opt.pack = opt.pack || (opt.memory? (opt.memory * 1000 * 1000) : 1399000000) * 0.3; // max_old_space_size defaults to 1400 MB. mesh.out = function(msg){ var tmp; if(this.to){ this.to.next(msg) } + //if(mesh.last != msg['#']){ return mesh.last = msg['#'], this.to.next(msg) } if((tmp = msg['@']) && (tmp = ctx.dup.s[tmp]) && (tmp = tmp.it) - && tmp.mesh){ - mesh.say(msg, tmp.mesh.via); + && tmp._){ + mesh.say(msg, (tmp._).via, 1); tmp['##'] = msg['##']; return; } // add hook for AXE? + if (Gun.AXE) { Gun.AXE.say(msg, mesh.say, this); return; } mesh.say(msg); } - mesh.hear = function(msg, peer){ - if(!msg){ return } - var dup = ctx.dup, id, hash, tmp = msg[0]; - try{msg = JSON.parse(msg); - }catch(e){} - if('{' === tmp){ + ctx.on('create', function(root){ + root.opt.pid = root.opt.pid || Type.text.random(9); + this.to.next(root); + ctx.on('out', mesh.out); + }); + mesh.hear = function(raw, peer){ + if(!raw){ return } + var dup = ctx.dup, id, hash, msg, tmp = raw[0]; + if(opt.pack <= raw.length){ return mesh.say({dam: '!', err: "Message too big!"}, peer) } + if('{' === tmp){ + try{msg = JSON.parse(raw);}catch(e){opt.log('DAM JSON parse error', e)} + if(!msg){ return } if(dup.check(id = msg['#'])){ return } dup.track(id, true).it = msg; // GUN core also dedups, so `true` is needed. if((tmp = msg['@']) && msg.put){ @@ -1838,16 +1989,23 @@ (tmp = dup.s)[hash] = tmp[id]; } } - (msg.mesh = function(){}).via = peer; + (msg._ = function(){}).via = peer; if((tmp = msg['><'])){ - msg.mesh.to = Type.obj.map(tmp.split(','), function(k,i,m){m(k,true)}); + (msg._).to = Type.obj.map(tmp.split(','), function(k,i,m){m(k,true)}); + } + if(msg.dam){ + if(tmp = mesh.hear[msg.dam]){ + tmp(msg, peer, ctx); + } + return; } ctx.on('in', msg); - + return; } else if('[' === tmp){ - + try{msg = JSON.parse(raw);}catch(e){opt.log('DAM JSON parse error', e)} + if(!msg){ return } var i = 0, m; while(m = msg[i++]){ mesh.hear(m, peer); @@ -1858,21 +2016,21 @@ } ;(function(){ - mesh.say = function(msg, peer){ + mesh.say = function(msg, peer, o){ /* TODO: Plenty of performance optimizations that can be made just based off of ordering, and reducing function calls for cached writes. */ if(!peer){ - Type.obj.map(ctx.opt.peers, function(peer){ + Type.obj.map(opt.peers, function(peer){ mesh.say(msg, peer); }); return; } - var tmp, wire = peer.wire || ((ctx.opt.wire) && ctx.opt.wire(peer)), msh, raw;// || open(peer, ctx); // TODO: Reopen! + var tmp, wire = peer.wire || ((opt.wire) && opt.wire(peer)), msh, raw;// || open(peer, ctx); // TODO: Reopen! if(!wire){ return } - msh = msg.mesh || empty; + msh = (msg._) || empty; if(peer === msh.via){ return } if(!(raw = msh.raw)){ raw = mesh.raw(msg) } if((tmp = msg['@']) @@ -1882,33 +2040,36 @@ return; // TODO: this still needs to be tested in the browser! } } - if((tmp = msh.to) && (tmp[peer.url] || tmp[peer.id])){ return } // TODO: still needs to be tested + 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); - }, ctx.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{ - 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); + } else + if(wire.send){ + if(wire.readyState && 1 != wire.readyState){ throw "socket not ready yet!" } + wire.send(raw); } }catch(e){ (peer.queue = peer.queue || []).push(raw); @@ -1916,12 +2077,12 @@ } }()); - + ;(function(){ mesh.raw = function(msg){ if(!msg){ return '' } - var dup = ctx.dup, msh = msg.mesh || {}, put, hash, tmp; + var dup = ctx.dup, msh = (msg._) || {}, put, hash, tmp; if(tmp = msh.raw){ return tmp } if(typeof msg === 'string'){ return msg } if(msg['@'] && (tmp = msg.put)){ @@ -1931,15 +2092,17 @@ msg['##'] = hash; } (tmp = dup.s)[hash = msg['@']+hash] = tmp[msg['#']]; - msg['#'] = hash; + msg['#'] = hash || msg['#']; if(put){ (msg = Type.obj.to(msg)).put = _ } } - var i = 0, to = []; Type.obj.map(ctx.opt.peers, function(p){ + var i = 0, to = []; Type.obj.map(opt.peers, function(p){ to.push(p.url || p.id); if(++i > 9){ return true } // limit server, fast fix, improve later! }); msg['><'] = to.join(); var raw = $(msg); if(u !== put){ - 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; @@ -1960,19 +2123,43 @@ function map(k){ this.to[k] = this.on[k]; } - var $ = JSON.stringify, _ = ':])([:' + var $ = JSON.stringify, _ = ':])([:'; }()); mesh.hi = function(peer){ - ctx.on('hi', peer); - var queue = peer.queue; - peer.queue = []; - Type.obj.map(queue, function(msg){ - mesh.say(msg, peer); - }); + var tmp = peer.wire || {}; + if(peer.id || peer.url){ + opt.peers[peer.url || peer.id] = peer; + Type.obj.del(opt.peers, tmp.id); + } else { + tmp = tmp.id = tmp.id || Type.text.random(9); + mesh.say({dam: '?'}, opt.peers[tmp] = peer); + } + if(!tmp.hied){ ctx.on(tmp.hied = 'hi', peer); } + // tmp = peer.queue; peer.queue = []; + // Type.obj.map(tmp, function(msg){ + // mesh.say(msg, peer); + // }); + } + mesh.bye = function(peer){ + Type.obj.del(opt.peers, peer.id); // assume if peer.url then reconnect + ctx.on('bye', peer); + } + mesh.hear['!'] = function(msg, peer){ opt.log('Error:', msg.err) } + mesh.hear['?'] = function(msg, peer){ + if(!msg.pid){ +// return mesh.say({dam: '?', pid: opt.pid, '@': msg['#']}, peer); + mesh.say({dam: '?', pid: opt.pid, '@': msg['#']}, peer); + var tmp = peer.queue; peer.queue = []; + Type.obj.map(tmp, function(msg){ + mesh.say(msg, peer); + }); + return; + } + peer.id = peer.id || msg.pid; + mesh.hi(peer); } - return mesh; } @@ -2015,15 +2202,15 @@ opt.WebSocket = websocket; var mesh = opt.mesh = opt.mesh || Gun.Mesh(root); - root.on('out', mesh.out); - opt.wire = opt.wire || open; - function open(peer){ - if(!peer || !peer.url){ return } + var wire = opt.wire; + opt.wire = open; + function open(peer){ try{ + if(!peer || !peer.url){ return wire && wire(peer) } var url = peer.url.replace('http', 'ws'); var wire = peer.wire = new opt.WebSocket(url); wire.onclose = function(){ - root.on('bye', peer); + opt.mesh.bye(peer); reconnect(peer); }; wire.onerror = function(error){ @@ -2034,14 +2221,14 @@ } }; wire.onopen = function(){ - mesh.hi(peer); + opt.mesh.hi(peer); } wire.onmessage = function(msg){ if(!msg){ return } - mesh.hear(msg.data || msg, peer); + opt.mesh.hear(msg.data || msg, peer); }; return wire; - } + }catch(e){}} function reconnect(peer){ clearTimeout(peer.defer); diff --git a/gun.min.js b/gun.min.js index 868cc1e8..14a5c717 100644 --- a/gun.min.js +++ b/gun.min.js @@ -1 +1 @@ -!function(){function t(n){function o(t){return t.split("/").slice(-1).toString().replace(".js","")}return n.slice?t[o(n)]:function(e,i){n(e={exports:{}}),t[o(i)]=e.exports}}var n;"undefined"!=typeof window&&(n=window),"undefined"!=typeof global&&(n=global);var o=(n=n||{}).console||{log:function(){}};if("undefined"!=typeof module)var e=module;t(function(t){var n={};n.fns=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){var e=!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;e=!0,t=t.slice(o["*"].length)}if(n.obj.has(o,"!")){if(t.slice(-o["!"].length)!==o["!"])return!1;e=!0}if(n.obj.has(o,"+")&&n.list.map(n.list.is(o["+"])?o["+"]:[o["+"]],function(n){if(!(t.indexOf(n)>=0))return!0;e=!0}))return!1;if(n.obj.has(o,"-")&&n.list.map(n.list.is(o["-"])?o["-"]:[o["-"]],function(n){if(!(t.indexOf(n)<0))return!0;e=!0}))return!1;if(n.obj.has(o,">")){if(!(t>o[">"]))return!1;e=!0}if(n.obj.has(o,"<")){if(!(to?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])}},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){if(t)return t[n]=null,delete t[n],t},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||void 0}n.obj.empty=function(n,o){return!n||!a(n,t,{n:o})}}(),function(){function t(n,o){if(2===arguments.length)return t.r=t.r||{},void(t.r[n]=o);t.r=t.r||[],t.r.push(n)}var i=Object.keys;n.obj.map=function(a,s,f){var c,l,p,g,h,d=0,v=o(s);if(t.r=null,i&&r(a)&&(g=i(a),h=!0),e(a)||g)for(l=(g||a).length;d",o.drift=0,o.is=function(t,n,e){var i=n&&t&&t[y]&&t[y][o._]||e;if(i)return m(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[y]){if(!u)return;t=i.soul.ify(t,u)}var a=g(t[y],o._);return l!==n&&n!==y&&(m(e)&&(a[n]=e),l!==r&&(t[n]=r)),t},o.to=function(t,n,e){var r=t[n];return d(r)&&(r=b(r)),o.ify(e,n,o.is(t,n),r,i.soul(t))},function(){function t(t,n){y!==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=_(n=n||e)?n:null,u&&!n?(e=m(e)?e:o(),u[y]=u[y]||{},v(u,t,{o:u,s:e}),u):(i=i||d(e)?e:r,e=m(e)?e:o(),function(o,u,a,s){if(!n)return t.call({o:a,s:e},o,u),o;n.call(i||this||{},o,u,a,s),h(a,u)&&r===a[u]||t.call({o:a,s:e},o,u)})}}();var l,p=e.obj,g=p.as,h=p.has,d=p.is,v=p.map,b=p.copy,m=e.num.is,_=e.fn.is,y=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){if(!t||o!==i.soul(t)||!i.is(t,this.fn,this.as))return!0;this.cb&&(n.n=t,n.as=this.as,this.cb.call(n.as,t,o,n))}function n(t){t&&i.is(n.n,t,n.as)}r.is=function(n,o,e,i){return!(!n||!s(n)||l(n))&&!g(n,t,{cb:o,fn:e,as:i})}}(),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)&&(t.graph[e.rel.is(o.rel)]=o.node),o)}function n(n,o,r){var a,s,p=this,g=p.env;if(i._===o&&c(n,e.rel._))return r._;if(a=l(n,o,r,p,g)){if(o||(p.node=p.node||r||{},c(n,i._)&&(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=g.map)&&(s.call(g.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,g)))return}if(!o)return p.node;if(!0===a)return n;if((s=t(g,{obj:n,path:p.path.concat(o)})).node)return s.rel}}function a(t){var n=this,o=e.rel.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)||(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.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)(o=e.rel.is(t))?(u=this.opt.seen[o])?this.obj[n]=u:this.obj[n]=this.opt.seen[o]=r.to(this.graph,o,this.opt):this.obj[n]=t;else{if(l(t,e.rel._))return;this.obj[n]=h(t)}}r.to=function(n,o,e){if(n){var i={};return e=e||{seen:{}},g(n[o],t,{obj:i,graph:n,opt:e}),i}}}();o.fn.is;var u,a=o.obj,s=a.is,f=a.del,c=a.has,l=a.empty,p=a.put,g=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}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."}),i.off()},(this.opt||{}).lack||9e3),o}}})(t,"./ask"),t(function(n){var o=t("./type"),e=o.time.is;n.exports=function(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))},n.track=function(i,r){var u=n.s[i]||(n.s[i]={});return u.was=e(),r&&(u.pass=!0),n.to||(n.to=setTimeout(function(){var i=e();o.obj.map(n.s,function(e,r){t.age>i-e.was||o.obj.del(n.s,r)}),n.to=null},t.age+9)),u},n}})(t,"./dup"),t(function(n){function i(t){return t instanceof i?(this._={gun:this}).gun:this instanceof i?i.create(this._={gun:this,opt:t}):new i(t)}i.is=function(t){return t instanceof i},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(t){var n,o,e=this.as,r=e.gun;(o=t["#"])||(o=t["#"]=c(9)),(n=e.dup).check(o)||(n.track(o),e.ask(t["@"],t)||(t.get&&i.on.get(t,r),t.put&&i.on.put(t,r)),e.on("out",t))}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.gun.opt(n.opt);return n.once||(n.on("in",t,n),n.on("out",t,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]||_,s=i.state.is(a,n,!0),f=a[n],c=i.HAM(r.machine,u,s,t,f);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]),r.souls[e]=!0):c.defer&&(r.defer=u<(r.defer||1/0)?u:r.defer)}function n(t,n){var i=this,u=i.gun._,a=(u.next||_)[n];if(a){var s=i.map[n]={put:t,get:n,gun:a.gun},f={ctx:i,msg:s};i.async=!!u.tag.node,i.ack&&(s["@"]=i.ack),d(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,d(t.put,e,t),d(i.souls,function(t){if(t)return t})||i.c||(i.c=1,this.off(),u.stop={},d(i.map,r,i)))}),i.and=!0,u.on("node",s))}else i.souls[n]=!1}function o(t,n){var o=this.ctx,e=o.graph,r=this.msg,u=r.get,a=r.put,s=r.gun._;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.gun._;r.put=i.state.to(e,n,r.put)}function r(t,n){t.gun&&t.gun._.on("in",t)}i.on.put=function(o,e){var a=e._,s={gun:e,graph:a.graph,put:{},map:{},souls:{},machine:i.state(),ack:o["@"]};if(i.graph.is(o.put,null,t,s)||(s.err="Error: Invalid graph!"),s.err)return a.on("in",{"@":o["#"],err:i.log(s.err)});d(s.put,n,s),s.async||(a.stop={},d(s.map,r,s)),u!==s.defer&&setTimeout(function(){i.on.put(o,e)},s.defer-s.machine),s.diff&&a.on("put",h(o,{put:s.diff}))},i.on.get=function(t,n){var o=n._,e=t.get[b],r=o.graph[e],u=t.get[m],a=(o.next||(o.next={}))[e];if(!r||!a)return o.on("get",t);if(u){if(!g(r,u))return o.on("get",t);r=i.state.to(r,u)}else r=i.obj.copy(r);r=i.graph.node(r),o.on("in",{"@":t["#"],how:"mem",put:r,gun:n}),o.on("get",t)}}(),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=d(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 v()+c(12)},n};var u,a=i.list.is,s=i.text,f=s.is,c=s.random,l=i.obj,p=l.is,g=l.has,h=l.to,d=l.map,v=(l.copy,i.state.lex),b=i.val.rel._,m=".",_=(i.node._,i.val.rel.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);try{void 0!==e&&(e.exports=i)}catch(t){}n.exports=i})(t,"./root"),t(function(n){var o=t("./root");o.chain.back=function(t,n){if(-1===(t=t||1)||1/0===t)return this._.root.gun;if(1===t)return(this._.back||this._).gun;var r=this,u=r._;if("string"==typeof t&&(t=t.split(".")),!(t instanceof Array)){if(t instanceof Function){for(var a,s={back:u};(s=s.back)&&!(a=t(s,n)););return a}return o.num.is(t)?(u.back||u).gun.back(t-1):this}var f=0,c=t.length,s=u;for(f;f .once, apologies unexpected."),this.once(t,n)},i.chain.once=function(t,n){var o=this,u=o._,a=u.put;if(0=(n.batch||1e3))return s();o||(o=setTimeout(s,n.wait||1))}),t.on("get",function(o){this.to.next(o);var e,i,r=o.get;if(r&&(e=r["#"])){var a=r["."];(i=u[e]||void 0)&&a&&(i=Gun.state.to(i,a)),(i||Gun.obj.empty(n.peers))&&t.on("in",{"@":o["#"],put:Gun.graph.node(i),how:"lS"})}});var a=function(t,n,o,e){u[e]=Gun.state.to(o,n,u[e])},s=function(){var a;r=0,clearTimeout(o),o=!1;var s=i;i={};try{e.setItem(n.file,JSON.stringify(u))}catch(t){Gun.log(a=t||"localStorage failure")}(a||Gun.obj.empty(n.peers))&&Gun.obj.map(s,function(n,o){t.on("in",{"@":o,err:a,ok:0})})}}})}})(t,"./adapters/localStorage"),t(function(n){function o(t){var n=function(){};return n.out=function(o){var e;if(this.to&&this.to.next(o),(e=o["@"])&&(e=t.dup.s[e])&&(e=e.it)&&e.mesh)return n.say(o,e.mesh.via),void(e["##"]=o["##"]);n.say(o)},n.hear=function(o,i){if(o){var r,u,a=t.dup,s=o[0];try{o=JSON.parse(o)}catch(t){}if("{"===s){if(a.check(r=o["#"]))return;if(a.track(r,!0).it=o,(s=o["@"])&&o.put&&(u=o["##"]||(o["##"]=n.hash(o)),(s+=u)!=r)){if(a.check(s))return;(s=a.s)[u]=s[r]}return(o.mesh=function(){}).via=i,(s=o["><"])&&(o.mesh.to=e.obj.map(s.split(","),function(t,n,o){o(t,!0)})),void t.on("in",o)}if("["!==s);else for(var f,c=0;f=o[c++];)n.hear(f,i)}},function(){function o(t,n){var o=n.wire;try{o.send?o.readyState===o.OPEN?o.send(t):(n.queue=n.queue||[]).push(t):n.say&&n.say(t)}catch(o){(n.queue=n.queue||[]).push(t)}}n.say=function(i,u){if(u){var a,s,f;(u.wire||t.opt.wire&&t.opt.wire(u))&&(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])||(u.batch?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.wait||1),o(f,u)))))}else e.obj.map(t.opt.peers,function(t){n.say(i,t)})}}(),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,u&&((o=e.obj.to(o)).put=s));var g=0,h=[];e.obj.map(t.opt.peers,function(t){if(h.push(t.url||t.id),++g>9)return!0}),o["><"]=h.join();var d=a(o);return i!==u&&(d=d.replace('"'+s+'"',u)),p&&(p.raw=d),d},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=0,e=t.length;o")){if(!(n>t[">"]))return!1;o=!0}if(p.obj.has(t,"<")){if(!(n",s.drift=0,s.is=function(t,n,o){var i=n&&t&&t[m]&&t[m][s._]||o;if(i)return g(i=i[n])?i:-1/0},s.lex=function(){return s().toString(36).replace(".","")},s.ify=function(t,n,o,i,e){if(!t||!t[m]){if(!e)return;t=a.soul.ify(t,e)}var r=c(t[m],s._);return void 0!==n&&n!==m&&(g(o)&&(r[n]=o),void 0!==i&&(t[n]=i)),t},s.to=function(t,n,o){var i=(t||{})[n];return p(i)&&(i=d(i)),s.ify(o,n,s.is(t,n),i,a.soul(t))},function(){function u(t,n){m!==n&&s.ify(this.o,n,this.s)}s.map=function(e,r,a){var t=p(t=e||r)?t:null;return e=v(e=e||r)?e:null,t&&!e?(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,i){if(!e)return u.call({o:o,s:r},t,n),t;e.call(a||this||{},t,n,o,i),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 e(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,i){return!(!t||!l(t)||u(t))&&!s(t,e,{cb:n,fn:o,as:i})}}(),function(){function u(t,n){var o;return(o=function(t,n){var o,i=t.seen,e=i.length;for(;e--;)if(o=i[e],n.obj===o.obj)return o;i.push(n)}(t,n))?o:(n.env=t,n.soul=e,c.ify(n.obj,i,n)&&(n.link=n.link||f.link.ify(c.soul(n.node)),n.obj!==t.shell&&(t.graph[f.link.is(n.link)]=n.node)),n)}function i(t,n,o){var i,e,r=this,a=r.env;if(c._===n&&h(t,f.link._))return o._;if(i=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.link.is(r.link)),r.link=r.link||f.link.ify(c.soul(r.node))),(e=a.map)&&(e.call(a.as||{},t,n,o,r),h(o,n))){if(void 0===(t=o[n]))return void p(o,n);if(!(i=s(t,n,o,r,a)))return}if(!n)return r.node;if(!0===i)return t;if((e=u(a,{obj:t,path:r.path.concat(n)})).node)return e.link}}function e(t){var n=this,o=f.link.is(n.link),i=n.env.graph;n.link=n.link||f.link.ify(t),n.link[f.link._]=t,n.node&&n.node[c._]&&(n.node[c._][f.link._]=t),h(i,o)&&(i[t]=i[o],p(i,o))}function s(t,n,o,i,e){var r;return!!f.is(t)||(l(t)?1:(r=e.invalid)?s(t=r.call(e.as||{},t,n,o),n,o,i,e):(e.err="Invalid value at '"+i.path.concat(n).join(".")+"'!",void(a.list.is(t)&&(e.err+=" Use `.set(item)` instead of an Array."))))}r.ify=function(t,n,o){var i={path:[],obj:t};return n?"string"==typeof n?n={soul:n}:n instanceof Function&&(n.map=n):n={},n.soul&&(i.link=f.link.ify(n.soul)),n.shell=(o||{}).shell,n.graph=n.graph||{},n.seen=n.seen||[],n.as=n.as||o,u(n,i),n.root=i.node,n.graph}}(),r.node=function(t){var n=c.soul(t);if(n)return o({},n,t)},function(){function e(t,n){var o,i;if(c._!==n)(o=f.link.is(t))?(i=this.opt.seen[o])?this.obj[n]=i:this.obj[n]=this.opt.seen[o]=r.to(this.graph,o,this.opt):this.obj[n]=t;else{if(u(t,f.link._))return;this.obj[n]=d(t)}}r.to=function(t,n,o){if(t){var i={};return o=o||{seen:{}},s(t[n],e,{obj:i,graph:t,opt:o}),i}}}();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,i=(this.tag||empty)[o];if(!i)return;return i=this.on(o,n),clearTimeout(i.err),!0}o=n&&n["#"]||Math.random().toString(36).slice(2);if(!t)return o;var e=this.on(o,t,n);return e.err=e.err||setTimeout(function(){e.next({err:"Error: No ACK received yet.",lack:!0}),e.off()},(this.opt||{}).lack||9e3),o}}})(_,"./ask"),_(function(t){var r=_("./type");var a=r.time.is;t.exports=function(i){var e={s:{}};return i=i||{max:1e3,age:9e3},e.check=function(t){var n;return!!(n=e.s[t])&&(n.pass?n.pass=!1:e.track(t))},e.track=function(t,n){var o=e.s[t]||(e.s[t]={});return o.was=a(),n&&(o.pass=!0),e.to||(e.to=setTimeout(function(){var o=a();r.obj.map(e.s,function(t,n){t&&i.age>o-t.was||r.obj.del(e.s,n)}),e.to=null},i.age+9)),o},e}})(_,"./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,i=this.as,e=i.at||i,r=e.$;(o=t["#"])||(o=t["#"]=u(9)),(n=e.dup).check(o)?i.out===t.out&&(t.out=void 0,this.to.next(t)):(n.track(o),e.ask(t["@"],t)||(t.get&&c.on.get(t,r),t.put&&c.on.put(t,r)),this.to.next(t),i.out||(t.out=a,e.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 e(t,n,o,i){var e=this,r=c.state.is(o,n);if(!r)return e.err="Error: No state on '"+n+"' in node '"+i+"'!";var a=e.graph[i]||v,u=c.state.is(a,n,!0),s=a[n],f=c.HAM(e.machine,r,u,t,s);f.incoming?(e.put[i]=c.state.to(o,n,e.put[i]),(e.diff||(e.diff={}))[i]=c.state.to(o,n,e.diff[i]),e.souls[i]=!0):f.defer&&(e.defer=r<(e.defer||1/0)?r:e.defer)}function r(t,n){var o=this,i=o.$._,e=(i.next||v)[n];if(!e){if(!(i.opt||v).super)return void(o.souls[n]=!1);e=o.$.get(n)._}var r=o.map[n]={put:t,get:n,$:e.$},a={ctx:o,msg:r};o.async=!!i.tag.node,o.ack&&(r["@"]=o.ack),h(t,u,a),o.async&&(o.and||i.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,i.on("node",r))}function u(t,n){var o=this.ctx,i=o.graph,e=this.msg,r=e.get,a=e.put,u=e.$._;i[r]=c.state.to(a,n,i[r]),o.async||(u.put=c.state.to(a,n,u.put))}function s(t,n){var o=this.put,i=this.$._;i.put=c.state.to(o,n,i.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._,i={$:n,graph:o.graph,put:{},map:{},souls:{},machine:c.state(),ack:t["@"],cat:o,stop:{}};if(c.graph.is(t.put,null,e,i)||(i.err="Error: Invalid graph!"),i.err)return o.on("in",{"@":t["#"],err:c.log(i.err)});h(i.put,r,i),i.async||h(i.map,f,i),void 0!==i.defer&&setTimeout(function(){c.on.put(t,n)},i.defer-i.machine),i.diff&&o.on("put",p(t,{put:i.diff}))},c.on.get=function(t,n){var o=n._,i=t.get,e=i[d],r=o.graph[e],a=i[g],u=(o.next||(o.next={}))[e];if(!r)return o.on("get",t);if(a){if("string"!=typeof a||!l(r,a))return o.on("get",t);r=c.state.to(r,a)}else r=c.obj.copy(r);r=c.graph.node(r),(u||v).ack,o.on("in",{"@":t["#"],how:"mem",put:r,$: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),e(o)&&(o=[o]),i(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 i=c.list.is,o=c.text,e=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.link._,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=window.Gun=c).window=window);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 i=0,e=t.length,r=o;i .once, apologies unexpected."),this.once(t,n)},f.chain.once=function(t,n){var o=this,i=o._,e=i.put;if(0=(u.batch||1e3))return f();i||(i=setTimeout(f,u.wait||1))}),a.on("get",function(n){this.to.next(n);var o,i,e,r=n.get;function t(){if(r&&(o=r["#"])){var t=r["."];(i=s[o]||e)&&t&&(i=Gun.state.to(i,t)),(i||Gun.obj.empty(u.peers))&&a.on("in",{"@":n["#"],put:Gun.graph.node(i),how:"lS",lS:n.$||a.$})}}Gun.debug?setTimeout(t,1):t()});var n=function(t,n,o,i){s[i]=Gun.state.to(o,n,s[i])},f=function(t){var o;r=0,clearTimeout(i),i=!1;var n=e;e={},t&&(s=t);try{p.setItem(u.prefix,JSON.stringify(s))}catch(t){Gun.log(o=(t||"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 ."),a.on("localStorage:error",{err:o,file:u.prefix,flush:s,retry:f})}(o||Gun.obj.empty(u.peers))&&Gun.obj.map(n,function(t,n){a.on("in",{"@":n,err:o,ok:0})})}}})}})(_,"./adapters/localStorage"),_(function(t){var g=_("../type");function o(p){var h=function(){},d=p.opt||{};return d.log=d.log||b.log,d.gap=d.gap||d.wait||1,d.pack=d.pack||.3*(d.memory?1e3*d.memory*1e3:1399e6),h.out=function(t){var n;if(this.to&&this.to.next(t),(n=t["@"])&&(n=p.dup.s[n])&&(n=n.it)&&n._)return h.say(t,n._.via,1),void(n["##"]=t["##"]);h.say(t)},p.on("create",function(t){t.opt.pid=t.opt.pid||g.text.random(9),this.to.next(t),p.on("out",h.out)}),h.hear=function(t,n){if(t){var o,i,e,r=p.dup,a=t[0];if(d.pack<=t.length)return h.say({dam:"!",err:"Message too big!"},n);try{e=JSON.parse(t)}catch(t){d.log("DAM JSON parse error",t)}if("{"===a){if(!e)return;if(r.check(o=e["#"]))return;if((a=(r.track(o,!0).it=e)["@"])&&e.put&&(a+=i=e["##"]||(e["##"]=h.hash(e)))!=o){if(r.check(a))return;(a=r.s)[i]=a[o]}return(e._=function(){}).via=n,(a=e["><"])&&(e._.to=g.obj.map(a.split(","),function(t,n,o){o(t,!0)})),e.dam?void((a=h.hear[e.dam])&&a(e,n,p)):void p.on("in",e)}if("["!==a);else{if(!e)return;for(var u,s=0;u=e[s++];)h.hear(u,n)}}},function(){function a(t){var n=t.batch;if(n&&(t.batch=t.tail=null,n.length))try{u(JSON.stringify(n),t)}catch(t){d.log("DAM JSON stringify error",t)}}function u(n,o){var t=o.wire;try{t.send?t.send(n):o.say&&o.say(n)}catch(t){(o.queue=o.queue||[]).push(n)}}h.say=function(n,t,o){var i,e,r;if(t){if((t.wire||d.wire&&d.wire(t))&&(e=n._||s,t!==e.via&&((r=e.raw)||(r=h.raw(n)),!((i=n["@"])&&(i=p.dup.s[i])&&(i=i.it)&&i.get&&i["##"]&&i["##"]===n["##"])&&(!(i=e.to)||!i[t.url]&&!i[t.id]||o)))){if(t.batch){if(t.tail=(t.tail||0)+r.length,t.tail<=d.pack)return void t.batch.push(r);a(t)}t.batch=[],setTimeout(function(){a(t)},d.gap),u(r,t)}}else g.obj.map(d.peers,function(t){h.say(n,t)})}}(),function(){function f(t,n){var o;return n instanceof Object?(g.obj.map(Object.keys(n).sort(),i,{to:o={},on:n}),o):n}function i(t){this.to[t]=this.on[t]}h.raw=function(t){if(!t)return"";var n,o,i,e=p.dup,r=t._||{};if(i=r.raw)return i;if("string"==typeof t)return t;t["@"]&&(i=t.put)&&((o=t["##"])||(n=c(i,f)||"",o=h.hash(t,n),t["##"]=o),(i=e.s)[o=t["@"]+o]=i[t["#"]],t["#"]=o||t["#"],n&&((t=g.obj.to(t)).put=l));var a=0,u=[];g.obj.map(d.peers,function(t){if(u.push(t.url||t.id),9<++a)return!0}),t["><"]=u.join();var s=c(t);return v!==n&&(i=s.indexOf(l,s.indexOf("put")),s=s.slice(0,i-1)+n+s.slice(i+l.length+1)),r&&(r.raw=s),s},h.hash=function(t,n){return o.hash(n||c(t.put,f)||"")||t["#"]||g.text.random(9)};var c=JSON.stringify,l=":])([:"}(),h.hi=function(n){var t=n.wire||{};n.id||n.url?(d.peers[n.url||n.id]=n,g.obj.del(d.peers,t.id)):(t=t.id=t.id||g.text.random(9),h.say({dam:"?"},d.peers[t]=n)),t.hied||p.on(t.hied="hi",n),t=n.queue,n.queue=[],g.obj.map(t,function(t){h.say(t,n)})},h.bye=function(t){g.obj.del(d.peers,t.id),p.on("bye",t)},h.hear["!"]=function(t,n){d.log("Error:",t.err)},h.hear["?"]=function(t,n){if(!t.pid)return h.say({dam:"?",pid:d.pid,"@":t["#"]},n);n.id=n.id||t.pid,h.hi(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,i=t.length;o 0) { - throw new Error('Invalid string. Length must be a multiple of 4') - } - - // the number of equal signs (place holders) - // if there are two placeholders, than the two characters before it - // represent one byte - // if there is only one, then the three characters before it represent 2 bytes - // this is just a cheap hack to not do indexOf twice - return b64[len - 2] === '=' ? 2 : b64[len - 1] === '=' ? 1 : 0 -} - -function byteLength (b64) { - // base64 is 4/3 + up to two characters of the original data - return b64.length * 3 / 4 - placeHoldersCount(b64) -} - -function toByteArray (b64) { - var i, j, l, tmp, placeHolders, arr - var len = b64.length - placeHolders = placeHoldersCount(b64) - - arr = new Arr(len * 3 / 4 - placeHolders) - - // if there are placeholders, only get up to the last complete 4 chars - l = placeHolders > 0 ? len - 4 : len - - var L = 0 - - for (i = 0, j = 0; i < l; i += 4, j += 3) { - tmp = (revLookup[b64.charCodeAt(i)] << 18) | (revLookup[b64.charCodeAt(i + 1)] << 12) | (revLookup[b64.charCodeAt(i + 2)] << 6) | revLookup[b64.charCodeAt(i + 3)] - arr[L++] = (tmp >> 16) & 0xFF - arr[L++] = (tmp >> 8) & 0xFF - arr[L++] = tmp & 0xFF - } - - if (placeHolders === 2) { - tmp = (revLookup[b64.charCodeAt(i)] << 2) | (revLookup[b64.charCodeAt(i + 1)] >> 4) - arr[L++] = tmp & 0xFF - } else if (placeHolders === 1) { - tmp = (revLookup[b64.charCodeAt(i)] << 10) | (revLookup[b64.charCodeAt(i + 1)] << 4) | (revLookup[b64.charCodeAt(i + 2)] >> 2) - arr[L++] = (tmp >> 8) & 0xFF - arr[L++] = tmp & 0xFF - } - - return arr -} - -function tripletToBase64 (num) { - return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F] -} - -function encodeChunk (uint8, start, end) { - var tmp - var output = [] - for (var i = start; i < end; i += 3) { - tmp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]) - output.push(tripletToBase64(tmp)) - } - return output.join('') -} - -function fromByteArray (uint8) { - var tmp - var len = uint8.length - var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes - var output = '' - var parts = [] - var maxChunkLength = 16383 // must be multiple of 3 - - // go through the array every three bytes, we'll deal with trailing stuff later - for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { - parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength))) - } - - // pad the end with zeros, but make sure to not forget the extra bytes - if (extraBytes === 1) { - tmp = uint8[len - 1] - output += lookup[tmp >> 2] - output += lookup[(tmp << 4) & 0x3F] - output += '==' - } else if (extraBytes === 2) { - tmp = (uint8[len - 2] << 8) + (uint8[len - 1]) - output += lookup[tmp >> 10] - output += lookup[(tmp >> 4) & 0x3F] - output += lookup[(tmp << 2) & 0x3F] - output += '=' - } - - parts.push(output) - - return parts.join('') -} - -},{}],2:[function(require,module,exports){ -exports.read = function (buffer, offset, isLE, mLen, nBytes) { - var e, m - var eLen = nBytes * 8 - mLen - 1 - var eMax = (1 << eLen) - 1 - var eBias = eMax >> 1 - var nBits = -7 - var i = isLE ? (nBytes - 1) : 0 - var d = isLE ? -1 : 1 - var s = buffer[offset + i] - - i += d - - e = s & ((1 << (-nBits)) - 1) - s >>= (-nBits) - nBits += eLen - for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {} - - m = e & ((1 << (-nBits)) - 1) - e >>= (-nBits) - nBits += mLen - for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {} - - if (e === 0) { - e = 1 - eBias - } else if (e === eMax) { - return m ? NaN : ((s ? -1 : 1) * Infinity) - } else { - m = m + Math.pow(2, mLen) - e = e - eBias - } - return (s ? -1 : 1) * m * Math.pow(2, e - mLen) -} - -exports.write = function (buffer, value, offset, isLE, mLen, nBytes) { - var e, m, c - var eLen = nBytes * 8 - mLen - 1 - var eMax = (1 << eLen) - 1 - var eBias = eMax >> 1 - var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0) - var i = isLE ? 0 : (nBytes - 1) - var d = isLE ? 1 : -1 - var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0 - - value = Math.abs(value) - - if (isNaN(value) || value === Infinity) { - m = isNaN(value) ? 1 : 0 - e = eMax - } else { - e = Math.floor(Math.log(value) / Math.LN2) - if (value * (c = Math.pow(2, -e)) < 1) { - e-- - c *= 2 - } - if (e + eBias >= 1) { - value += rt / c - } else { - value += rt * Math.pow(2, 1 - eBias) - } - if (value * c >= 2) { - e++ - c /= 2 - } - - if (e + eBias >= eMax) { - m = 0 - e = eMax - } else if (e + eBias >= 1) { - m = (value * c - 1) * Math.pow(2, mLen) - e = e + eBias - } else { - m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen) - e = 0 - } - } - - for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} - - e = (e << mLen) | m - eLen += mLen - for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} - - buffer[offset + i - d] |= s * 128 -} - -},{}],3:[function(require,module,exports){ -var toString = {}.toString; - -module.exports = Array.isArray || function (arr) { - return toString.call(arr) == '[object Array]'; -}; - -},{}],"buffer":[function(require,module,exports){ -(function (global){ -/*! - * The buffer module from node.js, for the browser. - * - * @author Feross Aboukhadijeh - * @license MIT - */ -/* eslint-disable no-proto */ - -'use strict' - -var base64 = require('base64-js') -var ieee754 = require('ieee754') -var isArray = require('isarray') - -exports.Buffer = Buffer -exports.SlowBuffer = SlowBuffer -exports.INSPECT_MAX_BYTES = 50 - -/** - * If `Buffer.TYPED_ARRAY_SUPPORT`: - * === true Use Uint8Array implementation (fastest) - * === false Use Object implementation (most compatible, even IE6) - * - * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+, - * Opera 11.6+, iOS 4.2+. - * - * Due to various browser bugs, sometimes the Object implementation will be used even - * when the browser supports typed arrays. - * - * Note: - * - * - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances, - * See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438. - * - * - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function. - * - * - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of - * incorrect length in some situations. - - * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they - * get the Object implementation, which is slower but behaves correctly. - */ -Buffer.TYPED_ARRAY_SUPPORT = global.TYPED_ARRAY_SUPPORT !== undefined - ? global.TYPED_ARRAY_SUPPORT - : typedArraySupport() - -/* - * Export kMaxLength after typed array support is determined. - */ -exports.kMaxLength = kMaxLength() - -function typedArraySupport () { - try { - var arr = new Uint8Array(1) - arr.__proto__ = {__proto__: Uint8Array.prototype, foo: function () { return 42 }} - return arr.foo() === 42 && // typed array instances can be augmented - typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray` - arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray` - } catch (e) { - return false - } -} - -function kMaxLength () { - return Buffer.TYPED_ARRAY_SUPPORT - ? 0x7fffffff - : 0x3fffffff -} - -function createBuffer (that, length) { - if (kMaxLength() < length) { - throw new RangeError('Invalid typed array length') - } - if (Buffer.TYPED_ARRAY_SUPPORT) { - // Return an augmented `Uint8Array` instance, for best performance - that = new Uint8Array(length) - that.__proto__ = Buffer.prototype - } else { - // Fallback: Return an object instance of the Buffer class - if (that === null) { - that = new Buffer(length) - } - that.length = length - } - - return that -} - -/** - * The Buffer constructor returns instances of `Uint8Array` that have their - * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of - * `Uint8Array`, so the returned instances will have all the node `Buffer` methods - * and the `Uint8Array` methods. Square bracket notation works as expected -- it - * returns a single octet. - * - * The `Uint8Array` prototype remains unmodified. - */ - -function Buffer (arg, encodingOrOffset, length) { - if (!Buffer.TYPED_ARRAY_SUPPORT && !(this instanceof Buffer)) { - return new Buffer(arg, encodingOrOffset, length) - } - - // Common case. - if (typeof arg === 'number') { - if (typeof encodingOrOffset === 'string') { - throw new Error( - 'If encoding is specified then the first argument must be a string' - ) - } - return allocUnsafe(this, arg) - } - return from(this, arg, encodingOrOffset, length) -} - -Buffer.poolSize = 8192 // not used by this implementation - -// TODO: Legacy, not needed anymore. Remove in next major version. -Buffer._augment = function (arr) { - arr.__proto__ = Buffer.prototype - return arr -} - -function from (that, value, encodingOrOffset, length) { - if (typeof value === 'number') { - throw new TypeError('"value" argument must not be a number') - } - - if (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) { - return fromArrayBuffer(that, value, encodingOrOffset, length) - } - - if (typeof value === 'string') { - return fromString(that, value, encodingOrOffset) - } - - return fromObject(that, value) -} - -/** - * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError - * if value is a number. - * Buffer.from(str[, encoding]) - * Buffer.from(array) - * Buffer.from(buffer) - * Buffer.from(arrayBuffer[, byteOffset[, length]]) - **/ -Buffer.from = function (value, encodingOrOffset, length) { - return from(null, value, encodingOrOffset, length) -} - -if (Buffer.TYPED_ARRAY_SUPPORT) { - Buffer.prototype.__proto__ = Uint8Array.prototype - Buffer.__proto__ = Uint8Array - if (typeof Symbol !== 'undefined' && Symbol.species && - Buffer[Symbol.species] === Buffer) { - // Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97 - Object.defineProperty(Buffer, Symbol.species, { - value: null, - configurable: true - }) - } -} - -function assertSize (size) { - if (typeof size !== 'number') { - throw new TypeError('"size" argument must be a number') - } else if (size < 0) { - throw new RangeError('"size" argument must not be negative') - } -} - -function alloc (that, size, fill, encoding) { - assertSize(size) - if (size <= 0) { - return createBuffer(that, size) - } - if (fill !== undefined) { - // Only pay attention to encoding if it's a string. This - // prevents accidentally sending in a number that would - // be interpretted as a start offset. - return typeof encoding === 'string' - ? createBuffer(that, size).fill(fill, encoding) - : createBuffer(that, size).fill(fill) - } - return createBuffer(that, size) -} - -/** - * Creates a new filled Buffer instance. - * alloc(size[, fill[, encoding]]) - **/ -Buffer.alloc = function (size, fill, encoding) { - return alloc(null, size, fill, encoding) -} - -function allocUnsafe (that, size) { - assertSize(size) - that = createBuffer(that, size < 0 ? 0 : checked(size) | 0) - if (!Buffer.TYPED_ARRAY_SUPPORT) { - for (var i = 0; i < size; ++i) { - that[i] = 0 - } - } - return that -} - -/** - * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance. - * */ -Buffer.allocUnsafe = function (size) { - return allocUnsafe(null, size) -} -/** - * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance. - */ -Buffer.allocUnsafeSlow = function (size) { - return allocUnsafe(null, size) -} - -function fromString (that, string, encoding) { - if (typeof encoding !== 'string' || encoding === '') { - encoding = 'utf8' - } - - if (!Buffer.isEncoding(encoding)) { - throw new TypeError('"encoding" must be a valid string encoding') - } - - var length = byteLength(string, encoding) | 0 - that = createBuffer(that, length) - - var actual = that.write(string, encoding) - - if (actual !== length) { - // Writing a hex string, for example, that contains invalid characters will - // cause everything after the first invalid character to be ignored. (e.g. - // 'abxxcd' will be treated as 'ab') - that = that.slice(0, actual) - } - - return that -} - -function fromArrayLike (that, array) { - var length = array.length < 0 ? 0 : checked(array.length) | 0 - that = createBuffer(that, length) - for (var i = 0; i < length; i += 1) { - that[i] = array[i] & 255 - } - return that -} - -function fromArrayBuffer (that, array, byteOffset, length) { - array.byteLength // this throws if `array` is not a valid ArrayBuffer - - if (byteOffset < 0 || array.byteLength < byteOffset) { - throw new RangeError('\'offset\' is out of bounds') - } - - if (array.byteLength < byteOffset + (length || 0)) { - throw new RangeError('\'length\' is out of bounds') - } - - if (byteOffset === undefined && length === undefined) { - array = new Uint8Array(array) - } else if (length === undefined) { - array = new Uint8Array(array, byteOffset) - } else { - array = new Uint8Array(array, byteOffset, length) - } - - if (Buffer.TYPED_ARRAY_SUPPORT) { - // Return an augmented `Uint8Array` instance, for best performance - that = array - that.__proto__ = Buffer.prototype - } else { - // Fallback: Return an object instance of the Buffer class - that = fromArrayLike(that, array) - } - return that -} - -function fromObject (that, obj) { - if (Buffer.isBuffer(obj)) { - var len = checked(obj.length) | 0 - that = createBuffer(that, len) - - if (that.length === 0) { - return that - } - - obj.copy(that, 0, 0, len) - return that - } - - if (obj) { - if ((typeof ArrayBuffer !== 'undefined' && - obj.buffer instanceof ArrayBuffer) || 'length' in obj) { - if (typeof obj.length !== 'number' || isnan(obj.length)) { - return createBuffer(that, 0) - } - return fromArrayLike(that, obj) - } - - if (obj.type === 'Buffer' && isArray(obj.data)) { - return fromArrayLike(that, obj.data) - } - } - - throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.') -} - -function checked (length) { - // Note: cannot use `length < kMaxLength()` here because that fails when - // length is NaN (which is otherwise coerced to zero.) - if (length >= kMaxLength()) { - throw new RangeError('Attempt to allocate Buffer larger than maximum ' + - 'size: 0x' + kMaxLength().toString(16) + ' bytes') - } - return length | 0 -} - -function SlowBuffer (length) { - if (+length != length) { // eslint-disable-line eqeqeq - length = 0 - } - return Buffer.alloc(+length) -} - -Buffer.isBuffer = function isBuffer (b) { - return !!(b != null && b._isBuffer) -} - -Buffer.compare = function compare (a, b) { - if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) { - throw new TypeError('Arguments must be Buffers') - } - - if (a === b) return 0 - - var x = a.length - var y = b.length - - for (var i = 0, len = Math.min(x, y); i < len; ++i) { - if (a[i] !== b[i]) { - x = a[i] - y = b[i] - break - } - } - - if (x < y) return -1 - if (y < x) return 1 - return 0 -} - -Buffer.isEncoding = function isEncoding (encoding) { - switch (String(encoding).toLowerCase()) { - case 'hex': - case 'utf8': - case 'utf-8': - case 'ascii': - case 'latin1': - case 'binary': - case 'base64': - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return true - default: - return false - } -} - -Buffer.concat = function concat (list, length) { - if (!isArray(list)) { - throw new TypeError('"list" argument must be an Array of Buffers') - } - - if (list.length === 0) { - return Buffer.alloc(0) - } - - var i - if (length === undefined) { - length = 0 - for (i = 0; i < list.length; ++i) { - length += list[i].length - } - } - - var buffer = Buffer.allocUnsafe(length) - var pos = 0 - for (i = 0; i < list.length; ++i) { - var buf = list[i] - if (!Buffer.isBuffer(buf)) { - throw new TypeError('"list" argument must be an Array of Buffers') - } - buf.copy(buffer, pos) - pos += buf.length - } - return buffer -} - -function byteLength (string, encoding) { - if (Buffer.isBuffer(string)) { - return string.length - } - if (typeof ArrayBuffer !== 'undefined' && typeof ArrayBuffer.isView === 'function' && - (ArrayBuffer.isView(string) || string instanceof ArrayBuffer)) { - return string.byteLength - } - if (typeof string !== 'string') { - string = '' + string - } - - var len = string.length - if (len === 0) return 0 - - // Use a for loop to avoid recursion - var loweredCase = false - for (;;) { - switch (encoding) { - case 'ascii': - case 'latin1': - case 'binary': - return len - case 'utf8': - case 'utf-8': - case undefined: - return utf8ToBytes(string).length - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return len * 2 - case 'hex': - return len >>> 1 - case 'base64': - return base64ToBytes(string).length - default: - if (loweredCase) return utf8ToBytes(string).length // assume utf8 - encoding = ('' + encoding).toLowerCase() - loweredCase = true - } - } -} -Buffer.byteLength = byteLength - -function slowToString (encoding, start, end) { - var loweredCase = false - - // No need to verify that "this.length <= MAX_UINT32" since it's a read-only - // property of a typed array. - - // This behaves neither like String nor Uint8Array in that we set start/end - // to their upper/lower bounds if the value passed is out of range. - // undefined is handled specially as per ECMA-262 6th Edition, - // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization. - if (start === undefined || start < 0) { - start = 0 - } - // Return early if start > this.length. Done here to prevent potential uint32 - // coercion fail below. - if (start > this.length) { - return '' - } - - if (end === undefined || end > this.length) { - end = this.length - } - - if (end <= 0) { - return '' - } - - // Force coersion to uint32. This will also coerce falsey/NaN values to 0. - end >>>= 0 - start >>>= 0 - - if (end <= start) { - return '' - } - - if (!encoding) encoding = 'utf8' - - while (true) { - switch (encoding) { - case 'hex': - return hexSlice(this, start, end) - - case 'utf8': - case 'utf-8': - return utf8Slice(this, start, end) - - case 'ascii': - return asciiSlice(this, start, end) - - case 'latin1': - case 'binary': - return latin1Slice(this, start, end) - - case 'base64': - return base64Slice(this, start, end) - - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return utf16leSlice(this, start, end) - - default: - if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) - encoding = (encoding + '').toLowerCase() - loweredCase = true - } - } -} - -// The property is used by `Buffer.isBuffer` and `is-buffer` (in Safari 5-7) to detect -// Buffer instances. -Buffer.prototype._isBuffer = true - -function swap (b, n, m) { - var i = b[n] - b[n] = b[m] - b[m] = i -} - -Buffer.prototype.swap16 = function swap16 () { - var len = this.length - if (len % 2 !== 0) { - throw new RangeError('Buffer size must be a multiple of 16-bits') - } - for (var i = 0; i < len; i += 2) { - swap(this, i, i + 1) - } - return this -} - -Buffer.prototype.swap32 = function swap32 () { - var len = this.length - if (len % 4 !== 0) { - throw new RangeError('Buffer size must be a multiple of 32-bits') - } - for (var i = 0; i < len; i += 4) { - swap(this, i, i + 3) - swap(this, i + 1, i + 2) - } - return this -} - -Buffer.prototype.swap64 = function swap64 () { - var len = this.length - if (len % 8 !== 0) { - throw new RangeError('Buffer size must be a multiple of 64-bits') - } - for (var i = 0; i < len; i += 8) { - swap(this, i, i + 7) - swap(this, i + 1, i + 6) - swap(this, i + 2, i + 5) - swap(this, i + 3, i + 4) - } - return this -} - -Buffer.prototype.toString = function toString () { - var length = this.length | 0 - if (length === 0) return '' - if (arguments.length === 0) return utf8Slice(this, 0, length) - return slowToString.apply(this, arguments) -} - -Buffer.prototype.equals = function equals (b) { - if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer') - if (this === b) return true - return Buffer.compare(this, b) === 0 -} - -Buffer.prototype.inspect = function inspect () { - var str = '' - var max = exports.INSPECT_MAX_BYTES - if (this.length > 0) { - str = this.toString('hex', 0, max).match(/.{2}/g).join(' ') - if (this.length > max) str += ' ... ' - } - return '' -} - -Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) { - if (!Buffer.isBuffer(target)) { - throw new TypeError('Argument must be a Buffer') - } - - if (start === undefined) { - start = 0 - } - if (end === undefined) { - end = target ? target.length : 0 - } - if (thisStart === undefined) { - thisStart = 0 - } - if (thisEnd === undefined) { - thisEnd = this.length - } - - if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) { - throw new RangeError('out of range index') - } - - if (thisStart >= thisEnd && start >= end) { - return 0 - } - if (thisStart >= thisEnd) { - return -1 - } - if (start >= end) { - return 1 - } - - start >>>= 0 - end >>>= 0 - thisStart >>>= 0 - thisEnd >>>= 0 - - if (this === target) return 0 - - var x = thisEnd - thisStart - var y = end - start - var len = Math.min(x, y) - - var thisCopy = this.slice(thisStart, thisEnd) - var targetCopy = target.slice(start, end) - - for (var i = 0; i < len; ++i) { - if (thisCopy[i] !== targetCopy[i]) { - x = thisCopy[i] - y = targetCopy[i] - break - } - } - - if (x < y) return -1 - if (y < x) return 1 - return 0 -} - -// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`, -// OR the last index of `val` in `buffer` at offset <= `byteOffset`. -// -// Arguments: -// - buffer - a Buffer to search -// - val - a string, Buffer, or number -// - byteOffset - an index into `buffer`; will be clamped to an int32 -// - encoding - an optional encoding, relevant is val is a string -// - dir - true for indexOf, false for lastIndexOf -function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) { - // Empty buffer means no match - if (buffer.length === 0) return -1 - - // Normalize byteOffset - if (typeof byteOffset === 'string') { - encoding = byteOffset - byteOffset = 0 - } else if (byteOffset > 0x7fffffff) { - byteOffset = 0x7fffffff - } else if (byteOffset < -0x80000000) { - byteOffset = -0x80000000 - } - byteOffset = +byteOffset // Coerce to Number. - if (isNaN(byteOffset)) { - // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer - byteOffset = dir ? 0 : (buffer.length - 1) - } - - // Normalize byteOffset: negative offsets start from the end of the buffer - if (byteOffset < 0) byteOffset = buffer.length + byteOffset - if (byteOffset >= buffer.length) { - if (dir) return -1 - else byteOffset = buffer.length - 1 - } else if (byteOffset < 0) { - if (dir) byteOffset = 0 - else return -1 - } - - // Normalize val - if (typeof val === 'string') { - val = Buffer.from(val, encoding) - } - - // Finally, search either indexOf (if dir is true) or lastIndexOf - if (Buffer.isBuffer(val)) { - // Special case: looking for empty string/buffer always fails - if (val.length === 0) { - return -1 - } - return arrayIndexOf(buffer, val, byteOffset, encoding, dir) - } else if (typeof val === 'number') { - val = val & 0xFF // Search for a byte value [0-255] - if (Buffer.TYPED_ARRAY_SUPPORT && - typeof Uint8Array.prototype.indexOf === 'function') { - if (dir) { - return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset) - } else { - return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset) - } - } - return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir) - } - - throw new TypeError('val must be string, number or Buffer') -} - -function arrayIndexOf (arr, val, byteOffset, encoding, dir) { - var indexSize = 1 - var arrLength = arr.length - var valLength = val.length - - if (encoding !== undefined) { - encoding = String(encoding).toLowerCase() - if (encoding === 'ucs2' || encoding === 'ucs-2' || - encoding === 'utf16le' || encoding === 'utf-16le') { - if (arr.length < 2 || val.length < 2) { - return -1 - } - indexSize = 2 - arrLength /= 2 - valLength /= 2 - byteOffset /= 2 - } - } - - function read (buf, i) { - if (indexSize === 1) { - return buf[i] - } else { - return buf.readUInt16BE(i * indexSize) - } - } - - var i - if (dir) { - var foundIndex = -1 - for (i = byteOffset; i < arrLength; i++) { - if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) { - if (foundIndex === -1) foundIndex = i - if (i - foundIndex + 1 === valLength) return foundIndex * indexSize - } else { - if (foundIndex !== -1) i -= i - foundIndex - foundIndex = -1 - } - } - } else { - if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength - for (i = byteOffset; i >= 0; i--) { - var found = true - for (var j = 0; j < valLength; j++) { - if (read(arr, i + j) !== read(val, j)) { - found = false - break - } - } - if (found) return i - } - } - - return -1 -} - -Buffer.prototype.includes = function includes (val, byteOffset, encoding) { - return this.indexOf(val, byteOffset, encoding) !== -1 -} - -Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) { - return bidirectionalIndexOf(this, val, byteOffset, encoding, true) -} - -Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) { - return bidirectionalIndexOf(this, val, byteOffset, encoding, false) -} - -function hexWrite (buf, string, offset, length) { - offset = Number(offset) || 0 - var remaining = buf.length - offset - if (!length) { - length = remaining - } else { - length = Number(length) - if (length > remaining) { - length = remaining - } - } - - // must be an even number of digits - var strLen = string.length - if (strLen % 2 !== 0) throw new TypeError('Invalid hex string') - - if (length > strLen / 2) { - length = strLen / 2 - } - for (var i = 0; i < length; ++i) { - var parsed = parseInt(string.substr(i * 2, 2), 16) - if (isNaN(parsed)) return i - buf[offset + i] = parsed - } - return i -} - -function utf8Write (buf, string, offset, length) { - return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length) -} - -function asciiWrite (buf, string, offset, length) { - return blitBuffer(asciiToBytes(string), buf, offset, length) -} - -function latin1Write (buf, string, offset, length) { - return asciiWrite(buf, string, offset, length) -} - -function base64Write (buf, string, offset, length) { - return blitBuffer(base64ToBytes(string), buf, offset, length) -} - -function ucs2Write (buf, string, offset, length) { - return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length) -} - -Buffer.prototype.write = function write (string, offset, length, encoding) { - // Buffer#write(string) - if (offset === undefined) { - encoding = 'utf8' - length = this.length - offset = 0 - // Buffer#write(string, encoding) - } else if (length === undefined && typeof offset === 'string') { - encoding = offset - length = this.length - offset = 0 - // Buffer#write(string, offset[, length][, encoding]) - } else if (isFinite(offset)) { - offset = offset | 0 - if (isFinite(length)) { - length = length | 0 - if (encoding === undefined) encoding = 'utf8' - } else { - encoding = length - length = undefined - } - // legacy write(string, encoding, offset, length) - remove in v0.13 - } else { - throw new Error( - 'Buffer.write(string, encoding, offset[, length]) is no longer supported' - ) - } - - var remaining = this.length - offset - if (length === undefined || length > remaining) length = remaining - - if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) { - throw new RangeError('Attempt to write outside buffer bounds') - } - - if (!encoding) encoding = 'utf8' - - var loweredCase = false - for (;;) { - switch (encoding) { - case 'hex': - return hexWrite(this, string, offset, length) - - case 'utf8': - case 'utf-8': - return utf8Write(this, string, offset, length) - - case 'ascii': - return asciiWrite(this, string, offset, length) - - case 'latin1': - case 'binary': - return latin1Write(this, string, offset, length) - - case 'base64': - // Warning: maxLength not taken into account in base64Write - return base64Write(this, string, offset, length) - - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return ucs2Write(this, string, offset, length) - - default: - if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) - encoding = ('' + encoding).toLowerCase() - loweredCase = true - } - } -} - -Buffer.prototype.toJSON = function toJSON () { - return { - type: 'Buffer', - data: Array.prototype.slice.call(this._arr || this, 0) - } -} - -function base64Slice (buf, start, end) { - if (start === 0 && end === buf.length) { - return base64.fromByteArray(buf) - } else { - return base64.fromByteArray(buf.slice(start, end)) - } -} - -function utf8Slice (buf, start, end) { - end = Math.min(buf.length, end) - var res = [] - - var i = start - while (i < end) { - var firstByte = buf[i] - var codePoint = null - var bytesPerSequence = (firstByte > 0xEF) ? 4 - : (firstByte > 0xDF) ? 3 - : (firstByte > 0xBF) ? 2 - : 1 - - if (i + bytesPerSequence <= end) { - var secondByte, thirdByte, fourthByte, tempCodePoint - - switch (bytesPerSequence) { - case 1: - if (firstByte < 0x80) { - codePoint = firstByte - } - break - case 2: - secondByte = buf[i + 1] - if ((secondByte & 0xC0) === 0x80) { - tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F) - if (tempCodePoint > 0x7F) { - codePoint = tempCodePoint - } - } - break - case 3: - secondByte = buf[i + 1] - thirdByte = buf[i + 2] - if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) { - tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F) - if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) { - codePoint = tempCodePoint - } - } - break - case 4: - secondByte = buf[i + 1] - thirdByte = buf[i + 2] - fourthByte = buf[i + 3] - if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) { - tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F) - if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) { - codePoint = tempCodePoint - } - } - } - } - - if (codePoint === null) { - // we did not generate a valid codePoint so insert a - // replacement char (U+FFFD) and advance only 1 byte - codePoint = 0xFFFD - bytesPerSequence = 1 - } else if (codePoint > 0xFFFF) { - // encode to utf16 (surrogate pair dance) - codePoint -= 0x10000 - res.push(codePoint >>> 10 & 0x3FF | 0xD800) - codePoint = 0xDC00 | codePoint & 0x3FF - } - - res.push(codePoint) - i += bytesPerSequence - } - - return decodeCodePointsArray(res) -} - -// Based on http://stackoverflow.com/a/22747272/680742, the browser with -// the lowest limit is Chrome, with 0x10000 args. -// We go 1 magnitude less, for safety -var MAX_ARGUMENTS_LENGTH = 0x1000 - -function decodeCodePointsArray (codePoints) { - var len = codePoints.length - if (len <= MAX_ARGUMENTS_LENGTH) { - return String.fromCharCode.apply(String, codePoints) // avoid extra slice() - } - - // Decode in chunks to avoid "call stack size exceeded". - var res = '' - var i = 0 - while (i < len) { - res += String.fromCharCode.apply( - String, - codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH) - ) - } - return res -} - -function asciiSlice (buf, start, end) { - var ret = '' - end = Math.min(buf.length, end) - - for (var i = start; i < end; ++i) { - ret += String.fromCharCode(buf[i] & 0x7F) - } - return ret -} - -function latin1Slice (buf, start, end) { - var ret = '' - end = Math.min(buf.length, end) - - for (var i = start; i < end; ++i) { - ret += String.fromCharCode(buf[i]) - } - return ret -} - -function hexSlice (buf, start, end) { - var len = buf.length - - if (!start || start < 0) start = 0 - if (!end || end < 0 || end > len) end = len - - var out = '' - for (var i = start; i < end; ++i) { - out += toHex(buf[i]) - } - return out -} - -function utf16leSlice (buf, start, end) { - var bytes = buf.slice(start, end) - var res = '' - for (var i = 0; i < bytes.length; i += 2) { - res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256) - } - return res -} - -Buffer.prototype.slice = function slice (start, end) { - var len = this.length - start = ~~start - end = end === undefined ? len : ~~end - - if (start < 0) { - start += len - if (start < 0) start = 0 - } else if (start > len) { - start = len - } - - if (end < 0) { - end += len - if (end < 0) end = 0 - } else if (end > len) { - end = len - } - - if (end < start) end = start - - var newBuf - if (Buffer.TYPED_ARRAY_SUPPORT) { - newBuf = this.subarray(start, end) - newBuf.__proto__ = Buffer.prototype - } else { - var sliceLen = end - start - newBuf = new Buffer(sliceLen, undefined) - for (var i = 0; i < sliceLen; ++i) { - newBuf[i] = this[i + start] - } - } - - return newBuf -} - -/* - * Need to make sure that buffer isn't trying to write out of bounds. - */ -function checkOffset (offset, ext, length) { - if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint') - if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length') -} - -Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) { - offset = offset | 0 - byteLength = byteLength | 0 - if (!noAssert) checkOffset(offset, byteLength, this.length) - - var val = this[offset] - var mul = 1 - var i = 0 - while (++i < byteLength && (mul *= 0x100)) { - val += this[offset + i] * mul - } - - return val -} - -Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) { - offset = offset | 0 - byteLength = byteLength | 0 - if (!noAssert) { - checkOffset(offset, byteLength, this.length) - } - - var val = this[offset + --byteLength] - var mul = 1 - while (byteLength > 0 && (mul *= 0x100)) { - val += this[offset + --byteLength] * mul - } - - return val -} - -Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) { - if (!noAssert) checkOffset(offset, 1, this.length) - return this[offset] -} - -Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) { - if (!noAssert) checkOffset(offset, 2, this.length) - return this[offset] | (this[offset + 1] << 8) -} - -Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) { - if (!noAssert) checkOffset(offset, 2, this.length) - return (this[offset] << 8) | this[offset + 1] -} - -Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) { - if (!noAssert) checkOffset(offset, 4, this.length) - - return ((this[offset]) | - (this[offset + 1] << 8) | - (this[offset + 2] << 16)) + - (this[offset + 3] * 0x1000000) -} - -Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) { - if (!noAssert) checkOffset(offset, 4, this.length) - - return (this[offset] * 0x1000000) + - ((this[offset + 1] << 16) | - (this[offset + 2] << 8) | - this[offset + 3]) -} - -Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) { - offset = offset | 0 - byteLength = byteLength | 0 - if (!noAssert) checkOffset(offset, byteLength, this.length) - - var val = this[offset] - var mul = 1 - var i = 0 - while (++i < byteLength && (mul *= 0x100)) { - val += this[offset + i] * mul - } - mul *= 0x80 - - if (val >= mul) val -= Math.pow(2, 8 * byteLength) - - return val -} - -Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) { - offset = offset | 0 - byteLength = byteLength | 0 - if (!noAssert) checkOffset(offset, byteLength, this.length) - - var i = byteLength - var mul = 1 - var val = this[offset + --i] - while (i > 0 && (mul *= 0x100)) { - val += this[offset + --i] * mul - } - mul *= 0x80 - - if (val >= mul) val -= Math.pow(2, 8 * byteLength) - - return val -} - -Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) { - if (!noAssert) checkOffset(offset, 1, this.length) - if (!(this[offset] & 0x80)) return (this[offset]) - return ((0xff - this[offset] + 1) * -1) -} - -Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) { - if (!noAssert) checkOffset(offset, 2, this.length) - var val = this[offset] | (this[offset + 1] << 8) - return (val & 0x8000) ? val | 0xFFFF0000 : val -} - -Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) { - if (!noAssert) checkOffset(offset, 2, this.length) - var val = this[offset + 1] | (this[offset] << 8) - return (val & 0x8000) ? val | 0xFFFF0000 : val -} - -Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) { - if (!noAssert) checkOffset(offset, 4, this.length) - - return (this[offset]) | - (this[offset + 1] << 8) | - (this[offset + 2] << 16) | - (this[offset + 3] << 24) -} - -Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) { - if (!noAssert) checkOffset(offset, 4, this.length) - - return (this[offset] << 24) | - (this[offset + 1] << 16) | - (this[offset + 2] << 8) | - (this[offset + 3]) -} - -Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) { - if (!noAssert) checkOffset(offset, 4, this.length) - return ieee754.read(this, offset, true, 23, 4) -} - -Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) { - if (!noAssert) checkOffset(offset, 4, this.length) - return ieee754.read(this, offset, false, 23, 4) -} - -Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) { - if (!noAssert) checkOffset(offset, 8, this.length) - return ieee754.read(this, offset, true, 52, 8) -} - -Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) { - if (!noAssert) checkOffset(offset, 8, this.length) - return ieee754.read(this, offset, false, 52, 8) -} - -function checkInt (buf, value, offset, ext, max, min) { - if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance') - if (value > max || value < min) throw new RangeError('"value" argument is out of bounds') - if (offset + ext > buf.length) throw new RangeError('Index out of range') -} - -Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) { - value = +value - offset = offset | 0 - byteLength = byteLength | 0 - if (!noAssert) { - var maxBytes = Math.pow(2, 8 * byteLength) - 1 - checkInt(this, value, offset, byteLength, maxBytes, 0) - } - - var mul = 1 - var i = 0 - this[offset] = value & 0xFF - while (++i < byteLength && (mul *= 0x100)) { - this[offset + i] = (value / mul) & 0xFF - } - - return offset + byteLength -} - -Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) { - value = +value - offset = offset | 0 - byteLength = byteLength | 0 - if (!noAssert) { - var maxBytes = Math.pow(2, 8 * byteLength) - 1 - checkInt(this, value, offset, byteLength, maxBytes, 0) - } - - var i = byteLength - 1 - var mul = 1 - this[offset + i] = value & 0xFF - while (--i >= 0 && (mul *= 0x100)) { - this[offset + i] = (value / mul) & 0xFF - } - - return offset + byteLength -} - -Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) { - value = +value - offset = offset | 0 - if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0) - if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value) - this[offset] = (value & 0xff) - return offset + 1 -} - -function objectWriteUInt16 (buf, value, offset, littleEndian) { - if (value < 0) value = 0xffff + value + 1 - for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; ++i) { - buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>> - (littleEndian ? i : 1 - i) * 8 - } -} - -Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) { - value = +value - offset = offset | 0 - if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) - if (Buffer.TYPED_ARRAY_SUPPORT) { - this[offset] = (value & 0xff) - this[offset + 1] = (value >>> 8) - } else { - objectWriteUInt16(this, value, offset, true) - } - return offset + 2 -} - -Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) { - value = +value - offset = offset | 0 - if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) - if (Buffer.TYPED_ARRAY_SUPPORT) { - this[offset] = (value >>> 8) - this[offset + 1] = (value & 0xff) - } else { - objectWriteUInt16(this, value, offset, false) - } - return offset + 2 -} - -function objectWriteUInt32 (buf, value, offset, littleEndian) { - if (value < 0) value = 0xffffffff + value + 1 - for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; ++i) { - buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff - } -} - -Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) { - value = +value - offset = offset | 0 - if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) - if (Buffer.TYPED_ARRAY_SUPPORT) { - this[offset + 3] = (value >>> 24) - this[offset + 2] = (value >>> 16) - this[offset + 1] = (value >>> 8) - this[offset] = (value & 0xff) - } else { - objectWriteUInt32(this, value, offset, true) - } - return offset + 4 -} - -Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) { - value = +value - offset = offset | 0 - if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) - if (Buffer.TYPED_ARRAY_SUPPORT) { - this[offset] = (value >>> 24) - this[offset + 1] = (value >>> 16) - this[offset + 2] = (value >>> 8) - this[offset + 3] = (value & 0xff) - } else { - objectWriteUInt32(this, value, offset, false) - } - return offset + 4 -} - -Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) { - value = +value - offset = offset | 0 - if (!noAssert) { - var limit = Math.pow(2, 8 * byteLength - 1) - - checkInt(this, value, offset, byteLength, limit - 1, -limit) - } - - var i = 0 - var mul = 1 - var sub = 0 - this[offset] = value & 0xFF - while (++i < byteLength && (mul *= 0x100)) { - if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) { - sub = 1 - } - this[offset + i] = ((value / mul) >> 0) - sub & 0xFF - } - - return offset + byteLength -} - -Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) { - value = +value - offset = offset | 0 - if (!noAssert) { - var limit = Math.pow(2, 8 * byteLength - 1) - - checkInt(this, value, offset, byteLength, limit - 1, -limit) - } - - var i = byteLength - 1 - var mul = 1 - var sub = 0 - this[offset + i] = value & 0xFF - while (--i >= 0 && (mul *= 0x100)) { - if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) { - sub = 1 - } - this[offset + i] = ((value / mul) >> 0) - sub & 0xFF - } - - return offset + byteLength -} - -Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) { - value = +value - offset = offset | 0 - if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80) - if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value) - if (value < 0) value = 0xff + value + 1 - this[offset] = (value & 0xff) - return offset + 1 -} - -Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) { - value = +value - offset = offset | 0 - if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) - if (Buffer.TYPED_ARRAY_SUPPORT) { - this[offset] = (value & 0xff) - this[offset + 1] = (value >>> 8) - } else { - objectWriteUInt16(this, value, offset, true) - } - return offset + 2 -} - -Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) { - value = +value - offset = offset | 0 - if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) - if (Buffer.TYPED_ARRAY_SUPPORT) { - this[offset] = (value >>> 8) - this[offset + 1] = (value & 0xff) - } else { - objectWriteUInt16(this, value, offset, false) - } - return offset + 2 -} - -Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) { - value = +value - offset = offset | 0 - if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) - if (Buffer.TYPED_ARRAY_SUPPORT) { - this[offset] = (value & 0xff) - this[offset + 1] = (value >>> 8) - this[offset + 2] = (value >>> 16) - this[offset + 3] = (value >>> 24) - } else { - objectWriteUInt32(this, value, offset, true) - } - return offset + 4 -} - -Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) { - value = +value - offset = offset | 0 - if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) - if (value < 0) value = 0xffffffff + value + 1 - if (Buffer.TYPED_ARRAY_SUPPORT) { - this[offset] = (value >>> 24) - this[offset + 1] = (value >>> 16) - this[offset + 2] = (value >>> 8) - this[offset + 3] = (value & 0xff) - } else { - objectWriteUInt32(this, value, offset, false) - } - return offset + 4 -} - -function checkIEEE754 (buf, value, offset, ext, max, min) { - if (offset + ext > buf.length) throw new RangeError('Index out of range') - if (offset < 0) throw new RangeError('Index out of range') -} - -function writeFloat (buf, value, offset, littleEndian, noAssert) { - if (!noAssert) { - checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38) - } - ieee754.write(buf, value, offset, littleEndian, 23, 4) - return offset + 4 -} - -Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) { - return writeFloat(this, value, offset, true, noAssert) -} - -Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) { - return writeFloat(this, value, offset, false, noAssert) -} - -function writeDouble (buf, value, offset, littleEndian, noAssert) { - if (!noAssert) { - checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308) - } - ieee754.write(buf, value, offset, littleEndian, 52, 8) - return offset + 8 -} - -Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) { - return writeDouble(this, value, offset, true, noAssert) -} - -Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) { - return writeDouble(this, value, offset, false, noAssert) -} - -// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) -Buffer.prototype.copy = function copy (target, targetStart, start, end) { - if (!start) start = 0 - if (!end && end !== 0) end = this.length - if (targetStart >= target.length) targetStart = target.length - if (!targetStart) targetStart = 0 - if (end > 0 && end < start) end = start - - // Copy 0 bytes; we're done - if (end === start) return 0 - if (target.length === 0 || this.length === 0) return 0 - - // Fatal error conditions - if (targetStart < 0) { - throw new RangeError('targetStart out of bounds') - } - if (start < 0 || start >= this.length) throw new RangeError('sourceStart out of bounds') - if (end < 0) throw new RangeError('sourceEnd out of bounds') - - // Are we oob? - if (end > this.length) end = this.length - if (target.length - targetStart < end - start) { - end = target.length - targetStart + start - } - - var len = end - start - var i - - if (this === target && start < targetStart && targetStart < end) { - // descending copy from end - for (i = len - 1; i >= 0; --i) { - target[i + targetStart] = this[i + start] - } - } else if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) { - // ascending copy from start - for (i = 0; i < len; ++i) { - target[i + targetStart] = this[i + start] - } - } else { - Uint8Array.prototype.set.call( - target, - this.subarray(start, start + len), - targetStart - ) - } - - return len -} - -// Usage: -// buffer.fill(number[, offset[, end]]) -// buffer.fill(buffer[, offset[, end]]) -// buffer.fill(string[, offset[, end]][, encoding]) -Buffer.prototype.fill = function fill (val, start, end, encoding) { - // Handle string cases: - if (typeof val === 'string') { - if (typeof start === 'string') { - encoding = start - start = 0 - end = this.length - } else if (typeof end === 'string') { - encoding = end - end = this.length - } - if (val.length === 1) { - var code = val.charCodeAt(0) - if (code < 256) { - val = code - } - } - if (encoding !== undefined && typeof encoding !== 'string') { - throw new TypeError('encoding must be a string') - } - if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) { - throw new TypeError('Unknown encoding: ' + encoding) - } - } else if (typeof val === 'number') { - val = val & 255 - } - - // Invalid ranges are not set to a default, so can range check early. - if (start < 0 || this.length < start || this.length < end) { - throw new RangeError('Out of range index') - } - - if (end <= start) { - return this - } - - start = start >>> 0 - end = end === undefined ? this.length : end >>> 0 - - if (!val) val = 0 - - var i - if (typeof val === 'number') { - for (i = start; i < end; ++i) { - this[i] = val - } - } else { - var bytes = Buffer.isBuffer(val) - ? val - : utf8ToBytes(new Buffer(val, encoding).toString()) - var len = bytes.length - for (i = 0; i < end - start; ++i) { - this[i + start] = bytes[i % len] - } - } - - return this -} - -// HELPER FUNCTIONS -// ================ - -var INVALID_BASE64_RE = /[^+\/0-9A-Za-z-_]/g - -function base64clean (str) { - // Node strips out invalid characters like \n and \t from the string, base64-js does not - str = stringtrim(str).replace(INVALID_BASE64_RE, '') - // Node converts strings with length < 2 to '' - if (str.length < 2) return '' - // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not - while (str.length % 4 !== 0) { - str = str + '=' - } - return str -} - -function stringtrim (str) { - if (str.trim) return str.trim() - return str.replace(/^\s+|\s+$/g, '') -} - -function toHex (n) { - if (n < 16) return '0' + n.toString(16) - return n.toString(16) -} - -function utf8ToBytes (string, units) { - units = units || Infinity - var codePoint - var length = string.length - var leadSurrogate = null - var bytes = [] - - for (var i = 0; i < length; ++i) { - codePoint = string.charCodeAt(i) - - // is surrogate component - if (codePoint > 0xD7FF && codePoint < 0xE000) { - // last char was a lead - if (!leadSurrogate) { - // no lead yet - if (codePoint > 0xDBFF) { - // unexpected trail - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - continue - } else if (i + 1 === length) { - // unpaired lead - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - continue - } - - // valid lead - leadSurrogate = codePoint - - continue - } - - // 2 leads in a row - if (codePoint < 0xDC00) { - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - leadSurrogate = codePoint - continue - } - - // valid surrogate pair - codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000 - } else if (leadSurrogate) { - // valid bmp char, but last char was a lead - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - } - - leadSurrogate = null - - // encode utf8 - if (codePoint < 0x80) { - if ((units -= 1) < 0) break - bytes.push(codePoint) - } else if (codePoint < 0x800) { - if ((units -= 2) < 0) break - bytes.push( - codePoint >> 0x6 | 0xC0, - codePoint & 0x3F | 0x80 - ) - } else if (codePoint < 0x10000) { - if ((units -= 3) < 0) break - bytes.push( - codePoint >> 0xC | 0xE0, - codePoint >> 0x6 & 0x3F | 0x80, - codePoint & 0x3F | 0x80 - ) - } else if (codePoint < 0x110000) { - if ((units -= 4) < 0) break - bytes.push( - codePoint >> 0x12 | 0xF0, - codePoint >> 0xC & 0x3F | 0x80, - codePoint >> 0x6 & 0x3F | 0x80, - codePoint & 0x3F | 0x80 - ) - } else { - throw new Error('Invalid code point') - } - } - - return bytes -} - -function asciiToBytes (str) { - var byteArray = [] - for (var i = 0; i < str.length; ++i) { - // Node's code seems to be doing this and not & 0x7F.. - byteArray.push(str.charCodeAt(i) & 0xFF) - } - return byteArray -} - -function utf16leToBytes (str, units) { - var c, hi, lo - var byteArray = [] - for (var i = 0; i < str.length; ++i) { - if ((units -= 2) < 0) break - - c = str.charCodeAt(i) - hi = c >> 8 - lo = c % 256 - byteArray.push(lo) - byteArray.push(hi) - } - - return byteArray -} - -function base64ToBytes (str) { - return base64.toByteArray(base64clean(str)) -} - -function blitBuffer (src, dst, offset, length) { - for (var i = 0; i < length; ++i) { - if ((i + offset >= dst.length) || (i >= src.length)) break - dst[i + offset] = src[i] - } - return i -} - -function isnan (val) { - return val !== val // eslint-disable-line no-self-compare -} - -}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"base64-js":1,"ieee754":2,"isarray":3}]},{},[])("buffer") -});(function(factory){if(typeof exports==="object"){module.exports=factory()}else if(typeof define==="function"&&define.amd){define(factory)}else{var glob;try{glob=window}catch(e){glob=self}glob.SparkMD5=factory()}})(function(undefined){"use strict";var add32=function(a,b){return a+b&4294967295},hex_chr=["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"];function cmn(q,a,b,x,s,t){a=add32(add32(a,q),add32(x,t));return add32(a<>>32-s,b)}function md5cycle(x,k){var a=x[0],b=x[1],c=x[2],d=x[3];a+=(b&c|~b&d)+k[0]-680876936|0;a=(a<<7|a>>>25)+b|0;d+=(a&b|~a&c)+k[1]-389564586|0;d=(d<<12|d>>>20)+a|0;c+=(d&a|~d&b)+k[2]+606105819|0;c=(c<<17|c>>>15)+d|0;b+=(c&d|~c&a)+k[3]-1044525330|0;b=(b<<22|b>>>10)+c|0;a+=(b&c|~b&d)+k[4]-176418897|0;a=(a<<7|a>>>25)+b|0;d+=(a&b|~a&c)+k[5]+1200080426|0;d=(d<<12|d>>>20)+a|0;c+=(d&a|~d&b)+k[6]-1473231341|0;c=(c<<17|c>>>15)+d|0;b+=(c&d|~c&a)+k[7]-45705983|0;b=(b<<22|b>>>10)+c|0;a+=(b&c|~b&d)+k[8]+1770035416|0;a=(a<<7|a>>>25)+b|0;d+=(a&b|~a&c)+k[9]-1958414417|0;d=(d<<12|d>>>20)+a|0;c+=(d&a|~d&b)+k[10]-42063|0;c=(c<<17|c>>>15)+d|0;b+=(c&d|~c&a)+k[11]-1990404162|0;b=(b<<22|b>>>10)+c|0;a+=(b&c|~b&d)+k[12]+1804603682|0;a=(a<<7|a>>>25)+b|0;d+=(a&b|~a&c)+k[13]-40341101|0;d=(d<<12|d>>>20)+a|0;c+=(d&a|~d&b)+k[14]-1502002290|0;c=(c<<17|c>>>15)+d|0;b+=(c&d|~c&a)+k[15]+1236535329|0;b=(b<<22|b>>>10)+c|0;a+=(b&d|c&~d)+k[1]-165796510|0;a=(a<<5|a>>>27)+b|0;d+=(a&c|b&~c)+k[6]-1069501632|0;d=(d<<9|d>>>23)+a|0;c+=(d&b|a&~b)+k[11]+643717713|0;c=(c<<14|c>>>18)+d|0;b+=(c&a|d&~a)+k[0]-373897302|0;b=(b<<20|b>>>12)+c|0;a+=(b&d|c&~d)+k[5]-701558691|0;a=(a<<5|a>>>27)+b|0;d+=(a&c|b&~c)+k[10]+38016083|0;d=(d<<9|d>>>23)+a|0;c+=(d&b|a&~b)+k[15]-660478335|0;c=(c<<14|c>>>18)+d|0;b+=(c&a|d&~a)+k[4]-405537848|0;b=(b<<20|b>>>12)+c|0;a+=(b&d|c&~d)+k[9]+568446438|0;a=(a<<5|a>>>27)+b|0;d+=(a&c|b&~c)+k[14]-1019803690|0;d=(d<<9|d>>>23)+a|0;c+=(d&b|a&~b)+k[3]-187363961|0;c=(c<<14|c>>>18)+d|0;b+=(c&a|d&~a)+k[8]+1163531501|0;b=(b<<20|b>>>12)+c|0;a+=(b&d|c&~d)+k[13]-1444681467|0;a=(a<<5|a>>>27)+b|0;d+=(a&c|b&~c)+k[2]-51403784|0;d=(d<<9|d>>>23)+a|0;c+=(d&b|a&~b)+k[7]+1735328473|0;c=(c<<14|c>>>18)+d|0;b+=(c&a|d&~a)+k[12]-1926607734|0;b=(b<<20|b>>>12)+c|0;a+=(b^c^d)+k[5]-378558|0;a=(a<<4|a>>>28)+b|0;d+=(a^b^c)+k[8]-2022574463|0;d=(d<<11|d>>>21)+a|0;c+=(d^a^b)+k[11]+1839030562|0;c=(c<<16|c>>>16)+d|0;b+=(c^d^a)+k[14]-35309556|0;b=(b<<23|b>>>9)+c|0;a+=(b^c^d)+k[1]-1530992060|0;a=(a<<4|a>>>28)+b|0;d+=(a^b^c)+k[4]+1272893353|0;d=(d<<11|d>>>21)+a|0;c+=(d^a^b)+k[7]-155497632|0;c=(c<<16|c>>>16)+d|0;b+=(c^d^a)+k[10]-1094730640|0;b=(b<<23|b>>>9)+c|0;a+=(b^c^d)+k[13]+681279174|0;a=(a<<4|a>>>28)+b|0;d+=(a^b^c)+k[0]-358537222|0;d=(d<<11|d>>>21)+a|0;c+=(d^a^b)+k[3]-722521979|0;c=(c<<16|c>>>16)+d|0;b+=(c^d^a)+k[6]+76029189|0;b=(b<<23|b>>>9)+c|0;a+=(b^c^d)+k[9]-640364487|0;a=(a<<4|a>>>28)+b|0;d+=(a^b^c)+k[12]-421815835|0;d=(d<<11|d>>>21)+a|0;c+=(d^a^b)+k[15]+530742520|0;c=(c<<16|c>>>16)+d|0;b+=(c^d^a)+k[2]-995338651|0;b=(b<<23|b>>>9)+c|0;a+=(c^(b|~d))+k[0]-198630844|0;a=(a<<6|a>>>26)+b|0;d+=(b^(a|~c))+k[7]+1126891415|0;d=(d<<10|d>>>22)+a|0;c+=(a^(d|~b))+k[14]-1416354905|0;c=(c<<15|c>>>17)+d|0;b+=(d^(c|~a))+k[5]-57434055|0;b=(b<<21|b>>>11)+c|0;a+=(c^(b|~d))+k[12]+1700485571|0;a=(a<<6|a>>>26)+b|0;d+=(b^(a|~c))+k[3]-1894986606|0;d=(d<<10|d>>>22)+a|0;c+=(a^(d|~b))+k[10]-1051523|0;c=(c<<15|c>>>17)+d|0;b+=(d^(c|~a))+k[1]-2054922799|0;b=(b<<21|b>>>11)+c|0;a+=(c^(b|~d))+k[8]+1873313359|0;a=(a<<6|a>>>26)+b|0;d+=(b^(a|~c))+k[15]-30611744|0;d=(d<<10|d>>>22)+a|0;c+=(a^(d|~b))+k[6]-1560198380|0;c=(c<<15|c>>>17)+d|0;b+=(d^(c|~a))+k[13]+1309151649|0;b=(b<<21|b>>>11)+c|0;a+=(c^(b|~d))+k[4]-145523070|0;a=(a<<6|a>>>26)+b|0;d+=(b^(a|~c))+k[11]-1120210379|0;d=(d<<10|d>>>22)+a|0;c+=(a^(d|~b))+k[2]+718787259|0;c=(c<<15|c>>>17)+d|0;b+=(d^(c|~a))+k[9]-343485551|0;b=(b<<21|b>>>11)+c|0;x[0]=a+x[0]|0;x[1]=b+x[1]|0;x[2]=c+x[2]|0;x[3]=d+x[3]|0}function md5blk(s){var md5blks=[],i;for(i=0;i<64;i+=4){md5blks[i>>2]=s.charCodeAt(i)+(s.charCodeAt(i+1)<<8)+(s.charCodeAt(i+2)<<16)+(s.charCodeAt(i+3)<<24)}return md5blks}function md5blk_array(a){var md5blks=[],i;for(i=0;i<64;i+=4){md5blks[i>>2]=a[i]+(a[i+1]<<8)+(a[i+2]<<16)+(a[i+3]<<24)}return md5blks}function md51(s){var n=s.length,state=[1732584193,-271733879,-1732584194,271733878],i,length,tail,tmp,lo,hi;for(i=64;i<=n;i+=64){md5cycle(state,md5blk(s.substring(i-64,i)))}s=s.substring(i-64);length=s.length;tail=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];for(i=0;i>2]|=s.charCodeAt(i)<<(i%4<<3)}tail[i>>2]|=128<<(i%4<<3);if(i>55){md5cycle(state,tail);for(i=0;i<16;i+=1){tail[i]=0}}tmp=n*8;tmp=tmp.toString(16).match(/(.*?)(.{0,8})$/);lo=parseInt(tmp[2],16);hi=parseInt(tmp[1],16)||0;tail[14]=lo;tail[15]=hi;md5cycle(state,tail);return state}function md51_array(a){var n=a.length,state=[1732584193,-271733879,-1732584194,271733878],i,length,tail,tmp,lo,hi;for(i=64;i<=n;i+=64){md5cycle(state,md5blk_array(a.subarray(i-64,i)))}a=i-64>2]|=a[i]<<(i%4<<3)}tail[i>>2]|=128<<(i%4<<3);if(i>55){md5cycle(state,tail);for(i=0;i<16;i+=1){tail[i]=0}}tmp=n*8;tmp=tmp.toString(16).match(/(.*?)(.{0,8})$/);lo=parseInt(tmp[2],16);hi=parseInt(tmp[1],16)||0;tail[14]=lo;tail[15]=hi;md5cycle(state,tail);return state}function rhex(n){var s="",j;for(j=0;j<4;j+=1){s+=hex_chr[n>>j*8+4&15]+hex_chr[n>>j*8&15]}return s}function hex(x){var i;for(i=0;i>16)+(y>>16)+(lsw>>16);return msw<<16|lsw&65535}}if(typeof ArrayBuffer!=="undefined"&&!ArrayBuffer.prototype.slice){(function(){function clamp(val,length){val=val|0||0;if(val<0){return Math.max(val+length,0)}return Math.min(val,length)}ArrayBuffer.prototype.slice=function(from,to){var length=this.byteLength,begin=clamp(from,length),end=length,num,target,targetArray,sourceArray;if(to!==undefined){end=clamp(to,length)}if(begin>end){return new ArrayBuffer(0)}num=end-begin;target=new ArrayBuffer(num);targetArray=new Uint8Array(target);sourceArray=new Uint8Array(this,begin,num);targetArray.set(sourceArray);return target}})()}function toUtf8(str){if(/[\u0080-\uFFFF]/.test(str)){str=unescape(encodeURIComponent(str))}return str}function utf8Str2ArrayBuffer(str,returnUInt8Array){var length=str.length,buff=new ArrayBuffer(length),arr=new Uint8Array(buff),i;for(i=0;i>2]|=buff.charCodeAt(i)<<(i%4<<3)}this._finish(tail,length);ret=hex(this._hash);if(raw){ret=hexToBinaryString(ret)}this.reset();return ret};SparkMD5.prototype.reset=function(){this._buff="";this._length=0;this._hash=[1732584193,-271733879,-1732584194,271733878];return this};SparkMD5.prototype.getState=function(){return{buff:this._buff,length:this._length,hash:this._hash}};SparkMD5.prototype.setState=function(state){this._buff=state.buff;this._length=state.length;this._hash=state.hash;return this};SparkMD5.prototype.destroy=function(){delete this._hash;delete this._buff;delete this._length};SparkMD5.prototype._finish=function(tail,length){var i=length,tmp,lo,hi;tail[i>>2]|=128<<(i%4<<3);if(i>55){md5cycle(this._hash,tail);for(i=0;i<16;i+=1){tail[i]=0}}tmp=this._length*8;tmp=tmp.toString(16).match(/(.*?)(.{0,8})$/);lo=parseInt(tmp[2],16);hi=parseInt(tmp[1],16)||0;tail[14]=lo;tail[15]=hi;md5cycle(this._hash,tail)};SparkMD5.hash=function(str,raw){return SparkMD5.hashBinary(toUtf8(str),raw)};SparkMD5.hashBinary=function(content,raw){var hash=md51(content),ret=hex(hash);return raw?hexToBinaryString(ret):ret};SparkMD5.ArrayBuffer=function(){this.reset()};SparkMD5.ArrayBuffer.prototype.append=function(arr){var buff=concatenateArrayBuffers(this._buff.buffer,arr,true),length=buff.length,i;this._length+=arr.byteLength;for(i=64;i<=length;i+=64){md5cycle(this._hash,md5blk_array(buff.subarray(i-64,i)))}this._buff=i-64>2]|=buff[i]<<(i%4<<3)}this._finish(tail,length);ret=hex(this._hash);if(raw){ret=hexToBinaryString(ret)}this.reset();return ret};SparkMD5.ArrayBuffer.prototype.reset=function(){this._buff=new Uint8Array(0);this._length=0;this._hash=[1732584193,-271733879,-1732584194,271733878];return this};SparkMD5.ArrayBuffer.prototype.getState=function(){var state=SparkMD5.prototype.getState.call(this);state.buff=arrayBuffer2Utf8Str(state.buff);return state};SparkMD5.ArrayBuffer.prototype.setState=function(state){state.buff=utf8Str2ArrayBuffer(state.buff,true);return SparkMD5.prototype.setState.call(this,state)};SparkMD5.ArrayBuffer.prototype.destroy=SparkMD5.prototype.destroy;SparkMD5.ArrayBuffer.prototype._finish=SparkMD5.prototype._finish;SparkMD5.ArrayBuffer.hash=function(arr,raw){var hash=md51_array(new Uint8Array(arr)),ret=hex(hash);return raw?hexToBinaryString(ret):ret};return SparkMD5}); +;console.log("cryptomodules no longer needed"); \ No newline at end of file diff --git a/lib/debug.js b/lib/debug.js index c3562cd1..2ed48df4 100644 --- a/lib/debug.js +++ b/lib/debug.js @@ -1,13 +1,73 @@ ;(function(){ if('debug' !== process.env.GUN_ENV){ return } + var db = {length: 0, hash: {}}; + console.log("start :)"); global.DEBUG = 1; setInterval(function(){ + var print = '', tmp; var mem = process.memoryUsage(); + var used = mem.rss / 1024 / 1024; + used = used.toFixed(1); + print += used +' MB rss. '; + var used = mem.heapTotal / 1024 / 1024; + used = used.toFixed(1); + print += used +' MB hT. '; var used = mem.heapUsed / 1024 / 1024; used = used.toFixed(1); - console.log(used, 'MB'); - }, 1000); + print += used +' MB hU. '; + if(db.root){ + db.concurrency = Object.keys(db.peers||{}).length; + print += db.concurrency +' peers. '; + db.nodes = Object.keys(db.root.graph||{}).length; + print += db.nodes + ' nodes. '; + if(db.count){ print += db.count + ' msgs. '} + if(tmp = db.root.msgsLength){ + tmp = (tmp / 1024 / 1024).toFixed(2); + print += tmp + ' length MB. '; + } + if(db.last){ print += '\n' + JSON.stringify(db.last, null, 2) } + if(db.hash){ + print += '\nSome 100 Fast Hash Counts: \n' + JSON.stringify(db.hash, null, 2); + var l = Object.keys(db.hash), i = l.length; + if(i > 100){ + i = i - 100; + Gun.list.map(l, function(k){ + if(--i <= 0){ return } + delete db.hash[k]; + }); + } + } + + } + db.print = print; + print = print.split('\n')[0]; + console.log(print); + }, 2500); + + var Gun = require('../gun'); + Gun.on('opt', function(root){ + this.to.next(root); + if(root.once){ return } + console.log(">>>>>>>>>", root); + root.debug = db; + db.root = root; + db.peers = root.opt.peers; + + db.count = 0; + root.on('in', function(msg){ + this.to.next(msg); + if(!msg.NTS){ db.last = msg } + db.count++; + var tmp = msg['##']; + if(tmp && msg.put){ + if(!db.hash[tmp]){ db.hash[tmp] = [0, ''] } + db.hash[tmp][0] = (db.hash[tmp][0] || 0) + 1; + var preview = Object.keys(msg.put||{}); + db.hash[tmp][1] = preview.toString(', ').slice(0,500) + ' ...'; + } + }); + }) }()); \ No newline at end of file diff --git a/lib/erase.js b/lib/erase.js index c6509325..4b4cd1be 100644 --- a/lib/erase.js +++ b/lib/erase.js @@ -1,13 +1,13 @@ -var Gun = Gun || require('../gun'); +var Gun = (typeof window !== "undefined")? window.Gun : require('../gun'); -Gun.on('opt', function(ctx){ - this.to.next(ctx); - if(ctx.once){ return } - ctx.on('put', function(msg){ +Gun.on('opt', function(root){ + this.to.next(root); + if(root.once){ return } + root.on('put', function(msg){ Gun.graph.is(msg.put, null, function(val, key, node, soul){ if(null !== val){ return } // TODO: Refactor this to use `.off()`? - var tmp = ctx.graph[soul]; + var tmp = root.graph[soul]; if(tmp){ delete tmp[key]; } @@ -15,7 +15,7 @@ Gun.on('opt', function(ctx){ if(tmp){ delete tmp[key]; } - tmp = ctx.next; + tmp = root.next; if(tmp && (tmp = tmp[soul]) && (tmp = tmp.put)){ delete tmp[key]; tmp = tmp._ && tmp._['>']; diff --git a/lib/evict.js b/lib/evict.js new file mode 100644 index 00000000..6aa2582a --- /dev/null +++ b/lib/evict.js @@ -0,0 +1,45 @@ +;(function(){ + var Gun = (typeof window !== "undefined")? window.Gun : require('../gun'); + var ev = {}, empty = {}, u; + Gun.on('opt', function(root){ + this.to.next(root); + if(root.once){ return } + if(typeof process == 'undefined'){ return } + var util = process.memoryUsage; + if(!util){ return } + + ev.max = parseFloat(root.opt.memory || process.env.WEB_MEMORY || 1399) * 0.8; // max_old_space_size defaults to 1400 MB. Note: old space !== memory space though. + + setInterval(check, 1000); + function check(){ + var used = ev.used = util().rss / 1024 / 1024; + if(used < ev.max){ return } + setTimeout(GC, 1); + } + function GC(){ + var souls = Object.keys(root.graph||empty); + var toss = Math.ceil(souls.length * 0.01); + //var start = Gun.state(), i = toss; + Gun.list.map(souls, function(soul){ + if(--toss < 0){ return } + root.gun.get(soul).off(); + }); + //console.log("evicted", i, 'nodes in', ((Gun.state() - start)/1000).toFixed(2), 'sec.'); + } + /* + root.on('in', function(msg){ + this.to.next(msg); + if(msg.get){ + return; + } + Gun.graph.is(msg, function(node, soul){ + var meta = (root.next||empty)[soul]; + if(!meta){ return } + Gun.node.is(node, function(data, key){ + + }); + }); + }); + */ + }); +}()); \ No newline at end of file diff --git a/lib/file.js b/lib/file.js index 18881334..4298e5d0 100644 --- a/lib/file.js +++ b/lib/file.js @@ -6,14 +6,15 @@ var Gun = require('../gun'), fs = require('fs'); -Gun.on('opt', function(ctx){ - this.to.next(ctx); - var opt = ctx.opt; - if(ctx.once){ return } +Gun.on('create', function(root){ + this.to.next(root); + var opt = root.opt; + if(true !== opt.localStorage){ return } if(false === opt.localStorage){ return } - if(process.env.AWS_S3_BUCKET){ return } + //if(process.env.RAD_ENV){ return } + //if(process.env.AWS_S3_BUCKET){ return } opt.file = String(opt.file || 'data.json'); - var graph = ctx.graph, acks = {}, count = 0, to; + var graph = root.graph, acks = {}, count = 0, to; var disk = Gun.obj.ify((fs.existsSync || require('path').existsSync)(opt.file)? fs.readFileSync(opt.file).toString() : null) || {}; @@ -24,7 +25,7 @@ Gun.on('opt', function(ctx){ 'intended for local development testing only!' ); - ctx.on('put', function(at){ + root.on('put', function(at){ this.to.next(at); Gun.graph.is(at.put, null, map); if(!at['@']){ acks[at['#']] = true; } // only ack non-acks. @@ -36,7 +37,7 @@ Gun.on('opt', function(ctx){ to = setTimeout(flush, opt.wait || 1); }); - ctx.on('get', function(at){ + root.on('get', function(at){ this.to.next(at); var lex = at.get, soul, data, opt, u; //setTimeout(function(){ @@ -48,7 +49,7 @@ Gun.on('opt', function(ctx){ if(data && field){ data = Gun.state.to(data, field); } - ctx.on('in', {'@': at['#'], put: Gun.graph.node(data)}); + root.on('in', {'@': at['#'], put: Gun.graph.node(data)}); //},11); }); @@ -68,7 +69,7 @@ Gun.on('opt', function(ctx){ var tmp = count; count = 0; Gun.obj.map(ack, function(yes, id){ - ctx.on('in', { + root.on('in', { '@': id, err: err, ok: err? u : 1 @@ -84,7 +85,7 @@ Gun.on('opt', function(ctx){ if(!Gun.text.match(soul, rgx)){ return } if(has){ node = Gun.state.to(node, has) } (put = {})[soul] = node; - ctx.on('in', {put: put, '@': at['#']}); + root.on('in', {put: put, '@': at['#']}); }); } }); \ No newline at end of file diff --git a/lib/forget.js b/lib/forget.js index effd74db..7cedd03f 100644 --- a/lib/forget.js +++ b/lib/forget.js @@ -1,16 +1,15 @@ ;(function(){ + var Gun = (typeof window !== "undefined")? window.Gun : require('../gun'); - var Gun = (this||{}).Gun || require('../gun'); - - Gun.on('opt', function(ctx){ - once(ctx); - this.to.next(ctx); + Gun.on('opt', function(root){ + once(root); + this.to.next(root); }); - function once(ctx){ - if(ctx.once){ return } - var forget = ctx.opt.forget = ctx.opt.forget || {}; - ctx.on('put', function(msg){ + function once(root){ + if(root.once){ return } + var forget = root.opt.forget = root.opt.forget || {}; + root.on('put', function(msg){ Gun.graph.is(msg.put, function(node, soul){ if(!Gun.obj.has(forget, soul)){ return } delete msg.put[soul]; diff --git a/lib/fsrm.js b/lib/fsrm.js new file mode 100644 index 00000000..89133f7c --- /dev/null +++ b/lib/fsrm.js @@ -0,0 +1,18 @@ +var fs = require('fs'); +var nodePath = require('path'); + +var dir = __dirname + '/../'; + +module.exports = function rm(path, full) { + path = full || nodePath.join(dir, path); + if(!fs.existsSync(path)){ return } + fs.readdirSync(path).forEach(function(file,index){ + var curPath = path + "/" + file; + if(fs.lstatSync(curPath).isDirectory()) { // recurse + rm(null, curPath); + } else { // delete file + fs.unlinkSync(curPath); + } + }); + fs.rmdirSync(path); +}; \ No newline at end of file diff --git a/lib/fun.js b/lib/fun.js new file mode 100644 index 00000000..83214b9b --- /dev/null +++ b/lib/fun.js @@ -0,0 +1,10 @@ +window.fun = function fun(e){ setTimeout(() => { + e = e || {}; var $img = $('
    ') + .css({position: 'fixed', width: 100, + top: (e.y || e.clientY || (Math.random() * $(window).height()))-50, + left: (e.x || e.clientX || e.pageX || (Math.random() * $(window).width()))-50, + transform: 'rotate('+(Math.random() * 360)+'deg)' + }).appendTo('body'); + setTimeout(() => { $img.remove() },800); +},10)}; +$(document).on('keyup', fun).on('touchstart', fun).on('mousedown', fun); \ No newline at end of file diff --git a/lib/gunwrapper.js b/lib/gunwrapper.js deleted file mode 100644 index 1c256edf..00000000 --- a/lib/gunwrapper.js +++ /dev/null @@ -1,15 +0,0 @@ - -// This does all old-fashion require stuff before '@std/mjs' steps in... -const Gun = require('../gun') -require('../nts') -require('./s3') -try { - require('./ws') -} catch(e) { - require('./wsp/server') -} -require('./verify') -require('./file') -require('./bye') - -module.exports = Gun diff --git a/lib/hub.js b/lib/hub.js new file mode 100644 index 00000000..38db6ee1 --- /dev/null +++ b/lib/hub.js @@ -0,0 +1,5 @@ +var fs = require('fs'); + +fs.watch('.', {persistent: false, recursive: true}, function(eve, name){ + console.log("changed!", eve, name); +}) \ No newline at end of file diff --git a/lib/ipfs.js b/lib/ipfs.js new file mode 100644 index 00000000..5d183217 --- /dev/null +++ b/lib/ipfs.js @@ -0,0 +1,46 @@ +console.log("IPFS PLUGIN NOT OFFICIALLY MAINTAINED! PROBABLY WON'T WORK! USE AT YOUR OWN RISK! PLEASE CONTRIBUTE FIXES!"); +var opt = gun._.opt, u; +if (u === opt.ipfs.directory) { + opt.ipfs.directory = '/gun'; +} +opt.store = {}; +opt.store.put = function(file, data, cb){ + var uri = opt.ipfs.directory + '/' + file; + opt.ipfs.instance.files.write(uri, Buffer.from(JSON.stringify(data)), {create:true}) + .then(res => { + console.log('File written to IPFS directory', uri, res); + return opt.ipfs.instance.files.stat(opt.ipfs.directory, {hash:true}); + }).then(res => { + console.log('Directory hash:', res.hash); + return opt.ipfs.instance.name.publish(res.hash); + // currently throws "This command must be run in online mode. Try running 'ipfs daemon' first." for some reason, maybe js-ipfs IPNS not ready yet + }).then(res => { + console.log('IPFS put request successful:', res); + cb(undefined, 1); + }).catch(error => { + console.error('IPFS put request failed', error); + }); +} +opt.store.get = function(file, cb){ + var uri = opt.ipfs.directory + '/' + file; + opt.ipfs.instance.files.read(uri, {}) + .then(res => { + var data = JSON.parse(res.toString()); + console.log(uri + ' was loaded from ipfs:', data); + cb(data); + }); +} +opt.store.list = function(cb){ + var stream = opt.ipfs.files.lsReadableStream(opt.ipfs.directory); + + stream.on('data', (file) => { + console.log('ls', file.name); + if (cb(file.name)) { + stream.destroy(); + } + }); + + stream.on('finish', () => { + cb(); + }); +} \ No newline at end of file diff --git a/lib/later.js b/lib/later.js index 0fd65062..62a0d491 100644 --- a/lib/later.js +++ b/lib/later.js @@ -1,5 +1,5 @@ var Gun = Gun || require('../gun'); -Gun.chain.open || require('gun/lib/open'); +Gun.chain.open || require('./open'); Gun.chain.later = function(cb, age){ var gun = this; diff --git a/lib/les.js b/lib/les.js new file mode 100644 index 00000000..75a44ece --- /dev/null +++ b/lib/les.js @@ -0,0 +1,222 @@ +; +(function() { + + // _ _____ ____ _ + // | | | ____/ ___| (_)___ + // | | | _| \___ \ | / __| + // | |___| |___ ___) | | \__ \ + // |_____|_____|____(_)/ |___/ + // ---------------------------- + // LES.js (Last rEcently uSed) + // ---------------------------- + // A Small, lightweight, queue-based + // Garbage Collector for Gun + // Originally By: Collin Conrad (@masterex1000) + + /** + * + * Usage: require the file in your application + * + * Gun Params: these are passed to the new gun constructor + * + * - gc_enable : enables the gc, good if you are running multiple instances of gun, etc... def. true + * - gc_delay : sets the amount of time between attempted garbage collections in milliseconds + * - gc_info_enable : Enables or Disables the info printout + * - gc_info : sets the ~ amount of time between info messages + * this is checked everytime the gc is ran + * - gc_info_mini : this will use a smaller, less user friendly info printout + * - gc_importance_func : This will be the function used for finding the importance of a potental collect + * takes the form of func(timestamp, ctime, memoryUsageRatio) {return val} + * Collects when returned value is 100 + */ + + //NOTE: set to false to use require for getting gun DEFUALT: false + var USELOCALGUN = false; + + + //NOTE: adds some debug messages DEFUALT: false + var DEBUG = false; + + if(!(typeof window !== "undefined") && USELOCALGUN) + console.log("NOTE: You currently have LES.js set to use the 'local' file version of gun, This might crash if set wrong!"); + + var Gun = (typeof window !== "undefined") ? window.Gun : (USELOCALGUN ? require('../gun') : require("gun")); + + //Removes a node from the garbage collection until next write + Gun.chain.gcDequeue = function() { + //console.log(this._.root.dequeueNode); + if(this._.root.dequeueNode) { // check that we actually have the dequeue command on this node + let ctx = this; + + this.get(function (soul) { + ctx._.root.dequeueNode(soul); + }, true); + } + } + + //Puts node at the front for garbage collection, NOTE: only collects when it is hit it's time + Gun.chain.gcCollect = function() { + if(this._.root.collectNode) { // check that we actually have the dequeue command on this node + let ctx = this; + + this.get(function (soul) { + ctx._.root.collectNode(soul); + }, true); + } + } + + Gun.on('opt', function(root) { + //Setup various options + + const gc_enable = root.opt.gc_enable ? root.opt.gc_enable : true; + const gc_delay = root.opt.gc_delay ? root.opt.gc_delay : 1000; + + const gc_info_enable = root.opt.gc_info_enable ? root.opt.gc_info_enable : true; + const gc_info = root.opt.gc_info ? root.opt.gc_info : 5000; + const gc_info_mini = root.opt.gc_info_mini ? root.opt.gc_info_mini : false; + + //This is long, but it works well + const calcRemoveImportance = root.opt.gc_importance_func ? root.opt.gc_importance_func : function (timestamp, ctime, memoryUsageRatio) { + var time = (ctime - timestamp) * 0.001; + return time * 10 * (memoryUsageRatio * memoryUsageRatio); + } + + if(DEBUG) console.log(root.opt); + + this.to.next(root); + + if (root.once) + return; + if (typeof process == 'undefined') + return + var mem = process.memoryUsage; + + if(!gc_enable) // exit because the gc is disabled + return; + + if (!mem) //exit because we are in the browser + return; + + var ev = {}; //stores the environment + var empty = {}; //An empty list used to prevent crashes + + //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 + + root.dequeueNode = (soul) => { //forward the call to our gc + dequeueNode(soul); + } + + root.collectNode = (soul) => { //forward the call to our gc + collectNode(soul); + } + + 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 + //GC(0.99); + }, 1); + } + + setInterval(check, gc_delay); // set the garbage collector to run every second + + //Executed every time a node gets modified + root.on("put", function(e) { + var ctime = Date.now(); + var souls = Object.keys(e.put || empty); // get all of the nodes in the update + for (var i = 0; i < souls.length; i++) { // iterate over them and add them + 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.error("Something happened and the node '" + soul + "' won't get garbage collection unless the value is updated again"); + return; + } else { + nodesArray.splice(index, 1); // remove the existing ref. faster than dequeue + nodesArray.push([soul, ctime]); // push the new instance + } + } else { + nodesArray.push([soul, ctime]); + nodes[soul] = true; + } + } + + //Removes a node from the queue + function dequeueNode(soul) { + if (nodes[soul] == true) { //The node already exists in the queue + var index = nodesArray.findIndex(function(e) { + return e[0] === soul; + }); + if (index != -1) { + //nodesArray.splice(index, 1); // remove the existing ref. + nodesArray.shift(); + nodes[soul] = false; // store that we no longer have that node in the queue + } + } + } + + //Moves a node to the start of the queue + function collectNode(soul) { + if (nodes[soul] == true) { //The node already exists in the queue + var index = nodesArray.findIndex(function(e) { + return e[0] === soul; + }); + if (index != -1) { + //nodesArray.splice(index, 1); // remove the existing ref. + nodesArray.shift(); // WAY faster than splice + } + nodesArray.unshift([soul, nodesArray[0][1]]); // create a new node with the next nodes time stamp + nodes[soul] = true; // store that we no longer have that node in the queue + } + } + + //The main garbage collecting routine + function GC(memRatio) { + var curTime = Date.now(); // get the current time + + if (gc_info_enable && curTime - memoryUpdate >= gc_info) { // check if we need to print info + if(!gc_info_mini) + 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); + else + console.log("|GC| %s, Mem Ratio %d, Ram %sMB, Nodes in mem %s, Tracked Nodes %s", new Date().toLocaleString(), round(memRatio, 2), round(ev.used, 2), Object.keys(root.graph || empty).length, nodesArray.length); + memoryUpdate = curTime; // reset the last update time + } + + var freed = 0; // Just a nice performance counter + + while (nodesArray.length > 0) { // iterate over all of our nodes + 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); + nodesArray.shift(); + freed++; // add one to our perf counter + } else + break; // Break out of the loop because we don't have any more nodes to free + } + if (freed > 0) + console.log("|GC| Removed %s nodes in %s seconds-----------------------------------------------------------------", freed, (Date.now() - curTime) * 0.001); + } + + function round(value, decimals) { //a basic rounding function + return Number(Math.round(value + 'e' + decimals) + 'e-' + decimals); + } + }); +}()); \ No newline at end of file diff --git a/lib/load.js b/lib/load.js index f34f9ca5..d888fe34 100644 --- a/lib/load.js +++ b/lib/load.js @@ -1,9 +1,5 @@ -if(typeof window !== "undefined"){ - var Gun = window.Gun; -} else { - var Gun = require('gun/gun'); -} -Gun.chain.open || require('gun/lib/open'); +var Gun = (typeof window !== "undefined")? window.Gun : require('../gun'); +Gun.chain.open || require('./open'); Gun.chain.load = function(cb, opt, at){ (opt = opt || {}).off = !0; diff --git a/lib/meta.js b/lib/meta.js new file mode 100644 index 00000000..b75fc924 --- /dev/null +++ b/lib/meta.js @@ -0,0 +1,382 @@ +$(function(){ + var noop = function(){}, u; + var m = window.meta = {edit:[]}; + var k = m.key = {}; + k.meta = {17:17, 91:17, 93:17, 224:17}; + k.down = function(eve){ + if(eve.repeat){ return } + var key = (k.eve = m.eve = eve).which = eve.which || eve.fake || eve.keyCode; + if(!eve.fake && key === k.last){ return } k.last = key; + if(!eve.fake && $(eve.target).closest('input, textarea, [contenteditable=true]').length){ + if(k.meta[key]){ k.down.meta = key = -1 } + if(!k.down.meta){ return } + } + (k.combo || (k.combo = [])).push(key); + m.check('on', key, k.at || (k.at = m.edit)); + if(k.meta[key]){ + m.list(k.at.back || m.edit); + if(k.at && !k.at.back){ m.flip() } + } + } + k.up = function(eve){ var tmp; + var key = (k.eve = m.eve = eve).which = eve.which || eve.fake || eve.keyCode; + if(!eve.fake && $(eve.target).closest('input, textarea, [contenteditable=true]').length){ + if(k.meta[key]){ + k.down.meta = null; + key = -1; + } else + if(!k.down.meta){ return } + } + k.last = null; + if($(':focus').closest('#meta').length){ return } + m.check('up', key); + if(-1 === key || 27 === eve.which){ k.wipe() } + } + m.flip = function(tmp){ + var board = $('#meta .meta-menu'); + ((tmp === false) || (!tmp && board.is(':visible')))? + board.addClass('meta-none') + : board.removeClass('meta-none'); + } + m.flip.is = function(){ + return $('#meta .meta-menu').is(':visible'); + } + m.flip.wait = 500; + m.check = function(how, key, at){ + at = k.at || m.edit; + var edit = at[key]; + if(!edit){ return } + var tmp = k.eve || noop; + if(tmp.preventDefault){ tmp.preventDefault() } + if(edit[how]){ + if(tmp.fake && !edit.fake){ + m.tap.edit = edit; + } else { + edit[how](m.eve); + /*if(k.at !== m.edit && 'up' === how){ + if(k.down.meta){ m.list(k.at = m.edit) } + else { k.wipe() } + }*/ + } + } + if('up' != how){ return } + if(at != edit){ edit.back = at } + m.list(edit, true); + } + m.list = function(at, opt){ + if(!at){ return m.flip(false) } + var l = []; + $.each(at, function(i,k){ 'back' != i && k.combo && k.name && l.push(k) }); + if(!l.length){ return } + k.at = at; + l = l.sort(function(a,b){ + a = a.combo.slice(-1)[0] || 0; + if(a.length){ a = a.toUpperCase().charCodeAt(0) } + b = b.combo.slice(-1)[0] || 0; + if(b.length){ b = b.toUpperCase().charCodeAt(0) } + return (a < b)? -1 : 1; + }); + var $ul = $('#meta .meta-menu ul') + $ul.children('li').addClass('meta-none').hide(); setTimeout(function(){ $ul.children('.meta-none').remove() },250); // necessary fix for weird bug glitch + $.each(l, function(i, k){ + $ul.append($('
  • ').text(k.name)); + }); + if(opt){ m.flip(true) } + $ul.append($('
  • ').html('←').one('click', function(){ + m.list(k.at = at.back); + })); + } + m.ask = function(help, cb){ + var $ul = $('#meta .meta-menu ul').empty(); + var $put = $('').attr('id', 'meta-ask').attr('placeholder', help); + var $form = $('
    ').append($put).on('submit', function(eve){ + eve.preventDefault(); + cb($put.val()); + $li.remove(); + k.wipe(); + }); + var $li = $('
  • ').append($form); + $ul.append($li); + m.flip(true); + $put.focus(); + } + k.wipe = function(opt){ + k.down.meta = false; + k.combo = []; + if(!opt){ m.flip(false) } + m.list(k.at = m.edit); + }; + m.tap = function(){ + var on = $('.meta-on') + .or($($(document.querySelectorAll(':hover')).get().reverse()).first()) + .or($(document.elementFromPoint(meta.tap.x, meta.tap.y))); + return on; + } + $(window).on('blur', k.wipe).on('focus', k.wipe); + $(document).on('mousedown mousemove mouseup', function(eve){ + m.tap.eve = eve; + m.tap.x = eve.pageX||0; + m.tap.y = eve.pageY||0; + m.tap.on = $(eve.target); + }).on('mousedown touchstart', function(eve){ + var tmp = m.tap.edit; + if(!tmp || !tmp.on){ return } + tmp.on(eve); + m.tap.edit = null; + }); + $(document).on('touchstart', '#meta .meta-start', function(eve){ m.tap.stun = true }); + $(document).on('click', '#meta .meta-menu li', function(eve){ + if(m.tap.stun){ return m.tap.stun = false } + if(!(eve.fake = eve.which = (($(this).text().match(/[A-Z]/)||{})[0]||'').toUpperCase().charCodeAt(0))){ return } + eve.tap = true; + k.down(eve); + k.up(eve); + }); + $(document).on('keydown', k.down).on('keyup', k.up); + meta.edit = function(edit){ + var tmp = edit.combow = []; + $.each(edit.combo || (edit.combo = []), function(i,k){ + if(!k || !k.length){ if('number' == typeof k){ tmp.push(k) } return } + tmp.push(k.toUpperCase().charCodeAt(0)); + }); + var at = meta.edit, l = edit.combo.length; + $.each(tmp, function(i,k){ at = at[k] = (++i >= l)? edit : at[k] || {} }); + edit.combow = edit.combow.join(','); + m.list(meta.edit); + } + $.fn.or = function(s){ return this.length ? this : $(s||'body') }; + ;(function(){try{ + /* UI */ + if(meta.css){ return } + var $m = $('
    ').attr('id', 'meta'); + $m.append($('').text('+').addClass('meta-start')); + $m.append($('
    ').addClass('meta-menu meta-none').append('
      ')); + $(document.body).append($m); + css({ + '#meta': { + display: 'block', + position: 'fixed', + bottom: '2em', + right: '2em', + background: 'white', + 'font-size': '18pt', + 'font-family': 'Tahoma, arial', + 'box-shadow': '0px 0px 1px #000044', + 'border-radius': '1em', + 'text-align': 'center', + 'z-index': 999999, + margin: 0, + padding: 0, + width: '2em', + height: '2em', + opacity: 0.7, + outline: 'none', + color: '#000044', + overflow: 'visible', + transition: 'all 0.2s ease-in' + }, + '#meta *': {outline: 'none'}, + '#meta .meta-none': {display: 'none'}, + '#meta span': {'line-height': '2em'}, + '#meta .meta-menu': { + background: 'rgba(0,0,0,0.1)', + width: '12em', + right: '-2em', + bottom: '-2em', + overflow: 'visible', + position: 'absolute', + 'overflow-y': 'scroll', + 'text-align': 'right', + 'min-height': '20em', + height: '100vh' + }, + '#meta .meta-menu ul': { + padding: 0, + margin: '1em 1em 2em 0', + 'list-style-type': 'none' + }, + '#meta .meta-menu ul li': { + display: 'block', + background: 'white', + padding: '0.5em 1em', + 'border-radius': '1em', + 'margin-left': '0.25em', + 'margin-top': '0.25em', + 'float': 'right' + }, + '#meta a': {color: 'black'}, + '#meta:hover': {opacity: 1}, + '#meta .meta-menu ul:before': { + content: "' '", + display: 'block', + 'min-height': '15em', + height: '50vh' + }, + '#meta li': { + background: 'white', + padding: '0.5em 1em', + 'border-radius': '1em', + 'margin-left': '0.25em', + 'margin-top': '0.25em', + 'float': 'right' + }, + '#meta:hover .meta-menu': {display: 'block'} + }); + function css(css){ + var tmp = ''; + $.each(css, function(c,r){ + tmp += c + ' {\n'; + $.each(r, function(k,v){ + tmp += '\t'+ k +': '+ v +';\n'; + }); + tmp += '}\n'; + }); + (node = document.createElement('style')).innerHTML = tmp; + document.body.appendChild(node); + } + }catch(e){}}()); + ;(function(){ + // include basic text editing by default. + var monotype = window.monotype || function(){console.log("monotype needed")}; + var m = meta; + m.text = {zws: '​'}; + m.text.on = function(eve){ var tmp; + if($((eve||{}).target).closest('#meta').length){ return } + m.text.range = null; + if(!(m.text.copy()||'').trim()){ + m.flip(false); + m.list(m.text.it); + return; + } + m.text.range = monotype((eve||{}).target); + m.text.it.on(eve); + } + m.text.copy = function(tmp){ + return ((tmp = window.getSelection) && tmp().toString()) || + ((tmp = document.selection) && tmp.createRange().text) || ''; + } + $(document).on('select contextmenu keyup mouseup', '[contenteditable=true]', m.text.on); + m.text.editor = function(opt, as){ var tmp; + if(!opt){ return } + opt = (typeof opt == 'string')? {edit: opt} : opt.tag? opt : {tag: opt}; + var r = opt.range = opt.range || m.text.range || monotype(), cmd = opt.edit; + as = opt.as = opt.as || as; + if(cmd && document.execCommand){ + r.restore(); + if(document.execCommand(cmd, null, as||null)){ + if(m.text.range){ m.text.range = monotype() } + return; + } + } + if(!opt.tag){ return } + opt.tag = $(opt.tag); + opt.name = opt.name || opt.tag.prop('tagName'); + if((tmp = $(r.get()).closest(opt.name)).length){ + if(r.s === r.e){ + tmp.after(m.text.zws); + r = r.select(monotype.next(tmp[0]),1); + } else { + tmp.contents().unwrap(opt.name); + } + } else + if(r.s === r.e){ + r.insert(opt.tag); + r = r.select(opt.tag); + } else { + r.wrap(opt.tag); + } + r.restore(); + opt.range = null; + if(m.text.range){ m.text.range = monotype() } + } + meta.edit(meta.text.it = {combo: [-1], on: function(){ m.list(this, true) }, back: meta.edit}); // -1 is key for typing. + meta.text.it[-1] = meta.text.it; + meta.edit({ + name: "Bold", + combo: [-1,'B'], fake: -1, + on: function(eve){ + meta.text.editor('bold'); + }, + up: function(){} + }); + meta.edit({ + name: "Italic", + combo: [-1,'I'], fake: -1, + on: function(eve){ + meta.text.editor('italic'); + }, + up: function(){} + }); + /*meta.edit({ + name: "Underline", + combo: [-1,'U'], fake: -1, + on: function(eve){ + meta.text.editor('underline'); + }, + up: function(){} + });*/ + meta.edit({ + name: "linK", + combo: [-1,'K'], fake: -1, + on: function(eve){ + var range = meta.text.range || monotype(); + meta.ask('Paste or type link...', function(url){ + meta.text.editor({tag: $('link'), edit: url? 'createLink' : 'unlink', as: url, range: range}); + }) + } + }); + //meta.edit({name: "aliGn", combo: [-1,'G']}); // MOVE TO ADVANCED MENu! + meta.edit({ + name: "Left", + combo: [-1,'G','L'], fake: -1, + on: function(eve){ meta.text.editor('justifyLeft') }, + up: function(){} + }); + meta.edit({ + name: "Right", + combo: [-1,'G','R'], fake: -1, + on: function(eve){ meta.text.editor('justifyRight') }, + up: function(){ } + }); + meta.edit({ + name: "Middle", + combo: [-1,'G','M'], fake: -1, + on: function(eve){ meta.text.editor('justifyCenter') }, + up: function(){ } + }); + meta.edit({ + name: "Justify", + combo: [-1,'G','J'], fake: -1, + on: function(eve){ meta.text.editor('justifyFull') }, + up: function(){} + }); + // Align Number + // Align Points + // Align Strike + meta.edit({name: "Size", combo: [-1,'S'], on: function(){ m.list(this, true) }}); + meta.edit({ + name: "Small", + combo: [-1,'S','S'], fake: -1, + on: function(eve){ meta.text.editor('fontSize', 2) }, + up: function(){ } + }); + meta.edit({ + name: "Normal", + combo: [-1,'S','N'], fake: -1, + on: function(eve){ meta.text.editor('fontSize', 5) }, + up: function(){} + }); + meta.edit({ + name: "Header", + combo: [-1,'S','H'], fake: -1, + on: function(eve){ meta.text.editor('fontSize', 6) }, + up: function(){} + }); + meta.edit({ + name: "Title", + combo: [-1,'S','T'], fake: -1, + on: function(eve){ meta.text.editor('fontSize', 7) }, + up: function(){} + }); + }()); +}); \ No newline at end of file diff --git a/lib/monotype.js b/lib/monotype.js new file mode 100644 index 00000000..3b02bf40 --- /dev/null +++ b/lib/monotype.js @@ -0,0 +1,310 @@ +;var monotype = monotype || (function(monotype){ + monotype.range = function(n){ + var R, s, t, n = n || 0, win = monotype.win || window, doc = win.document; + if(!arguments.length) return doc.createRange(); + if(!(win.Range && R instanceof Range)){ + s = win.getSelection? win.getSelection() : {}; + if(s.rangeCount){ + R = s.getRangeAt(n); + } else { + if(doc.createRange){ + R = doc.createRange(); + R.setStart(doc.body, 0); + } else + if (doc.selection){ // '+n+'
    ').contents():$(document.createTextNode(n)); + } + m.get = function(d){ + if(u === d){ return $([m.R.startContainer, m.R.endContainer]) } + return monotype.deep((d = (d && d > 0))? m.R.endContainer : m.R.startContainer + , d? m.R.endOffset : m.R.startOffset).container; + } + m.remove = function(n,R){ + R = m.R || m.range(); + R.deleteContents(); + monotype.restore(R); + m = monotype(m,opt); + return m; + } + m.insert = function(n,R){ + n = jqtxt(n); + R = m.R || m.range(); + R.deleteContents(); + $(n.get().reverse()).each(function(){ + R.insertNode(this); + }); + R.selectNodeContents(n.last()[0]); + monotype.restore(R); + m = monotype(m,opt); + return m; + } + m.wrap = function(n,R){ + var jq; + n = jqtxt(n); + n = n[0]; + R = m.R || m.range(); + if(monotype.text(R.startContainer) || monotype.text(R.endContainer)){ + var b = R.cloneContents(); + R.deleteContents(); + jq = $(n); + jq.html(b); + jq = jq[0]; + R.insertNode(jq); + }else{ + R.surroundContents(n); + } + R.selectNodeContents(jq||n); + monotype.restore(R); + m = monotype(m,opt); + return m; + } + m.select = function(n,i,e,j){ + var R = m.R || m.range(), t = e; + n = $(n); + if($.isNumeric(e)){ + e = j || n; + j = t; + } else { + e = e || n; + } + j = $.isNumeric(j)? j : $.isNumeric(i)? i : Infinity; + i = i || 0; + if(i < 0){ + t = n.contents().length || n.text().length; + i = t + i; + } if(j < 0){ + t = n.contents().length || n.text().length; + j = t + j; + } if(j === Infinity){ + R.selectNodeContents(n[0]); + } else { + R.setStart(n[0],i); + R.setEnd(e[0],j); + } + monotype.restore(R); + m = monotype(m,opt); + return m; + } + return m; +} \ No newline at end of file diff --git a/lib/multicast.js b/lib/multicast.js new file mode 100644 index 00000000..a49ca9dc --- /dev/null +++ b/lib/multicast.js @@ -0,0 +1,63 @@ +var Gun = (typeof window !== "undefined")? window.Gun : require('../gun'); + +Gun.on('create', function(root){ + this.to.next(root); + var opt = root.opt; + if(false === opt.multicast){ return } + + var udp = opt.multicast = opt.multicast || {}; + udp.address = udp.address || '233.255.255.255'; + udp.pack = udp.pack || 50000; // UDP messages limited to 65KB. + udp.port = udp.port || 23456; + + var noop = function(){}, port; + + var dgram = require("dgram"); + var socket = dgram.createSocket({type: "udp4", reuseAddr: true}); + socket.bind(udp.port); + + socket.on("listening", function() { + socket.addMembership(udp.address); + udp.peer = {url: udp.address + ':' + udp.port, wire: socket}; + + udp.peer.say = function(raw){ + var buf = Buffer.from(raw, 'utf8'); + if(udp.pack <= buf.length){ // message too big!!! + return; + } + socket.send(buf, 0, buf.length, udp.port, udp.address, noop); + } + opt.mesh.hi(udp.peer); + + console.log('multicasting on', udp.peer.url); + return; // below code only needed for when WebSocket connections desired! + setInterval(function broadcast(){ + port = port || (opt.web && opt.web.address()||{}).port; + if(!port){ return } + udp.peer.say(JSON.stringify({id: opt.pid || (opt.pid = Math.random().toString(36).slice(2)), port: port})); + }, 1000); + }); + + socket.on("message", function(raw, info) { try { + if(!raw){ return } + raw = raw.toString('utf8'); + opt.mesh.hear(raw, udp.peer); + + return; // below code only needed for when WebSocket connections desired! + var message; + message = JSON.parse(raw.toString('utf8')); + + if(opt.pid === message.id){ return } // ignore self + + var url = 'http://' + info.address + ':' + (port || (opt.web && opt.web.address()||{}).port) + '/gun'; + if(root.opt.peers[url]){ return } + + console.log('discovered', url, message, info); + root.$.opt(url); + + } catch(e){ + console.log('multicast error', e, raw); + return; + } }); + +}); diff --git a/lib/normalize.js b/lib/normalize.js new file mode 100644 index 00000000..bac4214d --- /dev/null +++ b/lib/normalize.js @@ -0,0 +1,290 @@ +(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', 'img'] // empty + ,tags: { + 'a': {attrs:{'href':1}, exclude:{'a':1}}, + 'b': {exclude:{'b':1,'p':1}}, + 'br': {empty: 1}, + 'i': {exclude:{'i':1,'p':1}}, + 'img': {attrs:{'src':1}, empty: 1}, + 'span': {exclude:{'p':1,'ul':1,'ol':1,'li':1,'br':1}}, + 's': {space:1}, + 'u': {exclude:{'u':1,'p':1},space:1}, + } + ,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]); + } + + var xssattr = /[^a-z:]/ig, xssjs = /javascript:/ig; + // url("javascript: // and all permutations + // stylesheets can apparently have XSS? + + // create key val attributes object from elements attributes + function attrsAsObj(e, filterCb){ + var attrObj = {}; + (e = $(e)) && e.length && $(e[0].attributes||[]).each(function(value,name){ + name = name.nodeName||name.name; + value = e.attr(name); + if(value.replace(xssattr,'').match(xssjs)){ e.removeAttr(name); return } + value = filterCb? filterCb(value,name,e) : value; + if(value !== undefined && value !== false) + attrObj[name] = value; + }); + 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 diff --git a/lib/not.js b/lib/not.js index 133c4fee..7a5255ab 100644 --- a/lib/not.js +++ b/lib/not.js @@ -1,7 +1,7 @@ if(typeof window !== "undefined"){ var Gun = window.Gun; } else { - var Gun = require('gun/gun'); + var Gun = require('../gun'); } var u; diff --git a/lib/open.js b/lib/open.js index 1df4f64f..499139fa 100644 --- a/lib/open.js +++ b/lib/open.js @@ -1,8 +1,4 @@ -if(typeof window !== "undefined"){ - var Gun = window.Gun; -} else { - var Gun = require('gun/gun'); -} +var Gun = (typeof window !== "undefined")? window.Gun : require('../gun'); Gun.chain.open = function(cb, opt, at){ opt = opt || {}; @@ -20,7 +16,7 @@ Gun.chain.open = function(cb, opt, at){ clearTimeout(opt.to); opt.to = setTimeout(function(){ if(!opt.any){ return } - opt.any.call(opt.at.gun, opt.doc, opt.key, opt, opt.ev); + opt.any.call(opt.at.$, opt.doc, opt.key, opt, opt.ev); if(opt.off){ opt.ev.off(); opt.any = null; @@ -39,7 +35,7 @@ Gun.chain.open = function(cb, opt, at){ } var tmp = this, id; Gun.obj.map(data, function(val, key){ - if(!(id = Gun.val.rel.is(val))){ + if(!(id = Gun.val.link.is(val))){ (at || opt.doc)[key] = val; return; } diff --git a/lib/path.js b/lib/path.js index d034cdbf..71fd3337 100644 --- a/lib/path.js +++ b/lib/path.js @@ -1,18 +1,11 @@ -if(typeof window !== "undefined"){ - var Gun = window.Gun; -} else { - var Gun = require('gun/gun'); -} +var Gun = (typeof window !== "undefined")? window.Gun : require('../gun'); -Gun.chain.path = function(field, cb, opt){ +Gun.chain.path = function(field, opt){ var back = this, gun = back, tmp; - opt = opt || {}; opt.path = true; - if(gun === gun.back(-1)){if(cb){cb({err: Gun.log("Can't do that on root instance.")})}return gun} if(typeof field === 'string'){ - tmp = field.split(opt.split || '.'); + tmp = field.split(opt || '.'); if(1 === tmp.length){ - gun = back.get(field, cb, opt); - gun._.opt = opt; + gun = back.get(field); return gun; } field = tmp; @@ -22,19 +15,17 @@ Gun.chain.path = function(field, cb, opt){ gun = back; var i = 0, l = field.length; for(i; i < l; i++){ - gun = gun.get(field[i], (i+1 === l)? cb : null, opt); + //gun = gun.get(field[i], (i+1 === l)? cb : null, opt); + gun = gun.get(field[i]); } - //gun.back = back; // TODO: API change! } else { - gun = back.get(field[0], cb, opt); + gun = back.get(field[0]); } - gun._.opt = opt; return gun; } if(!field && 0 != field){ return back; } - gun = back.get(''+field, cb, opt); - gun._.opt = opt; + gun = back.get(''+field); return gun; } \ No newline at end of file diff --git a/lib/radisk.js b/lib/radisk.js index cb2d571d..01847461 100644 --- a/lib/radisk.js +++ b/lib/radisk.js @@ -1,337 +1,494 @@ -var fs = require('fs'); -var Gun = require('../gun'); -var Radix = require('./radix'); +;(function(){ -function Radisk(opt){ + function Radisk(opt){ - opt = opt || {}; - opt.file = String(opt.file || 'radata'); - opt.thrash = opt.thrash || opt.wait || 1; - opt.batch = opt.batch || 10 * 1000; - opt.size = opt.size || (1024 * 1024 * 10); // 10MB - opt.code = opt.code || {}; - opt.code.from = opt.code.from || '!'; + opt = opt || {}; + opt.log = opt.log || console.log; + opt.file = String(opt.file || 'radata'); + var has = (Radisk.has || (Radisk.has = {}))[opt.file]; + if(has){ return has } - if(!opt.store){ - return Gun.log("ERROR: Radisk needs `opt.store` interface with `{get: fn, put: fn, list: fn}`!"); - } - if(!opt.store.put){ - return Gun.log("ERROR: Radisk needs `store.put` interface with `(file, data, cb)`!"); - } - if(!opt.store.get){ - return Gun.log("ERROR: Radisk needs `store.get` interface with `(file, cb)`!"); - } - if(!opt.store.list){ - return Gun.log("ERROR: Radisk needs a streaming `store.list` interface with `(cb)`!"); - } + opt.pack = opt.pack || (opt.memory? (opt.memory * 1000 * 1000) : 1399000000) * 0.3; // max_old_space_size defaults to 1400 MB. + opt.until = opt.until || opt.wait || 250; + opt.batch = opt.batch || (10 * 1000); + opt.chunk = opt.chunk || (1024 * 1024 * 10); // 10MB + opt.code = opt.code || {}; + opt.code.from = opt.code.from || '!'; + //opt.jsonify = true; // TODO: REMOVE!!!! - /* - Any and all storage adapters should... - 1. Because writing to disk takes time, we should batch data to disk. This improves performance, and reduces potential disk corruption. - 2. If a batch exceeds a certain number of writes, we should immediately write to disk when physically possible. This caps total performance, but reduces potential loss. - */ - var r = function(key, val, cb){ - key = ''+key; - if(val instanceof Function){ - cb = val; - val = r.batch(key); - if(u !== val){ - return cb(u, val); - } - if(r.thrash.at){ - val = r.thrash.at(key); - if(u !== val){ - return cb(u, val); - } - } - //console.log("READ FROM DISK"); - return r.read(key, cb); + function ename(t){ return encodeURIComponent(t).replace(/\*/g, '%2A') } + function atomic(v){ return u !== v && (!v || 'object' != typeof v) } + var map = Gun.obj.map; + var LOG = false; + + if(!opt.store){ + return opt.log("ERROR: Radisk needs `opt.store` interface with `{get: fn, put: fn (, list: fn)}`!"); + } + if(!opt.store.put){ + return opt.log("ERROR: Radisk needs `store.put` interface with `(file, data, cb)`!"); + } + if(!opt.store.get){ + return opt.log("ERROR: Radisk needs `store.get` interface with `(file, cb)`!"); + } + if(!opt.store.list){ + //opt.log("WARNING: `store.list` interface might be needed!"); } - r.batch(key, val); - if(cb){ r.batch.acks.push(cb) } - if(++r.batch.ed >= opt.batch){ return r.thrash() } // (2) - clearTimeout(r.batch.to); // (1) - r.batch.to = setTimeout(r.thrash, opt.thrash || 1); - } - r.batch = Radix(); - r.batch.acks = []; - r.batch.ed = 0; + /* + Any and all storage adapters should... + 1. Because writing to disk takes time, we should batch data to disk. This improves performance, and reduces potential disk corruption. + 2. If a batch exceeds a certain number of writes, we should immediately write to disk when physically possible. This caps total performance, but reduces potential loss. + */ + var r = function(key, val, cb){ + key = ''+key; + if(val instanceof Function){ + var o = cb; + cb = val; + val = r.batch(key); + if(u !== val){ + cb(u, val, o); + if(atomic(val)){ return } + // if a node is requested and some of it is cached... the other parts might not be. + } + if(r.thrash.at){ + val = r.thrash.at(key); + if(u !== val){ + cb(u, val, o); + if(atomic(val)){ cb(u, val, o); return } + // if a node is requested and some of it is cached... the other parts might not be. + } + } + return r.read(key, cb, o); + } + r.batch(key, val); + if(cb){ r.batch.acks.push(cb) } + if(++r.batch.ed >= opt.batch){ return r.thrash() } // (2) + if(r.batch.to){ return } + //clearTimeout(r.batch.to); // (1) // THIS LINE IS EVIL! NEVER USE IT! ALSO NEVER DELETE THIS SO WE NEVER MAKE THE SAME MISTAKE AGAIN! + r.batch.to = setTimeout(r.thrash, opt.until || 1); + } - r.thrash = function(){ - var thrash = r.thrash; - if(thrash.ing){ return thrash.more = true } - thrash.more = false; - thrash.ing = true; - var batch = thrash.at = r.batch, i = 0; - clearTimeout(r.batch.to); - r.batch = null; r.batch = Radix(); r.batch.acks = []; r.batch.ed = 0; - r.save(batch, function(err, ok){ - if(++i > 1){ return } - if(err){ Gun.log(err) } - Gun.obj.map(batch.acks, function(cb){ cb(err, ok) }); - thrash.at = null; - thrash.ing = false; - if(thrash.more){ thrash() } - }); - } - /* - 1. Find the first radix item in memory. - 2. Use that as the starting index in the directory of files. - 3. Find the first file that is lexically larger than it, - 4. Read the previous file to that into memory - 5. Scan through the in memory radix for all values lexically less than the limit. - 6. Merge and write all of those to the in-memory file and back to disk. - 7. If file to large, split. More details needed here. - */ - r.save = function(rad, cb){ - var s = function Span(){}; - s.find = function(tree, key){ - if(key < s.start){ return } - s.start = key; - opt.store.list(s.lex); - return true; - } - s.lex = function(file){ - if(!file || file > s.start){ - s.mix(s.file || opt.code.from, s.start, s.end = file); - return true; - } - s.file = file; - } - s.mix = function(file, start, end){ - s.start = s.end = s.file = u; - r.parse(file, function(err, disk){ - if(err){ return cb(err) } - Radix.map(rad, function(val, key){ - if(key < start){ return } - if(end && end < key){ return s.start = key } - disk(key, val); - }); - r.write(file, disk, s.next); + r.thrash = function(){ + var thrash = r.thrash; + if(thrash.ing){ return thrash.more = true } + thrash.more = false; + thrash.ing = true; + var batch = thrash.at = r.batch, i = 0; + clearTimeout(r.batch.to); + r.batch = null; + r.batch = Radix(); + r.batch.acks = []; + r.batch.ed = 0; + //var id = Gun.text.random(2), S = (+new Date); console.log("<<<<<<<<<<<<", id); + r.save(batch, function(err, ok){ + if(++i > 1){ opt.log('RAD ERR: Radisk has callbacked multiple times, please report this as a BUG at github.com/amark/gun/issues ! ' + i); return } + if(err){ opt.log('err', err) } + //console.log(">>>>>>>>>>>>", id, ((+new Date) - S), batch.acks.length); + map(batch.acks, function(cb){ cb(err, ok) }); + thrash.at = null; + thrash.ing = false; + if(thrash.more){ thrash() } }); } - s.next = function(err, ok){ - if(s.err = err){ return cb(err) } - if(s.start){ return Radix.map(rad, s.find) } - cb(err, ok); - } - Radix.map(rad, s.find); - } - /* - Any storage engine at some point will have to do a read in order to write. - This is true of even systems that use an append only log, if they support updates. - Therefore it is unavoidable that a read will have to happen, - the question is just how long you delay it. - */ - r.write = function(file, rad, cb){ - var f = function Fractal(){}; - f.text = ''; - f.count = 0; - f.file = file; - f.each = function(val, key, k, pre){ - f.count++; - var enc = Radisk.encode(pre.length) +'#'+ Radisk.encode(k) + (u === val? '' : '='+ Radisk.encode(val)) +'\n'; - if(opt.size < f.text.length + enc.length){ - f.text = ''; - f.limit = Math.ceil(f.count/2); - f.count = 0; - f.sub = Radix(); - Radix.map(rad, f.slice); + /* + 1. Find the first radix item in memory. + 2. Use that as the starting index in the directory of files. + 3. Find the first file that is lexically larger than it, + 4. Read the previous file to that into memory + 5. Scan through the in memory radix for all values lexically less than the limit. + 6. Merge and write all of those to the in-memory file and back to disk. + 7. If file too large, split. More details needed here. + */ + r.save = function(rad, cb){ + var s = function Span(){}; + s.find = function(tree, key){ + if(key < s.start){ return } + s.start = key; + r.list(s.lex); return true; } - f.text += enc; - } - f.write = function(){ opt.store.put(file, f.text, cb) } - f.slice = function(val, key){ - if(key < f.file){ return } - if(f.limit < (++f.count)){ - var name = f.file; - f.file = key; - f.count = 0; - r.write(name, f.sub, f.next); - return true; - } - f.sub(key, val); - } - f.next = function(err){ - if(err){ return cb(err) } - f.sub = Radix(); - if(!Radix.map(rad, f.slice)){ - r.write(f.file, f.sub, cb); - } - } - if(!Radix.map(rad, f.each, true)){ f.write() } - } - - r.read = function(key, cb){ - // TODO: BUG!!! If a node spans multiple file chunks, it won't return all! - // TODO: BUG!!! If a node spans multiple file chunks, it won't return all! - // TODO: BUG!!! If a node spans multiple file chunks, it won't return all! - // TODO: BUG!!! If a node spans multiple file chunks, it won't return all! - // TODO: BUG!!! If a node spans multiple file chunks, it won't return all! - // TODO: BUG!!! If a node spans multiple file chunks, it won't return all! - // TODO: BUG!!! If a node spans multiple file chunks, it won't return all! - if(RAD){ // cache - var val = RAD(key); - if(u !== val){ - return cb(u, val); - } - } - var g = function Get(){}, tmp; - g.lex = function(file){ - if(!file || file > key){ - if(tmp = q[g.file]){ - tmp.push({key: key, ack: cb}); + s.lex = function(file){ + file = (u === file)? u : decodeURIComponent(file); + if(!file || file > s.start){ + s.mix(s.file || opt.code.from, s.start, s.end = file); return true; } - q[g.file] = [{key: key, ack: cb}]; - r.parse(g.file, g.it); - return true; + s.file = file; } - g.file = file; + s.mix = function(file, start, end){ + s.start = s.end = s.file = u; + r.parse(file, function(err, disk){ + if(err){ return cb(err) } + disk = disk || Radix(); + Radix.map(rad, function(val, key){ + if(key < start){ return } + if(end && end < key){ return s.start = key } + // PLUGIN: consider adding HAM as an extra layer of protection + disk(key, val); // merge batch[key] -> disk[key] + }); + r.write(file, disk, s.next); + }); + } + s.next = function(err, ok){ + if(s.err = err){ return cb(err) } + if(s.start){ return Radix.map(rad, s.find) } + cb(err, ok); + } + Radix.map(rad, s.find); } - g.it = function(err, disk){ - if(g.err = err){ Gun.log(err) } - if(disk){ RAD = disk } - disk = q[g.file]; Gun.obj.del(q, g.file); - Gun.obj.map(disk, g.ack); + + /* + Any storage engine at some point will have to do a read in order to write. + This is true of even systems that use an append only log, if they support updates. + Therefore it is unavoidable that a read will have to happen, + the question is just how long you delay it. + */ + r.write = function(file, rad, cb, o){ + o = ('object' == typeof o)? o : {force: o}; + var f = function Fractal(){}; + f.text = ''; + f.count = 0; + f.file = file; + f.each = function(val, key, k, pre){ + //console.log("RAD:::", JSON.stringify([val, key, k, pre])); + if(u !== val){ f.count++ } + if(opt.pack <= (val||'').length){ return cb("Record too big!"), true } + var enc = Radisk.encode(pre.length) +'#'+ Radisk.encode(k) + (u === val? '' : ':'+ Radisk.encode(val)) +'\n'; + if((opt.chunk < f.text.length + enc.length) && (1 < f.count) && !o.force){ + f.text = ''; + f.limit = Math.ceil(f.count/2); + f.count = 0; + f.sub = Radix(); + Radix.map(rad, f.slice); + return true; + } + f.text += enc; + } + f.write = function(){ + var tmp = ename(file); + var start; LOG && (start = (+new Date)); // comment this out! + opt.store.put(tmp, f.text, function(err){ + LOG && console.log("wrote JSON in", (+new Date) - start); // comment this out! + if(err){ return cb(err) } + r.list.add(tmp, cb); + }); + } + f.slice = function(val, key){ + if(key < f.file){ return } + if(f.limit < (++f.count)){ + var name = f.file; + f.file = key; + f.count = 0; + r.write(name, f.sub, f.next, o); + return true; + } + f.sub(key, val); + } + f.next = function(err){ + if(err){ return cb(err) } + f.sub = Radix(); + if(!Radix.map(rad, f.slice)){ + r.write(f.file, f.sub, cb, o); + } + } + if(opt.jsonify){ return r.write.jsonify(f, file, rad, cb, o) } // temporary testing idea + if(!Radix.map(rad, f.each, true)){ f.write() } } - g.ack = function(as){ - if(!as.ack){ return } - as.ack(g.err, RAD(as.key)); + + r.write.jsonify = function(f, file, rad, cb, o){ + var raw; + var start; LOG && (start = (+new Date)); // comment this out! + try{raw = JSON.stringify(rad.$); + }catch(e){ return cb("Record too big!") } + LOG && console.log("stringified JSON in", (+new Date) - start); // comment this out! + if(opt.chunk < raw.length && !o.force){ + if(Radix.map(rad, f.each, true)){ return } + } + f.text = raw; + f.write(); } - opt.store.list(g.lex); - } - /* - Let us start by assuming we are the only process that is - changing the directory or bucket. Not because we do not want - to be multi-process/machine, but because we want to experiment - with how much performance and scale we can get out of only one. - Then we can work on the harder problem of being multi-process. - */ - r.parse = function(file, cb){ - var p = function Parse(){}, s = String.fromCharCode(31); - p.disk = Radix(); - p.read = function(err, data){ var tmp; - if(err){ return cb(err) } - if(!data){ return cb(u, p.disk) } - var tmp = p.split(data), pre = [], i, k, v; - while(tmp){ - k = v = u; - i = tmp[1]; - tmp = p.split(tmp[2])||''; - if('#' == tmp[0]){ - k = tmp[1]; - pre = pre.slice(0,i); - if(i <= pre.length){ - pre.push(k); + + ;(function(){ + var Q = {}; + r.read = function(key, cb, o){ + o = o || {}; + if(RAD && !o.next){ // cache + var val = RAD(key); + //if(u !== val){ + //cb(u, val, o); + if(atomic(val)){ cb(u, val, o); return } + // if a node is requested and some of it is cached... the other parts might not be. + //} + } + var g = function Get(){}, tmp; + g.lex = function(file){ + file = (u === file)? u : decodeURIComponent(file); + if(!file || file > (o.next || key)){ + if(o.next){ g.file = file } + if(tmp = Q[g.file]){ + tmp.push({key: key, ack: cb, file: g.file, opt: o}); + return true; + } + Q[g.file] = [{key: key, ack: cb, file: g.file, opt: o}]; + r.parse(g.file, g.it); + return true; } + g.file = file; } - tmp = p.split(tmp[2])||''; - if('\n' == tmp[0]){ continue } - if('=' == tmp[0]){ v = tmp[1] } - if(u !== k && u !== v){ p.disk(pre.join(''), v) } - tmp = p.split(tmp[2]); - } - cb(u, p.disk); - }; - p.split = function(t){ - if(!t){ return } - var l = [], o = {}, i = -1, a = '', b, c; - while(c = t[++i]){ - if(s === c){ break } - a += c; - } - if(!c){ return } - l[0] = a; - l[1] = b = Radisk.decode(t.slice(i), o); - l[2] = t.slice(i + o.i); - return l; - } - opt.store.get(file, p.read); - } - - var q = {}, RAD, u; - return r; -} - - -;(function(){ - s = String.fromCharCode(31); - Radisk.encode = function(d, o){ - var t = s, tmp; - if(typeof d == 'string'){ - var i = -1, c; - while(c = d[++i]){ - if(s === c){ - t += s; + g.it = function(err, disk, info){ + if(g.err = err){ opt.log('err', err) } + g.info = info; + if(disk){ RAD = g.disk = disk } + disk = Q[g.file]; delete Q[g.file]; + map(disk, g.ack); } + g.ack = function(as){ + if(!as.ack){ return } + var tmp = as.key, o = as.opt, info = g.info, rad = g.disk || noop, data = rad(tmp), last = rad.last; + o.parsed = (o.parsed || 0) + (info.parsed||0); + o.chunks = (o.chunks || 0) + 1; + if(!o.some){ o.some = (u !== data) } + if(u !== data){ as.ack(g.err, data, o) } + else if(!as.file){ !o.some && as.ack(g.err, u, o); return } + if(/*!last || */last === tmp){ !o.some && as.ack(g.err, u, o); return } + if(last && last > tmp && 0 != last.indexOf(tmp)){ !o.some && as.ack(g.err, u, o); return } + if(o.some && o.parsed >= o.limit){ return } + o.next = as.file; + r.read(tmp, as.ack, o); + } + r.list(g.lex); } - return t + '"' + d + s; - } else - if(d && d['#'] && (tmp = Gun.val.rel.is(d))){ - return t + '#' + tmp + t; - } else - if(Gun.num.is(d)){ - return t + '+' + (d||0) + t; - } else - if(null === d){ - return t + ' ' + t; - } else - if(true === d){ - return t + '+' + t; - } else - if(false === d){ - return t + '-' + t; - }// else - //if(binary){} - } - Radisk.decode = function(t, o){ - var d = '', i = -1, n = 0, c, p; - if(s !== t[0]){ return } - while(c = t[++i]){ - if(p){ - if(s === c){ - if(--n <= 0){ - break; + }()); + + ;(function(){ + /* + Let us start by assuming we are the only process that is + changing the directory or bucket. Not because we do not want + to be multi-process/machine, but because we want to experiment + with how much performance and scale we can get out of only one. + Then we can work on the harder problem of being multi-process. + */ + var Q = {}, s = String.fromCharCode(31); + r.parse = function(file, cb, raw){ var q; + if(q = Q[file]){ return q.push(cb) } q = Q[file] = [cb]; + var p = function Parse(){}, info = {}; + p.disk = Radix(); + p.read = function(err, data){ var tmp; + delete Q[file]; + if((p.err = err) || (p.not = !data)){ + return map(q, p.ack); } + if(typeof data !== 'string'){ + try{ + if(opt.pack <= data.length){ + p.err = "Chunk too big!"; + } else { + data = data.toString(); // If it crashes, it crashes here. How!?? We check size first! + } + }catch(e){ p.err = e } + if(p.err){ return map(q, p.ack) } + } + info.parsed = data.length; + + var start; LOG && (start = (+new Date)); // keep this commented out in production! + if(opt.jsonify){ // temporary testing idea + try{ + var json = JSON.parse(data); + p.disk.$ = json; + LOG && console.log('parsed JSON in', (+new Date) - start); // keep this commented out in production! + map(q, p.ack); + return; + }catch(e){ tmp = e } + if('{' === data[0]){ + p.err = tmp || "JSON error!"; + return map(q, p.ack); + } + } + + var start; LOG && (start = (+new Date)); // keep this commented out in production! + var tmp = p.split(data), pre = [], i, k, v; + if(!tmp || 0 !== tmp[1]){ + p.err = "File '"+file+"' does not have root radix! "; + return map(q, p.ack); + } + while(tmp){ + k = v = u; + i = tmp[1]; + tmp = p.split(tmp[2])||''; + if('#' == tmp[0]){ + k = tmp[1]; + pre = pre.slice(0,i); + if(i <= pre.length){ + pre.push(k); + } + } + tmp = p.split(tmp[2])||''; + if('\n' == tmp[0]){ continue } + if('=' == tmp[0] || ':' == tmp[0]){ v = tmp[1] } + if(u !== k && u !== v){ p.disk(pre.join(''), v) } + tmp = p.split(tmp[2]); + } + LOG && console.log('parsed JSON in', (+new Date) - start); // keep this commented out in production! + //cb(err, p.disk); + map(q, p.ack); + }; + p.split = function(t){ + if(!t){ return } + var l = [], o = {}, i = -1, a = '', b, c; + i = t.indexOf(s); + if(!t[i]){ return } + a = t.slice(0, i); + l[0] = a; + l[1] = b = Radisk.decode(t.slice(i), o); + l[2] = t.slice(i + o.i); + return l; } - d += c; + p.ack = function(cb){ + if(!cb){ return } + if(p.err || p.not){ return cb(p.err, u, info) } + cb(u, p.disk, info); + } + if(raw){ return p.read(null, raw) } + opt.store.get(ename(file), p.read); + } + }()); + + ;(function(){ + var dir, q, f = String.fromCharCode(28), ef = ename(f); + r.list = function(cb){ + if(dir){ + Radix.map(dir, function(val, key){ + return cb(key); + }) || cb(); + return; + } + if(q){ return q.push(cb) } q = [cb]; + r.parse(f, r.list.init); + } + r.list.add = function(file, cb){ + var has = dir(file); + if(has || file === ef){ + return cb(u, 1); + } + dir(file, true); + cb.listed = (cb.listed || 0) + 1; + r.write(f, dir, function(err, ok){ + if(err){ return cb(err) } + cb.listed = (cb.listed || 0) - 1; + if(cb.listed !== 0){ return } + cb(u, 1); + }, true); + } + r.list.init = function(err, disk){ + if(err){ + opt.log('list', err); + setTimeout(function(){ r.parse(f, r.list.init) }, 1000); + return; + } + if(disk){ + r.list.drain(disk); + return; + } + if(!opt.store.list){ + r.list.drain(Radix()); + return; + } + // import directory. + opt.store.list(function(file){ + dir = dir || Radix(); + if(!file){ return r.list.drain(dir) } + r.list.add(file, noop); + }); + } + r.list.drain = function(rad, tmp){ + r.list.dir = dir = rad; + tmp = q; q = null; + Gun.list.map(tmp, function(cb){ + Radix.map(dir, function(val, key){ + return cb(key); + }) || cb(); + }); + } + }()); + + var noop = function(){}, RAD, u; + Radisk.has[opt.file] = r; + return r; + } + + + + ;(function(){ + var _ = String.fromCharCode(31), u; + Radisk.encode = function(d, o, s){ s = s || _; + var t = s, tmp; + if(typeof d == 'string'){ + var i = d.indexOf(s); + while(i != -1){ t += s; i = d.indexOf(s, i+1) } + return t + '"' + d + s; } else - if(s === c){ - ++n; - } else { - p = c || true; + if(d && d['#'] && (tmp = Gun.val.link.is(d))){ + return t + '#' + tmp + t; + } else + if(Gun.num.is(d)){ + return t + '+' + (d||0) + t; + } else + if(null === d){ + return t + ' ' + t; + } else + if(true === d){ + return t + '+' + t; + } else + if(false === d){ + return t + '-' + t; + }// else + //if(binary){} + } + Radisk.decode = function(t, o, s){ s = s || _; + var d = '', i = -1, n = 0, c, p; + if(s !== t[0]){ return } + while(s === t[++i]){ ++n } + p = t[c = n] || true; + while(--n >= 0){ i = t.indexOf(s, i+1) } + if(i == -1){ i = t.length } + d = t.slice(c+1, i); + if(o){ o.i = i+1 } + if('"' === p){ + return d; + } else + if('#' === p){ + return Gun.val.link.ify(d); + } else + if('+' === p){ + if(0 === d.length){ + return true; + } + return parseFloat(d); + } else + if(' ' === p){ + return null; + } else + if('-' === p){ + return false; } } - if(o){ o.i = i+1 } - if('"' === p){ - return d; - } else - if('#' === p){ - return Gun.val.rel.ify(d); - } else - if('+' === p){ - if(0 === d.length){ - return true; - } - return parseFloat(d); - } else - if(' ' === p){ - return null; - } else - if('-' === p){ - return false; - } + }()); + + if(typeof window !== "undefined"){ + var Gun = window.Gun; + var Radix = window.Radix; + window.Radisk = Radisk; + } else { + var Gun = require('../gun'); + var Radix = require('./radix'); + try{ module.exports = Radisk }catch(e){} } -}()); -Radisk.Radix = Radix; + Radisk.Radix = Radix; -module.exports = Radisk; \ No newline at end of file +}()); \ No newline at end of file diff --git a/lib/radix.js b/lib/radix.js index 1fef14cb..03fa31bb 100644 --- a/lib/radix.js +++ b/lib/radix.js @@ -1,12 +1,13 @@ -var Gun = require('../gun'); ;(function(){ - var map = Gun.obj.map, no = {}, u; - - var $ = String.fromCharCode(30), _ = String.fromCharCode(29); function Radix(){ var radix = function(key, val, t){ - t = t || radix[_] || (radix[_] = {}); + key = ''+key; + if(!t && u !== val){ + radix.last = (key < radix.last)? radix.last : key; + delete (radix.$||{})[_]; + } + t = t || radix.$ || (radix.$ = {}); var i = 0, l = key.length-1, k = key[i], at, tmp; while(!(at = t[k]) && i < l){ k += key[++i]; @@ -14,9 +15,9 @@ var Gun = require('../gun'); if(!at){ if(!map(t, function(r, s){ var ii = 0, kk = ''; - while(s[ii] == key[ii]){ + if((s||'').length){ while(s[ii] == key[ii]){ kk += s[ii++]; - } + } } if(kk){ if(u === val){ if(ii <= l){ return } @@ -24,47 +25,66 @@ var Gun = require('../gun'); } var __ = {}; __[s.slice(ii)] = r; - (__[key.slice(ii)] = {})[$] = val; - (t[kk] = {})[_] = __; + ii = key.slice(ii); + ('' === ii)? (__[''] = val) : ((__[ii] = {})[''] = val); + t[kk] = __; delete t[s]; return true; } })){ if(u === val){ return; } - (t[k] || (t[k] = {}))[$] = val; + (t[k] || (t[k] = {}))[''] = val; } if(u === val){ return tmp; } } else if(i == l){ - if(u === val){ return (u === (tmp = at[$]))? at[_] : tmp } - at[$] = val; + if(u === val){ return (u === (tmp = at['']))? at : tmp } + at[''] = val; } else { - return radix(key.slice(++i), val, at[_] || (at[_] = {})); + if(u !== val){ delete at[_] } + return radix(key.slice(++i), val, at || (at = {})); } } return radix; }; - Radix.map = function map(radix, cb, opt, pre){ pre = pre || []; - var t = radix[_] || radix, keys = Object.keys(t).sort(), i = 0, l = keys.length; - for(;i < l; i++){ var key = keys[i], tree = t[key], tmp; - if(u !== (tmp = tree[$])){ - tmp = cb(tmp, pre.join('') + key, key, pre); + + Radix.map = function map(radix, cb, opt, pre){ pre = pre || []; + var t = ('function' == typeof radix)? radix.$ || {} : radix; + if(!t){ return } + var keys = (t[_]||no).sort || (t[_] = function $(){ $.sort = Object.keys(t).sort(); return $ }()).sort; + //var keys = Object.keys(t).sort(); + var i = 0, l = keys.length; + for(;i < l; i++){ var key = keys[i], tree = t[key], tmp, p; + if(!tree || '' === key || _ === key){ continue } + p = pre.slice(); p.push(key); + if(u !== (tmp = tree[''])){ + tmp = cb(tmp, p.join(''), key, pre); if(u !== tmp){ return tmp } - } else + } else if(opt){ - cb(u, pre.join(''), key, pre); - } - if(tmp = tree[_]){ - pre.push(key); - tmp = map(tmp, cb, opt, pre); + tmp = cb(u, pre.join(''), key, pre); if(u !== tmp){ return tmp } - pre.pop(); } + pre = p; + tmp = map(tree, cb, opt, pre); + if(u !== tmp){ return tmp } + pre.pop(); } }; + Object.keys = Object.keys || function(o){ return map(o, function(v,k,t){t(k)}) } + + if(typeof window !== "undefined"){ + var Gun = window.Gun; + window.Radix = Radix; + } else { + var Gun = require('../gun'); + try{ module.exports = Radix }catch(e){} + } - module.exports = Radix; -}()); + var map = Gun.obj.map, no = {}, u; + var _ = String.fromCharCode(24); + +}()); \ 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/rfs.js b/lib/rfs.js new file mode 100644 index 00000000..42a49e23 --- /dev/null +++ b/lib/rfs.js @@ -0,0 +1,89 @@ +var Gun = (typeof window !== "undefined")? window.Gun : require('../gun'); + +function Store(opt){ + opt = opt || {}; + opt.file = String(opt.file || 'radata'); + if(Gun.TESTING){ opt.file = 'radatatest' } + var fs = require('fs'), u; + var store = function Store(){}; + + store.put = function(file, data, cb){ + var random = Math.random().toString(36).slice(-3); + var tmp = opt.file+'-'+file+'-'+random+'.tmp'; + fs.writeFile(tmp, data, function(err, ok){ + if(err){ return cb(err) } + move(tmp, opt.file+'/'+file, cb); + }); + }; + store.get = function(file, cb){ + fs.readFile(opt.file+'/'+file, function(err, data){ + if(err){ + if('ENOENT' === (err.code||'').toUpperCase()){ + return cb(null); + } + Gun.log("ERROR:", err) + } + cb(err, data); + }); + }; + store.list = function(cb, match){ + fs.readdir(opt.file, function(err, dir){ + Gun.obj.map(dir, cb) || cb(); // Stream interface requires a final call to know when to be done. + }); + }; + if(!fs.existsSync(opt.file)){ fs.mkdirSync(opt.file) } + //store.list(function(){ return true }); + + function move(oldPath, newPath, cb) { + fs.rename(oldPath, newPath, function (err) { + if (err) { + if (err.code === 'EXDEV') { + var readStream = fs.createReadStream(oldPath); + var writeStream = fs.createWriteStream(newPath); + + readStream.on('error', cb); + writeStream.on('error', cb); + + readStream.on('close', function () { + fs.unlink(oldPath, cb); + }); + + readStream.pipe(writeStream); + } else { + cb(err); + } + } else { + cb(); + } + }); + }; + + return store; +} + +function Mem(opt){ + opt = opt || {}; + opt.file = String(opt.file || 'radata'); + var storage = Mem.storage || (Mem.storage = {}); + var store = function Store(){}, u; + store.put = function(file, data, cb){ + setTimeout(function(){ + storage[file] = data; + cb(null, 1); + }, 1); + }; + store.get = function(file, cb){ + setTimeout(function(){ + var tmp = storage[file] || u; + cb(null, tmp); + }, 1); + }; + store.list = function(cb, match){ // supporting this is no longer needed! Optional. + setTimeout(function(){ + Gun.obj.map(Object.keys(storage), cb) || cb(); + }, 1); + }; + return store; +} + +module.exports = Store;//Gun.TESTING? Mem : Store; diff --git a/lib/rindexed.js b/lib/rindexed.js new file mode 100644 index 00000000..e27f8d6c --- /dev/null +++ b/lib/rindexed.js @@ -0,0 +1,61 @@ +;(function(){ + var Gun = (typeof window !== "undefined")? window.Gun : require('../gun'); + + Gun.on('create', function(root){ + this.to.next(root); + root.opt.store = root.opt.store || Store(root.opt); + }); + + function Store(opt){ + opt = opt || {}; + opt.file = String(opt.file || 'radata'); + if(Gun.TESTING){ opt.file = 'radatatest' } + opt.chunk = opt.chunk || (1024 * 1024); // 1MB + var db = null, u; + + try{opt.indexedDB = opt.indexedDB || indexedDB}catch(e){} + try{if(!opt.indexedDB || 'file:' == location.protocol){ + var store = {}, s = {}; + store.put = function(f, d, cb){ s[f] = d; cb(null, 1) }; + store.get = function(f, cb){ cb(null, s[f] || u) }; + console.log('Warning: No indexedDB exists to persist data to!'); + return store; + }}catch(e){} + + var store = function Store(){}; + store.start = function(){ + var o = indexedDB.open(opt.file, 1); + o.onupgradeneeded = function(eve){ (eve.target.result).createObjectStore(opt.file) } + o.onsuccess = function(){ db = o.result } + o.onerror = function(eve){ console.log(eve||1); } + }; store.start(); + + store.put = function(key, data, cb){ + if(!db){ setTimeout(function(){ store.put(key, data, cb) },1); return } + var tx = db.transaction([opt.file], 'readwrite'); + var obj = tx.objectStore(opt.file); + var req = obj.put(data, ''+key); + req.onsuccess = obj.onsuccess = tx.onsuccess = function(){ cb(null, 1) } + req.onabort = obj.onabort = tx.onabort = function(eve){ cb(eve||'put.tx.abort') } + req.onerror = obj.onerror = tx.onerror = function(eve){ cb(eve||'put.tx.error') } + } + + store.get = function(key, cb){ + if(!db){ setTimeout(function(){ store.get(key, cb) },9); return } + var tx = db.transaction([opt.file], 'readonly'); + var obj = tx.objectStore(opt.file); + var req = obj.get(''+key); + req.onsuccess = function(){ cb(null, req.result) } + req.onabort = function(eve){ cb(eve||4) } + req.onerror = function(eve){ cb(eve||5) } + } + setInterval(function(){ db && db.close(); db = null; store.start() }, 1000 * 15); // reset webkit bug? + return store; + } + + if(Gun.window){ + Gun.window.RindexedDB = Store; + } else { + module.exports = Store; + } +}()); \ No newline at end of file diff --git a/lib/rls.js b/lib/rls.js new file mode 100644 index 00000000..f01f7801 --- /dev/null +++ b/lib/rls.js @@ -0,0 +1,27 @@ +;(function(){ + var Gun = (typeof window !== "undefined")? window.Gun : require('../gun'); + + Gun.on('create', function(root){ + this.to.next(root); + root.opt.store = root.opt.store || Store(root.opt); + }); + + function Store(opt){ + opt = opt || {}; + opt.file = String(opt.file || 'radata'); + if(Gun.TESTING){ opt.file = 'radatatest' } + var store = function Store(){}; + + var ls = localStorage; + store.put = function(key, data, cb){ ls[''+key] = data; cb(null, 1) } + store.get = function(key, cb){ cb(null, ls[''+key]) } + + return store; + } + + if(Gun.window){ + Gun.window.RlocalStorage = Store; + } else { + module.exports = Store; + } +}()); \ No newline at end of file diff --git a/lib/role.js b/lib/role.js new file mode 100644 index 00000000..e8a56ed0 --- /dev/null +++ b/lib/role.js @@ -0,0 +1,130 @@ +;(function(){ + + function resolve(chain){ + /* + If we say "Spray paint all of Bob's friends' cat blue", + the embedded question we might need to ask is: + If one of Bob's friends winds up unfriending him, + AND has their cat die that they get a new cat, + do we want THAT cat painted blue? + I believe the answer is NO. + */ + // soul.key=value + // soul ??? + // stream -> soul.key ... + // stream -> soul.key + // if you have a dynamic map, and you want to "process it", you wind up waiting until at least 1 item exists, calling off, then calling each. + // in this sense, resolve can only know it has processed each item once, but not whether it itself is done. and that is fair. + // what about ops that read before writing like timegraph? + //console.debug.i=1;console.log("----------------"); + /* +gun.get('a').get('b').put({ + x: { + x1: {c: {d: {'yay':1}}}, + x2: {c: {d: {'cake':1}}} + }, + y: { + y1: {c: {d: {'foo':1}}}, + y2: {c: {d: {'bar':1}}} + } +}); + /* + map {10:{}, 5:{}} -> get link to load + */ + // FOR THIS CHAIN: Each item gets called 3 times. + Gun.debug=1; + //return; + chain = chain || gun.get('a').get('b').map().map().get('c').get('d').get(function(a,b,c,tmp){ + // a.gun._.soul || a.gun._.link; + a.ID = a.ID || Gun.text.random(2); + console.log('********', a.put || a);//, Gun.node.soul(a.put), a.gun._);//, a.gun.back(function back(_){_.get && (tmp || (tmp = [])).push(_.get);return _.back? undefined : tmp;})); + //b.rid(a); + }); + console.log("~~~~~~~~~~~~~~"); + window.chain = chain; + } + /* + sync put: 5 node - 1 stop + sync reload: 1 link 2 node - X stop + sync resolve: 6 node - 0 stop : 3 node - 0 stop + async put: 5 node + 3 node - 1 stop + async reload: 2 link 1 node - X stop (2 links per each stop) + async resolve: 6 node - 0 stop : 3 node - 0 stop + + sync put: 1 mum + sync reload: 1 mum + sync resolve: 1 mum + async put: 1 mum + async reload: 0 mum: 2 link 1 node + async resolve: 1 mum + + */ + + function off(chain){ + //Gun.debug = 1; + chain = chain || gun.get('users').map().get(function(a,b,c,tmp){ + console.log("***", a.put); + b.rid(a); + }); + gun.get('users').get('alice').get(function(a,b){ + console.log(">>>", a.put); + }); + console.log("vvvvvvvvvvvvv"); + window.chain = chain; + } +/* +gun.get('users').put({ + alice: {age: 29}, + bob: {age: 32} +}); +*/ + + function soul(chain){ + Gun.debug = 1; + gun.get('x').get('y').get('z').get('q').get(function(a,b,c){ + console.log("***", a.put || a);//,b,c); + }); + setTimeout(function(){ + console.debug.j=1; + console.debug.i=1;console.log("------------"); + gun.get('x').get('y').put({ + z: { + q: {r: {hello: 'world'}} + } + }); + },20); + console.log(".............."); + window.chain = chain; + } +/* +gun.get('x').get('y').get('z').put({xyz: 'zyx'}); +*/ + + window.resolve = resolve; + window.off = off; + window.soul = soul; + //localStorage.clear();sessionStorage.clear(); + setTimeout(function(){ resolve() },1); + + /* + At the end of the day, you trust an entity, not data. + That entity might be a person, or a group of people, + it doesn't really matter - you do not trust a machine. + + Trust gives write access (public). + Grant gives read access (private). + + */ + + function Role(){} + if(typeof window !== "undefined"){ Role.window = window } + var Gun = (Role.window||{}).Gun || require('../gun'); + Gun.SEA || require('../sea'); + if(!Gun.User){ throw "No User System!" } + var User = Gun.User; + + User.prototype.trust = function(user){ + + } + +}()); \ No newline at end of file diff --git a/lib/rs3.js b/lib/rs3.js index ce73d99a..3c6c1630 100644 --- a/lib/rs3.js +++ b/lib/rs3.js @@ -4,14 +4,13 @@ var fs = require('fs'); var Radix = Radisk.Radix; var u, AWS; -Gun.on('opt', function(ctx){ - this.to.next(ctx); - var opt = ctx.opt; - if(ctx.once){ return } +Gun.on('create', function(root){ + this.to.next(root); + var opt = root.opt; if(!process.env.AWS_S3_BUCKET){ return } opt.batch = opt.batch || (1000 * 10); - opt.thrash = opt.thrash || (1000 * 15); - opt.size = opt.size || (1024 * 1024 * 10); // 10MB + opt.until = opt.until || (1000 * 3); + opt.chunk = opt.chunk || (1024 * 1024 * 10); // 10MB try{AWS = require('aws-sdk'); }catch(e){ @@ -63,7 +62,7 @@ function Store(opt){ if(!ack){ cb(null); return; } cb(err, data); }; - if(data = (ack||{}).Body){ data = data.toString() } + data = (ack||{}).Body; //if(data = (ack||{}).Body){ data = data.toString() } Gun.obj.map(cbs, cbe); }); }; diff --git a/lib/s3.js b/lib/s3.js deleted file mode 100644 index 4a0ccc1a..00000000 --- a/lib/s3.js +++ /dev/null @@ -1,102 +0,0 @@ -;(function(){ - - var Gun = require('../gun'); - var S3 = require('./aws'); - - // TODO: BUG! Mark, upgrade S3 in v0.8.X! And try to integrate with Radix Storage Engine!!! - - Gun.on('opt', function(ctx){ - this.to.next(ctx); - var opt = ctx.opt; - if(ctx.once){ return } - if(!process.env.AWS_S3_BUCKET){ return } - console.log("S3 STORAGE ENGINE IS BROKEN IN 0.8! DO NOT USE UNTIL FIXED!"); - var s3 = opt.store || S3(opt.s3 = opt.s3 || {}); - opt.s3.store = s3; - if(!s3 || !s3.config){ return Gun.log("No S3 config!") } - opt.file = opt.file || opt.prefix || ''; - opt.batch = opt.batch || 10; - opt.throttle = opt.throttle || process.env.AWS_S3_THROTTLE || 15; - opt.disconnect = opt.disconnect || 5; - ctx.on('get', function(at){ - this.to.next(at); - var id = at['#'], soul = at.get['#'], field = at.get['.']; - var key = opt.prefix+soul; - s3.GET(key, function(err, data, text, meta){ - meta = meta || {}; - if(err && err.statusCode == 404){ - err = null; // we want a difference between 'unfound' (data is null) and 'error' (auth is wrong). - } - if(!data){ - data = root._.graph[soul] || async[soul]; - } - if(data && !Gun.node.soul(data)){ - err = {err: Gun.log('No soul on S3 node data!')}; - } - if(err){ - root.on('in', {'@': id, err: err}); - return - } - var node = data; - graph[soul] = true; - if(data && field){ - node = Gun.state.ify({}, field, Gun.state.is(node, field), node[field], soul); - } - console.log("got", soul, node); - root.on('in', {'@': id, put: Gun.graph.node(node)}); - }); - }); - ctx.on('put', function(at){ - this.to.next(at); - var id = at['#'], check = {}, next = s3.next, err, u; - Gun.graph.is(at.put, null, function(val, key, node, soul){ - batch[soul] = Gun.state.to(node, key, disk[soul]); - }); - if(!at['@']){ acks[at['#']] = true; } // only ack non-acks. - s3.batching += 1; - if(opt.batch < s3.batching){ - return now(); - } - if(!opt.throttle){ - return now(); - } - s3.wait = s3.wait || setTimeout(now, opt.throttle * 1000); // in seconds - }); - function now(){ - clearTimeout(s3.wait); - var keep = batch; - batch = {}; - s3.batching = 0; - var now = s3.next; - s3.next = Gun.time.is(); - s3.wait = null; - Gun.obj.map(keep, function put(exists, soul, count){ - console.log("s3ving...", soul); - var node = root._.graph[soul] || async[soul]; // the batch does not actually have the nodes, but what happens when we do cold data? Could this be gone? - s3.PUT(opt.prefix+soul, node, function(err, reply){ - if(count < 5 && (err || !reply)){ - put(exists, soul, (count || 0) + 1); // naive implementation of retry TODO: BUG: need backoff and anti-infinite-loop! - return; - } - //console.log("S3VED", soul); - s3.on(now + ':' + soul, [err, reply]); - }); - }); - } - var graph = {}, batch = {}, acks = {}, ids = {}, async = {}; - var count = 0; - s3.next = s3.next || Gun.time.is(); - s3.on = s3.on || Gun.on; - }); - - ;(function(){return; - global.Gun = require('../gun'); - - process.env.AWS_S3_BUCKET = 'test-s3'; - process.env.AWS_ACCESS_KEY_ID = 'asdf'; - process.env.AWS_SECRET_ACCESS_KEY = 'fdsa'; - process.env.fakes3 = 'http://localhost:4567'; - process.env.AWS_S3_THROTTLE = 0; - require('../test/abc'); - }()); -}()); diff --git a/lib/serve.js b/lib/serve.js index 04c86a82..93b8235e 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -28,4 +28,4 @@ module.exports = function serve(req, res, next){ } } return next(); -} +} \ No newline at end of file diff --git a/lib/serve.mjs b/lib/serve.mjs deleted file mode 100644 index c3a54d56..00000000 --- a/lib/serve.mjs +++ /dev/null @@ -1,41 +0,0 @@ - -import fs from 'fs' - -let dirname // TODO: where did __dirname go ? - -const serve = (req, res, nxt) => { - if (!req || !res) { - return false - } - - const next = nxt || serve - - if (!req.url) { - return next() - } - - if (0 <= req.url.indexOf('gun.js')) { - res.writeHead(200, { 'Content-Type': 'text/javascript' }) - res.end(serve.js = serve.js || fs.readFileSync(dirname + '/gun.js')) - return true - } - - if (0 <= req.url.indexOf('gun/')) { - res.writeHead(200, { 'Content-Type': 'text/javascript' }) - var path = dirname + '/' + req.url.split('/').slice(2).join('/'), file - try { - file = fs.readFileSync(path) - } catch(e) {} // eslint-disable-line no-empty - if (file) { - res.end(file) - return true - } - } - - return next() -} - -export default (dir) => { - dirname = dir - return serve -} diff --git a/lib/server.js b/lib/server.js index 5f6d6c68..509af9e5 100644 --- a/lib/server.js +++ b/lib/server.js @@ -1,14 +1,22 @@ ;(function(){ - var Gun = require('../gun'); + var Gun = require('../gun'), u; 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); + }) require('../nts'); require('./store'); require('./rs3'); - //try{require('./ws');}catch(e){require('./wsp/server');} require('./wire'); - require('./verify'); + try{require('../sea');}catch(e){} + //try{require('../axe');}catch(e){} require('./file'); - require('./bye'); + require('./evict'); + require('./multicast'); if('debug' === process.env.GUN_ENV){ require('./debug') } module.exports = Gun; -}()); \ No newline at end of file +}()); diff --git a/lib/server.mjs b/lib/server.mjs deleted file mode 100644 index 8c3cc007..00000000 --- a/lib/server.mjs +++ /dev/null @@ -1,7 +0,0 @@ - -import serve from './serve' - -export default (Gun, dir) => { // TODO: where did __dirname go ? - Gun.serve = serve(dir) - return Gun -} diff --git a/lib/set.js b/lib/set.js deleted file mode 100644 index 47b0653c..00000000 --- a/lib/set.js +++ /dev/null @@ -1,35 +0,0 @@ -var Gun = Gun || require('../gun'); - -/* -Gun.chain.set = function(obj, cb, opt){ - var set = this; - opt = opt || {}; - cb = cb || function(){}; - set = set.put({}); // insert assumes a graph node. So either create it or merge with the existing one. - var error, item = set.chain().put(obj, function(err){ // create the new item in its own context. - error = err; // if this happens, it should get called before the .val - }).val(function(val){ - if(error){ return cb.call(set, error) } // which in case it is, allows us to fail fast. - var add = {}, soul = Gun.is.soul.on(val); - if(!soul){ return cb.call(set, {err: Gun.log("No soul!")}) } - add[soul] = val; // other wise, let's then - set.put(add, cb); // merge with the graph node. - }); - return item; -};*/ - -Gun.chain.set = function(val, cb, opt){ - var gun = this, ctx = {}, drift = Gun.time.now(); - cb = cb || function(){}; - opt = opt || {}; - - if(!gun._.back){ gun = gun.put({}) } - gun = gun.not(function(next, key){ - return key? this.put({}).key(key) : this.put({}); - }); - if(!val && !Gun.is.value(val)){ return gun } - - var obj = {}; - obj['I' + drift + 'R' + Gun.text.random(5)] = val; - return gun.put(obj, cb); -} \ No newline at end of file diff --git a/lib/shim.js b/lib/shim.js index 27c8e243..c33e73dc 100644 --- a/lib/shim.js +++ b/lib/shim.js @@ -1,10 +1,6 @@ -if(typeof window !== "undefined"){ - var Gun = window.Gun; -} else { - var Gun = require('gun/gun'); -} +var Gun = (typeof window !== "undefined")? window.Gun : require('../gun'); -Gun.chain.open || require('gun/lib/open'); +Gun.chain.open || require('./open'); var _on = Gun.chain.on; Gun.chain.on = function(a,b,c){ @@ -14,7 +10,7 @@ Gun.chain.on = function(a,b,c){ return _on.call(this, a,b,c); } -Gun.chain.bye || require('gun/lib/bye'); +Gun.chain.bye || require('./bye'); Gun.chain.onDisconnect = Gun.chain.bye; Gun.chain.connected = function(cb){ var root = this.back(-1), last; diff --git a/lib/space.js b/lib/space.js new file mode 100644 index 00000000..bb7e1a82 --- /dev/null +++ b/lib/space.js @@ -0,0 +1,118 @@ +;(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); + } + 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, 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; + 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.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){ + 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) } + }); + //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(); + 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){ + 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/lib/store.js b/lib/store.js index 943e0ac0..0b7b1e38 100644 --- a/lib/store.js +++ b/lib/store.js @@ -1,108 +1,86 @@ -var Gun = require('../gun'); -var Radisk = require('./radisk'); -var fs = require('fs'); -var Radix = Radisk.Radix; -var u; - -Gun.on('opt', function(ctx){ - this.to.next(ctx); - var opt = ctx.opt; - if(ctx.once){ return } - if(false !== opt.localStorage && !process.env.AWS_S3_BUCKET){ return } // TODO: Remove this after migration. - if(false === opt.radisk){ return } - console.log("BUG WARNING: Radix Storage Engine (RSE) has a known rare edge case, if data gets split between file chunks, a GET may only return the first chunk!!!"); - opt.store = opt.store || Store(opt); - var rad = Radisk(opt); - - ctx.on('put', function(at){ - this.to.next(at); - var id = at['#'], track = !at['@'], acks = track? 0 : u; // only ack non-acks. - Gun.graph.is(at.put, null, function(val, key, node, soul){ - if(track){ ++acks } - val = Radisk.encode(val)+'>'+Radisk.encode(Gun.state.is(node, key)); - rad(soul+'.'+key, val, (track? ack : u)); - }); - function ack(err, ok){ - acks--; - if(ack.err){ return } - if(ack.err = err){ - ctx.on('in', {'@': id, err: Gun.log(err)}); - return; - } - if(acks){ return } - ctx.on('in', {'@': id, ok: 1}); - } - }); - - ctx.on('get', function(at){ - this.to.next(at); - var id = at['#'], soul = at.get['#'], key = at.get['.']||'', tmp = soul+'.'+key, node; - rad(tmp, function(err, val){ - if(val){ - Radix.map(val, each); - if(!node){ each(val, key) } - } - ctx.on('in', {'@': id, put: Gun.graph.node(node), err: err? err : u}); - }); - function each(val, key){ - tmp = val.lastIndexOf('>'); - var state = Radisk.decode(val.slice(tmp+1)); - val = Radisk.decode(val.slice(0,tmp)); - node = Gun.state.ify(node, key, state, val, soul); - } - }); - -}); - -function Store(opt){ - opt = opt || {}; - opt.file = String(opt.file || 'radata'); - - var store = function Store(){}; - store.put = function(file, data, cb){ - var random = Math.random().toString(36).slice(-3) - fs.writeFile(opt.file+'-'+random+'.tmp', data, function(err, ok){ - if(err){ return cb(err) } - fs.rename(opt.file+'-'+random+'.tmp', opt.file+'/'+file, cb); - }); - }; - store.get = function(file, cb){ - fs.readFile(opt.file+'/'+file, function(err, data){ - if(err){ - if('ENOENT' === (err.code||'').toUpperCase()){ - return cb(null); - } - Gun.log("ERROR:", err) - } - if(data){ data = data.toString() } - cb(err, data); - }); - }; - store.list = function(cb, match){ - fs.readdir(opt.file, function(err, dir){ - Gun.obj.map(dir, cb) || cb(); // Stream interface requires a final call to know when to be done. - }); - }; - if(!fs.existsSync(opt.file)){ fs.mkdirSync(opt.file) } - //store.list(function(){ return true }); - return store; -} - -module.exports = Store; - - -;(function(){ - return; - process.env.AWS_S3_BUCKET = 'test-s3'; - process.env.AWS_ACCESS_KEY_ID = 'asdf'; - process.env.AWS_SECRET_ACCESS_KEY = 'fdsa'; - process.env.fakes3 = 'http://localhost:4567'; - process.env.AWS_S3_THROTTLE = 0; - - return; - global.Gun = require('../gun'); - //require('./rs3'); - - - require('../test/abc'); -}()); \ No newline at end of file +var Gun = (typeof window !== "undefined")? window.Gun : require('../gun'); + +Gun.on('create', function(root){ + this.to.next(root); + var opt = root.opt, u; + if(false === opt.radisk){ return } + var Radisk = (Gun.window && Gun.window.Radisk) || require('./radisk'); + var Radix = Radisk.Radix; + + opt.store = opt.store || (!Gun.window && require('./rfs')(opt)); + var rad = Radisk(opt), esc = String.fromCharCode(27); + + root.on('put', function(msg){ + this.to.next(msg); + var id = msg['#'] || Gun.text.random(3), track = !msg['@'], acks = track? 0 : u; // only ack non-acks. + if(msg.rad && !track){ return } // don't save our own acks + Gun.graph.is(msg.put, null, function(val, key, node, soul){ + if(track){ ++acks } + //console.log('put:', soul, key, val); + val = Radisk.encode(val, null, esc)+'>'+Radisk.encode(Gun.state.is(node, key), null, esc); + rad(soul+esc+key, val, (track? ack : u)); + }); + function ack(err, ok){ + acks--; + if(ack.err){ return } + if(ack.err = err){ + root.on('in', {'@': id, err: err}); + return; + } + if(acks){ return } + //console.log("PAT!", id); + root.on('in', {'@': id, ok: 1}); + } + }); + + root.on('get', function(msg){ + this.to.next(msg); + var id = msg['#'], get = msg.get, soul = msg.get['#'], has = msg.get['.']||'', opt = {}, graph, lex, key, tmp, force; + if(typeof soul == 'string'){ + key = soul; + } else + if(soul){ + if(u !== (tmp = soul['*'])){ opt.limit = force = 1 } + key = force? (''+tmp) : tmp || soul['=']; + } + if(key && !opt.limit){ // a soul.has must be on a soul, and not during soul* + if(typeof has == 'string'){ + key = key+esc+(opt.atom = has); + } else + if(has){ + if(u !== (tmp = has['*'])){ opt.limit = force = 1 } + if(key){ key = key+esc + (force? (''+tmp) : tmp || (opt.atom = has['='])) } + } + } + if((tmp = get['%']) || opt.limit){ + opt.limit = (tmp <= (opt.pack || (1000 * 100)))? tmp : 1; + } + //var start = (+new Date); // console.log("GET!", id, JSON.stringify(key)); + rad(key||'', function(err, data, o){ + if(data){ + if(typeof data !== 'string'){ + if(opt.atom){ + data = u; + } else { + Radix.map(data, each) + } + } + if(!graph && data){ each(data, '') } + } + //console.log("GOT!", id, JSON.stringify(key), ((+new Date) - start)); + root.on('in', {'@': id, put: graph, err: err? err : u, rad: Radix}); + }, opt); + function each(val, has, a,b){ + if(!val){ return } + has = (key+has).split(esc); + var soul = has.slice(0,1)[0]; + has = has.slice(-1)[0]; + opt.count = (opt.count || 0) + val.length; + tmp = val.lastIndexOf('>'); + var state = Radisk.decode(val.slice(tmp+1), null, esc); + val = Radisk.decode(val.slice(0,tmp), null, esc); + (graph = graph || {})[soul] = Gun.state.ify(graph[soul], has, state, val, soul); + if(opt.limit && opt.limit <= opt.count){ return true } + } + }); +}); \ No newline at end of file diff --git a/lib/super.js b/lib/super.js new file mode 100644 index 00000000..1724f40c --- /dev/null +++ b/lib/super.js @@ -0,0 +1,37 @@ +;(function(){ + var Gun = (typeof window !== "undefined")? window.Gun : require('../gun'); + var Rad = (Gun.window||{}).Radix || require('./radix'); + /// Store the subscribes + Gun.subs = Rad(); + function input(msg){ + var at = this.as, to = this.to, peer = (msg._||empty).via; + var get = msg.get, soul, key; + if(!peer || !get){ return to.next(msg) } + // console.log("super", msg); + if(soul = get['#']){ + if(key = get['.']){ + + } else { + + } + if (!peer.id) {console.log('[*** WARN] no peer.id %s', soul);} + var subs = Gun.subs(soul) || null; + var tmp = subs ? subs.split(',') : [], p = at.opt.peers; + if (subs) { + Gun.obj.map(subs.split(','), function(peerid) { + if (peerid in p) { tmp.push(peerid); } + }); + } + if (tmp.indexOf(peer.id) === -1) { tmp.push(peer.id);} + tmp = tmp.join(','); + Gun.subs(soul, tmp); + var dht = {}; + dht[soul] = tmp; + at.opt.mesh.say({dht:dht}, peer); + } + to.next(msg); + } + var empty = {}, u; + if(Gun.window){ return } + try{module.exports = input}catch(e){} +}()); diff --git a/lib/then.js b/lib/then.js index 94e7c80e..b0034094 100644 --- a/lib/then.js +++ b/lib/then.js @@ -1,9 +1,9 @@ -var Gun = Gun || require('../gun'); +var Gun = (typeof window !== "undefined")? window.Gun : require('../gun'); Gun.chain.promise = function(cb) { var gun = this, cb = cb || function(ctx) { return ctx }; return (new Promise(function(res, rej) { - gun.val(function(data, key){ + gun.once(function(data, key){ res({put: data, get: key, gun: this}); }); })).then(cb); diff --git a/lib/time.js b/lib/time.js new file mode 100644 index 00000000..b4d8d951 --- /dev/null +++ b/lib/time.js @@ -0,0 +1,138 @@ +;(function(){ + var Gun = (typeof window !== "undefined")? window.Gun : require('../gun'); + var ify = Gun.node.ify, u; + Gun.chain.time = function(data, a, b){ + if(data instanceof Function){ + return travel(data, a, b, this); + } + var gun = this, root = gun.back(-1); + var cb = (a instanceof Function && a) || (b instanceof Function && b); + if(Gun.is(data)){ + data.get(function(soul){ + if(!soul){ + return cb && cb({err: "Timegraph cannot link `undefined`!"}); + } + gun.time(Gun.val.link.ify(soul), a, b); + }, true); + return gun; + } + opt = (cb === a)? b : a; + opt = Gun.text.is(opt)? {key: opt} : opt || {}; + var t = new Date(Gun.state()).toISOString().split(/[\-t\:\.z]/ig); + var p, tmp = t.pop(); + gun.get(function(soul){ + var id = soul; + p = id; + if(!p){ id = p = (gun.back('opt.uuid') || Gun.text.random)(9) } + // could shrink this into a loop. Do later? + t = [p].concat(t); + var rid = opt.key || (gun.back('opt.uuid') || Gun.text.random)(9); + var milli = ify({}, t.join(':')); + milli[rid] = data; + tmp = t.pop(); + var sec = ify({}, t.join(':')); + sec[tmp] = milli; + tmp = t.pop(); + var min = ify({}, t.join(':')); + min[tmp] = sec; + tmp = t.pop(); + var hour = ify({}, t.join(':')); + hour[tmp] = min; + tmp = t.pop(); + var day = ify({}, t.join(':')); + day[tmp] = hour; + tmp = t.pop(); + var month = ify({}, t.join(':')); + month[tmp] = day; + tmp = t.pop(); + var year = ify({}, t.join(':')); + year[tmp] = month; + tmp = t.pop(); + var time = ify({}, t.join(':') || id); + time[tmp] = year; + gun.put(time, cb); + }, true); + return gun; + } + function travel(cb, opt, b, gun){ + var root = gun.back(-1), tmp; + (opt = Gun.num.is(opt)? {last: opt} : opt || {}).seen = opt.seen || {}; + var t = now(opt.start); + gun.on(function(data, key, msg, eve){ + var at = msg.$._, id = at.link || at.soul || Gun.node.soul(data); + if(!id){ return } + if(false === opt.next){ eve.off() } + else { opt.next = true } + opt.start = [opt.id = id].concat(t); + opt.low = opt.low || opt.start; + opt.top = opt.top || opt.start; + opt.now = [id].concat(now()); + //console.log("UPDATE"); + find(opt, cb, root, opt.at? opt.now : opt.at = opt.start); + }); + return gun; + } + function now(t){ + return new Date(t || Gun.state()).toISOString().split(/[\-t\:\.z]/ig).slice(0,-1); + } + function find(o, cb, root, at, off){ + var at = at || o.at, t = at.join(':'), tmp; + if(!off){ + if(o.seen[t]){ return } + o.seen[t] = true; + } + var next = (o.low || o.start)[at.length]; + root.get(t).get(function(msg, ev){ + if(off){ ev.off() } + var g = this; + //console.log(at, msg.put); + if(u === msg.put){ + find(o, cb, root, at.slice(0,-1), off); + return; + } + if(7 < at.length){ + var l = Object.keys(msg.put).length; + if(l === o.seen[t]){ return } + var when = +(toDate(at)); + Gun.node.is(msg.put, function(v, k){ + cb(v, k, when, ev); + if(o.last){ --o.last } + }); + o.seen[t] = l; + if(!o.last){ return } + if(o.last <= 0){ return } + o.low = at; + var tmp = at.slice(0,-1); + find(o, cb, root, tmp, true); + return; + } + if(o.last && false !== off){ + var keys = Object.keys(msg.put).sort().reverse(); + var less = Gun.list.map(keys, function(k){ + if(parseFloat(k) < parseFloat(next)){ return k } + }); + if(!less){ + find(o, cb, root, at.slice(0,-1), true); + } else { + var tmp = (at || o.at).slice(); + tmp.push(less); + (o.low = tmp.slice()).push(Infinity); + find(o, cb, root, tmp, true); + } + } + if(off){ return } + if(!o.next){ return } + if(at < o.start.slice(0, at.length)){ return } + var n = [o.id].concat(now()), top = n[at.length]; + Gun.node.is(msg.put, function(v, k){ + if(k > top){ return } + (v = at.slice()).push(k); + find(o, cb, root, v, false); + }); + }) + } + function toDate(at){ + at = at.slice(-7); + return new Date(Date.UTC(at[0], parseFloat(at[1])-1, at[2], at[3], at[4], at[5], at[6])); + } +}()); \ No newline at end of file diff --git a/lib/tmp.test.sql.js b/lib/tmp.test.sql.js index 1f9058ed..b7d5b664 100644 --- a/lib/tmp.test.sql.js +++ b/lib/tmp.test.sql.js @@ -32,7 +32,7 @@ test._.stack.push(fn); return test; } - var gun = window.gun = Gun(); //Gun('http://localhost:8080/gun'); + var gun = window.gun = Gun(); window.SPAM = function(read){ // TODO: BUG? gun-sid in transport layer not correct? //localStorage.clear(); var start = Gun.time.is(); diff --git a/lib/unbuild.js b/lib/unbuild.js index ef51e1bb..0e4a8e20 100644 --- a/lib/unbuild.js +++ b/lib/unbuild.js @@ -11,19 +11,7 @@ var write = function(path, data){ return fs.writeFileSync(nodePath.join(dir, path), data); } -var rm = function(path, full) { - path = full || nodePath.join(dir, path); - if(!fs.existsSync(path)){ return } - fs.readdirSync(path).forEach(function(file,index){ - var curPath = path + "/" + file; - if(fs.lstatSync(curPath).isDirectory()) { // recurse - rm(null, curPath); - } else { // delete file - fs.unlinkSync(curPath); - } - }); - fs.rmdirSync(path); -}; +var rm = require('./fsrm'); var mk = function(path){ path = nodePath.join(dir, path); diff --git a/lib/unset.js b/lib/unset.js index 93ac47fb..98dae1a9 100644 --- a/lib/unset.js +++ b/lib/unset.js @@ -1,10 +1,6 @@ -if(typeof window !== "undefined"){ - var Gun = window.Gun; -} else { - var Gun = require('gun/gun'); -} +var Gun = (typeof window !== "undefined")? window.Gun : require('../gun'); -const rel_ = Gun.val.rel._; // '#' +const rel_ = Gun.val.link._; // '#' const node_ = Gun.node._; // '_' Gun.chain.unset = function(node){ diff --git a/lib/upload.js b/lib/upload.js index 87985fd2..aaacba29 100644 --- a/lib/upload.js +++ b/lib/upload.js @@ -25,9 +25,14 @@ 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(!h && img.width > w){ h = img.height * (w / img.width) } + if(img.width < (w = w || 1000) && img.height < (h||Infinity) && "data:" == file.slice(0,5)){ + e.base64 = file; + return cb(e || file); + } + if(!h){ h = img.height * (w / img.width) } var canvas = document.createElement('canvas'), ctx = canvas.getContext('2d'); canvas.width = w; canvas.height = h; diff --git a/lib/uws.js b/lib/uws.js index 232f6711..c20d7ec2 100644 --- a/lib/uws.js +++ b/lib/uws.js @@ -51,7 +51,7 @@ Gun.on('opt', function mount(ctx){ if(!tmp.length){ return } batch = JSON.stringify(tmp); Gun.obj.map(opt.peers, send, ctx); - }, opt.wait || 1); + }, opt.gap || opt.wait || 1); Gun.obj.map(opt.peers, send, ctx); }); diff --git a/lib/webrtc.js b/lib/webrtc.js new file mode 100644 index 00000000..62831c17 --- /dev/null +++ b/lib/webrtc.js @@ -0,0 +1,110 @@ +;(function(){ + var Gun = (typeof window !== "undefined")? window.Gun : require('../gun'); + + Gun.on('opt', function(root){ + this.to.next(root); + var opt = root.opt; + if(root.once){ return } + if(!Gun.Mesh){ return } + if(false === opt.RTCPeerConnection){ return } + + var env; + if(typeof window !== "undefined"){ env = window } + if(typeof global !== "undefined"){ env = global } + env = env || {}; + + var rtcpc = opt.RTCPeerConnection || env.RTCPeerConnection || env.webkitRTCPeerConnection || env.mozRTCPeerConnection; + var rtcsd = opt.RTCSessionDescription || env.RTCSessionDescription || env.webkitRTCSessionDescription || env.mozRTCSessionDescription; + var rtcic = opt.RTCIceCandidate || env.RTCIceCandidate || env.webkitRTCIceCandidate || env.mozRTCIceCandidate; + if(!rtcpc || !rtcsd || !rtcic){ return } + opt.RTCPeerConnection = rtcpc; + opt.RTCSessionDescription = rtcsd; + opt.RTCIceCandidate = rtcic; + opt.rtc = opt.rtc || {'iceServers': [ + {url: 'stun:stun.l.google.com:19302'}, + {url: "stun:stun.sipgate.net:3478"}, + {url: "stun:stun.stunprotocol.org"}, + {url: "stun:stun.sipgate.net:10000"}, + {url: "stun:217.10.68.152:10000"}, + {url: 'stun:stun.services.mozilla.com'} + ]}; + opt.rtc.dataChannel = opt.rtc.dataChannel || {ordered: false, maxRetransmits: 2}; + opt.rtc.sdp = opt.rtc.sdp || {mandatory: {OfferToReceiveAudio: false, OfferToReceiveVideo: false}}; + opt.announce = function(to){ + //setTimeout(function() { + console.log('[WEBRTC] announce ', opt.pid, to); + root.on('out', {rtc: {id: opt.pid, to:to}}); +// }, 1); + }; // announce ourself + var mesh = opt.mesh = opt.mesh || Gun.Mesh(root); +// root.on('create', function(at){ +// this.to.next(at); +// setTimeout(function(){ root.on('out', {rtc: {id: opt.pid}}) },1); // announce ourself +// }); + root.on('in', function(msg){ + if(msg.rtc){ open(msg) } + this.to.next(msg); + }); + + function open(msg){ + var rtc = msg.rtc, peer, tmp; + if(!rtc || !rtc.id){ return } + delete opt.announce[rtc.id]; /// remove after connect + if(tmp = rtc.answer){ + if(!(peer = opt.peers[rtc.id]) || peer.remoteSet){ return } + return peer.setRemoteDescription(peer.remoteSet = new opt.RTCSessionDescription(tmp)); + } + if(tmp = rtc.candidate){ + peer = opt.peers[rtc.id] || open({rtc: {id: rtc.id}}); + return peer.addIceCandidate(new opt.RTCIceCandidate(tmp)); + } + if(opt.peers[rtc.id]){ return } + (peer = new opt.RTCPeerConnection(opt.rtc)).id = rtc.id; + var wire = peer.wire = peer.createDataChannel('dc', opt.rtc.dataChannel); + mesh.hi(peer); + wire.onclose = function(){ + mesh.bye(peer); + peer.wire = null; + //reconnect(peer); + }; + wire.onerror = function(error){ + //reconnect(peer); // placement? + if(!error){ return } + if(error.code === 'ECONNREFUSED'){ + //reconnect(peer, as); + } + }; + wire.onopen = function(e){ + mesh.hi(peer); + } + wire.onmessage = function(msg){ + if(!msg){ return } + mesh.hear(msg.data || msg, peer); + }; + peer.onicecandidate = function(e){ // source: EasyRTC! + if(!e.candidate){ return } + root.on('out', {'@': msg['#'], rtc: {candidate: e.candidate, id: opt.pid}}); + } + peer.ondatachannel = function(e){ + var rc = e.channel; + rc.onmessage = wire.onmessage; + rc.onopen = wire.onopen; + rc.onclose = wire.onclose; + } + if(tmp = rtc.offer){ + peer.setRemoteDescription(new opt.RTCSessionDescription(tmp)); + peer.createAnswer(function(answer){ + peer.setLocalDescription(answer); + root.on('out', {'@': msg['#'], rtc: {answer: answer, id: opt.pid}}); + }, function(){}, opt.rtc.sdp); + return; + } + peer.createOffer(function(offer){ + peer.setLocalDescription(offer); + root.on('out', {'@': msg['#'], rtc: {offer: offer, id: opt.pid}}); + }, function(){}, opt.rtc.sdp); + return peer; + } + }); + var noop = function(){}; +}()); diff --git a/lib/wire.js b/lib/wire.js index 2060d359..5e74b6b3 100644 --- a/lib/wire.js +++ b/lib/wire.js @@ -49,43 +49,37 @@ var Gun = require('../gun'); */ -var WebSocket = require('ws'); - -var url = require('url'); - -Gun.on('opt', function(ctx){ - var opt = ctx.opt; +Gun.on('opt', function(root){ + var opt = root.opt; if(false === opt.ws){ - this.to.next(ctx); + this.to.next(root); return; - } + } - opt.WebSocket = opt.WebSocket || WebSocket; + var url = require('url'); + opt.WebSocket = opt.WebSocket || require('ws'); var ws = opt.ws || {}; ws.server = ws.server || opt.web; if(ws.server && !ws.web){ + opt.mesh = opt.mesh || Gun.Mesh(root); ws.path = ws.path || '/gun'; + ws.maxPayload = ws.maxPayload; // || opt.pack || (opt.memory? (opt.memory * 1000 * 1000) : 1399000000) * 0.3; ws.web = new opt.WebSocket.Server(ws); - ws.web.on('connection', function(wire){ + ws.web.on('connection', function(wire){ var peer; wire.upgradeReq = wire.upgradeReq || {}; wire.url = url.parse(wire.upgradeReq.url||'', true); - wire.id = wire.id || Gun.text.random(6); - var peer = opt.peers[wire.id] = {id: wire.id, wire: wire}; - wire.peer = function(){ return peer }; - ctx.on('hi', peer); + opt.mesh.hi(peer = {wire: wire}); wire.on('message', function(msg){ - //console.log("MESSAGE", msg); opt.mesh.hear(msg.data || msg, peer); }); wire.on('close', function(){ - ctx.on('bye', peer); - Gun.obj.del(opt.peers, wire.id); + opt.mesh.bye(peer); }); wire.on('error', function(e){}); }); } - this.to.next(ctx); + this.to.next(root); }); \ No newline at end of file diff --git a/lib/ws.js b/lib/ws.js index 2cb2e88e..a37da6b7 100644 --- a/lib/ws.js +++ b/lib/ws.js @@ -55,7 +55,7 @@ Gun.on('opt', function mount(ctx){ if(!tmp.length){ return } batch = JSON.stringify(tmp); Gun.obj.map(opt.peers, send, ctx); - }, opt.wait || 1); + }, opt.gap || opt.wait || 1); Gun.obj.map(opt.peers, send, ctx); }); diff --git a/lib/wsp/Peer.js b/lib/wsp/Peer.js deleted file mode 100644 index 2b9dffed..00000000 --- a/lib/wsp/Peer.js +++ /dev/null @@ -1,190 +0,0 @@ -/* eslint-disable no-underscore-dangle */ -'use strict'; - -var WebSocket = require('ws'); -var Emitter = require('events'); -var util = require('util'); - -/** - * Calculates backoff instances. - * @param {Object} [options] - Override the default settings. - * @param {Object} options.time=50 - Initial backoff time. - * @param {Object} options.factor=2 - How much to multiply the time by. - * @param {Object} options.max=1min - Maximum backoff time. - * @class - */ -function Backoff (options) { - this.options = options || {}; - - // Sets the initial backoff settings. - this.reset(); -} - -/** - * Increments the time by the factor. - * @return {Number} - The next backoff time. - */ -Backoff.prototype.next = function () { - var next = this.time * this.factor; - - if (next > this.max) { - this.time = this.max; - return this.max; - } - - this.time = next; - - return this.time; -}; - -/** - * Resets the backoff state to it's original condition. - * @return {Backoff} - The context. - */ -Backoff.prototype.reset = function () { - var options = this.options; - - this.time = options.time || 50; - this.factor = options.factor || 2; - this.max = options.max || 1 * 60 * 1000; - - return this; -}; - -/** - * Schedules the next connection, according to the backoff. - * @param {Peer} peer - A peer instance. - * @return {Timeout} - The timeout value from `setTimeout`. - */ -function scheduleReconnect (peer) { - var backoff = peer.backoff; - var time = backoff.time; - backoff.next(); - - var reconnect = peer.connect.bind(peer); - - return setTimeout(reconnect, time); -} - -/** - * Handles reconnections and defers messages until the socket is ready. - * @param {String} url - The address to connect to. - * @param {Object} [options] - Override how the socket is managed. - * @param {Object} options.backoff - Backoff options (see the constructor). - * @class - */ -function Peer (url, options) { - if (!(this instanceof Peer)) { - return new Peer(url, options); - } - - // Extend EventEmitter. - Emitter.call(this); - this.setMaxListeners(Infinity); - - this.options = options || {}; - - // Messages sent before the socket is ready. - this.deferredMsgs = []; - - this.url = Peer.formatURL(url); - this.backoff = new Backoff(this.options.backoff); - - // Set up the websocket. - this.connect(); - - var peer = this; - var reconnect = scheduleReconnect.bind(null, peer); - - // Handle reconnection. - this.on('close', reconnect); - this.on('error', function (error) { - if (error.code === 'ECONNREFUSED') { - reconnect(); - } - }); - - // Send deferred messages. - this.on('open', function () { - peer.drainQueue(); - peer.backoff.reset(); - }); - -} - -/** - * Turns http URLs into WebSocket URLs. - * @param {String} url - The url to format. - * @return {String} - A correctly formatted WebSocket URL. - */ -Peer.formatURL = function (url) { - - // Works for `https` and `wss` URLs, too. - return url.replace(/^http/, 'ws'); -}; - -util.inherits(Peer, Emitter); -var API = Peer.prototype; - -/** - * Attempts a websocket connection. - * @return {WebSocket} - The new websocket instance. - */ -API.connect = function () { - var url = this.url; - - // Open a new websocket. - var socket = new WebSocket(url, this.options.wsc.protocols, this.options.wsc); - - // Re-use the previous listeners. - socket._events = this._events; - - this.socket = socket; - - return socket; -}; - -/** - * Sends all the messages in the deferred queue. - * @return {Peer} - The context. - */ -API.drainQueue = function () { - var peer = this; - - this.deferredMsgs.forEach(function (msg) { - peer.send(msg); - }); - - // Reset the queue. - this.deferredMsgs = []; - - return this; -}; - -/** - * Send data through the socket, or add it to a queue - * of deferred messages if it's not ready yet. - * @param {Mixed} msg - String, or anything that JSON can handle. - * @return {Peer} - The context. - */ -API.send = function (msg) { - var socket = this.socket; - var state = socket.readyState; - var ready = socket.OPEN; - - // Make sure it's a string. - if (typeof msg !== 'string') { - msg = JSON.stringify(msg); - } - - // Make sure the socket is ready. - if (state === ready) { - socket.send(msg); - } else { - this.deferredMsgs.push(msg); - } - - return this; -}; - -module.exports = Peer; diff --git a/lib/wsp/Pool.js b/lib/wsp/Pool.js deleted file mode 100644 index 542c0482..00000000 --- a/lib/wsp/Pool.js +++ /dev/null @@ -1,101 +0,0 @@ -'use strict'; - -/** - * Simpler interface over a collection of sockets. Works with - * WebSocket clients, or sockets from a WebSocket server. - * @class - */ -function Pool () { - if (!(this instanceof Pool)) { - return new Pool(); - } - - // Maps IDs to sockets. - this.sockets = {}; -} - -var API = Pool.prototype; - -/** - * Returns the socket by the given ID. - * @param {String} id - The unique socket ID. - * @return {WebSocket|Null} - The WebSocket, if found. - */ -API.get = function (id) { - return this.sockets[id] || null; -}; - -/** - * Adds a socket to the pool. - * @param {String} id - The socket ID. - * @param {WebSocket} socket - A websocket instance. - * @return {Pool} - The context. - */ -API.add = function (id, socket) { - this.sockets[id] = socket; - - return this; -}; - -/** - * Removes a socket from the pool. - * @param {String} id - The ID of the socket to remove. - * @return {Boolean} - Whether the pool contained the socket. - */ -API.remove = function (id) { - var sockets = this.sockets; - var hasSocket = sockets.hasOwnProperty(id); - - if (hasSocket) { - delete sockets[id]; - } - - return hasSocket; -}; - -/** - * Creates a filtered pool of sockets. Works the same as Array#filter. - * @param {Function} fn - Called for each socket in the pool. - * @param {Mixed} [_this] - The `this` context to use when invoking - * the callback. - * @return {Pool} - A new, filtered socket pool. - */ -API.filter = function (fn, _this) { - var filtered = Pool(); - var pool = this; - - _this = _this || pool; - - Object.keys(this.sockets).forEach(function (id) { - var socket = pool.sockets[id]; - - var shouldAdd = fn.call(_this, socket, id, pool); - - // Add it to the new pool. - if (shouldAdd) { - filtered.add(id, socket); - } - }); - - return filtered; -}; - -/** - * Send a message through each socket in the pool. - * @param {String} msg - The message to send. - * @return {Number} - How many sockets the message was sent to. - */ -API.send = function (msg) { - var pool = this; - - var ids = Object.keys(this.sockets); - - ids.forEach(function (id) { - var socket = pool.sockets[id]; - socket.send(msg); - }); - - return ids.length; -}; - -module.exports = Pool; diff --git a/lib/wsp/client.js b/lib/wsp/client.js deleted file mode 100644 index 3cdb3850..00000000 --- a/lib/wsp/client.js +++ /dev/null @@ -1,103 +0,0 @@ -/* - eslint-disable - no-warning-comments, - no-underscore-dangle, -*/ -'use strict'; - -var Gun = require('../../gun'); -var Socket = require('./Peer'); -var Pool = require('./Pool'); - -// Maps URLs to sockets. -// Shared between all gun instances. -var sockets = Pool(); -var sid = Gun.text.random(); - -/** - * Take a map of URLs pointing to options and ensure the - * urls are using the WS protocol. - * @param {Object} peers - Any object with URLs as keys. - * @return {Object} - Object with normalized URL keys. - */ -function normalizeURLs (peers) { - var formatted = {}; - - Object.keys(peers).forEach(function (url) { - var options = peers[url]; - var id = Socket.formatURL(url); - formatted[id] = options; - }); - - return formatted; -} - -/** - * Turns a map of URLs into a socket pool. - * @param {Object} peers - Any object with URLs as keys. - * @return {Pool} - A pool of sockets corresponding to the URLs. - */ -function getSocketSubset (peers) { - var urls = normalizeURLs(peers); - - return sockets.filter(function (socket) { - return urls.hasOwnProperty(socket.url); - }); -} - -Gun.on('out', function (ctx) { - this.to.next(ctx); - var gun = ctx.gun; - var opt = ctx.opt || {}; - var peers = opt.peers || gun.back('opt.peers'); - var headers = opt.headers || gun.back('opt.headers') || {}; - - if (!peers) { - return; - } - - var subset = getSocketSubset(peers); - - headers['gun-sid'] = sid; - subset.send({ - headers: headers, - body: ctx, - }); -}); - -// Open any new sockets listed, -// adding them to the global pool. -Gun.on('opt', function (context) { - var gun = context.gun; - var root = gun.back(Infinity); - - var peers = gun.back('opt.peers') || {}; - - Gun.obj.map(peers, function (options, url) { - if (sockets[url]) { - return; - } - if (!options.wsc){ options.wsc = gun.back('opt.wsc') || { protocols:null }; } - - var socket = Socket(url, options); - sockets.add(url, socket); - - socket.on('message', function (msg) { - var request; - - try { - request = JSON.parse(msg); - } catch (error) { - return; - } - - // Validate the request. - if (!request || !request.body) { - return; - } - - root.on('in', request.body); - }); - }); - this.to.next(context); -}); diff --git a/lib/wsp/duplicate.js b/lib/wsp/duplicate.js deleted file mode 100644 index 9080fee7..00000000 --- a/lib/wsp/duplicate.js +++ /dev/null @@ -1,90 +0,0 @@ -'use strict'; - -var Gun = require('../../gun'); - -var cache = {}; -var timeout = null; - -/** - * Remove all entries in the cache older than 5 minutes. - * Reschedules itself to run again when the oldest item - * might be too old. - * @return {undefined} - */ -function gc () { - var now = Date.now(); - var oldest = now; - var maxAge = 5 * 60 * 1000; - - Gun.obj.map(cache, function (time, id) { - oldest = Math.min(now, time); - - if ((now - time) < maxAge) { - return; - } - - delete cache[id]; - }); - - var done = Gun.obj.empty(cache); - - // Disengage GC. - if (done) { - timeout = null; - return; - } - - // Just how old? - var elapsed = now - oldest; - - // How long before it's too old? - var nextGC = maxAge - elapsed; - - // Schedule the next GC event. - timeout = setTimeout(gc, nextGC); -} - -/** - * Checks a memory-efficient cache to see if a string has been seen before. - * @param {String} id - A string to keep track of. - * @return {Boolean} - Whether it's been seen recently. - */ -function duplicate (id) { - - // Have we seen this ID recently? - var existing = cache.hasOwnProperty(id); - - // Add it to the cache. - duplicate.track(id); - - return existing; -} - -/** - * Starts tracking an ID as a possible future duplicate. - * @param {String} id - The ID to track. - * @return {String} - The same ID. - */ -duplicate.track = function (id) { - cache[id] = Date.now(); - - // Engage GC. - if (!timeout) { - gc(); - } - - return id; -}; - -/** - * Generate a new ID and start tracking it. - * @param {Number} [chars] - The number of characters to use. - * @return {String} - The newly created ID. - */ -duplicate.track.newID = function (chars) { - var id = Gun.text.random(chars); - - return duplicate.track(id); -}; - -module.exports = duplicate; diff --git a/lib/wsp/server-push.js b/lib/wsp/server-push.js deleted file mode 100644 index 772cd452..00000000 --- a/lib/wsp/server-push.js +++ /dev/null @@ -1,98 +0,0 @@ -'use strict'; -var Gun = require('../../gun.js'); - -/** - * Whether the gun instance is attached to a socket server. - * @param {Gun} gun - The gun instance in question. - * @param {WebSocket.Server} server - A socket server gun might be attached to. - * @return {Boolean} - Whether it's attached. - */ -function isUsingServer (gun, server) { - var servers = gun.back(-1)._.servers; - - return servers ? servers.indexOf(server) !== -1 : false; -} - -/** - * Calls a function when (or if) a socket is ready for messages. - * @param {WebSocket} socket - A websocket connection. - * @param {Function} cb - Called if or when the socket is ready. - * @return {Boolean} - Whether the socket is able to take messages. - */ -function ready (socket, cb) { - var state = socket.readyState; - - // The socket is ready. - if (state === socket.OPEN) { - cb(); - return true; - } - - // Still opening. - if (state === socket.OPENING) { - socket.once('open', cb); - } - - // Nope, closing or closed. - return false; -} - -/** - * Send a message to a group of clients. - * @param {Obejct} msg - An http envelope-like message. - * @param {Object} clients - IDs mapped to socket instances. - * @return {undefined} - */ -function send (msg, clients) { - Gun.obj.map(clients, function (client) { - ready(client, function () { - var serialized = JSON.stringify(msg); - client.send(serialized); - }); - }); -} - -/** * Attaches server push middleware to gun. - * @param {Gun} gun - The gun instance to attach to. - * @param {WebSocket.Server} server - A websocket server instance. - * @return {server} - The socket server. - */ -function attach (gun, server) { - var root = gun.back(-1); - root._.servers = root._.servers || []; - root._.servers.push(server); - var pool = {}; - var sid = Gun.text.random(); - server.on('connection', function (socket) { - socket.id = socket.id || Gun.text.random(10); - pool[socket.id] = socket; - /* - socket.on('message', function (message) { - var data = Gun.obj.ify(message); - - if (!data || !data.body) { - return; - } - root.on('in', data.body); - }); - */ - socket.once('close', function () { - delete pool[socket.id]; - }); - }); - - Gun.on('out', function (context) { - this.to.next(context); - if (!isUsingServer(context.gun, server) || Gun.obj.empty(pool)) { - return; - } - - var msg = { - headers: { 'gun-sid': sid }, - body: context, - }; - send(msg, pool); - }); -} - -module.exports = attach; diff --git a/lib/wsp/server.js b/lib/wsp/server.js deleted file mode 100644 index c7fb528c..00000000 --- a/lib/wsp/server.js +++ /dev/null @@ -1,257 +0,0 @@ -/* eslint-disable require-jsdoc, no-underscore-dangle */ -'use strict'; -var Gun = require('../../gun'); -var http = require('../http'); -var url = require('url'); -var WS = require('ws'); -var WSS = WS.Server; -var attach = require('./server-push'); - -// Handles server to server sync. -require('./client.js'); - -Gun.on('opt', function (at) { - this.to.next(at); - var gun = at.gun, opt = at.opt; - gun.__ = at.root; - gun.__.opt.ws = opt.ws = gun.__.opt.ws || opt.ws || {}; - gun.__.opt.ws.path = gun.__.opt.ws.path || '/gun'; - - if(gun.__.opt.web){ - setTimeout(function(){ - if(gun.__.opt.web.use){ - gun.__.opt.web.use(Gun.serve); - } - start(gun.__.opt.web); - },1); - } - function start (server, port, app) { - if (app && app.use) { - app.use(gun.wsp.server); - } - server = gun.__.opt.ws.server = gun.__.opt.ws.server || gun.__.opt.web || opt.ws.server || server; - - if (!gun.wsp.ws) { - //console.log("????????", gun.__.opt.ws); - gun.wsp.ws = new WSS(gun.__.opt.ws); - attach(gun, gun.wsp.ws); - } - - require('./ws')(gun.wsp.ws, function (req, res) { - var ws = this; - req.headers['gun-sid'] = ws.sid = ws.sid ? ws.sid : req.headers['gun-sid']; - ws.sub = ws.sub || gun.wsp.on('network', function (msg) { - var ev = this; ev.to.next(msg); - if (!ws || !ws.send || !ws._socket || !ws._socket.writable) { return ev.off(); } - if (!msg || (msg.headers && msg.headers['gun-sid'] === ws.sid)) { return; } - if (msg && msg.headers) { delete msg.headers['ws-rid']; } - // TODO: BUG? ^ What if other peers want to ack? Do they use the ws-rid or a gun declared id? - try { ws.send(Gun.text.ify(msg)); - } catch (e) {} // juuuust in case. - }); - gun.wsp.wire(req, res); - }, {headers: {'ws-rid': 1, 'gun-sid': 1}}); - gun.__.opt.ws.port = gun.__.opt.ws.port || opt.ws.port || port || 80; - } - var wsp = gun.wsp = gun.wsp || function (server) { - console.log("WARNING: gun.wsp(server) should be switched to Gun({web: server}) by v0.7!") - if (!server) { return gun; } - if (Gun.fns.is(server.address)) { - if (server.address()) { - start(server, server.address().port); - return gun; - } - } - if (Gun.fns.is(server.get) && server.get('port')) { - start(server, server.get('port')); - return gun; - } - var listen = server.listen; - server.listen = function (port) { - var serve = listen.apply(server, arguments); - start(serve, port, server); - return serve; - }; - return gun; - }; - gun.wsp.on = gun.wsp.on || Gun.on; - gun.wsp.regex = gun.wsp.regex || opt.route || opt.path || /^\/gun/i; - gun.wsp.poll = gun.wsp.poll || opt.poll || 1; - gun.wsp.pull = gun.wsp.pull || opt.pull || gun.wsp.poll * 1000; - gun.wsp.server = gun.wsp.server || function (req, res, next) { // http - next = next || function () {}; - if (!req || !res) { return next(), false; } - if (!req.url) { return next(), false; } - if (!req.method) { return next(), false; } - var msg = {}; - msg.url = url.parse(req.url, true); - if (!gun.wsp.regex.test(msg.url.pathname)) { return next(), false; } // TODO: BUG! If the option isn't a regex then this will fail! - if (msg.url.pathname.replace(gun.wsp.regex, '').slice(0, 3).toLowerCase() === '.js') { - res.writeHead(200, {'Content-Type': 'text/javascript'}); - res.end(gun.wsp.js = gun.wsp.js || require('fs').readFileSync(__dirname + '/../../gun.js')); // gun server is caching the gun library for the client - return true; - } - - if (!req.upgrade) { - next(); - return false; - } - - return http(req, res, function (req, res) { - if (!req) { return next(); } - var stream, cb = res = require('../jsonp')(req, res); - if (req.headers && (stream = req.headers['gun-sid'])) { - stream = (gun.wsp.peers = gun.wsp.peers || {})[stream] = gun.wsp.peers[stream] || {sid: stream}; - stream.drain = stream.drain || function (res) { - if (!res || !stream || !stream.queue || !stream.queue.length) { return; } - res({headers: {'gun-sid': stream.sid}, body: stream.queue }); - stream.off = setTimeout(function () { stream = null; }, gun.wsp.pull); - stream.reply = stream.queue = null; - return true; - }; - stream.sub = stream.sub || gun.wsp.on('network', function (req) { - var ev = this; ev.to.next(req); - if (!stream) { return ev.off(); } // self cleans up after itself! - if (!req || (req.headers && req.headers['gun-sid'] === stream.sid)) { return; } - (stream.queue = stream.queue || []).push(req); - stream.drain(stream.reply); - }); - cb = function (r) { (r.headers || {}).poll = gun.wsp.poll; res(r); }; - clearTimeout(stream.off); - if (req.headers.pull) { - if (stream.drain(cb)) { return; } - return stream.reply = cb; - } - } - gun.wsp.wire(req, cb); - }), true; - }; - if ((gun.__.opt.maxSockets = opt.maxSockets || gun.__.opt.maxSockets) !== false) { - require('https').globalAgent.maxSockets = require('http').globalAgent.maxSockets = gun.__.opt.maxSockets || Infinity; - } - gun.wsp.msg = gun.wsp.msg || function (id) { - if (!id) { - return gun.wsp.msg.debounce[id = Gun.text.random(9)] = Gun.time.is(), id; - } - clearTimeout(gun.wsp.msg.clear); - gun.wsp.msg.clear = setTimeout(function () { - var now = Gun.time.is(); - Gun.obj.map(gun.wsp.msg.debounce, function (t, id) { - if ((now - t) < (1000 * 60 * 5)) { return; } - Gun.obj.del(gun.wsp.msg.debounce, id); - }); - }, 500); - if (id = gun.wsp.msg.debounce[id]) { - return gun.wsp.msg.debounce[id] = Gun.time.is(), id; - } - gun.wsp.msg.debounce[id] = Gun.time.is(); - return; - }; - gun.wsp.msg.debounce = gun.wsp.msg.debounce || {}; - gun.wsp.wire = gun.wsp.wire || (function () { - // all streams, technically PATCH but implemented as - // PUT or POST, are forwarded to other trusted peers - // except for the ones that are listed in the message - // as having already been sent to. - // all states, implemented with GET, are replied to the - // source that asked for it. - function tran (req, res) { - if (!req || !res || !req.body || !req.headers) { - return; - } - if (req.url) { - req.url = url.format(req.url); - } - // var msg = req.body; - gun.on('in', req.body); - // // AUTH for non-replies. - // if(gun.wsp.msg(msg['#'])){ return } - // gun.wsp.on('network', Gun.obj.copy(req)); - // if(msg['@']){ return } // no need to process. - // if(msg['$'] && msg['$']['#']){ return tran.get(req, res) } - // //if(Gun.is.lex(msg['$'])){ return tran.get(req, res) } - // else { return tran.put(req, res) } - // cb({body: {hello: 'world'}}); - // // TODO: BUG! server put should push. - } - tran.get = function (req, cb) { - var body = req.body; - var lex = body.$; - var reply = { - headers: { 'Content-Type': tran.json }, - }; - - var graph = gun.back(Infinity)._.graph; - var node = graph[lex['#']]; - var result = Gun.graph.ify(node); - - if (node) { - cb({ - headers: reply.headers, - body: { - '#': gun.wsp.msg(), - '@': body['#'], - '$': result, - }, - }); - - return; - } - - gun.on('out', { - gun: gun, - get: lex, - req: 1, - '#': body['#'] || Gun.on.ask(function (at, ev) { - ev.off(); - var graph = at.put; - return cb({ - headers: reply.headers, - body: { - '#': gun.wsp.msg(), - '@': body['#'], - '$': graph, - '!': at.err, - }, - }); - }), - }); - }; - - tran.put = function (req, cb) { - // NOTE: It is highly recommended you do your own PUT/POSTs - // through your own API that then saves to gun manually. - // This will give you much more fine-grain control over - // security, transactions, and what not. - var body = req.body; - var graph = body.$; - var reply = { - headers: { 'Content-Type': tran.json }, - }; - - gun.on('out', { - gun: gun, - put: graph, - '#': Gun.on.ask(function (ack, ev) { - ev.off(); - return cb({ - headers: reply.headers, - body: { - '#': gun.wsp.msg(), - '@': body['#'], - '$': ack, - '!': ack.err, - }, - }); - }), - }); - }; - - tran.json = 'application/json'; - return tran; - }()); - - if (opt.server) { - wsp(opt.server); - } -}); diff --git a/lib/wsp/ws.js b/lib/wsp/ws.js deleted file mode 100644 index 57916afd..00000000 --- a/lib/wsp/ws.js +++ /dev/null @@ -1,40 +0,0 @@ -var Gun = require('../../gun') -, url = require('url'); -module.exports = function(wss, server, opt){ - wss.on('connection', function(ws){ - var req = {}; - ws.upgradeReq = ws.upgradeReq || {}; - req.url = url.parse(ws.upgradeReq.url||''); - req.method = (ws.upgradeReq.method||'').toLowerCase(); - req.headers = ws.upgradeReq.headers || {}; - //Gun.log("wsReq", req); - ws.on('message', function(msg){ - msg = Gun.obj.ify(msg); - msg.url = msg.url || {}; - msg.url.pathname = (req.url.pathname||'') + (msg.url.pathname||''); - Gun.obj.map(req.url, function(val, i){ - msg.url[i] = msg.url[i] || val; // reattach url - }); - msg.method = msg.method || msg.body? 'put' : 'get'; - msg.headers = msg.headers || {}; - Gun.obj.map(opt.headers || req.headers, function(val, i){ - msg.headers[i] = msg.headers[i]; // reattach headers - }); - server.call(ws, msg, function(reply){ - if(!ws || !ws.send || !ws._socket || !ws._socket.writable){ return } - reply = reply || {}; - if(msg && msg.headers && msg.headers['ws-rid']){ - (reply.headers = reply.headers || {})['ws-rid'] = msg.headers['ws-rid']; - } - try{ws.send(Gun.text.ify(reply)); - }catch(e){} // juuuust in case. - }); - }); - ws.off = function(m){ - //Gun.log("ws.off", m); - ws.send = null; - } - ws.on('close', ws.off); - ws.on('error', ws.off); - }); -} \ No newline at end of file diff --git a/nts.js b/nts.js index e8389f69..c0ee6b5b 100644 --- a/nts.js +++ b/nts.js @@ -2,6 +2,7 @@ // NOTE: While the algorithm is P2P, // the current implementation is one sided, // only browsers self-modify, servers do not. + // Need to fix this! Since WebRTC is now working. var env; if(typeof global !== "undefined"){ env = global } if(typeof window !== "undefined"){ var Gun = (env = window).Gun } @@ -13,7 +14,7 @@ this.to.next(ctx); if(ctx.once){ return } ctx.on('in', function(at){ - if(!at.NTS){ + if(!at.nts && !at.NTS){ return this.to.next(at); } if(at['@']){ @@ -23,21 +24,21 @@ if(env.window){ return this.to.next(at); } - this.to.next({'@': at['#'], NTS: Gun.time.is()}); + this.to.next({'@': at['#'], nts: Gun.time.is()}); }); var ask = {}, noop = function(){}; if(!env.window){ return } Gun.state.drift = Gun.state.drift || 0; setTimeout(function ping(){ - var NTS = {}, ack = Gun.text.random(), msg = {'#': ack, NTS: true, gun: ctx.gun}; + var NTS = {}, ack = Gun.text.random(), msg = {'#': ack, nts: true}; NTS.start = Gun.state(); ask[ack] = function(at){ NTS.end = Gun.state(); Gun.obj.del(ask, ack); NTS.latency = (NTS.end - NTS.start)/2; - if(!at.NTS){ return } - NTS.calc = NTS.latency + at.NTS; + if(!at.nts && !at.NTS){ return } + NTS.calc = NTS.latency + (at.NTS || at.nts); Gun.state.drift -= (NTS.end - NTS.calc)/2; setTimeout(ping, 1000); } @@ -45,4 +46,4 @@ }, 1); }); // test by opening up examples/game/nts.html on devices that aren't NTP synced. -}()); +}()); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 1977ed20..bd40b100 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,100 +1,25 @@ { "name": "gun", - "version": "0.9.991", + "version": "0.2019.416", "lockfileVersion": 1, "requires": true, "dependencies": { - "@std/esm": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/@std/esm/-/esm-0.8.3.tgz", - "integrity": "sha512-JZigVxIuy2mCkBZWxwS3Wu9eL0lJzR176Rmzb6hLjGIg3yBVBEK4XhEiFX6k5lXDY+e69XAvEVbp59PfUpfpBA==", - "dev": true - }, - "@trust/keyto": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@trust/keyto/-/keyto-0.3.1.tgz", - "integrity": "sha1-Q96AKTD4JvxZj/73VtqBR42GEaM=", - "dev": true, - "requires": { - "asn1.js": "4.9.2", - "base64url": "2.0.0", - "elliptic": "6.4.0" - } - }, - "@trust/webcrypto": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@trust/webcrypto/-/webcrypto-0.7.1.tgz", - "integrity": "sha512-aix+LOG/3Ku3MzClfVxVH88QbSdIL1HcBQ+gjXL/VnX05uyORf28CaQZOvsoEcCzGnWIVBUNwE2gxLBapWANWw==", - "dev": true, - "requires": { - "@trust/keyto": "0.3.1", - "base64url": "2.0.0", - "node-rsa": "0.4.2", - "text-encoding": "0.6.4" - } - }, - "accept": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/accept/-/accept-3.0.2.tgz", - "integrity": "sha512-bghLXFkCOsC1Y2TZ51etWfKDs6q249SAoHTZVfzWWdlZxoij+mgkj9AmUJWQpDY48TfnrTDIe43Xem4zdMe7mQ==", - "dev": true, - "requires": { - "boom": "7.1.1", - "hoek": "5.0.2" - }, - "dependencies": { - "boom": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-7.1.1.tgz", - "integrity": "sha512-qwEARHTliqgEQiVkzKkkbLt3q0vRPIW60VRZ8zRnbjsm7INkPe9NxfAYDDYLZOdhxyUHa1gIe639Cx7t6RH/4A==", - "dev": true, - "requires": { - "hoek": "5.0.2" - } - }, - "hoek": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.2.tgz", - "integrity": "sha512-NA10UYP9ufCtY2qYGkZktcQXwVyYK4zK0gkaFSB96xhtlo6V8tKXdQgx8eHolQTRemaW0uLn8BhjhwqrOU+QLQ==", - "dev": true - } - } - }, "accepts": { - "version": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", - "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", "dev": true, "requires": { - "mime-types": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.14.tgz", - "negotiator": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz" + "mime-types": "~2.1.18", + "negotiator": "0.6.1" } }, "after": { - "version": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", "dev": true }, - "ammo": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ammo/-/ammo-3.0.0.tgz", - "integrity": "sha512-6yoz9MXYV9sgCHrwprHWPxBaJ9/roQRfXzS//4JCNgKfPYcghFNwJQKBt6vWOoSGGRHsP6qsLJ+xtKStKJWdLQ==", - "dev": true, - "requires": { - "boom": "6.0.0", - "hoek": "5.0.2" - }, - "dependencies": { - "boom": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-6.0.0.tgz", - "integrity": "sha512-LYLa8BmiiOWjvxTMVh73lcZzd2E5yczrKvxAny1UuzO2tkarLrw4tdp3rdfmus3+YfKcZP0vRSM3Obh+fGK6eA==", - "dev": true, - "requires": { - "hoek": "5.0.2" - } - } - } - }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -102,41 +27,25 @@ "dev": true }, "arraybuffer.slice": { - "version": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz", + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz", "integrity": "sha1-8zshWfBTKj8xB6JywMz70a0peco=", "dev": true }, - "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", - "dev": true - }, - "asn1.js": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.2.tgz", - "integrity": "sha512-b/OsSjvWEo8Pi8H0zsDd2P6Uqo2TK2pH8gNLSJtNLM2Db0v2QaAZ0pBQJXVjAn4gBuugeVDr7s63ZogpUIwWDg==", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0" - } - }, "async-limiter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" }, "aws-sdk": { - "version": "2.153.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.153.0.tgz", - "integrity": "sha1-a6IuhJruFq1wdxihjGS3Vtjx6Bo=", + "version": "2.238.1", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.238.1.tgz", + "integrity": "sha1-o1/ewSLtkV2kkIQOiCgzbaW+Tn8=", "dev": true, "requires": { "buffer": "4.9.1", - "crypto-browserify": "1.0.9", "events": "1.1.1", + "ieee754": "1.1.8", "jmespath": "0.15.0", "querystring": "0.2.0", "sax": "1.2.1", @@ -144,82 +53,57 @@ "uuid": "3.1.0", "xml2js": "0.4.17", "xmlbuilder": "4.2.1" - }, - "dependencies": { - "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", - "dev": true, - "requires": { - "base64-js": "1.2.1", - "ieee754": "1.1.8", - "isarray": "1.0.0" - } - } } }, - "b64": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/b64/-/b64-4.0.0.tgz", - "integrity": "sha512-EhmUQodKB0sdzPPrbIWbGqA5cQeTWxYrAgNeeT1rLZWtD3tbNTnphz8J4vkXI3cPgBNlXBjzEbzDzq0Nwi4f9A==", - "dev": true - }, "backo2": { - "version": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", "dev": true }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, "base64-arraybuffer": { - "version": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", "dev": true }, "base64-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", - "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", "dev": true }, "base64id": { - "version": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=", "dev": true }, - "base64url": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz", - "integrity": "sha1-6sFuA+oUOO/5Qj1puqNiYu0fcLs=", - "dev": true - }, "better-assert": { - "version": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", "dev": true, "requires": { - "callsite": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz" + "callsite": "1.0.0" } }, - "big-time": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/big-time/-/big-time-2.0.0.tgz", - "integrity": "sha512-OXsmBxlRLwUc65MLta2EOyMTLcjZQkxHkJ81lVPeyVqZag8zhUfKRYIbF3E/IW/LWR8kf8a1GlRYkBXKVGqJOw==", - "dev": true - }, "blob": { - "version": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz", + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz", "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE=", "dev": true }, "bluebird": { - "version": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", - "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=", - "dev": true - }, - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==", "dev": true }, "body-parser": { @@ -229,264 +113,86 @@ "dev": true, "requires": { "bytes": "3.0.0", - "content-type": "1.0.4", + "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "1.1.1", - "http-errors": "1.6.2", + "depd": "~1.1.1", + "http-errors": "~1.6.2", "iconv-lite": "0.4.19", - "on-finished": "2.3.0", + "on-finished": "~2.3.0", "qs": "6.5.1", "raw-body": "2.3.2", - "type-is": "1.6.15" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } + "type-is": "~1.6.15" } }, - "boom": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-7.1.1.tgz", - "integrity": "sha512-qwEARHTliqgEQiVkzKkkbLt3q0vRPIW60VRZ8zRnbjsm7INkPe9NxfAYDDYLZOdhxyUHa1gIe639Cx7t6RH/4A==", + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { - "hoek": "5.0.2" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "bounce": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/bounce/-/bounce-1.2.0.tgz", - "integrity": "sha512-8syCGe8B2/WC53118/F/tFy5aW00j+eaGPXmAUP7iBhxc+EBZZxS1vKelWyBCH6IqojgS2t1gF0glH30qAJKEw==", - "dev": true, - "requires": { - "boom": "7.1.1", - "hoek": "5.0.2" - }, - "dependencies": { - "boom": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-7.1.1.tgz", - "integrity": "sha512-qwEARHTliqgEQiVkzKkkbLt3q0vRPIW60VRZ8zRnbjsm7INkPe9NxfAYDDYLZOdhxyUHa1gIe639Cx7t6RH/4A==", - "dev": true, - "requires": { - "hoek": "5.0.2" - } - }, - "hoek": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.2.tgz", - "integrity": "sha512-NA10UYP9ufCtY2qYGkZktcQXwVyYK4zK0gkaFSB96xhtlo6V8tKXdQgx8eHolQTRemaW0uLn8BhjhwqrOU+QLQ==", - "dev": true - } - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, "browser-stdout": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", - "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, "bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", "dev": true }, - "call": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/call/-/call-5.0.1.tgz", - "integrity": "sha512-ollfFPSshiuYLp7AsrmpkQJ/PxCi6AzV81rCjBwWhyF2QGyUY/vPDMzoh4aUcWyucheRglG2LaS5qkIEfLRh6A==", - "dev": true, - "requires": { - "boom": "7.1.1", - "hoek": "5.0.2" - }, - "dependencies": { - "boom": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-7.1.1.tgz", - "integrity": "sha512-qwEARHTliqgEQiVkzKkkbLt3q0vRPIW60VRZ8zRnbjsm7INkPe9NxfAYDDYLZOdhxyUHa1gIe639Cx7t6RH/4A==", - "dev": true, - "requires": { - "hoek": "5.0.2" - } - }, - "hoek": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.2.tgz", - "integrity": "sha512-NA10UYP9ufCtY2qYGkZktcQXwVyYK4zK0gkaFSB96xhtlo6V8tKXdQgx8eHolQTRemaW0uLn8BhjhwqrOU+QLQ==", - "dev": true - } - } - }, "callsite": { - "version": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", "dev": true }, - "catbox": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/catbox/-/catbox-10.0.2.tgz", - "integrity": "sha512-cTQTQeKMhWHU0lX8CADE3g1koGJu+AlcWFzAjMX/8P+XbkScGYw3tJsQpe2Oh8q68vOQbOLacz9k+6V/F3Z9DA==", - "dev": true, - "requires": { - "boom": "7.1.1", - "bounce": "1.2.0", - "hoek": "5.0.2", - "joi": "13.1.1" - }, - "dependencies": { - "boom": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-7.1.1.tgz", - "integrity": "sha512-qwEARHTliqgEQiVkzKkkbLt3q0vRPIW60VRZ8zRnbjsm7INkPe9NxfAYDDYLZOdhxyUHa1gIe639Cx7t6RH/4A==", - "dev": true, - "requires": { - "hoek": "5.0.2" - } - }, - "hoek": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.2.tgz", - "integrity": "sha512-NA10UYP9ufCtY2qYGkZktcQXwVyYK4zK0gkaFSB96xhtlo6V8tKXdQgx8eHolQTRemaW0uLn8BhjhwqrOU+QLQ==", - "dev": true - }, - "isemail": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.1.0.tgz", - "integrity": "sha512-Ke15MBbbhyIhZzWheiWuRlTO81tTH4RQvrbJFpVzJce8oyVrCVSDdrcw4TcyMsaS/fMGJSbU3lTsqCGDKwrzww==", - "dev": true, - "requires": { - "punycode": "2.1.0" - } - }, - "joi": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/joi/-/joi-13.1.1.tgz", - "integrity": "sha512-Y44bDwIoeCjFDRO18VaMRc0hIdPkLbZaF2VqU7t1tCcno3S3XzsmlYYpOu0Qk6nkzoI5RSao7W57NTvPKxbkcg==", - "dev": true, - "requires": { - "hoek": "5.0.2", - "isemail": "3.1.0", - "topo": "3.0.0" - } - }, - "punycode": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", - "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=", - "dev": true - }, - "topo": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/topo/-/topo-3.0.0.tgz", - "integrity": "sha512-Tlu1fGlR90iCdIPURqPiufqAlCZYzLjHYVVbcFWDMcX7+tK8hdZWAfsMrD/pBul9jqHHwFjNdf1WaxA9vTRRhw==", - "dev": true, - "requires": { - "hoek": "5.0.2" - } - } - } - }, - "catbox-memory": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/catbox-memory/-/catbox-memory-3.1.1.tgz", - "integrity": "sha512-fl6TI/uneeUb9NGClKWZWkpCZQrkPmuVz/Jaqqb15vqW6KGfJ/vMP/ZMp8VgAkyTrrRvFHbFcS67sbU7EkvbhQ==", - "dev": true, - "requires": { - "big-time": "2.0.0", - "boom": "7.1.1", - "hoek": "5.0.2" - }, - "dependencies": { - "boom": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-7.1.1.tgz", - "integrity": "sha512-qwEARHTliqgEQiVkzKkkbLt3q0vRPIW60VRZ8zRnbjsm7INkPe9NxfAYDDYLZOdhxyUHa1gIe639Cx7t6RH/4A==", - "dev": true, - "requires": { - "hoek": "5.0.2" - } - }, - "hoek": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.2.tgz", - "integrity": "sha512-NA10UYP9ufCtY2qYGkZktcQXwVyYK4zK0gkaFSB96xhtlo6V8tKXdQgx8eHolQTRemaW0uLn8BhjhwqrOU+QLQ==", - "dev": true - } - } - }, "commander": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", "dev": true }, "component-bind": { - "version": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=", "dev": true }, "component-emitter": { - "version": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", "dev": true }, "component-inherit": { - "version": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", "dev": true }, "concat-map": { - "version": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "content": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/content/-/content-4.0.3.tgz", - "integrity": "sha512-BrMfT1xXZHaXyPT/sneXc3IQzh8uL15JWV1R5tU0xo4sBGjF7BN+IRi9WoQLSbfNEs7bJ3E69rjxBKg/RL7lOQ==", - "dev": true, - "requires": { - "boom": "7.1.1" - }, - "dependencies": { - "boom": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-7.1.1.tgz", - "integrity": "sha512-qwEARHTliqgEQiVkzKkkbLt3q0vRPIW60VRZ8zRnbjsm7INkPe9NxfAYDDYLZOdhxyUHa1gIe639Cx7t6RH/4A==", - "dev": true, - "requires": { - "hoek": "5.0.2" - } - }, - "hoek": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.2.tgz", - "integrity": "sha512-NA10UYP9ufCtY2qYGkZktcQXwVyYK4zK0gkaFSB96xhtlo6V8tKXdQgx8eHolQTRemaW0uLn8BhjhwqrOU+QLQ==", - "dev": true - } - } - }, "content-disposition": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", @@ -511,56 +217,19 @@ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", "dev": true }, - "core-js": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", - "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=", - "dev": true - }, - "cryptiles": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-4.1.1.tgz", - "integrity": "sha512-YuQUPbcOmaZsdvxJZ25DCA1W+lLIRoPJKBDKin+St1RCYEERSfoe1d25B1MvWNHN3e8SpFSVsqYvEUjp8J9H2w==", - "dev": true, - "requires": { - "boom": "7.1.1" - }, - "dependencies": { - "boom": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-7.1.1.tgz", - "integrity": "sha512-qwEARHTliqgEQiVkzKkkbLt3q0vRPIW60VRZ8zRnbjsm7INkPe9NxfAYDDYLZOdhxyUHa1gIe639Cx7t6RH/4A==", - "dev": true, - "requires": { - "hoek": "5.0.2" - } - }, - "hoek": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.2.tgz", - "integrity": "sha512-NA10UYP9ufCtY2qYGkZktcQXwVyYK4zK0gkaFSB96xhtlo6V8tKXdQgx8eHolQTRemaW0uLn8BhjhwqrOU+QLQ==", - "dev": true - } - } - }, - "crypto-browserify": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-1.0.9.tgz", - "integrity": "sha1-zFRJaF37hesRyYKKzHy4erW7/MA=", - "dev": true - }, "debug": { - "version": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { - "ms": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" + "ms": "2.0.0" } }, "depd": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", - "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", "dev": true }, "destroy": { @@ -570,9 +239,9 @@ "dev": true }, "diff": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", - "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true }, "ee-first": { @@ -581,123 +250,134 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", "dev": true }, - "elliptic": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", - "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "brorand": "1.1.0", - "hash.js": "1.1.3", - "hmac-drbg": "1.0.1", - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0", - "minimalistic-crypto-utils": "1.0.1" - } - }, "encodeurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", - "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", "dev": true }, "engine.io": { - "version": "https://registry.npmjs.org/engine.io/-/engine.io-1.8.3.tgz", - "integrity": "sha1-jef5eJXSDTm4X4ju7nd7K9QrE9Q=", + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-1.8.5.tgz", + "integrity": "sha512-j1DWIcktw4hRwrv6nWx++5nFH2X64x16MAG2P0Lmi5Dvdfi3I+Jhc7JKJIdAmDJa+5aZ/imHV7dWRPy2Cqjh3A==", "dev": true, "requires": { - "accepts": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", - "base64id": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", - "cookie": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "debug": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "engine.io-parser": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-1.3.2.tgz", - "ws": "https://registry.npmjs.org/ws/-/ws-1.1.2.tgz" + "accepts": "1.3.3", + "base64id": "1.0.0", + "cookie": "0.3.1", + "debug": "2.3.3", + "engine.io-parser": "1.3.2", + "ws": "~1.1.5" }, "dependencies": { - "cookie": { - "version": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", - "dev": true + "accepts": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", + "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", + "dev": true, + "requires": { + "mime-types": "~2.1.11", + "negotiator": "0.6.1" + } }, "debug": { - "version": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", "dev": true, "requires": { - "ms": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz" + "ms": "0.7.2" } }, "ms": { - "version": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", "dev": true }, + "ultron": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", + "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=", + "dev": true + }, "ws": { - "version": "https://registry.npmjs.org/ws/-/ws-1.1.2.tgz", - "integrity": "sha1-iiRPoFJAHgjJiGz0SoUYnh/UBn8=", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.5.tgz", + "integrity": "sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w==", "dev": true, "requires": { - "options": "https://registry.npmjs.org/options/-/options-0.0.6.tgz", - "ultron": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz" + "options": ">=0.0.5", + "ultron": "1.0.x" } } } }, "engine.io-client": { - "version": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-1.8.3.tgz", - "integrity": "sha1-F5jtk0USRkU9TG9jXXogH+lA1as=", + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-1.8.5.tgz", + "integrity": "sha512-AYTgHyeVUPitsseqjoedjhYJapNVoSPShbZ+tEUX9/73jgZ/Z3sUlJf9oYgdEBBdVhupUpUqSxH0kBCXlQnmZg==", "dev": true, "requires": { - "component-emitter": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "component-inherit": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", - "debug": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "engine.io-parser": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-1.3.2.tgz", - "has-cors": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", - "indexof": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "parsejson": "https://registry.npmjs.org/parsejson/-/parsejson-0.0.3.tgz", - "parseqs": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", - "parseuri": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", - "ws": "https://registry.npmjs.org/ws/-/ws-1.1.2.tgz", - "xmlhttprequest-ssl": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz", - "yeast": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz" + "component-emitter": "1.2.1", + "component-inherit": "0.0.3", + "debug": "2.3.3", + "engine.io-parser": "1.3.2", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "parsejson": "0.0.3", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "ws": "~1.1.5", + "xmlhttprequest-ssl": "1.5.3", + "yeast": "0.1.2" }, "dependencies": { "debug": { - "version": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", "dev": true, "requires": { - "ms": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz" + "ms": "0.7.2" } }, "ms": { - "version": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", "dev": true }, + "ultron": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", + "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=", + "dev": true + }, "ws": { - "version": "https://registry.npmjs.org/ws/-/ws-1.1.2.tgz", - "integrity": "sha1-iiRPoFJAHgjJiGz0SoUYnh/UBn8=", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.5.tgz", + "integrity": "sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w==", "dev": true, "requires": { - "options": "https://registry.npmjs.org/options/-/options-0.0.6.tgz", - "ultron": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz" + "options": ">=0.0.5", + "ultron": "1.0.x" } } } }, "engine.io-parser": { - "version": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-1.3.2.tgz", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-1.3.2.tgz", "integrity": "sha1-k3sHnwAH0Ik+xW1GyyILjLQ1Igo=", "dev": true, "requires": { - "after": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", - "arraybuffer.slice": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz", - "base64-arraybuffer": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", - "blob": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz", - "has-binary": "https://registry.npmjs.org/has-binary/-/has-binary-0.1.7.tgz", - "wtf-8": "https://registry.npmjs.org/wtf-8/-/wtf-8-1.0.0.tgz" + "after": "0.8.2", + "arraybuffer.slice": "0.0.6", + "base64-arraybuffer": "0.1.5", + "blob": "0.0.4", + "has-binary": "0.1.7", + "wtf-8": "1.0.0" } }, "escape-html": { @@ -725,83 +405,43 @@ "dev": true }, "express": { - "version": "4.16.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz", - "integrity": "sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=", + "version": "4.16.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", + "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", "dev": true, "requires": { - "accepts": "1.3.4", + "accepts": "~1.3.5", "array-flatten": "1.1.1", "body-parser": "1.18.2", "content-disposition": "0.5.2", - "content-type": "1.0.4", + "content-type": "~1.0.4", "cookie": "0.3.1", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "1.1.1", - "encodeurl": "1.0.1", - "escape-html": "1.0.3", - "etag": "1.8.1", - "finalhandler": "1.1.0", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", "fresh": "0.5.2", "merge-descriptors": "1.0.1", - "methods": "1.1.2", - "on-finished": "2.3.0", - "parseurl": "1.3.2", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", "path-to-regexp": "0.1.7", - "proxy-addr": "2.0.2", + "proxy-addr": "~2.0.3", "qs": "6.5.1", - "range-parser": "1.2.0", + "range-parser": "~1.2.0", "safe-buffer": "5.1.1", - "send": "0.16.1", - "serve-static": "1.13.1", + "send": "0.16.2", + "serve-static": "1.13.2", "setprototypeof": "1.1.0", - "statuses": "1.3.1", - "type-is": "1.6.15", + "statuses": "~1.4.0", + "type-is": "~1.6.16", "utils-merge": "1.0.1", - "vary": "1.1.2" + "vary": "~1.1.2" }, "dependencies": { - "accepts": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", - "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", - "dev": true, - "requires": { - "mime-types": "2.1.17", - "negotiator": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "mime-db": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", - "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=", - "dev": true - }, - "mime-types": { - "version": "2.1.17", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", - "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", - "dev": true, - "requires": { - "mime-db": "1.30.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", @@ -810,47 +450,19 @@ } } }, - "fake-indexeddb": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/fake-indexeddb/-/fake-indexeddb-2.0.3.tgz", - "integrity": "sha1-YxmwzBaxxjQ/Ri1c+VKRyxGktYs=", - "dev": true, - "requires": { - "core-js": "2.5.1", - "realistic-structured-clone": "1.0.1", - "setimmediate": "1.0.5" - } - }, "finalhandler": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", - "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", "dev": true, "requires": { "debug": "2.6.9", - "encodeurl": "1.0.1", - "escape-html": "1.0.3", - "on-finished": "2.3.0", - "parseurl": "1.3.2", - "statuses": "1.3.1", - "unpipe": "1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" } }, "forwarded": { @@ -871,256 +483,71 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - }, - "growl": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", - "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", - "dev": true - }, - "hapi": { - "version": "17.2.0", - "resolved": "https://registry.npmjs.org/hapi/-/hapi-17.2.0.tgz", - "integrity": "sha512-zw2tqNimjT+qglgUNGNpeweHJ5To1xUcJcfGKsG5dWiTzwkEZtmtHJ8mBIvxuuZ3Buu4xkAGj0yWrrR95FVqQQ==", + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "accept": "3.0.2", - "ammo": "3.0.0", - "boom": "7.1.1", - "bounce": "1.2.0", - "call": "5.0.1", - "catbox": "10.0.2", - "catbox-memory": "3.1.1", - "heavy": "6.1.0", - "hoek": "5.0.2", - "joi": "13.1.1", - "mimos": "4.0.0", - "podium": "3.1.2", - "shot": "4.0.4", - "statehood": "6.0.5", - "subtext": "6.0.7", - "teamwork": "3.0.1", - "topo": "3.0.0" - }, - "dependencies": { - "ammo": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ammo/-/ammo-3.0.0.tgz", - "integrity": "sha512-6yoz9MXYV9sgCHrwprHWPxBaJ9/roQRfXzS//4JCNgKfPYcghFNwJQKBt6vWOoSGGRHsP6qsLJ+xtKStKJWdLQ==", - "dev": true, - "requires": { - "boom": "6.0.0", - "hoek": "5.0.2" - }, - "dependencies": { - "boom": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-6.0.0.tgz", - "integrity": "sha512-LYLa8BmiiOWjvxTMVh73lcZzd2E5yczrKvxAny1UuzO2tkarLrw4tdp3rdfmus3+YfKcZP0vRSM3Obh+fGK6eA==", - "dev": true, - "requires": { - "hoek": "5.0.2" - } - } - } - }, - "boom": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-7.1.1.tgz", - "integrity": "sha512-qwEARHTliqgEQiVkzKkkbLt3q0vRPIW60VRZ8zRnbjsm7INkPe9NxfAYDDYLZOdhxyUHa1gIe639Cx7t6RH/4A==", - "dev": true, - "requires": { - "hoek": "5.0.2" - } - }, - "hoek": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.2.tgz", - "integrity": "sha512-NA10UYP9ufCtY2qYGkZktcQXwVyYK4zK0gkaFSB96xhtlo6V8tKXdQgx8eHolQTRemaW0uLn8BhjhwqrOU+QLQ==", - "dev": true - }, - "isemail": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.1.0.tgz", - "integrity": "sha512-Ke15MBbbhyIhZzWheiWuRlTO81tTH4RQvrbJFpVzJce8oyVrCVSDdrcw4TcyMsaS/fMGJSbU3lTsqCGDKwrzww==", - "dev": true, - "requires": { - "punycode": "2.1.0" - } - }, - "joi": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/joi/-/joi-13.1.1.tgz", - "integrity": "sha512-Y44bDwIoeCjFDRO18VaMRc0hIdPkLbZaF2VqU7t1tCcno3S3XzsmlYYpOu0Qk6nkzoI5RSao7W57NTvPKxbkcg==", - "dev": true, - "requires": { - "hoek": "5.0.2", - "isemail": "3.1.0", - "topo": "3.0.0" - } - }, - "punycode": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", - "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=", - "dev": true - }, - "topo": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/topo/-/topo-3.0.0.tgz", - "integrity": "sha512-Tlu1fGlR90iCdIPURqPiufqAlCZYzLjHYVVbcFWDMcX7+tK8hdZWAfsMrD/pBul9jqHHwFjNdf1WaxA9vTRRhw==", - "dev": true, - "requires": { - "hoek": "5.0.2" - } - } + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, "has-binary": { - "version": "https://registry.npmjs.org/has-binary/-/has-binary-0.1.7.tgz", + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/has-binary/-/has-binary-0.1.7.tgz", "integrity": "sha1-aOYesWIQyVRaClzOBqhzkS/h5ow=", "dev": true, "requires": { - "isarray": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + "isarray": "0.0.1" }, "dependencies": { "isarray": { - "version": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", "dev": true } } }, "has-cors": { - "version": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", "dev": true }, "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, - "hash.js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", - "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", - "dev": true, - "requires": { - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0" - } - }, "he": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", "dev": true }, - "heavy": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/heavy/-/heavy-6.1.0.tgz", - "integrity": "sha512-TKS9DC9NOTGulHQI31Lx+bmeWmNOstbJbGMiN3pX6bF+Zc2GKSpbbym4oasNnB6yPGkqJ9TQXXYDGohqNSJRxA==", - "dev": true, - "requires": { - "boom": "7.1.1", - "hoek": "5.0.2", - "joi": "13.1.1" - }, - "dependencies": { - "boom": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-7.1.1.tgz", - "integrity": "sha512-qwEARHTliqgEQiVkzKkkbLt3q0vRPIW60VRZ8zRnbjsm7INkPe9NxfAYDDYLZOdhxyUHa1gIe639Cx7t6RH/4A==", - "dev": true, - "requires": { - "hoek": "5.0.2" - } - }, - "hoek": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.2.tgz", - "integrity": "sha512-NA10UYP9ufCtY2qYGkZktcQXwVyYK4zK0gkaFSB96xhtlo6V8tKXdQgx8eHolQTRemaW0uLn8BhjhwqrOU+QLQ==", - "dev": true - }, - "isemail": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.1.0.tgz", - "integrity": "sha512-Ke15MBbbhyIhZzWheiWuRlTO81tTH4RQvrbJFpVzJce8oyVrCVSDdrcw4TcyMsaS/fMGJSbU3lTsqCGDKwrzww==", - "dev": true, - "requires": { - "punycode": "2.1.0" - } - }, - "joi": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/joi/-/joi-13.1.1.tgz", - "integrity": "sha512-Y44bDwIoeCjFDRO18VaMRc0hIdPkLbZaF2VqU7t1tCcno3S3XzsmlYYpOu0Qk6nkzoI5RSao7W57NTvPKxbkcg==", - "dev": true, - "requires": { - "hoek": "5.0.2", - "isemail": "3.1.0", - "topo": "3.0.0" - } - }, - "punycode": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", - "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=", - "dev": true - }, - "topo": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/topo/-/topo-3.0.0.tgz", - "integrity": "sha512-Tlu1fGlR90iCdIPURqPiufqAlCZYzLjHYVVbcFWDMcX7+tK8hdZWAfsMrD/pBul9jqHHwFjNdf1WaxA9vTRRhw==", - "dev": true, - "requires": { - "hoek": "5.0.2" - } - } - } - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, - "requires": { - "hash.js": "1.1.3", - "minimalistic-assert": "1.0.0", - "minimalistic-crypto-utils": "1.0.1" - } - }, - "hoek": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.2.tgz", - "integrity": "sha512-NA10UYP9ufCtY2qYGkZktcQXwVyYK4zK0gkaFSB96xhtlo6V8tKXdQgx8eHolQTRemaW0uLn8BhjhwqrOU+QLQ==", - "dev": true - }, "http-errors": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", - "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { - "depd": "1.1.1", + "depd": "~1.1.2", "inherits": "2.0.3", - "setprototypeof": "1.0.3", - "statuses": "1.3.1" - }, - "dependencies": { - "setprototypeof": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", - "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", - "dev": true - } + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" } }, "iconv-lite": { @@ -1135,38 +562,20 @@ "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=", "dev": true }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, "indexof": { - "version": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", "dev": true }, - "inert": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/inert/-/inert-5.1.0.tgz", - "integrity": "sha512-5rJZbezGEkBN4QrP/HEEwsQ0N+7YzqDZrvBZrE7B0CrNY6I4XKI434aL3UNLCmbI4HzPGQs7Ae/4h1tiTMJ6Wg==", - "dev": true, - "requires": { - "ammo": "3.0.0", - "boom": "7.1.1", - "bounce": "1.2.0", - "hoek": "5.0.2", - "joi": "13.1.1", - "lru-cache": "4.1.1" - } - }, "inflight": { - "version": "https://registry.npmjs.org/inflight/-/inflight-1.0.5.tgz", - "integrity": "sha1-2zIEzVqd4ubNiQuFxuL2a89PYgo=", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "once": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", - "wrappy": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { @@ -1182,41 +591,14 @@ "dev": true }, "ipaddr.js": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz", - "integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A=", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz", + "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs=", "dev": true }, - "iron": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/iron/-/iron-5.0.4.tgz", - "integrity": "sha512-7iQ5/xFMIYaNt9g2oiNiWdhrOTdRUMFaWENUd0KghxwPUhrIH8DUY8FEyLNTTzf75jaII+jMexLdY/2HfV61RQ==", - "dev": true, - "requires": { - "boom": "7.1.1", - "cryptiles": "4.1.1", - "hoek": "5.0.2" - }, - "dependencies": { - "boom": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-7.1.1.tgz", - "integrity": "sha512-qwEARHTliqgEQiVkzKkkbLt3q0vRPIW60VRZ8zRnbjsm7INkPe9NxfAYDDYLZOdhxyUHa1gIe639Cx7t6RH/4A==", - "dev": true, - "requires": { - "hoek": "5.0.2" - } - }, - "hoek": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.2.tgz", - "integrity": "sha512-NA10UYP9ufCtY2qYGkZktcQXwVyYK4zK0gkaFSB96xhtlo6V8tKXdQgx8eHolQTRemaW0uLn8BhjhwqrOU+QLQ==", - "dev": true - } - } - }, "is-promise": { - "version": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "dev": true }, @@ -1226,100 +608,24 @@ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, - "isemail": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.1.0.tgz", - "integrity": "sha512-Ke15MBbbhyIhZzWheiWuRlTO81tTH4RQvrbJFpVzJce8oyVrCVSDdrcw4TcyMsaS/fMGJSbU3lTsqCGDKwrzww==", - "dev": true, - "requires": { - "punycode": "2.1.0" - }, - "dependencies": { - "punycode": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", - "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=", - "dev": true - } - } - }, "jmespath": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=", "dev": true }, - "joi": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/joi/-/joi-13.1.1.tgz", - "integrity": "sha512-Y44bDwIoeCjFDRO18VaMRc0hIdPkLbZaF2VqU7t1tCcno3S3XzsmlYYpOu0Qk6nkzoI5RSao7W57NTvPKxbkcg==", - "dev": true, - "requires": { - "hoek": "5.0.2", - "isemail": "3.1.0", - "topo": "3.0.0" - } - }, "json3": { - "version": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", "dev": true }, "lodash": { - "version": "4.17.4", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", "dev": true }, - "lodash._basefor": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash._basefor/-/lodash._basefor-3.0.3.tgz", - "integrity": "sha1-dVC06SGO8J+tJDQ7YSAhx5tMIMI=", - "dev": true - }, - "lodash.isarguments": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", - "dev": true - }, - "lodash.isarray": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", - "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", - "dev": true - }, - "lodash.isplainobject": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-3.2.0.tgz", - "integrity": "sha1-moI4rhayAEMpYM1zRlEtASP79MU=", - "dev": true, - "requires": { - "lodash._basefor": "3.0.3", - "lodash.isarguments": "3.1.0", - "lodash.keysin": "3.0.8" - } - }, - "lodash.keysin": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/lodash.keysin/-/lodash.keysin-3.0.8.tgz", - "integrity": "sha1-IsRJPrvtsUJ5YqVLRFssinZ/tH8=", - "dev": true, - "requires": { - "lodash.isarguments": "3.1.0", - "lodash.isarray": "3.0.4" - } - }, - "lru-cache": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", - "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", - "dev": true, - "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" - } - }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -1345,103 +651,61 @@ "dev": true }, "mime-db": { - "version": "https://registry.npmjs.org/mime-db/-/mime-db-1.26.0.tgz", - "integrity": "sha1-6v/NDk/Gk1z4E02iRuLmw1MFrf8=", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", "dev": true }, "mime-types": { - "version": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.14.tgz", - "integrity": "sha1-9+99l1g/yvO30oK2+LVnnaselO4=", + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", "dev": true, "requires": { - "mime-db": "https://registry.npmjs.org/mime-db/-/mime-db-1.26.0.tgz" + "mime-db": "~1.33.0" } }, - "mimos": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimos/-/mimos-4.0.0.tgz", - "integrity": "sha512-JvlvRLqGIlk+AYypWrbrDmhsM+6JVx/xBM5S3AMwTBz1trPCEoPN/swO2L4Wu653fL7oJdgk8DMQyG/Gq3JkZg==", + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "hoek": "5.0.2", - "mime-db": "1.32.0" - }, - "dependencies": { - "hoek": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.2.tgz", - "integrity": "sha512-NA10UYP9ufCtY2qYGkZktcQXwVyYK4zK0gkaFSB96xhtlo6V8tKXdQgx8eHolQTRemaW0uLn8BhjhwqrOU+QLQ==", - "dev": true - }, - "mime-db": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.32.0.tgz", - "integrity": "sha512-+ZWo/xZN40Tt6S+HyakUxnSOgff+JEdaneLWIm0Z6LmpCn5DMcZntLyUY5c/rTDog28LhXLKOUZKoTxTCAdBVw==", - "dev": true - } + "brace-expansion": "^1.1.7" } }, - "minimalistic-assert": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", - "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=", - "dev": true - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true - }, "minimist": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, "requires": { "minimist": "0.0.8" } }, "mocha": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.0.1.tgz", - "integrity": "sha512-evDmhkoA+cBNiQQQdSKZa2b9+W2mpLoj50367lhy+Klnx9OV8XlCIhigUnn1gaTFLQCa0kdNhEGDr0hCXOQFDw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", + "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", "dev": true, "requires": { - "browser-stdout": "1.3.0", - "commander": "2.11.0", + "browser-stdout": "1.3.1", + "commander": "2.15.1", "debug": "3.1.0", - "diff": "3.3.1", + "diff": "3.5.0", "escape-string-regexp": "1.0.5", "glob": "7.1.2", - "growl": "1.10.3", + "growl": "1.10.5", "he": "1.1.1", + "minimatch": "3.0.4", "mkdirp": "0.5.1", - "supports-color": "4.4.0" + "supports-color": "5.4.0" }, "dependencies": { - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "brace-expansion": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", - "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", - "dev": true, - "requires": { - "balanced-match": "1.0.0", - "concat-map": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" - } - }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", @@ -1450,109 +714,48 @@ "requires": { "ms": "2.0.0" } - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "https://registry.npmjs.org/inflight/-/inflight-1.0.5.tgz", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", - "path-is-absolute": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "1.1.8" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true } } }, "ms": { - "version": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, "nan": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", - "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", - "dev": true + "version": "2.13.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", + "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==", + "optional": true }, "negotiator": { - "version": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", "dev": true }, - "nigel": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/nigel/-/nigel-3.0.0.tgz", - "integrity": "sha512-ufFVFCe1zS/pfIQzQNa5uJxB8v8IcVTUn1zyPvQwb4CQGRxxBfdQPSXpEnI6ZzIwbV5L+GuAoRhYgcVSvTO7fA==", - "dev": true, - "requires": { - "hoek": "5.0.2", - "vise": "3.0.0" - }, - "dependencies": { - "hoek": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.2.tgz", - "integrity": "sha512-NA10UYP9ufCtY2qYGkZktcQXwVyYK4zK0gkaFSB96xhtlo6V8tKXdQgx8eHolQTRemaW0uLn8BhjhwqrOU+QLQ==", - "dev": true - } - } - }, - "node-localstorage": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/node-localstorage/-/node-localstorage-1.3.0.tgz", - "integrity": "sha1-LkNqro3Mms6XtDxlwWwNV3vgpVw=", - "dev": true, - "requires": { - "write-file-atomic": "1.3.4" - } - }, - "node-rsa": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/node-rsa/-/node-rsa-0.4.2.tgz", - "integrity": "sha1-1jkXKewWqDDtWjgEKzFX0tXXJTA=", - "dev": true, - "requires": { - "asn1": "0.2.3" - } - }, "node-webcrypto-ossl": { - "version": "1.0.36", - "resolved": "https://registry.npmjs.org/node-webcrypto-ossl/-/node-webcrypto-ossl-1.0.36.tgz", - "integrity": "sha512-/kmr+xk5yGkx6h7kx9Z8P0zWlJFvYqxPyeW5wPj/KcSeXEA1w9yzD1kdpzn9/HXpuHyWTQ9WSQ9zfbL8cFB1gA==", - "dev": true, + "version": "1.0.43", + "resolved": "https://registry.npmjs.org/node-webcrypto-ossl/-/node-webcrypto-ossl-1.0.43.tgz", + "integrity": "sha512-AavMGbUB2MaTzli0+swjSAr4aLqf9M5jHIrs5VP9EUDXyEaIOyyio3H1z3pZCxKpaDD9coKWD3bWAiaIBuN8wg==", + "optional": true, "requires": { - "mkdirp": "0.5.1", - "nan": "2.10.0", - "tslib": "1.9.0", - "webcrypto-core": "0.1.21" + "mkdirp": "^0.5.1", + "nan": "^2.12.1", + "tslib": "^1.9.3", + "webcrypto-core": "^0.1.26" } }, "object-assign": { - "version": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", "integrity": "sha1-ejs9DpgGPUP0wD8uiubNUahog6A=", "dev": true }, "object-component": { - "version": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", "dev": true }, @@ -1566,59 +769,49 @@ } }, "once": { - "version": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", - "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + "wrappy": "1" } }, "options": { - "version": "https://registry.npmjs.org/options/-/options-0.0.6.tgz", + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz", "integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8=", "dev": true }, "panic-client": { - "version": "https://registry.npmjs.org/panic-client/-/panic-client-1.0.1.tgz", - "integrity": "sha1-A3mfzPS/TNtcRex2i2OwN15m5uk=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/panic-client/-/panic-client-1.0.2.tgz", + "integrity": "sha1-4+yr3DQn6vELowviJZAaehGasXM=", "dev": true, "requires": { - "bluebird": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", - "is-promise": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "platform": "https://registry.npmjs.org/platform/-/platform-1.3.3.tgz", - "socket.io-client": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-1.7.3.tgz" + "bluebird": "^3.4.6", + "is-promise": "^2.1.0", + "platform": "1.3.1", + "socket.io-client": "^1.4.5" + } + }, + "panic-manager": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/panic-manager/-/panic-manager-1.2.0.tgz", + "integrity": "sha1-0tvHdgIAMsWwEw0QW/vqewZnMh4=", + "dev": true, + "requires": { + "isarray": "^2.0.0", + "panic-client": "^1.0.0", + "socket.io": "^1.4.8", + "socket.io-client": "^1.4.8" }, "dependencies": { - "debug": { - "version": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", - "dev": true, - "requires": { - "ms": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz" - } - }, - "ms": { - "version": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", + "isarray": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.4.tgz", + "integrity": "sha512-GMxXOiUirWg1xTKRipM0Ek07rX+ubx4nNVElTJdNLYmNO/2YrDkgJGw9CljXn+r4EWiDQg/8lsRdHyg2PJuUaA==", "dev": true - }, - "socket.io-client": { - "version": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-1.7.3.tgz", - "integrity": "sha1-sw6GqhDV7zVGYBwJzeR2Xjgdo3c=", - "dev": true, - "requires": { - "backo2": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "component-bind": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", - "component-emitter": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "debug": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "engine.io-client": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-1.8.3.tgz", - "has-binary": "https://registry.npmjs.org/has-binary/-/has-binary-0.1.7.tgz", - "indexof": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "object-component": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", - "parseuri": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", - "socket.io-parser": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-2.3.1.tgz", - "to-array": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz" - } } } }, @@ -1628,33 +821,36 @@ "integrity": "sha512-TcR6M4LaqKjHvAKoAi46w2Y11KPJiMchAEqu00+tlOBxHR8AYvUCBvDLw4+j3MymApVHHWtluOzDaWxEYeGuVw==", "dev": true, "requires": { - "bluebird": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", - "panic-client": "https://registry.npmjs.org/panic-client/-/panic-client-1.0.1.tgz", - "socket.io": "https://registry.npmjs.org/socket.io/-/socket.io-1.7.3.tgz" + "bluebird": "^3.3.5", + "panic-client": "^1.0.0", + "socket.io": "^1.4.5" } }, "parsejson": { - "version": "https://registry.npmjs.org/parsejson/-/parsejson-0.0.3.tgz", + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/parsejson/-/parsejson-0.0.3.tgz", "integrity": "sha1-q343WfIJ7OmUN5c/fQ8fZK4OZKs=", "dev": true, "requires": { - "better-assert": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz" + "better-assert": "~1.0.0" } }, "parseqs": { - "version": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", "dev": true, "requires": { - "better-assert": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz" + "better-assert": "~1.0.0" } }, "parseuri": { - "version": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", "dev": true, "requires": { - "better-assert": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz" + "better-assert": "~1.0.0" } }, "parseurl": { @@ -1664,8 +860,9 @@ "dev": true }, "path-is-absolute": { - "version": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz", - "integrity": "sha1-Jj2tpmqz8vsQv3+dJN2PPlcO+RI=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, "path-to-regexp": { @@ -1674,110 +871,22 @@ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", "dev": true }, - "pez": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pez/-/pez-4.0.1.tgz", - "integrity": "sha512-0c/SoW5MY7lPdc5U1Q/ixyjLZbluGWJonHVmn4mKwSq7vgO9+a9WzoCopHubIwkot6Q+fevNVElaA+1M9SqHrA==", - "dev": true, - "requires": { - "b64": "4.0.0", - "boom": "7.1.1", - "content": "4.0.3", - "hoek": "5.0.2", - "nigel": "3.0.0" - }, - "dependencies": { - "boom": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-7.1.1.tgz", - "integrity": "sha512-qwEARHTliqgEQiVkzKkkbLt3q0vRPIW60VRZ8zRnbjsm7INkPe9NxfAYDDYLZOdhxyUHa1gIe639Cx7t6RH/4A==", - "dev": true, - "requires": { - "hoek": "5.0.2" - } - }, - "hoek": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.2.tgz", - "integrity": "sha512-NA10UYP9ufCtY2qYGkZktcQXwVyYK4zK0gkaFSB96xhtlo6V8tKXdQgx8eHolQTRemaW0uLn8BhjhwqrOU+QLQ==", - "dev": true - } - } - }, "platform": { - "version": "https://registry.npmjs.org/platform/-/platform-1.3.3.tgz", - "integrity": "sha1-ZGx3ARiZhwtqCQPnXpl+jlHadGE=", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.1.tgz", + "integrity": "sha1-SSIQiSM1vTExwKCN2i2T7DVD5CM=", "dev": true }, - "podium": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/podium/-/podium-3.1.2.tgz", - "integrity": "sha512-18VrjJAduIdPv7d9zWsfmKxTj3cQTYC5Pv5gtKxcWujYBpGbV+mhNSPYhlHW5xeWoazYyKfB9FEsPT12r5rY1A==", - "dev": true, - "requires": { - "hoek": "5.0.2", - "joi": "13.1.1" - }, - "dependencies": { - "hoek": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.2.tgz", - "integrity": "sha512-NA10UYP9ufCtY2qYGkZktcQXwVyYK4zK0gkaFSB96xhtlo6V8tKXdQgx8eHolQTRemaW0uLn8BhjhwqrOU+QLQ==", - "dev": true - }, - "isemail": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.1.0.tgz", - "integrity": "sha512-Ke15MBbbhyIhZzWheiWuRlTO81tTH4RQvrbJFpVzJce8oyVrCVSDdrcw4TcyMsaS/fMGJSbU3lTsqCGDKwrzww==", - "dev": true, - "requires": { - "punycode": "2.1.0" - } - }, - "joi": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/joi/-/joi-13.1.1.tgz", - "integrity": "sha512-Y44bDwIoeCjFDRO18VaMRc0hIdPkLbZaF2VqU7t1tCcno3S3XzsmlYYpOu0Qk6nkzoI5RSao7W57NTvPKxbkcg==", - "dev": true, - "requires": { - "hoek": "5.0.2", - "isemail": "3.1.0", - "topo": "3.0.0" - } - }, - "punycode": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", - "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=", - "dev": true - }, - "topo": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/topo/-/topo-3.0.0.tgz", - "integrity": "sha512-Tlu1fGlR90iCdIPURqPiufqAlCZYzLjHYVVbcFWDMcX7+tK8hdZWAfsMrD/pBul9jqHHwFjNdf1WaxA9vTRRhw==", - "dev": true, - "requires": { - "hoek": "5.0.2" - } - } - } - }, "proxy-addr": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz", - "integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz", + "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", "dev": true, "requires": { - "forwarded": "0.1.2", - "ipaddr.js": "1.5.2" + "forwarded": "~0.1.2", + "ipaddr.js": "1.6.0" } }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, "punycode": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", @@ -1812,22 +921,34 @@ "http-errors": "1.6.2", "iconv-lite": "0.4.19", "unpipe": "1.0.0" + }, + "dependencies": { + "depd": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", + "dev": true + }, + "http-errors": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "dev": true, + "requires": { + "depd": "1.1.1", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": ">= 1.3.1 < 2" + } + }, + "setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", + "dev": true + } } }, - "realistic-structured-clone": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/realistic-structured-clone/-/realistic-structured-clone-1.0.1.tgz", - "integrity": "sha1-Gr6CrwuAzXsQn9r10pMIAyhS1F0=", - "dev": true, - "requires": { - "lodash.isplainobject": "3.2.0" - } - }, - "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" - }, "sax": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", @@ -1835,217 +956,177 @@ "dev": true }, "send": { - "version": "0.16.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz", - "integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==", + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", "dev": true, "requires": { "debug": "2.6.9", - "depd": "1.1.1", - "destroy": "1.0.4", - "encodeurl": "1.0.1", - "escape-html": "1.0.3", - "etag": "1.8.1", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "1.6.2", + "http-errors": "~1.6.2", "mime": "1.4.1", "ms": "2.0.0", - "on-finished": "2.3.0", - "range-parser": "1.2.0", - "statuses": "1.3.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" } }, "serve-static": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz", - "integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", "dev": true, "requires": { - "encodeurl": "1.0.1", - "escape-html": "1.0.3", - "parseurl": "1.3.2", - "send": "0.16.1" + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" } }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true - }, "setprototypeof": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", "dev": true }, - "shot": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/shot/-/shot-4.0.4.tgz", - "integrity": "sha512-V8wHSJSNqt8ZIgdbTQCFIrp5BwBH+tsFLNBL1REmkFN/0PJdmzUBQscZqsbdSvdLc+Qxq7J5mcwzai66vs3ozA==", - "dev": true, - "requires": { - "hoek": "5.0.2", - "joi": "13.1.1" - }, - "dependencies": { - "hoek": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.2.tgz", - "integrity": "sha512-NA10UYP9ufCtY2qYGkZktcQXwVyYK4zK0gkaFSB96xhtlo6V8tKXdQgx8eHolQTRemaW0uLn8BhjhwqrOU+QLQ==", - "dev": true - }, - "isemail": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.1.0.tgz", - "integrity": "sha512-Ke15MBbbhyIhZzWheiWuRlTO81tTH4RQvrbJFpVzJce8oyVrCVSDdrcw4TcyMsaS/fMGJSbU3lTsqCGDKwrzww==", - "dev": true, - "requires": { - "punycode": "2.1.0" - } - }, - "joi": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/joi/-/joi-13.1.1.tgz", - "integrity": "sha512-Y44bDwIoeCjFDRO18VaMRc0hIdPkLbZaF2VqU7t1tCcno3S3XzsmlYYpOu0Qk6nkzoI5RSao7W57NTvPKxbkcg==", - "dev": true, - "requires": { - "hoek": "5.0.2", - "isemail": "3.1.0", - "topo": "3.0.0" - } - }, - "punycode": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", - "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=", - "dev": true - }, - "topo": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/topo/-/topo-3.0.0.tgz", - "integrity": "sha512-Tlu1fGlR90iCdIPURqPiufqAlCZYzLjHYVVbcFWDMcX7+tK8hdZWAfsMrD/pBul9jqHHwFjNdf1WaxA9vTRRhw==", - "dev": true, - "requires": { - "hoek": "5.0.2" - } - } - } - }, - "slide": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", - "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", - "dev": true - }, "socket.io": { - "version": "https://registry.npmjs.org/socket.io/-/socket.io-1.7.3.tgz", - "integrity": "sha1-uK+cq6AJSeVo42nxMn6pvp6iRhs=", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-1.7.4.tgz", + "integrity": "sha1-L37O3DORvy1cc+KR/iM+bjTU3QA=", "dev": true, "requires": { - "debug": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "engine.io": "https://registry.npmjs.org/engine.io/-/engine.io-1.8.3.tgz", - "has-binary": "https://registry.npmjs.org/has-binary/-/has-binary-0.1.7.tgz", - "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", - "socket.io-adapter": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-0.5.0.tgz", - "socket.io-client": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-1.7.3.tgz", - "socket.io-parser": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-2.3.1.tgz" + "debug": "2.3.3", + "engine.io": "~1.8.4", + "has-binary": "0.1.7", + "object-assign": "4.1.0", + "socket.io-adapter": "0.5.0", + "socket.io-client": "1.7.4", + "socket.io-parser": "2.3.1" }, "dependencies": { "debug": { - "version": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", "dev": true, "requires": { - "ms": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz" + "ms": "0.7.2" } }, "ms": { - "version": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", "dev": true - }, - "socket.io-client": { - "version": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-1.7.3.tgz", - "integrity": "sha1-sw6GqhDV7zVGYBwJzeR2Xjgdo3c=", - "dev": true, - "requires": { - "backo2": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "component-bind": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", - "component-emitter": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "debug": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "engine.io-client": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-1.8.3.tgz", - "has-binary": "https://registry.npmjs.org/has-binary/-/has-binary-0.1.7.tgz", - "indexof": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "object-component": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", - "parseuri": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", - "socket.io-parser": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-2.3.1.tgz", - "to-array": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz" - } } } }, "socket.io-adapter": { - "version": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-0.5.0.tgz", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-0.5.0.tgz", "integrity": "sha1-y21LuL7IHhB4uZZ3+c7QBGBmu4s=", "dev": true, "requires": { - "debug": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "socket.io-parser": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-2.3.1.tgz" + "debug": "2.3.3", + "socket.io-parser": "2.3.1" }, "dependencies": { "debug": { - "version": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", "dev": true, "requires": { - "ms": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz" + "ms": "0.7.2" } }, "ms": { - "version": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", + "dev": true + } + } + }, + "socket.io-client": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-1.7.4.tgz", + "integrity": "sha1-7J+CA1btme9tNX8HVtZIcXvdQoE=", + "dev": true, + "requires": { + "backo2": "1.0.2", + "component-bind": "1.0.0", + "component-emitter": "1.2.1", + "debug": "2.3.3", + "engine.io-client": "~1.8.4", + "has-binary": "0.1.7", + "indexof": "0.0.1", + "object-component": "0.0.3", + "parseuri": "0.0.5", + "socket.io-parser": "2.3.1", + "to-array": "0.1.4" + }, + "dependencies": { + "debug": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", + "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", + "dev": true, + "requires": { + "ms": "0.7.2" + } + }, + "ms": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", "dev": true } } }, "socket.io-parser": { - "version": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-2.3.1.tgz", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-2.3.1.tgz", "integrity": "sha1-3VMgJRA85Clpcya+/WQAX8/ltKA=", "dev": true, "requires": { - "component-emitter": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz", - "debug": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "isarray": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "json3": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz" + "component-emitter": "1.1.2", + "debug": "2.2.0", + "isarray": "0.0.1", + "json3": "3.3.2" }, "dependencies": { "component-emitter": { - "version": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz", "integrity": "sha1-KWWU8nU9qmOZbSrwjRWpURbJrsM=", "dev": true }, + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "dev": true, + "requires": { + "ms": "0.7.1" + } + }, "isarray": { - "version": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", "dev": true + }, + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", + "dev": true } } }, @@ -2055,191 +1136,66 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, - "statehood": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/statehood/-/statehood-6.0.5.tgz", - "integrity": "sha512-HPa8qT5sGTBVn1Fc9czBYR1oo7gBaay3ysnb04cvcF80YrDIV7880KpjmMj54j7CrFuQFfgMRb44QCRxRmAdTg==", - "dev": true, - "requires": { - "boom": "7.1.1", - "bounce": "1.2.0", - "cryptiles": "4.1.1", - "hoek": "5.0.2", - "iron": "5.0.4", - "joi": "13.1.1" - }, - "dependencies": { - "boom": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-7.1.1.tgz", - "integrity": "sha512-qwEARHTliqgEQiVkzKkkbLt3q0vRPIW60VRZ8zRnbjsm7INkPe9NxfAYDDYLZOdhxyUHa1gIe639Cx7t6RH/4A==", - "dev": true, - "requires": { - "hoek": "5.0.2" - } - }, - "hoek": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.2.tgz", - "integrity": "sha512-NA10UYP9ufCtY2qYGkZktcQXwVyYK4zK0gkaFSB96xhtlo6V8tKXdQgx8eHolQTRemaW0uLn8BhjhwqrOU+QLQ==", - "dev": true - }, - "isemail": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.1.0.tgz", - "integrity": "sha512-Ke15MBbbhyIhZzWheiWuRlTO81tTH4RQvrbJFpVzJce8oyVrCVSDdrcw4TcyMsaS/fMGJSbU3lTsqCGDKwrzww==", - "dev": true, - "requires": { - "punycode": "2.1.0" - } - }, - "joi": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/joi/-/joi-13.1.1.tgz", - "integrity": "sha512-Y44bDwIoeCjFDRO18VaMRc0hIdPkLbZaF2VqU7t1tCcno3S3XzsmlYYpOu0Qk6nkzoI5RSao7W57NTvPKxbkcg==", - "dev": true, - "requires": { - "hoek": "5.0.2", - "isemail": "3.1.0", - "topo": "3.0.0" - } - }, - "punycode": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", - "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=", - "dev": true - }, - "topo": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/topo/-/topo-3.0.0.tgz", - "integrity": "sha512-Tlu1fGlR90iCdIPURqPiufqAlCZYzLjHYVVbcFWDMcX7+tK8hdZWAfsMrD/pBul9jqHHwFjNdf1WaxA9vTRRhw==", - "dev": true, - "requires": { - "hoek": "5.0.2" - } - } - } - }, "statuses": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", - "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", "dev": true }, - "subtext": { - "version": "6.0.7", - "resolved": "https://registry.npmjs.org/subtext/-/subtext-6.0.7.tgz", - "integrity": "sha512-IcJUvRjeR+NB437Iq+LORFNJW4L6Knqkj3oQrBrkdhIaS2VKJvx/9aYEq7vi+PEx5/OuehOL/40SkSZotLi/MA==", - "dev": true, - "requires": { - "boom": "7.1.1", - "content": "4.0.3", - "hoek": "5.0.2", - "pez": "4.0.1", - "wreck": "14.0.2" - }, - "dependencies": { - "boom": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-7.1.1.tgz", - "integrity": "sha512-qwEARHTliqgEQiVkzKkkbLt3q0vRPIW60VRZ8zRnbjsm7INkPe9NxfAYDDYLZOdhxyUHa1gIe639Cx7t6RH/4A==", - "dev": true, - "requires": { - "hoek": "5.0.2" - } - }, - "hoek": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.2.tgz", - "integrity": "sha512-NA10UYP9ufCtY2qYGkZktcQXwVyYK4zK0gkaFSB96xhtlo6V8tKXdQgx8eHolQTRemaW0uLn8BhjhwqrOU+QLQ==", - "dev": true - } - } - }, "supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "2.0.0" + "has-flag": "^3.0.0" } }, - "teamwork": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/teamwork/-/teamwork-3.0.1.tgz", - "integrity": "sha512-hEkJIpDOfOYe9NYaLFk00zQbzZeKNCY8T2pRH3I13Y1mJwxaSQ6NEsjY5rCp+11ezCiZpWGoGFTbOuhg4qKevQ==", - "dev": true - }, "text-encoding": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", - "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=", - "dev": true + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz", + "integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==", + "optional": true }, "to-array": { - "version": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", "dev": true }, - "topo": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/topo/-/topo-3.0.0.tgz", - "integrity": "sha512-Tlu1fGlR90iCdIPURqPiufqAlCZYzLjHYVVbcFWDMcX7+tK8hdZWAfsMrD/pBul9jqHHwFjNdf1WaxA9vTRRhw==", - "dev": true, - "requires": { - "hoek": "5.0.2" - } - }, "tslib": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.0.tgz", - "integrity": "sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==", - "dev": true + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" }, "type-is": { - "version": "1.6.15", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", - "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", + "version": "1.6.16", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", "dev": true, "requires": { "media-typer": "0.3.0", - "mime-types": "2.1.17" - }, - "dependencies": { - "mime-db": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", - "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=", - "dev": true - }, - "mime-types": { - "version": "2.1.17", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", - "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", - "dev": true, - "requires": { - "mime-db": "1.30.0" - } - } + "mime-types": "~2.1.18" } }, "uglify-js": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.1.4.tgz", - "integrity": "sha512-DcbkPg11Lw2lAWpwCmQDX+qoR4JiII6ypsQmF6tscZtlxGPFAmSRUGuMoVT3/0EHqypVik/TpkH4ITiMJeQqQA==", + "version": "3.3.24", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.3.24.tgz", + "integrity": "sha512-hS7+TDiqIqvWScCcKRybCQzmMnEzJ4ryl9ErRmW4GFyG48p0/dKZiy/5mVLbsFzU8CCnCgQdxMiJzZythvLzCg==", "dev": true, "requires": { - "commander": "2.11.0", - "source-map": "0.6.1" + "commander": "~2.15.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + } } }, - "ultron": { - "version": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", - "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=", - "dev": true - }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -2268,106 +1224,38 @@ "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==", "dev": true }, - "uws": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/uws/-/uws-0.14.5.tgz", - "integrity": "sha1-Z6rzPEaypYel9mZtAPdpEyjxSdw=", - "dev": true - }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", "dev": true }, - "vise": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/vise/-/vise-3.0.0.tgz", - "integrity": "sha512-kBFZLmiL1Vm3rHXphkhvvAcsjgeQXRrOFCbJb0I50YZZP4HGRNH+xGzK3matIMcpbsfr3I02u9odj4oCD0TWgA==", - "dev": true, - "requires": { - "hoek": "5.0.2" - }, - "dependencies": { - "hoek": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.2.tgz", - "integrity": "sha512-NA10UYP9ufCtY2qYGkZktcQXwVyYK4zK0gkaFSB96xhtlo6V8tKXdQgx8eHolQTRemaW0uLn8BhjhwqrOU+QLQ==", - "dev": true - } - } - }, "webcrypto-core": { - "version": "0.1.21", - "resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-0.1.21.tgz", - "integrity": "sha512-nH9oysjTC13+4HSYbXtK58sR3IHGsMRpOejT+BhEn+RT6ctBB6o589UQwkHQ2rHfudMfSqNRcMPEH4OlODtJ7w==", - "dev": true, + "version": "0.1.26", + "resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-0.1.26.tgz", + "integrity": "sha512-BZVgJZkkHyuz8loKvsaOKiBDXDpmMZf5xG4QAOlSeYdXlFUl9c1FRrVnAXcOdb4fTHMG+TRu81odJwwSfKnWTA==", + "optional": true, "requires": { - "tslib": "1.9.0" + "tslib": "^1.7.1" } }, "wrappy": { - "version": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, - "wreck": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/wreck/-/wreck-14.0.2.tgz", - "integrity": "sha512-QCm3omWNJUseqrSzwX2QZi1rBbmCfbFHJAXputLLyZ37VSiFnSYQB0ms/mPnSvrlIu7GVm89Y/gBNhSY26uVIQ==", - "dev": true, - "requires": { - "boom": "7.1.1", - "hoek": "5.0.2" - }, - "dependencies": { - "boom": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-7.1.1.tgz", - "integrity": "sha512-qwEARHTliqgEQiVkzKkkbLt3q0vRPIW60VRZ8zRnbjsm7INkPe9NxfAYDDYLZOdhxyUHa1gIe639Cx7t6RH/4A==", - "dev": true, - "requires": { - "hoek": "5.0.2" - } - }, - "hoek": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.2.tgz", - "integrity": "sha512-NA10UYP9ufCtY2qYGkZktcQXwVyYK4zK0gkaFSB96xhtlo6V8tKXdQgx8eHolQTRemaW0uLn8BhjhwqrOU+QLQ==", - "dev": true - } - } - }, - "write-file-atomic": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz", - "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "imurmurhash": "0.1.4", - "slide": "1.1.6" - } - }, "ws": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-4.0.0.tgz", - "integrity": "sha512-QYslsH44bH8O7/W2815u5DpnCpXWpEK44FmaHffNwgJI4JMaSZONgPBTOfrxJ29mXKbXak+LsJ2uAkDTYq2ptQ==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", "requires": { - "async-limiter": "1.0.0", - "safe-buffer": "5.1.1", - "ultron": "1.1.1" - }, - "dependencies": { - "ultron": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" - } + "async-limiter": "~1.0.0" } }, "wtf-8": { - "version": "https://registry.npmjs.org/wtf-8/-/wtf-8-1.0.0.tgz", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wtf-8/-/wtf-8-1.0.0.tgz", "integrity": "sha1-OS2LotDxw00e4tYw8V0O+2jhBIo=", "dev": true }, @@ -2377,8 +1265,8 @@ "integrity": "sha1-F76T6q4/O3eTWceVtBlwWogX6Gg=", "dev": true, "requires": { - "sax": "1.2.1", - "xmlbuilder": "4.2.1" + "sax": ">=0.6.0", + "xmlbuilder": "^4.1.0" } }, "xmlbuilder": { @@ -2387,22 +1275,18 @@ "integrity": "sha1-qlijBBoGb5DqoWwvU4n/GfP0YaU=", "dev": true, "requires": { - "lodash": "4.17.4" + "lodash": "^4.0.0" } }, "xmlhttprequest-ssl": { - "version": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz", "integrity": "sha1-GFqIjATspGw+QHDZn3tJ3jUomS0=", "dev": true }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, "yeast": { - "version": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", "dev": true } diff --git a/package.json b/package.json index e5dd9909..ef840765 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,20 @@ { "name": "gun", - "version": "0.9.992", + "version": "0.2019.416", "description": "A realtime, decentralized, offline-first, graph data synchronization engine.", "main": "index.js", - "browser": "gun.min.js", + "browser": "gun.js", "scripts": { - "start": "node examples/http.js 8080", + "start": "node examples/http.js 8765", + "https": "HTTPS_KEY=test/https/server.key HTTPS_CERT=test/https/server.crt npm start", "prepublishOnly": "npm run unbuild", "test": "mocha", "testsea": "mocha test/sea.js", + "testaxe": "mocha test/axe/holy-grail.js", "e2e": "mocha e2e/distributed.js", "docker": "hooks/build", - "unbuild": "node lib/unbuild.js && uglifyjs gun.js -o gun.min.js -c -m" + "minify": "uglifyjs gun.js -o gun.min.js -c -m", + "unbuild": "node lib/unbuild.js" }, "repository": { "type": "git", @@ -48,24 +51,20 @@ "node": ">=0.8.4" }, "dependencies": { - "ws": "~>4.0.0" + "ws": "~>6.2.1" + }, + "optionalDependencies": { + "text-encoding": "^0.7.0", + "node-webcrypto-ossl": "^1.0.43" }, "devDependencies": { - "@std/esm": "^0.8.3", - "@trust/webcrypto": "^0.7.1", "aws-sdk": ">=2.153.0", + "concat-map": "^0.0.1", "express": ">=4.15.2", - "fake-indexeddb": "^2.0.3", - "hapi": "^17.2.0", - "inert": "^5.1.0", "ip": "^1.1.5", - "mocha": ">=3.2.0", - "node-localstorage": "^1.3.0", - "node-webcrypto-ossl": "^1.0.36", + "mocha": "^5.2.0", "panic-manager": "^1.2.0", "panic-server": "^1.1.1", - "text-encoding": "^0.6.4", - "uglify-js": ">=2.8.22", - "uws": "~>0.14.1" + "uglify-js": ">=2.8.22" } } diff --git a/sea.js b/sea.js index cfbdd039..6b5a4e4d 100644 --- a/sea.js +++ b/sea.js @@ -6,8 +6,8 @@ if(typeof global !== "undefined"){ root = global } root = root || {}; var console = root.console || {log: function(){}}; - function USE(arg){ - return arg.slice? USE[R(arg)] : function(mod, path){ + function USE(arg, req){ + return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){ arg(mod = {exports: {}}); USE[R(path)] = mod.exports; } @@ -20,24 +20,30 @@ ;USE(function(module){ // Security, Encryption, and Authorization: SEA.js - // MANDATORY READING: http://gun.js.org/explainers/data/security.html + // MANDATORY READING: https://gun.eco/explainers/data/security.html + // IT IS IMPLEMENTED IN A POLYFILL/SHIM APPROACH. // THIS IS AN EARLY ALPHA! - function SEA(){} - if(typeof window !== "undefined"){ SEA.window = window } + if(typeof window !== "undefined"){ module.window = window } + var tmp = module.window || module; + var SEA = tmp.SEA || {}; + + if(SEA.window = module.window){ SEA.window.SEA = SEA } + + try{ if(typeof common !== "undefined"){ common.exports = SEA } }catch(e){} module.exports = SEA; })(USE, './root'); ;USE(function(module){ var SEA = USE('./root'); - if(SEA.window){ + try{ if(SEA.window){ if(location.protocol.indexOf('s') < 0 && location.host.indexOf('localhost') < 0 && location.protocol.indexOf('file:') < 0){ - location.protocol = 'https:'; + location.protocol = 'https:'; // WebCrypto does NOT work without HTTPS! } - } + } }catch(e){} })(USE, './https'); ;USE(function(module){ @@ -45,8 +51,8 @@ function SeaArray() {} Object.assign(SeaArray, { from: Array.from }) SeaArray.prototype = Object.create(Array.prototype) - SeaArray.prototype.toString = function(enc = 'utf8', start = 0, end) { - const { length } = this + SeaArray.prototype.toString = function(enc, start, end) { enc = enc || 'utf8'; start = start || 0; + const length = this.length if (enc === 'hex') { const buf = new Uint8Array(this) return [ ...Array(((end && (end + 1)) || length) - start).keys()] @@ -95,24 +101,25 @@ } buf = SeaArray.from(bytes) } else if (enc === 'utf8') { - const { length } = input + const length = input.length const words = new Uint16Array(length) - Array.from({ length }, (_, i) => words[i] = input.charCodeAt(i)) + Array.from({ length: length }, (_, i) => words[i] = input.charCodeAt(i)) buf = SeaArray.from(words) } else if (enc === 'base64') { const dec = atob(input) - const { length } = dec + const length = dec.length const bytes = new Uint8Array(length) - Array.from({ length }, (_, i) => bytes[i] = dec.charCodeAt(i)) + Array.from({ length: length }, (_, i) => bytes[i] = dec.charCodeAt(i)) buf = SeaArray.from(bytes) } else if (enc === 'binary') { buf = SeaArray.from(input) } else { - console.info(`SafeBuffer.from unknown encoding: '${enc}'`) + console.info('SafeBuffer.from unknown encoding: '+enc) } return buf } - const { byteLength, length = byteLength } = input + const byteLength = input.byteLength // what is going on here? FOR MARTTI + const length = input.byteLength ? input.byteLength : input.length if (length) { let buf if (input instanceof ArrayBuffer) { @@ -123,11 +130,11 @@ }, // This is 'safe-buffer.alloc' sans encoding support alloc(length, fill = 0 /*, enc*/ ) { - return SeaArray.from(new Uint8Array(Array.from({ length }, () => fill))) + return SeaArray.from(new Uint8Array(Array.from({ length: length }, () => fill))) }, // This is normal UNSAFE 'buffer.alloc' or 'new Buffer(length)' - don't use! allocUnsafe(length) { - return SeaArray.from(new Uint8Array(Array.from({ length }))) + return SeaArray.from(new Uint8Array(Array.from({ length : length }))) }, // This puts together array of array like members concat(arr) { // octet array @@ -144,115 +151,94 @@ })(USE, './buffer'); ;USE(function(module){ + const SEA = USE('./root') const Buffer = USE('./buffer') const api = {Buffer: Buffer} + var o = {}; - if (typeof __webpack_require__ === 'function' || typeof window !== 'undefined') { - const { msCrypto, crypto = msCrypto } = window // STD or M$ - const { webkitSubtle, subtle = webkitSubtle } = crypto // STD or iSafari - const { TextEncoder, TextDecoder } = window + 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 = USE('crypto', 1); + const { TextEncoder, TextDecoder } = USE('text-encoding', 1) Object.assign(api, { crypto, - subtle, - TextEncoder, - TextDecoder, - random: (len) => Buffer.from(crypto.getRandomValues(new Uint8Array(Buffer.alloc(len)))) - }) - } else { - try{ - const crypto = require('crypto') - //const WebCrypto = require('node-webcrypto-ossl') - //const { subtle: ossl } = new WebCrypto({directory: 'key_storage'}) // ECDH - const { subtle } = require('@trust/webcrypto') // All but ECDH - const { TextEncoder, TextDecoder } = require('text-encoding') - Object.assign(api, { - crypto, - subtle, - //ossl, + //subtle, TextEncoder, TextDecoder, random: (len) => Buffer.from(crypto.randomBytes(len)) - }) - }catch(e){ - console.log("@trust/webcrypto and text-encoding are not included by default, you must add it to your package.json!"); - TRUST_WEBCRYPTO_OR_TEXT_ENCODING_NOT_INSTALLED; - } - } + }); + //try{ + const WebCrypto = USE('node-webcrypto-ossl', 1); + api.ossl = api.subtle = new WebCrypto({directory: 'ossl'}).subtle // ECDH + //}catch(e){ + //console.log("node-webcrypto-ossl is optionally needed for ECDH, please install if needed."); + //} + }catch(e){ + console.log("node-webcrypto-ossl and text-encoding may not be included by default, please add it to your package.json!"); + OSSL_WEBCRYPTO_OR_TEXT_ENCODING_NOT_INSTALLED; + }} module.exports = api })(USE, './shim'); ;USE(function(module){ - const Buffer = USE('./buffer') - const settings = {} - // Encryption parameters - const pbkdf2 = { hash: 'SHA-256', iter: 100000, ks: 64 } + var SEA = USE('./root'); + var Buffer = USE('./buffer'); + var s = {}; + s.pbkdf2 = {hash: 'SHA-256', iter: 100000, ks: 64}; + s.ecdsa = { + pair: {name: 'ECDSA', namedCurve: 'P-256'}, + sign: {name: 'ECDSA', hash: {name: 'SHA-256'}} + }; + s.ecdh = {name: 'ECDH', namedCurve: 'P-256'}; - const ecdsaSignProps = { name: 'ECDSA', hash: { name: 'SHA-256' } } - const ecdsaKeyProps = { name: 'ECDSA', namedCurve: 'P-256' } - const ecdhKeyProps = { name: 'ECDH', namedCurve: 'P-256' } - - const _initial_authsettings = { - validity: 12 * 60 * 60, // internally in seconds : 12 hours - hook: (props) => props // { iat, exp, alias, remember } - // or return new Promise((resolve, reject) => resolve(props) - } - // These are used to persist user's authentication "session" - const authsettings = Object.assign({}, _initial_authsettings) // This creates Web Cryptography API compliant JWK for sign/verify purposes - const keysToEcdsaJwk = (pub, d) => { // d === priv - //const [ x, y ] = Buffer.from(pub, 'base64').toString('utf8').split(':') // old - const [ x, y ] = pub.split('.') // new - var jwk = { kty: "EC", crv: "P-256", x: x, y: y, ext: true } + s.jwk = function(pub, d){ // d === priv + pub = pub.split('.'); + var x = pub[0], y = pub[1]; + var jwk = {kty: "EC", crv: "P-256", x: x, y: y, ext: true}; jwk.key_ops = d ? ['sign'] : ['verify']; if(d){ jwk.d = d } return jwk; + }; + s.recall = { + validity: 12 * 60 * 60, // internally in seconds : 12 hours + hook: function(props){ return props } // { iat, exp, alias, remember } // or return new Promise((resolve, reject) => resolve(props) + }; + + s.check = function(t){ return (typeof t == 'string') && ('SEA{' === t.slice(0,4)) } + s.parse = function p(t){ try { + var yes = (typeof t == 'string'); + if(yes && 'SEA{' === t.slice(0,4)){ t = t.slice(3) } + return yes ? JSON.parse(t) : t; + } catch (e) {} + return t; } - Object.assign(settings, { - pbkdf2, - ecdsa: { - pair: ecdsaKeyProps, - sign: ecdsaSignProps - }, - ecdh: ecdhKeyProps, - jwk: keysToEcdsaJwk, - recall: authsettings - }) - module.exports = settings + SEA.opt = s; + module.exports = s })(USE, './settings'); ;USE(function(module){ - module.exports = (props) => { - try { - if(props.slice && 'SEA{' === props.slice(0,4)){ - props = props.slice(3); - } - return props.slice ? JSON.parse(props) : props - } catch (e) {} //eslint-disable-line no-empty - return props + var shim = USE('./shim'); + module.exports = async function(d, o){ + var t = (typeof d == 'string')? d : JSON.stringify(d); + var hash = await shim.subtle.digest({name: o||'SHA-256'}, new shim.TextEncoder().encode(t)); + return shim.Buffer.from(hash); } - })(USE, './parse'); - - ;USE(function(module){ - const { - subtle, ossl = subtle, random: getRandomBytes, TextEncoder, TextDecoder - } = 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)) - return Buffer.from(hash) - } - module.exports = sha256hash })(USE, './sha256'); ;USE(function(module){ // This internal func returns SHA-1 hashed data for KeyID generation - const { subtle, ossl = subtle } = USE('./shim') + const __shim = USE('./shim') + const subtle = __shim.subtle + const ossl = __shim.ossl ? __shim.ossl : subtle const sha1hash = (b) => ossl.digest({name: 'SHA-1'}, new ArrayBuffer(b)) module.exports = sha1hash })(USE, './sha1'); @@ -261,48 +247,41 @@ var SEA = USE('./root'); var shim = USE('./shim'); var S = USE('./settings'); + var sha = USE('./sha256'); var u; - SEA.work = async (data, pair, cb) => { try { // used to be named `proof` - var salt = pair.epub || pair; // epub not recommended, salt should be random! + SEA.work = SEA.work || (async (data, pair, cb, opt) => { try { // used to be named `proof` + var salt = (pair||{}).epub || pair; // epub not recommended, salt should be random! + var opt = opt || {}; if(salt instanceof Function){ cb = salt; salt = u; } salt = salt || shim.random(9); - if (SEA.window) { - // For browser subtle works fine - const key = await shim.subtle.importKey( - 'raw', new shim.TextEncoder().encode(data), { name: 'PBKDF2' }, false, ['deriveBits'] - ) - const result = await shim.subtle.deriveBits({ - name: 'PBKDF2', - iterations: S.pbkdf2.iter, - salt: new shim.TextEncoder().encode(salt), - hash: S.pbkdf2.hash, - }, key, S.pbkdf2.ks * 8) - data = shim.random(data.length) // Erase data in case of passphrase - const r = shim.Buffer.from(result, 'binary').toString('utf8') - if(cb){ cb(r) } - return r; + data = (typeof data == 'string')? data : JSON.stringify(data); + if('sha' === (opt.name||'').toLowerCase().slice(0,3)){ + var rsha = shim.Buffer.from(await sha(data, opt.name), 'binary').toString(opt.encode || 'base64') + if(cb){ try{ cb(rsha) }catch(e){console.log(e)} } + return rsha; } - // For NodeJS crypto.pkdf2 rocks - const hash = crypto.pbkdf2Sync( - data, - new shim.TextEncoder().encode(salt), - S.pbkdf2.iter, - S.pbkdf2.ks, - S.pbkdf2.hash.replace('-', '').toLowerCase() - ) - data = shim.random(data.length) // Erase passphrase for app - const r = hash && hash.toString('utf8') - if(cb){ cb(r) } + var key = await (shim.ossl || shim.subtle).importKey('raw', new shim.TextEncoder().encode(data), {name: opt.name || 'PBKDF2'}, false, ['deriveBits']); + var work = await (shim.ossl || shim.subtle).deriveBits({ + name: opt.name || 'PBKDF2', + iterations: opt.iterations || S.pbkdf2.iter, + salt: new shim.TextEncoder().encode(opt.salt || salt), + hash: opt.hash || S.pbkdf2.hash, + }, key, opt.length || (S.pbkdf2.ks * 8)) + data = shim.random(data.length) // Erase data in case of passphrase + var r = shim.Buffer.from(work, 'binary').toString(opt.encode || 'base64') + if(cb){ try{ cb(r) }catch(e){console.log(e)} } return r; } catch(e) { + console.log(e); SEA.err = e; + if(SEA.throw){ throw e } if(cb){ cb() } return; - }} + }}); module.exports = SEA.work; })(USE, './work'); @@ -311,51 +290,72 @@ var SEA = USE('./root'); var shim = USE('./shim'); 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(SEA.throw){ throw e } + if(cb){ cb() } + return; + }}); //SEA.pair = async (data, proof, cb) => { try { - SEA.pair = async (cb) => { try { + SEA.pair = SEA.pair || (async (cb, opt) => { try { - const ecdhSubtle = shim.ossl || shim.subtle + var ecdhSubtle = shim.ossl || shim.subtle; // First: ECDSA keys for signing/verifying... - const { pub, priv } = await shim.subtle.generateKey(S.ecdsa.pair, true, [ 'sign', 'verify' ]) + var sa = await shim.subtle.generateKey(S.ecdsa.pair, true, [ 'sign', 'verify' ]) .then(async (keys) => { // privateKey scope doesn't leak out from here! - const { d: priv } = await shim.subtle.exportKey('jwk', keys.privateKey) - const { x, y } = await shim.subtle.exportKey('jwk', keys.publicKey) + //const { d: priv } = await shim.subtle.exportKey('jwk', keys.privateKey) + var key = {}; + key.priv = (await shim.subtle.exportKey('jwk', keys.privateKey)).d; + var pub = await shim.subtle.exportKey('jwk', keys.publicKey); //const pub = Buff.from([ x, y ].join(':')).toString('base64') // old - const pub = x+'.'+y // new + key.pub = pub.x+'.'+pub.y; // new // x and y are already base64 // pub is UTF8 but filename/URL safe (https://www.ietf.org/rfc/rfc3986.txt) // but split on a non-base64 letter. - return { pub, priv } + return key; }) // To include PGPv4 kind of keyId: // const pubId = await SEA.keyid(keys.pub) // Next: ECDH keys for encryption/decryption... - const { epub, epriv } = await ecdhSubtle.generateKey(S.ecdh, true, ['deriveKey']) + try{ + var dh = await ecdhSubtle.generateKey(S.ecdh, true, ['deriveKey']) .then(async (keys) => { // privateKey scope doesn't leak out from here! - const { d: epriv } = await ecdhSubtle.exportKey('jwk', keys.privateKey) - const { x, y } = await ecdhSubtle.exportKey('jwk', keys.publicKey) + var key = {}; + key.epriv = (await ecdhSubtle.exportKey('jwk', keys.privateKey)).d; + var pub = await ecdhSubtle.exportKey('jwk', keys.publicKey); //const epub = Buff.from([ ex, ey ].join(':')).toString('base64') // old - const epub = x+'.'+y // new + key.epub = pub.x+'.'+pub.y; // new // ex and ey are already base64 // epub is UTF8 but filename/URL safe (https://www.ietf.org/rfc/rfc3986.txt) // but split on a non-base64 letter. - return { epub, epriv } + return key; }) + }catch(e){ + if(SEA.window){ throw e } + if(e == 'Error: ECDH is not a supported algorithm'){ console.log('Ignoring ECDH...') } + else { throw e } + } dh = dh || {}; - const r = { pub, priv, /* pubId, */ epub, epriv } - if(cb){ cb(r) } + var 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(SEA.throw){ throw e } if(cb){ cb() } return; - }} + }}); module.exports = SEA.pair; })(USE, './pair'); @@ -364,34 +364,42 @@ var SEA = USE('./root'); var shim = USE('./shim'); var S = USE('./settings'); - var sha256hash = USE('./sha256'); + var sha = USE('./sha256'); + var u; - SEA.sign = async (data, pair, cb) => { try { - if(data.slice - && 'SEA{' === data.slice(0,4) - && '"m":' === data.slice(4,8)){ - // TODO: This would prevent pair2 signing pair1's signature. - // So we may want to change this in the future. - // but for now, we want to prevent duplicate double signature. - if(cb){ cb(data) } - return data; + SEA.sign = SEA.sign || (async (data, pair, cb, opt) => { try { + opt = opt || {}; + if(!(pair||opt).priv){ + pair = await SEA.I(null, {what: data, how: 'sign', why: opt.why}); } - const pub = pair.pub - const priv = pair.priv - const jwk = S.jwk(pub, priv) - const msg = JSON.stringify(data) - const hash = await sha256hash(msg) - const sig = await shim.subtle.importKey('jwk', jwk, S.ecdsa.pair, false, ['sign']) - .then((key) => shim.subtle.sign(S.ecdsa.sign, key, new Uint8Array(hash))) // privateKey scope doesn't leak out from here! - const r = 'SEA'+JSON.stringify({m: msg, s: shim.Buffer.from(sig, 'binary').toString('utf8')}); + if(u === data){ throw '`undefined` not allowed.' } + var json = S.parse(data); + var check = opt.check = opt.check || json; + if(SEA.verify && (SEA.opt.check(check) || (check && check.s && check.m)) + && u !== await SEA.verify(check, pair)){ // don't sign if we already signed it. + var r = S.parse(check); + if(!opt.raw){ r = 'SEA'+JSON.stringify(r) } + if(cb){ try{ cb(r) }catch(e){console.log(e)} } + return r; + } + var pub = pair.pub; + var priv = pair.priv; + var jwk = S.jwk(pub, priv); + var hash = await sha(json); + var sig = await (shim.ossl || shim.subtle).importKey('jwk', jwk, S.ecdsa.pair, false, ['sign']) + .then((key) => (shim.ossl || shim.subtle).sign(S.ecdsa.sign, key, new Uint8Array(hash))) // privateKey scope doesn't leak out from here! + var r = {m: json, s: shim.Buffer.from(sig, 'binary').toString(opt.encode || 'base64')} + if(!opt.raw){ r = 'SEA'+JSON.stringify(r) } - if(cb){ cb(r) } + if(cb){ try{ cb(r) }catch(e){console.log(e)} } return r; - } catch(e) { + } catch(e) { + console.log(e); SEA.err = e; + if(SEA.throw){ throw e } if(cb){ cb() } return; - }} + }}); module.exports = SEA.sign; })(USE, './sign'); @@ -400,77 +408,128 @@ var SEA = USE('./root'); var shim = USE('./shim'); var S = USE('./settings'); - var sha256hash = USE('./sha256'); - var parse = USE('./parse'); + var sha = USE('./sha256'); var u; - SEA.verify = async (data, pair, cb) => { try { - const json = parse(data) + SEA.verify = SEA.verify || (async (data, pair, cb, opt) => { try { + var json = S.parse(data); if(false === pair){ // don't verify! - const raw = (json === data)? json : parse(json.m) - if(cb){ cb(raw) } + var raw = S.parse(json.m); + if(cb){ try{ cb(raw) }catch(e){console.log(e)} } return raw; } - const pub = pair.pub || pair - const jwk = S.jwk(pub) - const key = await shim.subtle.importKey('jwk', jwk, S.ecdsa.pair, false, ['verify']) - const hash = await sha256hash(json.m) - const sig = new Uint8Array(shim.Buffer.from(json.s, 'utf8')) - const check = await shim.subtle.verify(S.ecdsa.sign, key, sig, new Uint8Array(hash)) - if(!check){ throw "Signature did not match." } - const r = check? parse(json.m) : u; + opt = opt || {}; + // SEA.I // verify is free! Requires no user permission. + var pub = pair.pub || pair; + var key = SEA.opt.slow_leak? await SEA.opt.slow_leak(pub) : await (shim.ossl || shim.subtle).importKey('jwk', jwk, S.ecdsa.pair, false, ['verify']); + var hash = await sha(json.m); + var buf, sig, check, tmp; try{ + buf = shim.Buffer.from(json.s, opt.encode || 'base64'); // NEW DEFAULT! + sig = new Uint8Array(buf); + check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash)); + if(!check){ throw "Signature did not match." } + }catch(e){ + if(SEA.opt.fallback){ + return await SEA.opt.fall_verify(data, pair, cb, opt); + } + } + var r = check? S.parse(json.m) : u; - if(cb){ cb(r) } + if(cb){ try{ cb(r) }catch(e){console.log(e)} } return r; - } catch(e) { + } catch(e) { + console.log(e); // mismatched owner FOR MARTTI SEA.err = e; + if(SEA.throw){ throw e } if(cb){ cb() } return; - }} + }}); module.exports = SEA.verify; + // legacy & ossl leak mitigation: + + var knownKeys = {}; + var keyForPair = SEA.opt.slow_leak = pair => { + if (knownKeys[pair]) return knownKeys[pair]; + var jwk = S.jwk(pair); + knownKeys[pair] = (shim.ossl || shim.subtle).importKey("jwk", jwk, S.ecdsa.pair, false, ["verify"]); + return knownKeys[pair]; + }; + + + SEA.opt.fall_verify = async function(data, pair, cb, opt, f){ + if(f === SEA.opt.fallback){ throw "Signature did not match" } f = f || 1; + var json = S.parse(data), pub = pair.pub || pair, key = await SEA.opt.slow_leak(pub); + var hash = (f <= SEA.opt.fallback)? shim.Buffer.from(await shim.subtle.digest({name: 'SHA-256'}, new shim.TextEncoder().encode(S.parse(json.m)))) : await sha(json.m); // this line is old bad buggy code but necessary for old compatibility. + var buf; var sig; var check; try{ + buf = shim.Buffer.from(json.s, opt.encode || 'base64') // NEW DEFAULT! + sig = new Uint8Array(buf) + check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash)) + if(!check){ throw "Signature did not match." } + }catch(e){ + buf = shim.Buffer.from(json.s, 'utf8') // AUTO BACKWARD OLD UTF8 DATA! + sig = new Uint8Array(buf) + check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash)) + if(!check){ throw "Signature did not match." } + } + var r = check? S.parse(json.m) : u; + if(cb){ try{ cb(r) }catch(e){console.log(e)} } + return r; + } + SEA.opt.fallback = 2; + })(USE, './verify'); ;USE(function(module){ var shim = USE('./shim'); var sha256hash = USE('./sha256'); - const importGen = async (key, salt) => { + const importGen = async (key, salt, opt) => { //const combo = shim.Buffer.concat([shim.Buffer.from(key, 'utf8'), salt || shim.random(8)]).toString('utf8') // old + var opt = opt || {}; const combo = key + (salt || shim.random(8)).toString('utf8'); // new const hash = shim.Buffer.from(await sha256hash(combo), 'binary') - return await shim.subtle.importKey('raw', new Uint8Array(hash), 'AES-CBC', false, ['encrypt', 'decrypt']) + return await shim.subtle.importKey('raw', new Uint8Array(hash), opt.name || 'AES-GCM', false, ['encrypt', 'decrypt']) } module.exports = importGen; - })(USE, './aescbc'); + })(USE, './aeskey'); ;USE(function(module){ var SEA = USE('./root'); var shim = USE('./shim'); var S = USE('./settings'); - var aescbckey = USE('./aescbc'); + var aeskey = USE('./aeskey'); + var u; - SEA.encrypt = async (data, pair, cb) => { try { - const key = pair.epriv || pair; - const msg = JSON.stringify(data) - const rand = {s: shim.random(8), iv: shim.random(16)}; - const ct = await aescbckey(key, rand.s) - .then((aes) => shim.subtle.encrypt({ // Keeping the AES key scope as private as possible... - name: 'AES-CBC', iv: new Uint8Array(rand.iv) - }, aes, new shim.TextEncoder().encode(msg))) - const r = 'SEA'+JSON.stringify({ - ct: shim.Buffer.from(ct, 'binary').toString('utf8'), - iv: rand.iv.toString('utf8'), - s: rand.s.toString('utf8') - }); + SEA.encrypt = SEA.encrypt || (async (data, pair, cb, opt) => { try { + opt = opt || {}; + var key = (pair||opt).epriv || pair; + if(u === data){ throw '`undefined` not allowed.' } + if(!key){ + pair = await SEA.I(null, {what: data, how: 'encrypt', why: opt.why}); + key = pair.epriv || pair; + } + var msg = (typeof data == 'string')? data : JSON.stringify(data); + var rand = {s: shim.random(9), iv: shim.random(15)}; // consider making this 9 and 15 or 18 or 12 to reduce == padding. + var ct = await aeskey(key, rand.s, opt).then((aes) => (/*shim.ossl ||*/ shim.subtle).encrypt({ // Keeping the AES key scope as private as possible... + name: opt.name || 'AES-GCM', iv: new Uint8Array(rand.iv) + }, aes, new shim.TextEncoder().encode(msg))); + var r = { + ct: shim.Buffer.from(ct, 'binary').toString(opt.encode || 'base64'), + iv: rand.iv.toString(opt.encode || 'base64'), + s: rand.s.toString(opt.encode || 'base64') + } + if(!opt.raw){ r = 'SEA'+JSON.stringify(r) } - if(cb){ cb(r) } + if(cb){ try{ cb(r) }catch(e){console.log(e)} } return r; } catch(e) { + console.log(e); SEA.err = e; + if(SEA.throw){ throw e } if(cb){ cb() } return; - }} + }}); module.exports = SEA.encrypt; })(USE, './encrypt'); @@ -479,70 +538,110 @@ var SEA = USE('./root'); var shim = USE('./shim'); var S = USE('./settings'); - var aescbckey = USE('./aescbc'); - var parse = USE('./parse'); + var aeskey = USE('./aeskey'); - SEA.decrypt = async (data, pair, cb) => { try { - const key = pair.epriv || pair; - const json = parse(data) - const ct = await aescbckey(key, shim.Buffer.from(json.s, 'utf8')) - .then((aes) => shim.subtle.decrypt({ // Keeping aesKey scope as private as possible... - name: 'AES-CBC', iv: new Uint8Array(shim.Buffer.from(json.iv, 'utf8')) - }, aes, new Uint8Array(shim.Buffer.from(json.ct, 'utf8')))) - const r = parse(new shim.TextDecoder('utf8').decode(ct)) - - if(cb){ cb(r) } + SEA.decrypt = SEA.decrypt || (async (data, pair, cb, opt) => { try { + 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; + } + var json = S.parse(data); + var buf, bufiv, bufct; try{ + buf = shim.Buffer.from(json.s, opt.encode || 'base64'); + bufiv = shim.Buffer.from(json.iv, opt.encode || 'base64'); + bufct = shim.Buffer.from(json.ct, opt.encode || 'base64'); + var ct = await aeskey(key, buf, opt).then((aes) => (/*shim.ossl ||*/ shim.subtle).decrypt({ // Keeping aesKey scope as private as possible... + name: opt.name || 'AES-GCM', iv: new Uint8Array(bufiv) + }, aes, new Uint8Array(bufct))); + }catch(e){ + if('utf8' === opt.encode){ throw "Could not decrypt" } + if(SEA.opt.fallback){ + opt.encode = 'utf8'; + return await SEA.decrypt(data, pair, cb, opt); + } + } + var r = S.parse(new shim.TextDecoder('utf8').decode(ct)); + if(cb){ try{ cb(r) }catch(e){console.log(e)} } return r; } catch(e) { + console.log(e); SEA.err = e; + if(SEA.throw){ throw e } if(cb){ cb() } return; - }} + }}); module.exports = SEA.decrypt; })(USE, './decrypt'); ;USE(function(module){ - // Old Code... - const { - crypto, - subtle, - ossl, - random: getRandomBytes, - TextEncoder, - TextDecoder - } = USE('./shim') - const EasyIndexedDB = USE('./indexed') - const Buffer = USE('./buffer') - var settings = USE('./settings'); - const { - pbkdf2: pbKdf2, - ecdsa: { pair: ecdsaKeyProps, sign: ecdsaSignProps }, - ecdh: ecdhKeyProps, - jwk: keysToEcdsaJwk - } = USE('./settings') - const sha1hash = USE('./sha1') - const sha256hash = USE('./sha256') - const recallCryptoKey = USE('./remember') - const parseProps = USE('./parse') + 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, opt) => { try { + opt = opt || {}; + if(!pair || !pair.epriv || !pair.epub){ + pair = await SEA.I(null, {what: key, how: 'secret', why: opt.why}); + } + var pub = key.epub || key; + var epub = pair.epub; + var epriv = pair.epriv; + var ecdhSubtle = shim.ossl || shim.subtle; + var pubKeyData = keysToEcdhJwk(pub); + var props = Object.assign(S.ecdh, { public: await ecdhSubtle.importKey(...pubKeyData, true, []) }); + var privKeyData = keysToEcdhJwk(epub, epriv); + var derived = await ecdhSubtle.importKey(...privKeyData, false, ['deriveKey']).then(async (privKey) => { + // privateKey scope doesn't leak out from here! + var derivedKey = await ecdhSubtle.deriveKey(props, privKey, { name: 'AES-GCM', length: 256 }, true, [ 'encrypt', 'decrypt' ]); + return ecdhSubtle.exportKey('jwk', derivedKey).then(({ k }) => k); + }) + var r = derived; + if(cb){ try{ cb(r) }catch(e){console.log(e)} } + return r; + } catch(e) { + console.log(e); + SEA.err = e; + if(SEA.throw){ throw e } + if(cb){ cb() } + return; + }}); + // can this be replaced with settings.jwk? + var keysToEcdhJwk = (pub, d) => { // d === priv + //var [ x, y ] = Buffer.from(pub, 'base64').toString('utf8').split(':') // old + var [ x, y ] = pub.split('.') // new + var jwk = d ? { d: d } : {} + return [ // Use with spread returned value... + 'jwk', + Object.assign( + jwk, + { x: x, y: y, kty: 'EC', crv: 'P-256', ext: true } + ), // ??? refactor + S.ecdh + ] + } + + module.exports = SEA.secret; + })(USE, './secret'); + + ;USE(function(module){ + 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 = getRandomBytes; - - // This is easy way to use IndexedDB, all methods are Promises - // Note: Not all SEA interfaces have to support this. - SEA.EasyIndexedDB = EasyIndexedDB; + SEA.random = SEA.random || shim.random; // This is Buffer used in SEA and usable from Gun/SEA application also. // For documentation see https://nodejs.org/api/buffer.html - SEA.Buffer = Buffer; + SEA.Buffer = SEA.Buffer || USE('./buffer'); // These SEA functions support now ony Promises or // async/await (compatible) code, use those like Promises. @@ -550,11 +649,11 @@ // Creates a wrapper library around Web Crypto API // for various AES, ECDSA, PBKDF2 functions we called above. // Calculate public key KeyID aka PGPv4 (result: 8 bytes as hex string) - SEA.keyid = async (pub) => { + SEA.keyid = SEA.keyid || (async (pub) => { try { // base64('base64(x):base64(y)') => Buffer(xy) const pb = Buffer.concat( - Buffer.from(pub, 'base64').toString('utf8').split(':') + pub.replace(/-/g, '+').replace(/_/g, '/').split('.') .map((t) => Buffer.from(t, 'base64')) ) // id is PGPv4 compliant raw key @@ -568,39 +667,7 @@ console.log(e) throw e } - } - // Derive shared secret from other's pub and my epub/epriv - SEA.derive = async (pub, { epub, epriv }) => { - try { - const ecdhSubtle = ossl || subtle - const keysToEcdhJwk = (pub, d) => { // d === priv - const [ x, y ] = Buffer.from(pub, 'base64').toString('utf8').split(':') - const jwk = d ? { d } : {} - return [ // Use with spread returned value... - 'jwk', - { ...jwk, x, y, kty: 'EC', crv: 'P-256', ext: true }, - ecdhKeyProps - ] - } - const pubKeyData = keysToEcdhJwk(pub) - const props = { - ...ecdhKeyProps, - public: await ecdhSubtle.importKey(...pubKeyData, true, []) - } - const privKeyData = keysToEcdhJwk(epub, epriv) - const derived = await ecdhSubtle.importKey(...privKeyData, false, ['deriveKey']) - .then(async (privKey) => { - // privateKey scope doesn't leak out from here! - const derivedKey = await ecdhSubtle.deriveKey(props, privKey, { name: 'AES-CBC', length: 256 }, true, [ 'encrypt', 'decrypt' ]) - return ecdhSubtle.exportKey('jwk', derivedKey).then(({ k }) => k) - }) - return derived - } catch (e) { - console.log(e) - throw e - } - } - + }); // all done! // Obviously it is missing MANY necessary features. This is only an alpha release. // Please experiment with it, audit what I've done so far, and complain about what needs to be added. @@ -610,655 +677,336 @@ // But all other behavior needs to be equally easy, like opinionated ways of // Adding friends (trusted public keys), sending private messages, etc. // Cheers! Tell me what you think. - var Gun = (SEA.window||{}).Gun || require('./gun'); + var Gun = (SEA.window||{}).Gun || USE('./gun', 1); Gun.SEA = SEA; - SEA.Gun = Gun; + SEA.GUN = SEA.Gun = Gun; 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/${alias}`).get((rat, rev) => { - rev.off(); - if (!rat.put) { - // 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(rat.put, (at, pub) => { - if (!pub.slice || 'pub/' !== pub.slice(0, 4)) { - // TODO: ... this would then be .filter((at, pub)) - return - } - ++c - // grab the account associated with this public key. - gunRoot.get(pub).get((at, ev) => { - pub = pub.slice(4) - ev.off() - --c - if (at.put){ - aliases.push({ pub, at }) - } - if (!c && (c = -1)) { - resolve(aliases) - } - }) - }) - if (!c) { - reject({ err: 'Public key does not exist!' }) - } - }) - }) - module.exports = queryGunAliases - })(USE, './query'); + var Gun = USE('./sea').Gun; + Gun.chain.then = function(cb){ + var gun = this, p = (new Promise(function(res, rej){ + gun.once(res); + })); + return cb? p.then(cb) : p; + } + })(USE, './then'); ;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(({ pub, at: { put } = {} } = {}) => !!pub && !!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 [ user ] = await Promise.all(aliases.map(async ({ at, pub }) => { - // 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. - // SEA.verify(at.put.auth, pub).then(function(auth){ - try { - const proof = await SEA.work(pass, auth.s) - const props = { pub, proof, 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 - const sea = await SEA.decrypt(auth.ek, proof) - if (!sea) { - err = 'Failed to decrypt secret!' - 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, epriv } = sea - const { epub } = at.put - // TODO: 'salt' needed? - err = null - return Object.assign(props, { priv, salt, epub, epriv }) - } catch (e) { - err = 'Failed to decrypt secret!' - throw { err } - } - })) + var then = USE('./then'); - if (!user) { - throw { err: err || 'Public key does not exist!' } - } - return user + function User(root){ + this._ = {$: this}; } - module.exports = authenticate; - })(USE, './authenticate'); - - ;USE(function(module){ - // TODO: BUG! `SEA` needs to be USED! - const Gun = (typeof window !== 'undefined' ? window : global).Gun || USE('gun/gun') - const authsettings = USE('./settings') - const SEA = USE('./sea'); - //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, alias: id } = props - const remember = { alias, 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 }) - } - - 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 Gun = (typeof window !== 'undefined' ? window : global).Gun || USE('gun/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 || {} - const { validity: exp } = authsettings // 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, iat, exp, remember }) - const { pub, epub, sea: { priv, epriv } } = user - const key = { pub, priv, epub, 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._ - // add our credentials in-memory only to our root gun instance - user._ = key.at.gun._ - // so that way we can use the credentials to encrypt/decrypt data - // that is input/output through gun (see below) - const { pub, priv, epub, epriv } = key - user._.is = user.is = {alias: alias, pub: pub}; - Object.assign(user._, { alias, pub, epub, sea: { pub, priv, epub, 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. - if(typeof window !== 'undefined'){ - var tmp = window.sessionStorage; - if(tmp && gunRoot._.opt.remember){ - window.sessionStorage.alias = alias; - window.sessionStorage.tmp = key; - } - } - try { - gunRoot._.on('auth', user._) - } 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){ - // TODO: BUG! `SEA` needs to be USEd! - const Gun = (typeof window !== 'undefined' ? window : global).Gun || USE('gun/gun') - - 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 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, proof }) - } else { - Gun.log('Authentication expired!') - } - } - // We're not gonna give proof to hook! - const hooked = authsettings.hook({ alias, iat, exp, 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, pub }) => { - const readStorageData = async (args) => { - const props = args || parseProps(await SEA.verify(remember, pub, true)) - let { pin, alias: aLias } = props - - 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, data } - } - // got pub, try auth with pin & alias :: or unwrap Storage data... - const { data, pin: newPin } = await readStorageData(pin && { pin, alias }) - const { proof } = data || {} - - 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 - const sea = await SEA.decrypt(auth, proof) - if (!sea) { - err = 'Failed to decrypt private key!' - return - } - const { priv, epriv } = sea - const { epub } = at.put - // Success! we've found our private data! - err = null - return { proof, at, pin: newPin, key: { pub, priv, epriv, 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, 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.gun){ - delete user.gun.is; - } - // Let's use default - gunRoot.user(); - // Removes persisted authentication & CryptoKeys - try { - await authPersist({ alias }) - } catch (e) {} //eslint-disable-line no-empty - return { ok: 0 } - } - module.exports = authLeave - })(USE, './leave'); - - ;USE(function(module){ - // How does it work? - // TODO: Bug! Need to include SEA! - //const Gun = (typeof window !== 'undefined' ? window : global).Gun || USE('gun/gun') - - const SEA = USE('./sea') - const authRecall = USE('./recall') - const authsettings = USE('./settings') - const authenticate = USE('./authenticate') - const finalizeLogin = USE('./login') - const authLeave = USE('./leave') - const { recall: _initial_authsettings } = USE('./settings') - const Gun = SEA.Gun; + User.prototype = (function(){ function F(){}; F.prototype = Gun.chain; return new F() }()) // Object.create polyfill + User.prototype.constructor = User; // let's extend the gun chain with a `user` function. // only one user can be logged in at a time, per gun instance. - Gun.chain.user = function() { - const gunRoot = this.back(-1) // always reference the root gun instance. - const user = gunRoot._.user || (gunRoot._.user = gunRoot.chain()); // create a user context. - // then methods... - [ 'create', // factory - 'auth', // login - 'leave', // logout - 'delete', // account delete - 'recall', // existing auth boostrap - 'alive' // keep/check auth validity - ].map((method)=> user[method] = User[method]) - return user // return the user! + Gun.chain.user = function(pub){ + var gun = this, root = gun.back(-1), user; + if(pub){ return root.get('~'+pub) } + if(user = root.back('user')){ return user } + var root = (root._), at = root, uuid = at.opt.uuid || Gun.state.lex; + (at = (user = at.user = gun.chain(new User))._).opt = {}; + at.opt.uuid = function(cb){ + var id = uuid(), pub = root.user; + if(!pub || !(pub = pub.is) || !(pub = pub.pub)){ return id } + id = id + '~' + pub + '.'; + if(cb && cb.call){ cb(null, id) } + return id; + } + return user; } - var u; - function User(){} + Gun.User = User; + module.exports = User; + })(USE, './user'); + + ;USE(function(module){ + // TODO: This needs to be split into all separate functions. + // Not just everything thrown into 'create'. + + var SEA = USE('./sea'); + var User = USE('./user'); + var authsettings = USE('./settings'); + var Gun = SEA.Gun; + + var noop = function(){}; + // Well first we have to actually create a user. That is what this function does. - Object.assign(User, { - async create(username, pass, cb) { - const gunRoot = this.back(-1) - var gun = this, cat = (gun._); - cb = cb || function(){}; - if(cat.ing){ - cb({err: Gun.log("User is already being created or authenticated!"), wait: true}); - return 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; } - cat.ing = true; - var p = new Promise((resolve, reject) => { // Because no Promises or async - // 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(`alias/${username}`).get(async (at, ev) => { - ev.off() - if (at.put) { - // 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 }) + act.salt = Gun.text.random(64); // pseudo-randomly create a salt, then use PBKDF2 function to extend the password with it. + SEA.work(pass, act.salt, act.b); // this will take some short amount of time to produce a proof, which slows brute force attacks. + } + act.b = function(proof){ + act.proof = proof; + SEA.pair(act.c); // now we have generated a brand new ECDSA key pair for the user account. + } + act.c = function(pair){ var tmp; + act.pair = pair || {}; + if(tmp = cat.root.user){ + tmp._.sea = pair; + tmp.is = {pub: pair.pub, epub: pair.epub, alias: alias}; + } + // the user's public key doesn't need to be signed. But everything else needs to be signed with it! // we have now automated it! clean up these extra steps now! + act.data = {pub: pair.pub}; + act.d(); + } + act.d = function(){ + act.data.alias = alias; + act.e(); + } + act.e = function(){ + act.data.epub = act.pair.epub; + SEA.encrypt({priv: act.pair.priv, epriv: act.pair.epriv}, act.proof, act.f, {raw:1}); // to keep the private key safe, we AES encrypt it with the proof of work! + } + act.f = function(auth){ + act.data.auth = JSON.stringify({ek: auth, s: act.salt}); + act.g(act.data.auth); + } + act.g = function(auth){ var tmp; + act.data.auth = act.data.auth || auth; + root.get(tmp = '~'+act.pair.pub).put(act.data); // awesome, now we can actually save the user with their public key as their ID. + root.get('~@'+alias).put(Gun.obj.put({}, tmp, Gun.val.link.ify(tmp))); // next up, we want to associate the alias with the public key. So we add it to the alias list. + setTimeout(function(){ // we should be able to delete this now, right? + cat.ing = false; + cb({ok: 0, pub: act.pair.pub}); // callback that the user has been created. (Note: ok = 0 because we didn't wait for disk to ack) + if(noop === cb){ gun.auth(alias, pass) } // if no callback is passed, auto-login after signing up. + },10); + } + root.get('~@'+alias).once(act.a); + return gun; + } + // now that we have created a user, we want to authenticate them! + User.prototype.auth = function(alias, pass, cb, opt){ + 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}); + return gun; + } + cat.ing = true; + opt = opt || {}; + var pair = (alias && (alias.pub || alias.epub))? alias : (pass && (pass.pub || pass.epub))? pass : null; + var act = {}, u; + act.a = function(data){ + if(!data){ return act.b() } + if(!data.pub){ + var tmp = []; + Gun.node.is(data, function(v){ tmp.push(v) }) + return act.b(tmp); + } + if(act.name){ return act.f(data) } + act.c((act.data = data).auth); + } + act.b = function(list){ + var get = (act.list = (act.list||[]).concat(list||[])).shift(); + if(u === get){ + if(act.name){ return act.err('Your user account is not published for dApps to access, please consider syncing it online, or allowing local access by adding your device as a peer.') } + return act.err('Wrong user or password.') + } + root.get(get).once(act.a); + } + act.c = function(auth){ + if(u === auth){ return act.b() } + if(Gun.text.is(auth)){ return act.c(Gun.obj.ify(auth)) } // in case of legacy + SEA.work(pass, (act.auth = auth).s, act.d, act.enc); // the proof of work is evidence that we've spent some time/effort trying to log in, this slows brute force. + } + act.d = function(proof){ + SEA.decrypt(act.auth.ek, proof, act.e, act.enc); + } + act.e = function(half){ + if(u === half){ + if(!act.enc){ // try old format + act.enc = {encode: 'utf8'}; + return act.c(act.auth); + } act.enc = null; // end backwards + return act.b(); + } + 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; + try{if(pass && !Gun.obj.has(Gun.obj.ify(cat.root.graph['~'+pair.pub].auth), ':')){ opt.shuffle = opt.change = pass; } }catch(e){} // migrate UTF8 & Shuffle! + opt.change? act.z() : cb(at); + if(SEA.window && ((gun.back('user')._).opt||opt).remember){ + // TODO: this needs to be modular. + try{var sS = {}; + sS = window.sessionStorage; + sS.recall = true; + sS.alias = alias; + sS.tmp = pass; + }catch(e){} + } + 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, {raw:1}); + } + act.x = function(auth){ + act.w(JSON.stringify({ek: auth, s: act.salt})); + } + act.w = function(auth){ + if(opt.shuffle){ // delete in future! + console.log('migrate core account from UTF8 & shuffle'); + var tmp = Gun.obj.to(act.data); + Gun.obj.del(tmp, '_'); + tmp.auth = auth; + root.get('~'+act.pair.pub).put(tmp); + } // end delete + root.get('~'+act.pair.pub).get('auth').put(auth, cb); + } + act.err = function(e){ + var ack = {err: Gun.log(e || 'User cannot be found!')}; + cat.ing = false; + cb(ack); + } + act.plugin = function(name){ + if(!(act.name = name)){ return act.err() } + var tmp = [name]; + if('~' !== name[0]){ + tmp[1] = '~'+name; + tmp[2] = '~@'+name; + } + act.b(tmp); + } + if(pair){ + act.g(pair); + } else + if(alias){ + root.get('~@'+alias).once(act.a); + } else + if(!alias && !pass){ + SEA.name(act.plugin); + } + return gun; + } + User.prototype.pair = function(){ + console.log("user.pair() IS DEPRECATED AND WILL BE DELETED!!!"); + var user = this; + if(!user.is){ return false } + return user._.sea; + } + User.prototype.leave = function(opt, cb){ + var gun = this, user = (gun.back(-1)._).user; + if(user){ + delete user.is; + delete user._.is; + delete user._.sea; + } + if(SEA.window){ + try{var sS = {}; + sS = window.sessionStorage; + delete sS.alias; + delete sS.tmp; + delete sS.recall; + }catch(e){}; + } + return gun; + } + // If authenticated user wants to delete his/her account, let's support it! + User.prototype.delete = async function(alias, pass, cb){ + var gun = this, root = gun.back(-1), user = gun.back('user'); + try { + 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); + } + return gun; + } + User.prototype.recall = function(opt, cb){ + var gun = this, root = gun.back(-1), tmp; + opt = opt || {}; + if(opt && opt.sessionStorage){ + if(SEA.window){ + try{var sS = {}; + sS = window.sessionStorage; + if(sS){ + (root._).opt.remember = true; + ((gun.back('user')._).opt||opt).remember = true; + if(sS.recall || (sS.alias && sS.tmp)){ + root.user().auth(sS.alias, sS.tmp, cb); } - 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, priv, epriv } = pairs - // 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, 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, pub, epub, auth } - const tmp = `pub/${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(`alias/${username}`).put(Gun.obj.put({}, tmp, Gun.val.rel.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! - async auth(alias, pass, cb, opt) { - const opts = opt || (typeof cb !== 'function' && cb) - let { pin, newpass } = opts || {} - 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; - } - cat.ing = true; - - if (!pass && pin) { - try { - var r = await authRecall(gunRoot, { alias, 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; } - } - - 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; - } - - try { - const keys = await authenticate(alias, pass, gunRoot) - if (!keys) { - return putErr('Auth attempt failed!')({ message: 'No keys' }) - } - const { pub, priv, epub, epriv } = keys - // 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, epriv }, { pub, key, set: true }) - .then((auth) => SEA.sign({ salt, auth }, keys)) - ) - const signedEpub = await SEA.sign(epub, keys) - const signedAlias = await SEA.sign(alias, keys) - const user = { - pub, - alias: signedAlias, - auth: encSigAuth, - epub: signedEpub - } - // awesome, now we can update the user using public key ID. - gunRoot.get(`pub/${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 }) - login.catch(putErr('Finalizing login failed!')) - return cat.ing = false, cb(await login), gun; - } - } catch (e) { - return putErr('Auth attempt failed!')(e) + }catch(e){} } return gun; - }, - async leave() { - return await authLeave(this.back(-1)) - }, - // If authenticated user wants to delete his/her account, let's support it! - async delete(alias, pass) { - const gunRoot = this.back(-1) - try { - const { pub } = await authenticate(alias, pass, gunRoot) - await authLeave(gunRoot, alias) - // Delete user data - gunRoot.get(`pub/${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??? - } catch (e) { - Gun.log('User.delete failed! Error:', e) - throw e // TODO: proper error codes??? - } - }, - // If authentication is to be remembered over reloads or browser closing, - // set validity time in minutes. - async recall(setvalidity, options) { - 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); - } - } } - return this; + /* + TODO: copy mhelander's expiry code back in. + Although, we should check with community, + should expiry be core or a plugin? + */ + 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? - return await authRecall(gunRoot) - } 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 } - } - }, - async alive() { - const gunRoot = this.back(-1) - try { - // All is good. Should we do something more with actual recalled data? - await authRecall(gunRoot) - return gunRoot._.user._ - } catch (e) { - const err = 'No session!' - Gun.log(err) - throw { err } - } + User.prototype.alive = async function(){ + const gunRoot = this.back(-1) + try { + // All is good. Should we do something more with actual recalled data? + await authRecall(gunRoot) + return gunRoot._.user._ + } catch (e) { + const err = 'No session!' + Gun.log(err) + throw { err } } - }) - Gun.chain.trust = function(user) { + } + User.prototype.trust = async function(user){ // TODO: BUG!!! SEA `node` read listener needs to be async, which means core needs to be async too. //gun.get('alice').get('age').trust(bob); if (Gun.is(user)) { @@ -1267,26 +1015,56 @@ }) } } + 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.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); + if(!sec){ + sec = SEA.random(16).toString(); + enc = await SEA.encrypt(sec, pair); + user.get('trust').get(pair.pub).get(path).put(enc); + } + var pub = to.get('pub').then(); + var epub = to.get('epub').then(); + pub = await pub; epub = await epub; + var dh = await SEA.secret(epub, pair); + enc = await SEA.encrypt(sec, dh); + user.get('trust').get(pub).get(path).put(enc, cb); + }()); + return gun; + } + 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.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); + if(!sec){ + sec = SEA.random(16).toString(); + enc = await SEA.encrypt(sec, pair); + user.get('trust').get(pair.pub).get(path).put(enc); + } + enc = await SEA.encrypt(data, sec); + gun.put(enc, cb); + }()); + return gun; + } module.exports = User - })(USE, './user'); + })(USE, './create'); ;USE(function(module){ - const Gun = (typeof window !== 'undefined' ? window : global).Gun || USE('gun/gun') const SEA = USE('./sea') + const Gun = SEA.Gun; // After we have a GUN extension to make user registration/login easy, we then need to handle everything else. // We do this with a GUN adapter, we first listen to when a gun instance is created (and when its options change) Gun.on('opt', function(at){ if(!at.sea){ // only add SEA once per instance, on the "at" context. at.sea = {own: {}}; - var uuid = at.opt.uuid || Gun.state.lex; - at.opt.uuid = function(cb){ // TODO: consider async/await and drop callback pattern... - var id = uuid(), pub = at.user; - if(!pub || !(pub = at.user._.sea) || !(pub = pub.pub)){ return id } - id = id + '~' + pub; - if(cb){ cb(null, id) } - return id; - } at.on('in', security, at); // now listen to all input data, acting as a firewall. at.on('out', signature, at); // and output listeners, to encrypt outgoing data. at.on('node', each, at); @@ -1301,7 +1079,7 @@ // Now NOTE! Some data is "system" data, not user data. Example: List of public keys, aliases, etc. // This data is self-enforced (the value can only match its ID), but that is handled in the `security` function. // From the self-enforced data, we can see all the edges in the graph that belong to a public key. - // Example: pub/ASDF is the ID of a node with ASDF as its public key, signed alias and salt, and + // Example: ~ASDF is the ID of a node with ASDF as its public key, signed alias and salt, and // its encrypted private key, but it might also have other signed values on it like `profile = ` edge. // Using that directed edge's ID, we can then track (in memory) which IDs belong to which keys. // Here is a problem: Multiple public keys can "claim" any node's ID, so this is dangerous! @@ -1310,26 +1088,31 @@ function each(msg){ // TODO: Warning: Need to switch to `gun.on('node')`! Do not use `Gun.on('node'` in your apps! // NOTE: THE SECURITY FUNCTION HAS ALREADY VERIFIED THE DATA!!! // WE DO NOT NEED TO RE-VERIFY AGAIN, JUST TRANSFORM IT TO PLAINTEXT. - var to = this.to, vertex = (msg.gun._).put, c = 0, d; - Gun.node.is(msg.put, function(val, key, node){ c++; // for each property on the node - // TODO: consider async/await use here... - SEA.verify(val, false).then(function(data){ c--; // false just extracts the plain data. - node[key] = val = data; // transform to plain value. + var to = this.to, vertex = (msg.$._).put, c = 0, d; + Gun.node.is(msg.put, function(val, key, node){ + // only process if SEA formatted? + var tmp = Gun.obj.ify(val) || noop; + if(u !== tmp[':']){ + node[key] = SEA.opt.unpack(tmp); + return; + } + if(!SEA.opt.check(val)){ return } + c++; // for each property on the node + SEA.verify(val, false, function(data){ c--; // false just extracts the plain data. + node[key] = SEA.opt.unpack(data, key, node);; // transform to plain value. if(d && !c && (c = -1)){ to.next(msg) } }); }); - d = true; - if(d && !c){ to.next(msg) } - return; + if((d = true) && !c){ to.next(msg) } } // signature handles data output, it is a proxy to the security function. function signature(msg){ - if(msg.user){ + if((msg._||noop).user){ return this.to.next(msg); } var ctx = this.as; - msg.user = ctx.user; + (msg._||(msg._=function(){})).user = ctx.user; security.call(this, msg); } @@ -1342,10 +1125,11 @@ // 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('alias' === soul){ // Allow reading the list of usernames/aliases in the system? return to.next(msg); // yes. } else - if('alias/' === soul.slice(0,6)){ // Allow reading the list of public keys associated with an alias? + if('~@' === soul.slice(0,2)){ // Allow reading the list of public keys associated with an alias? return to.next(msg); // yes. } else { // Allow reading everything? return to.next(msg); // yes // TODO: No! Make this a callback/event that people can filter on. @@ -1362,104 +1146,98 @@ each.way = function(val, key){ var soul = this.soul, node = this.node, tmp; if('_' === key){ return } // ignore meta data - if('alias' === soul){ // special case for shared system data, the list of aliases. + if('~@' === soul){ // special case for shared system data, the list of aliases. each.alias(val, key, node, soul); return; } - if('alias/' === soul.slice(0,6)){ // special case for shared system data, the list of public keys for an alias. + if('~@' === soul.slice(0,2)){ // special case for shared system data, the list of public keys for an alias. each.pubs(val, key, node, soul); return; } - if('pub/' === soul.slice(0,4)){ // special case, account data for a public key. - each.pub(val, key, node, soul, soul.slice(4), msg.user); return; + if('~' === soul.slice(0,1) && 2 === (tmp = soul.slice(1)).split('.').length){ // special case, account data for a public key. + each.pub(val, key, node, soul, tmp, (msg._||noop).user); return; } - each.any(val, key, node, soul, msg.user); return; + each.any(val, key, node, soul, (msg._||noop).user); return; return each.end({err: "No other data allowed!"}); }; - each.alias = function(val, key, node, soul){ // Example: {_:#alias, alias/alice: {#alias/alice}} + each.alias = function(val, key, node, soul){ // Example: {_:#~@, ~@alice: {#~@alice}} if(!val){ return each.end({err: "Data must exist!"}) } // data MUST exist - if('alias/'+key === Gun.val.rel.is(val)){ return check['alias'+key] = 0 } // in fact, it must be EXACTLY equal to itself + if('~@'+key === Gun.val.link.is(val)){ return check['alias'+key] = 0 } // in fact, it must be EXACTLY equal to itself each.end({err: "Mismatching alias."}); // if it isn't, reject. }; - each.pubs = function(val, key, node, soul){ // Example: {_:#alias/alice, pub/asdf: {#pub/asdf}} + each.pubs = function(val, key, node, soul){ // Example: {_:#~@alice, ~asdf: {#~asdf}} if(!val){ return each.end({err: "Alias must exist!"}) } // data MUST exist - if(key === Gun.val.rel.is(val)){ return check['pubs'+soul+key] = 0 } // and the ID must be EXACTLY equal to its property + if(key === Gun.val.link.is(val)){ return check['pubs'+soul+key] = 0 } // and the ID must be EXACTLY equal to its property each.end({err: "Alias must match!"}); // that way nobody can tamper with the list of public keys. }; - each.pub = function(val, key, node, soul, pub, user){ // Example: {_:#pub/asdf, hello:SEA['world',fdsa]} + each.pub = function(val, key, node, soul, pub, user){ var tmp; // Example: {_:#~asdf, hello:'world'~fdsa}} if('pub' === key){ if(val === pub){ return (check['pub'+soul+key] = 0) } // the account MUST match `pub` property that equals the ID of the public key. return each.end({err: "Account must match!"}); } check['user'+soul+key] = 1; - if(user && (user = user._) && user.sea && pub === user.pub){ - //var id = Gun.text.random(3); - SEA.sign(val, user.sea).then(function(data){ var rel; - if(rel = Gun.val.rel.is(val)){ + if(Gun.is(msg.$) && user && user.is && pub === user.is.pub){ + SEA.sign(SEA.opt.prep(tmp = SEA.opt.parse(val), key, node, soul), (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; } - node[key] = data; + node[key] = JSON.stringify({':': SEA.opt.unpack(data.m), '~': data.s}); check['user'+soul+key] = 0; each.end({ok: 1}); - }); - // TODO: Handle error!!!! + }, {check: SEA.opt.pack(tmp, key, node, soul), raw: 1}); return; } - // TODO: consider async/await and drop callback pattern... - SEA.verify(val, pub).then(function(data){ var rel, tmp; + SEA.verify(SEA.opt.pack(val,key,node,soul), pub, function(data){ var rel, tmp; + data = SEA.opt.unpack(data, key, node); if(u === data){ // make sure the signature matches the account it claims to be on. return each.end({err: "Unverified data."}); // reject any updates that are signed with a mismatched account. } - if((rel = Gun.val.rel.is(data)) && (tmp = rel.split('~')) && 2 === tmp.length){ - if(pub === tmp[1]){ - (at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true; - } + if((rel = Gun.val.link.is(data)) && pub === SEA.opt.pub(rel)){ + (at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true; } check['user'+soul+key] = 0; each.end({ok: 1}); }); }; each.any = function(val, key, node, soul, user){ var tmp, pub; - if(!user || !(user = user._) || !(user = user.sea)){ - if((tmp = soul.split('~')) && 2 == tmp.length){ - check['any'+soul+key] = 1; - SEA.verify(val, (pub = tmp[1])).then(function(data){ var rel; - if(!data){ return each.end({err: "Mismatched owner on '" + key + "'."}) } - if((rel = Gun.val.rel.is(data)) && (tmp = rel.split('~')) && 2 === tmp.length){ - if(pub === tmp[1]){ - (at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true; - } - } - check['any'+soul+key] = 0; - each.end({ok: 1}); - }); + if(!(pub = SEA.opt.pub(soul))){ + if(at.opt.secure){ + each.end({err: "Soul is missing public key at '" + key + "'."}); return; } + // TODO: Ask community if should auto-sign non user-graph data. check['any'+soul+key] = 1; at.on('secure', function(msg){ this.off(); check['any'+soul+key] = 0; + if(at.opt.secure){ msg = null } each.end(msg || {err: "Data cannot be modified."}); }).on.on('secure', msg); //each.end({err: "Data cannot be modified."}); return; } - if(!(tmp = soul.split('~')) || 2 !== tmp.length){ - each.end({err: "Soul is missing public key at '" + key + "'."}); + if(Gun.is(msg.$) && user && user.is && pub === user.is.pub){ + /*var other = Gun.obj.map(at.sea.own[soul], function(v, p){ + if((user.is||{}).pub !== p){ return p } + }); + if(other){ + each.any(val, key, node, soul); + return; + }*/ + check['any'+soul+key] = 1; + SEA.sign(SEA.opt.prep(tmp = SEA.opt.parse(val), key, node, soul), (user._).sea, function(data){ + if(u === data){ return each.end({err: 'My signature fail.'}) } + node[key] = JSON.stringify({':': SEA.opt.unpack(data.m), '~': data.s}); + check['any'+soul+key] = 0; + each.end({ok: 1}); + }, {check: SEA.opt.pack(tmp, key, node, soul), raw: 1}); return; } - var pub = tmp[1]; - if(pub !== user.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(other){ - each.any(val, key, node, soul); - return; - }*/ check['any'+soul+key] = 1; - SEA.sign(val, user).then(function(data){ - node[key] = data; + SEA.verify(SEA.opt.pack(val,key,node,soul), pub, function(data){ var rel; + data = SEA.opt.unpack(data, key, node); + if(u === data){ return each.end({err: "Mismatched owner on '" + key + "'."}) } // thanks @rogowski ! + if((rel = Gun.val.link.is(data)) && pub === SEA.opt.pub(rel)){ + (at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true; + } check['any'+soul+key] = 0; each.end({ok: 1}); }); @@ -1467,13 +1245,14 @@ 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 } if(Gun.obj.map(check, function(no){ if(no){ return true } })){ return } + (msg._||{}).user = at.user || security; // already been through firewall, does not need to again on out. to.next(msg); }; Gun.obj.map(msg.put, each.node); @@ -1482,6 +1261,42 @@ } to.next(msg); // pass forward any data we do not know how to handle or process (this allows custom security protocols). } + SEA.opt.pub = function(s){ + if(!s){ return } + s = s.split('~'); + if(!s || !(s = s[1])){ return } + s = s.split('.'); + if(!s || 2 > s.length){ return } + s = s.slice(0,2).join('.'); + return s; + } + SEA.opt.prep = function(d,k, n,s){ // prep for signing + return {'#':s,'.':k,':':SEA.opt.parse(d),'>':Gun.state.is(n, k)}; + } + SEA.opt.pack = function(d,k, n,s){ // pack for verifying + if(SEA.opt.check(d)){ return d } + var meta = (Gun.obj.ify(d)||noop), sig = meta['~']; + return sig? {m: {'#':s,'.':k,':':meta[':'],'>':Gun.state.is(n, k)}, s: sig} : d; + } + SEA.opt.unpack = function(d, k, n){ var tmp; + if(u === d){ return } + if(d && (u !== (tmp = d[':']))){ return tmp } + if(!k || !n){ return } + if(d === n[k]){ return d } + if(!SEA.opt.check(n[k])){ return d } + var soul = Gun.node.soul(n), s = Gun.state.is(n, k); + if(d && 4 === d.length && soul === d[0] && k === d[1] && fl(s) === fl(d[3])){ + return d[2]; + } + if(s < SEA.opt.shuffle_attack){ + return d; + } + } + SEA.opt.shuffle_attack = 1546329600000; // Jan 1, 2019 + var noop = function(){}, u; + var fl = Math.floor; // TODO: Still need to fix inconsistent state issue. + var rel_is = Gun.val.rel.is; + // TODO: Potential bug? If pub/priv key starts with `-`? IDK how possible. })(USE, './index'); }()); \ No newline at end of file diff --git a/sea/aescbc.js b/sea/aeskey.js similarity index 76% rename from sea/aescbc.js rename to sea/aeskey.js index 7fa3a680..f003a38e 100644 --- a/sea/aescbc.js +++ b/sea/aeskey.js @@ -2,11 +2,12 @@ var shim = require('./shim'); var sha256hash = require('./sha256'); - const importGen = async (key, salt) => { + const importGen = async (key, salt, opt) => { //const combo = shim.Buffer.concat([shim.Buffer.from(key, 'utf8'), salt || shim.random(8)]).toString('utf8') // old + var opt = opt || {}; const combo = key + (salt || shim.random(8)).toString('utf8'); // new const hash = shim.Buffer.from(await sha256hash(combo), 'binary') - return await shim.subtle.importKey('raw', new Uint8Array(hash), 'AES-CBC', false, ['encrypt', 'decrypt']) + return await shim.subtle.importKey('raw', new Uint8Array(hash), opt.name || 'AES-GCM', false, ['encrypt', 'decrypt']) } module.exports = importGen; \ No newline at end of file diff --git a/sea/array.js b/sea/array.js index 2e6e0c25..fecdde5d 100644 --- a/sea/array.js +++ b/sea/array.js @@ -3,8 +3,8 @@ function SeaArray() {} Object.assign(SeaArray, { from: Array.from }) SeaArray.prototype = Object.create(Array.prototype) - SeaArray.prototype.toString = function(enc = 'utf8', start = 0, end) { - const { length } = this + SeaArray.prototype.toString = function(enc, start, end) { enc = enc || 'utf8'; start = start || 0; + const length = this.length if (enc === 'hex') { const buf = new Uint8Array(this) return [ ...Array(((end && (end + 1)) || length) - start).keys()] diff --git a/sea/authenticate.js b/sea/authenticate.js deleted file mode 100644 index 9a9f7b6f..00000000 --- a/sea/authenticate.js +++ /dev/null @@ -1,55 +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(({ pub, at: { put } = {} } = {}) => !!pub && !!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 [ user ] = await Promise.all(aliases.map(async ({ at, pub }) => { - // 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. - // SEA.verify(at.put.auth, pub).then(function(auth){ - try { - const proof = await SEA.work(pass, auth.s) - const props = { pub, proof, 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 - const sea = await SEA.decrypt(auth.ek, proof) - if (!sea) { - err = 'Failed to decrypt secret!' - 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, epriv } = sea - const { epub } = at.put - // TODO: 'salt' needed? - err = null - return Object.assign(props, { priv, salt, epub, epriv }) - } catch (e) { - err = 'Failed to decrypt secret!' - throw { err } - } - })) - - 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 76411ee9..1854bd1c 100644 --- a/sea/buffer.js +++ b/sea/buffer.js @@ -28,24 +28,25 @@ } buf = SeaArray.from(bytes) } else if (enc === 'utf8') { - const { length } = input + const length = input.length const words = new Uint16Array(length) - Array.from({ length }, (_, i) => words[i] = input.charCodeAt(i)) + Array.from({ length: length }, (_, i) => words[i] = input.charCodeAt(i)) buf = SeaArray.from(words) } else if (enc === 'base64') { const dec = atob(input) - const { length } = dec + const length = dec.length const bytes = new Uint8Array(length) - Array.from({ length }, (_, i) => bytes[i] = dec.charCodeAt(i)) + Array.from({ length: length }, (_, i) => bytes[i] = dec.charCodeAt(i)) buf = SeaArray.from(bytes) } else if (enc === 'binary') { buf = SeaArray.from(input) } else { - console.info(`SafeBuffer.from unknown encoding: '${enc}'`) + console.info('SafeBuffer.from unknown encoding: '+enc) } return buf } - const { byteLength, length = byteLength } = input + const byteLength = input.byteLength // what is going on here? FOR MARTTI + const length = input.byteLength ? input.byteLength : input.length if (length) { let buf if (input instanceof ArrayBuffer) { @@ -56,11 +57,11 @@ }, // This is 'safe-buffer.alloc' sans encoding support alloc(length, fill = 0 /*, enc*/ ) { - return SeaArray.from(new Uint8Array(Array.from({ length }, () => fill))) + return SeaArray.from(new Uint8Array(Array.from({ length: length }, () => fill))) }, // This is normal UNSAFE 'buffer.alloc' or 'new Buffer(length)' - don't use! allocUnsafe(length) { - return SeaArray.from(new Uint8Array(Array.from({ length }))) + return SeaArray.from(new Uint8Array(Array.from({ length : length }))) }, // This puts together array of array like members concat(arr) { // octet array diff --git a/sea/create.js b/sea/create.js new file mode 100644 index 00000000..e0946b40 --- /dev/null +++ b/sea/create.js @@ -0,0 +1,329 @@ + + // TODO: This needs to be split into all separate functions. + // Not just everything thrown into 'create'. + + var SEA = require('./sea'); + var User = require('./user'); + var authsettings = require('./settings'); + var Gun = SEA.Gun; + + var noop = function(){}; + + // Well first we have to actually create a user. That is what this function does. + 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){ var tmp; + act.pair = pair || {}; + if(tmp = cat.root.user){ + tmp._.sea = pair; + tmp.is = {pub: pair.pub, epub: pair.epub, alias: alias}; + } + // the user's public key doesn't need to be signed. But everything else needs to be signed with it! // we have now automated it! clean up these extra steps now! + act.data = {pub: pair.pub}; + act.d(); + } + act.d = function(){ + act.data.alias = alias; + act.e(); + } + act.e = function(){ + act.data.epub = act.pair.epub; + SEA.encrypt({priv: act.pair.priv, epriv: act.pair.epriv}, act.proof, act.f, {raw:1}); // to keep the private key safe, we AES encrypt it with the proof of work! + } + act.f = function(auth){ + act.data.auth = JSON.stringify({ek: auth, s: act.salt}); + act.g(act.data.auth); + } + act.g = function(auth){ var tmp; + act.data.auth = act.data.auth || auth; + root.get(tmp = '~'+act.pair.pub).put(act.data); // awesome, now we can actually save the user with their public key as their ID. + root.get('~@'+alias).put(Gun.obj.put({}, tmp, Gun.val.link.ify(tmp))); // next up, we want to associate the alias with the public key. So we add it to the alias list. + setTimeout(function(){ // we should be able to delete this now, right? + cat.ing = false; + cb({ok: 0, pub: act.pair.pub}); // callback that the user has been created. (Note: ok = 0 because we didn't wait for disk to ack) + if(noop === cb){ gun.auth(alias, pass) } // if no callback is passed, auto-login after signing up. + },10); + } + root.get('~@'+alias).once(act.a); + return gun; + } + // now that we have created a user, we want to authenticate them! + User.prototype.auth = function(alias, pass, cb, opt){ + 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}); + return gun; + } + cat.ing = true; + opt = opt || {}; + var pair = (alias && (alias.pub || alias.epub))? alias : (pass && (pass.pub || pass.epub))? pass : null; + var act = {}, u; + act.a = function(data){ + if(!data){ return act.b() } + if(!data.pub){ + var tmp = []; + Gun.node.is(data, function(v){ tmp.push(v) }) + return act.b(tmp); + } + if(act.name){ return act.f(data) } + act.c((act.data = data).auth); + } + act.b = function(list){ + var get = (act.list = (act.list||[]).concat(list||[])).shift(); + if(u === get){ + if(act.name){ return act.err('Your user account is not published for dApps to access, please consider syncing it online, or allowing local access by adding your device as a peer.') } + return act.err('Wrong user or password.') + } + root.get(get).once(act.a); + } + act.c = function(auth){ + if(u === auth){ return act.b() } + if(Gun.text.is(auth)){ return act.c(Gun.obj.ify(auth)) } // in case of legacy + SEA.work(pass, (act.auth = auth).s, act.d, act.enc); // the proof of work is evidence that we've spent some time/effort trying to log in, this slows brute force. + } + act.d = function(proof){ + SEA.decrypt(act.auth.ek, proof, act.e, act.enc); + } + act.e = function(half){ + if(u === half){ + if(!act.enc){ // try old format + act.enc = {encode: 'utf8'}; + return act.c(act.auth); + } act.enc = null; // end backwards + return act.b(); + } + 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; + try{if(pass && !Gun.obj.has(Gun.obj.ify(cat.root.graph['~'+pair.pub].auth), ':')){ opt.shuffle = opt.change = pass; } }catch(e){} // migrate UTF8 & Shuffle! + opt.change? act.z() : cb(at); + if(SEA.window && ((gun.back('user')._).opt||opt).remember){ + // TODO: this needs to be modular. + try{var sS = {}; + sS = window.sessionStorage; + sS.recall = true; + sS.alias = alias; + sS.tmp = pass; + }catch(e){} + } + 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, {raw:1}); + } + act.x = function(auth){ + act.w(JSON.stringify({ek: auth, s: act.salt})); + } + act.w = function(auth){ + if(opt.shuffle){ // delete in future! + console.log('migrate core account from UTF8 & shuffle'); + var tmp = Gun.obj.to(act.data); + Gun.obj.del(tmp, '_'); + tmp.auth = auth; + root.get('~'+act.pair.pub).put(tmp); + } // end delete + root.get('~'+act.pair.pub).get('auth').put(auth, cb); + } + act.err = function(e){ + var ack = {err: Gun.log(e || 'User cannot be found!')}; + cat.ing = false; + cb(ack); + } + act.plugin = function(name){ + if(!(act.name = name)){ return act.err() } + var tmp = [name]; + if('~' !== name[0]){ + tmp[1] = '~'+name; + tmp[2] = '~@'+name; + } + act.b(tmp); + } + if(pair){ + act.g(pair); + } else + if(alias){ + root.get('~@'+alias).once(act.a); + } else + if(!alias && !pass){ + SEA.name(act.plugin); + } + return gun; + } + User.prototype.pair = function(){ + console.log("user.pair() IS DEPRECATED AND WILL BE DELETED!!!"); + var user = this; + if(!user.is){ return false } + return user._.sea; + } + User.prototype.leave = function(opt, cb){ + var gun = this, user = (gun.back(-1)._).user; + if(user){ + delete user.is; + delete user._.is; + delete user._.sea; + } + if(SEA.window){ + try{var sS = {}; + sS = window.sessionStorage; + delete sS.alias; + delete sS.tmp; + delete sS.recall; + }catch(e){}; + } + return gun; + } + // If authenticated user wants to delete his/her account, let's support it! + User.prototype.delete = async function(alias, pass, cb){ + var gun = this, root = gun.back(-1), user = gun.back('user'); + try { + 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); + } + return gun; + } + User.prototype.recall = function(opt, cb){ + var gun = this, root = gun.back(-1), tmp; + opt = opt || {}; + if(opt && opt.sessionStorage){ + if(SEA.window){ + try{var sS = {}; + sS = window.sessionStorage; + if(sS){ + (root._).opt.remember = true; + ((gun.back('user')._).opt||opt).remember = true; + if(sS.recall || (sS.alias && sS.tmp)){ + root.user().auth(sS.alias, sS.tmp, cb); + } + } + }catch(e){} + } + return gun; + } + /* + 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) + try { + // All is good. Should we do something more with actual recalled data? + await authRecall(gunRoot) + return gunRoot._.user._ + } catch (e) { + const err = 'No session!' + Gun.log(err) + throw { err } + } + } + User.prototype.trust = async function(user){ + // TODO: BUG!!! SEA `node` read listener needs to be async, which means core needs to be async too. + //gun.get('alice').get('age').trust(bob); + if (Gun.is(user)) { + user.get('pub').get((ctx, ev) => { + console.log(ctx, ev) + }) + } + } + 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.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); + if(!sec){ + sec = SEA.random(16).toString(); + enc = await SEA.encrypt(sec, pair); + user.get('trust').get(pair.pub).get(path).put(enc); + } + var pub = to.get('pub').then(); + var epub = to.get('epub').then(); + pub = await pub; epub = await epub; + var dh = await SEA.secret(epub, pair); + enc = await SEA.encrypt(sec, dh); + user.get('trust').get(pub).get(path).put(enc, cb); + }()); + return gun; + } + 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.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); + if(!sec){ + sec = SEA.random(16).toString(); + enc = await SEA.encrypt(sec, pair); + user.get('trust').get(pair.pub).get(path).put(enc); + } + enc = await SEA.encrypt(data, sec); + gun.put(enc, cb); + }()); + return gun; + } + module.exports = User + \ No newline at end of file diff --git a/sea/decrypt.js b/sea/decrypt.js index 5357b41c..736fd4cc 100644 --- a/sea/decrypt.js +++ b/sea/decrypt.js @@ -2,25 +2,40 @@ var SEA = require('./root'); var shim = require('./shim'); var S = require('./settings'); - var aescbckey = require('./aescbc'); - var parse = require('./parse'); + var aeskey = require('./aeskey'); - SEA.decrypt = async (data, pair, cb) => { try { - const key = pair.epriv || pair; - const json = parse(data) - const ct = await aescbckey(key, shim.Buffer.from(json.s, 'utf8')) - .then((aes) => shim.subtle.decrypt({ // Keeping aesKey scope as private as possible... - name: 'AES-CBC', iv: new Uint8Array(shim.Buffer.from(json.iv, 'utf8')) - }, aes, new Uint8Array(shim.Buffer.from(json.ct, 'utf8')))) - const r = parse(new shim.TextDecoder('utf8').decode(ct)) - - if(cb){ cb(r) } + SEA.decrypt = SEA.decrypt || (async (data, pair, cb, opt) => { try { + 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; + } + var json = S.parse(data); + var buf, bufiv, bufct; try{ + buf = shim.Buffer.from(json.s, opt.encode || 'base64'); + bufiv = shim.Buffer.from(json.iv, opt.encode || 'base64'); + bufct = shim.Buffer.from(json.ct, opt.encode || 'base64'); + var ct = await aeskey(key, buf, opt).then((aes) => (/*shim.ossl ||*/ shim.subtle).decrypt({ // Keeping aesKey scope as private as possible... + name: opt.name || 'AES-GCM', iv: new Uint8Array(bufiv) + }, aes, new Uint8Array(bufct))); + }catch(e){ + if('utf8' === opt.encode){ throw "Could not decrypt" } + if(SEA.opt.fallback){ + opt.encode = 'utf8'; + return await SEA.decrypt(data, pair, cb, opt); + } + } + var r = S.parse(new shim.TextDecoder('utf8').decode(ct)); + if(cb){ try{ cb(r) }catch(e){console.log(e)} } return r; } catch(e) { + console.log(e); SEA.err = e; + if(SEA.throw){ throw e } if(cb){ cb() } return; - }} + }}); module.exports = SEA.decrypt; \ No newline at end of file diff --git a/sea/encrypt.js b/sea/encrypt.js index dc93d805..dc31cc04 100644 --- a/sea/encrypt.js +++ b/sea/encrypt.js @@ -2,29 +2,38 @@ var SEA = require('./root'); var shim = require('./shim'); var S = require('./settings'); - var aescbckey = require('./aescbc'); + var aeskey = require('./aeskey'); + var u; - SEA.encrypt = async (data, pair, cb) => { try { - const key = pair.epriv || pair; - const msg = JSON.stringify(data) - const rand = {s: shim.random(8), iv: shim.random(16)}; - const ct = await aescbckey(key, rand.s) - .then((aes) => shim.subtle.encrypt({ // Keeping the AES key scope as private as possible... - name: 'AES-CBC', iv: new Uint8Array(rand.iv) - }, aes, new shim.TextEncoder().encode(msg))) - const r = 'SEA'+JSON.stringify({ - ct: shim.Buffer.from(ct, 'binary').toString('utf8'), - iv: rand.iv.toString('utf8'), - s: rand.s.toString('utf8') - }); + SEA.encrypt = SEA.encrypt || (async (data, pair, cb, opt) => { try { + opt = opt || {}; + var key = (pair||opt).epriv || pair; + if(u === data){ throw '`undefined` not allowed.' } + if(!key){ + pair = await SEA.I(null, {what: data, how: 'encrypt', why: opt.why}); + key = pair.epriv || pair; + } + var msg = (typeof data == 'string')? data : JSON.stringify(data); + var rand = {s: shim.random(8), iv: shim.random(16)}; + var ct = await aeskey(key, rand.s, opt).then((aes) => (/*shim.ossl ||*/ shim.subtle).encrypt({ // Keeping the AES key scope as private as possible... + name: opt.name || 'AES-GCM', iv: new Uint8Array(rand.iv) + }, aes, new shim.TextEncoder().encode(msg))); + var r = { + ct: shim.Buffer.from(ct, 'binary').toString(opt.encode || 'base64'), + iv: rand.iv.toString(opt.encode || 'base64'), + s: rand.s.toString(opt.encode || 'base64') + } + if(!opt.raw){ r = 'SEA'+JSON.stringify(r) } - if(cb){ cb(r) } + if(cb){ try{ cb(r) }catch(e){console.log(e)} } return r; } catch(e) { + console.log(e); SEA.err = e; + if(SEA.throw){ throw e } if(cb){ cb() } return; - }} + }}); module.exports = SEA.encrypt; \ No newline at end of file diff --git a/sea/https.js b/sea/https.js index 86d83150..cb7916dc 100644 --- a/sea/https.js +++ b/sea/https.js @@ -1,10 +1,10 @@ var SEA = require('./root'); - if(SEA.window){ + try{ if(SEA.window){ if(location.protocol.indexOf('s') < 0 && location.host.indexOf('localhost') < 0 && location.protocol.indexOf('file:') < 0){ - location.protocol = 'https:'; + location.protocol = 'https:'; // WebCrypto does NOT work without HTTPS! } - } + } }catch(e){} \ No newline at end of file diff --git a/sea/index.js b/sea/index.js index 3b74efa5..a1d880af 100644 --- a/sea/index.js +++ b/sea/index.js @@ -1,20 +1,12 @@ - const Gun = (typeof window !== 'undefined' ? window : global).Gun || require('gun/gun') const SEA = require('./sea') + const Gun = SEA.Gun; // After we have a GUN extension to make user registration/login easy, we then need to handle everything else. // We do this with a GUN adapter, we first listen to when a gun instance is created (and when its options change) Gun.on('opt', function(at){ if(!at.sea){ // only add SEA once per instance, on the "at" context. at.sea = {own: {}}; - var uuid = at.opt.uuid || Gun.state.lex; - at.opt.uuid = function(cb){ // TODO: consider async/await and drop callback pattern... - var id = uuid(), pub = at.user; - if(!pub || !(pub = at.user._.sea) || !(pub = pub.pub)){ return id } - id = id + '~' + pub; - if(cb){ cb(null, id) } - return id; - } at.on('in', security, at); // now listen to all input data, acting as a firewall. at.on('out', signature, at); // and output listeners, to encrypt outgoing data. at.on('node', each, at); @@ -29,7 +21,7 @@ // Now NOTE! Some data is "system" data, not user data. Example: List of public keys, aliases, etc. // This data is self-enforced (the value can only match its ID), but that is handled in the `security` function. // From the self-enforced data, we can see all the edges in the graph that belong to a public key. - // Example: pub/ASDF is the ID of a node with ASDF as its public key, signed alias and salt, and + // Example: ~ASDF is the ID of a node with ASDF as its public key, signed alias and salt, and // its encrypted private key, but it might also have other signed values on it like `profile = ` edge. // Using that directed edge's ID, we can then track (in memory) which IDs belong to which keys. // Here is a problem: Multiple public keys can "claim" any node's ID, so this is dangerous! @@ -38,26 +30,31 @@ function each(msg){ // TODO: Warning: Need to switch to `gun.on('node')`! Do not use `Gun.on('node'` in your apps! // NOTE: THE SECURITY FUNCTION HAS ALREADY VERIFIED THE DATA!!! // WE DO NOT NEED TO RE-VERIFY AGAIN, JUST TRANSFORM IT TO PLAINTEXT. - var to = this.to, vertex = (msg.gun._).put, c = 0, d; - Gun.node.is(msg.put, function(val, key, node){ c++; // for each property on the node - // TODO: consider async/await use here... - SEA.verify(val, false).then(function(data){ c--; // false just extracts the plain data. - node[key] = val = data; // transform to plain value. + var to = this.to, vertex = (msg.$._).put, c = 0, d; + Gun.node.is(msg.put, function(val, key, node){ + // only process if SEA formatted? + var tmp = Gun.obj.ify(val) || noop; + if(u !== tmp[':']){ + node[key] = SEA.opt.unpack(tmp); + return; + } + if(!SEA.opt.check(val)){ return } + c++; // for each property on the node + SEA.verify(val, false, function(data){ c--; // false just extracts the plain data. + node[key] = SEA.opt.unpack(data, key, node);; // transform to plain value. if(d && !c && (c = -1)){ to.next(msg) } }); }); - d = true; - if(d && !c){ to.next(msg) } - return; + if((d = true) && !c){ to.next(msg) } } // signature handles data output, it is a proxy to the security function. function signature(msg){ - if(msg.user){ + if((msg._||noop).user){ return this.to.next(msg); } var ctx = this.as; - msg.user = ctx.user; + (msg._||(msg._=function(){})).user = ctx.user; security.call(this, msg); } @@ -70,10 +67,11 @@ // 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('alias' === soul){ // Allow reading the list of usernames/aliases in the system? return to.next(msg); // yes. } else - if('alias/' === soul.slice(0,6)){ // Allow reading the list of public keys associated with an alias? + if('~@' === soul.slice(0,2)){ // Allow reading the list of public keys associated with an alias? return to.next(msg); // yes. } else { // Allow reading everything? return to.next(msg); // yes // TODO: No! Make this a callback/event that people can filter on. @@ -90,104 +88,98 @@ each.way = function(val, key){ var soul = this.soul, node = this.node, tmp; if('_' === key){ return } // ignore meta data - if('alias' === soul){ // special case for shared system data, the list of aliases. + if('~@' === soul){ // special case for shared system data, the list of aliases. each.alias(val, key, node, soul); return; } - if('alias/' === soul.slice(0,6)){ // special case for shared system data, the list of public keys for an alias. + if('~@' === soul.slice(0,2)){ // special case for shared system data, the list of public keys for an alias. each.pubs(val, key, node, soul); return; } - if('pub/' === soul.slice(0,4)){ // special case, account data for a public key. - each.pub(val, key, node, soul, soul.slice(4), msg.user); return; + if('~' === soul.slice(0,1) && 2 === (tmp = soul.slice(1)).split('.').length){ // special case, account data for a public key. + each.pub(val, key, node, soul, tmp, (msg._||noop).user); return; } - each.any(val, key, node, soul, msg.user); return; + each.any(val, key, node, soul, (msg._||noop).user); return; return each.end({err: "No other data allowed!"}); }; - each.alias = function(val, key, node, soul){ // Example: {_:#alias, alias/alice: {#alias/alice}} + each.alias = function(val, key, node, soul){ // Example: {_:#~@, ~@alice: {#~@alice}} if(!val){ return each.end({err: "Data must exist!"}) } // data MUST exist - if('alias/'+key === Gun.val.rel.is(val)){ return check['alias'+key] = 0 } // in fact, it must be EXACTLY equal to itself + if('~@'+key === Gun.val.link.is(val)){ return check['alias'+key] = 0 } // in fact, it must be EXACTLY equal to itself each.end({err: "Mismatching alias."}); // if it isn't, reject. }; - each.pubs = function(val, key, node, soul){ // Example: {_:#alias/alice, pub/asdf: {#pub/asdf}} + each.pubs = function(val, key, node, soul){ // Example: {_:#~@alice, ~asdf: {#~asdf}} if(!val){ return each.end({err: "Alias must exist!"}) } // data MUST exist - if(key === Gun.val.rel.is(val)){ return check['pubs'+soul+key] = 0 } // and the ID must be EXACTLY equal to its property + if(key === Gun.val.link.is(val)){ return check['pubs'+soul+key] = 0 } // and the ID must be EXACTLY equal to its property each.end({err: "Alias must match!"}); // that way nobody can tamper with the list of public keys. }; - each.pub = function(val, key, node, soul, pub, user){ // Example: {_:#pub/asdf, hello:SEA['world',fdsa]} + each.pub = function(val, key, node, soul, pub, user){ var tmp; // Example: {_:#~asdf, hello:'world'~fdsa}} if('pub' === key){ if(val === pub){ return (check['pub'+soul+key] = 0) } // the account MUST match `pub` property that equals the ID of the public key. return each.end({err: "Account must match!"}); } check['user'+soul+key] = 1; - if(user && (user = user._) && user.sea && pub === user.pub){ - //var id = Gun.text.random(3); - SEA.sign(val, user.sea).then(function(data){ var rel; - if(rel = Gun.val.rel.is(val)){ + if(Gun.is(msg.$) && user && user.is && pub === user.is.pub){ + SEA.sign(SEA.opt.prep(tmp = SEA.opt.parse(val), key, node, soul), (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; } - node[key] = data; + node[key] = JSON.stringify({':': SEA.opt.unpack(data.m), '~': data.s}); check['user'+soul+key] = 0; each.end({ok: 1}); - }); - // TODO: Handle error!!!! + }, {check: SEA.opt.pack(tmp, key, node, soul), raw: 1}); return; } - // TODO: consider async/await and drop callback pattern... - SEA.verify(val, pub).then(function(data){ var rel, tmp; + SEA.verify(SEA.opt.pack(val,key,node,soul), pub, function(data){ var rel, tmp; + data = SEA.opt.unpack(data, key, node); if(u === data){ // make sure the signature matches the account it claims to be on. return each.end({err: "Unverified data."}); // reject any updates that are signed with a mismatched account. } - if((rel = Gun.val.rel.is(data)) && (tmp = rel.split('~')) && 2 === tmp.length){ - if(pub === tmp[1]){ - (at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true; - } + if((rel = Gun.val.link.is(data)) && pub === SEA.opt.pub(rel)){ + (at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true; } check['user'+soul+key] = 0; each.end({ok: 1}); }); }; each.any = function(val, key, node, soul, user){ var tmp, pub; - if(!user || !(user = user._) || !(user = user.sea)){ - if((tmp = soul.split('~')) && 2 == tmp.length){ - check['any'+soul+key] = 1; - SEA.verify(val, (pub = tmp[1])).then(function(data){ var rel; - if(!data){ return each.end({err: "Mismatched owner on '" + key + "'."}) } - if((rel = Gun.val.rel.is(data)) && (tmp = rel.split('~')) && 2 === tmp.length){ - if(pub === tmp[1]){ - (at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true; - } - } - check['any'+soul+key] = 0; - each.end({ok: 1}); - }); + if(!(pub = SEA.opt.pub(soul))){ + if(at.opt.secure){ + each.end({err: "Soul is missing public key at '" + key + "'."}); return; } + // TODO: Ask community if should auto-sign non user-graph data. check['any'+soul+key] = 1; at.on('secure', function(msg){ this.off(); check['any'+soul+key] = 0; + if(at.opt.secure){ msg = null } each.end(msg || {err: "Data cannot be modified."}); }).on.on('secure', msg); //each.end({err: "Data cannot be modified."}); return; } - if(!(tmp = soul.split('~')) || 2 !== tmp.length){ - each.end({err: "Soul is missing public key at '" + key + "'."}); + if(Gun.is(msg.$) && user && user.is && pub === user.is.pub){ + /*var other = Gun.obj.map(at.sea.own[soul], function(v, p){ + if((user.is||{}).pub !== p){ return p } + }); + if(other){ + each.any(val, key, node, soul); + return; + }*/ + check['any'+soul+key] = 1; + SEA.sign(SEA.opt.prep(tmp = SEA.opt.parse(val), key, node, soul), (user._).sea, function(data){ + if(u === data){ return each.end({err: 'My signature fail.'}) } + node[key] = JSON.stringify({':': SEA.opt.unpack(data.m), '~': data.s}); + check['any'+soul+key] = 0; + each.end({ok: 1}); + }, {check: SEA.opt.pack(tmp, key, node, soul), raw: 1}); return; } - var pub = tmp[1]; - if(pub !== user.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(other){ - each.any(val, key, node, soul); - return; - }*/ check['any'+soul+key] = 1; - SEA.sign(val, user).then(function(data){ - node[key] = data; + SEA.verify(SEA.opt.pack(val,key,node,soul), pub, function(data){ var rel; + data = SEA.opt.unpack(data, key, node); + if(u === data){ return each.end({err: "Mismatched owner on '" + key + "'."}) } // thanks @rogowski ! + if((rel = Gun.val.link.is(data)) && pub === SEA.opt.pub(rel)){ + (at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true; + } check['any'+soul+key] = 0; each.end({ok: 1}); }); @@ -195,13 +187,14 @@ 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 } if(Gun.obj.map(check, function(no){ if(no){ return true } })){ return } + (msg._||{}).user = at.user || security; // already been through firewall, does not need to again on out. to.next(msg); }; Gun.obj.map(msg.put, each.node); @@ -210,5 +203,41 @@ } to.next(msg); // pass forward any data we do not know how to handle or process (this allows custom security protocols). } + SEA.opt.pub = function(s){ + if(!s){ return } + s = s.split('~'); + if(!s || !(s = s[1])){ return } + s = s.split('.'); + if(!s || 2 > s.length){ return } + s = s.slice(0,2).join('.'); + return s; + } + SEA.opt.prep = function(d,k, n,s){ // prep for signing + return {'#':s,'.':k,':':SEA.opt.parse(d),'>':Gun.state.is(n, k)}; + } + SEA.opt.pack = function(d,k, n,s){ // pack for verifying + if(SEA.opt.check(d)){ return d } + var meta = (Gun.obj.ify(d)||noop), sig = meta['~']; + return sig? {m: {'#':s,'.':k,':':meta[':'],'>':Gun.state.is(n, k)}, s: sig} : d; + } + SEA.opt.unpack = function(d, k, n){ var tmp; + if(u === d){ return } + if(d && (u !== (tmp = d[':']))){ return tmp } + if(!k || !n){ return } + if(d === n[k]){ return d } + if(!SEA.opt.check(n[k])){ return d } + var soul = Gun.node.soul(n), s = Gun.state.is(n, k); + if(d && 4 === d.length && soul === d[0] && k === d[1] && fl(s) === fl(d[3])){ + return d[2]; + } + if(s < SEA.opt.shuffle_attack){ + return d; + } + } + SEA.opt.shuffle_attack = 1546329600000; // Jan 1, 2019 + var noop = function(){}, u; + var fl = Math.floor; // TODO: Still need to fix inconsistent state issue. + var rel_is = Gun.val.rel.is; + // TODO: Potential bug? If pub/priv key starts with `-`? IDK how possible. \ No newline at end of file diff --git a/sea/leave.js b/sea/leave.js deleted file mode 100644 index c0cb03d6..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.gun){ - delete user.gun.is; - } - // Let's use default - gunRoot.user(); - // Removes persisted authentication & CryptoKeys - try { - await authPersist({ 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 877378e5..00000000 --- a/sea/login.js +++ /dev/null @@ -1,33 +0,0 @@ - - const authPersist = require('./persist') - // This internal func finalizes User authentication - const finalizeLogin = async (alias, key, gunRoot, opts) => { - const { user } = gunRoot._ - // add our credentials in-memory only to our root gun instance - user._ = key.at.gun._ - // so that way we can use the credentials to encrypt/decrypt data - // that is input/output through gun (see below) - const { pub, priv, epub, epriv } = key - user._.is = user.is = {alias: alias, pub: pub}; - Object.assign(user._, { alias, pub, epub, sea: { pub, priv, epub, 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. - if(typeof window !== 'undefined'){ - var tmp = window.sessionStorage; - if(tmp && gunRoot._.opt.remember){ - window.sessionStorage.alias = alias; - window.sessionStorage.tmp = key; - } - } - try { - gunRoot._.on('auth', user._) - } 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 b603f791..3a59e177 100644 --- a/sea/pair.js +++ b/sea/pair.js @@ -2,51 +2,72 @@ var SEA = require('./root'); var shim = require('./shim'); var S = require('./settings'); - var Buff = (typeof Buffer !== 'undefined')? Buffer : shim.Buffer; + + SEA.name = SEA.name || (async (cb, opt) => { try { + if(cb){ try{ cb() }catch(e){console.log(e)} } + return; + } catch(e) { + console.log(e); + SEA.err = e; + if(SEA.throw){ throw e } + if(cb){ cb() } + return; + }}); //SEA.pair = async (data, proof, cb) => { try { - SEA.pair = async (cb) => { try { + SEA.pair = SEA.pair || (async (cb, opt) => { try { - const ecdhSubtle = shim.ossl || shim.subtle + var ecdhSubtle = shim.ossl || shim.subtle; // First: ECDSA keys for signing/verifying... - const { pub, priv } = await shim.subtle.generateKey(S.ecdsa.pair, true, [ 'sign', 'verify' ]) + var sa = await shim.subtle.generateKey(S.ecdsa.pair, true, [ 'sign', 'verify' ]) .then(async (keys) => { // privateKey scope doesn't leak out from here! - const { d: priv } = await shim.subtle.exportKey('jwk', keys.privateKey) - const { x, y } = await shim.subtle.exportKey('jwk', keys.publicKey) + //const { d: priv } = await shim.subtle.exportKey('jwk', keys.privateKey) + var key = {}; + key.priv = (await shim.subtle.exportKey('jwk', keys.privateKey)).d; + var pub = await shim.subtle.exportKey('jwk', keys.publicKey); //const pub = Buff.from([ x, y ].join(':')).toString('base64') // old - const pub = x+'.'+y // new + key.pub = pub.x+'.'+pub.y; // new // x and y are already base64 // pub is UTF8 but filename/URL safe (https://www.ietf.org/rfc/rfc3986.txt) // but split on a non-base64 letter. - return { pub, priv } + return key; }) // To include PGPv4 kind of keyId: // const pubId = await SEA.keyid(keys.pub) // Next: ECDH keys for encryption/decryption... - const { epub, epriv } = await ecdhSubtle.generateKey(S.ecdh, true, ['deriveKey']) + try{ + var dh = await ecdhSubtle.generateKey(S.ecdh, true, ['deriveKey']) .then(async (keys) => { // privateKey scope doesn't leak out from here! - const { d: epriv } = await ecdhSubtle.exportKey('jwk', keys.privateKey) - const { x, y } = await ecdhSubtle.exportKey('jwk', keys.publicKey) + var key = {}; + key.epriv = (await ecdhSubtle.exportKey('jwk', keys.privateKey)).d; + var pub = await ecdhSubtle.exportKey('jwk', keys.publicKey); //const epub = Buff.from([ ex, ey ].join(':')).toString('base64') // old - const epub = x+'.'+y // new + key.epub = pub.x+'.'+pub.y; // new // ex and ey are already base64 // epub is UTF8 but filename/URL safe (https://www.ietf.org/rfc/rfc3986.txt) // but split on a non-base64 letter. - return { epub, epriv } + return key; }) + }catch(e){ + if(SEA.window){ throw e } + if(e == 'Error: ECDH is not a supported algorithm'){ console.log('Ignoring ECDH...') } + else { throw e } + } dh = dh || {}; - const r = { pub, priv, /* pubId, */ epub, epriv } - if(cb){ cb(r) } + var 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(SEA.throw){ throw e } if(cb){ cb() } return; - }} + }}); module.exports = SEA.pair; \ No newline at end of file diff --git a/sea/parse.js b/sea/parse.js deleted file mode 100644 index 7372bc1f..00000000 --- a/sea/parse.js +++ /dev/null @@ -1,11 +0,0 @@ - - module.exports = (props) => { - try { - if(props.slice && 'SEA{' === props.slice(0,4)){ - props = props.slice(3); - } - return props.slice ? JSON.parse(props) : props - } catch (e) {} //eslint-disable-line no-empty - return props - } - \ No newline at end of file diff --git a/sea/persist.js b/sea/persist.js deleted file mode 100644 index c43587e1..00000000 --- a/sea/persist.js +++ /dev/null @@ -1,37 +0,0 @@ - - const Gun = (typeof window !== 'undefined' ? window : global).Gun || require('gun/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 || {} - const { validity: exp } = authsettings // 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, iat, exp, remember }) - const { pub, epub, sea: { priv, epriv } } = user - const key = { pub, priv, epub, 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 ee43a568..00000000 --- a/sea/query.js +++ /dev/null @@ -1,44 +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/${alias}`).get((rat, rev) => { - rev.off(); - if (!rat.put) { - // 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(rat.put, (at, pub) => { - if (!pub.slice || 'pub/' !== pub.slice(0, 4)) { - // TODO: ... this would then be .filter((at, pub)) - return - } - ++c - // grab the account associated with this public key. - gunRoot.get(pub).get((at, ev) => { - pub = pub.slice(4) - ev.off() - --c - if (at.put){ - aliases.push({ pub, at }) - } - 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 97a7489d..00000000 --- a/sea/recall.js +++ /dev/null @@ -1,139 +0,0 @@ - - // TODO: BUG! `SEA` needs to be USEd! - const Gun = (typeof window !== 'undefined' ? window : global).Gun || require('gun/gun') - - 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 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, proof }) - } else { - Gun.log('Authentication expired!') - } - } - // We're not gonna give proof to hook! - const hooked = authsettings.hook({ alias, iat, exp, 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, pub }) => { - const readStorageData = async (args) => { - const props = args || parseProps(await SEA.verify(remember, pub, true)) - let { pin, alias: aLias } = props - - 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, data } - } - // got pub, try auth with pin & alias :: or unwrap Storage data... - const { data, pin: newPin } = await readStorageData(pin && { pin, alias }) - const { proof } = data || {} - - 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 - const sea = await SEA.decrypt(auth, proof) - if (!sea) { - err = 'Failed to decrypt private key!' - return - } - const { priv, epriv } = sea - const { epub } = at.put - // Success! we've found our private data! - err = null - return { proof, at, pin: newPin, key: { pub, priv, epriv, 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, 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 23cd2bcc..7b99eb97 100644 --- a/sea/root.js +++ b/sea/root.js @@ -1,10 +1,16 @@ // Security, Encryption, and Authorization: SEA.js - // MANDATORY READING: http://gun.js.org/explainers/data/security.html + // MANDATORY READING: https://gun.eco/explainers/data/security.html + // IT IS IMPLEMENTED IN A POLYFILL/SHIM APPROACH. // THIS IS AN EARLY ALPHA! - function SEA(){} - if(typeof window !== "undefined"){ SEA.window = window } + if(typeof window !== "undefined"){ module.window = window } + var tmp = module.window || module; + var SEA = tmp.SEA || {}; + + if(SEA.window = module.window){ SEA.window.SEA = SEA } + + try{ if(typeof common !== "undefined"){ common.exports = SEA } }catch(e){} module.exports = SEA; \ No newline at end of file diff --git a/sea/sea.js b/sea/sea.js index dc1413b0..75f8b0dd 100644 --- a/sea/sea.js +++ b/sea/sea.js @@ -1,44 +1,18 @@ - // Old Code... - const { - crypto, - subtle, - ossl, - random: getRandomBytes, - TextEncoder, - TextDecoder - } = require('./shim') - const EasyIndexedDB = require('./indexed') - const Buffer = require('./buffer') - var settings = require('./settings'); - const { - pbkdf2: pbKdf2, - ecdsa: { pair: ecdsaKeyProps, sign: ecdsaSignProps }, - ecdh: ecdhKeyProps, - jwk: keysToEcdsaJwk - } = require('./settings') - const sha1hash = require('./sha1') - const sha256hash = require('./sha256') - const recallCryptoKey = require('./remember') - const parseProps = require('./parse') - + var shim = require('./shim'); // Practical examples about usage found from ./test/common.js - const SEA = require('./root'); + var SEA = require('./root'); SEA.work = require('./work'); SEA.sign = require('./sign'); SEA.verify = require('./verify'); SEA.encrypt = require('./encrypt'); SEA.decrypt = require('./decrypt'); - SEA.random = getRandomBytes; - - // This is easy way to use IndexedDB, all methods are Promises - // Note: Not all SEA interfaces have to support this. - SEA.EasyIndexedDB = EasyIndexedDB; + SEA.random = SEA.random || shim.random; // This is Buffer used in SEA and usable from Gun/SEA application also. // For documentation see https://nodejs.org/api/buffer.html - SEA.Buffer = Buffer; + SEA.Buffer = SEA.Buffer || require('./buffer'); // These SEA functions support now ony Promises or // async/await (compatible) code, use those like Promises. @@ -46,11 +20,11 @@ // Creates a wrapper library around Web Crypto API // for various AES, ECDSA, PBKDF2 functions we called above. // Calculate public key KeyID aka PGPv4 (result: 8 bytes as hex string) - SEA.keyid = async (pub) => { + SEA.keyid = SEA.keyid || (async (pub) => { try { // base64('base64(x):base64(y)') => Buffer(xy) const pb = Buffer.concat( - Buffer.from(pub, 'base64').toString('utf8').split(':') + pub.replace(/-/g, '+').replace(/_/g, '/').split('.') .map((t) => Buffer.from(t, 'base64')) ) // id is PGPv4 compliant raw key @@ -64,39 +38,7 @@ console.log(e) throw e } - } - // Derive shared secret from other's pub and my epub/epriv - SEA.derive = async (pub, { epub, epriv }) => { - try { - const ecdhSubtle = ossl || subtle - const keysToEcdhJwk = (pub, d) => { // d === priv - const [ x, y ] = Buffer.from(pub, 'base64').toString('utf8').split(':') - const jwk = d ? { d } : {} - return [ // Use with spread returned value... - 'jwk', - { ...jwk, x, y, kty: 'EC', crv: 'P-256', ext: true }, - ecdhKeyProps - ] - } - const pubKeyData = keysToEcdhJwk(pub) - const props = { - ...ecdhKeyProps, - public: await ecdhSubtle.importKey(...pubKeyData, true, []) - } - const privKeyData = keysToEcdhJwk(epub, epriv) - const derived = await ecdhSubtle.importKey(...privKeyData, false, ['deriveKey']) - .then(async (privKey) => { - // privateKey scope doesn't leak out from here! - const derivedKey = await ecdhSubtle.deriveKey(props, privKey, { name: 'AES-CBC', length: 256 }, true, [ 'encrypt', 'decrypt' ]) - return ecdhSubtle.exportKey('jwk', derivedKey).then(({ k }) => k) - }) - return derived - } catch (e) { - console.log(e) - throw e - } - } - + }); // all done! // Obviously it is missing MANY necessary features. This is only an alpha release. // Please experiment with it, audit what I've done so far, and complain about what needs to be added. @@ -106,9 +48,9 @@ // But all other behavior needs to be equally easy, like opinionated ways of // Adding friends (trusted public keys), sending private messages, etc. // Cheers! Tell me what you think. - var Gun = (SEA.window||{}).Gun || require('./gun'); + 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 new file mode 100644 index 00000000..4009ee31 --- /dev/null +++ b/sea/secret.js @@ -0,0 +1,50 @@ + + 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, opt) => { try { + opt = opt || {}; + if(!pair || !pair.epriv || !pair.epub){ + pair = await SEA.I(null, {what: key, how: 'secret', why: opt.why}); + } + var pub = key.epub || key; + var epub = pair.epub; + var epriv = pair.epriv; + var ecdhSubtle = shim.ossl || shim.subtle; + var pubKeyData = keysToEcdhJwk(pub); + var props = Object.assign(S.ecdh, { public: await ecdhSubtle.importKey(...pubKeyData, true, []) }); + var privKeyData = keysToEcdhJwk(epub, epriv); + var derived = await ecdhSubtle.importKey(...privKeyData, false, ['deriveKey']).then(async (privKey) => { + // privateKey scope doesn't leak out from here! + var derivedKey = await ecdhSubtle.deriveKey(props, privKey, { name: 'AES-GCM', length: 256 }, true, [ 'encrypt', 'decrypt' ]); + return ecdhSubtle.exportKey('jwk', derivedKey).then(({ k }) => k); + }) + var r = derived; + if(cb){ try{ cb(r) }catch(e){console.log(e)} } + return r; + } catch(e) { + console.log(e); + SEA.err = e; + if(SEA.throw){ throw e } + if(cb){ cb() } + return; + }}); + + // can this be replaced with settings.jwk? + var keysToEcdhJwk = (pub, d) => { // d === priv + //var [ x, y ] = Buffer.from(pub, 'base64').toString('utf8').split(':') // old + var [ x, y ] = pub.split('.') // new + var jwk = d ? { d: d } : {} + return [ // Use with spread returned value... + 'jwk', + Object.assign( + jwk, + { x: x, y: y, kty: 'EC', crv: 'P-256', ext: true } + ), // ??? refactor + S.ecdh + ] + } + + module.exports = SEA.secret; + \ No newline at end of file diff --git a/sea/settings.js b/sea/settings.js index 25c6aaa6..8e68e8c5 100644 --- a/sea/settings.js +++ b/sea/settings.js @@ -1,39 +1,37 @@ - const Buffer = require('./buffer') - const settings = {} - // Encryption parameters - const pbkdf2 = { hash: 'SHA-256', iter: 100000, ks: 64 } + var SEA = require('./root'); + var Buffer = require('./buffer'); + var s = {}; + s.pbkdf2 = {hash: 'SHA-256', iter: 100000, ks: 64}; + s.ecdsa = { + pair: {name: 'ECDSA', namedCurve: 'P-256'}, + sign: {name: 'ECDSA', hash: {name: 'SHA-256'}} + }; + s.ecdh = {name: 'ECDH', namedCurve: 'P-256'}; - const ecdsaSignProps = { name: 'ECDSA', hash: { name: 'SHA-256' } } - const ecdsaKeyProps = { name: 'ECDSA', namedCurve: 'P-256' } - const ecdhKeyProps = { name: 'ECDH', namedCurve: 'P-256' } - - const _initial_authsettings = { - validity: 12 * 60 * 60, // internally in seconds : 12 hours - hook: (props) => props // { iat, exp, alias, remember } - // or return new Promise((resolve, reject) => resolve(props) - } - // These are used to persist user's authentication "session" - const authsettings = Object.assign({}, _initial_authsettings) // This creates Web Cryptography API compliant JWK for sign/verify purposes - const keysToEcdsaJwk = (pub, d) => { // d === priv - //const [ x, y ] = Buffer.from(pub, 'base64').toString('utf8').split(':') // old - const [ x, y ] = pub.split('.') // new - var jwk = { kty: "EC", crv: "P-256", x: x, y: y, ext: true } + s.jwk = function(pub, d){ // d === priv + pub = pub.split('.'); + var x = pub[0], y = pub[1]; + var jwk = {kty: "EC", crv: "P-256", x: x, y: y, ext: true}; jwk.key_ops = d ? ['sign'] : ['verify']; if(d){ jwk.d = d } return jwk; + }; + s.recall = { + validity: 12 * 60 * 60, // internally in seconds : 12 hours + hook: function(props){ return props } // { iat, exp, alias, remember } // or return new Promise((resolve, reject) => resolve(props) + }; + + s.check = function(t){ return (typeof t == 'string') && ('SEA{' === t.slice(0,4)) } + s.parse = function p(t){ try { + var yes = (typeof t == 'string'); + if(yes && 'SEA{' === t.slice(0,4)){ t = t.slice(3) } + return yes ? JSON.parse(t) : t; + } catch (e) {} + return t; } - Object.assign(settings, { - pbkdf2, - ecdsa: { - pair: ecdsaKeyProps, - sign: ecdsaSignProps - }, - ecdh: ecdhKeyProps, - jwk: keysToEcdsaJwk, - recall: authsettings - }) - module.exports = settings + SEA.opt = s; + module.exports = s \ No newline at end of file diff --git a/sea/sha1.js b/sea/sha1.js index d6e0e7f5..9272cffc 100644 --- a/sea/sha1.js +++ b/sea/sha1.js @@ -1,6 +1,8 @@ // This internal func returns SHA-1 hashed data for KeyID generation - const { subtle, ossl = subtle } = require('./shim') + const __shim = require('./shim') + const subtle = __shim.subtle + const ossl = __shim.ossl ? __shim.ossl : subtle const sha1hash = (b) => ossl.digest({name: 'SHA-1'}, new ArrayBuffer(b)) module.exports = sha1hash \ No newline at end of file diff --git a/sea/sha256.js b/sea/sha256.js index 43f97dc9..c3182317 100644 --- a/sea/sha256.js +++ b/sea/sha256.js @@ -1,15 +1,8 @@ - const { - subtle, ossl = subtle, random: getRandomBytes, TextEncoder, TextDecoder - } = 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)) - return Buffer.from(hash) + var shim = require('./shim'); + module.exports = async function(d, o){ + var t = (typeof d == 'string')? d : JSON.stringify(d); + var hash = await shim.subtle.digest({name: o||'SHA-256'}, new shim.TextEncoder().encode(t)); + return shim.Buffer.from(hash); } - module.exports = sha256hash \ No newline at end of file diff --git a/sea/shim.js b/sea/shim.js index 92fcb349..b6026e8c 100644 --- a/sea/shim.js +++ b/sea/shim.js @@ -1,38 +1,36 @@ + const SEA = require('./root') const Buffer = require('./buffer') const api = {Buffer: Buffer} + var o = {}; - if (typeof __webpack_require__ === 'function' || typeof window !== 'undefined') { - const { msCrypto, crypto = msCrypto } = window // STD or M$ - const { webkitSubtle, subtle = webkitSubtle } = crypto // STD or iSafari - const { TextEncoder, TextDecoder } = window + 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 { 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{ - const crypto = require('crypto') - //const WebCrypto = require('node-webcrypto-ossl') - //const { subtle: ossl } = new WebCrypto({directory: 'key_storage'}) // ECDH - const { subtle } = require('@trust/webcrypto') // All but ECDH - const { TextEncoder, TextDecoder } = require('text-encoding') - Object.assign(api, { - crypto, - subtle, - //ossl, + //subtle, TextEncoder, TextDecoder, random: (len) => Buffer.from(crypto.randomBytes(len)) - }) - }catch(e){ - console.log("@trust/webcrypto and text-encoding are not included by default, you must add it to your package.json!"); - TRUST_WEBCRYPTO_OR_TEXT_ENCODING_NOT_INSTALLED; - } - } + }); + //try{ + const WebCrypto = require('node-webcrypto-ossl', 1); + api.ossl = api.subtle = new WebCrypto({directory: 'ossl'}).subtle // ECDH + //}catch(e){ + //console.log("node-webcrypto-ossl is optionally needed for ECDH, please install if needed."); + //} + }catch(e){ + console.log("node-webcrypto-ossl and text-encoding may not be included by default, please add it to your package.json!"); + OSSL_WEBCRYPTO_OR_TEXT_ENCODING_NOT_INSTALLED; + }} module.exports = api \ No newline at end of file diff --git a/sea/sign.js b/sea/sign.js index 4044b3e8..4efd2e5f 100644 --- a/sea/sign.js +++ b/sea/sign.js @@ -2,34 +2,42 @@ var SEA = require('./root'); var shim = require('./shim'); var S = require('./settings'); - var sha256hash = require('./sha256'); + var sha = require('./sha256'); + var u; - SEA.sign = async (data, pair, cb) => { try { - if(data.slice - && 'SEA{' === data.slice(0,4) - && '"m":' === data.slice(4,8)){ - // TODO: This would prevent pair2 signing pair1's signature. - // So we may want to change this in the future. - // but for now, we want to prevent duplicate double signature. - if(cb){ cb(data) } - return data; + SEA.sign = SEA.sign || (async (data, pair, cb, opt) => { try { + opt = opt || {}; + if(!(pair||opt).priv){ + pair = await SEA.I(null, {what: data, how: 'sign', why: opt.why}); } - const pub = pair.pub - const priv = pair.priv - const jwk = S.jwk(pub, priv) - const msg = JSON.stringify(data) - const hash = await sha256hash(msg) - const sig = await shim.subtle.importKey('jwk', jwk, S.ecdsa.pair, false, ['sign']) - .then((key) => shim.subtle.sign(S.ecdsa.sign, key, new Uint8Array(hash))) // privateKey scope doesn't leak out from here! - const r = 'SEA'+JSON.stringify({m: msg, s: shim.Buffer.from(sig, 'binary').toString('utf8')}); + if(u === data){ throw '`undefined` not allowed.' } + var json = S.parse(data); + var check = opt.check = opt.check || json; + if(SEA.verify && (SEA.opt.check(check) || (check && check.s && check.m)) + && u !== await SEA.verify(check, pair)){ // don't sign if we already signed it. + var r = S.parse(check); + if(!opt.raw){ r = 'SEA'+JSON.stringify(r) } + if(cb){ try{ cb(r) }catch(e){console.log(e)} } + return r; + } + var pub = pair.pub; + var priv = pair.priv; + var jwk = S.jwk(pub, priv); + var hash = await sha(json); + var sig = await (shim.ossl || shim.subtle).importKey('jwk', jwk, S.ecdsa.pair, false, ['sign']) + .then((key) => (shim.ossl || shim.subtle).sign(S.ecdsa.sign, key, new Uint8Array(hash))) // privateKey scope doesn't leak out from here! + var r = {m: json, s: shim.Buffer.from(sig, 'binary').toString(opt.encode || 'base64')} + if(!opt.raw){ r = 'SEA'+JSON.stringify(r) } - if(cb){ cb(r) } + if(cb){ try{ cb(r) }catch(e){console.log(e)} } return r; - } catch(e) { + } catch(e) { + console.log(e); SEA.err = e; + if(SEA.throw){ throw e } if(cb){ cb() } return; - }} + }}); module.exports = SEA.sign; \ No newline at end of file diff --git a/sea/then.js b/sea/then.js new file mode 100644 index 00000000..a97bcbc6 --- /dev/null +++ b/sea/then.js @@ -0,0 +1,9 @@ + + var Gun = require('./sea').Gun; + Gun.chain.then = function(cb){ + var gun = this, p = (new Promise(function(res, rej){ + gun.once(res); + })); + return cb? p.then(cb) : p; + } + \ No newline at end of file diff --git a/sea/update.js b/sea/update.js deleted file mode 100644 index fccac4a7..00000000 --- a/sea/update.js +++ /dev/null @@ -1,48 +0,0 @@ - - // TODO: BUG! `SEA` needs to be USED! - const Gun = (typeof window !== 'undefined' ? window : global).Gun || require('gun/gun') - const authsettings = require('./settings') - const SEA = require('./sea'); - //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, alias: id } = props - const remember = { alias, 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 }) - } - - 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 c3176dce..daab6da3 100644 --- a/sea/user.js +++ b/sea/user.js @@ -1,268 +1,31 @@ - // How does it work? - // TODO: Bug! Need to include SEA! - //const Gun = (typeof window !== 'undefined' ? window : global).Gun || require('gun/gun') + var SEA = require('./sea'); + var Gun = SEA.Gun; + var then = require('./then'); - const SEA = require('./sea') - const authRecall = require('./recall') - const authsettings = require('./settings') - const authenticate = require('./authenticate') - const finalizeLogin = require('./login') - const authLeave = require('./leave') - const { recall: _initial_authsettings } = require('./settings') - const Gun = SEA.Gun; + function User(root){ + this._ = {$: this}; + } + User.prototype = (function(){ function F(){}; F.prototype = Gun.chain; return new F() }()) // Object.create polyfill + User.prototype.constructor = User; // let's extend the gun chain with a `user` function. // only one user can be logged in at a time, per gun instance. - Gun.chain.user = function() { - const gunRoot = this.back(-1) // always reference the root gun instance. - const user = gunRoot._.user || (gunRoot._.user = gunRoot.chain()); // create a user context. - // then methods... - [ 'create', // factory - 'auth', // login - 'leave', // logout - 'delete', // account delete - 'recall', // existing auth boostrap - 'alive' // keep/check auth validity - ].map((method)=> user[method] = User[method]) - return user // return the user! - } - var u; - function User(){} - // Well first we have to actually create a user. That is what this function does. - Object.assign(User, { - async create(username, pass, cb) { - const gunRoot = this.back(-1) - var gun = this, cat = (gun._); - cb = cb || function(){}; - if(cat.ing){ - cb({err: Gun.log("User is already being created or authenticated!"), wait: true}); - return gun; - } - cat.ing = true; - var p = new Promise((resolve, reject) => { // Because no Promises or async - // 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(`alias/${username}`).get(async (at, ev) => { - ev.off() - if (at.put) { - // 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 }) - } - 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, priv, epriv } = pairs - // 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, 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, pub, epub, auth } - const tmp = `pub/${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(`alias/${username}`).put(Gun.obj.put({}, tmp, Gun.val.rel.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! - async auth(alias, pass, cb, opt) { - const opts = opt || (typeof cb !== 'function' && cb) - let { pin, newpass } = opts || {} - 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; - } - cat.ing = true; - - if (!pass && pin) { - try { - var r = await authRecall(gunRoot, { alias, 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; - } - } - - 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; - } - - try { - const keys = await authenticate(alias, pass, gunRoot) - if (!keys) { - return putErr('Auth attempt failed!')({ message: 'No keys' }) - } - const { pub, priv, epub, epriv } = keys - // 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, epriv }, { pub, key, set: true }) - .then((auth) => SEA.sign({ salt, auth }, keys)) - ) - const signedEpub = await SEA.sign(epub, keys) - const signedAlias = await SEA.sign(alias, keys) - const user = { - pub, - alias: signedAlias, - auth: encSigAuth, - epub: signedEpub - } - // awesome, now we can update the user using public key ID. - gunRoot.get(`pub/${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 }) - login.catch(putErr('Finalizing login failed!')) - return cat.ing = false, cb(await login), gun; - } - } catch (e) { - return putErr('Auth attempt failed!')(e) - } - return gun; - }, - async leave() { - return await authLeave(this.back(-1)) - }, - // If authenticated user wants to delete his/her account, let's support it! - async delete(alias, pass) { - const gunRoot = this.back(-1) - try { - const { pub } = await authenticate(alias, pass, gunRoot) - await authLeave(gunRoot, alias) - // Delete user data - gunRoot.get(`pub/${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??? - } catch (e) { - Gun.log('User.delete failed! Error:', e) - throw e // TODO: proper error codes??? - } - }, - // If authentication is to be remembered over reloads or browser closing, - // set validity time in minutes. - async recall(setvalidity, options) { - 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); - } - } + Gun.chain.user = function(pub){ + var gun = this, root = gun.back(-1), user; + if(pub){ return root.get('~'+pub) } + if(user = root.back('user')){ return user } + var root = (root._), at = root, uuid = at.opt.uuid || Gun.state.lex; + (at = (user = at.user = gun.chain(new User))._).opt = {}; + at.opt.uuid = function(cb){ + var id = uuid(), pub = root.user; + if(!pub || !(pub = pub.is) || !(pub = pub.pub)){ return id } + id = id + '~' + pub + '.'; + if(cb && cb.call){ cb(null, id) } + return id; } - return this; + return user; } - - 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? - return await authRecall(gunRoot) - } 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 } - } - }, - async alive() { - const gunRoot = this.back(-1) - try { - // All is good. Should we do something more with actual recalled data? - await authRecall(gunRoot) - return gunRoot._.user._ - } catch (e) { - const err = 'No session!' - Gun.log(err) - throw { err } - } - } - }) - Gun.chain.trust = function(user) { - // TODO: BUG!!! SEA `node` read listener needs to be async, which means core needs to be async too. - //gun.get('alice').get('age').trust(bob); - if (Gun.is(user)) { - user.get('pub').get((ctx, ev) => { - console.log(ctx, ev) - }) - } - } - module.exports = User + Gun.User = User; + module.exports = User; \ No newline at end of file diff --git a/sea/verify.js b/sea/verify.js index d89a6d49..06286e8c 100644 --- a/sea/verify.js +++ b/sea/verify.js @@ -2,33 +2,74 @@ var SEA = require('./root'); var shim = require('./shim'); var S = require('./settings'); - var sha256hash = require('./sha256'); - var parse = require('./parse'); + var sha = require('./sha256'); var u; - SEA.verify = async (data, pair, cb) => { try { - const json = parse(data) + SEA.verify = SEA.verify || (async (data, pair, cb, opt) => { try { + var json = S.parse(data); if(false === pair){ // don't verify! - const raw = (json === data)? json : parse(json.m) - if(cb){ cb(raw) } + var raw = S.parse(json.m); + if(cb){ try{ cb(raw) }catch(e){console.log(e)} } return raw; } - const pub = pair.pub || pair - const jwk = S.jwk(pub) - const key = await shim.subtle.importKey('jwk', jwk, S.ecdsa.pair, false, ['verify']) - const hash = await sha256hash(json.m) - const sig = new Uint8Array(shim.Buffer.from(json.s, 'utf8')) - const check = await shim.subtle.verify(S.ecdsa.sign, key, sig, new Uint8Array(hash)) - if(!check){ throw "Signature did not match." } - const r = check? parse(json.m) : u; + opt = opt || {}; + // SEA.I // verify is free! Requires no user permission. + var pub = pair.pub || pair; + var key = SEA.opt.slow_leak? await SEA.opt.slow_leak(pub) : await (shim.ossl || shim.subtle).importKey('jwk', jwk, S.ecdsa.pair, false, ['verify']); + var hash = await sha(json.m); + var buf, sig, check, tmp; try{ + buf = shim.Buffer.from(json.s, opt.encode || 'base64'); // NEW DEFAULT! + sig = new Uint8Array(buf); + check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash)); + if(!check){ throw "Signature did not match." } + }catch(e){ + if(SEA.opt.fallback){ + return await SEA.opt.fall_verify(data, pair, cb, opt); + } + } + var r = check? S.parse(json.m) : u; - if(cb){ cb(r) } + if(cb){ try{ cb(r) }catch(e){console.log(e)} } return r; - } catch(e) { + } catch(e) { + console.log(e); // mismatched owner FOR MARTTI SEA.err = e; + if(SEA.throw){ throw e } if(cb){ cb() } return; - }} + }}); module.exports = SEA.verify; + // legacy & ossl leak mitigation: + + var knownKeys = {}; + var keyForPair = SEA.opt.slow_leak = pair => { + if (knownKeys[pair]) return knownKeys[pair]; + var jwk = S.jwk(pair); + knownKeys[pair] = (shim.ossl || shim.subtle).importKey("jwk", jwk, S.ecdsa.pair, false, ["verify"]); + return knownKeys[pair]; + }; + + + SEA.opt.fall_verify = async function(data, pair, cb, opt, f){ + if(f === SEA.opt.fallback){ throw "Signature did not match" } f = f || 1; + var json = S.parse(data), pub = pair.pub || pair, key = await SEA.opt.slow_leak(pub); + var hash = (f <= SEA.opt.fallback)? shim.Buffer.from(await shim.subtle.digest({name: 'SHA-256'}, new shim.TextEncoder().encode(S.parse(json.m)))) : await sha(json.m); // this line is old bad buggy code but necessary for old compatibility. + var buf; var sig; var check; try{ + buf = shim.Buffer.from(json.s, opt.encode || 'base64') // NEW DEFAULT! + sig = new Uint8Array(buf) + check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash)) + if(!check){ throw "Signature did not match." } + }catch(e){ + buf = shim.Buffer.from(json.s, 'utf8') // AUTO BACKWARD OLD UTF8 DATA! + sig = new Uint8Array(buf) + check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash)) + if(!check){ throw "Signature did not match." } + } + var r = check? S.parse(json.m) : u; + if(cb){ try{ cb(r) }catch(e){console.log(e)} } + return r; + } + SEA.opt.fallback = 2; + \ No newline at end of file diff --git a/sea/work.js b/sea/work.js index 8e8f4c1f..37a6c043 100644 --- a/sea/work.js +++ b/sea/work.js @@ -2,48 +2,41 @@ var SEA = require('./root'); var shim = require('./shim'); var S = require('./settings'); + var sha = require('./sha256'); var u; - SEA.work = async (data, pair, cb) => { try { // used to be named `proof` - var salt = pair.epub || pair; // epub not recommended, salt should be random! + SEA.work = SEA.work || (async (data, pair, cb, opt) => { try { // used to be named `proof` + var salt = (pair||{}).epub || pair; // epub not recommended, salt should be random! + var opt = opt || {}; if(salt instanceof Function){ cb = salt; salt = u; } salt = salt || shim.random(9); - if (SEA.window) { - // For browser subtle works fine - const key = await shim.subtle.importKey( - 'raw', new shim.TextEncoder().encode(data), { name: 'PBKDF2' }, false, ['deriveBits'] - ) - const result = await shim.subtle.deriveBits({ - name: 'PBKDF2', - iterations: S.pbkdf2.iter, - salt: new shim.TextEncoder().encode(salt), - hash: S.pbkdf2.hash, - }, key, S.pbkdf2.ks * 8) - data = shim.random(data.length) // Erase data in case of passphrase - const r = shim.Buffer.from(result, 'binary').toString('utf8') - if(cb){ cb(r) } - return r; + data = (typeof data == 'string')? data : JSON.stringify(data); + if('sha' === (opt.name||'').toLowerCase().slice(0,3)){ + var rsha = shim.Buffer.from(await sha(data, opt.name), 'binary').toString(opt.encode || 'base64') + if(cb){ try{ cb(rsha) }catch(e){console.log(e)} } + return rsha; } - // For NodeJS crypto.pkdf2 rocks - const hash = crypto.pbkdf2Sync( - data, - new shim.TextEncoder().encode(salt), - S.pbkdf2.iter, - S.pbkdf2.ks, - S.pbkdf2.hash.replace('-', '').toLowerCase() - ) - data = shim.random(data.length) // Erase passphrase for app - const r = hash && hash.toString('utf8') - if(cb){ cb(r) } + var key = await (shim.ossl || shim.subtle).importKey('raw', new shim.TextEncoder().encode(data), {name: opt.name || 'PBKDF2'}, false, ['deriveBits']); + var work = await (shim.ossl || shim.subtle).deriveBits({ + name: opt.name || 'PBKDF2', + iterations: opt.iterations || S.pbkdf2.iter, + salt: new shim.TextEncoder().encode(opt.salt || salt), + hash: opt.hash || S.pbkdf2.hash, + }, key, opt.length || (S.pbkdf2.ks * 8)) + data = shim.random(data.length) // Erase data in case of passphrase + var r = shim.Buffer.from(work, 'binary').toString(opt.encode || 'base64') + if(cb){ try{ cb(r) }catch(e){console.log(e)} } return r; } catch(e) { + console.log(e); SEA.err = e; + if(SEA.throw){ throw e } if(cb){ cb() } return; - }} + }}); module.exports = SEA.work; \ No newline at end of file diff --git a/src/adapters/localStorage.js b/src/adapters/localStorage.js index 0be45cc9..e2a5fdea 100644 --- a/src/adapters/localStorage.js +++ b/src/adapters/localStorage.js @@ -1,24 +1,90 @@ if(typeof Gun === 'undefined'){ return } // TODO: localStorage is Browser only. But it would be nice if it could somehow plugin into NodeJS compatible localStorage APIs? -var root, noop = function(){}, u; -if(typeof window !== 'undefined'){ root = window } -var store = root.localStorage || {setItem: noop, removeItem: noop, getItem: noop}; - +var root, noop = function(){}, store, u; +try{store = (Gun.window||noop).localStorage}catch(e){} +if(!store){ + console.log("Warning: No localStorage exists to persist data to!"); + store = {setItem: function(k,v){this[k]=v}, removeItem: function(k){delete this[k]}, getItem: function(k){return this[k]}}; +} /* NOTE: Both `lib/file.js` and `lib/memdisk.js` are based on this design! If you update anything here, consider updating the other adapters as well. */ -Gun.on('opt', function(root){ +Gun.on('create', function(root){ + // This code is used to queue offline writes for resync. + // See the next 'opt' code below for actual saving of data. + var ev = this.to, opt = root.opt; + if(root.once){ return ev.next(root) } + //if(false === opt.localStorage){ return ev.next(root) } // we want offline resynce queue regardless! + opt.prefix = opt.file || 'gun/'; + var gap = Gun.obj.ify(store.getItem('gap/'+opt.prefix)) || {}; + var empty = Gun.obj.empty, id, to, go; + // add re-sync command. + if(!empty(gap)){ + var disk = Gun.obj.ify(store.getItem(opt.prefix)) || {}, send = {}; + Gun.obj.map(gap, function(node, soul){ + Gun.obj.map(node, function(val, key){ + send[soul] = Gun.state.to(disk[soul], key, send[soul]); + }); + }); + setTimeout(function(){ + root.on('out', {put: send, '#': root.ask(ack)}); + },1); + } + + root.on('out', function(msg){ + if(msg.lS){ return } + if(Gun.is(msg.$) && msg.put && !msg['@'] && !empty(opt.peers)){ + id = msg['#']; + Gun.graph.is(msg.put, null, map); + if(!to){ to = setTimeout(flush, opt.wait || 1) } + } + this.to.next(msg); + }); + root.on('ack', ack); + + function ack(ack){ // TODO: This is experimental, not sure if we should keep this type of event hook. + if(ack.err || !ack.ok){ return } + var id = ack['@']; + setTimeout(function(){ + Gun.obj.map(gap, function(node, soul){ + Gun.obj.map(node, function(val, key){ + if(id !== val){ return } + delete node[key]; + }); + if(empty(node)){ + delete gap[soul]; + } + }); + flush(); + }, opt.wait || 1); + }; + ev.next(root); + + var map = function(val, key, node, soul){ + (gap[soul] || (gap[soul] = {}))[key] = id; + } + var flush = function(){ + clearTimeout(to); + to = false; + try{store.setItem('gap/'+opt.prefix, JSON.stringify(gap)); + }catch(e){ Gun.log(err = e || "localStorage failure") } + } +}); + +Gun.on('create', function(root){ this.to.next(root); var opt = root.opt; if(root.once){ return } if(false === opt.localStorage){ return } - opt.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! + root.on('put', function(at){ this.to.next(at); Gun.graph.is(at.put, null, map); @@ -31,12 +97,12 @@ Gun.on('opt', function(root){ to = setTimeout(flush, opt.wait || 1); }); - root.on('get', function(at){ - this.to.next(at); - var lex = at.get, soul, data, u; - //setTimeout(function(){ + root.on('get', function(msg){ + this.to.next(msg); + var lex = msg.get, soul, data, u; + function to(){ if(!lex || !(soul = lex['#'])){ return } - //if(0 >= at.cap){ return } + //if(0 >= msg.cap){ return } var has = lex['.']; data = disk[soul] || u; if(data && has){ @@ -45,23 +111,29 @@ Gun.on('opt', function(root){ if(!data && !Gun.obj.empty(opt.peers)){ // if data not found, don't ack if there are peers. return; // Hmm, what if we have peers but we are disconnected? } - root.on('in', {'@': at['#'], put: Gun.graph.node(data), how: 'lS'}); - //},1); + //console.log("lS get", lex, data); + root.on('in', {'@': msg['#'], put: Gun.graph.node(data), how: 'lS', lS: msg.$ || root.$}); + }; + Gun.debug? setTimeout(to,1) : to(); }); var map = function(val, key, node, soul){ disk[soul] = Gun.state.to(node, key, disk[soul]); } - var flush = function(){ + var flush = function(data){ var err; count = 0; clearTimeout(to); to = false; var ack = acks; acks = {}; - try{store.setItem(opt.file, JSON.stringify(disk)); - }catch(e){ Gun.log(err = e || "localStorage failure") } + if(data){ disk = data } + try{store.setItem(opt.prefix, JSON.stringify(disk)); + }catch(e){ + Gun.log(err = (e || "localStorage failure") + " Consider using GUN's IndexedDB plugin for RAD for more storage space, temporary example at https://github.com/amark/gun/blob/master/test/tmp/indexedDB.html ."); + root.on('localStorage:error', {err: err, file: opt.prefix, flush: disk, retry: flush}); + } if(!err && !Gun.obj.empty(opt.peers)){ return } // only ack if there are no peers. Gun.obj.map(ack, function(yes, id){ root.on('in', { diff --git a/src/adapters/mesh.js b/src/adapters/mesh.js index 74f2a6f9..f15d735a 100644 --- a/src/adapters/mesh.js +++ b/src/adapters/mesh.js @@ -1,30 +1,43 @@ - +var Gun = require('../index'); var Type = require('../type'); function Mesh(ctx){ var mesh = function(){}; + var opt = ctx.opt || {}; + opt.log = opt.log || console.log; + opt.gap = opt.gap || opt.wait || 1; + opt.pack = opt.pack || (opt.memory? (opt.memory * 1000 * 1000) : 1399000000) * 0.3; // max_old_space_size defaults to 1400 MB. mesh.out = function(msg){ var tmp; if(this.to){ this.to.next(msg) } + //if(mesh.last != msg['#']){ return mesh.last = msg['#'], this.to.next(msg) } if((tmp = msg['@']) && (tmp = ctx.dup.s[tmp]) && (tmp = tmp.it) - && tmp.mesh){ - mesh.say(msg, tmp.mesh.via); + && tmp._){ + mesh.say(msg, (tmp._).via, 1); tmp['##'] = msg['##']; return; } // add hook for AXE? + if (Gun.AXE && opt && opt.super) { Gun.AXE.say(msg, mesh.say, this); return; } // rogowski mesh.say(msg); } - mesh.hear = function(msg, peer){ - if(!msg){ return } - var dup = ctx.dup, id, hash, tmp = msg[0]; - try{msg = JSON.parse(msg); - }catch(e){} - if('{' === tmp){ + ctx.on('create', function(root){ + root.opt.pid = root.opt.pid || Type.text.random(9); + this.to.next(root); + ctx.on('out', mesh.out); + }); + mesh.hear = function(raw, peer){ + if(!raw){ return } + var dup = ctx.dup, id, hash, msg, tmp = raw[0]; + if(opt.pack <= raw.length){ return mesh.say({dam: '!', err: "Message too big!"}, peer) } + try{msg = JSON.parse(raw); + }catch(e){opt.log('DAM JSON parse error', e)} + if('{' === tmp){ + if(!msg){ return } if(dup.check(id = msg['#'])){ return } dup.track(id, true).it = msg; // GUN core also dedups, so `true` is needed. if((tmp = msg['@']) && msg.put){ @@ -34,16 +47,22 @@ function Mesh(ctx){ (tmp = dup.s)[hash] = tmp[id]; } } - (msg.mesh = function(){}).via = peer; + (msg._ = function(){}).via = peer; if((tmp = msg['><'])){ - msg.mesh.to = Type.obj.map(tmp.split(','), function(k,i,m){m(k,true)}); + (msg._).to = Type.obj.map(tmp.split(','), function(k,i,m){m(k,true)}); + } + if(msg.dam){ + if(tmp = mesh.hear[msg.dam]){ + tmp(msg, peer, ctx); + } + return; } ctx.on('in', msg); - + return; } else if('[' === tmp){ - + if(!msg){ return } var i = 0, m; while(m = msg[i++]){ mesh.hear(m, peer); @@ -54,21 +73,21 @@ function Mesh(ctx){ } ;(function(){ - mesh.say = function(msg, peer){ + mesh.say = function(msg, peer, o){ /* TODO: Plenty of performance optimizations that can be made just based off of ordering, and reducing function calls for cached writes. */ if(!peer){ - Type.obj.map(ctx.opt.peers, function(peer){ + Type.obj.map(opt.peers, function(peer){ mesh.say(msg, peer); }); return; } - var tmp, wire = peer.wire || ((ctx.opt.wire) && ctx.opt.wire(peer)), msh, raw;// || open(peer, ctx); // TODO: Reopen! + var tmp, wire = peer.wire || ((opt.wire) && opt.wire(peer)), msh, raw;// || open(peer, ctx); // TODO: Reopen! if(!wire){ return } - msh = msg.mesh || empty; + msh = (msg._) || empty; if(peer === msh.via){ return } if(!(raw = msh.raw)){ raw = mesh.raw(msg) } if((tmp = msg['@']) @@ -78,30 +97,32 @@ function Mesh(ctx){ return; // TODO: this still needs to be tested in the browser! } } - if((tmp = msh.to) && (tmp[peer.url] || tmp[peer.id])){ return } // TODO: still needs to be tested + 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); - }, ctx.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{ if(wire.send){ - if(wire.readyState === wire.OPEN){ - wire.send(raw); - } else { - (peer.queue = peer.queue || []).push(raw); - } + wire.send(raw); } else if(peer.say){ peer.say(raw); @@ -112,12 +133,12 @@ function Mesh(ctx){ } }()); - + ;(function(){ mesh.raw = function(msg){ if(!msg){ return '' } - var dup = ctx.dup, msh = msg.mesh || {}, put, hash, tmp; + var dup = ctx.dup, msh = (msg._) || {}, put, hash, tmp; if(tmp = msh.raw){ return tmp } if(typeof msg === 'string'){ return msg } if(msg['@'] && (tmp = msg.put)){ @@ -127,15 +148,17 @@ function Mesh(ctx){ msg['##'] = hash; } (tmp = dup.s)[hash = msg['@']+hash] = tmp[msg['#']]; - msg['#'] = hash; + msg['#'] = hash || msg['#']; if(put){ (msg = Type.obj.to(msg)).put = _ } } - var i = 0, to = []; Type.obj.map(ctx.opt.peers, function(p){ + var i = 0, to = []; Type.obj.map(opt.peers, function(p){ to.push(p.url || p.id); if(++i > 9){ return true } // limit server, fast fix, improve later! }); msg['><'] = to.join(); var raw = $(msg); if(u !== put){ - 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; @@ -156,18 +179,38 @@ function Mesh(ctx){ function map(k){ this.to[k] = this.on[k]; } - var $ = JSON.stringify, _ = ':])([:' + var $ = JSON.stringify, _ = ':])([:'; }()); mesh.hi = function(peer){ - ctx.on('hi', peer); - var queue = peer.queue; - peer.queue = []; - Type.obj.map(queue, function(msg){ + var tmp = peer.wire || {}; + if(peer.id || peer.url){ + opt.peers[peer.url || peer.id] = peer; + Type.obj.del(opt.peers, tmp.id); + } else { + tmp = tmp.id = tmp.id || Type.text.random(9); + mesh.say({dam: '?'}, opt.peers[tmp] = peer); + } + if(!tmp.hied){ ctx.on(tmp.hied = 'hi', peer) } + tmp = peer.queue; peer.queue = []; + Type.obj.map(tmp, function(msg){ mesh.say(msg, peer); }); } + mesh.bye = function(peer){ + Type.obj.del(opt.peers, peer.id); // assume if peer.url then reconnect + ctx.on('bye', peer); + } + + mesh.hear['!'] = function(msg, peer){ opt.log('Error:', msg.err) } + mesh.hear['?'] = function(msg, peer){ + if(!msg.pid){ + return mesh.say({dam: '?', pid: opt.pid, '@': msg['#']}, peer); + } + peer.id = peer.id || msg.pid; + mesh.hi(peer); + } return mesh; } @@ -189,4 +232,4 @@ Mesh.hash = function(s){ // via SO try{ module.exports = Mesh }catch(e){} - \ No newline at end of file + diff --git a/src/adapters/websocket.js b/src/adapters/websocket.js index 03313687..ec71cf88 100644 --- a/src/adapters/websocket.js +++ b/src/adapters/websocket.js @@ -18,15 +18,15 @@ Gun.on('opt', function(root){ opt.WebSocket = websocket; var mesh = opt.mesh = opt.mesh || Gun.Mesh(root); - root.on('out', mesh.out); - opt.wire = opt.wire || open; - function open(peer){ - if(!peer || !peer.url){ return } + var wire = opt.wire; + opt.wire = open; + function open(peer){ try{ + if(!peer || !peer.url){ return wire && wire(peer) } var url = peer.url.replace('http', 'ws'); var wire = peer.wire = new opt.WebSocket(url); wire.onclose = function(){ - root.on('bye', peer); + opt.mesh.bye(peer); reconnect(peer); }; wire.onerror = function(error){ @@ -37,14 +37,14 @@ Gun.on('opt', function(root){ } }; wire.onopen = function(){ - mesh.hi(peer); + opt.mesh.hi(peer); } wire.onmessage = function(msg){ if(!msg){ return } - mesh.hear(msg.data || msg, peer); + opt.mesh.hear(msg.data || msg, peer); }; return wire; - } + }catch(e){}} function reconnect(peer){ clearTimeout(peer.defer); diff --git a/src/ask.js b/src/ask.js index abbe7540..fef9b151 100644 --- a/src/ask.js +++ b/src/ask.js @@ -15,7 +15,7 @@ module.exports = function ask(cb, as){ if(!cb){ return id } var to = this.on(id, cb, as); to.err = to.err || setTimeout(function(){ - to.next({err: "Error: No ACK received yet."}); + to.next({err: "Error: No ACK received yet.", lack: true}); to.off(); }, (this.opt||{}).lack || 9000); return id; diff --git a/src/back.js b/src/back.js index 8990a46d..32325b6f 100644 --- a/src/back.js +++ b/src/back.js @@ -3,10 +3,10 @@ var Gun = require('./root'); Gun.chain.back = function(n, opt){ var tmp; n = n || 1; if(-1 === n || Infinity === n){ - return this._.root.gun; + return this._.root.$; } else if(1 === n){ - return (this._.back || this._).gun; + return (this._.back || this._).$; } var gun = this, at = gun._; if(typeof n === 'string'){ @@ -21,18 +21,18 @@ Gun.chain.back = function(n, opt){ var tmp; return opt? gun : tmp; } else if((tmp = at.back)){ - return tmp.gun.back(n, opt); + return tmp.$.back(n, opt); } return; } if(n instanceof Function){ var yes, tmp = {back: at}; while((tmp = tmp.back) - && !(yes = n(tmp, opt))){} + && u === (yes = n(tmp, opt))){} return yes; } if(Gun.num.is(n)){ - return (at.back || at).gun.back(n - 1); + return (at.back || at).$.back(n - 1); } return this; } diff --git a/src/chain.js b/src/chain.js index f28a297b..18104481 100644 --- a/src/chain.js +++ b/src/chain.js @@ -3,11 +3,11 @@ // is complicated and was extremely hard to build. If you port GUN to another // language, consider implementing an easier API to build. var Gun = require('./root'); -Gun.chain.chain = function(){ - var at = this._, chain = new this.constructor(this), cat = chain._, root; +Gun.chain.chain = function(sub){ + var gun = this, at = gun._, chain = new (sub || gun).constructor(gun), cat = chain._, root; cat.root = root = at.root; cat.id = ++root.once; - cat.back = this._; + cat.back = gun._; cat.on = Gun.on; cat.on('in', input, cat); // For 'in' if I add my own listeners to each then I MUST do it before in gets called. If I listen globally for all incoming data instead though, regardless of individual listeners, I can transform the data there and then as well. cat.on('out', output, cat); // However for output, there isn't really the global option. I must listen by adding my own listener individually BEFORE this one is ever called. @@ -15,55 +15,69 @@ Gun.chain.chain = function(){ } function output(msg){ - var put, get, at = this.as, back = at.back, root = at.root; - if(!msg.gun){ msg.gun = at.gun } + var put, get, at = this.as, back = at.back, root = at.root, tmp; + if(!msg.$){ msg.$ = at.$ } this.to.next(msg); if(get = msg.get){ /*if(u !== at.put){ at.on('in', at); return; }*/ + if(at.lex){ msg.get = obj_to(at.lex, msg.get) } if(get['#'] || at.soul){ get['#'] = get['#'] || at.soul; msg['#'] || (msg['#'] = text_rand(9)); - back = (root.gun.get(get['#'])._); + back = (root.$.get(get['#'])._); if(!(get = get['.'])){ + tmp = back.ack; + if(!tmp){ back.ack = -1 } if(obj_has(back, 'put')){ - //if(u !== back.put){ back.on('in', back); } - if(back.ack){ return } - msg.gun = back.gun; - back.ack = -1; + if(tmp){ return } + msg.$ = back.$; } else if(obj_has(back.put, get)){ + put = (back.$.get(get)._); + if(!(tmp = put.ack)){ put.ack = -1 } back.on('in', { - gun: back.gun, + $: back.$, put: Gun.state.to(back.put, get), get: back.get }); - return; + if(tmp){ return } + } else + if('string' != typeof get){ + var put = {}, meta = (back.put||{})._; + Gun.obj.map(back.put, function(v,k){ + if(!Gun.text.match(k, get)){ return } + put[k] = v; + }) + if(!Gun.obj.empty(put)){ + put._ = meta; + back.on('in', {$: back.$, put: put, get: back.get}) + } } root.ask(ack, msg); return root.on('in', msg); } - if(root.now){ - root.now[at.id] = root.now[at.id] || true; - } + if(root.now){ root.now[at.id] = root.now[at.id] || true; at.pass = {} } if(get['.']){ if(at.get){ - msg = {get: {'.': at.get}, gun: at.gun}; - (back.ask || (back.ask = {}))[at.get] = msg.gun._; // TODO: PERFORMANCE? More elegant way? + msg = {get: {'.': at.get}, $: at.$}; + //if(back.ask || (back.ask = {})[at.get]){ return } + (back.ask || (back.ask = {})); + back.ask[at.get] = msg.$._; // TODO: PERFORMANCE? More elegant way? return back.on('out', msg); } - msg = {get: {}, gun: at.gun}; + msg = {get: {}, $: at.$}; return back.on('out', msg); } at.ack = at.ack || -1; if(at.get){ - msg.gun = at.gun; + msg.$ = at.$; get['.'] = at.get; - (back.ask || (back.ask = {}))[at.get] = msg.gun._; // TODO: PERFORMANCE? More elegant way? + (back.ask || (back.ask = {}))[at.get] = msg.$._; // TODO: PERFORMANCE? More elegant way? return back.on('out', msg); } } @@ -71,25 +85,23 @@ function output(msg){ } function input(msg){ - var ev = this, cat = this.as, gun = msg.gun, at = gun._, change = msg.put, rel, tmp; + var eve = this, cat = eve.as, root = cat.root, gun = msg.$, at = (gun||empty)._ || empty, change = msg.put, rel, tmp; if(cat.get && msg.get !== cat.get){ msg = obj_to(msg, {get: cat.get}); } if(cat.has && at !== cat){ - msg = obj_to(msg, {gun: cat.gun}); + msg = obj_to(msg, {$: cat.$}); if(at.ack){ cat.ack = at.ack; //cat.ack = cat.ack || at.ack; } } - if(node_ === cat.get && change && change['#']){ - // TODO: Potential bug? What if (soul.has = pointer) gets changed to (soul.has = primitive), we still need to clear out / wipe /reset (soul.has._) to have _id = nothing, or puts might have false positives (revert back to old soul). - cat._id = change['#']; - } if(u === change){ - ev.to.next(msg); - if(cat.soul){ return } // TODO: BUG, I believe the fresh input refactor caught an edge case that a `gun.get('soul').get('key')` that points to a soul that doesn't exist will not trigger val/get etc. - echo(cat, msg, ev); + tmp = at.put; + eve.to.next(msg); + if(cat.soul){ return } // TODO: BUG, I believee the fresh input refactor caught an edge case that a `gun.get('soul').get('key')` that points to a soul that doesn't exist will not trigger val/get etc. + if(u === tmp && u !== at.put){ return } + echo(cat, msg, eve); if(cat.has){ not(cat, msg); } @@ -98,107 +110,117 @@ function input(msg){ return; } if(cat.soul){ - ev.to.next(msg); - echo(cat, msg, ev); - obj_map(change, map, {at: msg, cat: cat}); + eve.to.next(msg); + echo(cat, msg, eve); + if(cat.next){ obj_map(change, map, {msg: msg, cat: cat}) } return; } - if(!(rel = Gun.val.rel.is(change))){ + if(!(rel = Gun.val.link.is(change))){ if(Gun.val.is(change)){ if(cat.has || cat.soul){ not(cat, msg); } else if(at.has || at.soul){ - (at.echo || (at.echo = {}))[cat.id] = cat; + (at.echo || (at.echo = {}))[cat.id] = at.echo[at.id] || cat; (cat.map || (cat.map = {}))[at.id] = cat.map[at.id] || {at: at}; //if(u === at.put){ return } // Not necessary but improves performance. If we have it but at does not, that means we got things out of order and at will get it. Once at gets it, it will tell us again. } - ev.to.next(msg); - echo(cat, msg, ev); + eve.to.next(msg); + echo(cat, msg, eve); return; } if(cat.has && at !== cat && obj_has(at, 'put')){ cat.put = at.put; }; if((rel = Gun.node.soul(change)) && at.has){ - at.put = (cat.root.gun.get(rel)._).put; + at.put = (cat.root.$.get(rel)._).put; } - ev.to.next(msg); - echo(cat, msg, ev); + tmp = (root.stop || {})[at.id]; + //if(tmp && tmp[cat.id]){ } else { + eve.to.next(msg); + //} relate(cat, msg, at, rel); - obj_map(change, map, {at: msg, cat: cat}); + echo(cat, msg, eve); + if(cat.next){ obj_map(change, map, {msg: msg, cat: cat}) } return; } + var was = root.stop; + tmp = root.stop || {}; + tmp = tmp[at.id] || (tmp[at.id] = {}); + //if(tmp[cat.id]){ return } + tmp.is = tmp.is || at.put; + tmp[cat.id] = at.put || true; + //if(root.stop){ + eve.to.next(msg) + //} relate(cat, msg, at, rel); - ev.to.next(msg); - echo(cat, msg, ev); + echo(cat, msg, eve); } function relate(at, msg, from, rel){ if(!rel || node_ === at.get){ return } - var tmp = (at.root.gun.get(rel)._); + var tmp = (at.root.$.get(rel)._); if(at.has){ from = tmp; - } else + } else if(from.has){ relate(from, msg, from, rel); } if(from === at){ return } - (from.echo || (from.echo = {}))[at.id] = at; + if(!from.$){ from = {} } + (from.echo || (from.echo = {}))[at.id] = from.echo[at.id] || at; if(at.has && !(at.map||empty)[from.id]){ // if we haven't seen this before. not(at, msg); } - tmp = (at.map || (at.map = {}))[from.id] = at.map[from.id] || {at: from}; - var now = at.root.now; - //now = now || at.root.stop; - if(rel === tmp.rel){ - // NOW is a hack to get synchronous replies to correctly call. - // and STOP is a hack to get async behavior to correctly call. - // neither of these are ideal, need to be fixed without hacks, - // but for now, this works for current tests. :/ - if(!now){ + tmp = from.id? ((at.map || (at.map = {}))[from.id] = at.map[from.id] || {at: from}) : {}; + if(rel === tmp.link){ + if(!(tmp.pass || at.pass)){ return; - /*var stop = at.root.stop; - if(!stop){ return } - if(stop[at.id] === rel){ return } - stop[at.id] = rel;*/ - } else { - if(u === now[at.id]){ return } - if((now._ || (now._ = {}))[at.id] === rel){ return } - now._[at.id] = rel; } } - ask(at, tmp.rel = rel); + if(at.pass){ + Gun.obj.map(at.map, function(tmp){ tmp.pass = true }) + obj_del(at, 'pass'); + } + if(tmp.pass){ obj_del(tmp, 'pass') } + if(at.has){ at.link = rel } + ask(at, tmp.link = rel); } function echo(at, msg, ev){ if(!at.echo){ return } // || node_ === at.get ? - if(at.has){ msg = obj_to(msg, {event: ev}) } + //if(at.has){ msg = obj_to(msg, {event: ev}) } obj_map(at.echo, reverb, msg); } function reverb(to){ + if(!to || !to.on){ return } to.on('in', this); } function map(data, key){ // Map over only the changes on every update. - var cat = this.cat, next = cat.next || empty, via = this.at, chain, at, tmp; + var cat = this.cat, next = cat.next || empty, via = this.msg, chain, at, tmp; if(node_ === key && !next[key]){ return } if(!(at = next[key])){ return; } - //if(data && data[_soul] && (tmp = Gun.val.rel.is(data)) && (tmp = (cat.root.gun.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; } - chain = at.gun; - } else { - chain = via.gun.get(key); + chain = at.$; + } else + if(tmp = via.$){ + tmp = (chain = via.$.get(key))._; + if(u === tmp.put || !Gun.val.link.is(data)){ + tmp.put = data; + } } at.on('in', { put: data, get: key, - gun: chain, + $: chain, via: via }); } @@ -206,66 +228,67 @@ function not(at, msg){ if(!(at.has || at.soul)){ return } var tmp = at.map, root = at.root; at.map = null; - if(!root.now || !root.now[at.id]){ - if((!msg['@']) && null === tmp){ return } + if(at.has){ + if(at.dub && at.root.stop){ at.dub = null } + at.link = null; } - if(u === tmp && Gun.val.rel.is(at.put)){ return } // TODO: Bug? Threw second condition in for a particular test, not sure if a counter example is tested though. + //if(!root.now || !root.now[at.id]){ + if(!at.pass){ + if((!msg['@']) && null === tmp){ return } + //obj_del(at, 'pass'); + } + if(u === tmp && Gun.val.link.is(at.put)){ return } // This prevents the very first call of a thing from triggering a "clean up" call. // TODO: link.is(at.put) || !val.is(at.put) ? obj_map(tmp, function(proxy){ if(!(proxy = proxy.at)){ return } obj_del(proxy.echo, at.id); }); + tmp = at.put; obj_map(at.next, function(neat, key){ + if(u === tmp && u !== at.put){ return true } neat.put = u; if(neat.ack){ neat.ack = -1; } neat.on('in', { get: key, - gun: neat.gun, + $: neat.$, put: u }); }); } function ask(at, soul){ - var tmp = (at.root.gun.get(soul)._); + var tmp = (at.root.$.get(soul)._); if(at.ack){ tmp.on('out', {get: {'#': soul}}); if(!at.ask){ return } // TODO: PERFORMANCE? More elegant way? } - obj_map(at.ask || at.next, function(neat, key){ - //(tmp.gun.get(key)._).on('out', {get: {'#': soul, '.': key}}); - //tmp.on('out', {get: {'#': soul, '.': key}}); + tmp = at.ask; Gun.obj.del(at, 'ask'); + obj_map(tmp || at.next, function(neat, key){ neat.on('out', {get: {'#': soul, '.': key}}); - //at.on('out', {get: {'#': soul, '.': key}}); }); Gun.obj.del(at, 'ask'); // TODO: PERFORMANCE? More elegant way? } function ack(msg, ev){ - var as = this.as, get = as.get || empty, at = as.gun._, tmp = (msg.put||empty)[get['#']]; - if(at.ack){ at.ack = (at.ack + 1) || 1 } - if(!msg.put /*|| node_ == get['.']*/ || (get['.'] && !obj_has(tmp, at.get))){ + var as = this.as, get = as.get || empty, at = as.$._, tmp = (msg.put||empty)[get['#']]; + if(at.ack){ at.ack = (at.ack + 1) || 1; } + if(!msg.put || ('string' == typeof get['.'] && !obj_has(tmp, at.get))){ if(at.put !== u){ return } - //at.ack = 0; at.on('in', { get: at.get, put: at.put = u, - gun: at.gun, + $: at.$, '@': msg['@'] - }) + }); return; } if(node_ == get['.']){ // is this a security concern? - at.on('in', {get: at.get, put: tmp[at.get], gun: at.gun, '@': msg['@']}); + at.on('in', {get: at.get, put: Gun.val.link.ify(get['#']), $: at.$, '@': msg['@']}); return; } - //if(/*!msg.gun &&*/ !get['.'] && get['#']){ at.ack = (at.ack + 1) || 1 } - //msg = obj_to(msg); - msg.gun = at.root.gun; - //Gun.on('put', at); - Gun.on.put(msg, at.root.gun); + Gun.on.put(msg, at.root.$); } var empty = {}, u; var obj = Gun.obj, obj_has = obj.has, obj_put = obj.put, obj_del = obj.del, obj_to = obj.to, obj_map = obj.map; var text_rand = Gun.text.random; -var _soul = Gun.val.rel._, node_ = Gun.node._; +var _soul = Gun.val.link._, node_ = Gun.node._; \ No newline at end of file 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; diff --git a/src/get.js b/src/get.js index a002c987..9f622b60 100644 --- a/src/get.js +++ b/src/get.js @@ -1,36 +1,49 @@ var Gun = require('./root'); Gun.chain.get = function(key, cb, as){ - var gun; + var gun, tmp; if(typeof key === 'string'){ var back = this, cat = back._; - var next = cat.next || empty, tmp; + var next = cat.next || empty; if(!(gun = next[key])){ gun = cache(key, back); } - gun = gun.gun; + gun = gun.$; } else if(key instanceof Function){ + if(true === cb){ return soul(this, key, cb, as) } gun = this; var at = gun._, root = at.root, tmp = root.now, ev; as = cb || {}; + as.at = at; as.use = key; as.out = as.out || {}; as.out.get = as.out.get || {}; - ev = at.on('in', use, as); + (ev = at.on('in', use, as)).rid = rid; (root.now = {$:1})[as.now = at.id] = ev; + var mum = root.mum; root.mum = {}; at.on('out', as.out); + root.mum = mum; root.now = tmp; return gun; } else if(num_is(key)){ return this.get(''+key, cb, as); + } else + if(tmp = rel.is(key)){ + return this.get(tmp, cb, as); + } else + if(obj.is(key)){ + gun = this; + if(tmp = ((tmp = key['#'])||empty)['='] || tmp){ gun = gun.get(tmp) } + gun._.lex = key; + return gun; } else { (as = this.chain())._.err = {err: Gun.log('Invalid get request!', key)}; // CLEAN UP if(cb){ cb.call(as, as._.err) } return as; } - if(tmp = cat.stun){ // TODO: Refactor? + if(tmp = this._.stun){ // TODO: Refactor? gun._.stun = gun._.stun || tmp; } if(cb && cb instanceof Function){ @@ -42,7 +55,7 @@ function cache(key, back){ var cat = back._, next = cat.next, gun = back.chain(), at = gun._; if(!next){ next = cat.next = {} } next[at.get = key] = at; - if(back === cat.root.gun){ + if(back === cat.root.$){ at.soul = key; } else if(cat.soul || cat.has){ @@ -53,25 +66,68 @@ function cache(key, back){ } return at; } +function soul(gun, cb, opt, as){ + var cat = gun._, acks = 0, tmp; + if(tmp = cat.soul || cat.link || cat.dub){ return cb(tmp, as, cat), gun } + gun.get(function(msg, ev){ + if(u === msg.put && (tmp = (obj_map(cat.root.opt.peers, function(v,k,t){t(k)})||[]).length) && ++acks < tmp){ + return; + } + ev.rid(msg); + var at = ((at = msg.$) && at._) || {}; + tmp = at.link || at.soul || rel.is(msg.put) || node_soul(msg.put) || at.dub; + cb(tmp, as, msg, ev); + }, {out: {get: {'.':true}}}); + return gun; +} function use(msg){ - var ev = this, as = ev.as, gun = msg.gun, at = gun._, root = at.root, data = msg.put, tmp; - if((tmp = root.now) && ev !== tmp[as.now]){ - return ev.to.next(msg); - } - if(u === data){ - data = at.put; - } + var eve = this, as = eve.as, cat = as.at, root = cat.root, gun = msg.$, at = (gun||{})._ || {}, data = msg.put || at.put, tmp; + if((tmp = root.now) && eve !== tmp[as.now]){ return eve.to.next(msg) } + //console.log("USE:", cat.id, cat.soul, cat.has, cat.get, msg, root.mum); + //if(at.async && msg.root){ return } + //if(at.async === 1 && cat.async !== true){ return } + //if(root.stop && root.stop[at.id]){ return } root.stop && (root.stop[at.id] = true); + //if(!at.async && !cat.async && at.put && msg.put === at.put){ return } + //else if(!cat.async && msg.put !== at.put && root.stop && root.stop[at.id]){ return } root.stop && (root.stop[at.id] = true); + + + //root.stop && (root.stop.id = root.stop.id || Gun.text.random(2)); + //if((tmp = root.stop) && (tmp = tmp[at.id] || (tmp[at.id] = {})) && tmp[cat.id]){ return } tmp && (tmp[cat.id] = true); + if(eve.seen && at.id && eve.seen[at.id]){ return eve.to.next(msg) } + //if((tmp = root.stop)){ if(tmp[at.id]){ return } tmp[at.id] = msg.root; } // temporary fix till a better solution? if((tmp = data) && tmp[rel._] && (tmp = rel.is(tmp))){ - tmp = (at.root.gun.get(tmp)._); + tmp = ((msg.$$ = at.root.gun.get(tmp))._); if(u !== tmp.put){ - msg = obj_to(msg, {put: tmp.put}); + msg = obj_to(msg, {put: data = tmp.put}); } } - as.use(msg, msg.event || ev); - ev.to.next(msg); + if((tmp = root.mum) && at.id){ // TODO: can we delete mum entirely now? + var id = at.id + (eve.id || (eve.id = Gun.text.random(9))); + if(tmp[id]){ return } + if(u !== data && !rel.is(data)){ tmp[id] = true; } + } + as.use(msg, eve); + if(eve.stun){ + eve.stun = null; + return; + } + eve.to.next(msg); } -var obj = Gun.obj, obj_has = obj.has, obj_to = Gun.obj.to; +function rid(at){ + var cat = this.on; + if(!at || cat.soul || cat.has){ return this.off() } + if(!(at = (at = (at = at.$ || at)._ || at).id)){ return } + var map = cat.map, tmp, seen; + //if(!map || !(tmp = map[at]) || !(tmp = tmp.at)){ return } + if(tmp = (seen = this.seen || (this.seen = {}))[at]){ return true } + seen[at] = true; + return; + //tmp.echo[cat.id] = {}; // TODO: Warning: This unsubscribes ALL of this chain's listeners from this link, not just the one callback event. + //obj.del(map, at); // TODO: Warning: This unsubscribes ALL of this chain's listeners from this link, not just the one callback event. + return; +} +var obj = Gun.obj, obj_map = obj.map, obj_has = obj.has, obj_to = Gun.obj.to; var num_is = Gun.num.is; -var rel = Gun.val.rel, node_ = Gun.node._; +var rel = Gun.val.link, node_soul = Gun.node.soul, node_ = Gun.node._; var empty = {}, u; \ No newline at end of file diff --git a/src/graph.js b/src/graph.js index 25472ce1..eb96fffe 100644 --- a/src/graph.js +++ b/src/graph.js @@ -31,8 +31,9 @@ var Graph = {}; 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 || {}; env.seen = env.seen || []; env.as = env.as || as; @@ -45,24 +46,26 @@ 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.link = at.link || Val.link.ify(Node.soul(at.node)); + if(at.obj !== env.shell){ + env.graph[Val.link.is(at.link)] = at.node; + } } return at; } function map(v,k,n){ var at = this, env = at.env, is, tmp; - if(Node._ === k && obj_has(v,Val.rel._)){ + if(Node._ === k && obj_has(v,Val.link._)){ return n._; // TODO: Bug? } if(!(is = valid(v,k,n, at,env))){ return } if(!k){ at.node = at.node || n || {}; - if(obj_has(v, Node._)){ + 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); @@ -81,14 +84,14 @@ var Graph = {}; } 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.rel.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]; @@ -128,13 +131,13 @@ Graph.node = function(node){ } 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; } diff --git a/src/map.js b/src/map.js index 741a4b91..cb60a711 100644 --- a/src/map.js +++ b/src/map.js @@ -3,10 +3,9 @@ var Gun = require('./index'); Gun.chain.map = function(cb, opt, t){ var gun = this, cat = gun._, chain; if(!cb){ - if(chain = cat.fields){ return chain } - chain = cat.fields = gun.chain(); - chain._.val = gun.back('val'); - chain._.MAPOF = cat.soul; + if(chain = cat.each){ return chain } + cat.each = chain = gun.chain(); + chain._.nix = gun.back('nix'); gun.on('in', map, chain._); return chain; } @@ -15,24 +14,23 @@ 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(Gun.is(next)){ - chain._.on('in', next._); - return; - } - chain._.on('in', {get: key, put: next, gun: chain}); + if(data === next){ return chain._.on('in', at) } + if(Gun.is(next)){ return chain._.on('in', next._) } + chain._.on('in', {get: key, put: next}); }); return chain; } function map(msg){ - if(!msg.put || Gun.val.is(msg.put)){ return } - if(this.as.val){ this.off() } // TODO: Ugly hack! + if(!msg.put || Gun.val.is(msg.put)){ return this.to.next(msg) } + if(this.as.nix){ this.off() } // TODO: Ugly hack! obj_map(msg.put, each, {at: this.as, msg: msg}); this.to.next(msg); } function each(v,k){ if(n_ === k){ return } - var msg = this.msg, gun = msg.gun, at = this.at, tmp = (gun.get(k)._); - (tmp.echo || (tmp.echo = {}))[at.id] = at; + var msg = this.msg, gun = msg.$, at = gun._, cat = this.at, tmp = at.lex; + if(tmp && !Gun.text.match(k, tmp['.'] || tmp['#'] || tmp)){ return } // TODO: Ugly hack! + ((tmp = gun.get(k)._).echo || (tmp.echo = {}))[cat.id] = tmp.echo[cat.id] || cat; } var obj_map = Gun.obj.map, noop = function(){}, event = {stun: noop, off: noop}, n_ = Gun.node._, u; \ No newline at end of file diff --git a/src/node.js b/src/node.js index 0a016d26..66472d1f 100644 --- a/src/node.js +++ b/src/node.js @@ -10,7 +10,7 @@ Node.soul.ify = function(n, o){ // put a soul on an object. n._[soul_] = o.soul || n._[soul_] || text_random(); // put the soul on it. return n; } -Node.soul._ = Val.rel._; +Node.soul._ = Val.link._; ;(function(){ Node.is = function(n, cb, as){ var s; // checks to see if an object is a valid node. if(!obj_is(n)){ return false } // must be an object. diff --git a/src/on.js b/src/on.js index d153bfb5..55d0167a 100644 --- a/src/on.js +++ b/src/on.js @@ -5,50 +5,45 @@ Gun.chain.on = function(tag, arg, eas, as){ if(typeof tag === 'string'){ if(!arg){ return at.on(tag) } act = at.on(tag, arg, eas || at, as); - if(eas && eas.gun){ + if(eas && eas.$){ (eas.subs || (eas.subs = [])).push(act); } - off = function() { - if (act && act.off) act.off(); - off.off(); - }; - off.off = gun.off.bind(gun) || noop; - gun.off = off; return gun; } var opt = arg; opt = (true === opt)? {change: true} : opt || {}; + opt.at = at; opt.ok = tag; - opt.last = {}; + //opt.last = {}; gun.get(ok, opt); // TODO: PERF! Event listener leak!!!? return gun; } -function ok(at, ev){ var opt = this; - var gun = at.gun, cat = gun._, data = cat.put || at.put, tmp = opt.last, id = cat.id+at.get, tmp; +function ok(msg, ev){ var opt = this; + var gun = msg.$, at = (gun||{})._ || {}, data = at.put || msg.put, cat = opt.at, tmp; if(u === data){ return; } - if(data && data[rel._] && (tmp = rel.is(data))){ - tmp = (cat.root.gun.get(tmp)._); + if(tmp = msg.$$){ + tmp = (msg.$$._); if(u === tmp.put){ return; } data = tmp.put; } if(opt.change){ // TODO: BUG? Move above the undef checks? - data = at.put; + data = msg.put; } // DEDUPLICATE // TODO: NEEDS WORK! BAD PROTOTYPE - if(tmp.put === data && tmp.get === id && !Gun.node.soul(data)){ return } - tmp.put = data; - tmp.get = id; + //if(tmp.put === data && tmp.get === id && !Gun.node.soul(data)){ return } + //tmp.put = data; + //tmp.get = id; // DEDUPLICATE // TODO: NEEDS WORK! BAD PROTOTYPE - cat.last = data; + //at.last = data; if(opt.as){ - opt.ok.call(opt.as, at, ev); + opt.ok.call(opt.as, msg, ev); } else { - opt.ok.call(gun, data, at.get, at, ev); + opt.ok.call(gun, data, msg.get, msg, ev); } } @@ -64,14 +59,14 @@ Gun.chain.once = function(cb, opt){ } if(cb){ (opt = opt || {}).ok = cb; - opt.cat = at; + opt.at = at; opt.out = {'#': Gun.text.random(9)}; gun.get(val, {as: opt}); opt.async = true; //opt.async = at.stun? 1 : true; } else { Gun.log.once("valonce", "Chainable val is experimental, its behavior and API may change moving forward. Please play with it and report bugs and ideas on how to improve it."); var chain = gun.chain(); - chain._.val = gun.once(function(){ + chain._.nix = gun.once(function(){ chain._.on('in', gun._); }); return chain; @@ -79,35 +74,26 @@ Gun.chain.once = function(cb, opt){ return gun; } -function val(msg, ev, to){ - var opt = this.as, cat = opt.cat, gun = msg.gun, coat = gun._, data = coat.put || msg.put, tmp; - if(u === data){ - //return; - } - //if(coat.soul && !(0 < coat.ack)){ return } - if(tmp = Gun.node.soul(data) || rel.is(data)){ - //if(data && data[rel._] && (tmp = rel.is(data))){ - tmp = (cat.root.gun.get(tmp)._); - if(u === tmp.put){//} || !(0 < tmp.ack)){ - return; +function val(msg, eve, to){ + if(!msg.$){ eve.off(); return } + var opt = this.as, cat = opt.at, gun = msg.$, at = gun._, data = at.put || msg.put, link, tmp; + if(tmp = msg.$$){ + link = tmp = (msg.$$._); + if(u !== link.put){ + data = link.put; } - data = tmp.put; } - if(ev.wait){ clearTimeout(ev.wait) } - //if(!to && (!(0 < coat.ack) || ((true === opt.async) && 0 !== opt.wait))){ - if(!to){ - ev.wait = setTimeout(function(){ - val.call({as:opt}, msg, ev, ev.wait || 1); + if((tmp = eve.wait) && (tmp = tmp[at.id])){ clearTimeout(tmp) } + if((!to && (u === data || at.soul || at.link || (link && !(0 < link.ack)))) + || (u === data && (tmp = (obj_map(at.root.opt.peers, function(v,k,t){t(k)})||[]).length) && (!to && (link||at).ack <= tmp))){ + tmp = (eve.wait = {})[at.id] = setTimeout(function(){ + val.call({as:opt}, msg, eve, tmp || 1); }, opt.wait || 99); return; } - if(cat.has || cat.soul){ - if(ev.off()){ return } // if it is already off, don't call again! - } else { - if((opt.seen = opt.seen || {})[coat.id]){ return } - opt.seen[coat.id] = true; - } - opt.ok.call(msg.gun || opt.gun, data, msg.get); + if(link && u === link.put && (tmp = rel.is(data))){ data = Gun.node.ify({}, tmp) } + eve.rid(msg); + opt.ok.call(gun || opt.$, data, msg.get); } Gun.chain.off = function(){ @@ -133,20 +119,20 @@ Gun.chain.off = function(){ } if(tmp = at.map){ obj_map(tmp, function(at){ - if(at.rel){ - cat.root.gun.get(at.rel).off(); + if(at.link){ + cat.root.$.get(at.link).off(); } }); } if(tmp = at.next){ obj_map(tmp, function(neat){ - neat.gun.off(); + neat.$.off(); }); } at.on('off', {}); return gun; } var obj = Gun.obj, obj_map = obj.map, obj_has = obj.has, obj_del = obj.del, obj_to = obj.to; -var rel = Gun.val.rel; +var rel = Gun.val.link; var empty = {}, noop = function(){}, u; \ No newline at end of file diff --git a/src/onto.js b/src/onto.js index a40a49d8..4851d32b 100644 --- a/src/onto.js +++ b/src/onto.js @@ -2,16 +2,16 @@ // On event emitter generic javascript utility. module.exports = function onto(tag, arg, as){ if(!tag){ return {to: onto} } - var tag = (this.tag || (this.tag = {}))[tag] || + var u, tag = (this.tag || (this.tag = {}))[tag] || (this.tag[tag] = {tag: tag, to: onto._ = { next: function(arg){ var tmp; - if((tmp = this.to)){ + if((tmp = this.to)){ tmp.next(arg); }} }}); if(arg instanceof Function){ var be = { - off: onto.off || + off: onto.off || (onto.off = function(){ if(this.next === onto._.next){ return !0 } if(this === this.the.last){ @@ -33,7 +33,7 @@ module.exports = function onto(tag, arg, as){ (be.back = tag.last || tag).to = be; return tag.last = be; } - (tag = tag.to).next(arg); + if((tag = tag.to) && u !== arg){ tag.next(arg) } return tag; }; \ No newline at end of file diff --git a/src/polyfill/unbuild.js b/src/polyfill/unbuild.js index a3738f02..20d0d0e9 100644 --- a/src/polyfill/unbuild.js +++ b/src/polyfill/unbuild.js @@ -1,16 +1,17 @@ -var root; -if(typeof window !== "undefined"){ root = window } -if(typeof global !== "undefined"){ root = global } -root = root || {}; -var console = root.console || {log: function(){}}; -function USE(arg){ - return arg.slice? USE[R(arg)] : function(mod, path){ - arg(mod = {exports: {}}); - USE[R(path)] = mod.exports; - } - function R(p){ - return p.split('/').slice(-1).toString().replace('.js',''); - } -} -if(typeof module !== "undefined"){ var common = module } + var root; + if(typeof window !== "undefined"){ root = window } + if(typeof global !== "undefined"){ root = global } + root = root || {}; + var console = root.console || {log: function(){}}; + function USE(arg, req){ + return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){ + arg(mod = {exports: {}}); + USE[R(path)] = mod.exports; + } + function R(p){ + return p.split('/').slice(-1).toString().replace('.js',''); + } + } + if(typeof module !== "undefined"){ var common = module } + \ No newline at end of file diff --git a/src/put.js b/src/put.js index a541aeb8..eb79b74e 100644 --- a/src/put.js +++ b/src/put.js @@ -4,14 +4,25 @@ Gun.chain.put = function(data, cb, as){ // #soul.has=value>state // ~who#where.where=what>when@was // TODO: BUG! Put probably cannot handle plural chains! - var gun = this, at = (gun._), root = at.root.gun, tmp; + var gun = this, at = (gun._), root = at.root.$, ctx = root._, M = 100, tmp; + if(!ctx.puta){ if(tmp = ctx.puts){ if(tmp > M){ // without this, when synchronous, writes to a 'not found' pile up, when 'not found' resolves it recursively calls `put` which incrementally resolves each write. Stack overflow limits can be as low as 10K, so this limit is hardcoded to 1% of 10K. + (ctx.stack || (ctx.stack = [])).push([gun, data, cb, as]); + if(ctx.puto){ return } + ctx.puto = setTimeout(function drain(){ + var d = ctx.stack.splice(0,M), i = 0, at; ctx.puta = true; + while(at = d[i++]){ at[0].put(at[1], at[2], at[3]) } delete ctx.puta; + if(ctx.stack.length){ return ctx.puto = setTimeout(drain, 0) } + ctx.stack = ctx.puts = ctx.puto = null; + }, 0); + return gun; + } ++ctx.puts } else { ctx.puts = 1 } } as = as || {}; as.data = data; - as.gun = as.gun || gun; + as.via = as.$ = as.via || as.$ || gun; if(typeof cb === 'string'){ as.soul = cb; } else { - as.ack = cb; + as.ack = as.ack || cb; } if(at.soul){ as.soul = at.soul; @@ -22,39 +33,40 @@ Gun.chain.put = function(data, cb, as){ if(as.res){ as.res() } return gun; } - as.soul = as.soul || (as.not = Gun.node.soul(as.data) || ((root._).opt.uuid || Gun.text.random)()); + as.soul = as.soul || (as.not = Gun.node.soul(as.data) || (as.via.back('opt.uuid') || Gun.text.random)()); if(!as.soul){ // polyfill async uuid for SEA - (root._).opt.uuid(function(err, soul){ // TODO: improve perf without anonymous callback + as.via.back('opt.uuid')(function(err, soul){ // TODO: improve perf without anonymous callback if(err){ return Gun.log(err) } // TODO: Handle error! - (as.ref||as.gun).put(as.data, as.soul = soul, as); + (as.ref||as.$).put(as.data, as.soul = soul, as); }); return gun; } - as.gun = gun = root.get(as.soul); - as.ref = as.gun; + as.$ = root.get(as.soul); + as.ref = as.$; ify(as); return gun; } if(Gun.is(data)){ - data.get('_').get(function(at, ev, tmp){ ev.off(); - if(!(tmp = at.gun) || !(tmp = tmp._.back) || !tmp.soul){ - return Gun.log("The reference you are saving is a", typeof at.put, '"'+ as.put +'", not a node (object)!'); + data.get(function(soul, o, msg){ + if(!soul){ + return Gun.log("The reference you are saving is a", typeof msg.put, '"'+ msg.put +'", not a node (object)!'); } - gun.put(Gun.val.rel.ify(tmp.soul), cb, as); - }); + gun.put(Gun.val.link.ify(soul), cb, as); + }, true); return gun; } - as.ref = as.ref || (root._ === (tmp = at.back))? gun : tmp.gun; + if(at.has && (tmp = Gun.val.link.is(data))){ at.dub = tmp } + as.ref = as.ref || (root._ === (tmp = at.back))? gun : tmp.$; if(as.ref._.soul && Gun.val.is(as.data) && at.get){ as.data = obj_put({}, at.get, as.data); as.ref.put(as.data, as.soul, as); return gun; } - as.ref.get('_').get(any, {as: as}); + as.ref.get(any, true, {as: as}); if(!as.out){ // TODO: Perf idea! Make a global lock, that blocks everything while it is on, but if it is on the lock it does the expensive lookup to see if it is a dependent write or not and if not then it proceeds full speed. Meh? For write heavy async apps that would be terrible. as.res = as.res || stun; // Gun.on.stun(as.ref); // TODO: BUG! Deal with locking? - as.gun._.stun = as.ref._.stun; + as.$._.stun = as.ref._.stun; } return gun; }; @@ -92,30 +104,33 @@ function batch(){ var as = this; if(!as.graph || obj_map(as.stun, no)){ return } as.res = as.res || function(cb){ if(cb){ cb() } }; as.res(function(){ - var cat = (as.gun.back(-1)._), ask = cat.ask(function(ack){ - this.off(); // One response is good enough for us currently. Later we may want to adjust this. + var cat = (as.$.back(-1)._), ask = cat.ask(function(ack){ + cat.root.on('ack', ack); + if(ack.err){ Gun.log(ack) } + if(!ack.lack){ this.off() } // One response is good enough for us currently. Later we may want to adjust this. if(!as.ack){ return } as.ack(ack, this); + //--C; }, as.opt); + //C++; // NOW is a hack to get synchronous replies to correctly call. // and STOP is a hack to get async behavior to correctly call. // neither of these are ideal, need to be fixed without hacks, // but for now, this works for current tests. :/ - var tmp = cat.root.now; obj.del(cat.root, 'now'); cat.root.PUT = true; - var tmp2 = cat.root.stop; - (as.ref._).now = true; + var tmp = cat.root.now; obj.del(cat.root, 'now'); + var mum = cat.root.mum; cat.root.mum = {}; (as.ref._).on('out', { - gun: as.ref, put: as.out = as.env.graph, opt: as.opt, '#': ask + $: as.ref, put: as.out = as.env.graph, opt: as.opt, '#': ask }); - obj.del((as.ref._), 'now'); - obj.del((cat.root), 'PUT'); + cat.root.mum = mum? obj.to(mum, cat.root.mum) : mum; cat.root.now = tmp; - cat.root.stop = tmp2; }, as); if(as.res){ as.res() } } function no(v,k){ if(v){ return true } } +//console.debug(999,1); var C = 0; setInterval(function(){ try{ debug.innerHTML = C }catch(e){console.log(e)} }, 500); function map(v,k,n, at){ var as = this; + var is = Gun.is(v); if(k || !at.path.length){ return } (as.res||iife)(function(){ var path = at.path, ref = as.ref, opt = as.opt; @@ -123,65 +138,53 @@ function map(v,k,n, at){ var as = this; for(i; i < l; i++){ ref = ref.get(path[i]); } - if(Gun.node.soul(at.obj)){ - var id = Gun.node.soul(at.obj) || (ref.back('opt.uuid') || Gun.text.random)(); - if(!id){ // polyfill async uuid for SEA - (as.stun = as.stun || {})[path] = true; // make DRY - ref.back('opt.uuid')(function(err, id){ // TODO: improve perf without anonymous callback - if(err){ return Gun.log(err) } // TODO: Handle error. - ref.back(-1).get(id); - at.soul(id); - as.stun[path] = false; - as.batch(); - }); - return; - } + if(is){ ref = v } + var id = (ref._).dub; + if(id || (id = Gun.node.soul(at.obj))){ ref.back(-1).get(id); at.soul(id); return; } (as.stun = as.stun || {})[path] = true; - ref.get('_').get(soul, {as: {at: at, as: as}}); + ref.get(soul, true, {as: {at: at, as: as, p:path}}); }, {as: as, at: at}); + //if(is){ return {} } } -function soul(msg, ev){ var as = this.as, cat = as.at; as = as.as; - //ev.stun(); // TODO: BUG!? - if(!msg.gun || !msg.gun._.back){ return } // TODO: Handle - var at = msg.gun._, at_ = at; - var _id = (msg.put||empty)['#']; - ev.off(); - at = (msg.gun._.back); // go up 1! - var id = id || Gun.node.soul(cat.obj) || Gun.node.soul(at.put) || Gun.val.rel.is(at.put) || _id || at_._id || (as.gun.back('opt.uuid') || Gun.text.random)(); // TODO: BUG!? Do we really want the soul of the object given to us? Could that be dangerous? +function soul(id, as, msg, eve){ + var as = as.as, cat = as.at; as = as.as; + var at = ((msg || {}).$ || {})._ || {}; + id = at.dub = at.dub || id || Gun.node.soul(cat.obj) || Gun.node.soul(msg.put || at.put) || Gun.val.link.is(msg.put || at.put) || (as.via.back('opt.uuid') || Gun.text.random)(); // TODO: BUG!? Do we really want the soul of the object given to us? Could that be dangerous? + if(eve){ eve.stun = true } if(!id){ // polyfill async uuid for SEA - at.gun.back('opt.uuid')(function(err, id){ // TODO: improve perf without anonymous callback + at.via.back('opt.uuid')(function(err, id){ // TODO: improve perf without anonymous callback if(err){ return Gun.log(err) } // TODO: Handle error. - solve(at, at_._id = at_._id || id, cat, as); + solve(at, at.dub = at.dub || id, cat, as); }); return; } - solve(at, at_._id = at_._id || id, cat, as); + solve(at, at.dub = id, cat, as); } function solve(at, id, cat, as){ - at.gun.back(-1).get(id); + at.$.back(-1).get(id); cat.soul(id); as.stun[cat.path] = false; as.batch(); } -function any(at, ev){ - var as = this.as; - if(!at.gun || !at.gun._){ return } // TODO: Handle - if(at.err){ // TODO: Handle +function any(soul, as, msg, eve){ + as = as.as; + if(!msg.$ || !msg.$._){ return } // TODO: Handle + if(msg.err){ // TODO: Handle console.log("Please report this as an issue! Put.any.err"); return; } - var cat = (at.gun._.back), data = cat.put, opt = as.opt||{}, root, tmp; + var at = (msg.$._), data = at.put, opt = as.opt||{}, root, tmp; if((tmp = as.ref) && tmp._.now){ return } - ev.off(); - if(as.ref !== as.gun){ - tmp = (as.gun._).get || cat.get; + if(eve){ eve.stun = true } + if(as.ref !== as.$){ + tmp = (as.$._).get || at.get; if(!tmp){ // TODO: Handle console.log("Please report this as an issue! Put.no.get"); // TODO: BUG!?? return; @@ -190,30 +193,30 @@ function any(at, ev){ tmp = null; } if(u === data){ - if(!cat.get){ return } // TODO: Handle - if(!cat.soul){ - tmp = cat.gun.back(function(at){ - if(at.soul){ return at.soul } + if(!at.get){ return } // TODO: Handle + if(!soul){ + tmp = at.$.back(function(at){ + if(at.link || at.soul){ return at.link || at.soul } as.data = obj_put({}, at.get, as.data); }); } - tmp = tmp || cat.get; - cat = (cat.root.gun.get(tmp)._); - as.not = as.soul = tmp; + tmp = tmp || at.soul || at.link || at.dub;// || at.get; + at = tmp? (at.root.$.get(tmp)._) : at; + as.soul = tmp; data = as.data; } - if(!as.not && !(as.soul = Gun.node.soul(data))){ - if(as.path && obj_is(as.data)){ // Apparently necessary - as.soul = (opt.uuid || cat.root.opt.uuid || Gun.text.random)(); + if(!as.not && !(as.soul = as.soul || soul)){ + if(as.path && obj_is(as.data)){ + as.soul = (opt.uuid || as.via.back('opt.uuid') || Gun.text.random)(); } else { - //as.data = obj_put({}, as.gun._.get, as.data); + //as.data = obj_put({}, as.$._.get, as.data); if(node_ == at.get){ - as.soul = (at.put||empty)['#'] || at._id; + as.soul = (at.put||empty)['#'] || at.dub; } - as.soul = as.soul || at.soul || cat.soul || (opt.uuid || cat.root.opt.uuid || Gun.text.random)(); + as.soul = as.soul || at.soul || at.link || (opt.uuid || as.via.back('opt.uuid') || Gun.text.random)(); } if(!as.soul){ // polyfill async uuid for SEA - as.ref.back('opt.uuid')(function(err, soul){ // TODO: improve perf without anonymous callback + as.via.back('opt.uuid')(function(err, soul){ // TODO: improve perf without anonymous callback if(err){ return Gun.log(err) } // Handle error. as.ref.put(as.data, as.soul = soul, as); }); diff --git a/src/root.js b/src/root.js index 5e0201d9..fc9bc474 100644 --- a/src/root.js +++ b/src/root.js @@ -1,12 +1,12 @@ function Gun(o){ - if(o instanceof Gun){ return (this._ = {gun: this}).gun } + if(o instanceof Gun){ return (this._ = {gun: this, $: this}).$ } if(!(this instanceof Gun)){ return new Gun(o) } - return Gun.create(this._ = {gun: this, opt: o}); + return Gun.create(this._ = {gun: this, $: this, opt: o}); } -Gun.is = function(gun){ return (gun instanceof Gun) } +Gun.is = function($){ return ($ instanceof Gun) || ($ && $._ && ($ === $._.$)) || false } Gun.version = 0.9; @@ -31,46 +31,51 @@ Gun.dup = require('./dup'); at.on = at.on || Gun.on; at.ask = at.ask || Gun.ask; at.dup = at.dup || Gun.dup(); - var gun = at.gun.opt(at.opt); + var gun = at.$.opt(at.opt); if(!at.once){ at.on('in', root, at); - at.on('out', root, at); + at.on('out', root, {at: at, out: root}); + Gun.on('create', at); + at.on('create', at); } at.once = 1; return gun; } function root(msg){ - //console.log("add to.next(at)"); // TODO: MISSING FEATURE!!! - var ev = this, at = ev.as, gun = at.gun, dup, tmp; - //if(!msg.gun){ msg.gun = at.gun } + //add to.next(at); // TODO: MISSING FEATURE!!! + var ev = this, as = ev.as, at = as.at || as, gun = at.$, dup, tmp; if(!(tmp = msg['#'])){ tmp = msg['#'] = text_rand(9) } - if((dup = at.dup).check(tmp)){ return } + if((dup = at.dup).check(tmp)){ + if(as.out === msg.out){ + msg.out = u; + ev.to.next(msg); + } + return; + } dup.track(tmp); - //msg = obj_to(msg);//, {gun: at.gun}); // can we delete this now? if(!at.ask(msg['@'], msg)){ if(msg.get){ - Gun.on.get(msg, gun); - //at.on('get', get(msg)); + Gun.on.get(msg, gun); //at.on('get', get(msg)); } if(msg.put){ - Gun.on.put(msg, gun); - //at.on('put', put(msg)); + Gun.on.put(msg, gun); //at.on('put', put(msg)); } } - at.on('out', msg); + ev.to.next(msg); + if(!as.out){ + msg.out = root; + at.on('out', msg); + } } }()); ;(function(){ Gun.on.put = function(msg, gun){ - var at = gun._, ctx = {gun: gun, graph: at.graph, put: {}, map: {}, souls: {}, machine: Gun.state(), ack: msg['@']}; + var at = gun._, ctx = {$: gun, graph: at.graph, put: {}, map: {}, souls: {}, machine: Gun.state(), ack: msg['@'], cat: at, stop: {}}; if(!Gun.graph.is(msg.put, null, verify, ctx)){ ctx.err = "Error: Invalid graph!" } if(ctx.err){ return at.on('in', {'@': msg['#'], err: Gun.log(ctx.err) }) } obj_map(ctx.put, merge, ctx); - if(!ctx.async){ - at.stop = {}; // temporary fix till a better solution? - obj_map(ctx.map, map, ctx) - } + if(!ctx.async){ obj_map(ctx.map, map, ctx) } if(u !== ctx.defer){ setTimeout(function(){ Gun.on.put(msg, gun); @@ -95,31 +100,33 @@ Gun.dup = require('./dup'); ctx.souls[soul] = true; } function merge(node, soul){ - var ctx = this, cat = ctx.gun._, at = (cat.next || empty)[soul]; + var ctx = this, cat = ctx.$._, at = (cat.next || empty)[soul]; if(!at){ - ctx.souls[soul] = false; - return + if(!(cat.opt||empty).super){ + ctx.souls[soul] = false; + return; + } + at = (ctx.$.get(soul)._); } var msg = ctx.map[soul] = { put: node, get: soul, - gun: at.gun + $: at.$ }, as = {ctx: ctx, msg: msg}; ctx.async = !!cat.tag.node; if(ctx.ack){ msg['@'] = ctx.ack } obj_map(node, each, as); if(!ctx.async){ return } if(!ctx.and){ - // If it is async, we only need to setup on listener per context (ctx) + // If it is async, we only need to setup one listener per context (ctx) cat.on('node', function(m){ this.to.next(m); // make sure to call other context's listeners. if(m !== ctx.map[m.get]){ return } // filter out events not from this context! ctx.souls[m.get] = false; // set our many-async flag - obj_map(m.put, aeach, m); // merge into view + obj_map(m.put, patch, m); // merge into view if(obj_map(ctx.souls, function(v){ if(v){ return v } })){ return } // if flag still outstanding, keep waiting. if(ctx.c){ return } ctx.c = 1; // failsafe for only being called once per context. this.off(); - cat.stop = {}; // temporary fix till a better solution? obj_map(ctx.map, map, ctx); // all done, trigger chains. }); } @@ -127,27 +134,28 @@ Gun.dup = require('./dup'); cat.on('node', msg); // each node on the current context's graph needs to be emitted though. } function each(val, key){ - var ctx = this.ctx, graph = ctx.graph, msg = this.msg, soul = msg.get, node = msg.put, at = (msg.gun._), tmp; + var ctx = this.ctx, graph = ctx.graph, msg = this.msg, soul = msg.get, node = msg.put, at = (msg.$._), tmp; graph[soul] = Gun.state.to(node, key, graph[soul]); if(ctx.async){ return } at.put = Gun.state.to(node, key, at.put); } - function aeach(val, key){ - var msg = this, node = msg.put, at = (msg.gun._); + function patch(val, key){ + var msg = this, node = msg.put, at = (msg.$._); at.put = Gun.state.to(node, key, at.put); } function map(msg, soul){ - if(!msg.gun){ return } - //console.log('map ->', soul, msg.put); - (msg.gun._).on('in', msg); + if(!msg.$){ return } + this.cat.stop = this.stop; // temporary fix till a better solution? + (msg.$._).on('in', msg); + this.cat.stop = null; // temporary fix till a better solution? } Gun.on.get = function(msg, gun){ - var root = gun._, 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(!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) } + if('string' != typeof has || !obj_has(node, has)){ return root.on('get', msg) } node = Gun.state.to(node, has); // If we have a key in-memory, do we really need to fetch? // Maybe... in case the in-memory key we have is a local write @@ -156,16 +164,14 @@ Gun.dup = require('./dup'); node = Gun.obj.copy(node); } node = Gun.graph.node(node); - //tmp = at.ack; + tmp = (at||empty).ack; root.on('in', { '@': msg['#'], how: 'mem', put: node, - gun: gun + $: gun }); - //if(0 < tmp){ - // return; - //} + //if(0 < tmp){ return } root.on('get', msg); } }()); @@ -195,7 +201,7 @@ Gun.dup = require('./dup'); 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.rel.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) }; @@ -207,7 +213,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 = window.Gun = Gun).window = window } try{ if(typeof common !== "undefined"){ common.exports = Gun } }catch(e){} module.exports = Gun; @@ -216,10 +222,8 @@ module.exports = Gun; if(ctx.once){ return } ctx.on('node', function(msg){ var to = this.to; - //console.log(">>>", msg.put); //Gun.node.is(msg.put, function(v,k){ msg.put[k] = v + v }); setTimeout(function(){ - //console.log("<<<<<", msg.put); to.next(msg); },1); }); diff --git a/src/set.js b/src/set.js index 995202bc..e489b2b4 100644 --- a/src/set.js +++ b/src/set.js @@ -4,24 +4,17 @@ Gun.chain.set = function(item, cb, opt){ var gun = this, soul; cb = cb || function(){}; opt = opt || {}; opt.item = opt.item || item; - if(soul = Gun.node.soul(item)){ return gun.set(gun.back(-1).get(soul), cb, opt) } + if(soul = Gun.node.soul(item)){ item = Gun.obj.put({}, soul, Gun.val.link.ify(soul)) } if(!Gun.is(item)){ - var id = gun._.root.opt.uuid(); - if(id && Gun.obj.is(item)){ - return gun.set(gun._.root.gun.put(item, id), cb, opt); + if(Gun.obj.is(item)){; + item = gun.back(-1).get(soul = soul || Gun.node.soul(item) || gun.back('opt.uuid')()).put(item); } - return gun.get(id || (Gun.state.lex() + Gun.text.random(12))).put(item, cb, opt); + return gun.get(soul || (Gun.state.lex() + Gun.text.random(7))).put(item, cb, opt); } - item.get('_').get(function(at, ev){ - if(!at.gun || !at.gun._.back){ return } - ev.off(); - var soul = (at.put||{})['#']; - at = (at.gun._.back); - var put = {}, node = at.put; - soul = at.soul || Gun.node.soul(node) || soul; - if(!soul){ return cb.call(gun, {err: Gun.log('Only a node can be linked! Not "' + node + '"!')}) } - gun.put(Gun.obj.put(put, soul, Gun.val.rel.ify(soul)), cb, opt); - },{wait:0}); + item.get(function(soul, o, msg){ + if(!soul){ return cb.call(gun, {err: Gun.log('Only a node can be linked! Not "' + msg.put + '"!')}) } + gun.put(Gun.obj.put({}, soul, Gun.val.link.ify(soul)), cb, opt); + },true); return item; } \ No newline at end of file diff --git a/src/state.js b/src/state.js index c77a37b4..1cddd3f1 100644 --- a/src/state.js +++ b/src/state.js @@ -3,11 +3,11 @@ var Type = require('./type'); var Node = require('./node'); function State(){ var t; - if(perf){ - t = start + perf.now(); - } else { + /*if(perf){ + t = start + perf.now(); // Danger: Accuracy decays significantly over time, even if precise. + } else {*/ t = time(); - } + //} if(last < t){ return N = 0, last = t + State.drift; } @@ -26,10 +26,10 @@ State.lex = function(){ return State().toString(36).replace('.','') } State.ify = function(n, k, s, v, soul){ // put a key's state on a node. if(!n || !n[N_]){ // reject if it is not node-like. if(!soul){ // unless they passed a soul - return; + return; } n = Node.soul.ify(n, soul); // then make it so! - } + } var tmp = obj_as(n[N_], State._); // grab the states data. if(u !== k && k !== N_){ if(num_is(s)){ @@ -42,7 +42,7 @@ State.ify = function(n, k, s, v, soul){ // put a key's state on a node. return n; } State.to = function(from, k, to){ - var val = from[k]; + var val = (from||{})[k]; if(obj_is(val)){ val = obj_copy(val); } diff --git a/src/type.js b/src/type.js index 0ef9cf0c..bab8fd65 100644 --- a/src/type.js +++ b/src/type.js @@ -2,7 +2,7 @@ // Generic javascript utilities. var Type = {}; //Type.fns = Type.fn = {is: function(fn){ return (!!fn && fn instanceof Function) }} -Type.fns = Type.fn = {is: function(fn){ return (!!fn && 'function' == typeof fn) }} +Type.fn = {is: function(fn){ return (!!fn && 'function' == typeof fn) }} Type.bi = {is: function(b){ return (b instanceof Boolean || typeof b == 'boolean') }} Type.num = {is: function(n){ return !list_is(n) && ((n - parseFloat(n) + 1) >= 0 || Infinity === n || -Infinity === n) }} Type.text = {is: function(t){ return (typeof t == 'string') }} diff --git a/src/val.js b/src/val.js index f9dd6456..50b4c43e 100644 --- a/src/val.js +++ b/src/val.js @@ -7,14 +7,14 @@ Val.is = function(v){ // Valid values are a subset of JSON: null, binary, number if(v === Infinity){ return false } // we want this to be, but JSON does not support it, sad face. if(text_is(v) // by "text" we mean strings. || bi_is(v) // by "binary" we mean boolean. - || num_is(v)){ // by "number" we mean integers or decimals. + || num_is(v)){ // by "number" we mean integers or decimals. return true; // simple values are valid. } - return Val.rel.is(v) || false; // is the value a soul relation? Then it is valid and return it. If not, everything else remaining is an invalid data type. Custom extensions can be built on top of these primitives to support other types. + 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.rel = {_: '#'}; +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); @@ -33,9 +33,9 @@ Val.rel = {_: '#'}; } } }()); -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.rel._, u; +var rel_ = Val.link._, u; var bi_is = Type.bi.is; var num_is = Type.num.is; var text_is = Type.text.is; diff --git a/test/axe/holy-grail.js b/test/axe/holy-grail.js new file mode 100644 index 00000000..420b5bb1 --- /dev/null +++ b/test/axe/holy-grail.js @@ -0,0 +1,425 @@ +/** + * AXE test 1 + * What we want here: (1) Superpeer and (n) peers + * - The peers receives only the requested data. + * - If the Superpeer crash, after restart, must recreate all subscriptions and update the peers. + * - If some peer crash or go offline, must receive the changes via RTC. + * + * Tip: to run this `npm run testaxe` + * Tip 2: if you clone the gun repo, you need to create a link do gun package. Do `npm install && cd node_modules && ln -s ../ gun` + * Tip 3: If you not in localhost, run the browsers in anonymous mode because of domain security policies. https://superuser.com/questions/565409/how-to-stop-an-automatic-redirect-from-http-to-https-in-chrome + */ +var config = { + IP: require('ip').address(), + port: 8765, + servers: 2, + browsers: 3, + route: { + '/': __dirname + '/index.html', + '/gun.js': __dirname + '/../../gun.js', + '/gun/axe.js': __dirname + '/../../axe.js', + '/gun/lib/radix.js': __dirname + '/../../lib/radix.js', + '/gun/lib/webrtc.js': __dirname + '/../../lib/webrtc.js', + '/jquery.js': __dirname + '/../../examples/jquery.js' + } +} +var panic = require('panic-server'); +panic.server().on('request', function(req, res){ + config.route[req.url] && require('fs').createReadStream(config.route[req.url]).pipe(res); +}).listen(config.port); + +var clients = panic.clients; +var manager = require('panic-manager')(); +manager.start({ + clients: Array(config.servers).fill().map(function(u, i){ + return { + type: 'node', + port: config.port + (i + 1) + } + }), + panic: 'http://' + config.IP + ':' + config.port +}); + +var servers = clients.filter('Node.js'); +var server = servers.pluck(1); +var server2 = servers.excluding(server).pluck(1); +var browsers = clients.excluding(servers); +var alice = browsers.pluck(1); +var bob = browsers.excluding(alice).pluck(1); +var john = browsers.excluding(alice).excluding(bob).pluck(1); +var again = {}; + +describe("The Holy Grail AXE Test!", function(){ + this.timeout(5 * 60 * 1000); +// this.timeout(10 * 60 * 1000); + + it("Servers have joined!", function(){ + return servers.atLeast(config.servers); + }); + + it("GUN started!", function(){ + return server.run(function(test){ + var env = test.props; + test.async(); + try{ require('fs').unlinkSync(env.i+'dataaxe') }catch(e){} + try{ require('fs').unlinkSync((env.i+1)+'dataaxe') }catch(e){} + var port = env.config.port + env.i; + var server = require('http').createServer(function(req, res){ + res.end("I am "+ env.i +"!"); + }); + var Gun = require('gun'); + require('gun/axe'); + var gun = Gun({ + file: env.i+'dataaxe', + web: server + }); + server.listen(port, function(){ + test.done(); + }); + }, {i: 1, config: config}); + }); + + it(config.browsers +" browser(s) have joined!", function(){ + console.log("PLEASE OPEN http://"+ config.IP +":"+ config.port +" IN "+ config.browsers +" BROWSER(S)!"); + return browsers.atLeast(config.browsers); + }); + + it("Browsers initialized gun!", function(){ + var tests = [], i = 0; + browsers.each(function(client, id){ + tests.push(client.run(function(test){ + localStorage.clear(); console.log('Clear localStorage!!!'); + var env = test.props; + var opt = {peers:['http://'+ env.config.IP + ':' + (env.config.port + 1) + '/gun'], wait: 1000}; + var pid = location.hash.slice(1); + if (pid) { opt.pid = pid; } + Gun.on('opt', function(ctx) { + this.to.next(ctx); + ctx.on('hi', function(opt) { + document.getElementById('pid').innerHTML = (document.getElementById('pid').innerHTML || "-") + ', ' + this.on.opt.pid; + }); + }); + var gun = window.gun = Gun(opt); + window.ref = gun.get('holy').get('grail'); + }, {i: i += 1, config: config})); + }); + return Promise.all(tests); + }); + + it("Wait for Alice, Bob and John...", function(done){ + setTimeout(done, 1000); + }); + + it("Alice Write: Hi Bob!", function(){ + return alice.run(function(test){ + console.log("I AM ALICE"); + $('#name').text('Alice'); + test.async(); + ref.once(function() { // TODO: Need `.once` first for subscription. If Alice do a `.put` before a `.once`, Alice will get old data from localStorage if Bob update + ref.put('Hi Bob!', function(ack) { + console.log(ack); + setTimeout(test.done, 2000); + }); + }); + }); + }); + + it("Bob receive ONCE from Alice: Hi Bob!", function(){ + return bob.run(function(test){ + console.log("I AM BOB"); + $('#name').text('Bob'); + test.async(); + ref.once(function(data){ + if('Hi Bob!' === data){ + console.log('[OK] Bob receive the question: ', data); + return test.done(); + } else { + var err = '[FAIL] Bob MUST receive: Hi Bob! but receive: ' + data + ' Storage: ' + localStorage.getItem('gun/'); + console.log(err); + return test.fail(err); + } + }) + }) + }); + + it("Bob Write response: Hi Alice!", function(){ + return bob.run(function(test){ + test.async(); + ref.put('Hi Alice!', function(ack) { + console.log('[OK] Bob Write response: Hi Alice!', ack); + setTimeout(test.done, 2000); + }); + }); + }); + + it("Alice Read response from Bob: Hi Alice!", function(){ + return alice.run(function(test){ + test.async(); + ref.once(function(data){ + if('Hi Alice!' === data){ + console.log('[OK] Alice receive the response: ', data); + return test.done(); + } else { + //TODO: aqui em duvida.. está pegando do localStorage, mas Bob alterou o dado. + var err = '[FAIL] Alice receive wrong response: "' + data + '" and must be "Hi Alice!"'; + console.log(err); + return test.fail(err); + } + }) + }) + }); + + it("Jhon Read what Bob say to Alice: Hi Alice!", function(){ + return john.run(function(test){ + test.async(); + console.log("I AM JOHN"); + $('#name').text('John'); + ref.once(function(data){ + if('Hi Alice!' === data){ + console.log('[OK] John receive the data: ', data); + return test.done(); + } else { + //TODO: aqui em duvida.. está pegando do localStorage, mas Bob alterou o dado. + var err = '[FAIL] John receive wrong data: "' + data + '" and must be "Hi Alice!"'; + console.log(err); + return test.fail(err); + } + }) + }) + }); + + it("Bob Write in some data, Alice not subscribed", function(){ + return bob.run(function(test){ + test.async(); + gun.get('bob').get('mine').put('Alice dont want this data now!', function() { + setTimeout(test.done, 2000); + }); + }); + }); + + it("Alice not subscribed. Must NOT receive data from Bob", function(){ + return alice.run(function(test){ + test.async(); + /// This must be empty, because alice don't make a subscription to this node. + var bobdata = JSON.parse(localStorage.getItem('gun/')).bob; + if (bobdata) { + var err = '[FAIL] Alice receive not subscribed data in localStorage: ' + JSON.stringify(bobdata); + console.log(err); + return test.fail(err); + } + if (gun._.graph.bob) { + var err = '[FAIL] Alice receive not subscribed data in in graph: ' + JSON.stringify(gun._.graph.bob); + console.log(err); + return test.fail(err); + } + console.log('[OK] Alice Read must NOT receive data from Bob: ', bobdata); + return test.done(); + }) + }); + + it("Alice subscription Bob data with ONCE, MUST receive", function(){ + return alice.run(function(test){ + test.async(); + gun.get('bob').once(function(data){ + if(data){ + console.log('[OK] Alice receive the value: ', data); + return test.done(); + } else { + var err = '[FAIL] Alice receive the value: ' + data; + console.log(err); + return test.fail(err); + } + }) + }) + }); + + it("Bob Write in some data. Now Alice is subscribed.", function(){ + return bob.run(function(test){ + test.async(); + gun.get('bob').get('mine').put('Alice WANT this data now!', function() { + setTimeout(test.done, 5000); + }); + }); + }); + + it("Alice must receive 'Alice WANT this data now!' from Bob node", function(){ + return alice.run(function(test){ + test.async(); + if (gun._.graph.bob && gun._.graph.bob.mine === 'Alice WANT this data now!') { + console.log('[OK] GRAPH: ', gun._.graph.bob); + test.done(); + } else { + var err = '[FAIL] GRAPH: ' + JSON.stringify(gun._.graph.bob); + console.log(err); + test.fail(err); + } + }) + }); + + it("Server has crashed!", function(){ + return server.run(function(test){ +// var env = test.props; +// try{ require('fs').unlinkSync(env.i+'data'); }catch(e){} + process.exit(0); + }, {i: 1, config: config}) + }); + + it("Wait...", function(done){ + setTimeout(done, 2000); + }); + + it("Alice change the data (superpeer crashed yet).", function(){ + return alice.run(function(test){ + var env = test.props; + if(window.WebSocket){ + var err; + try{ new WebSocket('http://'+ env.config.IP + ':' + (env.config.port + 2) + '/gun') }catch(e){ err = e } + if(!err){ + test.fail("Server did not crash."); + } + } + test.async() + ref.put("Superpeer? Where are you?", function() { + setTimeout(test.done, 1000); + }); + }, {config: config}); + }); + + it("Bob receive what Alice change via WebRTC.", function(){ + return bob.run(function(test){ + test.async(); + ref.once(function(data){ + if('Superpeer? Where are you?' === data){ + console.log('[OK] Bob received data via WebRTC: ', data); + return test.done(); + } else { + var err = '[FAIL] Bob MUST not receive: "Superpeer? Where are you?", but receive: ' + data; + console.log(err); + return test.fail(err); + } + }) + }) + }); + + it("Bob change the data again (superpeer crashed yet).", function(){ + return alice.run(function(test){ + var env = test.props; + if(window.WebSocket){ + var err; + try{ new WebSocket('http://'+ env.config.IP + ':' + (env.config.port + 2) + '/gun') }catch(e){ err = e } + if(!err){ + test.fail("Server did not crash."); + } + } + test.async() + ref.put("Alice, can you hear me?", function() { + setTimeout(test.done, 1000); + }); + }, {config: config}); + }); + + it("Alice MUST receive 'Alice, can you hear me?' via WebRTC.", function(){ + return bob.run(function(test){ + test.async(); + ref.once(function(data){ + if('Alice, can you hear me?' === data){ + console.log('[OK] Alice received data via WebRTC: ', data); + return test.done(); + } else { + var err = '[FAIL] Alice MUST not receive: "Superpeer? Where are you?", but receive: ' + data; + console.log(err); + return test.fail(err); + } + }) + }) + }); + + it("Superpeer come started again!", function(){ + return server2.run(function(test){ + var env = test.props; + test.async(); +// try{ require('fs').unlinkSync(env.i+'dataaxe') }catch(e){} +// try{ require('fs').unlinkSync((env.i+1)+'dataaxe') }catch(e){} + var port = env.config.port + env.i; + var server = require('http').createServer(function(req, res){ + res.end("I am "+ env.i +"!"); + }); + var Gun = require('gun'); + require('gun/axe'); + var gun = Gun({ + file: env.i+'dataaxe', + web: server + }); + server.listen(port, function(){ + test.done(); + }); + }, {i: 1, config: config}); + }); + + it("Wait sync...", function(done){ + setTimeout(done, 5000); + }); + + it("Alice change the data again (superpeer is UP!).", function(){ + return alice.run(function(test){ + var env = test.props; + test.async() + ref.put("Yes Bob! Thanks for asking!", function() { + setTimeout(test.done, 1000); + }); + }, {config: config}); + }); + + it("Bob MUST receive 'Yes Bob! Thanks for asking!'", function(){ + return bob.run(function(test){ + test.async(); + ref.once(function(data){ + if('Yes Bob! Thanks for asking!' === data){ + console.log('[OK] Bob received the data change: ', data); + return test.done(); + } else { + var err = '[FAIL] Bob MUST not receive: "Yes Bob! Thanks for asking!", but receive: ' + data; + console.log(err); + return test.fail(err); + } + }) + }) + }); + + it("John dont want to know what Bob say in his node!", function(){ + return john.run(function(test){ + test.async(); + /// This must be empty, because John don't make a subscription to this node. + var bobdata = JSON.parse(localStorage.getItem('gun/')).bob; + if (bobdata) { + var err = '[FAIL] John receive not subscribed data: ' + JSON.stringify(bobdata); + console.log(err); + return test.fail(err); + } + if (gun._.graph.bob) { + var err = '[FAIL] John receive not subscribed data in in graph: ' + JSON.stringify(gun._.graph.bob); + console.log(err); + return test.fail(err); + } + console.log('[OK] John Read must NOT receive data from Bob: ', bobdata, gun._.graph.bob); + return test.done(); + }) + }); + + it("All finished!", function(done){ + console.log("Done! Cleaning things up..."); + setTimeout(function(){ + done(); + },1000); + }); + after("Everything shut down.", function(){ + browsers.run(function(){ + //location.reload(); + //setTimeout(function(){ + //}, 15 * 1000); + }); + return servers.run(function(){ + process.exit(); + }); + }); +}); diff --git a/test/axe/index.html b/test/axe/index.html new file mode 100644 index 00000000..9fb3201d --- /dev/null +++ b/test/axe/index.html @@ -0,0 +1,13 @@ + + + + + + + + +

    Running AXE Tests

    +
    Name:
    +
    PID:
    +
    + diff --git a/test/common.js b/test/common.js index 971bd745..2725fd10 100644 --- a/test/common.js +++ b/test/common.js @@ -1,170 +1,38 @@ -var root; -(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){ - root.Gun = root.Gun; - } else { - root.Gun = require('../gun'); - Gun.serve = require('../lib/serve'); - //require('./s3'); - //require('./uws'); - //require('./wsp/server'); - require('../lib/file'); - } -}(this)); -//Gun.log.squelch = true; -var gleak = {globals: {}, check: function(){ // via tobyho - var leaked = [] - for (var key in gleak.globe){ if (!(key in gleak.globals)){ leaked.push(key)} } - if (leaked.length > 0){ console.log("GLOBAL LEAK!", leaked); return leaked } -}}; -(function(env){ - for (var key in (gleak.globe = env)){ gleak.globals[key] = true } -}(this)); - -describe('Performance', function(){ return; // performance tests - var console = root.console || {log: function(){}}; - function perf(fn, i){ - i = i || 1000; - while(--i){ - fn(i); - } - } - perf.now = this.performance? function(){ return performance.now() } : function(){ return Gun.time.now()/1000 }; - (function(){ - var t1 = perf.now(); - var obj = {1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e', 6: 'f', 7: 'g', 8: 'h', 9: 'i'}; - Object.keys && perf(function(){ - var l = Object.keys(obj), ll = l.length, i = 0, s = ''; - for(; i < ll; i++){ - var v = l[i]; - s += v; - } - }); - console.log('map: native', (t1 = (perf.now() - t1)/1000) + 's'); - - var t2 = perf.now(); - var obj = {1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e', 6: 'f', 7: 'g', 8: 'h', 9: 'i'}; - perf(function(){ - var s = ''; - Gun.obj.map(obj, function(v){ - s += v; - }) - }); - console.log('map: gun', (t2 = (perf.now() - t2)/1000) + 's', (t2 / t1).toFixed(1)+'x', 'slower.'); - }()); - (function(){ - if(!Gun.store){ - var tab = Gun().tab; - if(!tab){ return } - Gun.store = tab.store; - } - root.localStorage && root.localStorage.clear(); - var it = 1000; - var t1 = perf.now(); - perf(function(i){ - var obj = {'i': i, 'v': Gun.text.random(100)}; - Gun.store.put('test/native/' + i, obj); - }, it); - console.log('store: native', (t1 = (perf.now() - t1)/1000) + 's'); - - root.localStorage && root.localStorage.clear(); - var gun = Gun({wire: {get:function(l,cb){cb()},put:function(g,cb){ - Gun.is.graph(g, function(node, soul){ - Gun.store.put(soul, node); - }); - cb(null); - }}}); - var t2 = perf.now(); - perf(function(i){ - var obj = {'i': i, 'v': Gun.text.random(100)}; - gun.put(obj); - }, it); - console.log('store: gun', (t2 = (perf.now() - t2)/1000) + 's', (t2 / t1).toFixed(1)+'x', 'slower.'); - root.localStorage && root.localStorage.clear(); - }()); - (function(){ // setTimeout - if(!Gun.store){ - var tab = Gun().tab; - if(!tab){ return } - Gun.store = tab.store; - } - root.localStorage && root.localStorage.clear(); - var t1 = perf.now(); - i = i || 1000; - while(--i){ - var obj = {'i': i, 'v': Gun.text.random(100)}; - Gun.store.put('test/native/' + i, obj); - } - console.log('store: native', (t1 = (perf.now() - t1)/1000) + 's'); - - root.localStorage && root.localStorage.clear(); - var gun = Gun({wire: {get:function(l,cb){cb()},put:function(g,cb){ - Gun.is.graph(g, function(node, soul){ - Gun.store.put(soul, node); - }); - cb(null); - }}}); - var t2 = perf.now(); - perf(function(i){ - var obj = {'i': i, 'v': Gun.text.random(100)}; - gun.put(obj); - }, it); - console.log('store: gun', (t2 = (perf.now() - t2)/1000) + 's', (t2 / t1).toFixed(1)+'x', 'slower.'); - root.localStorage && root.localStorage.clear(); - }()); - (function(){ - var t1 = perf.now(); - var on = Gun.on.create(), c = 0, o = []; - perf(function(i){ - o.push(function(n){ - c += 1; - }); - var ii = 0, l = o.length; - for(; ii < l; ii++){ - o[ii](i); - } - }); - console.log('on: native', (t1 = (perf.now() - t1)/1000) + 's'); - - var on = Gun.on.create(), c = 0; - var t2 = perf.now(); - perf(function(i){ - on('change').event(function(n){ - c += 1; - }); - on('change').emit(i); - }); - console.log('on: gun', (t2 = (perf.now() - t2)/1000) + 's', (t2 / t1).toFixed(1)+'x', 'slower.'); - }());return; - (function(){ // always do this last! - var t1 = perf.now(); - perf(function(i){ - setTimeout(function(){ - if(i === 1){ - cb1(); - } - },0); - }); var cb1 = function(){ - console.log('setTimeout: native', (t1 = (perf.now() - t1)/1000) + 's', (t1 / t2).toFixed(1)+'x', 'slower.'); - } - var t2 = perf.now(); - perf(function(i){ - setImmediate(function(){ - if(i === 1){ - cb2(); - } - }); - }); var cb2 = function(){ - console.log('setImmediate: gun', (t2 = (perf.now() - t2)/1000) + 's', (t2 / t1).toFixed(1)+'x', 'slower.'); - } - }()); -}); - describe('Gun', function(){ + var root; + (function(){ + var env; + if(typeof global !== 'undefined'){ env = global } + if(typeof window !== 'undefined'){ env = window } + root = env.window? env.window : global; + try{ env.window && root.localStorage && root.localStorage.clear() }catch(e){} + try{ require('fs').unlinkSync('data.json') }catch(e){} + try{ require('../lib/fsrm')('radatatest') }catch(e){} + //root.Gun = root.Gun || require('../gun'); + if(root.Gun){ + root.Gun = root.Gun; + root.Gun.TESTING = true; + } else { + root.Gun = require('../gun'); + root.Gun.TESTING = true; + Gun.serve = require('../lib/serve'); + //require('../lib/file'); + require('../lib/store'); + require('../lib/rfs'); + require('./rad/rad.js'); + require('./sea/sea.js'); + } + }(this)); + //Gun.log.squelch = true; + var gleak = {globals: {}, check: function(){ // via tobyho + var leaked = [] + for (var key in gleak.globe){ if (!(key in gleak.globals)){ leaked.push(key)} } + if (leaked.length > 0){ console.log("GLOBAL LEAK!", leaked); return leaked } + }}; + (function(env){ + for (var key in (gleak.globe = env)){ gleak.globals[key] = true } + }(this)); + var t = {}; describe('Utility', function(){ @@ -278,20 +146,20 @@ describe('Gun', function(){ this.document && expect(Gun.obj.is(document.createElement('div'))).to.be(false); expect(Gun.obj.is(new (function Class(){ this.x = 1; this.y = 2 })())).to.be(true); }); - it('fns',function(){ - expect(Gun.fns.is(function(){})).to.be(true); - expect(Gun.fns.is(u)).to.be(false); - expect(Gun.fns.is(null)).to.be(false); - expect(Gun.fns.is('')).to.be(false); - expect(Gun.fns.is('a')).to.be(false); - expect(Gun.fns.is(0)).to.be(false); - expect(Gun.fns.is(1)).to.be(false); - expect(Gun.fns.is([])).to.be(false); - expect(Gun.fns.is([1])).to.be(false); - expect(Gun.fns.is({})).to.be(false); - expect(Gun.fns.is({a:1})).to.be(false); - expect(Gun.fns.is(false)).to.be(false); - expect(Gun.fns.is(true)).to.be(false); + it('fn',function(){ + expect(Gun.fn.is(function(){})).to.be(true); + expect(Gun.fn.is(u)).to.be(false); + expect(Gun.fn.is(null)).to.be(false); + expect(Gun.fn.is('')).to.be(false); + expect(Gun.fn.is('a')).to.be(false); + expect(Gun.fn.is(0)).to.be(false); + expect(Gun.fn.is(1)).to.be(false); + expect(Gun.fn.is([])).to.be(false); + expect(Gun.fn.is([1])).to.be(false); + expect(Gun.fn.is({})).to.be(false); + expect(Gun.fn.is({a:1})).to.be(false); + expect(Gun.fn.is(false)).to.be(false); + expect(Gun.fn.is(true)).to.be(false); }); it('time',function(){ t.ts = Gun.time.is(); @@ -402,7 +270,7 @@ describe('Gun', function(){ setTimeout(this.add(function(){ this.done(null, num * num); }, key), parseInt((""+Math.random()).substring(2,5))); - }, Gun.fns.sum(function(err, val){ + }, Gun.fn.sum(function(err, val){ expect(val.a).to.eql(4); expect(val.b).to.eql(4); expect(val.c).to.eql(9); @@ -746,6 +614,7 @@ describe('Gun', function(){ }); describe('Gun Safety', function(){ /* WARNING NOTE: Internal API has significant breaking changes! */ + var gun = Gun(); it('is',function(){ expect(Gun.is(gun)).to.be(true); @@ -780,24 +649,24 @@ describe('Gun', function(){ expect(Gun.val.is({a:1})).to.be(false); expect(Gun.val.is(function(){})).to.be(false); }); - it('is rel',function(){ - expect(Gun.val.rel.is({'#':'somesoulidhere'})).to.be('somesoulidhere'); - expect(Gun.val.rel.is({'#':'somethingelsehere'})).to.be('somethingelsehere'); - expect(Gun.val.rel.is({'#':'somesoulidhere', and: 'nope'})).to.be(false); - expect(Gun.val.rel.is({or: 'nope', '#':'somesoulidhere'})).to.be(false); - expect(Gun.val.rel.is(false)).to.be(false); - expect(Gun.val.rel.is(true)).to.be(false); - expect(Gun.val.rel.is('')).to.be(false); - expect(Gun.val.rel.is('a')).to.be(false); - expect(Gun.val.rel.is(0)).to.be(false); - expect(Gun.val.rel.is(1)).to.be(false); - expect(Gun.val.rel.is(Infinity)).to.be(false); // boohoo :( - expect(Gun.val.rel.is(NaN)).to.be(false); - expect(Gun.val.rel.is([])).to.be(false); - expect(Gun.val.rel.is([1])).to.be(false); - expect(Gun.val.rel.is({})).to.be(false); - expect(Gun.val.rel.is({a:1})).to.be(false); - expect(Gun.val.rel.is(function(){})).to.be(false); + it('is link',function(){ + expect(Gun.val.link.is({'#':'somesoulidhere'})).to.be('somesoulidhere'); + expect(Gun.val.link.is({'#':'somethingelsehere'})).to.be('somethingelsehere'); + expect(Gun.val.link.is({'#':'somesoulidhere', and: 'nope'})).to.be(false); + expect(Gun.val.link.is({or: 'nope', '#':'somesoulidhere'})).to.be(false); + expect(Gun.val.link.is(false)).to.be(false); + expect(Gun.val.link.is(true)).to.be(false); + expect(Gun.val.link.is('')).to.be(false); + expect(Gun.val.link.is('a')).to.be(false); + expect(Gun.val.link.is(0)).to.be(false); + expect(Gun.val.link.is(1)).to.be(false); + expect(Gun.val.link.is(Infinity)).to.be(false); // boohoo :( + expect(Gun.val.link.is(NaN)).to.be(false); + expect(Gun.val.link.is([])).to.be(false); + expect(Gun.val.link.is([1])).to.be(false); + expect(Gun.val.link.is({})).to.be(false); + expect(Gun.val.link.is({a:1})).to.be(false); + expect(Gun.val.link.is(function(){})).to.be(false); }); it.skip('is lex',function(){ expect(Gun.is.lex({'#': 'soul'})).to.eql({soul: 'soul'}); @@ -1021,11 +890,11 @@ describe('Gun', function(){ var data = {users: {1: {where: {lat: Math.random(), lng: Math.random(), i: 1}}}}; Gun.ify(data, function(err, ctx){ var soul, node; - expect(soul = Gun.val.rel.is(ctx.root.users)).to.be.ok(); + expect(soul = Gun.val.link.is(ctx.root.users)).to.be.ok(); node = ctx.graph[soul]; - expect(soul = Gun.val.rel.is(node[1])).to.be.ok(); + expect(soul = Gun.val.link.is(node[1])).to.be.ok(); node = ctx.graph[soul]; - expect(soul = Gun.val.rel.is(node.where)).to.be.ok(); + expect(soul = Gun.val.link.is(node.where)).to.be.ok(); node = ctx.graph[soul]; expect(node.lat).to.be.ok(); expect(node.lng).to.be.ok(); @@ -1397,6 +1266,16 @@ describe('Gun', function(){ describe('API', function(){ var gopt = {wire:{put:function(n,cb){cb()},get:function(k,cb){cb()}}}; + if(Gun.window && location.search){ + /*console.log("LOCALHOST PEER MUST BE ON!"); + var peer = {url: 'http://localhost:8765/gun'}; + Gun.on('opt', function(root){ + if(root.opt.test_no_peer){ return this.to.next(root) } + root.opt.peers = root.opt.peers || {}; + root.opt.peers['http://localhost:8765/gun'] = peer; + this.to.next(root); + });*/ + } var gun = Gun(); it.skip('gun chain separation', function(done){ // TODO: UNDO! @@ -1505,7 +1384,7 @@ describe('Gun', function(){ }); describe('plural chains', function(){ - this.timeout(5000); + this.timeout(9000); it('uncached synchronous map on', function(done){ /* Biggest challenges so far: @@ -1516,7 +1395,7 @@ describe('Gun', function(){ - Proxying event across maps. */ var s = Gun.state.map();s.soul = 'u/m'; - gun.on('put', {gun: gun, put: Gun.graph.ify({ + gun.on('put', {$: gun, put: Gun.graph.ify({ alice: { age: 26, name: "Alice", @@ -1538,11 +1417,11 @@ describe('Gun', function(){ done.to = setTimeout(function(){ expect(check.alice.age).to.be(26); expect(check.alice.name).to.be('Alice'); - expect(Gun.val.rel.is(check.alice.pet)).to.be.ok(); + expect(Gun.val.link.is(check.alice.pet)).to.be.ok(); //expect(count.alice).to.be(1); expect(check.bob.age).to.be(29); expect(check.bob.name).to.be('Bob!'); - expect(Gun.val.rel.is(check.bob.pet)).to.be.ok(); + expect(Gun.val.link.is(check.bob.pet)).to.be.ok(); //expect(count.bob).to.be(1); done(); },10); @@ -1552,7 +1431,7 @@ describe('Gun', function(){ it('uncached synchronous map get on', function(done){ var s = Gun.state.map();s.soul = 'u/m/p'; - gun.on('put', {gun: gun, put: Gun.graph.ify({ + gun.on('put', {$: gun, put: Gun.graph.ify({ alice: { age: 26, name: "alice", @@ -1584,7 +1463,7 @@ describe('Gun', function(){ it('uncached synchronous map get on node', function(done){ var s = Gun.state.map();s.soul = 'u/m/p/n'; - gun.on('put', {gun: gun, put: Gun.graph.ify({ + gun.on('put', {$: gun, put: Gun.graph.ify({ alice: { age: 26, name: "alice", @@ -1619,7 +1498,7 @@ describe('Gun', function(){ it('uncached synchronous map get on node get', function(done){ var gun = Gun(); var s = Gun.state.map();s.soul = 'u/m/p/n/p'; - gun.on('put', {gun: gun, put: Gun.graph.ify({ + gun.on('put', {$: gun, put: Gun.graph.ify({ alice: { age: 26, name: "alice", @@ -1658,7 +1537,7 @@ describe('Gun', function(){ it('uncached synchronous map on mutate', function(done){ var s = Gun.state.map();s.soul = 'u/m/mutate'; - gun.on('put', {gun: gun, put: Gun.graph.ify({ + gun.on('put', {$: gun, put: Gun.graph.ify({ alice: { age: 26, name: "Alice", @@ -1682,6 +1561,7 @@ describe('Gun', function(){ //expect(count.Alice).to.be(1); //expect(count.Bob).to.be(1); //expect(count['undefined']).to.be(1); + if(done.c){ return } done.c = 1; done(); },10); } @@ -1693,7 +1573,7 @@ describe('Gun', function(){ it('uncached synchronous map on mutate node', function(done){ var s = Gun.state.map();s.soul = 'u/m/mutate/n'; - gun.on('put', {gun: gun, put: Gun.graph.ify({ + gun.on('put', {$: gun, put: Gun.graph.ify({ alice: {_:{'#':'umaliceo'}, age: 26, name: "Alice", @@ -1740,7 +1620,7 @@ describe('Gun', function(){ it('uncached synchronous map on mutate node uncached', function(done){ var s = Gun.state.map();s.soul = 'u/m/mutate/n/u'; - gun.on('put', {gun: gun, put: Gun.graph.ify({ + gun.on('put', {$: gun, put: Gun.graph.ify({ alice: {_:{'#':'umaliceo1'}, age: 26, name: "Alice", @@ -1756,9 +1636,9 @@ describe('Gun', function(){ gun.get('u/m/mutate/n/u').map().on(function(v,f){ check[v.name] = f; count[v.name] = (count[v.name] || 0) + 1; - //console.log("*****************", f,v); if(check.Alice && check.Bob && check['Alice Zzxyz']){ clearTimeout(done.to); + //console.log("****", f, v) done.to = setTimeout(function(){ expect(done.last).to.be.ok(); //expect(check['Alice Aabca']).to.not.be.ok(); @@ -1772,7 +1652,7 @@ describe('Gun', function(){ }); setTimeout(function(){ var s = Gun.state.map();s.soul = 'u/m/m/n/u/soul'; - gun.on('put', {gun: gun, put: Gun.graph.ify({ + gun.on('put', {$: gun, put: Gun.graph.ify({ name: 'Alice Zzxyz' }, s)}); //console.debug.i=1;console.log("---------------"); @@ -1797,7 +1677,7 @@ describe('Gun', function(){ it('uncached synchronous map on get mutate node uncached', function(done){ var s = Gun.state.map();s.soul = 'u/m/p/mutate/n/u'; - gun.on('put', {gun: gun, put: Gun.graph.ify({ + gun.on('put', {$: gun, put: Gun.graph.ify({ alice: {_:{'#':'umaliceo2'}, age: 26, name: "Alice", @@ -1830,7 +1710,7 @@ describe('Gun', function(){ }); setTimeout(function(){ var s = Gun.state.map();s.soul = 'u/m/p/m/n/u/soul'; - gun.on('put', {gun: gun, put: Gun.graph.ify({ + gun.on('put', {$: gun, put: Gun.graph.ify({ name: 'Alice Zzxyz', age: 34 }, s)}); gun.get('u/m/p/mutate/n/u').put({ @@ -1847,7 +1727,7 @@ describe('Gun', function(){ it('uncached synchronous map on get node mutate node uncached', function(done){ var s = Gun.state.map();s.soul = 'u/m/p/n/mutate/n/u'; - gun.on('put', {gun: gun, put: Gun.graph.ify({ + gun.on('put', {$: gun, put: Gun.graph.ify({ alice: {_:{'#':'umaliceo3'}, age: 26, name: "Alice", @@ -1878,7 +1758,7 @@ describe('Gun', function(){ }); setTimeout(function(){ var s = Gun.state.map();s.soul = 'alice/fuzz/soul'; - gun.on('put', {gun: gun, put: Gun.graph.ify({ + gun.on('put', {$: gun, put: Gun.graph.ify({ name: 'Alice Zzxyz', age: 34, pet: {c:3, name: "Fuzzball"} }, s)}); @@ -1997,7 +1877,7 @@ describe('Gun', function(){ var check = {}; gun.get('g/n/m/f/l/n/b/p').map().get('name').on(function(v,f){ check[v] = f; - //console.log("****************", f,v); + //console.log("****************", f,v, gun); if(check.alice && check.bob && check.Alice){ clearTimeout(done.to); done.to = setTimeout(function(){ @@ -2035,6 +1915,7 @@ describe('Gun', function(){ } }); setTimeout(function(){ + //console.debug.i=1;console.log("-------------"); gun.get('GALICE3').put({name: 'Alice'}); },300); }); @@ -2090,6 +1971,7 @@ describe('Gun', function(){ var check = {}; gun.get('g/n/m/f/l/n/b/p/p/p').map().get('spouse').get('work').on(function(v,f){ check[v.name] = f; + //console.log("*******", f, v); if(check['GUN INC'] && check['ACME INC'] && check['ACME INC.']){ clearTimeout(done.to); done.to = setTimeout(function(){ @@ -2127,6 +2009,7 @@ describe('Gun', function(){ } }); setTimeout(function(){ + //console.debug.i=1;console.log("----------------"); gun.get('CCINEMA1').put({name: 'ACME INC.'}); },300); }); @@ -2187,10 +2070,10 @@ describe('Gun', function(){ done.to = setTimeout(function(){ expect(check.alice.name).to.be('alice'); expect(check.alice.age).to.be(24); - expect(Gun.val.rel.is(check.alice.spouse)).to.be.ok(); + expect(Gun.val.link.is(check.alice.spouse)).to.be.ok(); expect(check.bob.name).to.be('bob'); expect(check.bob.age).to.be(26); - expect(Gun.val.rel.is(check.bob.spouse)).to.be.ok(); + expect(Gun.val.link.is(check.bob.spouse)).to.be.ok(); expect(check.GUN.name).to.be('GUN'); expect(check.ACME.name).to.be('ACME'); expect(check.ACME.corp).to.be('C'); @@ -2230,6 +2113,7 @@ describe('Gun', function(){ } }); setTimeout(function(){ + //console.debug.i=1;console.log("-------------"); gun.get('CCINEMA3').put({corp: "C"}); },300); }); @@ -2274,10 +2158,10 @@ describe('Gun', function(){ done.to = setTimeout(function(){ expect(check.alice.name).to.be('alice'); expect(check.alice.age).to.be(24); - expect(Gun.val.rel.is(check.alice.spouse)).to.be.ok(); + expect(Gun.val.link.is(check.alice.spouse)).to.be.ok(); expect(check.bob.name).to.be('bob'); expect(check.bob.age).to.be(26); - expect(Gun.val.rel.is(check.bob.spouse)).to.be.ok(); + expect(Gun.val.link.is(check.bob.spouse)).to.be.ok(); expect(check.GUN.name).to.be('GUN'); expect(check.ACME.name).to.be('ACME'); expect(check.ACME.corp).to.be('C'); @@ -2963,14 +2847,15 @@ describe('Gun', function(){ var user = {bob: bob}; bob.pet = cat; cat.slave = bob; - gun.on('put', {gun: gun, put: Gun.graph.ify(user, s)}); - gun.get(s.soul).get('bob').get('pet').get('slave').val(function(data){ + gun.on('put', {$: gun, put: Gun.graph.ify(user, s)}); + //console.debug.i=1;console.log("-------------"); + gun.get(s.soul).get('bob').get('pet').get('slave').once(function(data){ //clearTimeout(done.to); //setTimeout(function(){ - //console.log("*****************", data); + //console.log("*****************", data);return; expect(data.age).to.be(29); expect(data.name).to.be("Bob!"); - expect(Gun.val.rel.is(data.pet)).to.ok(); + expect(Gun.val.link.is(data.pet)).to.ok(); done(); //},300); }); @@ -3008,7 +2893,7 @@ describe('Gun', function(){ it('empty val followed', function(done){ var gun = Gun(); - gun.get('val/follow').val(function(data){ + gun.get('val/follow').once(function(data){ //console.log("val", data); }).get(function(at){ //console.log("?????", at); @@ -3022,7 +2907,7 @@ describe('Gun', function(){ var gun = Gun().get('chat/asdf'); var check = {}, count = {}; - gun.map().val(function(v,f){ + gun.map().once(function(v,f){ check[f] = v; count[f] = (count[f] || 0) + 1; //console.log("**************", f, v); @@ -3038,7 +2923,6 @@ describe('Gun', function(){ } }); setTimeout(function(){ - //console.debug.i=1;console.log("-----------------"); gun.get('1-1').put({what: "hi"}); setTimeout(function(){ gun.get('2-2').put({what: "you."}); @@ -3058,7 +2942,7 @@ describe('Gun', function(){ list.set(gun.get('dave').put({name: "Dave", group: "awesome", married: true})); var check = {}, count = {}; - list.map().val(function(data, id){ + list.map().once(function(data, id){ //console.log("***************", id, data); check[id] = data; count[id] = (count[id] || 0) + 1; @@ -3124,6 +3008,7 @@ describe('Gun', function(){ it('get get get set root get put', function(done){ var gun = Gun().get('app'); + //console.debug.i=1;console.log('---------------'); gun.get('alias').get('mark').set( gun.back(-1).get('pub').put({ alias: 'mark', @@ -3135,7 +3020,9 @@ describe('Gun', function(){ ); //return; setTimeout(function(){ + //console.debug.i=1;console.log('---------------'); gun.get(function(at){ + //console.log("*", at.put);//return; done.app = done.app || at.put.alias; }); gun.back(-1).get('pub').get(function(at){ @@ -3146,11 +3033,12 @@ describe('Gun', function(){ //console.log("***", at.put); done.alias = done.alias || at.put.mark; //!console.debug.i&&(console.debug.i=1)&&console.log("---------------------"); - }).get('mark').get(function(at){ - //console.log("************", at.put);//return; - setTimeout(function(){ - done.mark = done.mark || at.put.pub; - expect(Gun.val.rel.is(done.mark)).to.be('pub'); + }).get('mark').on(function(data){ + //console.log("************", data);//return; + clearTimeout(done.to); + done.to = setTimeout(function(){ + done.mark = done.mark || data.pub; + expect(Gun.val.link.is(done.mark)).to.be('pub'); expect(done.app).to.be.ok(); expect(done.pub).to.be.ok(); expect(done.alias).to.be.ok(); @@ -3162,7 +3050,7 @@ describe('Gun', function(){ }); it('get put get get put reload get get then get', function(done){ - this.timeout(6000); + this.timeout(9000); var gun = Gun(); gun.get('stef').put({name:'Stef'}); @@ -3176,7 +3064,7 @@ describe('Gun', function(){ setTimeout(function(){ var gun2 = Gun(); //console.log(require('fs').readFileSync('./radata/!').toString()); - gun2.get('stef').get('address').val(function(data){ // Object {_: Object, country: "Netherlands", zip: "1766KP"} "adress" + gun2.get('stef').get('address').once(function(data){ // Object {_: Object, country: "Netherlands", zip: "1766KP"} "adress" //console.log("******", data); done.a = true; expect(data.country).to.be('Netherlands'); @@ -3185,7 +3073,7 @@ describe('Gun', function(){ if(done.c){ return } done.c = 1; done(); }); - gun2.get('stef').val(function(data){ //Object {_: Object, address: Object} "stef" + gun2.get('stef').once(function(data){ //Object {_: Object, address: Object} "stef" //console.log("**************", data); //return; done.s = true; @@ -3195,12 +3083,12 @@ describe('Gun', function(){ if(done.c){ return } done.c = 1; done(); }); - },5000); + },1200); }); it('get get get any parallel', function(done){ var s = Gun.state.map();s.soul = 'parallel'; - gun.on('put', {gun: gun, put: Gun.graph.ify({ + gun.on('put', {$: gun, put: Gun.graph.ify({ bob: { age: 29, name: "Bob!" @@ -3208,14 +3096,14 @@ describe('Gun', function(){ }, s)}); gun.get('parallel').get('bob').get('age').get(function(at, ev){ var err = at.err, data = at.put, field = at.get; - //console.log("***** age", data, at.gun._.ack);//return; + //console.log("***** age", data, at.$._.ack);//return; expect(data).to.be(29); expect(field).to.be('age'); done.age = true; }); gun.get('parallel').get('bob').get('name').get(function(at, ev){ var err = at.err, data = at.put, field = at.get; - //console.log("*********** name", data, at.gun._.ack);//return; + //console.log("*********** name", data, at.$._.ack);//return; expect(data).to.be('Bob!'); expect(field).to.be('name'); done.name = true; @@ -3227,7 +3115,7 @@ describe('Gun', function(){ it('get get get any later', function(done){ var s = Gun.state.map();s.soul = 'parallel/later'; - gun.on('put', {gun: gun, put: Gun.graph.ify({ + gun.on('put', {$: gun, put: Gun.graph.ify({ bob: {_:{'#':'ddfsa'}, age: 29, name: "Bob!" @@ -3299,18 +3187,18 @@ describe('Gun', function(){ it('get any any', function(done){ var s = Gun.state.map();s.soul = 'full'; - gun.on('put', {gun: gun, put: Gun.graph.ify({ + gun.on('put', {$: gun, put: Gun.graph.ify({ hello: 'world', goodbye: 'mars' }, s)}); gun.get('full').get(function(at, ev){ - var err = at.err, data = at.gun._.put || at.put, field = at.get; + var err = at.err, data = at.$._.put || at.put, field = at.get; //console.log("*****1", data); expect(data.hello).to.be('world'); expect(data.goodbye).to.be('mars'); }); gun.get('full').get(function(at, ev){ - var err = at.err, data = at.gun._.put || at.put, field = at.get; + var err = at.err, data = at.$._.put || at.put, field = at.get; //console.log("*****1", data); expect(data.hello).to.be('world'); expect(data.goodbye).to.be('mars'); @@ -3321,19 +3209,19 @@ describe('Gun', function(){ it('get any any later', function(done){ var s = Gun.state.map();s.soul = 'full/later'; - gun.on('put', {gun: gun, put: Gun.graph.ify({ + gun.on('put', {$: gun, put: Gun.graph.ify({ hello: 'world', goodbye: 'mars' }, s)}); gun.get('full/later').get(function(at, ev){ - var err = at.err, data = at.gun._.put || at.put, field = at.get; + var err = at.err, data = at.$._.put || at.put, field = at.get; //console.log("*****", data); expect(data.hello).to.be('world'); expect(data.goodbye).to.be('mars'); }); setTimeout(function(){ gun.get('full/later').get(function(at, ev){ - var err = at.err, data = at.gun._.put || at.put, field = at.get; + var err = at.err, data = at.$._.put || at.put, field = at.get; //console.log("*****2", field, data); expect(data.hello).to.be('world'); expect(data.goodbye).to.be('mars'); @@ -3363,14 +3251,14 @@ describe('Gun', function(){ var check = {A: {}, B: {}}; setTimeout(function(){ - gun.get('usersMM').map().map().val(function(data){ + gun.get('usersMM').map().map().once(function(data){ //console.log('A', data); check.A[data.pub] = true; }) }, 900); setTimeout(function(){ - gun.get('usersMM').map().map().val(function(data){ + gun.get('usersMM').map().map().once(function(data){ //console.log('B', data, check); check.B[data.pub] = true; if(check.A['asdf'] && check.A['fdsa'] && check.B['asdf'] && check.B['fdsa']){ @@ -3413,7 +3301,7 @@ describe('Gun', function(){ var gun = Gun(); var s = Gun.state.map();s.soul = 'mult/times/part'; - gun.on('put', {gun: gun, put: Gun.graph.ify({ + gun.on('put', {$: gun, put: Gun.graph.ify({ alias: { mark: { pub: {_:{'#':'PUB'}, @@ -3428,7 +3316,7 @@ describe('Gun', function(){ var app = gun.get(s.soul); //console.debug.i=1;console.log("==================="); - app.get('alias').get('mark').map().val(function(alias){ + app.get('alias').get('mark').map().once(function(alias){ //console.log("***", alias); done.alias = alias; }); @@ -3453,15 +3341,18 @@ describe('Gun', function(){ var bar = gun.get('put/on/put/ok').get('a').get('b'); bar.put({a:1}); - + //console.log("vvvvvvvvv"); bar.on(function(data){ + //console.log("***", data); if(1 === data.a && 3 === data.c){ if(done.c){ return } done.c = 1; + //console.log("-------"); done(); } }); foo.on(function(ack){ + //console.log("*", ack); bar.put({c:3}); }); foo.put({b:2}); @@ -3482,12 +3373,37 @@ describe('Gun', function(){ done(); } }); + //console.debug.i=1;console.log("--------------"); list.set({name: 'alice', age: 27}); // on put, table-scan flag doesn't get set, but is needed for initial!?? list.set({name: 'bob', age: 27}); list.set({name: 'carl', age: 29}); 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); @@ -3498,9 +3414,10 @@ describe('Gun', function(){ list.set(gun.get('dave').put({name: 'dave', age: 25})); var check = {}; - list.val().map().on(function(data, key){ + list.once().map().on(function(data, key){ check[key] = data; clearTimeout(done.to); + //console.log("*****", key, data); done.to = setTimeout(function(){ if(check.alice && check.bob && check.carl && check.dave && done.last){ expect(check.alice.age).to.be(27); @@ -3545,12 +3462,12 @@ describe('Gun', function(){ ctx.on('out', function(msg){ this.to.next(msg); var onGun = ctx; - if(onGun.gun === b) { + if(onGun.$ === b) { if(d){ //console.log("b can send to d....", Gun.obj.copy(msg)); d.on("in", msg); } - } else if(onGun.gun === d){ + } else if(onGun.$ === d){ //console.log("d sends to b....", Gun.obj.copy(msg)); b.on("in", msg); } @@ -3577,26 +3494,28 @@ describe('Gun', function(){ it('val should now get called if no data is found', function(done){ var gun = Gun(); - gun.get('nv/foo').get('bar').get('baz').val(function(val, key){ + gun.get('nv/foo').get('bar').get('baz').once(function(val, key){ //console.log('*******', key, val); expect(val).to.be(undefined); done.fbb = true; }); - gun.get('nv/totesnothing').val(function(val, key){ + gun.get('nv/totesnothing').once(function(val, key){ //console.log('***********', key, val); expect(val).to.be(undefined); done.t = true; }); - gun.get('nv/bz').get('lul').val(function(val, key){ + gun.get('nv/bz').get('lul').once(function(val, key){ //console.log('*****************', key, val); expect(val).to.be(undefined); done.bzl = true; - if(done.fbb && done.t && done.bzl){ - if(done.c){ return } done.c = 1; - done(); - } + setTimeout(function(){ + if(done.fbb && done.t && done.bzl){ + if(done.c){ return } done.c = 1; + done(); + } + },100); }); }); @@ -3609,7 +3528,7 @@ describe('Gun', function(){ data.b = 2; }); - gun.get('ds/safe').val(function(data){ + gun.get('ds/safe').once(function(data){ expect(gun.back(-1)._.graph['ds/safe'].b).to.not.be.ok(); if(done.c){ return } done.c = 1; done(); @@ -3619,7 +3538,7 @@ describe('Gun', function(){ it('If chain cannot be called, ack', function(done){ var gun = Gun(), u; - gun.on('put', {gun: gun, put: Gun.graph.ify({ + gun.on('put', {$: gun, put: Gun.graph.ify({ wat: 1, a: true }, 'nl/app')}); @@ -3643,7 +3562,7 @@ describe('Gun', function(){ it('Chain on known nested object should ack', function(done){ var gun = Gun(), u; - gun.on('put', {gun: gun, put: Gun.graph.ify({ + gun.on('put', {$: gun, put: Gun.graph.ify({ bar: { wat: 1 } @@ -3674,6 +3593,7 @@ describe('Gun', function(){ }); it('Soul above but not beneath', function(done){ + this.timeout(5000); var gun = Gun(); var a = gun.get('sabnb'); @@ -3683,7 +3603,7 @@ describe('Gun', function(){ setTimeout(function(){ a.get('profile').get('said').get('asdf').put('yes'); setTimeout(function(){ - a.val(function(data){ + a.once(function(data){ expect(data.profile).to.be.eql({'#': 'sabnbprofile'}); if(done.c){ return } done.c = 1; done(); @@ -3697,7 +3617,7 @@ describe('Gun', function(){ this.timeout(1000 * 9); var gun = Gun(); - gun.get('users').put({ + gun.get('users/mm').put({ alice: {_:{'#':'alias/alice'}, 'pub/asdf': {_:{'#':'pub/asdf'}, pub: 'asdf' @@ -3712,8 +3632,9 @@ describe('Gun', function(){ var check = {}, c = 0, end; //console.log(check); - gun.get('users').map().map() + gun.get('users/mm').map().map() .get('who').get('said').map().on(function(msg){ + //console.log("------>", msg.num); if(check[msg.num]){ //console.log("!!!!", msg.num, "!!!!"); } @@ -3732,6 +3653,7 @@ describe('Gun', function(){ //console.log("----", i, "----"); //2 === i && (console.debug.i = 1) && console.debug(1, '======= what happens?'); + //(console.debug.i = console.debug.i || 1); said.set({ what: i + " Hello world!", num: i, @@ -3755,14 +3677,14 @@ describe('Gun', function(){ it('get map should not slowdown', function(done){ this.timeout(5000); - var gun = Gun().get('g/m/no/slow'); + var gun = Gun({test_no_peer:true}).get('g/m/no/slow'); //console.log("---------- setup data done -----------"); - var prev, diff, max = 25, total = 9, largest = -1, gone = {}; + var prev, diff, max = 25, total = 9, largest = -1, gone = {}, u; //var prev, diff, max = Infinity, total = 10000, largest = -1, gone = {}; // TODO: It would be nice if we could change these numbers for different platforms/versions of javascript interpreters so we can squeeze as much out of them. gun.get('history').map().on(function(time, index){ - //console.log(">>>", index, time); diff = Gun.time.is() - time; + //console.log(">>>", index, time, diff); //return; expect(gone[index]).to.not.be.ok(); gone[index] = diff; @@ -3773,6 +3695,7 @@ describe('Gun', function(){ var turns = 0; var many = setInterval(function(){ if(turns > total || (diff || 0) > (max + 5)){ + if(u === diff){ return } clearTimeout(many); expect(Gun.num.is(diff)).to.be.ok(); if(done.c){ return } done.c = 1; @@ -3784,10 +3707,71 @@ describe('Gun', function(){ //console.log("put", put); //console.log("------", turns, "-------"); //3 === turns && (console.debug.i = 1); - console.debug(1, 'save', {history: put}); + //console.debug(1, 'save', {history: put}); gun.put({history: put}); }, 1); }); + + it('Check put callback', function(done){ + var gun = Gun(); + + gun.get('c/p/c').get('a').put('lol', function(ack){ + done(); + }); + }); + + it('Multiple subscribes should trigger', function(done){ + // thanks to @ivkan for reporting and providing test. + var gun = Gun(); + var check = {}; + gun.get('m/s/key').put({property: 'value'}); + + gun.get('m/s/key').on(function(data, key){ + check['a'+data.property] = 1; + }); + + gun.get('m/s/key').on(function(data, key){ + check['b'+data.property] = 1; + if(check.avalue && check.bvalue && check.anewValue && check.bnewValue){ + if(done.c){ return } done.c = true; + done(); + } + }); + + setTimeout(function(){ + gun.get('m/s/key').put({property: 'newValue'}); + }, 1000); + }); + + it('Deep puts with peer should work', function(done){ + // tests in async mode now automatically connect to localhost peer. + //var gun = Gun('http://localhost:8765/gun'); + var gun = Gun(); + //var user = gun.user(); + //user.create('alice', 'password', function(){ + gun.get('who').get('all').put({what: "hello world!", when: Gun.state()}, function(ack){ + //user.get('who').get('all').put({what: "hello world!", when: Gun.state()}, function(ack){ + gun.get('who').get('all').once(function(data){ + expect(data.what).to.be.ok(); + expect(data.when).to.be.ok(); + done(); + }); + }); + //}); + }); + + it('Set a ref should be found', function(done){ + var gun = Gun(); + var msg = {what: 'hello world'}; + //var ref = user.get('who').get('all').set(msg); + //user.get('who').get('said').set(ref); + var ref = gun.get('s/r/who').get('all').set(msg); + gun.get('s/r/who').get('said').set(ref); + gun.get('s/r/who').get('said').map().once(function(data){ + expect(data.what).to.be.ok(); + done(); + }) + }); return; it('Nested listener should be called', function(done){ @@ -3846,22 +3830,22 @@ describe('Gun', function(){ var chain = this.chain(); var context = this; var _tags; - context.val(function(obj, key){ + context.once(function(obj, key){ if(!obj.tags){ console.warn('Not tagged to anything!'); context._.valid = false; - chain._.on('in', {get: key, gun: this}); + chain._.on('in', {get: key, $: this}); return false; } else { _tags = Gun.obj.ify(obj.tags); if(Array.isArray(filter)){ context._.valid = filter.every(function(f){ return ( _tags[f] && _tags[f]==1) }); if(context._.valid){ - chain._.on('in', {get: key, put: obj, gun: this}); + chain._.on('in', {get: key, put: obj, $: this}); return context; } else { console.log("that was wrong"); - chain._.on('in', {get: key, put: undefined, gun: this}); + chain._.on('in', {get: key, put: undefined, $: this}); } return false; } else { @@ -3884,7 +3868,7 @@ describe('Gun', function(){ gun.get('fake1')//.map() .filter(['a','b']) // Gun.chain.filter = function(tags){ .... } .get(function(no){console.log("NO!", no)}) - .val(function(yes){console.log("YES!", yes)}) + .once(function(yes){console.log("YES!", yes)}) }); it.only('Check that events are called with multiple instances', function(done){ @@ -3950,7 +3934,7 @@ describe('Gun', function(){ it('get get any parallel', function(done){ var s = Gun.state.map();s.soul = 'parallel/get/get'; - Gun.on('put', {gun: gun, put: Gun.graph.ify({ + Gun.on('put', {$: gun, put: Gun.graph.ify({ bob: { age: 29, name: "Bob!" @@ -3971,7 +3955,7 @@ describe('Gun', function(){ it('get get any parallel later', function(done){ var s = Gun.state.map();s.soul = 'parallel/get/get/later'; - Gun.on('put', {gun: gun, put: Gun.graph.ify({ + Gun.on('put', {$: gun, put: Gun.graph.ify({ bob: { age: 29, name: "Bob!" @@ -3994,7 +3978,7 @@ describe('Gun', function(){ it('get get any none', function(done){ var s = Gun.state.map();s.soul = 'get/get/none'; - Gun.on('put', {gun: gun, put: Gun.graph.ify({ + Gun.on('put', {$: gun, put: Gun.graph.ify({ alice: { age: 31, name: "alice" @@ -4022,7 +4006,7 @@ describe('Gun', function(){ it('get get any none later', function(done){ var s = Gun.state.map();s.soul = 'get/get/none/later'; - Gun.on('put', {gun: gun, put: Gun.graph.ify({ + Gun.on('put', {$: gun, put: Gun.graph.ify({ alice: { age: 31, name: "alice" @@ -4048,7 +4032,7 @@ describe('Gun', function(){ it('get get primitive get any', function(done){ var s = Gun.state.map();s.soul = 'get/get/prim'; - Gun.on('put', {gun: gun, put: Gun.graph.ify({ + Gun.on('put', {$: gun, put: Gun.graph.ify({ bob: "is awesome" }, s)}); gun.get('get/get/prim').path('bob').path('age').any(function(err, data, field, at, ev){ @@ -4064,7 +4048,7 @@ describe('Gun', function(){ it('get put any', function(done){ var s = Gun.state.map();s.soul = 'get/put/any'; - Gun.on('put', {gun: gun, put: Gun.graph.ify({ + Gun.on('put', {$: gun, put: Gun.graph.ify({ here: "we go" }, s)}); //console.debug.i=1;console.log("---------------"); @@ -4078,7 +4062,7 @@ describe('Gun', function(){ return; it('get any, get put any', function(done){ var s = Gun.state.map();s.soul = 'get/any/get/put/any'; - Gun.on('put', {gun: gun, put: Gun.graph.ify({ + Gun.on('put', {$: gun, put: Gun.graph.ify({ here: "we go" }, s)}); gun.get('get/any/get/put/any') @@ -4107,7 +4091,7 @@ describe('Gun', function(){ it('mutate pointer to primitive deep on', function(done){ var s = Gun.state.map();s.soul = 'change/pointer'; - Gun.on('put', {gun: gun, put: Gun.graph.ify({ + Gun.on('put', {$: gun, put: Gun.graph.ify({ bob: { age: 29, name: "Bob!", @@ -4149,14 +4133,14 @@ describe('Gun', function(){ done.e = 2; return; } - expect(Gun.val.rel.is(data.bob)).to.be.ok(); + expect(Gun.val.link.is(data.bob)).to.be.ok(); done.e = 1; }); }); it('get only soul', function(done){ var s = Gun.state.map();s.soul = 'only/soul'; - Gun.on('put', {gun: gun, put: Gun.graph.ify({ + Gun.on('put', {$: gun, put: Gun.graph.ify({ bob: { age: 29, name: "Bob!", @@ -4174,7 +4158,7 @@ describe('Gun', function(){ it('get path only soul', function(done){ var s = Gun.state.map();s.soul = 'only/p/soul'; - Gun.on('put', {gun: gun, put: Gun.graph.ify({ + Gun.on('put', {$: gun, put: Gun.graph.ify({ bob: { age: 29, name: "Bob!", @@ -4186,7 +4170,7 @@ describe('Gun', function(){ }, s)}); gun.get('only/p/soul').path('bob').any(function(err, data){ //console.log("*********", err, data); - expect(Gun.val.rel.is(data)).to.be.ok(); + expect(Gun.val.link.is(data)).to.be.ok(); //expect(Gun.obj.empty(data, '_')).to.be.ok(); done(); }, {'.': null}); @@ -4194,7 +4178,7 @@ describe('Gun', function(){ it('mutate pointer to self', function(done){ var s = Gun.state.map();s.soul = 'change/pointer/point'; - Gun.on('put', {gun: gun, put: Gun.graph.ify({ + Gun.on('put', {$: gun, put: Gun.graph.ify({ bob: { age: 29, name: "Bob!", @@ -4208,7 +4192,7 @@ describe('Gun', function(){ if(done.c){ expect(data.age).to.be(30); expect(data.name).to.be('Bob!'); - expect(Gun.val.rel.is(data.pet)).to.be.ok(); + expect(Gun.val.link.is(data.pet)).to.be.ok(); expect(done.c).to.be(1); done(); done.c = 2; @@ -4216,7 +4200,7 @@ describe('Gun', function(){ } expect(data.age).to.be(29); expect(data.name).to.be('Bob!'); - expect(Gun.val.rel.is(data.pet)).to.be.ok(); + expect(Gun.val.link.is(data.pet)).to.be.ok(); done.c=1; }); setTimeout(function(){ @@ -4225,7 +4209,7 @@ describe('Gun', function(){ }); it('mutate pointer to self deep', function(done){ var s = Gun.state.map();s.soul = 'change/pointer/point/deep'; - Gun.on('put', {gun: gun, put: Gun.graph.ify({ + Gun.on('put', {$: gun, put: Gun.graph.ify({ bob: { age: 29, name: "Bob!", @@ -4240,13 +4224,13 @@ describe('Gun', function(){ if(done.c){ expect(data.age).to.be(30); expect(data.name).to.be('Bob!'); - expect(Gun.val.rel.is(data.pet)).to.be.ok(); + expect(Gun.val.link.is(data.pet)).to.be.ok(); done(); return; } expect(data.age).to.be(29); expect(data.name).to.be('Bob!'); - expect(Gun.val.rel.is(data.pet)).to.be.ok(); + expect(Gun.val.link.is(data.pet)).to.be.ok(); done.c=1; }); setTimeout(function(){ @@ -4256,7 +4240,7 @@ describe('Gun', function(){ it('mutate pointer to primitive after any', function(done){ var s = Gun.state.map();s.soul = 'change/pointer/to/prime'; - Gun.on('put', {gun: gun, put: Gun.graph.ify({ + Gun.on('put', {$: gun, put: Gun.graph.ify({ bob: {_: {'#': 'asdffdsa'}, age: 29, name: "Bob!", @@ -4279,7 +4263,7 @@ describe('Gun', function(){ } expect(data.age).to.be(29); expect(data.name).to.be('Bob!'); - expect(Gun.val.rel.is(data.pet)).to.be.ok(); + expect(Gun.val.link.is(data.pet)).to.be.ok(); }); setTimeout(function(){ gun.get('change/pointer/to/prime').path('bob').put(3); @@ -4291,7 +4275,7 @@ describe('Gun', function(){ it('mutate pointer to primitive after any deep', function(done){ var s = Gun.state.map();s.soul = 'change/pointer/to/prime/deep'; - Gun.on('put', {gun: gun, put: Gun.graph.ify({ + Gun.on('put', {$: gun, put: Gun.graph.ify({ bob: { age: 29, name: "Bob!", @@ -4325,7 +4309,7 @@ describe('Gun', function(){ return; it.only('mutate pointer to another pointer after any', function(done){ var s = Gun.state.map();s.soul = 'change/pointer/to/pointer'; - Gun.on('put', {gun: gun, put: Gun.graph.ify({ + Gun.on('put', {$: gun, put: Gun.graph.ify({ bob: {_: {'#': 'dafssfad'}, age: 29, name: "Bob!", @@ -4354,7 +4338,7 @@ describe('Gun', function(){ expect(done.soul = Gun.node.soul(data)).to.be('dafssfad'); expect(data.age).to.be(29); expect(data.name).to.be('Bob!'); - expect(Gun.val.rel.is(data.pet)).to.be.ok(); + expect(Gun.val.link.is(data.pet)).to.be.ok(); }); return; setTimeout(function(){ @@ -4429,7 +4413,7 @@ describe('Gun', function(){ get.put({foo: 'bar'}); get.any(function(err,data){ //console.log("data", data); - expect(Gun.val.rel.is(data.all)).to.be.ok(); + expect(Gun.val.link.is(data.all)).to.be.ok(); expect(data.foo).to.be('bar'); }); }); @@ -4634,7 +4618,7 @@ describe('Gun', function(){ kn = Gun.obj.copy(kn); delete kn._; expect(Gun.obj.empty(kn, '##')).to.be.ok(); - kn = gun.back(-1)._.graph[Gun.val.rel.is(kn['##'])]; + kn = gun.back(-1)._.graph[Gun.val.link.is(kn['##'])]; Gun.node.is(kn, function(node, s){ var n = gun.back(-1)._.graph[s]; if(Gun.obj.has(n, '##')){ @@ -4732,7 +4716,7 @@ describe('Gun', function(){ var gun = Gun().put({foo:'lol', extra: 'yes'}).key('key/path/put'); var data = gun.get('key/path/put'); data.path('foo').put('epic'); - data.val(function(val, field){ + data.once(function(val, field){ expect(val.foo).to.be('epic'); expect(Gun.node.soul(val)).to.be('key/path/put'); done(); @@ -4834,12 +4818,12 @@ describe('Gun', function(){ var root = gun.back(-1)._.graph[soul]; expect(root.hello).to.be('key'); expect(root.yay).to.not.be.ok(); - expect(Gun.val.rel.is(root.hi)).to.be.ok(); - expect(Gun.val.rel.is(root.hi)).to.not.be(soul); - var node = gun.back(-1)._.graph[Gun.val.rel.is(root.hi)]; + expect(Gun.val.link.is(root.hi)).to.be.ok(); + expect(Gun.val.link.is(root.hi)).to.not.be(soul); + var node = gun.back(-1)._.graph[Gun.val.link.is(root.hi)]; expect(node.yay).to.be('value'); - if(done.sub){ expect(done.sub).to.be(Gun.val.rel.is(root.hi)) } - else { done.sub = Gun.val.rel.is(root.hi) } + if(done.sub){ expect(done.sub).to.be(Gun.val.link.is(root.hi)) } + else { done.sub = Gun.val.link.is(root.hi) } done.w = 1; if(done.r){ done(); done.c = 1 }; }).on(function(node, field){ if(done.c){ return } @@ -4856,7 +4840,7 @@ describe('Gun', function(){ var get = gun.get('shallow/path'); var path = get.path('one'); var put = path.put('good'); - put.val(function(val, field){ + put.once(function(val, field){ expect(val).to.be('good'); expect(field).to.be('one'); done(); @@ -4868,7 +4852,7 @@ describe('Gun', function(){ var get = gun.get('slightly/shallow/path'); var path = get.path('one'); var put = path.put({you: 'are', here: 1}); - put.val(function(val, field){ + put.once(function(val, field){ //console.log('***********', field, val); expect(val.you).to.be('are'); expect(val.here).to.be(1); @@ -4924,10 +4908,10 @@ describe('Gun', function(){ },100); }); - it('any any not', function(done){ + it('get get not', function(done){ var s = Gun.state.map(); s.soul = 'a'; - Gun.on('put', {gun: gun, put: Gun.graph.ify({b: 1, c: 2}, s)}); + Gun.on('put', {$: gun, put: Gun.graph.ify({b: 1, c: 2}, s)}); function cb(e,d,f,a){ if('b' === f && 1 === d){ done.b = true; @@ -4944,9 +4928,9 @@ describe('Gun', function(){ done(); } } - gun.get('a').path('b').any(cb);//.err(cb).not(cb).on(cb).val(cb); - gun.get('a').path('c').any(cb);//.err(cb).not(cb).on(cb).val(cb); - gun.get('a').path('d').any(cb);//.err(cb).not(cb).on(cb).val(cb); + gun.get('a').path('b').get(cb);//.err(cb).not(cb).on(cb).once(cb); + gun.get('a').path('c').get(cb);//.err(cb).not(cb).on(cb).once(cb); + gun.get('a').path('d').get(cb);//.err(cb).not(cb).on(cb).once(cb); }); it('any not any not any not', function(done){ @@ -4964,19 +4948,19 @@ describe('Gun', function(){ done(); } } - gun.get('x').path('b').any(cb);//.err(cb).not(cb).on(cb).val(cb); - gun.get('x').path('c').any(cb);//.err(cb).not(cb).on(cb).val(cb); - gun.get('x').path('d').any(cb);//.err(cb).not(cb).on(cb).val(cb); + gun.get('x').path('b').any(cb);//.err(cb).not(cb).on(cb).once(cb); + gun.get('x').path('c').any(cb);//.err(cb).not(cb).on(cb).once(cb); + gun.get('x').path('d').any(cb);//.err(cb).not(cb).on(cb).once(cb); }); it('get put, put deep', function(done){ var gun = Gun(); var get = gun.get('put/deep/ish'); get.put({}); - get.val(function(data){ // TODO: API CHANGE! Empty objects should react. + get.once(function(data){ // TODO: API CHANGE! Empty objects should react. //console.log("...1", data); expect(Gun.obj.empty(data, '_')).to.be.ok(); // API CHANGED, - //expect(Gun.val.rel.is(data.very)).to.be.ok(); + //expect(Gun.val.link.is(data.very)).to.be.ok(); });//, {wait: 10000}); setTimeout(function(){ var put = get.put({ @@ -4988,23 +4972,23 @@ describe('Gun', function(){ } } }); - get.val(function(data){ + get.once(function(data){ //console.log("...2", data); - expect(Gun.val.rel.is(data.very)).to.be.ok(); + expect(Gun.val.link.is(data.very)).to.be.ok(); }); setTimeout(function(){ - put.val(function(data){ + put.once(function(data){ //console.log("...3", data); - expect(Gun.val.rel.is(data.very)).to.be.ok(); + expect(Gun.val.link.is(data.very)).to.be.ok(); done.val = true; }); var p = put.path('very'); p.put({we: 'have gone!'}); setTimeout(function(){ - p.val(function(data){ + p.once(function(data){ //console.log("...4", data); expect(data.we).to.be('have gone!'); - expect(Gun.val.rel.is(data.deep)).to.be.ok(); + expect(Gun.val.link.is(data.deep)).to.be.ok(); }); p.put('EXPLODE'); setTimeout(function(){ @@ -5021,7 +5005,7 @@ describe('Gun', function(){ var get = gun.get('slightly/shallow/path/swoop'); var path = get.path('one.two'); var put = path.put({oh: 'okay'}); - put.val(function(val, field){ + put.once(function(val, field){ //console.log("****", field, val); expect(val.oh).to.be('okay'); expect(field).to.be('two'); @@ -5035,13 +5019,13 @@ describe('Gun', function(){ var path = get.path('one.two'); var path3 = path.path('three'); var put = path3.put({you: 'found', the: 'bottom!'}); - put.val(function(val, field){ + put.once(function(val, field){ //console.log("********1********", field, val); expect(val.you).to.be('found'); expect(val.the).to.be('bottom!'); expect(field).to.be('three'); }); - gun.get('deep/path').path('one.two.three.you').put('are').val(function(val, field){ + gun.get('deep/path').path('one.two.three.you').put('are').once(function(val, field){ //console.log("********2*********", field, val);return; expect(val).to.be('are'); expect(field).to.be('you'); @@ -5096,7 +5080,7 @@ describe('Gun', function(){ var g = gun.get('hello/key', function(err, node){ if(done.soul){ return } expect(err).to.not.be.ok(); - expect(done.ref = Gun.val.rel.is(node.hi)).to.be.ok(); + expect(done.ref = Gun.val.link.is(node.hi)).to.be.ok(); done.soul = Gun.node.soul(node); }); g.path('hi').put({happy: "faces"}, function(err, ok){ @@ -5130,7 +5114,7 @@ describe('Gun', function(){ gun.get('hello/key', function(err, node){ if(done.soul){ return } expect(err).to.not.be.ok(); - expect(done.ref = Gun.val.rel.is(node.hi)).to.be.ok(); + expect(done.ref = Gun.val.link.is(node.hi)).to.be.ok(); done.soul = Gun.node.soul(node); }).path('hi').put('crushed', function(err, ok){ if(done.c){ return } @@ -5161,13 +5145,13 @@ describe('Gun', function(){ mark.path('wife').put(amber, function(err){ expect(err).to.not.be.ok(); }); - mark.path('wife.name').val(function(val){ + mark.path('wife.name').once(function(val){ expect(val).to.be("Amber Nadal"); }); }); it('put val', function(done){ - gun.put({hello: "world"}).val(function(val){ + gun.put({hello: "world"}).once(function(val){ expect(val.hello).to.be('world'); expect(done.c).to.not.be.ok(); done.c = 1; @@ -5179,7 +5163,7 @@ describe('Gun', function(){ }); it('put key val', function(done){ - gun.put({hello: "world"}).key('hello/world').val(function(val, field){ + gun.put({hello: "world"}).key('hello/world').once(function(val, field){ if(done.c){ return } expect(val.hello).to.be('world'); expect(done.c).to.not.be.ok(); @@ -5192,7 +5176,7 @@ describe('Gun', function(){ }); it('get val', function(done){ - gun.get('hello/world').val(function(val, field){ + gun.get('hello/world').once(function(val, field){ expect(val.hello).to.be('world'); expect(done.c).to.not.be.ok(); done.c = 1; @@ -5204,7 +5188,7 @@ describe('Gun', function(){ }); it('get path', function(done){ - gun.get('hello/world').path('hello').val(function(val){ + gun.get('hello/world').path('hello').once(function(val){ //console.log("**************", val); expect(val).to.be('world'); expect(done.c).to.not.be.ok(); @@ -5217,7 +5201,7 @@ describe('Gun', function(){ }); it('get put path', function(done){ - gun.get('hello/world').put({hello: 'Mark'}).path('hello').val(function(val, field){ + gun.get('hello/world').put({hello: 'Mark'}).path('hello').once(function(val, field){ expect(val).to.be('Mark'); expect(done.c).to.not.be.ok(); done.c = 1; @@ -5229,7 +5213,7 @@ describe('Gun', function(){ }); it('get path put', function(done){ - gun.get('hello/world').path('hello').put('World').val(function(val){ + gun.get('hello/world').path('hello').put('World').once(function(val){ expect(val).to.be('World'); expect(done.c).to.not.be.ok(); done.c = 1; @@ -5257,7 +5241,7 @@ describe('Gun', function(){ it('get path empty put val', function(done){ var gun = Gun({init: true}).put({hello: "Mark"}).key('hello/world/not'); - gun.get('hello/world/not').path('earth').put('mars').val(function(val){ + gun.get('hello/world/not').path('earth').put('mars').once(function(val){ done.c = 1; }); setTimeout(function(){ @@ -5270,7 +5254,7 @@ describe('Gun', function(){ var gun = Gun(); var get = gun.get('hello/imp/world'); var put = get.put({planet: 'the earth'}); - put.val(function(val){ + put.once(function(val){ expect(val.planet).to.be('the earth'); done(); }); @@ -5281,7 +5265,7 @@ describe('Gun', function(){ var get = gun.get('hello/imp/where'); var path = get.path('where'); var put = path.put('the mars'); - var val = put.val(function(val, field){ + var val = put.once(function(val, field){ expect(field).to.be('where'); expect(val).to.be('the mars'); done(); @@ -5289,7 +5273,7 @@ describe('Gun', function(){ }); it('get path empty put val implicit', function(done){ - gun.get('hello/world').path('earth').put('mars').val(function(val, field){ + gun.get('hello/world').path('earth').put('mars').once(function(val, field){ expect(val).to.be('mars'); expect(done.c).to.not.be.ok(); done.c = 1; @@ -5303,7 +5287,7 @@ describe('Gun', function(){ it('get path val', function(done){ var gun = Gun({init: true}).put({hello: "Mark"}).key('hello/world/not'); gun.get('hello/world').path('earth').put('mars'); - gun.get('hello/world/not').path('earth').val(function(val){ + gun.get('hello/world/not').path('earth').once(function(val){ expect(val).to.be('mars'); expect(done.c).to.not.be.ok(); done.c = 1; @@ -5315,7 +5299,7 @@ describe('Gun', function(){ }); it('get path val implicit', function(done){ - gun.get('hello/world').path('earth').val(function(val){ + gun.get('hello/world').path('earth').once(function(val){ expect(val).to.be('mars'); expect(done.c).to.not.be.ok(); done.c = 1; @@ -5330,7 +5314,7 @@ describe('Gun', function(){ it('get not kick val', function(done){ gun.get("some/empty/thing").not(function(key, kick){ // that if you call not first this.put({now: 'exists'}).key(key); // you can put stuff - }).val(function(val){ // and THEN still retrieve it. + }).once(function(val){ // and THEN still retrieve it. expect(val.now).to.be('exists'); done(); }); @@ -5342,7 +5326,7 @@ describe('Gun', function(){ foo.not(function(key, kick){ done.not = true; this.put({now: 'THIS SHOULD NOT HAPPEN'}).key(key); - }).val(function(val){ + }).once(function(val){ expect(val.now).to.be('exists'); expect(done.not).to.not.be.ok(); done(); @@ -5351,7 +5335,7 @@ describe('Gun', function(){ }); it('put path val sub', function(done){ - gun.put({last: {some: 'object'}}).path('last').val(function(val){ + gun.put({last: {some: 'object'}}).path('last').once(function(val){ expect(val.some).to.be('object'); done(); }); @@ -5388,11 +5372,11 @@ describe('Gun', function(){ }); it('get put null', function(done){ - gun.put({last: {some: 'object'}}).path('last').val(function(val, field){ + gun.put({last: {some: 'object'}}).path('last').once(function(val, field){ //console.log("**", field, val); expect(field).to.be('last'); expect(val.some).to.be('object'); - }).put(null).val(function(val, field){ + }).put(null).once(function(val, field){ //console.log("******", field, val); expect(field).to.be('last'); expect(val).to.be(null); @@ -5402,11 +5386,11 @@ describe('Gun', function(){ it('Gun get put null', function(done){ // flip flop bug var gun = Gun(); - gun.put({last: {some: 'object'}}).path('last').val(function(val, field){ + gun.put({last: {some: 'object'}}).path('last').once(function(val, field){ //console.log("**", field, val); done.some = true; expect(val.some).to.be('object'); - }).put(null).val(function(val, field){ + }).put(null).once(function(val, field){ //console.log("********", field, val); expect(val).to.be(null); expect(done.some).to.be.ok(); @@ -5418,7 +5402,7 @@ describe('Gun', function(){ var foo = gun.put({foo: 'bar'}).key('foo/bar'); foo.path('hello.world.nowhere'); // this should become a sub-context, that doesn't alter the original setTimeout(function(){ - foo.path('foo').val(function(val){ // and then the original should be able to be reused later + foo.path('foo').once(function(val){ // and then the original should be able to be reused later expect(val).to.be('bar'); // this should work done(); }); @@ -5429,7 +5413,7 @@ describe('Gun', function(){ var foo = gun.get('foo/bar'); foo.path('hello.world.nowhere'); // this should become a sub-context, that doesn't alter the original setTimeout(function(){ - foo.path('foo').val(function(val){ // and then the original should be able to be reused later + foo.path('foo').once(function(val){ // and then the original should be able to be reused later expect(val).to.be('bar'); // this should work done(); }); @@ -5443,10 +5427,10 @@ describe('Gun', function(){ title: 'awesome title', todos: {} }).key(key); - }).val(function(data){ + }).once(function(data){ expect(data.id).to.be('foobar'); - //}).path('todos').val(function(todos, field){ - }).path('todos').val(function(todos, field){ + //}).path('todos').once(function(todos, field){ + }).path('todos').once(function(todos, field){ expect(field).to.be('todos'); expect(todos).to.not.have.property('id'); done(); @@ -5462,14 +5446,14 @@ describe('Gun', function(){ data.b.parent = data.a; gun.put(data, function(err, ok){ expect(err).to.not.be.ok(); - }).val(function(val){ + }).once(function(val){ setTimeout(function(){ // TODO: Is this cheating? I don't think so cause we are using things outside of the API! - var a = gun.back(-1)._.graph[Gun.val.rel.is(val.a)]; - var b = gun.back(-1)._.graph[Gun.val.rel.is(val.b)]; - expect(Gun.val.rel.is(val.a)).to.be(Gun.node.soul(a)); - expect(Gun.val.rel.is(val.b)).to.be(Gun.node.soul(b)); - expect(Gun.val.rel.is(a.kid)).to.be(Gun.node.soul(b)); - expect(Gun.val.rel.is(b.parent)).to.be(Gun.node.soul(a)); + var a = gun.back(-1)._.graph[Gun.val.link.is(val.a)]; + var b = gun.back(-1)._.graph[Gun.val.link.is(val.b)]; + expect(Gun.val.link.is(val.a)).to.be(Gun.node.soul(a)); + expect(Gun.val.link.is(val.b)).to.be(Gun.node.soul(b)); + expect(Gun.val.link.is(a.kid)).to.be(Gun.node.soul(b)); + expect(Gun.val.link.is(b.parent)).to.be(Gun.node.soul(a)); done(); },10); }); @@ -5526,7 +5510,7 @@ describe('Gun', function(){ lol: true } } - }).path('foo.bar.lol').val(function(val){ + }).path('foo.bar.lol').once(function(val){ expect(val).to.be(true); done(); }); @@ -5539,7 +5523,7 @@ describe('Gun', function(){ lol: {ok: true} } } - }).path('foo.bar.lol').val(function(val){ + }).path('foo.bar.lol').once(function(val){ expect(val.ok).to.be(true); done(); }); @@ -5568,20 +5552,20 @@ describe('Gun', function(){ //console.debug.i=1;console.log("------------"); gun.put(mark, function(err, ok){ expect(err).to.not.be.ok(); - }).val(function(val){ + }).once(function(val){ expect(val.age).to.be(23); expect(val.name).to.be("Mark Nadal"); - expect(Gun.val.rel.is(val.wife)).to.be.ok(); - expect(Gun.val.rel.is(val.pet)).to.be.ok(); - }).path('wife.pet.name').val(function(val){ + expect(Gun.val.link.is(val.wife)).to.be.ok(); + expect(Gun.val.link.is(val.pet)).to.be.ok(); + }).path('wife.pet.name').once(function(val){ //console.debug(1, "*****************", val); expect(val).to.be('Hobbes'); - }).back().path('pet.master').val(function(val){ + }).back().path('pet.master').once(function(val){ //console.log("*****************", val); expect(val.name).to.be("Amber Nadal"); expect(val.phd).to.be.ok(); expect(val.age).to.be(23); - expect(Gun.val.rel.is(val.pet)).to.be.ok(); + expect(Gun.val.link.is(val.pet)).to.be.ok(); done(); }); }); @@ -5598,14 +5582,14 @@ describe('Gun', function(){ it('put partial sub merge', function(done){ var gun = Gun(); - var mark = gun.put({name: "Mark", wife: { name: "Amber" }}).key('person/mark').val(function(mark){ + var mark = gun.put({name: "Mark", wife: { name: "Amber" }}).key('person/mark').once(function(mark){ //console.log("VAL1", mark); done.marksoul = Gun.node.soul(mark); expect(mark.name).to.be("Mark"); }); mark.put({age: 23, wife: {age: 23}}); setTimeout(function(){ - mark.put({citizen: "USA", wife: {citizen: "USA"}}).val(function(mark){ + mark.put({citizen: "USA", wife: {citizen: "USA"}}).once(function(mark){ //console.log("VAL2", mark, gun); expect(mark.name).to.be("Mark"); expect(mark.age).to.be(23); @@ -5625,10 +5609,10 @@ describe('Gun', function(){ it('path path', function(done){ var deep = gun.put({some: {deeply: {nested: 'value'}}}); - deep.path('some.deeply.nested').val(function(val){ + deep.path('some.deeply.nested').once(function(val){ expect(val).to.be('value'); }); - deep.path('some').path('deeply').path('nested').val(function(val){ + deep.path('some').path('deeply').path('nested').once(function(val){ expect(val).to.be('value'); done(); }); @@ -5646,7 +5630,7 @@ describe('Gun', function(){ var gun = Gun(); var fo = gun.put({fo: 'bar'}); Gun.log.ba = 1; - fo.put({ba: {}}).val(function(obj, field){ + fo.put({ba: {}}).once(function(obj, field){ c += 1; expect(c).to.be(1); done(); @@ -5659,7 +5643,7 @@ describe('Gun', function(){ describe('random', function(){ var foo; it('context null put node', function(done){ - foo = gun.put({foo: 'bar'}).val(function(obj){ + foo = gun.put({foo: 'bar'}).once(function(obj){ expect(obj.foo).to.be('bar'); done(); //setTimeout(function(){ done() },1); }); @@ -5675,10 +5659,10 @@ describe('Gun', function(){ it('context node put node', function(done){ // EFFECTIVELY a TIMEOUT from the previous test. NO LONGER! - foo.put({bar: {zoo: 'who'}}).val(function(obj, field){ + foo.put({bar: {zoo: 'who'}}).once(function(obj, field){ //console.log("terribly terrilby unpleasant", field, obj); expect(obj.foo).to.be('bar'); - expect(Gun.val.rel.is(obj.bar)).to.ok(); + expect(Gun.val.link.is(obj.bar)).to.ok(); done(); //setTimeout(function(){ done() },1); }); }); @@ -5688,7 +5672,7 @@ describe('Gun', function(){ // EFFECTIVELY a TIMEOUT from the previous test. NO LONGER! bar = foo.path('bar'); expect(gleak.check()).to.not.be.ok(); - bar.put({combo: 'double'}).val(function(obj, field){ + bar.put({combo: 'double'}).once(function(obj, field){ //expect(obj.zoo).to.be('who'); expect(obj.combo).to.be('double'); done(); //setTimeout(function(){ done() },1); @@ -5698,7 +5682,7 @@ describe('Gun', function(){ it('context node and field put value', function(done){ // EFFECTIVELY a TIMEOUT from the previous test. NO LONGER! var tar = foo.path('tar'); - tar.put('zebra').val(function(val){ + tar.put('zebra').once(function(val){ expect(val).to.be('zebra'); done(); //setTimeout(function(){ done() },1); }); @@ -5706,12 +5690,12 @@ describe('Gun', function(){ it('context node and field, put node', function(done){ // EFFECTIVELY a TIMEOUT from the previous test. NO LONGER! - bar.path('combo').put({another: 'node'}).val(function(obj){ + bar.path('combo').put({another: 'node'}).once(function(obj){ expect(obj.another).to.be('node'); // double .vals here also RELATED to the #"context no double emit" but because of a faulty .not or .init system. - bar.val(function(node){ - expect(Gun.val.rel.is(node.combo)).to.be.ok(); - expect(Gun.val.rel.is(node.combo)).to.be(Gun.node.soul(obj)); + bar.once(function(node){ + expect(Gun.val.link.is(node.combo)).to.be.ok(); + expect(Gun.val.link.is(node.combo)).to.be(Gun.node.soul(obj)); done(); //setTimeout(function(){ done() },1); }); }); @@ -5724,10 +5708,10 @@ describe('Gun', function(){ var al = gun.put({gender:'m', age:30, name:'alfred'}).key('user/alfred'); var beth = gun.put({gender:'f', age:22, name:'beth'}).key('user/beth'); - al.val(function(a){ + al.once(function(a){ beth.put({friend: a}, function(err, ok){ expect(err).to.not.be.ok(); - }).path('friend').val(function(aa){ + }).path('friend').once(function(aa){ expect(Gun.node.soul(a)).to.be(Gun.node.soul(aa)); done(); }); @@ -5744,7 +5728,7 @@ describe('Gun', function(){ gun.put({gender:'m', age:30, name:'alfred'}).key('user/alfred'); gun.put({gender:'f', age:22, name:'beth' }).key('user/beth'); //gun.get('user/beth').path('friend').put(gun.get('user/alfred')); // ideal format which we have a future test for. - gun.get('user/alfred').val(function(a){ + gun.get('user/alfred').once(function(a){ //console.log("*****", a); //expect(a['_']['key']).to.be.ok(); gun.get('user/beth').put({friend: a}, function(err, ok){ // b - friend_of -> a @@ -5753,13 +5737,13 @@ describe('Gun', function(){ var c = soulnode(gun, keynode), soul = c[0]; expect(c.length).to.be(1); }); - gun.get('user/beth').val(function(b){ + gun.get('user/beth').once(function(b){ //console.log("beth", b); - gun.get('user/alfred').put({friend: b}).val(function(al){ // a - friend_of -> b + gun.get('user/alfred').put({friend: b}).once(function(al){ // a - friend_of -> b //console.log("al again", al); - gun.get('user/beth').put({cat: {name: "fluffy", age: 3, coat: "tabby"}}).val(function(bet){ + gun.get('user/beth').put({cat: {name: "fluffy", age: 3, coat: "tabby"}}).once(function(bet){ gun.get('user/alfred').path('friend.cat').key('the/cat'); - gun.get('the/cat').val(function(c){ + gun.get('the/cat').once(function(c){ //console.log("cat!!!", c); expect(c.name).to.be('fluffy'); expect(c.age).to.be(3); @@ -5823,7 +5807,7 @@ describe('Gun', function(){ }}}), soul = Gun.text.random(); gun.get(soul).not(function(err, ok){ done.fail = true; - }).val(function(val){ + }).once(function(val){ setTimeout(function(){ expect(val.a).to.be('b'); expect(val.c).to.be('d'); @@ -5854,7 +5838,7 @@ describe('Gun', function(){ gun.get('me', function(err, data){ - }).val(function(val){ + }).once(function(val){ done.count = (done.count || 0) + 1; setTimeout(function(){ expect(val.a).to.be('b'); @@ -5882,7 +5866,7 @@ describe('Gun', function(){ cb(null, n); }}}), soul = Gun.text.random(); - gun.get(soul).path('a').val(function(val){ + gun.get(soul).path('a').once(function(val){ done.count = (done.count || 0) + 1; setTimeout(function(){ expect(val).to.be('b'); @@ -5900,7 +5884,7 @@ describe('Gun', function(){ setTimeout(function(){ gun.not(function(){ done.not = true; - }).val(function(){ + }).once(function(){ expect(done.not).to.not.be.ok(); done(); }, {empty: true}); @@ -6022,7 +6006,7 @@ describe('Gun', function(){ }); it('instance.val', function(done){ - Gun().val(); + Gun().once(); done(); }); }); @@ -6185,7 +6169,7 @@ describe('Gun', function(){ it('set', function(done){ done.c = 0; var u, gun = Gun(); - gun.get('set').set().set().val(function(val){ + gun.get('set').set().set().once(function(val){ var keynode = gun.__.graph['set']; expect(Gun.node.soul.ify(keynode, '.')).to.be.ok(); Gun.is.node(keynode, function(rel, soul){ @@ -6211,7 +6195,7 @@ describe('Gun', function(){ // TODO: BUG! We need 2 more tests... without .set()... and multiple paths on the same node. it('set multiple', function(done){ // kinda related to flip flop? var gun = Gun().get('sets').set(), i = 0; - gun.val(function(val){ + gun.once(function(val){ console.log("TEST 1", val); expect(Gun.obj.empty(val, Gun._.meta)).to.be.ok(); expect(Gun.node.soul(val)).to.be('sets'); @@ -6220,7 +6204,7 @@ describe('Gun', function(){ }); gun.set(1); //.set(2).set(3).set(4); // if you set an object you'd have to do a `.back` gun.map(function(val, field){ - //gun.map().val(function(val, field){ // TODO: SEAN! DON'T LET ME FORGET! + //gun.map().once(function(val, field){ // TODO: SEAN! DON'T LET ME FORGET! console.log("\n TEST 2+", field, val); return; i += 1; @@ -6244,8 +6228,8 @@ describe('Gun', function(){ users.path(Gun.text.random()).put('bob'); users.path(Gun.text.random()).put('sam'); setTimeout(function(){ - users.val(function(v){ - expect(Gun.val.rel.is(v)).to.not.be.ok(); + users.once(function(v){ + expect(Gun.val.link.is(v)).to.not.be.ok(); expect(Object.keys(v).length).to.be(3); done(); }); @@ -6279,7 +6263,7 @@ describe('Gun', function(){ gun.put({a: 1, z: -1}).key('pseudo'); gun.put({b: 2, z: 0}).key('pseudo'); - gun.get('pseudo').val(function(val){ + gun.get('pseudo').once(function(val){ expect(val.a).to.be(1); expect(val.b).to.be(2); expect(val.z).to.be(0); @@ -6337,14 +6321,14 @@ describe('Gun', function(){ var connect, gun1 = Gun({alice: true}).get('pseudo/merge').put({hello: 'world!'})/*.not(function(key){ this.put({hello: "world!"}).key(key); })*/, gun2; - gun1.val(function(val){ + gun1.once(function(val){ expect(val.hello).to.be('world!'); }); setTimeout(function(){ gun2 = Gun({bob: true}).get('pseudo/merge').put({hi: 'mars!'})/*.not(function(key){ this.put({hi: "mars!"}).key(key); });*/ - gun2.val(function(val){ + gun2.once(function(val){ expect(val.hi).to.be('mars!'); }); setTimeout(function(){ @@ -6355,16 +6339,16 @@ describe('Gun', function(){ //gun1.get('pseudo/merge', null, {force: true}); // fake a browser refersh, in real world we should auto-reconnect //gun2.get('pseudo/merge', null, {force: true}); // fake a browser refersh, in real world we should auto-reconnect setTimeout(function(){ - gun1.val(function(val){ + gun1.once(function(val){ expect(val.hello).to.be('world!'); expect(val.hi).to.be('mars!'); - done.gun1 = true; + done.g1 = true; }); //return; - gun2.val(function(val){ + gun2.once(function(val){ expect(val.hello).to.be('world!'); expect(val.hi).to.be('mars!'); - expect(done.gun1).to.be.ok(); + expect(done.g1).to.be.ok(); Gun({}); done(); }); @@ -6387,8 +6371,8 @@ describe('Gun', function(){ }}).key(key); }); // this is now a list of passengers that we will map over. var ctx = {n: 0, d: 0, l: 0}; - passengers.map().val(function(passenger, id){ - this.map().val(function(change, field){ + passengers.map().once(function(passenger, id){ + this.map().once(function(change, field){ if('name' == field){ expect(change).to.be(passenger.name); ctx.n++ } if('direction' == field){ expect(change).to.be(passenger.direction); ctx.d++ } if('location' == field){ @@ -6429,7 +6413,7 @@ describe('Gun', function(){ list.put({a: {x:1}, b: {y: 1}}); list.path('a').path('w').put(2); var check = {}; - list.map().val(function(v,f){ + list.map().once(function(v,f){ check[f] = v; console.log("*************************", f,v); if(check.a && check.b){ @@ -6445,7 +6429,7 @@ describe('Gun', function(){ var g = Gun(); var list = gun.get('map/sub/val/after'); var check = {}; - list.map().val(function(v,f){ + list.map().once(function(v,f){ check[f] = v; if(check.a && check.b){ setTimeout(function(){ @@ -6467,7 +6451,7 @@ describe('Gun', function(){ var g = Gun(); var list = gun.get('map/sub/val/after/to'); var check = {}; - list.map().val(function(v,f){ + list.map().once(function(v,f){ //console.log("*************", f,v);return; check[f] = v; if(check.a && check.b){ @@ -6488,7 +6472,7 @@ describe('Gun', function(){ var g = Gun(); var list = gun.get('map/simple/after'); var check = {}; - list.map().val(function(v,f){ + list.map().once(function(v,f){ check[f] = v; if(check.a && check.b){ setTimeout(function(){ @@ -6507,7 +6491,7 @@ describe('Gun', function(){ var g = Gun(); var list = gun.get('map/simple/after/to'); var check = {}; - list.map().val(function(v,f){ + list.map().once(function(v,f){ check[f] = v; if(check.a && check.b){ setTimeout(function(){ @@ -6564,7 +6548,7 @@ describe('Gun', function(){ }}).key(key); }); // this is now a list of passengers that we will map over. var ctx = {n: 0, d: 0, l: 0}; - passengers.map().map().val(function(val, field){ + passengers.map().map().once(function(val, field){ if('name' == field){ expect(val).to.be(!ctx.n? 'Bob' : 'Fred'); ctx.n++ } if('direction' == field){ expect(val).to.be(!ctx.d? '128.2' : 'f128.2'); ctx.d++ } if('location' == field){ @@ -6599,7 +6583,7 @@ describe('Gun', function(){ }).key('n/b/l/a/c'); }); var check = {a:{},b:{}}, F = 'a'; - g.map().map().val(function(v,f){ + g.map().map().once(function(v,f){ var c = check[F]; c[f] = v; if(check.b && check.b.x && check.b.y){ @@ -6628,7 +6612,7 @@ describe('Gun', function(){ }).key('n/b/l/a'); }); var check = {}; - g.map().map().val(function(v,f){ + g.map().map().once(function(v,f){ check[f] = v; if(check.x && check.y && check.w && check.u){ expect(check.x).to.be(1); @@ -6648,7 +6632,7 @@ describe('Gun', function(){ var g = gun.get('b/l/a'); g.put({a: {x:1,y:1}}); var check = {}; - g.map().map().val(function(v,f){ + g.map().map().once(function(v,f){ check[f] = v; if(check.x && check.y && check.w && check.u && check.z){ expect(check.x).to.be(1); @@ -6669,7 +6653,7 @@ describe('Gun', function(){ var g = gun.get('b/d/l/a'); g.put({a: {x:1,y:1}}); var check = {}; - g.map().map().val(function(v,f){ + g.map().map().once(function(v,f){ check[f] = v; if(check.x && check.y && check.w && check.u){ expect(check.x).to.be(1); @@ -6709,7 +6693,7 @@ describe('Gun', function(){ } }); var check = {}; - g.map().map().map().map().val(function(v,f){ + g.map().map().map().map().once(function(v,f){ check[f] = (check[f] || 0) + 1; if(check.d === 2 && check.e === 2 && check.f === 2){ done(); @@ -6886,7 +6870,7 @@ describe('Gun', function(){ direction: '128.2' }}).key(key); }); - passengers.map().path('location.lng').val(function(val, field){ + passengers.map().path('location.lng').once(function(val, field){ //passengers.map().path('location.lng').on(function(val, field){ console.log("******", field, val); expect(field).to.be('lng'); @@ -6964,9 +6948,9 @@ describe('Gun', function(){ it("put path deep val -> path val", function(done){ // Terje's bug var gun = Gun(); - gun.put({you: {have: {got: {to: {be: {kidding: "me!"}}}}}}).path('you.have.got.to.be').val(function(val, field){ + gun.put({you: {have: {got: {to: {be: {kidding: "me!"}}}}}}).path('you.have.got.to.be').once(function(val, field){ expect(val.kidding).to.be('me!'); - this.path('kidding').val(function(val){ + this.path('kidding').once(function(val){ expect(val).to.be('me!'); done(); }); @@ -6980,10 +6964,10 @@ describe('Gun', function(){ passengers = passengers.put({randombob: {name: 'Bob', direction: {}}}); passengers.path('randombob.direction', function(err, ok, field){ }).put({lol: {just: 'kidding', dude: '!'}}); - passengers.map().path('direction.lol').val(function(val){ - this.path('just').val(function(val){ + passengers.map().path('direction.lol').once(function(val){ + this.path('just').once(function(val){ expect(val).to.be('kidding'); - }).back().path('dude').val(function(val){ + }).back().path('dude').once(function(val){ expect(val).to.be('!'); done(); }); @@ -7274,13 +7258,13 @@ describe('Gun', function(){ gun._.at('soul').event( //( function($){ - var chain = $.gun || gun; + var chain = $.$ || gun; var ctx = {}, obj = val, $ = Gun.obj.copy($); var hash = $.field? $.soul + $.field : ($.from? $.from + ($.at || '') : $.soul); if(call[hash]){ return } gun.__.meta($.soul).put = true; call[hash] = true; - if(Gun.is.val(obj)){ + if(Gun.is.once(obj)){ if($.from && $.at){ $.soul = $.from; $.field = $.at; @@ -7335,11 +7319,11 @@ describe('Gun', function(){ //console.log("chain.put PUT <----", ify.graph, '\n'); if(err || ify.err){ return cb.call(gun, err || ify.err) } if(err = Gun.union(gun, ify.graph).err){ return cb.call(gun, err) } - if($.from = Gun.val.rel.is(ify.root[$.field])){ $.soul = $.from; $.field = null } + if($.from = Gun.val.link.is(ify.root[$.field])){ $.soul = $.from; $.field = null } Gun.obj.map(ify.graph, function(node, soul){ Gun.union(gun, Gun.union.pseudo(soul)) }); gun._.at('soul').emit({soul: $.soul, field: $.field, key: $.key, PUT: 'SOUL', WAS: 'ON'}); // WAS ON //return cb(null, true); - if(Gun.fns.is(ctx.hook = gun.__.opt.hooks.put)){ + if(Gun.fn.is(ctx.hook = gun.__.opt.hooks.put)){ ctx.hook(ify.graph, function(err, data){ // now iterate through those nodes to a persistence layer and get a callback once all are saved if(err){ return cb.call(gun, err) } return cb.call(gun, null, data); @@ -7496,13 +7480,13 @@ describe('Gun', function(){ } }, function(err,ok){ expect(done.c++).to.be(0); - }).val(function(p){ + }).once(function(p){ done.p = Gun.node.soul(p); - done.m = Gun.val.rel.is(p[0]); - expect(Gun.val.rel.is(p[0])).to.be.ok(); - expect(Gun.val.rel.is(p[1])).to.be.ok(); - expect(Gun.val.rel.is(p[2])).to.be.ok(); - expect(Gun.val.rel.is(p[3])).to.be.ok(); + done.m = Gun.val.link.is(p[0]); + expect(Gun.val.link.is(p[0])).to.be.ok(); + expect(Gun.val.link.is(p[1])).to.be.ok(); + expect(Gun.val.link.is(p[2])).to.be.ok(); + expect(Gun.val.link.is(p[3])).to.be.ok(); }) var players = [], me; @@ -7542,7 +7526,7 @@ describe('Gun', function(){ var u; var gun = Gun(gopt); var game = gun.get('game1/players'); - var me = game.path('player1').val(function(val){ + var me = game.path('player1').once(function(val){ if(!done.c){ done.fail = true } expect(val).to.not.be(u); expect(val.x).to.be(0); @@ -7594,7 +7578,7 @@ describe('Gun', function(){ var u; var gun = Gun(gopt).opt({init: true}); var game = gun.get('game4/players').init(); - var me = game.path('player4').init().path('alias').init().put({oh: 'awesome'}).val(function(val, field){ + var me = game.path('player4').init().path('alias').init().put({oh: 'awesome'}).once(function(val, field){ expect(val.oh).to.be('awesome'); expect(field).to.be('alias'); done(); @@ -7621,7 +7605,7 @@ describe('Gun', function(){ var chat = gun.get('example/chat/data/graph/field').not(function(key){ gun.put({1: {who: 'Welcome', what: "to the chat app!", when: 1}}).key(key); }); - chat.map().val(function renderToDo(val, field){ + chat.map().once(function renderToDo(val, field){ expect(field).to.be.ok(); expect(val.who).to.be.ok(); expect(val.when).to.be.ok(); @@ -7680,12 +7664,12 @@ describe('Gun', function(){ },5); } }} - peers.gun = Gun(gopt); + peers.g = Gun(gopt); function reload(){ - peers.localStorage = Gun.obj.copy(peers.gun.__.graph); - peers.gun2 = Gun(gopt); + peers.localStorage = Gun.obj.copy(peers.g.__.graph); + peers.g2 = Gun(gopt); } - var ref = peers.gun.get('example/json/data/test'); + var ref = peers.g.get('example/json/data/test'); setTimeout(function(){ ref.path('hello').put("value"); setTimeout(function(){ @@ -7693,7 +7677,7 @@ describe('Gun', function(){ reload(); setTimeout(function(){ Gun.log.debug = 1; console.log("~~~~~~~~~~~~~~~~~~~"); - var ref = peers.gun2.get('example/json/data/test'); + var ref = peers.g2.get('example/json/data/test'); ref.on(function(data){ console.log("on!", data); }); @@ -7732,7 +7716,7 @@ describe('Gun', function(){ var chat = gun.get('example/chat/data/graph/field').not(function(key){ gun.put({1: {who: 'Welcome', what: "to the chat app!", when: 1}}).key(key); }); - chat.map().val(function renderToDo(val, field){ + chat.map().once(function renderToDo(val, field){ //console.log("ALICE", field, val); expect(field).to.be.ok(); expect(val.who).to.be.ok(); @@ -7746,7 +7730,7 @@ describe('Gun', function(){ //console.log("BOB's key", key); gun2.put({1: {who: 'Welcome', what: "to the chat app!", when: 1}}).key(key); }); - chat2.map().val(function renderToDo(val, field){ + chat2.map().once(function renderToDo(val, field){ //console.log("BOB", field, val); expect(field).to.be.ok(); expect(val.who).to.be.ok(); @@ -7766,7 +7750,7 @@ describe('Gun', function(){ it("gun set", function(done){ var gun = Gun(); - var users = gun.get('users'); + var users = gun.get('users/s'); var alice = gun.put({name: 'alice', birth: Math.random()}).key('person/alice'); var bob = gun.put({name: 'bob', birth: Math.random()}).key('person/bob'); var carl = gun.put({name: 'carl', birth: Math.random()}).key('person/carl'); @@ -7774,7 +7758,7 @@ describe('Gun', function(){ // Test set with new object var alan = users.set({name: 'alan', birth: Math.random()}).key('person/alan'); - alan.val(function(alan) { + alan.once(function(alan) { // Test set with node dave.path('friends').set(alan); }); @@ -7796,7 +7780,7 @@ describe('Gun', function(){ alice.path('team').put(team); bob.path('team').put(team); - dave.path('friends').map().path('team.members').map().val(function(member){ + dave.path('friends').map().path('team.members').map().once(function(member){ //console.log("Dave's friend is on a team that has", member.name, "on it."); if('alice' === member.name){ done.alice = true; @@ -7837,14 +7821,14 @@ describe('Gun', function(){ it("get context", function(done){ // TODO: HUH?????? This was randomly causing errors? var gun = Gun(); var ref = gun.get('ctx/lol').get('ctx/foo').put({hello: 'world'}); - gun.get('ctx/lol').val(function(implicit){ + gun.get('ctx/lol').once(function(implicit){ done.fail = true; expect(implicit).to.not.be.ok(); }); gun.get('ctx/lol').not(function(){ done.please = true; }); - gun.get('ctx/foo').val(function(data){ + gun.get('ctx/foo').once(function(data){ expect(data.hello).to.be('world'); expect(done.fail).to.not.be.ok(); expect(done.please).to.be.ok(); @@ -7854,9 +7838,9 @@ describe('Gun', function(){ it.skip("chaining val", function(done){ // Not implemented yet! var gun = Gun(); - gun.get('users').set(gun.put({name: 'alice'})); - gun.get('users').set(gun.put({name: 'bob'}));; - gun.get('users').val().map(function(person){ + gun.get('users/cv').set(gun.put({name: 'alice'})); + gun.get('users/cv').set(gun.put({name: 'bob'}));; + gun.get('users/cv').once().map(function(person){ if(person.name === 'alice'){ done.alice = true; } @@ -7867,7 +7851,7 @@ describe('Gun', function(){ done.carl = true; } }); - gun.get('users').set(gun.put({name: 'carl'})); + gun.get('users/cv').set(gun.put({name: 'carl'})); setTimeout(function(){ console.log('wha?', done.alice, done.bob, done.carl); expect(done.alice).to.be.ok(); @@ -7923,12 +7907,12 @@ describe('Gun', function(){ }); setTimeout(function(){ - //list.path('next').val('wat'); + //list.path('next').once('wat'); //console.log("!!!!!!", gun.__.graph); // try to read the third item - list.path('next.to').val(function () { // TODO: BUG! If this is 'next.next' as with the data, then it fails. + list.path('next.to').once(function () { // TODO: BUG! If this is 'next.next' as with the data, then it fails. done(); }); },100); @@ -7982,7 +7966,7 @@ describe('Gun', function(){ BSMI.path(path).put({status:false}); }); setTimeout(function(){ - BSMI.path(allPaths[0]).val(function(a,b,c){ + BSMI.path(allPaths[0]).once(function(a,b,c){ expect(a.a).to.be(1); expect(a.b).to.be(2); expect(a.c).to.be(3); @@ -8012,22 +7996,22 @@ describe('Gun', function(){ it("Don't put on parents", function(done){ // TODO: ADD TO 0.5 BRANCH! // Another Stefdv find. var test = gun.get('test'); test.path('try.this.at.lvl4').put({msg:'hoi'}) - test.val(function(node,b){ + test.once(function(node,b){ delete node._; expect(Gun.obj.empty(node, 'try')).to.be.ok(); - node = Gun.obj.copy(gun.__.graph[Gun.val.rel.is(node.try)]); + node = Gun.obj.copy(gun.__.graph[Gun.val.link.is(node.try)]); delete node._; expect(Gun.obj.empty(node, 'this')).to.be.ok(); - node = Gun.obj.copy(gun.__.graph[Gun.val.rel.is(node.this)]); + node = Gun.obj.copy(gun.__.graph[Gun.val.link.is(node.this)]); delete node._; expect(Gun.obj.empty(node, 'at')).to.be.ok(); - node = Gun.obj.copy(gun.__.graph[Gun.val.rel.is(node.at)]); + node = Gun.obj.copy(gun.__.graph[Gun.val.link.is(node.at)]); delete node._; expect(Gun.obj.empty(node, 'lvl4')).to.be.ok(); - node = Gun.obj.copy(gun.__.graph[Gun.val.rel.is(node.lvl4)]); + node = Gun.obj.copy(gun.__.graph[Gun.val.link.is(node.lvl4)]); delete node._; expect(Gun.obj.empty(node, 'msg')).to.be.ok(); @@ -8048,7 +8032,7 @@ describe('Gun', function(){ var world = 0; player.path("id").put(id); player.path("world_id").put(world); - }).val(function(data){ + }).once(function(data){ //console.log("we have value!", data); expect(done.not).to.be.ok(); expect(data).to.be('fluffy'); @@ -8072,7 +8056,7 @@ describe('Gun', function(){ // 3: bacon // 9: `.not` - depp.path('spouse.pet.name').val().on(log); + depp.path('spouse.pet.name').once().on(log); // 0: fluffy // 1: fluff */ @@ -8113,7 +8097,7 @@ describe('Gun', function(){ ctx.length = i; } ctx.get.fake = Gun.is.node.ify(ctx.get.fake, 'big'); - var big = peer.put(ctx.get.fake).val(function(val){ + var big = peer.put(ctx.get.fake).once(function(val){ ref = val; ctx.get({'#': 'big'}, function(err, graph){ if(Gun.obj.empty(graph)){ done() } @@ -8124,7 +8108,7 @@ describe('Gun', function(){ it('map chain', function(done){ var set = gun.put({a: {here: 'you'}, b: {go: 'dear'}, c: {sir: '!'} }); - set.map().val(function(obj, field){ + set.map().once(function(obj, field){ if(obj.here){ done.a = obj.here; expect(obj.here).to.be('you'); @@ -8153,7 +8137,7 @@ describe('Gun', function(){ pet: {coat: "tux", name: "Casper"} } }); - set.map().path('pet').val(function(obj, field){ + set.map().path('pet').once(function(obj, field){ if(obj.name === 'Hobbes'){ done.hobbes = obj.name; expect(obj.name).to.be('Hobbes'); @@ -8216,7 +8200,7 @@ describe('Gun', function(){ it('get val', function(done){ this.timeout(ctx.gen * ctx.extra); - g().get('big').val(function(obj){ + g().get('big').once(function(obj){ delete obj._; expect(obj.f1).to.be(1); expect(obj['f' + ctx.length]).to.be(ctx.length); @@ -8231,7 +8215,7 @@ describe('Gun', function(){ it('get big map val', function(done){ this.timeout(ctx.gen * ctx.extra); var test = {c: 0, seen: {}}; - g().get('big').map().val(function(val, field){ + g().get('big').map().once(function(val, field){ if(test.seen[field]){ return } test.seen[field] = true; delete val._; @@ -8254,7 +8238,7 @@ describe('Gun', function(){ chat.put({random5: {who: 'mark', what: "5", when: 5}}); var seen = {1: false, 2: false, 3: false, 4: false, 5: false} setTimeout(function(){ - chat.map(function(m){ }).val(function(msg, field){ + chat.map(function(m){ }).once(function(msg, field){ var msg = Gun.obj.copy(msg); if(msg.what){ expect(msg.what).to.be.ok(); diff --git a/test/debug/deep-set.html b/test/debug/deep-set.html index e0a5dade..35e6ec93 100644 --- a/test/debug/deep-set.html +++ b/test/debug/deep-set.html @@ -3,7 +3,7 @@ ;(function(){ //localStorage.clear(); - var gun = window.gun = Gun('http://localhost:8080/gun'); + var gun = window.gun = Gun('http://localhost:8765/gun'); var user = window.user = gun.get('pub/alice'); return; user.put({pub: 'alice'}, write); diff --git a/test/gun.html b/test/gun.html index 47f6f986..0109c8ee 100644 --- a/test/gun.html +++ b/test/gun.html @@ -1,6 +1,5 @@ - \ No newline at end of file diff --git a/test/https/test.js b/test/https/test.js index de78a980..6eb7bf04 100644 --- a/test/https/test.js +++ b/test/https/test.js @@ -1,5 +1,5 @@ module.exports = function(port, file, cb, inject){ - port = port || process.env.OPENSHIFT_NODEJS_PORT || process.env.VCAP_APP_PORT || process.env.PORT || process.argv[2] || 8080; + port = port || process.env.OPENSHIFT_NODEJS_PORT || process.env.VCAP_APP_PORT || process.env.PORT || process.argv[2] || 8765; var fs = require('fs'); var Gun = require(__dirname+'/../../'); diff --git a/test/mocha.html b/test/mocha.html index cc6088d6..229040e6 100644 --- a/test/mocha.html +++ b/test/mocha.html @@ -17,7 +17,49 @@ + + + + + + + + + + - + \ No newline at end of file diff --git a/test/normalize/normalize.html b/test/normalize/normalize.html new file mode 100644 index 00000000..86a09182 --- /dev/null +++ b/test/normalize/normalize.html @@ -0,0 +1,71 @@ + + + + + + + + + +
    the world is a beautiful place.
    +
    The world is a beautiful place.
    +
    + + + + +
    + + + + + \ No newline at end of file diff --git a/test/old/performance.js b/test/old/performance.js new file mode 100644 index 00000000..3243db11 --- /dev/null +++ b/test/old/performance.js @@ -0,0 +1,138 @@ +describe('Performance', function(){ return; // performance tests + var console = root.console || {log: function(){}}; + function perf(fn, i){ + i = i || 1000; + while(--i){ + fn(i); + } + } + perf.now = this.performance? function(){ return performance.now() } : function(){ return Gun.time.now()/1000 }; + (function(){ + var t1 = perf.now(); + var obj = {1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e', 6: 'f', 7: 'g', 8: 'h', 9: 'i'}; + Object.keys && perf(function(){ + var l = Object.keys(obj), ll = l.length, i = 0, s = ''; + for(; i < ll; i++){ + var v = l[i]; + s += v; + } + }); + console.log('map: native', (t1 = (perf.now() - t1)/1000) + 's'); + + var t2 = perf.now(); + var obj = {1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e', 6: 'f', 7: 'g', 8: 'h', 9: 'i'}; + perf(function(){ + var s = ''; + Gun.obj.map(obj, function(v){ + s += v; + }) + }); + console.log('map: gun', (t2 = (perf.now() - t2)/1000) + 's', (t2 / t1).toFixed(1)+'x', 'slower.'); + }()); + (function(){ + if(!Gun.store){ + var tab = Gun().tab; + if(!tab){ return } + Gun.store = tab.store; + } + root.localStorage && root.localStorage.clear(); + var it = 1000; + var t1 = perf.now(); + perf(function(i){ + var obj = {'i': i, 'v': Gun.text.random(100)}; + Gun.store.put('test/native/' + i, obj); + }, it); + console.log('store: native', (t1 = (perf.now() - t1)/1000) + 's'); + + root.localStorage && root.localStorage.clear(); + var gun = Gun({wire: {get:function(l,cb){cb()},put:function(g,cb){ + Gun.is.graph(g, function(node, soul){ + Gun.store.put(soul, node); + }); + cb(null); + }}}); + var t2 = perf.now(); + perf(function(i){ + var obj = {'i': i, 'v': Gun.text.random(100)}; + gun.put(obj); + }, it); + console.log('store: gun', (t2 = (perf.now() - t2)/1000) + 's', (t2 / t1).toFixed(1)+'x', 'slower.'); + root.localStorage && root.localStorage.clear(); + }()); + (function(){ // setTimeout + if(!Gun.store){ + var tab = Gun().tab; + if(!tab){ return } + Gun.store = tab.store; + } + root.localStorage && root.localStorage.clear(); + var t1 = perf.now(); + i = i || 1000; + while(--i){ + var obj = {'i': i, 'v': Gun.text.random(100)}; + Gun.store.put('test/native/' + i, obj); + } + console.log('store: native', (t1 = (perf.now() - t1)/1000) + 's'); + + root.localStorage && root.localStorage.clear(); + var gun = Gun({wire: {get:function(l,cb){cb()},put:function(g,cb){ + Gun.is.graph(g, function(node, soul){ + Gun.store.put(soul, node); + }); + cb(null); + }}}); + var t2 = perf.now(); + perf(function(i){ + var obj = {'i': i, 'v': Gun.text.random(100)}; + gun.put(obj); + }, it); + console.log('store: gun', (t2 = (perf.now() - t2)/1000) + 's', (t2 / t1).toFixed(1)+'x', 'slower.'); + root.localStorage && root.localStorage.clear(); + }()); + (function(){ + var t1 = perf.now(); + var on = Gun.on.create(), c = 0, o = []; + perf(function(i){ + o.push(function(n){ + c += 1; + }); + var ii = 0, l = o.length; + for(; ii < l; ii++){ + o[ii](i); + } + }); + console.log('on: native', (t1 = (perf.now() - t1)/1000) + 's'); + + var on = Gun.on.create(), c = 0; + var t2 = perf.now(); + perf(function(i){ + on('change').event(function(n){ + c += 1; + }); + on('change').emit(i); + }); + console.log('on: gun', (t2 = (perf.now() - t2)/1000) + 's', (t2 / t1).toFixed(1)+'x', 'slower.'); + }());return; + (function(){ // always do this last! + var t1 = perf.now(); + perf(function(i){ + setTimeout(function(){ + if(i === 1){ + cb1(); + } + },0); + }); var cb1 = function(){ + console.log('setTimeout: native', (t1 = (perf.now() - t1)/1000) + 's', (t1 / t2).toFixed(1)+'x', 'slower.'); + } + var t2 = perf.now(); + perf(function(i){ + setImmediate(function(){ + if(i === 1){ + cb2(); + } + }); + }); var cb2 = function(){ + console.log('setImmediate: gun', (t2 = (perf.now() - t2)/1000) + 's', (t2 / t1).toFixed(1)+'x', 'slower.'); + } + }()); +}); \ No newline at end of file diff --git a/test/panic/b2s2s2b.js b/test/panic/b2s2s2b.js index 4f9b0778..1b30bcda 100644 --- a/test/panic/b2s2s2b.js +++ b/test/panic/b2s2s2b.js @@ -1,11 +1,12 @@ var config = { IP: require('ip').address(), - port: 8080, + port: 8765, servers: 2, browsers: 2, - each: 12000, - burst: 1000, + each: 500, + burst: 50, wait: 1, + notrad: true, route: { '/': __dirname + '/index.html', '/gun.js': __dirname + '/../../gun.js', @@ -62,7 +63,7 @@ describe("Load test "+ config.browsers +" browser(s) across "+ config.servers +" } } console.log(port, " connect to ", peers); - var gun = Gun({file: env.i+'data', peers: peers, web: server, localStorage: false}); + var gun = Gun({file: env.i+'data', peers: peers, web: server, localStorage: env.config.notrad}); server.listen(port, function(){ test.done(); }); @@ -96,7 +97,7 @@ describe("Load test "+ config.browsers +" browser(s) across "+ config.servers +" }); browsers.each(function(client, id){ tests.push(client.run(function(test){ - Gun.state.drift = Math.random() * 10000; + //Gun.state.drift = Math.random() * 10000; localStorage.clear(); var env = test.props; test.async(); diff --git a/test/panic/curl-server.js b/test/panic/curl-server.js index 0397b96b..e45051da 100644 --- a/test/panic/curl-server.js +++ b/test/panic/curl-server.js @@ -1,6 +1,6 @@ var config = { IP: require('ip').address(), - port: 8080, + port: 8765, servers: 2, dir: __dirname } diff --git a/test/panic/e2e/distributed.js b/test/panic/e2e/distributed.js index 4005787f..0ec86045 100644 --- a/test/panic/e2e/distributed.js +++ b/test/panic/e2e/distributed.js @@ -22,17 +22,17 @@ describe('PANIC!', function(){ panic.server(server); gun.wsp(server); - server.listen(8080); + server.listen(8765); var clients = panic.clients; var wd = require('selenium-webdriver'); var ff1 = new wd.Builder() .forBrowser('firefox').build() - .get('http://localhost:8080/panic.html'); + .get('http://localhost:8765/panic.html'); var ff2 = new wd.Builder() .forBrowser('firefox').build() - .get('http://localhost:8080/panic.html'); + .get('http://localhost:8765/panic.html'); function min(n, done, list){ list = list || clients; diff --git a/test/panic/e2e/holy/grail.js b/test/panic/e2e/holy/grail.js index eb49f46f..7adac50d 100644 --- a/test/panic/e2e/holy/grail.js +++ b/test/panic/e2e/holy/grail.js @@ -47,7 +47,7 @@ var bob = browsers.excluding(alice).pluck(1); var serverPath = path.join(__dirname, 'gun-server.js'); -// start the server on :8080 +// start the server on :8765 spawn('node', [serverPath]); function waitFor (num, list) { diff --git a/test/panic/e2e/holy/index.html b/test/panic/e2e/holy/index.html index 5a8a87bb..6cf66963 100644 --- a/test/panic/e2e/holy/index.html +++ b/test/panic/e2e/holy/index.html @@ -7,7 +7,7 @@ - + + + + + + + + + \ No newline at end of file diff --git a/test/ptsd/radix.js b/test/ptsd/radix.js new file mode 100644 index 00000000..81a8227d --- /dev/null +++ b/test/ptsd/radix.js @@ -0,0 +1,73 @@ +;(function(){ + // Performance Testing Stress Development + // Performance Testing Style Development + // Performance Testing Speed Development + // Performance Testing Superior Development + // Performance Testing Snippet Development + // Performance Testing Skilled Development + // Performance Testing Steady Development + // Performance Testing Stepwise Development + // Performance Testing Strong Development + // Performance Testing Specified Development + // Performance Testing Stipulated Development + // Performance Testing Systematic Development + if(!this.stool){ return } + setTimeout(function(){ + stool.run(); + },1); + stool.setup(window.setup = function(){ + window.BigText = Gun.text.random(1024, 'abcdef'); + window.MedText = Gun.text.random(200, 'abcdef'); + window.jsonText = JSON.stringify(window.BigText); + window.radText = Radisk.encode(window.BigText); + }); + stool.add('JSON encode string', function(){ + JSON.stringify(window.BigText); + }); + stool.add('RAD encode string', function(){ + Radisk.encode(window.BigText); + }); + stool.add('JSON decode string', function(){ + JSON.parse(window.jsonText); + }); + stool.add('RAD decode string', function(){ + Radisk.decode(window.radText); + }); + return; + stool.add('JSON null', function(){ + JSON.parse(JSON.stringify(null)); + }); + stool.add('RAD null', function(){ + Radisk.decode(Radisk.encode(null)); + }); + stool.add('JSON false', function(){ + JSON.parse(JSON.stringify(false)); + }); + stool.add('RAD false', function(){ + Radisk.decode(Radisk.encode(false)); + }); + stool.add('JSON true', function(){ + JSON.parse(JSON.stringify(true)); + }); + stool.add('RAD true', function(){ + Radisk.decode(Radisk.encode(true)); + }); + stool.add('JSON number', function(){ + JSON.parse(JSON.stringify(23)); + }); + stool.add('RAD number', function(){ + Radisk.decode(Radisk.encode(23)); + }); + stool.add('JSON text', function(){ + JSON.parse(JSON.stringify("hello world")); + }); + stool.add('RAD text', function(){ + Radisk.decode(Radisk.encode("hello world")); + }); + stool.add('JSON text big', function(){ + JSON.parse(JSON.stringify(window.BigText)); + }); + stool.add('RAD text big', function(){ + Radisk.decode(Radisk.encode(window.BigText)); + }); +}()); \ No newline at end of file diff --git a/test/ptsd/spam.js b/test/ptsd/spam.js index 89745a5b..4e51a0a3 100644 --- a/test/ptsd/spam.js +++ b/test/ptsd/spam.js @@ -24,7 +24,7 @@ var t; } }()); -var gun = Gun({localStorage: false, peers: 'http://localhost:8080/gun'}); +var gun = Gun({localStorage: false, peers: 'http://localhost:8765/gun'}); var g = gun.get('test'); var room = Gun.text.random(100); var pub = Gun.text.random(1000); diff --git a/test/rad/browser.html b/test/rad/browser.html new file mode 100644 index 00000000..d864fa0e --- /dev/null +++ b/test/rad/browser.html @@ -0,0 +1,102 @@ + + +

    RindexedDB

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

    RindexedDB

    + + + + + + + + diff --git a/test/tmp/mitra/client_to_server.js b/test/tmp/mitra/client_to_server.js new file mode 100644 index 00000000..c6adacd3 --- /dev/null +++ b/test/tmp/mitra/client_to_server.js @@ -0,0 +1,6 @@ +process.env.GUN_ENV = "false"; +var Gun=require('gun') +var g = new Gun({peers: ['http://localhost:4246/gun'],localStorage: false}); +g.get("FOOxx").get("BARxx").once(data=>console.log("RCVD: (SHOULD NOT BE UNDEFINED!)", data)); + +console.log('now run ```var g = new Gun({peers: ["http://localhost:4246/gun"],localStorage: false}); g.get("FOOxx").get("BARxx").once(data=>console.log("RCVD:", data))```'); \ No newline at end of file diff --git a/test/tmp/mitra/gun_https2.js b/test/tmp/mitra/gun_https2.js new file mode 100644 index 00000000..b1497f81 --- /dev/null +++ b/test/tmp/mitra/gun_https2.js @@ -0,0 +1,103 @@ +//var port = process.env.OPENSHIFT_NODEJS_PORT || process.env.VCAP_APP_PORT || process.env.PORT || process.argv[2] || 8080; + +const port = 4246; + +const https = require('https'); +const http = require('http'); +const fs = require('fs'); +process.env.GUN_ENV = "false"; +const Gun = require('gun'); +const path = require('path'); + +const usehttps = false; + +const options = + usehttps ? { + key: fs.readFileSync('/etc/letsencrypt/live/dweb.me/privkey.pem'), + cert: fs.readFileSync('/etc/letsencrypt/live/dweb.me/fullchain.pem'), + } : {}; +var h = usehttps ? https : http +//var server = h.createServer(options, (req, res) => { +var server = h.createServer((req, res) => { + if(Gun.serve(req, res)){ return } // filters gun requests! + res.writeHead(200); + res.end('go away - nothing for browsers here\n'); + + /* + fs.createReadStream(path.join(__dirname, req.url)) + .on('error',function(){ // static files! + res.writeHead(200, {'Content-Type': 'text/html'}); + res.end(fs.readFileSync(path.join(__dirname, 'index.html'))); // or default to index + }) + .pipe(res); // stream + */ +}); + + +//TODO-GUN put this into a seperate require +function hijack(cb) { + /* Intercept outgoing message and replace result with + result from cb({soul, key, msg, original}) + */ + + Gun.on('opt', function (root) { + console.log("GUN: Hikacking loading trap"); + if (root.once) { + return + } + root.on('out', function (msg) { + console.log("GUN: Hikacking starting outgoing message=", msg); + let to = this.to; + // TODO-GUN - this wont work when running locally in a script ONLY when running in server + if(msg['@'] && !msg.put) { + console.log("GUN: Hikacking outgoing message", msg); + setTimeout(function(){ // TODO-GUN its unclear why this timeout is here, other than for testing + let tmp = root.dup.s[msg['@']]; + let original = tmp && tmp.it && tmp.it.get; + console.log("GUN: Hikacking outgoing message original=", original); + if (original) { + let soul = original['#']; + let key = original['.']; + console.log("GUN.hijack: soul=",soul,"key=", key); + let res; + try { + //TODO - this res now has to be async + res = cb({soul, key, msg, original}); // Note response can be undefined if error + } catch(err) { + console.warn("Gun.hijack callback error",err); + res = undefined; + } + msg.put = { + [soul]: { + _: { + '#': soul, + '>': {[key]: Gun.state()} + }, + [key]: res // Note undefined should (hopefully) be a valid response + } }; + console.log("GUN.hijack updated msg =", msg); + // NOTE: this doesn't necessarily save it back to + // this peers GUN data, (I (Mitra) thinks that may depend on other processes and order of Gun.on) + } + to.next(msg); + }, 100); //Just for testing and note that its async + } else { + to.next(msg); // pass to next middleware + } + }); + this.to.next(root); // This is next for the Gun.on('opt'), not for the root.on('out') + }); +} +hijack(function({soul=undefined, key=undefined, msg=undefined, original=undefined}={}) { + console.log("GUN: hijack testing", soul, key, msg, original); + return ("GUN: This is a test result"); +}); + +var gun = new Gun({ + web: server +}); + + +server.listen(port); + +console.log(usehttps ? "HTTPS" : "HTTP", 'Server started on port ' + port + ' with /gun'); diff --git a/test/tmp/radisk.html b/test/tmp/radisk.html new file mode 100644 index 00000000..d2142a38 --- /dev/null +++ b/test/tmp/radisk.html @@ -0,0 +1,49 @@ +

    Radisk

    + + + + + + + + + +

    + + + + \ No newline at end of file diff --git a/test/say.html b/test/tmp/say.html similarity index 96% rename from test/say.html rename to test/tmp/say.html index 316e6fb6..27d1ccb8 100644 --- a/test/say.html +++ b/test/tmp/say.html @@ -15,7 +15,7 @@ var no; Gun.node.is(at.put.users, function(val, key){ Gun.SEA.read(val, false, function(val){ - if('alias/'+key === Gun.val.rel.is(val)){ return } + if('alias/'+key === Gun.val.link.is(val)){ return } no = true; }) if(no){ return no } diff --git a/test/tmp/seanode.js b/test/tmp/seanode.js new file mode 100644 index 00000000..1a6855f9 --- /dev/null +++ b/test/tmp/seanode.js @@ -0,0 +1,13 @@ +var Gun = require('../../gun.js'); +require('../../sea.js'); + +var pub = "tgEJ8TTeTN8Xi0D3oMVbZGyVVFDGT4AUoqyrUguAguU.9-yZfWtPSZ_4ILnttWy-KWvwUUyO2-dB1DHRYud-CDE"; + +var sig = decodeURIComponent("SEA%7B%22m%22%3A%22%5C%22hello%5C%22%22%2C%22s%22%3A%22%C2%90%C3%B1%C3%93sy%5Cu0011%C3%87%C2%A5%C2%97%C3%93%5Cu0011%C3%A1JV%C2%AA%C3%94C%C3%A3%C3%85%C2%80a%5Cu0006%C2%B6%C2%A7%C3%BE%C2%9F%C2%92%5Cu000eS%C2%90%C3%A8%C3%B1kH%7D%C2%9A%5Cb%C3%B0g%C2%B9%7F%C2%B2%C3%9F%C3%A0j%C3%9Bk%5C%22%3E%C2%B6%C2%8F%5Cu001b%C3%81%C2%8B%C2%97%C3%92%C2%AA%C3%A5%C2%B6%5D%C3%85%C2%9A%3BA%22%7D"); + +console.log(sig); + +;(async function(){ + var test = await Gun.SEA.verify(sig, pub); + console.log("???", test); +}()); \ No newline at end of file diff --git a/test/tmp/space.html b/test/tmp/space.html new file mode 100644 index 00000000..72a7f15e --- /dev/null +++ b/test/tmp/space.html @@ -0,0 +1,50 @@ +

    Search

    + + + + + +
    +
      + +Note: No data is indexed by default, you need to add some! + + + + + + + \ No newline at end of file diff --git a/test/tmp/time.html b/test/tmp/time.html new file mode 100644 index 00000000..4d1c1d49 --- /dev/null +++ b/test/tmp/time.html @@ -0,0 +1,72 @@ +

      Infinite Stream of Tweets/Chats:

      + +
        +
        while in scroll, latest tweet should show here
        +Scroll: + + + + + + + + + + \ No newline at end of file