From 664011be91fcb08684f73fbdb1bc3c91352712b9 Mon Sep 17 00:00:00 2001 From: shankar2105 Date: Wed, 21 Feb 2018 12:52:23 +0530 Subject: [PATCH 1/9] feat/webrtc: implement webrtc signalling Implemented webRTC secure signalling application on SAFE Network. --- safe_webrtc_example/.babelrc | 8 + safe_webrtc_example/.gitignore | 2 + safe_webrtc_example/LICENSE | 21 + safe_webrtc_example/README.md | 34 + safe_webrtc_example/app/appStore.js | 445 ++ safe_webrtc_example/app/components/app.js | 32 + .../app/components/chat_room.js | 285 + safe_webrtc_example/app/components/error.js | 28 + safe_webrtc_example/app/components/home.js | 96 + safe_webrtc_example/app/components/invites.js | 65 + safe_webrtc_example/app/components/loader.js | 20 + .../app/components/new_chat.js | 83 + .../app/components/selected_pub_id.js | 41 + .../app/components/switch_id.js | 108 + safe_webrtc_example/app/constants.js | 74 + safe_webrtc_example/app/images/checked.svg | 1 + safe_webrtc_example/app/images/error.svg | 42 + safe_webrtc_example/app/images/invites.svg | 1 + safe_webrtc_example/app/images/logo.png | Bin 0 -> 105283 bytes safe_webrtc_example/app/images/start-chat.svg | 1 + safe_webrtc_example/app/index.html | 9 + safe_webrtc_example/app/main.js | 29 + safe_webrtc_example/app/router.js | 20 + safe_webrtc_example/app/safe_comm.js | 620 ++ safe_webrtc_example/app/sass/buttons.sass | 29 + safe_webrtc_example/app/sass/chat_room.sass | 72 + safe_webrtc_example/app/sass/common.sass | 36 + safe_webrtc_example/app/sass/components.sass | 224 + safe_webrtc_example/app/sass/loader.sass | 31 + safe_webrtc_example/app/sass/main.sass | 9 + safe_webrtc_example/app/sass/mixins.sass | 15 + safe_webrtc_example/app/sass/variables.sass | 11 + safe_webrtc_example/app/utils.js | 36 + safe_webrtc_example/package.json | 65 + safe_webrtc_example/webpack.dev.config.js | 100 + safe_webrtc_example/webpack.prod.config.js | 124 + safe_webrtc_example/yarn.lock | 5305 +++++++++++++++++ 37 files changed, 8122 insertions(+) create mode 100644 safe_webrtc_example/.babelrc create mode 100644 safe_webrtc_example/.gitignore create mode 100644 safe_webrtc_example/LICENSE create mode 100644 safe_webrtc_example/README.md create mode 100644 safe_webrtc_example/app/appStore.js create mode 100644 safe_webrtc_example/app/components/app.js create mode 100644 safe_webrtc_example/app/components/chat_room.js create mode 100644 safe_webrtc_example/app/components/error.js create mode 100644 safe_webrtc_example/app/components/home.js create mode 100644 safe_webrtc_example/app/components/invites.js create mode 100644 safe_webrtc_example/app/components/loader.js create mode 100644 safe_webrtc_example/app/components/new_chat.js create mode 100644 safe_webrtc_example/app/components/selected_pub_id.js create mode 100644 safe_webrtc_example/app/components/switch_id.js create mode 100644 safe_webrtc_example/app/constants.js create mode 100644 safe_webrtc_example/app/images/checked.svg create mode 100644 safe_webrtc_example/app/images/error.svg create mode 100644 safe_webrtc_example/app/images/invites.svg create mode 100644 safe_webrtc_example/app/images/logo.png create mode 100644 safe_webrtc_example/app/images/start-chat.svg create mode 100644 safe_webrtc_example/app/index.html create mode 100644 safe_webrtc_example/app/main.js create mode 100644 safe_webrtc_example/app/router.js create mode 100644 safe_webrtc_example/app/safe_comm.js create mode 100644 safe_webrtc_example/app/sass/buttons.sass create mode 100644 safe_webrtc_example/app/sass/chat_room.sass create mode 100644 safe_webrtc_example/app/sass/common.sass create mode 100644 safe_webrtc_example/app/sass/components.sass create mode 100644 safe_webrtc_example/app/sass/loader.sass create mode 100644 safe_webrtc_example/app/sass/main.sass create mode 100644 safe_webrtc_example/app/sass/mixins.sass create mode 100644 safe_webrtc_example/app/sass/variables.sass create mode 100644 safe_webrtc_example/app/utils.js create mode 100644 safe_webrtc_example/package.json create mode 100644 safe_webrtc_example/webpack.dev.config.js create mode 100644 safe_webrtc_example/webpack.prod.config.js create mode 100644 safe_webrtc_example/yarn.lock diff --git a/safe_webrtc_example/.babelrc b/safe_webrtc_example/.babelrc new file mode 100644 index 0000000..079c07e --- /dev/null +++ b/safe_webrtc_example/.babelrc @@ -0,0 +1,8 @@ +{ + "presets": [ + "react", + "es2015", + "stage-1" + ], + "plugins": ["transform-decorators-legacy", "transform-object-rest-spread", "transform-async-to-generator"] +} diff --git a/safe_webrtc_example/.gitignore b/safe_webrtc_example/.gitignore new file mode 100644 index 0000000..76add87 --- /dev/null +++ b/safe_webrtc_example/.gitignore @@ -0,0 +1,2 @@ +node_modules +dist \ No newline at end of file diff --git a/safe_webrtc_example/LICENSE b/safe_webrtc_example/LICENSE new file mode 100644 index 0000000..46fcd71 --- /dev/null +++ b/safe_webrtc_example/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 MaidSafe + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/safe_webrtc_example/README.md b/safe_webrtc_example/README.md new file mode 100644 index 0000000..49522cb --- /dev/null +++ b/safe_webrtc_example/README.md @@ -0,0 +1,34 @@ +# SAFE WebRTC Example +Webrtc secure signalling example application on SAFE Network. + +> NOTE: This application is supported only on latest [Peruse](https://github.com/joshuef/peruse/releases/) application. +# Install + +First, clone the repo via git: +``` +$ git clone https://github.com/maidsafe/safe_examples && cd safe_examples/safe_webrtc_example +``` + +And then install Node.js dependencies. + +``` +$ yarn +``` + +# Build + +Bundle the application to host it on SAFE Network: +``` +$ yarn build +``` + +Bundled files are found within `dist` folder. + + +# Run + +To open application on `localhost` run, +``` +$ yarn start +``` +and open `localhost/:8080/` on Peruse application. diff --git a/safe_webrtc_example/app/appStore.js b/safe_webrtc_example/app/appStore.js new file mode 100644 index 0000000..8d661b8 --- /dev/null +++ b/safe_webrtc_example/app/appStore.js @@ -0,0 +1,445 @@ +import { observable, transaction, isObservable, extendObservable, action, autorun, ObservableMap } from 'mobx'; +import 'babel-polyfill'; + +import CONST from './constants'; +import SafeApi from './safe_comm'; + +export default class AppStore { + @observable error = ''; + @observable loading = false; + @observable loaded = false; + @observable loaderDesc = CONST.UI.DEFAULT_LOADING_DESC; + @observable publicNames = []; + @observable invites = []; + @observable newInvites = 0; + @observable selectedPubName = ''; + @observable connectionState = CONST.CONN_STATE.INIT; + @observable isNwConnected = true; + @observable isNwConnecting = false; + + @observable uid = null; + @observable initiater = null; + @observable persona = null; + @observable state = null; + @observable offer = null; + @observable answer = null; + @observable offerCandidates = []; + @observable answerCandidates = []; + @observable remoteOffer = null; + @observable remoteAnswer = null; + @observable remoteOfferCandidates = []; + @observable remoteAnswerCandidates = []; + + constructor() { + this.api = null; + this.isAuthorised = false; + } + + createUid() { + return new Date().getTime(); + } + + transformConnInfo() { + const isCaller = (this.persona === CONST.USER_POSITION.CALLER); + const res = { + state: this.state, + uid: this.uid + }; + const obj = {}; + obj['persona'] = this.persona; + obj['initiater'] = this.initiater; + obj['caller'] = {}; + obj['callee'] = {}; + obj.caller['offer'] = isCaller ? this.offer : this.remoteOffer; + obj.caller['offerCandidates'] = isCaller ? this.offerCandidates : this.remoteOfferCandidates; + obj.caller['answer'] = isCaller ? this.answer : this.remoteAnswer; + obj.caller['answerCandidates'] = isCaller ? this.answerCandidates : this.remoteAnswerCandidates; + obj.callee['offer'] = isCaller ? this.remoteOffer : this.offer; + obj.callee['offerCandidates'] = isCaller ? this.remoteOfferCandidates : this.offerCandidates; + obj.callee['answer'] = isCaller ? this.remoteAnswer : this.answer; + obj.callee['answerCandidates'] = isCaller ? this.remoteAnswerCandidates : this.answerCandidates; + + res.data = obj; + return res; + } + + parseConnStr(connStr) { + const obj = {}; + const parsedObj = JSON.parse(connStr); + obj['state'] = parsedObj.state; + obj['uid'] = parsedObj.uid; + + const data = parsedObj.data; + obj['initiater'] = data.initiater; + obj['caller'] = {}; + obj['callee'] = {}; + obj.caller['offer'] = data.caller.offer; + obj.caller['offerCandidates'] = data.caller.offerCandidates; + obj.caller['answer'] = data.caller.answer; + obj.caller['answerCandidates'] = data.caller.answerCandidates; + obj.callee['offer'] = data.callee.offer; + obj.callee['offerCandidates'] = data.callee.offerCandidates; + obj.callee['answer'] = data.callee.answer; + obj.callee['answerCandidates'] = data.callee.answerCandidates; + return obj; + } + + @action + resetConnInfo() { + this.connectionState = CONST.CONN_STATE.INIT; + this.uid = null; + this.initiater = null; + this.persona = null; + this.state = null; + this.offer = null; + this.answer = null; + this.offerCandidates = []; + this.answerCandidates = []; + this.remoteOffer = null; + this.remoteAnswer = null; + this.remoteOfferCandidates = []; + this.remoteAnswerCandidates = []; + } + + @action + setLoader(state, desc, isloaded) { + this.loaded = !!isloaded; + this.loading = state; + this.loaderDesc = desc || CONST.UI.DEFAULT_LOADING_DESC; + } + + @action + createConn(initiater, persona, uid) { + this.initiater = initiater; + this.uid = uid; + this.persona = persona; + } + + @action + setInitiater(initiater) { + this.initiater = initiater; + } + + @action + setConnState(state) { + this.connectionState = state; + this.state = state; + } + + @action + setRemoteOffer(offer) { + this.remoteOffer = offer; + } + + @action + setRemoteOfferCandidates(candidates) { + this.remoteOfferCandidates = candidates; + } + + @action + setRemoteAnswer(answer) { + this.remoteAnswer = answer; + } + + @action + setRemoteAnswerCandidates(candidates) { + this.remoteAnswerCandidates = candidates; + } + + @action + checkInviteAccepted() { + return new Promise(async (resolve, reject) => { + try { + const connInfo = this.transformConnInfo(); + const connStr = await this.api.fetchConnInfo(connInfo); + const parsedConnInfo = this.parseConnStr(connStr); + if (parsedConnInfo.state === CONST.CONN_STATE.INVITE_ACCEPTED) { + this.setRemoteOffer(parsedConnInfo.callee.offer); + this.setRemoteOfferCandidates(parsedConnInfo.callee.offerCandidates); + this.setRemoteAnswer(parsedConnInfo.callee.answer); + this.setRemoteAnswerCandidates(parsedConnInfo.callee.answerCandidates); + const connInfo1 = this.transformConnInfo(); + return resolve(true); + } + resolve(false); + } catch (err) { + reject(err); + } + }); + } + + @action + checkCallAccepted() { + return new Promise(async (resolve, reject) => { + try { + const connInfo = this.transformConnInfo(); + const connStr = await this.api.fetchConnInfo(connInfo); + const parsedConnInfo = this.parseConnStr(connStr); + if (parsedConnInfo.state === CONST.CONN_STATE.CONNECTED) { + this.connectionState = CONST.CONN_STATE.CONNECTED; + return resolve(true); + } + resolve(false); + } catch (err) { + reject(err); + } + }); + } + + @action + checkForCalling() { + return new Promise(async (resolve, reject) => { + try { + const connInfo = this.transformConnInfo(); + const connStr = await this.api.fetchConnInfo(connInfo, CONST.CONN_STATE.CALLING); + const parsedConnInfo = this.parseConnStr(connStr); + if (parsedConnInfo.state === CONST.CONN_STATE.CALLING) { + this.setRemoteAnswer(parsedConnInfo.caller.answer); + this.setRemoteAnswerCandidates(parsedConnInfo.caller.answerCandidates); + return resolve(true); + } + resolve(false); + } catch (err) { + reject(err); + } + }); + } + + @action + setUid(uid) { + this.uid = uid; + } + + @action + nwStateCb(newState) { + if (newState === CONST.NET_STATE.CONNECTED) { + this.isNwConnected = true; + return; + } + this.isNwConnected = false; + } + + @action + setOffer(offer) { + this.offer = offer; + } + + @action + setAnswer(answer) { + this.answer = answer; + } + + @action + setOfferCandidates(candidates) { + this.offerCandidates = candidates; + } + + @action + setAnswerCandidates(candidates) { + this.answerCandidates = candidates; + } + + @action + authorisation() { + return new Promise(async (resolve, reject) => { + try { + this.setLoader(true, 'Authorising application'); + this.api = new SafeApi(this.nwStateCb); + await this.api.authorise(); + this.isAuthorised = true; + this.setLoader(false); + resolve(true); + } catch (err) { + console.error(`Authorisation error :: ${err}`); + } + }); + } + + @action + fetchPublicNames() { + return new Promise(async (resolve, reject) => { + try { + this.setLoader(true, 'Fetching public names'); + this.publicNames = await this.api.getPublicNames(); + if (this.publicNames.length !== 0 && !this.selectedPubName) { + this.setLoader(true, 'Initializing'); + this.selectedPubName = this.publicNames[0]; + await this.api.setupPublicName(this.selectedPubName); + } + this.setLoader(false); + } catch (err) { + console.error(`Fetch publicNames error :: ${err}`); + } + }); + } + + @action + fetchInvites(isPolling) { + return new Promise(async (resolve, reject) => { + try { + + if (isPolling && !this.isAuthorised) { + return resolve(true); + } + + if (!isPolling) { + this.setLoader(true, 'Fetching invites'); + } + const oldCount = this.invites.length; + this.invites = await this.api.fetchInvites(); + const diff = this.invites.length - oldCount; + if (diff >= 1) { + this.newInvites += diff; + } + this.setLoader(false); + resolve(true); + } catch (err) { + console.error('Fetch invites :: ', err); + reject(err); + } + }); + } + + @action + activatePubName(pubName) { + if (!pubName || !this.publicNames.includes(pubName)) { + return; + } + return new Promise(async (resolve, reject) => { + this.setLoader(true, `Activating selected ${pubName}`); + this.selectedPubName = pubName; + await this.api.setupPublicName(this.selectedPubName); + // reset invite count + this.newInvites = 0; + this.setLoader(false, null, true); + }); + } + + @action + reset() { + this.loaded = false; + this.error = ''; + this.setLoader(false); + } + + @action + initialiseConnInfo(friendID, friendUID) { + return new Promise(async(resolve, reject) => { + try { + if (friendID) { + await this.api.connect(friendID); + } + const isCallee = !!friendID; + const userPosition = isCallee ? CONST.USER_POSITION.CALLEE : CONST.USER_POSITION.CALLER; + const uid = friendUID || this.createUid(); + this.createConn(this.selectedPubName, userPosition, uid); + if (isCallee) { + this.setInitiater(friendID); + const connInfo = this.transformConnInfo(); + const connInfoStr = await this.api.fetchConnInfo(connInfo); + const parsedJson = this.parseConnStr(connInfoStr); + this.setUid(parsedJson.uid); + this.setRemoteOffer(parsedJson.caller.offer); + this.setRemoteOfferCandidates(parsedJson.caller.offerCandidates); + } + resolve(true); + } catch (err) { + console.error('Initialise connInfo error :: ', err); + } + }); + } + + // connInfo with caller offer and offer candidates + @action + sendInvite() { + return new Promise(async (resolve, reject) => { + try { + this.setConnState(CONST.CONN_STATE.SEND_INVITE); + const connInfo = this.transformConnInfo(); + await this.api.sendInvite(connInfo); + resolve(true); + } catch (err) { + console.error('Send invite error :: ', err); + } + }); + } + + // connInfo with caller offer and offer candidates + // and callee offer and offer candidates with answer and answer candidates + @action + acceptInvite() { + return new Promise(async (resolve, reject) => { + try { + this.setConnState(CONST.CONN_STATE.INVITE_ACCEPTED); + const connInfo = this.transformConnInfo(); + await this.api.acceptInvite(connInfo); + resolve(true); + } catch (err) { + console.error('Accept invite error :: ', err); + } + }); + } + + // connInfo with callee answer and candidates + @action + calling() { + return new Promise(async (resolve) => { + try { + this.setConnState(CONST.CONN_STATE.CALLING); + const connInfo = this.transformConnInfo(); + await this.api.calling(connInfo); + resolve(true); + } catch (err) { + console.error('Calling error :: ', err); + } + }); + } + + @action + connect(friendID) { + return new Promise(async (resolve, reject) => { + try { + this.setLoader(true, `Connecting to ${friendID}`); + await this.api.connect(friendID); + this.setLoader(false, null, true); + resolve(true); + } catch (err) { + this.error = new Error('Make sure the Callee has initialised with WebRTC app'); + console.log('Connect error :: ', err); + } + }); + } + + @action + connected() { + return new Promise(async (resolve) => { + try { + this.setConnState(CONST.CONN_STATE.CONNECTED); + const connInfo = this.transformConnInfo(); + await this.api.connected(connInfo); + resolve(true); + } catch (err) { + console.log('Connected error :: ', err); + } + }); + } + + @action + resetFetchCount() { + this.newInvites = 0; + } + + @action + deleteInvite() { + return new Promise(async (resolve, reject) => { + try { + const connInfo = this.transformConnInfo(); + await this.api.deleteInvite(connInfo); + return resolve(true); + } catch (err) { + console.log('Connected error :: ', err); + reject(err); + } + }); + } +} diff --git a/safe_webrtc_example/app/components/app.js b/safe_webrtc_example/app/components/app.js new file mode 100644 index 0000000..8cf8c44 --- /dev/null +++ b/safe_webrtc_example/app/components/app.js @@ -0,0 +1,32 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { inject, observer } from "mobx-react"; + +import WebrtcLogo from '../images/logo.png'; + +@inject("store") +@observer +export default class App extends Component { + componentDidMount() { + this.props.store.authorisation() + .then(() => this.props.store.fetchPublicNames()) + } + + render() { + return ( +
+
+
+
WebRTC logo
+
WebRTC Signalling Example App
+
+
+ {this.props.children} +
+ ); + } +} + +App.propTypes = { + children: PropTypes.element.isRequired, +}; diff --git a/safe_webrtc_example/app/components/chat_room.js b/safe_webrtc_example/app/components/chat_room.js new file mode 100644 index 0000000..2bb03bb --- /dev/null +++ b/safe_webrtc_example/app/components/chat_room.js @@ -0,0 +1,285 @@ +import React, { Component } from 'react'; +import { observable } from 'mobx'; +import { observer, inject } from 'mobx-react'; +import CONST from '../constants'; + +@inject("store") +@observer +export default class ChatRoom extends Component { + @observable originConn = null; + @observable destConn = null; + + constructor() { + super(); + this.friendID = null; + this.friendUID = null; + this.offerOptions = CONST.CONFIG.OFFER; + this.mediaOffer = CONST.CONFIG.MEDIA_OFFER; + this.originStream = null; + this.originCandidates = []; + this.destCandidates = []; + this.onCreateOfferSuccess = this.onCreateOfferSuccess.bind(this); + this.onClickCancel = this.onClickCancel.bind(this); + this.setTimer = this.setTimer.bind(this); + this.timer = null; + } + + componentDidMount() { + this.friendID = this.props.match.params.friendId; + this.friendUID = this.props.match.params.uid; + this.props.store.initialiseConnInfo(this.friendID, this.friendUID); + this.startStream() + .then(() => this.setupOrigin()) + .then(() => this.setupRemote()) + .then(() => { + this.originConn.createOffer(this.offerOptions) + .then(this.onCreateOfferSuccess, (err) => { + console.error('create offer error :: ', err); + }); + }); + } + + componentWillUnmount() { + this.reset(); + } + + setTimer(fn) { + const { store } = this.props; + this.timer = setTimeout(() => { + fn.call(store).then((res) => { + clearTimeout(this.timer); + if (!res) { + this.setTimer(fn); + return; + } + if (store.persona === CONST.USER_POSITION.CALLER && store.remoteOffer) { + this.call(); + } + + if(store.persona === CONST.USER_POSITION.CALLEE && store.remoteAnswer) { + this.finishConnection(); + } + }); + }, CONST.UI.TIMER_INTERVAL.CONNECTION_POLL); + } + + startStream() { + return window.navigator.mediaDevices.getUserMedia(this.mediaOffer) + .then((stream) => { + this.originStream = stream; + this.origin.srcObject = stream; + }); + } + + setupOrigin() { + return new Promise((resolve) => { + this.originConn = new window.RTCPeerConnection(CONST.CONFIG.SERVER); + this.originConn.onicecandidate = (e) => { + if (!e.candidate) { + this.props.store.setOfferCandidates(this.originCandidates); + if (!this.friendID) { + this.props.store.sendInvite() + .then(() => { + this.setTimer(this.props.store.checkInviteAccepted); + }) + } else { + this.call(); + } + return; + } + if (!this.originCandidates) { + this.originCandidates = []; + } + this.originCandidates.push(e.candidate); + }; + + this.originConn.addStream(this.originStream); + resolve(); + }); + } + + setupRemote() { + return new Promise((resolve) => { + this.destConn = new window.RTCPeerConnection(CONST.CONFIG.SERVER); + + this.destConn.onicecandidate = (e) => { + if (!e.candidate) { + this.props.store.setAnswerCandidates(this.destCandidates); + if (!this.friendID) { + this.props.store.calling().then(() => { + this.setTimer(this.props.store.checkCallAccepted); + }); + } else { + this.props.store.acceptInvite().then(() => { + this.setTimer(this.props.store.checkForCalling); + }); + } + return; + } + if (!this.destCandidates) { + this.destCandidates = []; + } + this.destCandidates.push(e.candidate); + }; + + this.destConn.onaddstream = (e) => { + this.destinaton.srcObject = e.stream; + } + resolve(); + }); + } + + call() { + const { store } = this.props; + this.destConn.setRemoteDescription(store.remoteOffer) + .then(() => { + return Promise.all(store.remoteOfferCandidates.map((can) => { + return this.destConn.addIceCandidate(new RTCIceCandidate(can)) + .then(() => { + console.log('set ICE candidate success'); + }, (err) => { + console.error('set ICE candidate failed ::', err); + }); + })); + }, (err) => { + console.error('set destination remote session failed ::', err); + }).then(() => { + this.destConn.createAnswer().then((ansDesc) => { + this.onCreateAnswerSuccess(ansDesc); + }, (err) => { + console.error('create answer error :: ', err); + }); + }); + } + + onCreateOfferSuccess(offer) { + this.originConn.setLocalDescription(offer) + .then(() => { + console.log('set origin local session success'); + return this.props.store.setOffer(offer); + }, (err) => { + console.error('set origin local session failed ::', err); + }); + } + + onCreateAnswerSuccess(answer) { + this.destConn.setLocalDescription(answer) + .then(() => { + return this.props.store.setAnswer(answer); + console.log('set destination local session success'); + }, (err) => { + console.error('set destination local session failed ::', err); + }); + } + + reset() { + clearTimeout(this.timer); + this.props.store.resetConnInfo(); + } + + endCall(e) { + e.preventDefault(); + this.originConn.close(); + this.destConn.close(); + this.originConn = null; + this.destConn = null; + this.reset(); + this.props.history.push('/'); + } + + onClickCancel(e) { + e.preventDefault(); + const self = this; + const moveHome = () => { + console.log('moveHome'); + self.reset(); + self.props.history.push('/'); + }; + this.props.store.deleteInvite() + .then(moveHome, moveHome); + } + + getConnectionStatus() { + let connectionMsg = null; + const { connectionState } = this.props.store; + const { CONN_STATE, UI } = CONST; + const { CONN_MSGS } = UI; + + // FIXME check for not caller persona + if (connectionState === CONN_STATE.CONNECTED) { + this.finishConnection(); + return; + } + + switch (connectionState) { + case CONN_STATE.INIT: + connectionMsg = CONN_MSGS.INIT; + break; + case CONN_STATE.SEND_INVITE: + connectionMsg = CONN_MSGS.SEND_INVITE; + break; + case CONN_STATE.INVITE_ACCEPTED: + connectionMsg = CONN_MSGS.INVITE_ACCEPTED; + break; + case CONN_STATE.CALLING: + connectionMsg = CONN_MSGS.CALLING; + break; + default: + connectionMsg = UI.DEFAULT_LOADING_DESC + } + return ( +
+
+

{connectionMsg}

+
+ +
+
+
+ ); + } + + finishConnection() { + const { store } = this.props; + this.originConn.setRemoteDescription(store.remoteAnswer) + .then(() => { + Promise.all(store.remoteAnswerCandidates.map((can) => { + return this.originConn.addIceCandidate(new RTCIceCandidate(can)) + .then(() => { + console.log('set ICE candidate success'); + }, (err) => { + console.error('set ICE candidate failed ::', err); + }); + })).then(() => { + store.connected(); + }); + }, (err) => { + console.error('set origin remote session failed ::', err); + }); + } + + render() { + const { match } = this.props; + + return ( +
+
+
+ +
+
+ +
+
+ {this.getConnectionStatus()} +
+ +
+
+ ); + } +} diff --git a/safe_webrtc_example/app/components/error.js b/safe_webrtc_example/app/components/error.js new file mode 100644 index 0000000..a6542ce --- /dev/null +++ b/safe_webrtc_example/app/components/error.js @@ -0,0 +1,28 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; + +export default class ErrorComp extends Component { + render() { + return ( +
+
+
+

{this.props.desc}

+
+ +
+
+
+ ); + } +} + +ErrorComp.propTypes = { +}; diff --git a/safe_webrtc_example/app/components/home.js b/safe_webrtc_example/app/components/home.js new file mode 100644 index 0000000..8c3dc0b --- /dev/null +++ b/safe_webrtc_example/app/components/home.js @@ -0,0 +1,96 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { inject, observer } from "mobx-react"; +import CONST from '../constants'; + +import SelectedPubID from './selected_pub_id'; +import Loader from './loader'; + +@inject("store") +@observer +export default class Home extends Component { + constructor() { + super(); + this.timer = null; + } + + componentDidMount() { + this.pollInvite(); + } + + componentWillUnmount() { + clearTimeout(this.timer); + this.timer = null; + this.props.store.reset(); + } + + noPublicNameContainer() { + return ( +
+

No Public Name found. Please create one to start using this application.

+
+ ) + } + + pollInvite() { + const { store } = this.props; + const self = this; + const poll = () => { + clearTimeout(self.timer); + self.pollInvite(); + }; + this.timer = setTimeout(() => { + store.fetchInvites(true).then(poll, poll); + }, CONST.UI.TIMER_INTERVAL.FETCH_INVITES_POLL); + } + + render() { + const { store, history } = this.props; + + if (store.loading) { + return ; + } + + // if no public name found + if (store.publicNames.length === 0) { + return this.noPublicNameContainer(); + } + + return ( +
+ +
+
+
+ +
+
+ +
+
+
+
+ ); + } +} + +Home.propTypes = { +}; diff --git a/safe_webrtc_example/app/components/invites.js b/safe_webrtc_example/app/components/invites.js new file mode 100644 index 0000000..32111fa --- /dev/null +++ b/safe_webrtc_example/app/components/invites.js @@ -0,0 +1,65 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { inject, observer } from "mobx-react"; + +import SelectedPubID from './selected_pub_id'; +import Loader from './loader'; + +@inject("store") +@observer +export default class Invites extends Component { + componentWillMount() { + this.props.store.resetFetchCount(); + this.props.store.fetchInvites(); + } + + onClickInvite(invite) { + console.log('invite selected', invite); + this.props.history.push(`chat-room/${invite.publicId}/${invite.uid}`); + } + + render() { + const { store, history } = this.props; + + if (store.loading) { + return ; + } + + return ( +
+ +
+

You have {store.invites.length} invite(s)

+
+ { + store.invites.map((invite, i) => { + return ( +
{ + this.onClickInvite(invite); + }} + >{invite.publicId} {invite.uid}
+ ); + }) + } +
+
+ +
+
+
+ ); + } +} + +Invites.propTypes = { +}; diff --git a/safe_webrtc_example/app/components/loader.js b/safe_webrtc_example/app/components/loader.js new file mode 100644 index 0000000..5ce5df2 --- /dev/null +++ b/safe_webrtc_example/app/components/loader.js @@ -0,0 +1,20 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; + +export default class Loader extends Component { + render() { + return ( +
+
+
+
+

{this.props.desc}

+
+
+
+ ); + } +} + +Loader.propTypes = { +}; diff --git a/safe_webrtc_example/app/components/new_chat.js b/safe_webrtc_example/app/components/new_chat.js new file mode 100644 index 0000000..3b73101 --- /dev/null +++ b/safe_webrtc_example/app/components/new_chat.js @@ -0,0 +1,83 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { inject, observer } from "mobx-react"; + +import SelectedPubID from './selected_pub_id'; +import Loader from './loader'; +import Err from './error'; + +@inject("store") +@observer +export default class NewChat extends Component { + componentWillUpdate() { + console.log('new chat loaded', this.props.store.loaded); + // const title = this.title.value; + if (this.props.store.loaded) { + this.props.history.push(`chat-room`); + } + } + + componentDidMount() { + this.friendID.focus(); + } + + onNewChatSubmit(e) { + e.preventDefault(); + console.log('Friend ID', this.friendID.value) + const friendID = this.friendID.value; + // const title = this.title.value; + // if (!friendID || !title) { + // return; + // } + this.props.store.connect(this.friendID.value); + } + + render() { + const { store, history } = this.props; + + if (store.error) { + return { + store.reset(); + }} />; + } + + if (store.loading) { + return ; + } + + return ( +
+ +
+
+

Enter the PublicID you want to chat with

+
+
+
+ {this.friendID = c;}} /> +
+ {/*
+ {this.title = c;}} /> +
*/} +
+ +
+
+
+
+
+
+ ); + } +} + +NewChat.propTypes = { +}; diff --git a/safe_webrtc_example/app/components/selected_pub_id.js b/safe_webrtc_example/app/components/selected_pub_id.js new file mode 100644 index 0000000..4d22934 --- /dev/null +++ b/safe_webrtc_example/app/components/selected_pub_id.js @@ -0,0 +1,41 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { inject, observer } from "mobx-react"; + +export default class SelectedPubID extends Component { + render() { + const backBtn = ( +
+ +
+ ); + + return ( +
+
+

Your Public ID

+

{this.props.pubId}

+ {this.props.showBackBtn ? backBtn : null} +
+ +
+
+
+ ); + } +} + +SelectedPubID.propTypes = { +}; diff --git a/safe_webrtc_example/app/components/switch_id.js b/safe_webrtc_example/app/components/switch_id.js new file mode 100644 index 0000000..3164493 --- /dev/null +++ b/safe_webrtc_example/app/components/switch_id.js @@ -0,0 +1,108 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { inject, observer } from "mobx-react"; +import classNames from 'classnames'; + +import Loader from './loader'; + +@inject("store") +@observer +export default class SwitchID extends Component { + constructor() { + super(); + this.state = { + selectedPubName: '' + }; + } + + componentWillUpdate(nextProps) { + if(this.state.selectedPubName && this.props.store.loaded) { + this.props.history.go(-1); + } + } + + componentWillUnmount() { + this.props.store.reset(); + } + + onClickPubName(pubName) { + if (!pubName) { + return; + } + this.setState({selectedPubName: pubName}); + } + + render() { + const { store, history } = this.props; + + let container = null; + + if (store.loading) { + return ; + } + + return ( +
+
+

Choose a Public ID

+
+ { + store.publicNames.map((pub, i) => { + const lsClass = classNames('id-ls-i', { + checked: pub === (this.state.selectedPubName || store.selectedPubName) + }); + return ( +
{ + this.onClickPubName(pub); + }} + >{pub}
+ ); + }) + } +
+
+ +
+
+

Do you want to activate this Public ID with the WebRTC service?

+
+
+
+
+ +
+
+
+
+
+ ); + } +} + +SwitchID.propTypes = { +}; diff --git a/safe_webrtc_example/app/constants.js b/safe_webrtc_example/app/constants.js new file mode 100644 index 0000000..3f7f0a2 --- /dev/null +++ b/safe_webrtc_example/app/constants.js @@ -0,0 +1,74 @@ +export default { + UI: { + DEFAULT_LOADING_DESC: 'Please wait...', + CONN_MSGS: { + INIT: 'Initialising connection', + SEND_INVITE: 'Invite sent. Wait to remote to accept it', + INVITE_ACCEPTED: 'Invite accepted. Establishing connection with remote', + CALLING: 'Remote accepted the invite. Establishing connection with remote', + }, + CONN_TIMER_INTERVAL: 2000, + TIMER_INTERVAL: { + FETCH_INVITES_POLL: 5000, + CONNECTION_POLL: 4000, + }, + }, + CONFIG: { + SERVER: { + iceServers: [ + { url: 'stun:stun1.l.google.com:19302' }, + { + url: 'turn:numb.viagenie.ca', + credential: 'string21', + username: 'shankar21mail@gmail.com' + }, + ] + }, + OFFER: { + offerToReceiveAudio: 1, + offerToReceiveVideo: 1 + }, + MEDIA_OFFER: { + audio: true, + video: true + }, + }, + USER_POSITION: { + CALLER: 'CALLER', + CALLEE: 'CALLEE', + }, + CONN_STATE: { + INIT: 'INIT', + SEND_INVITE: 'SEND_INVITE', + INVITE_ACCEPTED: 'INVITE_ACCEPTED', + CALLING: 'CALLING', + CONNECTED: 'CONNECTED', + }, + NET_STATE: { + INIT: 'Init', + DISCONNECTED: 'Disconnected', + CONNECTED: 'Connected', + UNKNOWN: 'Unknown', + }, + PERMISSIONS: { + READ: 'Read', + INSERT: 'Insert', + UPDATE: 'Update', + }, + MD_KEY: '@webrtcSignalSample', + SELECTED_PUB_NAME_KEY: 'selected_pub_name', + MD_META_KEY: '_metadata', + TYPE_TAG: { + CHANNEL: 15005, + DNS: 15001, + }, + ERR_CODE: { + NO_SUCH_ENTRY: -106, + }, + CRYPTO_KEYS: { + SEC_SIGN_KEY: '__SEC_SIGN_KEY__', + PUB_SIGN_KEY: '__PUB_SIGN_KEY__', + SEC_ENC_KEY: '__SEC_ENC_KEY__', + PUB_ENC_KEY: '__PUB_ENC_KEY__', + }, +}; diff --git a/safe_webrtc_example/app/images/checked.svg b/safe_webrtc_example/app/images/checked.svg new file mode 100644 index 0000000..e0e5fc2 --- /dev/null +++ b/safe_webrtc_example/app/images/checked.svg @@ -0,0 +1 @@ +Artboard 1 \ No newline at end of file diff --git a/safe_webrtc_example/app/images/error.svg b/safe_webrtc_example/app/images/error.svg new file mode 100644 index 0000000..abe0ff7 --- /dev/null +++ b/safe_webrtc_example/app/images/error.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safe_webrtc_example/app/images/invites.svg b/safe_webrtc_example/app/images/invites.svg new file mode 100644 index 0000000..cc717a6 --- /dev/null +++ b/safe_webrtc_example/app/images/invites.svg @@ -0,0 +1 @@ +Artboard 1 \ No newline at end of file diff --git a/safe_webrtc_example/app/images/logo.png b/safe_webrtc_example/app/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..65e3382b268d6dfe9e8a1c67d527d0865f5af82a GIT binary patch literal 105283 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±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 1 \ No newline at end of file diff --git a/safe_webrtc_example/app/index.html b/safe_webrtc_example/app/index.html new file mode 100644 index 0000000..e55b27f --- /dev/null +++ b/safe_webrtc_example/app/index.html @@ -0,0 +1,9 @@ + + + SAFE WebRTC example + + +
+ + + diff --git a/safe_webrtc_example/app/main.js b/safe_webrtc_example/app/main.js new file mode 100644 index 0000000..129c835 --- /dev/null +++ b/safe_webrtc_example/app/main.js @@ -0,0 +1,29 @@ +import React from 'react'; +import {render} from 'react-dom'; +import createHashHistory from 'history/createHashHistory'; +import { Provider } from 'mobx-react'; +import { RouterStore, syncHistoryWithStore } from 'mobx-react-router'; +import { Router } from 'react-router'; +import AppRouter from './router'; +import AppStore from './appStore'; + +const browserHistory = createHashHistory(); +const routingStore = new RouterStore(); +const appStore = new AppStore(); + + +const stores = { + routing: routingStore, + store: appStore, +}; + +const history = syncHistoryWithStore(browserHistory, routingStore); + +render( + + + + + , + document.getElementById('__WEBRTC_APP__') +); diff --git a/safe_webrtc_example/app/router.js b/safe_webrtc_example/app/router.js new file mode 100644 index 0000000..7797f5e --- /dev/null +++ b/safe_webrtc_example/app/router.js @@ -0,0 +1,20 @@ +import React from 'react'; +import { Switch, Route } from 'react-router'; +import App from './components/app'; +import Home from './components/home'; +import SwitchID from './components/switch_id'; +import Invites from './components/invites'; +import NewChat from './components/new_chat'; +import ChatRoom from './components/chat_room'; + +export default () => ( + + + + + + + + + +); diff --git a/safe_webrtc_example/app/safe_comm.js b/safe_webrtc_example/app/safe_comm.js new file mode 100644 index 0000000..1170994 --- /dev/null +++ b/safe_webrtc_example/app/safe_comm.js @@ -0,0 +1,620 @@ +import CONST from './constants'; +import * as utils from './utils'; + +const DOT = '.'; + +let hostName = window.location.hostname; +if (hostName.split(DOT).length === 1) { + hostName = `www.${hostName}`; +} + +// Authorisation model +const APP = { + info: { + id: 'net.maidsafe.example.webrtc', + name: 'WebRTC example', + vendor: 'MaidSafe.net Ltd', + }, + opts: {}, + containers: { + _publicNames: [ + CONST.PERMISSIONS.READ, + CONST.PERMISSIONS.INSERT, + ], + }, +}; + +const ERROR_MSG = { + ENTRY_NOT_FOUND: 'Core error: Routing client error -> Requested entry not found', +}; + +const keySeparator = '-'; + +export default class SafeApi { + constructor(nwStateCb) { + this.app = null; + this.keys = {}; + this.remoteKeys = {}; + this.pubNameCntr = null; + this.serviceCntr = null; + this.channelMD = null; + this.remoteChannelMD = null; + this.selectedPubName = null; + this.nwStateCb = (newState) => { + nwStateCb(newState); + }; + } + + // set origin keys + _setKeys(keys) { + utils.putLog('set origin keys'); + if (keys) { + utils.putLog('set origin keys as agrument', keys); + this.keys = keys; + return Promise.resolve(true); + } + + return new Promise(async (resolve, reject) => { + if (!this.channelMD) { + return reject(new Error('Channel MD not set')); + } + try { + const pubEncKeyStr = await window.safeMutableData.get(this.channelMD, CONST.CRYPTO_KEYS.PUB_ENC_KEY); + const secEncKeyStr = await window.safeMutableData.get(this.channelMD, CONST.CRYPTO_KEYS.SEC_ENC_KEY); + const decSecEncKey = await window.safeMutableData.decrypt(this.channelMD, secEncKeyStr.buf); + + this.keys[CONST.CRYPTO_KEYS.PUB_ENC_KEY] = await window.safeCrypto.pubEncKeyFromRaw(this.app, pubEncKeyStr.buf); + this.keys[CONST.CRYPTO_KEYS.SEC_ENC_KEY] = await window.safeCrypto.secEncKeyFromRaw(this.app, decSecEncKey); + + utils.putLog('set origin keys from channel container', this.keys); + + resolve(true); + } catch (err) { + utils.putLog('set origin keys error', err); + reject(err); + } + }); + } + + // set remote keys + _setRemoteKeys() { + utils.putLog('set remote keys'); + return new Promise(async (resolve, reject) => { + if (!this.remoteChannelMD) { + return reject(new Error('Channel MD not set')); + } + try { + const pubEncKeyStr = await window.safeMutableData.get(this.remoteChannelMD, CONST.CRYPTO_KEYS.PUB_ENC_KEY); + + this.remoteKeys[CONST.CRYPTO_KEYS.PUB_ENC_KEY] = await window.safeCrypto.pubEncKeyFromRaw(this.app, pubEncKeyStr.buf); + utils.putLog('set remote keys from remote channel', this.remoteKeys); + resolve(true); + } catch (err) { + utils.putLog('set remote keys error', err); + reject(err); + } + }); + } + + // create channel + _createChannel() { + utils.putLog('Create channel'); + return new Promise(async (resolve, reject) => { + try { + if (!this.serviceCntr) { + return reject(new Error('service container handle is empty')); + } + + this.channelMD = await window.safeMutableData.newRandomPrivate(this.app, CONST.TYPE_TAG.CHANNEL); + const keysHandle = {}; + + utils.putLog('Generate Encvryption key pairs'); + utils.putLog('Generate Encvryption key pairs'); + + const encKeyPairHandle = await window.safeCrypto.generateEncKeyPair(this.app); + + // public encryption key + const pubEncKey = await window.safeCryptoEncKeyPair.getPubEncKey(encKeyPairHandle); + keysHandle[CONST.CRYPTO_KEYS.PUB_ENC_KEY] = pubEncKey; + const pubEncKeyRaw = await window.safeCryptoPubEncKey.getRaw(pubEncKey); + const pubEncKeyArr = utils.bufToArr(pubEncKeyRaw.buffer); + + // secret encryption key + const secEncKey = await window.safeCryptoEncKeyPair.getSecEncKey(encKeyPairHandle); + keysHandle[CONST.CRYPTO_KEYS.SEC_ENC_KEY] = secEncKey; + const secEncKeyRaw = await window.safeCryptoSecEncKey.getRaw(secEncKey); + const encSecEncKey = await window.safeMutableData.encryptValue(this.channelMD, secEncKeyRaw.buffer); + const secEncKeyArr = utils.bufToArr(encSecEncKey); + + const entries = {}; + entries[CONST.CRYPTO_KEYS.PUB_ENC_KEY] = pubEncKeyArr; + entries[CONST.CRYPTO_KEYS.SEC_ENC_KEY] = secEncKeyArr; + await window.safeMutableData.quickSetup(this.channelMD, entries, 'WebRTC Channel', `WebRTC channel for ${hostName}`); + + // create a new permission set + const permSet = [CONST.PERMISSIONS.READ, CONST.PERMISSIONS.INSERT]; + await window.safeMutableData.setUserPermissions(this.channelMD, null, permSet, 1); + + const channelSerialData = await window.safeMutableData.serialise(this.channelMD); + const entriesHandle = await window.safeMutableData.getEntries(this.serviceCntr); + const mutationHandle = await window.safeMutableDataEntries.mutate(entriesHandle); + const encryptedKey = await window.safeMutableData.encryptKey(this.serviceCntr, CONST.MD_KEY); + await window.safeMutableDataMutation.insert(mutationHandle, encryptedKey, channelSerialData); + await window.safeMutableData.applyEntriesMutation(this.serviceCntr, mutationHandle); + window.safeMutableDataMutation.free(mutationHandle); + window.safeMutableDataEntries.free(entriesHandle); + this._setKeys(keysHandle); + resolve(true); + } catch (err) { + utils.putLog('Create channel error', err); + reject(err); + } + }); + } + + _getPublicNamesCntr() { + utils.putLog('Get public names container'); + return new Promise(async (resolve, reject) => { + try { + if (!this.app) { + return reject(new Error('Unauthorised')); + } + const hasAccess = await window.safeApp.canAccessContainer(this.app, '_publicNames', APP.containers._publicNames); + if (!hasAccess) { + return reject(new Error('No publicNames container access found')); + } + this.pubNameCntr = await window.safeApp.getContainer(this.app, '_publicNames'); + resolve(true); + } catch (err) { + utils.putLog('Get public names container error', err); + reject(err); + } + }); + } + + _getSelectedPublicName() { + utils.putLog('Select public name'); + return new Promise(async (resolve, reject) => { + try { + if (!this.app) { + return reject(new Error('Unauthorised')); + } + const ownCntr = await window.safeApp.getOwnContainer(this.app); + this.selectedPubName = await window.safeMutableData.get(ownCntr, CONST.SELECTED_PUB_NAME_KEY); + resolve(true); + } catch (err) { + // if (err.code !== CONST.ERR_CODE.NO_SUCH_ENTRY) { + if (err.message !== ERROR_MSG.ENTRY_NOT_FOUND) { + utils.putLog('Select public name error', err); + reject(err); + } + resolve(true); + } + }); + } + + _checkServiceContainerAccess(mdHandle) { + utils.putLog('Check service container access'); + return new Promise(async (resolve, reject) => { + try { + if (!this.serviceCntr) { + return reject(new Error('service container handle is empty')); + } + + const appSignKey = await window.safeCrypto.getAppPubSignKey(this.app); + const result = await window.safeMutableData.getUserPermissions(mdHandle, appSignKey); + + resolve(true); + } catch (err) { + // if (err.code !== -1011) { + if (err.message !== 'Core error: Routing client error -> Key does not exists') { + utils.putLog('Check service container access error', err); + return reject(err); + } + + utils.putLog('Send service container auth request'); + + const serviceCntrInfo = await window.safeMutableData.getNameAndTag(this.serviceCntr); + await window.safeApp.authoriseShareMd(this.app, [ + { + type_tag: serviceCntrInfo.type_tag, + name: serviceCntrInfo.name.buffer, + perms: [CONST.PERMISSIONS.INSERT] + } + ]); + resolve(true); + } + }); + } + + _getDataKey(initiater, state, id) { + utils.putLog('Get data key'); + let dataKey = null; + if (!state) { + dataKey = `${initiater}${keySeparator}${CONST.CONN_STATE.SEND_INVITE}${keySeparator}${id}`; + } else if (state === CONST.CONN_STATE.SEND_INVITE || state === CONST.CONN_STATE.CALLING) { + dataKey = `${initiater}${keySeparator}${state}${keySeparator}${id}`; + } else { + dataKey = `${initiater}${keySeparator}${CONST.CONN_STATE.SEND_INVITE}${keySeparator}${id}`; + if (state === CONST.CONN_STATE.CONNECTED) { + dataKey = `${initiater}${keySeparator}${CONST.CONN_STATE.CALLING}${keySeparator}${id}`; + } + } + utils.putLog('Get data key success', dataKey); + return dataKey; + } + + _isCaller(connInfo) { + return (connInfo.data.persona === CONST.USER_POSITION.CALLER); + } + + _encryptData(data) { + utils.putLog('Encrypt data'); + return new Promise(async (resolve, reject) => { + try { + const encryptedData = await window.safeCryptoPubEncKey.encryptSealed(this.remoteKeys[CONST.CRYPTO_KEYS.PUB_ENC_KEY], data); + resolve(utils.bufToArr(encryptedData)); + } catch(err) { + utils.putLog('Encrypt data error', err); + reject(err); + } + }); + } + + _decryptData(cipher) { + utils.putLog('Decrypt data'); + return new Promise(async (resolve, reject) => { + try { + const rawPubEncKey = await window.safeCryptoPubEncKey.getRaw(this.keys[CONST.CRYPTO_KEYS.PUB_ENC_KEY]); + const rawSecEncKey = await window.safeCryptoSecEncKey.getRaw(this.keys[CONST.CRYPTO_KEYS.SEC_ENC_KEY]); + const encKeyPairHandle = await window.safeCrypto.generateEncKeyPairFromRaw(this.app, rawPubEncKey.buffer, rawSecEncKey.buffer); + const decryptedData = await window.safeCryptoEncKeyPair.decryptSealed( + encKeyPairHandle, + utils.arrToBuf(cipher)); + resolve(utils.parseConnInfo(decryptedData.toString())); + } catch(err) { + utils.putLog('Decrypt data error', err); + reject(err); + } + }); + } + + authorise() { + utils.putLog('Authorise app'); + return new Promise(async (resolve, reject) => { + try { + this.app = await window.safeApp.initialise(APP.info, this.nwStateCb); + const uri = await window.safeApp.authorise(this.app, APP.containers, APP.opts); + await window.safeApp.connectAuthorised(this.app, uri); + await this._getPublicNamesCntr(); + resolve(true); + } catch (err) { + utils.putLog('Authorise app error', err); + reject(err); + } + }); + } + + getPublicNames(publicName) { + utils.putLog('Get public names', publicName); + const decryptKey = (key) => { + return new Promise(async (resolve, reject) => { + try { + const deckey = await window.safeMutableData.decrypt(this.pubNameCntr, key); + resolve(utils.uint8ToStr(deckey)); + } catch (err) { + utils.putLog('Get public names - decrypt keys error', err); + reject(err); + } + }); + }; + + return new Promise(async (resolve, reject) => { + try { + if (!this.pubNameCntr) { + return reject(new Error('_publicnames container handle not set')); + } + const publicNames = []; + const entriesHandle = await window.safeMutableData.getEntries(this.pubNameCntr); + await window.safeMutableDataEntries.forEach(entriesHandle, (k, v) => { + publicNames.push(k); + }); + const encPubNamesQ = []; + for (const i in publicNames) { + encPubNamesQ.push(decryptKey(publicNames[i])); + } + const decPubNames = await Promise.all(encPubNamesQ); + resolve(decPubNames); + } catch (err) { + utils.putLog('Get public names error', err); + reject(err); + } + }); + } + + setupPublicName(pubName) { + utils.putLog('Setup public name', pubName); + return new Promise(async (resolve, reject) => { + try { + if (!this.app) { + return reject(new Error('Unauthorised')); + } + + if (!this.pubNameCntr) { + return reject(new Error('_publicNames container handle is empty')); + } + + // get service container + const encPublicName = await window.safeMutableData.encryptKey(this.pubNameCntr, pubName); + const encServiceCntr = await window.safeMutableData.get(this.pubNameCntr, encPublicName); + const decServiceCntr = await window.safeMutableData.decrypt(this.pubNameCntr, encServiceCntr.buf); + this.serviceCntr = await window.safeMutableData.newPublic(this.app, decServiceCntr, CONST.TYPE_TAG.DNS); + + // check for access + await this._checkServiceContainerAccess(this.serviceCntr); + + try { + // search for webrtc channel + const encChannelKey = await window.safeMutableData.encryptKey(this.serviceCntr, CONST.MD_KEY); + const channelSerial = await window.safeMutableData.get(this.serviceCntr, encChannelKey); + this.channelMD = await window.safeMutableData.fromSerial(this.app, channelSerial.buf); + await this._setKeys(null); + } catch (e) { + + // if (e.code !== CONST.ERR_CODE.NO_SUCH_ENTRY) { + if (e.message !== ERROR_MSG.ENTRY_NOT_FOUND) { + utils.putLog('setup public name - search channel error', e); + throw e; + } + utils.putLog('setup public name - create new channel'); + await this._createChannel(); + } + resolve(true); + } catch (err) { + utils.putLog('Setup public name error', err); + reject(err); + } + }); + } + + fetchInvites() { + utils.putLog('Fetch invites'); + return new Promise(async (resolve, reject) => { + try { + if (!this.channelMD) { + return reject(new Error('channel handle is empty')); + } + const whiteListKeys = [ + CONST.CRYPTO_KEYS.PUB_ENC_KEY, + CONST.CRYPTO_KEYS.SEC_ENC_KEY, + CONST.CRYPTO_KEYS.PUB_ENC_KEY, + CONST.CRYPTO_KEYS.SEC_ENC_KEY, + CONST.MD_META_KEY + ]; + const invites = []; + const entriesHandle = await window.safeMutableData.getEntries(this.channelMD); + await window.safeMutableDataEntries.forEach(entriesHandle, (k, v) => { + const keyStr = k.toString(); + if (!whiteListKeys.includes(keyStr)) { + const dataArr = keyStr.split(keySeparator); + if (dataArr.length == 3 && dataArr[1] === CONST.CONN_STATE.SEND_INVITE && v.buf.length !== 0) { + invites.push({ + publicId: dataArr[0], + uid: dataArr[2] + }); + } + } + }); + utils.putLog('Fetch invites success', invites); + resolve(invites); + } catch (err) { + utils.putLog('Fetch invites error', err); + reject(err); + } + }); + } + + connect(friendID) { + utils.putLog('Connect with friendID', friendID); + return new Promise(async (resolve, reject) => { + try { + if (!friendID) { + return reject(new Error('Friend ID was empty')); + } + const pubNameSha = await window.safeCrypto.sha3Hash(this.app, friendID); + const fdPubNameCntr = await window.safeMutableData.newPublic(this.app, pubNameSha, CONST.TYPE_TAG.DNS); + + const encChannelKey = await window.safeMutableData.encryptKey(fdPubNameCntr, CONST.MD_KEY); + const fdChannelSerial = await window.safeMutableData.get(fdPubNameCntr, encChannelKey); + this.remoteChannelMD = await window.safeMutableData.fromSerial(this.app, fdChannelSerial.buf); + await this._setRemoteKeys(); + resolve(true); + } catch (err) { + utils.putLog('Connect with friendID error', err); + reject(err); + } + }); + } + + putConnInfo(connInfo) { + utils.putLog('Put connection info'); + return new Promise(async (resolve, reject) => { + try { + const isCaller = this._isCaller(connInfo); + const channelMD = isCaller ? this.remoteChannelMD : this.channelMD; + + if (!channelMD) { + return reject(new Error('channel not set')); + } + + const entriesHandle = await window.safeMutableData.getEntries(channelMD); + const mutationHandle = await window.safeMutableDataEntries.mutate(entriesHandle); + + const data = utils.stringifyConnInfo(connInfo.data); + const encData = await this._encryptData(data); + const dataKey = this._getDataKey(connInfo.data.initiater, connInfo.state, connInfo.uid); + + const connInfoStr = utils.stringifyConnInfo({ + state: connInfo.state, + uid: connInfo.uid, + data: encData + }); + + // insert if it is caller + if (isCaller) { + utils.putLog('Insert data', dataKey); + await window.safeMutableDataMutation.insert(mutationHandle, dataKey, connInfoStr); + } else { + utils.putLog('Update data', dataKey); + const connStr = await window.safeMutableData.get(channelMD, dataKey); + await window.safeMutableDataMutation.update(mutationHandle, dataKey, connInfoStr, connStr.version + 1); + } + + await window.safeMutableData.applyEntriesMutation(channelMD, mutationHandle); + window.safeMutableDataMutation.free(mutationHandle); + window.safeMutableDataEntries.free(entriesHandle); + resolve(true); + } catch (err) { + utils.putLog('Put connection info error', err); + reject(err); + } + }); + } + + fetchConnInfo(connInfo, nextState) { + utils.putLog('Fetch connection info', connInfo); + const isCaller = this._isCaller(connInfo); + return new Promise(async (resolve, reject) => { + try { + const channelMD = isCaller ? this.remoteChannelMD : this.channelMD; + if (!channelMD) { + return reject(new Error('channel not set')); + } + + const dataKey = this._getDataKey(connInfo.data.initiater, nextState || connInfo.state, connInfo.uid); + const connStr = await window.safeMutableData.get(channelMD, dataKey); + const parsedConnInfo = utils.parseConnInfo(connStr.buf.toString()); + + if (isCaller && parsedConnInfo.state && (parsedConnInfo.state === CONST.CONN_STATE.SEND_INVITE || parsedConnInfo.state === CONST.CONN_STATE.CALLING)) { + return resolve(utils.stringifyConnInfo(connInfo)); + } + + if (!isCaller && parsedConnInfo.state && (parsedConnInfo.state === CONST.CONN_STATE.INVITE_ACCEPTED || parsedConnInfo.state === CONST.CONN_STATE.CONNECTED)) { + return resolve(utils.stringifyConnInfo(connInfo)); + } + + const decryptedData = await this._decryptData(parsedConnInfo.data) + + resolve(utils.stringifyConnInfo({ + state: parsedConnInfo.state, + uid: parsedConnInfo.uid, + data: decryptedData + })); + } catch (err) { + if (err.message !== ERROR_MSG.ENTRY_NOT_FOUND) { + return reject(err); + } + + if (!isCaller && connInfo.state && (connInfo.state === CONST.CONN_STATE.INVITE_ACCEPTED)) { + return resolve(JSON.stringify(connInfo)); + } + + return reject(err); + } + }); + } + + deleteInvite(connInfo) { + utils.putLog('Delete invite', connInfo); + return new Promise(async (resolve, reject) => { + try { + const isCaller = this._isCaller(connInfo); + if (isCaller) { + return reject(new Error('Restricted access')); + } + if (!this.channelMD) { + return reject(new Error('channel handle not set')); + } + const entriesHandle = await window.safeMutableData.getEntries(this.channelMD); + const mutationHandle = await window.safeMutableDataEntries.mutate(entriesHandle); + + const dataKey = this._getDataKey(connInfo.data.initiater, CONST.CONN_STATE.SEND_INVITE, connInfo.uid); + const connStr = await window.safeMutableData.get(this.channelMD, dataKey); + await window.safeMutableDataMutation.remove(mutationHandle, dataKey, connStr.version + 1); + + await window.safeMutableData.applyEntriesMutation(this.channelMD, mutationHandle); + window.safeMutableDataMutation.free(mutationHandle); + window.safeMutableDataEntries.free(entriesHandle); + resolve(true); + } catch (err) { + utils.putLog('Delete invite error', connInfo) + reject(err); + } + }); + } + + sendInvite(connInfo) { + utils.putLog('Send invite', connInfo); + return new Promise(async (resolve, reject) => { + try { + if (!this.remoteChannelMD) { + return reject(new Error('remote channel not set')); + } + await this.putConnInfo(connInfo); + resolve(true); + } catch (err) { + utils.putLog('Send invite error', connInfo) + reject(err); + } + }); + } + + acceptInvite(connInfo) { + utils.putLog('Accept invite', connInfo) + return new Promise(async (resolve, reject) => { + try { + if (!this.channelMD) { + return reject(new Error('channel handle is empty')); + } + await this.putConnInfo(connInfo); + resolve(true); + } catch (err) { + utils.putLog('Accept invite error', err); + reject(err); + } + }); + } + + calling(connInfo) { + utils.putLog('Calling', connInfo); + return new Promise(async (resolve, reject) => { + try { + if (!this.remoteChannelMD) { + return reject(new Error('channel handle is empty')); + } + await this.putConnInfo(connInfo); + resolve(true); + } catch (err) { + utils.putLog('Calling error', err); + reject(err); + } + }); + } + + connected(connInfo) { + utils.putLog('Connected', connInfo); + return new Promise(async (resolve, reject) => { + try { + if (!this.channelMD) { + return reject(new Error('channel handle is empty')); + } + await this.putConnInfo(connInfo); + await this.deleteInvite(connInfo); + resolve(true); + } catch (err) { + utils.putLog('Connected error', err); + reject(err); + } + }); + } +} diff --git a/safe_webrtc_example/app/sass/buttons.sass b/safe_webrtc_example/app/sass/buttons.sass new file mode 100644 index 0000000..303e9d4 --- /dev/null +++ b/safe_webrtc_example/app/sass/buttons.sass @@ -0,0 +1,29 @@ +.btn + +btnDefault() + line-height: 30px + padding: 0 16px + background-color: $pr-pale-brown-2 + font-weight: 600 + &:hover + background-color: $pr-pale-brown + &.flat + padding: 0 + font-size: 14px + color: $color-common-text + background-color: transparent + &:hover + background-color: transparent + &:disabled + opacity: 0.5 + &:hover + background-color: transparent !important + &.primary + background-color: transparent !important + color: $pr-pale-blue + &:hover + color: $pr-blue + &.primary + background-color: $pr-pale-blue + color: #ffffff + &:hover + background-color: $pr-blue diff --git a/safe_webrtc_example/app/sass/chat_room.sass b/safe_webrtc_example/app/sass/chat_room.sass new file mode 100644 index 0000000..453e04e --- /dev/null +++ b/safe_webrtc_example/app/sass/chat_room.sass @@ -0,0 +1,72 @@ +.chat-room + position: fixed + width: 100% + height: 100% + padding-top: 50px + top: 0 + left: 0 + .chat-room-b + .chat-room-remote + position: fixed + top: 0 + left: 0 + width: 100% + height: 100% + background-color: #f4f4f4 + z-index: 99 + video + width: 100% + height: 100% + .chat-room-origin + position: fixed + top: 50px + right: 50px + width: 300px + z-index: 99 + video + width: 100% + // height: 100% + .chat-room-opts + position: fixed + bottom: 50px + left: 50% + width: auto + transform: translate(-50%, 0) + z-index: 99 + button + width: 50px + border-radius: 50% + height: 50px + line-height: 50px + background-color: $pr-red-dark + color: white + font-size: 14px + font-weight: 600 + border: 0 + cursor: pointer + outline: none + .chat-room-conn-status + position: fixed + bottom: 0 + left: 0 + width: 100% + height: 100% + background-color: rgba(255, 255, 255, 0.5) + z-index: 999 + .chat-room-conn-status-b + width: 400px + padding: 24px + background-color: #ffffff + position: relative + top: 50% + left: 50% + transform: translate(-50%, -50%) + border-radius: 3px + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25) + .status + font-size: 14px + font-weight: normal + text-align: center + padding: 40px 16px + .cancel-btn + text-align: center diff --git a/safe_webrtc_example/app/sass/common.sass b/safe_webrtc_example/app/sass/common.sass new file mode 100644 index 0000000..0c6bac9 --- /dev/null +++ b/safe_webrtc_example/app/sass/common.sass @@ -0,0 +1,36 @@ +* + margin: 0 + padding: 0 + box-sizing: border-box + +body + font-family: 'Open Sans', sans-serif + +.root + .root-b + .root-h + background-color: $bg-header + +.base + width: 960px + margin: 0 auto + padding: 50px 0 + +.brand + padding: 16px 0 + .brand-img + width: 100px + margin: 0 auto + img + width: 100% + .brand-name + font-size: 16px + font-weight: 600 + text-align: center + color: $pr-blue + +.no-pub-name + text-align: center + font-size: 16px + font-weight: 600 + color: #808080 diff --git a/safe_webrtc_example/app/sass/components.sass b/safe_webrtc_example/app/sass/components.sass new file mode 100644 index 0000000..9944776 --- /dev/null +++ b/safe_webrtc_example/app/sass/components.sass @@ -0,0 +1,224 @@ +.selected-pub-id + padding: 50px 0 + text-align: center + position: relative + .selected-pub-id-b + position: relative + display: inline-block + min-width: 600px + h3 + font-size: 18px + font-weight: normal + color: $color-common-text + h4 + font-size: 20px + font-weight: normal + color: $color-common-text + margin-top: 16px + .switch-btn + position: absolute + top: 0 + right: 0 + .back-btn + position: absolute + top: 0 + left: 0 + +.switch-id + width: 600px + position: relative + padding: 50px 0 + margin: 0 auto + .title + font-size: 18px + font-weight: normal + color: $color-common-text + text-align: center + .id-ls + width: 400px + margin: 0 auto + padding: 40px 0 0 + .id-ls-i + padding: 0 36px + font-size: 14px + font-weight: normal + line-height: 36px + cursor: pointer + position: relative + &:after + content: "" + display: none + width: 30px + height: 30px + position: absolute + top: 3px + left: 0 + background-image: url('../images/checked.svg') + background-position: center + background-repeat: no-repeat + background-size: 16px + &.checked:after + display: inline-block + &:hover + background-color: $pr-pale-blue-2 + .refresh-btn + position: absolute + top: 50px + right: 0 + .ack + text-align: center + margin: 80px 0 0 + p + font-size: 18px + font-weight: normal + color: $color-common-text + line-height: 24px + .opts + margin: 40px 0 0 + .opt + display: inline-block + margin: 0 8px +.choose-chat + text-align: center + margin-top: 50px + .choose-chat-b + display: inline-block + position: relative + &:after + content: "" + display: inline-block + height: 50px + border: 1px solid #dddddd + position: absolute + top: 50% + left: 50% + transform: translate(-50%, -50%) + .choose-chat-i + display: inline-block + width: 200px + height: 200px + background-color: $pr-pale-blue-2 + margin: 0 25px + button + width: 100% + height: 100% + background-color: transparent + .icn + display: block + width: 30px + height: 30px + margin: 0 auto + background-position: center + background-size: 30px + background-repeat: no-repeat + &.invites + .icn + background-image: url('../images/invites.svg') + &.new-chat + .icn + background-image: url('../images/start-chat.svg') + &:hover + background-color: $pr-pale-blue + button + color: #ffffff + &.no-invites + .choose-chat-b + &:after + display: none + .choose-chat-i + &.invites + display: none + +.invites + width: 600px + position: relative + padding: 50px 0 + margin: 0 auto + position: relative + .title + font-size: 18px + font-weight: normal + color: $color-common-text + text-align: center + .invites-ls + width: 400px + margin: 0 auto + padding: 40px 0 0 + .invites-ls-i + text-align: center + font-size: 14px + font-weight: normal + line-height: 36px + cursor: pointer + position: relative + &:hover + background-color: $pr-pale-blue-2 + .refresh-btn + position: absolute + top: 50px + right: 0 + +.new-chat + .title + font-size: 18px + font-weight: normal + color: $color-common-text + text-align: center + .new-chat-form + width: 80% + padding: 25px 0 0 + margin: 0 auto + .inpt + input + width: 100% + line-height: 30px + font-size: 14px + padding: 0 8px + .inpt-btn + margin-top: 50px + text-align: center + +.card + width: 600px + position: relative + margin: 0 auto + &.margin-top + margin-top: 100px + .card-b + background-color: $pr-pale-blue-2 + padding: 50px 0 + border-radius: 10px + &.danger + background-color: $pr-red + +.loading + .icn + width: 50px + height: 50px + margin: 0 auto + .desc + font-size: 14px + font-weight: normal + color: $color-common-text + text-align: center + margin-top: 25px + + +.error + .icn + width: 30px + height: 30px + margin: 0 auto + background-image: url("../images/error.svg") + background-position: center + background-size: 30px + background-repeat: no-repeat + .desc + font-size: 16px + font-weight: normal + color: $color-common-text + text-align: center + margin-top: 25px + .opt + text-align: center + margin-top: 50px diff --git a/safe_webrtc_example/app/sass/loader.sass b/safe_webrtc_example/app/sass/loader.sass new file mode 100644 index 0000000..e222896 --- /dev/null +++ b/safe_webrtc_example/app/sass/loader.sass @@ -0,0 +1,31 @@ +.loader + position: relative + width: 40px + height: 40px + display: inline-flex + background-color: transparent + border: 5px solid #d6dde4 + border-radius: 50% + box-sizing: content-box + &:after + position: absolute + content: "" + top: 0px + left: 0px + width: 100% + height: 100% + padding: 3px + border-width: 2px + border-style: solid + border-color: #404040 transparent transparent transparent + border-radius: 50% + box-sizing: border-box + animation: ringrotate .8s infinite ease-in-out + -webkit-animation: ringrotate .8s infinite ease-in-out +@keyframes ringrotate + 0% + transform: rotateZ(0deg) + -webkit-transform: rotateZ(0deg) + 100% + transform: rotateZ(360deg) + -webkit-transform: rotateZ(360deg) diff --git a/safe_webrtc_example/app/sass/main.sass b/safe_webrtc_example/app/sass/main.sass new file mode 100644 index 0000000..775a679 --- /dev/null +++ b/safe_webrtc_example/app/sass/main.sass @@ -0,0 +1,9 @@ +$FontPathOpenSans: "../../node_modules/npm-font-open-sans/fonts" +@import '../../node_modules/npm-font-open-sans/open-sans.scss' +@import 'variables' +@import 'mixins' +@import 'common' +@import 'buttons' +@import 'components' +@import 'chat_room' +@import 'loader' diff --git a/safe_webrtc_example/app/sass/mixins.sass b/safe_webrtc_example/app/sass/mixins.sass new file mode 100644 index 0000000..e828116 --- /dev/null +++ b/safe_webrtc_example/app/sass/mixins.sass @@ -0,0 +1,15 @@ +=clearfix() + &:after + content: "" + display: block + clear: both + visibility: hidden + +=btnDefault() + border: 0 + outline: 0 + background-color: transparent + cursor: pointer + text-transform: uppercase + &:disabled + cursor: not-allowed diff --git a/safe_webrtc_example/app/sass/variables.sass b/safe_webrtc_example/app/sass/variables.sass new file mode 100644 index 0000000..606e795 --- /dev/null +++ b/safe_webrtc_example/app/sass/variables.sass @@ -0,0 +1,11 @@ +$pr-blue: #5593d7 +$pr-pale-blue: #75adeb +$pr-pale-blue-2: #eff6fd +$pr-pale-brown: #f4f3f3 +$pr-pale-brown-2: #fbfbfb +$pr-red: #fdefef +$pr-red-dark: #e26464 + +$color-common-text: #4a4a4a + +$bg-header: #f3f7f9 diff --git a/safe_webrtc_example/app/utils.js b/safe_webrtc_example/app/utils.js new file mode 100644 index 0000000..09fb02c --- /dev/null +++ b/safe_webrtc_example/app/utils.js @@ -0,0 +1,36 @@ + +export const stringifyConnInfo = (connInfo) => { + return JSON.stringify(connInfo); +}; + +export const parseConnInfo = (connInfo) => { + return JSON.parse(connInfo); +}; + +export const putLog = (msg, data) => { + if (!msg) { + return; + } + // console.log(`${(new Date()).toISOString()} :: ${msg} :: `, data); +}; + +export const bufToArr = (buf) => { + if (!(buf instanceof Uint8Array)) { + throw new Error('buf is not instance of Uint8Array'); + } + return Array.from(buf); +}; + +export const arrToBuf = (arr) => { + if (!(arr instanceof Array)) { + throw new Error('arr is not instance of Array'); + } + return new Uint8Array(arr); +}; + +export const uint8ToStr = (buf) => { + if (!(buf instanceof Uint8Array)) { + throw new Error('buf is not instance of Uint8Array'); + } + return String.fromCharCode.apply(null, new Uint8Array(buf)); +}; diff --git a/safe_webrtc_example/package.json b/safe_webrtc_example/package.json new file mode 100644 index 0000000..ff7ee77 --- /dev/null +++ b/safe_webrtc_example/package.json @@ -0,0 +1,65 @@ +{ + "name": "safe_webrtc_example", + "productName": "SAFE WebRTC Example", + "version": "0.1.0", + "description": "Application to showcase webRTC secure signalling on SAFE Network", + "main": "app/main.js", + "repository": { + "type": "git", + "url": "git+https://github.com/maidsafe/safe_examples.git" + }, + "author": { + "name": "MaidSafe", + "email": "dev@maidsafe.net", + "url": "https://maidsafe.net" + }, + "bugs": { + "url": "https://github.com/maidsafe/safe_examples/issues" + }, + "keywords": [ + "MaidSafe", + "DemoApp", + "Safe webRTC", + "Safe webRTC example" + ], + "homepage": "https://github.com/maidsafe/safe_examples#readme", + "license": "MIT", + "devDependencies": { + "babel-core": "^6.26.0", + "babel-loader": "^7.1.2", + "babel-plugin-transform-async-to-generator": "^6.24.1", + "babel-plugin-transform-decorators-legacy": "^1.3.4", + "babel-plugin-transform-object-rest-spread": "^6.26.0", + "babel-plugin-transform-runtime": "^6.23.0", + "babel-polyfill": "^6.26.0", + "babel-preset-es2015": "^6.24.1", + "babel-preset-react": "^6.24.1", + "babel-preset-stage-1": "^6.24.1", + "css-loader": "^0.28.7", + "extract-text-webpack-plugin": "^3.0.2", + "file-loader": "^1.1.5", + "html-webpack-plugin": "^2.30.1", + "node-sass": "^4.7.2", + "sass-loader": "^6.0.6", + "style-loader": "^0.19.0", + "url-loader": "^0.6.2", + "webpack": "^3.9.1", + "webpack-dev-server": "^2.9.5" + }, + "dependencies": { + "classnames": "^2.2.5", + "mobx": "^3.3.2", + "mobx-react": "^4.3.5", + "mobx-react-router": "^4.0.1", + "npm-font-open-sans": "^1.1.0", + "prop-types": "^15.6.0", + "react": "^16.2.0", + "react-dom": "^16.2.0", + "react-route": "^1.0.3", + "react-router-dom": "^4.2.2" + }, + "scripts": { + "start": "webpack-dev-server --hot --open --config webpack.dev.config.js --content-base ./app", + "build": "webpack --config webpack.prod.config.js" + } +} diff --git a/safe_webrtc_example/webpack.dev.config.js b/safe_webrtc_example/webpack.dev.config.js new file mode 100644 index 0000000..2d096c9 --- /dev/null +++ b/safe_webrtc_example/webpack.dev.config.js @@ -0,0 +1,100 @@ +const path = require('path'); +const webpack = require('webpack'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const ExtractTextPlugin = require('extract-text-webpack-plugin'); + +module.exports = { + devtool: 'eval', + entry: [ + 'babel-polyfill', + './main.js', + './sass/main.sass' + ], + context: path.resolve(__dirname, 'app'), + output: { + path: path.join(__dirname, 'dist'), + filename: 'bundle.js', + publicPath: '/static/' + }, + plugins: [ + new webpack.HotModuleReplacementPlugin(), + ], + resolve: { + extensions: ['.js', '.jsx'] + }, + devServer: { + historyApiFallback: true, + }, + module: { + rules: [ + { + test: /\.js?$/, + use: ['babel-loader'], + include: path.join(__dirname, 'app') + }, + { + test: /\.sass$/, + exclude: /node_modules/, + use: [ + { loader: "style-loader" }, + { loader: "css-loader" }, + { loader: 'sass-loader', query: { sourceMap: false } }, + ], + }, + // SVG Font + { + test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, + use: { + loader: 'url-loader', + options: { + limit: 10000, + mimetype: 'image/svg+xml', + } + } + }, + // Common Image Formats + { + test: /\.(?:ico|gif|png|jpg|jpeg|webp)$/, + use: 'url-loader', + }, + // WOFF Font + { + test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, + use: { + loader: 'url-loader', + options: { + limit: 10000, + mimetype: 'application/font-woff', + } + }, + }, + // WOFF2 Font + { + test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, + use: { + loader: 'url-loader', + options: { + limit: 10000, + mimetype: 'application/font-woff', + } + } + }, + // TTF Font + { + test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, + use: { + loader: 'url-loader', + options: { + limit: 10000, + mimetype: 'application/octet-stream' + } + } + }, + // EOT Font + { + test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, + use: 'file-loader', + } + ] + } +}; diff --git a/safe_webrtc_example/webpack.prod.config.js b/safe_webrtc_example/webpack.prod.config.js new file mode 100644 index 0000000..b8d04d6 --- /dev/null +++ b/safe_webrtc_example/webpack.prod.config.js @@ -0,0 +1,124 @@ +const { resolve, join } = require('path'); +var webpack = require('webpack'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const ExtractTextPlugin = require('extract-text-webpack-plugin'); + +module.exports = { + devtool: 'cheap-module-source-map', + entry: [ + './main.js', + './sass/main.sass', + ], + + context: resolve(__dirname, 'app'), + + output: { + filename: 'safe-webrtc.js', + path: resolve(__dirname, 'dist'), + publicPath: '', + }, + plugins: [ + new webpack.optimize.ModuleConcatenationPlugin(), + new HtmlWebpackPlugin({ + template: `${__dirname}/app/index.html`, + filename: 'index.html', + inject: 'body', + }), + new webpack.optimize.OccurrenceOrderPlugin(), + new webpack.LoaderOptionsPlugin({ + minimize: true, + debug: false, + }), + new webpack.optimize.UglifyJsPlugin({ + beautify: false, + sourceMap: false + }), + new webpack.NoEmitOnErrorsPlugin(), + new webpack.DefinePlugin({ 'process.env': { NODE_ENV: JSON.stringify('production') } }), + new ExtractTextPlugin({ filename: './styles/style.css', disable: false, allChunks: true }), + ], + resolve: { + extensions: ['.js', '.jsx'] + }, + module: { + rules: [ + { + test: /\.js?$/, + use: ['babel-loader'], + include: join(__dirname, 'app') + }, + { + test: /\.sass$/, + exclude: /node_modules/, + use: [ + { loader: "style-loader" }, + { loader: "css-loader" }, + { loader: 'sass-loader', query: { sourceMap: false } }, + ], + }, + // SVG Font + { + test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, + use: { + loader: 'url-loader', + options: { + name: 'images/[name].[ext]', + limit: 10000, + mimetype: 'image/svg+xml', + } + } + }, + // Common Image Formats + { + test: /\.(?:ico|gif|png|jpg|jpeg|webp)$/, + use: 'url-loader', + }, + // WOFF Font + { + test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, + use: { + loader: 'url-loader', + options: { + name: 'fonts/[name].[ext]', + limit: 10000, + mimetype: 'application/font-woff', + } + }, + }, + // WOFF2 Font + { + test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, + use: { + loader: 'url-loader', + options: { + name: 'fonts/[name].[ext]', + limit: 10000, + mimetype: 'application/font-woff', + } + } + }, + // TTF Font + { + test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, + use: { + loader: 'url-loader', + options: { + name: 'fonts/[name].[ext]', + limit: 10000, + mimetype: 'application/octet-stream' + } + } + }, + // EOT Font + { + test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, + use: { + loader: 'file-loader', + options: { + name: 'fonts/[name].[ext]', + } + }, + } + ] + } +}; diff --git a/safe_webrtc_example/yarn.lock b/safe_webrtc_example/yarn.lock new file mode 100644 index 0000000..f5eb6c9 --- /dev/null +++ b/safe_webrtc_example/yarn.lock @@ -0,0 +1,5305 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + +accepts@~1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.4.tgz#86246758c7dd6d21a6474ff084a4740ec05eb21f" + dependencies: + mime-types "~2.1.16" + negotiator "0.6.1" + +acorn-dynamic-import@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef679501b6c6cb7fc84f8f47158cc4" + dependencies: + acorn "^4.0.3" + +acorn@^4.0.3: + version "4.0.13" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" + +acorn@^5.0.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.2.1.tgz#317ac7821826c22c702d66189ab8359675f135d7" + +ajv-keywords@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762" + +ajv@^4.9.1: + version "4.11.8" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" + dependencies: + co "^4.6.0" + json-stable-stringify "^1.0.1" + +ajv@^5.0.0, ajv@^5.1.0, ajv@^5.1.5: + version "5.5.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.0.tgz#eb2840746e9dc48bd5e063a36e3fd400c5eab5a9" + dependencies: + co "^4.6.0" + fast-deep-equal "^1.0.0" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.3.0" + +align-text@^0.1.1, align-text@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" + dependencies: + kind-of "^3.0.2" + longest "^1.0.1" + repeat-string "^1.5.2" + +alphanum-sort@^1.0.1, alphanum-sort@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" + +amdefine@>=0.0.4: + version "1.0.1" + resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + +ansi-html@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + +ansi-styles@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.0.tgz#c159b8d5be0f9e5a6f346dab94f16ce022161b88" + dependencies: + color-convert "^1.9.0" + +anymatch@^1.3.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a" + dependencies: + micromatch "^2.1.5" + normalize-path "^2.0.0" + +aproba@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + +are-we-there-yet@~1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d" + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +argparse@^1.0.7: + version "1.0.9" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86" + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" + dependencies: + arr-flatten "^1.0.1" + +arr-flatten@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + +array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + +array-flatten@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.1.tgz#426bb9da84090c1838d812c8150af20a8331e296" + +array-includes@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d" + dependencies: + define-properties "^1.1.2" + es-abstract "^1.7.0" + +array-union@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + dependencies: + array-uniq "^1.0.1" + +array-uniq@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + +array-unique@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" + +asap@~2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + +asn1.js@^4.0.0: + version "4.9.2" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.9.2.tgz#8117ef4f7ed87cd8f89044b5bff97ac243a16c9a" + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +asn1@~0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + +assert-plus@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" + +assert@^1.1.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" + dependencies: + util "0.10.3" + +async-each@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" + +async-foreach@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" + +async@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + +async@^2.1.2, async@^2.1.5, async@^2.4.1: + version "2.6.0" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.0.tgz#61a29abb6fcc026fea77e56d1c6ec53a795951f4" + dependencies: + lodash "^4.14.0" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + +autoprefixer@^6.3.1: + version "6.7.7" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014" + dependencies: + browserslist "^1.7.6" + caniuse-db "^1.0.30000634" + normalize-range "^0.1.2" + num2fraction "^1.2.2" + postcss "^5.2.16" + postcss-value-parser "^3.2.3" + +aws-sign2@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + +aws4@^1.2.1, aws4@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" + +babel-code-frame@^6.11.0, babel-code-frame@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + +babel-core@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.0.tgz#af32f78b31a6fcef119c87b0fd8d9753f03a0bb8" + dependencies: + babel-code-frame "^6.26.0" + babel-generator "^6.26.0" + babel-helpers "^6.24.1" + babel-messages "^6.23.0" + babel-register "^6.26.0" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + convert-source-map "^1.5.0" + debug "^2.6.8" + json5 "^0.5.1" + lodash "^4.17.4" + minimatch "^3.0.4" + path-is-absolute "^1.0.1" + private "^0.1.7" + slash "^1.0.0" + source-map "^0.5.6" + +babel-generator@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.0.tgz#ac1ae20070b79f6e3ca1d3269613053774f20dc5" + dependencies: + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + detect-indent "^4.0.0" + jsesc "^1.3.0" + lodash "^4.17.4" + source-map "^0.5.6" + trim-right "^1.0.1" + +babel-helper-bindify-decorators@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.24.1.tgz#14c19e5f142d7b47f19a52431e52b1ccbc40a330" + dependencies: + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-builder-binary-assignment-operator-visitor@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz#cce4517ada356f4220bcae8a02c2b346f9a56664" + dependencies: + babel-helper-explode-assignable-expression "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-builder-react-jsx@^6.24.1: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.26.0.tgz#39ff8313b75c8b65dceff1f31d383e0ff2a408a0" + dependencies: + babel-runtime "^6.26.0" + babel-types "^6.26.0" + esutils "^2.0.2" + +babel-helper-call-delegate@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d" + dependencies: + babel-helper-hoist-variables "^6.24.1" + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-define-map@^6.24.1: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz#a5f56dab41a25f97ecb498c7ebaca9819f95be5f" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-helper-explode-assignable-expression@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz#f25b82cf7dc10433c55f70592d5746400ac22caa" + dependencies: + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-explode-class@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-explode-class/-/babel-helper-explode-class-6.24.1.tgz#7dc2a3910dee007056e1e31d640ced3d54eaa9eb" + dependencies: + babel-helper-bindify-decorators "^6.24.1" + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-function-name@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9" + dependencies: + babel-helper-get-function-arity "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-get-function-arity@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-hoist-variables@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-optimise-call-expression@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-regex@^6.24.1: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz#325c59f902f82f24b74faceed0363954f6495e72" + dependencies: + babel-runtime "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-helper-remap-async-to-generator@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz#5ec581827ad723fecdd381f1c928390676e4551b" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-replace-supers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a" + dependencies: + babel-helper-optimise-call-expression "^6.24.1" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helpers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-loader@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-7.1.2.tgz#f6cbe122710f1aa2af4d881c6d5b54358ca24126" + dependencies: + find-cache-dir "^1.0.0" + loader-utils "^1.0.2" + mkdirp "^0.5.1" + +babel-messages@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-check-es2015-constants@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-syntax-async-functions@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" + +babel-plugin-syntax-async-generators@^6.5.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz#6bc963ebb16eccbae6b92b596eb7f35c342a8b9a" + +babel-plugin-syntax-class-constructor-call@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-constructor-call/-/babel-plugin-syntax-class-constructor-call-6.18.0.tgz#9cb9d39fe43c8600bec8146456ddcbd4e1a76416" + +babel-plugin-syntax-class-properties@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz#d7eb23b79a317f8543962c505b827c7d6cac27de" + +babel-plugin-syntax-decorators@^6.1.18, babel-plugin-syntax-decorators@^6.13.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz#312563b4dbde3cc806cee3e416cceeaddd11ac0b" + +babel-plugin-syntax-dynamic-import@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz#8d6a26229c83745a9982a441051572caa179b1da" + +babel-plugin-syntax-exponentiation-operator@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" + +babel-plugin-syntax-export-extensions@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-export-extensions/-/babel-plugin-syntax-export-extensions-6.13.0.tgz#70a1484f0f9089a4e84ad44bac353c95b9b12721" + +babel-plugin-syntax-flow@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz#4c3ab20a2af26aa20cd25995c398c4eb70310c8d" + +babel-plugin-syntax-jsx@^6.3.13, babel-plugin-syntax-jsx@^6.8.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" + +babel-plugin-syntax-object-rest-spread@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5" + +babel-plugin-syntax-trailing-function-commas@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3" + +babel-plugin-transform-async-generator-functions@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz#f058900145fd3e9907a6ddf28da59f215258a5db" + dependencies: + babel-helper-remap-async-to-generator "^6.24.1" + babel-plugin-syntax-async-generators "^6.5.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-async-to-generator@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz#6536e378aff6cb1d5517ac0e40eb3e9fc8d08761" + dependencies: + babel-helper-remap-async-to-generator "^6.24.1" + babel-plugin-syntax-async-functions "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-class-constructor-call@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-constructor-call/-/babel-plugin-transform-class-constructor-call-6.24.1.tgz#80dc285505ac067dcb8d6c65e2f6f11ab7765ef9" + dependencies: + babel-plugin-syntax-class-constructor-call "^6.18.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-class-properties@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz#6a79763ea61d33d36f37b611aa9def81a81b46ac" + dependencies: + babel-helper-function-name "^6.24.1" + babel-plugin-syntax-class-properties "^6.8.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-decorators-legacy@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-decorators-legacy/-/babel-plugin-transform-decorators-legacy-1.3.4.tgz#741b58f6c5bce9e6027e0882d9c994f04f366925" + dependencies: + babel-plugin-syntax-decorators "^6.1.18" + babel-runtime "^6.2.0" + babel-template "^6.3.0" + +babel-plugin-transform-decorators@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.24.1.tgz#788013d8f8c6b5222bdf7b344390dfd77569e24d" + dependencies: + babel-helper-explode-class "^6.24.1" + babel-plugin-syntax-decorators "^6.13.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-arrow-functions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoped-functions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz#bbc51b49f964d70cb8d8e0b94e820246ce3a6141" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoping@^6.24.1: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz#d70f5299c1308d05c12f463813b0a09e73b1895f" + dependencies: + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-plugin-transform-es2015-classes@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db" + dependencies: + babel-helper-define-map "^6.24.1" + babel-helper-function-name "^6.24.1" + babel-helper-optimise-call-expression "^6.24.1" + babel-helper-replace-supers "^6.24.1" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-computed-properties@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-destructuring@^6.22.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-duplicate-keys@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-for-of@^6.22.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-function-name@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-literals@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz#4f54a02d6cd66cf915280019a31d31925377ca2e" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-modules-amd@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154" + dependencies: + babel-plugin-transform-es2015-modules-commonjs "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-modules-commonjs@^6.24.1: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz#0d8394029b7dc6abe1a97ef181e00758dd2e5d8a" + dependencies: + babel-plugin-transform-strict-mode "^6.24.1" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-types "^6.26.0" + +babel-plugin-transform-es2015-modules-systemjs@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23" + dependencies: + babel-helper-hoist-variables "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-modules-umd@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468" + dependencies: + babel-plugin-transform-es2015-modules-amd "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-object-super@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d" + dependencies: + babel-helper-replace-supers "^6.24.1" + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-parameters@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b" + dependencies: + babel-helper-call-delegate "^6.24.1" + babel-helper-get-function-arity "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-shorthand-properties@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-spread@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz#d6d68a99f89aedc4536c81a542e8dd9f1746f8d1" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-sticky-regex@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc" + dependencies: + babel-helper-regex "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-template-literals@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz#a84b3450f7e9f8f1f6839d6d687da84bb1236d8d" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-typeof-symbol@^6.22.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-unicode-regex@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9" + dependencies: + babel-helper-regex "^6.24.1" + babel-runtime "^6.22.0" + regexpu-core "^2.0.0" + +babel-plugin-transform-exponentiation-operator@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz#2ab0c9c7f3098fa48907772bb813fe41e8de3a0e" + dependencies: + babel-helper-builder-binary-assignment-operator-visitor "^6.24.1" + babel-plugin-syntax-exponentiation-operator "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-export-extensions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-export-extensions/-/babel-plugin-transform-export-extensions-6.22.0.tgz#53738b47e75e8218589eea946cbbd39109bbe653" + dependencies: + babel-plugin-syntax-export-extensions "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-flow-strip-types@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz#84cb672935d43714fdc32bce84568d87441cf7cf" + dependencies: + babel-plugin-syntax-flow "^6.18.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-object-rest-spread@^6.22.0, babel-plugin-transform-object-rest-spread@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz#0f36692d50fef6b7e2d4b3ac1478137a963b7b06" + dependencies: + babel-plugin-syntax-object-rest-spread "^6.8.0" + babel-runtime "^6.26.0" + +babel-plugin-transform-react-display-name@^6.23.0: + version "6.25.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz#67e2bf1f1e9c93ab08db96792e05392bf2cc28d1" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-react-jsx-self@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx-self/-/babel-plugin-transform-react-jsx-self-6.22.0.tgz#df6d80a9da2612a121e6ddd7558bcbecf06e636e" + dependencies: + babel-plugin-syntax-jsx "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-react-jsx-source@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.22.0.tgz#66ac12153f5cd2d17b3c19268f4bf0197f44ecd6" + dependencies: + babel-plugin-syntax-jsx "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-react-jsx@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz#840a028e7df460dfc3a2d29f0c0d91f6376e66a3" + dependencies: + babel-helper-builder-react-jsx "^6.24.1" + babel-plugin-syntax-jsx "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-regenerator@^6.24.1: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz#e0703696fbde27f0a3efcacf8b4dca2f7b3a8f2f" + dependencies: + regenerator-transform "^0.10.0" + +babel-plugin-transform-runtime@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.23.0.tgz#88490d446502ea9b8e7efb0fe09ec4d99479b1ee" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-strict-mode@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-polyfill@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.26.0.tgz#379937abc67d7895970adc621f284cd966cf2153" + dependencies: + babel-runtime "^6.26.0" + core-js "^2.5.0" + regenerator-runtime "^0.10.5" + +babel-preset-es2015@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz#d44050d6bc2c9feea702aaf38d727a0210538939" + dependencies: + babel-plugin-check-es2015-constants "^6.22.0" + babel-plugin-transform-es2015-arrow-functions "^6.22.0" + babel-plugin-transform-es2015-block-scoped-functions "^6.22.0" + babel-plugin-transform-es2015-block-scoping "^6.24.1" + babel-plugin-transform-es2015-classes "^6.24.1" + babel-plugin-transform-es2015-computed-properties "^6.24.1" + babel-plugin-transform-es2015-destructuring "^6.22.0" + babel-plugin-transform-es2015-duplicate-keys "^6.24.1" + babel-plugin-transform-es2015-for-of "^6.22.0" + babel-plugin-transform-es2015-function-name "^6.24.1" + babel-plugin-transform-es2015-literals "^6.22.0" + babel-plugin-transform-es2015-modules-amd "^6.24.1" + babel-plugin-transform-es2015-modules-commonjs "^6.24.1" + babel-plugin-transform-es2015-modules-systemjs "^6.24.1" + babel-plugin-transform-es2015-modules-umd "^6.24.1" + babel-plugin-transform-es2015-object-super "^6.24.1" + babel-plugin-transform-es2015-parameters "^6.24.1" + babel-plugin-transform-es2015-shorthand-properties "^6.24.1" + babel-plugin-transform-es2015-spread "^6.22.0" + babel-plugin-transform-es2015-sticky-regex "^6.24.1" + babel-plugin-transform-es2015-template-literals "^6.22.0" + babel-plugin-transform-es2015-typeof-symbol "^6.22.0" + babel-plugin-transform-es2015-unicode-regex "^6.24.1" + babel-plugin-transform-regenerator "^6.24.1" + +babel-preset-flow@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-preset-flow/-/babel-preset-flow-6.23.0.tgz#e71218887085ae9a24b5be4169affb599816c49d" + dependencies: + babel-plugin-transform-flow-strip-types "^6.22.0" + +babel-preset-react@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-preset-react/-/babel-preset-react-6.24.1.tgz#ba69dfaea45fc3ec639b6a4ecea6e17702c91380" + dependencies: + babel-plugin-syntax-jsx "^6.3.13" + babel-plugin-transform-react-display-name "^6.23.0" + babel-plugin-transform-react-jsx "^6.24.1" + babel-plugin-transform-react-jsx-self "^6.22.0" + babel-plugin-transform-react-jsx-source "^6.22.0" + babel-preset-flow "^6.23.0" + +babel-preset-stage-1@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-preset-stage-1/-/babel-preset-stage-1-6.24.1.tgz#7692cd7dcd6849907e6ae4a0a85589cfb9e2bfb0" + dependencies: + babel-plugin-transform-class-constructor-call "^6.24.1" + babel-plugin-transform-export-extensions "^6.22.0" + babel-preset-stage-2 "^6.24.1" + +babel-preset-stage-2@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-preset-stage-2/-/babel-preset-stage-2-6.24.1.tgz#d9e2960fb3d71187f0e64eec62bc07767219bdc1" + dependencies: + babel-plugin-syntax-dynamic-import "^6.18.0" + babel-plugin-transform-class-properties "^6.24.1" + babel-plugin-transform-decorators "^6.24.1" + babel-preset-stage-3 "^6.24.1" + +babel-preset-stage-3@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz#836ada0a9e7a7fa37cb138fb9326f87934a48395" + dependencies: + babel-plugin-syntax-trailing-function-commas "^6.22.0" + babel-plugin-transform-async-generator-functions "^6.24.1" + babel-plugin-transform-async-to-generator "^6.24.1" + babel-plugin-transform-exponentiation-operator "^6.24.1" + babel-plugin-transform-object-rest-spread "^6.22.0" + +babel-register@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071" + dependencies: + babel-core "^6.26.0" + babel-runtime "^6.26.0" + core-js "^2.5.0" + home-or-tmp "^2.0.0" + lodash "^4.17.4" + mkdirp "^0.5.1" + source-map-support "^0.4.15" + +babel-runtime@^6.18.0, babel-runtime@^6.2.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + +babel-template@^6.24.1, babel-template@^6.26.0, babel-template@^6.3.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" + dependencies: + babel-runtime "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + lodash "^4.17.4" + +babel-traverse@^6.24.1, babel-traverse@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" + dependencies: + babel-code-frame "^6.26.0" + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + debug "^2.6.8" + globals "^9.18.0" + invariant "^2.2.2" + lodash "^4.17.4" + +babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + dependencies: + babel-runtime "^6.26.0" + esutils "^2.0.2" + lodash "^4.17.4" + to-fast-properties "^1.0.3" + +babylon@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + +balanced-match@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + +base64-js@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.1.tgz#a91947da1f4a516ea38e5b4ec0ec3773675e0886" + +batch@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" + +bcrypt-pbkdf@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" + dependencies: + tweetnacl "^0.14.3" + +big.js@^3.1.3: + version "3.2.0" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" + +binary-extensions@^1.0.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205" + +block-stream@*: + version "0.0.9" + resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" + dependencies: + inherits "~2.0.0" + +bluebird@^3.4.7: + version "3.5.1" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: + version "4.11.8" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" + +body-parser@1.18.2: + version "1.18.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.2.tgz#87678a19d84b47d859b83199bd59bce222b10454" + dependencies: + bytes "3.0.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.1" + http-errors "~1.6.2" + iconv-lite "0.4.19" + on-finished "~2.3.0" + qs "6.5.1" + raw-body "2.3.2" + type-is "~1.6.15" + +bonjour@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" + dependencies: + array-flatten "^2.1.0" + deep-equal "^1.0.1" + dns-equal "^1.0.0" + dns-txt "^2.0.2" + multicast-dns "^6.0.1" + multicast-dns-service-types "^1.1.0" + +boolbase@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + +boom@2.x.x: + version "2.10.1" + resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" + dependencies: + hoek "2.x.x" + +boom@4.x.x: + version "4.3.1" + resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31" + dependencies: + hoek "4.x.x" + +boom@5.x.x: + version "5.2.0" + resolved "https://registry.yarnpkg.com/boom/-/boom-5.2.0.tgz#5dd9da6ee3a5f302077436290cb717d3f4a54e02" + dependencies: + hoek "4.x.x" + +brace-expansion@^1.1.7: + version "1.1.8" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^1.8.2: + version "1.8.5" + resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" + dependencies: + expand-range "^1.8.1" + preserve "^0.2.0" + repeat-element "^1.1.2" + +brorand@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.1.1" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.1.1.tgz#38b7ab55edb806ff2dcda1a7f1620773a477c49f" + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.0.tgz#9988244874bf5ed4e28da95666dcd66ac8fc363a" + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.0.tgz#daa277717470922ed2fe18594118a175439721dd" + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + +browserify-rsa@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" + dependencies: + bn.js "^4.1.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" + dependencies: + bn.js "^4.1.1" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.2" + elliptic "^6.0.0" + inherits "^2.0.1" + parse-asn1 "^5.0.0" + +browserify-zlib@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + dependencies: + pako "~1.0.5" + +browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6: + version "1.7.7" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9" + dependencies: + caniuse-db "^1.0.30000639" + electron-to-chromium "^1.2.7" + +buffer-indexof@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + +buffer@^4.3.0: + version "4.9.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + +builtin-modules@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + +builtin-status-codes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + +camel-case@3.0.x: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" + dependencies: + no-case "^2.2.0" + upper-case "^1.1.1" + +camelcase-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + dependencies: + camelcase "^2.0.0" + map-obj "^1.0.0" + +camelcase@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" + +camelcase@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + +camelcase@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" + +camelcase@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + +caniuse-api@^1.5.2: + version "1.6.1" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c" + dependencies: + browserslist "^1.3.6" + caniuse-db "^1.0.30000529" + lodash.memoize "^4.1.2" + lodash.uniq "^4.5.0" + +caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: + version "1.0.30000777" + resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000777.tgz#2e19adba63bdd7c501df637a862adead7f4bc054" + +caseless@~0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + +center-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" + dependencies: + align-text "^0.1.3" + lazy-cache "^1.0.3" + +chalk@^1.1.1, chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.0.tgz#b5ea48efc9c1793dccc9b4767c93914d3f2d52ba" + dependencies: + ansi-styles "^3.1.0" + escape-string-regexp "^1.0.5" + supports-color "^4.0.0" + +chokidar@^1.6.0, chokidar@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" + dependencies: + anymatch "^1.3.0" + async-each "^1.0.0" + glob-parent "^2.0.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^2.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + optionalDependencies: + fsevents "^1.0.0" + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +clap@^1.0.9: + version "1.2.3" + resolved "https://registry.yarnpkg.com/clap/-/clap-1.2.3.tgz#4f36745b32008492557f46412d66d50cb99bce51" + dependencies: + chalk "^1.1.3" + +classnames@^2.2.5: + version "2.2.5" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d" + +clean-css@4.1.x: + version "4.1.9" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.1.9.tgz#35cee8ae7687a49b98034f70de00c4edd3826301" + dependencies: + source-map "0.5.x" + +cliui@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" + dependencies: + center-align "^0.1.1" + right-align "^0.1.1" + wordwrap "0.0.2" + +cliui@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrap-ansi "^2.0.0" + +clone-deep@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-0.3.0.tgz#348c61ae9cdbe0edfe053d91ff4cc521d790ede8" + dependencies: + for-own "^1.0.0" + is-plain-object "^2.0.1" + kind-of "^3.2.2" + shallow-clone "^0.1.2" + +clone@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.3.tgz#298d7e2231660f40c003c2ed3140decf3f53085f" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + +coa@~1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.4.tgz#a9ef153660d6a86a8bdec0289a5c684d217432fd" + dependencies: + q "^1.1.2" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + +color-convert@^1.3.0, color-convert@^1.9.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed" + dependencies: + color-name "^1.1.1" + +color-name@^1.0.0, color-name@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + +color-string@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991" + dependencies: + color-name "^1.0.0" + +color@^0.11.0: + version "0.11.4" + resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764" + dependencies: + clone "^1.0.2" + color-convert "^1.3.0" + color-string "^0.3.0" + +colormin@^1.0.5: + version "1.1.2" + resolved "https://registry.yarnpkg.com/colormin/-/colormin-1.1.2.tgz#ea2f7420a72b96881a38aae59ec124a6f7298133" + dependencies: + color "^0.11.0" + css-color-names "0.0.4" + has "^1.0.1" + +colors@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" + +combined-stream@^1.0.5, combined-stream@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" + dependencies: + delayed-stream "~1.0.0" + +commander@2.12.x, commander@^2.9.0, commander@~2.12.1: + version "2.12.2" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.12.2.tgz#0f5946c427ed9ec0d91a46bb9def53e54650e555" + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + +component-emitter@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + +compressible@~2.0.11: + version "2.0.12" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.12.tgz#c59a5c99db76767e9876500e271ef63b3493bd66" + dependencies: + mime-db ">= 1.30.0 < 2" + +compression@^1.5.2: + version "1.7.1" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.1.tgz#eff2603efc2e22cf86f35d2eb93589f9875373db" + dependencies: + accepts "~1.3.4" + bytes "3.0.0" + compressible "~2.0.11" + debug "2.6.9" + on-headers "~1.0.1" + safe-buffer "5.1.1" + vary "~1.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +connect-history-api-fallback@^1.3.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz#b06873934bc5e344fef611a196a6faae0aee015a" + +console-browserify@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" + dependencies: + date-now "^0.1.4" + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + +constants-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + +content-disposition@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + +convert-source-map@^1.5.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5" + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + +cookie@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" + +core-js@^1.0.0: + version "1.2.7" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" + +core-js@^2.4.0, core-js@^2.5.0: + version "2.5.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b" + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +create-ecdh@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.0.tgz#888c723596cdf7612f6498233eebd7a35301737d" + dependencies: + bn.js "^4.1.0" + elliptic "^6.0.0" + +create-hash@^1.1.0, create-hash@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.3.tgz#606042ac8b9262750f483caddab0f5819172d8fd" + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + ripemd160 "^2.0.0" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: + version "1.1.6" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.6.tgz#acb9e221a4e17bdb076e90657c42b93e3726cf06" + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +cross-spawn@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982" + dependencies: + lru-cache "^4.0.1" + which "^1.2.9" + +cross-spawn@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + +cryptiles@2.x.x: + version "2.0.5" + resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" + dependencies: + boom "2.x.x" + +cryptiles@3.x.x: + version "3.1.2" + resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe" + dependencies: + boom "5.x.x" + +crypto-browserify@^3.11.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +css-color-names@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" + +css-loader@^0.28.7: + version "0.28.7" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.28.7.tgz#5f2ee989dd32edd907717f953317656160999c1b" + dependencies: + babel-code-frame "^6.11.0" + css-selector-tokenizer "^0.7.0" + cssnano ">=2.6.1 <4" + icss-utils "^2.1.0" + loader-utils "^1.0.2" + lodash.camelcase "^4.3.0" + object-assign "^4.0.1" + postcss "^5.0.6" + postcss-modules-extract-imports "^1.0.0" + postcss-modules-local-by-default "^1.0.1" + postcss-modules-scope "^1.0.0" + postcss-modules-values "^1.1.0" + postcss-value-parser "^3.3.0" + source-list-map "^2.0.0" + +css-select@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" + dependencies: + boolbase "~1.0.0" + css-what "2.1" + domutils "1.5.1" + nth-check "~1.0.1" + +css-selector-tokenizer@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz#e6988474ae8c953477bf5e7efecfceccd9cf4c86" + dependencies: + cssesc "^0.1.0" + fastparse "^1.1.1" + regexpu-core "^1.0.0" + +css-what@2.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd" + +cssesc@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4" + +"cssnano@>=2.6.1 <4": + version "3.10.0" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38" + dependencies: + autoprefixer "^6.3.1" + decamelize "^1.1.2" + defined "^1.0.0" + has "^1.0.1" + object-assign "^4.0.1" + postcss "^5.0.14" + postcss-calc "^5.2.0" + postcss-colormin "^2.1.8" + postcss-convert-values "^2.3.4" + postcss-discard-comments "^2.0.4" + postcss-discard-duplicates "^2.0.1" + postcss-discard-empty "^2.0.1" + postcss-discard-overridden "^0.1.1" + postcss-discard-unused "^2.2.1" + postcss-filter-plugins "^2.0.0" + postcss-merge-idents "^2.1.5" + postcss-merge-longhand "^2.0.1" + postcss-merge-rules "^2.0.3" + postcss-minify-font-values "^1.0.2" + postcss-minify-gradients "^1.0.1" + postcss-minify-params "^1.0.4" + postcss-minify-selectors "^2.0.4" + postcss-normalize-charset "^1.1.0" + postcss-normalize-url "^3.0.7" + postcss-ordered-values "^2.1.0" + postcss-reduce-idents "^2.2.2" + postcss-reduce-initial "^1.0.0" + postcss-reduce-transforms "^1.0.3" + postcss-svgo "^2.1.1" + postcss-unique-selectors "^2.0.2" + postcss-value-parser "^3.2.3" + postcss-zindex "^2.0.1" + +csso@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/csso/-/csso-2.3.2.tgz#ddd52c587033f49e94b71fc55569f252e8ff5f85" + dependencies: + clap "^1.0.9" + source-map "^0.5.3" + +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + dependencies: + array-find-index "^1.0.1" + +d@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" + dependencies: + es5-ext "^0.10.9" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + dependencies: + assert-plus "^1.0.0" + +date-now@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" + +debug@2.6.9, debug@^2.2.0, debug@^2.6.6, debug@^2.6.8: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + dependencies: + ms "2.0.0" + +debug@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + dependencies: + ms "2.0.0" + +decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + +deep-equal@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" + +deep-extend@~0.4.0: + version "0.4.2" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" + +define-properties@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94" + dependencies: + foreach "^2.0.5" + object-keys "^1.0.8" + +defined@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" + +del@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/del/-/del-3.0.0.tgz#53ecf699ffcbcb39637691ab13baf160819766e5" + dependencies: + globby "^6.1.0" + is-path-cwd "^1.0.0" + is-path-in-cwd "^1.0.0" + p-map "^1.1.1" + pify "^3.0.0" + rimraf "^2.2.8" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + +depd@1.1.1, depd@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359" + +des.js@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + +detect-indent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" + dependencies: + repeating "^2.0.0" + +detect-libc@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + +detect-node@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.3.tgz#a2033c09cc8e158d37748fbde7507832bd6ce127" + +diffie-hellman@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.2.tgz#b5835739270cfe26acf632099fded2a07f209e5e" + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +dns-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" + +dns-packet@^1.0.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.2.2.tgz#a8a26bec7646438963fc86e06f8f8b16d6c8bf7a" + dependencies: + ip "^1.1.0" + safe-buffer "^5.0.1" + +dns-txt@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6" + dependencies: + buffer-indexof "^1.0.0" + +dom-converter@~0.1: + version "0.1.4" + resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.1.4.tgz#a45ef5727b890c9bffe6d7c876e7b19cb0e17f3b" + dependencies: + utila "~0.3" + +dom-serializer@0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" + dependencies: + domelementtype "~1.1.1" + entities "~1.1.1" + +domain-browser@^1.1.1: + version "1.1.7" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc" + +domelementtype@1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" + +domelementtype@~1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" + +domhandler@2.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.1.0.tgz#d2646f5e57f6c3bab11cf6cb05d3c0acf7412594" + dependencies: + domelementtype "1" + +domutils@1.1: + version "1.1.6" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.1.6.tgz#bddc3de099b9a2efacc51c623f28f416ecc57485" + dependencies: + domelementtype "1" + +domutils@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + dependencies: + dom-serializer "0" + domelementtype "1" + +ecc-jsbn@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" + dependencies: + jsbn "~0.1.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + +electron-to-chromium@^1.2.7: + version "1.3.27" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.27.tgz#78ecb8a399066187bb374eede35d9c70565a803d" + +elliptic@^6.0.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + +emojis-list@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" + +encodeurl@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20" + +encoding@^0.1.11: + version "0.1.12" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" + dependencies: + iconv-lite "~0.4.13" + +enhanced-resolve@^3.4.0: + version "3.4.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz#0421e339fd71419b3da13d129b3979040230476e" + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.4.0" + object-assign "^4.0.1" + tapable "^0.2.7" + +entities@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" + +errno@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.4.tgz#b896e23a9e5e8ba33871fc996abd3635fc9a1c7d" + dependencies: + prr "~0.0.0" + +error-ex@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc" + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.7.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.10.0.tgz#1ecb36c197842a00d8ee4c2dfd8646bb97d60864" + dependencies: + es-to-primitive "^1.1.1" + function-bind "^1.1.1" + has "^1.0.1" + is-callable "^1.1.3" + is-regex "^1.0.4" + +es-to-primitive@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d" + dependencies: + is-callable "^1.1.1" + is-date-object "^1.0.1" + is-symbol "^1.0.1" + +es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14: + version "0.10.37" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.37.tgz#0ee741d148b80069ba27d020393756af257defc3" + dependencies: + es6-iterator "~2.0.1" + es6-symbol "~3.1.1" + +es6-iterator@^2.0.1, es6-iterator@~2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-map@^0.1.3: + version "0.1.5" + resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0" + dependencies: + d "1" + es5-ext "~0.10.14" + es6-iterator "~2.0.1" + es6-set "~0.1.5" + es6-symbol "~3.1.1" + event-emitter "~0.3.5" + +es6-set@~0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1" + dependencies: + d "1" + es5-ext "~0.10.14" + es6-iterator "~2.0.1" + es6-symbol "3.1.1" + event-emitter "~0.3.5" + +es6-symbol@3.1.1, es6-symbol@^3.1.1, es6-symbol@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" + dependencies: + d "1" + es5-ext "~0.10.14" + +es6-weak-map@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.2.tgz#5e3ab32251ffd1538a1f8e5ffa1357772f92d96f" + dependencies: + d "1" + es5-ext "^0.10.14" + es6-iterator "^2.0.1" + es6-symbol "^3.1.1" + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +escope@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" + dependencies: + es6-map "^0.1.3" + es6-weak-map "^2.0.1" + esrecurse "^4.1.0" + estraverse "^4.1.1" + +esprima@^2.6.0: + version "2.7.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" + +esrecurse@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.0.tgz#fa9568d98d3823f9a41d91e902dcab9ea6e5b163" + dependencies: + estraverse "^4.1.0" + object-assign "^4.0.1" + +estraverse@^4.1.0, estraverse@^4.1.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + +event-emitter@~0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + dependencies: + d "1" + es5-ext "~0.10.14" + +eventemitter3@1.x.x: + version "1.2.0" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508" + +events@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" + +eventsource@0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-0.1.6.tgz#0acede849ed7dd1ccc32c811bb11b944d4f29232" + dependencies: + original ">=0.0.5" + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +execa@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +expand-brackets@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" + dependencies: + is-posix-bracket "^0.1.0" + +expand-range@^1.8.1: + version "1.8.2" + resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" + dependencies: + fill-range "^2.1.0" + +express@^4.16.2: + version "4.16.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.16.2.tgz#e35c6dfe2d64b7dca0a5cd4f21781be3299e076c" + dependencies: + accepts "~1.3.4" + array-flatten "1.1.1" + body-parser "1.18.2" + content-disposition "0.5.2" + 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" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.2" + path-to-regexp "0.1.7" + proxy-addr "~2.0.2" + qs "6.5.1" + range-parser "~1.2.0" + safe-buffer "5.1.1" + send "0.16.1" + serve-static "1.13.1" + setprototypeof "1.1.0" + statuses "~1.3.1" + type-is "~1.6.15" + utils-merge "1.0.1" + vary "~1.1.2" + +extend@~3.0.0, extend@~3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" + +extglob@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" + dependencies: + is-extglob "^1.0.0" + +extract-text-webpack-plugin@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extract-text-webpack-plugin/-/extract-text-webpack-plugin-3.0.2.tgz#5f043eaa02f9750a9258b78c0a6e0dc1408fb2f7" + dependencies: + async "^2.4.1" + loader-utils "^1.1.0" + schema-utils "^0.3.0" + webpack-sources "^1.0.1" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + +fast-deep-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff" + +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + +fastparse@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8" + +faye-websocket@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" + dependencies: + websocket-driver ">=0.5.1" + +faye-websocket@~0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.1.tgz#f0efe18c4f56e4f40afc7e06c719fd5ee6188f38" + dependencies: + websocket-driver ">=0.5.1" + +fbjs@^0.8.16: + version "0.8.16" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db" + dependencies: + core-js "^1.0.0" + isomorphic-fetch "^2.1.1" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^0.7.9" + +file-loader@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-1.1.5.tgz#91c25b6b6fbe56dae99f10a425fd64933b5c9daa" + dependencies: + loader-utils "^1.0.2" + schema-utils "^0.3.0" + +filename-regex@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" + +fill-range@^2.1.0: + version "2.2.3" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" + dependencies: + is-number "^2.1.0" + isobject "^2.0.0" + randomatic "^1.1.3" + repeat-element "^1.1.2" + repeat-string "^1.5.2" + +finalhandler@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.0.tgz#ce0b6855b45853e791b2fcc680046d88253dd7f5" + dependencies: + 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" + +find-cache-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-1.0.0.tgz#9288e3e9e3cc3748717d39eade17cf71fc30ee6f" + dependencies: + commondir "^1.0.1" + make-dir "^1.0.0" + pkg-dir "^2.0.0" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^2.0.0, find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + dependencies: + locate-path "^2.0.0" + +flatten@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" + +for-in@^0.1.3: + version "0.1.8" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1" + +for-in@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + +for-own@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" + dependencies: + for-in "^1.0.1" + +for-own@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-1.0.0.tgz#c63332f415cedc4b04dbfe70cf836494c53cb44b" + dependencies: + for-in "^1.0.1" + +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + +form-data@~2.1.1: + version "2.1.4" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.12" + +form-data@~2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.1.tgz#6fb94fbd71885306d73d15cc497fe4cc4ecd44bf" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.12" + +forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +fsevents@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.3.tgz#11f82318f5fe7bb2cd22965a108e9306208216d8" + dependencies: + nan "^2.3.0" + node-pre-gyp "^0.6.39" + +fstream-ignore@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105" + dependencies: + fstream "^1.0.0" + inherits "2" + minimatch "^3.0.0" + +fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2: + version "1.0.11" + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" + dependencies: + graceful-fs "^4.1.2" + inherits "~2.0.0" + mkdirp ">=0.5 0" + rimraf "2" + +function-bind@^1.0.2, function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +gaze@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.2.tgz#847224677adb8870d679257ed3388fdb61e40105" + dependencies: + globule "^1.0.0" + +generate-function@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" + +generate-object-property@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" + dependencies: + is-property "^1.0.0" + +get-caller-file@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" + +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + dependencies: + assert-plus "^1.0.0" + +glob-base@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" + dependencies: + glob-parent "^2.0.0" + is-glob "^2.0.0" + +glob-parent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + dependencies: + is-glob "^2.0.0" + +glob@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@~7.1.1: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + dependencies: + 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" + +globals@^9.18.0: + version "9.18.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" + +globby@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" + dependencies: + array-union "^1.0.1" + glob "^7.0.3" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +globule@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/globule/-/globule-1.2.0.tgz#1dc49c6822dd9e8a2fa00ba2a295006e8664bd09" + dependencies: + glob "~7.1.1" + lodash "~4.17.4" + minimatch "~3.0.2" + +graceful-fs@^4.1.2: + version "4.1.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + +handle-thing@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4" + +har-schema@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + +har-validator@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d" + dependencies: + chalk "^1.1.1" + commander "^2.9.0" + is-my-json-valid "^2.12.4" + pinkie-promise "^2.0.0" + +har-validator@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" + dependencies: + ajv "^4.9.1" + har-schema "^1.0.5" + +har-validator@~5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd" + dependencies: + ajv "^5.1.0" + har-schema "^2.0.0" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +has-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" + +has-flag@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + +has@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" + dependencies: + function-bind "^1.0.2" + +hash-base@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-2.0.2.tgz#66ea1d856db4e8a5470cadf6fce23ae5244ef2e1" + dependencies: + inherits "^2.0.1" + +hash-base@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846" + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.0" + +hawk@3.1.3, hawk@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" + dependencies: + boom "2.x.x" + cryptiles "2.x.x" + hoek "2.x.x" + sntp "1.x.x" + +hawk@~6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/hawk/-/hawk-6.0.2.tgz#af4d914eb065f9b5ce4d9d11c1cb2126eecc3038" + dependencies: + boom "4.x.x" + cryptiles "3.x.x" + hoek "4.x.x" + sntp "2.x.x" + +he@1.1.x: + version "1.1.1" + resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" + +history@^4.7.2: + version "4.7.2" + resolved "https://registry.yarnpkg.com/history/-/history-4.7.2.tgz#22b5c7f31633c5b8021c7f4a8a954ac139ee8d5b" + dependencies: + invariant "^2.2.1" + loose-envify "^1.2.0" + resolve-pathname "^2.2.0" + value-equal "^0.4.0" + warning "^3.0.0" + +hmac-drbg@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +hoek@2.x.x: + version "2.16.3" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" + +hoek@4.x.x: + version "4.2.0" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d" + +hoist-non-react-statics@^2.3.0, hoist-non-react-statics@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz#343db84c6018c650778898240135a1420ee22ce0" + +home-or-tmp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.1" + +hosted-git-info@^2.1.4: + version "2.5.0" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c" + +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +html-comment-regex@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e" + +html-entities@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" + +html-minifier@^3.2.3: + version "3.5.7" + resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.7.tgz#511e69bb5a8e7677d1012ebe03819aa02ca06208" + dependencies: + camel-case "3.0.x" + clean-css "4.1.x" + commander "2.12.x" + he "1.1.x" + ncname "1.0.x" + param-case "2.1.x" + relateurl "0.2.x" + uglify-js "3.2.x" + +html-webpack-plugin@^2.30.1: + version "2.30.1" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-2.30.1.tgz#7f9c421b7ea91ec460f56527d78df484ee7537d5" + dependencies: + bluebird "^3.4.7" + html-minifier "^3.2.3" + loader-utils "^0.2.16" + lodash "^4.17.3" + pretty-error "^2.0.2" + toposort "^1.0.0" + +htmlparser2@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.3.0.tgz#cc70d05a59f6542e43f0e685c982e14c924a9efe" + dependencies: + domelementtype "1" + domhandler "2.1" + domutils "1.1" + readable-stream "1.0" + +http-deceiver@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + +http-errors@1.6.2, http-errors@~1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" + dependencies: + depd "1.1.1" + inherits "2.0.3" + setprototypeof "1.0.3" + statuses ">= 1.3.1 < 2" + +http-parser-js@>=0.4.0: + version "0.4.9" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.9.tgz#ea1a04fb64adff0242e9974f297dd4c3cad271e1" + +http-proxy-middleware@~0.17.4: + version "0.17.4" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.17.4.tgz#642e8848851d66f09d4f124912846dbaeb41b833" + dependencies: + http-proxy "^1.16.2" + is-glob "^3.1.0" + lodash "^4.17.2" + micromatch "^2.3.11" + +http-proxy@^1.16.2: + version "1.16.2" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.16.2.tgz#06dff292952bf64dbe8471fa9df73066d4f37742" + dependencies: + eventemitter3 "1.x.x" + requires-port "1.x.x" + +http-signature@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" + dependencies: + assert-plus "^0.2.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" + +iconv-lite@0.4.19, iconv-lite@~0.4.13: + version "0.4.19" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" + +icss-replace-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" + +icss-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-2.1.0.tgz#83f0a0ec378bf3246178b6c2ad9136f135b1c962" + dependencies: + postcss "^6.0.1" + +ieee754@^1.1.4: + version "1.1.8" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" + +import-local@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-0.1.1.tgz#b1179572aacdc11c6a91009fb430dbcab5f668a8" + dependencies: + pkg-dir "^2.0.0" + resolve-cwd "^2.0.0" + +in-publish@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.0.tgz#e20ff5e3a2afc2690320b6dc552682a9c7fadf51" + +indent-string@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + dependencies: + repeating "^2.0.0" + +indexes-of@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" + +indexof@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + +ini@~1.3.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + +internal-ip@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-1.2.0.tgz#ae9fbf93b984878785d50a8de1b356956058cf5c" + dependencies: + meow "^3.3.0" + +interpret@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" + +invariant@^2.2.1, invariant@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" + dependencies: + loose-envify "^1.0.0" + +invert-kv@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + +ip@^1.1.0, ip@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + +ipaddr.js@1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.5.2.tgz#d4b505bde9946987ccf0fc58d9010ff9607e3fa0" + +is-absolute-url@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + dependencies: + binary-extensions "^1.0.0" + +is-buffer@^1.0.2, is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + +is-builtin-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" + dependencies: + builtin-modules "^1.0.0" + +is-callable@^1.1.1, is-callable@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2" + +is-date-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" + +is-dotfile@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" + +is-equal-shallow@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" + dependencies: + is-primitive "^2.0.0" + +is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + +is-extglob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + +is-extglob@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + +is-finite@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + +is-glob@^2.0.0, is-glob@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + dependencies: + is-extglob "^1.0.0" + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + dependencies: + is-extglob "^2.1.0" + +is-my-json-valid@^2.12.4: + version "2.16.1" + resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz#5a846777e2c2620d1e69104e5d3a03b1f6088f11" + dependencies: + generate-function "^2.0.0" + generate-object-property "^1.1.0" + jsonpointer "^4.0.0" + xtend "^4.0.0" + +is-number@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + dependencies: + kind-of "^3.0.2" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + dependencies: + kind-of "^3.0.2" + +is-path-cwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" + +is-path-in-cwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc" + dependencies: + is-path-inside "^1.0.0" + +is-path-inside@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" + dependencies: + path-is-inside "^1.0.1" + +is-plain-obj@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + +is-plain-object@^2.0.1: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + dependencies: + isobject "^3.0.1" + +is-posix-bracket@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" + +is-primitive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" + +is-property@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" + +is-regex@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" + dependencies: + has "^1.0.1" + +is-stream@^1.0.1, is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + +is-svg@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-2.1.0.tgz#cf61090da0d9efbcab8722deba6f032208dbb0e9" + dependencies: + html-comment-regex "^1.1.0" + +is-symbol@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + dependencies: + isarray "1.0.0" + +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + +isomorphic-fetch@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" + dependencies: + node-fetch "^1.0.1" + whatwg-fetch ">=0.10.0" + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + +js-base64@^2.1.8, js-base64@^2.1.9: + version "2.4.0" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.0.tgz#9e566fee624751a1d720c966cd6226d29d4025aa" + +js-tokens@^3.0.0, js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + +js-yaml@~3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80" + dependencies: + argparse "^1.0.7" + esprima "^2.6.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + +jsesc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + +json-loader@^0.5.4: + version "0.5.7" + resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.7.tgz#dca14a70235ff82f0ac9a3abeb60d337a365185d" + +json-schema-traverse@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + +json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + +json3@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" + +json5@^0.5.0, json5@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + +jsonpointer@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +killable@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.0.tgz#da8b84bd47de5395878f95d64d02f2449fe05e6b" + +kind-of@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-2.0.1.tgz#018ec7a4ce7e3a86cb9141be519d24c8faa981b5" + dependencies: + is-buffer "^1.0.2" + +kind-of@^3.0.2, kind-of@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + dependencies: + is-buffer "^1.1.5" + +lazy-cache@^0.2.3: + version "0.2.7" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-0.2.7.tgz#7feddf2dcb6edb77d11ef1d117ab5ffdf0ab1b65" + +lazy-cache@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" + +lcid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + dependencies: + invert-kv "^1.0.0" + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +load-json-file@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + strip-bom "^3.0.0" + +loader-runner@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2" + +loader-utils@^0.2.16: + version "0.2.17" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348" + dependencies: + big.js "^3.1.3" + emojis-list "^2.0.0" + json5 "^0.5.0" + object-assign "^4.0.1" + +loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd" + dependencies: + big.js "^3.1.3" + emojis-list "^2.0.0" + json5 "^0.5.0" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +lodash.assign@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" + +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + +lodash.clonedeep@^4.3.2: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + +lodash.mergewith@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz#150cf0a16791f5903b8891eab154609274bdea55" + +lodash.tail@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.tail/-/lodash.tail-4.1.1.tgz#d2333a36d9e7717c8ad2f7cacafec7c32b444664" + +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + +lodash@^4.0.0, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@~4.17.4: + version "4.17.4" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" + +loglevel@^1.4.1: + version "1.6.0" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.0.tgz#ae0caa561111498c5ba13723d6fb631d24003934" + +longest@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" + +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" + dependencies: + js-tokens "^3.0.0" + +loud-rejection@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + +lower-case@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" + +lru-cache@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.1.tgz#622e32e82488b49279114a4f9ecf45e7cd6bba55" + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +macaddress@^0.2.8: + version "0.2.8" + resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12" + +make-dir@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.1.0.tgz#19b4369fe48c116f53c2af95ad102c0e39e85d51" + dependencies: + pify "^3.0.0" + +map-obj@^1.0.0, map-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + +math-expression-evaluator@^1.2.14: + version "1.2.17" + resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac" + +md5.js@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.4.tgz#e9bdbde94a20a5ac18b04340fc5764d5b09d901d" + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + +mem@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" + dependencies: + mimic-fn "^1.0.0" + +memory-fs@^0.4.0, memory-fs@~0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +meow@^3.3.0, meow@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + dependencies: + camelcase-keys "^2.0.0" + decamelize "^1.1.2" + loud-rejection "^1.0.0" + map-obj "^1.0.1" + minimist "^1.1.3" + normalize-package-data "^2.3.4" + object-assign "^4.0.1" + read-pkg-up "^1.0.1" + redent "^1.0.0" + trim-newlines "^1.0.0" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + +micromatch@^2.1.5, micromatch@^2.3.11: + version "2.3.11" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" + dependencies: + arr-diff "^2.0.0" + array-unique "^0.2.1" + braces "^1.8.2" + expand-brackets "^0.1.4" + extglob "^0.3.1" + filename-regex "^2.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.1" + kind-of "^3.0.2" + normalize-path "^2.0.1" + object.omit "^2.0.0" + parse-glob "^3.0.4" + regex-cache "^0.4.2" + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +"mime-db@>= 1.30.0 < 2": + version "1.32.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.32.0.tgz#485b3848b01a3cda5f968b4882c0771e58e09414" + +mime-db@~1.30.0: + version "1.30.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" + +mime-types@^2.1.12, mime-types@~2.1.15, mime-types@~2.1.16, mime-types@~2.1.17, mime-types@~2.1.7: + version "2.1.17" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a" + dependencies: + mime-db "~1.30.0" + +mime@1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" + +mime@^1.4.1, mime@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + +mimic-fn@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" + +minimalistic-assert@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + +"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.2: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +minimist@^1.1.3, minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + +mixin-object@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/mixin-object/-/mixin-object-2.0.1.tgz#4fb949441dab182540f1fe035ba60e1947a5e57e" + dependencies: + for-in "^0.1.3" + is-extendable "^0.1.1" + +mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +mobx-react-router@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/mobx-react-router/-/mobx-react-router-4.0.1.tgz#72f67b5c637d187595415500e8afa5cddb2231ab" + +mobx-react@^4.3.5: + version "4.3.5" + resolved "https://registry.yarnpkg.com/mobx-react/-/mobx-react-4.3.5.tgz#76853f2f2ef4a6f960c374bcd9f01e875929c04c" + dependencies: + hoist-non-react-statics "^2.3.1" + +mobx@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/mobx/-/mobx-3.3.2.tgz#e912a9f7c82b2df69f1abe6515ecaa551828a024" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + +multicast-dns-service-types@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" + +multicast-dns@^6.0.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.2.1.tgz#c5035defa9219d30640558a49298067352098060" + dependencies: + dns-packet "^1.0.1" + thunky "^0.1.0" + +nan@^2.3.0, nan@^2.3.2: + version "2.8.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.8.0.tgz#ed715f3fe9de02b57a5e6252d90a96675e1f085a" + +ncname@1.0.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/ncname/-/ncname-1.0.0.tgz#5b57ad18b1ca092864ef62b0b1ed8194f383b71c" + dependencies: + xml-char-classes "^1.0.0" + +negotiator@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" + +no-case@^2.2.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" + dependencies: + lower-case "^1.1.1" + +node-fetch@^1.0.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" + dependencies: + encoding "^0.1.11" + is-stream "^1.0.1" + +node-forge@0.6.33: + version "0.6.33" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.6.33.tgz#463811879f573d45155ad6a9f43dc296e8e85ebc" + +node-gyp@^3.3.1: + version "3.6.2" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.6.2.tgz#9bfbe54562286284838e750eac05295853fa1c60" + dependencies: + fstream "^1.0.0" + glob "^7.0.3" + graceful-fs "^4.1.2" + minimatch "^3.0.2" + mkdirp "^0.5.0" + nopt "2 || 3" + npmlog "0 || 1 || 2 || 3 || 4" + osenv "0" + request "2" + rimraf "2" + semver "~5.3.0" + tar "^2.0.0" + which "1" + +node-libs-browser@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.1.0.tgz#5f94263d404f6e44767d726901fff05478d600df" + dependencies: + assert "^1.1.1" + browserify-zlib "^0.2.0" + buffer "^4.3.0" + console-browserify "^1.1.0" + constants-browserify "^1.0.0" + crypto-browserify "^3.11.0" + domain-browser "^1.1.1" + events "^1.0.0" + https-browserify "^1.0.0" + os-browserify "^0.3.0" + path-browserify "0.0.0" + process "^0.11.10" + punycode "^1.2.4" + querystring-es3 "^0.2.0" + readable-stream "^2.3.3" + stream-browserify "^2.0.1" + stream-http "^2.7.2" + string_decoder "^1.0.0" + timers-browserify "^2.0.4" + tty-browserify "0.0.0" + url "^0.11.0" + util "^0.10.3" + vm-browserify "0.0.4" + +node-pre-gyp@^0.6.39: + version "0.6.39" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz#c00e96860b23c0e1420ac7befc5044e1d78d8649" + dependencies: + detect-libc "^1.0.2" + hawk "3.1.3" + mkdirp "^0.5.1" + nopt "^4.0.1" + npmlog "^4.0.2" + rc "^1.1.7" + request "2.81.0" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^2.2.1" + tar-pack "^3.4.0" + +node-sass@^4.7.2: + version "4.7.2" + resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.7.2.tgz#9366778ba1469eb01438a9e8592f4262bcb6794e" + dependencies: + async-foreach "^0.1.3" + chalk "^1.1.1" + cross-spawn "^3.0.0" + gaze "^1.0.0" + get-stdin "^4.0.1" + glob "^7.0.3" + in-publish "^2.0.0" + lodash.assign "^4.2.0" + lodash.clonedeep "^4.3.2" + lodash.mergewith "^4.6.0" + meow "^3.7.0" + mkdirp "^0.5.1" + nan "^2.3.2" + node-gyp "^3.3.1" + npmlog "^4.0.0" + request "~2.79.0" + sass-graph "^2.2.4" + stdout-stream "^1.4.0" + "true-case-path" "^1.0.2" + +"nopt@2 || 3": + version "3.0.6" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + dependencies: + abbrev "1" + +nopt@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + dependencies: + abbrev "1" + osenv "^0.1.4" + +normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: + version "2.4.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" + dependencies: + hosted-git-info "^2.1.4" + is-builtin-module "^1.0.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.0.0, normalize-path@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + +normalize-url@^1.4.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" + dependencies: + object-assign "^4.0.1" + prepend-http "^1.0.0" + query-string "^4.1.0" + sort-keys "^1.0.0" + +npm-font-open-sans@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/npm-font-open-sans/-/npm-font-open-sans-1.1.0.tgz#8c27a56e43872747b8448dcc30653a649866c6ef" + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + dependencies: + path-key "^2.0.0" + +"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +nth-check@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4" + dependencies: + boolbase "~1.0.0" + +num2fraction@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + +oauth-sign@~0.8.1, oauth-sign@~0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" + +object-assign@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" + +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +object-keys@^1.0.8: + version "1.0.11" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d" + +object.omit@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" + dependencies: + for-own "^0.1.4" + is-extendable "^0.1.1" + +obuf@^1.0.0, obuf@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.1.tgz#104124b6c602c6796881a042541d36db43a5264e" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" + +once@^1.3.0, once@^1.3.3: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +opn@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.1.0.tgz#72ce2306a17dbea58ff1041853352b4a8fc77519" + dependencies: + is-wsl "^1.1.0" + +original@>=0.0.5: + version "1.0.0" + resolved "https://registry.yarnpkg.com/original/-/original-1.0.0.tgz#9147f93fa1696d04be61e01bd50baeaca656bd3b" + dependencies: + url-parse "1.0.x" + +os-browserify@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + +os-locale@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + dependencies: + lcid "^1.0.0" + +os-locale@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" + dependencies: + execa "^0.7.0" + lcid "^1.0.0" + mem "^1.1.0" + +os-tmpdir@^1.0.0, os-tmpdir@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +osenv@0, osenv@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + +p-limit@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.1.0.tgz#b07ff2d9a5d88bec806035895a2bab66a27988bc" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + dependencies: + p-limit "^1.1.0" + +p-map@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" + +pako@~1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258" + +param-case@2.1.x: + version "2.1.1" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" + dependencies: + no-case "^2.2.0" + +parse-asn1@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.0.tgz#37c4f9b7ed3ab65c74817b5f2480937fbf97c712" + dependencies: + asn1.js "^4.0.0" + browserify-aes "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + +parse-glob@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" + dependencies: + glob-base "^0.3.0" + is-dotfile "^1.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.0" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + dependencies: + error-ex "^1.2.0" + +parseurl@~1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" + +path-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + +path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +path-is-inside@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + +path-key@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + +path-to-regexp@^1.2.0, path-to-regexp@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d" + dependencies: + isarray "0.0.1" + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +path-type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" + dependencies: + pify "^2.0.0" + +pbkdf2@^3.0.3: + version "3.0.14" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.14.tgz#a35e13c64799b06ce15320f459c230e68e73bade" + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +performance-now@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + +pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + dependencies: + find-up "^2.1.0" + +portfinder@^1.0.9: + version "1.0.13" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.13.tgz#bb32ecd87c27104ae6ee44b5a3ccbf0ebb1aede9" + dependencies: + async "^1.5.2" + debug "^2.2.0" + mkdirp "0.5.x" + +postcss-calc@^5.2.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e" + dependencies: + postcss "^5.0.2" + postcss-message-helpers "^2.0.0" + reduce-css-calc "^1.2.6" + +postcss-colormin@^2.1.8: + version "2.2.2" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-2.2.2.tgz#6631417d5f0e909a3d7ec26b24c8a8d1e4f96e4b" + dependencies: + colormin "^1.0.5" + postcss "^5.0.13" + postcss-value-parser "^3.2.3" + +postcss-convert-values@^2.3.4: + version "2.6.1" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz#bbd8593c5c1fd2e3d1c322bb925dcae8dae4d62d" + dependencies: + postcss "^5.0.11" + postcss-value-parser "^3.1.2" + +postcss-discard-comments@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz#befe89fafd5b3dace5ccce51b76b81514be00e3d" + dependencies: + postcss "^5.0.14" + +postcss-discard-duplicates@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz#b9abf27b88ac188158a5eb12abcae20263b91932" + dependencies: + postcss "^5.0.4" + +postcss-discard-empty@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz#d2b4bd9d5ced5ebd8dcade7640c7d7cd7f4f92b5" + dependencies: + postcss "^5.0.14" + +postcss-discard-overridden@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz#8b1eaf554f686fb288cd874c55667b0aa3668d58" + dependencies: + postcss "^5.0.16" + +postcss-discard-unused@^2.2.1: + version "2.2.3" + resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz#bce30b2cc591ffc634322b5fb3464b6d934f4433" + dependencies: + postcss "^5.0.14" + uniqs "^2.0.0" + +postcss-filter-plugins@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz#6d85862534d735ac420e4a85806e1f5d4286d84c" + dependencies: + postcss "^5.0.4" + uniqid "^4.0.0" + +postcss-merge-idents@^2.1.5: + version "2.1.7" + resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz#4c5530313c08e1d5b3bbf3d2bbc747e278eea270" + dependencies: + has "^1.0.1" + postcss "^5.0.10" + postcss-value-parser "^3.1.1" + +postcss-merge-longhand@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz#23d90cd127b0a77994915332739034a1a4f3d658" + dependencies: + postcss "^5.0.4" + +postcss-merge-rules@^2.0.3: + version "2.1.2" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz#d1df5dfaa7b1acc3be553f0e9e10e87c61b5f721" + dependencies: + browserslist "^1.5.2" + caniuse-api "^1.5.2" + postcss "^5.0.4" + postcss-selector-parser "^2.2.2" + vendors "^1.0.0" + +postcss-message-helpers@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz#a4f2f4fab6e4fe002f0aed000478cdf52f9ba60e" + +postcss-minify-font-values@^1.0.2: + version "1.0.5" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz#4b58edb56641eba7c8474ab3526cafd7bbdecb69" + dependencies: + object-assign "^4.0.1" + postcss "^5.0.4" + postcss-value-parser "^3.0.2" + +postcss-minify-gradients@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz#5dbda11373703f83cfb4a3ea3881d8d75ff5e6e1" + dependencies: + postcss "^5.0.12" + postcss-value-parser "^3.3.0" + +postcss-minify-params@^1.0.4: + version "1.2.2" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz#ad2ce071373b943b3d930a3fa59a358c28d6f1f3" + dependencies: + alphanum-sort "^1.0.1" + postcss "^5.0.2" + postcss-value-parser "^3.0.2" + uniqs "^2.0.0" + +postcss-minify-selectors@^2.0.4: + version "2.1.1" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz#b2c6a98c0072cf91b932d1a496508114311735bf" + dependencies: + alphanum-sort "^1.0.2" + has "^1.0.1" + postcss "^5.0.14" + postcss-selector-parser "^2.0.0" + +postcss-modules-extract-imports@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz#66140ecece38ef06bf0d3e355d69bf59d141ea85" + dependencies: + postcss "^6.0.1" + +postcss-modules-local-by-default@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz#f7d80c398c5a393fa7964466bd19500a7d61c069" + dependencies: + css-selector-tokenizer "^0.7.0" + postcss "^6.0.1" + +postcss-modules-scope@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz#d6ea64994c79f97b62a72b426fbe6056a194bb90" + dependencies: + css-selector-tokenizer "^0.7.0" + postcss "^6.0.1" + +postcss-modules-values@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz#ecffa9d7e192518389f42ad0e83f72aec456ea20" + dependencies: + icss-replace-symbols "^1.1.0" + postcss "^6.0.1" + +postcss-normalize-charset@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz#ef9ee71212d7fe759c78ed162f61ed62b5cb93f1" + dependencies: + postcss "^5.0.5" + +postcss-normalize-url@^3.0.7: + version "3.0.8" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz#108f74b3f2fcdaf891a2ffa3ea4592279fc78222" + dependencies: + is-absolute-url "^2.0.0" + normalize-url "^1.4.0" + postcss "^5.0.14" + postcss-value-parser "^3.2.3" + +postcss-ordered-values@^2.1.0: + version "2.2.3" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz#eec6c2a67b6c412a8db2042e77fe8da43f95c11d" + dependencies: + postcss "^5.0.4" + postcss-value-parser "^3.0.1" + +postcss-reduce-idents@^2.2.2: + version "2.4.0" + resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz#c2c6d20cc958284f6abfbe63f7609bf409059ad3" + dependencies: + postcss "^5.0.4" + postcss-value-parser "^3.0.2" + +postcss-reduce-initial@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz#68f80695f045d08263a879ad240df8dd64f644ea" + dependencies: + postcss "^5.0.4" + +postcss-reduce-transforms@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz#ff76f4d8212437b31c298a42d2e1444025771ae1" + dependencies: + has "^1.0.1" + postcss "^5.0.8" + postcss-value-parser "^3.0.1" + +postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz#f9437788606c3c9acee16ffe8d8b16297f27bb90" + dependencies: + flatten "^1.0.2" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-svgo@^2.1.1: + version "2.1.6" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.6.tgz#b6df18aa613b666e133f08adb5219c2684ac108d" + dependencies: + is-svg "^2.0.0" + postcss "^5.0.14" + postcss-value-parser "^3.2.3" + svgo "^0.7.0" + +postcss-unique-selectors@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz#981d57d29ddcb33e7b1dfe1fd43b8649f933ca1d" + dependencies: + alphanum-sort "^1.0.1" + postcss "^5.0.4" + uniqs "^2.0.0" + +postcss-value-parser@^3.0.1, postcss-value-parser@^3.0.2, postcss-value-parser@^3.1.1, postcss-value-parser@^3.1.2, postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15" + +postcss-zindex@^2.0.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-2.2.0.tgz#d2109ddc055b91af67fc4cb3b025946639d2af22" + dependencies: + has "^1.0.1" + postcss "^5.0.4" + uniqs "^2.0.0" + +postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.2, postcss@^5.0.4, postcss@^5.0.5, postcss@^5.0.6, postcss@^5.0.8, postcss@^5.2.16: + version "5.2.18" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.18.tgz#badfa1497d46244f6390f58b319830d9107853c5" + dependencies: + chalk "^1.1.3" + js-base64 "^2.1.9" + source-map "^0.5.6" + supports-color "^3.2.3" + +postcss@^6.0.1: + version "6.0.14" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.14.tgz#5534c72114739e75d0afcf017db853099f562885" + dependencies: + chalk "^2.3.0" + source-map "^0.6.1" + supports-color "^4.4.0" + +prepend-http@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + +preserve@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" + +pretty-error@^2.0.2: + version "2.1.1" + resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.1.tgz#5f4f87c8f91e5ae3f3ba87ab4cf5e03b1a17f1a3" + dependencies: + renderkid "^2.0.1" + utila "~0.4" + +private@^0.1.6, private@^0.1.7: + version "0.1.8" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" + +process-nextick-args@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + +promise@^7.1.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + dependencies: + asap "~2.0.3" + +prop-types@^15.5.4, prop-types@^15.6.0: + version "15.6.0" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856" + dependencies: + fbjs "^0.8.16" + loose-envify "^1.3.1" + object-assign "^4.1.1" + +proxy-addr@~2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.2.tgz#6571504f47bb988ec8180253f85dd7e14952bdec" + dependencies: + forwarded "~0.1.2" + ipaddr.js "1.5.2" + +prr@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a" + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + +public-encrypt@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.0.tgz#39f699f3a46560dd5ebacbca693caf7c65c18cc6" + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + +punycode@^1.2.4, punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + +q@^1.1.2: + version "1.5.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" + +qs@6.5.1, qs@~6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" + +qs@~6.3.0: + version "6.3.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.2.tgz#e75bd5f6e268122a2a0e0bda630b2550c166502c" + +qs@~6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" + +query-string@^4.1.0: + version "4.3.4" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" + dependencies: + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + +querystring-es3@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + +querystringify@0.0.x: + version "0.0.4" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-0.0.4.tgz#0cf7f84f9463ff0ae51c4c4b142d95be37724d9c" + +querystringify@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-1.0.0.tgz#6286242112c5b712fa654e526652bf6a13ff05cb" + +randomatic@^1.1.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c" + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.5.tgz#dc009a246b8d09a177b4b7a0ae77bc570f4b1b79" + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.3.tgz#b96b7df587f01dd91726c418f30553b1418e3d62" + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +range-parser@^1.0.3, range-parser@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + +raw-body@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89" + dependencies: + bytes "3.0.0" + http-errors "1.6.2" + iconv-lite "0.4.19" + unpipe "1.0.0" + +rc@^1.1.7: + version "1.2.2" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.2.tgz#d8ce9cb57e8d64d9c7badd9876c7c34cbe3c7077" + dependencies: + deep-extend "~0.4.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +react-dom@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.2.0.tgz#69003178601c0ca19b709b33a83369fe6124c044" + dependencies: + fbjs "^0.8.16" + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.0" + +react-route@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/react-route/-/react-route-1.0.3.tgz#44220aa1c7f17a5af91f11cfc256e6ad4fbd535d" + dependencies: + component-emitter "^1.2.0" + object-assign "^3.0.0" + path-to-regexp "^1.2.0" + +react-router-dom@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-4.2.2.tgz#c8a81df3adc58bba8a76782e946cbd4eae649b8d" + dependencies: + history "^4.7.2" + invariant "^2.2.2" + loose-envify "^1.3.1" + prop-types "^15.5.4" + react-router "^4.2.0" + warning "^3.0.0" + +react-router@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-4.2.0.tgz#61f7b3e3770daeb24062dae3eedef1b054155986" + dependencies: + history "^4.7.2" + hoist-non-react-statics "^2.3.0" + invariant "^2.2.2" + loose-envify "^1.3.1" + path-to-regexp "^1.7.0" + prop-types "^15.5.4" + warning "^3.0.0" + +react@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/react/-/react-16.2.0.tgz#a31bd2dab89bff65d42134fa187f24d054c273ba" + dependencies: + fbjs "^0.8.16" + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.0" + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" + dependencies: + find-up "^2.0.0" + read-pkg "^2.0.0" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +read-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" + dependencies: + load-json-file "^2.0.0" + normalize-package-data "^2.3.2" + path-type "^2.0.0" + +readable-stream@1.0: + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.2.6, readable-stream@^2.2.9, readable-stream@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + safe-buffer "~5.1.1" + string_decoder "~1.0.3" + util-deprecate "~1.0.1" + +readdirp@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" + dependencies: + graceful-fs "^4.1.2" + minimatch "^3.0.2" + readable-stream "^2.0.2" + set-immediate-shim "^1.0.1" + +redent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + dependencies: + indent-string "^2.1.0" + strip-indent "^1.0.1" + +reduce-css-calc@^1.2.6: + version "1.3.0" + resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716" + dependencies: + balanced-match "^0.4.2" + math-expression-evaluator "^1.2.14" + reduce-function-call "^1.0.1" + +reduce-function-call@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.2.tgz#5a200bf92e0e37751752fe45b0ab330fd4b6be99" + dependencies: + balanced-match "^0.4.2" + +regenerate@^1.2.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.3.tgz#0c336d3980553d755c39b586ae3b20aa49c82b7f" + +regenerator-runtime@^0.10.5: + version "0.10.5" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" + +regenerator-runtime@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz#7e54fe5b5ccd5d6624ea6255c3473be090b802e1" + +regenerator-transform@^0.10.0: + version "0.10.1" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd" + dependencies: + babel-runtime "^6.18.0" + babel-types "^6.19.0" + private "^0.1.6" + +regex-cache@^0.4.2: + version "0.4.4" + resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" + dependencies: + is-equal-shallow "^0.1.3" + +regexpu-core@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b" + dependencies: + regenerate "^1.2.1" + regjsgen "^0.2.0" + regjsparser "^0.1.4" + +regexpu-core@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240" + dependencies: + regenerate "^1.2.1" + regjsgen "^0.2.0" + regjsparser "^0.1.4" + +regjsgen@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" + +regjsparser@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" + dependencies: + jsesc "~0.5.0" + +relateurl@0.2.x: + version "0.2.7" + resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + +renderkid@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.1.tgz#898cabfc8bede4b7b91135a3ffd323e58c0db319" + dependencies: + css-select "^1.1.0" + dom-converter "~0.1" + htmlparser2 "~3.3.0" + strip-ansi "^3.0.0" + utila "~0.3" + +repeat-element@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" + +repeat-string@^1.5.2: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + dependencies: + is-finite "^1.0.0" + +request@2: + version "2.83.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356" + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.6.0" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.1" + forever-agent "~0.6.1" + form-data "~2.3.1" + har-validator "~5.0.3" + hawk "~6.0.2" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.17" + oauth-sign "~0.8.2" + performance-now "^2.1.0" + qs "~6.5.1" + safe-buffer "^5.1.1" + stringstream "~0.0.5" + tough-cookie "~2.3.3" + tunnel-agent "^0.6.0" + uuid "^3.1.0" + +request@2.81.0: + version "2.81.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.1.1" + har-validator "~4.2.1" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + oauth-sign "~0.8.1" + performance-now "^0.2.0" + qs "~6.4.0" + safe-buffer "^5.0.1" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "^0.6.0" + uuid "^3.0.0" + +request@~2.79.0: + version "2.79.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + caseless "~0.11.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.1.1" + har-validator "~2.0.6" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + oauth-sign "~0.8.1" + qs "~6.3.0" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "~0.4.1" + uuid "^3.0.0" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + +requires-port@1.0.x, requires-port@1.x.x, requires-port@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + +resolve-cwd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" + dependencies: + resolve-from "^3.0.0" + +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + +resolve-pathname@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-2.2.0.tgz#7e9ae21ed815fd63ab189adeee64dc831eefa879" + +right-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" + dependencies: + align-text "^0.1.1" + +rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.6.1: + version "2.6.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" + dependencies: + glob "^7.0.5" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.1.tgz#0f4584295c53a3628af7e6d79aca21ce57d1c6e7" + dependencies: + hash-base "^2.0.0" + inherits "^2.0.1" + +safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" + +sass-graph@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.4.tgz#13fbd63cd1caf0908b9fd93476ad43a51d1e0b49" + dependencies: + glob "^7.0.0" + lodash "^4.0.0" + scss-tokenizer "^0.2.3" + yargs "^7.0.0" + +sass-loader@^6.0.6: + version "6.0.6" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-6.0.6.tgz#e9d5e6c1f155faa32a4b26d7a9b7107c225e40f9" + dependencies: + async "^2.1.5" + clone-deep "^0.3.0" + loader-utils "^1.0.1" + lodash.tail "^4.1.1" + pify "^3.0.0" + +sax@~1.2.1: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + +schema-utils@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.3.0.tgz#f5877222ce3e931edae039f17eb3716e7137f8cf" + dependencies: + ajv "^5.0.0" + +scss-tokenizer@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1" + dependencies: + js-base64 "^2.1.8" + source-map "^0.4.2" + +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + +selfsigned@^1.9.1: + version "1.10.1" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.1.tgz#bf8cb7b83256c4551e31347c6311778db99eec52" + dependencies: + node-forge "0.6.33" + +"semver@2 || 3 || 4 || 5", semver@^5.3.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" + +semver@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" + +send@0.16.1: + version "0.16.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.16.1.tgz#a70e1ca21d1382c11d0d9f6231deb281080d7ab3" + dependencies: + 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" + fresh "0.5.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" + +serve-index@^1.7.2: + version "1.9.1" + resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + dependencies: + accepts "~1.3.4" + batch "0.6.1" + debug "2.6.9" + escape-html "~1.0.3" + http-errors "~1.6.2" + mime-types "~2.1.17" + parseurl "~1.3.2" + +serve-static@1.13.1: + version "1.13.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.1.tgz#4c57d53404a761d8f2e7c1e8a18a47dbf278a719" + dependencies: + encodeurl "~1.0.1" + escape-html "~1.0.3" + parseurl "~1.3.2" + send "0.16.1" + +set-blocking@^2.0.0, set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + +set-immediate-shim@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + +setimmediate@^1.0.4, setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + +setprototypeof@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.9" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.9.tgz#98f64880474b74f4a38b8da9d3c0f2d104633e7d" + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +shallow-clone@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-0.1.2.tgz#5909e874ba77106d73ac414cfec1ffca87d97060" + dependencies: + is-extendable "^0.1.1" + kind-of "^2.0.1" + lazy-cache "^0.2.3" + mixin-object "^2.0.1" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + +signal-exit@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + +sntp@1.x.x: + version "1.0.9" + resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" + dependencies: + hoek "2.x.x" + +sntp@2.x.x: + version "2.1.0" + resolved "https://registry.yarnpkg.com/sntp/-/sntp-2.1.0.tgz#2c6cec14fedc2222739caf9b5c3d85d1cc5a2cc8" + dependencies: + hoek "4.x.x" + +sockjs-client@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.4.tgz#5babe386b775e4cf14e7520911452654016c8b12" + dependencies: + debug "^2.6.6" + eventsource "0.1.6" + faye-websocket "~0.11.0" + inherits "^2.0.1" + json3 "^3.3.2" + url-parse "^1.1.8" + +sockjs@0.3.18: + version "0.3.18" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.18.tgz#d9b289316ca7df77595ef299e075f0f937eb4207" + dependencies: + faye-websocket "^0.10.0" + uuid "^2.0.2" + +sort-keys@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" + dependencies: + is-plain-obj "^1.0.0" + +source-list-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.0.tgz#aaa47403f7b245a92fbc97ea08f250d6087ed085" + +source-map-support@^0.4.15: + version "0.4.18" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f" + dependencies: + source-map "^0.5.6" + +source-map@0.5.x, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + +source-map@^0.4.2: + version "0.4.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" + dependencies: + amdefine ">=0.0.4" + +source-map@^0.6.1, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + +spdx-correct@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40" + dependencies: + spdx-license-ids "^1.0.2" + +spdx-expression-parse@~1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c" + +spdx-license-ids@^1.0.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57" + +spdy-transport@^2.0.18: + version "2.0.20" + resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-2.0.20.tgz#735e72054c486b2354fe89e702256004a39ace4d" + dependencies: + debug "^2.6.8" + detect-node "^2.0.3" + hpack.js "^2.1.6" + obuf "^1.1.1" + readable-stream "^2.2.9" + safe-buffer "^5.0.1" + wbuf "^1.7.2" + +spdy@^3.4.1: + version "3.4.7" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-3.4.7.tgz#42ff41ece5cc0f99a3a6c28aabb73f5c3b03acbc" + dependencies: + debug "^2.6.8" + handle-thing "^1.2.5" + http-deceiver "^1.2.7" + safe-buffer "^5.0.1" + select-hose "^2.0.0" + spdy-transport "^2.0.18" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + +sshpk@^1.7.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3" + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + dashdash "^1.12.0" + getpass "^0.1.1" + optionalDependencies: + bcrypt-pbkdf "^1.0.0" + ecc-jsbn "~0.1.1" + jsbn "~0.1.0" + tweetnacl "~0.14.0" + +"statuses@>= 1.3.1 < 2": + version "1.4.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" + +statuses@~1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" + +stdout-stream@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.0.tgz#a2c7c8587e54d9427ea9edb3ac3f2cd522df378b" + dependencies: + readable-stream "^2.0.1" + +stream-browserify@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" + dependencies: + inherits "~2.0.1" + readable-stream "^2.0.2" + +stream-http@^2.7.2: + version "2.7.2" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.7.2.tgz#40a050ec8dc3b53b33d9909415c02c0bf1abfbad" + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.1" + readable-stream "^2.2.6" + to-arraybuffer "^1.0.0" + xtend "^4.0.0" + +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + +string-width@^1.0.1, string-width@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +string-width@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string_decoder@^1.0.0, string_decoder@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" + dependencies: + safe-buffer "~5.1.0" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + +stringstream@~0.0.4, stringstream@~0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + dependencies: + ansi-regex "^3.0.0" + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + dependencies: + is-utf8 "^0.2.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + +strip-indent@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + dependencies: + get-stdin "^4.0.1" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + +style-loader@^0.19.0: + version "0.19.0" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.19.0.tgz#7258e788f0fee6a42d710eaf7d6c2412a4c50759" + dependencies: + loader-utils "^1.0.2" + schema-utils "^0.3.0" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +supports-color@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" + dependencies: + has-flag "^1.0.0" + +supports-color@^4.0.0, supports-color@^4.2.1, supports-color@^4.4.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b" + dependencies: + has-flag "^2.0.0" + +svgo@^0.7.0: + version "0.7.2" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5" + dependencies: + coa "~1.0.1" + colors "~1.1.2" + csso "~2.3.1" + js-yaml "~3.7.0" + mkdirp "~0.5.1" + sax "~1.2.1" + whet.extend "~0.9.9" + +tapable@^0.2.7: + version "0.2.8" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.8.tgz#99372a5c999bf2df160afc0d74bed4f47948cd22" + +tar-pack@^3.4.0: + version "3.4.1" + resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.1.tgz#e1dbc03a9b9d3ba07e896ad027317eb679a10a1f" + dependencies: + debug "^2.2.0" + fstream "^1.0.10" + fstream-ignore "^1.0.5" + once "^1.3.3" + readable-stream "^2.1.4" + rimraf "^2.5.1" + tar "^2.2.1" + uid-number "^0.0.6" + +tar@^2.0.0, tar@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" + dependencies: + block-stream "*" + fstream "^1.0.2" + inherits "2" + +thunky@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/thunky/-/thunky-0.1.0.tgz#bf30146824e2b6e67b0f2d7a4ac8beb26908684e" + +time-stamp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-2.0.0.tgz#95c6a44530e15ba8d6f4a3ecb8c3a3fac46da357" + +timers-browserify@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.4.tgz#96ca53f4b794a5e7c0e1bd7cc88a372298fa01e6" + dependencies: + setimmediate "^1.0.4" + +to-arraybuffer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + +to-fast-properties@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + +toposort@^1.0.0: + version "1.0.6" + resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.6.tgz#c31748e55d210effc00fdcdc7d6e68d7d7bb9cec" + +tough-cookie@~2.3.0, tough-cookie@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561" + dependencies: + punycode "^1.4.1" + +trim-newlines@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + +trim-right@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + +"true-case-path@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.2.tgz#7ec91130924766c7f573be3020c34f8fdfd00d62" + dependencies: + glob "^6.0.4" + +tty-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + dependencies: + safe-buffer "^5.0.1" + +tunnel-agent@~0.4.1: + version "0.4.3" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + +type-is@~1.6.15: + version "1.6.15" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410" + dependencies: + media-typer "0.3.0" + mime-types "~2.1.15" + +ua-parser-js@^0.7.9: + version "0.7.17" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.17.tgz#e9ec5f9498b9ec910e7ae3ac626a805c4d09ecac" + +uglify-js@3.2.x: + version "3.2.0" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.2.0.tgz#cb411ee4ca0e0cadbfe3a4e1a1da97e6fa0d19c1" + dependencies: + commander "~2.12.1" + source-map "~0.6.1" + +uglify-js@^2.8.29: + version "2.8.29" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" + dependencies: + source-map "~0.5.1" + yargs "~3.10.0" + optionalDependencies: + uglify-to-browserify "~1.0.0" + +uglify-to-browserify@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" + +uglifyjs-webpack-plugin@^0.4.6: + version "0.4.6" + resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz#b951f4abb6bd617e66f63eb891498e391763e309" + dependencies: + source-map "^0.5.6" + uglify-js "^2.8.29" + webpack-sources "^1.0.1" + +uid-number@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" + +uniq@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" + +uniqid@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/uniqid/-/uniqid-4.1.1.tgz#89220ddf6b751ae52b5f72484863528596bb84c1" + dependencies: + macaddress "^0.2.8" + +uniqs@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + +upper-case@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" + +url-loader@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-0.6.2.tgz#a007a7109620e9d988d14bce677a1decb9a993f7" + dependencies: + loader-utils "^1.0.2" + mime "^1.4.1" + schema-utils "^0.3.0" + +url-parse@1.0.x: + version "1.0.5" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.0.5.tgz#0854860422afdcfefeb6c965c662d4800169927b" + dependencies: + querystringify "0.0.x" + requires-port "1.0.x" + +url-parse@^1.1.8: + version "1.2.0" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.2.0.tgz#3a19e8aaa6d023ddd27dcc44cb4fc8f7fec23986" + dependencies: + querystringify "~1.0.0" + requires-port "~1.0.0" + +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +util@0.10.3, util@^0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + dependencies: + inherits "2.0.1" + +utila@~0.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/utila/-/utila-0.3.3.tgz#d7e8e7d7e309107092b05f8d9688824d633a4226" + +utila@~0.4: + version "0.4.0" + resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + +uuid@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" + +uuid@^3.0.0, uuid@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" + +validate-npm-package-license@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" + dependencies: + spdx-correct "~1.0.0" + spdx-expression-parse "~1.0.0" + +value-equal@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-0.4.0.tgz#c5bdd2f54ee093c04839d71ce2e4758a6890abc7" + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + +vendors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22" + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +vm-browserify@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" + dependencies: + indexof "0.0.1" + +warning@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c" + dependencies: + loose-envify "^1.0.0" + +watchpack@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.4.0.tgz#4a1472bcbb952bd0a9bb4036801f954dfb39faac" + dependencies: + async "^2.1.2" + chokidar "^1.7.0" + graceful-fs "^4.1.2" + +wbuf@^1.1.0, wbuf@^1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.2.tgz#d697b99f1f59512df2751be42769c1580b5801fe" + dependencies: + minimalistic-assert "^1.0.0" + +webpack-dev-middleware@^1.11.0: + version "1.12.2" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz#f8fc1120ce3b4fc5680ceecb43d777966b21105e" + dependencies: + memory-fs "~0.4.1" + mime "^1.5.0" + path-is-absolute "^1.0.0" + range-parser "^1.0.3" + time-stamp "^2.0.0" + +webpack-dev-server@^2.9.5: + version "2.9.5" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-2.9.5.tgz#79336fba0087a66ae491f4869f6545775b18daa8" + dependencies: + ansi-html "0.0.7" + array-includes "^3.0.3" + bonjour "^3.5.0" + chokidar "^1.6.0" + compression "^1.5.2" + connect-history-api-fallback "^1.3.0" + debug "^3.1.0" + del "^3.0.0" + express "^4.16.2" + html-entities "^1.2.0" + http-proxy-middleware "~0.17.4" + import-local "^0.1.1" + internal-ip "1.2.0" + ip "^1.1.5" + killable "^1.0.0" + loglevel "^1.4.1" + opn "^5.1.0" + portfinder "^1.0.9" + selfsigned "^1.9.1" + serve-index "^1.7.2" + sockjs "0.3.18" + sockjs-client "1.1.4" + spdy "^3.4.1" + strip-ansi "^3.0.1" + supports-color "^4.2.1" + webpack-dev-middleware "^1.11.0" + yargs "^6.6.0" + +webpack-sources@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.1.0.tgz#a101ebae59d6507354d71d8013950a3a8b7a5a54" + dependencies: + source-list-map "^2.0.0" + source-map "~0.6.1" + +webpack@^3.9.1: + version "3.9.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.9.1.tgz#9a60aa544ed5d4d454c069e3f521aa007e02643c" + dependencies: + acorn "^5.0.0" + acorn-dynamic-import "^2.0.0" + ajv "^5.1.5" + ajv-keywords "^2.0.0" + async "^2.1.2" + enhanced-resolve "^3.4.0" + escope "^3.6.0" + interpret "^1.0.0" + json-loader "^0.5.4" + json5 "^0.5.1" + loader-runner "^2.3.0" + loader-utils "^1.1.0" + memory-fs "~0.4.1" + mkdirp "~0.5.0" + node-libs-browser "^2.0.0" + source-map "^0.5.3" + supports-color "^4.2.1" + tapable "^0.2.7" + uglifyjs-webpack-plugin "^0.4.6" + watchpack "^1.4.0" + webpack-sources "^1.0.1" + yargs "^8.0.2" + +websocket-driver@>=0.5.1: + version "0.7.0" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.0.tgz#0caf9d2d755d93aee049d4bdd0d3fe2cca2a24eb" + dependencies: + http-parser-js ">=0.4.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" + +whatwg-fetch@>=0.10.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84" + +whet.extend@~0.9.9: + version "0.9.9" + resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1" + +which-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + +which@1, which@^1.2.9: + version "1.3.0" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" + dependencies: + isexe "^2.0.0" + +wide-align@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710" + dependencies: + string-width "^1.0.2" + +window-size@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" + +wordwrap@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +xml-char-classes@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/xml-char-classes/-/xml-char-classes-1.0.0.tgz#64657848a20ffc5df583a42ad8a277b4512bbc4d" + +xtend@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + +y18n@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + +yargs-parser@^4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-4.2.1.tgz#29cceac0dc4f03c6c87b4a9f217dd18c9f74871c" + dependencies: + camelcase "^3.0.0" + +yargs-parser@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a" + dependencies: + camelcase "^3.0.0" + +yargs-parser@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9" + dependencies: + camelcase "^4.1.0" + +yargs@^6.6.0: + version "6.6.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208" + dependencies: + camelcase "^3.0.0" + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + os-locale "^1.4.0" + read-pkg-up "^1.0.1" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^1.0.2" + which-module "^1.0.0" + y18n "^3.2.1" + yargs-parser "^4.2.0" + +yargs@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8" + dependencies: + camelcase "^3.0.0" + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + os-locale "^1.4.0" + read-pkg-up "^1.0.1" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^1.0.2" + which-module "^1.0.0" + y18n "^3.2.1" + yargs-parser "^5.0.0" + +yargs@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-8.0.2.tgz#6299a9055b1cefc969ff7e79c1d918dceb22c360" + dependencies: + camelcase "^4.1.0" + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + os-locale "^2.0.0" + read-pkg-up "^2.0.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1" + yargs-parser "^7.0.0" + +yargs@~3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" + dependencies: + camelcase "^1.0.2" + cliui "^2.1.0" + decamelize "^1.0.0" + window-size "0.1.0" From c7f4a703229087628fa3c6d5c7fbdba57ba6b8a9 Mon Sep 17 00:00:00 2001 From: shankar2105 Date: Thu, 22 Feb 2018 18:52:39 +0530 Subject: [PATCH 2/9] fix/credential: remove credential Removed credentials --- safe_webrtc_example/app/constants.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/safe_webrtc_example/app/constants.js b/safe_webrtc_example/app/constants.js index 3f7f0a2..f589575 100644 --- a/safe_webrtc_example/app/constants.js +++ b/safe_webrtc_example/app/constants.js @@ -16,11 +16,11 @@ export default { CONFIG: { SERVER: { iceServers: [ - { url: 'stun:stun1.l.google.com:19302' }, + { url: 'stun:stun1.l.google.com:19302' }, // URL to STUN Server { url: 'turn:numb.viagenie.ca', - credential: 'string21', - username: 'shankar21mail@gmail.com' + credential: 'PASSWORD', // fill turn server password + username: 'USERNAME' // fill turn server username }, ] }, From b785a5dd618ef783292eba8ae15e8929f4b933a6 Mon Sep 17 00:00:00 2001 From: shankar2105 Date: Thu, 22 Feb 2018 19:19:28 +0530 Subject: [PATCH 3/9] Chore/readme: update readme.md Update readme.md for configuration of STUN and TURN server --- safe_webrtc_example/README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/safe_webrtc_example/README.md b/safe_webrtc_example/README.md index 49522cb..e6d069d 100644 --- a/safe_webrtc_example/README.md +++ b/safe_webrtc_example/README.md @@ -15,6 +15,25 @@ And then install Node.js dependencies. $ yarn ``` +# Configure STUN and TURN server + +Configure the STUN and TURN server in `app/constants.js` before building the application. + +``` +CONFIG: { + SERVER: { + iceServers: [ + { url: 'stun:stun1.l.google.com:19302' }, // URL to STUN Server + { + url: 'turn:numb.viagenie.ca', + credential: 'TURN_PASSWORD', // fill turn server password + username: 'TURN_USERNAME' // fill turn server username + }, + ] + }, +} +``` + # Build Bundle the application to host it on SAFE Network: From aca282d47b94288a6d05d53e24225a504239a5d8 Mon Sep 17 00:00:00 2001 From: shankar2105 Date: Fri, 23 Feb 2018 13:54:35 +0530 Subject: [PATCH 4/9] fix/fetch_publicNames: resolve fetch public name issue Resolve public name issues --- safe_webrtc_example/app/constants.js | 2 +- safe_webrtc_example/app/safe_comm.js | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/safe_webrtc_example/app/constants.js b/safe_webrtc_example/app/constants.js index f589575..2baa455 100644 --- a/safe_webrtc_example/app/constants.js +++ b/safe_webrtc_example/app/constants.js @@ -3,7 +3,7 @@ export default { DEFAULT_LOADING_DESC: 'Please wait...', CONN_MSGS: { INIT: 'Initialising connection', - SEND_INVITE: 'Invite sent. Wait to remote to accept it', + SEND_INVITE: 'Invite sent. Waiting for the remote peer to accept the connection', INVITE_ACCEPTED: 'Invite accepted. Establishing connection with remote', CALLING: 'Remote accepted the invite. Establishing connection with remote', }, diff --git a/safe_webrtc_example/app/safe_comm.js b/safe_webrtc_example/app/safe_comm.js index 1170994..7b0fc2a 100644 --- a/safe_webrtc_example/app/safe_comm.js +++ b/safe_webrtc_example/app/safe_comm.js @@ -26,6 +26,7 @@ const APP = { const ERROR_MSG = { ENTRY_NOT_FOUND: 'Core error: Routing client error -> Requested entry not found', + SYMMETRIC_DECIPHER_FAILURE: 'Core error: Symmetric decryption failed', }; const keySeparator = '-'; @@ -303,6 +304,9 @@ export default class SafeApi { const deckey = await window.safeMutableData.decrypt(this.pubNameCntr, key); resolve(utils.uint8ToStr(deckey)); } catch (err) { + if (err.message === ERROR_MSG.SYMMETRIC_DECIPHER_FAILURE) { + return resolve(''); + } utils.putLog('Get public names - decrypt keys error', err); reject(err); } @@ -324,7 +328,7 @@ export default class SafeApi { encPubNamesQ.push(decryptKey(publicNames[i])); } const decPubNames = await Promise.all(encPubNamesQ); - resolve(decPubNames); + resolve(decPubNames.filter(k => !!k)); } catch (err) { utils.putLog('Get public names error', err); reject(err); From a52d056fe73b5ffc584e7799a4a153ae2ea9b949 Mon Sep 17 00:00:00 2001 From: shankar2105 Date: Thu, 1 Mar 2018 14:28:49 +0530 Subject: [PATCH 5/9] fix/ux: improve UI/UX Improved UI/UX features of the application. Removed server details. Localised texts. --- safe_webrtc_example/README.md | 4 +- .../app/{appStore.js => app_store.js} | 237 ++++++---- .../app/components/active_public_name.js | 29 ++ safe_webrtc_example/app/components/app.js | 13 - .../app/components/bootstrap.js | 74 ++++ .../app/components/chat_room.js | 196 ++++++--- safe_webrtc_example/app/components/error.js | 28 -- safe_webrtc_example/app/components/home.js | 84 ++-- safe_webrtc_example/app/components/invites.js | 183 ++++++-- safe_webrtc_example/app/components/loader.js | 20 - .../app/components/new_chat.js | 153 ++++--- .../app/components/selected_pub_id.js | 41 -- .../app/components/switch_id.js | 108 ----- .../app/components/switch_public_name.js | 179 ++++++++ safe_webrtc_example/app/constants.js | 50 ++- safe_webrtc_example/app/images/call.svg | 39 ++ safe_webrtc_example/app/images/error.svg | 2 +- safe_webrtc_example/app/images/loader.svg | 1 + safe_webrtc_example/app/images/more.svg | 40 ++ .../app/images/notification.svg | 40 ++ safe_webrtc_example/app/main.js | 2 +- safe_webrtc_example/app/router.js | 12 +- safe_webrtc_example/app/safe_comm.js | 7 + safe_webrtc_example/app/sass/button.sass | 45 ++ safe_webrtc_example/app/sass/buttons.sass | 29 -- safe_webrtc_example/app/sass/chat_room.sass | 176 +++++--- safe_webrtc_example/app/sass/common.sass | 42 +- safe_webrtc_example/app/sass/components.sass | 409 +++++++++--------- safe_webrtc_example/app/sass/loader.sass | 31 -- safe_webrtc_example/app/sass/main.sass | 3 +- safe_webrtc_example/app/sass/variables.sass | 39 +- safe_webrtc_example/app/utils.js | 2 +- safe_webrtc_example/webpack.dev.config.js | 1 + 33 files changed, 1456 insertions(+), 863 deletions(-) rename safe_webrtc_example/app/{appStore.js => app_store.js} (67%) create mode 100644 safe_webrtc_example/app/components/active_public_name.js create mode 100644 safe_webrtc_example/app/components/bootstrap.js delete mode 100644 safe_webrtc_example/app/components/error.js delete mode 100644 safe_webrtc_example/app/components/loader.js delete mode 100644 safe_webrtc_example/app/components/selected_pub_id.js delete mode 100644 safe_webrtc_example/app/components/switch_id.js create mode 100644 safe_webrtc_example/app/components/switch_public_name.js create mode 100644 safe_webrtc_example/app/images/call.svg create mode 100644 safe_webrtc_example/app/images/loader.svg create mode 100644 safe_webrtc_example/app/images/more.svg create mode 100644 safe_webrtc_example/app/images/notification.svg create mode 100644 safe_webrtc_example/app/sass/button.sass delete mode 100644 safe_webrtc_example/app/sass/buttons.sass delete mode 100644 safe_webrtc_example/app/sass/loader.sass diff --git a/safe_webrtc_example/README.md b/safe_webrtc_example/README.md index e6d069d..e6e3bac 100644 --- a/safe_webrtc_example/README.md +++ b/safe_webrtc_example/README.md @@ -23,9 +23,9 @@ Configure the STUN and TURN server in `app/constants.js` before building the app CONFIG: { SERVER: { iceServers: [ - { url: 'stun:stun1.l.google.com:19302' }, // URL to STUN Server + { url: 'STUN_SERVER_URL' }, // fill STUN Server url { - url: 'turn:numb.viagenie.ca', + url: 'TURN_SERVER_URL', // fill turn server url credential: 'TURN_PASSWORD', // fill turn server password username: 'TURN_USERNAME' // fill turn server username }, diff --git a/safe_webrtc_example/app/appStore.js b/safe_webrtc_example/app/app_store.js similarity index 67% rename from safe_webrtc_example/app/appStore.js rename to safe_webrtc_example/app/app_store.js index 8d661b8..3b9410d 100644 --- a/safe_webrtc_example/app/appStore.js +++ b/safe_webrtc_example/app/app_store.js @@ -6,17 +6,23 @@ import SafeApi from './safe_comm'; export default class AppStore { @observable error = ''; - @observable loading = false; - @observable loaded = false; - @observable loaderDesc = CONST.UI.DEFAULT_LOADING_DESC; + @observable progress = ''; + @observable isAuthorised = false; @observable publicNames = []; + @observable activePublicName = ''; @observable invites = []; - @observable newInvites = 0; - @observable selectedPubName = ''; - @observable connectionState = CONST.CONN_STATE.INIT; + @observable invitesCount = 0; + @observable switchIDProgress = ''; + @observable switchIDError = ''; + @observable newChatProgress = ''; + @observable newChatError = ''; + @observable chatRoomProgress = ''; + @observable chatRoomError = ''; @observable isNwConnected = true; @observable isNwConnecting = false; + @observable connectionState = CONST.CONN_STATE.INIT; + @observable friendID = null; @observable uid = null; @observable initiater = null; @observable persona = null; @@ -31,8 +37,45 @@ export default class AppStore { @observable remoteAnswerCandidates = []; constructor() { - this.api = null; - this.isAuthorised = false; + this.app = null; + } + + timout(state) { + return new Promise(async (resolve, reject) => { + setTimeout(() => { + if (state) { + return resolve(); + } + return reject(); + }, 2000); + }); + } + + @action + reset() { + this.error = ''; + this.progress = ''; + } + + @action + resetSwitchIDState() { + this.switchIDError = ''; + this.switchIDProgress = ''; + } + + @action + resetNewChatState() { + this.newChatError = ''; + this.newChatProgress = ''; + } + + @action + nwStateCb(newState) { + if (newState === CONST.NET_STATE.CONNECTED) { + this.isNwConnected = true; + return; + } + this.isNwConnected = false; } createUid() { @@ -154,6 +197,7 @@ export default class AppStore { const connStr = await this.api.fetchConnInfo(connInfo); const parsedConnInfo = this.parseConnStr(connStr); if (parsedConnInfo.state === CONST.CONN_STATE.INVITE_ACCEPTED) { + this.setConnState(CONST.CONN_STATE.INVITE_ACCEPTED); this.setRemoteOffer(parsedConnInfo.callee.offer); this.setRemoteOfferCandidates(parsedConnInfo.callee.offerCandidates); this.setRemoteAnswer(parsedConnInfo.callee.answer); @@ -163,6 +207,8 @@ export default class AppStore { } resolve(false); } catch (err) { + console.error(`Failed to accept invite :: ${err.message}`); + this.chatRoomError = CONST.UI.MESSAGES.inviteAcceptFail reject(err); } }); @@ -176,11 +222,13 @@ export default class AppStore { const connStr = await this.api.fetchConnInfo(connInfo); const parsedConnInfo = this.parseConnStr(connStr); if (parsedConnInfo.state === CONST.CONN_STATE.CONNECTED) { - this.connectionState = CONST.CONN_STATE.CONNECTED; + this.setConnState(CONST.CONN_STATE.CONNECTED); return resolve(true); } resolve(false); } catch (err) { + console.error(`Failed to accept call :: ${err.message}`); + this.chatRoomError = CONST.UI.MESSAGES.callAcceptFail reject(err); } }); @@ -200,6 +248,8 @@ export default class AppStore { } resolve(false); } catch (err) { + console.error(`Failed to check whether remote trying to connect :: ${err.message}`); + this.chatRoomError = CONST.UI.MESSAGES.checkCallingFail reject(err); } }); @@ -210,15 +260,6 @@ export default class AppStore { this.uid = uid; } - @action - nwStateCb(newState) { - if (newState === CONST.NET_STATE.CONNECTED) { - this.isNwConnected = true; - return; - } - this.isNwConnected = false; - } - @action setOffer(offer) { this.offer = offer; @@ -243,14 +284,15 @@ export default class AppStore { authorisation() { return new Promise(async (resolve, reject) => { try { - this.setLoader(true, 'Authorising application'); + this.progress = CONST.UI.MESSAGES.authorise; this.api = new SafeApi(this.nwStateCb); await this.api.authorise(); this.isAuthorised = true; - this.setLoader(false); - resolve(true); - } catch (err) { - console.error(`Authorisation error :: ${err}`); + resolve(); + } catch(err) { + this.error = CONST.UI.MESSAGES.authoriseFail; + console.error(`Authorisation error :: ${err.message}`); + reject(err); } }); } @@ -259,16 +301,37 @@ export default class AppStore { fetchPublicNames() { return new Promise(async (resolve, reject) => { try { - this.setLoader(true, 'Fetching public names'); + this.progress = CONST.UI.MESSAGES.fetchPublicName; this.publicNames = await this.api.getPublicNames(); - if (this.publicNames.length !== 0 && !this.selectedPubName) { - this.setLoader(true, 'Initializing'); - this.selectedPubName = this.publicNames[0]; - await this.api.setupPublicName(this.selectedPubName); + this.progress = ''; + resolve(); + } catch(err) { + console.error(`Fetch public names error :: ${err.message}`); + this.error = CONST.UI.MESSAGES.fetchPublicNameFail; + reject(err); + } + }); + } + + @action + setupPublicName() { + return new Promise(async (resolve, reject) => { + try { + if (this.publicNames.length === 0) { + this.error = CONST.UI.MESSAGES.noPubNameFound; + return reject(); } - this.setLoader(false); - } catch (err) { - console.error(`Fetch publicNames error :: ${err}`); + if (!this.activePublicName) { + this.activePublicName = this.publicNames[0]; + } + this.progress = CONST.UI.MESSAGES.initialise; + await this.api.setupPublicName(this.activePublicName); + this.progress = ''; + resolve(); + } catch(err) { + console.error(`Initilise error :: ${err.message}`); + this.error = CONST.UI.MESSAGES.initialiseFail; + reject(err); } }); } @@ -277,49 +340,69 @@ export default class AppStore { fetchInvites(isPolling) { return new Promise(async (resolve, reject) => { try { - if (isPolling && !this.isAuthorised) { return resolve(true); } if (!isPolling) { - this.setLoader(true, 'Fetching invites'); + this.progress = CONST.UI.MESSAGES.fetchInvites; } - const oldCount = this.invites.length; this.invites = await this.api.fetchInvites(); - const diff = this.invites.length - oldCount; - if (diff >= 1) { - this.newInvites += diff; + this.invitesCount = this.invites.length; + this.progress = ''; + resolve(); + } catch(err) { + console.error(`Fetch invites error :: ${err.message}`); + if (!isPolling) { + this.error = CONST.UI.MESSAGES.fetchInvitesFail; } - this.setLoader(false); - resolve(true); - } catch (err) { - console.error('Fetch invites :: ', err); reject(err); } }); } @action - activatePubName(pubName) { - if (!pubName || !this.publicNames.includes(pubName)) { - return; - } + activatePublicName(pubName) { return new Promise(async (resolve, reject) => { - this.setLoader(true, `Activating selected ${pubName}`); - this.selectedPubName = pubName; - await this.api.setupPublicName(this.selectedPubName); - // reset invite count - this.newInvites = 0; - this.setLoader(false, null, true); + if (!pubName || !this.publicNames.includes(pubName)) { + this.switchIDError = CONST.UI.MESSAGES.invalidPublicName; + return reject(); + } + try { + this.switchIDProgress = CONST.UI.MESSAGES.activatePublicName; + await this.api.setupPublicName(pubName); + this.activePublicName = pubName; + this.switchIDProgress = ''; + resolve(); + } catch(err) { + console.error(`Activate public name error :: ${err.message}`); + this.switchIDProgress = ''; + this.switchIDError = CONST.UI.MESSAGES.activatePublicNameFail; + reject(err); + } }); } @action - reset() { - this.loaded = false; - this.error = ''; - this.setLoader(false); + connect(friendID) { + return new Promise(async (resolve, reject) => { + try { + if (this.publicNames.includes(friendID)) { + this.newChatError = CONST.UI.MESSAGES.cantInviteYourself; + return reject(); + } + this.newChatProgress = CONST.UI.MESSAGES.connecting; + await this.api.connect(friendID); + this.friendID = friendID; + this.newChatProgress = ''; + resolve(); + } catch(err) { + console.error(`Connect error :: ${err.message}`); + this.newChatProgress = ''; + this.newChatError = CONST.UI.MESSAGES.connectingFail; + reject(err); + } + }); } @action @@ -332,7 +415,7 @@ export default class AppStore { const isCallee = !!friendID; const userPosition = isCallee ? CONST.USER_POSITION.CALLEE : CONST.USER_POSITION.CALLER; const uid = friendUID || this.createUid(); - this.createConn(this.selectedPubName, userPosition, uid); + this.createConn(this.activePublicName, userPosition, uid); if (isCallee) { this.setInitiater(friendID); const connInfo = this.transformConnInfo(); @@ -344,7 +427,9 @@ export default class AppStore { } resolve(true); } catch (err) { - console.error('Initialise connInfo error :: ', err); + console.error(`Initialise connInfo error :: ${err.message}`); + this.chatRoomError = CONST.UI.MESSAGES.initialisationFail; + reject(err); } }); } @@ -359,7 +444,9 @@ export default class AppStore { await this.api.sendInvite(connInfo); resolve(true); } catch (err) { - console.error('Send invite error :: ', err); + console.error(`Send invite error :: ${err.message}`); + this.chatRoomError = CONST.UI.MESSAGES.sendInviteFail; + reject(err); } }); } @@ -375,7 +462,9 @@ export default class AppStore { await this.api.acceptInvite(connInfo); resolve(true); } catch (err) { - console.error('Accept invite error :: ', err); + console.error(`Accept invite error :: ${err.message}`); + this.chatRoomError = CONST.UI.MESSAGES.acceptInviteFail; + reject(err); } }); } @@ -390,22 +479,9 @@ export default class AppStore { await this.api.calling(connInfo); resolve(true); } catch (err) { - console.error('Calling error :: ', err); - } - }); - } - - @action - connect(friendID) { - return new Promise(async (resolve, reject) => { - try { - this.setLoader(true, `Connecting to ${friendID}`); - await this.api.connect(friendID); - this.setLoader(false, null, true); - resolve(true); - } catch (err) { - this.error = new Error('Make sure the Callee has initialised with WebRTC app'); - console.log('Connect error :: ', err); + console.error(`Calling error :: ${err.message}`); + this.chatRoomError = CONST.UI.MESSAGES.callingFail; + reject(err); } }); } @@ -419,16 +495,13 @@ export default class AppStore { await this.api.connected(connInfo); resolve(true); } catch (err) { - console.log('Connected error :: ', err); + console.error(`Connected error :: ${err.message}`); + this.chatRoomError = CONST.UI.MESSAGES.connectingFail; + reject(err); } }); } - @action - resetFetchCount() { - this.newInvites = 0; - } - @action deleteInvite() { return new Promise(async (resolve, reject) => { @@ -437,7 +510,7 @@ export default class AppStore { await this.api.deleteInvite(connInfo); return resolve(true); } catch (err) { - console.log('Connected error :: ', err); + console.error(`Delete invite error :: ${err.message}`); reject(err); } }); diff --git a/safe_webrtc_example/app/components/active_public_name.js b/safe_webrtc_example/app/components/active_public_name.js new file mode 100644 index 0000000..e073c98 --- /dev/null +++ b/safe_webrtc_example/app/components/active_public_name.js @@ -0,0 +1,29 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { inject, observer } from "mobx-react"; +import CONST from '../constants'; + +@inject("store") +@observer +export default class ActivePublicName extends Component { + render() { + const { history, publicName, disableOptions } = this.props; + + return ( +
+
+
{CONST.UI.LABELS.activePubName}
+
{publicName}
+ {!disableOptions ? (
+ +
) : null} +
+
+ ); + } +} + +ActivePublicName.propTypes = { +}; diff --git a/safe_webrtc_example/app/components/app.js b/safe_webrtc_example/app/components/app.js index 8cf8c44..913e1dd 100644 --- a/safe_webrtc_example/app/components/app.js +++ b/safe_webrtc_example/app/components/app.js @@ -2,25 +2,12 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { inject, observer } from "mobx-react"; -import WebrtcLogo from '../images/logo.png'; - @inject("store") @observer export default class App extends Component { - componentDidMount() { - this.props.store.authorisation() - .then(() => this.props.store.fetchPublicNames()) - } - render() { return (
-
-
-
WebRTC logo
-
WebRTC Signalling Example App
-
-
{this.props.children}
); diff --git a/safe_webrtc_example/app/components/bootstrap.js b/safe_webrtc_example/app/components/bootstrap.js new file mode 100644 index 0000000..87cdd8e --- /dev/null +++ b/safe_webrtc_example/app/components/bootstrap.js @@ -0,0 +1,74 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { inject, observer } from "mobx-react"; + +import CONST from '../constants'; + +@inject("store") +@observer +export default class Bootstrap extends Component { + componentDidMount() { + const { store, history } = this.props; + + store.authorisation() + .then(() => store.fetchPublicNames()) + .then(() => store.setupPublicName()) + .then(() => { + history.push('home'); + }) + } + + getError(err) { + if(!err) { + return ; + } + + return ( +
+
+
{err}
+
+ ); + } + + getProgress(desc) { + if (!desc) { + return ; + } + + return ( +
+
+
{desc}
+
+ ); + } + + render() { + const { store } = this.props; + + let container = undefined; + + if (store.error) { + container = this.getError(store.error); + } else { + container = this.getProgress(store.progress); + } + + return ( +
+
+
+
{CONST.UI.LABELS.title}
+
+
+ {container} +
+
+ ); + } +} + +Bootstrap.propTypes = { + +}; diff --git a/safe_webrtc_example/app/components/chat_room.js b/safe_webrtc_example/app/components/chat_room.js index 2bb03bb..a279432 100644 --- a/safe_webrtc_example/app/components/chat_room.js +++ b/safe_webrtc_example/app/components/chat_room.js @@ -1,8 +1,9 @@ import React, { Component } from 'react'; +import PropTypes from 'prop-types'; import { observable } from 'mobx'; -import { observer, inject } from 'mobx-react'; +import { inject, observer } from "mobx-react"; +import classNames from 'classnames'; import CONST from '../constants'; - @inject("store") @observer export default class ChatRoom extends Component { @@ -21,20 +22,26 @@ export default class ChatRoom extends Component { this.onCreateOfferSuccess = this.onCreateOfferSuccess.bind(this); this.onClickCancel = this.onClickCancel.bind(this); this.setTimer = this.setTimer.bind(this); + this.getConnectionStatus = this.getConnectionStatus.bind(this); this.timer = null; } - componentDidMount() { + componentWillMount() { + if (!this.props.store.isAuthorised) { + return this.props.history.push('/'); + } this.friendID = this.props.match.params.friendId; this.friendUID = this.props.match.params.uid; - this.props.store.initialiseConnInfo(this.friendID, this.friendUID); - this.startStream() - .then(() => this.setupOrigin()) - .then(() => this.setupRemote()) + this.props.store.initialiseConnInfo(this.friendID, this.friendUID) .then(() => { - this.originConn.createOffer(this.offerOptions) - .then(this.onCreateOfferSuccess, (err) => { - console.error('create offer error :: ', err); + this.startStream() + .then(() => this.setupOrigin()) + .then(() => this.setupRemote()) + .then(() => { + this.originConn.createOffer(this.offerOptions) + .then(this.onCreateOfferSuccess, (err) => { + console.error('create offer error :: ', err); + }); }); }); } @@ -52,11 +59,11 @@ export default class ChatRoom extends Component { this.setTimer(fn); return; } - if (store.persona === CONST.USER_POSITION.CALLER && store.remoteOffer) { + if (store.persona === CONST.USER_POSITION.CALLER && store.remoteOffer && store.state === CONST.CONN_STATE.INVITE_ACCEPTED) { this.call(); } - if(store.persona === CONST.USER_POSITION.CALLEE && store.remoteAnswer) { + if (store.persona === CONST.USER_POSITION.CALLEE && store.remoteAnswer) { this.finishConnection(); } }); @@ -71,6 +78,15 @@ export default class ChatRoom extends Component { }); } + stopAllStreams() { + if (!this.originStream) { + return; + } + this.originStream.getTracks().forEach((track) => { + track.stop(); + }); + } + setupOrigin() { return new Promise((resolve) => { this.originConn = new window.RTCPeerConnection(CONST.CONFIG.SERVER); @@ -143,12 +159,17 @@ export default class ChatRoom extends Component { })); }, (err) => { console.error('set destination remote session failed ::', err); - }).then(() => { - this.destConn.createAnswer().then((ansDesc) => { - this.onCreateAnswerSuccess(ansDesc); - }, (err) => { - console.error('create answer error :: ', err); - }); + }) + .then(() => this.destConn.createAnswer()) + .then((ansDesc) => { + this.onCreateAnswerSuccess(ansDesc); + }, (err) => { + console.error('create answer error :: ', err); + }) + .then(() => { + if (store.persona === CONST.USER_POSITION.CALLER && store.remoteAnswer) { + this.finishConnection(); + } }); } @@ -163,9 +184,10 @@ export default class ChatRoom extends Component { } onCreateAnswerSuccess(answer) { + const { store } = this.props; this.destConn.setLocalDescription(answer) .then(() => { - return this.props.store.setAnswer(answer); + return store.setAnswer(answer); console.log('set destination local session success'); }, (err) => { console.error('set destination local session failed ::', err); @@ -175,6 +197,7 @@ export default class ChatRoom extends Component { reset() { clearTimeout(this.timer); this.props.store.resetConnInfo(); + this.stopAllStreams(); } endCall(e) { @@ -184,59 +207,96 @@ export default class ChatRoom extends Component { this.originConn = null; this.destConn = null; this.reset(); - this.props.history.push('/'); + this.props.history.push('/home'); } onClickCancel(e) { e.preventDefault(); const self = this; const moveHome = () => { - console.log('moveHome'); self.reset(); - self.props.history.push('/'); + self.props.history.push('/home'); }; this.props.store.deleteInvite() - .then(moveHome, moveHome); + .then(moveHome, moveHome); + } + + getProgress(progress, error) { + if (error) { + return ( +
+
+
+
{error}
+
+
+ ); + } else if (progress) { + return ( +
+
+
+
{progress}
+
+
+ ); + } + return ; } getConnectionStatus() { let connectionMsg = null; - const { connectionState } = this.props.store; + const { store } = this.props; + const { connectionState } = store; const { CONN_STATE, UI } = CONST; const { CONN_MSGS } = UI; - // FIXME check for not caller persona - if (connectionState === CONN_STATE.CONNECTED) { - this.finishConnection(); - return; - } + const isConnected = (connectionState === CONN_STATE.CONNECTED); - switch (connectionState) { - case CONN_STATE.INIT: - connectionMsg = CONN_MSGS.INIT; - break; - case CONN_STATE.SEND_INVITE: - connectionMsg = CONN_MSGS.SEND_INVITE; - break; - case CONN_STATE.INVITE_ACCEPTED: - connectionMsg = CONN_MSGS.INVITE_ACCEPTED; - break; - case CONN_STATE.CALLING: - connectionMsg = CONN_MSGS.CALLING; - break; - default: - connectionMsg = UI.DEFAULT_LOADING_DESC + if (!isConnected) { + switch (connectionState) { + case CONN_STATE.INIT: + connectionMsg = CONN_MSGS.INIT; + break; + case CONN_STATE.SEND_INVITE: + connectionMsg = CONN_MSGS.SEND_INVITE; + break; + case CONN_STATE.INVITE_ACCEPTED: + connectionMsg = CONN_MSGS.INVITE_ACCEPTED; + break; + case CONN_STATE.CALLING: + connectionMsg = CONN_MSGS.CALLING; + break; + default: + connectionMsg = UI.DEFAULT_LOADING_DESC + } } + + const statusClassName = classNames('status', { + 'connected': connectionState === CONN_STATE.CONNECTED + }); + return ( -
-
-

{connectionMsg}

-
- +
+
+
+
+
+
+
+
+
{store.activePublicName}
+
+
{this.friendID || store.friendID}
+
+
#{this.friendUID || store.uid}
+
+ {this.getProgress(connectionMsg, store.chatRoomError)} +
+
+ +
+
@@ -246,15 +306,18 @@ export default class ChatRoom extends Component { finishConnection() { const { store } = this.props; this.originConn.setRemoteDescription(store.remoteAnswer) - .then(() => { + .then(() => { Promise.all(store.remoteAnswerCandidates.map((can) => { return this.originConn.addIceCandidate(new RTCIceCandidate(can)) .then(() => { - console.log('set ICE candidate success'); + console.log('set ICE candidate origin success'); }, (err) => { - console.error('set ICE candidate failed ::', err); + console.error('set ICE candidate origin failed ::', err); }); })).then(() => { + if (store.persona === CONST.USER_POSITION.CALLER) { + return; + } store.connected(); }); }, (err) => { @@ -263,23 +326,24 @@ export default class ChatRoom extends Component { } render() { - const { match } = this.props; - return (
-
-
- -
-
+
+ +
+
+
+ +
+
{this.getConnectionStatus()} -
- -
); } } + +ChatRoom.propTypes = { +}; diff --git a/safe_webrtc_example/app/components/error.js b/safe_webrtc_example/app/components/error.js deleted file mode 100644 index a6542ce..0000000 --- a/safe_webrtc_example/app/components/error.js +++ /dev/null @@ -1,28 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; - -export default class ErrorComp extends Component { - render() { - return ( -
-
-
-

{this.props.desc}

-
- -
-
-
- ); - } -} - -ErrorComp.propTypes = { -}; diff --git a/safe_webrtc_example/app/components/home.js b/safe_webrtc_example/app/components/home.js index 8c3dc0b..de13d27 100644 --- a/safe_webrtc_example/app/components/home.js +++ b/safe_webrtc_example/app/components/home.js @@ -1,10 +1,9 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { inject, observer } from "mobx-react"; -import CONST from '../constants'; -import SelectedPubID from './selected_pub_id'; -import Loader from './loader'; +import CONST from '../constants'; +import ActivePublicName from './active_public_name'; @inject("store") @observer @@ -14,6 +13,12 @@ export default class Home extends Component { this.timer = null; } + componentWillMount() { + if (!this.props.store.isAuthorised) { + return this.props.history.push('/'); + } + } + componentDidMount() { this.pollInvite(); } @@ -24,14 +29,6 @@ export default class Home extends Component { this.props.store.reset(); } - noPublicNameContainer() { - return ( -
-

No Public Name found. Please create one to start using this application.

-
- ) - } - pollInvite() { const { store } = this.props; const self = this; @@ -44,49 +41,46 @@ export default class Home extends Component { }, CONST.UI.TIMER_INTERVAL.FETCH_INVITES_POLL); } - render() { + getActivePublicContainer() { const { store, history } = this.props; - - if (store.loading) { - return ; + if (!store.activePublicName) { + return } - // if no public name found - if (store.publicNames.length === 0) { - return this.noPublicNameContainer(); - } + return + } + + render() { + const { store, history } = this.props; return ( -
- -
-
-
- +
+
+
+
+
+
+ -
- +
+
+ {this.getActivePublicContainer()}
); } diff --git a/safe_webrtc_example/app/components/invites.js b/safe_webrtc_example/app/components/invites.js index 32111fa..3ac1295 100644 --- a/safe_webrtc_example/app/components/invites.js +++ b/safe_webrtc_example/app/components/invites.js @@ -1,61 +1,168 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { inject, observer } from "mobx-react"; - -import SelectedPubID from './selected_pub_id'; -import Loader from './loader'; +import classNames from 'classnames'; +import CONST from '../constants'; +import ActivePublicName from './active_public_name'; @inject("store") @observer export default class Invites extends Component { + constructor() { + super(); + this.state = { + selectedInvite: { + publicId: null, + uid: null + } + }; + } + componentWillMount() { - this.props.store.resetFetchCount(); + if (!this.props.store.isAuthorised) { + return this.props.history.push('/'); + } this.props.store.fetchInvites(); } + componentWillUnmount() { + this.props.store.reset(); + } + onClickInvite(invite) { - console.log('invite selected', invite); - this.props.history.push(`chat-room/${invite.publicId}/${invite.uid}`); + if (!invite.publicId || !invite.uid) { return }; + + this.setState({ selectedInvite: invite }); } - render() { + getOptions(onlyCancel) { + const { store, history } = this.props; + return ( +
+ { + !onlyCancel ? ( +
+ +
+ ) : null + } +
+ +
+
+ ); + } + + getError(msg) { + return ( +
+
+
+
+
{msg}
+
+
+ {this.getOptions(true)} +
+ ); + } + + getProgressLoader(msg) { + return ( +
+
+
+
{msg}
+
+
+ ) + } + + // getProgress() { + // const { store } = this.props; + + // if (store.error) { + // return ( + //
+ //
+ //
+ //
{store.error}
+ //
+ //
+ // ); + // } + // return this.getProgressLoader(store.progress); + // } + + getInvitesList() { + const { store, history } = this.props; + let container = undefined; + if (store.invites.length === 0) { + container =
{CONST.UI.LABELS.noInvites}
+ } else { + container = ( +
    + { + store.invites.map((invite, i) => { + const listClassName = classNames({ + active: (invite.uid === this.state.selectedInvite.uid) && (invite.publicId === this.state.selectedInvite.publicId) + }); + return ( +
  • { + this.onClickInvite(invite); + }}>{invite.publicId} {invite.uid}
  • + ); + }) + } +
+ ); + } + return ( +
+

{CONST.UI.LABELS.chooseInvite}

+ {container} + {this.getOptions()} +
+ ); + } + + getActivePublicContainer() { const { store, history } = this.props; + if (!store.activePublicName) { + return + } + + return + } - if (store.loading) { - return ; + render() { + const { store } = this.props; + let container = undefined; + + if (store.error) { + container = this.getError(store.error); + } else if (store.progress) { + container = this.getProgressLoader(store.progress); + } else { + container = this.getInvitesList(); } return ( -
- -
-

You have {store.invites.length} invite(s)

-
- { - store.invites.map((invite, i) => { - return ( -
{ - this.onClickInvite(invite); - }} - >{invite.publicId} {invite.uid}
- ); - }) - } -
-
- -
+
+
+
+
+
+ {container}
+ {this.getActivePublicContainer()}
); } diff --git a/safe_webrtc_example/app/components/loader.js b/safe_webrtc_example/app/components/loader.js deleted file mode 100644 index 5ce5df2..0000000 --- a/safe_webrtc_example/app/components/loader.js +++ /dev/null @@ -1,20 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; - -export default class Loader extends Component { - render() { - return ( -
-
-
-
-

{this.props.desc}

-
-
-
- ); - } -} - -Loader.propTypes = { -}; diff --git a/safe_webrtc_example/app/components/new_chat.js b/safe_webrtc_example/app/components/new_chat.js index 3b73101..baf919e 100644 --- a/safe_webrtc_example/app/components/new_chat.js +++ b/safe_webrtc_example/app/components/new_chat.js @@ -1,19 +1,20 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { inject, observer } from "mobx-react"; - -import SelectedPubID from './selected_pub_id'; -import Loader from './loader'; -import Err from './error'; - +import CONST from '../constants'; +import ActivePublicName from './active_public_name'; @inject("store") @observer export default class NewChat extends Component { - componentWillUpdate() { - console.log('new chat loaded', this.props.store.loaded); - // const title = this.title.value; - if (this.props.store.loaded) { - this.props.history.push(`chat-room`); + constructor() { + super(); + this.onSubmitFriendID = this.onSubmitFriendID.bind(this); + this.onFocusInput = this.onFocusInput.bind(this); + } + + componentWillMount() { + if (!this.props.store.isAuthorised) { + return this.props.history.push('/'); } } @@ -21,59 +22,103 @@ export default class NewChat extends Component { this.friendID.focus(); } - onNewChatSubmit(e) { - e.preventDefault(); - console.log('Friend ID', this.friendID.value) - const friendID = this.friendID.value; - // const title = this.title.value; - // if (!friendID || !title) { - // return; - // } - this.props.store.connect(this.friendID.value); + componentWillUnmount() { + this.props.store.resetNewChatState(); } - render() { - const { store, history } = this.props; - - if (store.error) { - return { - store.reset(); - }} />; + getActivePublicContainer() { + const { store } = this.props; + if (!store.activePublicName) { + return } - if (store.loading) { - return ; + return + } + + getOptions(onlyCancel) { + const { history } = this.props; + return ( +
+ {!onlyCancel ? (
+ +
) : null} +
+ +
+
+ ); + } + + getProgress() { + const { store } = this.props; + + if (store.newChatError) { + return ( +
+
+
+
{store.newChatError}
+
+
+ ); + } + if (store.newChatProgress) { + return ( +
+
+
+
{store.newChatProgress}
+
+
+ ) } + return + } + + onSubmitFriendID(e) { + e.preventDefault(); + const { history } = this.props; + this.props.store.resetNewChatState(); + const friendID = this.friendID.value.trim(); + this.props.store.connect(friendID) + .then(() => { + history.push('chat-room'); + }); + } + + onFocusInput(e) { + this.props.store.resetNewChatState(); + } + + render() { + const { store } = this.props; return ( -
- -
-
-

Enter the PublicID you want to chat with

-
-
-
- {this.friendID = c;}} /> -
- {/*
- {this.title = c;}} /> -
*/} -
- -
-
-
+
+
+
+
+
+

{CONST.UI.LABELS.newVideoCall}

+
+
+
+ { this.friendID = c }} + onFocus={this.onFocusInput} + placeholder={CONST.UI.LABELS.friendIdPlaceholder} /> +
+ {!store.newChatProgress ? this.getOptions() : null} +
+ {this.getProgress()}
+ {this.getActivePublicContainer()}
); } diff --git a/safe_webrtc_example/app/components/selected_pub_id.js b/safe_webrtc_example/app/components/selected_pub_id.js deleted file mode 100644 index 4d22934..0000000 --- a/safe_webrtc_example/app/components/selected_pub_id.js +++ /dev/null @@ -1,41 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { inject, observer } from "mobx-react"; - -export default class SelectedPubID extends Component { - render() { - const backBtn = ( -
- -
- ); - - return ( -
-
-

Your Public ID

-

{this.props.pubId}

- {this.props.showBackBtn ? backBtn : null} -
- -
-
-
- ); - } -} - -SelectedPubID.propTypes = { -}; diff --git a/safe_webrtc_example/app/components/switch_id.js b/safe_webrtc_example/app/components/switch_id.js deleted file mode 100644 index 3164493..0000000 --- a/safe_webrtc_example/app/components/switch_id.js +++ /dev/null @@ -1,108 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { inject, observer } from "mobx-react"; -import classNames from 'classnames'; - -import Loader from './loader'; - -@inject("store") -@observer -export default class SwitchID extends Component { - constructor() { - super(); - this.state = { - selectedPubName: '' - }; - } - - componentWillUpdate(nextProps) { - if(this.state.selectedPubName && this.props.store.loaded) { - this.props.history.go(-1); - } - } - - componentWillUnmount() { - this.props.store.reset(); - } - - onClickPubName(pubName) { - if (!pubName) { - return; - } - this.setState({selectedPubName: pubName}); - } - - render() { - const { store, history } = this.props; - - let container = null; - - if (store.loading) { - return ; - } - - return ( -
-
-

Choose a Public ID

-
- { - store.publicNames.map((pub, i) => { - const lsClass = classNames('id-ls-i', { - checked: pub === (this.state.selectedPubName || store.selectedPubName) - }); - return ( -
{ - this.onClickPubName(pub); - }} - >{pub}
- ); - }) - } -
-
- -
-
-

Do you want to activate this Public ID with the WebRTC service?

-
-
-
-
- -
-
-
-
-
- ); - } -} - -SwitchID.propTypes = { -}; diff --git a/safe_webrtc_example/app/components/switch_public_name.js b/safe_webrtc_example/app/components/switch_public_name.js new file mode 100644 index 0000000..d615e87 --- /dev/null +++ b/safe_webrtc_example/app/components/switch_public_name.js @@ -0,0 +1,179 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { inject, observer } from "mobx-react"; +import classNames from 'classnames'; + +import CONST from '../constants'; +import ActivePublicName from './active_public_name'; + +@inject("store") +@observer +export default class SwitchPublicName extends Component { + constructor() { + super(); + this.state = { + selectedPubName: null + }; + } + componentWillMount() { + if (!this.props.store.isAuthorised) { + return this.props.history.push('/'); + } + this.props.store.fetchPublicNames(); + } + + componentWillUnmount() { + this.props.store.reset(); + this.props.store.resetSwitchIDState(); + } + + onClickPubName(name) { + if (!name) { return }; + this.props.store.resetSwitchIDState(); + this.setState({ selectedPubName: name }); + } + + getOptions(onlyCancel) { + const { store, history } = this.props; + + return ( +
+ { + !onlyCancel ? ( +
+ +
+ ) : null + } +
+ +
+
+ ); + } + + getProgressLoader(msg) { + return ( +
+
+
+
{msg}
+
+
+ ) + } + + getError(msg) { + return ( +
+
+
+
+
{msg}
+
+
+ {this.getOptions(true)} +
+ ); + } + + getProgress() { + const { store } = this.props; + + if (store.switchIDError) { + return ( +
+
+
+
{store.switchIDError}
+
+
+ ); + } + if (store.switchIDProgress) { + return this.getProgressLoader(store.switchIDProgress); + } + + return + } + + getPubNamesList() { + const { store, history } = this.props; + let container = undefined; + if (store.publicNames.length === 1) { + container =
{CONST.UI.LABELS.noPublicName}
+ } else { + container = ( +
    + { + store.publicNames.map((pub, i) => { + if (pub === store.activePublicName) { + return null; + } + const listClassName = classNames({ + active: pub === this.state.selectedPubName + }); + return ( +
  • { + this.onClickPubName(pub); + }}>{pub}
  • + ); + }) + } +
+ ) + } + return ( +
+

{CONST.UI.LABELS.choosePublicName}

+ {container} + {!store.switchIDProgress ? this.getOptions() : null} +
+ ); + } + + getActivePublicContainer() { + const { store, history } = this.props; + if (!store.activePublicName) { + return + } + + return + } + + render() { + const { store } = this.props; + let container = undefined; + + if (store.error) { + container = this.getError(store.error); + } else if (store.progress) { + container = this.getProgressLoader(store.progress); + } else { + container = this.getPubNamesList(); + } + + return ( +
+
+
+
+
+ {container} +
+ {this.getProgress()} + {this.getActivePublicContainer()} +
+ ); + } +} + +SwitchPublicName.propTypes = { +}; diff --git a/safe_webrtc_example/app/constants.js b/safe_webrtc_example/app/constants.js index 2baa455..c5bd286 100644 --- a/safe_webrtc_example/app/constants.js +++ b/safe_webrtc_example/app/constants.js @@ -1,5 +1,44 @@ export default { UI: { + LABELS: { + title: 'SAFE WebRTC Signalling', + activePubName: 'Active public name', + newVideoCall: 'New Video Call', + invites: 'Invites', + switch: 'Switch', + connect: 'Connect', + cancel: 'Cancel', + activate: 'Activate', + friendIdPlaceholder: 'Enter friend\'s public name', + chooseInvite: 'Choose an invite', + noInvites: 'No invites available', + noPublicName: 'No Public Name available to switch', + choosePublicName: 'Select Public Name', + }, + MESSAGES: { + authorise: 'Authorising with Authenticator', + authoriseFail: 'Authorisation Failed', + initialise: 'Initialising application', + initialiseFail: 'Failed to initialise application', + noPubNameFound: 'No Public Name found.', + fetchPublicName: 'Fetching Public Names', + fetchPublicNameFail: 'Unable to fetch Public Names', + fetchInvites: 'Fetching Invites', + fetchInvitesFail: 'Unable to fetch Invites', + activatePublicName: 'Activating public name', + activatePublicNameFail: 'Failed to activate Public Name', + connecting: 'Connecting with friend', + connectingFail: 'Failed to connect with friend', + invalidPublicName: 'Invalid Public Name', + cantInviteYourself: 'Can\'t invite yourself', + inviteAcceptFail: 'Failed to accept invite', + callAcceptFail: 'Failed after remote accepted the call', + checkCallingFail: 'Failed to accept remote call', + initialisationFail: 'Failed to initialise the connection', + sendInviteFail: 'Failed to send invitation to remote', + callingFail: 'Failed to call remote', + connectingFail: 'Failed to connect with remote' + }, DEFAULT_LOADING_DESC: 'Please wait...', CONN_MSGS: { INIT: 'Initialising connection', @@ -16,11 +55,11 @@ export default { CONFIG: { SERVER: { iceServers: [ - { url: 'stun:stun1.l.google.com:19302' }, // URL to STUN Server + { url: 'STUN_SERVER_URL' }, // fill STUN Server url { - url: 'turn:numb.viagenie.ca', - credential: 'PASSWORD', // fill turn server password - username: 'USERNAME' // fill turn server username + url: 'TURN_SERVER_URL', // fill turn server url + credential: 'TURN_PASSWORD', // fill turn server password + username: 'TURN_USERNAME' // fill turn server username }, ] }, @@ -62,9 +101,6 @@ export default { CHANNEL: 15005, DNS: 15001, }, - ERR_CODE: { - NO_SUCH_ENTRY: -106, - }, CRYPTO_KEYS: { SEC_SIGN_KEY: '__SEC_SIGN_KEY__', PUB_SIGN_KEY: '__PUB_SIGN_KEY__', diff --git a/safe_webrtc_example/app/images/call.svg b/safe_webrtc_example/app/images/call.svg new file mode 100644 index 0000000..b29d08f --- /dev/null +++ b/safe_webrtc_example/app/images/call.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safe_webrtc_example/app/images/error.svg b/safe_webrtc_example/app/images/error.svg index abe0ff7..7db12ca 100644 --- a/safe_webrtc_example/app/images/error.svg +++ b/safe_webrtc_example/app/images/error.svg @@ -3,7 +3,7 @@ - Asset 1 diff --git a/safe_webrtc_example/app/images/more.svg b/safe_webrtc_example/app/images/more.svg new file mode 100644 index 0000000..0bf9543 --- /dev/null +++ b/safe_webrtc_example/app/images/more.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safe_webrtc_example/app/images/notification.svg b/safe_webrtc_example/app/images/notification.svg new file mode 100644 index 0000000..73bc5be --- /dev/null +++ b/safe_webrtc_example/app/images/notification.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safe_webrtc_example/app/main.js b/safe_webrtc_example/app/main.js index 129c835..fe9eb75 100644 --- a/safe_webrtc_example/app/main.js +++ b/safe_webrtc_example/app/main.js @@ -5,7 +5,7 @@ import { Provider } from 'mobx-react'; import { RouterStore, syncHistoryWithStore } from 'mobx-react-router'; import { Router } from 'react-router'; import AppRouter from './router'; -import AppStore from './appStore'; +import AppStore from './app_store'; const browserHistory = createHashHistory(); const routingStore = new RouterStore(); diff --git a/safe_webrtc_example/app/router.js b/safe_webrtc_example/app/router.js index 7797f5e..9a7326b 100644 --- a/safe_webrtc_example/app/router.js +++ b/safe_webrtc_example/app/router.js @@ -1,8 +1,9 @@ import React from 'react'; import { Switch, Route } from 'react-router'; import App from './components/app'; +import Bootstrap from './components/bootstrap'; import Home from './components/home'; -import SwitchID from './components/switch_id'; +import SwitchPublicName from './components/switch_public_name'; import Invites from './components/invites'; import NewChat from './components/new_chat'; import ChatRoom from './components/chat_room'; @@ -10,11 +11,12 @@ import ChatRoom from './components/chat_room'; export default () => ( - - + + - - + + + ); diff --git a/safe_webrtc_example/app/safe_comm.js b/safe_webrtc_example/app/safe_comm.js index 7b0fc2a..85b831a 100644 --- a/safe_webrtc_example/app/safe_comm.js +++ b/safe_webrtc_example/app/safe_comm.js @@ -184,6 +184,7 @@ export default class SafeApi { this.selectedPubName = await window.safeMutableData.get(ownCntr, CONST.SELECTED_PUB_NAME_KEY); resolve(true); } catch (err) { + // FIXME: change from error message to code // if (err.code !== CONST.ERR_CODE.NO_SUCH_ENTRY) { if (err.message !== ERROR_MSG.ENTRY_NOT_FOUND) { utils.putLog('Select public name error', err); @@ -207,6 +208,7 @@ export default class SafeApi { resolve(true); } catch (err) { + // FIXME: change from error message to code // if (err.code !== -1011) { if (err.message !== 'Core error: Routing client error -> Key does not exists') { utils.putLog('Check service container access error', err); @@ -304,6 +306,7 @@ export default class SafeApi { const deckey = await window.safeMutableData.decrypt(this.pubNameCntr, key); resolve(utils.uint8ToStr(deckey)); } catch (err) { + // FIXME: change from error message to code if (err.message === ERROR_MSG.SYMMETRIC_DECIPHER_FAILURE) { return resolve(''); } @@ -468,13 +471,16 @@ export default class SafeApi { if (isCaller) { utils.putLog('Insert data', dataKey); await window.safeMutableDataMutation.insert(mutationHandle, dataKey, connInfoStr); + utils.putLog('Inserted data', dataKey); } else { utils.putLog('Update data', dataKey); const connStr = await window.safeMutableData.get(channelMD, dataKey); await window.safeMutableDataMutation.update(mutationHandle, dataKey, connInfoStr, connStr.version + 1); + utils.putLog('Updated data', dataKey); } await window.safeMutableData.applyEntriesMutation(channelMD, mutationHandle); + utils.putLog('put confirmed', dataKey); window.safeMutableDataMutation.free(mutationHandle); window.safeMutableDataEntries.free(entriesHandle); resolve(true); @@ -515,6 +521,7 @@ export default class SafeApi { data: decryptedData })); } catch (err) { + // FIXME: change from error message to code if (err.message !== ERROR_MSG.ENTRY_NOT_FOUND) { return reject(err); } diff --git a/safe_webrtc_example/app/sass/button.sass b/safe_webrtc_example/app/sass/button.sass new file mode 100644 index 0000000..4888f77 --- /dev/null +++ b/safe_webrtc_example/app/sass/button.sass @@ -0,0 +1,45 @@ +.btn + +btnDefault() + font-size: 14px + line-height: 30px + padding: 0 16px + font-weight: 600 + border-radius: 3px + color: $color-smoke-medium + &:hover + background-color: $color-smoke-light + color: $color-snow-light + &:disabled + background-color: transparent !important + color: $color-smoke-light !important + &.start-call + font-size: 24px + background-color: $color-green + color: #ffffff + line-height: 70px + padding: 0 48px + font-weight: normal + &:hover + background-color: $color-green-dark + &.primary + color: $color-pr-blue + &:hover + background-color: $color-pr-blue + color: #fff + &.danger + color: $color-error + &:hover + background-color: $color-snow-light + color: $color-error + &.end-call + width: 50px + height: 50px + background-color: $color-end-call + background-image: url($end-call-url) + background-position: center + background-size: 24px + background-repeat: no-repeat + border-radius: 50% + transform: rotate(135deg) + &:hover + background-color: $color-end-call-hover diff --git a/safe_webrtc_example/app/sass/buttons.sass b/safe_webrtc_example/app/sass/buttons.sass deleted file mode 100644 index 303e9d4..0000000 --- a/safe_webrtc_example/app/sass/buttons.sass +++ /dev/null @@ -1,29 +0,0 @@ -.btn - +btnDefault() - line-height: 30px - padding: 0 16px - background-color: $pr-pale-brown-2 - font-weight: 600 - &:hover - background-color: $pr-pale-brown - &.flat - padding: 0 - font-size: 14px - color: $color-common-text - background-color: transparent - &:hover - background-color: transparent - &:disabled - opacity: 0.5 - &:hover - background-color: transparent !important - &.primary - background-color: transparent !important - color: $pr-pale-blue - &:hover - color: $pr-blue - &.primary - background-color: $pr-pale-blue - color: #ffffff - &:hover - background-color: $pr-blue diff --git a/safe_webrtc_example/app/sass/chat_room.sass b/safe_webrtc_example/app/sass/chat_room.sass index 453e04e..827e1c3 100644 --- a/safe_webrtc_example/app/sass/chat_room.sass +++ b/safe_webrtc_example/app/sass/chat_room.sass @@ -2,71 +2,125 @@ position: fixed width: 100% height: 100% - padding-top: 50px - top: 0 - left: 0 - .chat-room-b - .chat-room-remote - position: fixed - top: 0 - left: 0 + background-color: $color-smoke-dark + .remote + width: $remote-width + height: $remote-width + background: $color-smoke-medium + position: relative + top: 130px + left: 50% + transform: translate(-50%, 0) + border-radius: 5px + overflow: hidden + video width: 100% height: 100% - background-color: #f4f4f4 - z-index: 99 + transform: scale(1.3) + .origin + width: $origin-width + height: $origin-width + background: $color-smoke-light + position: absolute + top: 0 + right: 0 + border-radius: 5px + overflow: hidden video width: 100% height: 100% - .chat-room-origin - position: fixed - top: 50px - right: 50px - width: 300px - z-index: 99 - video - width: 100% - // height: 100% - .chat-room-opts - position: fixed - bottom: 50px - left: 50% - width: auto - transform: translate(-50%, 0) - z-index: 99 - button - width: 50px - border-radius: 50% - height: 50px - line-height: 50px - background-color: $pr-red-dark - color: white + transform: scale(1.3) + .opts + width: 100% + position: absolute + left: 0 + bottom: 24px + text-align: center + .opts + display: inline-block + .status + text-align: center + .status-b + width: 100% + height: 100% + background-color: rgba(0, 0, 0, 0.5) + position: absolute + top: 0 + left: 0 + .card-1 + min-height: 220px + .call-for + text-align: center + width: 100% + position: relative + .call-for-b + display: inline-block + .caller, + .callee + font-size: 18px + color: $color-smoke-medium + line-height: 50px + width: 50% + .caller + float: left + padding-right: 50px + text-align: right + .callee + float: left + padding-left: 50px + text-align: left + .split + float: left + width: 100px + height: 30px + margin: 10px 0 + background-image: url($connect-url) + background-position: center + background-size: contain + background-repeat: no-repeat + position: absolute + left: 50% + transform: translate(-50%, 0) + &.connected + .status-b + height: auto + background-color: transparent + .card-1 + top: 16px + transform: translate(-50%, 0) + min-height: 80px + padding: 16px + .logo + width: $logo-xs + height: $logo-xs + top: 16px + left: 16px + &.logo-sm + .logo-img + height: 30px + .call-for + .caller, + .callee + line-height: 30px + .split + margin: 0 + .opts + display: none !important + .id font-size: 14px + color: $color-smoke-light font-weight: 600 - border: 0 - cursor: pointer - outline: none - .chat-room-conn-status - position: fixed - bottom: 0 - left: 0 - width: 100% - height: 100% - background-color: rgba(255, 255, 255, 0.5) - z-index: 999 - .chat-room-conn-status-b - width: 400px - padding: 24px - background-color: #ffffff - position: relative - top: 50% - left: 50% - transform: translate(-50%, -50%) - border-radius: 3px - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25) - .status - font-size: 14px - font-weight: normal - text-align: center - padding: 40px 16px - .cancel-btn - text-align: center + width: 100% + margin-top: 8px + line-height: 24px + .opts + display: inline-block + .opt + margin: 16px 0 0 + button.primary-green + font-size: 18px + background-color: $color-green + color: #ffffff + line-height: 46px + padding: 0 36px + diff --git a/safe_webrtc_example/app/sass/common.sass b/safe_webrtc_example/app/sass/common.sass index 0c6bac9..3a029c5 100644 --- a/safe_webrtc_example/app/sass/common.sass +++ b/safe_webrtc_example/app/sass/common.sass @@ -5,32 +5,22 @@ body font-family: 'Open Sans', sans-serif + background-color: $body-bg -.root - .root-b - .root-h - background-color: $bg-header +.card-1 + width: $card-width + min-height: $card-height + background-color: #ffffff + border-radius: 5px + transform: translate(-50%, -50%) + position: absolute + top: 50% + left: 50% + padding: $card-padd -.base - width: 960px - margin: 0 auto - padding: 50px 0 - -.brand - padding: 16px 0 - .brand-img - width: 100px - margin: 0 auto - img - width: 100% - .brand-name - font-size: 16px - font-weight: 600 - text-align: center - color: $pr-blue - -.no-pub-name - text-align: center +.default font-size: 16px - font-weight: 600 - color: #808080 + color: $color-smoke-medium + text-align: center + line-height: 36px + padding: 8px 0 diff --git a/safe_webrtc_example/app/sass/components.sass b/safe_webrtc_example/app/sass/components.sass index 9944776..3eb5f04 100644 --- a/safe_webrtc_example/app/sass/components.sass +++ b/safe_webrtc_example/app/sass/components.sass @@ -1,224 +1,247 @@ -.selected-pub-id - padding: 50px 0 +.logo text-align: center - position: relative - .selected-pub-id-b - position: relative - display: inline-block - min-width: 600px - h3 - font-size: 18px - font-weight: normal - color: $color-common-text - h4 - font-size: 20px + .logo-img + display: block + height: $logo-lg + background-image: url($logo-url) + background-position: center + background-size: contain + background-repeat: no-repeat + .logo-desc + font-size: 14px font-weight: normal - color: $color-common-text - margin-top: 16px - .switch-btn + color: $color-logo-desc + transform: translateY(-10px) + &.logo-sm + width: $logo-sm position: absolute - top: 0 - right: 0 - .back-btn - position: absolute - top: 0 - left: 0 + top: $card-padd + left: $card-padd + .logo-img + height: $logo-sm -.switch-id - width: 600px - position: relative - padding: 50px 0 - margin: 0 auto - .title - font-size: 18px - font-weight: normal - color: $color-common-text +.bootstrap + padding: 24px + .logo + margin-top: 20px + .context + margin-top: 20px text-align: center - .id-ls - width: 400px - margin: 0 auto - padding: 40px 0 0 - .id-ls-i - padding: 0 36px - font-size: 14px - font-weight: normal - line-height: 36px - cursor: pointer - position: relative - &:after - content: "" - display: none + .context-b + display: inline-block + .icn + display: inline-block width: 30px height: 30px - position: absolute - top: 3px - left: 0 - background-image: url('../images/checked.svg') - background-position: center - background-repeat: no-repeat - background-size: 16px - &.checked:after + margin-right: 16px + float: left + .desc display: inline-block - &:hover - background-color: $pr-pale-blue-2 - .refresh-btn + line-height: 30px + float: left + font-size: 18px + font-weight: normal + color: $color-smoke-medium + &.error + .icn + background-image: url($error-url) + .desc + color: $color-error + +.home + min-height: 500px + .split-view + width: 100% + margin-top: 10px + padding: 24px + display: block position: absolute - top: 50px - right: 0 - .ack - text-align: center - margin: 80px 0 0 - p + top: 50% + left: 50% + transform: translate(-50%, -50%) + .split-view-b + width: 100% + display: table + min-height: 130px + .split-view-30, + .split-view-70 + display: table-cell + vertical-align: middle + text-align: center + position: relative + .split-view-30 + width: 30% + &.split + &:after + content: "" + position: absolute + width: 2px + height: 80% + background-color: $color-smoke-light + top: 10% + right: -2px + z-index: 9 + .split-view-70 + width: 70% + +.invite-label + .invite-label-b + display: inline-block + .icn + width: 24px + height: 24px + background-image: url($invite-url) + background-position: center + background-size: contain + background-repeat: no-repeat + float: left + margin-right: 16px + .desc font-size: 18px - font-weight: normal - color: $color-common-text line-height: 24px - .opts - margin: 40px 0 0 - .opt - display: inline-block - margin: 0 8px -.choose-chat + float: left + color: $color-smoke-medium + a + text-decoration: none + color: inherit + &:hover + .desc + a + color: $color-smoke-dark + +.active-public-name text-align: center - margin-top: 50px - .choose-chat-b + width: 100% + position: absolute + bottom: $card-padd + 5px + left: 0 + .active-public-name-b display: inline-block - position: relative - &:after - content: "" - display: inline-block - height: 50px - border: 1px solid #dddddd - position: absolute - top: 50% - left: 50% - transform: translate(-50%, -50%) - .choose-chat-i - display: inline-block - width: 200px - height: 200px - background-color: $pr-pale-blue-2 - margin: 0 25px - button - width: 100% - height: 100% - background-color: transparent - .icn - display: block - width: 30px - height: 30px - margin: 0 auto - background-position: center - background-size: 30px - background-repeat: no-repeat - &.invites - .icn - background-image: url('../images/invites.svg') - &.new-chat - .icn - background-image: url('../images/start-chat.svg') + .label + float: left + line-height: 30px + margin-right: 16px + font-size: 14px + font-weight: normal + color: $color-smoke-medium + .value + float: left + line-height: 30px + margin-right: 16px + font-size: 18px + font-weight: 600 + color: $color-smoke-dark + .opt + line-height: 30px + float: left + button + color: $color-pr-blue &:hover - background-color: $pr-pale-blue - button - color: #ffffff - &.no-invites - .choose-chat-b - &:after - display: none - .choose-chat-i - &.invites - display: none + background-color: $color-snow-light + color: $color-pr-blue -.invites - width: 600px - position: relative - padding: 50px 0 - margin: 0 auto - position: relative - .title +.list + width: 80% + margin: 60px auto 0 + text-align: center + h3 font-size: 18px - font-weight: normal - color: $color-common-text - text-align: center - .invites-ls - width: 400px - margin: 0 auto - padding: 40px 0 0 - .invites-ls-i - text-align: center - font-size: 14px - font-weight: normal + color: $color-smoke-medium + font-weight: 600 + margin-bottom: 30px + ul + list-style: none + margin-bottom: 24px + li + font-size: 18px line-height: 36px + font-weight: normal + color: $color-smoke-dark + margin-bottom: 8px cursor: pointer - position: relative + &.active, &:hover - background-color: $pr-pale-blue-2 - .refresh-btn - position: absolute - top: 50px - right: 0 + background-color: $color-snow-light + color: $color-pr-blue + .opts + display: inline-block + margin-top: 24px + .opt + float: left + margin: 0 8px + +.progress + text-align: center + padding-top: 24px + .progress-b + display: inline-block + .icn + width: 30px + height: 30px + float: left + .desc + font-size: 18px + font-weight: normal + float: left + margin-left: 16px + color: $color-smoke-medium + &.error + .icn + background-image: url($error-url) + background-position: center + background-size: contain + background-repeat: no-repeat + .desc + color: $color-error .new-chat - .title + margin-top: 60px + text-align: center + h3 font-size: 18px - font-weight: normal - color: $color-common-text - text-align: center + color: $color-smoke-medium + font-weight: 600 + margin-bottom: 30px .new-chat-form - width: 80% - padding: 25px 0 0 - margin: 0 auto + padding-top: 24px .inpt + width: 70% + margin: 0 auto 24px input width: 100% line-height: 30px - font-size: 14px - padding: 0 8px - .inpt-btn - margin-top: 50px - text-align: center + border: 0 + border-bottom: 2px solid $color-smoke-light + font-size: 16px + outline: 0 + &:focus + border-color: $color-pr-blue + .opts + display: inline-block + .opt + margin: 16px 0 0 + button.primary-green + font-size: 18px + background-color: $color-green + color: #ffffff + line-height: 46px + padding: 0 36px + &:hover + background-color: $color-green-dark -.card - width: 600px - position: relative - margin: 0 auto - &.margin-top - margin-top: 100px - .card-b - background-color: $pr-pale-blue-2 - padding: 50px 0 - border-radius: 10px - &.danger - background-color: $pr-red +.spinner + width: 100% + height: 100% + background-image: url($spinner-url) + background-position: center + background-size: contain + background-repeat: no-repeat + animation: rotator .5s infinite linear -.loading - .icn - width: 50px - height: 50px - margin: 0 auto - .desc - font-size: 14px - font-weight: normal - color: $color-common-text - text-align: center - margin-top: 25px + @keyframes rotator + from + transform: rotate(0deg) + to + transform: rotate(360deg) -.error - .icn - width: 30px - height: 30px - margin: 0 auto - background-image: url("../images/error.svg") - background-position: center - background-size: 30px - background-repeat: no-repeat - .desc - font-size: 16px - font-weight: normal - color: $color-common-text - text-align: center - margin-top: 25px - .opt - text-align: center - margin-top: 50px diff --git a/safe_webrtc_example/app/sass/loader.sass b/safe_webrtc_example/app/sass/loader.sass deleted file mode 100644 index e222896..0000000 --- a/safe_webrtc_example/app/sass/loader.sass +++ /dev/null @@ -1,31 +0,0 @@ -.loader - position: relative - width: 40px - height: 40px - display: inline-flex - background-color: transparent - border: 5px solid #d6dde4 - border-radius: 50% - box-sizing: content-box - &:after - position: absolute - content: "" - top: 0px - left: 0px - width: 100% - height: 100% - padding: 3px - border-width: 2px - border-style: solid - border-color: #404040 transparent transparent transparent - border-radius: 50% - box-sizing: border-box - animation: ringrotate .8s infinite ease-in-out - -webkit-animation: ringrotate .8s infinite ease-in-out -@keyframes ringrotate - 0% - transform: rotateZ(0deg) - -webkit-transform: rotateZ(0deg) - 100% - transform: rotateZ(360deg) - -webkit-transform: rotateZ(360deg) diff --git a/safe_webrtc_example/app/sass/main.sass b/safe_webrtc_example/app/sass/main.sass index 775a679..3dfb51d 100644 --- a/safe_webrtc_example/app/sass/main.sass +++ b/safe_webrtc_example/app/sass/main.sass @@ -2,8 +2,7 @@ $FontPathOpenSans: "../../node_modules/npm-font-open-sans/fonts" @import '../../node_modules/npm-font-open-sans/open-sans.scss' @import 'variables' @import 'mixins' +@import 'button' @import 'common' -@import 'buttons' @import 'components' @import 'chat_room' -@import 'loader' diff --git a/safe_webrtc_example/app/sass/variables.sass b/safe_webrtc_example/app/sass/variables.sass index 606e795..9e4f85c 100644 --- a/safe_webrtc_example/app/sass/variables.sass +++ b/safe_webrtc_example/app/sass/variables.sass @@ -1,11 +1,32 @@ -$pr-blue: #5593d7 -$pr-pale-blue: #75adeb -$pr-pale-blue-2: #eff6fd -$pr-pale-brown: #f4f3f3 -$pr-pale-brown-2: #fbfbfb -$pr-red: #fdefef -$pr-red-dark: #e26464 -$color-common-text: #4a4a4a +$logo-lg: 120px +$logo-sm: 50px +$logo-xs: 30px -$bg-header: #f3f7f9 +$color-pr-blue: #5593d7 +$color-smoke-dark: #555555 +$color-smoke-medium: #8B8B8B +$color-smoke-light: #c2c1c1 +$color-snow-light: #fafafa +$color-green: #9ED167 +$color-green-dark: #7AB639 +$color-logo-desc: $color-pr-blue +$color-end-call: #F20808 +$color-end-call-hover: #FD5454 +$color-error: #FFBBBB + +$body-bg: #fafafa + +$logo-url: '../images/logo.png' +$error-url: '../images/error.svg' +$spinner-url: '../images/loader.svg' +$invite-url: '../images/notification.svg' +$connect-url: '../images/more.svg' +$end-call-url: '../images/call.svg' + +$card-width: 650px +$card-height: 400px +$card-padd: 24px + +$remote-width: 70% +$origin-width: 30% diff --git a/safe_webrtc_example/app/utils.js b/safe_webrtc_example/app/utils.js index 09fb02c..91b3bc1 100644 --- a/safe_webrtc_example/app/utils.js +++ b/safe_webrtc_example/app/utils.js @@ -11,7 +11,7 @@ export const putLog = (msg, data) => { if (!msg) { return; } - // console.log(`${(new Date()).toISOString()} :: ${msg} :: `, data); + console.log(`${(new Date()).toISOString()} :: ${msg} :: `, data); }; export const bufToArr = (buf) => { diff --git a/safe_webrtc_example/webpack.dev.config.js b/safe_webrtc_example/webpack.dev.config.js index 2d096c9..5b6ff33 100644 --- a/safe_webrtc_example/webpack.dev.config.js +++ b/safe_webrtc_example/webpack.dev.config.js @@ -18,6 +18,7 @@ module.exports = { }, plugins: [ new webpack.HotModuleReplacementPlugin(), + new ExtractTextPlugin({ filename: './styles/style.css', disable: false, allChunks: true }), ], resolve: { extensions: ['.js', '.jsx'] From c2f95deb78add543a32ec0bcd547001b20f71a8f Mon Sep 17 00:00:00 2001 From: shankar2105 Date: Thu, 1 Mar 2018 14:33:27 +0530 Subject: [PATCH 6/9] fix/disable_logs: disable logs for production disable logs for production --- safe_webrtc_example/app/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/safe_webrtc_example/app/utils.js b/safe_webrtc_example/app/utils.js index 91b3bc1..09fb02c 100644 --- a/safe_webrtc_example/app/utils.js +++ b/safe_webrtc_example/app/utils.js @@ -11,7 +11,7 @@ export const putLog = (msg, data) => { if (!msg) { return; } - console.log(`${(new Date()).toISOString()} :: ${msg} :: `, data); + // console.log(`${(new Date()).toISOString()} :: ${msg} :: `, data); }; export const bufToArr = (buf) => { From 4e020579b4a664aae84a0daf21f145138b7f6109 Mon Sep 17 00:00:00 2001 From: shankar2105 Date: Thu, 1 Mar 2018 20:28:05 +0530 Subject: [PATCH 7/9] fix/improve_messages: improve label and message texts Improved label and message texts to make things consistent throughout the application. --- safe_webrtc_example/app/constants.js | 40 ++++++++++++++-------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/safe_webrtc_example/app/constants.js b/safe_webrtc_example/app/constants.js index c5bd286..29b6294 100644 --- a/safe_webrtc_example/app/constants.js +++ b/safe_webrtc_example/app/constants.js @@ -2,14 +2,14 @@ export default { UI: { LABELS: { title: 'SAFE WebRTC Signalling', - activePubName: 'Active public name', - newVideoCall: 'New Video Call', + activePubName: 'Active Public Name', + newVideoCall: 'New video call', invites: 'Invites', switch: 'Switch', connect: 'Connect', cancel: 'Cancel', activate: 'Activate', - friendIdPlaceholder: 'Enter friend\'s public name', + friendIdPlaceholder: 'Enter remote peer\'s Public Name', chooseInvite: 'Choose an invite', noInvites: 'No invites available', noPublicName: 'No Public Name available to switch', @@ -17,34 +17,34 @@ export default { }, MESSAGES: { authorise: 'Authorising with Authenticator', - authoriseFail: 'Authorisation Failed', + authoriseFail: 'Authorisation failed', initialise: 'Initialising application', initialiseFail: 'Failed to initialise application', noPubNameFound: 'No Public Name found.', fetchPublicName: 'Fetching Public Names', fetchPublicNameFail: 'Unable to fetch Public Names', - fetchInvites: 'Fetching Invites', - fetchInvitesFail: 'Unable to fetch Invites', - activatePublicName: 'Activating public name', + fetchInvites: 'Fetching invites', + fetchInvitesFail: 'Unable to fetch invites', + activatePublicName: 'Activating Public Name', activatePublicNameFail: 'Failed to activate Public Name', - connecting: 'Connecting with friend', - connectingFail: 'Failed to connect with friend', + connecting: 'Connecting with remote peer', + connectingFail: 'Failed to connect with remote peer', invalidPublicName: 'Invalid Public Name', cantInviteYourself: 'Can\'t invite yourself', inviteAcceptFail: 'Failed to accept invite', - callAcceptFail: 'Failed after remote accepted the call', - checkCallingFail: 'Failed to accept remote call', + callAcceptFail: 'Failed after remote peer accepted the call', + checkCallingFail: 'Failed to accept remote peer call', initialisationFail: 'Failed to initialise the connection', - sendInviteFail: 'Failed to send invitation to remote', - callingFail: 'Failed to call remote', - connectingFail: 'Failed to connect with remote' + sendInviteFail: 'Failed to send invitation to remote peer', + callingFail: 'Failed to call remote peer', + connectingFail: 'Failed to connect with remote peer' }, DEFAULT_LOADING_DESC: 'Please wait...', CONN_MSGS: { INIT: 'Initialising connection', SEND_INVITE: 'Invite sent. Waiting for the remote peer to accept the connection', - INVITE_ACCEPTED: 'Invite accepted. Establishing connection with remote', - CALLING: 'Remote accepted the invite. Establishing connection with remote', + INVITE_ACCEPTED: 'Invite accepted. Establishing connection with remote peer', + CALLING: 'Remote peer accepted invite. Establishing connection', }, CONN_TIMER_INTERVAL: 2000, TIMER_INTERVAL: { @@ -55,11 +55,11 @@ export default { CONFIG: { SERVER: { iceServers: [ - { url: 'STUN_SERVER_URL' }, // fill STUN Server url + { url: 'stun:stun1.l.google.com:19302' }, // fill STUN Server url { - url: 'TURN_SERVER_URL', // fill turn server url - credential: 'TURN_PASSWORD', // fill turn server password - username: 'TURN_USERNAME' // fill turn server username + url: 'turn:numb.viagenie.ca', // fill turn server url + credential: 'string21', // fill turn server password + username: 'shankar21mail@gmail.com' // fill turn server username }, ] }, From 35f4a5fc7463e4422d0b980d290de740c50bde80 Mon Sep 17 00:00:00 2001 From: shankar2105 Date: Thu, 1 Mar 2018 21:05:20 +0530 Subject: [PATCH 8/9] fix/remove_credentials: remove credentials Replace place holder instead of credentials. --- safe_webrtc_example/app/constants.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/safe_webrtc_example/app/constants.js b/safe_webrtc_example/app/constants.js index 29b6294..91a252c 100644 --- a/safe_webrtc_example/app/constants.js +++ b/safe_webrtc_example/app/constants.js @@ -55,11 +55,11 @@ export default { CONFIG: { SERVER: { iceServers: [ - { url: 'stun:stun1.l.google.com:19302' }, // fill STUN Server url + { url: 'STUN_SERVER_URL' }, // fill STUN Server url { - url: 'turn:numb.viagenie.ca', // fill turn server url - credential: 'string21', // fill turn server password - username: 'shankar21mail@gmail.com' // fill turn server username + url: 'TURN_SERVER_URL', // fill turn server url + credential: 'TURN_PASSWORD', // fill turn server password + username: 'TURN_USERNAME' // fill turn server username }, ] }, From 221194f60a754db7da493b54e9a861ea5f2492e0 Mon Sep 17 00:00:00 2001 From: shankar2105 Date: Thu, 1 Mar 2018 22:50:24 +0530 Subject: [PATCH 9/9] fix/travis: fix travis issue Fixed travis issue, failing to find test script on root package.json --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index f5886e8..c76b640 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "pack:webhosting": "cd web_hosting_manager && yarn package && yarn post-package", "post-pack:webhosting": "cd web_hosting_manager && yarn post-package", "pack:email": "cd email_app && yarn package && yarn post-package", - "post-pack:email": "cd email_app && yarn post-package" + "post-pack:email": "cd email_app && yarn post-package", + "test": "echo \"No test specified\" && exit 0" }, "dependencies": { "archiver": "^2.0.3",