Pouria Ezzati 8 gadi atpakaļ
revīzija
6af694826d
100 mainītis faili ar 13281 papildinājumiem un 0 dzēšanām
  1. 4 0
      .babelrc
  2. 1 0
      .env
  3. 3 0
      .eslintignore
  4. 34 0
      .eslintrc
  5. 5 0
      .gitignore
  6. 12 0
      .travis.yml
  7. 21 0
      LICENSE
  8. 84 0
      README.md
  9. 30 0
      client/actions/actionTypes.js
  10. 137 0
      client/actions/index.js
  11. 95 0
      client/components/BodyWrapper/BodyWrapper.js
  12. 1 0
      client/components/BodyWrapper/index.js
  13. 155 0
      client/components/Button/Button.js
  14. 1 0
      client/components/Button/index.js
  15. 92 0
      client/components/Checkbox/Checkbox.js
  16. 1 0
      client/components/Checkbox/index.js
  17. 63 0
      client/components/Error/Error.js
  18. 1 0
      client/components/Error/index.js
  19. 71 0
      client/components/Features/Features.js
  20. 97 0
      client/components/Features/FeaturesItem.js
  21. 1 0
      client/components/Features/index.js
  22. 49 0
      client/components/Footer/Footer.js
  23. 1 0
      client/components/Footer/index.js
  24. 54 0
      client/components/Header/Header.js
  25. 32 0
      client/components/Header/HeaderLeftMenu.js
  26. 54 0
      client/components/Header/HeaderLogo.js
  27. 38 0
      client/components/Header/HeaderMenuItem.js
  28. 74 0
      client/components/Header/HeaderRightMenu.js
  29. 1 0
      client/components/Header/index.js
  30. 195 0
      client/components/Login/Login.js
  31. 31 0
      client/components/Login/LoginBox.js
  32. 21 0
      client/components/Login/LoginInputLabel.js
  33. 1 0
      client/components/Login/index.js
  34. 66 0
      client/components/Modal/Modal.js
  35. 1 0
      client/components/Modal/index.js
  36. 89 0
      client/components/NeedToLogin/NeedToLogin.js
  37. 1 0
      client/components/NeedToLogin/index.js
  38. 28 0
      client/components/PageLoading/PageLoading.js
  39. 1 0
      client/components/PageLoading/index.js
  40. 224 0
      client/components/Settings/Settings.js
  41. 82 0
      client/components/Settings/SettingsApi.js
  42. 103 0
      client/components/Settings/SettingsDomain.js
  43. 59 0
      client/components/Settings/SettingsPassword.js
  44. 29 0
      client/components/Settings/SettingsWelcome.js
  45. 1 0
      client/components/Settings/index.js
  46. 155 0
      client/components/Shortener/Shortener.js
  47. 19 0
      client/components/Shortener/ShortenerCaptcha.js
  48. 76 0
      client/components/Shortener/ShortenerInput.js
  49. 141 0
      client/components/Shortener/ShortenerOptions.js
  50. 70 0
      client/components/Shortener/ShortenerResult.js
  51. 25 0
      client/components/Shortener/ShortenerTitle.js
  52. 1 0
      client/components/Shortener/index.js
  53. 168 0
      client/components/Stats/Stats.js
  54. 76 0
      client/components/Stats/StatsCharts/Area.js
  55. 31 0
      client/components/Stats/StatsCharts/Bar.js
  56. 34 0
      client/components/Stats/StatsCharts/Pie.js
  57. 78 0
      client/components/Stats/StatsCharts/StatsCharts.js
  58. 1 0
      client/components/Stats/StatsCharts/index.js
  59. 54 0
      client/components/Stats/StatsCharts/withTitle.js
  60. 46 0
      client/components/Stats/StatsError.js
  61. 102 0
      client/components/Stats/StatsHead.js
  62. 1 0
      client/components/Stats/index.js
  63. 137 0
      client/components/Table/TBody/TBody.js
  64. 73 0
      client/components/Table/TBody/TBodyButton.js
  65. 87 0
      client/components/Table/TBody/TBodyCount.js
  66. 46 0
      client/components/Table/TBody/TBodyShortUrl.js
  67. 1 0
      client/components/Table/TBody/index.js
  68. 55 0
      client/components/Table/THead/THead.js
  69. 1 0
      client/components/Table/THead/index.js
  70. 161 0
      client/components/Table/Table.js
  71. 67 0
      client/components/Table/TableNav.js
  72. 200 0
      client/components/Table/TableOptions.js
  73. 1 0
      client/components/Table/index.js
  74. 122 0
      client/components/TextInput/TextInput.js
  75. 1 0
      client/components/TextInput/index.js
  76. 10 0
      client/config.example.js
  77. 23 0
      client/helpers/analytics.js
  78. 19 0
      client/helpers/animations.js
  79. 17 0
      client/helpers/recaptcha.js
  80. 67 0
      client/pages/_document.js
  81. 55 0
      client/pages/index.js
  82. 39 0
      client/pages/login.js
  83. 23 0
      client/pages/logout.js
  84. 130 0
      client/pages/reset-password.js
  85. 28 0
      client/pages/settings.js
  86. 29 0
      client/pages/stats.js
  87. 62 0
      client/pages/terms.js
  88. 120 0
      client/pages/url-password.js
  89. 101 0
      client/pages/verify.js
  90. 191 0
      client/reducers/index.js
  91. 9 0
      client/store/index.js
  92. 9 0
      client/store/store.dev.js
  93. 7 0
      client/store/store.prod.js
  94. 7665 0
      package-lock.json
  95. 86 0
      package.json
  96. 30 0
      server/config.example.js
  97. 179 0
      server/controllers/authController.js
  98. 172 0
      server/controllers/urlController.js
  99. 27 0
      server/controllers/validateBodyController.js
  100. 9 0
      server/db/neo4j.js

+ 4 - 0
.babelrc

@@ -0,0 +1,4 @@
+{
+  "presets": ["next/babel", "env"],
+  "plugins": [["styled-components", { "ssr": true, "displayName": true, "preprocess": false }]]
+}

+ 1 - 0
.env

@@ -0,0 +1 @@
+TEST=test

+ 3 - 0
.eslintignore

@@ -0,0 +1,3 @@
+.next/
+flow-typed/
+node_modules/

+ 34 - 0
.eslintrc

@@ -0,0 +1,34 @@
+{
+  "extends": [
+    "airbnb",
+    "prettier",
+    "prettier/react"
+  ],
+  "parser": "babel-eslint",
+  "env": {
+    "browser": true,
+    "node": true
+  },
+  "rules": {
+    "react/jsx-filename-extension": [
+      1,
+      {
+        "extensions": [
+          ".js",
+          ".jsx"
+        ]
+      }
+    ],
+    "prettier/prettier": [
+      "error",
+      {
+        "trailingComma": "es5",
+        "singleQuote": true,
+        "printWidth": 100
+      }
+    ]
+  },
+  "plugins": [
+    "prettier"
+  ]
+}

+ 5 - 0
.gitignore

@@ -0,0 +1,5 @@
+.vscode/
+client/.next/
+node_modules/
+client/config.js
+server/config.js

+ 12 - 0
.travis.yml

@@ -0,0 +1,12 @@
+language: node_js
+
+node_js:
+  - "8"
+
+before_install:
+  - cp ./server/config.example.js ./server/config.js
+  - cp ./client/config.example.js ./client/config.js
+
+script:
+  - npm run lint:nofix
+  - npm run build

+ 21 - 0
LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 The Devs
+
+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.

+ 84 - 0
README.md

@@ -0,0 +1,84 @@
+<a href="https://kutt.it" title="kutt.it"><img src="https://camo.githubusercontent.com/073e709d02d3cf6ee5439ee6ce0bb0895f9f3733/687474703a2f2f6f6936372e74696e797069632e636f6d2f3636797a346f2e6a7067" alt="Kutt.it"></a>
+
+# Kutt.it
+
+**Kutt** is a modern URL shortener which lets you set custom domains for your shortened URLs, manage your links and view the click rate statistics.
+
+*Contributions and bug reports are welcome.*
+
+[https://kutt.it](https://kutt.it)
+
+[![Build Status](https://travis-ci.org/thedevs-network/kutt.svg?branch=develop)](https://travis-ci.org/thedevs-network/kutt)
+[![Contributions](https://img.shields.io/badge/contributions-welcome-brightgreen.svg)](https://github.com/thedevs-network/kutt/#contributing)
+[![GitHub license](https://img.shields.io/github/license/thedevs-network/kutt.svg)](https://github.com/thedevs-network/kutt/blob/develop/LICENSE)
+[![Twitter](https://img.shields.io/twitter/url/https/github.com/thedevs-network/kutt/.svg?style=social)](https://twitter.com/intent/tweet?text=Wow:&url=https%3A%2F%2Fgithub.com%2Fthedevs-network%2Fkutt%2F)
+
+## Table of Contents
+* [Key Features](#key-features)
+* [Stack](#stack)
+* [Setup](#setup)
+* [API](#api)
+* [Contributing](#contributing)
+
+## Key Features
+* Free and open source.
+* Setting custom domain.
+* Using custom URLs for shortened links
+* Setting password for links.
+* Private statistics for shortened URLs.
+* View and manage your links.
+* Provided API.
+
+## Stack
+* Node (Web server)
+* Express (Web server framework)
+* Passport (Authentication)
+* React (UI library)
+* Next (Universal/server-side rendered React)
+* Redux (State management)
+* styled-components (CSS styling solution library)
+* Recharts (Chart library)
+* Neo4j (Graph database)
+
+## Setup
+You need to have [Node.js](https://nodejs.org/) and [Neo4j](https://neo4j.com/) installed on your system.
+
+1. Clone this repository on [downlaod zip](https://github.com/thedevs-network/kutt/archive/master.zip).
+2. Copy `config.example.js` to `config.js` in both server and client folders and fill them properly.
+3. Install dependencies: `npm install`.
+4. Start Neo4j database.
+5. Run for development: `npm run dev`.
+6. Run for production: `npm run build` then `npm start`.
+
+## API
+In additional to website, you can use these APIs to create, delete and get URLs.
+
+In order to use these APIs you need to generate an API key from settings. Don not ever put this key in the client side of your app or anywhere that is exposed to others.
+
+Include API key as `apikey` in the body of all below requests. Available API URLs with body parameters:
+
+**Get shortened URLs list:**
+```
+POST /api/url/geturls
+```
+
+**Submit a links to be shortened**:
+```
+POST /api/url/submit
+```
+Body:
+  * `target`: Original long URL to be shortened.
+
+**Delete a shortened URL** and **Get stats for a shortened URL:**
+```
+POST /api/url/deleteurl
+POST /api/url/stats
+```
+Body
+  * `id`: ID of the shortened URL.
+  * `domain` (optional):  Required if a custom domain is used for short URL.
+
+## Contributing
+Pull requests are welcome. You'll probably find lots of improvements to be made.
+
+Open issues for feadback, needed features, reporting bugs or discussing ideas.

+ 30 - 0
client/actions/actionTypes.js

@@ -0,0 +1,30 @@
+/* Homepage input actions */
+export const ADD_URL = 'ADD_URL';
+export const UPDATE_URL_LIST = 'UPDATE_URL_LIST';
+export const LIST_URLS = 'LIST_URLS';
+export const DELETE_URL = 'DELETE_URL';
+export const SHORTENER_ERROR = 'SHORTENER_ERROR';
+export const SHORTENER_LOADING = 'SHORTENER_LOADING';
+export const TABLE_LOADING = 'TABLE_LOADING';
+
+/* Page loading actions */
+export const SHOW_PAGE_LOADING = 'SHOW_PAGE_LOADING';
+export const HIDE_PAGE_LOADING = 'HIDE_PAGE_LOADING';
+
+/* Login & signup actions */
+export const AUTH_USER = 'AUTH_USER';
+export const AUTH_RENEW = 'AUTH_RENEW';
+export const UNAUTH_USER = 'UNAUTH_USER';
+export const SENT_VERIFICATION = 'SENT_VERIFICATION';
+export const AUTH_ERROR = 'AUTH_ERROR';
+export const LOGIN_LOADING = 'LOGIN_LOADING';
+export const SIGNUP_LOADING = 'SIGNUP_LOADING';
+
+/* Settings actions */
+export const SET_DOMAIN = 'SET_DOMAIN';
+export const SET_APIKEY = 'SET_APIKEY';
+export const DELETE_DOMAIN = 'DELETE_DOMAIN';
+export const DOMAIN_LOADING = 'DOMAIN_LOADING';
+export const API_LOADING = 'API_LOADING';
+export const DOMAIN_ERROR = 'DOMAIN_ERROR';
+export const SHOW_DOMAIN_INPUT = 'SHOW_DOMAIN_INPUT';

+ 137 - 0
client/actions/index.js

@@ -0,0 +1,137 @@
+import Router from 'next/router';
+import axios from 'axios';
+import cookie from 'js-cookie';
+import decodeJwt from 'jwt-decode';
+import * as types from './actionTypes';
+
+/* Homepage input actions */
+const addUrl = payload => ({ type: types.ADD_URL, payload });
+const listUrls = payload => ({ type: types.LIST_URLS, payload });
+const updateUrlList = payload => ({ type: types.UPDATE_URL_LIST, payload });
+const deleteUrl = payload => ({ type: types.DELETE_URL, payload });
+const showShortenerLoading = () => ({ type: types.SHORTENER_LOADING });
+const showTableLoading = () => ({ type: types.TABLE_LOADING });
+export const setShortenerFormError = payload => ({ type: types.SHORTENER_ERROR, payload });
+
+export const createShortUrl = params => dispatch => {
+  dispatch(showShortenerLoading());
+  return axios
+    .post('/api/url/submit', params, { headers: { Authorization: cookie.get('token') } })
+    .then(({ data }) => dispatch(addUrl(data)))
+    .catch(({ response }) => dispatch(setShortenerFormError(response.data.error)));
+};
+
+export const getUrlsList = params => (dispatch, getState) => {
+  if (params) dispatch(updateUrlList(params));
+  dispatch(showTableLoading());
+  return axios
+    .post('/api/url/geturls', getState().url, { headers: { Authorization: cookie.get('token') } })
+    .then(({ data }) => dispatch(listUrls(data)));
+};
+
+export const deleteShortUrl = params => dispatch => {
+  dispatch(showTableLoading());
+  return axios
+    .post('/api/url/deleteurl', params, { headers: { Authorization: cookie.get('token') } })
+    .then(() => dispatch(deleteUrl(params.id)))
+    .catch(({ response }) => dispatch(setShortenerFormError(response.data.error)));
+};
+/* Page loading actions */
+export const showPageLoading = () => ({ type: types.SHOW_PAGE_LOADING });
+export const hidePageLoading = () => ({ type: types.HIDE_PAGE_LOADING });
+
+/* Settings actions */
+export const setDomain = payload => ({ type: types.SET_DOMAIN, payload });
+export const setApiKey = payload => ({ type: types.SET_APIKEY, payload });
+const deleteDomain = () => ({ type: types.DELETE_DOMAIN });
+const setDomainError = payload => ({ type: types.DOMAIN_ERROR, payload });
+const showDomainLoading = () => ({ type: types.DOMAIN_LOADING });
+const showApiLoading = () => ({ type: types.API_LOADING });
+export const showDomainInput = () => ({ type: types.SHOW_DOMAIN_INPUT });
+
+export const getUserSettings = () => dispatch =>
+  axios
+    .post('/api/auth/usersettings', null, { headers: { Authorization: cookie.get('token') } })
+    .then(({ data }) => {
+      dispatch(setDomain(data.customDomain));
+      dispatch(setApiKey(data.apikey));
+    });
+
+export const setCustomDomain = params => dispatch => {
+  dispatch(showDomainLoading());
+  return axios
+    .post('/api/url/customdomain', params, { headers: { Authorization: cookie.get('token') } })
+    .then(({ data }) => dispatch(setDomain(data.customDomain)))
+    .catch(({ response }) => dispatch(setDomainError(response.data.error)));
+};
+
+export const deleteCustomDomain = () => dispatch =>
+  axios
+    .delete('/api/url/customdomain', { headers: { Authorization: cookie.get('token') } })
+    .then(() => dispatch(deleteDomain()))
+    .catch(({ response }) => dispatch(setDomainError(response.data.error)));
+
+export const generateApiKey = () => dispatch => {
+  dispatch(showApiLoading());
+  return axios
+    .post('/api/auth/generateapikey', null, { headers: { Authorization: cookie.get('token') } })
+    .then(({ data }) => dispatch(setApiKey(data.apikey)));
+};
+
+/* Login & signup actions */
+export const authUser = payload => ({ type: types.AUTH_USER, payload: decodeJwt(payload).sub });
+export const unauthUser = () => ({ type: types.UNAUTH_USER });
+export const sentVerification = payload => ({ type: types.SENT_VERIFICATION, payload });
+export const showAuthError = payload => ({ type: types.AUTH_ERROR, payload });
+export const showLoginLoading = () => ({ type: types.LOGIN_LOADING });
+export const showSignupLoading = () => ({ type: types.SIGNUP_LOADING });
+export const authRenew = () => ({ type: types.AUTH_RENEW });
+
+export const signupUser = body => dispatch => {
+  dispatch(showSignupLoading());
+  return axios
+    .post('/api/auth/signup', body)
+    .then(res => {
+      const { email } = res.data;
+      dispatch(sentVerification(email));
+    })
+    .catch(err => dispatch(showAuthError(err.response.data.error)));
+};
+
+export const loginUser = body => dispatch => {
+  dispatch(showLoginLoading());
+  return axios
+    .post('/api/auth/login', body)
+    .then(res => {
+      const { token } = res.data;
+      cookie.set('token', token, { expires: 7 });
+      dispatch(authRenew());
+      dispatch(authUser(token));
+      dispatch(showPageLoading());
+      Router.push('/');
+    })
+    .catch(err => dispatch(showAuthError(err.response.data.error)));
+};
+
+export const logoutUser = () => dispatch => {
+  dispatch(showPageLoading());
+  cookie.remove('token');
+  dispatch(unauthUser());
+  return Router.push('/login');
+};
+
+export const renewAuthUser = () => (dispatch, getState) => {
+  if (getState().auth.renew) return null;
+  return axios
+    .post('/api/auth/renew', null, { headers: { Authorization: cookie.get('token') } })
+    .then(res => {
+      const { token } = res.data;
+      cookie.set('token', token, { expires: 7 });
+      dispatch(authRenew());
+      dispatch(authUser(token));
+    })
+    .catch(() => {
+      cookie.remove('token');
+      dispatch(unauthUser());
+    });
+};

+ 95 - 0
client/components/BodyWrapper/BodyWrapper.js

@@ -0,0 +1,95 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { bindActionCreators } from 'redux';
+import { connect } from 'react-redux';
+import styled from 'styled-components';
+import cookie from 'js-cookie';
+import Header from '../Header';
+import PageLoading from '../PageLoading';
+import { renewAuthUser, hidePageLoading } from '../../actions';
+import { initGA, logPageView } from '../../helpers/analytics';
+import { GOOGLE_ANALYTICS_ID } from '../../config';
+
+const Wrapper = styled.div`
+  position: relative;
+  min-height: 100vh;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  box-sizing: border-box;
+
+  * {
+    box-sizing: border-box;
+  }
+
+  *::-moz-focus-inner {
+    border: none;
+  }
+
+  @media only screen and (max-width: 448px) {
+    font-size: 14px;
+  }
+`;
+
+const ContentWrapper = styled.div`
+  min-height: 100vh;
+  width: 100%;
+  flex: 0 0 auto;
+  display: flex;
+  align-items: center;
+  flex-direction: column;
+  box-sizing: border-box;
+`;
+
+class BodyWrapper extends React.Component {
+  componentDidMount() {
+    if (GOOGLE_ANALYTICS_ID) {
+      if (!window.GA_INITIALIZED) {
+        initGA();
+        window.GA_INITIALIZED = true;
+      }
+      logPageView();
+    }
+
+    const token = cookie.get('token');
+    this.props.hidePageLoading();
+    if (!token || this.props.norenew) return null;
+    return this.props.renewAuthUser(token);
+  }
+
+  render() {
+    const { children, pageLoading } = this.props;
+
+    const content = pageLoading ? <PageLoading /> : children;
+
+    return (
+      <Wrapper>
+        <ContentWrapper>
+          <Header />
+          {content}
+        </ContentWrapper>
+      </Wrapper>
+    );
+  }
+}
+
+BodyWrapper.propTypes = {
+  children: PropTypes.node.isRequired,
+  hidePageLoading: PropTypes.func.isRequired,
+  norenew: PropTypes.bool,
+  pageLoading: PropTypes.bool.isRequired,
+  renewAuthUser: PropTypes.func.isRequired,
+};
+
+BodyWrapper.defaultProps = {
+  norenew: false,
+};
+
+const mapStateToProps = ({ loading: { page: pageLoading } }) => ({ pageLoading });
+
+const mapDispatchToProps = dispatch => ({
+  hidePageLoading: bindActionCreators(hidePageLoading, dispatch),
+  renewAuthUser: bindActionCreators(renewAuthUser, dispatch),
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(BodyWrapper);

+ 1 - 0
client/components/BodyWrapper/index.js

@@ -0,0 +1 @@
+export { default } from './BodyWrapper';

+ 155 - 0
client/components/Button/Button.js

@@ -0,0 +1,155 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import styled, { css } from 'styled-components';
+import SVG from 'react-inlinesvg';
+import { spin } from '../../helpers/animations';
+
+const StyledButton = styled.button`
+  position: relative;
+  height: 40px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 0px 32px;
+  font-size: 13px;
+  font-weight: normal;
+  text-align: center;
+  line-height: 1;
+  word-break: keep-all;
+  color: white;
+  background: linear-gradient(to right, #42a5f5, #2979ff);
+  box-shadow: 0 5px 6px rgba(66, 165, 245, 0.5);
+  border: none;
+  border-radius: 100px;
+  transition: all 0.4s ease-out;
+  cursor: pointer;
+  overflow: hidden;
+
+  :hover,
+  :focus {
+    outline: none;
+    box-shadow: 0 6px 15px rgba(66, 165, 245, 0.5);
+    transform: translateY(-2px) scale(1.02, 1.02);
+  }
+
+  a & {
+    text-decoration: none;
+    border: none;
+  }
+
+  @media only screen and (max-width: 448px) {
+    height: 32px;
+    padding: 0 24px;
+    font-size: 12px;
+  }
+
+  ${({ color }) => {
+    if (color === 'purple') {
+      return css`
+        background: linear-gradient(to right, #7e57c2, #6200ea);
+        box-shadow: 0 5px 6px rgba(81, 45, 168, 0.5);
+
+        :focus,
+        :hover {
+          box-shadow: 0 6px 15px rgba(81, 45, 168, 0.5);
+        }
+      `;
+    }
+    if (color === 'gray') {
+      return css`
+        color: black;
+        background: linear-gradient(to right, #e0e0e0, #bdbdbd);
+        box-shadow: 0 5px 6px rgba(160, 160, 160, 0.5);
+
+        :focus,
+        :hover {
+          box-shadow: 0 6px 15px rgba(160, 160, 160, 0.5);
+        }
+      `;
+    }
+    return null;
+  }};
+
+  ${({ big }) =>
+    big &&
+    css`
+      height: 56px;
+      @media only screen and (max-width: 448px) {
+        height: 40px;
+      }
+    `};
+`;
+
+const Icon = styled(SVG)`
+  svg {
+    width: 16px;
+    height: 16px;
+    margin-right: 12px;
+    stroke: #fff;
+
+    ${({ type }) =>
+      type === 'loader' &&
+      css`
+        width: 20px;
+        height: 20px;
+        margin: 0;
+        animation: ${spin} 1s linear infinite;
+      `};
+
+    ${({ round }) =>
+      round &&
+      css`
+        width: 15px;
+        height: 15px;
+        margin: 0;
+      `};
+
+    ${({ color }) =>
+      color === 'gray' &&
+      css`
+        stroke: #444;
+      `};
+
+    @media only screen and (max-width: 768px) {
+      width: 12px;
+      height: 12px;
+      margin-right: 6px;
+    }
+  }
+`;
+
+const Button = props => {
+  const SVGIcon = props.icon ? (
+    <Icon
+      type={props.icon}
+      round={props.round}
+      color={props.color}
+      src={`/images/${props.icon}.svg`}
+    />
+  ) : (
+    ''
+  );
+  return (
+    <StyledButton {...props}>
+      {SVGIcon}
+      {props.icon !== 'loader' && props.children}
+    </StyledButton>
+  );
+};
+
+Button.propTypes = {
+  children: PropTypes.node.isRequired,
+  color: PropTypes.string,
+  icon: PropTypes.string,
+  round: PropTypes.bool,
+  type: PropTypes.string,
+};
+
+Button.defaultProps = {
+  color: 'blue',
+  icon: '',
+  type: '',
+  round: false,
+};
+
+export default Button;

+ 1 - 0
client/components/Button/index.js

@@ -0,0 +1 @@
+export { default } from './Button';

+ 92 - 0
client/components/Checkbox/Checkbox.js

@@ -0,0 +1,92 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import styled, { css } from 'styled-components';
+
+const Wrapper = styled.div`
+  display: flex;
+  justify-content: flex-start;
+  align-items: center;
+  margin: 24px 32px 24px 0;
+`;
+
+const Box = styled.span`
+  position: relative;
+  display: flex;
+  align-items: center;
+  font-weight: normal;
+  color: #666;
+  transition: color 0.3s ease-out;
+  cursor: pointer;
+
+  :hover {
+    color: black;
+  }
+  :before {
+    content: '';
+    display: block;
+    width: 18px;
+    height: 18px;
+    margin-right: 10px;
+    border-radius: 4px;
+    background-color: white;
+    box-shadow: 0 2px 4px rgba(50, 50, 50, 0.2);
+    cursor: pointer;
+
+    @media only screen and (max-width: 768px) {
+      width: 14px;
+      height: 14px;
+      margin-right: 8px;
+    }
+  }
+
+  ${({ checked }) =>
+    checked &&
+    css`
+      :before {
+        box-shadow: 0 3px 5px rgba(50, 50, 50, 0.4);
+      }
+      :after {
+        content: '';
+        position: absolute;
+        left: 2px;
+        top: 4px;
+        width: 14px;
+        height: 14px;
+        display: block;
+        margin-right: 10px;
+        border-radius: 2px;
+        background-color: #9575cd;
+        box-shadow: 0 2px 4px rgba(50, 50, 50, 0.2);
+        cursor: pointer;
+
+        @media only screen and (max-width: 768px) {
+          left: 2px;
+          top: 5px;
+          width: 10px;
+          height: 10px;
+        }
+      }
+    `};
+`;
+
+const Checkbox = ({ checked, label, id, onClick }) => (
+  <Wrapper>
+    <Box checked={checked} id={id} onClick={onClick}>
+      {label}
+    </Box>
+  </Wrapper>
+);
+
+Checkbox.propTypes = {
+  checked: PropTypes.bool,
+  label: PropTypes.string.isRequired,
+  id: PropTypes.string.isRequired,
+  onClick: PropTypes.func,
+};
+
+Checkbox.defaultProps = {
+  checked: false,
+  onClick: f => f,
+};
+
+export default Checkbox;

+ 1 - 0
client/components/Checkbox/index.js

@@ -0,0 +1 @@
+export { default } from './Checkbox';

+ 63 - 0
client/components/Error/Error.js

@@ -0,0 +1,63 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+import styled, { css } from 'styled-components';
+import { fadeIn } from '../../helpers/animations';
+
+const ErrorMessage = styled.p`
+  content: '';
+  position: absolute;
+  right: 36px;
+  bottom: -64px;
+  display: block;
+  font-size: 14px;
+  color: red;
+  animation: ${fadeIn} 0.3s ease-out;
+
+  @media only screen and (max-width: 768px) {
+    right: 8px;
+    bottom: -40px;
+    font-size: 12px;
+  }
+
+  ${({ left }) =>
+    left > -1 &&
+    css`
+      right: auto;
+      left: ${left}px;
+    `};
+
+  ${({ bottom }) =>
+    bottom &&
+    css`
+      bottom: ${bottom}px;
+    `};
+`;
+
+const Error = ({ bottom, error, left, type }) => {
+  const message = error[type] && (
+    <ErrorMessage left={left} bottom={bottom}>
+      {error[type]}
+    </ErrorMessage>
+  );
+  return <div>{message}</div>;
+};
+
+Error.propTypes = {
+  bottom: PropTypes.number,
+  error: PropTypes.shape({
+    auth: PropTypes.string.isRequired,
+    shortener: PropTypes.string.isRequired,
+  }).isRequired,
+  type: PropTypes.string.isRequired,
+  left: PropTypes.number,
+};
+
+Error.defaultProps = {
+  bottom: -64,
+  left: -1,
+};
+
+const mapStateToProps = ({ error }) => ({ error });
+
+export default connect(mapStateToProps)(Error);

+ 1 - 0
client/components/Error/index.js

@@ -0,0 +1 @@
+export { default } from './Error';

+ 71 - 0
client/components/Features/Features.js

@@ -0,0 +1,71 @@
+import React from 'react';
+import styled from 'styled-components';
+import FeaturesItem from './FeaturesItem';
+
+const Section = styled.div`
+  position: relative;
+  width: 100%;
+  flex: 0 0 auto;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  margin: 0;
+  padding: 102px 0;
+  background-color: #eaeaea;
+
+  @media only screen and (max-width: 768px) {
+    margin: 0;
+    padding: 64px 0 16px;
+    flex-wrap: wrap;
+  }
+`;
+
+const Wrapper = styled.div`
+  width: 1200px;
+  max-width: 100%;
+  flex: 1 1 auto;
+  display: flex;
+  justify-content: center;
+
+  @media only screen and (max-width: 1200px) {
+    flex-wrap: wrap;
+  }
+`;
+
+const Title = styled.h3`
+  font-size: 28px;
+  font-weight: 300;
+  margin: 0 0 72px;
+
+  @media only screen and (max-width: 768px) {
+    font-size: 24px;
+    margin-bottom: 56px;
+  }
+
+  @media only screen and (max-width: 448px) {
+    font-size: 20px;
+    margin-bottom: 40px;
+  }
+`;
+
+const Features = () => (
+  <Section>
+    <Title>Kutting edge features.</Title>
+    <Wrapper>
+      <FeaturesItem title="Managing links" icon="edit">
+        Create, protect and delete your links and monitor them with detailed statistics.
+      </FeaturesItem>
+      <FeaturesItem title="Custom domain" icon="navigation">
+        Use custom domains for your links. Add or remove them for free.
+      </FeaturesItem>
+      <FeaturesItem title="API" icon="zap">
+        Use the provided API to create, delete and get URLs from anywhere.
+      </FeaturesItem>
+      <FeaturesItem title="Free &amp; open source" icon="heart">
+        Completely open source and free. You can host it on your own server.
+      </FeaturesItem>
+    </Wrapper>
+  </Section>
+);
+
+export default Features;

+ 97 - 0
client/components/Features/FeaturesItem.js

@@ -0,0 +1,97 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import styled from 'styled-components';
+import { fadeIn } from '../../helpers/animations';
+
+const Block = styled.div`
+  max-width: 25%;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  padding: 0 24px;
+  animation: ${fadeIn} 0.8s ease-out;
+
+  :last-child {
+    margin-right: 0;
+  }
+
+  @media only screen and (max-width: 1200px) {
+    margin-bottom: 48px;
+  }
+
+  @media only screen and (max-width: 980px) {
+    max-width: 50%;
+  }
+
+  @media only screen and (max-width: 760px) {
+    max-width: 100%;
+  }
+`;
+
+const IconBox = styled.div`
+  width: 48px;
+  height: 48px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  border-radius: 100%;
+  box-sizing: border-box;
+  background-color: #2196f3;
+
+  @media only screen and (max-width: 448px) {
+    width: 40px;
+    height: 40px;
+  }
+`;
+
+const Icon = styled.img`
+  display: inline-block;
+  width: 16px;
+  height: 16px;
+  margin: 0;
+  padding: 0;
+
+  @media only screen and (max-width: 448px) {
+    width: 14px;
+    height: 14px;
+  }
+`;
+
+const Title = styled.h3`
+  margin: 16px;
+  font-size: 15px;
+
+  @media only screen and (max-width: 448px) {
+    margin: 12px;
+    font-size: 14px;
+  }
+`;
+
+const Description = styled.p`
+  margin: 0;
+  font-size: 14px;
+  font-weight: 300;
+  text-align: center;
+
+  @media only screen and (max-width: 448px) {
+    font-size: 13px;
+  }
+`;
+
+const FeaturesItem = ({ children, icon, title }) => (
+  <Block>
+    <IconBox>
+      <Icon src={`/images/${icon}.svg`} />
+    </IconBox>
+    <Title>{title}</Title>
+    <Description>{children}</Description>
+  </Block>
+);
+
+FeaturesItem.propTypes = {
+  children: PropTypes.node.isRequired,
+  icon: PropTypes.string.isRequired,
+  title: PropTypes.string.isRequired,
+};
+
+export default FeaturesItem;

+ 1 - 0
client/components/Features/index.js

@@ -0,0 +1 @@
+export { default } from './Features';

+ 49 - 0
client/components/Footer/Footer.js

@@ -0,0 +1,49 @@
+import React from 'react';
+import styled from 'styled-components';
+
+const Wrapper = styled.footer`
+  width: 100%;
+  display: flex;
+  justify-content: center;
+  padding: 4px 0;
+  background-color: white;
+
+  a {
+    text-decoration: none;
+    color: #2196f3;
+  }
+`;
+
+const Text = styled.p`
+  font-size: 13px;
+  font-weight: 300;
+  color: #666;
+
+  @media only screen and (max-width: 768px) {
+    font-size: 11px;
+  }
+`;
+
+const Footer = () => (
+  <Wrapper>
+    <Text>
+      Made with love by{' '}
+      <a href="//thedevs.network/" title="The Devs">
+        The Devs
+      </a>.{' | '}
+      <a
+        href="https://github.com/thedevs-network/kutt"
+        title="GitHub"
+        target="_blank" // eslint-disable-line react/jsx-no-target-blank
+      >
+        GitHub
+      </a>
+      {' | '}
+      <a href="/terms" title="Terms of Service" target="_blank">
+        Terms of Service
+      </a>.
+    </Text>
+  </Wrapper>
+);
+
+export default Footer;

+ 1 - 0
client/components/Footer/index.js

@@ -0,0 +1 @@
+export { default } from './Footer';

+ 54 - 0
client/components/Header/Header.js

@@ -0,0 +1,54 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { bindActionCreators } from 'redux';
+import { connect } from 'react-redux';
+import styled from 'styled-components';
+import HeaderLogo from './HeaderLogo';
+import HeaderLeftMenu from './HeaderLeftMenu';
+import HeaderRightMenu from './HeaderRightMenu';
+import { showPageLoading } from '../../actions';
+
+const Wrapper = styled.header`
+  display: flex;
+  width: 1232px;
+  max-width: 100%;
+  padding: 0 32px;
+  height: 102px;
+  justify-content: space-between;
+  align-items: center;
+
+  @media only screen and (max-width: 768px) {
+    height: auto;
+    align-items: flex-start;
+    padding: 16px;
+    margin-bottom: 32px;
+  }
+`;
+
+const LeftMenuWrapper = styled.div`
+  display: flex;
+
+  @media only screen and (max-width: 488px) {
+    flex-direction: column;
+  }
+`;
+
+const Header = props => (
+  <Wrapper>
+    <LeftMenuWrapper>
+      <HeaderLogo showPageLoading={props.showPageLoading} />
+      <HeaderLeftMenu />
+    </LeftMenuWrapper>
+    <HeaderRightMenu showPageLoading={props.showPageLoading} />
+  </Wrapper>
+);
+
+Header.propTypes = {
+  showPageLoading: PropTypes.func.isRequired,
+};
+
+const mapDispatchToProps = dispatch => ({
+  showPageLoading: bindActionCreators(showPageLoading, dispatch),
+});
+
+export default connect(null, mapDispatchToProps)(Header);

+ 32 - 0
client/components/Header/HeaderLeftMenu.js

@@ -0,0 +1,32 @@
+import React from 'react';
+import styled from 'styled-components';
+import HeaderMenuItem from './HeaderMenuItem';
+
+const List = styled.ul`
+  display: flex;
+  align-items: flex-end;
+  list-style: none;
+  margin: 0 0 3px;
+  padding: 0;
+
+  @media only screen and (max-width: 488px) {
+    display: none;
+  }
+`;
+
+const HeaderLeftMenu = () => (
+  <List>
+    <HeaderMenuItem>
+      <a
+        href="//github.com/thedevs-network/kutt"
+        target="_blank"
+        rel="noopener noreferrer"
+        title="GitHub"
+      >
+        GitHub
+      </a>
+    </HeaderMenuItem>
+  </List>
+);
+
+export default HeaderLeftMenu;

+ 54 - 0
client/components/Header/HeaderLogo.js

@@ -0,0 +1,54 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Router from 'next/router';
+import styled from 'styled-components';
+
+const LogoImage = styled.div`
+  & > a {
+    position: relative;
+    display: flex;
+    align-items: center;
+    margin: 0 8px 0 0;
+    font-size: 22px;
+    font-weight: bold;
+    text-decoration: none;
+    color: inherit;
+    transition: border-color 0.2s ease-out;
+  }
+
+  @media only screen and (max-width: 488px) {
+    a {
+      font-size: 18px;
+    }
+  }
+
+  img {
+    width: 18px;
+    margin-right: 11px;
+  }
+`;
+
+const HeaderLogo = props => {
+  const goTo = e => {
+    e.preventDefault();
+    const path = e.target.getAttribute('href');
+    if (window.location.pathname === path) return;
+    props.showPageLoading();
+    Router.push(path);
+  };
+
+  return (
+    <LogoImage>
+      <a href="/" title="Homepage" onClick={goTo}>
+        <img src="/images/logo.svg" alt="Kutt.it" />
+        Kutt.it
+      </a>
+    </LogoImage>
+  );
+};
+
+HeaderLogo.propTypes = {
+  showPageLoading: PropTypes.func.isRequired,
+};
+
+export default HeaderLogo;

+ 38 - 0
client/components/Header/HeaderMenuItem.js

@@ -0,0 +1,38 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import styled from 'styled-components';
+import { fadeIn } from '../../helpers/animations';
+
+const ListItem = styled.li`
+  margin-left: 32px;
+  animation: ${fadeIn} 0.8s ease;
+
+  @media only screen and (max-width: 488px) {
+    margin-left: 16px;
+    font-size: 13px;
+  }
+`;
+
+const ListLink = styled.div`
+  & > a {
+    padding-bottom: 1px;
+    color: inherit;
+    text-decoration: none;
+  }
+  & > a:hover {
+    color: #2196f3;
+    border-bottom: 1px dotted #2196f3;
+  }
+`;
+
+const HeaderMenuItem = ({ children }) => (
+  <ListItem>
+    <ListLink>{children}</ListLink>
+  </ListItem>
+);
+
+HeaderMenuItem.propTypes = {
+  children: PropTypes.node.isRequired,
+};
+
+export default HeaderMenuItem;

+ 74 - 0
client/components/Header/HeaderRightMenu.js

@@ -0,0 +1,74 @@
+import React from 'react';
+import { bindActionCreators } from 'redux';
+import { connect } from 'react-redux';
+import PropTypes from 'prop-types';
+import Router from 'next/router';
+import styled from 'styled-components';
+import HeaderMenuItem from './HeaderMenuItem';
+import { logoutUser, showPageLoading } from '../../actions';
+import Button from '../Button';
+
+const List = styled.ul`
+  display: flex;
+  float: right;
+  justify-content: flex-end;
+  align-items: center;
+  margin: 0;
+  padding: 0;
+  list-style: none;
+`;
+
+const HeaderMenu = props => {
+  const goTo = e => {
+    e.preventDefault();
+    const path = e.currentTarget.getAttribute('href');
+    if (!path || window.location.pathname === path) return;
+    props.showPageLoading();
+    Router.push(path);
+  };
+
+  const login = !props.auth.isAuthenticated && (
+    <HeaderMenuItem>
+      <a href="/login" title="login / signup" onClick={goTo}>
+        <Button>Login / sign up</Button>
+      </a>
+    </HeaderMenuItem>
+  );
+  const logout = props.auth.isAuthenticated && (
+    <HeaderMenuItem>
+      <a href="/logout" title="logout" onClick={goTo}>
+        Logout
+      </a>
+    </HeaderMenuItem>
+  );
+  const settings = props.auth.isAuthenticated && (
+    <HeaderMenuItem>
+      <a href="/settings" title="settings" onClick={goTo}>
+        <Button>Settings</Button>
+      </a>
+    </HeaderMenuItem>
+  );
+  return (
+    <List>
+      {logout}
+      {settings}
+      {login}
+    </List>
+  );
+};
+
+HeaderMenu.propTypes = {
+  auth: PropTypes.shape({
+    isAuthenticated: PropTypes.bool.isRequired,
+  }).isRequired,
+  showPageLoading: PropTypes.func.isRequired,
+};
+
+const mapStateToProps = ({ auth }) => ({ auth });
+
+const mapDispatchToProps = dispatch => ({
+  logoutUser: bindActionCreators(logoutUser, dispatch),
+  showPageLoading: bindActionCreators(showPageLoading, dispatch),
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(HeaderMenu);

+ 1 - 0
client/components/Header/index.js

@@ -0,0 +1 @@
+export { default } from './Header';

+ 195 - 0
client/components/Login/Login.js

@@ -0,0 +1,195 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import Router from 'next/router';
+import { bindActionCreators } from 'redux';
+import { connect } from 'react-redux';
+import styled from 'styled-components';
+import emailValidator from 'email-validator';
+import LoginBox from './LoginBox';
+import LoginInputLabel from './LoginInputLabel';
+import TextInput from '../TextInput';
+import Button from '../Button';
+import Error from '../Error';
+import { loginUser, showAuthError, signupUser, showPageLoading } from '../../actions';
+import showRecaptcha from '../../helpers/recaptcha';
+import config from '../../config';
+
+const Wrapper = styled.div`
+  flex: 0 0 auto;
+  display: flex;
+  align-items: center;
+  margin: 24px 0 64px;
+`;
+
+const ButtonWrapper = styled.div`
+  display: flex;
+  justify-content: space-between;
+  & > * {
+    flex: 1 1 0;
+  }
+  & > *:last-child {
+    margin-left: 32px;
+  }
+  @media only screen and (max-width: 768px) {
+    & > *:last-child {
+      margin-left: 16px;
+    }
+  }
+`;
+
+const VerificationMsg = styled.p`
+  font-size: 24px;
+  font-weight: 300;
+`;
+
+const User = styled.span`
+  font-weight: normal;
+  color: #512da8;
+  border-bottom: 1px dotted #999;
+`;
+
+const ForgetPassLink = styled.a`
+  align-self: flex-start;
+  margin: -24px 0 32px;
+  font-size: 14px;
+  text-decoration: none;
+  color: #2196f3;
+  border-bottom: 1px dotted transparent;
+
+  :hover {
+    border-bottom-color: #2196f3;
+  }
+`;
+
+const Recaptcha = styled.div`
+  display: block;
+  margin: 0 0 32px 0;
+`;
+
+class Login extends Component {
+  constructor() {
+    super();
+    this.authHandler = this.authHandler.bind(this);
+    this.loginHandler = this.loginHandler.bind(this);
+    this.signupHandler = this.signupHandler.bind(this);
+    this.goTo = this.goTo.bind(this);
+  }
+
+  componentDidMount() {
+    showRecaptcha();
+  }
+
+  goTo(e) {
+    e.preventDefault();
+    const path = e.currentTarget.getAttribute('href');
+    this.props.showPageLoading();
+    Router.push(path);
+  }
+
+  authHandler(type) {
+    const { loading, showError } = this.props;
+    if (loading.login || loading.signup) return null;
+    const form = document.getElementById('login-form');
+    const { value: email } = form.elements.email;
+    const { value: password } = form.elements.password;
+    const { value: reCaptchaToken } = form.elements['g-recaptcha-input'];
+    if (!email) return showError('Email address must not be empty.');
+    if (!emailValidator.validate(email)) return showError('Email address is not valid.');
+    if (password.trim().length < 8) {
+      return showError('Password must be at least 8 chars long.');
+    }
+    if (!reCaptchaToken) {
+      window.grecaptcha.reset();
+      return showError('reCAPTCHA is not valid. Try again.');
+    }
+    window.grecaptcha.reset();
+    return type === 'login'
+      ? this.props.login({ email, password, reCaptchaToken })
+      : this.props.signup({ email, password, reCaptchaToken });
+  }
+
+  loginHandler(e) {
+    e.preventDefault();
+    this.authHandler('login');
+  }
+
+  signupHandler(e) {
+    e.preventDefault();
+    this.authHandler('signup');
+  }
+
+  render() {
+    return (
+      <Wrapper>
+        {this.props.auth.sentVerification ? (
+          <VerificationMsg>
+            A verification email has been sent to <User>{this.props.auth.user}</User>.
+          </VerificationMsg>
+        ) : (
+          <LoginBox id="login-form" onSubmit={this.loginHandler}>
+            <LoginInputLabel htmlFor="email" test="test">
+              Email address
+            </LoginInputLabel>
+            <TextInput type="email" name="email" id="email" autoFocus />
+            <LoginInputLabel htmlFor="password">Password (min chars: 8)</LoginInputLabel>
+            <TextInput type="password" name="password" id="password" />
+            <Recaptcha
+              id="g-recaptcha"
+              className="g-recaptcha"
+              data-sitekey={config.RECAPTCHA_SITE_KEY}
+              data-callback="recaptchaCallback"
+            />
+            <input type="hidden" id="g-recaptcha-input" name="g-recaptcha-input" />
+            <ForgetPassLink href="/reset-password" title="Forget password" onClick={this.goTo}>
+              Forgot your password?
+            </ForgetPassLink>
+            <ButtonWrapper>
+              <Button
+                icon={this.props.loading.login ? 'loader' : 'login'}
+                onClick={this.loginHandler}
+                big
+              >
+                Login
+              </Button>
+              <Button
+                icon={this.props.loading.signup ? 'loader' : 'signup'}
+                color="purple"
+                onClick={this.signupHandler}
+                big
+              >
+                Sign up
+              </Button>
+            </ButtonWrapper>
+            <Error type="auth" left={0} />
+          </LoginBox>
+        )}
+      </Wrapper>
+    );
+  }
+}
+
+Login.propTypes = {
+  auth: PropTypes.shape({
+    sentVerification: PropTypes.bool.isRequired,
+    user: PropTypes.string.isRequired,
+  }).isRequired,
+  loading: PropTypes.shape({
+    login: PropTypes.bool.isRequired,
+    signup: PropTypes.bool.isRequired,
+  }).isRequired,
+  login: PropTypes.func.isRequired,
+  signup: PropTypes.func.isRequired,
+  showError: PropTypes.func.isRequired,
+  showPageLoading: PropTypes.func.isRequired,
+};
+
+const mapStateToProps = ({ auth, loading }) => ({ auth, loading });
+
+const mapDispatchToProps = dispatch => ({
+  login: bindActionCreators(loginUser, dispatch),
+  signup: bindActionCreators(signupUser, dispatch),
+  showError: bindActionCreators(showAuthError, dispatch),
+  showPageLoading: bindActionCreators(showPageLoading, dispatch),
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(Login);

+ 31 - 0
client/components/Login/LoginBox.js

@@ -0,0 +1,31 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import styled from 'styled-components';
+import { fadeIn } from '../../helpers/animations';
+
+const Box = styled.form`
+  position: relative;
+  flex-basis: 400px;
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+  align-items: stretch;
+  animation: ${fadeIn} 0.8s ease-out;
+
+  input {
+    margin-bottom: 48px;
+  }
+  @media only screen and (max-width: 768px) {
+    input {
+      margin-bottom: 32px;
+    }
+  }
+`;
+
+const LoginBox = ({ children, ...props }) => <Box {...props}>{children}</Box>;
+
+LoginBox.propTypes = {
+  children: PropTypes.node.isRequired,
+};
+
+export default LoginBox;

+ 21 - 0
client/components/Login/LoginInputLabel.js

@@ -0,0 +1,21 @@
+/* eslint-disable jsx-a11y/label-has-for */
+import React from 'react';
+import PropTypes from 'prop-types';
+import styled from 'styled-components';
+
+const Label = styled.div`
+  margin-bottom: 8px;
+`;
+
+const LoginInputLabel = ({ children, htmlFor }) => (
+  <Label>
+    <label htmlFor={htmlFor}>{children}</label>
+  </Label>
+);
+
+LoginInputLabel.propTypes = {
+  children: PropTypes.node.isRequired,
+  htmlFor: PropTypes.string.isRequired,
+};
+
+export default LoginInputLabel;

+ 1 - 0
client/components/Login/index.js

@@ -0,0 +1 @@
+export { default } from './Login';

+ 66 - 0
client/components/Modal/Modal.js

@@ -0,0 +1,66 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import styled from 'styled-components';
+import Button from '../Button';
+
+const Wrapper = styled.div`
+  position: fixed;
+  width: 100%;
+  height: 100%;
+  top: 0;
+  left: 0;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  background-color: rgba(50, 50, 50, 0.8);
+  z-index: 1000;
+`;
+
+const Content = styled.div`
+  padding: 48px 64px;
+  text-align: center;
+  border-radius: 8px;
+  background-color: white;
+
+  @media only screen and (max-width: 768px) {
+    width: 90%;
+    padding: 32px;
+  }
+`;
+
+const ButtonsWrapper = styled.div`
+  display: flex;
+  justify-content: center;
+  margin-top: 40px;
+  button {
+    margin: 0 16px;
+  }
+`;
+
+const Modal = ({ children, handler, show, close }) =>
+  show ? (
+    <Wrapper>
+      <Content>
+        {children}
+        <ButtonsWrapper>
+          <Button color="gray" onClick={close}>
+            No
+          </Button>
+          <Button onClick={handler}>Yes</Button>
+        </ButtonsWrapper>
+      </Content>
+    </Wrapper>
+  ) : null;
+
+Modal.propTypes = {
+  children: PropTypes.node.isRequired,
+  close: PropTypes.func.isRequired,
+  handler: PropTypes.func.isRequired,
+  show: PropTypes.bool,
+};
+
+Modal.defaultProps = {
+  show: false,
+};
+
+export default Modal;

+ 1 - 0
client/components/Modal/index.js

@@ -0,0 +1 @@
+export { default } from './Modal';

+ 89 - 0
client/components/NeedToLogin/NeedToLogin.js

@@ -0,0 +1,89 @@
+import React from 'react';
+import Link from 'next/link';
+import styled from 'styled-components';
+import Button from '../Button';
+import { fadeIn } from '../../helpers/animations';
+
+const Wrapper = styled.div`
+  position: relative;
+  width: 1200px;
+  max-width: 98%;
+  display: flex;
+  align-items: center;
+  margin: 16px 0 0;
+  animation: ${fadeIn} 0.8s ease-out;
+  box-sizing: border-box;
+
+  a {
+    text-decoration: none;
+  }
+
+  @media only screen and (max-width: 768px) {
+    flex-direction: column;
+    align-items: center;
+  }
+`;
+
+const TitleWrapper = styled.div`
+  display: flex;
+  flex-direction: column;
+  align-items: flex-start;
+  margin-top: -32px;
+
+  @media only screen and (max-width: 768px) {
+    flex-direction: column;
+    align-items: center;
+    margin-bottom: 32px;
+  }
+`;
+
+const Title = styled.h2`
+  font-size: 28px;
+  font-weight: 300;
+  padding-right: 32px;
+  margin-bottom: 48px;
+
+  @media only screen and (max-width: 768px) {
+    font-size: 22px;
+    text-align: center;
+    padding-right: 0;
+    margin-bottom: 32px;
+    padding: 0 40px;
+  }
+
+  @media only screen and (max-width: 448px) {
+    font-size: 18px;
+    text-align: center;
+    margin-bottom: 24px;
+  }
+`;
+
+const Image = styled.img`
+  flex: 0 0 60%;
+  width: 60%;
+  max-width: 100%;
+  height: auto;
+
+  @media only screen and (max-width: 768px) {
+    flex-basis: 100%;
+    width: 100%;
+  }
+`;
+
+const NeedToLogin = () => (
+  <Wrapper>
+    <TitleWrapper>
+      <Title>
+        Manage links, set custom <b>domains</b> and view <b>stats</b>.
+      </Title>
+      <Link href="/login" prefetch>
+        <a href="/login" title="login / signup">
+          <Button>Login / sign up</Button>
+        </a>
+      </Link>
+    </TitleWrapper>
+    <Image src="/images/callout.png" />
+  </Wrapper>
+);
+
+export default NeedToLogin;

+ 1 - 0
client/components/NeedToLogin/index.js

@@ -0,0 +1 @@
+export { default } from './NeedToLogin';

+ 28 - 0
client/components/PageLoading/PageLoading.js

@@ -0,0 +1,28 @@
+import React from 'react';
+import styled from 'styled-components';
+import { spin } from '../../helpers/animations';
+
+const Loading = styled.div`
+  margin: 0 0 48px;
+  flex: 1 1 auto;
+  flex-basis: 250px;
+  display: flex;
+  align-self: center;
+  align-items: center;
+  justify-content: center;
+`;
+
+const Icon = styled.img`
+  display: block;
+  width: 28px;
+  height: 28px;
+  animation: ${spin} 1s linear infinite;
+`;
+
+const pageLoading = () => (
+  <Loading>
+    <Icon src="/images/loader.svg" />
+  </Loading>
+);
+
+export default pageLoading;

+ 1 - 0
client/components/PageLoading/index.js

@@ -0,0 +1 @@
+export { default } from './PageLoading';

+ 224 - 0
client/components/Settings/Settings.js

@@ -0,0 +1,224 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import Router from 'next/router';
+import { bindActionCreators } from 'redux';
+import { connect } from 'react-redux';
+import styled from 'styled-components';
+import cookie from 'js-cookie';
+import axios from 'axios';
+import SettingsWelcome from './SettingsWelcome';
+import SettingsDomain from './SettingsDomain';
+import SettingsPassword from './SettingsPassword';
+import SettingsApi from './SettingsApi';
+import Modal from '../Modal';
+import { fadeIn } from '../../helpers/animations';
+import {
+  deleteCustomDomain,
+  generateApiKey,
+  getUserSettings,
+  setCustomDomain,
+  showDomainInput,
+} from '../../actions';
+
+const Wrapper = styled.div`
+  poistion: relative;
+  width: 600px;
+  max-width: 97%;
+  display: flex;
+  flex-direction: column;
+  align-items: flex-start;
+  padding: 0 0 80px;
+  animation: ${fadeIn} 0.8s ease;
+
+  > * {
+    max-width: 100%;
+  }
+
+  hr {
+    width: 100%;
+    height: 1px;
+    outline: none;
+    border: none;
+    background-color: #e3e3e3;
+    margin: 24px 0;
+
+    @media only screen and (max-width: 768px) {
+      margin: 12px 0;
+    }
+  }
+  h3 {
+    font-size: 24px;
+    margin: 32px 0 16px;
+
+    @media only screen and (max-width: 768px) {
+      font-size: 18px;
+    }
+  }
+  p {
+    margin: 24px 0;
+  }
+  a {
+    margin: 32px 0 0;
+    color: #2196f3;
+    text-decoration: none;
+
+    :hover {
+      color: #2196f3;
+      border-bottom: 1px dotted #2196f3;
+    }
+  }
+`;
+
+class Settings extends Component {
+  constructor() {
+    super();
+    this.state = {
+      showModal: false,
+      passwordMessage: '',
+      passwordError: '',
+    };
+    this.handleCustomDomain = this.handleCustomDomain.bind(this);
+    this.deleteDomain = this.deleteDomain.bind(this);
+    this.showModal = this.showModal.bind(this);
+    this.closeModal = this.closeModal.bind(this);
+    this.changePassword = this.changePassword.bind(this);
+  }
+
+  componentDidMount() {
+    if (!this.props.auth.isAuthenticated) Router.push('/login');
+    this.props.getUserSettings();
+  }
+
+  handleCustomDomain(e) {
+    e.preventDefault();
+    if (this.props.domainLoading) return null;
+    const customDomain = e.currentTarget.elements.customdomain.value;
+    return this.props.setCustomDomain({ customDomain });
+  }
+
+  deleteDomain() {
+    this.closeModal();
+    this.props.deleteCustomDomain();
+  }
+
+  showModal() {
+    this.setState({ showModal: true });
+  }
+
+  closeModal() {
+    this.setState({ showModal: false });
+  }
+
+  changePassword(e) {
+    e.preventDefault();
+    const form = e.target;
+    const password = form.elements.password.value;
+    if (password.length < 8) {
+      return this.setState({ passwordError: 'Password must be at least 8 chars long.' }, () => {
+        setTimeout(() => {
+          this.setState({
+            passwordError: '',
+          });
+        }, 1500);
+      });
+    }
+    return axios
+      .post(
+        '/api/auth/changepassword',
+        { password },
+        { headers: { Authorization: cookie.get('token') } }
+      )
+      .then(res =>
+        this.setState({ passwordMessage: res.data.message }, () => {
+          setTimeout(() => {
+            this.setState({ passwordMessage: '' });
+          }, 1500);
+          form.reset();
+        })
+      )
+      .catch(err =>
+        this.setState({ passwordError: err.response.data.error }, () => {
+          setTimeout(() => {
+            this.setState({
+              passwordError: '',
+            });
+          }, 1500);
+        })
+      );
+  }
+
+  render() {
+    return (
+      <Wrapper>
+        <SettingsWelcome user={this.props.auth.user} />
+        <SettingsDomain
+          handleCustomDomain={this.handleCustomDomain}
+          loading={this.props.domainLoading}
+          settings={this.props.settings}
+          showDomainInput={this.props.showDomainInput}
+          showModal={this.showModal}
+        />
+        <hr />
+        <SettingsPassword
+          message={this.state.passwordMessage}
+          error={this.state.passwordError}
+          changePassword={this.changePassword}
+        />
+        <hr />
+        <SettingsApi
+          loader={this.props.apiLoading}
+          generateKey={this.props.generateApiKey}
+          apikey={this.props.settings.apikey}
+        />
+        <Modal show={this.state.showModal} close={this.closeModal} handler={this.deleteDomain}>
+          Are you sure do you want to delete the domain?
+        </Modal>
+      </Wrapper>
+    );
+  }
+}
+
+Settings.propTypes = {
+  auth: PropTypes.shape({
+    isAuthenticated: PropTypes.bool.isRequired,
+    user: PropTypes.string.isRequired,
+  }).isRequired,
+  apiLoading: PropTypes.bool,
+  deleteCustomDomain: PropTypes.func.isRequired,
+  domainLoading: PropTypes.bool,
+  setCustomDomain: PropTypes.func.isRequired,
+  generateApiKey: PropTypes.func.isRequired,
+  getUserSettings: PropTypes.func.isRequired,
+  settings: PropTypes.shape({
+    apikey: PropTypes.string.isRequired,
+    customDomain: PropTypes.string.isRequired,
+    domainInput: PropTypes.bool.isRequired,
+  }).isRequired,
+  showDomainInput: PropTypes.func.isRequired,
+};
+
+Settings.defaultProps = {
+  apiLoading: false,
+  domainLoading: false,
+};
+
+const mapStateToProps = ({
+  auth,
+  loading: { api: apiLoading, domain: domainLoading },
+  settings,
+}) => ({
+  auth,
+  apiLoading,
+  domainLoading,
+  settings,
+});
+
+const mapDispatchToProps = dispatch => ({
+  deleteCustomDomain: bindActionCreators(deleteCustomDomain, dispatch),
+  setCustomDomain: bindActionCreators(setCustomDomain, dispatch),
+  generateApiKey: bindActionCreators(generateApiKey, dispatch),
+  getUserSettings: bindActionCreators(getUserSettings, dispatch),
+  showDomainInput: bindActionCreators(showDomainInput, dispatch),
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(Settings);

+ 82 - 0
client/components/Settings/SettingsApi.js

@@ -0,0 +1,82 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import styled, { css } from 'styled-components';
+import Button from '../Button';
+
+const Wrapper = styled.div`
+  display: flex;
+  flex-direction: column;
+  align-items: flex-start;
+`;
+
+const ApiKeyWrapper = styled.div`
+  display: flex;
+  align-items: center;
+  margin: 16px 0;
+
+  button {
+    margin-right: 16px;
+  }
+
+  ${({ apikey }) =>
+    apikey &&
+    css`
+      flex-direction: column;
+      align-items: flex-start;
+      > span {
+        margin-bottom: 32px;
+      }
+    `};
+
+  @media only screen and (max-width: 768px) {
+    width: 100%;
+    overflow-wrap: break-word;
+  }
+`;
+
+const ApiKey = styled.span`
+  max-width: 100%;
+  font-size: 18px;
+  font-weight: bold;
+  border-bottom: 2px dotted #999;
+
+  @media only screen and (max-width: 768px) {
+    font-size: 14px;
+  }
+`;
+
+const Link = styled.a`
+  margin: 16px 0;
+
+  @media only screen and (max-width: 768px) {
+    margin: 8px 0;
+  }
+`;
+
+const SettingsApi = ({ apikey, generateKey, loader }) => (
+  <Wrapper>
+    <h3>API</h3>
+    <p>
+      In additional to this website, you can use the API to create, delete and get shortend URLs. If
+      {"you're"} not familiar with API, {"don't"} generate the key. DO NOT share this key on the
+      client side of your website.
+    </p>
+    <ApiKeyWrapper apikey={apikey}>
+      {apikey && <ApiKey>{apikey}</ApiKey>}
+      <Button color="purple" icon={loader ? 'loader' : 'zap'} onClick={generateKey}>
+        {apikey ? 'Regenerate' : 'Generate'} key
+      </Button>
+    </ApiKeyWrapper>
+    <Link href="http://github.com/thedevs-network/kutt#api" title="API Docs" target="_blank">
+      Read API docs
+    </Link>
+  </Wrapper>
+);
+
+SettingsApi.propTypes = {
+  apikey: PropTypes.string.isRequired,
+  loader: PropTypes.bool.isRequired,
+  generateKey: PropTypes.func.isRequired,
+};
+
+export default SettingsApi;

+ 103 - 0
client/components/Settings/SettingsDomain.js

@@ -0,0 +1,103 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import styled from 'styled-components';
+import TextInput from '../TextInput';
+import Button from '../Button';
+import Error from '../Error';
+import { fadeIn } from '../../helpers/animations';
+
+const Form = styled.form`
+  position: relative;
+  display: flex;
+  margin: 32px 0;
+  animation: ${fadeIn} 0.8s ease;
+
+  input {
+    flex: 0 0 auto;
+    margin-right: 16px;
+  }
+
+  @media only screen and (max-width: 768px) {
+    margin: 16px 0;
+  }
+`;
+
+const DomainWrapper = styled.div`
+  display: flex;
+  align-items: center;
+  margin: 32px 0;
+  animation: ${fadeIn} 0.8s ease;
+
+  button {
+    margin-right: 16px;
+  }
+
+  @media only screen and (max-width: 768px) {
+    flex-direction: column;
+    align-items: flex-start;
+
+    > * {
+      margin: 8px 0;
+    }
+  }
+`;
+
+const Domain = styled.span`
+  margin-right: 16px;
+  font-size: 20px;
+  font-weight: bold;
+  border-bottom: 2px dotted #999;
+`;
+
+const SettingsDomain = ({ settings, handleCustomDomain, loading, showDomainInput, showModal }) => (
+  <div>
+    <h3>Custom domain</h3>
+    <p>
+      You can set a custom domain for your short URLs, so instead of <b>kutt.it/shorturl</b> you can
+      have <b>example.com/shorturl.</b>
+    </p>
+    <p>
+      Point your domain A record to <b>31.220.54.35</b> then add the domain via form below:
+    </p>
+    {settings.customDomain && !settings.domainInput ? (
+      <DomainWrapper>
+        <Domain>{settings.customDomain}</Domain>
+        <Button icon="edit" onClick={showDomainInput}>
+          Change
+        </Button>
+        <Button color="gray" icon="x" onClick={showModal}>
+          Delete
+        </Button>
+      </DomainWrapper>
+    ) : (
+      <Form onSubmit={handleCustomDomain}>
+        <Error type="domain" left={0} bottom={-48} />
+        <TextInput
+          id="customdomain"
+          name="customdomain"
+          type="text"
+          placeholder="example.com"
+          defaultValue={settings.customDomain}
+          height={44}
+          small
+        />
+        <Button type="submit" color="purple" icon={loading ? 'loader' : ''}>
+          Set domain
+        </Button>
+      </Form>
+    )}
+  </div>
+);
+
+SettingsDomain.propTypes = {
+  settings: PropTypes.shape({
+    customDomain: PropTypes.string.isRequired,
+    domainInput: PropTypes.bool.isRequired,
+  }).isRequired,
+  handleCustomDomain: PropTypes.func.isRequired,
+  loading: PropTypes.bool.isRequired,
+  showDomainInput: PropTypes.func.isRequired,
+  showModal: PropTypes.func.isRequired,
+};
+
+export default SettingsDomain;

+ 59 - 0
client/components/Settings/SettingsPassword.js

@@ -0,0 +1,59 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import styled from 'styled-components';
+import TextInput from '../TextInput';
+import Button from '../Button';
+
+const Form = styled.form`
+  position: relative;
+  display: flex;
+  margin: 32px 0;
+
+  input {
+    flex: 0 0 auto;
+    margin-right: 16px;
+  }
+`;
+
+const Message = styled.div`
+  position: absolute;
+  left: 0;
+  bottom: -32px;
+  color: green;
+`;
+
+const Error = styled.div`
+  position: absolute;
+  left: 0;
+  bottom: -32px;
+  color: red;
+`;
+
+const SettingsPassword = ({ changePassword, error, message }) => (
+  <div>
+    <h3>Change password</h3>
+    <Form onSubmit={changePassword}>
+      <Message>{message}</Message>
+      <TextInput
+        id="password"
+        name="password"
+        type="password"
+        placeholder="New password"
+        height={44}
+        small
+      />
+      <Button type="submit" icon="refresh">
+        Update
+      </Button>
+      <Error>{error}</Error>
+    </Form>
+  </div>
+);
+
+SettingsPassword.propTypes = {
+  error: PropTypes.string.isRequired,
+  changePassword: PropTypes.func.isRequired,
+  message: PropTypes.string.isRequired,
+};
+
+export default SettingsPassword;

+ 29 - 0
client/components/Settings/SettingsWelcome.js

@@ -0,0 +1,29 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import styled from 'styled-components';
+
+const Title = styled.h2`
+  font-size: 28px;
+  font-weight: 300;
+
+  span {
+    padding-bottom: 2px;
+    border-bottom: 2px dotted #999;
+  }
+
+  @media only screen and (max-width: 768px) {
+    font-size: 22px;
+  }
+`;
+
+const SettingsWelcome = ({ user }) => (
+  <Title>
+    Welcome, <span>{user}</span>.
+  </Title>
+);
+
+SettingsWelcome.propTypes = {
+  user: PropTypes.string.isRequired,
+};
+
+export default SettingsWelcome;

+ 1 - 0
client/components/Settings/index.js

@@ -0,0 +1 @@
+export { default } from './Settings';

+ 155 - 0
client/components/Shortener/Shortener.js

@@ -0,0 +1,155 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { bindActionCreators } from 'redux';
+import { connect } from 'react-redux';
+import styled from 'styled-components';
+import ShortenerResult from './ShortenerResult';
+import ShortenerTitle from './ShortenerTitle';
+import ShortenerInput from './ShortenerInput';
+import ShortenerCaptcha from './ShortenerCaptcha';
+import { createShortUrl, setShortenerFormError } from '../../actions';
+import showRecaptcha from '../../helpers/recaptcha';
+import { fadeIn } from '../../helpers/animations';
+
+const Wrapper = styled.div`
+  position: relative;
+  width: 800px;
+  max-width: 100%;
+  flex: 0 0 auto;
+  display: flex;
+  flex-direction: column;
+  margin: 16px 0 40px;
+  padding-bottom: 125px;
+  animation: ${fadeIn} 0.8s ease-out;
+
+  @media only screen and (max-width: 800px) {
+    padding: 0 8px 96px;
+  }
+`;
+
+const ResultWrapper = styled.div`
+  position: relative;
+  height: 96px;
+  display: flex;
+  justify-content: center;
+  align-items: flex-start;
+  box-sizing: border-box;
+
+  @media only screen and (max-width: 448px) {
+    height: 72px;
+  }
+`;
+
+class Shortener extends Component {
+  constructor() {
+    super();
+    this.state = {
+      isCopied: false,
+    };
+    this.handleSubmit = this.handleSubmit.bind(this);
+    this.copyHandler = this.copyHandler.bind(this);
+  }
+
+  componentDidMount() {
+    showRecaptcha();
+  }
+
+  shouldComponentUpdate(nextProps, nextState) {
+    const { isAuthenticated, shortenerError, shortenerLoading, url: { isShortened } } = this.props;
+    return (
+      isAuthenticated !== nextProps.isAuthenticated ||
+      shortenerError !== nextProps.shortenerError ||
+      isShortened !== nextProps.url.isShortened ||
+      shortenerLoading !== nextProps.shortenerLoading ||
+      this.state.isCopied !== nextState.isCopied
+    );
+  }
+
+  handleSubmit(e) {
+    const { isAuthenticated } = this.props;
+    e.preventDefault();
+    const shortenerForm = document.getElementById('shortenerform');
+    const {
+      target: originalUrl,
+      customurl: customurlInput,
+      password: pwd,
+      'g-recaptcha-input': recaptcha,
+    } = shortenerForm.elements;
+    const target = originalUrl.value.trim();
+    const customurl = customurlInput && customurlInput.value.trim();
+    const password = pwd && pwd.value;
+    const reCaptchaToken = !isAuthenticated && recaptcha && recaptcha.value;
+    if (!isAuthenticated && !reCaptchaToken) {
+      window.grecaptcha.reset();
+      return this.props.setShortenerFormError('reCAPTCHA is not valid. Try again.');
+    }
+    const options = isAuthenticated && { customurl, password };
+    shortenerForm.reset();
+    if (!isAuthenticated && recaptcha) window.grecaptcha.reset();
+    return this.props.createShortUrl({ target, reCaptchaToken, ...options });
+  }
+
+  copyHandler() {
+    this.setState({ isCopied: true });
+    setTimeout(() => {
+      this.setState({ isCopied: false });
+    }, 1500);
+  }
+
+  render() {
+    const { isCopied } = this.state;
+    const { isAuthenticated, shortenerError, shortenerLoading, url } = this.props;
+    return (
+      <Wrapper>
+        <ResultWrapper>
+          {!shortenerError && (url.isShortened || shortenerLoading) ? (
+            <ShortenerResult
+              copyHandler={this.copyHandler}
+              loading={shortenerLoading}
+              url={url}
+              isCopied={isCopied}
+            />
+          ) : (
+            <ShortenerTitle />
+          )}
+        </ResultWrapper>
+        <ShortenerInput
+          isAuthenticated={isAuthenticated}
+          handleSubmit={this.handleSubmit}
+          setShortenerFormError={this.props.setShortenerFormError}
+        />
+        {!isAuthenticated && <ShortenerCaptcha />}
+      </Wrapper>
+    );
+  }
+}
+
+Shortener.propTypes = {
+  isAuthenticated: PropTypes.bool.isRequired,
+  createShortUrl: PropTypes.func.isRequired,
+  shortenerError: PropTypes.string.isRequired,
+  shortenerLoading: PropTypes.bool.isRequired,
+  setShortenerFormError: PropTypes.func.isRequired,
+  url: PropTypes.shape({
+    isShortened: PropTypes.bool.isRequired,
+  }).isRequired,
+};
+
+const mapStateToProps = ({
+  auth: { isAuthenticated },
+  error: { shortener: shortenerError },
+  loading: { shortener: shortenerLoading },
+  url,
+}) => ({
+  isAuthenticated,
+  shortenerError,
+  shortenerLoading,
+  url,
+});
+
+const mapDispatchToProps = dispatch => ({
+  createShortUrl: bindActionCreators(createShortUrl, dispatch),
+  setShortenerFormError: bindActionCreators(setShortenerFormError, dispatch),
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(Shortener);

+ 19 - 0
client/components/Shortener/ShortenerCaptcha.js

@@ -0,0 +1,19 @@
+import React from 'react';
+import styled from 'styled-components';
+import config from '../../config';
+
+const Recaptcha = styled.div`
+  display: block;
+  margin: 32px 0;
+`;
+
+const ShortenerCaptcha = () => (
+  <Recaptcha
+    id="g-recaptcha"
+    className="g-recaptcha"
+    data-sitekey={config.RECAPTCHA_SITE_KEY}
+    data-callback="recaptchaCallback"
+  />
+);
+
+export default ShortenerCaptcha;

+ 76 - 0
client/components/Shortener/ShortenerInput.js

@@ -0,0 +1,76 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import styled from 'styled-components';
+import SVG from 'react-inlinesvg';
+import ShortenerOptions from './ShortenerOptions';
+import TextInput from '../TextInput';
+import Error from '../Error';
+
+const ShortenerForm = styled.form`
+  position: relative;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  width: 800px;
+  max-width: 100%;
+`;
+
+const Submit = styled.div`
+  content: '';
+  position: absolute;
+  top: 0;
+  right: 12px;
+  width: 64px;
+  height: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  cursor: pointer;
+
+  :hover svg {
+    fill: #673ab7;
+  }
+  @media only screen and (max-width: 448px) {
+    right: 8px;
+    width: 40px;
+  }
+`;
+
+const Icon = styled(SVG)`
+  svg {
+    width: 28px;
+    height: 28px;
+    margin-right: 8px;
+    margin-top: 2px;
+    fill: #aaa;
+    transition: all 0.2s ease-out;
+
+    @media only screen and (max-width: 448px) {
+      height: 22px;
+      width: 22px;
+    }
+  }
+`;
+
+const ShortenerInput = ({ isAuthenticated, handleSubmit, setShortenerFormError }) => (
+  <ShortenerForm id="shortenerform" onSubmit={handleSubmit}>
+    <TextInput id="target" name="target" placeholder="Paste your long URL" autoFocus />
+    <Submit onClick={handleSubmit}>
+      <Icon src="/images/send.svg" />
+    </Submit>
+    <input type="hidden" id="g-recaptcha-input" name="g-recaptcha-input" />
+    <Error type="shortener" />
+    <ShortenerOptions
+      isAuthenticated={isAuthenticated}
+      setShortenerFormError={setShortenerFormError}
+    />
+  </ShortenerForm>
+);
+
+ShortenerInput.propTypes = {
+  handleSubmit: PropTypes.func.isRequired,
+  isAuthenticated: PropTypes.bool.isRequired,
+  setShortenerFormError: PropTypes.func.isRequired,
+};
+
+export default ShortenerInput;

+ 141 - 0
client/components/Shortener/ShortenerOptions.js

@@ -0,0 +1,141 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import styled, { css } from 'styled-components';
+import Checkbox from '../Checkbox';
+import TextInput from '../TextInput';
+import { fadeIn } from '../../helpers/animations';
+
+const Wrapper = styled.div`
+  position: absolute;
+  top: 74px;
+  left: 0;
+  display: flex;
+  flex-direction: column;
+  align-self: flex-start;
+  justify-content: flex-start;
+  z-index: 2;
+
+  ${({ isAuthenticated }) =>
+    !isAuthenticated &&
+    css`
+      top: 180px;
+    `};
+
+  @media only screen and (max-width: 448px) {
+    top: 56px;
+  }
+
+  ${({ isAuthenticated }) =>
+    !isAuthenticated &&
+    css`
+      @media only screen and (max-width: 448px) {
+        top: 156px;
+      }
+    `};
+`;
+
+const CheckboxWrapper = styled.div`
+  display: flex;
+`;
+
+const InputWrapper = styled.div`
+  display: flex;
+  align-items: center;
+
+  @media only screen and (max-width: 448px) {
+    flex-direction: column;
+    align-items: flex-start;
+
+    > * {
+      margin-bottom: 16px;
+    }
+  }
+`;
+
+const Label = styled.label`
+  font-size: 18px;
+  margin-right: 16px;
+  animation: ${fadeIn} 0.5s ease-out;
+
+  @media only screen and (max-width: 448px) {
+    font-size: 14px;
+    margin-right: 8px;
+  }
+`;
+
+class ShortenerOptions extends Component {
+  constructor() {
+    super();
+    this.state = {
+      customurlCheckbox: false,
+      passwordCheckbox: false,
+    };
+    this.handleCheckbox = this.handleCheckbox.bind(this);
+  }
+
+  shouldComponentUpdate(nextProps, nextState) {
+    const { customurlCheckbox, passwordCheckbox } = this.state;
+    return (
+      this.props.isAuthenticated !== nextProps.isAuthenticated ||
+      customurlCheckbox !== nextState.customurlCheckbox ||
+      passwordCheckbox !== nextState.passwordCheckbox
+    );
+  }
+
+  handleCheckbox(e) {
+    e.preventDefault();
+    if (!this.props.isAuthenticated) {
+      return this.props.setShortenerFormError('Please login or sign up to use this feature.');
+    }
+    const type = e.target.id;
+    return this.setState({ [type]: !this.state[type] });
+  }
+
+  render() {
+    const { customurlCheckbox, passwordCheckbox } = this.state;
+    const { isAuthenticated } = this.props;
+    const customUrlInput = customurlCheckbox && (
+      <div>
+        <Label htmlFor="customurl">{window.location.hostname}/</Label>
+        <TextInput id="customurl" type="text" placeholder="custom name" small />
+      </div>
+    );
+    const passwordInput = passwordCheckbox && (
+      <div>
+        <Label htmlFor="customurl">password:</Label>
+        <TextInput id="password" type="password" placeholder="password" small />
+      </div>
+    );
+    return (
+      <Wrapper isAuthenticated={isAuthenticated}>
+        <CheckboxWrapper>
+          <Checkbox
+            id="customurlCheckbox"
+            name="customurlCheckbox"
+            label="Set custom URL"
+            checked={this.state.customurlCheckbox}
+            onClick={this.handleCheckbox}
+          />
+          <Checkbox
+            id="passwordCheckbox"
+            name="passwordCheckbox"
+            label="Set password"
+            checked={this.state.passwordCheckbox}
+            onClick={this.handleCheckbox}
+          />
+        </CheckboxWrapper>
+        <InputWrapper>
+          {customUrlInput}
+          {passwordInput}
+        </InputWrapper>
+      </Wrapper>
+    );
+  }
+}
+
+ShortenerOptions.propTypes = {
+  isAuthenticated: PropTypes.bool.isRequired,
+  setShortenerFormError: PropTypes.func.isRequired,
+};
+
+export default ShortenerOptions;

+ 70 - 0
client/components/Shortener/ShortenerResult.js

@@ -0,0 +1,70 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import styled from 'styled-components';
+import { CopyToClipboard } from 'react-copy-to-clipboard';
+import Button from '../Button';
+import Loading from '../PageLoading';
+import { fadeIn } from '../../helpers/animations';
+
+const Wrapper = styled.div`
+  position: relative;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+
+  button {
+    margin-left: 24px;
+  }
+`;
+
+const Url = styled.h2`
+  margin: 8px 0;
+  font-size: 32px;
+  font-weight: 300;
+  border-bottom: 2px dotted #aaa;
+  cursor: pointer;
+  transition: all 0.2s ease;
+
+  :hover {
+    opacity: 0.5;
+  }
+
+  @media only screen and (max-width: 448px) {
+    font-size: 24px;
+  }
+`;
+
+const CopyMessage = styled.p`
+  position: absolute;
+  top: -32px;
+  left: 0;
+  font-size: 14px;
+  color: #689f38;
+  animation: ${fadeIn} 0.3s ease-out;
+`;
+
+const ShortenerResult = ({ copyHandler, isCopied, loading, url }) =>
+  loading ? (
+    <Loading />
+  ) : (
+    <Wrapper>
+      {isCopied && <CopyMessage>Copied to clipboard.</CopyMessage>}
+      <CopyToClipboard text={url.list[0].shortUrl} onCopy={copyHandler}>
+        <Url>{url.list[0].shortUrl.replace(/^https?:\/\//, '')}</Url>
+      </CopyToClipboard>
+      <CopyToClipboard text={url.list[0].shortUrl} onCopy={copyHandler}>
+        <Button icon="copy">Copy</Button>
+      </CopyToClipboard>
+    </Wrapper>
+  );
+
+ShortenerResult.propTypes = {
+  copyHandler: PropTypes.func.isRequired,
+  isCopied: PropTypes.bool.isRequired,
+  loading: PropTypes.bool.isRequired,
+  url: PropTypes.shape({
+    list: PropTypes.array.isRequired,
+  }).isRequired,
+};
+
+export default ShortenerResult;

+ 25 - 0
client/components/Shortener/ShortenerTitle.js

@@ -0,0 +1,25 @@
+import React from 'react';
+import styled from 'styled-components';
+
+const Title = styled.h1`
+  font-size: 32px;
+  font-weight: 300;
+  margin: 8px 0 0;
+  color: #333;
+
+  @media only screen and (max-width: 448px) {
+    font-size: 22px;
+  }
+`;
+
+const Underline = styled.span`
+  border-bottom: 2px dotted #999;
+`;
+
+const ShortenerTitle = () => (
+  <Title>
+    Kutt your links <Underline>shorter</Underline>.
+  </Title>
+);
+
+export default ShortenerTitle;

+ 1 - 0
client/components/Shortener/index.js

@@ -0,0 +1 @@
+export { default } from './Shortener';

+ 168 - 0
client/components/Stats/Stats.js

@@ -0,0 +1,168 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { bindActionCreators } from 'redux';
+import { connect } from 'react-redux';
+import Router from 'next/router';
+import styled from 'styled-components';
+import axios from 'axios';
+import cookie from 'js-cookie';
+import StatsError from './StatsError';
+import StatsHead from './StatsHead';
+import StatsCharts from './StatsCharts';
+import PageLoading from '../PageLoading';
+import Button from '../Button';
+import { showPageLoading } from '../../actions';
+
+const Wrapper = styled.div`
+  width: 1200px;
+  max-width: 95%;
+  display: flex;
+  flex-direction: column;
+  align-items: stretch;
+  margin: 40px 0;
+`;
+
+const TitleWrapper = styled.div`
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+`;
+
+const Title = styled.h2`
+  font-size: 24px;
+  font-weight: 300;
+
+  a {
+    color: #2196f3;
+    text-decoration: none;
+    border-bottom: 1px dotted transparent;
+
+    :hover {
+      border-bottom-color: #2196f3;
+    }
+  }
+
+  @media only screen and (max-width: 768px) {
+    font-size: 18px;
+  }
+`;
+
+const TitleTarget = styled.p`
+  font-size: 14px;
+  text-align: right;
+  color: #333;
+
+  @media only screen and (max-width: 768px) {
+    font-size: 11px;
+  }
+`;
+
+const Content = styled.div`
+  display: flex;
+  flex: 1 1 auto;
+  flex-direction: column;
+  background-color: white;
+  border-radius: 12px;
+  box-shadow: 0 6px 30px rgba(50, 50, 50, 0.2);
+`;
+
+const ButtonWrapper = styled.div`
+  align-self: center;
+  margin: 64px 0;
+`;
+
+class Stats extends Component {
+  constructor() {
+    super();
+    this.state = {
+      error: false,
+      loading: true,
+      period: 'lastDay',
+      stats: null,
+    };
+    this.changePeriod = this.changePeriod.bind(this);
+    this.goToHomepage = this.goToHomepage.bind(this);
+  }
+
+  componentDidMount() {
+    const { id } = this.props;
+    if (!id) return null;
+    return axios
+      .post('/api/url/stats', { id }, { headers: { Authorization: cookie.get('token') } })
+      .then(({ data }) =>
+        this.setState({
+          stats: data,
+          loading: false,
+          error: !data,
+        })
+      )
+      .catch(() => this.setState({ error: true, loading: false }));
+  }
+
+  changePeriod(e) {
+    e.preventDefault();
+    const { period } = e.currentTarget.dataset;
+    this.setState({ period });
+  }
+
+  goToHomepage(e) {
+    e.preventDefault();
+    this.props.showPageLoading();
+    Router.push('/');
+  }
+
+  render() {
+    const { error, loading, period, stats } = this.state;
+    const { isAuthenticated, id } = this.props;
+
+    if (!isAuthenticated) return <StatsError text="You need to login to view stats." />;
+
+    if (!id || error) return <StatsError />;
+
+    if (loading) return <PageLoading />;
+
+    return (
+      <Wrapper>
+        <TitleWrapper>
+          <Title>
+            Stats for:{' '}
+            <a href={stats.shortUrl} title="Short URL">
+              {stats.shortUrl.replace(/https?:\/\//, '')}
+            </a>
+          </Title>
+          <TitleTarget>
+            {stats.target.length > 80
+              ? `${stats.target
+                  .split('')
+                  .slice(0, 80)
+                  .join('')}...`
+              : stats.target}
+          </TitleTarget>
+        </TitleWrapper>
+        <Content>
+          <StatsHead total={stats.total} period={period} changePeriod={this.changePeriod} />
+          <StatsCharts stats={stats[period]} period={period} />
+        </Content>
+        <ButtonWrapper>
+          <Button icon="arrow-left" onClick={this.goToHomepage}>
+            Back to homepage
+          </Button>
+        </ButtonWrapper>
+      </Wrapper>
+    );
+  }
+}
+
+Stats.propTypes = {
+  isAuthenticated: PropTypes.bool.isRequired,
+  id: PropTypes.string.isRequired,
+  showPageLoading: PropTypes.func.isRequired,
+};
+
+const mapStateToProps = ({ auth: { isAuthenticated } }) => ({ isAuthenticated });
+
+const mapDispatchToProps = dispatch => ({
+  showPageLoading: bindActionCreators(showPageLoading, dispatch),
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(Stats);

+ 76 - 0
client/components/Stats/StatsCharts/Area.js

@@ -0,0 +1,76 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import subHours from 'date-fns/sub_hours';
+import subDays from 'date-fns/sub_days';
+import subMonths from 'date-fns/sub_months';
+import formatDate from 'date-fns/format';
+import {
+  AreaChart,
+  Area,
+  XAxis,
+  YAxis,
+  CartesianGrid,
+  ResponsiveContainer,
+  Tooltip,
+} from 'recharts';
+import withTitle from './withTitle';
+
+const ChartArea = ({ data: rawData, period }) => {
+  const now = new Date();
+  const getDate = index => {
+    switch (period) {
+      case 'allTime':
+        return formatDate(subMonths(now, rawData.length - index), 'MMM YY');
+      case 'lastDay':
+        return formatDate(subHours(now, rawData.length - index), 'HH:00');
+      case 'lastMonth':
+      case 'lastWeek':
+      default:
+        return formatDate(subDays(now, rawData.length - index), 'MMM DD');
+    }
+  };
+  const data = rawData.map((view, index) => ({
+    name: getDate(index),
+    views: view,
+  }));
+
+  return (
+    <ResponsiveContainer width="100%" height={window.innerWidth < 468 ? 240 : 320}>
+      <AreaChart
+        data={data}
+        margin={{
+          top: 16,
+          right: 0,
+          left: 0,
+          bottom: 16,
+        }}
+      >
+        <defs>
+          <linearGradient id="colorUv" x1="0" y1="0" x2="0" y2="1">
+            <stop offset="5%" stopColor="#B39DDB" stopOpacity={0.8} />
+            <stop offset="95%" stopColor="#B39DDB" stopOpacity={0} />
+          </linearGradient>
+        </defs>
+        <XAxis dataKey="name" />
+        <YAxis />
+        <CartesianGrid strokeDasharray="1 1" />
+        <Tooltip />
+        <Area
+          type="monotone"
+          dataKey="views"
+          isAnimationActive={false}
+          stroke="#B39DDB"
+          fillOpacity={1}
+          fill="url(#colorUv)"
+        />
+      </AreaChart>
+    </ResponsiveContainer>
+  );
+};
+
+ChartArea.propTypes = {
+  data: PropTypes.arrayOf(PropTypes.number.isRequired).isRequired,
+  period: PropTypes.string.isRequired,
+};
+
+export default withTitle(ChartArea);

+ 31 - 0
client/components/Stats/StatsCharts/Bar.js

@@ -0,0 +1,31 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
+import withTitle from './withTitle';
+
+const ChartBar = ({ data }) => (
+  <ResponsiveContainer width="100%" height={window.innerWidth < 468 ? 240 : 320}>
+    <BarChart
+      data={data}
+      layout="vertical"
+      margin={{
+        top: 0,
+        right: 0,
+        left: 24,
+        bottom: 0,
+      }}
+    >
+      <XAxis type="number" dataKey="value" />
+      <YAxis type="category" dataKey="name" />
+      <CartesianGrid strokeDasharray="1 1" />
+      <Tooltip />
+      <Bar dataKey="value" fill="#B39DDB" />
+    </BarChart>
+  </ResponsiveContainer>
+);
+
+ChartBar.propTypes = {
+  data: PropTypes.arrayOf(PropTypes.object.isRequired).isRequired,
+};
+
+export default withTitle(ChartBar);

+ 34 - 0
client/components/Stats/StatsCharts/Pie.js

@@ -0,0 +1,34 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { PieChart, Pie, Tooltip, ResponsiveContainer } from 'recharts';
+import withTitle from './withTitle';
+
+const renderCustomLabel = ({ name }) => name;
+
+const ChartPie = ({ data }) => (
+  <ResponsiveContainer width="100%" height={window.innerWidth < 468 ? 240 : 320}>
+    <PieChart
+      margin={{
+        top: window.innerWidth < 468 ? 56 : 0,
+        right: window.innerWidth < 468 ? 56 : 0,
+        bottom: window.innerWidth < 468 ? 56 : 0,
+        left: window.innerWidth < 468 ? 56 : 0,
+      }}
+    >
+      <Pie
+        data={data}
+        dataKey="value"
+        innerRadius={window.innerWidth < 468 ? 20 : 80}
+        fill="#B39DDB"
+        label={renderCustomLabel}
+      />
+      <Tooltip />
+    </PieChart>
+  </ResponsiveContainer>
+);
+
+ChartPie.propTypes = {
+  data: PropTypes.arrayOf(PropTypes.object.isRequired).isRequired,
+};
+
+export default withTitle(ChartPie);

+ 78 - 0
client/components/Stats/StatsCharts/StatsCharts.js

@@ -0,0 +1,78 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import styled from 'styled-components';
+import Area from './Area';
+import Pie from './Pie';
+import Bar from './Bar';
+
+const ChartsWrapper = styled.div`
+  display: flex;
+  flex-direction: column;
+  align-items: stretch;
+  padding: 32px;
+
+  @media only screen and (max-width: 768px) {
+    padding: 16px 16px 32px 16px;
+  }
+`;
+
+const Row = styled.div`
+  display: flex;
+  border-bottom: 1px dotted #aaa;
+  padding: 0 0 40px 0;
+  margin: 0 0 32px 0;
+
+  :last-child {
+    border: none;
+    margin: 0;
+  }
+
+  @media only screen and (max-width: 768px) {
+    flex-direction: column;
+    padding-bottom: 0;
+    margin-bottom: 0;
+    border-bottom: none;
+
+    > *:not(:last-child) {
+      padding-bottom: 24px;
+      margin-bottom: 16px;
+      border-bottom: 1px dotted #aaa;
+    }
+  }
+`;
+
+const StatsCharts = ({ stats, period }) => {
+  const periodText = period.includes('last')
+    ? `the last ${period.replace('last', '').toLocaleLowerCase()}`
+    : 'all time';
+  const hasView = stats.views.filter(view => view > 0);
+  return (
+    <ChartsWrapper>
+      <Row>
+        <Area data={stats.views} period={period} periodText={periodText} />
+      </Row>
+      {hasView.length
+        ? [
+            <Row key="second-row">
+              <Pie data={stats.stats.referrer} title="Referrals" />
+              <Bar data={stats.stats.browser} title="Browsers" />
+            </Row>,
+            <Row key="third-row">
+              <Pie data={stats.stats.country} title="Country" />
+              <Bar data={stats.stats.os} title="OS" />
+            </Row>,
+          ]
+        : null}
+    </ChartsWrapper>
+  );
+};
+
+StatsCharts.propTypes = {
+  period: PropTypes.string.isRequired,
+  stats: PropTypes.shape({
+    stats: PropTypes.object.isRequired,
+    views: PropTypes.array.isRequired,
+  }).isRequired,
+};
+
+export default StatsCharts;

+ 1 - 0
client/components/Stats/StatsCharts/index.js

@@ -0,0 +1 @@
+export { default } from './StatsCharts';

+ 54 - 0
client/components/Stats/StatsCharts/withTitle.js

@@ -0,0 +1,54 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import styled from 'styled-components';
+
+const Wrapper = styled.div`
+  flex: 1 1 50%;
+  display: flex;
+  flex-direction: column;
+  align-items: stretch;
+
+  @media only screen and (max-width: 768px) {
+    flex-basis: 100%;
+  }
+`;
+
+const Title = styled.h3`
+  font-size: 24px;
+  font-weight: 300;
+
+  @media only screen and (max-width: 768px) {
+    font-size: 18px;
+  }
+`;
+
+const Count = styled.span`
+  font-weight: bold;
+  border-bottom: 1px dotted #999;
+`;
+
+const withTitle = ChartComponent => {
+  function WithTitle(props) {
+    return (
+      <Wrapper>
+        <Title>
+          {props.periodText && <Count>{props.data.reduce((sum, view) => sum + view, 0)}</Count>}
+          {props.periodText ? ` clicks in ${props.periodText}` : props.title}.
+        </Title>
+        <ChartComponent {...props} />
+      </Wrapper>
+    );
+  }
+  WithTitle.propTypes = {
+    data: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.object])).isRequired,
+    periodText: PropTypes.string,
+    title: PropTypes.string,
+  };
+  WithTitle.defaultProps = {
+    title: '',
+    periodText: '',
+  };
+  return WithTitle;
+};
+
+export default withTitle;

+ 46 - 0
client/components/Stats/StatsError.js

@@ -0,0 +1,46 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import styled from 'styled-components';
+
+const ErrorWrapper = styled.div`
+  display: flex;
+  justify-content: center;
+  align-items: center;
+`;
+
+const ErrorMessage = styled.h3`
+  font-size: 24px;
+  font-weight: 300;
+
+  @media only screen and (max-width: 768px) {
+    font-size: 18px;
+  }
+`;
+
+const Icon = styled.img`
+  width: 24px;
+  height: 24px;
+  margin: 6px 12px 0 0;
+
+  @media only screen and (max-width: 768px) {
+    width: 18px;
+    height: 18px;
+  }
+`;
+
+const StatsError = ({ text }) => (
+  <ErrorWrapper>
+    <Icon src="/images/x.svg" />
+    <ErrorMessage>{text || 'Could not get the short URL stats.'}</ErrorMessage>
+  </ErrorWrapper>
+);
+
+StatsError.propTypes = {
+  text: PropTypes.string,
+};
+
+StatsError.defaultProps = {
+  text: '',
+};
+
+export default StatsError;

+ 102 - 0
client/components/Stats/StatsHead.js

@@ -0,0 +1,102 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import styled, { css } from 'styled-components';
+
+const Wrapper = styled.div`
+  display: flex;
+  flex: 1 1 auto;
+  justify-content: space-between;
+  align-items: center;
+  padding: 24px 32px;
+  background-color: #f1f1f1;
+  border-top-left-radius: 12px;
+  border-top-right-radius: 12px;
+
+  @media only screen and (max-width: 768px) {
+    padding: 16px;
+  }
+`;
+
+const TotalText = styled.p`
+  margin: 0;
+  padding: 0;
+
+  span {
+    font-weight: bold;
+    border-bottom: 1px dotted #999;
+  }
+
+  @media only screen and (max-width: 768px) {
+    font-size: 13px;
+  }
+`;
+
+const TimeWrapper = styled.div`
+  display: flex;
+`;
+
+const Button = styled.button`
+  display: flex;
+  padding: 6px 12px;
+  margin: 0 4px;
+  border: none;
+  font-size: 12px;
+  border-radius: 4px;
+  box-shadow: 0 0px 10px rgba(100, 100, 100, 0.1);
+  background-color: white;
+  cursor: pointer;
+  transition: all 0.2s ease-out;
+  box-sizing: border-box;
+
+  :last-child {
+    margin-right: 0;
+  }
+
+  ${({ active }) =>
+    !active &&
+    css`
+      border: 1px solid #ddd;
+      background-color: #f5f5f5;
+      box-shadow: 0 2px 6px rgba(150, 150, 150, 0.2);
+
+      :hover {
+        border-color: 1px solid #ccc;
+        background-color: white;
+      }
+    `};
+
+  @media only screen and (max-width: 768px) {
+    padding: 4px 8px;
+    margin: 0 2px;
+    font-size: 11px;
+  }
+`;
+
+const StatsHead = ({ changePeriod, period, total }) => {
+  const buttonWithPeriod = (periodText, text) => (
+    <Button active={period === periodText} data-period={periodText} onClick={changePeriod}>
+      {text}
+    </Button>
+  );
+  return (
+    <Wrapper>
+      <TotalText>
+        Total clicks: <span>{total}</span>
+      </TotalText>
+      <TimeWrapper>
+        {buttonWithPeriod('allTime', 'All Time')}
+        {buttonWithPeriod('lastMonth', 'Month')}
+        {buttonWithPeriod('lastWeek', 'Week')}
+        {buttonWithPeriod('lastDay', 'Day')}
+      </TimeWrapper>
+    </Wrapper>
+  );
+};
+
+StatsHead.propTypes = {
+  changePeriod: PropTypes.func.isRequired,
+  period: PropTypes.string.isRequired,
+  total: PropTypes.number.isRequired,
+};
+
+export default StatsHead;

+ 1 - 0
client/components/Stats/index.js

@@ -0,0 +1 @@
+export { default } from './Stats';

+ 137 - 0
client/components/Table/TBody/TBody.js

@@ -0,0 +1,137 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+import styled, { css } from 'styled-components';
+import distanceInWordsToNow from 'date-fns/distance_in_words_to_now';
+import TBodyShortUrl from './TBodyShortUrl';
+import TBodyCount from './TBodyCount';
+
+const TBody = styled.tbody`
+  display: flex;
+  flex: 1 1 auto;
+  flex-direction: column;
+
+  ${({ loading }) =>
+    loading &&
+    css`
+      opacity: 0.2;
+    `};
+
+  tr:hover {
+    background-color: #f8f8f8;
+
+    td:after {
+      background: linear-gradient(to left, #f8f8f8, #f8f8f8, transparent);
+    }
+  }
+`;
+
+const Td = styled.td`
+  white-space: nowrap;
+  overflow: hidden;
+
+  ${({ withFade }) =>
+    withFade &&
+    css`
+      :after {
+        content: '';
+        position: absolute;
+        right: 0;
+        top: 0;
+        height: 100%;
+        width: 56px;
+        background: linear-gradient(to left, white, white, transparent);
+      }
+    `};
+
+  :last-child {
+    justify-content: space-between;
+  }
+
+  a {
+    color: #2196f3;
+    text-decoration: none;
+    box-sizing: border-box;
+    border-bottom: 1px dotted transparent;
+    transition: all 0.2s ease-out;
+
+    :hover {
+      border-bottom-color: #2196f3;
+    }
+  }
+
+  ${({ date }) =>
+    date &&
+    css`
+      font-size: 15px;
+    `};
+
+  ${({ flex }) =>
+    flex &&
+    css`
+      flex: ${`${flex} ${flex}`} 0;
+    `};
+
+  @media only screen and (max-width: 768px) {
+    flex: 1;
+    :nth-child(2) {
+      display: none;
+    }
+  }
+
+  @media only screen and (max-width: 510px) {
+    :nth-child(1) {
+      display: none;
+    }
+  }
+`;
+
+const TableBody = ({ copiedIndex, handleCopy, tableLoading, showModal, urls }) => {
+  const showList = (url, index) => (
+    <tr key={`tbody-${index}`}>
+      <Td flex="2" withFade>
+        <a href={url.target}>{url.target}</a>
+      </Td>
+      <Td flex="1" date>
+        {`${distanceInWordsToNow(url.createdAt)} ago`}
+      </Td>
+      <Td flex="1" withFade>
+        <TBodyShortUrl index={index} copiedIndex={copiedIndex} handleCopy={handleCopy} url={url} />
+      </Td>
+      <Td flex="1">
+        <TBodyCount url={url} showModal={showModal} />
+      </Td>
+    </tr>
+  );
+  return (
+    <TBody loading={tableLoading}>
+      {urls.length ? (
+        urls.map(showList)
+      ) : (
+        <tr>
+          <Td>Nothing to show.</Td>
+        </tr>
+      )}
+    </TBody>
+  );
+};
+
+TableBody.propTypes = {
+  urls: PropTypes.arrayOf(
+    PropTypes.shape({
+      id: PropTypes.string.isRequired,
+      count: PropTypes.number,
+      createdAt: PropTypes.string.isRequired,
+      password: PropTypes.bool,
+      target: PropTypes.string.isRequired,
+    })
+  ).isRequired,
+  copiedIndex: PropTypes.number.isRequired,
+  showModal: PropTypes.func.isRequired,
+  tableLoading: PropTypes.bool.isRequired,
+  handleCopy: PropTypes.func.isRequired,
+};
+
+const mapStateToProps = ({ loading: { table: tableLoading } }) => ({ tableLoading });
+
+export default connect(mapStateToProps)(TableBody);

+ 73 - 0
client/components/Table/TBody/TBodyButton.js

@@ -0,0 +1,73 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import styled, { css } from 'styled-components';
+
+const Button = styled.button`
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  width: 26px;
+  height: 26px;
+  margin: 0 12px 0 2px;
+  padding: 0;
+  border: none;
+  outline: none;
+  border-radius: 100%;
+  box-shadow: 0 2px 4px rgba(100, 100, 100, 0.1);
+  background-color: #dedede;
+  cursor: pointer;
+  transition: all 0.2s ease-out;
+
+  @media only screen and (max-width: 768px) {
+    height: 22px;
+    width: 22px;
+    margin: 0 8px 0 2px;
+
+    img {
+      width: 10px;
+      height: 10px;
+    }
+  }
+
+  ${({ withText }) =>
+    withText &&
+    css`
+      width: auto;
+      padding: 0 12px;
+      border-radius: 100px;
+
+      img {
+        margin: 4px 6px 0 0;
+      }
+
+      @media only screen and (max-width: 768px) {
+        width: auto;
+      }
+    `};
+
+  :active,
+  :focus {
+    outline: none;
+  }
+
+  :hover {
+    transform: translateY(-2px);
+  }
+`;
+
+const TBodyButton = ({ children, withText, ...props }) => (
+  <Button withText={withText} {...props}>
+    {children}
+  </Button>
+);
+
+TBodyButton.propTypes = {
+  children: PropTypes.node.isRequired,
+  withText: PropTypes.bool,
+};
+
+TBodyButton.defaultProps = {
+  withText: null,
+};
+
+export default TBodyButton;

+ 87 - 0
client/components/Table/TBody/TBodyCount.js

@@ -0,0 +1,87 @@
+import React, { Component } from 'react';
+import { bindActionCreators } from 'redux';
+import { connect } from 'react-redux';
+import PropTypes from 'prop-types';
+import Router from 'next/router';
+import styled from 'styled-components';
+import URL from 'url';
+import TBodyButton from './TBodyButton';
+import { showPageLoading } from '../../../actions';
+
+const Wrapper = styled.div`
+  display: flex;
+  flex: 1 1 auto;
+  justify-content: space-between;
+  align-items: center;
+`;
+
+const Actions = styled.div`
+  display: flex;
+  justify-content: flex-end;
+  align-items: center;
+
+  button {
+    margin: 0 2px 0 12px;
+  }
+`;
+
+const Icon = styled.img`
+  width: 12px;
+  height: 12px;
+`;
+
+class TBodyCount extends Component {
+  constructor() {
+    super();
+    this.goTo = this.goTo.bind(this);
+  }
+
+  goTo(e) {
+    e.preventDefault();
+    this.props.showLoading();
+    const host = URL.parse(this.props.url.shortUrl).hostname;
+    Router.push(`/stats?id=${this.props.url.id}${`&domain=${host}`}`);
+  }
+
+  render() {
+    const { showModal, url } = this.props;
+    return (
+      <Wrapper>
+        {url.count || 0}
+        <Actions>
+          {url.password && <Icon src="/images/lock.svg" lowopacity />}
+          {url.count > 0 && (
+            <TBodyButton withText onClick={this.goTo}>
+              <Icon src="/images/chart.svg" />
+              Stats
+            </TBodyButton>
+          )}
+          <TBodyButton
+            data-id={url.id}
+            data-host={URL.parse(url.shortUrl).hostname}
+            onClick={showModal}
+          >
+            <Icon src="/images/trash.svg" />
+          </TBodyButton>
+        </Actions>
+      </Wrapper>
+    );
+  }
+}
+
+TBodyCount.propTypes = {
+  showLoading: PropTypes.func.isRequired,
+  showModal: PropTypes.func.isRequired,
+  url: PropTypes.shape({
+    count: PropTypes.number,
+    id: PropTypes.string,
+    password: PropTypes.bool,
+    shortUrl: PropTypes.string,
+  }).isRequired,
+};
+
+const mapDispatchToProps = dispatch => ({
+  showLoading: bindActionCreators(showPageLoading, dispatch),
+});
+
+export default connect(null, mapDispatchToProps)(TBodyCount);

+ 46 - 0
client/components/Table/TBody/TBodyShortUrl.js

@@ -0,0 +1,46 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import styled from 'styled-components';
+import { CopyToClipboard } from 'react-copy-to-clipboard';
+import TBodyButton from './TBodyButton';
+
+const Wrapper = styled.div`
+  display: flex;
+  align-items: center;
+`;
+
+const CopyText = styled.div`
+  position: absolute;
+  top: 0;
+  left: 40px;
+  font-size: 11px;
+  color: green;
+`;
+
+const Icon = styled.img`
+  width: 12px;
+  height: 12px;
+`;
+
+const TBodyShortUrl = ({ index, copiedIndex, handleCopy, url }) => (
+  <Wrapper>
+    {copiedIndex === index && <CopyText>Copied to clipboard!</CopyText>}
+    <CopyToClipboard onCopy={() => handleCopy(index)} text={`${url.shortUrl}`}>
+      <TBodyButton>
+        <Icon src="/images/copy.svg" />
+      </TBodyButton>
+    </CopyToClipboard>
+    <a href={`${url.shortUrl}`}>{`${url.shortUrl.replace(/^https?:\/\//, '')}`}</a>
+  </Wrapper>
+);
+
+TBodyShortUrl.propTypes = {
+  copiedIndex: PropTypes.number.isRequired,
+  handleCopy: PropTypes.func.isRequired,
+  index: PropTypes.number.isRequired,
+  url: PropTypes.shape({
+    id: PropTypes.string.isRequired,
+  }).isRequired,
+};
+
+export default TBodyShortUrl;

+ 1 - 0
client/components/Table/TBody/index.js

@@ -0,0 +1 @@
+export { default } from './TBody';

+ 55 - 0
client/components/Table/THead/THead.js

@@ -0,0 +1,55 @@
+import React from 'react';
+import styled, { css } from 'styled-components';
+import TableOptions from '../TableOptions';
+
+const THead = styled.thead`
+  display: flex;
+  flex-direction: column;
+  flex: 1 1 auto;
+  background-color: #f1f1f1;
+  border-top-right-radius: 12px;
+  border-top-left-radius: 12px;
+
+  tr {
+    border-bottom: 1px solid #dedede;
+  }
+`;
+
+const Th = styled.th`
+  display: flex;
+  justify-content: start;
+  align-items: center;
+
+  ${({ flex }) =>
+    flex &&
+    css`
+      flex: ${`${flex} ${flex}`} 0;
+    `};
+
+  @media only screen and (max-width: 768px) {
+    flex: 1;
+    :nth-child(2) {
+      display: none;
+    }
+  }
+
+  @media only screen and (max-width: 510px) {
+    :nth-child(1) {
+      display: none;
+    }
+  }
+`;
+
+const TableHead = () => (
+  <THead>
+    <TableOptions />
+    <tr>
+      <Th flex="2">Original URL</Th>
+      <Th flex="1">Created</Th>
+      <Th flex="1">Short URL</Th>
+      <Th flex="1">Clicks</Th>
+    </tr>
+  </THead>
+);
+
+export default TableHead;

+ 1 - 0
client/components/Table/THead/index.js

@@ -0,0 +1 @@
+export { default } from './THead';

+ 161 - 0
client/components/Table/Table.js

@@ -0,0 +1,161 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { bindActionCreators } from 'redux';
+import { connect } from 'react-redux';
+import styled from 'styled-components';
+import THead from './THead';
+import TBody from './TBody';
+import TableOptions from './TableOptions';
+import { deleteShortUrl, getUrlsList } from '../../actions';
+import Modal from '../Modal';
+
+const Wrapper = styled.div`
+  width: 1200px;
+  max-width: 95%;
+  display: flex;
+  flex-direction: column;
+  margin: 40px 0 120px;
+`;
+
+const Title = styled.h2`
+  font-size: 24px;
+  font-weight: 300;
+
+  @media only screen and (max-width: 768px) {
+    font-size: 18px;
+  }
+`;
+
+const TableWrapper = styled.table`
+  display: flex;
+  flex: 1 1 auto;
+  flex-direction: column;
+  background-color: white;
+  border-radius: 12px;
+  box-shadow: 0 6px 30px rgba(50, 50, 50, 0.2);
+
+  tr {
+    display: flex;
+    flex: 1 1 auto;
+    padding: 0 24px;
+    justify-content: space-between;
+    border-bottom: 1px solid #eaeaea;
+  }
+
+  th,
+  td {
+    position: relative;
+    display: flex;
+    padding: 16px 0;
+    align-items: center;
+  }
+
+  @media only screen and (max-width: 768px) {
+    font-size: 13px;
+  }
+
+  @media only screen and (max-width: 510px) {
+    tr {
+      padding: 0 16px;
+    }
+    th,
+    td {
+      padding: 12px 0;
+    }
+  }
+`;
+
+const TFoot = styled.tfoot`
+  background-color: #f1f1f1;
+  border-bottom-right-radius: 12px;
+  border-bottom-left-radius: 12px;
+`;
+
+class Table extends Component {
+  constructor() {
+    super();
+    this.state = {
+      copiedIndex: -1,
+      modalUrlId: '',
+      modalUrlDomain: '',
+      showModal: false,
+    };
+    this.handleCopy = this.handleCopy.bind(this);
+    this.showModal = this.showModal.bind(this);
+    this.closeModal = this.closeModal.bind(this);
+    this.deleteUrl = this.deleteUrl.bind(this);
+  }
+
+  handleCopy(index) {
+    this.setState({ copiedIndex: index });
+    setTimeout(() => {
+      this.setState({ copiedIndex: -1 });
+    }, 1500);
+  }
+
+  showModal(e) {
+    e.preventDefault();
+    const modalUrlId = e.currentTarget.dataset.id;
+    const modalUrlDomain = e.currentTarget.dataset.host;
+    this.setState({
+      modalUrlId,
+      modalUrlDomain,
+      showModal: true,
+    });
+  }
+
+  closeModal() {
+    this.setState({
+      modalUrlId: '',
+      modalUrlDomain: '',
+      showModal: false,
+    });
+  }
+
+  deleteUrl() {
+    this.closeModal();
+    const { modalUrlId, modalUrlDomain } = this.state;
+    this.props.deleteShortUrl({ id: modalUrlId, domain: modalUrlDomain });
+  }
+
+  render() {
+    const { copiedIndex } = this.state;
+    const { url } = this.props;
+    return (
+      <Wrapper>
+        <Title>Recent shortened links.</Title>
+        <TableWrapper>
+          <THead />
+          <TBody
+            copiedIndex={copiedIndex}
+            handleCopy={this.handleCopy}
+            urls={url.list}
+            showModal={this.showModal}
+          />
+          <TFoot>
+            <TableOptions nosearch />
+          </TFoot>
+        </TableWrapper>
+        <Modal show={this.state.showModal} handler={this.deleteUrl} close={this.closeModal}>
+          Are you sure do you want to delete the short URL and its stats?
+        </Modal>
+      </Wrapper>
+    );
+  }
+}
+
+Table.propTypes = {
+  deleteShortUrl: PropTypes.func.isRequired,
+  url: PropTypes.shape({
+    list: PropTypes.array.isRequired,
+  }).isRequired,
+};
+
+const mapStateToProps = ({ url }) => ({ url });
+
+const mapDispatchToProps = dispatch => ({
+  deleteShortUrl: bindActionCreators(deleteShortUrl, dispatch),
+  getUrlsList: bindActionCreators(getUrlsList, dispatch),
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(Table);

+ 67 - 0
client/components/Table/TableNav.js

@@ -0,0 +1,67 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import styled, { css } from 'styled-components';
+
+const Wrapper = styled.div`
+  display: flex;
+  align-items: center;
+`;
+
+const Nav = styled.button`
+  margin-left: 12px;
+  padding: 5px 8px 3px;
+  border-radius: 4px;
+  border: 1px solid #eee;
+  background-color: transparent;
+  box-shadow: 0 0px 10px rgba(100, 100, 100, 0.1);
+  transition: all 0.2s ease-out;
+
+  ${({ active }) =>
+    active &&
+    css`
+      background-color: white;
+      cursor: pointer;
+    `};
+
+  :hover {
+    ${({ active }) =>
+      active &&
+      css`
+        transform: translateY(-2px);
+        box-shadow: 0 5px 25px rgba(50, 50, 50, 0.1);
+      `};
+  }
+
+  @media only screen and (max-width: 768px) {
+    padding: 4px 6px 2px;
+  }
+`;
+
+const Icon = styled.img`
+  width: 14px;
+  height: 14px;
+
+  @media only screen and (max-width: 768px) {
+    width: 12px;
+    height: 12px;
+  }
+`;
+
+const TableNav = ({ handleNav, next, prev }) => (
+  <Wrapper>
+    <Nav active={prev} data-active={prev} data-type="prev" onClick={handleNav}>
+      <Icon src="/images/nav-left.svg" />
+    </Nav>
+    <Nav active={next} data-active={next} data-type="next" onClick={handleNav}>
+      <Icon src="/images/nav-right.svg" />
+    </Nav>
+  </Wrapper>
+);
+
+TableNav.propTypes = {
+  handleNav: PropTypes.func.isRequired,
+  next: PropTypes.bool.isRequired,
+  prev: PropTypes.bool.isRequired,
+};
+
+export default TableNav;

+ 200 - 0
client/components/Table/TableOptions.js

@@ -0,0 +1,200 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { bindActionCreators } from 'redux';
+import { connect } from 'react-redux';
+import styled, { css } from 'styled-components';
+import TableNav from './TableNav';
+import TextInput from '../TextInput';
+import { getUrlsList } from '../../actions';
+
+const Tr = styled.tr`
+  display: flex;
+  align-items: center;
+
+  thead & {
+    border-bottom: 1px solid #ddd !important;
+  }
+`;
+
+const Th = styled.th`
+  display: flex;
+  align-items: center;
+
+  ${({ flex }) =>
+    flex &&
+    css`
+      flex: ${`${flex} ${flex}`} 0;
+    `};
+`;
+
+const Divider = styled.div`
+  margin: 0 16px 0 24px;
+  width: 1px;
+  height: 20px;
+  background-color: #ccc;
+
+  @media only screen and (max-width: 768px) {
+    margin: 0 4px 0 12px;
+  }
+
+  @media only screen and (max-width: 510px) {
+    display: none;
+  }
+`;
+
+const ListCount = styled.div`
+  display: flex;
+  align-items: center;
+`;
+
+const Ul = styled.ul`
+  display: flex;
+  margin: 0;
+  padding: 0;
+  list-style: none;
+
+  li {
+    display: flex;
+    margin: 0 0 0 12px;
+    list-style: none;
+
+    @media only screen and (max-width: 768px) {
+      margin-left: 8px;
+    }
+  }
+
+  @media only screen and (max-width: 510px) {
+    display: none;
+  }
+`;
+
+const Button = styled.button`
+  display: flex;
+  padding: 4px 8px;
+  border: none;
+  font-size: 12px;
+  border-radius: 4px;
+  box-shadow: 0 0px 10px rgba(100, 100, 100, 0.1);
+  background-color: white;
+  cursor: pointer;
+  transition: all 0.2s ease-out;
+  box-sizing: border-box;
+
+  ${({ active }) =>
+    !active &&
+    css`
+      border: 1px solid #ddd;
+      background-color: #f5f5f5;
+      box-shadow: 0 0px 5px rgba(150, 150, 150, 0.1);
+
+      :hover {
+        border-color: 1px solid #ccc;
+        background-color: white;
+      }
+    `};
+
+  @media only screen and (max-width: 768px) {
+    font-size: 10px;
+  }
+`;
+
+class TableOptions extends Component {
+  constructor() {
+    super();
+    this.state = {
+      search: '',
+    };
+    this.submitSearch = this.submitSearch.bind(this);
+    this.handleSearch = this.handleSearch.bind(this);
+    this.handleCount = this.handleCount.bind(this);
+    this.handleNav = this.handleNav.bind(this);
+  }
+
+  submitSearch(e) {
+    e.preventDefault();
+    this.props.getUrlsList({ search: this.state.search });
+  }
+
+  handleSearch(e) {
+    this.setState({ search: e.currentTarget.value });
+  }
+
+  handleCount(e) {
+    const count = Number(e.target.textContent);
+    this.props.getUrlsList({ count });
+  }
+
+  handleNav(e) {
+    const { active, type } = e.target.dataset;
+    if (active === 'false') return null;
+    const number = type === 'next' ? 1 : -1;
+    return this.props.getUrlsList({ page: this.props.url.page + number });
+  }
+
+  render() {
+    const { count, countAll, page } = this.props.url;
+    return (
+      <Tr>
+        <Th>
+          {!this.props.nosearch && (
+            <form onSubmit={this.submitSearch}>
+              <TextInput
+                id="search"
+                name="search"
+                value={this.state.search}
+                placeholder="Search..."
+                onChange={this.handleSearch}
+                tiny
+              />
+            </form>
+          )}
+        </Th>
+        <Th>
+          <ListCount>
+            <Ul>
+              <li>
+                <Button active={count === 10} onClick={this.handleCount}>
+                  10
+                </Button>
+              </li>
+              <li>
+                <Button active={count === 25} onClick={this.handleCount}>
+                  25
+                </Button>
+              </li>
+              <li>
+                <Button active={count === 50} onClick={this.handleCount}>
+                  50
+                </Button>
+              </li>
+            </Ul>
+          </ListCount>
+          <Divider />
+          <TableNav handleNav={this.handleNav} next={page * count < countAll} prev={page > 1} />
+        </Th>
+      </Tr>
+    );
+  }
+}
+
+TableOptions.propTypes = {
+  getUrlsList: PropTypes.func.isRequired,
+  nosearch: PropTypes.bool,
+  url: PropTypes.shape({
+    page: PropTypes.number.isRequired,
+    count: PropTypes.number.isRequired,
+    countAll: PropTypes.number.isRequired,
+  }).isRequired,
+};
+
+TableOptions.defaultProps = {
+  nosearch: false,
+};
+
+const mapStateToProps = ({ url }) => ({ url });
+
+const mapDispatchToProps = dispatch => ({
+  getUrlsList: bindActionCreators(getUrlsList, dispatch),
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(TableOptions);

+ 1 - 0
client/components/Table/index.js

@@ -0,0 +1 @@
+export { default } from './Table';

+ 122 - 0
client/components/TextInput/TextInput.js

@@ -0,0 +1,122 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import styled, { css } from 'styled-components';
+import { fadeIn } from '../../helpers/animations';
+
+const LinkInput = styled.input`
+  position: relative;
+  width: auto;
+  flex: 1 1 auto;
+  height: 72px;
+  padding: 0 84px 0 40px;
+  font-size: 20px;
+  letter-spacing: 0.05em;
+  color: #444;
+  box-sizing: border-box;
+  background-color: white;
+  box-shadow: 0 10px 35px rgba(50, 50, 50, 0.1);
+  border-radius: 100px;
+  border: none;
+  border-bottom: 6px solid #f5f5f5;
+  animation: ${fadeIn} 0.5s ease-out;
+  transition: all 0.5s ease-out;
+
+  :focus {
+    outline: none;
+    box-shadow: 0 20px 35px rgba(50, 50, 50, 0.2);
+  }
+
+  ::placeholder {
+    font-size: 16px;
+    letter-spacing: 0.1em;
+    color: #888;
+  }
+
+  @media only screen and (max-width: 488px) {
+    height: 56px;
+    padding: 0 48px 0 32px;
+    font-size: 14px;
+    border-bottom-width: 5px;
+    ::placeholder {
+      font-size: 14px;
+    }
+  }
+
+  ${({ small }) =>
+    small &&
+    css`
+      width: 240px;
+      height: 54px;
+      margin-right: 32px;
+      padding: 0 24px 2px;
+      font-size: 18px;
+      border-bottom: 4px solid #f5f5f5;
+      ::placeholder {
+        font-size: 13px;
+      }
+
+      @media only screen and (max-width: 448px) {
+        width: 200px;
+        height: 40px;
+        padding: 0 16px 2px;
+        font-size: 13px;
+        border-bottom-width: 3px;
+      }
+    `};
+
+  ${({ tiny }) =>
+    tiny &&
+    css`
+      flex: 0 0 auto;
+      width: 280px;
+      height: 32px;
+      margin: 0;
+      padding: 0 16px 1px;
+      font-size: 13px;
+      border-bottom-width: 1px;
+      border-radius: 4px;
+      box-shadow: 0 4px 10px rgba(100, 100, 100, 0.1);
+
+      :focus {
+        box-shadow: 0 10px 25px rgba(50, 50, 50, 0.1);
+      }
+
+      ::placeholder {
+        font-size: 12px;
+        letter-spacing: 0;
+      }
+
+      @media only screen and (max-width: 768px) {
+        width: 240px;
+        height: 28px;
+      }
+
+      @media only screen and (max-width: 510px) {
+        width: 180px;
+        height: 24px;
+        padding: 0 8px 1px;
+        font-size: 12px;
+        border-bottom-width: 3px;
+      }
+    `};
+
+  ${({ height }) =>
+    height &&
+    css`
+      height: ${height}px;
+    `};
+`;
+
+const TextInput = props => <LinkInput {...props} />;
+
+TextInput.propTypes = {
+  small: PropTypes.bool,
+  tiny: PropTypes.bool,
+};
+
+TextInput.defaultProps = {
+  small: false,
+  tiny: false,
+};
+
+export default TextInput;

+ 1 - 0
client/components/TextInput/index.js

@@ -0,0 +1 @@
+export { default } from './TextInput';

+ 10 - 0
client/config.example.js

@@ -0,0 +1,10 @@
+module.exports = {
+  /*
+    reCaptcha site key
+    Create one in https://www.google.com/recaptcha/intro/
+  */
+  RECAPTCHA_SITE_KEY: '',
+
+  // Google analytics tracking ID
+  GOOGLE_ANALYTICS_ID: '6Lc4TUAUAAAAAMRHnlEEt21UkPlOXKCXHaIapdTT',
+};

+ 23 - 0
client/helpers/analytics.js

@@ -0,0 +1,23 @@
+import ReactGA from 'react-ga';
+import { GOOGLE_ANALYTICS_ID } from '../config';
+
+export const initGA = () => {
+  ReactGA.initialize(GOOGLE_ANALYTICS_ID, { debug: true });
+};
+
+export const logPageView = () => {
+  ReactGA.set({ page: window.location.pathname });
+  ReactGA.pageview(window.location.pathname);
+};
+
+export const logEvent = (category = '', action = '') => {
+  if (category && action) {
+    ReactGA.event({ category, action });
+  }
+};
+
+export const logException = (description = '', fatal = false) => {
+  if (description) {
+    ReactGA.exception({ description, fatal });
+  }
+};

+ 19 - 0
client/helpers/animations.js

@@ -0,0 +1,19 @@
+import { keyframes } from 'styled-components';
+
+export const fadeIn = keyframes`
+  from {
+    opacity: 0;
+  }
+  to {
+    opacity: 1;
+  }
+`;
+
+export const spin = keyframes`
+  from {
+    transform: rotate(0);
+  }
+  to {
+    transform: rotate(-360deg);
+  }
+`;

+ 17 - 0
client/helpers/recaptcha.js

@@ -0,0 +1,17 @@
+export default function showRecaptcha() {
+  const captcha = document.getElementById('g-recaptcha');
+  if (!captcha) return null;
+  window.recaptchaCallback = response => {
+    const captchaInput = document.getElementById('g-recaptcha-input');
+    captchaInput.value = response;
+  };
+  if (!window.grecaptcha) {
+    return setTimeout(() => showRecaptcha(captcha), 200);
+  }
+  return setTimeout(() => {
+    if ((window, captcha, !captcha.childNodes.length)) {
+      window.grecaptcha.render(captcha);
+    }
+    return null;
+  }, 1000);
+}

+ 67 - 0
client/pages/_document.js

@@ -0,0 +1,67 @@
+import React from 'react';
+import Document, { Head, Main, NextScript } from 'next/document';
+import { ServerStyleSheet } from 'styled-components';
+
+const style = {
+  margin: 0,
+  backgroundColor: '#f3f3f3',
+  font: '16px/1.45 "Nunito", sans-serif',
+  overflowX: 'hidden',
+  color: 'black',
+};
+
+class AppDocument extends Document {
+  static getInitialProps({ renderPage }) {
+    const sheet = new ServerStyleSheet();
+    const page = renderPage(App => props => sheet.collectStyles(<App {...props} />));
+    const styleTags = sheet.getStyleElement();
+    return { ...page, styleTags };
+  }
+
+  render() {
+    return (
+      <html lang="en">
+        <Head>
+          <meta charSet="utf-8" />
+          <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
+          <title>Kutt.it | Modern URL shortener.</title>
+          <meta
+            name="description"
+            content="Kutt.it is a free and open source URL shortener with custom domains and stats."
+          />
+          <link
+            href="https://fonts.googleapis.com/css?family=Nunito:300,400,700"
+            rel="stylesheet"
+          />
+          <link rel="icon" sizes="196x196" href="/images/favicon-196x196.png" />
+          <link rel="icon" sizes="32x32" href="/images/favicon-32x32.png" />
+          <link rel="icon" sizes="16x16" href="/images/favicon-16x16.png" />
+          <link rel="apple-touch-icon" href="/images/favicon-196x196.png" />
+          <link rel="mask-icon" href="/images/icon.svg" color="blue" />
+
+          <meta property="fb:app_id" content="123456789" />
+          <meta property="og:url" content="https://kutt.it" />
+          <meta property="og:type" content="website" />
+          <meta property="og:title" content="Kutt.it" />
+          <meta property="og:image" content="https://kutt.it/card.png" />
+          <meta property="og:description" content="Free Modern URL Shortener" />
+          <meta name="twitter:url" content="https://kutt.it" />
+          <meta name="twitter:title" content="Kutt.it" />
+          <meta name="twitter:description" content="Free Modern URL Shortener" />
+          <meta name="twitter:image" content="https://kutt.it/card.png" />
+
+          {this.props.styleTags}
+
+          <script src="https://www.google.com/recaptcha/api.js?render=explicit" />
+          <script src="/analytics.js" />
+        </Head>
+        <body style={style}>
+          <Main />
+          <NextScript />
+        </body>
+      </html>
+    );
+  }
+}
+
+export default AppDocument;

+ 55 - 0
client/pages/index.js

@@ -0,0 +1,55 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import withRedux from 'next-redux-wrapper';
+import { bindActionCreators } from 'redux';
+import initialState from '../store';
+import BodyWrapper from '../components/BodyWrapper';
+import Shortener from '../components/Shortener';
+import Features from '../components/Features';
+import Table from '../components/Table';
+import NeedToLogin from '../components/NeedToLogin';
+import Footer from '../components/Footer/Footer';
+import { authUser, getUrlsList } from '../actions';
+
+class Homepage extends Component {
+  static getInitialProps({ req, store }) {
+    const token = req && req.cookies && req.cookies.token;
+    if (token && store) store.dispatch(authUser(token));
+  }
+
+  componentDidMount() {
+    if (this.props.isAuthenticated) this.props.getUrlsList();
+  }
+
+  shouldComponentUpdate(nextProps) {
+    return this.props.isAuthenticated !== nextProps.isAuthenticated;
+  }
+
+  render() {
+    const { isAuthenticated } = this.props;
+    const needToLogin = !isAuthenticated && <NeedToLogin />;
+    const table = isAuthenticated && <Table />;
+    return (
+      <BodyWrapper>
+        <Shortener />
+        {needToLogin}
+        {table}
+        <Features />
+        <Footer />
+      </BodyWrapper>
+    );
+  }
+}
+
+Homepage.propTypes = {
+  isAuthenticated: PropTypes.bool.isRequired,
+  getUrlsList: PropTypes.func.isRequired,
+};
+
+const mapStateToProps = ({ auth: { isAuthenticated } }) => ({ isAuthenticated });
+
+const mapDispatchToProps = dispatch => ({
+  getUrlsList: bindActionCreators(getUrlsList, dispatch),
+});
+
+export default withRedux(initialState, mapStateToProps, mapDispatchToProps)(Homepage);

+ 39 - 0
client/pages/login.js

@@ -0,0 +1,39 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import withRedux from 'next-redux-wrapper';
+import Router from 'next/router';
+import initialState from '../store';
+import BodyWrapper from '../components/BodyWrapper';
+import Login from '../components/Login';
+import { authUser } from '../actions';
+
+class LoginPage extends Component {
+  componentDidMount() {
+    if (this.props.isAuthenticated) {
+      Router.push('/');
+    }
+  }
+
+  render() {
+    return (
+      !this.props.isAuthenticated && (
+        <BodyWrapper>
+          <Login />
+        </BodyWrapper>
+      )
+    );
+  }
+}
+
+LoginPage.getInitialProps = ({ req, store }) => {
+  const token = req && req.cookies && req.cookies.token;
+  if (token && store) store.dispatch(authUser(token));
+};
+
+LoginPage.propTypes = {
+  isAuthenticated: PropTypes.bool.isRequired,
+};
+
+const mapStateToProps = ({ auth: { isAuthenticated } }) => ({ isAuthenticated });
+
+export default withRedux(initialState, mapStateToProps)(LoginPage);

+ 23 - 0
client/pages/logout.js

@@ -0,0 +1,23 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { bindActionCreators } from 'redux';
+import withRedux from 'next-redux-wrapper';
+import initialState from '../store';
+import { logoutUser } from '../actions';
+
+class LogoutPage extends Component {
+  componentDidMount() {
+    this.props.logoutUser();
+  }
+  render() {
+    return <div />;
+  }
+}
+
+LogoutPage.propTypes = {
+  logoutUser: PropTypes.func.isRequired,
+};
+
+const mapDispatchToProps = dispatch => ({ logoutUser: bindActionCreators(logoutUser, dispatch) });
+
+export default withRedux(initialState, null, mapDispatchToProps)(LogoutPage);

+ 130 - 0
client/pages/reset-password.js

@@ -0,0 +1,130 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import Router from 'next/router';
+import withRedux from 'next-redux-wrapper';
+import styled, { css } from 'styled-components';
+import cookie from 'js-cookie';
+import axios from 'axios';
+import initialState from '../store';
+import BodyWrapper from '../components/BodyWrapper';
+import TextInput from '../components/TextInput';
+import Button from '../components/Button';
+import { authUser } from '../actions';
+
+const Form = styled.form`
+  position: relative;
+  display: flex;
+  flex-direction: column;
+  align-items: flex-start;
+
+  input {
+    margin: 16px 0 32px;
+  }
+`;
+
+const Message = styled.p`
+  position: absolute;
+  right: 0;
+  bottom: 16px;
+  font-size: 14px;
+  color: green;
+
+  ${({ error }) =>
+    error &&
+    css`
+      color: red;
+    `};
+
+  @media only screen and (max-width: 768px) {
+    bottom: 32px;
+    font-size: 12px;
+  }
+`;
+
+class ResetPassword extends Component {
+  constructor() {
+    super();
+    this.state = {
+      error: '',
+      loading: false,
+      success: '',
+    };
+    this.handleReset = this.handleReset.bind(this);
+  }
+
+  componentDidMount() {
+    if (this.props.query || cookie.get('token')) {
+      cookie.set('token', this.props.query.token, { expires: 7 });
+      Router.push('/settings');
+    }
+  }
+
+  handleReset(e) {
+    if (this.state.loading) return null;
+    e.preventDefault();
+    const form = document.getElementById('reset-password-form');
+    const { email: { value } } = form.elements;
+    if (!value) {
+      this.setState({ error: 'Please provide an Email address.' }, () => {
+        setTimeout(() => {
+          this.setState({ error: '' });
+        }, 1500);
+      });
+    }
+    this.setState({ loading: true });
+    return axios
+      .post('/api/auth/resetpassword', { email: value })
+      .then(() =>
+        this.setState({ success: 'Reset password email has been sent.', loading: false }, () => {
+          setTimeout(() => {
+            this.setState({ success: '' });
+          }, 2500);
+        })
+      )
+      .catch(() =>
+        this.setState({ error: "Couldn't reset password", loading: false }, () => {
+          setTimeout(() => {
+            this.setState({ error: '' });
+          }, 1500);
+        })
+      );
+  }
+
+  render() {
+    const { error, loading, success } = this.state;
+    return (
+      <BodyWrapper>
+        <Form id="reset-password-form" onSubmit={this.handleReset}>
+          <TextInput type="email" name="email" id="email" placeholder="Email address" autoFocus />
+          <Button onClick={this.handleReset} icon={loading ? 'loader' : ''} big>
+            Reset password
+          </Button>
+          <Message error={!success && error}>
+            {!success && error}
+            {success}
+          </Message>
+        </Form>
+      </BodyWrapper>
+    );
+  }
+}
+
+ResetPassword.getInitialProps = ({ store, query }) => {
+  if (query && query.token) {
+    store.dispatch(authUser(query.token));
+    return { query };
+  }
+  return null;
+};
+
+ResetPassword.propTypes = {
+  query: PropTypes.shape({
+    token: PropTypes.string,
+  }),
+};
+
+ResetPassword.defaultProps = {
+  query: null,
+};
+
+export default withRedux(initialState)(ResetPassword);

+ 28 - 0
client/pages/settings.js

@@ -0,0 +1,28 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import withRedux from 'next-redux-wrapper';
+import initialState from '../store';
+import BodyWrapper from '../components/BodyWrapper';
+import Footer from '../components/Footer';
+import { authUser } from '../actions';
+import Settings from '../components/Settings';
+
+const SettingsPage = ({ isAuthenticated }) => (
+  <BodyWrapper>
+    {isAuthenticated ? <Settings /> : null}
+    <Footer />
+  </BodyWrapper>
+);
+
+SettingsPage.getInitialProps = ({ req, store }) => {
+  const token = req && req.cookies && req.cookies.token;
+  if (token && store) store.dispatch(authUser(token));
+};
+
+SettingsPage.propTypes = {
+  isAuthenticated: PropTypes.bool.isRequired,
+};
+
+const mapStateToProps = ({ auth: { isAuthenticated } }) => ({ isAuthenticated });
+
+export default withRedux(initialState, mapStateToProps)(SettingsPage);

+ 29 - 0
client/pages/stats.js

@@ -0,0 +1,29 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import withRedux from 'next-redux-wrapper';
+import initialState from '../store';
+import BodyWrapper from '../components/BodyWrapper';
+import Stats from '../components/Stats';
+import { authUser } from '../actions';
+
+const StatsPage = ({ id }) => (
+  <BodyWrapper>
+    <Stats id={id} />
+  </BodyWrapper>
+);
+
+StatsPage.getInitialProps = ({ req, store, query }) => {
+  const token = req && req.cookies && req.cookies.token;
+  if (token && store) store.dispatch(authUser(token));
+  return { id: query && query.id };
+};
+
+StatsPage.propTypes = {
+  id: PropTypes.string,
+};
+
+StatsPage.defaultProps = {
+  id: '',
+};
+
+export default withRedux(initialState)(StatsPage);

+ 62 - 0
client/pages/terms.js

@@ -0,0 +1,62 @@
+import React from 'react';
+import withRedux from 'next-redux-wrapper';
+import styled from 'styled-components';
+import initialState from '../store';
+import BodyWrapper from '../components/BodyWrapper';
+import { authUser } from '../actions';
+
+const Wrapper = styled.div`
+  width: 600px;
+  max-width: 97%;
+  display: flex;
+  flex-direction: column;
+  align-items: flex-start;
+`;
+
+const SettingsPage = () => (
+  <BodyWrapper>
+    <Wrapper>
+      <h3>Kutt Terms of Service</h3>
+      <p>
+        By accessing the website at <a href="https://kutt.it">https://kutt.it</a>, you are agreeing
+        to be bound by these terms of service, all applicable laws and regulations, and agree that
+        you are responsible for compliance with any applicable local laws. If you do not agree with
+        any of these terms, you are prohibited from using or accessing this site. The materials
+        contained in this website are protected by applicable copyright and trademark law.
+      </p>
+      <p>
+        In no event shall Kutt or its suppliers be liable for any damages (including, without
+        limitation, damages for loss of data or profit, or due to business interruption) arising out
+        of the use or inability to use the materials on {"Kutt's"} website, even if Kutt or a Kutt
+        authorized representative has been notified orally or in writing of the possibility of such
+        damage. Because some jurisdictions do not allow limitations on implied warranties, or
+        limitations of liability for consequential or incidental damages, these limitations may not
+        apply to you.
+      </p>
+      <p>
+        The materials appearing on Kutt website could include technical, typographical, or
+        photographic errors. Kutt does not warrant that any of the materials on its website are
+        accurate, complete or current. Kutt may make changes to the materials contained on its
+        website at any time without notice. However Kutt does not make any commitment to update the
+        materials.
+      </p>
+      <p>
+        Kutt has not reviewed all of the sites linked to its website and is not responsible for the
+        contents of any such linked site. The inclusion of any link does not imply endorsement by
+        Kutt of the site. Use of any such linked website is at the {"user's"} own risk.
+      </p>
+      <p>
+        Kutt may revise these terms of service for its website at any time without notice. By using
+        this website you are agreeing to be bound by the then current version of these terms of
+        service.
+      </p>
+    </Wrapper>
+  </BodyWrapper>
+);
+
+SettingsPage.getInitialProps = ({ req, store }) => {
+  const token = req && req.cookies && req.cookies.token;
+  if (token && store) store.dispatch(authUser(token));
+};
+
+export default withRedux(initialState)(SettingsPage);

+ 120 - 0
client/pages/url-password.js

@@ -0,0 +1,120 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import withRedux from 'next-redux-wrapper';
+import styled from 'styled-components';
+import axios from 'axios';
+import initialState from '../store';
+import BodyWrapper from '../components/BodyWrapper';
+import TextInput from '../components/TextInput';
+import Button from '../components/Button';
+
+const Title = styled.h3`
+  font-size: 24px;
+  font-weight: 300;
+  text-align: center;
+
+  @media only screen and (max-width: 448px) {
+    font-size: 18px;
+  }
+`;
+
+const Form = styled.form`
+  position: relative;
+  display: flex;
+  align-items: center;
+`;
+
+const Error = styled.p`
+  position: absolute;
+  left: 0;
+  bottom: -48px;
+  font-size: 14px;
+  color: red;
+
+  @media only screen and (max-width: 448px) {
+    bottom: -40px;
+    font-size: 12px;
+  }
+`;
+
+class UrlPasswordPage extends Component {
+  static getInitialProps({ query }) {
+    return { query };
+  }
+
+  constructor() {
+    super();
+    this.state = {
+      error: '',
+      loading: false,
+      password: '',
+    };
+    this.updatePassword = this.updatePassword.bind(this);
+    this.requestUrl = this.requestUrl.bind(this);
+  }
+
+  shouldComponentUpdate() {
+    return true;
+  }
+
+  updatePassword(e) {
+    this.setState({
+      password: e.currentTarget.value,
+    });
+  }
+
+  requestUrl(e) {
+    e.preventDefault();
+    const { password } = this.state;
+    if (!password) {
+      return this.setState({
+        error: 'Password must not be empty',
+      });
+    }
+    this.setState({ error: '' });
+    this.setState({ loading: true });
+    return axios
+      .post('/api/url/requesturl', { id: this.props.query, password })
+      .then(({ data }) => window.location.replace(data.target))
+      .catch(({ response }) =>
+        this.setState({
+          loading: false,
+          error: response.data.error,
+        })
+      );
+  }
+
+  render() {
+    if (!this.props.query) {
+      return (
+        <BodyWrapper>
+          <Title>404 | Not found.</Title>
+        </BodyWrapper>
+      );
+    }
+    return (
+      <BodyWrapper>
+        <Title>Enter the password to access the URL.</Title>
+        <Form onSubmit={this.requestUrl}>
+          <TextInput placeholder="Password" onChange={this.updatePassword} small />
+          <Button type="submit" icon={this.state.loading ? 'loader' : ''}>
+            Go
+          </Button>
+          <Error>{this.state.error}</Error>
+        </Form>
+      </BodyWrapper>
+    );
+  }
+}
+
+UrlPasswordPage.propTypes = {
+  query: PropTypes.shape({
+    id: PropTypes.string,
+  }),
+};
+
+UrlPasswordPage.defaultProps = {
+  query: null,
+};
+
+export default withRedux(initialState)(UrlPasswordPage);

+ 101 - 0
client/pages/verify.js

@@ -0,0 +1,101 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Router from 'next/router';
+import withRedux from 'next-redux-wrapper';
+import { bindActionCreators } from 'redux';
+import styled from 'styled-components';
+import cookie from 'js-cookie';
+import initialState from '../store';
+import BodyWrapper from '../components/BodyWrapper';
+import { authRenew, authUser, showPageLoading } from '../actions';
+import Button from '../components/Button';
+
+const Wrapper = styled.div`
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+`;
+
+const MessageWrapper = styled.div`
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  margin: 32px 0;
+`;
+
+const Message = styled.p`
+  font-size: 24px;
+  font-weight: 300;
+
+  @media only screen and (max-width: 768px) {
+    font-size: 18px;
+  }
+`;
+
+const Icon = styled.img`
+  width: 32px;
+  height: 32px;
+  margin-right: 16px;
+
+  @media only screen and (max-width: 768px) {
+    width: 26px;
+    height: 26px;
+    margin-right: 8px;
+  }
+`;
+
+const Verify = ({ showLoading, query }) => {
+  if (query) {
+    cookie.set('token', query.token, { expires: 7 });
+  }
+
+  const goToHomepage = e => {
+    e.preventDefault();
+    showLoading();
+    Router.push('/');
+  };
+
+  const message = query ? (
+    <Wrapper>
+      <MessageWrapper>
+        <Icon src="/images/check.svg" />
+        <Message>Your account has been verified successfully!</Message>
+      </MessageWrapper>
+      <Button icon="arrow-left" onClick={goToHomepage}>
+        Back to homepage
+      </Button>
+    </Wrapper>
+  ) : (
+    <MessageWrapper>
+      <Icon src="/images/x.svg" />
+      <Message>Invalid verification.</Message>
+    </MessageWrapper>
+  );
+  return <BodyWrapper norenew>{message}</BodyWrapper>;
+};
+
+Verify.getInitialProps = ({ store, query }) => {
+  if (query && query.token) {
+    store.dispatch(authUser(query.token));
+    store.dispatch(authRenew());
+    return { query };
+  }
+  return null;
+};
+
+Verify.propTypes = {
+  query: PropTypes.shape({
+    token: PropTypes.string,
+  }),
+  showLoading: PropTypes.func.isRequired,
+};
+
+Verify.defaultProps = {
+  query: null,
+};
+
+const mapDispatchToProps = dispatch => ({
+  showLoading: bindActionCreators(showPageLoading, dispatch),
+});
+
+export default withRedux(initialState, null, mapDispatchToProps)(Verify);

+ 191 - 0
client/reducers/index.js

@@ -0,0 +1,191 @@
+import { combineReducers } from 'redux';
+import * as types from '../actions/actionTypes';
+
+const initialState = {
+  list: [],
+  isShortened: false,
+  count: 10,
+  countAll: 0,
+  page: 1,
+  search: '',
+};
+
+const url = (state = initialState, action) => {
+  const { count, page, search } = action.payload || {};
+  const isSearch = typeof search !== 'undefined';
+  switch (action.type) {
+    case types.ADD_URL:
+      return { ...state, isShortened: true, list: [action.payload, ...state.list] };
+    case types.UPDATE_URL_LIST:
+      return Object.assign({}, state, count && { count }, page && { page }, isSearch && { search });
+    case types.LIST_URLS:
+      return {
+        ...state,
+        list: action.payload.list,
+        countAll: action.payload.countAll,
+        isShortened: false,
+      };
+    case types.DELETE_URL:
+      return { ...state, list: state.list.filter(item => item.id !== action.payload) };
+    case types.UNAUTH_USER:
+      return initialState;
+    default:
+      return state;
+  }
+};
+
+/* All errors */
+const errorInitialState = {
+  auth: '',
+  domain: '',
+  shortener: '',
+};
+
+const error = (state = errorInitialState, action) => {
+  switch (action.type) {
+    case types.SHORTENER_ERROR:
+      return { ...state, shortener: action.payload };
+    case types.DOMAIN_ERROR:
+      return { ...state, domain: action.payload };
+    case types.SET_DOMAIN:
+      return { ...state, domain: '' };
+    case types.SHOW_DOMAIN_INPUT:
+      return { ...state, domain: '' };
+    case types.ADD_URL:
+      return { ...state, shortener: '' };
+    case types.UPDATE_URL:
+      return { ...state, urlOptions: '' };
+    case types.AUTH_ERROR:
+      return { ...state, auth: action.payload };
+    case types.AUTH_USER:
+      return { ...state, auth: '' };
+    case types.HIDE_PAGE_LOADING:
+      return {
+        ...state,
+        auth: '',
+        shortener: '',
+        urlOptions: '',
+      };
+    default:
+      return state;
+  }
+};
+
+/* All loadings */
+const loadingInitialState = {
+  api: false,
+  domain: false,
+  shortener: false,
+  login: false,
+  page: false,
+  table: false,
+  signup: false,
+};
+
+const loading = (state = loadingInitialState, action) => {
+  switch (action.type) {
+    case types.SHOW_PAGE_LOADING:
+      return { ...state, page: true };
+    case types.HIDE_PAGE_LOADING:
+      return {
+        shortener: false,
+        login: false,
+        page: false,
+        signup: false,
+      };
+    case types.TABLE_LOADING:
+      return { ...state, table: true };
+    case types.LOGIN_LOADING:
+      return { ...state, login: true };
+    case types.SIGNUP_LOADING:
+      return { ...state, signup: true };
+    case types.SHORTENER_LOADING:
+      return { ...state, shortener: true };
+    case types.ADD_URL:
+      return { ...state, shortener: false };
+    case types.SHORTENER_ERROR:
+      return { ...state, shortener: false };
+    case types.LIST_URLS:
+      return { ...state, table: false };
+    case types.DELETE_URL:
+      return { ...state, table: false };
+    case types.AUTH_ERROR:
+      return { ...state, login: false, signup: false };
+    case types.AUTH_USER:
+      return { ...state, login: false, signup: false };
+    case types.DOMAIN_LOADING:
+      return { ...state, domain: true };
+    case types.SET_DOMAIN:
+      return { ...state, domain: false };
+    case types.DOMAIN_ERROR:
+      return { ...state, domain: false };
+    case types.API_LOADING:
+      return { ...state, api: true };
+    case types.SET_APIKEY:
+      return { ...state, api: false };
+    default:
+      return state;
+  }
+};
+
+/* User authentication */
+const authInitialState = {
+  isAuthenticated: false,
+  sentVerification: false,
+  user: '',
+  renew: false,
+};
+
+const auth = (state = authInitialState, action) => {
+  switch (action.type) {
+    case types.AUTH_USER:
+      return {
+        ...state,
+        isAuthenticated: true,
+        user: action.payload,
+        sentVerification: false,
+      };
+    case types.AUTH_RENEW:
+      return { ...state, renew: true };
+    case types.UNAUTH_USER:
+      return authInitialState;
+    case types.SENT_VERIFICATION:
+      return { ...state, sentVerification: true, user: action.payload };
+    default:
+      return state;
+  }
+};
+
+/* Settings */
+const settingsInitialState = {
+  apikey: '',
+  customDomain: '',
+  domainInput: true,
+};
+
+const settings = (state = settingsInitialState, action) => {
+  switch (action.type) {
+    case types.SET_DOMAIN:
+      return { ...state, customDomain: action.payload, domainInput: false };
+    case types.SET_APIKEY:
+      return { ...state, apikey: action.payload };
+    case types.DELETE_DOMAIN:
+      return { ...state, customDomain: '', domainInput: true };
+    case types.SHOW_DOMAIN_INPUT:
+      return { ...state, domainInput: true };
+    case types.UNAUTH_USER:
+      return settingsInitialState;
+    default:
+      return state;
+  }
+};
+
+const rootReducer = combineReducers({
+  auth,
+  error,
+  loading,
+  settings,
+  url,
+});
+
+export default rootReducer;

+ 9 - 0
client/store/index.js

@@ -0,0 +1,9 @@
+import { createStore, applyMiddleware } from 'redux';
+import { composeWithDevTools } from 'redux-devtools-extension';
+import thunk from 'redux-thunk';
+import rootReducer from '../reducers';
+
+const store = initialState =>
+  createStore(rootReducer, initialState, composeWithDevTools(applyMiddleware(thunk)));
+
+export default store;

+ 9 - 0
client/store/store.dev.js

@@ -0,0 +1,9 @@
+import { createStore, applyMiddleware } from 'redux';
+import { composeWithDevTools } from 'redux-devtools-extension';
+import thunk from 'redux-thunk';
+import rootReducer from '../reducers';
+
+const store = initialState =>
+  createStore(rootReducer, initialState, composeWithDevTools(applyMiddleware(thunk)));
+
+export default store;

+ 7 - 0
client/store/store.prod.js

@@ -0,0 +1,7 @@
+import { createStore, applyMiddleware } from 'redux';
+import thunk from 'redux-thunk';
+import rootReducer from '../reducers';
+
+const store = initialState => createStore(rootReducer, initialState, applyMiddleware(thunk));
+
+export default store;

+ 7665 - 0
package-lock.json

@@ -0,0 +1,7665 @@
+{
+  "name": "kutt",
+  "version": "1.0.0",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "@babel/code-frame": {
+      "version": "7.0.0-beta.36",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.36.tgz",
+      "integrity": "sha512-sW77BFwJ48YvQp3Gzz5xtAUiXuYOL2aMJKDwiaY3OcvdqBFurtYfOpSa4QrNyDxmOGRFSYzUpabU2m9QrlWE7w==",
+      "dev": true,
+      "requires": {
+        "chalk": "2.3.0",
+        "esutils": "2.0.2",
+        "js-tokens": "3.0.2"
+      }
+    },
+    "@babel/helper-annotate-as-pure": {
+      "version": "7.0.0-beta.39",
+      "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0-beta.39.tgz",
+      "integrity": "sha512-q+rE/5K8dTi5FYhG4iOsh11BTgEP8FvQ/zVInOezcihPUUJxpi5t9t3bi/SGooIIoYQdwSL4/bo5s6Yl4ZoMcA==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "7.0.0-beta.39"
+      },
+      "dependencies": {
+        "@babel/types": {
+          "version": "7.0.0-beta.39",
+          "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.39.tgz",
+          "integrity": "sha512-wrEe0z4kFP0KbFz8aHbPOGQal0gn+J2Iv9ZJGYbD77JN4BpavbF5l/BvLNZ0Omn665VENncoLVmQpclMbh64sQ==",
+          "dev": true,
+          "requires": {
+            "esutils": "2.0.2",
+            "lodash": "4.17.5",
+            "to-fast-properties": "2.0.0"
+          }
+        },
+        "to-fast-properties": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+          "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
+          "dev": true
+        }
+      }
+    },
+    "@babel/helper-function-name": {
+      "version": "7.0.0-beta.36",
+      "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.36.tgz",
+      "integrity": "sha512-/SGPOyifPf20iTrMN+WdlY2MbKa7/o4j7B/4IAsdOusASp2icT+Wcdjf4tjJHaXNX8Pe9bpgVxLNxhRvcf8E5w==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-get-function-arity": "7.0.0-beta.36",
+        "@babel/template": "7.0.0-beta.36",
+        "@babel/types": "7.0.0-beta.36"
+      }
+    },
+    "@babel/helper-get-function-arity": {
+      "version": "7.0.0-beta.36",
+      "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.36.tgz",
+      "integrity": "sha512-vPPcx2vsSoDbcyWr9S3nd0FM3B4hEXnt0p1oKpwa08GwK0fSRxa98MyaRGf8suk8frdQlG1P3mDrz5p/Rr3pbA==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "7.0.0-beta.36"
+      }
+    },
+    "@babel/template": {
+      "version": "7.0.0-beta.36",
+      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.0.0-beta.36.tgz",
+      "integrity": "sha512-mUBi90WRyZ9iVvlWLEdeo8gn/tROyJdjKNC4W5xJTSZL+9MS89rTJSqiaJKXIkxk/YRDL/g/8snrG/O0xl33uA==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "7.0.0-beta.36",
+        "@babel/types": "7.0.0-beta.36",
+        "babylon": "7.0.0-beta.36",
+        "lodash": "4.17.5"
+      },
+      "dependencies": {
+        "babylon": {
+          "version": "7.0.0-beta.36",
+          "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.36.tgz",
+          "integrity": "sha512-rw4YdadGwajAMMRl6a5swhQ0JCOOFyaYCfJ0AsmNBD8uBD/r4J8mux7wBaqavvFKqUKQYWOzA1Speams4YDzsQ==",
+          "dev": true
+        }
+      }
+    },
+    "@babel/traverse": {
+      "version": "7.0.0-beta.36",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.0.0-beta.36.tgz",
+      "integrity": "sha512-OTUb6iSKVR/98dGThRJ1BiyfwbuX10BVnkz89IpaerjTPRhDfMBfLsqmzxz5MiywUOW4M0Clta0o7rSxkfcuzw==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "7.0.0-beta.36",
+        "@babel/helper-function-name": "7.0.0-beta.36",
+        "@babel/types": "7.0.0-beta.36",
+        "babylon": "7.0.0-beta.36",
+        "debug": "3.1.0",
+        "globals": "11.3.0",
+        "invariant": "2.2.2",
+        "lodash": "4.17.5"
+      },
+      "dependencies": {
+        "babylon": {
+          "version": "7.0.0-beta.36",
+          "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.36.tgz",
+          "integrity": "sha512-rw4YdadGwajAMMRl6a5swhQ0JCOOFyaYCfJ0AsmNBD8uBD/r4J8mux7wBaqavvFKqUKQYWOzA1Speams4YDzsQ==",
+          "dev": true
+        },
+        "globals": {
+          "version": "11.3.0",
+          "resolved": "https://registry.npmjs.org/globals/-/globals-11.3.0.tgz",
+          "integrity": "sha512-kkpcKNlmQan9Z5ZmgqKH/SMbSmjxQ7QjyNqfXVc8VJcoBV2UEg+sxQD15GQofGRh2hfpwUb70VC31DR7Rq5Hdw==",
+          "dev": true
+        }
+      }
+    },
+    "@babel/types": {
+      "version": "7.0.0-beta.36",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.36.tgz",
+      "integrity": "sha512-PyAORDO9um9tfnrddXgmWN9e6Sq9qxraQIt5ynqBOSXKA5qvK1kUr+Q3nSzKFdzorsiK+oqcUnAFvEoKxv9D+Q==",
+      "dev": true,
+      "requires": {
+        "esutils": "2.0.2",
+        "lodash": "4.17.5",
+        "to-fast-properties": "2.0.0"
+      },
+      "dependencies": {
+        "to-fast-properties": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+          "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
+          "dev": true
+        }
+      }
+    },
+    "@types/body-parser": {
+      "version": "1.16.8",
+      "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.16.8.tgz",
+      "integrity": "sha512-BdN2PXxOFnTXFcyONPW6t0fHjz2fvRZHVMFpaS0wYr+Y8fWEaNOs4V8LEu/fpzQlMx+ahdndgTaGTwPC+J/EeA==",
+      "requires": {
+        "@types/express": "4.0.39",
+        "@types/node": "8.9.1"
+      }
+    },
+    "@types/events": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@types/events/-/events-1.1.0.tgz",
+      "integrity": "sha512-y3bR98mzYOo0pAZuiLari+cQyiKk3UXRuT45h1RjhfeCzqkjaVsfZJNaxdgtk7/3tzOm1ozLTqEqMP3VbI48jw=="
+    },
+    "@types/express": {
+      "version": "4.0.39",
+      "resolved": "https://registry.npmjs.org/@types/express/-/express-4.0.39.tgz",
+      "integrity": "sha512-dBUam7jEjyuEofigUXCtublUHknRZvcRgITlGsTbFgPvnTwtQUt2NgLakbsf+PsGo/Nupqr3IXCYsOpBpofyrA==",
+      "requires": {
+        "@types/body-parser": "1.16.8",
+        "@types/express-serve-static-core": "4.11.1",
+        "@types/serve-static": "1.13.1"
+      }
+    },
+    "@types/express-serve-static-core": {
+      "version": "4.11.1",
+      "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.11.1.tgz",
+      "integrity": "sha512-EehCl3tpuqiM8RUb+0255M8PhhSwTtLfmO7zBBdv0ay/VTd/zmrqDfQdZFsa5z/PVMbH2yCMZPXsnrImpATyIw==",
+      "requires": {
+        "@types/events": "1.1.0",
+        "@types/node": "8.9.1"
+      }
+    },
+    "@types/mime": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.0.tgz",
+      "integrity": "sha512-A2TAGbTFdBw9azHbpVd+/FkdW2T6msN1uct1O9bH3vTerEHKZhTXJUQXy+hNq1B0RagfU8U+KBdqiZpxjhOUQA=="
+    },
+    "@types/node": {
+      "version": "8.9.1",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-8.9.1.tgz",
+      "integrity": "sha512-4JFGIC1RSoFngVsT5EZcL793/uRi/OJ3ilsp9DQUr4LZOaMhNM1pPrt9TqlXOnXj3h73hl6NF31v87eQAPXYTg=="
+    },
+    "@types/serve-static": {
+      "version": "1.13.1",
+      "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.1.tgz",
+      "integrity": "sha512-jDMH+3BQPtvqZVIcsH700Dfi8Q3MIcEx16g/VdxjoqiGR/NntekB10xdBpirMKnPe9z2C5cBmL0vte0YttOr3Q==",
+      "requires": {
+        "@types/express-serve-static-core": "4.11.1",
+        "@types/mime": "2.0.0"
+      }
+    },
+    "@zeit/check-updates": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@zeit/check-updates/-/check-updates-1.1.0.tgz",
+      "integrity": "sha512-HoAzBs/w1V72BV8t+DVdqb/0Us+sx37En3mVL5u4lPMlZjESDNq5Z/ZnvDTtVg2OkarcES5gTAFzt5vWTvj0Lg==",
+      "requires": {
+        "chalk": "2.3.0",
+        "ms": "2.1.1",
+        "update-notifier": "2.3.0"
+      },
+      "dependencies": {
+        "ms": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+          "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+        }
+      }
+    },
+    "@zeit/source-map-support": {
+      "version": "0.6.2",
+      "resolved": "https://registry.npmjs.org/@zeit/source-map-support/-/source-map-support-0.6.2.tgz",
+      "integrity": "sha512-hSGbAEUxKhrE6DAWe5cPVdlRxtpjOnNdeMI5kNIHOvJxYrWUrIdIDeTbM7hpoz0R/SSWYFxKjPJRSLs74ffVIQ==",
+      "requires": {
+        "source-map": "0.6.1"
+      }
+    },
+    "abbrev": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+      "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
+    },
+    "accepts": {
+      "version": "1.3.4",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz",
+      "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=",
+      "requires": {
+        "mime-types": "2.1.17",
+        "negotiator": "0.6.1"
+      }
+    },
+    "acorn": {
+      "version": "5.4.1",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.4.1.tgz",
+      "integrity": "sha512-XLmq3H/BVvW6/GbxKryGxWORz1ebilSsUDlyC27bXhWGWAZWkGwS6FLHjOlwFXNFoWFQEO/Df4u0YYd0K3BQgQ=="
+    },
+    "acorn-dynamic-import": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz",
+      "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=",
+      "requires": {
+        "acorn": "4.0.13"
+      },
+      "dependencies": {
+        "acorn": {
+          "version": "4.0.13",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz",
+          "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c="
+        }
+      }
+    },
+    "acorn-jsx": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz",
+      "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=",
+      "dev": true,
+      "requires": {
+        "acorn": "3.3.0"
+      },
+      "dependencies": {
+        "acorn": {
+          "version": "3.3.0",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
+          "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=",
+          "dev": true
+        }
+      }
+    },
+    "ajv": {
+      "version": "5.5.2",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
+      "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
+      "requires": {
+        "co": "4.6.0",
+        "fast-deep-equal": "1.0.0",
+        "fast-json-stable-stringify": "2.0.0",
+        "json-schema-traverse": "0.3.1"
+      }
+    },
+    "ajv-keywords": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz",
+      "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I="
+    },
+    "align-text": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
+      "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
+      "requires": {
+        "kind-of": "3.2.2",
+        "longest": "1.0.1",
+        "repeat-string": "1.6.1"
+      }
+    },
+    "ansi-align": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz",
+      "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=",
+      "requires": {
+        "string-width": "2.1.1"
+      }
+    },
+    "ansi-escapes": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.0.0.tgz",
+      "integrity": "sha512-O/klc27mWNUigtv0F8NJWbLF00OcegQalkqKURWdosW08YZKi4m6CnSUSvIZG1otNJbTWhN01Hhz389DW7mvDQ==",
+      "dev": true
+    },
+    "ansi-html": {
+      "version": "0.0.7",
+      "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz",
+      "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4="
+    },
+    "ansi-regex": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+      "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
+    },
+    "ansi-styles": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz",
+      "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
+      "requires": {
+        "color-convert": "1.9.1"
+      }
+    },
+    "any-promise": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+      "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8="
+    },
+    "anymatch": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz",
+      "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==",
+      "requires": {
+        "micromatch": "2.3.11",
+        "normalize-path": "2.1.1"
+      }
+    },
+    "aproba": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+      "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
+    },
+    "argparse": {
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz",
+      "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=",
+      "dev": true,
+      "requires": {
+        "sprintf-js": "1.0.3"
+      }
+    },
+    "aria-query": {
+      "version": "0.7.1",
+      "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-0.7.1.tgz",
+      "integrity": "sha1-Jsu1r/ZBRLCoJb4YRuCxbPoAsR4=",
+      "dev": true,
+      "requires": {
+        "ast-types-flow": "0.0.7",
+        "commander": "2.14.1"
+      }
+    },
+    "arr-diff": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
+      "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
+      "requires": {
+        "arr-flatten": "1.1.0"
+      }
+    },
+    "arr-flatten": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+      "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg=="
+    },
+    "array-differ": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz",
+      "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE="
+    },
+    "array-flatten": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+      "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
+    },
+    "array-includes": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz",
+      "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=",
+      "dev": true,
+      "requires": {
+        "define-properties": "1.1.2",
+        "es-abstract": "1.10.0"
+      }
+    },
+    "array-union": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+      "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
+      "requires": {
+        "array-uniq": "1.0.3"
+      }
+    },
+    "array-uniq": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+      "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY="
+    },
+    "array-unique": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
+      "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM="
+    },
+    "arrify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
+      "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0="
+    },
+    "asap": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+      "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
+    },
+    "asn1.js": {
+      "version": "4.9.2",
+      "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.2.tgz",
+      "integrity": "sha512-b/OsSjvWEo8Pi8H0zsDd2P6Uqo2TK2pH8gNLSJtNLM2Db0v2QaAZ0pBQJXVjAn4gBuugeVDr7s63ZogpUIwWDg==",
+      "requires": {
+        "bn.js": "4.11.8",
+        "inherits": "2.0.3",
+        "minimalistic-assert": "1.0.0"
+      }
+    },
+    "assert": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz",
+      "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=",
+      "requires": {
+        "util": "0.10.3"
+      }
+    },
+    "ast-types-flow": {
+      "version": "0.0.7",
+      "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
+      "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=",
+      "dev": true
+    },
+    "async": {
+      "version": "2.6.0",
+      "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz",
+      "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==",
+      "requires": {
+        "lodash": "4.17.5"
+      }
+    },
+    "async-each": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz",
+      "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0="
+    },
+    "axios": {
+      "version": "0.17.1",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.17.1.tgz",
+      "integrity": "sha1-LY4+XQvb1zJ/kbyBT1xXZg+Bgk0=",
+      "requires": {
+        "follow-redirects": "1.4.1",
+        "is-buffer": "1.1.6"
+      }
+    },
+    "axobject-query": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-0.1.0.tgz",
+      "integrity": "sha1-YvWdvFnJ+SQnWco0mWDnov48NsA=",
+      "dev": true,
+      "requires": {
+        "ast-types-flow": "0.0.7"
+      }
+    },
+    "babel-cli": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npmjs.org/babel-cli/-/babel-cli-6.26.0.tgz",
+      "integrity": "sha1-UCq1SHTX24itALiHoGODzgPQAvE=",
+      "dev": true,
+      "requires": {
+        "babel-core": "6.26.0",
+        "babel-polyfill": "6.26.0",
+        "babel-register": "6.26.0",
+        "babel-runtime": "6.26.0",
+        "chokidar": "1.7.0",
+        "commander": "2.14.1",
+        "convert-source-map": "1.5.1",
+        "fs-readdir-recursive": "1.1.0",
+        "glob": "7.1.2",
+        "lodash": "4.17.5",
+        "output-file-sync": "1.1.2",
+        "path-is-absolute": "1.0.1",
+        "slash": "1.0.0",
+        "source-map": "0.5.7",
+        "v8flags": "2.1.1"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+          "dev": true
+        }
+      }
+    },
+    "babel-code-frame": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
+      "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
+      "requires": {
+        "chalk": "1.1.3",
+        "esutils": "2.0.2",
+        "js-tokens": "3.0.2"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "2.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
+        },
+        "chalk": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+          "requires": {
+            "ansi-styles": "2.2.1",
+            "escape-string-regexp": "1.0.5",
+            "has-ansi": "2.0.0",
+            "strip-ansi": "3.0.1",
+            "supports-color": "2.0.0"
+          }
+        },
+        "supports-color": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+          "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
+        }
+      }
+    },
+    "babel-core": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.0.tgz",
+      "integrity": "sha1-rzL3izGm/O8RnIew/Y2XU/A6C7g=",
+      "requires": {
+        "babel-code-frame": "6.26.0",
+        "babel-generator": "6.26.1",
+        "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.1",
+        "debug": "2.6.9",
+        "json5": "0.5.1",
+        "lodash": "4.17.5",
+        "minimatch": "3.0.4",
+        "path-is-absolute": "1.0.1",
+        "private": "0.1.8",
+        "slash": "1.0.0",
+        "source-map": "0.5.7"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
+        }
+      }
+    },
+    "babel-eslint": {
+      "version": "8.2.1",
+      "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-8.2.1.tgz",
+      "integrity": "sha512-RzdVOyWKQRUnLXhwLk+eKb4oyW+BykZSkpYwFhM4tnfzAG5OWfvG0w/uyzMp5XKEU0jN82+JefHr39bG2+KhRQ==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "7.0.0-beta.36",
+        "@babel/traverse": "7.0.0-beta.36",
+        "@babel/types": "7.0.0-beta.36",
+        "babylon": "7.0.0-beta.36",
+        "eslint-scope": "3.7.1",
+        "eslint-visitor-keys": "1.0.0"
+      },
+      "dependencies": {
+        "babylon": {
+          "version": "7.0.0-beta.36",
+          "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.36.tgz",
+          "integrity": "sha512-rw4YdadGwajAMMRl6a5swhQ0JCOOFyaYCfJ0AsmNBD8uBD/r4J8mux7wBaqavvFKqUKQYWOzA1Speams4YDzsQ==",
+          "dev": true
+        }
+      }
+    },
+    "babel-generator": {
+      "version": "6.26.1",
+      "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz",
+      "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==",
+      "requires": {
+        "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.5",
+        "source-map": "0.5.7",
+        "trim-right": "1.0.1"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
+        }
+      }
+    },
+    "babel-helper-builder-binary-assignment-operator-visitor": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz",
+      "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=",
+      "requires": {
+        "babel-helper-explode-assignable-expression": "6.24.1",
+        "babel-runtime": "6.26.0",
+        "babel-types": "6.26.0"
+      }
+    },
+    "babel-helper-builder-react-jsx": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npmjs.org/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.26.0.tgz",
+      "integrity": "sha1-Of+DE7dci2Xc7/HzHTg+D/KkCKA=",
+      "requires": {
+        "babel-runtime": "6.26.0",
+        "babel-types": "6.26.0",
+        "esutils": "2.0.2"
+      }
+    },
+    "babel-helper-call-delegate": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz",
+      "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=",
+      "requires": {
+        "babel-helper-hoist-variables": "6.24.1",
+        "babel-runtime": "6.26.0",
+        "babel-traverse": "6.26.0",
+        "babel-types": "6.26.0"
+      }
+    },
+    "babel-helper-define-map": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz",
+      "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=",
+      "requires": {
+        "babel-helper-function-name": "6.24.1",
+        "babel-runtime": "6.26.0",
+        "babel-types": "6.26.0",
+        "lodash": "4.17.5"
+      }
+    },
+    "babel-helper-explode-assignable-expression": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz",
+      "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=",
+      "requires": {
+        "babel-runtime": "6.26.0",
+        "babel-traverse": "6.26.0",
+        "babel-types": "6.26.0"
+      }
+    },
+    "babel-helper-function-name": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz",
+      "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=",
+      "requires": {
+        "babel-helper-get-function-arity": "6.24.1",
+        "babel-runtime": "6.26.0",
+        "babel-template": "6.26.0",
+        "babel-traverse": "6.26.0",
+        "babel-types": "6.26.0"
+      }
+    },
+    "babel-helper-get-function-arity": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz",
+      "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=",
+      "requires": {
+        "babel-runtime": "6.26.0",
+        "babel-types": "6.26.0"
+      }
+    },
+    "babel-helper-hoist-variables": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz",
+      "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=",
+      "requires": {
+        "babel-runtime": "6.26.0",
+        "babel-types": "6.26.0"
+      }
+    },
+    "babel-helper-optimise-call-expression": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz",
+      "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=",
+      "requires": {
+        "babel-runtime": "6.26.0",
+        "babel-types": "6.26.0"
+      }
+    },
+    "babel-helper-regex": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz",
+      "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=",
+      "requires": {
+        "babel-runtime": "6.26.0",
+        "babel-types": "6.26.0",
+        "lodash": "4.17.5"
+      }
+    },
+    "babel-helper-remap-async-to-generator": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz",
+      "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=",
+      "requires": {
+        "babel-helper-function-name": "6.24.1",
+        "babel-runtime": "6.26.0",
+        "babel-template": "6.26.0",
+        "babel-traverse": "6.26.0",
+        "babel-types": "6.26.0"
+      }
+    },
+    "babel-helper-replace-supers": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz",
+      "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=",
+      "requires": {
+        "babel-helper-optimise-call-expression": "6.24.1",
+        "babel-messages": "6.23.0",
+        "babel-runtime": "6.26.0",
+        "babel-template": "6.26.0",
+        "babel-traverse": "6.26.0",
+        "babel-types": "6.26.0"
+      }
+    },
+    "babel-helpers": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz",
+      "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=",
+      "requires": {
+        "babel-runtime": "6.26.0",
+        "babel-template": "6.26.0"
+      }
+    },
+    "babel-loader": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-7.1.2.tgz",
+      "integrity": "sha512-jRwlFbINAeyDStqK6Dd5YuY0k5YuzQUvlz2ZamuXrXmxav3pNqe9vfJ402+2G+OmlJSXxCOpB6Uz0INM7RQe2A==",
+      "requires": {
+        "find-cache-dir": "1.0.0",
+        "loader-utils": "1.1.0",
+        "mkdirp": "0.5.1"
+      }
+    },
+    "babel-messages": {
+      "version": "6.23.0",
+      "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz",
+      "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=",
+      "requires": {
+        "babel-runtime": "6.26.0"
+      }
+    },
+    "babel-plugin-check-es2015-constants": {
+      "version": "6.22.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz",
+      "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=",
+      "requires": {
+        "babel-runtime": "6.26.0"
+      }
+    },
+    "babel-plugin-react-require": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-react-require/-/babel-plugin-react-require-3.0.0.tgz",
+      "integrity": "sha1-Lk57RJa5OmVKHIAEInbeTk7rIOM="
+    },
+    "babel-plugin-styled-components": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-1.5.0.tgz",
+      "integrity": "sha1-RUnyihn7BRcKqe5Cm03g6sLSQB0=",
+      "dev": true,
+      "requires": {
+        "@babel/helper-annotate-as-pure": "7.0.0-beta.39",
+        "babel-types": "6.26.0",
+        "stylis": "3.4.5"
+      }
+    },
+    "babel-plugin-syntax-async-functions": {
+      "version": "6.13.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz",
+      "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU="
+    },
+    "babel-plugin-syntax-class-properties": {
+      "version": "6.13.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz",
+      "integrity": "sha1-1+sjt5oxf4VDlixQW4J8fWysJ94="
+    },
+    "babel-plugin-syntax-dynamic-import": {
+      "version": "6.18.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz",
+      "integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo="
+    },
+    "babel-plugin-syntax-exponentiation-operator": {
+      "version": "6.13.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz",
+      "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4="
+    },
+    "babel-plugin-syntax-flow": {
+      "version": "6.18.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz",
+      "integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0="
+    },
+    "babel-plugin-syntax-jsx": {
+      "version": "6.18.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
+      "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY="
+    },
+    "babel-plugin-syntax-object-rest-spread": {
+      "version": "6.13.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz",
+      "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U="
+    },
+    "babel-plugin-syntax-trailing-function-commas": {
+      "version": "6.22.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz",
+      "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM="
+    },
+    "babel-plugin-transform-async-to-generator": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz",
+      "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=",
+      "requires": {
+        "babel-helper-remap-async-to-generator": "6.24.1",
+        "babel-plugin-syntax-async-functions": "6.13.0",
+        "babel-runtime": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-class-properties": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz",
+      "integrity": "sha1-anl2PqYdM9NvN7YRqp3vgagbRqw=",
+      "requires": {
+        "babel-helper-function-name": "6.24.1",
+        "babel-plugin-syntax-class-properties": "6.13.0",
+        "babel-runtime": "6.26.0",
+        "babel-template": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-es2015-arrow-functions": {
+      "version": "6.22.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz",
+      "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=",
+      "requires": {
+        "babel-runtime": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-es2015-block-scoped-functions": {
+      "version": "6.22.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz",
+      "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=",
+      "requires": {
+        "babel-runtime": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-es2015-block-scoping": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz",
+      "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=",
+      "requires": {
+        "babel-runtime": "6.26.0",
+        "babel-template": "6.26.0",
+        "babel-traverse": "6.26.0",
+        "babel-types": "6.26.0",
+        "lodash": "4.17.5"
+      }
+    },
+    "babel-plugin-transform-es2015-classes": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz",
+      "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=",
+      "requires": {
+        "babel-helper-define-map": "6.26.0",
+        "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.26.0",
+        "babel-template": "6.26.0",
+        "babel-traverse": "6.26.0",
+        "babel-types": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-es2015-computed-properties": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz",
+      "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=",
+      "requires": {
+        "babel-runtime": "6.26.0",
+        "babel-template": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-es2015-destructuring": {
+      "version": "6.23.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz",
+      "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=",
+      "requires": {
+        "babel-runtime": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-es2015-duplicate-keys": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz",
+      "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=",
+      "requires": {
+        "babel-runtime": "6.26.0",
+        "babel-types": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-es2015-for-of": {
+      "version": "6.23.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz",
+      "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=",
+      "requires": {
+        "babel-runtime": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-es2015-function-name": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz",
+      "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=",
+      "requires": {
+        "babel-helper-function-name": "6.24.1",
+        "babel-runtime": "6.26.0",
+        "babel-types": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-es2015-literals": {
+      "version": "6.22.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz",
+      "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=",
+      "requires": {
+        "babel-runtime": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-es2015-modules-amd": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz",
+      "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=",
+      "requires": {
+        "babel-plugin-transform-es2015-modules-commonjs": "6.26.0",
+        "babel-runtime": "6.26.0",
+        "babel-template": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-es2015-modules-commonjs": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz",
+      "integrity": "sha1-DYOUApt9xqvhqX7xgeAHWN0uXYo=",
+      "requires": {
+        "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": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz",
+      "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=",
+      "requires": {
+        "babel-helper-hoist-variables": "6.24.1",
+        "babel-runtime": "6.26.0",
+        "babel-template": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-es2015-modules-umd": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz",
+      "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=",
+      "requires": {
+        "babel-plugin-transform-es2015-modules-amd": "6.24.1",
+        "babel-runtime": "6.26.0",
+        "babel-template": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-es2015-object-super": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz",
+      "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=",
+      "requires": {
+        "babel-helper-replace-supers": "6.24.1",
+        "babel-runtime": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-es2015-parameters": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz",
+      "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=",
+      "requires": {
+        "babel-helper-call-delegate": "6.24.1",
+        "babel-helper-get-function-arity": "6.24.1",
+        "babel-runtime": "6.26.0",
+        "babel-template": "6.26.0",
+        "babel-traverse": "6.26.0",
+        "babel-types": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-es2015-shorthand-properties": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz",
+      "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=",
+      "requires": {
+        "babel-runtime": "6.26.0",
+        "babel-types": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-es2015-spread": {
+      "version": "6.22.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz",
+      "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=",
+      "requires": {
+        "babel-runtime": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-es2015-sticky-regex": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz",
+      "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=",
+      "requires": {
+        "babel-helper-regex": "6.26.0",
+        "babel-runtime": "6.26.0",
+        "babel-types": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-es2015-template-literals": {
+      "version": "6.22.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz",
+      "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=",
+      "requires": {
+        "babel-runtime": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-es2015-typeof-symbol": {
+      "version": "6.23.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz",
+      "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=",
+      "requires": {
+        "babel-runtime": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-es2015-unicode-regex": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz",
+      "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=",
+      "requires": {
+        "babel-helper-regex": "6.26.0",
+        "babel-runtime": "6.26.0",
+        "regexpu-core": "2.0.0"
+      }
+    },
+    "babel-plugin-transform-exponentiation-operator": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz",
+      "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=",
+      "requires": {
+        "babel-helper-builder-binary-assignment-operator-visitor": "6.24.1",
+        "babel-plugin-syntax-exponentiation-operator": "6.13.0",
+        "babel-runtime": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-flow-strip-types": {
+      "version": "6.22.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz",
+      "integrity": "sha1-hMtnKTXUNxT9wyvOhFaNh0Qc988=",
+      "requires": {
+        "babel-plugin-syntax-flow": "6.18.0",
+        "babel-runtime": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-object-rest-spread": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz",
+      "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=",
+      "requires": {
+        "babel-plugin-syntax-object-rest-spread": "6.13.0",
+        "babel-runtime": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-react-display-name": {
+      "version": "6.25.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz",
+      "integrity": "sha1-Z+K/Hx6ck6sI25Z5LgU5K/LMKNE=",
+      "requires": {
+        "babel-runtime": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-react-jsx": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz",
+      "integrity": "sha1-hAoCjn30YN/DotKfDA2R9jduZqM=",
+      "requires": {
+        "babel-helper-builder-react-jsx": "6.26.0",
+        "babel-plugin-syntax-jsx": "6.18.0",
+        "babel-runtime": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-react-jsx-self": {
+      "version": "6.22.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-self/-/babel-plugin-transform-react-jsx-self-6.22.0.tgz",
+      "integrity": "sha1-322AqdomEqEh5t3XVYvL7PBuY24=",
+      "requires": {
+        "babel-plugin-syntax-jsx": "6.18.0",
+        "babel-runtime": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-react-jsx-source": {
+      "version": "6.22.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.22.0.tgz",
+      "integrity": "sha1-ZqwSFT9c0tF7PBkmj0vwGX9E7NY=",
+      "requires": {
+        "babel-plugin-syntax-jsx": "6.18.0",
+        "babel-runtime": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-react-remove-prop-types": {
+      "version": "0.4.8",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.8.tgz",
+      "integrity": "sha1-Cv8EvB1lZOxJzyO8/7mcEYgZWNs=",
+      "requires": {
+        "babel-traverse": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-regenerator": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz",
+      "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=",
+      "requires": {
+        "regenerator-transform": "0.10.1"
+      }
+    },
+    "babel-plugin-transform-runtime": {
+      "version": "6.23.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.23.0.tgz",
+      "integrity": "sha1-iEkNRGUC6puOfvsP4J7E2ZR5se4=",
+      "requires": {
+        "babel-runtime": "6.26.0"
+      }
+    },
+    "babel-plugin-transform-strict-mode": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz",
+      "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=",
+      "requires": {
+        "babel-runtime": "6.26.0",
+        "babel-types": "6.26.0"
+      }
+    },
+    "babel-polyfill": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz",
+      "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=",
+      "dev": true,
+      "requires": {
+        "babel-runtime": "6.26.0",
+        "core-js": "2.5.3",
+        "regenerator-runtime": "0.10.5"
+      },
+      "dependencies": {
+        "regenerator-runtime": {
+          "version": "0.10.5",
+          "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz",
+          "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=",
+          "dev": true
+        }
+      }
+    },
+    "babel-preset-env": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.6.0.tgz",
+      "integrity": "sha512-OVgtQRuOZKckrILgMA5rvctvFZPv72Gua9Rt006AiPoB0DJKGN07UmaQA+qRrYgK71MVct8fFhT0EyNWYorVew==",
+      "requires": {
+        "babel-plugin-check-es2015-constants": "6.22.0",
+        "babel-plugin-syntax-trailing-function-commas": "6.22.0",
+        "babel-plugin-transform-async-to-generator": "6.24.1",
+        "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.26.0",
+        "babel-plugin-transform-es2015-classes": "6.24.1",
+        "babel-plugin-transform-es2015-computed-properties": "6.24.1",
+        "babel-plugin-transform-es2015-destructuring": "6.23.0",
+        "babel-plugin-transform-es2015-duplicate-keys": "6.24.1",
+        "babel-plugin-transform-es2015-for-of": "6.23.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.26.0",
+        "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.23.0",
+        "babel-plugin-transform-es2015-unicode-regex": "6.24.1",
+        "babel-plugin-transform-exponentiation-operator": "6.24.1",
+        "babel-plugin-transform-regenerator": "6.26.0",
+        "browserslist": "2.11.3",
+        "invariant": "2.2.2",
+        "semver": "5.5.0"
+      }
+    },
+    "babel-preset-flow": {
+      "version": "6.23.0",
+      "resolved": "https://registry.npmjs.org/babel-preset-flow/-/babel-preset-flow-6.23.0.tgz",
+      "integrity": "sha1-5xIYiHCFrpoktb5Baa/7WZgWxJ0=",
+      "requires": {
+        "babel-plugin-transform-flow-strip-types": "6.22.0"
+      }
+    },
+    "babel-preset-react": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-preset-react/-/babel-preset-react-6.24.1.tgz",
+      "integrity": "sha1-umnfrqRfw+xjm2pOzqbhdwLJE4A=",
+      "requires": {
+        "babel-plugin-syntax-jsx": "6.18.0",
+        "babel-plugin-transform-react-display-name": "6.25.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-register": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz",
+      "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=",
+      "requires": {
+        "babel-core": "6.26.0",
+        "babel-runtime": "6.26.0",
+        "core-js": "2.5.3",
+        "home-or-tmp": "2.0.0",
+        "lodash": "4.17.5",
+        "mkdirp": "0.5.1",
+        "source-map-support": "0.4.18"
+      }
+    },
+    "babel-runtime": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
+      "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
+      "requires": {
+        "core-js": "2.5.3",
+        "regenerator-runtime": "0.11.1"
+      }
+    },
+    "babel-template": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz",
+      "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=",
+      "requires": {
+        "babel-runtime": "6.26.0",
+        "babel-traverse": "6.26.0",
+        "babel-types": "6.26.0",
+        "babylon": "6.18.0",
+        "lodash": "4.17.5"
+      }
+    },
+    "babel-traverse": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz",
+      "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=",
+      "requires": {
+        "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.9",
+        "globals": "9.18.0",
+        "invariant": "2.2.2",
+        "lodash": "4.17.5"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "requires": {
+            "ms": "2.0.0"
+          }
+        }
+      }
+    },
+    "babel-types": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz",
+      "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
+      "requires": {
+        "babel-runtime": "6.26.0",
+        "esutils": "2.0.2",
+        "lodash": "4.17.5",
+        "to-fast-properties": "1.0.3"
+      }
+    },
+    "babylon": {
+      "version": "6.18.0",
+      "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
+      "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ=="
+    },
+    "balanced-match": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
+    },
+    "base64-js": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz",
+      "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw=="
+    },
+    "base64url": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz",
+      "integrity": "sha1-6sFuA+oUOO/5Qj1puqNiYu0fcLs="
+    },
+    "basic-auth": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.0.tgz",
+      "integrity": "sha1-AV2z81PgLlY3d1X5YnQuiYHnu7o=",
+      "requires": {
+        "safe-buffer": "5.1.1"
+      }
+    },
+    "bcryptjs": {
+      "version": "2.4.3",
+      "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
+      "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms="
+    },
+    "big.js": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz",
+      "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q=="
+    },
+    "binary": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz",
+      "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=",
+      "requires": {
+        "buffers": "0.1.1",
+        "chainsaw": "0.1.0"
+      }
+    },
+    "binary-extensions": {
+      "version": "1.11.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz",
+      "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU="
+    },
+    "bluebird": {
+      "version": "3.5.1",
+      "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
+      "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA=="
+    },
+    "bn.js": {
+      "version": "4.11.8",
+      "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
+      "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA=="
+    },
+    "body-parser": {
+      "version": "1.18.2",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz",
+      "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=",
+      "requires": {
+        "bytes": "3.0.0",
+        "content-type": "1.0.4",
+        "debug": "2.6.9",
+        "depd": "1.1.2",
+        "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"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "requires": {
+            "ms": "2.0.0"
+          }
+        }
+      }
+    },
+    "boxen": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz",
+      "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==",
+      "requires": {
+        "ansi-align": "2.0.0",
+        "camelcase": "4.1.0",
+        "chalk": "2.3.0",
+        "cli-boxes": "1.0.0",
+        "string-width": "2.1.1",
+        "term-size": "1.2.0",
+        "widest-line": "2.0.0"
+      }
+    },
+    "brace-expansion": {
+      "version": "1.1.9",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.9.tgz",
+      "integrity": "sha512-/+o3o6OV1cm3WKrO7U4wykU+ZICE6HiMEuravc2d03NIuM/VaRn5iMcoQ7NyxFXjvpmRICP2EER0YOnh4yIapA==",
+      "requires": {
+        "balanced-match": "1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "braces": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
+      "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
+      "requires": {
+        "expand-range": "1.8.2",
+        "preserve": "0.2.0",
+        "repeat-element": "1.1.2"
+      }
+    },
+    "brorand": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+      "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8="
+    },
+    "browserify-aes": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.1.1.tgz",
+      "integrity": "sha512-UGnTYAnB2a3YuYKIRy1/4FB2HdM866E0qC46JXvVTYKlBlZlnvfpSfY6OKfXZAkv70eJ2a1SqzpAo5CRhZGDFg==",
+      "requires": {
+        "buffer-xor": "1.0.3",
+        "cipher-base": "1.0.4",
+        "create-hash": "1.1.3",
+        "evp_bytestokey": "1.0.3",
+        "inherits": "2.0.3",
+        "safe-buffer": "5.1.1"
+      }
+    },
+    "browserify-cipher": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz",
+      "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=",
+      "requires": {
+        "browserify-aes": "1.1.1",
+        "browserify-des": "1.0.0",
+        "evp_bytestokey": "1.0.3"
+      }
+    },
+    "browserify-des": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz",
+      "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=",
+      "requires": {
+        "cipher-base": "1.0.4",
+        "des.js": "1.0.0",
+        "inherits": "2.0.3"
+      }
+    },
+    "browserify-rsa": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
+      "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
+      "requires": {
+        "bn.js": "4.11.8",
+        "randombytes": "2.0.6"
+      }
+    },
+    "browserify-sign": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz",
+      "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=",
+      "requires": {
+        "bn.js": "4.11.8",
+        "browserify-rsa": "4.0.1",
+        "create-hash": "1.1.3",
+        "create-hmac": "1.1.6",
+        "elliptic": "6.4.0",
+        "inherits": "2.0.3",
+        "parse-asn1": "5.1.0"
+      }
+    },
+    "browserify-zlib": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
+      "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
+      "requires": {
+        "pako": "1.0.6"
+      }
+    },
+    "browserslist": {
+      "version": "2.11.3",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.11.3.tgz",
+      "integrity": "sha512-yWu5cXT7Av6mVwzWc8lMsJMHWn4xyjSuGYi4IozbVTLUOEYPSagUB8kiMDUHA1fS3zjr8nkxkn9jdvug4BBRmA==",
+      "requires": {
+        "caniuse-lite": "1.0.30000805",
+        "electron-to-chromium": "1.3.33"
+      }
+    },
+    "buffer": {
+      "version": "4.9.1",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
+      "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
+      "requires": {
+        "base64-js": "1.2.1",
+        "ieee754": "1.1.8",
+        "isarray": "1.0.0"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+        }
+      }
+    },
+    "buffer-alloc": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.1.0.tgz",
+      "integrity": "sha1-BVFNM78WVtNUDGhPZbEgLpDsowM=",
+      "requires": {
+        "buffer-alloc-unsafe": "0.1.1",
+        "buffer-fill": "0.1.0"
+      }
+    },
+    "buffer-alloc-unsafe": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-0.1.1.tgz",
+      "integrity": "sha1-/+H2dVHdBVc33iUzN7/oU9+rGmo="
+    },
+    "buffer-equal-constant-time": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+      "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
+    },
+    "buffer-fill": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-0.1.0.tgz",
+      "integrity": "sha1-ypRw6NTRuXf9dUP04qtqfclRAag="
+    },
+    "buffer-xor": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
+      "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk="
+    },
+    "buffers": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz",
+      "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s="
+    },
+    "builtin-modules": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+      "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8="
+    },
+    "builtin-status-codes": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
+      "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug="
+    },
+    "bytes": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+      "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
+    },
+    "cacache": {
+      "version": "10.0.2",
+      "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.2.tgz",
+      "integrity": "sha512-dljb7dk1jqO5ogE+dRpoR9tpHYv5xz9vPSNunh1+0wRuNdYxmzp9WmsyokgW/DUF1FDRVA/TMsmxt027R8djbQ==",
+      "requires": {
+        "bluebird": "3.5.1",
+        "chownr": "1.0.1",
+        "glob": "7.1.2",
+        "graceful-fs": "4.1.11",
+        "lru-cache": "4.1.1",
+        "mississippi": "1.3.1",
+        "mkdirp": "0.5.1",
+        "move-concurrently": "1.0.1",
+        "promise-inflight": "1.0.1",
+        "rimraf": "2.6.2",
+        "ssri": "5.2.1",
+        "unique-filename": "1.1.0",
+        "y18n": "3.2.1"
+      },
+      "dependencies": {
+        "graceful-fs": {
+          "version": "4.1.11",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+          "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
+        }
+      }
+    },
+    "caller-path": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz",
+      "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=",
+      "dev": true,
+      "requires": {
+        "callsites": "0.2.0"
+      }
+    },
+    "callsites": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz",
+      "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=",
+      "dev": true
+    },
+    "camelcase": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
+      "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0="
+    },
+    "camelize": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz",
+      "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs="
+    },
+    "caniuse-lite": {
+      "version": "1.0.30000805",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000805.tgz",
+      "integrity": "sha512-g04TTapYF47M05D8dshTSTfuAHTaAyfYUsD926QTcNvnqitFJb277L3y3RdDbcrjxmqzJBEqorkc0AykqMv8Ig=="
+    },
+    "capture-stack-trace": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz",
+      "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0="
+    },
+    "case-sensitive-paths-webpack-plugin": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.1.1.tgz",
+      "integrity": "sha1-PSnO2MHxJL9vU4Rvs/WJRzH9yQk="
+    },
+    "center-align": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
+      "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=",
+      "requires": {
+        "align-text": "0.1.4",
+        "lazy-cache": "1.0.4"
+      }
+    },
+    "chain-function": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/chain-function/-/chain-function-1.0.0.tgz",
+      "integrity": "sha1-DUqzfn4Y6tC9xHuSB2QRjOWHM9w="
+    },
+    "chainsaw": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz",
+      "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=",
+      "requires": {
+        "traverse": "0.3.9"
+      }
+    },
+    "chalk": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz",
+      "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==",
+      "requires": {
+        "ansi-styles": "3.2.0",
+        "escape-string-regexp": "1.0.5",
+        "supports-color": "4.5.0"
+      }
+    },
+    "chardet": {
+      "version": "0.4.2",
+      "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz",
+      "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=",
+      "dev": true
+    },
+    "charenc": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
+      "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc="
+    },
+    "chokidar": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz",
+      "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=",
+      "requires": {
+        "anymatch": "1.3.2",
+        "async-each": "1.0.1",
+        "glob-parent": "2.0.0",
+        "inherits": "2.0.3",
+        "is-binary-path": "1.0.1",
+        "is-glob": "2.0.1",
+        "path-is-absolute": "1.0.1",
+        "readdirp": "2.1.0"
+      }
+    },
+    "chownr": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz",
+      "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE="
+    },
+    "ci-info": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.1.2.tgz",
+      "integrity": "sha512-uTGIPNx/nSpBdsF6xnseRXLLtfr9VLqkz8ZqHXr3Y7b6SftyRxBGjwMtJj1OhNbmlc1wZzLNAlAcvyIiE8a6ZA==",
+      "dev": true
+    },
+    "cipher-base": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
+      "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
+      "requires": {
+        "inherits": "2.0.3",
+        "safe-buffer": "5.1.1"
+      }
+    },
+    "circular-json": {
+      "version": "0.3.3",
+      "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
+      "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==",
+      "dev": true
+    },
+    "classnames": {
+      "version": "2.2.5",
+      "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.5.tgz",
+      "integrity": "sha1-+zgB1FNGdknvNgPH1hoCvRKb3m0="
+    },
+    "cli-boxes": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz",
+      "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM="
+    },
+    "cli-cursor": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
+      "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
+      "dev": true,
+      "requires": {
+        "restore-cursor": "2.0.0"
+      }
+    },
+    "cli-width": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
+      "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
+      "dev": true
+    },
+    "cliui": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
+      "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
+      "requires": {
+        "center-align": "0.1.3",
+        "right-align": "0.1.3",
+        "wordwrap": "0.0.2"
+      }
+    },
+    "co": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+      "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
+    },
+    "code-point-at": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+      "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
+    },
+    "color-convert": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
+      "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
+      "requires": {
+        "color-name": "1.1.3"
+      }
+    },
+    "color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
+    },
+    "colors": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
+      "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM="
+    },
+    "commander": {
+      "version": "2.14.1",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.14.1.tgz",
+      "integrity": "sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw=="
+    },
+    "commondir": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
+      "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs="
+    },
+    "concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+    },
+    "concat-stream": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz",
+      "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=",
+      "requires": {
+        "inherits": "2.0.3",
+        "readable-stream": "2.3.3",
+        "typedarray": "0.0.6"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+        },
+        "readable-stream": {
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
+          "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
+          "requires": {
+            "core-util-is": "1.0.2",
+            "inherits": "2.0.3",
+            "isarray": "1.0.0",
+            "process-nextick-args": "1.0.7",
+            "safe-buffer": "5.1.1",
+            "string_decoder": "1.0.3",
+            "util-deprecate": "1.0.2"
+          }
+        },
+        "string_decoder": {
+          "version": "1.0.3",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
+          "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
+          "requires": {
+            "safe-buffer": "5.1.1"
+          }
+        }
+      }
+    },
+    "configstore": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.1.tgz",
+      "integrity": "sha512-5oNkD/L++l0O6xGXxb1EWS7SivtjfGQlRyxJsYgE0Z495/L81e2h4/d3r969hoPXuFItzNOKMtsXgYG4c7dYvw==",
+      "requires": {
+        "dot-prop": "4.2.0",
+        "graceful-fs": "4.1.11",
+        "make-dir": "1.1.0",
+        "unique-string": "1.0.0",
+        "write-file-atomic": "2.3.0",
+        "xdg-basedir": "3.0.0"
+      },
+      "dependencies": {
+        "graceful-fs": {
+          "version": "4.1.11",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+          "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
+        }
+      }
+    },
+    "console-browserify": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
+      "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=",
+      "requires": {
+        "date-now": "0.1.4"
+      }
+    },
+    "constants-browserify": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
+      "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U="
+    },
+    "contains-path": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
+      "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=",
+      "dev": true
+    },
+    "content-disposition": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
+      "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
+    },
+    "content-security-policy-builder": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/content-security-policy-builder/-/content-security-policy-builder-2.0.0.tgz",
+      "integrity": "sha512-j+Nhmj1yfZAikJLImCvPJFE29x/UuBi+/MWqggGGc515JKaZrjuei2RhULJmy0MsstW3E3htl002bwmBNMKr7w=="
+    },
+    "content-type": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+      "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
+    },
+    "convert-source-map": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz",
+      "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU="
+    },
+    "cookie": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
+      "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
+    },
+    "cookie-parser": {
+      "version": "1.4.3",
+      "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.3.tgz",
+      "integrity": "sha1-D+MfoZ0AC5X0qt8fU/3CuKIDuqU=",
+      "requires": {
+        "cookie": "0.3.1",
+        "cookie-signature": "1.0.6"
+      }
+    },
+    "cookie-signature": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+      "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
+    },
+    "copy-concurrently": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz",
+      "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==",
+      "requires": {
+        "aproba": "1.2.0",
+        "fs-write-stream-atomic": "1.0.10",
+        "iferr": "0.1.5",
+        "mkdirp": "0.5.1",
+        "rimraf": "2.6.2",
+        "run-queue": "1.0.3"
+      }
+    },
+    "copy-to-clipboard": {
+      "version": "3.0.8",
+      "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.0.8.tgz",
+      "integrity": "sha512-c3GdeY8qxCHGezVb1EFQfHYK/8NZRemgcTIzPq7PuxjHAf/raKibn2QdhHPb/y6q74PMgH6yizaDZlRmw6QyKw==",
+      "requires": {
+        "toggle-selection": "1.0.6"
+      }
+    },
+    "core-js": {
+      "version": "2.5.3",
+      "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.3.tgz",
+      "integrity": "sha1-isw4NFgk8W2DZbfJtCWRaOjtYD4="
+    },
+    "core-util-is": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+    },
+    "cosmiconfig": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-4.0.0.tgz",
+      "integrity": "sha512-6e5vDdrXZD+t5v0L8CrurPeybg4Fmf+FCSYxXKYVAqLUtyCSbuyqE059d0kDthTNRzKVjL7QMgNpEUlsoYH3iQ==",
+      "dev": true,
+      "requires": {
+        "is-directory": "0.3.1",
+        "js-yaml": "3.10.0",
+        "parse-json": "4.0.0",
+        "require-from-string": "2.0.1"
+      },
+      "dependencies": {
+        "parse-json": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+          "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+          "dev": true,
+          "requires": {
+            "error-ex": "1.3.1",
+            "json-parse-better-errors": "1.0.1"
+          }
+        }
+      }
+    },
+    "create-ecdh": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz",
+      "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=",
+      "requires": {
+        "bn.js": "4.11.8",
+        "elliptic": "6.4.0"
+      }
+    },
+    "create-error-class": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz",
+      "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=",
+      "requires": {
+        "capture-stack-trace": "1.0.0"
+      }
+    },
+    "create-hash": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz",
+      "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=",
+      "requires": {
+        "cipher-base": "1.0.4",
+        "inherits": "2.0.3",
+        "ripemd160": "2.0.1",
+        "sha.js": "2.4.10"
+      }
+    },
+    "create-hmac": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz",
+      "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=",
+      "requires": {
+        "cipher-base": "1.0.4",
+        "create-hash": "1.1.3",
+        "inherits": "2.0.3",
+        "ripemd160": "2.0.1",
+        "safe-buffer": "5.1.1",
+        "sha.js": "2.4.10"
+      }
+    },
+    "cross-spawn": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
+      "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
+      "requires": {
+        "lru-cache": "4.1.1",
+        "shebang-command": "1.2.0",
+        "which": "1.3.0"
+      }
+    },
+    "crypt": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
+      "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs="
+    },
+    "crypto-browserify": {
+      "version": "3.12.0",
+      "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
+      "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
+      "requires": {
+        "browserify-cipher": "1.0.0",
+        "browserify-sign": "4.0.4",
+        "create-ecdh": "4.0.0",
+        "create-hash": "1.1.3",
+        "create-hmac": "1.1.6",
+        "diffie-hellman": "5.0.2",
+        "inherits": "2.0.3",
+        "pbkdf2": "3.0.14",
+        "public-encrypt": "4.0.0",
+        "randombytes": "2.0.6",
+        "randomfill": "1.0.3"
+      }
+    },
+    "crypto-random-string": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",
+      "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4="
+    },
+    "css-color-keywords": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
+      "integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU="
+    },
+    "css-to-react-native": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-2.1.1.tgz",
+      "integrity": "sha512-TrP3FRQ1AOhuElnUD4/I/Fd4HuSvnUH2KOmF0aYLjxbAX2kTd/WRUW3TuGkrsJwAGxNyYx8e/oaMSEKQKarrGQ==",
+      "requires": {
+        "css-color-keywords": "1.0.0",
+        "fbjs": "0.8.16",
+        "postcss-value-parser": "3.3.0"
+      }
+    },
+    "cyclist": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz",
+      "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA="
+    },
+    "d": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz",
+      "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=",
+      "requires": {
+        "es5-ext": "0.10.38"
+      }
+    },
+    "d3-array": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.1.tgz",
+      "integrity": "sha512-CyINJQ0SOUHojDdFDH4JEM0552vCR1utGyLHegJHyYH0JyCpSeTPxi4OBqHMA2jJZq4NH782LtaJWBImqI/HBw=="
+    },
+    "d3-collection": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.4.tgz",
+      "integrity": "sha1-NC39EoN8kJdPM/HMCnha6lcNzcI="
+    },
+    "d3-color": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.0.3.tgz",
+      "integrity": "sha1-vHZD/KjlOoNH4vva/6I2eWtYUJs="
+    },
+    "d3-format": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.2.2.tgz",
+      "integrity": "sha512-zH9CfF/3C8zUI47nsiKfD0+AGDEuM8LwBIP7pBVpyR4l/sKkZqITmMtxRp04rwBrlshIZ17XeFAaovN3++wzkw=="
+    },
+    "d3-interpolate": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.1.6.tgz",
+      "integrity": "sha512-mOnv5a+pZzkNIHtw/V6I+w9Lqm9L5bG3OTXPM5A+QO0yyVMQ4W1uZhR+VOJmazaOZXri2ppbiZ5BUNWT0pFM9A==",
+      "requires": {
+        "d3-color": "1.0.3"
+      }
+    },
+    "d3-path": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.5.tgz",
+      "integrity": "sha1-JB6xhJvZ6egCHA0KeZ+KDo5EF2Q="
+    },
+    "d3-scale": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-1.0.6.tgz",
+      "integrity": "sha1-vOGdqA06DPQiyVQ64zIghiILNO0=",
+      "requires": {
+        "d3-array": "1.2.1",
+        "d3-collection": "1.0.4",
+        "d3-color": "1.0.3",
+        "d3-format": "1.2.2",
+        "d3-interpolate": "1.1.6",
+        "d3-time": "1.0.8",
+        "d3-time-format": "2.1.1"
+      }
+    },
+    "d3-shape": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.2.0.tgz",
+      "integrity": "sha1-RdAVOPBkuv0F6j1tLLdI/YxB93c=",
+      "requires": {
+        "d3-path": "1.0.5"
+      }
+    },
+    "d3-time": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.0.8.tgz",
+      "integrity": "sha512-YRZkNhphZh3KcnBfitvF3c6E0JOFGikHZ4YqD+Lzv83ZHn1/u6yGenRU1m+KAk9J1GnZMnKcrtfvSktlA1DXNQ=="
+    },
+    "d3-time-format": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.1.1.tgz",
+      "integrity": "sha512-8kAkymq2WMfzW7e+s/IUNAtN/y3gZXGRrdGfo6R8NKPAA85UBTxZg5E61bR6nLwjPjj4d3zywSQe1CkYLPFyrw==",
+      "requires": {
+        "d3-time": "1.0.8"
+      }
+    },
+    "damerau-levenshtein": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz",
+      "integrity": "sha1-AxkcQyy27qFou3fzpV/9zLiXhRQ=",
+      "dev": true
+    },
+    "dasherize": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/dasherize/-/dasherize-2.0.0.tgz",
+      "integrity": "sha1-bYCcnNDPe7iVLYD8hPoT1H3bEwg="
+    },
+    "date-fns": {
+      "version": "1.29.0",
+      "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.29.0.tgz",
+      "integrity": "sha512-lbTXWZ6M20cWH8N9S6afb0SBm6tMk+uUg6z3MqHPKE9atmsY3kJkTm8vKe93izJ2B2+q5MV990sM2CHgtAZaOw=="
+    },
+    "date-now": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
+      "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs="
+    },
+    "debug": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+      "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+      "requires": {
+        "ms": "2.0.0"
+      }
+    },
+    "decamelize": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
+    },
+    "deep-extend": {
+      "version": "0.4.2",
+      "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz",
+      "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8="
+    },
+    "deep-is": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+      "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+      "dev": true
+    },
+    "define-properties": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz",
+      "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=",
+      "requires": {
+        "foreach": "2.0.5",
+        "object-keys": "1.0.11"
+      }
+    },
+    "del": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz",
+      "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=",
+      "requires": {
+        "globby": "6.1.0",
+        "is-path-cwd": "1.0.0",
+        "is-path-in-cwd": "1.0.0",
+        "p-map": "1.2.0",
+        "pify": "3.0.0",
+        "rimraf": "2.6.2"
+      }
+    },
+    "depd": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+      "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
+    },
+    "des.js": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz",
+      "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=",
+      "requires": {
+        "inherits": "2.0.3",
+        "minimalistic-assert": "1.0.0"
+      }
+    },
+    "destroy": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+      "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
+    },
+    "detect-indent": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz",
+      "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=",
+      "requires": {
+        "repeating": "2.0.1"
+      }
+    },
+    "diffie-hellman": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz",
+      "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=",
+      "requires": {
+        "bn.js": "4.11.8",
+        "miller-rabin": "4.0.1",
+        "randombytes": "2.0.6"
+      }
+    },
+    "dns-prefetch-control": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/dns-prefetch-control/-/dns-prefetch-control-0.1.0.tgz",
+      "integrity": "sha1-YN20V3dOF48flBXwyrsOhbCzALI="
+    },
+    "doctrine": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+      "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+      "dev": true,
+      "requires": {
+        "esutils": "2.0.2"
+      }
+    },
+    "dom-helpers": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.3.1.tgz",
+      "integrity": "sha512-2Sm+JaYn74OiTM2wHvxJOo3roiq/h25Yi69Fqk269cNUwIXsCvATB6CRSFC9Am/20G2b28hGv/+7NiWydIrPvg=="
+    },
+    "dom-walk": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz",
+      "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg="
+    },
+    "domain-browser": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
+      "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA=="
+    },
+    "dont-sniff-mimetype": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/dont-sniff-mimetype/-/dont-sniff-mimetype-1.0.0.tgz",
+      "integrity": "sha1-WTKJDcn04vGeXrAqIAJuXl78j1g="
+    },
+    "dot-prop": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz",
+      "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==",
+      "requires": {
+        "is-obj": "1.0.1"
+      }
+    },
+    "duplexer3": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
+      "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
+    },
+    "duplexify": {
+      "version": "3.5.3",
+      "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.3.tgz",
+      "integrity": "sha512-g8ID9OroF9hKt2POf8YLayy+9594PzmM3scI00/uBXocX3TWNgoB67hjzkFe9ITAbQOne/lLdBxHXvYUM4ZgGA==",
+      "requires": {
+        "end-of-stream": "1.4.1",
+        "inherits": "2.0.3",
+        "readable-stream": "2.3.3",
+        "stream-shift": "1.0.0"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+        },
+        "readable-stream": {
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
+          "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
+          "requires": {
+            "core-util-is": "1.0.2",
+            "inherits": "2.0.3",
+            "isarray": "1.0.0",
+            "process-nextick-args": "1.0.7",
+            "safe-buffer": "5.1.1",
+            "string_decoder": "1.0.3",
+            "util-deprecate": "1.0.2"
+          }
+        },
+        "string_decoder": {
+          "version": "1.0.3",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
+          "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
+          "requires": {
+            "safe-buffer": "5.1.1"
+          }
+        }
+      }
+    },
+    "ecdsa-sig-formatter": {
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.9.tgz",
+      "integrity": "sha1-S8kmJ07Dtau1AW5+HWCSGsJisqE=",
+      "requires": {
+        "base64url": "2.0.0",
+        "safe-buffer": "5.1.1"
+      }
+    },
+    "ee-first": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+      "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
+    },
+    "electron-to-chromium": {
+      "version": "1.3.33",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.33.tgz",
+      "integrity": "sha1-vwBwPWKnxlI4E2V4w1LWxcBCpUU="
+    },
+    "elliptic": {
+      "version": "6.4.0",
+      "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz",
+      "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=",
+      "requires": {
+        "bn.js": "4.11.8",
+        "brorand": "1.1.0",
+        "hash.js": "1.1.3",
+        "hmac-drbg": "1.0.1",
+        "inherits": "2.0.3",
+        "minimalistic-assert": "1.0.0",
+        "minimalistic-crypto-utils": "1.0.1"
+      }
+    },
+    "email-validator": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/email-validator/-/email-validator-1.1.1.tgz",
+      "integrity": "sha512-vkcJJZEb7JXDY883Nx1Lkmb6noM3j1SfSt8L9tVFhZPnPQiFq+Nkd5evc77+tRVS4ChTUSr34voThsglI/ja/A=="
+    },
+    "emitter-mixin": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/emitter-mixin/-/emitter-mixin-0.0.3.tgz",
+      "integrity": "sha1-WUjLKG8uSO3DslGnz8H3iDOW1lw="
+    },
+    "emoji-regex": {
+      "version": "6.5.1",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.5.1.tgz",
+      "integrity": "sha512-PAHp6TxrCy7MGMFidro8uikr+zlJJKJ/Q6mm2ExZ7HwkyR9lSVFfE3kt36qcwa24BQL7y0G9axycGjK1A/0uNQ==",
+      "dev": true
+    },
+    "emojis-list": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
+      "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k="
+    },
+    "encodeurl": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+      "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
+    },
+    "encoding": {
+      "version": "0.1.12",
+      "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
+      "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
+      "requires": {
+        "iconv-lite": "0.4.19"
+      }
+    },
+    "end-of-stream": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
+      "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
+      "requires": {
+        "once": "1.4.0"
+      }
+    },
+    "enhanced-resolve": {
+      "version": "3.4.1",
+      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz",
+      "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=",
+      "requires": {
+        "graceful-fs": "4.1.11",
+        "memory-fs": "0.4.1",
+        "object-assign": "4.1.1",
+        "tapable": "0.2.8"
+      },
+      "dependencies": {
+        "graceful-fs": {
+          "version": "4.1.11",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+          "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
+        }
+      }
+    },
+    "errno": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.6.tgz",
+      "integrity": "sha512-IsORQDpaaSwcDP4ZZnHxgE85werpo34VYn1Ud3mq+eUsF593faR8oCZNXrROVkpFu2TsbrNhHin0aUrTsQ9vNw==",
+      "requires": {
+        "prr": "1.0.1"
+      }
+    },
+    "error-ex": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz",
+      "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=",
+      "requires": {
+        "is-arrayish": "0.2.1"
+      }
+    },
+    "error-stack-parser": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.1.tgz",
+      "integrity": "sha1-oyArj7AxFKqbQKDjZp5IsrZaAQo=",
+      "requires": {
+        "stackframe": "1.0.4"
+      }
+    },
+    "es-abstract": {
+      "version": "1.10.0",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.10.0.tgz",
+      "integrity": "sha512-/uh/DhdqIOSkAWifU+8nG78vlQxdLckUdI/sPgy0VhuXi2qJ7T8czBmqIYtLQVpCIFYafChnsRsB5pyb1JdmCQ==",
+      "dev": true,
+      "requires": {
+        "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": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz",
+      "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=",
+      "dev": true,
+      "requires": {
+        "is-callable": "1.1.3",
+        "is-date-object": "1.0.1",
+        "is-symbol": "1.0.1"
+      }
+    },
+    "es5-ext": {
+      "version": "0.10.38",
+      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.38.tgz",
+      "integrity": "sha512-jCMyePo7AXbUESwbl8Qi01VSH2piY9s/a3rSU/5w/MlTIx8HPL1xn2InGN8ejt/xulcJgnTO7vqNtOAxzYd2Kg==",
+      "requires": {
+        "es6-iterator": "2.0.3",
+        "es6-symbol": "3.1.1"
+      }
+    },
+    "es6-iterator": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+      "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
+      "requires": {
+        "d": "1.0.0",
+        "es5-ext": "0.10.38",
+        "es6-symbol": "3.1.1"
+      }
+    },
+    "es6-map": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz",
+      "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=",
+      "requires": {
+        "d": "1.0.0",
+        "es5-ext": "0.10.38",
+        "es6-iterator": "2.0.3",
+        "es6-set": "0.1.5",
+        "es6-symbol": "3.1.1",
+        "event-emitter": "0.3.5"
+      }
+    },
+    "es6-set": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz",
+      "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=",
+      "requires": {
+        "d": "1.0.0",
+        "es5-ext": "0.10.38",
+        "es6-iterator": "2.0.3",
+        "es6-symbol": "3.1.1",
+        "event-emitter": "0.3.5"
+      }
+    },
+    "es6-symbol": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz",
+      "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=",
+      "requires": {
+        "d": "1.0.0",
+        "es5-ext": "0.10.38"
+      }
+    },
+    "es6-weak-map": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz",
+      "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=",
+      "requires": {
+        "d": "1.0.0",
+        "es5-ext": "0.10.38",
+        "es6-iterator": "2.0.3",
+        "es6-symbol": "3.1.1"
+      }
+    },
+    "escape-html": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+      "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+    },
+    "escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
+    },
+    "escope": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz",
+      "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=",
+      "requires": {
+        "es6-map": "0.1.5",
+        "es6-weak-map": "2.0.2",
+        "esrecurse": "4.2.0",
+        "estraverse": "4.2.0"
+      }
+    },
+    "eslint": {
+      "version": "4.17.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.17.0.tgz",
+      "integrity": "sha512-AyxBUCANU/o/xC0ijGMKavo5Ls3oK6xykiOITlMdjFjrKOsqLrA7Nf5cnrDgcKrHzBirclAZt63XO7YZlVUPwA==",
+      "dev": true,
+      "requires": {
+        "ajv": "5.5.2",
+        "babel-code-frame": "6.26.0",
+        "chalk": "2.3.0",
+        "concat-stream": "1.6.0",
+        "cross-spawn": "5.1.0",
+        "debug": "3.1.0",
+        "doctrine": "2.1.0",
+        "eslint-scope": "3.7.1",
+        "eslint-visitor-keys": "1.0.0",
+        "espree": "3.5.3",
+        "esquery": "1.0.0",
+        "esutils": "2.0.2",
+        "file-entry-cache": "2.0.0",
+        "functional-red-black-tree": "1.0.1",
+        "glob": "7.1.2",
+        "globals": "11.3.0",
+        "ignore": "3.3.7",
+        "imurmurhash": "0.1.4",
+        "inquirer": "3.3.0",
+        "is-resolvable": "1.1.0",
+        "js-yaml": "3.10.0",
+        "json-stable-stringify-without-jsonify": "1.0.1",
+        "levn": "0.3.0",
+        "lodash": "4.17.5",
+        "minimatch": "3.0.4",
+        "mkdirp": "0.5.1",
+        "natural-compare": "1.4.0",
+        "optionator": "0.8.2",
+        "path-is-inside": "1.0.2",
+        "pluralize": "7.0.0",
+        "progress": "2.0.0",
+        "require-uncached": "1.0.3",
+        "semver": "5.5.0",
+        "strip-ansi": "4.0.0",
+        "strip-json-comments": "2.0.1",
+        "table": "4.0.2",
+        "text-table": "0.2.0"
+      },
+      "dependencies": {
+        "globals": {
+          "version": "11.3.0",
+          "resolved": "https://registry.npmjs.org/globals/-/globals-11.3.0.tgz",
+          "integrity": "sha512-kkpcKNlmQan9Z5ZmgqKH/SMbSmjxQ7QjyNqfXVc8VJcoBV2UEg+sxQD15GQofGRh2hfpwUb70VC31DR7Rq5Hdw==",
+          "dev": true
+        },
+        "strip-ansi": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "3.0.0"
+          }
+        }
+      }
+    },
+    "eslint-config-airbnb": {
+      "version": "16.1.0",
+      "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-16.1.0.tgz",
+      "integrity": "sha512-zLyOhVWhzB/jwbz7IPSbkUuj7X2ox4PHXTcZkEmDqTvd0baJmJyuxlFPDlZOE/Y5bC+HQRaEkT3FoHo9wIdRiw==",
+      "dev": true,
+      "requires": {
+        "eslint-config-airbnb-base": "12.1.0"
+      }
+    },
+    "eslint-config-airbnb-base": {
+      "version": "12.1.0",
+      "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-12.1.0.tgz",
+      "integrity": "sha512-/vjm0Px5ZCpmJqnjIzcFb9TKZrKWz0gnuG/7Gfkt0Db1ELJR51xkZth+t14rYdqWgX836XbuxtArbIHlVhbLBA==",
+      "dev": true,
+      "requires": {
+        "eslint-restricted-globals": "0.1.1"
+      }
+    },
+    "eslint-config-prettier": {
+      "version": "2.9.0",
+      "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-2.9.0.tgz",
+      "integrity": "sha512-ag8YEyBXsm3nmOv1Hz991VtNNDMRa+MNy8cY47Pl4bw6iuzqKbJajXdqUpiw13STdLLrznxgm1hj9NhxeOYq0A==",
+      "dev": true,
+      "requires": {
+        "get-stdin": "5.0.1"
+      }
+    },
+    "eslint-import-resolver-node": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz",
+      "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==",
+      "dev": true,
+      "requires": {
+        "debug": "2.6.9",
+        "resolve": "1.5.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        }
+      }
+    },
+    "eslint-module-utils": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.1.1.tgz",
+      "integrity": "sha512-jDI/X5l/6D1rRD/3T43q8Qgbls2nq5km5KSqiwlyUbGo5+04fXhMKdCPhjwbqAa6HXWaMxj8Q4hQDIh7IadJQw==",
+      "dev": true,
+      "requires": {
+        "debug": "2.6.9",
+        "pkg-dir": "1.0.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "find-up": {
+          "version": "1.1.2",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
+          "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
+          "dev": true,
+          "requires": {
+            "path-exists": "2.1.0",
+            "pinkie-promise": "2.0.1"
+          }
+        },
+        "path-exists": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
+          "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
+          "dev": true,
+          "requires": {
+            "pinkie-promise": "2.0.1"
+          }
+        },
+        "pkg-dir": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz",
+          "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=",
+          "dev": true,
+          "requires": {
+            "find-up": "1.1.2"
+          }
+        }
+      }
+    },
+    "eslint-plugin-import": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.8.0.tgz",
+      "integrity": "sha512-Rf7dfKJxZ16QuTgVv1OYNxkZcsu/hULFnC+e+w0Gzi6jMC3guQoWQgxYxc54IDRinlb6/0v5z/PxxIKmVctN+g==",
+      "dev": true,
+      "requires": {
+        "builtin-modules": "1.1.1",
+        "contains-path": "0.1.0",
+        "debug": "2.6.9",
+        "doctrine": "1.5.0",
+        "eslint-import-resolver-node": "0.3.2",
+        "eslint-module-utils": "2.1.1",
+        "has": "1.0.1",
+        "lodash.cond": "4.5.2",
+        "minimatch": "3.0.4",
+        "read-pkg-up": "2.0.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "doctrine": {
+          "version": "1.5.0",
+          "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
+          "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
+          "dev": true,
+          "requires": {
+            "esutils": "2.0.2",
+            "isarray": "1.0.0"
+          }
+        },
+        "isarray": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+          "dev": true
+        }
+      }
+    },
+    "eslint-plugin-jsx-a11y": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.0.3.tgz",
+      "integrity": "sha1-VFg9GuRCSDFi4EDhPMMYZUZRAOU=",
+      "dev": true,
+      "requires": {
+        "aria-query": "0.7.1",
+        "array-includes": "3.0.3",
+        "ast-types-flow": "0.0.7",
+        "axobject-query": "0.1.0",
+        "damerau-levenshtein": "1.0.4",
+        "emoji-regex": "6.5.1",
+        "jsx-ast-utils": "2.0.1"
+      }
+    },
+    "eslint-plugin-prettier": {
+      "version": "2.6.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-2.6.0.tgz",
+      "integrity": "sha512-floiaI4F7hRkTrFe8V2ItOK97QYrX75DjmdzmVITZoAP6Cn06oEDPQRsO6MlHEP/u2SxI3xQ52Kpjw6j5WGfeQ==",
+      "dev": true,
+      "requires": {
+        "fast-diff": "1.1.2",
+        "jest-docblock": "21.2.0"
+      }
+    },
+    "eslint-plugin-react": {
+      "version": "7.6.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.6.1.tgz",
+      "integrity": "sha512-30aMOHWX/DOaaLJVBHz6RMvYM2qy5GH63+y2PLFdIrYe4YLtODFmT3N1YA7ZqUnaBweVbedr4K4cqxOlWAPjIw==",
+      "dev": true,
+      "requires": {
+        "doctrine": "2.1.0",
+        "has": "1.0.1",
+        "jsx-ast-utils": "2.0.1",
+        "prop-types": "15.6.0"
+      }
+    },
+    "eslint-restricted-globals": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz",
+      "integrity": "sha1-NfDVy8ZMLj7WLpO0saevBbp+1Nc=",
+      "dev": true
+    },
+    "eslint-scope": {
+      "version": "3.7.1",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz",
+      "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=",
+      "dev": true,
+      "requires": {
+        "esrecurse": "4.2.0",
+        "estraverse": "4.2.0"
+      }
+    },
+    "eslint-visitor-keys": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
+      "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==",
+      "dev": true
+    },
+    "espree": {
+      "version": "3.5.3",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.3.tgz",
+      "integrity": "sha512-Zy3tAJDORxQZLl2baguiRU1syPERAIg0L+JB2MWorORgTu/CplzvxS9WWA7Xh4+Q+eOQihNs/1o1Xep8cvCxWQ==",
+      "dev": true,
+      "requires": {
+        "acorn": "5.4.1",
+        "acorn-jsx": "3.0.1"
+      }
+    },
+    "esprima": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz",
+      "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==",
+      "dev": true
+    },
+    "esquery": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz",
+      "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=",
+      "dev": true,
+      "requires": {
+        "estraverse": "4.2.0"
+      }
+    },
+    "esrecurse": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz",
+      "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=",
+      "requires": {
+        "estraverse": "4.2.0",
+        "object-assign": "4.1.1"
+      }
+    },
+    "estraverse": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
+      "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM="
+    },
+    "esutils": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
+      "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs="
+    },
+    "etag": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+      "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
+    },
+    "event-emitter": {
+      "version": "0.3.5",
+      "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
+      "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=",
+      "requires": {
+        "d": "1.0.0",
+        "es5-ext": "0.10.38"
+      }
+    },
+    "events": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
+      "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ="
+    },
+    "evp_bytestokey": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
+      "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
+      "requires": {
+        "md5.js": "1.3.4",
+        "safe-buffer": "5.1.1"
+      }
+    },
+    "execa": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
+      "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=",
+      "requires": {
+        "cross-spawn": "5.1.0",
+        "get-stream": "3.0.0",
+        "is-stream": "1.1.0",
+        "npm-run-path": "2.0.2",
+        "p-finally": "1.0.0",
+        "signal-exit": "3.0.2",
+        "strip-eof": "1.0.0"
+      }
+    },
+    "expand-brackets": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
+      "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=",
+      "requires": {
+        "is-posix-bracket": "0.1.1"
+      }
+    },
+    "expand-range": {
+      "version": "1.8.2",
+      "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz",
+      "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=",
+      "requires": {
+        "fill-range": "2.2.3"
+      }
+    },
+    "expect-ct": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/expect-ct/-/expect-ct-0.1.0.tgz",
+      "integrity": "sha1-UnNWeN4YUwiQ2Ne5XwrGNkCVgJQ="
+    },
+    "express": {
+      "version": "4.16.2",
+      "resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz",
+      "integrity": "sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=",
+      "requires": {
+        "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.2",
+        "encodeurl": "1.0.2",
+        "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"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "setprototypeof": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+          "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
+        },
+        "statuses": {
+          "version": "1.3.1",
+          "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz",
+          "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4="
+        }
+      }
+    },
+    "express-validator": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-4.3.0.tgz",
+      "integrity": "sha512-EYU+JJ2EoLpcw+GKwbB1K8UGb/w1A70Wf3gD/zE9QScQxeSt8qad93lxGtsLwZFoiYM0EByVoSzHJnskp+eVHQ==",
+      "requires": {
+        "@types/express": "4.0.39",
+        "lodash": "4.17.5",
+        "validator": "8.2.0"
+      }
+    },
+    "external-editor": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.1.0.tgz",
+      "integrity": "sha512-E44iT5QVOUJBKij4IIV3uvxuNlbKS38Tw1HiupxEIHPv9qtC2PrDYohbXV5U+1jnfIXttny8gUhj+oZvflFlzA==",
+      "dev": true,
+      "requires": {
+        "chardet": "0.4.2",
+        "iconv-lite": "0.4.19",
+        "tmp": "0.0.33"
+      }
+    },
+    "extglob": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
+      "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
+      "requires": {
+        "is-extglob": "1.0.0"
+      }
+    },
+    "fast-deep-equal": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz",
+      "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8="
+    },
+    "fast-diff": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz",
+      "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==",
+      "dev": true
+    },
+    "fast-json-stable-stringify": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
+      "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
+    },
+    "fast-levenshtein": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+      "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
+    },
+    "fbjs": {
+      "version": "0.8.16",
+      "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.16.tgz",
+      "integrity": "sha1-XmdDL1UNxBtXK/VYR7ispk5TN9s=",
+      "requires": {
+        "core-js": "1.2.7",
+        "isomorphic-fetch": "2.2.1",
+        "loose-envify": "1.3.1",
+        "object-assign": "4.1.1",
+        "promise": "7.3.1",
+        "setimmediate": "1.0.5",
+        "ua-parser-js": "0.7.17"
+      },
+      "dependencies": {
+        "core-js": {
+          "version": "1.2.7",
+          "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
+          "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
+        }
+      }
+    },
+    "figures": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
+      "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
+      "dev": true,
+      "requires": {
+        "escape-string-regexp": "1.0.5"
+      }
+    },
+    "file-entry-cache": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz",
+      "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=",
+      "dev": true,
+      "requires": {
+        "flat-cache": "1.3.0",
+        "object-assign": "4.1.1"
+      }
+    },
+    "filename-regex": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",
+      "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY="
+    },
+    "filesize": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.0.tgz",
+      "integrity": "sha512-g5OWtoZWcPI56js1DFhIEqyG9tnu/7sG3foHwgS9KGYFMfsYguI3E+PRVCmtmE96VajQIEMRU2OhN+ME589Gdw=="
+    },
+    "fill-range": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz",
+      "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=",
+      "requires": {
+        "is-number": "2.1.0",
+        "isobject": "2.1.0",
+        "randomatic": "1.1.7",
+        "repeat-element": "1.1.2",
+        "repeat-string": "1.6.1"
+      }
+    },
+    "finalhandler": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz",
+      "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=",
+      "requires": {
+        "debug": "2.6.9",
+        "encodeurl": "1.0.2",
+        "escape-html": "1.0.3",
+        "on-finished": "2.3.0",
+        "parseurl": "1.3.2",
+        "statuses": "1.3.1",
+        "unpipe": "1.0.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "statuses": {
+          "version": "1.3.1",
+          "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz",
+          "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4="
+        }
+      }
+    },
+    "find-cache-dir": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz",
+      "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=",
+      "requires": {
+        "commondir": "1.0.1",
+        "make-dir": "1.1.0",
+        "pkg-dir": "2.0.0"
+      }
+    },
+    "find-up": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+      "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+      "requires": {
+        "locate-path": "2.0.0"
+      }
+    },
+    "flat-cache": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz",
+      "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=",
+      "dev": true,
+      "requires": {
+        "circular-json": "0.3.3",
+        "del": "2.2.2",
+        "graceful-fs": "4.1.11",
+        "write": "0.2.1"
+      },
+      "dependencies": {
+        "del": {
+          "version": "2.2.2",
+          "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz",
+          "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=",
+          "dev": true,
+          "requires": {
+            "globby": "5.0.0",
+            "is-path-cwd": "1.0.0",
+            "is-path-in-cwd": "1.0.0",
+            "object-assign": "4.1.1",
+            "pify": "2.3.0",
+            "pinkie-promise": "2.0.1",
+            "rimraf": "2.6.2"
+          }
+        },
+        "globby": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz",
+          "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=",
+          "dev": true,
+          "requires": {
+            "array-union": "1.0.2",
+            "arrify": "1.0.1",
+            "glob": "7.1.2",
+            "object-assign": "4.1.1",
+            "pify": "2.3.0",
+            "pinkie-promise": "2.0.1"
+          }
+        },
+        "graceful-fs": {
+          "version": "4.1.11",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+          "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
+          "dev": true
+        },
+        "pify": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+          "dev": true
+        }
+      }
+    },
+    "flush-write-stream": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.2.tgz",
+      "integrity": "sha1-yBuQ2HRnZvGmCaRoCZRsRd2K5Bc=",
+      "requires": {
+        "inherits": "2.0.3",
+        "readable-stream": "2.3.3"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+        },
+        "readable-stream": {
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
+          "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
+          "requires": {
+            "core-util-is": "1.0.2",
+            "inherits": "2.0.3",
+            "isarray": "1.0.0",
+            "process-nextick-args": "1.0.7",
+            "safe-buffer": "5.1.1",
+            "string_decoder": "1.0.3",
+            "util-deprecate": "1.0.2"
+          }
+        },
+        "string_decoder": {
+          "version": "1.0.3",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
+          "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
+          "requires": {
+            "safe-buffer": "5.1.1"
+          }
+        }
+      }
+    },
+    "follow-redirects": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.4.1.tgz",
+      "integrity": "sha512-uxYePVPogtya1ktGnAAXOacnbIuRMB4dkvqeNz2qTtTQsuzSfbDolV+wMMKxAmCx0bLgAKLbBOkjItMbbkR1vg==",
+      "requires": {
+        "debug": "3.1.0"
+      }
+    },
+    "for-in": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+      "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA="
+    },
+    "for-own": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz",
+      "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=",
+      "requires": {
+        "for-in": "1.0.2"
+      }
+    },
+    "foreach": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz",
+      "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k="
+    },
+    "foreachasync": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz",
+      "integrity": "sha1-VQKYfchxS+M5IJfzLgBxyd7gfPY="
+    },
+    "forwarded": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
+      "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
+    },
+    "frameguard": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/frameguard/-/frameguard-3.0.0.tgz",
+      "integrity": "sha1-e8rUae57lukdEs6zlZx4I1qScuk="
+    },
+    "fresh": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+      "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
+    },
+    "friendly-errors-webpack-plugin": {
+      "version": "1.6.1",
+      "resolved": "https://registry.npmjs.org/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.6.1.tgz",
+      "integrity": "sha1-4yeBxHIvVGoGqbXXp8+ihSA3XXA=",
+      "requires": {
+        "chalk": "1.1.3",
+        "error-stack-parser": "2.0.1",
+        "string-length": "1.0.1"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "2.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
+        },
+        "chalk": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+          "requires": {
+            "ansi-styles": "2.2.1",
+            "escape-string-regexp": "1.0.5",
+            "has-ansi": "2.0.0",
+            "strip-ansi": "3.0.1",
+            "supports-color": "2.0.0"
+          }
+        },
+        "supports-color": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+          "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
+        }
+      }
+    },
+    "from2": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
+      "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=",
+      "requires": {
+        "inherits": "2.0.3",
+        "readable-stream": "2.3.3"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+        },
+        "readable-stream": {
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
+          "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
+          "requires": {
+            "core-util-is": "1.0.2",
+            "inherits": "2.0.3",
+            "isarray": "1.0.0",
+            "process-nextick-args": "1.0.7",
+            "safe-buffer": "5.1.1",
+            "string_decoder": "1.0.3",
+            "util-deprecate": "1.0.2"
+          }
+        },
+        "string_decoder": {
+          "version": "1.0.3",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
+          "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
+          "requires": {
+            "safe-buffer": "5.1.1"
+          }
+        }
+      }
+    },
+    "fs-readdir-recursive": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz",
+      "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==",
+      "dev": true
+    },
+    "fs-write-stream-atomic": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
+      "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=",
+      "requires": {
+        "graceful-fs": "4.1.11",
+        "iferr": "0.1.5",
+        "imurmurhash": "0.1.4",
+        "readable-stream": "1.0.34"
+      },
+      "dependencies": {
+        "graceful-fs": {
+          "version": "4.1.11",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+          "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
+        }
+      }
+    },
+    "fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+    },
+    "fstream": {
+      "version": "0.1.31",
+      "resolved": "https://registry.npmjs.org/fstream/-/fstream-0.1.31.tgz",
+      "integrity": "sha1-czfwWPu7vvqMn1YaKMqwhJICyYg=",
+      "requires": {
+        "graceful-fs": "3.0.11",
+        "inherits": "2.0.3",
+        "mkdirp": "0.5.1",
+        "rimraf": "2.6.2"
+      }
+    },
+    "function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+    },
+    "functional-red-black-tree": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+      "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+      "dev": true
+    },
+    "geoip-lite": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/geoip-lite/-/geoip-lite-1.2.1.tgz",
+      "integrity": "sha1-OcSO+T5JiiWezka8npjPPdtoDYs=",
+      "requires": {
+        "async": "2.6.0",
+        "colors": "1.1.2",
+        "glob": "7.1.2",
+        "iconv-lite": "0.4.19",
+        "lazy": "1.0.11",
+        "rimraf": "2.6.2",
+        "unzip": "0.1.11"
+      }
+    },
+    "get-caller-file": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz",
+      "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U="
+    },
+    "get-stdin": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz",
+      "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=",
+      "dev": true
+    },
+    "get-stream": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
+      "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
+    },
+    "glob": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
+      "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
+      "requires": {
+        "fs.realpath": "1.0.0",
+        "inflight": "1.0.6",
+        "inherits": "2.0.3",
+        "minimatch": "3.0.4",
+        "once": "1.4.0",
+        "path-is-absolute": "1.0.1"
+      }
+    },
+    "glob-base": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz",
+      "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=",
+      "requires": {
+        "glob-parent": "2.0.0",
+        "is-glob": "2.0.1"
+      }
+    },
+    "glob-parent": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
+      "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
+      "requires": {
+        "is-glob": "2.0.1"
+      }
+    },
+    "glob-promise": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/glob-promise/-/glob-promise-3.3.0.tgz",
+      "integrity": "sha512-X5VIEO/yy5NH3p9ORhlNodakQo/cK4I0lCtARNeAkWueJiToew5Xs9DiukVKsW5dgpjnnQgou9Rbj3HUkM6OUw=="
+    },
+    "global": {
+      "version": "4.3.2",
+      "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz",
+      "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=",
+      "requires": {
+        "min-document": "2.19.0",
+        "process": "0.5.2"
+      }
+    },
+    "global-dirs": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz",
+      "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=",
+      "requires": {
+        "ini": "1.3.5"
+      }
+    },
+    "globals": {
+      "version": "9.18.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
+      "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ=="
+    },
+    "globby": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
+      "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
+      "requires": {
+        "array-union": "1.0.2",
+        "glob": "7.1.2",
+        "object-assign": "4.1.1",
+        "pify": "2.3.0",
+        "pinkie-promise": "2.0.1"
+      },
+      "dependencies": {
+        "pify": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
+        }
+      }
+    },
+    "got": {
+      "version": "6.7.1",
+      "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz",
+      "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=",
+      "requires": {
+        "create-error-class": "3.0.2",
+        "duplexer3": "0.1.4",
+        "get-stream": "3.0.0",
+        "is-redirect": "1.0.0",
+        "is-retry-allowed": "1.1.0",
+        "is-stream": "1.1.0",
+        "lowercase-keys": "1.0.0",
+        "safe-buffer": "5.1.1",
+        "timed-out": "4.0.1",
+        "unzip-response": "2.0.1",
+        "url-parse-lax": "1.0.0"
+      }
+    },
+    "graceful-fs": {
+      "version": "3.0.11",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz",
+      "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=",
+      "requires": {
+        "natives": "1.1.1"
+      }
+    },
+    "has": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz",
+      "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=",
+      "requires": {
+        "function-bind": "1.1.1"
+      }
+    },
+    "has-ansi": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+      "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+      "requires": {
+        "ansi-regex": "2.1.1"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+          "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+        }
+      }
+    },
+    "has-flag": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
+      "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE="
+    },
+    "has-symbols": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
+      "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q="
+    },
+    "hash-base": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz",
+      "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=",
+      "requires": {
+        "inherits": "2.0.3"
+      }
+    },
+    "hash.js": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz",
+      "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==",
+      "requires": {
+        "inherits": "2.0.3",
+        "minimalistic-assert": "1.0.0"
+      }
+    },
+    "helmet": {
+      "version": "3.10.0",
+      "resolved": "https://registry.npmjs.org/helmet/-/helmet-3.10.0.tgz",
+      "integrity": "sha512-wVu5jSeImztLqNQPc4hqGr1DG0Ki2UJVmQ1KTugIrtl1f4Zw5SqVqh6QPyw5b6/Jo/iAnyTt+pcehB0RdEJsbw==",
+      "requires": {
+        "dns-prefetch-control": "0.1.0",
+        "dont-sniff-mimetype": "1.0.0",
+        "expect-ct": "0.1.0",
+        "frameguard": "3.0.0",
+        "helmet-csp": "2.7.0",
+        "hide-powered-by": "1.0.0",
+        "hpkp": "2.0.0",
+        "hsts": "2.1.0",
+        "ienoopen": "1.0.0",
+        "nocache": "2.0.0",
+        "referrer-policy": "1.1.0",
+        "x-xss-protection": "1.0.0"
+      }
+    },
+    "helmet-csp": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.7.0.tgz",
+      "integrity": "sha512-IGIAkWnxjRbgMXFA2/kmDqSIrIaSfZ6vhMHlSHw7jm7Gm9nVVXqwJ2B1YEpYrJsLrqY+w2Bbimk7snux9+sZAw==",
+      "requires": {
+        "camelize": "1.0.0",
+        "content-security-policy-builder": "2.0.0",
+        "dasherize": "2.0.0",
+        "lodash.reduce": "4.6.0",
+        "platform": "1.3.5"
+      }
+    },
+    "hide-powered-by": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/hide-powered-by/-/hide-powered-by-1.0.0.tgz",
+      "integrity": "sha1-SoWtZYgfYoV/xwr3F0oRhNzM4ys="
+    },
+    "hmac-drbg": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+      "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
+      "requires": {
+        "hash.js": "1.1.3",
+        "minimalistic-assert": "1.0.0",
+        "minimalistic-crypto-utils": "1.0.1"
+      }
+    },
+    "hoek": {
+      "version": "2.16.3",
+      "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz",
+      "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0="
+    },
+    "hoist-non-react-statics": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz",
+      "integrity": "sha1-ND24TGAYxlB3iJgkATWhQg7iLOA="
+    },
+    "home-or-tmp": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
+      "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=",
+      "requires": {
+        "os-homedir": "1.0.2",
+        "os-tmpdir": "1.0.2"
+      }
+    },
+    "hosted-git-info": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz",
+      "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg=="
+    },
+    "hpkp": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/hpkp/-/hpkp-2.0.0.tgz",
+      "integrity": "sha1-EOFCJk52IVpdMMROxD3mTe5tFnI="
+    },
+    "hsts": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/hsts/-/hsts-2.1.0.tgz",
+      "integrity": "sha512-zXhh/DqgrTXJ7erTN6Fh5k/xjMhDGXCqdYN3wvxUvGUQvnxcFfUd8E+6vLg/nk3ss1TYMb+DhRl25fYABioTvA=="
+    },
+    "html-entities": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz",
+      "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8="
+    },
+    "htmlescape": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz",
+      "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E="
+    },
+    "http-errors": {
+      "version": "1.6.2",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz",
+      "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=",
+      "requires": {
+        "depd": "1.1.1",
+        "inherits": "2.0.3",
+        "setprototypeof": "1.0.3",
+        "statuses": "1.4.0"
+      },
+      "dependencies": {
+        "depd": {
+          "version": "1.1.1",
+          "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz",
+          "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k="
+        }
+      }
+    },
+    "http-status": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/http-status/-/http-status-1.0.1.tgz",
+      "integrity": "sha1-3EMAGov8UKyH1IWokvdXiWS8lKI="
+    },
+    "httpplease": {
+      "version": "0.16.4",
+      "resolved": "https://registry.npmjs.org/httpplease/-/httpplease-0.16.4.tgz",
+      "integrity": "sha1-04Lr4jDvUHkIC06f/r8xap51wNo=",
+      "requires": {
+        "urllite": "0.5.0",
+        "xmlhttprequest": "1.8.0",
+        "xtend": "3.0.0"
+      },
+      "dependencies": {
+        "xtend": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz",
+          "integrity": "sha1-XM50B7r2Qsunvs2laBEcST9ZZlo="
+        }
+      }
+    },
+    "https-browserify": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
+      "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM="
+    },
+    "husky": {
+      "version": "0.15.0-rc.8",
+      "resolved": "https://registry.npmjs.org/husky/-/husky-0.15.0-rc.8.tgz",
+      "integrity": "sha512-zNWnUZAADCltrqrr6ZyDnit82Dn7Y/T6bgZe2O1MEDwZkLFuXr7ttZ3toz6MB89gffdoeqEoS86O8Upf4o8xfA==",
+      "dev": true,
+      "requires": {
+        "cosmiconfig": "4.0.0",
+        "execa": "0.9.0",
+        "is-ci": "1.1.0",
+        "pkg-dir": "2.0.0",
+        "pupa": "1.0.0",
+        "read-pkg": "3.0.0",
+        "run-node": "0.2.0",
+        "slash": "1.0.0"
+      },
+      "dependencies": {
+        "execa": {
+          "version": "0.9.0",
+          "resolved": "https://registry.npmjs.org/execa/-/execa-0.9.0.tgz",
+          "integrity": "sha512-BbUMBiX4hqiHZUA5+JujIjNb6TyAlp2D5KLheMjMluwOuzcnylDL4AxZYLLn1n2AGB49eSWwyKvvEQoRpnAtmA==",
+          "dev": true,
+          "requires": {
+            "cross-spawn": "5.1.0",
+            "get-stream": "3.0.0",
+            "is-stream": "1.1.0",
+            "npm-run-path": "2.0.2",
+            "p-finally": "1.0.0",
+            "signal-exit": "3.0.2",
+            "strip-eof": "1.0.0"
+          }
+        },
+        "graceful-fs": {
+          "version": "4.1.11",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+          "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
+          "dev": true
+        },
+        "load-json-file": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
+          "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
+          "dev": true,
+          "requires": {
+            "graceful-fs": "4.1.11",
+            "parse-json": "4.0.0",
+            "pify": "3.0.0",
+            "strip-bom": "3.0.0"
+          }
+        },
+        "parse-json": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+          "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+          "dev": true,
+          "requires": {
+            "error-ex": "1.3.1",
+            "json-parse-better-errors": "1.0.1"
+          }
+        },
+        "path-type": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
+          "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
+          "dev": true,
+          "requires": {
+            "pify": "3.0.0"
+          }
+        },
+        "read-pkg": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
+          "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
+          "dev": true,
+          "requires": {
+            "load-json-file": "4.0.0",
+            "normalize-package-data": "2.4.0",
+            "path-type": "3.0.0"
+          }
+        }
+      }
+    },
+    "iconv-lite": {
+      "version": "0.4.19",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
+      "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ=="
+    },
+    "ieee754": {
+      "version": "1.1.8",
+      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz",
+      "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q="
+    },
+    "ienoopen": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/ienoopen/-/ienoopen-1.0.0.tgz",
+      "integrity": "sha1-NGpCj0dKrI9QzzeE6i0PFvYr2ms="
+    },
+    "iferr": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
+      "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE="
+    },
+    "ignore": {
+      "version": "3.3.7",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.7.tgz",
+      "integrity": "sha512-YGG3ejvBNHRqu0559EOxxNFihD0AjpvHlC/pdGKd3X3ofe+CoJkYazwNJYTNebqpPKN+VVQbh4ZFn1DivMNuHA==",
+      "dev": true
+    },
+    "import-lazy": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz",
+      "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM="
+    },
+    "imurmurhash": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+      "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
+    },
+    "indexof": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
+      "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
+    },
+    "inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+      "requires": {
+        "once": "1.4.0",
+        "wrappy": "1.0.2"
+      }
+    },
+    "inherits": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+      "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+    },
+    "ini": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
+      "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
+    },
+    "inquirer": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz",
+      "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==",
+      "dev": true,
+      "requires": {
+        "ansi-escapes": "3.0.0",
+        "chalk": "2.3.0",
+        "cli-cursor": "2.1.0",
+        "cli-width": "2.2.0",
+        "external-editor": "2.1.0",
+        "figures": "2.0.0",
+        "lodash": "4.17.5",
+        "mute-stream": "0.0.7",
+        "run-async": "2.3.0",
+        "rx-lite": "4.0.8",
+        "rx-lite-aggregates": "4.0.8",
+        "string-width": "2.1.1",
+        "strip-ansi": "4.0.0",
+        "through": "2.3.8"
+      },
+      "dependencies": {
+        "strip-ansi": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "3.0.0"
+          }
+        }
+      }
+    },
+    "interpret": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz",
+      "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ="
+    },
+    "invariant": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz",
+      "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=",
+      "requires": {
+        "loose-envify": "1.3.1"
+      }
+    },
+    "invert-kv": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
+      "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY="
+    },
+    "ip-regex": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-1.0.3.tgz",
+      "integrity": "sha1-3FiQdvZZ9BnCIgOaMzFvHHOH7/0="
+    },
+    "ipaddr.js": {
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz",
+      "integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A="
+    },
+    "is-arrayish": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
+    },
+    "is-binary-path": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
+      "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
+      "requires": {
+        "binary-extensions": "1.11.0"
+      }
+    },
+    "is-buffer": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+      "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+    },
+    "is-builtin-module": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
+      "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
+      "requires": {
+        "builtin-modules": "1.1.1"
+      }
+    },
+    "is-callable": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz",
+      "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=",
+      "dev": true
+    },
+    "is-ci": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.1.0.tgz",
+      "integrity": "sha512-c7TnwxLePuqIlxHgr7xtxzycJPegNHFuIrBkwbf8hc58//+Op1CqFkyS+xnIMkwn9UsJIwc174BIjkyBmSpjKg==",
+      "dev": true,
+      "requires": {
+        "ci-info": "1.1.2"
+      }
+    },
+    "is-date-object": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
+      "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=",
+      "dev": true
+    },
+    "is-directory": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
+      "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=",
+      "dev": true
+    },
+    "is-dotfile": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz",
+      "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE="
+    },
+    "is-equal-shallow": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz",
+      "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=",
+      "requires": {
+        "is-primitive": "2.0.0"
+      }
+    },
+    "is-extendable": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+      "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="
+    },
+    "is-extglob": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
+      "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA="
+    },
+    "is-finite": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz",
+      "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=",
+      "requires": {
+        "number-is-nan": "1.0.1"
+      }
+    },
+    "is-fullwidth-code-point": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+      "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
+    },
+    "is-glob": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
+      "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
+      "requires": {
+        "is-extglob": "1.0.0"
+      }
+    },
+    "is-installed-globally": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz",
+      "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=",
+      "requires": {
+        "global-dirs": "0.1.1",
+        "is-path-inside": "1.0.1"
+      }
+    },
+    "is-npm": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz",
+      "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ="
+    },
+    "is-number": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz",
+      "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=",
+      "requires": {
+        "kind-of": "3.2.2"
+      }
+    },
+    "is-obj": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
+      "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8="
+    },
+    "is-path-cwd": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz",
+      "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0="
+    },
+    "is-path-in-cwd": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz",
+      "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=",
+      "requires": {
+        "is-path-inside": "1.0.1"
+      }
+    },
+    "is-path-inside": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
+      "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=",
+      "requires": {
+        "path-is-inside": "1.0.2"
+      }
+    },
+    "is-plain-object": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+      "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+      "requires": {
+        "isobject": "3.0.1"
+      },
+      "dependencies": {
+        "isobject": {
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
+        }
+      }
+    },
+    "is-posix-bracket": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz",
+      "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q="
+    },
+    "is-primitive": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz",
+      "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU="
+    },
+    "is-promise": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
+      "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
+      "dev": true
+    },
+    "is-redirect": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz",
+      "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ="
+    },
+    "is-regex": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
+      "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
+      "dev": true,
+      "requires": {
+        "has": "1.0.1"
+      }
+    },
+    "is-resolvable": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz",
+      "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==",
+      "dev": true
+    },
+    "is-retry-allowed": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz",
+      "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ="
+    },
+    "is-stream": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+      "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
+    },
+    "is-symbol": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz",
+      "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=",
+      "dev": true
+    },
+    "isarray": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+      "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+    },
+    "isemail": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/isemail/-/isemail-1.2.0.tgz",
+      "integrity": "sha1-vgPfjMPineTSxd9lASY/H6RZXpo="
+    },
+    "isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
+    },
+    "isobject": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+      "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+      "requires": {
+        "isarray": "1.0.0"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+        }
+      }
+    },
+    "isomorphic-fetch": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
+      "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
+      "requires": {
+        "node-fetch": "1.7.3",
+        "whatwg-fetch": "2.0.3"
+      }
+    },
+    "jest-docblock": {
+      "version": "21.2.0",
+      "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-21.2.0.tgz",
+      "integrity": "sha512-5IZ7sY9dBAYSV+YjQ0Ovb540Ku7AO9Z5o2Cg789xj167iQuZ2cG+z0f3Uct6WeYLbU6aQiM2pCs7sZ+4dotydw==",
+      "dev": true
+    },
+    "joi": {
+      "version": "6.10.1",
+      "resolved": "https://registry.npmjs.org/joi/-/joi-6.10.1.tgz",
+      "integrity": "sha1-TVDDGAeRIgAP5fFq8f+OGRe3fgY=",
+      "requires": {
+        "hoek": "2.16.3",
+        "isemail": "1.2.0",
+        "moment": "2.20.1",
+        "topo": "1.1.0"
+      }
+    },
+    "js-cookie": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.0.tgz",
+      "integrity": "sha1-Gywnmm7s44ChIWi5JIUmWzWx7/s="
+    },
+    "js-tokens": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
+      "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls="
+    },
+    "js-yaml": {
+      "version": "3.10.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz",
+      "integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==",
+      "dev": true,
+      "requires": {
+        "argparse": "1.0.9",
+        "esprima": "4.0.0"
+      }
+    },
+    "jsesc": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
+      "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s="
+    },
+    "json-loader": {
+      "version": "0.5.7",
+      "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz",
+      "integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w=="
+    },
+    "json-parse-better-errors": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.1.tgz",
+      "integrity": "sha512-xyQpxeWWMKyJps9CuGJYeng6ssI5bpqS9ltQpdVQ90t4ql6NdnxFKh95JcRt2cun/DjMVNrdjniLPuMA69xmCw==",
+      "dev": true
+    },
+    "json-schema-traverse": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
+      "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A="
+    },
+    "json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+      "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+      "dev": true
+    },
+    "json5": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
+      "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE="
+    },
+    "jsonwebtoken": {
+      "version": "8.1.1",
+      "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.1.1.tgz",
+      "integrity": "sha512-+ijVOtfLMlCII8LJkvabaKX3+8tGrGjiCTfzoed2D1b/ebKTO1hIYBQUJHbd9dJ9Fa4kH+dhYEd1qDwyzDLUUw==",
+      "requires": {
+        "jws": "3.1.4",
+        "lodash.includes": "4.3.0",
+        "lodash.isboolean": "3.0.3",
+        "lodash.isinteger": "4.0.4",
+        "lodash.isnumber": "3.0.3",
+        "lodash.isplainobject": "4.0.6",
+        "lodash.isstring": "4.0.1",
+        "lodash.once": "4.1.1",
+        "ms": "2.1.1",
+        "xtend": "4.0.1"
+      },
+      "dependencies": {
+        "ms": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+          "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+        }
+      }
+    },
+    "jsx-ast-utils": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz",
+      "integrity": "sha1-6AGxs5mF4g//yHtA43SAgOLcrH8=",
+      "dev": true,
+      "requires": {
+        "array-includes": "3.0.3"
+      }
+    },
+    "junk": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/junk/-/junk-1.0.3.tgz",
+      "integrity": "sha1-h75jSIZJy9ym9Tqzm+yczSNH9ZI="
+    },
+    "jwa": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.5.tgz",
+      "integrity": "sha1-oFUs4CIHQs1S4VN3SjKQXDDnVuU=",
+      "requires": {
+        "base64url": "2.0.0",
+        "buffer-equal-constant-time": "1.0.1",
+        "ecdsa-sig-formatter": "1.0.9",
+        "safe-buffer": "5.1.1"
+      }
+    },
+    "jws": {
+      "version": "3.1.4",
+      "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.4.tgz",
+      "integrity": "sha1-+ei5M46KhHJ31kRLFGT2GIDgUKI=",
+      "requires": {
+        "base64url": "2.0.0",
+        "jwa": "1.1.5",
+        "safe-buffer": "5.1.1"
+      }
+    },
+    "jwt-decode": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-2.2.0.tgz",
+      "integrity": "sha1-fYa9VmefWM5qhHBKZX3TkruoGnk="
+    },
+    "kind-of": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+      "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+      "requires": {
+        "is-buffer": "1.1.6"
+      }
+    },
+    "latest-version": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz",
+      "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=",
+      "requires": {
+        "package-json": "4.0.1"
+      }
+    },
+    "lazy": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/lazy/-/lazy-1.0.11.tgz",
+      "integrity": "sha1-2qBoIGKCVCwIgojpdcKXwa53tpA="
+    },
+    "lazy-cache": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
+      "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4="
+    },
+    "lcid": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
+      "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
+      "requires": {
+        "invert-kv": "1.0.0"
+      }
+    },
+    "levn": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+      "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+      "dev": true,
+      "requires": {
+        "prelude-ls": "1.1.2",
+        "type-check": "0.3.2"
+      }
+    },
+    "load-json-file": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
+      "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
+      "requires": {
+        "graceful-fs": "4.1.11",
+        "parse-json": "2.2.0",
+        "pify": "2.3.0",
+        "strip-bom": "3.0.0"
+      },
+      "dependencies": {
+        "graceful-fs": {
+          "version": "4.1.11",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+          "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
+        },
+        "pify": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
+        }
+      }
+    },
+    "loader-runner": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz",
+      "integrity": "sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI="
+    },
+    "loader-utils": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz",
+      "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=",
+      "requires": {
+        "big.js": "3.2.0",
+        "emojis-list": "2.1.0",
+        "json5": "0.5.1"
+      }
+    },
+    "locate-path": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+      "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+      "requires": {
+        "p-locate": "2.0.0",
+        "path-exists": "3.0.0"
+      }
+    },
+    "lodash": {
+      "version": "4.17.5",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz",
+      "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw=="
+    },
+    "lodash-es": {
+      "version": "4.17.5",
+      "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.5.tgz",
+      "integrity": "sha512-Ez3ONp3TK9gX1HYKp6IhetcVybD+2F+Yp6GS9dfH8ue6EOCEzQtQEh4K0FYWBP9qLv+lzeQAYXw+3ySfxyZqkw=="
+    },
+    "lodash.cond": {
+      "version": "4.5.2",
+      "resolved": "https://registry.npmjs.org/lodash.cond/-/lodash.cond-4.5.2.tgz",
+      "integrity": "sha1-9HGh2khr5g9quVXRcRVSPdHSVdU=",
+      "dev": true
+    },
+    "lodash.includes": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+      "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8="
+    },
+    "lodash.isboolean": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+      "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY="
+    },
+    "lodash.isinteger": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+      "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M="
+    },
+    "lodash.isnumber": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+      "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w="
+    },
+    "lodash.isplainobject": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+      "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
+    },
+    "lodash.isstring": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+      "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
+    },
+    "lodash.once": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+      "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
+    },
+    "lodash.reduce": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz",
+      "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs="
+    },
+    "longest": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
+      "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc="
+    },
+    "loose-envify": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz",
+      "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=",
+      "requires": {
+        "js-tokens": "3.0.2"
+      }
+    },
+    "lowercase-keys": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz",
+      "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY="
+    },
+    "lru-cache": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz",
+      "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==",
+      "requires": {
+        "pseudomap": "1.0.2",
+        "yallist": "2.1.2"
+      }
+    },
+    "lsmod": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-1.0.0.tgz",
+      "integrity": "sha1-mgD3bco26yP6BTUK/htYXUKZ5ks="
+    },
+    "make-dir": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.1.0.tgz",
+      "integrity": "sha512-0Pkui4wLJ7rxvmfUvs87skoEaxmu0hCUApF8nonzpl7q//FWp9zu8W61Scz4sd/kUiqDxvUhtoam2efDyiBzcA==",
+      "requires": {
+        "pify": "3.0.0"
+      }
+    },
+    "match-stream": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/match-stream/-/match-stream-0.0.2.tgz",
+      "integrity": "sha1-mesFAJOzTf+t5CG5rAtBCpz6F88=",
+      "requires": {
+        "buffers": "0.1.1",
+        "readable-stream": "1.0.34"
+      }
+    },
+    "math-expression-evaluator": {
+      "version": "1.2.17",
+      "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz",
+      "integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw="
+    },
+    "maximatch": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/maximatch/-/maximatch-0.1.0.tgz",
+      "integrity": "sha1-hs2NawTJ8wfAWmuUGZBtA2D7E6I=",
+      "requires": {
+        "array-differ": "1.0.0",
+        "array-union": "1.0.2",
+        "arrify": "1.0.1",
+        "minimatch": "3.0.4"
+      }
+    },
+    "md5": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz",
+      "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=",
+      "requires": {
+        "charenc": "0.0.2",
+        "crypt": "0.0.2",
+        "is-buffer": "1.1.6"
+      }
+    },
+    "md5-file": {
+      "version": "3.2.3",
+      "resolved": "https://registry.npmjs.org/md5-file/-/md5-file-3.2.3.tgz",
+      "integrity": "sha512-3Tkp1piAHaworfcCgH0jKbTvj1jWWFgbvh2cXaNCgHwyTCBxxvD1Y04rmfpvdPm1P4oXMOpm6+2H7sr7v9v8Fw==",
+      "requires": {
+        "buffer-alloc": "1.1.0"
+      }
+    },
+    "md5.js": {
+      "version": "1.3.4",
+      "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz",
+      "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=",
+      "requires": {
+        "hash-base": "3.0.4",
+        "inherits": "2.0.3"
+      },
+      "dependencies": {
+        "hash-base": {
+          "version": "3.0.4",
+          "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz",
+          "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=",
+          "requires": {
+            "inherits": "2.0.3",
+            "safe-buffer": "5.1.1"
+          }
+        }
+      }
+    },
+    "media-typer": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+      "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
+    },
+    "mem": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz",
+      "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=",
+      "requires": {
+        "mimic-fn": "1.2.0"
+      }
+    },
+    "memory-fs": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
+      "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
+      "requires": {
+        "errno": "0.1.6",
+        "readable-stream": "2.3.3"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+        },
+        "readable-stream": {
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
+          "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
+          "requires": {
+            "core-util-is": "1.0.2",
+            "inherits": "2.0.3",
+            "isarray": "1.0.0",
+            "process-nextick-args": "1.0.7",
+            "safe-buffer": "5.1.1",
+            "string_decoder": "1.0.3",
+            "util-deprecate": "1.0.2"
+          }
+        },
+        "string_decoder": {
+          "version": "1.0.3",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
+          "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
+          "requires": {
+            "safe-buffer": "5.1.1"
+          }
+        }
+      }
+    },
+    "merge-descriptors": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+      "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
+    },
+    "methods": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+      "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
+    },
+    "micromatch": {
+      "version": "2.3.11",
+      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
+      "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=",
+      "requires": {
+        "arr-diff": "2.0.0",
+        "array-unique": "0.2.1",
+        "braces": "1.8.5",
+        "expand-brackets": "0.1.5",
+        "extglob": "0.3.2",
+        "filename-regex": "2.0.1",
+        "is-extglob": "1.0.0",
+        "is-glob": "2.0.1",
+        "kind-of": "3.2.2",
+        "normalize-path": "2.1.1",
+        "object.omit": "2.0.1",
+        "parse-glob": "3.0.4",
+        "regex-cache": "0.4.4"
+      }
+    },
+    "miller-rabin": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
+      "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
+      "requires": {
+        "bn.js": "4.11.8",
+        "brorand": "1.1.0"
+      }
+    },
+    "mime": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
+      "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
+    },
+    "mime-db": {
+      "version": "1.30.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz",
+      "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE="
+    },
+    "mime-types": {
+      "version": "2.1.17",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz",
+      "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=",
+      "requires": {
+        "mime-db": "1.30.0"
+      }
+    },
+    "mimic-fn": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
+      "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="
+    },
+    "min-document": {
+      "version": "2.19.0",
+      "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz",
+      "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=",
+      "requires": {
+        "dom-walk": "0.1.1"
+      }
+    },
+    "minimalistic-assert": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz",
+      "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M="
+    },
+    "minimalistic-crypto-utils": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+      "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo="
+    },
+    "minimatch": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+      "requires": {
+        "brace-expansion": "1.1.9"
+      }
+    },
+    "minimist": {
+      "version": "0.0.8",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+      "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
+    },
+    "mississippi": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-1.3.1.tgz",
+      "integrity": "sha512-/6rB8YXFbAtsUVRphIRQqB0+9c7VaPHCjVtvto+JqwVxgz8Zz+I+f68/JgQ+Pb4VlZb2svA9OtdXnHHsZz7ltg==",
+      "requires": {
+        "concat-stream": "1.6.0",
+        "duplexify": "3.5.3",
+        "end-of-stream": "1.4.1",
+        "flush-write-stream": "1.0.2",
+        "from2": "2.3.0",
+        "parallel-transform": "1.1.0",
+        "pump": "1.0.3",
+        "pumpify": "1.4.0",
+        "stream-each": "1.2.2",
+        "through2": "2.0.3"
+      }
+    },
+    "mkdirp": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+      "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+      "requires": {
+        "minimist": "0.0.8"
+      }
+    },
+    "mkdirp-then": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/mkdirp-then/-/mkdirp-then-1.2.0.tgz",
+      "integrity": "sha1-pJLIecpNhz9e5FAI+PVf0BUN48U=",
+      "requires": {
+        "any-promise": "1.3.0",
+        "mkdirp": "0.5.1"
+      }
+    },
+    "moment": {
+      "version": "2.20.1",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.20.1.tgz",
+      "integrity": "sha512-Yh9y73JRljxW5QxN08Fner68eFLxM5ynNOAw2LbIB1YAGeQzZT8QFSUvkAz609Zf+IHhhaUxqZK8dG3W/+HEvg=="
+    },
+    "morgan": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.0.tgz",
+      "integrity": "sha1-0B+mxlhZt2/PMbPLU6OCGjEdgFE=",
+      "requires": {
+        "basic-auth": "2.0.0",
+        "debug": "2.6.9",
+        "depd": "1.1.2",
+        "on-finished": "2.3.0",
+        "on-headers": "1.0.1"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "requires": {
+            "ms": "2.0.0"
+          }
+        }
+      }
+    },
+    "move-concurrently": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
+      "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=",
+      "requires": {
+        "aproba": "1.2.0",
+        "copy-concurrently": "1.0.5",
+        "fs-write-stream-atomic": "1.0.10",
+        "mkdirp": "0.5.1",
+        "rimraf": "2.6.2",
+        "run-queue": "1.0.3"
+      }
+    },
+    "ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+    },
+    "mute-stream": {
+      "version": "0.0.7",
+      "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
+      "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
+      "dev": true
+    },
+    "mv": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz",
+      "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=",
+      "requires": {
+        "mkdirp": "0.5.1",
+        "ncp": "2.0.0",
+        "rimraf": "2.4.5"
+      },
+      "dependencies": {
+        "glob": {
+          "version": "6.0.4",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz",
+          "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=",
+          "requires": {
+            "inflight": "1.0.6",
+            "inherits": "2.0.3",
+            "minimatch": "3.0.4",
+            "once": "1.4.0",
+            "path-is-absolute": "1.0.1"
+          }
+        },
+        "rimraf": {
+          "version": "2.4.5",
+          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz",
+          "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=",
+          "requires": {
+            "glob": "6.0.4"
+          }
+        }
+      }
+    },
+    "mz": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
+      "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+      "requires": {
+        "any-promise": "1.3.0",
+        "object-assign": "4.1.1",
+        "thenify-all": "1.6.0"
+      }
+    },
+    "nanoid": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-1.0.1.tgz",
+      "integrity": "sha512-BXapDjcA0QjUPLBqcC5EcvwB8LMOY7zXf3UqDGp93R73PoMOfipmRVxpTogAhtqViE/pJzP9bS+jGK31rzkE2w=="
+    },
+    "natives": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.1.tgz",
+      "integrity": "sha512-8eRaxn8u/4wN8tGkhlc2cgwwvOLMLUMUn4IYTexMgWd+LyUDfeXVkk2ygQR0hvIHbJQXgHujia3ieUUDwNGkEA=="
+    },
+    "natural-compare": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+      "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+      "dev": true
+    },
+    "ncp": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz",
+      "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M="
+    },
+    "negotiator": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
+      "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
+    },
+    "neo4j-driver": {
+      "version": "1.5.3",
+      "resolved": "https://registry.npmjs.org/neo4j-driver/-/neo4j-driver-1.5.3.tgz",
+      "integrity": "sha512-/TEVUs061mhCkm1mlehbt+0y6k5qwTPUj+Biqqct0e7foptyrHeQjUozi4P5qi31u0cnuj5fm1Sirvz0arwcsg==",
+      "requires": {
+        "babel-runtime": "6.26.0",
+        "url-parse": "1.2.0"
+      }
+    },
+    "next": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/next/-/next-5.0.0.tgz",
+      "integrity": "sha512-GBpLGbCNdb0U7vgo6gNtPdAfeWIOhzNXgJV3zLBbUhmIxDeNb5wmM1Z1nne8xObRHppx6rKq0fShHYFHC8+g+g==",
+      "requires": {
+        "@zeit/check-updates": "1.1.0",
+        "@zeit/source-map-support": "0.6.2",
+        "ansi-html": "0.0.7",
+        "babel-core": "6.26.0",
+        "babel-loader": "7.1.2",
+        "babel-plugin-react-require": "3.0.0",
+        "babel-plugin-syntax-dynamic-import": "6.18.0",
+        "babel-plugin-transform-class-properties": "6.24.1",
+        "babel-plugin-transform-object-rest-spread": "6.26.0",
+        "babel-plugin-transform-react-jsx-source": "6.22.0",
+        "babel-plugin-transform-react-remove-prop-types": "0.4.8",
+        "babel-plugin-transform-runtime": "6.23.0",
+        "babel-preset-env": "1.6.0",
+        "babel-preset-react": "6.24.1",
+        "babel-runtime": "6.26.0",
+        "babel-template": "6.26.0",
+        "case-sensitive-paths-webpack-plugin": "2.1.1",
+        "cross-spawn": "5.1.0",
+        "del": "3.0.0",
+        "etag": "1.8.1",
+        "find-up": "2.1.0",
+        "fresh": "0.5.2",
+        "friendly-errors-webpack-plugin": "1.6.1",
+        "glob": "7.1.2",
+        "glob-promise": "3.3.0",
+        "hoist-non-react-statics": "2.3.1",
+        "htmlescape": "1.1.1",
+        "http-errors": "1.6.2",
+        "http-status": "1.0.1",
+        "json-loader": "0.5.7",
+        "loader-utils": "1.1.0",
+        "md5-file": "3.2.3",
+        "minimist": "1.2.0",
+        "mkdirp-then": "1.2.0",
+        "mv": "2.1.1",
+        "mz": "2.7.0",
+        "path-to-regexp": "2.1.0",
+        "pkg-up": "2.0.0",
+        "prop-types": "15.6.0",
+        "prop-types-exact": "1.1.1",
+        "react-hot-loader": "4.0.0-beta.18",
+        "recursive-copy": "2.0.6",
+        "resolve": "1.5.0",
+        "send": "0.16.1",
+        "strip-ansi": "3.0.1",
+        "styled-jsx": "2.2.3",
+        "touch": "3.1.0",
+        "uglifyjs-webpack-plugin": "1.1.6",
+        "unfetch": "3.0.0",
+        "url": "0.11.0",
+        "uuid": "3.1.0",
+        "walk": "2.3.9",
+        "webpack": "3.10.0",
+        "webpack-dev-middleware": "1.12.0",
+        "webpack-hot-middleware": "2.21.0",
+        "write-file-webpack-plugin": "4.2.0",
+        "xss-filters": "1.2.7"
+      },
+      "dependencies": {
+        "minimist": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
+        },
+        "path-to-regexp": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.1.0.tgz",
+          "integrity": "sha512-dZY7QPCPp5r9cnNuQ955mOv4ZFVDXY/yvqeV7Y1W2PJA3PEFcuow9xKFfJxbBj1pIjOAP+M2B4/7xubmykLrXw=="
+        }
+      }
+    },
+    "next-redux-wrapper": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/next-redux-wrapper/-/next-redux-wrapper-1.3.5.tgz",
+      "integrity": "sha512-VZFl/CIIujxCGDlBM99W8pDYkSr5o10UMUt5/pZHZVgnaEqpFSsRsKz9yAkx/8C3RYFhGK4MHiNaf9+LtUUemA==",
+      "requires": {
+        "object.assign": "4.1.0"
+      }
+    },
+    "nocache": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/nocache/-/nocache-2.0.0.tgz",
+      "integrity": "sha1-ICtIAhoMTL3i34DeFaF0Q8i0OYA="
+    },
+    "node-fetch": {
+      "version": "1.7.3",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
+      "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
+      "requires": {
+        "encoding": "0.1.12",
+        "is-stream": "1.1.0"
+      }
+    },
+    "node-libs-browser": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz",
+      "integrity": "sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==",
+      "requires": {
+        "assert": "1.4.1",
+        "browserify-zlib": "0.2.0",
+        "buffer": "4.9.1",
+        "console-browserify": "1.1.0",
+        "constants-browserify": "1.0.0",
+        "crypto-browserify": "3.12.0",
+        "domain-browser": "1.2.0",
+        "events": "1.1.1",
+        "https-browserify": "1.0.0",
+        "os-browserify": "0.3.0",
+        "path-browserify": "0.0.0",
+        "process": "0.11.10",
+        "punycode": "1.3.2",
+        "querystring-es3": "0.2.1",
+        "readable-stream": "2.3.3",
+        "stream-browserify": "2.0.1",
+        "stream-http": "2.8.0",
+        "string_decoder": "1.0.3",
+        "timers-browserify": "2.0.6",
+        "tty-browserify": "0.0.0",
+        "url": "0.11.0",
+        "util": "0.10.3",
+        "vm-browserify": "0.0.4"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+        },
+        "process": {
+          "version": "0.11.10",
+          "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+          "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI="
+        },
+        "readable-stream": {
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
+          "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
+          "requires": {
+            "core-util-is": "1.0.2",
+            "inherits": "2.0.3",
+            "isarray": "1.0.0",
+            "process-nextick-args": "1.0.7",
+            "safe-buffer": "5.1.1",
+            "string_decoder": "1.0.3",
+            "util-deprecate": "1.0.2"
+          }
+        },
+        "string_decoder": {
+          "version": "1.0.3",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
+          "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
+          "requires": {
+            "safe-buffer": "5.1.1"
+          }
+        }
+      }
+    },
+    "nodemailer": {
+      "version": "4.4.2",
+      "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-4.4.2.tgz",
+      "integrity": "sha512-sstDxbCSPHDXrWRQhH++khr3yVU0CGvH2dCtAHOPQQ0lRR7xwq2txItEXckMpX481B/cN+0akER2bfExh7Gu/Q=="
+    },
+    "nopt": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
+      "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=",
+      "requires": {
+        "abbrev": "1.1.1"
+      }
+    },
+    "normalize-package-data": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
+      "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
+      "requires": {
+        "hosted-git-info": "2.5.0",
+        "is-builtin-module": "1.0.0",
+        "semver": "5.5.0",
+        "validate-npm-package-license": "3.0.1"
+      }
+    },
+    "normalize-path": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+      "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+      "requires": {
+        "remove-trailing-separator": "1.1.0"
+      }
+    },
+    "npm-run-path": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
+      "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
+      "requires": {
+        "path-key": "2.0.1"
+      }
+    },
+    "number-is-nan": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+      "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
+    },
+    "object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
+    },
+    "object-keys": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz",
+      "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0="
+    },
+    "object.assign": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
+      "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
+      "requires": {
+        "define-properties": "1.1.2",
+        "function-bind": "1.1.1",
+        "has-symbols": "1.0.0",
+        "object-keys": "1.0.11"
+      }
+    },
+    "object.omit": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz",
+      "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=",
+      "requires": {
+        "for-own": "0.1.5",
+        "is-extendable": "0.1.1"
+      }
+    },
+    "on-finished": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+      "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+      "requires": {
+        "ee-first": "1.1.1"
+      }
+    },
+    "on-headers": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz",
+      "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c="
+    },
+    "once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "requires": {
+        "wrappy": "1.0.2"
+      }
+    },
+    "onetime": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
+      "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
+      "dev": true,
+      "requires": {
+        "mimic-fn": "1.2.0"
+      }
+    },
+    "optionator": {
+      "version": "0.8.2",
+      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
+      "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
+      "dev": true,
+      "requires": {
+        "deep-is": "0.1.3",
+        "fast-levenshtein": "2.0.6",
+        "levn": "0.3.0",
+        "prelude-ls": "1.1.2",
+        "type-check": "0.3.2",
+        "wordwrap": "1.0.0"
+      },
+      "dependencies": {
+        "wordwrap": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+          "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
+          "dev": true
+        }
+      }
+    },
+    "os-browserify": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
+      "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc="
+    },
+    "os-homedir": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+      "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
+    },
+    "os-locale": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz",
+      "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==",
+      "requires": {
+        "execa": "0.7.0",
+        "lcid": "1.0.0",
+        "mem": "1.1.0"
+      }
+    },
+    "os-tmpdir": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+      "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
+    },
+    "output-file-sync": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/output-file-sync/-/output-file-sync-1.1.2.tgz",
+      "integrity": "sha1-0KM+7+YaIF+suQCS6CZZjVJFznY=",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "4.1.11",
+        "mkdirp": "0.5.1",
+        "object-assign": "4.1.1"
+      },
+      "dependencies": {
+        "graceful-fs": {
+          "version": "4.1.11",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+          "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
+          "dev": true
+        }
+      }
+    },
+    "over": {
+      "version": "0.0.5",
+      "resolved": "https://registry.npmjs.org/over/-/over-0.0.5.tgz",
+      "integrity": "sha1-8phS5w/X4l82DgE6jsRMgq7bVwg="
+    },
+    "p-finally": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
+      "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
+    },
+    "p-limit": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz",
+      "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==",
+      "requires": {
+        "p-try": "1.0.0"
+      }
+    },
+    "p-locate": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+      "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+      "requires": {
+        "p-limit": "1.2.0"
+      }
+    },
+    "p-map": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz",
+      "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA=="
+    },
+    "p-try": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+      "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M="
+    },
+    "package-json": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz",
+      "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=",
+      "requires": {
+        "got": "6.7.1",
+        "registry-auth-token": "3.3.2",
+        "registry-url": "3.1.0",
+        "semver": "5.5.0"
+      }
+    },
+    "pako": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz",
+      "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg=="
+    },
+    "parallel-transform": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz",
+      "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=",
+      "requires": {
+        "cyclist": "0.2.2",
+        "inherits": "2.0.3",
+        "readable-stream": "2.3.3"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+        },
+        "readable-stream": {
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
+          "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
+          "requires": {
+            "core-util-is": "1.0.2",
+            "inherits": "2.0.3",
+            "isarray": "1.0.0",
+            "process-nextick-args": "1.0.7",
+            "safe-buffer": "5.1.1",
+            "string_decoder": "1.0.3",
+            "util-deprecate": "1.0.2"
+          }
+        },
+        "string_decoder": {
+          "version": "1.0.3",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
+          "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
+          "requires": {
+            "safe-buffer": "5.1.1"
+          }
+        }
+      }
+    },
+    "parse-asn1": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz",
+      "integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=",
+      "requires": {
+        "asn1.js": "4.9.2",
+        "browserify-aes": "1.1.1",
+        "create-hash": "1.1.3",
+        "evp_bytestokey": "1.0.3",
+        "pbkdf2": "3.0.14"
+      }
+    },
+    "parse-glob": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz",
+      "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=",
+      "requires": {
+        "glob-base": "0.3.0",
+        "is-dotfile": "1.0.3",
+        "is-extglob": "1.0.0",
+        "is-glob": "2.0.1"
+      }
+    },
+    "parse-json": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+      "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+      "requires": {
+        "error-ex": "1.3.1"
+      }
+    },
+    "parseurl": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
+      "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
+    },
+    "passport": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.0.tgz",
+      "integrity": "sha1-xQlWkTR71a07XhgCOMORTRbwWBE=",
+      "requires": {
+        "passport-strategy": "1.0.0",
+        "pause": "0.0.1"
+      }
+    },
+    "passport-jwt": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-3.0.1.tgz",
+      "integrity": "sha1-5Pcnba2L0lHUPG/DiIMTC5YycvY=",
+      "requires": {
+        "jsonwebtoken": "7.4.3",
+        "passport-strategy": "1.0.0"
+      },
+      "dependencies": {
+        "jsonwebtoken": {
+          "version": "7.4.3",
+          "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-7.4.3.tgz",
+          "integrity": "sha1-d/UCHeBYtgWheD+hKD6ZgS5kVjg=",
+          "requires": {
+            "joi": "6.10.1",
+            "jws": "3.1.4",
+            "lodash.once": "4.1.1",
+            "ms": "2.0.0",
+            "xtend": "4.0.1"
+          }
+        }
+      }
+    },
+    "passport-local": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz",
+      "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=",
+      "requires": {
+        "passport-strategy": "1.0.0"
+      }
+    },
+    "passport-localapikey": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/passport-localapikey/-/passport-localapikey-0.0.3.tgz",
+      "integrity": "sha1-pERzXILME1J2gp3eJ9etAfqj/aA=",
+      "requires": {
+        "passport": "0.1.18",
+        "pkginfo": "0.2.3"
+      },
+      "dependencies": {
+        "passport": {
+          "version": "0.1.18",
+          "resolved": "https://registry.npmjs.org/passport/-/passport-0.1.18.tgz",
+          "integrity": "sha1-yCZEedy2QUytu2Z1LRKzfgtlJaE=",
+          "requires": {
+            "pause": "0.0.1",
+            "pkginfo": "0.2.3"
+          }
+        }
+      }
+    },
+    "passport-strategy": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
+      "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ="
+    },
+    "path-browserify": {
+      "version": "0.0.0",
+      "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz",
+      "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo="
+    },
+    "path-exists": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+      "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
+    },
+    "path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+    },
+    "path-is-inside": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+      "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM="
+    },
+    "path-key": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+      "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
+    },
+    "path-parse": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz",
+      "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME="
+    },
+    "path-to-regexp": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+      "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
+    },
+    "path-type": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
+      "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
+      "requires": {
+        "pify": "2.3.0"
+      },
+      "dependencies": {
+        "pify": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
+        }
+      }
+    },
+    "pause": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
+      "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10="
+    },
+    "pbkdf2": {
+      "version": "3.0.14",
+      "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.14.tgz",
+      "integrity": "sha512-gjsZW9O34fm0R7PaLHRJmLLVfSoesxztjPjE9o6R+qtVJij90ltg1joIovN9GKrRW3t1PzhDDG3UMEMFfZ+1wA==",
+      "requires": {
+        "create-hash": "1.1.3",
+        "create-hmac": "1.1.6",
+        "ripemd160": "2.0.1",
+        "safe-buffer": "5.1.1",
+        "sha.js": "2.4.10"
+      }
+    },
+    "performance-now": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+      "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
+    },
+    "pify": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+      "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
+    },
+    "pinkie": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+      "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA="
+    },
+    "pinkie-promise": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+      "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+      "requires": {
+        "pinkie": "2.0.4"
+      }
+    },
+    "pkg-dir": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
+      "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
+      "requires": {
+        "find-up": "2.1.0"
+      }
+    },
+    "pkg-up": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz",
+      "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=",
+      "requires": {
+        "find-up": "2.1.0"
+      }
+    },
+    "pkginfo": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.2.3.tgz",
+      "integrity": "sha1-cjnEKl72wwuPMoQ52bn/cQQkkPg="
+    },
+    "platform": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.5.tgz",
+      "integrity": "sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q=="
+    },
+    "pluralize": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz",
+      "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==",
+      "dev": true
+    },
+    "postcss-value-parser": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz",
+      "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU="
+    },
+    "prelude-ls": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+      "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+      "dev": true
+    },
+    "prepend-http": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
+      "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw="
+    },
+    "preserve": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz",
+      "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks="
+    },
+    "prettier": {
+      "version": "1.10.2",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.10.2.tgz",
+      "integrity": "sha512-TcdNoQIWFoHblurqqU6d1ysopjq7UX0oRcT/hJ8qvBAELiYWn+Ugf0AXdnzISEJ7vuhNnQ98N8jR8Sh53x4IZg==",
+      "dev": true
+    },
+    "private": {
+      "version": "0.1.8",
+      "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz",
+      "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg=="
+    },
+    "process": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz",
+      "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8="
+    },
+    "process-nextick-args": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
+      "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
+    },
+    "progress": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz",
+      "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=",
+      "dev": true
+    },
+    "promise": {
+      "version": "7.3.1",
+      "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
+      "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
+      "requires": {
+        "asap": "2.0.6"
+      }
+    },
+    "promise-inflight": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
+      "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM="
+    },
+    "prop-types": {
+      "version": "15.6.0",
+      "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.0.tgz",
+      "integrity": "sha1-zq8IMCL8RrSjX2nhPvda7Q1jmFY=",
+      "requires": {
+        "fbjs": "0.8.16",
+        "loose-envify": "1.3.1",
+        "object-assign": "4.1.1"
+      }
+    },
+    "prop-types-exact": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/prop-types-exact/-/prop-types-exact-1.1.1.tgz",
+      "integrity": "sha512-VrFC2QoIt7oj9bxVTAYDfDMBYV3u+VHQUOWJWk9OURT7uwPEenH879MQoAQ/reY2GZ55il6p/eCqHRs2nqm8wA==",
+      "requires": {
+        "has": "1.0.1",
+        "object.assign": "4.1.0"
+      }
+    },
+    "proxy-addr": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz",
+      "integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=",
+      "requires": {
+        "forwarded": "0.1.2",
+        "ipaddr.js": "1.5.2"
+      }
+    },
+    "prr": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
+      "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY="
+    },
+    "pseudomap": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+      "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
+    },
+    "public-encrypt": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz",
+      "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=",
+      "requires": {
+        "bn.js": "4.11.8",
+        "browserify-rsa": "4.0.1",
+        "create-hash": "1.1.3",
+        "parse-asn1": "5.1.0",
+        "randombytes": "2.0.6"
+      }
+    },
+    "pullstream": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/pullstream/-/pullstream-0.4.1.tgz",
+      "integrity": "sha1-1vs79a7Wl+gxFQ6xACwlo/iuExQ=",
+      "requires": {
+        "over": "0.0.5",
+        "readable-stream": "1.0.34",
+        "setimmediate": "1.0.5",
+        "slice-stream": "1.0.0"
+      }
+    },
+    "pump": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz",
+      "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==",
+      "requires": {
+        "end-of-stream": "1.4.1",
+        "once": "1.4.0"
+      }
+    },
+    "pumpify": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.4.0.tgz",
+      "integrity": "sha512-2kmNR9ry+Pf45opRVirpNuIFotsxUGLaYqxIwuR77AYrYRMuFCz9eryHBS52L360O+NcR383CL4QYlMKPq4zYA==",
+      "requires": {
+        "duplexify": "3.5.3",
+        "inherits": "2.0.3",
+        "pump": "2.0.1"
+      },
+      "dependencies": {
+        "pump": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
+          "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
+          "requires": {
+            "end-of-stream": "1.4.1",
+            "once": "1.4.0"
+          }
+        }
+      }
+    },
+    "punycode": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
+      "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
+    },
+    "pupa": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/pupa/-/pupa-1.0.0.tgz",
+      "integrity": "sha1-mpVopa9+ZXuEYqbp1TKHQ1YM7/Y=",
+      "dev": true
+    },
+    "qs": {
+      "version": "6.5.1",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
+      "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A=="
+    },
+    "querystring": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
+      "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
+    },
+    "querystring-es3": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
+      "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM="
+    },
+    "querystringify": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-1.0.0.tgz",
+      "integrity": "sha1-YoYkIRLFtxL6ZU5SZlK/ahP/Bcs="
+    },
+    "raf": {
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.0.tgz",
+      "integrity": "sha512-pDP/NMRAXoTfrhCfyfSEwJAKLaxBU9eApMeBPB1TkDouZmvPerIClV8lTAd+uF8ZiTaVl69e1FCxQrAd/VTjGw==",
+      "requires": {
+        "performance-now": "2.1.0"
+      }
+    },
+    "randomatic": {
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz",
+      "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==",
+      "requires": {
+        "is-number": "3.0.0",
+        "kind-of": "4.0.0"
+      },
+      "dependencies": {
+        "is-number": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+          "requires": {
+            "kind-of": "3.2.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "requires": {
+                "is-buffer": "1.1.6"
+              }
+            }
+          }
+        },
+        "kind-of": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+          "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
+          "requires": {
+            "is-buffer": "1.1.6"
+          }
+        }
+      }
+    },
+    "randombytes": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz",
+      "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==",
+      "requires": {
+        "safe-buffer": "5.1.1"
+      }
+    },
+    "randomfill": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.3.tgz",
+      "integrity": "sha512-YL6GrhrWoic0Eq8rXVbMptH7dAxCs0J+mh5Y0euNekPPYaxEmdVGim6GdoxoRzKW2yJoU8tueifS7mYxvcFDEQ==",
+      "requires": {
+        "randombytes": "2.0.6",
+        "safe-buffer": "5.1.1"
+      }
+    },
+    "range-parser": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
+      "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
+    },
+    "raven": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/raven/-/raven-2.4.0.tgz",
+      "integrity": "sha1-SbfV+DjliT8x3XL4LQWjXkIgP2A=",
+      "requires": {
+        "cookie": "0.3.1",
+        "lsmod": "1.0.0",
+        "md5": "2.2.1",
+        "stack-trace": "0.0.9",
+        "timed-out": "4.0.1",
+        "uuid": "3.0.0"
+      },
+      "dependencies": {
+        "uuid": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz",
+          "integrity": "sha1-Zyj8BFnEUNeWqZwxg3VpvfZy1yg="
+        }
+      }
+    },
+    "raw-body": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz",
+      "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=",
+      "requires": {
+        "bytes": "3.0.0",
+        "http-errors": "1.6.2",
+        "iconv-lite": "0.4.19",
+        "unpipe": "1.0.0"
+      }
+    },
+    "rc": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.5.tgz",
+      "integrity": "sha1-J1zWh/bjs2zHVrqibf7oCnkDAf0=",
+      "requires": {
+        "deep-extend": "0.4.2",
+        "ini": "1.3.5",
+        "minimist": "1.2.0",
+        "strip-json-comments": "2.0.1"
+      },
+      "dependencies": {
+        "minimist": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
+        }
+      }
+    },
+    "react": {
+      "version": "16.2.0",
+      "resolved": "https://registry.npmjs.org/react/-/react-16.2.0.tgz",
+      "integrity": "sha512-ZmIomM7EE1DvPEnSFAHZn9Vs9zJl5A9H7el0EGTE6ZbW9FKe/14IYAlPbC8iH25YarEQxZL+E8VW7Mi7kfQrDQ==",
+      "requires": {
+        "fbjs": "0.8.16",
+        "loose-envify": "1.3.1",
+        "object-assign": "4.1.1",
+        "prop-types": "15.6.0"
+      }
+    },
+    "react-copy-to-clipboard": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.1.tgz",
+      "integrity": "sha512-ELKq31/E3zjFs5rDWNCfFL4NvNFQvGRoJdAKReD/rUPA+xxiLPQmZBZBvy2vgH7V0GE9isIQpT9WXbwIVErYdA==",
+      "requires": {
+        "copy-to-clipboard": "3.0.8",
+        "prop-types": "15.6.0"
+      }
+    },
+    "react-dom": {
+      "version": "16.2.0",
+      "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.2.0.tgz",
+      "integrity": "sha512-zpGAdwHVn9K0091d+hr+R0qrjoJ84cIBFL2uU60KvWBPfZ7LPSrfqviTxGHWN0sjPZb2hxWzMexwrvJdKePvjg==",
+      "requires": {
+        "fbjs": "0.8.16",
+        "loose-envify": "1.3.1",
+        "object-assign": "4.1.1",
+        "prop-types": "15.6.0"
+      }
+    },
+    "react-ga": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/react-ga/-/react-ga-2.4.1.tgz",
+      "integrity": "sha512-hjn2SRdhB3JrdWvodNVegjxLi6xiQyCEELVTvutSB/g57GmfGqSxIvKmxgEN3c6Rjp21rxOYou6TrwzTE288Ig==",
+      "requires": {
+        "prop-types": "15.6.0",
+        "react": "16.2.0"
+      }
+    },
+    "react-hot-loader": {
+      "version": "4.0.0-beta.18",
+      "resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.0.0-beta.18.tgz",
+      "integrity": "sha1-Wj0bW9gTYzOAuIwMZgAZ2/Y4l10=",
+      "requires": {
+        "fast-levenshtein": "2.0.6",
+        "global": "4.3.2",
+        "hoist-non-react-statics": "2.3.1",
+        "react-stand-in": "4.0.0-beta.21"
+      }
+    },
+    "react-inlinesvg": {
+      "version": "0.7.5",
+      "resolved": "https://registry.npmjs.org/react-inlinesvg/-/react-inlinesvg-0.7.5.tgz",
+      "integrity": "sha512-oaU/t2vjf8yNGj3y2G3uYsS58BDwTi2jz0FIbZel3JwuJq/3vwpTR5Bsvp9WfY6sWn+f0KBW2vQ8oDy7csifIg==",
+      "requires": {
+        "httpplease": "0.16.4",
+        "once": "1.4.0"
+      }
+    },
+    "react-redux": {
+      "version": "5.0.6",
+      "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.0.6.tgz",
+      "integrity": "sha512-8taaaGu+J7PMJQDJrk/xiWEYQmdo3mkXw6wPr3K3LxvXis3Fymiq7c13S+Tpls/AyNUAsoONkU81AP0RA6y6Vw==",
+      "requires": {
+        "hoist-non-react-statics": "2.3.1",
+        "invariant": "2.2.2",
+        "lodash": "4.17.5",
+        "lodash-es": "4.17.5",
+        "loose-envify": "1.3.1",
+        "prop-types": "15.6.0"
+      }
+    },
+    "react-resize-detector": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-1.1.0.tgz",
+      "integrity": "sha512-68KVcQlhcWQGXMAie82YueCa4f4yqwEoiQbVyYlSgJEin1zMtNBLLeU/+6FLNf1TTgjwSfpbMTJTw/uU0HNgtQ==",
+      "requires": {
+        "prop-types": "15.6.0"
+      }
+    },
+    "react-smooth": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-1.0.0.tgz",
+      "integrity": "sha1-sp2+vd3bBtIbWwiWIWf7nqwYl9g=",
+      "requires": {
+        "lodash": "4.17.5",
+        "prop-types": "15.6.0",
+        "raf": "3.4.0",
+        "react-transition-group": "2.2.1"
+      }
+    },
+    "react-stand-in": {
+      "version": "4.0.0-beta.21",
+      "resolved": "https://registry.npmjs.org/react-stand-in/-/react-stand-in-4.0.0-beta.21.tgz",
+      "integrity": "sha1-+2lORlyyD6t/NtMoT4K2i716ZX4=",
+      "requires": {
+        "shallowequal": "1.0.2"
+      }
+    },
+    "react-svg": {
+      "version": "2.2.11",
+      "resolved": "https://registry.npmjs.org/react-svg/-/react-svg-2.2.11.tgz",
+      "integrity": "sha512-3tzRscdeJ/RhVKUtG377zc9/TluSv74USJ+nx+WvlwfA5zrN/bomsgLBdz2jCz++cUujkmzHU+XEicramvPoRw==",
+      "requires": {
+        "svg-injector": "1.1.3"
+      }
+    },
+    "react-transition-group": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.2.1.tgz",
+      "integrity": "sha512-q54UBM22bs/CekG8r3+vi9TugSqh0t7qcEVycaRc9M0p0aCEu+h6rp/RFiW7fHfgd1IKpd9oILFTl5QK+FpiPA==",
+      "requires": {
+        "chain-function": "1.0.0",
+        "classnames": "2.2.5",
+        "dom-helpers": "3.3.1",
+        "loose-envify": "1.3.1",
+        "prop-types": "15.6.0",
+        "warning": "3.0.0"
+      }
+    },
+    "read-pkg": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
+      "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
+      "requires": {
+        "load-json-file": "2.0.0",
+        "normalize-package-data": "2.4.0",
+        "path-type": "2.0.0"
+      }
+    },
+    "read-pkg-up": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
+      "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
+      "requires": {
+        "find-up": "2.1.0",
+        "read-pkg": "2.0.0"
+      }
+    },
+    "readable-stream": {
+      "version": "1.0.34",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
+      "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
+      "requires": {
+        "core-util-is": "1.0.2",
+        "inherits": "2.0.3",
+        "isarray": "0.0.1",
+        "string_decoder": "0.10.31"
+      }
+    },
+    "readdirp": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz",
+      "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=",
+      "requires": {
+        "graceful-fs": "4.1.11",
+        "minimatch": "3.0.4",
+        "readable-stream": "2.3.3",
+        "set-immediate-shim": "1.0.1"
+      },
+      "dependencies": {
+        "graceful-fs": {
+          "version": "4.1.11",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+          "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
+        },
+        "isarray": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+        },
+        "readable-stream": {
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
+          "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
+          "requires": {
+            "core-util-is": "1.0.2",
+            "inherits": "2.0.3",
+            "isarray": "1.0.0",
+            "process-nextick-args": "1.0.7",
+            "safe-buffer": "5.1.1",
+            "string_decoder": "1.0.3",
+            "util-deprecate": "1.0.2"
+          }
+        },
+        "string_decoder": {
+          "version": "1.0.3",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
+          "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
+          "requires": {
+            "safe-buffer": "5.1.1"
+          }
+        }
+      }
+    },
+    "recharts": {
+      "version": "1.0.0-beta.10",
+      "resolved": "https://registry.npmjs.org/recharts/-/recharts-1.0.0-beta.10.tgz",
+      "integrity": "sha1-080V32t4edWWjaPJQrX82vJQT+E=",
+      "requires": {
+        "classnames": "2.2.5",
+        "core-js": "2.5.1",
+        "d3-interpolate": "1.1.6",
+        "d3-scale": "1.0.6",
+        "d3-shape": "1.2.0",
+        "lodash": "4.17.5",
+        "prop-types": "15.6.0",
+        "react-resize-detector": "1.1.0",
+        "react-smooth": "1.0.0",
+        "recharts-scale": "0.3.2",
+        "reduce-css-calc": "1.3.0"
+      },
+      "dependencies": {
+        "core-js": {
+          "version": "2.5.1",
+          "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz",
+          "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs="
+        }
+      }
+    },
+    "recharts-scale": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.3.2.tgz",
+      "integrity": "sha1-2sdiFxSkdl0VLLKtvDDHO4MSCMk="
+    },
+    "recursive-copy": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/recursive-copy/-/recursive-copy-2.0.6.tgz",
+      "integrity": "sha1-1ZD5618WW5ahuAvI+cvLXG+ciek=",
+      "requires": {
+        "del": "2.2.2",
+        "emitter-mixin": "0.0.3",
+        "errno": "0.1.6",
+        "graceful-fs": "4.1.11",
+        "junk": "1.0.3",
+        "maximatch": "0.1.0",
+        "mkdirp": "0.5.1",
+        "pify": "2.3.0",
+        "promise": "7.3.1",
+        "slash": "1.0.0"
+      },
+      "dependencies": {
+        "del": {
+          "version": "2.2.2",
+          "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz",
+          "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=",
+          "requires": {
+            "globby": "5.0.0",
+            "is-path-cwd": "1.0.0",
+            "is-path-in-cwd": "1.0.0",
+            "object-assign": "4.1.1",
+            "pify": "2.3.0",
+            "pinkie-promise": "2.0.1",
+            "rimraf": "2.6.2"
+          }
+        },
+        "globby": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz",
+          "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=",
+          "requires": {
+            "array-union": "1.0.2",
+            "arrify": "1.0.1",
+            "glob": "7.1.2",
+            "object-assign": "4.1.1",
+            "pify": "2.3.0",
+            "pinkie-promise": "2.0.1"
+          }
+        },
+        "graceful-fs": {
+          "version": "4.1.11",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+          "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
+        },
+        "pify": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
+        }
+      }
+    },
+    "reduce-css-calc": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz",
+      "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=",
+      "requires": {
+        "balanced-match": "0.4.2",
+        "math-expression-evaluator": "1.2.17",
+        "reduce-function-call": "1.0.2"
+      },
+      "dependencies": {
+        "balanced-match": {
+          "version": "0.4.2",
+          "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz",
+          "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg="
+        }
+      }
+    },
+    "reduce-function-call": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.2.tgz",
+      "integrity": "sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk=",
+      "requires": {
+        "balanced-match": "0.4.2"
+      },
+      "dependencies": {
+        "balanced-match": {
+          "version": "0.4.2",
+          "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz",
+          "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg="
+        }
+      }
+    },
+    "redux": {
+      "version": "3.7.2",
+      "resolved": "https://registry.npmjs.org/redux/-/redux-3.7.2.tgz",
+      "integrity": "sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A==",
+      "requires": {
+        "lodash": "4.17.5",
+        "lodash-es": "4.17.5",
+        "loose-envify": "1.3.1",
+        "symbol-observable": "1.2.0"
+      }
+    },
+    "redux-devtools-extension": {
+      "version": "2.13.2",
+      "resolved": "https://registry.npmjs.org/redux-devtools-extension/-/redux-devtools-extension-2.13.2.tgz",
+      "integrity": "sha1-4Pmo6N/KfBe+kscSSVijuU6ykR0="
+    },
+    "redux-thunk": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.2.0.tgz",
+      "integrity": "sha1-5hWhbha0ehmlFXZhM9Hj6Zt4UuU="
+    },
+    "referrer-policy": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/referrer-policy/-/referrer-policy-1.1.0.tgz",
+      "integrity": "sha1-NXdOtzW/UPtsB46DM0tHI1AgfXk="
+    },
+    "regenerate": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz",
+      "integrity": "sha512-jVpo1GadrDAK59t/0jRx5VxYWQEDkkEKi6+HjE3joFVLfDOh9Xrdh0dF1eSq+BI/SwvTQ44gSscJ8N5zYL61sg=="
+    },
+    "regenerator-runtime": {
+      "version": "0.11.1",
+      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
+      "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
+    },
+    "regenerator-transform": {
+      "version": "0.10.1",
+      "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz",
+      "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==",
+      "requires": {
+        "babel-runtime": "6.26.0",
+        "babel-types": "6.26.0",
+        "private": "0.1.8"
+      }
+    },
+    "regex-cache": {
+      "version": "0.4.4",
+      "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz",
+      "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==",
+      "requires": {
+        "is-equal-shallow": "0.1.3"
+      }
+    },
+    "regexpu-core": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz",
+      "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=",
+      "requires": {
+        "regenerate": "1.3.3",
+        "regjsgen": "0.2.0",
+        "regjsparser": "0.1.5"
+      }
+    },
+    "registry-auth-token": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz",
+      "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==",
+      "requires": {
+        "rc": "1.2.5",
+        "safe-buffer": "5.1.1"
+      }
+    },
+    "registry-url": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz",
+      "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=",
+      "requires": {
+        "rc": "1.2.5"
+      }
+    },
+    "regjsgen": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz",
+      "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc="
+    },
+    "regjsparser": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz",
+      "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=",
+      "requires": {
+        "jsesc": "0.5.0"
+      },
+      "dependencies": {
+        "jsesc": {
+          "version": "0.5.0",
+          "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
+          "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0="
+        }
+      }
+    },
+    "remove-trailing-separator": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+      "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8="
+    },
+    "repeat-element": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz",
+      "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo="
+    },
+    "repeat-string": {
+      "version": "1.6.1",
+      "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+      "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc="
+    },
+    "repeating": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
+      "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
+      "requires": {
+        "is-finite": "1.0.2"
+      }
+    },
+    "require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
+    },
+    "require-from-string": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.1.tgz",
+      "integrity": "sha1-xUUjPp19pmFunVmt+zn8n1iGdv8=",
+      "dev": true
+    },
+    "require-main-filename": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
+      "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE="
+    },
+    "require-uncached": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz",
+      "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=",
+      "dev": true,
+      "requires": {
+        "caller-path": "0.1.0",
+        "resolve-from": "1.0.1"
+      }
+    },
+    "requires-port": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+      "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
+    },
+    "resolve": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz",
+      "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==",
+      "requires": {
+        "path-parse": "1.0.5"
+      }
+    },
+    "resolve-from": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz",
+      "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=",
+      "dev": true
+    },
+    "restore-cursor": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
+      "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
+      "dev": true,
+      "requires": {
+        "onetime": "2.0.1",
+        "signal-exit": "3.0.2"
+      }
+    },
+    "right-align": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz",
+      "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=",
+      "requires": {
+        "align-text": "0.1.4"
+      }
+    },
+    "rimraf": {
+      "version": "2.6.2",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
+      "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
+      "requires": {
+        "glob": "7.1.2"
+      }
+    },
+    "ripemd160": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz",
+      "integrity": "sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=",
+      "requires": {
+        "hash-base": "2.0.2",
+        "inherits": "2.0.3"
+      }
+    },
+    "run-async": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
+      "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
+      "dev": true,
+      "requires": {
+        "is-promise": "2.1.0"
+      }
+    },
+    "run-node": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/run-node/-/run-node-0.2.0.tgz",
+      "integrity": "sha512-Zsnxrr+CMGfm7VFuCj96E8tOpFHTEuZS9EvlXcKapVr2RUvr+fxTMxNgK5fXi3TprSgWoxobtR/3TXZT4na/Ng==",
+      "dev": true
+    },
+    "run-queue": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
+      "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=",
+      "requires": {
+        "aproba": "1.2.0"
+      }
+    },
+    "rx-lite": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz",
+      "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=",
+      "dev": true
+    },
+    "rx-lite-aggregates": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz",
+      "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=",
+      "dev": true,
+      "requires": {
+        "rx-lite": "4.0.8"
+      }
+    },
+    "safe-buffer": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
+      "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
+    },
+    "schema-utils": {
+      "version": "0.4.3",
+      "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.3.tgz",
+      "integrity": "sha512-sgv/iF/T4/SewJkaVpldKC4WjSkz0JsOh2eKtxCPpCO1oR05+7MOF+H476HVRbLArkgA7j5TRJJ4p2jdFkUGQQ==",
+      "requires": {
+        "ajv": "5.5.2",
+        "ajv-keywords": "2.1.1"
+      }
+    },
+    "semver": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
+      "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA=="
+    },
+    "semver-diff": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz",
+      "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=",
+      "requires": {
+        "semver": "5.5.0"
+      }
+    },
+    "send": {
+      "version": "0.16.1",
+      "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz",
+      "integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==",
+      "requires": {
+        "debug": "2.6.9",
+        "depd": "1.1.2",
+        "destroy": "1.0.4",
+        "encodeurl": "1.0.2",
+        "escape-html": "1.0.3",
+        "etag": "1.8.1",
+        "fresh": "0.5.2",
+        "http-errors": "1.6.2",
+        "mime": "1.4.1",
+        "ms": "2.0.0",
+        "on-finished": "2.3.0",
+        "range-parser": "1.2.0",
+        "statuses": "1.3.1"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "statuses": {
+          "version": "1.3.1",
+          "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz",
+          "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4="
+        }
+      }
+    },
+    "serialize-javascript": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.4.0.tgz",
+      "integrity": "sha1-fJWFFNtqwkQ6irwGLcn3iGp/YAU="
+    },
+    "serve-static": {
+      "version": "1.13.1",
+      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz",
+      "integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==",
+      "requires": {
+        "encodeurl": "1.0.2",
+        "escape-html": "1.0.3",
+        "parseurl": "1.3.2",
+        "send": "0.16.1"
+      }
+    },
+    "set-blocking": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+      "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
+    },
+    "set-immediate-shim": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
+      "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E="
+    },
+    "setimmediate": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+      "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
+    },
+    "setprototypeof": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz",
+      "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ="
+    },
+    "sha.js": {
+      "version": "2.4.10",
+      "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.10.tgz",
+      "integrity": "sha512-vnwmrFDlOExK4Nm16J2KMWHLrp14lBrjxMxBJpu++EnsuBmpiYaM/MEs46Vxxm/4FvdP5yTwuCTO9it5FSjrqA==",
+      "requires": {
+        "inherits": "2.0.3",
+        "safe-buffer": "5.1.1"
+      }
+    },
+    "shallowequal": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.0.2.tgz",
+      "integrity": "sha512-zlVXeVUKvo+HEv1e2KQF/csyeMKx2oHvatQ9l6XjCUj3agvC8XGf6R9HvIPDSmp8FNPvx7b5kaEJTRi7CqxtEw=="
+    },
+    "shebang-command": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+      "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+      "requires": {
+        "shebang-regex": "1.0.0"
+      }
+    },
+    "shebang-regex": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+      "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
+    },
+    "signal-exit": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+      "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
+    },
+    "slash": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
+      "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU="
+    },
+    "slice-ansi": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz",
+      "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==",
+      "dev": true,
+      "requires": {
+        "is-fullwidth-code-point": "2.0.0"
+      }
+    },
+    "slice-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/slice-stream/-/slice-stream-1.0.0.tgz",
+      "integrity": "sha1-WzO9ZvATsaf4ZGCwPUY97DmtPqA=",
+      "requires": {
+        "readable-stream": "1.0.34"
+      }
+    },
+    "source-list-map": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz",
+      "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A=="
+    },
+    "source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+    },
+    "source-map-support": {
+      "version": "0.4.18",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
+      "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
+      "requires": {
+        "source-map": "0.5.7"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
+        }
+      }
+    },
+    "spdx-correct": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz",
+      "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=",
+      "requires": {
+        "spdx-license-ids": "1.2.2"
+      }
+    },
+    "spdx-expression-parse": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz",
+      "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw="
+    },
+    "spdx-license-ids": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz",
+      "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc="
+    },
+    "sprintf-js": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+      "dev": true
+    },
+    "ssri": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.2.1.tgz",
+      "integrity": "sha512-y4PjOWlAuxt+yAcXitQYOnOzZpKaH3+f/qGV3OWxbyC2noC9FA9GNC9uILnVdV7jruA1aDKr4OKz3ZDBcVZwFQ==",
+      "requires": {
+        "safe-buffer": "5.1.1"
+      }
+    },
+    "stack-trace": {
+      "version": "0.0.9",
+      "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz",
+      "integrity": "sha1-qPbq7KkGdMMz58Q5U/J1tFFRBpU="
+    },
+    "stackframe": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.0.4.tgz",
+      "integrity": "sha512-to7oADIniaYwS3MhtCa/sQhrxidCCQiF/qp4/m5iN3ipf0Y7Xlri0f6eG29r08aL7JYl8n32AF3Q5GYBZ7K8vw=="
+    },
+    "statuses": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
+      "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
+    },
+    "stream-browserify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz",
+      "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=",
+      "requires": {
+        "inherits": "2.0.3",
+        "readable-stream": "2.3.3"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+        },
+        "readable-stream": {
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
+          "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
+          "requires": {
+            "core-util-is": "1.0.2",
+            "inherits": "2.0.3",
+            "isarray": "1.0.0",
+            "process-nextick-args": "1.0.7",
+            "safe-buffer": "5.1.1",
+            "string_decoder": "1.0.3",
+            "util-deprecate": "1.0.2"
+          }
+        },
+        "string_decoder": {
+          "version": "1.0.3",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
+          "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
+          "requires": {
+            "safe-buffer": "5.1.1"
+          }
+        }
+      }
+    },
+    "stream-each": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.2.tgz",
+      "integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==",
+      "requires": {
+        "end-of-stream": "1.4.1",
+        "stream-shift": "1.0.0"
+      }
+    },
+    "stream-http": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.0.tgz",
+      "integrity": "sha512-sZOFxI/5xw058XIRHl4dU3dZ+TTOIGJR78Dvo0oEAejIt4ou27k+3ne1zYmCV+v7UucbxIFQuOgnkTVHh8YPnw==",
+      "requires": {
+        "builtin-status-codes": "3.0.0",
+        "inherits": "2.0.3",
+        "readable-stream": "2.3.3",
+        "to-arraybuffer": "1.0.1",
+        "xtend": "4.0.1"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+        },
+        "readable-stream": {
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
+          "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
+          "requires": {
+            "core-util-is": "1.0.2",
+            "inherits": "2.0.3",
+            "isarray": "1.0.0",
+            "process-nextick-args": "1.0.7",
+            "safe-buffer": "5.1.1",
+            "string_decoder": "1.0.3",
+            "util-deprecate": "1.0.2"
+          }
+        },
+        "string_decoder": {
+          "version": "1.0.3",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
+          "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
+          "requires": {
+            "safe-buffer": "5.1.1"
+          }
+        }
+      }
+    },
+    "stream-shift": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz",
+      "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI="
+    },
+    "string-hash": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz",
+      "integrity": "sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs="
+    },
+    "string-length": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/string-length/-/string-length-1.0.1.tgz",
+      "integrity": "sha1-VpcPscOFWOnnC3KL894mmsRa36w=",
+      "requires": {
+        "strip-ansi": "3.0.1"
+      }
+    },
+    "string-width": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+      "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+      "requires": {
+        "is-fullwidth-code-point": "2.0.0",
+        "strip-ansi": "4.0.0"
+      },
+      "dependencies": {
+        "strip-ansi": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+          "requires": {
+            "ansi-regex": "3.0.0"
+          }
+        }
+      }
+    },
+    "string_decoder": {
+      "version": "0.10.31",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+      "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+    },
+    "strip-ansi": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+      "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+      "requires": {
+        "ansi-regex": "2.1.1"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+          "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+        }
+      }
+    },
+    "strip-bom": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+      "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM="
+    },
+    "strip-eof": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
+      "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="
+    },
+    "strip-json-comments": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+      "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
+    },
+    "styled-components": {
+      "version": "3.1.6",
+      "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-3.1.6.tgz",
+      "integrity": "sha1-nEQxRvqCxmWan2TdSTvyICSANC4=",
+      "requires": {
+        "buffer": "5.0.8",
+        "css-to-react-native": "2.1.1",
+        "fbjs": "0.8.16",
+        "hoist-non-react-statics": "1.2.0",
+        "is-plain-object": "2.0.4",
+        "prop-types": "15.6.0",
+        "stylis": "3.4.5",
+        "stylis-rule-sheet": "0.0.7",
+        "supports-color": "3.2.3"
+      },
+      "dependencies": {
+        "buffer": {
+          "version": "5.0.8",
+          "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.0.8.tgz",
+          "integrity": "sha512-xXvjQhVNz50v2nPeoOsNqWCLGfiv4ji/gXZM28jnVwdLJxH4mFyqgqCKfaK9zf1KUbG6zTkjLOy7ou+jSMarGA==",
+          "requires": {
+            "base64-js": "1.2.1",
+            "ieee754": "1.1.8"
+          }
+        },
+        "has-flag": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
+          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo="
+        },
+        "hoist-non-react-statics": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz",
+          "integrity": "sha1-qkSM8JhtVcxAdzsXF0t90GbLfPs="
+        },
+        "supports-color": {
+          "version": "3.2.3",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
+          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
+          "requires": {
+            "has-flag": "1.0.0"
+          }
+        }
+      }
+    },
+    "styled-jsx": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-2.2.3.tgz",
+      "integrity": "sha512-n8uOj492SwZBCf4nDYuWSlB+ROWgXffo9srPv7V2EyOyirWJtwMTRTJmNA/EmbFwHsfxvsFGfQjPe2/diocZ+g==",
+      "requires": {
+        "babel-plugin-syntax-jsx": "6.18.0",
+        "babel-types": "6.26.0",
+        "convert-source-map": "1.5.1",
+        "source-map": "0.6.1",
+        "string-hash": "1.1.3",
+        "stylis": "3.4.5",
+        "stylis-rule-sheet": "0.0.7"
+      }
+    },
+    "stylis": {
+      "version": "3.4.5",
+      "resolved": "https://registry.npmjs.org/stylis/-/stylis-3.4.5.tgz",
+      "integrity": "sha512-xxfO3FlxEKcNL1gTX4Tb/tyDLOlUcWCQopceIoQe7sBsX81Na83PNba7DFqMqgb9Rn1VjHkSAWdS9uhL/NVo+Q=="
+    },
+    "stylis-rule-sheet": {
+      "version": "0.0.7",
+      "resolved": "https://registry.npmjs.org/stylis-rule-sheet/-/stylis-rule-sheet-0.0.7.tgz",
+      "integrity": "sha512-qxzlUBO40tgcGMhYxk2gXAPcaZYpfCqHMoVHj92lFMyiFotcqaEl7Jb5eW1ccCanGwf1N9dVBKF9+i/gmDfzyQ=="
+    },
+    "supports-color": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz",
+      "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=",
+      "requires": {
+        "has-flag": "2.0.0"
+      }
+    },
+    "svg-injector": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/svg-injector/-/svg-injector-1.1.3.tgz",
+      "integrity": "sha1-j7oY10GeX4GOcSxPgtg+41dhDmE="
+    },
+    "symbol-observable": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
+      "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ=="
+    },
+    "table": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz",
+      "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==",
+      "dev": true,
+      "requires": {
+        "ajv": "5.5.2",
+        "ajv-keywords": "2.1.1",
+        "chalk": "2.3.0",
+        "lodash": "4.17.5",
+        "slice-ansi": "1.0.0",
+        "string-width": "2.1.1"
+      }
+    },
+    "tapable": {
+      "version": "0.2.8",
+      "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.8.tgz",
+      "integrity": "sha1-mTcqXJmb8t8WCvwNdL7U9HlIzSI="
+    },
+    "term-size": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz",
+      "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=",
+      "requires": {
+        "execa": "0.7.0"
+      }
+    },
+    "text-table": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+      "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+      "dev": true
+    },
+    "thenify": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz",
+      "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=",
+      "requires": {
+        "any-promise": "1.3.0"
+      }
+    },
+    "thenify-all": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+      "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=",
+      "requires": {
+        "thenify": "3.3.0"
+      }
+    },
+    "through": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+      "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
+      "dev": true
+    },
+    "through2": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz",
+      "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=",
+      "requires": {
+        "readable-stream": "2.3.3",
+        "xtend": "4.0.1"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+        },
+        "readable-stream": {
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
+          "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
+          "requires": {
+            "core-util-is": "1.0.2",
+            "inherits": "2.0.3",
+            "isarray": "1.0.0",
+            "process-nextick-args": "1.0.7",
+            "safe-buffer": "5.1.1",
+            "string_decoder": "1.0.3",
+            "util-deprecate": "1.0.2"
+          }
+        },
+        "string_decoder": {
+          "version": "1.0.3",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
+          "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
+          "requires": {
+            "safe-buffer": "5.1.1"
+          }
+        }
+      }
+    },
+    "time-stamp": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-2.0.0.tgz",
+      "integrity": "sha1-lcakRTDhW6jW9KPsuMOj+sRto1c="
+    },
+    "timed-out": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz",
+      "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8="
+    },
+    "timers-browserify": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.6.tgz",
+      "integrity": "sha512-HQ3nbYRAowdVd0ckGFvmJPPCOH/CHleFN/Y0YQCX1DVaB7t+KFvisuyN09fuP8Jtp1CpfSh8O8bMkHbdbPe6Pw==",
+      "requires": {
+        "setimmediate": "1.0.5"
+      }
+    },
+    "tlds": {
+      "version": "1.199.0",
+      "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.199.0.tgz",
+      "integrity": "sha512-NM0jUhibJjEX4g0+1ETxOhuODIDpyvCC0A2BjxrTfMUMZ+uRZc6ZnJl9SmFtAW1s5iQgQIxezFpUij6/6OiRbg=="
+    },
+    "tmp": {
+      "version": "0.0.33",
+      "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+      "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+      "requires": {
+        "os-tmpdir": "1.0.2"
+      }
+    },
+    "to-arraybuffer": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
+      "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M="
+    },
+    "to-fast-properties": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
+      "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc="
+    },
+    "toggle-selection": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
+      "integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI="
+    },
+    "topo": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/topo/-/topo-1.1.0.tgz",
+      "integrity": "sha1-6ddRYV0buH3IZdsYL6HKCl71NtU=",
+      "requires": {
+        "hoek": "2.16.3"
+      }
+    },
+    "touch": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
+      "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
+      "requires": {
+        "nopt": "1.0.10"
+      }
+    },
+    "traverse": {
+      "version": "0.3.9",
+      "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz",
+      "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk="
+    },
+    "trim-right": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
+      "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM="
+    },
+    "tty-browserify": {
+      "version": "0.0.0",
+      "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
+      "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY="
+    },
+    "type-check": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+      "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+      "dev": true,
+      "requires": {
+        "prelude-ls": "1.1.2"
+      }
+    },
+    "type-is": {
+      "version": "1.6.15",
+      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz",
+      "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=",
+      "requires": {
+        "media-typer": "0.3.0",
+        "mime-types": "2.1.17"
+      }
+    },
+    "typedarray": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+      "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
+    },
+    "ua-parser-js": {
+      "version": "0.7.17",
+      "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.17.tgz",
+      "integrity": "sha512-uRdSdu1oA1rncCQL7sCj8vSyZkgtL7faaw9Tc9rZ3mGgraQ7+Pdx7w5mnOSF3gw9ZNG6oc+KXfkon3bKuROm0g=="
+    },
+    "uglify-es": {
+      "version": "3.3.10",
+      "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.10.tgz",
+      "integrity": "sha512-rPzPisCzW68Okj1zNrfa2dR9uEm43SevDmpR6FChoZABFk9dANGnzzBMgHYUXI3609//63fnVkyQ1SQmAMyjww==",
+      "requires": {
+        "commander": "2.14.1",
+        "source-map": "0.6.1"
+      }
+    },
+    "uglify-to-browserify": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz",
+      "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=",
+      "optional": true
+    },
+    "uglifyjs-webpack-plugin": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.1.6.tgz",
+      "integrity": "sha512-VUja+7rYbznEvUaeb8IxOCTUrq4BCb1ml0vffa+mfwKtrAwlqnU0ENF14DtYltV1cxd/HSuK51CCA/D/8kMQVw==",
+      "requires": {
+        "cacache": "10.0.2",
+        "find-cache-dir": "1.0.0",
+        "schema-utils": "0.4.3",
+        "serialize-javascript": "1.4.0",
+        "source-map": "0.6.1",
+        "uglify-es": "3.3.10",
+        "webpack-sources": "1.1.0",
+        "worker-farm": "1.5.2"
+      }
+    },
+    "unfetch": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-3.0.0.tgz",
+      "integrity": "sha1-jR4FE6Ts0OX/LUGmund3Gq6LZII="
+    },
+    "unique-filename": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.0.tgz",
+      "integrity": "sha1-0F8v5AMlYIcfMOk8vnNe6iAVFPM=",
+      "requires": {
+        "unique-slug": "2.0.0"
+      }
+    },
+    "unique-slug": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz",
+      "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=",
+      "requires": {
+        "imurmurhash": "0.1.4"
+      }
+    },
+    "unique-string": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz",
+      "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=",
+      "requires": {
+        "crypto-random-string": "1.0.0"
+      }
+    },
+    "unpipe": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+      "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
+    },
+    "unzip": {
+      "version": "0.1.11",
+      "resolved": "https://registry.npmjs.org/unzip/-/unzip-0.1.11.tgz",
+      "integrity": "sha1-iXScY7BY19kNYZ+GuYqhU107l/A=",
+      "requires": {
+        "binary": "0.3.0",
+        "fstream": "0.1.31",
+        "match-stream": "0.0.2",
+        "pullstream": "0.4.1",
+        "readable-stream": "1.0.34",
+        "setimmediate": "1.0.5"
+      }
+    },
+    "unzip-response": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz",
+      "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c="
+    },
+    "update-notifier": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.3.0.tgz",
+      "integrity": "sha1-TognpruRUUCrCTVZ1wFOPruDdFE=",
+      "requires": {
+        "boxen": "1.3.0",
+        "chalk": "2.3.0",
+        "configstore": "3.1.1",
+        "import-lazy": "2.1.0",
+        "is-installed-globally": "0.1.0",
+        "is-npm": "1.0.0",
+        "latest-version": "3.1.0",
+        "semver-diff": "2.1.0",
+        "xdg-basedir": "3.0.0"
+      }
+    },
+    "url": {
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
+      "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
+      "requires": {
+        "punycode": "1.3.2",
+        "querystring": "0.2.0"
+      }
+    },
+    "url-parse": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.2.0.tgz",
+      "integrity": "sha512-DT1XbYAfmQP65M/mE6OALxmXzZ/z1+e5zk2TcSKe/KiYbNGZxgtttzC0mR/sjopbpOXcbniq7eIKmocJnUWlEw==",
+      "requires": {
+        "querystringify": "1.0.0",
+        "requires-port": "1.0.0"
+      }
+    },
+    "url-parse-lax": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz",
+      "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=",
+      "requires": {
+        "prepend-http": "1.0.4"
+      }
+    },
+    "url-regex": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/url-regex/-/url-regex-4.1.1.tgz",
+      "integrity": "sha512-ViSDgDPNKkrQHI81GLCjdDN+Rsk3tAW/uLXlBOJxtcHzWZjta58Z0APXhfXzS89YszsheMnEvXeDXsWUB53wwA==",
+      "requires": {
+        "ip-regex": "1.0.3",
+        "tlds": "1.199.0"
+      }
+    },
+    "urllite": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/urllite/-/urllite-0.5.0.tgz",
+      "integrity": "sha1-G3u5yj+w25Ug3hE0ZrvPfMNBRRo=",
+      "requires": {
+        "xtend": "4.0.1"
+      }
+    },
+    "user-home": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz",
+      "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=",
+      "dev": true
+    },
+    "useragent": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz",
+      "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==",
+      "requires": {
+        "lru-cache": "4.1.1",
+        "tmp": "0.0.33"
+      }
+    },
+    "util": {
+      "version": "0.10.3",
+      "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
+      "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
+      "requires": {
+        "inherits": "2.0.1"
+      },
+      "dependencies": {
+        "inherits": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
+          "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE="
+        }
+      }
+    },
+    "util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+    },
+    "utils-merge": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+      "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
+    },
+    "uuid": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz",
+      "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g=="
+    },
+    "v8flags": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz",
+      "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=",
+      "dev": true,
+      "requires": {
+        "user-home": "1.1.1"
+      }
+    },
+    "validate-npm-package-license": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz",
+      "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=",
+      "requires": {
+        "spdx-correct": "1.0.2",
+        "spdx-expression-parse": "1.0.4"
+      }
+    },
+    "validator": {
+      "version": "8.2.0",
+      "resolved": "https://registry.npmjs.org/validator/-/validator-8.2.0.tgz",
+      "integrity": "sha512-Yw5wW34fSv5spzTXNkokD6S6/Oq92d8q/t14TqsS3fAiA1RYnxSFSIZ+CY3n6PGGRCq5HhJTSepQvFUS2QUDxA=="
+    },
+    "vary": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+      "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
+    },
+    "vm-browserify": {
+      "version": "0.0.4",
+      "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz",
+      "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=",
+      "requires": {
+        "indexof": "0.0.1"
+      }
+    },
+    "walk": {
+      "version": "2.3.9",
+      "resolved": "https://registry.npmjs.org/walk/-/walk-2.3.9.tgz",
+      "integrity": "sha1-MbTbZnjyrgHDnqn7hyWpAx5Vins=",
+      "requires": {
+        "foreachasync": "3.0.0"
+      }
+    },
+    "warning": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
+      "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=",
+      "requires": {
+        "loose-envify": "1.3.1"
+      }
+    },
+    "watchpack": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.4.0.tgz",
+      "integrity": "sha1-ShRyvLuVK9Cpu0A2gB+VTfs5+qw=",
+      "requires": {
+        "async": "2.6.0",
+        "chokidar": "1.7.0",
+        "graceful-fs": "4.1.11"
+      },
+      "dependencies": {
+        "graceful-fs": {
+          "version": "4.1.11",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+          "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
+        }
+      }
+    },
+    "webpack": {
+      "version": "3.10.0",
+      "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.10.0.tgz",
+      "integrity": "sha512-fxxKXoicjdXNUMY7LIdY89tkJJJ0m1Oo8PQutZ5rLgWbV5QVKI15Cn7+/IHnRTd3vfKfiwBx6SBqlorAuNA8LA==",
+      "requires": {
+        "acorn": "5.4.1",
+        "acorn-dynamic-import": "2.0.2",
+        "ajv": "5.5.2",
+        "ajv-keywords": "2.1.1",
+        "async": "2.6.0",
+        "enhanced-resolve": "3.4.1",
+        "escope": "3.6.0",
+        "interpret": "1.1.0",
+        "json-loader": "0.5.7",
+        "json5": "0.5.1",
+        "loader-runner": "2.3.0",
+        "loader-utils": "1.1.0",
+        "memory-fs": "0.4.1",
+        "mkdirp": "0.5.1",
+        "node-libs-browser": "2.1.0",
+        "source-map": "0.5.7",
+        "supports-color": "4.5.0",
+        "tapable": "0.2.8",
+        "uglifyjs-webpack-plugin": "0.4.6",
+        "watchpack": "1.4.0",
+        "webpack-sources": "1.1.0",
+        "yargs": "8.0.2"
+      },
+      "dependencies": {
+        "camelcase": {
+          "version": "1.2.1",
+          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
+          "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk="
+        },
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
+        },
+        "uglify-js": {
+          "version": "2.8.29",
+          "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
+          "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=",
+          "requires": {
+            "source-map": "0.5.7",
+            "uglify-to-browserify": "1.0.2",
+            "yargs": "3.10.0"
+          },
+          "dependencies": {
+            "yargs": {
+              "version": "3.10.0",
+              "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
+              "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
+              "requires": {
+                "camelcase": "1.2.1",
+                "cliui": "2.1.0",
+                "decamelize": "1.2.0",
+                "window-size": "0.1.0"
+              }
+            }
+          }
+        },
+        "uglifyjs-webpack-plugin": {
+          "version": "0.4.6",
+          "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz",
+          "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=",
+          "requires": {
+            "source-map": "0.5.7",
+            "uglify-js": "2.8.29",
+            "webpack-sources": "1.1.0"
+          }
+        }
+      }
+    },
+    "webpack-dev-middleware": {
+      "version": "1.12.0",
+      "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-1.12.0.tgz",
+      "integrity": "sha1-007++y7dp+HTtdvgcolRMhllFwk=",
+      "requires": {
+        "memory-fs": "0.4.1",
+        "mime": "1.4.1",
+        "path-is-absolute": "1.0.1",
+        "range-parser": "1.2.0",
+        "time-stamp": "2.0.0"
+      }
+    },
+    "webpack-hot-middleware": {
+      "version": "2.21.0",
+      "resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.21.0.tgz",
+      "integrity": "sha512-P6xiOLy10QlSVSO7GanU9PLxN6zLLQ7RG16MPTvmFwf2KUG7jMp6m+fmdgsR7xoaVVLA7OlX3YO6JjoZEKjCuA==",
+      "requires": {
+        "ansi-html": "0.0.7",
+        "html-entities": "1.2.1",
+        "querystring": "0.2.0",
+        "strip-ansi": "3.0.1"
+      }
+    },
+    "webpack-sources": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.1.0.tgz",
+      "integrity": "sha512-aqYp18kPphgoO5c/+NaUvEeACtZjMESmDChuD3NBciVpah3XpMEU9VAAtIaB1BsfJWWTSdv8Vv1m3T0aRk2dUw==",
+      "requires": {
+        "source-list-map": "2.0.0",
+        "source-map": "0.6.1"
+      }
+    },
+    "whatwg-fetch": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz",
+      "integrity": "sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ="
+    },
+    "which": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz",
+      "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==",
+      "requires": {
+        "isexe": "2.0.0"
+      }
+    },
+    "which-module": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+      "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
+    },
+    "widest-line": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.0.tgz",
+      "integrity": "sha1-AUKk6KJD+IgsAjOqDgKBqnYVInM=",
+      "requires": {
+        "string-width": "2.1.1"
+      }
+    },
+    "window-size": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz",
+      "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0="
+    },
+    "wordwrap": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
+      "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8="
+    },
+    "worker-farm": {
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.5.2.tgz",
+      "integrity": "sha512-XxiQ9kZN5n6mmnW+mFJ+wXjNNI/Nx4DIdaAKLX1Bn6LYBWlN/zaBhu34DQYPZ1AJobQuu67S2OfDdNSVULvXkQ==",
+      "requires": {
+        "errno": "0.1.6",
+        "xtend": "4.0.1"
+      }
+    },
+    "wrap-ansi": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
+      "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
+      "requires": {
+        "string-width": "1.0.2",
+        "strip-ansi": "3.0.1"
+      },
+      "dependencies": {
+        "is-fullwidth-code-point": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+          "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+          "requires": {
+            "number-is-nan": "1.0.1"
+          }
+        },
+        "string-width": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+          "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+          "requires": {
+            "code-point-at": "1.1.0",
+            "is-fullwidth-code-point": "1.0.0",
+            "strip-ansi": "3.0.1"
+          }
+        }
+      }
+    },
+    "wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+    },
+    "write": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz",
+      "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=",
+      "dev": true,
+      "requires": {
+        "mkdirp": "0.5.1"
+      }
+    },
+    "write-file-atomic": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz",
+      "integrity": "sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==",
+      "requires": {
+        "graceful-fs": "4.1.11",
+        "imurmurhash": "0.1.4",
+        "signal-exit": "3.0.2"
+      },
+      "dependencies": {
+        "graceful-fs": {
+          "version": "4.1.11",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+          "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
+        }
+      }
+    },
+    "write-file-webpack-plugin": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/write-file-webpack-plugin/-/write-file-webpack-plugin-4.2.0.tgz",
+      "integrity": "sha512-sIjfV+M1Ia8p/lVcLjvM2I0Lq/40tCMZe+k0Pxg2TG6TKjUgHGwQeM42QdYLiHAIAITGQK1HEQA3YknFubzfDQ==",
+      "requires": {
+        "chalk": "1.1.3",
+        "debug": "2.6.9",
+        "filesize": "3.6.0",
+        "lodash": "4.17.5",
+        "mkdirp": "0.5.1",
+        "moment": "2.20.1"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "2.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
+        },
+        "chalk": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+          "requires": {
+            "ansi-styles": "2.2.1",
+            "escape-string-regexp": "1.0.5",
+            "has-ansi": "2.0.0",
+            "strip-ansi": "3.0.1",
+            "supports-color": "2.0.0"
+          }
+        },
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "supports-color": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+          "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
+        }
+      }
+    },
+    "x-xss-protection": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/x-xss-protection/-/x-xss-protection-1.0.0.tgz",
+      "integrity": "sha1-iYr7k4abJGYc+cUvnujbjtB2Tdk="
+    },
+    "xdg-basedir": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz",
+      "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ="
+    },
+    "xmlhttprequest": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz",
+      "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw="
+    },
+    "xss-filters": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/xss-filters/-/xss-filters-1.2.7.tgz",
+      "integrity": "sha1-Wfod4gHzby80cNysX1jMwoMLCpo="
+    },
+    "xtend": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
+      "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
+    },
+    "y18n": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
+      "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE="
+    },
+    "yallist": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+      "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
+    },
+    "yargs": {
+      "version": "8.0.2",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz",
+      "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=",
+      "requires": {
+        "camelcase": "4.1.0",
+        "cliui": "3.2.0",
+        "decamelize": "1.2.0",
+        "get-caller-file": "1.0.2",
+        "os-locale": "2.1.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.1.1",
+        "which-module": "2.0.0",
+        "y18n": "3.2.1",
+        "yargs-parser": "7.0.0"
+      },
+      "dependencies": {
+        "cliui": {
+          "version": "3.2.0",
+          "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
+          "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
+          "requires": {
+            "string-width": "1.0.2",
+            "strip-ansi": "3.0.1",
+            "wrap-ansi": "2.1.0"
+          },
+          "dependencies": {
+            "string-width": {
+              "version": "1.0.2",
+              "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+              "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+              "requires": {
+                "code-point-at": "1.1.0",
+                "is-fullwidth-code-point": "1.0.0",
+                "strip-ansi": "3.0.1"
+              }
+            }
+          }
+        },
+        "is-fullwidth-code-point": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+          "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+          "requires": {
+            "number-is-nan": "1.0.1"
+          }
+        }
+      }
+    },
+    "yargs-parser": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz",
+      "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=",
+      "requires": {
+        "camelcase": "4.1.0"
+      }
+    }
+  }
+}

+ 86 - 0
package.json

@@ -0,0 +1,86 @@
+{
+  "name": "kutt",
+  "version": "1.0.0",
+  "description": "Modern URL shortener.",
+  "main": "./server/server.js",
+  "scripts": {
+    "dev": "node ./server/server.js",
+    "build": "next build ./client",
+    "start": "NODE_ENV=production node ./server/server.js",
+    "lint": "./node_modules/.bin/eslint . --fix",
+    "lint:nofix": "./node_modules/.bin/eslint ."
+  },
+  "husky": {
+    "hooks": {
+      "pre-commit": "npm run lint:nofix"
+    }
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/TheDevs-Network/kutt.git"
+  },
+  "keywords": [
+    "url-shortener"
+  ],
+  "author": "Pouria Ezzati <ezzati.upt@gmail.com>",
+  "license": "MIT",
+  "bugs": {
+    "url": "https://github.com/TheDevs-Network/kutt/issues"
+  },
+  "homepage": "https://github.com/TheDevs-Network/kutt#readme",
+  "dependencies": {
+    "axios": "^0.17.1",
+    "bcryptjs": "^2.4.3",
+    "body-parser": "^1.18.2",
+    "cookie-parser": "^1.4.3",
+    "date-fns": "^1.29.0",
+    "email-validator": "^1.1.1",
+    "express": "^4.16.2",
+    "express-validator": "^4.3.0",
+    "geoip-lite": "^1.2.1",
+    "helmet": "^3.9.0",
+    "js-cookie": "^2.2.0",
+    "jsonwebtoken": "^8.1.0",
+    "jwt-decode": "^2.2.0",
+    "lodash": "^4.17.4",
+    "morgan": "^1.9.0",
+    "nanoid": "^1.0.1",
+    "neo4j-driver": "^1.5.2",
+    "next": "^5.0.0",
+    "next-redux-wrapper": "^1.3.5",
+    "nodemailer": "^4.4.1",
+    "passport": "^0.4.0",
+    "passport-jwt": "^3.0.1",
+    "passport-local": "^1.0.0",
+    "passport-localapikey": "0.0.3",
+    "prop-types": "^15.6.0",
+    "raven": "^2.4.0",
+    "react": "^16.2.0",
+    "react-copy-to-clipboard": "^5.0.1",
+    "react-dom": "^16.2.0",
+    "react-ga": "^2.4.1",
+    "react-inlinesvg": "^0.7.5",
+    "react-redux": "^5.0.6",
+    "recharts": "^1.0.0-beta.10",
+    "redux": "^3.7.2",
+    "redux-devtools-extension": "^2.13.2",
+    "redux-thunk": "^2.2.0",
+    "styled-components": "^3.1.6",
+    "url-regex": "^4.1.1",
+    "useragent": "^2.2.1"
+  },
+  "devDependencies": {
+    "babel-cli": "^6.26.0",
+    "babel-eslint": "^8.0.2",
+    "babel-plugin-styled-components": "^1.3.0",
+    "eslint": "^4.12.0",
+    "eslint-config-airbnb": "^16.1.0",
+    "eslint-config-prettier": "^2.9.0",
+    "eslint-plugin-import": "^2.8.0",
+    "eslint-plugin-jsx-a11y": "^6.0.2",
+    "eslint-plugin-prettier": "^2.6.0",
+    "eslint-plugin-react": "^7.5.1",
+    "husky": "^0.15.0-rc.6",
+    "prettier": "^1.10.2"
+  }
+}

+ 30 - 0
server/config.example.js

@@ -0,0 +1,30 @@
+module.exports = {
+  PORT: 3000,
+
+  /* The domain that this website is on */
+  DEFAULT_DOMAIN: 'kutt.it',
+
+  /* Neo4j database credential details */
+  DB_URI: 'bolt://localhost',
+  DB_USERNAME: '',
+  DB_PASSWORD: '',
+
+  /* A passphrase to encrypt JWT. Use a long and secure key. */
+  JWT_SECRET: 'securekey',
+
+  /*
+    reCaptcha secret key
+    Create one in https://www.google.com/recaptcha/intro/
+  */
+  RECAPTCHA_SECRET_KEY: '',
+
+  /*
+    Your email host details to use to send verification emails.
+    More info on http://nodemailer.com/
+  */
+  MAIL_HOST: '',
+  MAIL_PORT: 587,
+  MAIL_SECURE: false,
+  MAIL_USER: '',
+  MAIL_PASSWORD: '',
+};

+ 179 - 0
server/controllers/authController.js

@@ -0,0 +1,179 @@
+const fs = require('fs');
+const path = require('path');
+const passport = require('passport');
+const JWT = require('jsonwebtoken');
+const axios = require('axios');
+const config = require('../config');
+const transporter = require('../mail/mail');
+const { resetMailText, verifyMailText } = require('../mail/text');
+const {
+  createUser,
+  changePassword,
+  generateApiKey,
+  getUser,
+  verifyUser,
+  requestPasswordReset,
+  resetPassword,
+} = require('../db/user');
+
+/* Read email template */
+const resetEmailTemplatePath = path.join(__dirname, '../mail/template-reset.html');
+const verifyEmailTemplatePath = path.join(__dirname, '../mail/template-verify.html');
+const resetEmailTemplate = fs.readFileSync(resetEmailTemplatePath, { encoding: 'utf-8' });
+const verifyEmailTemplate = fs.readFileSync(verifyEmailTemplatePath, { encoding: 'utf-8' });
+
+/* Function to generate JWT */
+const signToken = user =>
+  JWT.sign(
+    {
+      iss: 'ApiAuth',
+      sub: user.email,
+      iat: new Date().getTime(),
+      exp: new Date().setDate(new Date().getDate() + 7),
+    },
+    config.JWT_SECRET
+  );
+
+/* Passport.js authentication controller */
+const authenticate = (type, error, isStrict = true) =>
+  function auth(req, res, next) {
+    if (req.user) return next();
+    return passport.authenticate(type, (err, user) => {
+      if (err) return res.status(400);
+      if (!user && isStrict) return res.status(401).json({ error });
+      req.user = user;
+      return next();
+    })(req, res, next);
+  };
+
+exports.authLocal = authenticate('local', 'Login email and/or password are wrong.');
+exports.authJwt = authenticate('jwt', 'Unauthorized.');
+exports.authJwtLoose = authenticate('jwt', 'Unauthorized.', false);
+exports.authApikey = authenticate('localapikey', 'API key is not correct.', false);
+
+/* reCaptcha controller */
+exports.recaptcha = async (req, res, next) => {
+  if (!req.user) {
+    const isReCaptchaValid = await axios({
+      method: 'post',
+      url: 'https://www.google.com/recaptcha/api/siteverify',
+      headers: {
+        'Content-type': 'application/x-www-form-urlencoded',
+      },
+      params: {
+        secret: config.RECAPTCHA_SECRET_KEY,
+        response: req.body.reCaptchaToken,
+        remoteip: req.ip,
+      },
+    });
+    if (!isReCaptchaValid.data.success) {
+      return res.status(401).json({ error: 'reCAPTCHA is not valid. Try again.' });
+    }
+  }
+  return next();
+};
+
+exports.signup = async (req, res) => {
+  const { email, password } = req.body;
+  if (password.length > 64) {
+    return res.status(400).json({ error: 'Maximum password length is 64.' });
+  }
+  if (email.length > 64) {
+    return res.status(400).json({ error: 'Maximum email length is 64.' });
+  }
+  const user = await getUser({ email });
+  if (user && user.verified) return res.status(403).json({ error: 'Email is already in use.' });
+  const newUser = await createUser({ email, password });
+  const mail = await transporter.sendMail({
+    from: config.MAIL_USER,
+    to: newUser.email,
+    subject: 'Verify your account',
+    text: verifyMailText
+      .replace('{{verification}}', newUser.verificationToken)
+      .replace('{{domain}}', config.DEFAULT_DOMAIN),
+    html: verifyEmailTemplate
+      .replace('{{verification}}', newUser.verificationToken)
+      .replace('{{domain}}', config.DEFAULT_DOMAIN),
+  });
+  if (mail.accepted.length) {
+    return res.status(201).json({ email, message: 'Verification email has been sent.' });
+  }
+  return res.status(400).json({ error: "Couldn't send verification email. Try again." });
+};
+
+exports.login = ({ user }, res) => {
+  const token = signToken(user);
+  return res.status(200).json({ token });
+};
+
+exports.renew = ({ user }, res) => {
+  const token = signToken(user);
+  return res.status(200).json({ token });
+};
+
+exports.verify = async (req, res, next) => {
+  const { verificationToken = '' } = req.params;
+  const user = await verifyUser({ verificationToken });
+  if (user) {
+    const token = signToken(user);
+    req.user = { token };
+  }
+  return next();
+};
+
+exports.changePassword = async ({ body: { password }, user }, res) => {
+  if (password.length < 8) {
+    return res.status(400).json({ error: 'Password must be at least 8 chars long.' });
+  }
+  if (password.length > 64) {
+    return res.status(400).json({ error: 'Maximum password length is 64.' });
+  }
+  const changedUser = await changePassword({ email: user.email, password });
+  if (changedUser) {
+    return res.status(200).json({ message: 'Your password has been changed successfully.' });
+  }
+  return res.status(400).json({ error: "Couldn't change the password. Try again later" });
+};
+
+exports.generateApiKey = async ({ user }, res) => {
+  const { apikey } = await generateApiKey({ email: user.email });
+  if (apikey) {
+    return res.status(201).json({ apikey });
+  }
+  return res.status(400).json({ error: 'Sorry, an error occured. Please try again later.' });
+};
+
+exports.userSettings = ({ user }, res) =>
+  res.status(200).json({ apikey: user.apikey || '', customDomain: user.domain || '' });
+
+exports.requestPasswordReset = async ({ body: { email } }, res) => {
+  const user = await requestPasswordReset({ email });
+  if (!user) {
+    return res.status(400).json({ error: "Couldn't reset password." });
+  }
+  const mail = await transporter.sendMail({
+    from: config.MAIL_USER,
+    to: user.email,
+    subject: 'Reset your password',
+    text: resetMailText
+      .replace('{{resetpassword}}', user.resetPasswordToken)
+      .replace('{{domain}}', config.DEFAULT_DOMAIN),
+    html: resetEmailTemplate
+      .replace('{{resetpassword}}', user.resetPasswordToken)
+      .replace('{{domain}}', config.DEFAULT_DOMAIN),
+  });
+  if (mail.accepted.length) {
+    return res.status(200).json({ email, message: 'Reset password email has been sent.' });
+  }
+  return res.status(400).json({ error: "Couldn't reset password." });
+};
+
+exports.resetPassword = async (req, res, next) => {
+  const { resetPasswordToken = '' } = req.params;
+  const user = await resetPassword({ resetPasswordToken });
+  if (user) {
+    const token = signToken(user);
+    req.user = { token };
+  }
+  return next();
+};

+ 172 - 0
server/controllers/urlController.js

@@ -0,0 +1,172 @@
+const urlRegex = require('url-regex');
+const URL = require('url');
+const useragent = require('useragent');
+const geoip = require('geoip-lite');
+const bcrypt = require('bcryptjs');
+const {
+  createShortUrl,
+  createVisit,
+  findUrl,
+  getStats,
+  getUrls,
+  getCustomDomain,
+  setCustomDomain,
+  deleteCustomDomain,
+  deleteUrl,
+} = require('../db/url');
+const config = require('../config');
+
+const preservedUrls = [
+  'login',
+  'logout',
+  'signup',
+  'reset-password',
+  'resetpassword',
+  'url-password',
+  'settings',
+  'stats',
+  'verify',
+  'api',
+  '404',
+  'static',
+  'images',
+];
+
+exports.preservedUrls = preservedUrls;
+
+exports.urlShortener = async ({ body, user }, res) => {
+  if (!body.target) return res.status(400).json({ error: 'No target has been provided.' });
+  if (body.target.length > 1024) {
+    return res.status(400).json({ error: 'Maximum URL length is 1024.' });
+  }
+  const isValidUrl = urlRegex({ exact: true, strict: false }).test(body.target);
+  if (!isValidUrl) return res.status(400).json({ error: 'URL is not valid.' });
+  const target = URL.parse(body.target).protocol ? body.target : `http://${body.target}`;
+  if (body.password && body.password.length > 64) {
+    return res.status(400).json({ error: 'Maximum password length is 64.' });
+  }
+  if (user && body.customurl) {
+    if (!/^[a-zA-Z1-9-_]+$/g.test(body.customurl.trim())) {
+      return res.status(400).json({ error: 'Custom URL is not valid.' });
+    }
+    if (preservedUrls.some(url => url === body.customurl)) {
+      return res.status(400).json({ error: "You can't use this custom URL name." });
+    }
+    if (body.customurl.length > 64) {
+      return res.status(400).json({ error: 'Maximum custom URL length is 64.' });
+    }
+    const urls = await findUrl({ id: body.customurl || '' });
+    if (urls.length) {
+      const urlWithNoDomain = !user.domain && urls.some(url => !url.domain);
+      const urlWithDmoain = user.domain && urls.some(url => url.domain === user.domain);
+      if (urlWithNoDomain || urlWithDmoain) {
+        return res.status(400).json({ error: 'Custom URL is already in use.' });
+      }
+    }
+  }
+  const url = await createShortUrl({ ...body, target, user });
+  return res.json(url);
+};
+
+const browsersList = ['IE', 'Firefox', 'Chrome', 'Opera', 'Safari', 'Edge'];
+const osList = ['Windows', 'Mac Os X', 'Linux', 'Chrom OS', 'Android', 'iOS'];
+const filterInBrowser = agent => item =>
+  agent.family.toLowerCase().includes(item.toLocaleLowerCase());
+const filterInOs = agent => item =>
+  agent.os.family.toLowerCase().includes(item.toLocaleLowerCase());
+
+exports.goToUrl = async (req, res, next) => {
+  const { host } = req.headers;
+  const id = req.params.id || req.body.id;
+  const domain = host !== config.DEFAULT_DOMAIN && host;
+  const agent = useragent.parse(req.headers['user-agent']);
+  const [browser = 'Other'] = browsersList.filter(filterInBrowser(agent));
+  const [os = 'Other'] = osList.filter(filterInOs(agent));
+  const referrer = req.header('Referer') && URL.parse(req.header('Referer')).hostname;
+  const location = geoip.lookup(req.ip);
+  const country = location && location.country;
+  const urls = await findUrl({ id, domain });
+  if (!urls && !urls.length) return next();
+  const [url] = urls;
+  if (url.password && !req.body.password) {
+    req.protectedUrl = id;
+    return next();
+  }
+  if (url.password) {
+    const isMatch = await bcrypt.compare(req.body.password, url.password);
+    if (!isMatch) {
+      return res.status(401).json({ error: 'Password is not correct' });
+    }
+    if (url.user) {
+      await createVisit({
+        browser,
+        country: country || 'Unknown',
+        domain,
+        id: url.id,
+        os,
+        referrer: referrer || 'Direct',
+      });
+    }
+    return res.status(200).json({ target: url.target });
+  }
+  if (url.user) {
+    await createVisit({
+      browser,
+      country,
+      domain,
+      id: url.id,
+      os,
+      referrer,
+    });
+  }
+  return res.redirect(url.target);
+};
+
+exports.getUrls = async ({ body, user }, res) => {
+  const urlsList = await getUrls({ options: body, user });
+  return res.json(urlsList);
+};
+
+exports.setCustomDomain = async ({ body: { customDomain }, user }, res) => {
+  if (customDomain.length > 40) {
+    return res.status(400).json({ error: 'Maximum custom domain length is 40.' });
+  }
+  if (customDomain === config.DEFAULT_DOMAIN) {
+    return res.status(400).json({ error: "You can't use default domain." });
+  }
+  const isValidDomain = urlRegex({ exact: true, strict: false }).test(customDomain);
+  if (!isValidDomain) return res.status(400).json({ error: 'Domain is not valid.' });
+  const isOwned = await getCustomDomain({ customDomain });
+  if (isOwned && isOwned.email !== user.email) {
+    return res
+      .status(400)
+      .json({ error: 'Domain is already taken. Contact us for multiple users.' });
+  }
+  const userCustomDomain = await setCustomDomain({ user, customDomain });
+  if (userCustomDomain) return res.status(201).json({ customDomain: userCustomDomain.name });
+  return res.status(400).json({ error: "Couldn't set custom domain." });
+};
+
+exports.deleteCustomDomain = async ({ user }, res) => {
+  const response = await deleteCustomDomain({ user });
+  if (response) return res.status(200).json({ message: 'Domain deleted successfully' });
+  return res.status(400).json({ error: "Couldn't delete custom domain." });
+};
+
+exports.deleteUrl = async ({ body: { id, domain }, user }, res) => {
+  if (!id) return res.status(400).json({ error: 'No id has been provided.' });
+  const customDomain = domain !== config.DEFAULT_DOMAIN && domain;
+  const urls = await findUrl({ id, domain: customDomain });
+  if (!urls && !urls.length) return res.status(400).json({ error: "Couldn't find the short URL." });
+  const response = await deleteUrl({ id, domain: customDomain, user });
+  if (response) return res.status(200).json({ message: 'Sort URL deleted successfully' });
+  return res.status(400).json({ error: "Couldn't delete short URL." });
+};
+
+exports.getStats = async ({ body: { id, domain }, user }, res) => {
+  if (!id) return res.status(400).json({ error: 'No id has been provided.' });
+  const customDomain = domain !== config.DEFAULT_DOMAIN && domain;
+  const stats = await getStats({ id, domain: customDomain, user });
+  if (!stats) return res.status(400).json({ error: 'Could not get the short URL stats.' });
+  return res.status(200).json(stats);
+};

+ 27 - 0
server/controllers/validateBodyController.js

@@ -0,0 +1,27 @@
+const { body } = require('express-validator/check');
+const { validationResult } = require('express-validator/check');
+
+exports.validationCriterias = [
+  body('email')
+    .exists()
+    .withMessage('Email must be provided.')
+    .isEmail()
+    .withMessage('Email is not valid.')
+    .trim()
+    .normalizeEmail(),
+  body('password', 'Password must be at least 8 chars long.')
+    .exists()
+    .withMessage('Password must be provided.')
+    .isLength({ min: 8 }),
+];
+
+exports.validateBody = (req, res, next) => {
+  const errors = validationResult(req);
+  if (!errors.isEmpty()) {
+    const errorsObj = errors.mapped();
+    const emailError = errorsObj.email && errorsObj.email.msg;
+    const passwordError = errorsObj.password && errorsObj.password.msg;
+    return res.status(400).json({ error: emailError || passwordError });
+  }
+  return next();
+};

+ 9 - 0
server/db/neo4j.js

@@ -0,0 +1,9 @@
+const neo4j = require('neo4j-driver').v1;
+const config = require('../config');
+
+const driver = neo4j.driver(
+  config.DB_URI,
+  neo4j.auth.basic(config.DB_USERNAME, config.DB_PASSWORD)
+);
+
+module.exports = driver;

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels