diff --git a/packages/pubsweet-client/.babelrc b/packages/pubsweet-client/.babelrc
new file mode 100644
index 0000000000000000000000000000000000000000..8e05f6ce40a1827aba4a03391e136548a0589e5d
--- /dev/null
+++ b/packages/pubsweet-client/.babelrc
@@ -0,0 +1,7 @@
+{
+  "presets": [
+    "env",
+    "react",
+    "stage-2"
+  ]
+}
diff --git a/packages/pubsweet-client/.eslintrc b/packages/pubsweet-client/.eslintrc
new file mode 100644
index 0000000000000000000000000000000000000000..2c80442dbcd4471f228a994d6a9321437de8418e
--- /dev/null
+++ b/packages/pubsweet-client/.eslintrc
@@ -0,0 +1,21 @@
+{
+  "extends": [
+    "standard",
+    "plugin:react/recommended",
+    "prettier",
+    "prettier/standard",
+    "prettier/react",
+  ],
+  "parserOptions": {
+    "ecmaFeatures": {
+      "jsx": true
+    },
+    "ecmaVersion": 2017
+  },
+  "env": {
+    "browser": true
+  },
+  "globals": {
+    "PUBSWEET_COMPONENTS": true
+  }
+}
diff --git a/packages/pubsweet-client/.gitignore b/packages/pubsweet-client/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..4d1df6e2c7a98808e41d2ef34e5ae71115078371
--- /dev/null
+++ b/packages/pubsweet-client/.gitignore
@@ -0,0 +1,12 @@
+node_modules/*
+npm-debug.log
+public/assets/*
+public/uploads/*
+.happypack
+.DS_Store
+.idea
+
+test.log
+scratch
+coverage
+mockapp.json
diff --git a/packages/pubsweet-client/.gitlab-ci.yml b/packages/pubsweet-client/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d1cd29c5d58949f702dca86467eb75664bc29d28
--- /dev/null
+++ b/packages/pubsweet-client/.gitlab-ci.yml
@@ -0,0 +1,14 @@
+image: pubsweet/pubsweet-test-base
+
+before_script:
+  - yarn
+
+test:
+  script:
+    - npm test
+  coverage: '/^All files\s+\|\s+(\d+.\d+)\s\|.*$/'
+
+lint:
+  script:
+    - npm run lint
+
diff --git a/packages/pubsweet-client/.lintstagedrc b/packages/pubsweet-client/.lintstagedrc
new file mode 100644
index 0000000000000000000000000000000000000000..f8d909b97a21f1943b144709cdfe21ddcb45e267
--- /dev/null
+++ b/packages/pubsweet-client/.lintstagedrc
@@ -0,0 +1,3 @@
+{
+  "*.{js, jsx}": ["prettier --write", "eslint --fix", "git add"]
+}
diff --git a/packages/pubsweet-client/.prettierrc b/packages/pubsweet-client/.prettierrc
new file mode 100644
index 0000000000000000000000000000000000000000..63b7777aac4961f6217197df86d7e2fcc4f98a5e
--- /dev/null
+++ b/packages/pubsweet-client/.prettierrc
@@ -0,0 +1,6 @@
+{
+  "singleQuote": true,
+  "semi": false,
+  "printWidth": 80,
+  "trailingComma": "all"
+}
diff --git a/packages/pubsweet-client/CONTRIBUTING.md b/packages/pubsweet-client/CONTRIBUTING.md
new file mode 100644
index 0000000000000000000000000000000000000000..66b3456044b75cf2f3ea98f79a1a7f3877cb5f9d
--- /dev/null
+++ b/packages/pubsweet-client/CONTRIBUTING.md
@@ -0,0 +1,21 @@
+# CONTRIBUTING
+
+## Branches
+We maintain master as the production branch and tag it with release names. If you wish to contribute to PubSweet then you need to make a branch and then issue a pull request following this procedure:
+1. create a user account on Coko GitLab : http://gitlab.coko.foundation
+2. Clone master with ```git clone git@gitlab.coko.foundation:pubsweet/core.git```
+3. Create a new branch and work off that. Please name the branch which sensibly identifies the feature you are working on. You can push the branch to Coko Gitlab at anytime.
+
+## Getting your contributions merged
+This is a two part process, first ask for comments, then ask for the changes to be merged.
+1. Ask for feedback generate a Merge Request (Pull Request) from the gitlab interface but do not assign this request to anyone. You do this from the Gitlab UI on your branch.
+2. Look at the feedback and alter your branch as necessary. 
+3. To merge with master - generate a merge request (Pull Request) and assign to Jure Triglav. You do this from the Gitlab UI on your branch.
+
+We encourage feedback and discussion from as many people as possible on Merge Requests!
+
+## Bug reports, feature requests, support questions
+This is all done through GitLab using their native issue tracker
+1. Visit the master issue tracker for PubSweet (https://gitlab.coko.foundation/pubsweet/core/issues)
+2. Add an issue 
+3. Tag the issue with 'support', 'bug', or 'feature' to identify the nature of your issue
\ No newline at end of file
diff --git a/packages/pubsweet-client/LICENSE b/packages/pubsweet-client/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..e218890defd2f464caae083d38bc1b29e0d78a45
--- /dev/null
+++ b/packages/pubsweet-client/LICENSE
@@ -0,0 +1,7 @@
+Copyright (c) 2015 Adam Hyde
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/packages/pubsweet-client/README.md b/packages/pubsweet-client/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..7cd88bff0625cc4f834cbb1300f9085daf40f2e1
--- /dev/null
+++ b/packages/pubsweet-client/README.md
@@ -0,0 +1,8 @@
+# Build status
+
+[![build status](https://gitlab.coko.foundation/pubsweet/pubsweet-client/badges/master/build.svg)](https://gitlab.coko.foundation/pubsweet/pubsweet-client/builds)
+
+# Description
+
+This is the PubSweet client, to be used as a dependency in PubSweet apps, such as: [Editoria](https://gitlab.coko.foundation/editoria/editoria).
+
diff --git a/packages/pubsweet-client/config/default.js b/packages/pubsweet-client/config/default.js
new file mode 100644
index 0000000000000000000000000000000000000000..3e1cd0b18868e4104b84b65b2f3ab6056b573320
--- /dev/null
+++ b/packages/pubsweet-client/config/default.js
@@ -0,0 +1,5 @@
+module.exports = {
+  'pubsweet-client': {
+    API_ENDPOINT: 'http://localhost:3000/api',
+  },
+}
diff --git a/packages/pubsweet-client/config/test.js b/packages/pubsweet-client/config/test.js
new file mode 100644
index 0000000000000000000000000000000000000000..bf905ac797acea67e836c178eda263d5adf93862
--- /dev/null
+++ b/packages/pubsweet-client/config/test.js
@@ -0,0 +1,11 @@
+module.exports = {
+  'pubsweet-client': {
+    API_ENDPOINT: 'http://example.com',
+    'update-subscriber': {
+      visible: true,
+    },
+  },
+  authsome: {
+    mode: 'fake-mode'
+  },
+}
diff --git a/packages/pubsweet-client/package.json b/packages/pubsweet-client/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..f928c59f11011ddc44fb5c944516d921f07bf121
--- /dev/null
+++ b/packages/pubsweet-client/package.json
@@ -0,0 +1,82 @@
+{
+  "name": "pubsweet-client",
+  "version": "1.0.0",
+  "main": "src/index.js",
+  "scripts": {
+    "compile": "babel -d lib/ src/",
+    "lint": "eslint --ext js,jsx src test",
+    "precommit": "lint-staged",
+    "test": "jest",
+    "testci": "gitlab-ci-multi-runner exec docker test"
+  },
+  "engines": {
+    "node": ">=8.6.0",
+    "npm": ">=5.0.0"
+  },
+  "author": "Collaborative Knowledge Foundation",
+  "license": "MIT",
+  "dependencies": {
+    "authsome": "0.0.9",
+    "config": "^1.21.0",
+    "eslint-config-prettier": "^2.6.0",
+    "event-source-polyfill": "^0.0.10",
+    "global": "^4.3.1",
+    "husky": "^0.14.3",
+    "isomorphic-fetch": "^2.1.1",
+    "lint-staged": "^4.2.3",
+    "lodash": "^4.0.0",
+    "prettier": "^1.7.4",
+    "prop-types": "^15.5.8",
+    "pubsweet-component-login": "^0.5.2",
+    "react": "^15.4.4",
+    "react-css-themr": "^2.1.2",
+    "react-redux": "^5.0.2",
+    "react-router-dom": "^4.2.2",
+    "react-router-redux": "next",
+    "redux": "^3.6.0",
+    "redux-form": "^7.0.3",
+    "redux-logger": "^3.0.1",
+    "redux-thunk": "^2.2.0"
+  },
+  "devDependencies": {
+    "babel-cli": "^6.26.0",
+    "babel-preset-env": "^1.6.0",
+    "babel-preset-react": "^6.24.1",
+    "babel-preset-stage-2": "^6.24.1",
+    "enzyme": "^2.9.1",
+    "eslint": "^4.8.0",
+    "eslint-config-standard": "^10.2.1",
+    "eslint-plugin-import": "^2.7.0",
+    "eslint-plugin-node": "^5.2.0",
+    "eslint-plugin-promise": "^3.5.0",
+    "eslint-plugin-react": "^7.4.0",
+    "eslint-plugin-standard": "^3.0.1",
+    "eventsourcemock": "^1.0.1",
+    "isomorphic-form-data": "^1.0.0",
+    "jest": "^21.2.1",
+    "nock": "^9.0.14",
+    "react-dom": "^15.6.1",
+    "react-test-renderer": "^15.6.1",
+    "redux-mock-store": "^1.3.0"
+  },
+  "peerDependencies": {
+    "pubsweet-server": "^1.0.0-beta.2"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://gitlab.coko.foundation/pubsweet/pubsweet-client"
+  },
+  "jest": {
+    "testRegex": "/test/.+test.jsx?$",
+    "collectCoverage": true,
+    "collectCoverageFrom": [
+      "src/**/*.{js,jsx}"
+    ],
+    "globals": {
+      "window": {}
+    },
+    "transformIgnorePatterns": [
+      "<rootDir>/node_modules/(?!pubsweet)"
+    ]
+  }
+}
diff --git a/packages/pubsweet-client/src/actions/collections.js b/packages/pubsweet-client/src/actions/collections.js
new file mode 100644
index 0000000000000000000000000000000000000000..2a06e4044450e9c13a98f082aba1c4c4f08c799c
--- /dev/null
+++ b/packages/pubsweet-client/src/actions/collections.js
@@ -0,0 +1,244 @@
+import * as api from '../helpers/api'
+import * as T from './types'
+
+const collectionUrl = (collection, suffix) => {
+  let url = '/collections'
+
+  if (collection) url += `/${collection.id}`
+
+  if (suffix) url += `/${suffix}`
+
+  return url
+}
+
+function getCollectionsRequest() {
+  return {
+    type: T.GET_COLLECTIONS_REQUEST,
+  }
+}
+
+function getCollectionsFailure(error) {
+  return {
+    type: T.GET_COLLECTIONS_FAILURE,
+    error: error,
+  }
+}
+
+function getCollectionsSuccess(collections) {
+  return {
+    type: T.GET_COLLECTIONS_SUCCESS,
+    collections: collections,
+    receivedAt: Date.now(),
+  }
+}
+
+export function getCollections(options) {
+  return dispatch => {
+    dispatch(getCollectionsRequest())
+
+    let url = collectionUrl()
+
+    if (options && options.fields) {
+      url += '?fields=' + encodeURIComponent(options.fields.join(','))
+    }
+
+    return api
+      .get(url)
+      .then(
+        collections => dispatch(getCollectionsSuccess(collections)),
+        err => dispatch(getCollectionsFailure(err)),
+      )
+  }
+}
+
+function getCollectionTeamsRequest() {
+  return {
+    type: T.GET_COLLECTION_TEAMS_REQUEST,
+  }
+}
+
+function getCollectionTeamsFailure(error) {
+  return {
+    type: T.GET_COLLECTION_TEAMS_FAILURE,
+    error: error,
+  }
+}
+
+function getCollectionTeamsSuccess(teams) {
+  return {
+    type: T.GET_COLLECTION_TEAMS_SUCCESS,
+    teams,
+    receivedAt: Date.now(),
+  }
+}
+
+export function getCollectionTeams(collection) {
+  return dispatch => {
+    dispatch(getCollectionTeamsRequest())
+
+    let url = collectionUrl(collection, 'teams')
+
+    return api
+      .get(url)
+      .then(
+        teams => dispatch(getCollectionTeamsSuccess(teams)),
+        err => dispatch(getCollectionTeamsFailure(err)),
+      )
+  }
+}
+
+function createCollectionRequest(collection) {
+  return {
+    type: T.CREATE_COLLECTION_REQUEST,
+    collection: collection,
+  }
+}
+
+function createCollectionSuccess(collection) {
+  return {
+    type: T.CREATE_COLLECTION_SUCCESS,
+    collection: collection,
+  }
+}
+
+function createCollectionFailure(collection, error) {
+  return {
+    type: T.CREATE_COLLECTION_FAILURE,
+    isFetching: false,
+    collection: collection,
+    error: error,
+  }
+}
+
+export function createCollection(collection) {
+  return dispatch => {
+    dispatch(createCollectionRequest(collection))
+
+    const url = collectionUrl()
+
+    return api
+      .create(url, collection)
+      .then(
+        collection => dispatch(createCollectionSuccess(collection)),
+        err => dispatch(createCollectionFailure(collection, err)),
+      )
+  }
+}
+
+function getCollectionRequest(collection) {
+  return {
+    type: T.GET_COLLECTION_REQUEST,
+    collection: collection,
+  }
+}
+
+function getCollectionSuccess(collection) {
+  return {
+    type: T.GET_COLLECTION_SUCCESS,
+    collection: collection,
+    receivedAt: Date.now(),
+  }
+}
+
+function getCollectionFailure(collection, error) {
+  return {
+    type: T.GET_COLLECTION_FAILURE,
+    isFetching: false,
+    collection: collection,
+    error: error,
+  }
+}
+
+export function getCollection(collection) {
+  return dispatch => {
+    dispatch(getCollectionRequest(collection))
+
+    const url = collectionUrl(collection)
+
+    return api
+      .get(url)
+      .then(
+        collection => dispatch(getCollectionSuccess(collection)),
+        err => dispatch(getCollectionFailure(collection, err)),
+      )
+  }
+}
+
+function updateCollectionRequest(collection) {
+  return {
+    type: T.UPDATE_COLLECTION_REQUEST,
+    collection: collection,
+  }
+}
+
+function updateCollectionSuccess(collection, update) {
+  return {
+    type: T.UPDATE_COLLECTION_SUCCESS,
+    collection: collection,
+    update: update,
+    receivedAt: Date.now(),
+  }
+}
+
+function updateCollectionFailure(collection, error) {
+  return {
+    type: T.UPDATE_COLLECTION_FAILURE,
+    isFetching: false,
+    collection: collection,
+    error: error,
+  }
+}
+
+export function updateCollection(collection) {
+  return dispatch => {
+    dispatch(updateCollectionRequest(collection))
+
+    const url = collectionUrl(collection)
+
+    return api
+      .update(url, collection)
+      .then(
+        update => dispatch(updateCollectionSuccess(collection, update)),
+        err => dispatch(updateCollectionFailure(collection, err)),
+      )
+  }
+}
+
+function deleteCollectionRequest(collection) {
+  return {
+    type: T.DELETE_COLLECTION_REQUEST,
+    collection: collection,
+    update: { deleted: true },
+  }
+}
+
+function deleteCollectionSuccess(collection) {
+  return {
+    type: T.DELETE_COLLECTION_SUCCESS,
+    collection: collection,
+  }
+}
+
+function deleteCollectionFailure(collection, error) {
+  return {
+    type: T.DELETE_COLLECTION_FAILURE,
+    collection: collection,
+    update: { deleted: undefined },
+    error: error,
+  }
+}
+
+export function deleteCollection(collection) {
+  return dispatch => {
+    dispatch(deleteCollectionRequest(collection))
+
+    const url = collectionUrl(collection)
+
+    return api
+      .remove(url)
+      .then(
+        () => dispatch(deleteCollectionSuccess(collection)),
+        err => dispatch(deleteCollectionFailure(collection, err)),
+      )
+  }
+}
diff --git a/packages/pubsweet-client/src/actions/currentUser.js b/packages/pubsweet-client/src/actions/currentUser.js
new file mode 100644
index 0000000000000000000000000000000000000000..a3a08b0a2f30ce926b989b1f976c26bfa4494409
--- /dev/null
+++ b/packages/pubsweet-client/src/actions/currentUser.js
@@ -0,0 +1,33 @@
+import * as api from '../helpers/api'
+import * as T from './types'
+
+function getCurrentUserRequest() {
+  return {
+    type: T.GET_CURRENT_USER_REQUEST,
+  }
+}
+
+function getCurrentUserSuccess(user) {
+  return {
+    type: T.GET_CURRENT_USER_SUCCESS,
+    user,
+  }
+}
+
+function getCurrentUserFailure(error) {
+  return {
+    type: T.GET_CURRENT_USER_FAILURE,
+    error,
+  }
+}
+
+export function getCurrentUser() {
+  return dispatch => {
+    dispatch(getCurrentUserRequest())
+
+    return api
+      .get('/users/authenticate')
+      .then(user => dispatch(getCurrentUserSuccess(user)))
+      .catch(err => dispatch(getCurrentUserFailure(err)))
+  }
+}
diff --git a/packages/pubsweet-client/src/actions/fileUpload.js b/packages/pubsweet-client/src/actions/fileUpload.js
new file mode 100644
index 0000000000000000000000000000000000000000..ee67814de34d550bb8746867443accd2213defca
--- /dev/null
+++ b/packages/pubsweet-client/src/actions/fileUpload.js
@@ -0,0 +1,48 @@
+import request from '../helpers/api'
+import * as T from './types'
+
+function fileUploadRequest() {
+  return {
+    type: T.FILE_UPLOAD_REQUEST,
+    isFetching: true,
+  }
+}
+
+function fileUploadSuccess(file) {
+  return {
+    type: T.FILE_UPLOAD_SUCCESS,
+    isFetching: false,
+    file: file,
+  }
+}
+
+function fileUploadFailure(message) {
+  return {
+    type: T.FILE_UPLOAD_FAILURE,
+    isFetching: false,
+    error: message,
+  }
+}
+
+export function fileUpload(file) {
+  return dispatch => {
+    dispatch(fileUploadRequest())
+
+    const data = new FormData()
+    data.append('file', file)
+
+    let opts = {
+      method: 'POST',
+      headers: {
+        Accept: 'text/plain', // the response is a URL
+        // TODO: set the Location header of the response instead
+      },
+      body: data,
+    }
+
+    return request('/upload', opts).then(
+      file => dispatch(fileUploadSuccess(file)),
+      err => dispatch(fileUploadFailure(err)),
+    )
+  }
+}
diff --git a/packages/pubsweet-client/src/actions/fragments.js b/packages/pubsweet-client/src/actions/fragments.js
new file mode 100644
index 0000000000000000000000000000000000000000..0f9197be1c6a1590c50336c3fcd233effe358f11
--- /dev/null
+++ b/packages/pubsweet-client/src/actions/fragments.js
@@ -0,0 +1,211 @@
+import * as api from '../helpers/api'
+import * as T from './types'
+
+export const fragmentUrl = (collection, fragment) => {
+  let url = ''
+  if (collection) url += `/collections/${collection.id}`
+  url += '/fragments'
+  if (fragment && fragment.id) url += `/${fragment.id}`
+
+  return url
+}
+
+function getFragmentsRequest(collection) {
+  return {
+    type: T.GET_FRAGMENTS_REQUEST,
+    collection: collection,
+  }
+}
+
+function getFragmentsSuccess(collection, fragments) {
+  return {
+    type: T.GET_FRAGMENTS_SUCCESS,
+    collection: collection,
+    fragments: fragments,
+    receivedAt: Date.now(),
+  }
+}
+
+function getFragmentsFailure(error) {
+  return {
+    type: T.GET_FRAGMENTS_FAILURE,
+    error: error,
+  }
+}
+
+export function getFragments(collection, options) {
+  return dispatch => {
+    dispatch(getFragmentsRequest(collection))
+
+    let url = fragmentUrl(collection)
+
+    if (options && options.fields) {
+      url += '?fields=' + encodeURIComponent(options.fields.join(','))
+    }
+
+    return api
+      .get(url)
+      .then(
+        fragments => dispatch(getFragmentsSuccess(collection, fragments)),
+        err => dispatch(getFragmentsFailure(err)),
+      )
+  }
+}
+
+function createFragmentRequest(fragment) {
+  return {
+    type: T.CREATE_FRAGMENT_REQUEST,
+    fragment: fragment,
+  }
+}
+
+function createFragmentSuccess(collection, fragment) {
+  return {
+    type: T.CREATE_FRAGMENT_SUCCESS,
+    collection: collection,
+    fragment: fragment,
+  }
+}
+
+function createFragmentFailure(fragment, error) {
+  return {
+    type: T.CREATE_FRAGMENT_FAILURE,
+    isFetching: false,
+    fragment: fragment,
+    error: error,
+  }
+}
+
+export function createFragment(collection, fragment) {
+  return dispatch => {
+    dispatch(createFragmentRequest(fragment))
+
+    const url = fragmentUrl(collection, fragment)
+
+    return api
+      .create(url, fragment)
+      .then(
+        fragment => dispatch(createFragmentSuccess(collection, fragment)),
+        err => dispatch(createFragmentFailure(fragment, err)),
+      )
+  }
+}
+
+function getFragmentRequest(fragment) {
+  return {
+    type: T.GET_FRAGMENT_REQUEST,
+    fragment: fragment,
+  }
+}
+
+function getFragmentSuccess(fragment) {
+  return {
+    type: T.GET_FRAGMENT_SUCCESS,
+    fragment: fragment,
+    receivedAt: Date.now(),
+  }
+}
+
+function getFragmentFailure(fragment, error) {
+  return {
+    type: T.GET_FRAGMENT_FAILURE,
+    isFetching: false,
+    fragment: fragment,
+    error: error,
+  }
+}
+
+export function getFragment(collection, fragment) {
+  return dispatch => {
+    dispatch(getFragmentRequest(fragment))
+
+    const url = fragmentUrl(collection, fragment)
+
+    return api
+      .get(url)
+      .then(
+        fragment => dispatch(getFragmentSuccess(fragment)),
+        err => dispatch(getFragmentFailure(fragment, err)),
+      )
+  }
+}
+
+function updateFragmentRequest(fragment) {
+  return {
+    type: T.UPDATE_FRAGMENT_REQUEST,
+    fragment: fragment,
+  }
+}
+
+function updateFragmentSuccess(fragment, update) {
+  return {
+    type: T.UPDATE_FRAGMENT_SUCCESS,
+    fragment: fragment,
+    update: update,
+    receivedAt: Date.now(),
+  }
+}
+
+function updateFragmentFailure(fragment, error) {
+  return {
+    type: T.UPDATE_FRAGMENT_FAILURE,
+    isFetching: false,
+    fragment: fragment,
+    error: error,
+  }
+}
+
+export function updateFragment(collection, fragment) {
+  return dispatch => {
+    dispatch(updateFragmentRequest(fragment))
+
+    const url = fragmentUrl(collection, fragment)
+
+    return api
+      .update(url, fragment)
+      .then(
+        update => dispatch(updateFragmentSuccess(fragment, update)),
+        err => dispatch(updateFragmentFailure(fragment, err)),
+      )
+  }
+}
+
+function deleteFragmentRequest(fragment) {
+  return {
+    type: T.DELETE_FRAGMENT_REQUEST,
+    fragment: fragment,
+    update: { deleted: true },
+  }
+}
+
+function deleteFragmentSuccess(collection, fragment) {
+  return {
+    type: T.DELETE_FRAGMENT_SUCCESS,
+    collection: collection,
+    fragment: fragment,
+  }
+}
+
+function deleteFragmentFailure(fragment, error) {
+  return {
+    type: T.DELETE_FRAGMENT_FAILURE,
+    fragment: fragment,
+    update: { deleted: undefined },
+    error: error,
+  }
+}
+
+export function deleteFragment(collection, fragment) {
+  return dispatch => {
+    dispatch(deleteFragmentRequest(fragment))
+
+    const url = fragmentUrl(collection, fragment)
+
+    return api
+      .remove(url)
+      .then(
+        json => dispatch(deleteFragmentSuccess(collection, fragment)),
+        err => dispatch(deleteFragmentFailure(fragment, err)),
+      )
+  }
+}
diff --git a/packages/pubsweet-client/src/actions/index.js b/packages/pubsweet-client/src/actions/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..0ad2f8c2a6f287006007fe015dd4a4957ebce461
--- /dev/null
+++ b/packages/pubsweet-client/src/actions/index.js
@@ -0,0 +1,35 @@
+import { RESET_ERROR_MESSAGE } from './types'
+
+import * as collections from './collections'
+import * as currentUser from './currentUser'
+import * as fileUpload from './fileUpload'
+import * as fragments from './fragments'
+import * as teams from './teams'
+import * as users from './users'
+
+import componentActions from '../components/actions'
+
+// Resets the currently visible error message.
+const resetErrorMessage = () => ({
+  type: RESET_ERROR_MESSAGE,
+})
+
+// Hydrate hydrates the store from a persistent store, the backend.
+// It gets collections, fragments and user data (via token).
+const hydrate = () => dispatch =>
+  Promise.all([
+    dispatch(currentUser.getCurrentUser()),
+    dispatch(collections.getCollections()),
+  ])
+
+export default {
+  ...collections,
+  ...currentUser,
+  ...fileUpload,
+  ...fragments,
+  ...teams,
+  ...users,
+  ...componentActions,
+  hydrate,
+  resetErrorMessage,
+}
diff --git a/packages/pubsweet-client/src/actions/teams.js b/packages/pubsweet-client/src/actions/teams.js
new file mode 100644
index 0000000000000000000000000000000000000000..741bf172a26fd230d0ed6614d823889fc6dac988
--- /dev/null
+++ b/packages/pubsweet-client/src/actions/teams.js
@@ -0,0 +1,159 @@
+import * as api from '../helpers/api'
+import * as T from './types'
+
+const teamUrl = team => {
+  let url = '/teams'
+
+  if (team) url += `/${team.id}`
+
+  return url
+}
+
+function getTeamsRequest() {
+  return {
+    type: T.GET_TEAMS_REQUEST,
+    isFetching: true,
+  }
+}
+
+function getTeamsSuccess(teams) {
+  return {
+    type: T.GET_TEAMS_SUCCESS,
+    isFetching: false,
+    teams: teams,
+  }
+}
+
+function getTeamsFailure(message) {
+  return {
+    type: T.GET_TEAMS_FAILURE,
+    isFetching: false,
+    message,
+  }
+}
+
+export function getTeams() {
+  return dispatch => {
+    dispatch(getTeamsRequest())
+
+    return api
+      .get(teamUrl())
+      .then(
+        teams => dispatch(getTeamsSuccess(teams)),
+        err => dispatch(getTeamsFailure(err)),
+      )
+  }
+}
+
+function createTeamRequest(team) {
+  return {
+    type: T.CREATE_TEAM_REQUEST,
+    team: team,
+  }
+}
+
+function createTeamSuccess(team) {
+  return {
+    type: T.CREATE_TEAM_SUCCESS,
+    team: team,
+  }
+}
+
+function createTeamFailure(team, error) {
+  return {
+    type: T.CREATE_TEAM_FAILURE,
+    isFetching: false,
+    team: team,
+    error: error,
+  }
+}
+
+export function createTeam(team) {
+  return dispatch => {
+    dispatch(createTeamRequest(team))
+
+    const url = teamUrl()
+
+    return api
+      .create(url, team)
+      .then(
+        team => dispatch(createTeamSuccess(team)),
+        err => dispatch(createTeamFailure(team, err)),
+      )
+  }
+}
+
+function updateTeamRequest(team) {
+  return {
+    type: T.UPDATE_TEAM_REQUEST,
+    team: team,
+  }
+}
+
+function updateTeamSuccess(team) {
+  return {
+    type: T.UPDATE_TEAM_SUCCESS,
+    team: team,
+  }
+}
+
+function updateTeamFailure(team, error) {
+  return {
+    type: T.UPDATE_TEAM_FAILURE,
+    isFetching: false,
+    team: team,
+    error: error,
+  }
+}
+
+export function updateTeam(team) {
+  return dispatch => {
+    dispatch(updateTeamRequest(team))
+    const url = teamUrl(team)
+
+    return api
+      .update(url, team)
+      .then(
+        team => dispatch(updateTeamSuccess(team)),
+        err => dispatch(updateTeamFailure(team, err)),
+      )
+  }
+}
+
+function deleteTeamRequest(team) {
+  return {
+    type: T.DELETE_TEAM_REQUEST,
+    team: team,
+  }
+}
+
+function deleteTeamSuccess(team) {
+  return {
+    type: T.DELETE_TEAM_SUCCESS,
+    team: team,
+  }
+}
+
+function deleteTeamFailure(team, error) {
+  return {
+    type: T.DELETE_TEAM_FAILURE,
+    isFetching: false,
+    team: team,
+    error: error,
+  }
+}
+
+export function deleteTeam(team) {
+  return dispatch => {
+    dispatch(deleteTeamRequest(team))
+
+    const url = teamUrl(team)
+
+    return api
+      .remove(url)
+      .then(
+        team => dispatch(deleteTeamSuccess(team)),
+        err => dispatch(deleteTeamFailure(team, err)),
+      )
+  }
+}
diff --git a/packages/pubsweet-client/src/actions/types.js b/packages/pubsweet-client/src/actions/types.js
new file mode 100644
index 0000000000000000000000000000000000000000..0ff0ae9f0389cfdf22b44d58889a43b8b50db8d7
--- /dev/null
+++ b/packages/pubsweet-client/src/actions/types.js
@@ -0,0 +1,98 @@
+// Action types
+
+export const GET_COLLECTIONS_REQUEST = 'GET_COLLECTIONS_REQUEST'
+export const GET_COLLECTIONS_SUCCESS = 'GET_COLLECTIONS_SUCCESS'
+export const GET_COLLECTIONS_FAILURE = 'GET_COLLECTIONS_FAILURE'
+
+export const CREATE_COLLECTION_REQUEST = 'CREATE_COLLECTION_REQUEST'
+export const CREATE_COLLECTION_SUCCESS = 'CREATE_COLLECTION_SUCCESS'
+export const CREATE_COLLECTION_FAILURE = 'CREATE_COLLECTION_FAILURE'
+
+export const GET_COLLECTION_REQUEST = 'GET_COLLECTION_REQUEST'
+export const GET_COLLECTION_SUCCESS = 'GET_COLLECTION_SUCCESS'
+export const GET_COLLECTION_FAILURE = 'GET_COLLECTION_FAILURE'
+
+export const GET_COLLECTION_TEAMS_REQUEST = 'GET_COLLECTION_TEAMS_REQUEST'
+export const GET_COLLECTION_TEAMS_SUCCESS = 'GET_COLLECTION_TEAMS_SUCCESS'
+export const GET_COLLECTION_TEAMS_FAILURE = 'GET_COLLECTION_TEAMS_FAILURE'
+
+export const UPDATE_COLLECTION_REQUEST = 'UPDATE_COLLECTION_REQUEST'
+export const UPDATE_COLLECTION_SUCCESS = 'UPDATE_COLLECTION_SUCCESS'
+export const UPDATE_COLLECTION_FAILURE = 'UPDATE_COLLECTION_FAILURE'
+
+export const PATCH_COLLECTION_REQUEST = 'PATCH_COLLECTION_REQUEST'
+export const PATCH_COLLECTION_SUCCESS = 'PATCH_COLLECTION_SUCCESS'
+export const PATCH_COLLECTION_FAILURE = 'PATCH_COLLECTION_FAILURE'
+
+export const DELETE_COLLECTION_REQUEST = 'DELETE_COLLECTION_REQUEST'
+export const DELETE_COLLECTION_SUCCESS = 'DELETE_COLLECTION_SUCCESS'
+export const DELETE_COLLECTION_FAILURE = 'DELETE_COLLECTION_FAILURE'
+
+export const GET_FRAGMENTS_REQUEST = 'GET_FRAGMENTS_REQUEST'
+export const GET_FRAGMENTS_SUCCESS = 'GET_FRAGMENTS_SUCCESS'
+export const GET_FRAGMENTS_FAILURE = 'GET_FRAGMENTS_FAILURE'
+
+export const CREATE_FRAGMENT_REQUEST = 'CREATE_FRAGMENT_REQUEST'
+export const CREATE_FRAGMENT_SUCCESS = 'CREATE_FRAGMENT_SUCCESS'
+export const CREATE_FRAGMENT_FAILURE = 'CREATE_FRAGMENT_FAILURE'
+
+export const GET_FRAGMENT_REQUEST = 'GET_FRAGMENT_REQUEST'
+export const GET_FRAGMENT_SUCCESS = 'GET_FRAGMENT_SUCCESS'
+export const GET_FRAGMENT_FAILURE = 'GET_FRAGMENT_FAILURE'
+
+export const UPDATE_FRAGMENT_REQUEST = 'UPDATE_FRAGMENT_REQUEST'
+export const UPDATE_FRAGMENT_SUCCESS = 'UPDATE_FRAGMENT_SUCCESS'
+export const UPDATE_FRAGMENT_FAILURE = 'UPDATE_FRAGMENT_FAILURE'
+
+export const DELETE_FRAGMENT_REQUEST = 'DELETE_FRAGMENT_REQUEST'
+export const DELETE_FRAGMENT_SUCCESS = 'DELETE_FRAGMENT_SUCCESS'
+export const DELETE_FRAGMENT_FAILURE = 'DELETE_FRAGMENT_FAILURE'
+
+export const GET_CURRENT_USER_REQUEST = 'GET_CURRENT_USER_REQUEST'
+export const GET_CURRENT_USER_SUCCESS = 'GET_CURRENT_USER_SUCCESS'
+export const GET_CURRENT_USER_FAILURE = 'GET_CURRENT_USER_FAILURE'
+
+export const GET_USER_REQUEST = 'GET_USER_REQUEST'
+export const GET_USER_SUCCESS = 'GET_USER_SUCCESS'
+export const GET_USER_FAILURE = 'GET_USER_FAILURE'
+
+export const GET_USERS_REQUEST = 'GET_USERS_REQUEST'
+export const GET_USERS_SUCCESS = 'GET_USERS_SUCCESS'
+export const GET_USERS_FAILURE = 'GET_USERS_FAILURE'
+
+export const UPDATE_USER_REQUEST = 'UPDATE_USER_REQUEST'
+export const UPDATE_USER_SUCCESS = 'UPDATE_USER_SUCCESS'
+export const UPDATE_USER_FAILURE = 'UPDATE_USER_FAILURE'
+
+export const GET_TEAMS_REQUEST = 'GET_TEAMS_REQUEST'
+export const GET_TEAMS_SUCCESS = 'GET_TEAMS_SUCCESS'
+export const GET_TEAMS_FAILURE = 'GET_TEAMS_FAILURE'
+
+export const CREATE_TEAM_REQUEST = 'CREATE_TEAM_REQUEST'
+export const CREATE_TEAM_SUCCESS = 'CREATE_TEAM_SUCCESS'
+export const CREATE_TEAM_FAILURE = 'CREATE_TEAM_FAILURE'
+
+export const UPDATE_TEAM_REQUEST = 'UPDATE_TEAM_REQUEST'
+export const UPDATE_TEAM_SUCCESS = 'UPDATE_TEAM_SUCCESS'
+export const UPDATE_TEAM_FAILURE = 'UPDATE_TEAM_FAILURE'
+
+export const DELETE_TEAM_REQUEST = 'DELETE_TEAM_REQUEST'
+export const DELETE_TEAM_SUCCESS = 'DELETE_TEAM_SUCCESS'
+export const DELETE_TEAM_FAILURE = 'DELETE_TEAM_FAILURE'
+
+export const FILE_UPLOAD_REQUEST = 'FILE_UPLOAD_REQUEST'
+export const FILE_UPLOAD_SUCCESS = 'FILE_UPLOAD_SUCCESS'
+export const FILE_UPLOAD_FAILURE = 'FILE_UPLOAD_FAILURE'
+
+export const RESET_ERROR_MESSAGE = 'RESET_ERROR_MESSAGE'
+export const HYDRATE = 'HYDRATE'
+
+// The following actions are implemented elsewhere in pubsweet components
+
+export const LOGIN_REQUEST = 'LOGIN_REQUEST'
+export const LOGIN_SUCCESS = 'LOGIN_SUCCESS'
+export const LOGIN_FAILURE = 'LOGIN_FAILURE'
+
+export const LOGOUT_REQUEST = 'LOGOUT_REQUEST'
+export const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS'
+export const LOGOUT_FAILURE = 'LOGOUT_FAILURE'
diff --git a/packages/pubsweet-client/src/actions/users.js b/packages/pubsweet-client/src/actions/users.js
new file mode 100644
index 0000000000000000000000000000000000000000..1c76c0429949f5649ffd55ba68e44fab19f8ad32
--- /dev/null
+++ b/packages/pubsweet-client/src/actions/users.js
@@ -0,0 +1,112 @@
+import * as api from '../helpers/api'
+import * as T from './types'
+
+function getUsersRequest() {
+  return {
+    type: T.GET_USERS_REQUEST,
+    isFetching: true,
+  }
+}
+
+function getUsersSuccess(users) {
+  return {
+    type: T.GET_USERS_SUCCESS,
+    isFetching: false,
+    users: users,
+  }
+}
+
+function getUsersFailure(message) {
+  return {
+    type: T.GET_USERS_FAILURE,
+    isFetching: false,
+    message,
+  }
+}
+
+export function getUsers() {
+  return dispatch => {
+    dispatch(getUsersRequest())
+
+    return api
+      .get('/users')
+      .then(
+        users => dispatch(getUsersSuccess(users.users)),
+        err => dispatch(getUsersFailure(err)),
+      )
+  }
+}
+
+function getUserRequest(user) {
+  return {
+    type: T.GET_USER_REQUEST,
+    user,
+  }
+}
+
+function getUserSuccess(user) {
+  return {
+    type: T.GET_USER_SUCCESS,
+    user,
+  }
+}
+
+function getUserFailure(user, error) {
+  return {
+    type: T.GET_USER_FAILURE,
+    user,
+    error,
+  }
+}
+
+export function getUser(user) {
+  return dispatch => {
+    dispatch(getUserRequest(user))
+
+    return api
+      .get('/users/' + user.id)
+      .then(
+        user => dispatch(getUserSuccess(user)),
+        err => dispatch(getUserFailure(err)),
+      )
+  }
+}
+
+function updateUserRequest(user) {
+  return {
+    type: T.UPDATE_USER_REQUEST,
+    user: user,
+    isFetching: true,
+  }
+}
+
+function updateUserSuccess(users) {
+  return {
+    type: T.UPDATE_USER_SUCCESS,
+    isFetching: false,
+    users: users,
+  }
+}
+
+function updateUserFailure(message) {
+  return {
+    type: T.UPDATE_USER_FAILURE,
+    isFetching: false,
+    error: message,
+  }
+}
+
+export function updateUser(user) {
+  return dispatch => {
+    dispatch(updateUserRequest(user))
+
+    const url = '/users/' + user.id
+
+    return api
+      .update(url, user)
+      .then(
+        user => dispatch(updateUserSuccess(user)),
+        err => dispatch(updateUserFailure(err)),
+      )
+  }
+}
diff --git a/packages/pubsweet-client/src/components/AuthenticatedComponent.jsx b/packages/pubsweet-client/src/components/AuthenticatedComponent.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..996014060f574e630f1f5ca1812eafc39dd73d81
--- /dev/null
+++ b/packages/pubsweet-client/src/components/AuthenticatedComponent.jsx
@@ -0,0 +1,53 @@
+import React from 'react'
+import { connect } from 'react-redux'
+import { withRouter } from 'react-router'
+import { push } from 'react-router-redux'
+import PropTypes from 'prop-types'
+
+import actions from '../actions'
+
+export class AuthenticatedComponent extends React.Component {
+  componentWillMount() {
+    this.props.getCurrentUser().then(() => this.checkAuth(this.props))
+  }
+
+  componentWillReceiveProps(nextProps) {
+    this.checkAuth(nextProps)
+  }
+
+  checkAuth({ isFetching, isAuthenticated }) {
+    if (!isFetching && !isAuthenticated) {
+      const redirectAfterLogin = this.props.location.pathname
+      this.props.pushState(`/login?next=${redirectAfterLogin}`)
+    }
+  }
+
+  render() {
+    return this.props.isAuthenticated ? this.props.children : null
+  }
+}
+
+AuthenticatedComponent.propTypes = {
+  children: PropTypes.node,
+  location: PropTypes.object,
+  getCurrentUser: PropTypes.func.isRequired,
+  isFetching: PropTypes.bool,
+  isAuthenticated: PropTypes.bool,
+  pushState: PropTypes.func.isRequired,
+}
+
+function mapState(state) {
+  return {
+    isFetching: state.currentUser.isFetching,
+    isAuthenticated: state.currentUser.isAuthenticated,
+  }
+}
+
+const ConnectedAuthenticatedComponent = withRouter(
+  connect(mapState, {
+    getCurrentUser: actions.getCurrentUser,
+    pushState: push,
+  })(AuthenticatedComponent),
+)
+
+export default ConnectedAuthenticatedComponent
diff --git a/packages/pubsweet-client/src/components/Root.js b/packages/pubsweet-client/src/components/Root.js
new file mode 100644
index 0000000000000000000000000000000000000000..f40aafc770a532d7aa17be50218d3d270826d322
--- /dev/null
+++ b/packages/pubsweet-client/src/components/Root.js
@@ -0,0 +1,22 @@
+import React from 'react'
+import { ConnectedRouter } from 'react-router-redux'
+import { Provider } from 'react-redux'
+import PropTypes from 'prop-types'
+import { ThemeProvider } from 'react-css-themr'
+
+const Root = ({ store, history, routes, theme }) => (
+  <Provider store={store}>
+    <ConnectedRouter history={history}>
+      <ThemeProvider theme={theme}>{routes}</ThemeProvider>
+    </ConnectedRouter>
+  </Provider>
+)
+
+Root.propTypes = {
+  routes: PropTypes.node.isRequired,
+  history: PropTypes.object.isRequired,
+  store: PropTypes.object.isRequired,
+  theme: PropTypes.object.isRequired,
+}
+
+export default Root
diff --git a/packages/pubsweet-client/src/components/UpdateSubscriber.jsx b/packages/pubsweet-client/src/components/UpdateSubscriber.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..9f483e24f67f90331404033947f7ff20bff94d9e
--- /dev/null
+++ b/packages/pubsweet-client/src/components/UpdateSubscriber.jsx
@@ -0,0 +1,179 @@
+import { connect } from 'react-redux'
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
+import config from 'config'
+import _ from 'lodash/fp'
+
+import * as T from '../actions/types'
+import 'event-source-polyfill'
+import token from '../helpers/token'
+
+const actionMap = {
+  'collection:create': T.CREATE_COLLECTION_SUCCESS,
+  'collection:patch': T.UPDATE_COLLECTION_SUCCESS,
+  'collection:delete': T.DELETE_COLLECTION_SUCCESS,
+  'fragment:create': T.CREATE_FRAGMENT_SUCCESS,
+  'fragment:patch': T.UPDATE_FRAGMENT_SUCCESS,
+  'fragment:delete': T.DELETE_FRAGMENT_SUCCESS,
+}
+
+export class UpdateSubscriber extends Component {
+  constructor(props) {
+    super(props)
+
+    this.state = {
+      connected: false,
+      visible: false,
+    }
+
+    this.listeners = {}
+  }
+
+  componentDidMount() {
+    this.subscribe(this.props)
+    this.setState({ visible: this.visible() })
+  }
+
+  componentWillReceiveProps(nextProps) {
+    if (!this.eventSource) {
+      this.subscribe(nextProps)
+    }
+  }
+
+  componentWillUnmount() {
+    if (this.eventSource) {
+      this.removeAllEventListeners()
+      this.eventSource.close()
+      // delete this.eventSource
+    }
+  }
+
+  visible() {
+    return _.get('pubsweet-client.update-subscriber.visible', config, false)
+  }
+
+  // if haven't received a heartbeat for 30 seconds, try to reconnect
+  monitor() {
+    if (this.heartbeat) {
+      window.clearTimeout(this.heartbeat)
+    }
+
+    this.heartbeat = window.setTimeout(() => {
+      // console.log('no heartbeat - reconnecting…')
+      this.subscribe(this.props)
+    }, 30000)
+  }
+
+  subscribe(props) {
+    const { currentUser, handleUpdate } = props
+
+    if (currentUser) {
+      // ignore if not supported
+      if (!window.EventSource) return
+
+      // clear any existing heartbeat monitor
+      if (this.heartbeat) {
+        // console.log('clearing timeout')
+        window.clearTimeout(this.heartbeat)
+      }
+
+      // close any existing connection
+      if (this.eventSource) {
+        // console.log('closing')
+        this.eventSource.close()
+      }
+
+      // EventSource can't have Authorization header, so have to use query string
+      const url = '/updates?access_token=' + encodeURIComponent(token())
+
+      this.eventSource = new window.EventSource(url)
+
+      this.listeners.error = () => {
+        // console.log('error', this.eventSource.readyState)
+
+        switch (this.eventSource.readyState) {
+          case 0: // CONNECTING
+            this.setState({ connected: false })
+            break
+
+          case 2: // CLOSED
+            this.setState({ connected: false })
+            this.monitor() // try again in a while if not connected
+            break
+        }
+      }
+
+      this.listeners.close = () => {
+        // console.log('close')
+
+        this.setState({ connected: false })
+        // this.monitor() // don't try to reconnect, as "close" without error is deliberate
+      }
+
+      this.listeners.message = event => {
+        // console.log('message', event)
+
+        if (event.origin !== window.location.origin) {
+          // console.error('Message from unexpected origin', event.origin)
+          return
+        }
+
+        const { action, data } = JSON.parse(event.data)
+
+        const actionType = actionMap[action]
+
+        handleUpdate(actionType, data)
+      }
+
+      this.listeners.open = () => {
+        // console.log('open')
+        this.setState({ connected: true })
+      }
+
+      // listen for a heartbeat message
+      this.listeners.pulse = () => {
+        // console.log('❤️')
+        this.monitor()
+      }
+
+      Object.keys(this.listeners).forEach(key => {
+        this.eventSource.addEventListener(key, this.listeners[key])
+      })
+    }
+  }
+
+  removeAllEventListeners() {
+    Object.keys(this.listeners).forEach(key => {
+      this.eventSource.removeEventListener(key, this.listeners[key])
+    })
+  }
+
+  render() {
+    const { connected, visible } = this.state
+
+    if (!visible) return null
+
+    return (
+      <i
+        className="fa fa-wifi"
+        style={{
+          color: connected ? 'green' : 'gray',
+        }}
+      />
+    )
+  }
+}
+
+UpdateSubscriber.propTypes = {
+  currentUser: PropTypes.object,
+  handleUpdate: PropTypes.func,
+}
+
+export default connect(
+  state => ({
+    currentUser: state.currentUser,
+  }),
+  dispatch => ({
+    handleUpdate: (type, body) => dispatch({ type, ...body }),
+  }),
+)(UpdateSubscriber)
diff --git a/packages/pubsweet-client/src/components/actions.js b/packages/pubsweet-client/src/components/actions.js
new file mode 100644
index 0000000000000000000000000000000000000000..2d4b6fb4f7b6cbf8ed8e4a56a1892c23544dd3f2
--- /dev/null
+++ b/packages/pubsweet-client/src/components/actions.js
@@ -0,0 +1,19 @@
+// const components = require('./components')
+const components = PUBSWEET_COMPONENTS
+
+module.exports = components
+  .map(component => {
+    // handle old style component export
+    const clientDef = component.client || component.frontend
+    const actions = clientDef && clientDef.actions
+
+    if (actions && typeof actions === 'function') {
+      return actions
+    } else if (actions) {
+      throw new Error("Component's actions are not exported as a function")
+    }
+  })
+  // filter out falsy values
+  .filter(Boolean)
+  .map(actions => actions())
+  .reduce((output, actions) => ({ ...output, ...actions }), {})
diff --git a/packages/pubsweet-client/src/components/index.js b/packages/pubsweet-client/src/components/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..dfbded0e63572cba580270301ce3373692552bba
--- /dev/null
+++ b/packages/pubsweet-client/src/components/index.js
@@ -0,0 +1,3 @@
+// NOTE: Webpack replaces the string PUBSWEET_COMPONENTS with an array containing a require statement for each client component
+
+// module.exports = PUBSWEET_COMPONENTS
diff --git a/packages/pubsweet-client/src/components/reducers.js b/packages/pubsweet-client/src/components/reducers.js
new file mode 100644
index 0000000000000000000000000000000000000000..e43c49009eb5d9aa1e3ee8dc42c10483607290a2
--- /dev/null
+++ b/packages/pubsweet-client/src/components/reducers.js
@@ -0,0 +1,28 @@
+// const components = require('./components')
+const isPlainObject = require('lodash/isPlainObject')
+const components = PUBSWEET_COMPONENTS
+
+module.exports = components
+  .map(component => {
+    // handle old style component export
+    const clientDef = component.client || component.frontend
+    return clientDef && clientDef.reducers
+  })
+  // filter out falsy values
+  .filter(Boolean)
+  .map(reducers => {
+    if (typeof reducers === 'function') {
+      const reducer = reducers()
+      return {
+        [reducer.default.name]: reducer.default,
+      }
+    } else if (isPlainObject(reducers)) {
+      // component exports an object where each value is a function
+      return Object.keys(reducers).reduce((output, key) => {
+        output[key] = reducers[key]()
+        return output
+      }, {})
+    }
+
+    throw new Error("Component's reducers are exported incorrectly")
+  })
diff --git a/packages/pubsweet-client/src/helpers/Authorize.jsx b/packages/pubsweet-client/src/helpers/Authorize.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..2d239859786a5dc67cbceb6b3674352c94b59ce7
--- /dev/null
+++ b/packages/pubsweet-client/src/helpers/Authorize.jsx
@@ -0,0 +1,67 @@
+'use strict'
+
+import React from 'react'
+import PropTypes from 'prop-types'
+import { connect } from 'react-redux'
+import { compose } from 'redux'
+
+import withAuthsome from './withAuthsome'
+
+export class Authorize extends React.Component {
+  constructor(props) {
+    super(props)
+
+    this.state = {
+      authorized: false,
+    }
+  }
+
+  componentWillMount() {
+    this.checkAuth(this.props)
+  }
+
+  componentWillReceiveProps(nextProps) {
+    this.checkAuth(nextProps)
+  }
+
+  async checkAuth({ authsome, currentUser, operation, object }) {
+    try {
+      const authorized = await authsome.can(
+        currentUser && currentUser.id,
+        operation,
+        object,
+      )
+      this.setState({
+        authorized,
+      })
+    } catch (err) {
+      console.error(err)
+    }
+  }
+
+  render() {
+    return this.state.authorized
+      ? this.props.children
+      : this.props.unauthorized || null
+  }
+}
+
+Authorize.propTypes = {
+  currentUser: PropTypes.object,
+  operation: PropTypes.string,
+  object: PropTypes.object,
+  children: PropTypes.element,
+  unauthorized: PropTypes.node,
+  authsome: PropTypes.object.isRequired,
+}
+
+function mapState(state) {
+  return {
+    teams: state.teams,
+    collections: state.collections,
+    fragments: state.fragments,
+    currentUser: state.currentUser.user,
+  }
+}
+
+export default compose(withAuthsome(), connect(mapState))(Authorize)
diff --git a/packages/pubsweet-client/src/helpers/Utils.js b/packages/pubsweet-client/src/helpers/Utils.js
new file mode 100644
index 0000000000000000000000000000000000000000..bb9a20f63458935c5c9dfff5646d1f1de8edc932
--- /dev/null
+++ b/packages/pubsweet-client/src/helpers/Utils.js
@@ -0,0 +1,5 @@
+// NOTE: deprecated - only retained for backwards compatibility
+
+import { deprecatedFetch } from './api'
+
+export const fetch = deprecatedFetch
diff --git a/packages/pubsweet-client/src/helpers/api.js b/packages/pubsweet-client/src/helpers/api.js
new file mode 100644
index 0000000000000000000000000000000000000000..b3e29c4b2cfd7d2576eeb760737ed9c59a533556
--- /dev/null
+++ b/packages/pubsweet-client/src/helpers/api.js
@@ -0,0 +1,81 @@
+import fetch from 'isomorphic-fetch'
+import endpoint from './endpoint'
+
+// read the authentication token from LocalStorage
+import getToken from './token'
+
+const parse = response => {
+  if (response.headers.get('content-type').includes('application/json')) {
+    return response.json()
+  }
+
+  return response.text()
+}
+
+const request = (url, options = {}) => {
+  options.headers = options.headers || {}
+  options.headers['Accept'] = options.headers['Accept'] || 'application/json'
+
+  const token = getToken()
+
+  if (token) {
+    options.headers['Authorization'] = 'Bearer ' + token
+  }
+
+  if (!url.match(/^https?:/)) {
+    url = endpoint + url
+  }
+
+  return fetch(url, options).then(response => {
+    if (!response.ok) {
+      return response.text().then(errorText => {
+        const error = new Error(response.statusText || response.status)
+        error.response = errorText
+        error.statusCode = response.status
+        throw error
+      })
+    }
+
+    return options.parse === false ? response : parse(response)
+  })
+  // .catch(error => {
+  //   // TODO: handle network errors
+  //   console.error(error)
+  // })
+}
+
+// for backwards compatibility
+export const deprecatedFetch = (url, options = {}) => {
+  options.parse = false
+  return request(url, options)
+}
+
+export const get = url =>
+  request(url, {
+    method: 'GET',
+  })
+
+export const create = (url, data) =>
+  request(url, {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+    },
+    body: JSON.stringify(data),
+  })
+
+export const update = (url, data, replace = false) =>
+  request(url, {
+    method: replace ? 'PUT' : 'PATCH',
+    headers: {
+      'Content-Type': 'application/json',
+    },
+    body: JSON.stringify(data),
+  })
+
+export const remove = url =>
+  request(url, {
+    method: 'DELETE',
+  })
+
+export default request
diff --git a/packages/pubsweet-client/src/helpers/endpoint.js b/packages/pubsweet-client/src/helpers/endpoint.js
new file mode 100644
index 0000000000000000000000000000000000000000..84a7b261f64324a184ea4ec52fbf3a7387719a89
--- /dev/null
+++ b/packages/pubsweet-client/src/helpers/endpoint.js
@@ -0,0 +1,3 @@
+import config from 'config'
+
+module.exports = config['pubsweet-client'].API_ENDPOINT
diff --git a/packages/pubsweet-client/src/helpers/token.js b/packages/pubsweet-client/src/helpers/token.js
new file mode 100644
index 0000000000000000000000000000000000000000..0fb7d80d1a57bfd9fc7f8ad819b2ccecf25491c6
--- /dev/null
+++ b/packages/pubsweet-client/src/helpers/token.js
@@ -0,0 +1,7 @@
+module.exports = () => {
+  const localStorage = window.localStorage || global.window.localStorage
+
+  if (!localStorage) throw new Error('localstorage is not available')
+
+  return localStorage.getItem('token')
+}
diff --git a/packages/pubsweet-client/src/helpers/withAuthsome.js b/packages/pubsweet-client/src/helpers/withAuthsome.js
new file mode 100644
index 0000000000000000000000000000000000000000..01ab39ee4f3affd6f201aec3ad078177199952e0
--- /dev/null
+++ b/packages/pubsweet-client/src/helpers/withAuthsome.js
@@ -0,0 +1,36 @@
+import Authsome from 'authsome'
+import { connect } from 'react-redux'
+import config from 'config'
+const mode = require(config.authsome.mode)
+
+// higher order component to inject authsome into a component
+export default function withAuthsome() {
+  const authsome = new Authsome({...config.authsome, mode}, {})
+
+  function mapState(state) {
+    authsome.context = {
+      // fetch entities from store instead of database
+      models: {
+        Collection: {
+          find: id =>
+            state.collections.find(collection => collection.id === id),
+        },
+        Fragment: {
+          find: id => state.fragments[id],
+        },
+        Team: {
+          find: id => state.teams.find(team => team.id === id),
+        },
+        User: {
+          find: id => {
+            return state.users.users.find(user => user.id === id)
+          },
+        },
+      },
+    }
+
+    return { authsome }
+  }
+
+  return connect(mapState)
+}
diff --git a/packages/pubsweet-client/src/images/pubsweet-rgb-small.jpg b/packages/pubsweet-client/src/images/pubsweet-rgb-small.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..57efdcb962ffac6c6ce60777af38e62d4f872c87
Binary files /dev/null and b/packages/pubsweet-client/src/images/pubsweet-rgb-small.jpg differ
diff --git a/packages/pubsweet-client/src/index.js b/packages/pubsweet-client/src/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..029662791397fc15fc5e04ccb8b93d2626465768
--- /dev/null
+++ b/packages/pubsweet-client/src/index.js
@@ -0,0 +1,6 @@
+export { default as configureStore } from './store/configureStore'
+export { default as Root } from './components/Root'
+export { requireAuthentication } from './components/AuthenticatedComponent'
+export { default as actions } from './actions'
+export { default as reducers } from './reducers'
+export { default as validations } from './validations'
diff --git a/packages/pubsweet-client/src/reducers/collections.js b/packages/pubsweet-client/src/reducers/collections.js
new file mode 100644
index 0000000000000000000000000000000000000000..25d4fbb61b20ce841ee440bfce21d19312e0011d
--- /dev/null
+++ b/packages/pubsweet-client/src/reducers/collections.js
@@ -0,0 +1,130 @@
+import {
+  GET_COLLECTIONS_SUCCESS,
+  GET_COLLECTIONS_FAILURE,
+  CREATE_COLLECTION_SUCCESS,
+  GET_COLLECTION_REQUEST,
+  GET_COLLECTION_SUCCESS,
+  UPDATE_COLLECTION_SUCCESS,
+  PATCH_COLLECTION_SUCCESS,
+  DELETE_COLLECTION_SUCCESS,
+  GET_FRAGMENTS_SUCCESS,
+  CREATE_FRAGMENT_SUCCESS,
+  DELETE_FRAGMENT_SUCCESS,
+  LOGOUT_SUCCESS,
+} from '../actions/types'
+
+import find from 'lodash/find'
+import union from 'lodash/union'
+import difference from 'lodash/difference'
+import clone from 'lodash/clone'
+import findIndex from 'lodash/findIndex'
+import without from 'lodash/without'
+
+export default function(state = [], action) {
+  const collections = clone(state)
+
+  // TODO: store entities as an object or immutable Map, with the id as the key
+  function getCollection() {
+    return find(collections, { id: action.collection.id })
+  }
+
+  function getCollectionIndex() {
+    return findIndex(collections, { id: action.collection.id })
+  }
+
+  function addCollection() {
+    // only add the collection if it hasn't already been added
+    if (!getCollection()) {
+      collections.push(action.collection)
+    }
+
+    return collections
+  }
+
+  function setCollection() {
+    const index = getCollectionIndex()
+
+    // NOTE: this is necessary because the collections state is an array
+    if (index === -1) {
+      collections.push(action.collection)
+    } else {
+      collections[index] = action.collection
+    }
+
+    return collections
+  }
+
+  function updateCollection() {
+    const index = getCollectionIndex()
+
+    collections[index] = { ...collections[index], ...action.update }
+
+    return collections
+  }
+
+  function deleteCollection() {
+    const collection = getCollection()
+
+    return without(collections, collection)
+  }
+
+  function addFragments() {
+    const collection = getCollection()
+
+    if (collection) {
+      collection.fragments = union(
+        collection.fragments,
+        (action.fragments || [action.fragment]).map(fragment => fragment.id),
+      )
+    }
+
+    return collections
+  }
+
+  function removeFragments() {
+    const collection = getCollection()
+
+    if (collection) {
+      collection.fragments = difference(
+        collection.fragments,
+        (action.fragments || [action.fragment]).map(fragment => fragment.id),
+      )
+    }
+
+    return collections
+  }
+
+  switch (action.type) {
+    case GET_COLLECTIONS_SUCCESS:
+      return clone(action.collections)
+
+    case GET_COLLECTIONS_FAILURE:
+      return []
+
+    case CREATE_COLLECTION_SUCCESS:
+      return addCollection()
+
+    case GET_COLLECTION_SUCCESS:
+      return setCollection()
+
+    case UPDATE_COLLECTION_SUCCESS:
+    case PATCH_COLLECTION_SUCCESS:
+      return updateCollection()
+
+    case GET_COLLECTION_REQUEST:
+    case DELETE_COLLECTION_SUCCESS:
+      return deleteCollection()
+
+    case DELETE_FRAGMENT_SUCCESS:
+      return removeFragments()
+
+    case GET_FRAGMENTS_SUCCESS:
+    case CREATE_FRAGMENT_SUCCESS:
+      return addFragments()
+
+    case LOGOUT_SUCCESS:
+      return []
+  }
+
+  return state
+}
diff --git a/packages/pubsweet-client/src/reducers/currentUser.js b/packages/pubsweet-client/src/reducers/currentUser.js
new file mode 100644
index 0000000000000000000000000000000000000000..a14eb84953ef93b62cab41a64474a2d4b13ffa3c
--- /dev/null
+++ b/packages/pubsweet-client/src/reducers/currentUser.js
@@ -0,0 +1,48 @@
+import {
+  GET_CURRENT_USER_REQUEST,
+  GET_CURRENT_USER_SUCCESS,
+  GET_CURRENT_USER_FAILURE,
+  LOGOUT_SUCCESS,
+} from '../actions/types'
+
+export default function(
+  state = {
+    isFetching: false,
+    isAuthenticated: false,
+  },
+  action,
+) {
+  switch (action.type) {
+    case GET_CURRENT_USER_REQUEST:
+      return {
+        ...state,
+        isFetching: true,
+        isAuthenticated: false,
+      }
+
+    case GET_CURRENT_USER_SUCCESS:
+      return {
+        ...state,
+        isFetching: false,
+        isAuthenticated: true,
+        user: action.user,
+      }
+
+    case GET_CURRENT_USER_FAILURE:
+      return {
+        ...state,
+        isFetching: false,
+        isAuthenticated: false,
+      }
+
+    case LOGOUT_SUCCESS:
+      return {
+        isFetching: false,
+        isAuthenticated: false,
+        user: null,
+      }
+
+    default:
+      return state
+  }
+}
diff --git a/packages/pubsweet-client/src/reducers/error.js b/packages/pubsweet-client/src/reducers/error.js
new file mode 100644
index 0000000000000000000000000000000000000000..df299d1c008d4cfc21318731dcbd25b0a142467d
--- /dev/null
+++ b/packages/pubsweet-client/src/reducers/error.js
@@ -0,0 +1,9 @@
+// Updates error message to notify about the failed fetches.
+export default function(state = null, { error }) {
+  if (error && error.message) {
+    console.error(error)
+    return error.message
+  }
+
+  return null
+}
diff --git a/packages/pubsweet-client/src/reducers/fileUpload.js b/packages/pubsweet-client/src/reducers/fileUpload.js
new file mode 100644
index 0000000000000000000000000000000000000000..d5db71dbf81f921f1f9b1178594ee79665217177
--- /dev/null
+++ b/packages/pubsweet-client/src/reducers/fileUpload.js
@@ -0,0 +1,24 @@
+import { FILE_UPLOAD_REQUEST, FILE_UPLOAD_SUCCESS } from '../actions/types'
+
+export default function(
+  state = {
+    isFetching: false,
+  },
+  action,
+) {
+  switch (action.type) {
+    case FILE_UPLOAD_SUCCESS:
+      return {
+        ...state,
+        isFetching: false,
+        file: action.file,
+      }
+    case FILE_UPLOAD_REQUEST:
+      return {
+        ...state,
+        isFetching: true,
+      }
+    default:
+      return state
+  }
+}
diff --git a/packages/pubsweet-client/src/reducers/fragments.js b/packages/pubsweet-client/src/reducers/fragments.js
new file mode 100644
index 0000000000000000000000000000000000000000..4d5cafe9687ded581e3dcaf99212dc0e55fd6f68
--- /dev/null
+++ b/packages/pubsweet-client/src/reducers/fragments.js
@@ -0,0 +1,80 @@
+import {
+  GET_FRAGMENTS_SUCCESS,
+  CREATE_FRAGMENT_REQUEST,
+  CREATE_FRAGMENT_SUCCESS,
+  CREATE_FRAGMENT_FAILURE,
+  GET_FRAGMENT_REQUEST,
+  GET_FRAGMENT_SUCCESS,
+  UPDATE_FRAGMENT_REQUEST,
+  UPDATE_FRAGMENT_SUCCESS,
+  // UPDATE_FRAGMENT_FAILURE,
+  DELETE_FRAGMENT_REQUEST,
+  DELETE_FRAGMENT_FAILURE,
+  DELETE_FRAGMENT_SUCCESS,
+  LOGOUT_SUCCESS,
+} from '../actions/types'
+
+import clone from 'lodash/clone'
+import unset from 'lodash/unset'
+
+export default function(state = {}, action) {
+  const fragments = clone(state)
+
+  function replaceAll() {
+    unset(fragments, action.collection.fragments)
+    action.fragments.forEach(fragment => {
+      fragments[fragment.id] = fragment
+    })
+    return fragments
+  }
+
+  function setOne() {
+    fragments[action.fragment.id] = action.fragment
+
+    return fragments
+  }
+
+  function updateOne() {
+    const oldfragment = fragments[action.fragment.id] || {}
+    const update = action.update || action.fragment
+
+    fragments[action.fragment.id] = { ...oldfragment, ...update }
+
+    return fragments
+  }
+
+  function removeOne() {
+    unset(fragments, action.fragment.id)
+    return fragments
+  }
+
+  // choose the sword, and you will join me
+  // choose the ball, and you join your mother... in death
+  // you don't understand my words, but you must choose
+  // 拝 一刀 | Ogami Ittō
+  switch (action.type) {
+    case UPDATE_FRAGMENT_REQUEST:
+    case UPDATE_FRAGMENT_SUCCESS:
+    case DELETE_FRAGMENT_REQUEST:
+    case DELETE_FRAGMENT_FAILURE:
+    case CREATE_FRAGMENT_SUCCESS:
+    case CREATE_FRAGMENT_REQUEST:
+      return updateOne()
+
+    case GET_FRAGMENT_SUCCESS:
+      return setOne()
+
+    case GET_FRAGMENT_REQUEST:
+    case CREATE_FRAGMENT_FAILURE:
+    case DELETE_FRAGMENT_SUCCESS:
+      return removeOne()
+
+    case GET_FRAGMENTS_SUCCESS:
+      return replaceAll()
+
+    case LOGOUT_SUCCESS:
+      return {}
+  }
+
+  return state
+}
diff --git a/packages/pubsweet-client/src/reducers/index.js b/packages/pubsweet-client/src/reducers/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..25a28908514b239cdd2b359c3d8c7af7e1985e91
--- /dev/null
+++ b/packages/pubsweet-client/src/reducers/index.js
@@ -0,0 +1,17 @@
+import collections from './collections'
+import currentUser from './currentUser'
+import error from './error'
+import fileUpload from './fileUpload'
+import fragments from './fragments'
+import users from './users'
+import teams from './teams'
+
+export default {
+  collections,
+  currentUser,
+  error,
+  fileUpload,
+  fragments,
+  teams,
+  users,
+}
diff --git a/packages/pubsweet-client/src/reducers/teams.js b/packages/pubsweet-client/src/reducers/teams.js
new file mode 100644
index 0000000000000000000000000000000000000000..4055822be4e206d529324210630a70431504727a
--- /dev/null
+++ b/packages/pubsweet-client/src/reducers/teams.js
@@ -0,0 +1,44 @@
+import {
+  GET_TEAMS_SUCCESS,
+  CREATE_TEAM_SUCCESS,
+  UPDATE_TEAM_SUCCESS,
+  DELETE_TEAM_SUCCESS,
+  GET_COLLECTION_TEAMS_SUCCESS,
+  LOGOUT_SUCCESS,
+} from '../actions/types'
+
+import clone from 'lodash/clone'
+import findIndex from 'lodash/findIndex'
+import differenceBy from 'lodash/differenceBy'
+import unionBy from 'lodash/unionBy'
+
+export default function(state = [], action) {
+  const teams = clone(state)
+
+  function updateOne() {
+    const index = findIndex(teams, { id: action.team.id })
+    if (index !== -1) {
+      teams[index] = { ...teams[index], ...action.team }
+    } else {
+      teams.push(action.team)
+    }
+
+    return teams
+  }
+
+  switch (action.type) {
+    case CREATE_TEAM_SUCCESS:
+    case UPDATE_TEAM_SUCCESS:
+      return updateOne()
+    case GET_TEAMS_SUCCESS:
+      return clone(action.teams)
+    case DELETE_TEAM_SUCCESS:
+      return differenceBy(state, [action.team], 'id')
+    case LOGOUT_SUCCESS:
+      return []
+    case GET_COLLECTION_TEAMS_SUCCESS:
+      return unionBy(state, action.teams, 'id')
+  }
+
+  return state
+}
diff --git a/packages/pubsweet-client/src/reducers/users.js b/packages/pubsweet-client/src/reducers/users.js
new file mode 100644
index 0000000000000000000000000000000000000000..80d00fb9cae8056e75877c008186364b568d8d03
--- /dev/null
+++ b/packages/pubsweet-client/src/reducers/users.js
@@ -0,0 +1,72 @@
+import { unionBy } from 'lodash'
+
+import {
+  GET_USERS_REQUEST,
+  GET_USERS_SUCCESS,
+  GET_USER_SUCCESS,
+  UPDATE_USER_REQUEST,
+  UPDATE_USER_SUCCESS,
+  GET_CURRENT_USER_SUCCESS,
+  LOGOUT_SUCCESS,
+} from '../actions/types'
+
+// TODO: store users as an object/map instead of an array
+
+// The users reducer.
+export default (
+  state = {
+    isFetching: false,
+    users: [],
+  },
+  action,
+) => {
+  switch (action.type) {
+    case GET_USERS_REQUEST:
+      return {
+        ...state,
+        isFetching: true,
+      }
+
+    case GET_USERS_SUCCESS:
+      return {
+        ...state,
+        isFetching: false,
+        users: action.users,
+      }
+
+    case GET_USER_SUCCESS:
+      return {
+        ...state,
+        isFetching: false,
+        users: unionBy([action.user], state.users, 'id'),
+      }
+
+    case UPDATE_USER_REQUEST:
+      return {
+        ...state,
+        isFetching: true,
+      }
+
+    case UPDATE_USER_SUCCESS:
+      return {
+        ...state,
+        isFetching: false,
+        users: unionBy([action.user], state.users, 'id'),
+      }
+
+    case LOGOUT_SUCCESS:
+      return {
+        isFetching: false,
+        users: [],
+      }
+
+    case GET_CURRENT_USER_SUCCESS:
+      return {
+        ...state,
+        users: unionBy([action.user], state.users, 'id'),
+      }
+
+    default:
+      return state
+  }
+}
diff --git a/packages/pubsweet-client/src/store/configureStore.js b/packages/pubsweet-client/src/store/configureStore.js
new file mode 100644
index 0000000000000000000000000000000000000000..9e65befb6c512dfe7d7e63c737d2ada1b5ad4ae1
--- /dev/null
+++ b/packages/pubsweet-client/src/store/configureStore.js
@@ -0,0 +1,66 @@
+import { createStore, combineReducers, compose, applyMiddleware } from 'redux'
+import { routerReducer, routerMiddleware } from 'react-router-redux'
+
+import thunk from 'redux-thunk'
+import { createLogger } from 'redux-logger'
+import { reducer as formReducer } from 'redux-form'
+import config from 'config'
+import reducers from '../reducers'
+
+require('../components/reducers').forEach(componentReducers =>
+  Object.assign(reducers, componentReducers),
+)
+
+function createConfigureStore(env) {
+  return function configureStore(
+    history,
+    initialState = {},
+    customReducers = {},
+    customMiddlewares = [],
+  ) {
+    const reducer = combineReducers({
+      ...reducers,
+      form: formReducer,
+      routing: routerReducer,
+      ...customReducers,
+    })
+
+    let logger
+    if (config['pubsweet-client']['redux-log'] && env !== 'production') {
+      logger = applyMiddleware(createLogger())
+    }
+
+    const middleware = [
+      applyMiddleware(thunk),
+      logger,
+      applyMiddleware(routerMiddleware(history)),
+      ...customMiddlewares.map(mw => applyMiddleware(mw)),
+    ].filter(value => value)
+
+    // https://github.com/zalmoxisus/redux-devtools-extension#12-advanced-store-setup
+    const composeEnhancers =
+      window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
+
+    const store = createStore(
+      reducer,
+      initialState,
+      composeEnhancers(...middleware),
+    )
+
+    if (module.hot) {
+      // Enable Webpack hot module replacement for reducers
+      module.hot.accept('../reducers', () => {
+        const nextRootReducer = require('../reducers')
+        store.replaceReducer(nextRootReducer)
+      })
+    }
+
+    return store
+  }
+}
+
+if (process.env.NODE_ENV === 'production') {
+  module.exports = createConfigureStore('production')
+} else {
+  module.exports = createConfigureStore('development')
+}
diff --git a/packages/pubsweet-client/src/validations.js b/packages/pubsweet-client/src/validations.js
new file mode 100644
index 0000000000000000000000000000000000000000..3aaca1f79c46a911c754d4bec8ded771aa4a45f8
--- /dev/null
+++ b/packages/pubsweet-client/src/validations.js
@@ -0,0 +1,3 @@
+import config from 'config'
+
+module.exports = require('pubsweet-server/src/models/validations')(config)
diff --git a/packages/pubsweet-client/test/.eslintrc b/packages/pubsweet-client/test/.eslintrc
new file mode 100644
index 0000000000000000000000000000000000000000..ba085893fda75b0f8c92738825d42acb312b632f
--- /dev/null
+++ b/packages/pubsweet-client/test/.eslintrc
@@ -0,0 +1,7 @@
+{
+  "env": {
+    "node": true,
+    "jest": true,
+    "jasmine": true
+  }
+}
diff --git a/packages/pubsweet-client/test/actions/collections.test.js b/packages/pubsweet-client/test/actions/collections.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..65ed309f625e67324ed9536f69134aa7b0dbea94
--- /dev/null
+++ b/packages/pubsweet-client/test/actions/collections.test.js
@@ -0,0 +1,120 @@
+global.PUBSWEET_COMPONENTS = []
+
+const actions = require('../../src/actions/collections')
+const describeAction = require('../helpers/describeAction')(actions)
+const T = require('../../src/actions/types')
+
+describe('Collection actions', () => {
+  describeAction('getCollections', {
+    types: {
+      request: T.GET_COLLECTIONS_REQUEST,
+      success: T.GET_COLLECTIONS_SUCCESS,
+      failure: T.GET_COLLECTIONS_FAILURE,
+    },
+    properties: {
+      request: ['type'],
+      success: ['type', 'collections', 'receivedAt'],
+      failure: ['type', 'error'],
+    },
+  })
+
+  describeAction('getCollectionTeams', {
+    firstarg: { id: 123 },
+    types: {
+      request: T.GET_COLLECTION_TEAMS_REQUEST,
+      success: T.GET_COLLECTION_TEAMS_SUCCESS,
+      failure: T.GET_COLLECTION_TEAMS_FAILURE,
+    },
+    properties: {
+      request: ['type'],
+      success: ['type', 'teams', 'receivedAt'],
+      failure: ['type', 'error'],
+    },
+  })
+
+  describeAction('createCollection', {
+    firstarg: {
+      type: 'testing',
+      title: 'this is a test collection',
+    },
+    types: {
+      request: T.CREATE_COLLECTION_REQUEST,
+      success: T.CREATE_COLLECTION_SUCCESS,
+      failure: T.CREATE_COLLECTION_FAILURE,
+    },
+    properties: {
+      request: ['type', 'collection'],
+      success: ['type', 'collection'],
+      failure: ['type', 'isFetching', 'collection', 'error'],
+    },
+  })
+
+  describeAction('getCollection', {
+    firstarg: { id: 123 },
+    types: {
+      request: T.GET_COLLECTION_REQUEST,
+      success: T.GET_COLLECTION_SUCCESS,
+      failure: T.GET_COLLECTION_FAILURE,
+    },
+    properties: {
+      request: ['type', 'collection'],
+      success: ['type', 'collection', 'receivedAt'],
+      failure: ['type', 'isFetching', 'collection', 'error'],
+    },
+  })
+
+  describeAction('updateCollection', {
+    firstarg: { id: 123 },
+    secondarg: {
+      type: 'testing',
+      title: 'this is an updated collection',
+    },
+    types: {
+      request: T.UPDATE_COLLECTION_REQUEST,
+      success: T.UPDATE_COLLECTION_SUCCESS,
+      failure: T.UPDATE_COLLECTION_FAILURE,
+    },
+    properties: {
+      request: ['type', 'collection'],
+      success: ['type', 'collection'],
+      failure: ['type', 'isFetching', 'collection', 'error'],
+    },
+  })
+
+  // NOTE: enable this once PATCH method is implemented on the server
+  // describeAction('patchCollection', {
+  //   firstarg: newcol,
+  //   secondarg: {
+  //     type: 'testing',
+  //     title: 'this is a patched collection'
+  //   },
+  //   types: {
+  //     request: T.PATCH_COLLECTION_REQUEST,
+  //     success: T.PATCH_COLLECTION_SUCCESS,
+  //     failure: T.PATCH_COLLECTION_FAILURE
+  //   },
+  //   properties: {
+  //     request: ['type', 'collection'],
+  //     success: ['type', 'collection'],
+  //     failure: ['type', 'isFetching', 'collection', 'error']
+  //   }
+  // }, (action, data) => {
+  //   expect(
+  //     data.PATCH_COLLECTION_SUCCESS.collection.title
+  //   ).toBe('this is a patched collection')
+  // })
+
+  describeAction('deleteCollection', {
+    firstarg: { id: 123 },
+    types: {
+      request: T.DELETE_COLLECTION_REQUEST,
+      success: T.DELETE_COLLECTION_SUCCESS,
+      failure: T.DELETE_COLLECTION_FAILURE,
+    },
+    properties: {
+      request: ['type', 'collection', 'update'],
+      success: ['type', 'collection'],
+      failure: ['type', 'collection', 'update', 'error'],
+    },
+  })
+})
diff --git a/packages/pubsweet-client/test/actions/currentUser.test.js b/packages/pubsweet-client/test/actions/currentUser.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..4bfd727a596382b25851eabd42c4fedeaeeee7dc
--- /dev/null
+++ b/packages/pubsweet-client/test/actions/currentUser.test.js
@@ -0,0 +1,20 @@
+global.PUBSWEET_COMPONENTS = []
+
+const actions = require('../../src/actions/currentUser')
+const describeAction = require('../helpers/describeAction')(actions)
+const T = require('../../src/actions/types')
+
+describe('currentUser actions', () => {
+  describeAction('getCurrentUser', {
+    types: {
+      request: T.GET_CURRENT_USER_REQUEST,
+      success: T.GET_CURRENT_USER_SUCCESS,
+      failure: T.GET_CURRENT_USER_FAILURE,
+    },
+    properties: {
+      request: [],
+      success: ['user'],
+      failure: ['error'],
+    },
+  })
+})
diff --git a/packages/pubsweet-client/test/actions/fileUpload.test.js b/packages/pubsweet-client/test/actions/fileUpload.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..33b3eaaba8a0dea5d53250cc8ff603fdf6d06828
--- /dev/null
+++ b/packages/pubsweet-client/test/actions/fileUpload.test.js
@@ -0,0 +1,32 @@
+global.PUBSWEET_COMPONENTS = []
+
+require('isomorphic-form-data')
+global.FormData.prototype.oldAppend = global.FormData.prototype.append
+global.FormData.prototype.append = function(field, value, options) {
+  // File upload testing hack
+  if (typeof value === 'string') {
+    value = fs.createReadStream(value)
+  }
+  this.oldAppend(field, value, options)
+}
+
+const fs = require('fs')
+const actions = require('../../src/actions/fileUpload')
+const describeAction = require('../helpers/describeAction')(actions)
+const T = require('../../src/actions/types')
+
+describe('fileUpload actions', () => {
+  describeAction('fileUpload', {
+    firstarg: './test/helpers/mockapp.js',
+    types: {
+      request: T.FILE_UPLOAD_REQUEST,
+      success: T.FILE_UPLOAD_SUCCESS,
+      failure: T.FILE_UPLOAD_FAILURE,
+    },
+    properties: {
+      request: ['isFetching'],
+      success: ['isFetching', 'file'],
+      failure: ['isFetching', 'error'],
+    },
+  })
+})
diff --git a/packages/pubsweet-client/test/actions/fragments.test.js b/packages/pubsweet-client/test/actions/fragments.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..97c50fb914c1e0c96643f5b141130ca7549da857
--- /dev/null
+++ b/packages/pubsweet-client/test/actions/fragments.test.js
@@ -0,0 +1,105 @@
+global.PUBSWEET_COMPONENTS = []
+
+const actions = require('../../src/actions/fragments')
+const describeAction = require('../helpers/describeAction')(actions)
+const T = require('../../src/actions/types')
+
+describe('fragments actions', () => {
+  const mockcol = { id: '123' }
+  const mockfragment = { id: '1234' }
+
+  describeAction('getFragments', {
+    firstarg: mockcol,
+    types: {
+      request: T.GET_FRAGMENTS_REQUEST,
+      success: T.GET_FRAGMENTS_SUCCESS,
+    },
+    properties: {
+      success: ['fragments'],
+    },
+  })
+
+  // get a list of collections, with the specified fields
+  describeAction('getFragments', {
+    firstarg: mockcol,
+    secondarg: {
+      fields: ['type', 'presentation'],
+    },
+    types: {
+      request: T.GET_FRAGMENTS_REQUEST,
+      success: T.GET_FRAGMENTS_SUCCESS,
+      failure: T.GET_FRAGMENTS_FAILURE,
+    },
+    properties: {
+      success: ['fragments'],
+    },
+  })
+
+  describeAction('createFragment', {
+    // no collection routes to top level fragment endpoint
+    firstarg: null,
+    secondarg: {
+      title: 'mock fragment',
+      type: 'some_fragment',
+      owners: [],
+    },
+    types: {
+      request: T.CREATE_FRAGMENT_REQUEST,
+      success: T.CREATE_FRAGMENT_SUCCESS,
+      failure: T.CREATE_FRAGMENT_FAILURE,
+    },
+    properties: {
+      success: ['collection', 'fragment'],
+      failure: ['fragment', 'error'],
+    },
+  })
+
+  describeAction('getFragment', {
+    firstarg: mockcol,
+    secondarg: mockfragment,
+    types: {
+      request: T.GET_FRAGMENT_REQUEST,
+      success: T.GET_FRAGMENT_SUCCESS,
+      failure: T.GET_FRAGMENT_FAILURE,
+    },
+    properties: {
+      request: ['fragment'],
+      success: ['fragment', 'receivedAt'],
+      failure: ['isFetching', 'fragment', 'error'],
+    },
+  })
+
+  describeAction('updateFragment', {
+    firstarg: mockcol,
+    secondarg: {
+      id: '1234',
+      title: 'modded fragment',
+      type: 'some_fragment',
+      owners: [],
+    },
+    types: {
+      request: T.UPDATE_FRAGMENT_REQUEST,
+      success: T.UPDATE_FRAGMENT_SUCCESS,
+      failure: T.UPDATE_FRAGMENT_FAILURE,
+    },
+    properties: {
+      success: ['fragment', 'receivedAt'],
+      failure: ['fragment', 'error'],
+    },
+  })
+
+  describeAction('deleteFragment', {
+    firstarg: mockcol,
+    secondarg: mockfragment,
+    types: {
+      request: T.DELETE_FRAGMENT_REQUEST,
+      success: T.DELETE_FRAGMENT_SUCCESS,
+      failure: T.DELETE_FRAGMENT_FAILURE,
+    },
+    properties: {
+      request: ['fragment', 'update'],
+      success: ['fragment', 'collection'],
+      failure: ['fragment', 'error', 'update'],
+    },
+  })
+})
diff --git a/packages/pubsweet-client/test/actions/index.test.js b/packages/pubsweet-client/test/actions/index.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..7f0012843384e351bf0d2e30f1c16c4a7de7f02f
--- /dev/null
+++ b/packages/pubsweet-client/test/actions/index.test.js
@@ -0,0 +1,14 @@
+global.PUBSWEET_COMPONENTS = []
+
+const { hydrate } = require('../../src/actions').default
+
+describe('actions index', () => {
+  describe('hydrate', () => {
+    it('dispatches two actions', async () => {
+      const mockDispatch = jest.fn()
+      await hydrate()(mockDispatch)
+
+      expect(mockDispatch.mock.calls).toHaveLength(2)
+    })
+  })
+})
diff --git a/packages/pubsweet-client/test/actions/teams.test.js b/packages/pubsweet-client/test/actions/teams.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..e0206a22ab5a9fea3978916ce885922f871ecc51
--- /dev/null
+++ b/packages/pubsweet-client/test/actions/teams.test.js
@@ -0,0 +1,85 @@
+global.PUBSWEET_COMPONENTS = []
+
+const actions = require('../../src/actions/teams')
+const describeAction = require('../helpers/describeAction')(actions)
+const T = require('../../src/actions/types')
+
+describe('teams actions', () => {
+  describeAction('getTeams', {
+    types: {
+      request: T.GET_TEAMS_REQUEST,
+      success: T.GET_TEAMS_SUCCESS,
+      failure: T.GET_TEAMS_FAILURE,
+    },
+    properties: {
+      request: ['isFetching'],
+      success: ['isFetching', 'teams'],
+      failure: ['isFetching', 'message'],
+    },
+  })
+
+  describeAction('createTeam', {
+    firstarg: {
+      name: 'My readers',
+      teamType: {
+        name: 'Readers',
+        permissions: 'read',
+      },
+      object: {
+        kind: 'blogpost',
+        source: '<blog></blog>',
+        presentation: '<p></p>',
+      },
+    },
+    types: {
+      request: T.CREATE_TEAM_REQUEST,
+      success: T.CREATE_TEAM_SUCCESS,
+      failure: T.CREATE_TEAM_FAILURE,
+    },
+    properties: {
+      request: ['team'],
+      success: ['team'],
+      failure: ['isFetching', 'team', 'error'],
+    },
+  })
+
+  describeAction('updateTeam', {
+    firstarg: { id: 234 },
+    secondard: {
+      name: 'My readers',
+      teamType: {
+        name: 'Readers',
+        permissions: 'read',
+      },
+      object: {
+        kind: 'blogpost',
+        source: '<blog></blog>',
+        presentation: '<p></p>',
+      },
+    },
+    types: {
+      request: T.UPDATE_TEAM_REQUEST,
+      success: T.UPDATE_TEAM_SUCCESS,
+      failure: T.UPDATE_TEAM_FAILURE,
+    },
+    properties: {
+      request: ['team'],
+      success: ['team'],
+      failure: ['isFetching', 'team', 'error'],
+    },
+  })
+
+  describeAction('deleteTeam', {
+    firstarg: { id: 234 },
+    types: {
+      request: T.DELETE_TEAM_REQUEST,
+      success: T.DELETE_TEAM_SUCCESS,
+      failure: T.DELETE_TEAM_FAILURE,
+    },
+    properties: {
+      request: ['team'],
+      success: ['team'],
+      failure: ['isFetching', 'team', 'error'],
+    },
+  })
+})
diff --git a/packages/pubsweet-client/test/actions/users.test.js b/packages/pubsweet-client/test/actions/users.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..e65136f00971810412cca58c4e0a9e490f45daf5
--- /dev/null
+++ b/packages/pubsweet-client/test/actions/users.test.js
@@ -0,0 +1,54 @@
+global.PUBSWEET_COMPONENTS = []
+
+const actions = require('../../src/actions/users')
+const describeAction = require('../helpers/describeAction')(actions)
+const T = require('../../src/actions/types')
+
+describe('users actions', () => {
+  const user = {
+    username: 'fakeymcfake',
+    password: 'correct battery horse staple',
+    email: 'fakey_mcfake@pseudonymous.com',
+    id: '57d0fc8e-ece9-47bf-87d3-7935326b0128',
+  }
+
+  describeAction('getUsers', {
+    types: {
+      request: T.GET_USERS_REQUEST,
+      success: T.GET_USERS_SUCCESS,
+      failure: T.GET_USERS_FAILURE,
+    },
+    properties: {
+      success: ['users'],
+      failure: ['isFetching', 'message'],
+    },
+  })
+
+  describeAction('getUser', {
+    firstarg: { id: user.id },
+    types: {
+      request: T.GET_USER_REQUEST,
+      success: T.GET_USER_SUCCESS,
+      failure: T.GET_USER_FAILURE,
+    },
+    properties: {
+      request: ['user'],
+      success: ['user'],
+      failure: ['user', 'error'],
+    },
+  })
+
+  describeAction('updateUser', {
+    firstarg: user,
+    secondarg: user,
+    types: {
+      request: T.UPDATE_USER_REQUEST,
+      success: T.UPDATE_USER_SUCCESS,
+      failure: T.UPDATE_USER_FAILURE,
+    },
+    properties: {
+      success: ['users'],
+      failure: ['isFetching', 'error'],
+    },
+  })
+})
diff --git a/packages/pubsweet-client/test/components/AuthenticatedComponent.test.jsx b/packages/pubsweet-client/test/components/AuthenticatedComponent.test.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..13bf2a122d0ad9ae0e24551347b111ddf18c9355
--- /dev/null
+++ b/packages/pubsweet-client/test/components/AuthenticatedComponent.test.jsx
@@ -0,0 +1,58 @@
+import React from 'react'
+import { shallow } from 'enzyme'
+
+global.PUBSWEET_COMPONENTS = []
+
+jest.mock('fake-mode', () => false, {virtual: true})
+const {
+  AuthenticatedComponent,
+} = require('../../src/components/AuthenticatedComponent')
+
+function makeWrapper(props = {}) {
+  return shallow(
+    <AuthenticatedComponent
+      getCurrentUser={() => Promise.resolve()}
+      pushState={jest.fn()}
+      location={{}}
+      {...props}
+    >
+      <button />
+    </AuthenticatedComponent>,
+  )
+}
+
+describe('<AuthenticatedComponent/>', () => {
+  it('does nothing when fetching', async () => {
+    const pushState = jest.fn()
+    const wrapper = makeWrapper({ pushState })
+    wrapper.instance().checkAuth({ isFetching: true })
+
+    expect(wrapper.find('button').length).toBe(0)
+    expect(pushState).not.toHaveBeenCalled()
+  })
+
+  it('redirects to login if not authenticated', async () => {
+    const pushState = jest.fn()
+    const wrapper = makeWrapper({ pushState, location: { pathname: 'blah' } })
+    wrapper.instance().checkAuth({ isAuthenticated: false })
+
+    expect(pushState).toHaveBeenCalledWith('/login?next=blah')
+  })
+
+  it('calls checkAuth() when props change', () => {
+    const pushState = jest.fn()
+    const wrapper = makeWrapper({ pushState, location: { pathname: 'blah' } })
+
+    expect(pushState).not.toHaveBeenCalled()
+
+    wrapper.setProps({ isAuthenticated: false })
+    expect(pushState).toHaveBeenCalledWith('/login?next=blah')
+  })
+
+  it('renders children components when authenticated', async () => {
+    const pushState = jest.fn()
+    const wrapper = makeWrapper({ pushState, location: { pathname: 'blah' }, isFetching: false, isAuthenticated: true })
+
+    expect(wrapper.find('button').length).toBe(1)
+  })
+})
diff --git a/packages/pubsweet-client/test/components/Root.test.jsx b/packages/pubsweet-client/test/components/Root.test.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..d53aa3cf91f2ad66ea92e9d00fcca7905e24a679
--- /dev/null
+++ b/packages/pubsweet-client/test/components/Root.test.jsx
@@ -0,0 +1,37 @@
+import React from 'react'
+import { shallow } from 'enzyme'
+import { themr } from 'react-css-themr'
+import createHistory from 'history/createBrowserHistory'
+
+global.PUBSWEET_COMPONENTS = []
+
+const Root = require('../../src/components/Root').default
+const configureStore = require('../../src/store/configureStore')
+
+const history = createHistory()
+const store = configureStore(history, {})
+
+const theme = { ThemedComponent: {testClass: 'mappedClassName'} }
+
+const ThemedComponent = themr('ThemedComponent')(({ theme }) => (
+  <div className={theme.testClass}></div>
+))
+
+function makeWrapper(props = {}) {
+  return shallow(
+    <Root
+      store={store}
+      history={history}
+      routes={<ThemedComponent />}
+      theme={theme}
+      {...props}
+    />
+  )
+}
+
+describe('<Root/>', () => {
+  it('Adds a theme to context', async () => {
+    const wrapper = makeWrapper({ theme })
+    expect(wrapper.html()).toBe('<div class="mappedClassName"></div>')
+  })
+})
diff --git a/packages/pubsweet-client/test/components/UpdateSubscriber.test.jsx b/packages/pubsweet-client/test/components/UpdateSubscriber.test.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..3df25f06c4b10bc2639143846917c10f403be128
--- /dev/null
+++ b/packages/pubsweet-client/test/components/UpdateSubscriber.test.jsx
@@ -0,0 +1,122 @@
+import React from 'react'
+import { shallow } from 'enzyme'
+import EventSourceMock, { sources } from 'eventsourcemock'
+
+import { UpdateSubscriber } from '../../src/components/UpdateSubscriber'
+
+global.PUBSWEET_COMPONENTS = []
+
+function makeWrapper(props = {}) {
+  const wrapper = shallow(
+    <UpdateSubscriber currentUser={{}} handleUpdate={jest.fn()} {...props} />,
+  )
+  // did* lifecycle methods don't get automatically called on shallow render
+  wrapper.instance().componentDidMount()
+  return wrapper
+}
+
+describe('<UpdateSubscriber/>', () => {
+  beforeAll(() => {
+    global.window.localStorage = {
+      getItem: jest.fn(() => 'tok'),
+    }
+    global.window.EventSource = EventSourceMock
+  })
+
+  it('is disconnected by default', () => {
+    const wrapper = makeWrapper()
+
+    expect(wrapper.html()).toContain('color:gray')
+  })
+
+  it('is connected after open event', () => {
+    const wrapper = makeWrapper()
+    sources['/updates?access_token=tok'].emit('open')
+
+    expect(wrapper.html()).toContain('color:green')
+  })
+
+  it('is disconnected after error event', () => {
+    const wrapper = makeWrapper()
+
+    sources['/updates?access_token=tok'].emit('open')
+    expect(wrapper.html()).toContain('color:green')
+
+    sources['/updates?access_token=tok'].readyState = 0
+    sources['/updates?access_token=tok'].emit('error')
+    wrapper.update()
+    expect(wrapper.html()).toContain('color:gray')
+  })
+
+  it('is disconnected after close event', () => {
+    const wrapper = makeWrapper()
+
+    sources['/updates?access_token=tok'].emit('open')
+    expect(wrapper.html()).toContain('color:green')
+
+    sources['/updates?access_token=tok'].emit('close')
+    wrapper.update()
+    expect(wrapper.html()).toContain('color:gray')
+  })
+
+  it('calls handleUpdate on message event', () => {
+    const handleUpdate = jest.fn()
+    makeWrapper({ handleUpdate })
+
+    sources['/updates?access_token=tok'].emit('message', {
+      origin: 'null',
+      data: '{"action":"collection:create", "data":{"stuff":42}}',
+    })
+    expect(handleUpdate).toHaveBeenCalledWith('CREATE_COLLECTION_SUCCESS', {
+      stuff: 42,
+    })
+  })
+
+  // TODO enable this test once PR is merged
+  // https://github.com/gcedo/eventsourcemock/pull/1
+  it.skip('unregisters listeners on unmount', () => {
+    const handleUpdate = jest.fn()
+    const wrapper = makeWrapper({ handleUpdate })
+    wrapper.instance().componentWillUnmount()
+
+    sources['/updates?access_token=tok'].emit('message', {
+      origin: 'null',
+      data: '{}',
+    })
+    expect(handleUpdate).not.toHaveBeenCalled()
+  })
+
+  it('connects on prop change once user is provided', () => {
+    const wrapper = makeWrapper({ currentUser: null })
+
+    sources['/updates?access_token=tok'].emit('open')
+    expect(wrapper.html()).toContain('color:gray')
+
+    wrapper.setProps({ currentUser: {} }).update()
+    sources['/updates?access_token=tok'].emit('open')
+    expect(wrapper.html()).toContain('color:green')
+  })
+
+  it('reconnects after timeout if no pulse', () => {
+    jest.useFakeTimers()
+
+    const wrapper = makeWrapper()
+    sources['/updates?access_token=tok'].emit('open')
+
+    const instance = wrapper.instance()
+    jest.spyOn(instance, 'subscribe')
+
+    sources['/updates?access_token=tok'].emit('pulse')
+    jest.runTimersToTime(20000)
+    expect(instance.subscribe).not.toHaveBeenCalled()
+
+    sources['/updates?access_token=tok'].emit('pulse')
+    jest.runTimersToTime(20000)
+    expect(instance.subscribe).not.toHaveBeenCalled()
+
+    jest.runTimersToTime(20000)
+    expect(instance.subscribe).toHaveBeenCalled()
+
+    jest.useRealTimers()
+  })
+})
diff --git a/packages/pubsweet-client/test/components/actions.test.js b/packages/pubsweet-client/test/components/actions.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..97640d5ddc79e7a1bf3525824a534c986f0a3f9c
--- /dev/null
+++ b/packages/pubsweet-client/test/components/actions.test.js
@@ -0,0 +1,41 @@
+describe('component actions combiner', () => {
+  afterEach(() => jest.resetModules())
+
+  it('merges actions into one object', () => {
+    global.PUBSWEET_COMPONENTS = [
+      {
+        client: {
+          actions: () => ({ cowSay: 'moo', dogSay: 'woof' }),
+        },
+      },
+      {
+        frontend: {
+          actions: () => ({ squirrelEat: 'nuts' }),
+        },
+      },
+      {
+        backend: "don't care",
+      },
+    ]
+    const actions = require('../../src/components/actions')
+    expect(actions).toEqual({
+      cowSay: 'moo',
+      dogSay: 'woof',
+      squirrelEat: 'nuts',
+    })
+  })
+
+  it('throws if actions is exported incorrectly', () => {
+    global.PUBSWEET_COMPONENTS = [
+      {
+        client: {
+          actions: [() => 'wrong', () => 'export'],
+        },
+      },
+    ]
+
+    expect(() => {
+      require('../../src/components/actions')
+    }).toThrow()
+  })
+})
diff --git a/packages/pubsweet-client/test/components/reducers.test.js b/packages/pubsweet-client/test/components/reducers.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..a5c27a9323126d070b3a04f831dcc35f5233fddb
--- /dev/null
+++ b/packages/pubsweet-client/test/components/reducers.test.js
@@ -0,0 +1,47 @@
+describe('component actions combiner', () => {
+  afterEach(() => jest.resetModules())
+
+  it('merges actions into one object', () => {
+    global.PUBSWEET_COMPONENTS = [
+      {
+        client: {
+          reducers: () => ({ default: { name: 'handleCows' } }),
+        },
+      },
+      {
+        frontend: {
+          reducers: () => ({ default: { name: 'handleDogs' } }),
+        },
+      },
+      {
+        client: {
+          reducers: {
+            handleDoors: () => 'I am reducer',
+          },
+        },
+      },
+      {
+        backend: "don't care",
+      },
+    ]
+    const reducers = require('../../src/components/reducers')
+    expect(reducers).toEqual([
+      { handleCows: { name: 'handleCows' } },
+      { handleDogs: { name: 'handleDogs' } },
+      { handleDoors: 'I am reducer' },
+    ])
+  })
+
+  it('throws if reducers exported incorrectly', () => {
+    global.PUBSWEET_COMPONENTS = [
+      {
+        client: {
+          reducers: [() => 'wrong', () => 'export'],
+        },
+      },
+    ]
+    expect(() => {
+      require('../../src/components/reducers')
+    }).toThrow()
+  })
+})
diff --git a/packages/pubsweet-client/test/helpers/Authorize.test.jsx b/packages/pubsweet-client/test/helpers/Authorize.test.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..e02eacee80985916fb8937b0beda995a0dee738d
--- /dev/null
+++ b/packages/pubsweet-client/test/helpers/Authorize.test.jsx
@@ -0,0 +1,87 @@
+import React from 'react'
+import { shallow } from 'enzyme'
+import { Authorize } from '../../src/helpers/Authorize'
+jest.mock('fake-mode', () => false, {virtual: true})
+
+global.PUBSWEET_COMPONENTS = []
+
+function makeWrapper(props = {}) {
+  return shallow(
+    <Authorize currentUser={{ id: 'user1' }} {...props}>
+      <div />
+    </Authorize>,
+  )
+}
+
+describe('<Authorize/>', () => {
+  let resolveMode
+  let rejectMode
+  let modePromise
+  let mode
+  let authsome
+
+  beforeEach(() => {
+    modePromise = new Promise((resolve, reject) => {
+      resolveMode = resolve
+      rejectMode = reject
+    })
+    mode = jest.fn(() => modePromise)
+    authsome = { can: mode }
+  })
+
+  it('is empty when not authorized', async () => {
+    const wrapper = makeWrapper({ authsome })
+
+    // mode is called synchronously but returns asynchronously
+    expect(mode).toHaveBeenCalled()
+
+    resolveMode(false)
+
+    // wait for mode promise to resolve before asserting on content
+    await modePromise
+
+    expect(wrapper.type()).toBe(null)
+  })
+
+  it('optionally shows alternative content when not authorized', async () => {
+    const wrapper = makeWrapper({ authsome, unauthorized: <span /> })
+
+    resolveMode(false)
+    await modePromise
+    expect(wrapper.type()).toBe('span')
+  })
+
+  it('is empty when authsome throws', async () => {
+    const wrapper = makeWrapper({ authsome })
+    jest.spyOn(console, 'error').mockImplementation(jest.fn())
+
+    rejectMode(new Error('Authorize test error'))
+
+    try {
+      await modePromise
+    } catch (err) {
+      expect(wrapper.type()).toBe(null)
+      expect(console.error).toHaveBeenCalled()
+    }
+
+    expect.hasAssertions()
+  })
+
+  it('renders children when authorized', async () => {
+    const wrapper = makeWrapper({ authsome })
+
+    resolveMode(true)
+    await modePromise
+    expect(wrapper.type()).toBe('div')
+  })
+
+  it('rechecks auth when props change', () => {
+    const wrapper = makeWrapper({ authsome })
+
+    expect(mode).toHaveBeenCalled()
+    mode.mockClear()
+
+    wrapper.setProps({ authsome, currentUser: { id: 'user2' } })
+    expect(mode).toHaveBeenCalled()
+  })
+})
diff --git a/packages/pubsweet-client/test/helpers/api.test.js b/packages/pubsweet-client/test/helpers/api.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..dd1d4ed6576f4894df784bdf57d8de1db6bf22ec
--- /dev/null
+++ b/packages/pubsweet-client/test/helpers/api.test.js
@@ -0,0 +1,118 @@
+import nock from 'nock'
+import endpoint from '../../src/helpers/endpoint'
+
+// must be require, not import, due to mocking global above
+const api = require('../../src/helpers/api')
+
+describe('API helper', () => {
+  beforeAll(() => {
+    global.window.localStorage = {
+      getItem: jest.fn(() => 'tok123'),
+    }
+  })
+
+  it('makes a GET request', async () => {
+    nock(endpoint)
+      .get('/thing')
+      .reply(200, 'A thing', { 'content-type': 'text/plain' })
+
+    const actual = await api.get('/thing')
+    expect(actual).toBe('A thing')
+  })
+
+  it('makes a POST request', async () => {
+    nock(endpoint)
+      .post('/thing', {
+        some: 'data',
+      })
+      .reply(200, 'A new thing', { 'content-type': 'text/plain' })
+
+    const actual = await api.create('/thing', { some: 'data' })
+    expect(actual).toBe('A new thing')
+  })
+
+  it('makes a PATCH request', async () => {
+    nock(endpoint)
+      .patch('/thing/1', {
+        some: 'data',
+      })
+      .reply(200, 'A partially updated thing', { 'content-type': 'text/plain' })
+
+    const actual = await api.update('/thing/1', { some: 'data' })
+    expect(actual).toBe('A partially updated thing')
+  })
+
+  it('makes a PUT request', async () => {
+    nock(endpoint)
+      .put('/thing/1', {
+        some: 'data',
+      })
+      .reply(200, 'An updated thing', { 'content-type': 'text/plain' })
+
+    const actual = await api.update('/thing/1', { some: 'data' }, true)
+    expect(actual).toBe('An updated thing')
+  })
+
+  it('makes a DELETE request', async () => {
+    nock(endpoint)
+      .delete('/thing/1')
+      .reply(200, 'No thing', { 'content-type': 'text/plain' })
+
+    const actual = await api.remove('/thing/1')
+    expect(actual).toBe('No thing')
+  })
+
+  it('automatically parses JSON response', async () => {
+    const expected = { oh: 'yeah' }
+    nock(endpoint)
+      .get('/thing')
+      .reply(200, expected, { 'content-type': 'application/json' })
+
+    const actual = await api.get('/thing')
+    expect(actual).toEqual(expected)
+  })
+
+  it('includes token in header', async () => {
+    nock(endpoint, {
+      reqheaders: {
+        authorization: 'Bearer tok123',
+      },
+    })
+      .get('/thing')
+      .reply(200, 'OK', { 'content-type': '' })
+
+    const actual = await api.get('/thing')
+    expect(actual).toEqual('OK')
+  })
+
+  it('wraps HTTP errors', async () => {
+    nock(endpoint)
+      .get('/thing')
+      .reply(500, 'Yikes!', { 'content-type': '' })
+
+    try {
+      await api.get('/thing')
+    } catch (e) {
+      expect(e.message).toBe('Internal Server Error')
+      expect(e.response).toBe('Yikes!')
+      expect(e.statusCode).toBe(500)
+    }
+    expect.hasAssertions()
+  })
+
+  it('optionally returns raw response', async () => {
+    nock(endpoint)
+      .get('/thing')
+      .reply(204)
+
+    const response = await api.default('/thing', {
+      method: 'GET',
+      parse: false,
+    })
+
+    expect(response).toMatchObject({
+      ok: true,
+      statusText: 'No Content',
+    })
+  })
+})
diff --git a/packages/pubsweet-client/test/helpers/describeAction.js b/packages/pubsweet-client/test/helpers/describeAction.js
new file mode 100644
index 0000000000000000000000000000000000000000..4d6222d18f3291de6eb0788ad7b2f0e9650e12c3
--- /dev/null
+++ b/packages/pubsweet-client/test/helpers/describeAction.js
@@ -0,0 +1,128 @@
+const allactions = require('../../src/actions').default
+const api = require('../../src/helpers/api')
+
+const mockGetState = () => {
+  return {
+    currentUser: {},
+  }
+}
+
+function mockApi(succeed = true) {
+  const implementation = () =>
+    succeed ? Promise.resolve({}) : Promise.reject(new Error({}))
+  Object.keys(api)
+    .filter(method => typeof api[method] === 'function')
+    .forEach(method =>
+      jest.spyOn(api, method).mockImplementation(implementation),
+    )
+}
+
+// custom Jest matcher
+expect.extend({
+  toHaveProperties(object, expectedKeys) {
+    const actualKeys = Object.keys(object)
+    const pass = expectedKeys.every(key => actualKeys.includes(key))
+    return {
+      message: `Expected object ${pass
+        ? 'not to'
+        : 'to'} have properties: ${expectedKeys.join(', ')}`,
+      pass,
+    }
+  },
+})
+
+const describeAction = actions => (key, opts) => {
+  describe(key, () => {
+    const actionCreator = actions[key]
+
+    beforeEach(mockApi)
+
+    afterEach(() => jest.restoreAllMocks())
+
+    it('is exported from the file', () => {
+      expect(actions).toHaveProperty(key)
+    })
+
+    it('is exported in the all actions object', () => {
+      expect(allactions).toHaveProperty(key)
+    })
+
+    it('returns a fetcher function', () => {
+      const thunk = actionCreator(() => {}, mockGetState)
+      expect(typeof thunk).toBe('function')
+    })
+
+    it('returns a promise from the fetcher function', () => {
+      const thunk = actionCreator(opts.firstarg, opts.secondarg)
+      const returned = thunk(() => {}, mockGetState)
+      expect(typeof returned.then).toBe('function')
+    })
+
+    it('dispatches an action object with a type property', () => {
+      const actions = []
+      const thunk = actionCreator(opts.firstarg, opts.secondarg)
+      thunk(action => actions.push(action), mockGetState)
+      expect(actions).toHaveLength(1)
+      expect(actions[0]).toHaveProperty('type')
+    })
+
+    if (opts.types.request) {
+      const properties = opts.properties.request
+      const propmsg = properties ? `with [${properties.join(', ')}] ` : ''
+
+      it(`dispatches ${key}Request ${propmsg}immediately`, () => {
+        const actions = []
+        const thunk = actionCreator(opts.firstarg, opts.secondarg)
+        thunk(action => actions.push(action), mockGetState)
+
+        const firstAction = actions[0]
+        expect(firstAction).toBeTruthy()
+        expect(firstAction.type).toBe(opts.types.request)
+        if (properties) {
+          expect(firstAction).toHaveProperties(properties)
+        }
+      })
+    }
+
+    if (opts.types.success) {
+      const properties = opts.properties.success
+      const propmsg = properties ? `with [${properties.join(', ')}] ` : ''
+
+      it(`dispatches ${key}Success ${propmsg}on successful response`, async () => {
+        const actions = []
+        const thunk = actionCreator(opts.firstarg, opts.secondarg)
+        await thunk(action => actions.push(action), mockGetState)
+
+        const secondAction = actions[1]
+        expect(secondAction).toBeTruthy()
+        expect(secondAction.type).toBe(opts.types.success)
+        if (properties) {
+          expect(secondAction).toHaveProperties(properties)
+        }
+      })
+    }
+
+    if (opts.types.failure) {
+      const properties = opts.properties.failure
+      const propmsg = properties ? `with [${properties.join(', ')}] ` : ''
+
+      it(`dispatches ${key}Failure ${propmsg}on failed response`, async () => {
+        // make API reject every request
+        mockApi(false)
+
+        const actions = []
+        const thunk = actionCreator(opts.firstarg, opts.secondarg)
+        await thunk(action => actions.push(action), mockGetState)
+
+        const secondAction = actions[1]
+        expect(secondAction).toBeTruthy()
+        expect(secondAction.type).toBe(opts.types.failure)
+        if (properties) {
+          expect(secondAction).toHaveProperties(properties)
+        }
+      })
+    }
+  })
+}
+
+module.exports = describeAction
diff --git a/packages/pubsweet-client/test/helpers/withAuthsome.test.js b/packages/pubsweet-client/test/helpers/withAuthsome.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..ade5c259180f166f4fa66a5aff620a1b94607f3f
--- /dev/null
+++ b/packages/pubsweet-client/test/helpers/withAuthsome.test.js
@@ -0,0 +1,37 @@
+import React from 'react'
+import { shallow } from 'enzyme'
+import configureStore from 'redux-mock-store'
+import Authsome from 'authsome'
+
+import withAuthsome from '../../src/helpers/withAuthsome'
+jest.mock('fake-mode', () => false, {virtual: true})
+
+describe('withAuthsome higher order component', () => {
+  const createStore = configureStore()
+
+  it('injects authsome instance as prop', () => {
+    const store = createStore()
+    const Component = withAuthsome()(() => null)
+    const wrapper = shallow(<Component store={store} />)
+
+    const authsome = wrapper.prop('authsome')
+    expect(authsome).toBeInstanceOf(Authsome)
+  })
+
+  it('reads values from store', () => {
+    const user1 = { id: 1, admin: true }
+    const user2 = { id: 2 }
+    const store = createStore({
+      users: {
+        users: [user1, user2],
+      },
+    })
+    const Component = withAuthsome()(() => null)
+    const wrapper = shallow(<Component store={store} />)
+
+    const { context } = wrapper.prop('authsome')
+    expect(context.models.User.find(1)).toEqual(user1)
+    expect(context.models.User.find(2)).toEqual(user2)
+    expect(context.models.User.find(3)).toBeUndefined()
+  })
+})
diff --git a/packages/pubsweet-client/test/reducers/collections.test.js b/packages/pubsweet-client/test/reducers/collections.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..0ff025c93b75bc726e37be2102e0e8c236d2fdfb
--- /dev/null
+++ b/packages/pubsweet-client/test/reducers/collections.test.js
@@ -0,0 +1,116 @@
+const allReducers = require('../../src/reducers').default
+const reducer = require('../../src/reducers/collections').default
+
+const T = require('../../src/actions/types')
+
+describe('collections reducers', () => {
+  it('is exported in the all reducers object', () => {
+    expect(allReducers.collections).toBe(reducer)
+  })
+
+  const mockCollection = { id: '123' }
+  const mockFragment = { name: 'mock fragment', id: '1234' }
+  const mockCollectionWithFragment = {
+    ...mockCollection,
+    fragments: [mockFragment.id],
+  }
+
+  it('getCollections success', () => {
+    const actual = reducer([mockCollection], {
+      type: T.GET_COLLECTIONS_SUCCESS,
+      collections: [mockCollection],
+    })
+    expect(actual).toEqual([mockCollection])
+  })
+
+  it('getCollections failure', () => {
+    const actual = reducer(undefined, {
+      type: T.GET_COLLECTIONS_FAILURE,
+    })
+    expect(actual).toEqual([])
+  })
+
+  it('getCollection request', () => {
+    const actual = reducer([mockCollection], {
+      type: T.GET_COLLECTION_REQUEST,
+      collection: mockCollection,
+    })
+    expect(actual).toEqual([])
+  })
+
+  it('getCollection success adds collection to store', () => {
+    const actual = reducer([], {
+      type: T.GET_COLLECTION_SUCCESS,
+      collection: mockCollection,
+    })
+    expect(actual).toEqual([mockCollection])
+  })
+
+  it('getCollection success updates collection in store', () => {
+    const actual = reducer([mockCollection], {
+      type: T.GET_COLLECTION_SUCCESS,
+      collection: mockCollectionWithFragment,
+    })
+    expect(actual).toEqual([mockCollectionWithFragment])
+  })
+
+  it('createCollection success', () => {
+    const actual = reducer(['dummy'], {
+      type: T.CREATE_COLLECTION_SUCCESS,
+      collection: mockCollection,
+    })
+    expect(actual).toEqual(['dummy', mockCollection])
+  })
+
+  it('createCollection success ignores duplicate', () => {
+    const actual = reducer([mockCollection], {
+      type: T.CREATE_COLLECTION_SUCCESS,
+      collection: { ...mockCollection, same: 'but different' },
+    })
+    expect(actual).toEqual([mockCollection])
+  })
+
+  it('updateCollection success', () => {
+    const actual = reducer(['dummy', mockCollection], {
+      type: T.UPDATE_COLLECTION_SUCCESS,
+      collection: mockCollection,
+      update: {
+        some: 'value',
+      },
+    })
+    expect(actual).toEqual(['dummy', { ...mockCollection, some: 'value' }])
+  })
+
+  it('addFragments success', () => {
+    const actual = reducer([mockCollection], {
+      type: T.CREATE_FRAGMENT_SUCCESS,
+      collection: mockCollection,
+      fragment: mockFragment,
+    })
+    expect(actual).toEqual([mockCollectionWithFragment])
+  })
+
+  it('removeFragments success', () => {
+    const actual = reducer([mockCollectionWithFragment], {
+      type: T.DELETE_FRAGMENT_SUCCESS,
+      collection: mockCollectionWithFragment,
+      fragment: mockFragment,
+    })
+    expect(actual).toEqual([mockCollectionWithFragment])
+  })
+
+  it('logout success', () => {
+    const actual = reducer([mockCollectionWithFragment], {
+      type: T.LOGOUT_SUCCESS,
+    })
+    expect(actual).toEqual([])
+  })
+
+  it('returns same state for unrecognised action', () => {
+    const state = []
+    const actual = reducer(state, {
+      type: 'something else',
+    })
+    expect(actual).toBe(state)
+  })
+})
diff --git a/packages/pubsweet-client/test/reducers/currentUser.test.js b/packages/pubsweet-client/test/reducers/currentUser.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..de55c028c7faf919ebc2a7ee6b3d95d7a82845bc
--- /dev/null
+++ b/packages/pubsweet-client/test/reducers/currentUser.test.js
@@ -0,0 +1,73 @@
+const allReducers = require('../../src/reducers').default
+const reducer = require('../../src/reducers/currentUser').default
+
+const T = require('../../src/actions/types')
+
+describe('currentUser reducers', () => {
+  it('is exported in the all reducers object', () => {
+    expect(allReducers.currentUser).toBe(reducer)
+  })
+
+  const mockuser = {
+    name: 'jo johnson',
+  }
+
+  it('currentUser success', () => {
+    const actual = reducer(
+      {},
+      {
+        type: T.GET_CURRENT_USER_SUCCESS,
+        user: mockuser,
+      },
+    )
+    expect(actual).toEqual({
+      isFetching: false,
+      isAuthenticated: true,
+      user: mockuser,
+    })
+  })
+
+  it('currentUser failure', () => {
+    const actual = reducer(
+      {},
+      {
+        type: T.GET_CURRENT_USER_FAILURE,
+      },
+    )
+    expect(actual).toEqual({ isFetching: false, isAuthenticated: false })
+  })
+
+  it('currentUser request', () => {
+    const actual = reducer(
+      {},
+      {
+        type: T.GET_CURRENT_USER_REQUEST,
+      },
+    )
+    expect(actual).toEqual({ isFetching: true, isAuthenticated: false })
+  })
+
+  it('logout success', () => {
+    const actual = reducer(
+      {
+        user: mockuser,
+      },
+      {
+        type: T.LOGOUT_SUCCESS,
+      },
+    )
+    expect(actual).toEqual({
+      isFetching: false,
+      isAuthenticated: false,
+      user: null,
+    })
+  })
+
+  it('returns same state for unrecognised action', () => {
+    const state = {}
+    const actual = reducer(state, {
+      type: 'something else',
+    })
+    expect(actual).toBe(state)
+  })
+})
diff --git a/packages/pubsweet-client/test/reducers/error.test.js b/packages/pubsweet-client/test/reducers/error.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..ad2fc75f496f0ba936928fdbb6ae15eaed2a63a4
--- /dev/null
+++ b/packages/pubsweet-client/test/reducers/error.test.js
@@ -0,0 +1,22 @@
+const allReducers = require('../../src/reducers').default
+const reducer = require('../../src/reducers/error').default
+
+describe('error reducers', () => {
+  it('is exported in the all reducers object', () => {
+    expect(allReducers.error).toBe(reducer)
+  })
+
+  describe('reducer error handler', () => {
+    it("doesn't do anything if there's no error", () => {
+      expect(reducer(null, { error: null })).toBeNull()
+    })
+
+    it("returns the error message if there's an error", () => {
+      jest.spyOn(console, 'error').mockImplementation(jest.fn())
+      const error = new Error('this is a fake error')
+      const action = { error }
+      expect(reducer(null, action)).toBe(error.message)
+      expect(console.error.mock.calls[0][0]).toBe(error)
+    })
+  })
+})
diff --git a/packages/pubsweet-client/test/reducers/fileUpload.test.js b/packages/pubsweet-client/test/reducers/fileUpload.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..4e0a014fe82e3a7b6c35580fe828332e2386f6a0
--- /dev/null
+++ b/packages/pubsweet-client/test/reducers/fileUpload.test.js
@@ -0,0 +1,36 @@
+const allReducers = require('../../src/reducers').default
+const reducer = require('../../src/reducers/fileUpload').default
+
+const T = require('../../src/actions/types')
+
+describe('fileUpload reducers', () => {
+  it('is exported in the all reducers object', () => {
+    expect(allReducers.fileUpload).toBe(reducer)
+  })
+
+  it('fileUpload success', () => {
+    const actual = reducer(undefined, {
+      type: T.FILE_UPLOAD_SUCCESS,
+      file: 'somefile',
+    })
+    expect(actual).toEqual({
+      isFetching: false,
+      file: 'somefile',
+    })
+  })
+
+  it('fileUpload request', () => {
+    const actual = reducer(undefined, {
+      type: T.FILE_UPLOAD_REQUEST,
+    })
+    expect(actual).toEqual({ isFetching: true })
+  })
+
+  it('returns same state for unrecognised action', () => {
+    const state = {}
+    const actual = reducer(state, {
+      type: 'something else',
+    })
+    expect(actual).toBe(state)
+  })
+})
diff --git a/packages/pubsweet-client/test/reducers/fragments.test.js b/packages/pubsweet-client/test/reducers/fragments.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..6a51433adf76b231cb5eba7c1337c0a93ca0a03e
--- /dev/null
+++ b/packages/pubsweet-client/test/reducers/fragments.test.js
@@ -0,0 +1,97 @@
+const allReducers = require('../../src/reducers').default
+const reducer = require('../../src/reducers/fragments').default
+
+const T = require('../../src/actions/types')
+
+const clone = require('lodash/clone')
+
+describe('fragments reducers', () => {
+  it('is exported in the all reducers object', () => {
+    expect(allReducers.fragments).toBe(reducer)
+  })
+
+  const mockcol = { id: '123' }
+  mockcol.fragments = []
+
+  const mockfrag = {
+    title: 'mock fragment',
+    type: 'some_fragment',
+    owners: [],
+  }
+  const mockstate = {}
+  mockstate[mockfrag.id] = mockfrag
+
+  const mockfragmod = {
+    title: 'modded fragment',
+    type: 'some_fragment',
+    owners: [],
+  }
+  const mockstatemod = {}
+  mockstatemod[mockfrag.id] = mockfragmod
+
+  const colwithfrag = clone(mockcol)
+  colwithfrag.fragments = [mockfrag]
+
+  it('getOne request', () => {
+    const actual = reducer(mockstate, {
+      type: T.GET_FRAGMENT_REQUEST,
+      collection: colwithfrag,
+      fragment: mockfragmod,
+    })
+    expect(actual).toEqual({})
+  })
+
+  it('getOne success', () => {
+    const actual = reducer(
+      {},
+      {
+        type: T.GET_FRAGMENT_SUCCESS,
+        collection: colwithfrag,
+        fragment: mockfragmod,
+      },
+    )
+    expect(actual).toEqual(mockstatemod)
+  })
+
+  it('updateOne success', () => {
+    const actual = reducer(mockstate, {
+      type: T.UPDATE_FRAGMENT_SUCCESS,
+      collection: colwithfrag,
+      fragment: mockfragmod,
+    })
+    expect(actual).toEqual(mockstatemod)
+  })
+
+  it('removeOne success', () => {
+    const actual = reducer(mockstate, {
+      type: T.DELETE_FRAGMENT_SUCCESS,
+      collection: colwithfrag,
+      fragment: mockfrag,
+    })
+    expect(actual).toEqual({})
+  })
+
+  it('replaceAll success', () => {
+    const actual = reducer(mockstate, {
+      type: T.GET_FRAGMENTS_SUCCESS,
+      collection: colwithfrag,
+      fragments: [mockfragmod],
+    })
+    expect(actual).toEqual(mockstatemod)
+  })
+
+  it('logout success', () => {
+    const actual = reducer(mockstate, {
+      type: T.LOGOUT_SUCCESS,
+    })
+    expect(actual).toEqual({})
+  })
+
+  it('returns same state for unrecognised action', () => {
+    const state = {}
+    const actual = reducer(state, {
+      type: 'something else',
+    })
+    expect(actual).toBe(state)
+  })
+})
diff --git a/packages/pubsweet-client/test/reducers/teams.test.js b/packages/pubsweet-client/test/reducers/teams.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..49160d689d64f63bad90205aa03450330f9f8c24
--- /dev/null
+++ b/packages/pubsweet-client/test/reducers/teams.test.js
@@ -0,0 +1,70 @@
+const allReducers = require('../../src/reducers').default
+const reducer = require('../../src/reducers/teams').default
+
+const T = require('../../src/actions/types')
+
+describe('teams reducers', () => {
+  it('is exported in the all reducers object', () => {
+    expect(allReducers.teams).toBe(reducer)
+  })
+
+  const mockteam = { name: 'someteam', id: '1234' }
+  const mockstate = [mockteam]
+
+  it('createTeam success', () => {
+    const actual = reducer([], {
+      type: T.CREATE_TEAM_SUCCESS,
+      team: mockteam,
+    })
+    expect(actual).toEqual(mockstate)
+  })
+
+  it('updateTeam success', () => {
+    const updatedTeam = { ...mockteam, foo: 'bar' }
+    const actual = reducer(mockstate, {
+      type: T.CREATE_TEAM_SUCCESS,
+      team: updatedTeam,
+    })
+    expect(actual).toEqual([updatedTeam])
+  })
+
+  it('getTeams success', () => {
+    const actual = reducer(mockstate, {
+      type: T.GET_TEAMS_SUCCESS,
+      teams: mockstate,
+    })
+    expect(actual).toEqual(mockstate)
+  })
+
+  it('deleteTeam success', () => {
+    const actual = reducer(mockstate, {
+      type: T.DELETE_TEAM_SUCCESS,
+      team: { ...mockteam },
+    })
+    expect(actual).toEqual([])
+  })
+
+  it('logout success', () => {
+    const actual = reducer(mockstate, {
+      type: T.LOGOUT_SUCCESS,
+    })
+    expect(actual).toEqual([])
+  })
+
+  it('getCollectionTeam success', () => {
+    const extraTeam = { id: '4321', name: 'Another team' }
+    const actual = reducer(mockstate, {
+      type: T.GET_COLLECTION_TEAMS_SUCCESS,
+      teams: [extraTeam, mockteam],
+    })
+    expect(actual).toEqual([mockteam, extraTeam])
+  })
+
+  it('returns same state for unrecognised action', () => {
+    const state = []
+    const actual = reducer(state, {
+      type: 'something else',
+    })
+    expect(actual).toBe(state)
+  })
+})
diff --git a/packages/pubsweet-client/test/reducers/users.test.js b/packages/pubsweet-client/test/reducers/users.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..33e2c1726caff14ba7617da63cfa53d533b13ed6
--- /dev/null
+++ b/packages/pubsweet-client/test/reducers/users.test.js
@@ -0,0 +1,109 @@
+import { GET_CURRENT_USER_SUCCESS } from '../../src/actions/types'
+
+const allReducers = require('../../src/reducers').default
+const reducer = require('../../src/reducers/users').default
+
+const T = require('../../src/actions/types')
+
+describe('users reducers', () => {
+  it('is exported in the all reducers object', () => {
+    expect(allReducers.users).toBe(reducer)
+  })
+
+  const user = {
+    username: 'fakeymcfake',
+    password: 'correct battery horse staple',
+    email: 'fakey_mcfake@pseudonymous.com',
+    id: '57d0fc8e-ece9-47bf-87d3-7935326b0128',
+  }
+
+  const usermod = { ...user, email: 'new@email.com' }
+
+  const mockstate = { users: [user] }
+
+  it('getUsers success', () => {
+    const actual = reducer(undefined, {
+      type: T.GET_USERS_SUCCESS,
+      users: [user],
+    })
+    expect(actual).toEqual({
+      isFetching: false,
+      users: [user],
+    })
+  })
+
+  it('getUsers request', () => {
+    const actual = reducer(undefined, {
+      type: T.GET_USERS_REQUEST,
+    })
+    expect(actual).toEqual({
+      isFetching: true,
+      users: [],
+    })
+  })
+
+  it('getUser success', () => {
+    const actual = reducer(
+      { users: [] },
+      {
+        type: T.GET_USER_SUCCESS,
+        user: user,
+      },
+    )
+    expect(actual).toEqual({
+      users: [user],
+      isFetching: false,
+    })
+  })
+
+  it('updateUser request', () => {
+    const actual = reducer(mockstate, {
+      type: T.UPDATE_USER_REQUEST,
+      user: usermod,
+    })
+    expect(actual).toEqual({
+      users: [user],
+      isFetching: true,
+    })
+  })
+
+  it('updateUser success', () => {
+    const actual = reducer(mockstate, {
+      type: T.UPDATE_USER_SUCCESS,
+      user: usermod,
+    })
+    expect(actual).toEqual({
+      users: [usermod],
+      isFetching: false,
+    })
+  })
+
+  it('logout success', () => {
+    const actual = reducer(mockstate, {
+      type: T.LOGOUT_SUCCESS,
+      user: usermod,
+    })
+    expect(actual).toEqual({
+      users: [],
+      isFetching: false,
+    })
+  })
+
+  it('getCurrentUser success adds user to users array', () => {
+    const actual = reducer(mockstate, {
+      type: GET_CURRENT_USER_SUCCESS,
+      user,
+    })
+    expect(actual).toEqual({
+      users: [user],
+    })
+  })
+
+  it('returns same state for unrecognised action', () => {
+    const state = {}
+    const actual = reducer(state, {
+      type: 'something else',
+    })
+    expect(actual).toBe(state)
+  })
+})
diff --git a/packages/pubsweet-client/test/store/configureStore.test.js b/packages/pubsweet-client/test/store/configureStore.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..ccf70ba2066a29503c48e3123dd59096de266650
--- /dev/null
+++ b/packages/pubsweet-client/test/store/configureStore.test.js
@@ -0,0 +1,41 @@
+describe('configureStore', () => {
+  afterEach(() => jest.resetModules())
+
+  it('allows initialization of store with custom initialState, reducers and middleware', () => {
+    global.PUBSWEET_COMPONENTS = []
+
+    const configureStore = require('../../src/store/configureStore')
+
+    const customHistory = {}
+    const customInitialState = { collections: ['initial'] }
+
+    const customReducers = {
+      test: (state, action) => {
+        return {
+          ...state,
+          fired: action.fired,
+        }
+      },
+    }
+
+    const customMiddlewares = [
+      () => next => action => {
+        action.fired = true
+        return next(action)
+      },
+    ]
+
+    const store = configureStore(
+      customHistory,
+      customInitialState,
+      customReducers,
+      customMiddlewares,
+    )
+
+    expect(store.getState().collections[0]).toBe(
+      customInitialState.collections[0],
+    )
+    store.dispatch({ type: 'TEST', fired: false })
+    expect(store.getState().test.fired).toBe(true)
+  })
+})
diff --git a/packages/pubsweet-client/yarn.lock b/packages/pubsweet-client/yarn.lock
new file mode 100644
index 0000000000000000000000000000000000000000..2860c32ad9120581836b3365919e9540073b4357
--- /dev/null
+++ b/packages/pubsweet-client/yarn.lock
@@ -0,0 +1,4578 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+abab@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.4.tgz#5faad9c2c07f60dd76770f71cf025b62a63cfd4e"
+
+abbrev@1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
+
+acorn-globals@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-3.1.0.tgz#fd8270f71fbb4996b004fa880ee5d46573a731bf"
+  dependencies:
+    acorn "^4.0.4"
+
+acorn-jsx@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b"
+  dependencies:
+    acorn "^3.0.4"
+
+acorn@^3.0.4:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
+
+acorn@^4.0.4:
+  version "4.0.13"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787"
+
+acorn@^5.1.1:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.1.2.tgz#911cb53e036807cf0fa778dc5d370fbd864246d7"
+
+ajv-keywords@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.0.tgz#a296e17f7bfae7c1ce4f7e0de53d29cb32162df0"
+
+ajv@^4.9.1:
+  version "4.11.8"
+  resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536"
+  dependencies:
+    co "^4.6.0"
+    json-stable-stringify "^1.0.1"
+
+ajv@^5.1.0, ajv@^5.2.0, ajv@^5.2.3:
+  version "5.2.3"
+  resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.2.3.tgz#c06f598778c44c6b161abafe3466b81ad1814ed2"
+  dependencies:
+    co "^4.6.0"
+    fast-deep-equal "^1.0.0"
+    json-schema-traverse "^0.3.0"
+    json-stable-stringify "^1.0.1"
+
+align-text@^0.1.1, align-text@^0.1.3:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117"
+  dependencies:
+    kind-of "^3.0.2"
+    longest "^1.0.1"
+    repeat-string "^1.5.2"
+
+amdefine@>=0.0.4:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
+
+ansi-escapes@^1.0.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e"
+
+ansi-escapes@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.0.0.tgz#ec3e8b4e9f8064fc02c3ac9b65f1c275bda8ef92"
+
+ansi-regex@^2.0.0:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
+
+ansi-regex@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
+
+ansi-styles@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
+
+ansi-styles@^3.1.0, ansi-styles@^3.2.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.0.tgz#c159b8d5be0f9e5a6f346dab94f16ce022161b88"
+  dependencies:
+    color-convert "^1.9.0"
+
+anymatch@^1.3.0:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a"
+  dependencies:
+    micromatch "^2.1.5"
+    normalize-path "^2.0.0"
+
+app-root-path@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.0.1.tgz#cd62dcf8e4fd5a417efc664d2e5b10653c651b46"
+
+append-transform@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.4.0.tgz#d76ebf8ca94d276e247a36bad44a4b74ab611991"
+  dependencies:
+    default-require-extensions "^1.0.0"
+
+aproba@^1.0.3:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
+
+are-we-there-yet@~1.1.2:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d"
+  dependencies:
+    delegates "^1.0.0"
+    readable-stream "^2.0.6"
+
+argparse@^1.0.7:
+  version "1.0.9"
+  resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86"
+  dependencies:
+    sprintf-js "~1.0.2"
+
+arr-diff@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf"
+  dependencies:
+    arr-flatten "^1.0.1"
+
+arr-flatten@^1.0.1:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1"
+
+array-equal@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93"
+
+array-includes@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d"
+  dependencies:
+    define-properties "^1.1.2"
+    es-abstract "^1.7.0"
+
+array-union@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
+  dependencies:
+    array-uniq "^1.0.1"
+
+array-uniq@^1.0.1:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
+
+array-unique@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53"
+
+arrify@^1.0.0, arrify@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
+
+asap@~2.0.3:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
+
+asn1@~0.2.3:
+  version "0.2.3"
+  resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86"
+
+assert-plus@1.0.0, assert-plus@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
+
+assert-plus@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234"
+
+assertion-error@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.0.2.tgz#13ca515d86206da0bac66e834dd397d87581094c"
+
+astral-regex@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
+
+async-each@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d"
+
+async@^1.4.0:
+  version "1.5.2"
+  resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
+
+async@^2.0.1, async@^2.1.4:
+  version "2.5.0"
+  resolved "https://registry.yarnpkg.com/async/-/async-2.5.0.tgz#843190fd6b7357a0b9e1c956edddd5ec8462b54d"
+  dependencies:
+    lodash "^4.14.0"
+
+asynckit@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+
+authsome@0.0.9:
+  version "0.0.9"
+  resolved "https://registry.yarnpkg.com/authsome/-/authsome-0.0.9.tgz#08b34f1797b3539e6a362f0fb11a01ae0613f928"
+
+aws-sign2@~0.6.0:
+  version "0.6.0"
+  resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f"
+
+aws-sign2@~0.7.0:
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
+
+aws4@^1.2.1, aws4@^1.6.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e"
+
+babel-cli@^6.26.0:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-cli/-/babel-cli-6.26.0.tgz#502ab54874d7db88ad00b887a06383ce03d002f1"
+  dependencies:
+    babel-core "^6.26.0"
+    babel-polyfill "^6.26.0"
+    babel-register "^6.26.0"
+    babel-runtime "^6.26.0"
+    commander "^2.11.0"
+    convert-source-map "^1.5.0"
+    fs-readdir-recursive "^1.0.0"
+    glob "^7.1.2"
+    lodash "^4.17.4"
+    output-file-sync "^1.1.2"
+    path-is-absolute "^1.0.1"
+    slash "^1.0.0"
+    source-map "^0.5.6"
+    v8flags "^2.1.1"
+  optionalDependencies:
+    chokidar "^1.6.1"
+
+babel-code-frame@^6.22.0, babel-code-frame@^6.26.0:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
+  dependencies:
+    chalk "^1.1.3"
+    esutils "^2.0.2"
+    js-tokens "^3.0.2"
+
+babel-core@^6.0.0, babel-core@^6.26.0:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.0.tgz#af32f78b31a6fcef119c87b0fd8d9753f03a0bb8"
+  dependencies:
+    babel-code-frame "^6.26.0"
+    babel-generator "^6.26.0"
+    babel-helpers "^6.24.1"
+    babel-messages "^6.23.0"
+    babel-register "^6.26.0"
+    babel-runtime "^6.26.0"
+    babel-template "^6.26.0"
+    babel-traverse "^6.26.0"
+    babel-types "^6.26.0"
+    babylon "^6.18.0"
+    convert-source-map "^1.5.0"
+    debug "^2.6.8"
+    json5 "^0.5.1"
+    lodash "^4.17.4"
+    minimatch "^3.0.4"
+    path-is-absolute "^1.0.1"
+    private "^0.1.7"
+    slash "^1.0.0"
+    source-map "^0.5.6"
+
+babel-generator@^6.18.0, babel-generator@^6.26.0:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.0.tgz#ac1ae20070b79f6e3ca1d3269613053774f20dc5"
+  dependencies:
+    babel-messages "^6.23.0"
+    babel-runtime "^6.26.0"
+    babel-types "^6.26.0"
+    detect-indent "^4.0.0"
+    jsesc "^1.3.0"
+    lodash "^4.17.4"
+    source-map "^0.5.6"
+    trim-right "^1.0.1"
+
+babel-helper-bindify-decorators@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.24.1.tgz#14c19e5f142d7b47f19a52431e52b1ccbc40a330"
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
+
+babel-helper-builder-binary-assignment-operator-visitor@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz#cce4517ada356f4220bcae8a02c2b346f9a56664"
+  dependencies:
+    babel-helper-explode-assignable-expression "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+
+babel-helper-builder-react-jsx@^6.24.1:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.26.0.tgz#39ff8313b75c8b65dceff1f31d383e0ff2a408a0"
+  dependencies:
+    babel-runtime "^6.26.0"
+    babel-types "^6.26.0"
+    esutils "^2.0.2"
+
+babel-helper-call-delegate@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d"
+  dependencies:
+    babel-helper-hoist-variables "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
+
+babel-helper-define-map@^6.24.1:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz#a5f56dab41a25f97ecb498c7ebaca9819f95be5f"
+  dependencies:
+    babel-helper-function-name "^6.24.1"
+    babel-runtime "^6.26.0"
+    babel-types "^6.26.0"
+    lodash "^4.17.4"
+
+babel-helper-explode-assignable-expression@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz#f25b82cf7dc10433c55f70592d5746400ac22caa"
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
+
+babel-helper-explode-class@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-explode-class/-/babel-helper-explode-class-6.24.1.tgz#7dc2a3910dee007056e1e31d640ced3d54eaa9eb"
+  dependencies:
+    babel-helper-bindify-decorators "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
+
+babel-helper-function-name@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9"
+  dependencies:
+    babel-helper-get-function-arity "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
+
+babel-helper-get-function-arity@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d"
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+
+babel-helper-hoist-variables@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76"
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+
+babel-helper-optimise-call-expression@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257"
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+
+babel-helper-regex@^6.24.1:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz#325c59f902f82f24b74faceed0363954f6495e72"
+  dependencies:
+    babel-runtime "^6.26.0"
+    babel-types "^6.26.0"
+    lodash "^4.17.4"
+
+babel-helper-remap-async-to-generator@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz#5ec581827ad723fecdd381f1c928390676e4551b"
+  dependencies:
+    babel-helper-function-name "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
+
+babel-helper-replace-supers@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a"
+  dependencies:
+    babel-helper-optimise-call-expression "^6.24.1"
+    babel-messages "^6.23.0"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
+
+babel-helpers@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2"
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+
+babel-jest@^21.2.0:
+  version "21.2.0"
+  resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-21.2.0.tgz#2ce059519a9374a2c46f2455b6fbef5ad75d863e"
+  dependencies:
+    babel-plugin-istanbul "^4.0.0"
+    babel-preset-jest "^21.2.0"
+
+babel-messages@^6.23.0:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e"
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-check-es2015-constants@^6.22.0:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a"
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-istanbul@^4.0.0:
+  version "4.1.5"
+  resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.5.tgz#6760cdd977f411d3e175bb064f2bc327d99b2b6e"
+  dependencies:
+    find-up "^2.1.0"
+    istanbul-lib-instrument "^1.7.5"
+    test-exclude "^4.1.1"
+
+babel-plugin-jest-hoist@^21.2.0:
+  version "21.2.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-21.2.0.tgz#2cef637259bd4b628a6cace039de5fcd14dbb006"
+
+babel-plugin-syntax-async-functions@^6.8.0:
+  version "6.13.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95"
+
+babel-plugin-syntax-async-generators@^6.5.0:
+  version "6.13.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz#6bc963ebb16eccbae6b92b596eb7f35c342a8b9a"
+
+babel-plugin-syntax-class-properties@^6.8.0:
+  version "6.13.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz#d7eb23b79a317f8543962c505b827c7d6cac27de"
+
+babel-plugin-syntax-decorators@^6.13.0:
+  version "6.13.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz#312563b4dbde3cc806cee3e416cceeaddd11ac0b"
+
+babel-plugin-syntax-dynamic-import@^6.18.0:
+  version "6.18.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz#8d6a26229c83745a9982a441051572caa179b1da"
+
+babel-plugin-syntax-exponentiation-operator@^6.8.0:
+  version "6.13.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de"
+
+babel-plugin-syntax-flow@^6.18.0:
+  version "6.18.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz#4c3ab20a2af26aa20cd25995c398c4eb70310c8d"
+
+babel-plugin-syntax-jsx@^6.3.13, babel-plugin-syntax-jsx@^6.8.0:
+  version "6.18.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946"
+
+babel-plugin-syntax-object-rest-spread@^6.13.0, babel-plugin-syntax-object-rest-spread@^6.8.0:
+  version "6.13.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5"
+
+babel-plugin-syntax-trailing-function-commas@^6.22.0:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3"
+
+babel-plugin-transform-async-generator-functions@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz#f058900145fd3e9907a6ddf28da59f215258a5db"
+  dependencies:
+    babel-helper-remap-async-to-generator "^6.24.1"
+    babel-plugin-syntax-async-generators "^6.5.0"
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-async-to-generator@^6.22.0, babel-plugin-transform-async-to-generator@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz#6536e378aff6cb1d5517ac0e40eb3e9fc8d08761"
+  dependencies:
+    babel-helper-remap-async-to-generator "^6.24.1"
+    babel-plugin-syntax-async-functions "^6.8.0"
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-class-properties@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz#6a79763ea61d33d36f37b611aa9def81a81b46ac"
+  dependencies:
+    babel-helper-function-name "^6.24.1"
+    babel-plugin-syntax-class-properties "^6.8.0"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+
+babel-plugin-transform-decorators@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.24.1.tgz#788013d8f8c6b5222bdf7b344390dfd77569e24d"
+  dependencies:
+    babel-helper-explode-class "^6.24.1"
+    babel-plugin-syntax-decorators "^6.13.0"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+    babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-arrow-functions@^6.22.0:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221"
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-block-scoped-functions@^6.22.0:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz#bbc51b49f964d70cb8d8e0b94e820246ce3a6141"
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-block-scoping@^6.23.0:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz#d70f5299c1308d05c12f463813b0a09e73b1895f"
+  dependencies:
+    babel-runtime "^6.26.0"
+    babel-template "^6.26.0"
+    babel-traverse "^6.26.0"
+    babel-types "^6.26.0"
+    lodash "^4.17.4"
+
+babel-plugin-transform-es2015-classes@^6.23.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db"
+  dependencies:
+    babel-helper-define-map "^6.24.1"
+    babel-helper-function-name "^6.24.1"
+    babel-helper-optimise-call-expression "^6.24.1"
+    babel-helper-replace-supers "^6.24.1"
+    babel-messages "^6.23.0"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-computed-properties@^6.22.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3"
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+
+babel-plugin-transform-es2015-destructuring@^6.23.0:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d"
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-duplicate-keys@^6.22.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e"
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-for-of@^6.23.0:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691"
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-function-name@^6.22.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b"
+  dependencies:
+    babel-helper-function-name "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-literals@^6.22.0:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz#4f54a02d6cd66cf915280019a31d31925377ca2e"
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-modules-amd@^6.22.0, babel-plugin-transform-es2015-modules-amd@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154"
+  dependencies:
+    babel-plugin-transform-es2015-modules-commonjs "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+
+babel-plugin-transform-es2015-modules-commonjs@^6.23.0, babel-plugin-transform-es2015-modules-commonjs@^6.24.1:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz#0d8394029b7dc6abe1a97ef181e00758dd2e5d8a"
+  dependencies:
+    babel-plugin-transform-strict-mode "^6.24.1"
+    babel-runtime "^6.26.0"
+    babel-template "^6.26.0"
+    babel-types "^6.26.0"
+
+babel-plugin-transform-es2015-modules-systemjs@^6.23.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23"
+  dependencies:
+    babel-helper-hoist-variables "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+
+babel-plugin-transform-es2015-modules-umd@^6.23.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468"
+  dependencies:
+    babel-plugin-transform-es2015-modules-amd "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+
+babel-plugin-transform-es2015-object-super@^6.22.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d"
+  dependencies:
+    babel-helper-replace-supers "^6.24.1"
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-parameters@^6.23.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b"
+  dependencies:
+    babel-helper-call-delegate "^6.24.1"
+    babel-helper-get-function-arity "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-shorthand-properties@^6.22.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0"
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-spread@^6.22.0:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz#d6d68a99f89aedc4536c81a542e8dd9f1746f8d1"
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-sticky-regex@^6.22.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc"
+  dependencies:
+    babel-helper-regex "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-template-literals@^6.22.0:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz#a84b3450f7e9f8f1f6839d6d687da84bb1236d8d"
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-typeof-symbol@^6.23.0:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372"
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-unicode-regex@^6.22.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9"
+  dependencies:
+    babel-helper-regex "^6.24.1"
+    babel-runtime "^6.22.0"
+    regexpu-core "^2.0.0"
+
+babel-plugin-transform-exponentiation-operator@^6.22.0, babel-plugin-transform-exponentiation-operator@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz#2ab0c9c7f3098fa48907772bb813fe41e8de3a0e"
+  dependencies:
+    babel-helper-builder-binary-assignment-operator-visitor "^6.24.1"
+    babel-plugin-syntax-exponentiation-operator "^6.8.0"
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-flow-strip-types@^6.22.0:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz#84cb672935d43714fdc32bce84568d87441cf7cf"
+  dependencies:
+    babel-plugin-syntax-flow "^6.18.0"
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-object-rest-spread@^6.22.0:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz#0f36692d50fef6b7e2d4b3ac1478137a963b7b06"
+  dependencies:
+    babel-plugin-syntax-object-rest-spread "^6.8.0"
+    babel-runtime "^6.26.0"
+
+babel-plugin-transform-react-display-name@^6.23.0:
+  version "6.25.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz#67e2bf1f1e9c93ab08db96792e05392bf2cc28d1"
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-react-jsx-self@^6.22.0:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx-self/-/babel-plugin-transform-react-jsx-self-6.22.0.tgz#df6d80a9da2612a121e6ddd7558bcbecf06e636e"
+  dependencies:
+    babel-plugin-syntax-jsx "^6.8.0"
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-react-jsx-source@^6.22.0:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.22.0.tgz#66ac12153f5cd2d17b3c19268f4bf0197f44ecd6"
+  dependencies:
+    babel-plugin-syntax-jsx "^6.8.0"
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-react-jsx@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz#840a028e7df460dfc3a2d29f0c0d91f6376e66a3"
+  dependencies:
+    babel-helper-builder-react-jsx "^6.24.1"
+    babel-plugin-syntax-jsx "^6.8.0"
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-regenerator@^6.22.0:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz#e0703696fbde27f0a3efcacf8b4dca2f7b3a8f2f"
+  dependencies:
+    regenerator-transform "^0.10.0"
+
+babel-plugin-transform-strict-mode@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758"
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+
+babel-polyfill@^6.26.0:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.26.0.tgz#379937abc67d7895970adc621f284cd966cf2153"
+  dependencies:
+    babel-runtime "^6.26.0"
+    core-js "^2.5.0"
+    regenerator-runtime "^0.10.5"
+
+babel-preset-env@^1.6.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.6.0.tgz#2de1c782a780a0a5d605d199c957596da43c44e4"
+  dependencies:
+    babel-plugin-check-es2015-constants "^6.22.0"
+    babel-plugin-syntax-trailing-function-commas "^6.22.0"
+    babel-plugin-transform-async-to-generator "^6.22.0"
+    babel-plugin-transform-es2015-arrow-functions "^6.22.0"
+    babel-plugin-transform-es2015-block-scoped-functions "^6.22.0"
+    babel-plugin-transform-es2015-block-scoping "^6.23.0"
+    babel-plugin-transform-es2015-classes "^6.23.0"
+    babel-plugin-transform-es2015-computed-properties "^6.22.0"
+    babel-plugin-transform-es2015-destructuring "^6.23.0"
+    babel-plugin-transform-es2015-duplicate-keys "^6.22.0"
+    babel-plugin-transform-es2015-for-of "^6.23.0"
+    babel-plugin-transform-es2015-function-name "^6.22.0"
+    babel-plugin-transform-es2015-literals "^6.22.0"
+    babel-plugin-transform-es2015-modules-amd "^6.22.0"
+    babel-plugin-transform-es2015-modules-commonjs "^6.23.0"
+    babel-plugin-transform-es2015-modules-systemjs "^6.23.0"
+    babel-plugin-transform-es2015-modules-umd "^6.23.0"
+    babel-plugin-transform-es2015-object-super "^6.22.0"
+    babel-plugin-transform-es2015-parameters "^6.23.0"
+    babel-plugin-transform-es2015-shorthand-properties "^6.22.0"
+    babel-plugin-transform-es2015-spread "^6.22.0"
+    babel-plugin-transform-es2015-sticky-regex "^6.22.0"
+    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.22.0"
+    babel-plugin-transform-exponentiation-operator "^6.22.0"
+    babel-plugin-transform-regenerator "^6.22.0"
+    browserslist "^2.1.2"
+    invariant "^2.2.2"
+    semver "^5.3.0"
+
+babel-preset-flow@^6.23.0:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-preset-flow/-/babel-preset-flow-6.23.0.tgz#e71218887085ae9a24b5be4169affb599816c49d"
+  dependencies:
+    babel-plugin-transform-flow-strip-types "^6.22.0"
+
+babel-preset-jest@^21.2.0:
+  version "21.2.0"
+  resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-21.2.0.tgz#ff9d2bce08abd98e8a36d9a8a5189b9173b85638"
+  dependencies:
+    babel-plugin-jest-hoist "^21.2.0"
+    babel-plugin-syntax-object-rest-spread "^6.13.0"
+
+babel-preset-react@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-preset-react/-/babel-preset-react-6.24.1.tgz#ba69dfaea45fc3ec639b6a4ecea6e17702c91380"
+  dependencies:
+    babel-plugin-syntax-jsx "^6.3.13"
+    babel-plugin-transform-react-display-name "^6.23.0"
+    babel-plugin-transform-react-jsx "^6.24.1"
+    babel-plugin-transform-react-jsx-self "^6.22.0"
+    babel-plugin-transform-react-jsx-source "^6.22.0"
+    babel-preset-flow "^6.23.0"
+
+babel-preset-stage-2@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-preset-stage-2/-/babel-preset-stage-2-6.24.1.tgz#d9e2960fb3d71187f0e64eec62bc07767219bdc1"
+  dependencies:
+    babel-plugin-syntax-dynamic-import "^6.18.0"
+    babel-plugin-transform-class-properties "^6.24.1"
+    babel-plugin-transform-decorators "^6.24.1"
+    babel-preset-stage-3 "^6.24.1"
+
+babel-preset-stage-3@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz#836ada0a9e7a7fa37cb138fb9326f87934a48395"
+  dependencies:
+    babel-plugin-syntax-trailing-function-commas "^6.22.0"
+    babel-plugin-transform-async-generator-functions "^6.24.1"
+    babel-plugin-transform-async-to-generator "^6.24.1"
+    babel-plugin-transform-exponentiation-operator "^6.24.1"
+    babel-plugin-transform-object-rest-spread "^6.22.0"
+
+babel-register@^6.26.0:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071"
+  dependencies:
+    babel-core "^6.26.0"
+    babel-runtime "^6.26.0"
+    core-js "^2.5.0"
+    home-or-tmp "^2.0.0"
+    lodash "^4.17.4"
+    mkdirp "^0.5.1"
+    source-map-support "^0.4.15"
+
+babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
+  dependencies:
+    core-js "^2.4.0"
+    regenerator-runtime "^0.11.0"
+
+babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.26.0:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02"
+  dependencies:
+    babel-runtime "^6.26.0"
+    babel-traverse "^6.26.0"
+    babel-types "^6.26.0"
+    babylon "^6.18.0"
+    lodash "^4.17.4"
+
+babel-traverse@^6.18.0, babel-traverse@^6.24.1, babel-traverse@^6.26.0:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee"
+  dependencies:
+    babel-code-frame "^6.26.0"
+    babel-messages "^6.23.0"
+    babel-runtime "^6.26.0"
+    babel-types "^6.26.0"
+    babylon "^6.18.0"
+    debug "^2.6.8"
+    globals "^9.18.0"
+    invariant "^2.2.2"
+    lodash "^4.17.4"
+
+babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497"
+  dependencies:
+    babel-runtime "^6.26.0"
+    esutils "^2.0.2"
+    lodash "^4.17.4"
+    to-fast-properties "^1.0.3"
+
+babylon@^6.18.0:
+  version "6.18.0"
+  resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3"
+
+balanced-match@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
+
+bcrypt-pbkdf@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d"
+  dependencies:
+    tweetnacl "^0.14.3"
+
+binary-extensions@^1.0.0:
+  version "1.10.0"
+  resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.10.0.tgz#9aeb9a6c5e88638aad171e167f5900abe24835d0"
+
+block-stream@*:
+  version "0.0.9"
+  resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a"
+  dependencies:
+    inherits "~2.0.0"
+
+boolbase@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
+
+boom@2.x.x:
+  version "2.10.1"
+  resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f"
+  dependencies:
+    hoek "2.x.x"
+
+boom@4.x.x:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31"
+  dependencies:
+    hoek "4.x.x"
+
+boom@5.x.x:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/boom/-/boom-5.2.0.tgz#5dd9da6ee3a5f302077436290cb717d3f4a54e02"
+  dependencies:
+    hoek "4.x.x"
+
+brace-expansion@^1.1.7:
+  version "1.1.8"
+  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292"
+  dependencies:
+    balanced-match "^1.0.0"
+    concat-map "0.0.1"
+
+braces@^1.8.2:
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7"
+  dependencies:
+    expand-range "^1.8.1"
+    preserve "^0.2.0"
+    repeat-element "^1.1.2"
+
+browser-resolve@^1.11.2:
+  version "1.11.2"
+  resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.2.tgz#8ff09b0a2c421718a1051c260b32e48f442938ce"
+  dependencies:
+    resolve "1.1.7"
+
+browserslist@^2.1.2:
+  version "2.5.1"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.5.1.tgz#68e4bc536bbcc6086d62843a2ffccea8396821c6"
+  dependencies:
+    caniuse-lite "^1.0.30000744"
+    electron-to-chromium "^1.3.24"
+
+bser@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719"
+  dependencies:
+    node-int64 "^0.4.0"
+
+builtin-modules@^1.0.0, builtin-modules@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
+
+caller-path@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f"
+  dependencies:
+    callsites "^0.2.0"
+
+callsites@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca"
+
+callsites@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50"
+
+camelcase@^1.0.2:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39"
+
+camelcase@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
+
+caniuse-lite@^1.0.30000744:
+  version "1.0.30000746"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000746.tgz#c64f95a3925cfd30207a308ed76c1ae96ea09ea0"
+
+caseless@~0.12.0:
+  version "0.12.0"
+  resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
+
+center-align@^0.1.1:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad"
+  dependencies:
+    align-text "^0.1.3"
+    lazy-cache "^1.0.3"
+
+"chai@>=1.9.2 <4.0.0":
+  version "3.5.0"
+  resolved "https://registry.yarnpkg.com/chai/-/chai-3.5.0.tgz#4d02637b067fe958bdbfdd3a40ec56fef7373247"
+  dependencies:
+    assertion-error "^1.0.1"
+    deep-eql "^0.1.3"
+    type-detect "^1.0.0"
+
+chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
+  dependencies:
+    ansi-styles "^2.2.1"
+    escape-string-regexp "^1.0.2"
+    has-ansi "^2.0.0"
+    strip-ansi "^3.0.0"
+    supports-color "^2.0.0"
+
+chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.1.0.tgz#ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e"
+  dependencies:
+    ansi-styles "^3.1.0"
+    escape-string-regexp "^1.0.5"
+    supports-color "^4.0.0"
+
+cheerio@^0.22.0:
+  version "0.22.0"
+  resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e"
+  dependencies:
+    css-select "~1.2.0"
+    dom-serializer "~0.1.0"
+    entities "~1.1.1"
+    htmlparser2 "^3.9.1"
+    lodash.assignin "^4.0.9"
+    lodash.bind "^4.1.4"
+    lodash.defaults "^4.0.1"
+    lodash.filter "^4.4.0"
+    lodash.flatten "^4.2.0"
+    lodash.foreach "^4.3.0"
+    lodash.map "^4.4.0"
+    lodash.merge "^4.4.0"
+    lodash.pick "^4.2.1"
+    lodash.reduce "^4.4.0"
+    lodash.reject "^4.4.0"
+    lodash.some "^4.4.0"
+
+chokidar@^1.6.1:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468"
+  dependencies:
+    anymatch "^1.3.0"
+    async-each "^1.0.0"
+    glob-parent "^2.0.0"
+    inherits "^2.0.1"
+    is-binary-path "^1.0.0"
+    is-glob "^2.0.0"
+    path-is-absolute "^1.0.0"
+    readdirp "^2.0.0"
+  optionalDependencies:
+    fsevents "^1.0.0"
+
+ci-info@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.1.1.tgz#47b44df118c48d2597b56d342e7e25791060171a"
+
+circular-json@^0.3.1:
+  version "0.3.3"
+  resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66"
+
+classnames@^2.2.5:
+  version "2.2.5"
+  resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d"
+
+cli-cursor@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987"
+  dependencies:
+    restore-cursor "^1.0.1"
+
+cli-cursor@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
+  dependencies:
+    restore-cursor "^2.0.0"
+
+cli-spinners@^0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-0.1.2.tgz#bb764d88e185fb9e1e6a2a1f19772318f605e31c"
+
+cli-truncate@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574"
+  dependencies:
+    slice-ansi "0.0.4"
+    string-width "^1.0.1"
+
+cli-width@^2.0.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639"
+
+cliui@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1"
+  dependencies:
+    center-align "^0.1.1"
+    right-align "^0.1.1"
+    wordwrap "0.0.2"
+
+cliui@^3.2.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d"
+  dependencies:
+    string-width "^1.0.1"
+    strip-ansi "^3.0.1"
+    wrap-ansi "^2.0.0"
+
+co@^4.6.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
+
+code-point-at@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
+
+color-convert@^1.9.0:
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a"
+  dependencies:
+    color-name "^1.1.1"
+
+color-name@^1.1.1:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+
+combined-stream@^1.0.5, combined-stream@~1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009"
+  dependencies:
+    delayed-stream "~1.0.0"
+
+commander@^2.11.0, commander@^2.9.0:
+  version "2.11.0"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563"
+
+concat-map@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+
+concat-stream@^1.6.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7"
+  dependencies:
+    inherits "^2.0.3"
+    readable-stream "^2.2.2"
+    typedarray "^0.0.6"
+
+config@^1.21.0:
+  version "1.26.2"
+  resolved "https://registry.yarnpkg.com/config/-/config-1.26.2.tgz#2466291168d8afae0aae8ab99ea4d4272f520cae"
+  dependencies:
+    json5 "0.4.0"
+    os-homedir "1.0.2"
+
+console-control-strings@^1.0.0, console-control-strings@~1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
+
+contains-path@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a"
+
+content-type-parser@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/content-type-parser/-/content-type-parser-1.0.1.tgz#c3e56988c53c65127fb46d4032a3a900246fdc94"
+
+convert-source-map@^1.4.0, convert-source-map@^1.5.0:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5"
+
+core-js@^1.0.0:
+  version "1.2.7"
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
+
+core-js@^2.4.0, core-js@^2.5.0:
+  version "2.5.1"
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b"
+
+core-util-is@1.0.2, core-util-is@~1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
+
+cosmiconfig@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-1.1.0.tgz#0dea0f9804efdfb929fbb1b188e25553ea053d37"
+  dependencies:
+    graceful-fs "^4.1.2"
+    js-yaml "^3.4.3"
+    minimist "^1.2.0"
+    object-assign "^4.0.1"
+    os-homedir "^1.0.1"
+    parse-json "^2.2.0"
+    pinkie-promise "^2.0.0"
+    require-from-string "^1.1.0"
+
+create-react-class@^15.6.0:
+  version "15.6.2"
+  resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.2.tgz#cf1ed15f12aad7f14ef5f2dfe05e6c42f91ef02a"
+  dependencies:
+    fbjs "^0.8.9"
+    loose-envify "^1.3.1"
+    object-assign "^4.1.1"
+
+cross-spawn@^5.0.1, cross-spawn@^5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
+  dependencies:
+    lru-cache "^4.0.1"
+    shebang-command "^1.2.0"
+    which "^1.2.9"
+
+cryptiles@2.x.x:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8"
+  dependencies:
+    boom "2.x.x"
+
+cryptiles@3.x.x:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe"
+  dependencies:
+    boom "5.x.x"
+
+css-select@~1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858"
+  dependencies:
+    boolbase "~1.0.0"
+    css-what "2.1"
+    domutils "1.5.1"
+    nth-check "~1.0.1"
+
+css-what@2.1:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd"
+
+cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0":
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.2.tgz#b8036170c79f07a90ff2f16e22284027a243848b"
+
+"cssstyle@>= 0.2.37 < 0.3.0":
+  version "0.2.37"
+  resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-0.2.37.tgz#541097234cb2513c83ceed3acddc27ff27987d54"
+  dependencies:
+    cssom "0.3.x"
+
+dashdash@^1.12.0:
+  version "1.14.1"
+  resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
+  dependencies:
+    assert-plus "^1.0.0"
+
+date-fns@^1.27.2:
+  version "1.29.0"
+  resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.29.0.tgz#12e609cdcb935127311d04d33334e2960a2a54e6"
+
+debug@^2.2.0, debug@^2.6.3, debug@^2.6.8:
+  version "2.6.9"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
+  dependencies:
+    ms "2.0.0"
+
+debug@^3.0.1:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
+  dependencies:
+    ms "2.0.0"
+
+decamelize@^1.0.0, decamelize@^1.1.1:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
+
+deep-diff@^0.3.5:
+  version "0.3.8"
+  resolved "https://registry.yarnpkg.com/deep-diff/-/deep-diff-0.3.8.tgz#c01de63efb0eec9798801d40c7e0dae25b582c84"
+
+deep-eql@^0.1.3:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-0.1.3.tgz#ef558acab8de25206cd713906d74e56930eb69f2"
+  dependencies:
+    type-detect "0.1.1"
+
+deep-equal@^1.0.0, deep-equal@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
+
+deep-extend@~0.4.0:
+  version "0.4.2"
+  resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f"
+
+deep-is@~0.1.3:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
+
+default-require-extensions@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-1.0.0.tgz#f37ea15d3e13ffd9b437d33e1a75b5fb97874cb8"
+  dependencies:
+    strip-bom "^2.0.0"
+
+define-properties@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94"
+  dependencies:
+    foreach "^2.0.5"
+    object-keys "^1.0.8"
+
+del@^2.0.2:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8"
+  dependencies:
+    globby "^5.0.0"
+    is-path-cwd "^1.0.0"
+    is-path-in-cwd "^1.0.0"
+    object-assign "^4.0.1"
+    pify "^2.0.0"
+    pinkie-promise "^2.0.0"
+    rimraf "^2.2.8"
+
+delayed-stream@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+
+delegates@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
+
+detect-indent@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208"
+  dependencies:
+    repeating "^2.0.0"
+
+diff@^3.2.0:
+  version "3.4.0"
+  resolved "https://registry.yarnpkg.com/diff/-/diff-3.4.0.tgz#b1d85507daf3964828de54b37d0d73ba67dda56c"
+
+doctrine@1.5.0:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"
+  dependencies:
+    esutils "^2.0.2"
+    isarray "^1.0.0"
+
+doctrine@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.0.0.tgz#c73d8d2909d22291e1a007a395804da8b665fe63"
+  dependencies:
+    esutils "^2.0.2"
+    isarray "^1.0.0"
+
+dom-helpers@^3.2.0, dom-helpers@^3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.2.1.tgz#3203e07fed217bd1f424b019735582fc37b2825a"
+
+dom-serializer@0, dom-serializer@~0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
+  dependencies:
+    domelementtype "~1.1.1"
+    entities "~1.1.1"
+
+dom-walk@^0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018"
+
+domelementtype@1, domelementtype@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2"
+
+domelementtype@~1.1.1:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b"
+
+domhandler@^2.3.0:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.1.tgz#892e47000a99be55bbf3774ffea0561d8879c259"
+  dependencies:
+    domelementtype "1"
+
+domutils@1.5.1:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
+  dependencies:
+    dom-serializer "0"
+    domelementtype "1"
+
+domutils@^1.5.1:
+  version "1.6.2"
+  resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.6.2.tgz#1958cc0b4c9426e9ed367fb1c8e854891b0fa3ff"
+  dependencies:
+    dom-serializer "0"
+    domelementtype "1"
+
+ecc-jsbn@~0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505"
+  dependencies:
+    jsbn "~0.1.0"
+
+electron-to-chromium@^1.3.24:
+  version "1.3.26"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.26.tgz#996427294861a74d9c7c82b9260ea301e8c02d66"
+
+elegant-spinner@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e"
+
+encoding@^0.1.11:
+  version "0.1.12"
+  resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb"
+  dependencies:
+    iconv-lite "~0.4.13"
+
+entities@^1.1.1, entities@~1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
+
+enzyme@^2.9.1:
+  version "2.9.1"
+  resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-2.9.1.tgz#07d5ce691241240fb817bf2c4b18d6e530240df6"
+  dependencies:
+    cheerio "^0.22.0"
+    function.prototype.name "^1.0.0"
+    is-subset "^0.1.1"
+    lodash "^4.17.4"
+    object-is "^1.0.1"
+    object.assign "^4.0.4"
+    object.entries "^1.0.4"
+    object.values "^1.0.4"
+    prop-types "^15.5.10"
+    uuid "^3.0.1"
+
+errno@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.4.tgz#b896e23a9e5e8ba33871fc996abd3635fc9a1c7d"
+  dependencies:
+    prr "~0.0.0"
+
+error-ex@^1.2.0:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc"
+  dependencies:
+    is-arrayish "^0.2.1"
+
+es-abstract@^1.6.1, es-abstract@^1.7.0:
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.9.0.tgz#690829a07cae36b222e7fd9b75c0d0573eb25227"
+  dependencies:
+    es-to-primitive "^1.1.1"
+    function-bind "^1.1.1"
+    has "^1.0.1"
+    is-callable "^1.1.3"
+    is-regex "^1.0.4"
+
+es-to-primitive@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d"
+  dependencies:
+    is-callable "^1.1.1"
+    is-date-object "^1.0.1"
+    is-symbol "^1.0.1"
+
+es6-error@^4.0.0:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.0.2.tgz#eec5c726eacef51b7f6b73c20db6e1b13b069c98"
+
+escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+
+escodegen@^1.6.1:
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.9.0.tgz#9811a2f265dc1cd3894420ee3717064b632b8852"
+  dependencies:
+    esprima "^3.1.3"
+    estraverse "^4.2.0"
+    esutils "^2.0.2"
+    optionator "^0.8.1"
+  optionalDependencies:
+    source-map "~0.5.6"
+
+eslint-config-prettier@^2.6.0:
+  version "2.6.0"
+  resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-2.6.0.tgz#f21db0ebb438ad678fb98946097c4bb198befccc"
+  dependencies:
+    get-stdin "^5.0.1"
+
+eslint-config-standard@^10.2.1:
+  version "10.2.1"
+  resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-10.2.1.tgz#c061e4d066f379dc17cd562c64e819b4dd454591"
+
+eslint-import-resolver-node@^0.3.1:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.1.tgz#4422574cde66a9a7b099938ee4d508a199e0e3cc"
+  dependencies:
+    debug "^2.6.8"
+    resolve "^1.2.0"
+
+eslint-module-utils@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.1.1.tgz#abaec824177613b8a95b299639e1b6facf473449"
+  dependencies:
+    debug "^2.6.8"
+    pkg-dir "^1.0.0"
+
+eslint-plugin-import@^2.7.0:
+  version "2.7.0"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.7.0.tgz#21de33380b9efb55f5ef6d2e210ec0e07e7fa69f"
+  dependencies:
+    builtin-modules "^1.1.1"
+    contains-path "^0.1.0"
+    debug "^2.6.8"
+    doctrine "1.5.0"
+    eslint-import-resolver-node "^0.3.1"
+    eslint-module-utils "^2.1.1"
+    has "^1.0.1"
+    lodash.cond "^4.3.0"
+    minimatch "^3.0.3"
+    read-pkg-up "^2.0.0"
+
+eslint-plugin-node@^5.2.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-5.2.0.tgz#e1efca04a385516cff3f2f04027ce8c5ae6db749"
+  dependencies:
+    ignore "^3.3.3"
+    minimatch "^3.0.4"
+    resolve "^1.3.3"
+    semver "5.3.0"
+
+eslint-plugin-promise@^3.5.0:
+  version "3.5.0"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-3.5.0.tgz#78fbb6ffe047201627569e85a6c5373af2a68fca"
+
+eslint-plugin-react@^7.4.0:
+  version "7.4.0"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.4.0.tgz#300a95861b9729c087d362dd64abcc351a74364a"
+  dependencies:
+    doctrine "^2.0.0"
+    has "^1.0.1"
+    jsx-ast-utils "^2.0.0"
+    prop-types "^15.5.10"
+
+eslint-plugin-standard@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-3.0.1.tgz#34d0c915b45edc6f010393c7eef3823b08565cf2"
+
+eslint-scope@^3.7.1:
+  version "3.7.1"
+  resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8"
+  dependencies:
+    esrecurse "^4.1.0"
+    estraverse "^4.1.1"
+
+eslint@^4.8.0:
+  version "4.8.0"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.8.0.tgz#229ef0e354e0e61d837c7a80fdfba825e199815e"
+  dependencies:
+    ajv "^5.2.0"
+    babel-code-frame "^6.22.0"
+    chalk "^2.1.0"
+    concat-stream "^1.6.0"
+    cross-spawn "^5.1.0"
+    debug "^3.0.1"
+    doctrine "^2.0.0"
+    eslint-scope "^3.7.1"
+    espree "^3.5.1"
+    esquery "^1.0.0"
+    estraverse "^4.2.0"
+    esutils "^2.0.2"
+    file-entry-cache "^2.0.0"
+    functional-red-black-tree "^1.0.1"
+    glob "^7.1.2"
+    globals "^9.17.0"
+    ignore "^3.3.3"
+    imurmurhash "^0.1.4"
+    inquirer "^3.0.6"
+    is-resolvable "^1.0.0"
+    js-yaml "^3.9.1"
+    json-stable-stringify "^1.0.1"
+    levn "^0.3.0"
+    lodash "^4.17.4"
+    minimatch "^3.0.2"
+    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.3.0"
+    strip-ansi "^4.0.0"
+    strip-json-comments "~2.0.1"
+    table "^4.0.1"
+    text-table "~0.2.0"
+
+espree@^3.5.1:
+  version "3.5.1"
+  resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.1.tgz#0c988b8ab46db53100a1954ae4ba995ddd27d87e"
+  dependencies:
+    acorn "^5.1.1"
+    acorn-jsx "^3.0.0"
+
+esprima@^3.1.3:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
+
+esprima@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804"
+
+esquery@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.0.tgz#cfba8b57d7fba93f17298a8a006a04cda13d80fa"
+  dependencies:
+    estraverse "^4.0.0"
+
+esrecurse@^4.1.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.0.tgz#fa9568d98d3823f9a41d91e902dcab9ea6e5b163"
+  dependencies:
+    estraverse "^4.1.0"
+    object-assign "^4.0.1"
+
+estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13"
+
+esutils@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
+
+event-source-polyfill@^0.0.10:
+  version "0.0.10"
+  resolved "https://registry.yarnpkg.com/event-source-polyfill/-/event-source-polyfill-0.0.10.tgz#e7e81863c333f5a842e3bf0a3b570bedaf042e09"
+
+eventsourcemock@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/eventsourcemock/-/eventsourcemock-1.0.1.tgz#1bcd88f602097c33d4e95f85923bbba7bd847a86"
+
+exec-sh@^0.2.0:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.2.1.tgz#163b98a6e89e6b65b47c2a28d215bc1f63989c38"
+  dependencies:
+    merge "^1.1.3"
+
+execa@^0.7.0:
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"
+  dependencies:
+    cross-spawn "^5.0.1"
+    get-stream "^3.0.0"
+    is-stream "^1.1.0"
+    npm-run-path "^2.0.0"
+    p-finally "^1.0.0"
+    signal-exit "^3.0.0"
+    strip-eof "^1.0.0"
+
+execa@^0.8.0:
+  version "0.8.0"
+  resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da"
+  dependencies:
+    cross-spawn "^5.0.1"
+    get-stream "^3.0.0"
+    is-stream "^1.1.0"
+    npm-run-path "^2.0.0"
+    p-finally "^1.0.0"
+    signal-exit "^3.0.0"
+    strip-eof "^1.0.0"
+
+exit-hook@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8"
+
+expand-brackets@^0.1.4:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b"
+  dependencies:
+    is-posix-bracket "^0.1.0"
+
+expand-range@^1.8.1:
+  version "1.8.2"
+  resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337"
+  dependencies:
+    fill-range "^2.1.0"
+
+expect@^21.2.1:
+  version "21.2.1"
+  resolved "https://registry.yarnpkg.com/expect/-/expect-21.2.1.tgz#003ac2ac7005c3c29e73b38a272d4afadd6d1d7b"
+  dependencies:
+    ansi-styles "^3.2.0"
+    jest-diff "^21.2.1"
+    jest-get-type "^21.2.0"
+    jest-matcher-utils "^21.2.1"
+    jest-message-util "^21.2.1"
+    jest-regex-util "^21.2.0"
+
+extend@~3.0.0, extend@~3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
+
+external-editor@^2.0.4:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.0.5.tgz#52c249a3981b9ba187c7cacf5beb50bf1d91a6bc"
+  dependencies:
+    iconv-lite "^0.4.17"
+    jschardet "^1.4.2"
+    tmp "^0.0.33"
+
+extglob@^0.3.1:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1"
+  dependencies:
+    is-extglob "^1.0.0"
+
+extsprintf@1.3.0, extsprintf@^1.2.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
+
+fast-deep-equal@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff"
+
+fast-levenshtein@~2.0.4:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+
+fb-watchman@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.0.tgz#54e9abf7dfa2f26cd9b1636c588c1afc05de5d58"
+  dependencies:
+    bser "^2.0.0"
+
+fbjs@^0.8.16, fbjs@^0.8.9:
+  version "0.8.16"
+  resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db"
+  dependencies:
+    core-js "^1.0.0"
+    isomorphic-fetch "^2.1.1"
+    loose-envify "^1.0.0"
+    object-assign "^4.1.0"
+    promise "^7.1.1"
+    setimmediate "^1.0.5"
+    ua-parser-js "^0.7.9"
+
+figures@^1.7.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e"
+  dependencies:
+    escape-string-regexp "^1.0.5"
+    object-assign "^4.1.0"
+
+figures@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
+  dependencies:
+    escape-string-regexp "^1.0.5"
+
+file-entry-cache@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361"
+  dependencies:
+    flat-cache "^1.2.1"
+    object-assign "^4.0.1"
+
+filename-regex@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26"
+
+fileset@^2.0.2:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/fileset/-/fileset-2.0.3.tgz#8e7548a96d3cc2327ee5e674168723a333bba2a0"
+  dependencies:
+    glob "^7.0.3"
+    minimatch "^3.0.3"
+
+fill-range@^2.1.0:
+  version "2.2.3"
+  resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723"
+  dependencies:
+    is-number "^2.1.0"
+    isobject "^2.0.0"
+    randomatic "^1.1.3"
+    repeat-element "^1.1.2"
+    repeat-string "^1.5.2"
+
+find-up@^1.0.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
+  dependencies:
+    path-exists "^2.0.0"
+    pinkie-promise "^2.0.0"
+
+find-up@^2.0.0, find-up@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
+  dependencies:
+    locate-path "^2.0.0"
+
+flat-cache@^1.2.1:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.0.tgz#d3030b32b38154f4e3b7e9c709f490f7ef97c481"
+  dependencies:
+    circular-json "^0.3.1"
+    del "^2.0.2"
+    graceful-fs "^4.1.2"
+    write "^0.2.1"
+
+for-in@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
+
+for-own@^0.1.4:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce"
+  dependencies:
+    for-in "^1.0.1"
+
+foreach@^2.0.5:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
+
+forever-agent@~0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
+
+form-data@^1.0.0-rc3:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/form-data/-/form-data-1.0.1.tgz#ae315db9a4907fa065502304a66d7733475ee37c"
+  dependencies:
+    async "^2.0.1"
+    combined-stream "^1.0.5"
+    mime-types "^2.1.11"
+
+form-data@~2.1.1:
+  version "2.1.4"
+  resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1"
+  dependencies:
+    asynckit "^0.4.0"
+    combined-stream "^1.0.5"
+    mime-types "^2.1.12"
+
+form-data@~2.3.1:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.1.tgz#6fb94fbd71885306d73d15cc497fe4cc4ecd44bf"
+  dependencies:
+    asynckit "^0.4.0"
+    combined-stream "^1.0.5"
+    mime-types "^2.1.12"
+
+fs-readdir-recursive@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.0.0.tgz#8cd1745c8b4f8a29c8caec392476921ba195f560"
+
+fs.realpath@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+
+fsevents@^1.0.0, fsevents@^1.1.1:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.2.tgz#3282b713fb3ad80ede0e9fcf4611b5aa6fc033f4"
+  dependencies:
+    nan "^2.3.0"
+    node-pre-gyp "^0.6.36"
+
+fstream-ignore@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105"
+  dependencies:
+    fstream "^1.0.0"
+    inherits "2"
+    minimatch "^3.0.0"
+
+fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2:
+  version "1.0.11"
+  resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171"
+  dependencies:
+    graceful-fs "^4.1.2"
+    inherits "~2.0.0"
+    mkdirp ">=0.5 0"
+    rimraf "2"
+
+function-bind@^1.0.2, function-bind@^1.1.0, function-bind@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
+
+function.prototype.name@^1.0.0:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.0.3.tgz#0099ae5572e9dd6f03c97d023fd92bcc5e639eac"
+  dependencies:
+    define-properties "^1.1.2"
+    function-bind "^1.1.0"
+    is-callable "^1.1.3"
+
+functional-red-black-tree@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
+
+gauge@~2.7.3:
+  version "2.7.4"
+  resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
+  dependencies:
+    aproba "^1.0.3"
+    console-control-strings "^1.0.0"
+    has-unicode "^2.0.0"
+    object-assign "^4.1.0"
+    signal-exit "^3.0.0"
+    string-width "^1.0.1"
+    strip-ansi "^3.0.1"
+    wide-align "^1.1.0"
+
+get-caller-file@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5"
+
+get-own-enumerable-property-symbols@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-2.0.1.tgz#5c4ad87f2834c4b9b4e84549dc1e0650fb38c24b"
+
+get-stdin@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398"
+
+get-stream@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
+
+getpass@^0.1.1:
+  version "0.1.7"
+  resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
+  dependencies:
+    assert-plus "^1.0.0"
+
+glob-base@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4"
+  dependencies:
+    glob-parent "^2.0.0"
+    is-glob "^2.0.0"
+
+glob-parent@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28"
+  dependencies:
+    is-glob "^2.0.0"
+
+glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2:
+  version "7.1.2"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
+  dependencies:
+    fs.realpath "^1.0.0"
+    inflight "^1.0.4"
+    inherits "2"
+    minimatch "^3.0.4"
+    once "^1.3.0"
+    path-is-absolute "^1.0.0"
+
+global@^4.3.1:
+  version "4.3.2"
+  resolved "https://registry.yarnpkg.com/global/-/global-4.3.2.tgz#e76989268a6c74c38908b1305b10fc0e394e9d0f"
+  dependencies:
+    min-document "^2.19.0"
+    process "~0.5.1"
+
+globals@^9.17.0, globals@^9.18.0:
+  version "9.18.0"
+  resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
+
+globby@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d"
+  dependencies:
+    array-union "^1.0.1"
+    arrify "^1.0.0"
+    glob "^7.0.3"
+    object-assign "^4.0.1"
+    pify "^2.0.0"
+    pinkie-promise "^2.0.0"
+
+graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.4:
+  version "4.1.11"
+  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
+
+growly@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
+
+handlebars@^4.0.3:
+  version "4.0.10"
+  resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.10.tgz#3d30c718b09a3d96f23ea4cc1f403c4d3ba9ff4f"
+  dependencies:
+    async "^1.4.0"
+    optimist "^0.6.1"
+    source-map "^0.4.4"
+  optionalDependencies:
+    uglify-js "^2.6"
+
+har-schema@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e"
+
+har-schema@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
+
+har-validator@~4.2.1:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a"
+  dependencies:
+    ajv "^4.9.1"
+    har-schema "^1.0.5"
+
+har-validator@~5.0.3:
+  version "5.0.3"
+  resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd"
+  dependencies:
+    ajv "^5.1.0"
+    har-schema "^2.0.0"
+
+has-ansi@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
+  dependencies:
+    ansi-regex "^2.0.0"
+
+has-flag@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
+
+has-flag@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51"
+
+has-unicode@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
+
+has@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28"
+  dependencies:
+    function-bind "^1.0.2"
+
+hawk@3.1.3, hawk@~3.1.3:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4"
+  dependencies:
+    boom "2.x.x"
+    cryptiles "2.x.x"
+    hoek "2.x.x"
+    sntp "1.x.x"
+
+hawk@~6.0.2:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/hawk/-/hawk-6.0.2.tgz#af4d914eb065f9b5ce4d9d11c1cb2126eecc3038"
+  dependencies:
+    boom "4.x.x"
+    cryptiles "3.x.x"
+    hoek "4.x.x"
+    sntp "2.x.x"
+
+history@^4.5.1, history@^4.7.2:
+  version "4.7.2"
+  resolved "https://registry.yarnpkg.com/history/-/history-4.7.2.tgz#22b5c7f31633c5b8021c7f4a8a954ac139ee8d5b"
+  dependencies:
+    invariant "^2.2.1"
+    loose-envify "^1.2.0"
+    resolve-pathname "^2.2.0"
+    value-equal "^0.4.0"
+    warning "^3.0.0"
+
+hoek@2.x.x:
+  version "2.16.3"
+  resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed"
+
+hoek@4.x.x:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d"
+
+hoist-non-react-statics@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb"
+
+hoist-non-react-statics@^2.2.1, hoist-non-react-statics@^2.3.0, hoist-non-react-statics@^2.3.1:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz#343db84c6018c650778898240135a1420ee22ce0"
+
+home-or-tmp@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"
+  dependencies:
+    os-homedir "^1.0.0"
+    os-tmpdir "^1.0.1"
+
+hosted-git-info@^2.1.4:
+  version "2.5.0"
+  resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c"
+
+html-encoding-sniffer@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.1.tgz#79bf7a785ea495fe66165e734153f363ff5437da"
+  dependencies:
+    whatwg-encoding "^1.0.1"
+
+htmlparser2@^3.9.1:
+  version "3.9.2"
+  resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338"
+  dependencies:
+    domelementtype "^1.3.0"
+    domhandler "^2.3.0"
+    domutils "^1.5.1"
+    entities "^1.1.1"
+    inherits "^2.0.1"
+    readable-stream "^2.0.2"
+
+http-signature@~1.1.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf"
+  dependencies:
+    assert-plus "^0.2.0"
+    jsprim "^1.2.2"
+    sshpk "^1.7.0"
+
+http-signature@~1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
+  dependencies:
+    assert-plus "^1.0.0"
+    jsprim "^1.2.2"
+    sshpk "^1.7.0"
+
+husky@^0.14.3:
+  version "0.14.3"
+  resolved "https://registry.yarnpkg.com/husky/-/husky-0.14.3.tgz#c69ed74e2d2779769a17ba8399b54ce0b63c12c3"
+  dependencies:
+    is-ci "^1.0.10"
+    normalize-path "^1.0.0"
+    strip-indent "^2.0.0"
+
+iconv-lite@0.4.13:
+  version "0.4.13"
+  resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2"
+
+iconv-lite@^0.4.17, iconv-lite@~0.4.13:
+  version "0.4.19"
+  resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
+
+ignore@^3.3.3:
+  version "3.3.5"
+  resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.5.tgz#c4e715455f6073a8d7e5dae72d2fc9d71663dba6"
+
+imurmurhash@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+
+indent-string@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80"
+  dependencies:
+    repeating "^2.0.0"
+
+indent-string@^3.0.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289"
+
+inflight@^1.0.4:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+  dependencies:
+    once "^1.3.0"
+    wrappy "1"
+
+inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
+
+ini@~1.3.0:
+  version "1.3.4"
+  resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e"
+
+inquirer@^3.0.6:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9"
+  dependencies:
+    ansi-escapes "^3.0.0"
+    chalk "^2.0.0"
+    cli-cursor "^2.1.0"
+    cli-width "^2.0.0"
+    external-editor "^2.0.4"
+    figures "^2.0.0"
+    lodash "^4.3.0"
+    mute-stream "0.0.7"
+    run-async "^2.2.0"
+    rx-lite "^4.0.8"
+    rx-lite-aggregates "^4.0.8"
+    string-width "^2.1.0"
+    strip-ansi "^4.0.0"
+    through "^2.3.6"
+
+invariant@^2.0.0, invariant@^2.1.0, invariant@^2.2.1, invariant@^2.2.2:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360"
+  dependencies:
+    loose-envify "^1.0.0"
+
+invert-kv@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
+
+is-arrayish@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+
+is-binary-path@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898"
+  dependencies:
+    binary-extensions "^1.0.0"
+
+is-buffer@^1.1.5:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc"
+
+is-builtin-module@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe"
+  dependencies:
+    builtin-modules "^1.0.0"
+
+is-callable@^1.1.1, is-callable@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2"
+
+is-ci@^1.0.10:
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.0.10.tgz#f739336b2632365061a9d48270cd56ae3369318e"
+  dependencies:
+    ci-info "^1.0.0"
+
+is-date-object@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16"
+
+is-dotfile@^1.0.0:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1"
+
+is-equal-shallow@^0.1.3:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534"
+  dependencies:
+    is-primitive "^2.0.0"
+
+is-extendable@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
+
+is-extglob@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0"
+
+is-extglob@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+
+is-finite@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa"
+  dependencies:
+    number-is-nan "^1.0.0"
+
+is-fullwidth-code-point@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
+  dependencies:
+    number-is-nan "^1.0.0"
+
+is-fullwidth-code-point@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
+
+is-glob@^2.0.0, is-glob@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863"
+  dependencies:
+    is-extglob "^1.0.0"
+
+is-glob@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0"
+  dependencies:
+    is-extglob "^2.1.1"
+
+is-number@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f"
+  dependencies:
+    kind-of "^3.0.2"
+
+is-number@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
+  dependencies:
+    kind-of "^3.0.2"
+
+is-obj@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
+
+is-path-cwd@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d"
+
+is-path-in-cwd@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc"
+  dependencies:
+    is-path-inside "^1.0.0"
+
+is-path-inside@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.0.tgz#fc06e5a1683fbda13de667aff717bbc10a48f37f"
+  dependencies:
+    path-is-inside "^1.0.1"
+
+is-posix-bracket@^0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4"
+
+is-primitive@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575"
+
+is-promise@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
+
+is-regex@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491"
+  dependencies:
+    has "^1.0.1"
+
+is-regexp@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069"
+
+is-resolvable@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62"
+  dependencies:
+    tryit "^1.0.1"
+
+is-stream@^1.0.1, is-stream@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
+
+is-subset@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6"
+
+is-symbol@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572"
+
+is-typedarray@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
+
+is-utf8@^0.2.0:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
+
+isarray@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
+
+isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+
+isexe@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+
+isobject@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
+  dependencies:
+    isarray "1.0.0"
+
+isomorphic-fetch@^2.1.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
+  dependencies:
+    node-fetch "^1.0.1"
+    whatwg-fetch ">=0.10.0"
+
+isomorphic-form-data@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/isomorphic-form-data/-/isomorphic-form-data-1.0.0.tgz#0447be7e0f6b86def3d393036cf7664a98919100"
+  dependencies:
+    form-data "^1.0.0-rc3"
+
+isstream@~0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
+
+istanbul-api@^1.1.1:
+  version "1.1.14"
+  resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-1.1.14.tgz#25bc5701f7c680c0ffff913de46e3619a3a6e680"
+  dependencies:
+    async "^2.1.4"
+    fileset "^2.0.2"
+    istanbul-lib-coverage "^1.1.1"
+    istanbul-lib-hook "^1.0.7"
+    istanbul-lib-instrument "^1.8.0"
+    istanbul-lib-report "^1.1.1"
+    istanbul-lib-source-maps "^1.2.1"
+    istanbul-reports "^1.1.2"
+    js-yaml "^3.7.0"
+    mkdirp "^0.5.1"
+    once "^1.4.0"
+
+istanbul-lib-coverage@^1.0.1, istanbul-lib-coverage@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz#73bfb998885299415c93d38a3e9adf784a77a9da"
+
+istanbul-lib-hook@^1.0.7:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.0.7.tgz#dd6607f03076578fe7d6f2a630cf143b49bacddc"
+  dependencies:
+    append-transform "^0.4.0"
+
+istanbul-lib-instrument@^1.4.2, istanbul-lib-instrument@^1.7.5, istanbul-lib-instrument@^1.8.0:
+  version "1.8.0"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.8.0.tgz#66f6c9421cc9ec4704f76f2db084ba9078a2b532"
+  dependencies:
+    babel-generator "^6.18.0"
+    babel-template "^6.16.0"
+    babel-traverse "^6.18.0"
+    babel-types "^6.18.0"
+    babylon "^6.18.0"
+    istanbul-lib-coverage "^1.1.1"
+    semver "^5.3.0"
+
+istanbul-lib-report@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz#f0e55f56655ffa34222080b7a0cd4760e1405fc9"
+  dependencies:
+    istanbul-lib-coverage "^1.1.1"
+    mkdirp "^0.5.1"
+    path-parse "^1.0.5"
+    supports-color "^3.1.2"
+
+istanbul-lib-source-maps@^1.1.0, istanbul-lib-source-maps@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.1.tgz#a6fe1acba8ce08eebc638e572e294d267008aa0c"
+  dependencies:
+    debug "^2.6.3"
+    istanbul-lib-coverage "^1.1.1"
+    mkdirp "^0.5.1"
+    rimraf "^2.6.1"
+    source-map "^0.5.3"
+
+istanbul-reports@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.1.2.tgz#0fb2e3f6aa9922bd3ce45d05d8ab4d5e8e07bd4f"
+  dependencies:
+    handlebars "^4.0.3"
+
+jest-changed-files@^21.2.0:
+  version "21.2.0"
+  resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-21.2.0.tgz#5dbeecad42f5d88b482334902ce1cba6d9798d29"
+  dependencies:
+    throat "^4.0.0"
+
+jest-cli@^21.2.1:
+  version "21.2.1"
+  resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-21.2.1.tgz#9c528b6629d651911138d228bdb033c157ec8c00"
+  dependencies:
+    ansi-escapes "^3.0.0"
+    chalk "^2.0.1"
+    glob "^7.1.2"
+    graceful-fs "^4.1.11"
+    is-ci "^1.0.10"
+    istanbul-api "^1.1.1"
+    istanbul-lib-coverage "^1.0.1"
+    istanbul-lib-instrument "^1.4.2"
+    istanbul-lib-source-maps "^1.1.0"
+    jest-changed-files "^21.2.0"
+    jest-config "^21.2.1"
+    jest-environment-jsdom "^21.2.1"
+    jest-haste-map "^21.2.0"
+    jest-message-util "^21.2.1"
+    jest-regex-util "^21.2.0"
+    jest-resolve-dependencies "^21.2.0"
+    jest-runner "^21.2.1"
+    jest-runtime "^21.2.1"
+    jest-snapshot "^21.2.1"
+    jest-util "^21.2.1"
+    micromatch "^2.3.11"
+    node-notifier "^5.0.2"
+    pify "^3.0.0"
+    slash "^1.0.0"
+    string-length "^2.0.0"
+    strip-ansi "^4.0.0"
+    which "^1.2.12"
+    worker-farm "^1.3.1"
+    yargs "^9.0.0"
+
+jest-config@^21.2.1:
+  version "21.2.1"
+  resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-21.2.1.tgz#c7586c79ead0bcc1f38c401e55f964f13bf2a480"
+  dependencies:
+    chalk "^2.0.1"
+    glob "^7.1.1"
+    jest-environment-jsdom "^21.2.1"
+    jest-environment-node "^21.2.1"
+    jest-get-type "^21.2.0"
+    jest-jasmine2 "^21.2.1"
+    jest-regex-util "^21.2.0"
+    jest-resolve "^21.2.0"
+    jest-util "^21.2.1"
+    jest-validate "^21.2.1"
+    pretty-format "^21.2.1"
+
+jest-diff@^21.2.1:
+  version "21.2.1"
+  resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-21.2.1.tgz#46cccb6cab2d02ce98bc314011764bb95b065b4f"
+  dependencies:
+    chalk "^2.0.1"
+    diff "^3.2.0"
+    jest-get-type "^21.2.0"
+    pretty-format "^21.2.1"
+
+jest-docblock@^21.2.0:
+  version "21.2.0"
+  resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-21.2.0.tgz#51529c3b30d5fd159da60c27ceedc195faf8d414"
+
+jest-environment-jsdom@^21.2.1:
+  version "21.2.1"
+  resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-21.2.1.tgz#38d9980c8259b2a608ec232deee6289a60d9d5b4"
+  dependencies:
+    jest-mock "^21.2.0"
+    jest-util "^21.2.1"
+    jsdom "^9.12.0"
+
+jest-environment-node@^21.2.1:
+  version "21.2.1"
+  resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-21.2.1.tgz#98c67df5663c7fbe20f6e792ac2272c740d3b8c8"
+  dependencies:
+    jest-mock "^21.2.0"
+    jest-util "^21.2.1"
+
+jest-get-type@^21.2.0:
+  version "21.2.0"
+  resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-21.2.0.tgz#f6376ab9db4b60d81e39f30749c6c466f40d4a23"
+
+jest-haste-map@^21.2.0:
+  version "21.2.0"
+  resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-21.2.0.tgz#1363f0a8bb4338f24f001806571eff7a4b2ff3d8"
+  dependencies:
+    fb-watchman "^2.0.0"
+    graceful-fs "^4.1.11"
+    jest-docblock "^21.2.0"
+    micromatch "^2.3.11"
+    sane "^2.0.0"
+    worker-farm "^1.3.1"
+
+jest-jasmine2@^21.2.1:
+  version "21.2.1"
+  resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-21.2.1.tgz#9cc6fc108accfa97efebce10c4308548a4ea7592"
+  dependencies:
+    chalk "^2.0.1"
+    expect "^21.2.1"
+    graceful-fs "^4.1.11"
+    jest-diff "^21.2.1"
+    jest-matcher-utils "^21.2.1"
+    jest-message-util "^21.2.1"
+    jest-snapshot "^21.2.1"
+    p-cancelable "^0.3.0"
+
+jest-matcher-utils@^21.2.1:
+  version "21.2.1"
+  resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-21.2.1.tgz#72c826eaba41a093ac2b4565f865eb8475de0f64"
+  dependencies:
+    chalk "^2.0.1"
+    jest-get-type "^21.2.0"
+    pretty-format "^21.2.1"
+
+jest-message-util@^21.2.1:
+  version "21.2.1"
+  resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-21.2.1.tgz#bfe5d4692c84c827d1dcf41823795558f0a1acbe"
+  dependencies:
+    chalk "^2.0.1"
+    micromatch "^2.3.11"
+    slash "^1.0.0"
+
+jest-mock@^21.2.0:
+  version "21.2.0"
+  resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-21.2.0.tgz#7eb0770e7317968165f61ea2a7281131534b3c0f"
+
+jest-regex-util@^21.2.0:
+  version "21.2.0"
+  resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-21.2.0.tgz#1b1e33e63143babc3e0f2e6c9b5ba1eb34b2d530"
+
+jest-resolve-dependencies@^21.2.0:
+  version "21.2.0"
+  resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-21.2.0.tgz#9e231e371e1a736a1ad4e4b9a843bc72bfe03d09"
+  dependencies:
+    jest-regex-util "^21.2.0"
+
+jest-resolve@^21.2.0:
+  version "21.2.0"
+  resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-21.2.0.tgz#068913ad2ba6a20218e5fd32471f3874005de3a6"
+  dependencies:
+    browser-resolve "^1.11.2"
+    chalk "^2.0.1"
+    is-builtin-module "^1.0.0"
+
+jest-runner@^21.2.1:
+  version "21.2.1"
+  resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-21.2.1.tgz#194732e3e518bfb3d7cbfc0fd5871246c7e1a467"
+  dependencies:
+    jest-config "^21.2.1"
+    jest-docblock "^21.2.0"
+    jest-haste-map "^21.2.0"
+    jest-jasmine2 "^21.2.1"
+    jest-message-util "^21.2.1"
+    jest-runtime "^21.2.1"
+    jest-util "^21.2.1"
+    pify "^3.0.0"
+    throat "^4.0.0"
+    worker-farm "^1.3.1"
+
+jest-runtime@^21.2.1:
+  version "21.2.1"
+  resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-21.2.1.tgz#99dce15309c670442eee2ebe1ff53a3cbdbbb73e"
+  dependencies:
+    babel-core "^6.0.0"
+    babel-jest "^21.2.0"
+    babel-plugin-istanbul "^4.0.0"
+    chalk "^2.0.1"
+    convert-source-map "^1.4.0"
+    graceful-fs "^4.1.11"
+    jest-config "^21.2.1"
+    jest-haste-map "^21.2.0"
+    jest-regex-util "^21.2.0"
+    jest-resolve "^21.2.0"
+    jest-util "^21.2.1"
+    json-stable-stringify "^1.0.1"
+    micromatch "^2.3.11"
+    slash "^1.0.0"
+    strip-bom "3.0.0"
+    write-file-atomic "^2.1.0"
+    yargs "^9.0.0"
+
+jest-snapshot@^21.2.1:
+  version "21.2.1"
+  resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-21.2.1.tgz#29e49f16202416e47343e757e5eff948c07fd7b0"
+  dependencies:
+    chalk "^2.0.1"
+    jest-diff "^21.2.1"
+    jest-matcher-utils "^21.2.1"
+    mkdirp "^0.5.1"
+    natural-compare "^1.4.0"
+    pretty-format "^21.2.1"
+
+jest-util@^21.2.1:
+  version "21.2.1"
+  resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-21.2.1.tgz#a274b2f726b0897494d694a6c3d6a61ab819bb78"
+  dependencies:
+    callsites "^2.0.0"
+    chalk "^2.0.1"
+    graceful-fs "^4.1.11"
+    jest-message-util "^21.2.1"
+    jest-mock "^21.2.0"
+    jest-validate "^21.2.1"
+    mkdirp "^0.5.1"
+
+jest-validate@^21.1.0, jest-validate@^21.2.1:
+  version "21.2.1"
+  resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-21.2.1.tgz#cc0cbca653cd54937ba4f2a111796774530dd3c7"
+  dependencies:
+    chalk "^2.0.1"
+    jest-get-type "^21.2.0"
+    leven "^2.1.0"
+    pretty-format "^21.2.1"
+
+jest@^21.2.1:
+  version "21.2.1"
+  resolved "https://registry.yarnpkg.com/jest/-/jest-21.2.1.tgz#c964e0b47383768a1438e3ccf3c3d470327604e1"
+  dependencies:
+    jest-cli "^21.2.1"
+
+js-tokens@^3.0.0, js-tokens@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
+
+js-yaml@^3.4.3, js-yaml@^3.7.0, js-yaml@^3.9.1:
+  version "3.10.0"
+  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.10.0.tgz#2e78441646bd4682e963f22b6e92823c309c62dc"
+  dependencies:
+    argparse "^1.0.7"
+    esprima "^4.0.0"
+
+jsbn@~0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
+
+jschardet@^1.4.2:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-1.5.1.tgz#c519f629f86b3a5bedba58a88d311309eec097f9"
+
+jsdom@^9.12.0:
+  version "9.12.0"
+  resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-9.12.0.tgz#e8c546fffcb06c00d4833ca84410fed7f8a097d4"
+  dependencies:
+    abab "^1.0.3"
+    acorn "^4.0.4"
+    acorn-globals "^3.1.0"
+    array-equal "^1.0.0"
+    content-type-parser "^1.0.1"
+    cssom ">= 0.3.2 < 0.4.0"
+    cssstyle ">= 0.2.37 < 0.3.0"
+    escodegen "^1.6.1"
+    html-encoding-sniffer "^1.0.1"
+    nwmatcher ">= 1.3.9 < 2.0.0"
+    parse5 "^1.5.1"
+    request "^2.79.0"
+    sax "^1.2.1"
+    symbol-tree "^3.2.1"
+    tough-cookie "^2.3.2"
+    webidl-conversions "^4.0.0"
+    whatwg-encoding "^1.0.1"
+    whatwg-url "^4.3.0"
+    xml-name-validator "^2.0.1"
+
+jsesc@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
+
+jsesc@~0.5.0:
+  version "0.5.0"
+  resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
+
+json-schema-traverse@^0.3.0:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340"
+
+json-schema@0.2.3:
+  version "0.2.3"
+  resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
+
+json-stable-stringify@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af"
+  dependencies:
+    jsonify "~0.0.0"
+
+json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
+
+json5@0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-0.4.0.tgz#054352e4c4c80c86c0923877d449de176a732c8d"
+
+json5@^0.5.1:
+  version "0.5.1"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
+
+jsonify@~0.0.0:
+  version "0.0.0"
+  resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
+
+jsprim@^1.2.2:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
+  dependencies:
+    assert-plus "1.0.0"
+    extsprintf "1.3.0"
+    json-schema "0.2.3"
+    verror "1.10.0"
+
+jsx-ast-utils@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz#e801b1b39985e20fffc87b40e3748080e2dcac7f"
+  dependencies:
+    array-includes "^3.0.3"
+
+keycode@^2.1.2:
+  version "2.1.9"
+  resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.1.9.tgz#964a23c54e4889405b4861a5c9f0480d45141dfa"
+
+kind-of@^3.0.2:
+  version "3.2.2"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
+  dependencies:
+    is-buffer "^1.1.5"
+
+kind-of@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57"
+  dependencies:
+    is-buffer "^1.1.5"
+
+lazy-cache@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e"
+
+lcid@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835"
+  dependencies:
+    invert-kv "^1.0.0"
+
+leven@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580"
+
+levn@^0.3.0, levn@~0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
+  dependencies:
+    prelude-ls "~1.1.2"
+    type-check "~0.3.2"
+
+lint-staged@^4.2.3:
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-4.2.3.tgz#5a1f12256af06110b96225f109dbf215009a37a9"
+  dependencies:
+    app-root-path "^2.0.0"
+    chalk "^2.1.0"
+    cosmiconfig "^1.1.0"
+    execa "^0.8.0"
+    is-glob "^4.0.0"
+    jest-validate "^21.1.0"
+    listr "^0.12.0"
+    lodash "^4.17.4"
+    log-symbols "^2.0.0"
+    minimatch "^3.0.0"
+    npm-which "^3.0.1"
+    p-map "^1.1.1"
+    staged-git-files "0.0.4"
+    stringify-object "^3.2.0"
+
+listr-silent-renderer@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e"
+
+listr-update-renderer@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.2.0.tgz#ca80e1779b4e70266807e8eed1ad6abe398550f9"
+  dependencies:
+    chalk "^1.1.3"
+    cli-truncate "^0.2.1"
+    elegant-spinner "^1.0.1"
+    figures "^1.7.0"
+    indent-string "^3.0.0"
+    log-symbols "^1.0.2"
+    log-update "^1.0.2"
+    strip-ansi "^3.0.1"
+
+listr-verbose-renderer@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.4.0.tgz#44dc01bb0c34a03c572154d4d08cde9b1dc5620f"
+  dependencies:
+    chalk "^1.1.3"
+    cli-cursor "^1.0.2"
+    date-fns "^1.27.2"
+    figures "^1.7.0"
+
+listr@^0.12.0:
+  version "0.12.0"
+  resolved "https://registry.yarnpkg.com/listr/-/listr-0.12.0.tgz#6bce2c0f5603fa49580ea17cd6a00cc0e5fa451a"
+  dependencies:
+    chalk "^1.1.3"
+    cli-truncate "^0.2.1"
+    figures "^1.7.0"
+    indent-string "^2.1.0"
+    is-promise "^2.1.0"
+    is-stream "^1.1.0"
+    listr-silent-renderer "^1.1.1"
+    listr-update-renderer "^0.2.0"
+    listr-verbose-renderer "^0.4.0"
+    log-symbols "^1.0.2"
+    log-update "^1.0.2"
+    ora "^0.2.3"
+    p-map "^1.1.1"
+    rxjs "^5.0.0-beta.11"
+    stream-to-observable "^0.1.0"
+    strip-ansi "^3.0.1"
+
+load-json-file@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
+  dependencies:
+    graceful-fs "^4.1.2"
+    parse-json "^2.2.0"
+    pify "^2.0.0"
+    pinkie-promise "^2.0.0"
+    strip-bom "^2.0.0"
+
+load-json-file@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8"
+  dependencies:
+    graceful-fs "^4.1.2"
+    parse-json "^2.2.0"
+    pify "^2.0.0"
+    strip-bom "^3.0.0"
+
+locate-path@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
+  dependencies:
+    p-locate "^2.0.0"
+    path-exists "^3.0.0"
+
+lodash-es@^4.17.3, lodash-es@^4.2.0, lodash-es@^4.2.1:
+  version "4.17.4"
+  resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.4.tgz#dcc1d7552e150a0640073ba9cb31d70f032950e7"
+
+lodash.assignin@^4.0.9:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2"
+
+lodash.bind@^4.1.4:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/lodash.bind/-/lodash.bind-4.2.1.tgz#7ae3017e939622ac31b7d7d7dcb1b34db1690d35"
+
+lodash.cond@^4.3.0:
+  version "4.5.2"
+  resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5"
+
+lodash.defaults@^4.0.1:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
+
+lodash.filter@^4.4.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace"
+
+lodash.flatten@^4.2.0:
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
+
+lodash.foreach@^4.3.0:
+  version "4.5.0"
+  resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53"
+
+lodash.map@^4.4.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3"
+
+lodash.merge@^4.4.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.0.tgz#69884ba144ac33fe699737a6086deffadd0f89c5"
+
+lodash.pick@^4.2.1:
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3"
+
+lodash.reduce@^4.4.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b"
+
+lodash.reject@^4.4.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/lodash.reject/-/lodash.reject-4.6.0.tgz#80d6492dc1470864bbf583533b651f42a9f52415"
+
+lodash.some@^4.4.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d"
+
+lodash@^4.0.0, lodash@^4.14.0, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@~4.17.2:
+  version "4.17.4"
+  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
+
+log-symbols@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18"
+  dependencies:
+    chalk "^1.0.0"
+
+log-symbols@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.1.0.tgz#f35fa60e278832b538dc4dddcbb478a45d3e3be6"
+  dependencies:
+    chalk "^2.0.1"
+
+log-update@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/log-update/-/log-update-1.0.2.tgz#19929f64c4093d2d2e7075a1dad8af59c296b8d1"
+  dependencies:
+    ansi-escapes "^1.0.0"
+    cli-cursor "^1.0.2"
+
+longest@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
+
+loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
+  dependencies:
+    js-tokens "^3.0.0"
+
+lru-cache@^4.0.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.1.tgz#622e32e82488b49279114a4f9ecf45e7cd6bba55"
+  dependencies:
+    pseudomap "^1.0.2"
+    yallist "^2.1.2"
+
+makeerror@1.0.x:
+  version "1.0.11"
+  resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c"
+  dependencies:
+    tmpl "1.0.x"
+
+mem@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76"
+  dependencies:
+    mimic-fn "^1.0.0"
+
+merge@^1.1.3:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.0.tgz#7531e39d4949c281a66b8c5a6e0265e8b05894da"
+
+micromatch@^2.1.5, micromatch@^2.3.11:
+  version "2.3.11"
+  resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565"
+  dependencies:
+    arr-diff "^2.0.0"
+    array-unique "^0.2.1"
+    braces "^1.8.2"
+    expand-brackets "^0.1.4"
+    extglob "^0.3.1"
+    filename-regex "^2.0.0"
+    is-extglob "^1.0.0"
+    is-glob "^2.0.1"
+    kind-of "^3.0.2"
+    normalize-path "^2.0.1"
+    object.omit "^2.0.0"
+    parse-glob "^3.0.4"
+    regex-cache "^0.4.2"
+
+mime-db@~1.30.0:
+  version "1.30.0"
+  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01"
+
+mime-types@^2.1.11, mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.7:
+  version "2.1.17"
+  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a"
+  dependencies:
+    mime-db "~1.30.0"
+
+mimic-fn@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18"
+
+min-document@^2.19.0:
+  version "2.19.0"
+  resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685"
+  dependencies:
+    dom-walk "^0.1.0"
+
+minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
+  dependencies:
+    brace-expansion "^1.1.7"
+
+minimist@0.0.8:
+  version "0.0.8"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
+
+minimist@^1.1.1, minimist@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
+
+minimist@~0.0.1:
+  version "0.0.10"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
+
+"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1:
+  version "0.5.1"
+  resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
+  dependencies:
+    minimist "0.0.8"
+
+ms@2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+
+mute-stream@0.0.7:
+  version "0.0.7"
+  resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
+
+nan@^2.3.0:
+  version "2.7.0"
+  resolved "https://registry.yarnpkg.com/nan/-/nan-2.7.0.tgz#d95bf721ec877e08db276ed3fc6eb78f9083ad46"
+
+natural-compare@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
+
+nock@^9.0.14:
+  version "9.0.22"
+  resolved "https://registry.yarnpkg.com/nock/-/nock-9.0.22.tgz#f6eb8ea58c6232dead857484370c2e46f010a087"
+  dependencies:
+    chai ">=1.9.2 <4.0.0"
+    debug "^2.2.0"
+    deep-equal "^1.0.0"
+    json-stringify-safe "^5.0.1"
+    lodash "~4.17.2"
+    mkdirp "^0.5.0"
+    propagate "0.4.0"
+    qs "^6.0.2"
+    semver "^5.3.0"
+
+node-fetch@^1.0.1:
+  version "1.7.3"
+  resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
+  dependencies:
+    encoding "^0.1.11"
+    is-stream "^1.0.1"
+
+node-int64@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
+
+node-notifier@^5.0.2:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.1.2.tgz#2fa9e12605fa10009d44549d6fcd8a63dde0e4ff"
+  dependencies:
+    growly "^1.3.0"
+    semver "^5.3.0"
+    shellwords "^0.1.0"
+    which "^1.2.12"
+
+node-pre-gyp@^0.6.36:
+  version "0.6.38"
+  resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.38.tgz#e92a20f83416415bb4086f6d1fb78b3da73d113d"
+  dependencies:
+    hawk "3.1.3"
+    mkdirp "^0.5.1"
+    nopt "^4.0.1"
+    npmlog "^4.0.2"
+    rc "^1.1.7"
+    request "2.81.0"
+    rimraf "^2.6.1"
+    semver "^5.3.0"
+    tar "^2.2.1"
+    tar-pack "^3.4.0"
+
+nopt@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d"
+  dependencies:
+    abbrev "1"
+    osenv "^0.1.4"
+
+normalize-package-data@^2.3.2:
+  version "2.4.0"
+  resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f"
+  dependencies:
+    hosted-git-info "^2.1.4"
+    is-builtin-module "^1.0.0"
+    semver "2 || 3 || 4 || 5"
+    validate-npm-package-license "^3.0.1"
+
+normalize-path@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-1.0.0.tgz#32d0e472f91ff345701c15a8311018d3b0a90379"
+
+normalize-path@^2.0.0, normalize-path@^2.0.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
+  dependencies:
+    remove-trailing-separator "^1.0.1"
+
+npm-path@^2.0.2:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/npm-path/-/npm-path-2.0.3.tgz#15cff4e1c89a38da77f56f6055b24f975dfb2bbe"
+  dependencies:
+    which "^1.2.10"
+
+npm-run-path@^2.0.0:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
+  dependencies:
+    path-key "^2.0.0"
+
+npm-which@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/npm-which/-/npm-which-3.0.1.tgz#9225f26ec3a285c209cae67c3b11a6b4ab7140aa"
+  dependencies:
+    commander "^2.9.0"
+    npm-path "^2.0.2"
+    which "^1.2.10"
+
+npmlog@^4.0.2:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
+  dependencies:
+    are-we-there-yet "~1.1.2"
+    console-control-strings "~1.1.0"
+    gauge "~2.7.3"
+    set-blocking "~2.0.0"
+
+nth-check@~1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4"
+  dependencies:
+    boolbase "~1.0.0"
+
+number-is-nan@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
+
+"nwmatcher@>= 1.3.9 < 2.0.0":
+  version "1.4.3"
+  resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.3.tgz#64348e3b3d80f035b40ac11563d278f8b72db89c"
+
+oauth-sign@~0.8.1, oauth-sign@~0.8.2:
+  version "0.8.2"
+  resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
+
+object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+
+object-is@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.1.tgz#0aa60ec9989a0b3ed795cf4d06f62cf1ad6539b6"
+
+object-keys@^1.0.10, object-keys@^1.0.8:
+  version "1.0.11"
+  resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d"
+
+object.assign@^4.0.4:
+  version "4.0.4"
+  resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.0.4.tgz#b1c9cc044ef1b9fe63606fc141abbb32e14730cc"
+  dependencies:
+    define-properties "^1.1.2"
+    function-bind "^1.1.0"
+    object-keys "^1.0.10"
+
+object.entries@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.0.4.tgz#1bf9a4dd2288f5b33f3a993d257661f05d161a5f"
+  dependencies:
+    define-properties "^1.1.2"
+    es-abstract "^1.6.1"
+    function-bind "^1.1.0"
+    has "^1.0.1"
+
+object.omit@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa"
+  dependencies:
+    for-own "^0.1.4"
+    is-extendable "^0.1.1"
+
+object.values@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.0.4.tgz#e524da09b4f66ff05df457546ec72ac99f13069a"
+  dependencies:
+    define-properties "^1.1.2"
+    es-abstract "^1.6.1"
+    function-bind "^1.1.0"
+    has "^1.0.1"
+
+once@^1.3.0, once@^1.3.3, once@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+  dependencies:
+    wrappy "1"
+
+onetime@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789"
+
+onetime@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
+  dependencies:
+    mimic-fn "^1.0.0"
+
+optimist@^0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
+  dependencies:
+    minimist "~0.0.1"
+    wordwrap "~0.0.2"
+
+optionator@^0.8.1, optionator@^0.8.2:
+  version "0.8.2"
+  resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64"
+  dependencies:
+    deep-is "~0.1.3"
+    fast-levenshtein "~2.0.4"
+    levn "~0.3.0"
+    prelude-ls "~1.1.2"
+    type-check "~0.3.2"
+    wordwrap "~1.0.0"
+
+ora@^0.2.3:
+  version "0.2.3"
+  resolved "https://registry.yarnpkg.com/ora/-/ora-0.2.3.tgz#37527d220adcd53c39b73571d754156d5db657a4"
+  dependencies:
+    chalk "^1.1.1"
+    cli-cursor "^1.0.2"
+    cli-spinners "^0.1.2"
+    object-assign "^4.0.1"
+
+os-homedir@1.0.2, os-homedir@^1.0.0, os-homedir@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
+
+os-locale@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2"
+  dependencies:
+    execa "^0.7.0"
+    lcid "^1.0.0"
+    mem "^1.1.0"
+
+os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
+
+osenv@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644"
+  dependencies:
+    os-homedir "^1.0.0"
+    os-tmpdir "^1.0.0"
+
+output-file-sync@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/output-file-sync/-/output-file-sync-1.1.2.tgz#d0a33eefe61a205facb90092e826598d5245ce76"
+  dependencies:
+    graceful-fs "^4.1.4"
+    mkdirp "^0.5.1"
+    object-assign "^4.1.0"
+
+p-cancelable@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa"
+
+p-finally@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
+
+p-limit@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.1.0.tgz#b07ff2d9a5d88bec806035895a2bab66a27988bc"
+
+p-locate@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
+  dependencies:
+    p-limit "^1.1.0"
+
+p-map@^1.1.1:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b"
+
+parse-glob@^3.0.4:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c"
+  dependencies:
+    glob-base "^0.3.0"
+    is-dotfile "^1.0.0"
+    is-extglob "^1.0.0"
+    is-glob "^2.0.0"
+
+parse-json@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
+  dependencies:
+    error-ex "^1.2.0"
+
+parse5@^1.5.1:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/parse5/-/parse5-1.5.1.tgz#9b7f3b0de32be78dc2401b17573ccaf0f6f59d94"
+
+path-exists@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
+  dependencies:
+    pinkie-promise "^2.0.0"
+
+path-exists@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
+
+path-is-absolute@^1.0.0, path-is-absolute@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+
+path-is-inside@^1.0.1, path-is-inside@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
+
+path-key@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
+
+path-parse@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1"
+
+path-to-regexp@^1.7.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d"
+  dependencies:
+    isarray "0.0.1"
+
+path-type@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
+  dependencies:
+    graceful-fs "^4.1.2"
+    pify "^2.0.0"
+    pinkie-promise "^2.0.0"
+
+path-type@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73"
+  dependencies:
+    pify "^2.0.0"
+
+performance-now@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5"
+
+performance-now@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
+
+pify@^2.0.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
+
+pify@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
+
+pinkie-promise@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
+  dependencies:
+    pinkie "^2.0.0"
+
+pinkie@^2.0.0:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
+
+pkg-dir@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4"
+  dependencies:
+    find-up "^1.0.0"
+
+pluralize@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777"
+
+prelude-ls@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
+
+preserve@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
+
+prettier@^1.7.4:
+  version "1.7.4"
+  resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.7.4.tgz#5e8624ae9363c80f95ec644584ecdf55d74f93fa"
+
+pretty-format@^21.2.1:
+  version "21.2.1"
+  resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-21.2.1.tgz#ae5407f3cf21066cd011aa1ba5fce7b6a2eddb36"
+  dependencies:
+    ansi-regex "^3.0.0"
+    ansi-styles "^3.2.0"
+
+private@^0.1.6, private@^0.1.7:
+  version "0.1.8"
+  resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
+
+process-nextick-args@~1.0.6:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3"
+
+process@~0.5.1:
+  version "0.5.2"
+  resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf"
+
+progress@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f"
+
+promise@^7.1.1:
+  version "7.3.1"
+  resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
+  dependencies:
+    asap "~2.0.3"
+
+prop-types-extra@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/prop-types-extra/-/prop-types-extra-1.0.1.tgz#a57bd4810e82d27a3ff4317ecc1b4ad005f79a82"
+  dependencies:
+    warning "^3.0.0"
+
+prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.8, prop-types@^15.5.9:
+  version "15.6.0"
+  resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856"
+  dependencies:
+    fbjs "^0.8.16"
+    loose-envify "^1.3.1"
+    object-assign "^4.1.1"
+
+propagate@0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/propagate/-/propagate-0.4.0.tgz#f3fcca0a6fe06736a7ba572966069617c130b481"
+
+prr@~0.0.0:
+  version "0.0.0"
+  resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a"
+
+pseudomap@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
+
+pubsweet-component-login@^0.5.2:
+  version "0.5.2"
+  resolved "https://registry.yarnpkg.com/pubsweet-component-login/-/pubsweet-component-login-0.5.2.tgz#ff942838be4c64607020f67c777d6e1da9197115"
+  dependencies:
+    prop-types "^15.5.10"
+    react-bootstrap "^0.31.3"
+    react-redux "^5.0.6"
+    react-router-dom "^4.2.2"
+    react-router-redux "^4.0.8"
+    redux "^3.7.2"
+
+punycode@^1.4.1:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
+
+qs@^6.0.2, qs@~6.5.1:
+  version "6.5.1"
+  resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8"
+
+qs@~6.4.0:
+  version "6.4.0"
+  resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
+
+randomatic@^1.1.3:
+  version "1.1.7"
+  resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c"
+  dependencies:
+    is-number "^3.0.0"
+    kind-of "^4.0.0"
+
+rc@^1.1.7:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95"
+  dependencies:
+    deep-extend "~0.4.0"
+    ini "~1.3.0"
+    minimist "^1.2.0"
+    strip-json-comments "~2.0.1"
+
+react-bootstrap@^0.31.3:
+  version "0.31.3"
+  resolved "https://registry.yarnpkg.com/react-bootstrap/-/react-bootstrap-0.31.3.tgz#db2b7d45b00b5dac1ab8b6de3dd97feb3091b849"
+  dependencies:
+    babel-runtime "^6.11.6"
+    classnames "^2.2.5"
+    dom-helpers "^3.2.0"
+    invariant "^2.2.1"
+    keycode "^2.1.2"
+    prop-types "^15.5.10"
+    prop-types-extra "^1.0.1"
+    react-overlays "^0.7.0"
+    react-prop-types "^0.4.0"
+    uncontrollable "^4.1.0"
+    warning "^3.0.0"
+
+react-css-themr@^2.1.2:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/react-css-themr/-/react-css-themr-2.1.2.tgz#e017514e471c232f43a754a55b49d81faf5dafb8"
+  dependencies:
+    hoist-non-react-statics "^1.2.0"
+    invariant "^2.2.1"
+
+react-dom@^15.6.1:
+  version "15.6.2"
+  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.6.2.tgz#41cfadf693b757faf2708443a1d1fd5a02bef730"
+  dependencies:
+    fbjs "^0.8.9"
+    loose-envify "^1.1.0"
+    object-assign "^4.1.0"
+    prop-types "^15.5.10"
+
+react-overlays@^0.7.0:
+  version "0.7.2"
+  resolved "https://registry.yarnpkg.com/react-overlays/-/react-overlays-0.7.2.tgz#03808f80d99dfadd93d67438c619aa55d07b3f80"
+  dependencies:
+    classnames "^2.2.5"
+    dom-helpers "^3.2.1"
+    prop-types "^15.5.10"
+    prop-types-extra "^1.0.1"
+    warning "^3.0.0"
+
+react-prop-types@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/react-prop-types/-/react-prop-types-0.4.0.tgz#f99b0bfb4006929c9af2051e7c1414a5c75b93d0"
+  dependencies:
+    warning "^3.0.0"
+
+react-redux@^5.0.2, react-redux@^5.0.6:
+  version "5.0.6"
+  resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.6.tgz#23ed3a4f986359d68b5212eaaa681e60d6574946"
+  dependencies:
+    hoist-non-react-statics "^2.2.1"
+    invariant "^2.0.0"
+    lodash "^4.2.0"
+    lodash-es "^4.2.0"
+    loose-envify "^1.1.0"
+    prop-types "^15.5.10"
+
+react-router-dom@^4.2.2:
+  version "4.2.2"
+  resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-4.2.2.tgz#c8a81df3adc58bba8a76782e946cbd4eae649b8d"
+  dependencies:
+    history "^4.7.2"
+    invariant "^2.2.2"
+    loose-envify "^1.3.1"
+    prop-types "^15.5.4"
+    react-router "^4.2.0"
+    warning "^3.0.0"
+
+react-router-redux@^4.0.8:
+  version "4.0.8"
+  resolved "https://registry.yarnpkg.com/react-router-redux/-/react-router-redux-4.0.8.tgz#227403596b5151e182377dab835b5d45f0f8054e"
+
+react-router-redux@next:
+  version "5.0.0-alpha.6"
+  resolved "https://registry.yarnpkg.com/react-router-redux/-/react-router-redux-5.0.0-alpha.6.tgz#7418663c2ecd3c51be856fcf28f3d1deecc1a576"
+  dependencies:
+    history "^4.5.1"
+    prop-types "^15.5.4"
+    react-router "^4.1.1"
+
+react-router@^4.1.1, react-router@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/react-router/-/react-router-4.2.0.tgz#61f7b3e3770daeb24062dae3eedef1b054155986"
+  dependencies:
+    history "^4.7.2"
+    hoist-non-react-statics "^2.3.0"
+    invariant "^2.2.2"
+    loose-envify "^1.3.1"
+    path-to-regexp "^1.7.0"
+    prop-types "^15.5.4"
+    warning "^3.0.0"
+
+react-test-renderer@^15.6.1:
+  version "15.6.2"
+  resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-15.6.2.tgz#d0333434fc2c438092696ca770da5ed48037efa8"
+  dependencies:
+    fbjs "^0.8.9"
+    object-assign "^4.1.0"
+
+react@^15.4.4:
+  version "15.6.2"
+  resolved "https://registry.yarnpkg.com/react/-/react-15.6.2.tgz#dba0434ab439cfe82f108f0f511663908179aa72"
+  dependencies:
+    create-react-class "^15.6.0"
+    fbjs "^0.8.9"
+    loose-envify "^1.1.0"
+    object-assign "^4.1.0"
+    prop-types "^15.5.10"
+
+read-pkg-up@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
+  dependencies:
+    find-up "^1.0.0"
+    read-pkg "^1.0.0"
+
+read-pkg-up@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be"
+  dependencies:
+    find-up "^2.0.0"
+    read-pkg "^2.0.0"
+
+read-pkg@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28"
+  dependencies:
+    load-json-file "^1.0.0"
+    normalize-package-data "^2.3.2"
+    path-type "^1.0.0"
+
+read-pkg@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8"
+  dependencies:
+    load-json-file "^2.0.0"
+    normalize-package-data "^2.3.2"
+    path-type "^2.0.0"
+
+readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.2.2:
+  version "2.3.3"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c"
+  dependencies:
+    core-util-is "~1.0.0"
+    inherits "~2.0.3"
+    isarray "~1.0.0"
+    process-nextick-args "~1.0.6"
+    safe-buffer "~5.1.1"
+    string_decoder "~1.0.3"
+    util-deprecate "~1.0.1"
+
+readdirp@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78"
+  dependencies:
+    graceful-fs "^4.1.2"
+    minimatch "^3.0.2"
+    readable-stream "^2.0.2"
+    set-immediate-shim "^1.0.1"
+
+redux-form@^7.0.3:
+  version "7.1.1"
+  resolved "https://registry.yarnpkg.com/redux-form/-/redux-form-7.1.1.tgz#4d9ab1d9c03beb3a8b5f8e5d0f398cff4209081f"
+  dependencies:
+    babel-jest "^21.2.0"
+    deep-equal "^1.0.1"
+    es6-error "^4.0.0"
+    hoist-non-react-statics "^2.3.1"
+    invariant "^2.2.2"
+    is-promise "^2.1.0"
+    lodash "^4.17.3"
+    lodash-es "^4.17.3"
+    prop-types "^15.5.9"
+
+redux-logger@^3.0.1:
+  version "3.0.6"
+  resolved "https://registry.yarnpkg.com/redux-logger/-/redux-logger-3.0.6.tgz#f7555966f3098f3c88604c449cf0baf5778274bf"
+  dependencies:
+    deep-diff "^0.3.5"
+
+redux-mock-store@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/redux-mock-store/-/redux-mock-store-1.3.0.tgz#6edfef0d2332f20576381069d6d889a6d0a4451c"
+
+redux-thunk@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.2.0.tgz#e615a16e16b47a19a515766133d1e3e99b7852e5"
+
+redux@^3.6.0, redux@^3.7.2:
+  version "3.7.2"
+  resolved "https://registry.yarnpkg.com/redux/-/redux-3.7.2.tgz#06b73123215901d25d065be342eb026bc1c8537b"
+  dependencies:
+    lodash "^4.2.1"
+    lodash-es "^4.2.1"
+    loose-envify "^1.1.0"
+    symbol-observable "^1.0.3"
+
+regenerate@^1.2.1:
+  version "1.3.3"
+  resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.3.tgz#0c336d3980553d755c39b586ae3b20aa49c82b7f"
+
+regenerator-runtime@^0.10.5:
+  version "0.10.5"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658"
+
+regenerator-runtime@^0.11.0:
+  version "0.11.0"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz#7e54fe5b5ccd5d6624ea6255c3473be090b802e1"
+
+regenerator-transform@^0.10.0:
+  version "0.10.1"
+  resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd"
+  dependencies:
+    babel-runtime "^6.18.0"
+    babel-types "^6.19.0"
+    private "^0.1.6"
+
+regex-cache@^0.4.2:
+  version "0.4.4"
+  resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd"
+  dependencies:
+    is-equal-shallow "^0.1.3"
+
+regexpu-core@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240"
+  dependencies:
+    regenerate "^1.2.1"
+    regjsgen "^0.2.0"
+    regjsparser "^0.1.4"
+
+regjsgen@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7"
+
+regjsparser@^0.1.4:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c"
+  dependencies:
+    jsesc "~0.5.0"
+
+remove-trailing-separator@^1.0.1:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
+
+repeat-element@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a"
+
+repeat-string@^1.5.2:
+  version "1.6.1"
+  resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
+
+repeating@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda"
+  dependencies:
+    is-finite "^1.0.0"
+
+request@2.81.0:
+  version "2.81.0"
+  resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
+  dependencies:
+    aws-sign2 "~0.6.0"
+    aws4 "^1.2.1"
+    caseless "~0.12.0"
+    combined-stream "~1.0.5"
+    extend "~3.0.0"
+    forever-agent "~0.6.1"
+    form-data "~2.1.1"
+    har-validator "~4.2.1"
+    hawk "~3.1.3"
+    http-signature "~1.1.0"
+    is-typedarray "~1.0.0"
+    isstream "~0.1.2"
+    json-stringify-safe "~5.0.1"
+    mime-types "~2.1.7"
+    oauth-sign "~0.8.1"
+    performance-now "^0.2.0"
+    qs "~6.4.0"
+    safe-buffer "^5.0.1"
+    stringstream "~0.0.4"
+    tough-cookie "~2.3.0"
+    tunnel-agent "^0.6.0"
+    uuid "^3.0.0"
+
+request@^2.79.0:
+  version "2.83.0"
+  resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356"
+  dependencies:
+    aws-sign2 "~0.7.0"
+    aws4 "^1.6.0"
+    caseless "~0.12.0"
+    combined-stream "~1.0.5"
+    extend "~3.0.1"
+    forever-agent "~0.6.1"
+    form-data "~2.3.1"
+    har-validator "~5.0.3"
+    hawk "~6.0.2"
+    http-signature "~1.2.0"
+    is-typedarray "~1.0.0"
+    isstream "~0.1.2"
+    json-stringify-safe "~5.0.1"
+    mime-types "~2.1.17"
+    oauth-sign "~0.8.2"
+    performance-now "^2.1.0"
+    qs "~6.5.1"
+    safe-buffer "^5.1.1"
+    stringstream "~0.0.5"
+    tough-cookie "~2.3.3"
+    tunnel-agent "^0.6.0"
+    uuid "^3.1.0"
+
+require-directory@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
+
+require-from-string@^1.1.0:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418"
+
+require-main-filename@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
+
+require-uncached@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3"
+  dependencies:
+    caller-path "^0.1.0"
+    resolve-from "^1.0.0"
+
+resolve-from@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226"
+
+resolve-pathname@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-2.2.0.tgz#7e9ae21ed815fd63ab189adeee64dc831eefa879"
+
+resolve@1.1.7:
+  version "1.1.7"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
+
+resolve@^1.2.0, resolve@^1.3.3:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.4.0.tgz#a75be01c53da25d934a98ebd0e4c4a7312f92a86"
+  dependencies:
+    path-parse "^1.0.5"
+
+restore-cursor@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541"
+  dependencies:
+    exit-hook "^1.0.0"
+    onetime "^1.0.0"
+
+restore-cursor@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
+  dependencies:
+    onetime "^2.0.0"
+    signal-exit "^3.0.2"
+
+right-align@^0.1.1:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef"
+  dependencies:
+    align-text "^0.1.1"
+
+rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.6.1:
+  version "2.6.2"
+  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36"
+  dependencies:
+    glob "^7.0.5"
+
+run-async@^2.2.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
+  dependencies:
+    is-promise "^2.1.0"
+
+rx-lite-aggregates@^4.0.8:
+  version "4.0.8"
+  resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be"
+  dependencies:
+    rx-lite "*"
+
+rx-lite@*, rx-lite@^4.0.8:
+  version "4.0.8"
+  resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444"
+
+rxjs@^5.0.0-beta.11:
+  version "5.4.3"
+  resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.4.3.tgz#0758cddee6033d68e0fd53676f0f3596ce3d483f"
+  dependencies:
+    symbol-observable "^1.0.1"
+
+safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
+  version "5.1.1"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
+
+sane@^2.0.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/sane/-/sane-2.2.0.tgz#d6d2e2fcab00e3d283c93b912b7c3a20846f1d56"
+  dependencies:
+    anymatch "^1.3.0"
+    exec-sh "^0.2.0"
+    fb-watchman "^2.0.0"
+    minimatch "^3.0.2"
+    minimist "^1.1.1"
+    walker "~1.0.5"
+    watch "~0.18.0"
+  optionalDependencies:
+    fsevents "^1.1.1"
+
+sax@^1.2.1:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
+
+"semver@2 || 3 || 4 || 5", semver@^5.3.0:
+  version "5.4.1"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e"
+
+semver@5.3.0:
+  version "5.3.0"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
+
+set-blocking@^2.0.0, set-blocking@~2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
+
+set-immediate-shim@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61"
+
+setimmediate@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
+
+shebang-command@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
+  dependencies:
+    shebang-regex "^1.0.0"
+
+shebang-regex@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
+
+shellwords@^0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
+
+signal-exit@^3.0.0, signal-exit@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
+
+slash@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
+
+slice-ansi@0.0.4:
+  version "0.0.4"
+  resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
+
+slice-ansi@1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d"
+  dependencies:
+    is-fullwidth-code-point "^2.0.0"
+
+sntp@1.x.x:
+  version "1.0.9"
+  resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198"
+  dependencies:
+    hoek "2.x.x"
+
+sntp@2.x.x:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/sntp/-/sntp-2.0.2.tgz#5064110f0af85f7cfdb7d6b67a40028ce52b4b2b"
+  dependencies:
+    hoek "4.x.x"
+
+source-map-support@^0.4.15:
+  version "0.4.18"
+  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f"
+  dependencies:
+    source-map "^0.5.6"
+
+source-map@^0.4.4:
+  version "0.4.4"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
+  dependencies:
+    amdefine ">=0.0.4"
+
+source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1, source-map@~0.5.6:
+  version "0.5.7"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
+
+spdx-correct@~1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40"
+  dependencies:
+    spdx-license-ids "^1.0.2"
+
+spdx-expression-parse@~1.0.0:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c"
+
+spdx-license-ids@^1.0.2:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57"
+
+sprintf-js@~1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
+
+sshpk@^1.7.0:
+  version "1.13.1"
+  resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3"
+  dependencies:
+    asn1 "~0.2.3"
+    assert-plus "^1.0.0"
+    dashdash "^1.12.0"
+    getpass "^0.1.1"
+  optionalDependencies:
+    bcrypt-pbkdf "^1.0.0"
+    ecc-jsbn "~0.1.1"
+    jsbn "~0.1.0"
+    tweetnacl "~0.14.0"
+
+staged-git-files@0.0.4:
+  version "0.0.4"
+  resolved "https://registry.yarnpkg.com/staged-git-files/-/staged-git-files-0.0.4.tgz#d797e1b551ca7a639dec0237dc6eb4bb9be17d35"
+
+stream-to-observable@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/stream-to-observable/-/stream-to-observable-0.1.0.tgz#45bf1d9f2d7dc09bed81f1c307c430e68b84cffe"
+
+string-length@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed"
+  dependencies:
+    astral-regex "^1.0.0"
+    strip-ansi "^4.0.0"
+
+string-width@^1.0.1, string-width@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
+  dependencies:
+    code-point-at "^1.0.0"
+    is-fullwidth-code-point "^1.0.0"
+    strip-ansi "^3.0.0"
+
+string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
+  dependencies:
+    is-fullwidth-code-point "^2.0.0"
+    strip-ansi "^4.0.0"
+
+string_decoder@~1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab"
+  dependencies:
+    safe-buffer "~5.1.0"
+
+stringify-object@^3.2.0:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.2.1.tgz#2720c2eff940854c819f6ee252aaeb581f30624d"
+  dependencies:
+    get-own-enumerable-property-symbols "^2.0.1"
+    is-obj "^1.0.1"
+    is-regexp "^1.0.0"
+
+stringstream@~0.0.4, stringstream@~0.0.5:
+  version "0.0.5"
+  resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878"
+
+strip-ansi@^3.0.0, strip-ansi@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
+  dependencies:
+    ansi-regex "^2.0.0"
+
+strip-ansi@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
+  dependencies:
+    ansi-regex "^3.0.0"
+
+strip-bom@3.0.0, strip-bom@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
+
+strip-bom@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
+  dependencies:
+    is-utf8 "^0.2.0"
+
+strip-eof@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
+
+strip-indent@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68"
+
+strip-json-comments@~2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
+
+supports-color@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
+
+supports-color@^3.1.2:
+  version "3.2.3"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
+  dependencies:
+    has-flag "^1.0.0"
+
+supports-color@^4.0.0:
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.4.0.tgz#883f7ddabc165142b2a61427f3352ded195d1a3e"
+  dependencies:
+    has-flag "^2.0.0"
+
+symbol-observable@^1.0.1, symbol-observable@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d"
+
+symbol-tree@^3.2.1:
+  version "3.2.2"
+  resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6"
+
+table@^4.0.1:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36"
+  dependencies:
+    ajv "^5.2.3"
+    ajv-keywords "^2.1.0"
+    chalk "^2.1.0"
+    lodash "^4.17.4"
+    slice-ansi "1.0.0"
+    string-width "^2.1.1"
+
+tar-pack@^3.4.0:
+  version "3.4.0"
+  resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.0.tgz#23be2d7f671a8339376cbdb0b8fe3fdebf317984"
+  dependencies:
+    debug "^2.2.0"
+    fstream "^1.0.10"
+    fstream-ignore "^1.0.5"
+    once "^1.3.3"
+    readable-stream "^2.1.4"
+    rimraf "^2.5.1"
+    tar "^2.2.1"
+    uid-number "^0.0.6"
+
+tar@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1"
+  dependencies:
+    block-stream "*"
+    fstream "^1.0.2"
+    inherits "2"
+
+test-exclude@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.1.1.tgz#4d84964b0966b0087ecc334a2ce002d3d9341e26"
+  dependencies:
+    arrify "^1.0.1"
+    micromatch "^2.3.11"
+    object-assign "^4.1.0"
+    read-pkg-up "^1.0.1"
+    require-main-filename "^1.0.1"
+
+text-table@~0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+
+throat@^4.0.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a"
+
+through@^2.3.6:
+  version "2.3.8"
+  resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
+
+tmp@^0.0.33:
+  version "0.0.33"
+  resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
+  dependencies:
+    os-tmpdir "~1.0.2"
+
+tmpl@1.0.x:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1"
+
+to-fast-properties@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
+
+tough-cookie@^2.3.2, tough-cookie@~2.3.0, tough-cookie@~2.3.3:
+  version "2.3.3"
+  resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561"
+  dependencies:
+    punycode "^1.4.1"
+
+tr46@~0.0.3:
+  version "0.0.3"
+  resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
+
+trim-right@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
+
+tryit@^1.0.1:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb"
+
+tunnel-agent@^0.6.0:
+  version "0.6.0"
+  resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
+  dependencies:
+    safe-buffer "^5.0.1"
+
+tweetnacl@^0.14.3, tweetnacl@~0.14.0:
+  version "0.14.5"
+  resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
+
+type-check@~0.3.2:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
+  dependencies:
+    prelude-ls "~1.1.2"
+
+type-detect@0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-0.1.1.tgz#0ba5ec2a885640e470ea4e8505971900dac58822"
+
+type-detect@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-1.0.0.tgz#762217cc06db258ec48908a1298e8b95121e8ea2"
+
+typedarray@^0.0.6:
+  version "0.0.6"
+  resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
+
+ua-parser-js@^0.7.9:
+  version "0.7.17"
+  resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.17.tgz#e9ec5f9498b9ec910e7ae3ac626a805c4d09ecac"
+
+uglify-js@^2.6:
+  version "2.8.29"
+  resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd"
+  dependencies:
+    source-map "~0.5.1"
+    yargs "~3.10.0"
+  optionalDependencies:
+    uglify-to-browserify "~1.0.0"
+
+uglify-to-browserify@~1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7"
+
+uid-number@^0.0.6:
+  version "0.0.6"
+  resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81"
+
+uncontrollable@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/uncontrollable/-/uncontrollable-4.1.0.tgz#e0358291252e1865222d90939b19f2f49f81c1a9"
+  dependencies:
+    invariant "^2.1.0"
+
+user-home@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190"
+
+util-deprecate@~1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+
+uuid@^3.0.0, uuid@^3.0.1, uuid@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04"
+
+v8flags@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.1.1.tgz#aab1a1fa30d45f88dd321148875ac02c0b55e5b4"
+  dependencies:
+    user-home "^1.1.1"
+
+validate-npm-package-license@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc"
+  dependencies:
+    spdx-correct "~1.0.0"
+    spdx-expression-parse "~1.0.0"
+
+value-equal@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-0.4.0.tgz#c5bdd2f54ee093c04839d71ce2e4758a6890abc7"
+
+verror@1.10.0:
+  version "1.10.0"
+  resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
+  dependencies:
+    assert-plus "^1.0.0"
+    core-util-is "1.0.2"
+    extsprintf "^1.2.0"
+
+walker@~1.0.5:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb"
+  dependencies:
+    makeerror "1.0.x"
+
+warning@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c"
+  dependencies:
+    loose-envify "^1.0.0"
+
+watch@~0.18.0:
+  version "0.18.0"
+  resolved "https://registry.yarnpkg.com/watch/-/watch-0.18.0.tgz#28095476c6df7c90c963138990c0a5423eb4b986"
+  dependencies:
+    exec-sh "^0.2.0"
+    minimist "^1.2.0"
+
+webidl-conversions@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
+
+webidl-conversions@^4.0.0:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
+
+whatwg-encoding@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.1.tgz#3c6c451a198ee7aec55b1ec61d0920c67801a5f4"
+  dependencies:
+    iconv-lite "0.4.13"
+
+whatwg-fetch@>=0.10.0:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84"
+
+whatwg-url@^4.3.0:
+  version "4.8.0"
+  resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-4.8.0.tgz#d2981aa9148c1e00a41c5a6131166ab4683bbcc0"
+  dependencies:
+    tr46 "~0.0.3"
+    webidl-conversions "^3.0.0"
+
+which-module@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
+
+which@^1.2.10, which@^1.2.12, which@^1.2.9:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a"
+  dependencies:
+    isexe "^2.0.0"
+
+wide-align@^1.1.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710"
+  dependencies:
+    string-width "^1.0.2"
+
+window-size@0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"
+
+wordwrap@0.0.2:
+  version "0.0.2"
+  resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"
+
+wordwrap@~0.0.2:
+  version "0.0.3"
+  resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
+
+wordwrap@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
+
+worker-farm@^1.3.1:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.5.0.tgz#adfdf0cd40581465ed0a1f648f9735722afd5c8d"
+  dependencies:
+    errno "^0.1.4"
+    xtend "^4.0.1"
+
+wrap-ansi@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
+  dependencies:
+    string-width "^1.0.1"
+    strip-ansi "^3.0.1"
+
+wrappy@1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+
+write-file-atomic@^2.1.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.3.0.tgz#1ff61575c2e2a4e8e510d6fa4e243cce183999ab"
+  dependencies:
+    graceful-fs "^4.1.11"
+    imurmurhash "^0.1.4"
+    signal-exit "^3.0.2"
+
+write@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757"
+  dependencies:
+    mkdirp "^0.5.1"
+
+xml-name-validator@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635"
+
+xtend@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
+
+y18n@^3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
+
+yallist@^2.1.2:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
+
+yargs-parser@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9"
+  dependencies:
+    camelcase "^4.1.0"
+
+yargs@^9.0.0:
+  version "9.0.1"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-9.0.1.tgz#52acc23feecac34042078ee78c0c007f5085db4c"
+  dependencies:
+    camelcase "^4.1.0"
+    cliui "^3.2.0"
+    decamelize "^1.1.1"
+    get-caller-file "^1.0.1"
+    os-locale "^2.0.0"
+    read-pkg-up "^2.0.0"
+    require-directory "^2.1.1"
+    require-main-filename "^1.0.1"
+    set-blocking "^2.0.0"
+    string-width "^2.0.0"
+    which-module "^2.0.0"
+    y18n "^3.2.1"
+    yargs-parser "^7.0.0"
+
+yargs@~3.10.0:
+  version "3.10.0"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1"
+  dependencies:
+    camelcase "^1.0.2"
+    cliui "^2.1.0"
+    decamelize "^1.0.0"
+    window-size "0.1.0"