diff --git a/package.json b/package.json
index a5367e461f94dd5bb25d15145a79033ae0d461da..4e05a2d1ee595f0011d8372e7cde77a3d6dfa6fe 100644
--- a/package.json
+++ b/package.json
@@ -21,7 +21,7 @@
     "eslint-plugin-react": "^7.4.0",
     "eslint-plugin-standard": "^3.0.1",
     "husky": "^0.14.3",
-    "lerna": "^2.5.1",
+    "lerna": "^3.4.3",
     "lint-staged": "^6.0.0",
     "prettier": "^1.8.2",
     "stylelint": "^8.2.0",
diff --git a/packages/base-model/src/index.js b/packages/base-model/src/index.js
index 2c6d502045395098b2df1ef9db9b801a07935f5e..6b74a9c00859ba47050fd5e5d46ca5dadfb43e4e 100644
--- a/packages/base-model/src/index.js
+++ b/packages/base-model/src/index.js
@@ -1,5 +1,5 @@
 const uuid = require('uuid')
-const { Model, ValidationError, transaction } = require('objection')
+const { Model, transaction } = require('objection')
 const logger = require('@pubsweet/logger')
 const { db, NotFoundError } = require('pubsweet-server')
 const { merge } = require('lodash')
@@ -7,12 +7,6 @@ const config = require('config')
 
 Model.knex(db)
 
-const validationError = (prop, className) =>
-  new ValidationError({
-    type: 'ModelValidation',
-    message: `${prop} is not a property in ${className}'s schema`,
-  })
-
 const notFoundError = (property, value, className) =>
   new NotFoundError(`Object not found: ${className} with ${property} ${value}`)
 
@@ -26,24 +20,16 @@ class BaseModel extends Model {
     super(properties)
 
     if (properties) {
-      this.updateProperties(properties)
-    }
-
-    const handler = {
-      set: (obj, prop, value) => {
-        if (this.isSettable(prop)) {
-          obj[prop] = value
-          return true
-        }
-
-        throw validationError(prop, obj.constructor.name)
-      },
+      this._updateProperties(properties)
     }
-
-    return new Proxy(this, handler)
   }
 
   static get jsonSchema() {
+    // JSON schema validation is getting proper support for inheritance in
+    // its draft 8: https://github.com/json-schema-org/json-schema-spec/issues/556
+    // Until then, we're not using additionalProperties: false, and letting the
+    // database handle this bit of the integrity checks.
+
     let schema
 
     const mergeSchema = additionalSchema => {
@@ -56,7 +42,9 @@ class BaseModel extends Model {
     // information from models and extended models
     const getSchemasRecursively = object => {
       mergeSchema(object.schema)
-      mergeSchema(config.schema[object.name])
+      if (config.has('schema')) {
+        mergeSchema(config.schema[object.name])
+      }
 
       const proto = Object.getPrototypeOf(object)
 
@@ -73,12 +61,7 @@ class BaseModel extends Model {
         type: { type: 'string' },
         id: { type: 'string', format: 'uuid' },
         created: { type: ['string', 'object'], format: 'date-time' },
-        updated: {
-          anyOf: [
-            { type: ['string', 'object'], format: 'date-time' },
-            { type: 'null' },
-          ],
-        },
+        updated: { type: ['string', 'object'], format: 'date-time' },
       },
       additionalProperties: false,
     }
@@ -86,17 +69,8 @@ class BaseModel extends Model {
     if (schema) {
       return merge(baseSchema, schema)
     }
-    return baseSchema
-  }
 
-  isSettable(prop) {
-    const special = ['#id', '#ref']
-    return (
-      special.includes(prop) ||
-      this.constructor.jsonSchema.properties[prop] ||
-      (this.constructor.relationMappings &&
-        this.constructor.relationMappings[prop])
-    )
+    return baseSchema
   }
 
   $beforeInsert() {
@@ -111,7 +85,7 @@ class BaseModel extends Model {
 
   async save() {
     const simpleSave = async (trx = null) =>
-      this.constructor.query(trx).patchAndFetchById(this.id, this.toJSON())
+      this.constructor.query(trx).patchAndFetchById(this.id, this)
 
     const protectedSave = async () => {
       let trx, saved
@@ -143,6 +117,11 @@ class BaseModel extends Model {
     // start of save function...
 
     let saved
+    // Do the validation manually here, since inserting
+    // model instances skips validation, and using toJSON() first will
+    // not save certain fields ommited in $formatJSON (e.g. passwordHash)
+    this.$validate()
+
     if (this.id) {
       if (!this.updated && this.created) {
         throw integrityError(
@@ -160,8 +139,9 @@ class BaseModel extends Model {
     }
     if (!saved) {
       // either model has no ID or the ID was not found in the database
-      saved = await this.constructor.query().insert(this.toJSON())
+      saved = await this.constructor.query().insertAndFetch(this)
     }
+
     logger.info(`Saved ${this.constructor.name} with UUID ${saved.id}`)
     return saved
   }
@@ -172,14 +152,16 @@ class BaseModel extends Model {
     return this
   }
 
-  updateProperties(properties) {
+  // A private method that you shouldn't override
+  _updateProperties(properties) {
     Object.keys(properties).forEach(prop => {
-      if (this.isSettable(prop)) {
-        this[prop] = properties[prop]
-      } else {
-        throw validationError(prop, this.constructor.name)
-      }
+      this[prop] = properties[prop]
     })
+    return this
+  }
+
+  updateProperties(properties) {
+    return this._updateProperties(properties)
   }
 
   setOwners(owners) {
@@ -190,18 +172,22 @@ class BaseModel extends Model {
     const object = await this.query().findById(id)
 
     if (!object) {
-      throw notFoundError('id', id, this.constructor.name)
+      throw notFoundError('id', id, this.name)
     }
 
     return object
   }
 
-  static async findByField(field, value) {
+  // `field` is a string, `value` is a primitive or
+  // `field` is an object of field, value pairs
+  static findByField(field, value) {
     logger.debug('Finding', field, value)
 
-    const results = await this.query().where(field, value)
+    if (value === undefined) {
+      return this.query().where(field)
+    }
 
-    return results
+    return this.query().where(field, value)
   }
 
   static async findOneByField(field, value) {
@@ -209,7 +195,7 @@ class BaseModel extends Model {
       .where(field, value)
       .limit(1)
     if (!results.length) {
-      throw notFoundError(field, value, this.constructor.name)
+      throw notFoundError(field, value, this.name)
     }
 
     return results[0]
@@ -220,5 +206,5 @@ class BaseModel extends Model {
   }
 }
 
-BaseModel.pickJsonSchemaProperties = true
+BaseModel.pickJsonSchemaProperties = false
 module.exports = BaseModel
diff --git a/packages/base-model/test/data-model-component/src/manuscript.js b/packages/base-model/test/data-model-component/src/manuscript.js
index bb1b61f66544bb4572442bea31f97852b3e2ada1..76c741882d920850c9d9d507dc140ffd5a58af22 100644
--- a/packages/base-model/test/data-model-component/src/manuscript.js
+++ b/packages/base-model/test/data-model-component/src/manuscript.js
@@ -1,5 +1,4 @@
 const BaseModel = require('../../../src')
-const { Team } = require('pubsweet-server')
 
 class Manuscript extends BaseModel {
   static get tableName() {
@@ -22,10 +21,13 @@ class Manuscript extends BaseModel {
           items: { type: 'string', format: 'uuid' },
         },
       },
+      additionalProperties: false,
     }
   }
 
   async $beforeDelete() {
+    const { model: Team } = require('@pubsweet/model-team')
+
     await Team.deleteAssociated(this.type, this.id)
   }
 }
diff --git a/packages/base-model/test/extended_manuscript_graphql_test.js b/packages/base-model/test/extended_manuscript_graphql_test.js
index 299131bc3e1c2ed68cb5c9ad5829a728988f36b6..02cd9a282f0bbd24e9fbe73ffaa68f34db16dde8 100644
--- a/packages/base-model/test/extended_manuscript_graphql_test.js
+++ b/packages/base-model/test/extended_manuscript_graphql_test.js
@@ -1,13 +1,21 @@
 const path = require('path')
 
 const pathToComponent = path.resolve(__dirname, 'extended-data-model-component')
-process.env.NODE_CONFIG = `{"pubsweet":{"components":["${pathToComponent}"]}}`
+process.env.NODE_CONFIG = `{"pubsweet":{
+  "components":[
+    "@pubsweet/model-user",
+    "@pubsweet/model-team",
+    "@pubsweet/model-fragment",
+    "@pubsweet/model-collection",
+    "${pathToComponent}"
+  ]
+}}`
 global.NODE_CONFIG = null
 delete require.cache[require.resolve('config')]
 const { model: Manuscript } = require('./extended-data-model-component')
-const { User } = require('pubsweet-server')
+const { model: User } = require('@pubsweet/model-user')
 const fixtures = require('pubsweet-server/test/fixtures/fixtures')
-const authentication = require('pubsweet-server/src/authentication')
+const authentication = require('@pubsweet/model-user/src/authentication')
 
 const { dbCleaner, api } = require('pubsweet-server/test')
 
diff --git a/packages/base-model/test/extended_manuscript_test.js b/packages/base-model/test/extended_manuscript_test.js
index 588b69f5306df9c022d8627caed60eab216ee0c0..f27968767146cfc1512061670bb9634b639e0fea 100644
--- a/packages/base-model/test/extended_manuscript_test.js
+++ b/packages/base-model/test/extended_manuscript_test.js
@@ -1,7 +1,15 @@
 const path = require('path')
 
 const pathToComponent = path.resolve(__dirname, 'extended-data-model-component')
-process.env.NODE_CONFIG = `{"pubsweet":{"components":["${pathToComponent}"]}}`
+process.env.NODE_CONFIG = `{"pubsweet":{
+  "components":[
+    "@pubsweet/model-user",
+    "@pubsweet/model-team",
+    "@pubsweet/model-fragment",
+    "@pubsweet/model-collection",
+    "${pathToComponent}"
+  ]
+}}`
 
 const { model: Manuscript } = require('./extended-data-model-component')
 const { dbCleaner } = require('pubsweet-server/test')
diff --git a/packages/base-model/test/manuscript_graphql_test.js b/packages/base-model/test/manuscript_graphql_test.js
index 2283af2a2c19bbfe124543aad9ded763b8f84132..acf9930b47668f510041e5077edf3983a4338baa 100644
--- a/packages/base-model/test/manuscript_graphql_test.js
+++ b/packages/base-model/test/manuscript_graphql_test.js
@@ -1,13 +1,19 @@
 const path = require('path')
 
 const pathToComponent = path.resolve(__dirname, 'data-model-component')
-process.env.NODE_CONFIG = `{"pubsweet":{"components":["${pathToComponent}"]}}`
-
-const { User } = require('pubsweet-server')
+process.env.NODE_CONFIG = `{"pubsweet":{
+  "components":[
+    "@pubsweet/model-user",
+    "@pubsweet/model-team",
+    "@pubsweet/model-fragment",
+    "${pathToComponent}"
+  ]
+}}`
+const { model: User } = require('@pubsweet/model-user')
 const { dbCleaner, api } = require('pubsweet-server/test')
 
 const fixtures = require('pubsweet-server/test/fixtures/fixtures')
-const authentication = require('pubsweet-server/src/authentication')
+const authentication = require('@pubsweet/model-user/src/authentication')
 
 const { model: Manuscript } = require('./data-model-component')
 
diff --git a/packages/base-model/test/manuscript_test.js b/packages/base-model/test/manuscript_test.js
index 7c16255fbdc9d92b835a91dc79bfde5a65aa0054..903d4e1c0fe226c603215433416ca350039585a4 100644
--- a/packages/base-model/test/manuscript_test.js
+++ b/packages/base-model/test/manuscript_test.js
@@ -1,7 +1,14 @@
 const path = require('path')
 
 const pathToComponent = path.resolve(__dirname, 'data-model-component')
-process.env.NODE_CONFIG = `{"pubsweet":{"components":["${pathToComponent}"]}}`
+process.env.NODE_CONFIG = `{"pubsweet":{
+  "components":[
+    "@pubsweet/model-user",
+    "@pubsweet/model-team",
+    "@pubsweet/model-fragment",
+    "${pathToComponent}"
+  ]
+}}`
 
 const { model: Manuscript } = require('./data-model-component')
 const { dbCleaner } = require('pubsweet-server/test')
@@ -11,11 +18,11 @@ describe('Manuscript', () => {
     await dbCleaner()
   })
 
-  it('has upated set when created', async () => {
+  it('has updated set when created', async () => {
     const manuscript = await new Manuscript({ title: 'Test' }).save()
     expect(manuscript.title).toEqual('Test')
     const now = new Date().toISOString()
-    expect(manuscript.updated).toHaveLength(now.length)
+    expect(manuscript.updated.toISOString()).toHaveLength(now.length)
   })
 
   it('can be saved and found and deleted', async () => {
@@ -57,18 +64,7 @@ describe('Manuscript', () => {
     }
 
     await expect(createNonValidManuscript()).rejects.toThrow(
-      "mumbo is not a property in Manuscript's schema",
-    )
-  })
-
-  it('throws if an unknown column is assigned', async () => {
-    function createNonValidManuscript() {
-      const manuscript = new Manuscript()
-      manuscript.titldee = 'x'
-    }
-
-    expect(createNonValidManuscript).toThrow(
-      "titldee is not a property in Manuscript's schema",
+      'mumbo: is an invalid additional property',
     )
   })
 
diff --git a/packages/cli/cli/new.js b/packages/cli/cli/new.js
index 809362fc699c1eebb91648cf92c0cad37738de58..d7fb5d53c295ea1ae06941fa77875119423bc7d2 100644
--- a/packages/cli/cli/new.js
+++ b/packages/cli/cli/new.js
@@ -2,10 +2,9 @@ const logger = require('@pubsweet/logger')
 const colors = require('colors/safe')
 const program = require('commander')
 const fs = require('fs-extra')
-const { spawnSync } = require('child_process')
+const { execSync } = require('child_process')
 const path = require('path')
 const crypto = require('crypto')
-const { STARTER_REPO_URL } = require('../src/constants')
 
 const readCommand = async argsOverride => {
   program
@@ -45,13 +44,14 @@ module.exports = async argsOverride => {
     overWrite(appPath)
   }
 
-  spawnSync('git', ['clone', STARTER_REPO_URL, appName], { stdio: 'inherit' })
+  execSync(
+    `git clone https://gitlab.coko.foundation/pubsweet/pubsweet-starter.git --branch release ${appName}`,
+    { stdio: 'inherit' },
+  )
 
   logger.info('Installing app dependencies')
 
-  // TODO: There is an error when using local yarn. Fix it.
-  // const localYarn = path.join(__dirname, '..', 'node_modules', '.bin', 'yarn')
-  spawnSync('yarn', ['install'], {
+  execSync('yarn install', {
     cwd: appPath,
     stdio: 'inherit',
   })
@@ -61,6 +61,5 @@ module.exports = async argsOverride => {
   const secret = crypto.randomBytes(64).toString('hex')
   fs.writeJsonSync(configFilePath, { 'pubsweet-server': { secret } })
   logger.info(`Added secret to ${configFilePath} under pubsweet-server.secret`)
-
   logger.info('Finished generating initial app')
 }
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 706d359abb01abd619f849be63c1bd4b4e79c9ae..a8e123c6e44a41c83c7581ec9ecbd8a6533ff4ad 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -35,7 +35,6 @@
     "url": "https://gitlab.coko.foundation/pubsweet/pubsweet"
   },
   "devDependencies": {
-    "@pubsweet/starter": "git+https://gitlab.coko.foundation/pubsweet/pubsweet-starter.git",
     "jest": "^23.5.0",
     "jest-environment-db": "^2.0.0",
     "nsp": "^2.8.1"
diff --git a/packages/cli/src/constants.js b/packages/cli/src/constants.js
deleted file mode 100644
index e4e410e18e1b46cd5b110ba5f161dfe9e6fd17cd..0000000000000000000000000000000000000000
--- a/packages/cli/src/constants.js
+++ /dev/null
@@ -1,4 +0,0 @@
-module.exports = {
-  STARTER_REPO_URL:
-    'https://gitlab.coko.foundation/pubsweet/pubsweet-starter.git',
-}
diff --git a/packages/cli/test/cli/add.test.js b/packages/cli/test/cli/add.test.js
index bfae775ecf67d047b467893953b407530243f495..2e70367ce0ff76f061e7587730cb00335e1833e9 100644
--- a/packages/cli/test/cli/add.test.js
+++ b/packages/cli/test/cli/add.test.js
@@ -1,40 +1,34 @@
 jest.mock('child_process', () => ({ spawnSync: jest.fn() }))
+
 jest.mock('fs-extra', () => {
   const fs = require.requireActual('fs-extra')
   fs.writeJsonSync = jest.fn()
   fs.ensureFileSync = jest.fn()
   return fs
 })
+
 jest.mock('../../src/package-management/helpers/', () => {
   const helpers = require.requireActual('../../src/package-management/helpers/')
   helpers.getDepsFromPackageJson = jest.fn()
   return helpers
 })
 
-const path = require('path')
+const spawnSpy = require('child_process').spawnSync
+
 const fs = require('fs-extra')
 const { getMockArgv } = require('../helpers/')
 const runAdd = require('../../cli/add')
 
-const spawnSpy = require('child_process').spawnSync
 const readPkgSpy = require('../../src/package-management/helpers/')
   .getDepsFromPackageJson
 
 const writeSpy = fs.writeJsonSync
 
 describe('add', () => {
-  beforeAll(() => {
-    process.chdir(path.dirname(require.resolve('@pubsweet/starter')))
-  })
-
   beforeEach(() => {
     jest.clearAllMocks()
   })
 
-  afterAll(() => {
-    process.chdir(path.join(__dirname, '..', '..'))
-  })
-
   it('requires a component', async () => {
     await expect(runAdd(getMockArgv(''))).rejects.toBeInstanceOf(Error)
   })
diff --git a/packages/cli/test/cli/remove.test.js b/packages/cli/test/cli/remove.test.js
index 4807ef653042e112bf0cd0d1076026e4816eb060..a571ecf0e26a42c5b38b27aaef807af12ecbbd49 100644
--- a/packages/cli/test/cli/remove.test.js
+++ b/packages/cli/test/cli/remove.test.js
@@ -11,7 +11,6 @@ jest.mock('../../src/package-management/helpers/', () => {
   return helpers
 })
 
-const path = require('path')
 const fs = require('fs-extra')
 const { getMockArgv } = require('../helpers/')
 const runRemove = require('../../cli/remove')
@@ -22,19 +21,11 @@ const readPkgSpy = require('../../src/package-management/helpers/')
 
 const writeSpy = fs.writeJsonSync
 
-describe.skip('remove', () => {
-  beforeAll(() => {
-    process.chdir(path.dirname(require.resolve('@pubsweet/starter')))
-  })
-
+describe('remove', () => {
   beforeEach(() => {
     jest.clearAllMocks()
   })
 
-  afterAll(() => {
-    process.chdir(path.join(__dirname, '..', '..'))
-  })
-
   it('requires a component', async () => {
     await expect(runRemove(getMockArgv(''))).rejects.toBeInstanceOf(Error)
   })
diff --git a/packages/cli/test/integration.test.js b/packages/cli/test/integration.test.js
index 179384fc9af64c18726127dde2eafdb81e062785..75af795f18053138d00c5e08aad00357631b8c7f 100644
--- a/packages/cli/test/integration.test.js
+++ b/packages/cli/test/integration.test.js
@@ -20,7 +20,7 @@ const nodeConfig = {
     mode: 'authsome/src/modes/blog',
   },
   pubsweet: {
-    components: [],
+    components: ['@pubsweet/model-user'],
   },
   'pubsweet-client': {
     theme: 'PepperTheme',
@@ -36,7 +36,7 @@ const setupDbOptions = {
 /* These tests run "pubsweet" commands as child processes with no mocking */
 /* They perform a full installation cycle, including multiple yarn commands */
 
-describe('CLI: integration test', () => {
+describe.skip('CLI: integration test', () => {
   afterAll(() => fs.removeSync(appPath))
 
   describe('new', () => {
diff --git a/packages/components/FormGroup/FormGroup.jsx b/packages/components/FormGroup/FormGroup.jsx
deleted file mode 100644
index e0aff75f0d3ce37e996017c92976b09c278507d0..0000000000000000000000000000000000000000
--- a/packages/components/FormGroup/FormGroup.jsx
+++ /dev/null
@@ -1,84 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-import {
-  FormGroup as BootstrapFormGroup,
-  ControlLabel,
-  FormControl,
-  HelpBlock,
-} from 'react-bootstrap'
-import Joi from 'joi-browser'
-
-import mergeValidations from 'pubsweet-server/src/models/validations'
-import config from 'config'
-
-const appValidationsPath = config.validations
-const validations = mergeValidations(require(appValidationsPath))
-
-class FormGroup extends React.Component {
-  constructor(props) {
-    super(props)
-    this.state = {
-      startedEditing: false,
-    }
-  }
-
-  validateState(state) {
-    if (!state.startedEditing) return state
-
-    const [model, property] = this.props.modelProperty.split('.')
-    const validation = Joi.reach(validations[model], property)
-
-    const result = validation.label(this.props.label).validate(state.value)
-    if (result.error) {
-      return {
-        ...state,
-        validation: 'error',
-        validationMessage: result.error.message,
-      }
-    }
-
-    return {
-      ...state,
-      validation: 'success',
-      validationMessage: '',
-    }
-  }
-
-  handleChange(e) {
-    const validatedState = this.validateState({
-      startedEditing: true,
-      value: e.target.value,
-    })
-    this.setState(validatedState)
-  }
-
-  render() {
-    return (
-      <BootstrapFormGroup
-        controlId={this.props.controlId}
-        validationState={this.state.validation}
-      >
-        <ControlLabel>{this.props.label}</ControlLabel>
-        <FormControl
-          inputRef={this.props.inputRef}
-          onChange={e => this.handleChange(e)}
-          placeholder={this.props.placeholder}
-          type="text"
-          value={this.state.value}
-        />
-        <FormControl.Feedback />
-        <HelpBlock>{this.state.validationMessage}</HelpBlock>
-      </BootstrapFormGroup>
-    )
-  }
-}
-
-FormGroup.propTypes = {
-  controlId: PropTypes.string.isRequired,
-  label: PropTypes.string,
-  placeholder: PropTypes.string,
-  modelProperty: PropTypes.string,
-  inputRef: PropTypes.func,
-}
-
-export default FormGroup
diff --git a/packages/components/FormGroup/FormGroup.test.jsx b/packages/components/FormGroup/FormGroup.test.jsx
deleted file mode 100644
index b0cdd538000c2bddd52c43775a1d8eb7eafb8b4e..0000000000000000000000000000000000000000
--- a/packages/components/FormGroup/FormGroup.test.jsx
+++ /dev/null
@@ -1,39 +0,0 @@
-import { shallow } from 'enzyme'
-import React from 'react'
-
-import FormGroup from './FormGroup'
-
-// otherwise you end up with two different Jois and it goes wrong
-jest.mock('joi-browser', () => require('joi'))
-
-describe('<FormGroup/>', () => {
-  const makeWrapper = (props = {}) =>
-    shallow(
-      <FormGroup
-        controlId=""
-        label="Testing"
-        modelProperty="team.name"
-        {...props}
-      />,
-    )
-
-  it('shows error on invalid input', () => {
-    const wrapper = makeWrapper({})
-    wrapper.find('FormControl').simulate('change', { target: { value: 123 } })
-    expect(wrapper.state()).toMatchObject({
-      validation: 'error',
-    })
-    expect(wrapper.html()).toContain('must be a string')
-  })
-
-  it('no error good input', () => {
-    const wrapper = makeWrapper({})
-    wrapper
-      .find('FormControl')
-      .simulate('change', { target: { value: 'something' } })
-    expect(wrapper.state()).toMatchObject({
-      validation: 'success',
-    })
-    expect(wrapper.html()).not.toContain('must be a string')
-  })
-})
diff --git a/packages/components/FormGroup/README.md b/packages/components/FormGroup/README.md
deleted file mode 100644
index 38ae6bb80688ffec9e2217f7f43b3e353a34e751..0000000000000000000000000000000000000000
--- a/packages/components/FormGroup/README.md
+++ /dev/null
@@ -1,38 +0,0 @@
-# PubSweet component - Form Group
-
-> This is a text input component (with validations) meant to be used in PubSweet applications.
-
-## Install
-
-```bash
-npm install pubsweet-component-form-group
-```
-
-## Usage
-
-In a PubSweet application, you can use this text input field like so:
-
-```
-<div>
-    <h3>Create a new blog post</h3>
-    <PubSweetFormGroup
-      controlId='fragment.title'
-      label='Title'
-      placeholder='One fine day...'
-      modelProperty='fragment.title'
-      inputRef={(input) => { this.title = input }}
-    />
-</div>
-```
-
-The `modelProperty` tells the component which model and property this input field is describing, for example, `user.name` or `collection.title`. Validations for the input field will then be based on the validations for that specific model and property (defined in `pubsweet-server` and your configuration).
-
-You can then get the value like so:
-
-```js static
-var title = ReactDOM.findDOMNode(this.title).value
-```
-
-## License
-
-MIT
diff --git a/packages/components/FormGroup/index.js b/packages/components/FormGroup/index.js
deleted file mode 100644
index 40282d99663c419e2ca62e038f520bba77335dba..0000000000000000000000000000000000000000
--- a/packages/components/FormGroup/index.js
+++ /dev/null
@@ -1,5 +0,0 @@
-module.exports = {
-  frontend: {
-    components: [require('./FormGroup')],
-  },
-}
diff --git a/packages/components/PasswordReset-server/PasswordResetBackend.js b/packages/components/PasswordReset-server/PasswordResetBackend.js
index a0fd309c2821b4427fc459129e4d39820c10c816..148adc3785b3988377317bb6a6bea3fe9fc66f40 100644
--- a/packages/components/PasswordReset-server/PasswordResetBackend.js
+++ b/packages/components/PasswordReset-server/PasswordResetBackend.js
@@ -69,7 +69,7 @@ const PasswordResetBackend = app => {
         user.passwordResetToken = crypto
           .randomBytes(config.get('password-reset.token-length'))
           .toString('hex')
-        user.passwordResetTimestamp = Number(moment())
+        user.passwordResetTimestamp = moment().format()
 
         await user.save()
 
diff --git a/packages/components/model-blog/.babelrc b/packages/components/model-blog/.babelrc
new file mode 100644
index 0000000000000000000000000000000000000000..d3f11333a6b54b34a93878af4b902fcf1bf6d45f
--- /dev/null
+++ b/packages/components/model-blog/.babelrc
@@ -0,0 +1,12 @@
+{
+  "presets": [
+    [
+      "env",
+      {
+        "targets": {
+          "node": "current"
+        }
+      }
+    ]
+  ]
+}
\ No newline at end of file
diff --git a/packages/components/model-blog/README.md b/packages/components/model-blog/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..a60b14a894a0af7fdb095337c67d6136394d7709
--- /dev/null
+++ b/packages/components/model-blog/README.md
@@ -0,0 +1,3 @@
+# Blog model
+
+This is a skeleton/testing model, used in pubsweet-starter and tests in pubsweet-server. It extends the Collection model.
diff --git a/packages/components/model-blog/package.json b/packages/components/model-blog/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..604daf28798c4630faf890555c26d846d0ee0a90
--- /dev/null
+++ b/packages/components/model-blog/package.json
@@ -0,0 +1,18 @@
+
+{
+  "name": "@pubsweet/model-blog",
+  "version": "0.0.1",
+  "description": "Testing model for pubsweet-starter and pubsweet-server",
+  "main": "src/index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "author": "Adam Hyde",
+  "license": "MIT",
+  "dependencies": {
+    "@pubsweet/model-collection": "^1.0.0"
+  },
+  "publishConfig": {
+    "access": "public"
+  }
+}
\ No newline at end of file
diff --git a/packages/components/model-blog/src/blog.js b/packages/components/model-blog/src/blog.js
new file mode 100644
index 0000000000000000000000000000000000000000..b6868d99e28472704a7026a9b3b4cd1876401db3
--- /dev/null
+++ b/packages/components/model-blog/src/blog.js
@@ -0,0 +1,17 @@
+const { model: Collection } = require('@pubsweet/model-collection')
+
+class Blog extends Collection {
+  static get schema() {
+    return {
+      properties: {
+        title: { type: 'string' },
+        nonPublicProperty: { type: ['string', 'null'] },
+        published: { type: ['boolean', 'null'] },
+        filtered: { type: ['string', 'null'] },
+      },
+    }
+  }
+}
+
+Blog.type = 'collection'
+module.exports = Blog
diff --git a/packages/components/model-blog/src/graphql/index.js b/packages/components/model-blog/src/graphql/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..a5fddde0a94d542d8dd7fa5499b1ecc767f4802c
--- /dev/null
+++ b/packages/components/model-blog/src/graphql/index.js
@@ -0,0 +1,17 @@
+const typeDefs = `
+  extend type Collection {
+    non_public_property: String
+    title: String
+    published: Boolean
+    filtered: String
+  }
+
+  extend input CollectionInput {
+    non_public_property: String
+    title: String
+    published: Boolean
+    filtered: String
+  }
+`
+
+module.exports = { typeDefs }
diff --git a/packages/components/model-blog/src/index.js b/packages/components/model-blog/src/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..6ca34281b2b93571a276c09b7dafa767e893d02b
--- /dev/null
+++ b/packages/components/model-blog/src/index.js
@@ -0,0 +1,6 @@
+module.exports = {
+  ...require('./graphql'),
+  modelName: 'Collection',
+  model: require('./blog'),
+  extending: '@pubsweet/model-collection',
+}
diff --git a/packages/components/model-blog/src/migrations/1543621635-add-columns-to-collections.sql b/packages/components/model-blog/src/migrations/1543621635-add-columns-to-collections.sql
new file mode 100644
index 0000000000000000000000000000000000000000..2bbbe4525f78884bc64103ac94405ed9797d4ae0
--- /dev/null
+++ b/packages/components/model-blog/src/migrations/1543621635-add-columns-to-collections.sql
@@ -0,0 +1,5 @@
+ALTER TABLE collections
+ADD COLUMN title TEXT,
+ADD COLUMN published BOOLEAN,
+ADD COLUMN filtered TEXT,
+ADD COLUMN non_public_property TEXT;
\ No newline at end of file
diff --git a/packages/components/model-blogpost/.babelrc b/packages/components/model-blogpost/.babelrc
new file mode 100644
index 0000000000000000000000000000000000000000..d3f11333a6b54b34a93878af4b902fcf1bf6d45f
--- /dev/null
+++ b/packages/components/model-blogpost/.babelrc
@@ -0,0 +1,12 @@
+{
+  "presets": [
+    [
+      "env",
+      {
+        "targets": {
+          "node": "current"
+        }
+      }
+    ]
+  ]
+}
\ No newline at end of file
diff --git a/packages/components/model-blogpost/README.md b/packages/components/model-blogpost/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..c2540bf4a3def2f62a0a070e315e6ee5c2e6b08c
--- /dev/null
+++ b/packages/components/model-blogpost/README.md
@@ -0,0 +1,3 @@
+# Blogpost model
+
+This is a skeleton/testing model, used in pubsweet-starter and tests in pubsweet-server. It extends the Fragment model.
diff --git a/packages/components/model-blogpost/package.json b/packages/components/model-blogpost/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..86a484b0f22f8db8f40eb64165db002dbc79e632
--- /dev/null
+++ b/packages/components/model-blogpost/package.json
@@ -0,0 +1,18 @@
+
+{
+  "name": "@pubsweet/model-blogpost",
+  "version": "0.0.1",
+  "description": "",
+  "main": "src/index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "author": "Adam Hyde",
+  "license": "MIT",
+  "dependencies": {
+    "@pubsweet/model-fragment": "^1.0.0"
+  },
+  "publishConfig": {
+    "access": "public"
+  }
+}
\ No newline at end of file
diff --git a/packages/components/model-blogpost/src/blogpost.js b/packages/components/model-blogpost/src/blogpost.js
new file mode 100644
index 0000000000000000000000000000000000000000..5fee39290ca2b549aa679f86ba84fde8339c90ff
--- /dev/null
+++ b/packages/components/model-blogpost/src/blogpost.js
@@ -0,0 +1,20 @@
+const { model: Fragment } = require('@pubsweet/model-fragment')
+
+class Blogpost extends Fragment {
+  static get schema() {
+    return {
+      properties: {
+        fragmentType: { type: 'string', const: 'blogpost' },
+        source: { type: 'string' },
+        kind: { type: ['string', 'null'] },
+        title: { type: 'string' },
+        presentation: { type: 'string' },
+        published: { type: ['boolean', 'null'] },
+        filtered: { type: ['string', 'null'] },
+      },
+    }
+  }
+}
+
+Blogpost.type = 'fragment'
+module.exports = Blogpost
diff --git a/packages/components/model-blogpost/src/graphql/index.js b/packages/components/model-blogpost/src/graphql/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..a3688ffdd414b6c46c9a474979a82c56adbf430d
--- /dev/null
+++ b/packages/components/model-blogpost/src/graphql/index.js
@@ -0,0 +1,23 @@
+const typeDefs = `
+  extend type Fragment {
+    source: String
+    kind: String
+    title: String
+    presentation: String
+    published: Boolean
+    filtered: String
+    path: String
+  }
+
+  extend input FragmentInput {
+    source: String
+    kind: String
+    title: String
+    presentation: String
+    published: Boolean
+    filtered: String
+    path: String
+  }
+`
+
+module.exports = { typeDefs }
diff --git a/packages/components/model-blogpost/src/index.js b/packages/components/model-blogpost/src/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..3b6903cdc93a0927d8326bf36125d99b302f2f37
--- /dev/null
+++ b/packages/components/model-blogpost/src/index.js
@@ -0,0 +1,6 @@
+module.exports = {
+  ...require('./graphql'),
+  modelName: 'Fragment',
+  model: require('./blogpost'),
+  extending: '@pubsweet/model-fragment',
+}
diff --git a/packages/components/model-blogpost/src/migrations/1543166818-add-columns-to-fragment.sql b/packages/components/model-blogpost/src/migrations/1543166818-add-columns-to-fragment.sql
new file mode 100644
index 0000000000000000000000000000000000000000..439ec835d3cf79ffcb6c46d36966a88ad704fb4b
--- /dev/null
+++ b/packages/components/model-blogpost/src/migrations/1543166818-add-columns-to-fragment.sql
@@ -0,0 +1,7 @@
+ALTER TABLE fragments
+ADD COLUMN source TEXT,
+ADD COLUMN kind TEXT,
+ADD COLUMN title TEXT,
+ADD COLUMN presentation TEXT,
+ADD COLUMN published BOOLEAN,
+ADD COLUMN filtered TEXT;
\ No newline at end of file
diff --git a/packages/components/model-collection/.babelrc b/packages/components/model-collection/.babelrc
new file mode 100644
index 0000000000000000000000000000000000000000..d3f11333a6b54b34a93878af4b902fcf1bf6d45f
--- /dev/null
+++ b/packages/components/model-collection/.babelrc
@@ -0,0 +1,12 @@
+{
+  "presets": [
+    [
+      "env",
+      {
+        "targets": {
+          "node": "current"
+        }
+      }
+    ]
+  ]
+}
\ No newline at end of file
diff --git a/packages/components/model-collection/README.md b/packages/components/model-collection/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..64d0593e5c15be1b31cd18077461bde7de2f85f9
--- /dev/null
+++ b/packages/components/model-collection/README.md
@@ -0,0 +1 @@
+# Collection model
diff --git a/packages/components/model-collection/package.json b/packages/components/model-collection/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..d6f060e87e957590fcff1731565461a7a6cd250f
--- /dev/null
+++ b/packages/components/model-collection/package.json
@@ -0,0 +1,17 @@
+{
+  "name": "@pubsweet/model-collection",
+  "version": "1.0.0",
+  "description": "",
+  "main": "src/index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "author": "Adam Hyde",
+  "license": "MIT",
+  "dependencies": {
+    "@pubsweet/base-model": "^1.0.9-alpha.0"
+  },
+  "publishConfig": {
+    "access": "public"
+  }
+}
diff --git a/packages/components/model-collection/src/api_collections.js b/packages/components/model-collection/src/api_collections.js
new file mode 100644
index 0000000000000000000000000000000000000000..5858cf8455d075969eecb45395d5cef9dd41a71a
--- /dev/null
+++ b/packages/components/model-collection/src/api_collections.js
@@ -0,0 +1,173 @@
+const STATUS = require('http-status-codes')
+const sse = require('pubsweet-sse')
+const passport = require('passport')
+
+const {
+  util: {
+    createFilterFromQuery,
+    objectId,
+    buildChangeData,
+    fieldSelector,
+    getTeams,
+    applyPermissionFilter,
+  },
+} = require('pubsweet-server')
+
+const authBearer = passport.authenticate('bearer', { session: false })
+const authBearerAndPublic = passport.authenticate(['bearer', 'anonymous'], {
+  session: false,
+})
+
+const CollectionsAPI = app => {
+  const { Collection, Team } = require('pubsweet-server/src/models')
+  const { model: User } = require('@pubsweet/model-user')
+
+  // List collections
+  app.get('/api/collections', authBearerAndPublic, async (req, res, next) => {
+    try {
+      const collections = await Collection.all()
+      const filteredCollections = await applyPermissionFilter({
+        req,
+        target: req.route,
+        filterable: collections,
+      })
+
+      const collectionsWithSelectedFields = (await Promise.all(
+        filteredCollections.map(async collection => {
+          collection.owners = await User.ownersWithUsername(collection)
+          const properties = await applyPermissionFilter({
+            req,
+            target: collection,
+          })
+          return fieldSelector(req)(properties)
+        }),
+      )).filter(createFilterFromQuery(req.query))
+
+      res.status(STATUS.OK).json(collectionsWithSelectedFields)
+    } catch (err) {
+      next(err)
+    }
+  })
+
+  // Create a collection
+  app.post('/api/collections', authBearer, async (req, res, next) => {
+    try {
+      const properties = await applyPermissionFilter({
+        req,
+        target: req.route,
+        filterable: req.body,
+      })
+
+      const collection = new Collection(properties)
+      collection.setOwners([req.user])
+
+      await collection.save()
+
+      // TODO: filter the output?
+
+      res.status(STATUS.CREATED).json(collection)
+      sse.send({ action: 'collection:create', data: { collection } })
+    } catch (err) {
+      next(err)
+    }
+  })
+
+  // Retrieve a collection
+  app.get(
+    '/api/collections/:collectionId',
+    authBearerAndPublic,
+    async (req, res, next) => {
+      try {
+        const collection = await Collection.find(req.params.collectionId)
+        collection.owners = await User.ownersWithUsername(collection)
+        const properties = await applyPermissionFilter({
+          req,
+          target: collection,
+        })
+
+        res.status(STATUS.OK).json(properties)
+      } catch (err) {
+        next(err)
+      }
+    },
+  )
+
+  // Update a collection
+  app.patch(
+    '/api/collections/:collectionId',
+    authBearer,
+    async (req, res, next) => {
+      try {
+        const collection = await Collection.find(req.params.collectionId)
+        const currentAndUpdate = { current: collection, update: req.body }
+        const properties = await applyPermissionFilter({
+          req,
+          target: currentAndUpdate,
+          filterable: req.body,
+        })
+
+        await collection.updateProperties(properties)
+        await collection.save()
+
+        const update = buildChangeData(properties, collection)
+
+        res.status(STATUS.OK).json(update)
+        sse.send({
+          action: 'collection:patch',
+          data: { collection: objectId(collection), update },
+        })
+      } catch (err) {
+        next(err)
+      }
+    },
+  )
+
+  // Delete a collection
+  app.delete(
+    '/api/collections/:collectionId',
+    authBearer,
+    async (req, res, next) => {
+      try {
+        const collection = await Collection.find(req.params.collectionId)
+        const output = await applyPermissionFilter({ req, target: collection })
+
+        // TODO: filter the output, or return nothing?
+
+        await collection.delete()
+
+        res.status(STATUS.OK).json(output)
+        sse.send({
+          action: 'collection:delete',
+          data: { collection: objectId(collection) },
+        })
+      } catch (err) {
+        next(err)
+      }
+    },
+  )
+
+  // Retrieve teams for a collection
+  app.get(
+    '/api/collections/:collectionId/teams',
+    authBearerAndPublic,
+    async (req, res, next) => {
+      const collection = await Collection.find(req.params.collectionId)
+      await applyPermissionFilter({ req, target: collection })
+
+      try {
+        const teams = (await getTeams({
+          req,
+          Team,
+          id: collection.id,
+          type: 'collection',
+        })).filter(createFilterFromQuery(req.query))
+
+        res.status(STATUS.OK).json(teams)
+      } catch (err) {
+        next(err)
+      }
+    },
+  )
+}
+
+module.exports = CollectionsAPI
diff --git a/packages/server/src/models/Collection.js b/packages/components/model-collection/src/collection.js
similarity index 70%
rename from packages/server/src/models/Collection.js
rename to packages/components/model-collection/src/collection.js
index 8f4f1c1b72c5981c7e57bd595bc6a81963ff3775..ba4cee2f36b7025521b52a3f00064951f37234bc 100644
--- a/packages/server/src/models/Collection.js
+++ b/packages/components/model-collection/src/collection.js
@@ -1,9 +1,7 @@
-const Model = require('./Model')
-const Fragment = require('./Fragment')
-const Team = require('./Team')
+const BaseModel = require('@pubsweet/base-model')
 const without = require('lodash/without')
 
-class Collection extends Model {
+class Collection extends BaseModel {
   constructor(properties) {
     super(properties)
     this.type = 'collection'
@@ -11,14 +9,40 @@ class Collection extends Model {
     this.owners = this.owners || []
   }
 
+  static get tableName() {
+    return 'collections'
+  }
+
+  static get schema() {
+    return {
+      properties: {
+        fragments: {
+          type: ['array', 'null'],
+          items: { type: 'string', format: 'uuid' },
+        },
+        owners: {
+          type: ['array', 'null'],
+          items: { type: 'string', format: 'uuid' },
+        },
+      },
+    }
+  }
+
   async save() {
     this.fragments = this.fragments.map(fragment => fragment.id || fragment)
     return super.save()
   }
 
+  async delete() {
+    const { model: Team } = require('@pubsweet/model-team')
+    await Team.deleteAssociated(this.type, this.id)
+    return super.delete()
+  }
+
   // Gets fragments in a collection, supports filtering by function e.g.
   // collection.getFragments({filter: fragment => {Authorize.can(req.user, 'read', fragment)})
   getFragments(options) {
+    const { Fragment } = require('pubsweet-server/src/models')
     options = options || {}
     options.filter = options.filter || (() => Promise.resolve(true))
 
@@ -39,6 +63,7 @@ class Collection extends Model {
   }
 
   addFragment(fragment) {
+    const { Fragment } = require('pubsweet-server/src/models')
     this.fragments = this.fragments.map(fragment => {
       if (typeof fragment === 'object') {
         return fragment
@@ -58,12 +83,10 @@ class Collection extends Model {
     this.fragments = without(this.fragments, fragment.id)
   }
 
-  async delete() {
-    await Team.deleteAssociated(this.type, this.id)
-    return super.delete()
+  isOwner(userId) {
+    return Array.isArray(this.owners) && this.owners.includes(userId)
   }
 }
 
 Collection.type = 'collection'
-
 module.exports = Collection
diff --git a/packages/components/model-collection/src/graphql.js b/packages/components/model-collection/src/graphql.js
new file mode 100644
index 0000000000000000000000000000000000000000..5c76ff92b94efd9c3f8730419ba40071900bcaf2
--- /dev/null
+++ b/packages/components/model-collection/src/graphql.js
@@ -0,0 +1,58 @@
+const resolvers = {
+  Query: {
+    collection(_, { id }, ctx) {
+      return ctx.connectors.Collection.fetchOne(id, ctx)
+    },
+    collections(_, { id }, ctx) {
+      return ctx.connectors.Collection.fetchAll(ctx)
+    },
+  },
+  Mutation: {
+    deleteCollection(_, { id }, ctx) {
+      return ctx.connectors.Collection.delete(id, ctx)
+    },
+    createCollection(_, { input }, ctx) {
+      return ctx.connectors.Collection.create(input, ctx)
+    },
+    updateCollection(_, { id, input }, ctx) {
+      return ctx.connectors.Collection.update(id, input, ctx)
+    },
+  },
+  Collection: {
+    owners(collection, vars, ctx) {
+      return ctx.connectors.User.fetchSome(collection.owners, ctx)
+    },
+    fragments(collection, vars, ctx) {
+      return ctx.connectors.Fragment.fetchSome(collection.fragments, ctx)
+    },
+  },
+}
+
+const typeDefs = `
+  extend type Query {
+    collection(id: ID): Collection
+    collections: [Collection]
+  }
+
+  extend type Mutation {
+    createCollection(input: CollectionInput): Collection
+    deleteCollection(id: ID): Collection
+    updateCollection(id: ID, input: CollectionInput): Collection
+  }
+
+  type Collection {
+    id: ID!
+    rev: String
+    type: String!
+    owners: [User!]!
+    fragments: [Fragment!]!
+  }
+
+  input CollectionInput {
+    owners: [ID!]
+    fragments: [ID!]
+    rev: String
+  }
+`
+
+module.exports = { resolvers, typeDefs }
diff --git a/packages/components/model-collection/src/index.js b/packages/components/model-collection/src/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..56f3237dbf782b76739b68af978e41d66eafa4cc
--- /dev/null
+++ b/packages/components/model-collection/src/index.js
@@ -0,0 +1,6 @@
+module.exports = {
+  ...require('./graphql'),
+  modelName: 'Collection',
+  model: require('./collection'),
+  server: () => app => require('./api_collections')(app),
+}
diff --git a/packages/components/model-collection/src/migrations/1543621173-initial-collection-migration.sql b/packages/components/model-collection/src/migrations/1543621173-initial-collection-migration.sql
new file mode 100644
index 0000000000000000000000000000000000000000..3936d9a9e4ed78d2dd50606257cbe90e990c7265
--- /dev/null
+++ b/packages/components/model-collection/src/migrations/1543621173-initial-collection-migration.sql
@@ -0,0 +1,8 @@
+CREATE TABLE collections (
+  id UUID PRIMARY KEY,
+  created TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT current_timestamp,
+  updated TIMESTAMP WITH TIME ZONE,
+  fragments JSONB,
+  owners JSONB,
+  type TEXT NOT NULL
+);
\ No newline at end of file
diff --git a/packages/components/model-fragment/.babelrc b/packages/components/model-fragment/.babelrc
new file mode 100644
index 0000000000000000000000000000000000000000..d3f11333a6b54b34a93878af4b902fcf1bf6d45f
--- /dev/null
+++ b/packages/components/model-fragment/.babelrc
@@ -0,0 +1,12 @@
+{
+  "presets": [
+    [
+      "env",
+      {
+        "targets": {
+          "node": "current"
+        }
+      }
+    ]
+  ]
+}
\ No newline at end of file
diff --git a/packages/components/model-fragment/README.md b/packages/components/model-fragment/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..63bdc59663fef5ab7b42b49efced37327884c7e4
--- /dev/null
+++ b/packages/components/model-fragment/README.md
@@ -0,0 +1 @@
+# Fragment model
diff --git a/packages/components/model-fragment/package.json b/packages/components/model-fragment/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..7eada093afa5bf362b6c575189c13016915eeb13
--- /dev/null
+++ b/packages/components/model-fragment/package.json
@@ -0,0 +1,17 @@
+{
+  "name": "@pubsweet/model-fragment",
+  "version": "1.0.0",
+  "description": "",
+  "main": "src/index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "author": "Adam Hyde",
+  "license": "MIT",
+  "dependencies": {
+    "@pubsweet/base-model": "^1.0.9-alpha.0"
+  },
+  "publishConfig": {
+    "access": "public"
+  }
+}
diff --git a/packages/components/model-fragment/src/api_fragments.js b/packages/components/model-fragment/src/api_fragments.js
new file mode 100644
index 0000000000000000000000000000000000000000..86e2fd9c52112ee799bf27f19132d3986d0e1212
--- /dev/null
+++ b/packages/components/model-fragment/src/api_fragments.js
@@ -0,0 +1,377 @@
+const STATUS = require('http-status-codes')
+const sse = require('pubsweet-sse')
+const passport = require('passport')
+
+const {
+  util: {
+    objectId,
+    createFilterFromQuery,
+    buildChangeData,
+    fieldSelector,
+    authorizationError,
+    getTeams,
+    applyPermissionFilter,
+    getFragment,
+  },
+} = require('pubsweet-server')
+
+const authBearer = passport.authenticate('bearer', { session: false })
+const authBearerAndPublic = passport.authenticate(['bearer', 'anonymous'], {
+  session: false,
+})
+
+const FragmentsAPI = app => {
+  const authsome = require('pubsweet-server/src/helpers/authsome')
+
+  const { AuthorizationError } = require('pubsweet-server')
+
+  const { model: Team } = require('@pubsweet/model-team')
+  const { model: User } = require('@pubsweet/model-user')
+  const { Fragment, Collection } = require('pubsweet-server/src/models')
+
+  // Create a fragment and update the collection with the fragment
+  app.post(
+    '/api/collections/:collectionId/fragments',
+    authBearer,
+    async (req, res, next) => {
+      try {
+        const collection = await Collection.find(req.params.collectionId)
+
+        const object = {
+          path: req.route.path,
+          collection,
+          fragment: req.body,
+        }
+
+        const filteredProperties = await applyPermissionFilter({
+          req,
+          target: object,
+          filterable: req.body,
+        })
+
+        const fragment = new Fragment(filteredProperties)
+
+        fragment.setOwners([req.user])
+        await fragment.save()
+
+        collection.addFragment(fragment)
+        await collection.save()
+
+        fragment.owners = await User.ownersWithUsername(fragment)
+
+        res.status(STATUS.CREATED).json(fragment)
+        sse.send({
+          action: 'fragment:create',
+          data: { collection: objectId(collection), fragment },
+        })
+      } catch (err) {
+        next(err)
+      }
+    },
+  )
+
+  // Get all fragments
+  app.get(
+    '/api/collections/:collectionId/fragments',
+    authBearerAndPublic,
+    async (req, res, next) => {
+      try {
+        const collection = await Collection.find(req.params.collectionId)
+        let fragments = await collection.getFragments()
+
+        // Filter fragments and their properties
+        fragments = await Promise.all(
+          fragments.map(async fragment => {
+            try {
+              return await applyPermissionFilter({ req, target: fragment })
+            } catch (e) {
+              if (e instanceof AuthorizationError) {
+                return undefined
+              }
+
+              throw e
+            }
+          }),
+        )
+
+        fragments = fragments
+          .filter(fragment => fragment !== undefined)
+          .filter(createFilterFromQuery(req.query))
+
+        // Decorate owners with usernames
+        await Promise.all(
+          fragments.map(async fragment => {
+            fragment.owners = await User.ownersWithUsername(fragment)
+          }),
+        )
+
+        fragments = fragments.map(fieldSelector(req))
+
+        res.status(STATUS.OK).json(fragments)
+      } catch (err) {
+        next(err)
+      }
+    },
+  )
+
+  // Retrieve a fragment
+  app.get(
+    '/api/collections/:collectionId/fragments/:fragmentId',
+    authBearerAndPublic,
+    async (req, res, next) => {
+      try {
+        const fragment = await getFragment({ req, Collection, Fragment })
+        fragment.owners = await User.ownersWithUsername(fragment)
+        const properties = await applyPermissionFilter({
+          req,
+          target: fragment,
+        })
+
+        res.status(STATUS.OK).json(properties)
+      } catch (err) {
+        res.status(STATUS.NOT_FOUND).json(err.message)
+      }
+    },
+  )
+
+  // Update a fragment
+  app.patch(
+    '/api/collections/:collectionId/fragments/:fragmentId',
+    authBearer,
+    async (req, res, next) => {
+      try {
+        const fragment = await getFragment({ req, Collection, Fragment })
+        const currentAndUpdate = { current: fragment, update: req.body }
+        const properties = await applyPermissionFilter({
+          req,
+          target: currentAndUpdate,
+          filterable: req.body,
+        })
+
+        await fragment.updateProperties(properties)
+        await fragment.save()
+        fragment.owners = await User.ownersWithUsername(fragment)
+
+        const update = buildChangeData(properties, fragment)
+
+        res.status(STATUS.OK).json(update)
+        sse.send({
+          action: 'fragment:patch',
+          data: { fragment: objectId(fragment), update },
+        })
+      } catch (err) {
+        next(err)
+      }
+    },
+  )
+
+  // Delete a fragment
+  app.delete(
+    '/api/collections/:collectionId/fragments/:fragmentId',
+    authBearer,
+    async (req, res, next) => {
+      try {
+        const collection = await Collection.find(req.params.collectionId)
+        const fragment = await getFragment({ req, Collection, Fragment })
+        await applyPermissionFilter({ req, target: fragment })
+
+        await fragment.delete()
+        collection.removeFragment(fragment)
+        await collection.save()
+
+        res.status(STATUS.OK).json(fragment)
+        sse.send({
+          action: 'fragment:delete',
+          data: { collection: objectId(collection), fragment },
+        })
+      } catch (err) {
+        next(err)
+      }
+    },
+  )
+
+  // Retrieve teams for a fragment
+  app.get(
+    '/api/collections/:collectionId/fragments/:fragmentId/teams',
+    authBearerAndPublic,
+    async (req, res, next) => {
+      try {
+        const fragment = await getFragment({ req, Collection, Fragment })
+        await applyPermissionFilter({ req, target: fragment })
+
+        const teams = (await getTeams({
+          req,
+          Team,
+          id: fragment.id,
+          type: 'fragment',
+        })).filter(createFilterFromQuery(req.query))
+
+        res.status(STATUS.OK).json(teams)
+      } catch (err) {
+        next(err)
+      }
+    },
+  )
+
+  // Get all fragments
+  app.get('/api/fragments', authBearerAndPublic, async (req, res, next) => {
+    try {
+      const fragments = (await Fragment.all()).filter(
+        createFilterFromQuery(req.query),
+      )
+
+      // Filter fragments and their properties
+      const propertyFilter = fieldSelector(req)
+      const filteredFragments = await Promise.all(
+        fragments.map(async fragment => {
+          try {
+            return await applyPermissionFilter({
+              req,
+              target: propertyFilter(fragment),
+            })
+          } catch (e) {
+            if (e instanceof AuthorizationError) {
+              return undefined
+            }
+
+            throw e
+          }
+        }),
+      )
+
+      res.status(STATUS.OK).json(filteredFragments)
+    } catch (err) {
+      next(err)
+    }
+  })
+
+  app.post('/api/fragments', authBearer, async (req, res, next) => {
+    try {
+      const permission = await authsome.can(req.user, req.method, {
+        path: req.route.path,
+        fragment: req.body,
+      })
+
+      if (!permission) {
+        throw authorizationError(req.user, req.method, req.body)
+      }
+
+      if (permission.filter) {
+        req.body = permission.filter(req.body)
+      }
+
+      let fragment = new Fragment(req.body)
+
+      fragment.setOwners([req.user])
+      fragment = await fragment.save()
+
+      // How to address this?
+      fragment.owners = await User.ownersWithUsername(fragment)
+
+      res.status(STATUS.CREATED).json(fragment)
+      sse.send({ action: 'fragment:create', data: { fragment } })
+    } catch (err) {
+      next(err)
+    }
+  })
+
+  // Retrieve a fragment
+  app.get(
+    '/api/fragments/:fragmentId',
+    authBearerAndPublic,
+    async (req, res, next) => {
+      try {
+        let fragment = await Fragment.find(req.params.fragmentId)
+        const permission = await authsome.can(req.user, req.method, fragment)
+
+        if (!permission) {
+          throw authorizationError(req.user, req.method, fragment)
+        }
+
+        if (permission.filter) {
+          fragment = permission.filter(fragment)
+        }
+        res.status(STATUS.OK).json(fragment)
+      } catch (err) {
+        next(err)
+      }
+    },
+  )
+
+  // Update a fragment
+  app.patch(
+    '/api/fragments/:fragmentId',
+    authBearer,
+    async (req, res, next) => {
+      try {
+        let fragment = await Fragment.find(req.params.fragmentId)
+
+        const currentAndUpdate = { current: fragment, update: req.body }
+        const properties = await applyPermissionFilter({
+          req,
+          target: currentAndUpdate,
+          filterable: req.body,
+        })
+
+        fragment.updateProperties(properties)
+        fragment = await fragment.save()
+        fragment.owners = await User.ownersWithUsername(fragment)
+
+        const update = buildChangeData(properties, fragment)
+        res.status(STATUS.OK).json(update)
+
+        sse.send({
+          action: 'fragment:patch',
+          data: { fragment: objectId(fragment), update },
+        })
+      } catch (err) {
+        next(err)
+      }
+    },
+  )
+
+  // Delete a fragment
+  app.delete(
+    '/api/fragments/:fragmentId',
+    authBearer,
+    async (req, res, next) => {
+      try {
+        let fragment = await Fragment.find(req.params.fragmentId)
+        const permission = await authsome.can(req.user, req.method, fragment)
+
+        if (!permission) {
+          throw authorizationError(req.user, req.method, fragment)
+        }
+
+        fragment = await fragment.delete()
+
+        res.status(STATUS.OK).json(fragment)
+        sse.send({ action: 'fragment:delete', data: { fragment } })
+      } catch (err) {
+        next(err)
+      }
+    },
+  )
+
+  // Retrieve teams for a fragment
+  app.get(
+    '/api/fragments/:fragmentId/teams',
+    authBearerAndPublic,
+    async (req, res, next) => {
+      try {
+        const teams = (await getTeams({
+          req,
+          Team,
+          id: req.params.fragmentId,
+          type: 'fragment',
+        })).filter(createFilterFromQuery(req.query))
+
+        res.status(STATUS.OK).json(teams)
+      } catch (err) {
+        next(err)
+      }
+    },
+  )
+}
+
+module.exports = FragmentsAPI
diff --git a/packages/components/model-fragment/src/fragment.js b/packages/components/model-fragment/src/fragment.js
new file mode 100644
index 0000000000000000000000000000000000000000..d48f806c370ab1fb40d768d14ef58e49651ad715
--- /dev/null
+++ b/packages/components/model-fragment/src/fragment.js
@@ -0,0 +1,40 @@
+const BaseModel = require('@pubsweet/base-model')
+
+class Fragment extends BaseModel {
+  constructor(properties) {
+    super(properties)
+    this.type = 'fragment'
+    this.owners = this.owners || []
+  }
+
+  static get tableName() {
+    return 'fragments'
+  }
+
+  static get schema() {
+    return {
+      required: ['fragmentType'],
+      type: 'object',
+      properties: {
+        fragmentType: { type: 'string' },
+        fragments: {
+          type: ['array', 'null'],
+          items: { type: 'string', format: 'uuid' },
+        },
+        owners: {
+          type: ['array', 'null'],
+          items: { type: 'string', format: 'uuid' },
+        },
+      },
+    }
+  }
+
+  async delete() {
+    const { model: Team } = require('@pubsweet/model-team')
+    await Team.deleteAssociated(this.type, this.id)
+    return super.delete()
+  }
+}
+
+Fragment.type = 'fragment'
+module.exports = Fragment
diff --git a/packages/server/src/graphql/definitions/fragment.js b/packages/components/model-fragment/src/graphql.js
similarity index 100%
rename from packages/server/src/graphql/definitions/fragment.js
rename to packages/components/model-fragment/src/graphql.js
diff --git a/packages/components/model-fragment/src/index.js b/packages/components/model-fragment/src/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..7df51823c9000d648f944e3542087237cb04884d
--- /dev/null
+++ b/packages/components/model-fragment/src/index.js
@@ -0,0 +1,6 @@
+module.exports = {
+  ...require('./graphql'),
+  modelName: 'Fragment',
+  model: require('./fragment'),
+  server: () => app => require('./api_fragments')(app),
+}
diff --git a/packages/components/model-fragment/src/migrations/1543163068-initial-fragment-migration.sql b/packages/components/model-fragment/src/migrations/1543163068-initial-fragment-migration.sql
new file mode 100644
index 0000000000000000000000000000000000000000..fc1b030ac0adc992d546d368bb769726b6d9a339
--- /dev/null
+++ b/packages/components/model-fragment/src/migrations/1543163068-initial-fragment-migration.sql
@@ -0,0 +1,9 @@
+CREATE TABLE fragments (
+  id UUID PRIMARY KEY,
+  created TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT current_timestamp,
+  updated TIMESTAMP WITH TIME ZONE,
+  fragment_type TEXT,
+  fragments JSONB,
+  owners JSONB,
+  type TEXT NOT NULL
+);
\ No newline at end of file
diff --git a/packages/components/model-team/.babelrc b/packages/components/model-team/.babelrc
new file mode 100644
index 0000000000000000000000000000000000000000..d3f11333a6b54b34a93878af4b902fcf1bf6d45f
--- /dev/null
+++ b/packages/components/model-team/.babelrc
@@ -0,0 +1,12 @@
+{
+  "presets": [
+    [
+      "env",
+      {
+        "targets": {
+          "node": "current"
+        }
+      }
+    ]
+  ]
+}
\ No newline at end of file
diff --git a/packages/components/model-team/CHANGELOG.md b/packages/components/model-team/CHANGELOG.md
new file mode 100644
index 0000000000000000000000000000000000000000..fd0ae789e02f18c067ee723e82809f21b5fe1014
--- /dev/null
+++ b/packages/components/model-team/CHANGELOG.md
@@ -0,0 +1,16 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+
+## 1.0.1-alpha.0 (2018-11-23)
+
+
+### Bug Fixes
+
+* **model-team:** use correct dependencies ([21552e1](https://gitlab.coko.foundation/pubsweet/pubsweet/commit/21552e1))
+
+
+### Features
+
+* migrate team to BaseModel ([512a562](https://gitlab.coko.foundation/pubsweet/pubsweet/commit/512a562))
diff --git a/packages/components/model-team/README.md b/packages/components/model-team/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..d9e7a707e45bcc38e7c4ac081f2e93228f482929
--- /dev/null
+++ b/packages/components/model-team/README.md
@@ -0,0 +1 @@
+# Team model
diff --git a/packages/components/model-team/package.json b/packages/components/model-team/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..c188e93c13a1ee708f32c1a44051624a3b080237
--- /dev/null
+++ b/packages/components/model-team/package.json
@@ -0,0 +1,17 @@
+{
+  "name": "@pubsweet/model-team",
+  "version": "1.0.1-alpha.0",
+  "description": "",
+  "main": "src/index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "author": "Adam Hyde",
+  "license": "MIT",
+  "dependencies": {
+    "@pubsweet/base-model": "^1.0.9-alpha.0"
+  },
+  "publishConfig": {
+    "access": "public"
+  }
+}
diff --git a/packages/components/model-team/src/api_teams.js b/packages/components/model-team/src/api_teams.js
new file mode 100644
index 0000000000000000000000000000000000000000..d6ee0222cf70ac9d33f3539460f3f0c27d8fe47a
--- /dev/null
+++ b/packages/components/model-team/src/api_teams.js
@@ -0,0 +1,115 @@
+const STATUS = require('http-status-codes')
+const passport = require('passport')
+const sse = require('pubsweet-sse')
+
+const authBearer = passport.authenticate('bearer', { session: false })
+
+const TeamsAPI = app => {
+  const Team = require('./team')
+
+  const {
+    util: {
+      createFilterFromQuery,
+      applyPermissionFilter,
+      buildChangeData,
+      objectId,
+    },
+  } = require('pubsweet-server')
+
+  app.get('/api/teams', authBearer, async (req, res, next) => {
+    try {
+      const teams = await Team.all()
+      const filteredTeams = await applyPermissionFilter({
+        req,
+        target: req.route,
+        filterable: teams,
+      })
+      filteredTeams.filter(createFilterFromQuery(req.query))
+
+      res.status(STATUS.OK).json(filteredTeams)
+    } catch (err) {
+      next(err)
+    }
+  })
+
+  app.post('/api/teams', authBearer, async (req, res, next) => {
+    try {
+      // Teams are either based around objects or not,
+      // we need to know if they are and around which object
+      const target = Object.assign(
+        {},
+        { path: req.route.path },
+        { team: req.body },
+      )
+
+      const properties = await applyPermissionFilter({
+        req,
+        target,
+        filterable: req.body,
+      })
+
+      let team = new Team(properties)
+      team = await team.save()
+
+      res.status(STATUS.CREATED).json(team)
+      sse.send({ action: 'team:create', data: { team } })
+    } catch (err) {
+      next(err)
+    }
+  })
+
+  app.get('/api/teams/:teamId', authBearer, async (req, res, next) => {
+    try {
+      const team = await Team.find(req.params.teamId)
+      const properties = await applyPermissionFilter({
+        req,
+        target: team,
+      })
+
+      res.status(STATUS.OK).json(properties)
+    } catch (err) {
+      next(err)
+    }
+  })
+
+  app.delete('/api/teams/:teamId', authBearer, async (req, res, next) => {
+    try {
+      const team = await Team.find(req.params.teamId)
+      const output = await applyPermissionFilter({ req, target: team })
+
+      await team.delete()
+
+      res.status(STATUS.OK).json(output)
+      sse.send({ action: 'team:delete', data: { team: objectId(team) } })
+    } catch (err) {
+      next(err)
+    }
+  })
+
+  app.patch('/api/teams/:teamId', authBearer, async (req, res, next) => {
+    try {
+      const team = await Team.find(req.params.teamId)
+      const currentAndUpdate = { current: team, update: req.body }
+      const properties = await applyPermissionFilter({
+        req,
+        target: currentAndUpdate,
+        filterable: req.body,
+      })
+
+      await team.updateProperties(properties)
+      await team.save()
+
+      const update = buildChangeData(properties, team)
+
+      res.status(STATUS.OK).json(update)
+      sse.send({
+        action: 'team:patch',
+        data: { team: objectId(team), update },
+      })
+    } catch (err) {
+      next(err)
+    }
+  })
+}
+
+module.exports = TeamsAPI
diff --git a/packages/server/src/graphql/definitions/team.js b/packages/components/model-team/src/graphql.js
similarity index 100%
rename from packages/server/src/graphql/definitions/team.js
rename to packages/components/model-team/src/graphql.js
diff --git a/packages/components/model-team/src/index.js b/packages/components/model-team/src/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..0a26aecfff8b0f4664948d34ce3ab151f3036b4b
--- /dev/null
+++ b/packages/components/model-team/src/index.js
@@ -0,0 +1,6 @@
+module.exports = {
+  ...require('./graphql'),
+  modelName: 'Team',
+  model: require('./team'),
+  server: () => app => require('./api_teams')(app),
+}
diff --git a/packages/components/model-team/src/migrations/1542801241-initial-team-migration.sql b/packages/components/model-team/src/migrations/1542801241-initial-team-migration.sql
new file mode 100644
index 0000000000000000000000000000000000000000..8b82968844bc80ba5b0ddfdeb8c91a90a52aa2f2
--- /dev/null
+++ b/packages/components/model-team/src/migrations/1542801241-initial-team-migration.sql
@@ -0,0 +1,12 @@
+CREATE TABLE teams (
+  id UUID PRIMARY KEY,
+  created TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT current_timestamp,
+  updated TIMESTAMP WITH TIME ZONE,
+  object JSONB,
+  name TEXT,
+  team_type TEXT NOT NULL,
+  members JSONB,
+  owners JSONB,
+  global BOOLEAN,
+  type TEXT NOT NULL
+);
\ No newline at end of file
diff --git a/packages/server/src/models/Team.js b/packages/components/model-team/src/team.js
similarity index 53%
rename from packages/server/src/models/Team.js
rename to packages/components/model-team/src/team.js
index 3dcbbfe8fcb7ef3ec0e12dfc547671a67b18fee6..f6c2a4dd6a2573a755c704a178fd4e1fa078294b 100644
--- a/packages/server/src/models/Team.js
+++ b/packages/components/model-team/src/team.js
@@ -1,9 +1,9 @@
 const _ = require('lodash')
+const logger = require('@pubsweet/logger')
 
-const Model = require('./Model')
-const User = require('./User')
+const BaseModel = require('@pubsweet/base-model')
 
-class Team extends Model {
+class Team extends BaseModel {
   constructor(properties) {
     super(properties)
 
@@ -14,6 +14,29 @@ class Team extends Model {
     }
   }
 
+  static get tableName() {
+    return 'teams'
+  }
+
+  static get schema() {
+    return {
+      properties: {
+        object: { type: ['object', 'null'] },
+        name: { type: 'string' },
+        teamType: { type: ['string'] },
+        members: {
+          type: 'array',
+          items: { type: 'string', format: 'uuid' },
+        },
+        owners: {
+          type: ['array', 'null'],
+          items: { type: 'string', format: 'uuid' },
+        },
+        global: { type: ['boolean', 'null'] },
+      },
+    }
+  }
+
   static async deleteAssociated(type, id) {
     const teams = await Team.all()
 
@@ -28,6 +51,8 @@ class Team extends Model {
   }
 
   async updateProperties(properties) {
+    const { model: User } = require('@pubsweet/model-user')
+
     const currentMembers = new Set(this.members)
     const newMembers = new Set(properties.members)
     const removedMembers = new Set(
@@ -47,22 +72,31 @@ class Team extends Model {
   }
 
   async save() {
-    await super.save()
+    let team
 
-    await Promise.all(
-      this.members.map(async member => {
-        const user = await User.find(member)
-        if (!user.teams.includes(this.id)) {
-          user.teams.push(this.id)
-          await user.save()
-        }
-      }),
-    )
+    try {
+      team = await super.save()
 
-    return this
+      const { model: User } = require('@pubsweet/model-user')
+
+      await Promise.all(
+        team.members.map(async member => {
+          const user = await User.find(member)
+          if (!user.teams.includes(team.id)) {
+            user.teams.push(team.id)
+            await user.save()
+          }
+        }),
+      )
+    } catch (e) {
+      logger.error(e)
+    }
+    return team
   }
 
   async delete() {
+    const { model: User } = require('@pubsweet/model-user')
+
     await Promise.all(
       this.members.map(async member => {
         const user = await User.find(member)
diff --git a/packages/components/model-user/.babelrc b/packages/components/model-user/.babelrc
new file mode 100644
index 0000000000000000000000000000000000000000..d3f11333a6b54b34a93878af4b902fcf1bf6d45f
--- /dev/null
+++ b/packages/components/model-user/.babelrc
@@ -0,0 +1,12 @@
+{
+  "presets": [
+    [
+      "env",
+      {
+        "targets": {
+          "node": "current"
+        }
+      }
+    ]
+  ]
+}
\ No newline at end of file
diff --git a/packages/components/model-user/CHANGELOG.md b/packages/components/model-user/CHANGELOG.md
new file mode 100644
index 0000000000000000000000000000000000000000..9bfed22c9047e6a4a133a151595e5043825f309e
--- /dev/null
+++ b/packages/components/model-user/CHANGELOG.md
@@ -0,0 +1,16 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+
+## 1.0.1-alpha.0 (2018-11-23)
+
+
+### Bug Fixes
+
+* **model-user:** omit passwordHash from JSON representation ([c33fbee](https://gitlab.coko.foundation/pubsweet/pubsweet/commit/c33fbee))
+
+
+### Features
+
+* add standalone user model ([240beca](https://gitlab.coko.foundation/pubsweet/pubsweet/commit/240beca))
diff --git a/packages/components/model-user/README.md b/packages/components/model-user/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..d5844c822d80f22e8b092af9c0e604104b12f3d3
--- /dev/null
+++ b/packages/components/model-user/README.md
@@ -0,0 +1 @@
+# User model
diff --git a/packages/components/model-user/package.json b/packages/components/model-user/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..f574b0b21cda5e667001a91aa5871bfd85719eb4
--- /dev/null
+++ b/packages/components/model-user/package.json
@@ -0,0 +1,17 @@
+{
+  "name": "@pubsweet/model-user",
+  "version": "1.0.1-alpha.0",
+  "description": "",
+  "main": "src/index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "author": "Adam Hyde",
+  "license": "MIT",
+  "dependencies": {
+    "@pubsweet/base-model": "^1.0.9-alpha.0"
+  },
+  "publishConfig": {
+    "access": "public"
+  }
+}
diff --git a/packages/components/model-user/src/api_users.js b/packages/components/model-user/src/api_users.js
new file mode 100644
index 0000000000000000000000000000000000000000..df6f2d3cd6cdf57976601a563076a57d04f8ee64
--- /dev/null
+++ b/packages/components/model-user/src/api_users.js
@@ -0,0 +1,168 @@
+const STATUS = require('http-status-codes')
+const passport = require('passport')
+
+const authLocal = passport.authenticate('local', {
+  failWithError: true,
+  session: false,
+})
+
+const authBearer = passport.authenticate('bearer', { session: false })
+const authentication = require('./authentication')
+
+const UsersAPI = app => {
+  const User = require('./user')
+  const { ValidationError } = require('pubsweet-server')
+
+  const {
+    util: {
+      applyPermissionFilter,
+      fieldSelector,
+      createFilterFromQuery,
+      authorizationError,
+    },
+  } = require('pubsweet-server')
+
+  // Register passport authentication strategies
+  app.locals.passport.use('bearer', authentication.strategies.bearer)
+  app.locals.passport.use('anonymous', authentication.strategies.anonymous)
+  app.locals.passport.use('local', authentication.strategies.local)
+
+  // Issue a token
+  app.post('/api/users/authenticate', authLocal, async (req, res) => {
+    const user = Object.assign(
+      { token: authentication.token.create(req.user) },
+      req.user,
+    )
+    req.user = req.user.id
+    const properties = await applyPermissionFilter({
+      req,
+      target: user,
+    })
+
+    return res.status(STATUS.CREATED).json(properties)
+  })
+
+  // Verify a token
+  app.get('/api/users/authenticate', authBearer, async (req, res, next) => {
+    try {
+      const user = (await User.find(req.user)).toJSON()
+      user.token = req.authInfo.token
+      res.status(STATUS.OK).json(user)
+    } catch (err) {
+      next(err)
+    }
+  })
+
+  // Create a user
+  app.post('/api/users', async (req, res, next) => {
+    try {
+      // TODO: Remove this in favor of checking in authsome
+      if (req.body.admin) throw new ValidationError('invalid property: admin')
+
+      const properties = await applyPermissionFilter({
+        req,
+        target: req.route,
+        filterable: req.body,
+      })
+
+      let user = new User(properties)
+
+      user = await user.save()
+      res.status(STATUS.CREATED).json(user)
+    } catch (err) {
+      next(err)
+    }
+  })
+
+  // List users
+  app.get('/api/users', authBearer, async (req, res, next) => {
+    try {
+      const users = await User.all()
+      const filteredUsers = await applyPermissionFilter({
+        req,
+        target: req.route,
+        filterable: users,
+      })
+
+      const usersWithSelectedFields = (await Promise.all(
+        filteredUsers.map(async user => {
+          const properties = await applyPermissionFilter({
+            req,
+            target: user,
+          })
+          return fieldSelector(req)(properties)
+        }),
+      )).filter(createFilterFromQuery(req.query))
+
+      res.status(STATUS.OK).json({ users: usersWithSelectedFields })
+    } catch (err) {
+      next(err)
+    }
+  })
+
+  // Get a user
+  app.get('/api/users/:id', authBearer, async (req, res, next) => {
+    try {
+      // TODO: Untangle
+      const authsome = require('pubsweet-server/src/helpers/authsome')
+      const user = await User.find(req.params.id)
+      const permission = await authsome.can(req.user, req.method, user)
+
+      const properties = await applyPermissionFilter({
+        req,
+        target: user,
+      })
+
+      if (!permission) {
+        throw authorizationError(req.user, req.method, req.path)
+      }
+
+      res.status(STATUS.OK).json(properties)
+    } catch (err) {
+      next(err)
+    }
+  })
+
+  // Delete a user
+  app.delete('/api/users/:id', authBearer, async (req, res, next) => {
+    try {
+      // TODO: Untangle
+      const authsome = require('pubsweet-server/src/helpers/authsome')
+
+      let user = await User.find(req.params.id)
+      const permission = await authsome.can(req.user, req.method, user)
+
+      if (!permission) {
+        throw authorizationError(req.user, req.method, req.path)
+      }
+      user = await user.delete()
+      res.status(STATUS.OK).json(user)
+    } catch (err) {
+      next(err)
+    }
+  })
+
+  // Patch a user
+  app.patch('/api/users/:id', authBearer, async (req, res, next) => {
+    try {
+      let user = await User.find(req.params.id)
+
+      const currentAndUpdate = { current: user, update: req.body }
+      const properties = await applyPermissionFilter({
+        req,
+        target: currentAndUpdate,
+        filterable: req.body,
+      })
+
+      user = await user.updateProperties(properties)
+      user = await user.save()
+      user = await User.find(req.params.id)
+
+      res.status(STATUS.OK).json(user)
+    } catch (err) {
+      next(err)
+    }
+  })
+}
+
+module.exports = UsersAPI
diff --git a/packages/server/src/authentication.js b/packages/components/model-user/src/authentication.js
similarity index 98%
rename from packages/server/src/authentication.js
rename to packages/components/model-user/src/authentication.js
index dbedf2cbe0260b0a4946d68721cc36407d55bff8..baef1e8021ccd3c71245eb14e9a052ddbaa26c52 100644
--- a/packages/server/src/authentication.js
+++ b/packages/components/model-user/src/authentication.js
@@ -5,7 +5,6 @@ const BearerStrategy = require('passport-http-bearer').Strategy
 const AnonymousStrategy = require('passport-anonymous').Strategy
 const LocalStrategy = require('passport-local').Strategy
 
-const User = require('./models/User')
 const config = require('config')
 
 const createToken = user => {
@@ -41,6 +40,7 @@ const verifyPassword = (username, password, done) => {
   const errorMessage = 'Wrong username or password.'
   logger.debug('User finding:', username)
 
+  const User = require('./user')
   User.findByUsername(username)
     .then(user => {
       logger.debug('User found:', user.username)
diff --git a/packages/server/src/graphql/definitions/user.js b/packages/components/model-user/src/graphql.js
similarity index 52%
rename from packages/server/src/graphql/definitions/user.js
rename to packages/components/model-user/src/graphql.js
index f97231d1673d4b02309ce512c9a3af05a6fd2978..77b0108e6cfe597a46fcfecbe8860db437df0569 100644
--- a/packages/server/src/graphql/definitions/user.js
+++ b/packages/components/model-user/src/graphql.js
@@ -1,3 +1,7 @@
+const authentication = require('./authentication')
+const logger = require('@pubsweet/logger')
+const User = require('./user')
+
 const resolvers = {
   Query: {
     user(_, { id }, ctx) {
@@ -6,6 +10,11 @@ const resolvers = {
     users(_, vars, ctx) {
       return ctx.connectors.User.fetchAll(ctx)
     },
+    // Authentication
+    currentUser(_, vars, ctx) {
+      if (!ctx.user) return null
+      return User.find(ctx.user)
+    },
   },
   Mutation: {
     createUser(_, { input }, ctx) {
@@ -17,6 +26,24 @@ const resolvers = {
     updateUser(_, { id, input }, ctx) {
       return ctx.connectors.User.update(id, input, ctx)
     },
+    // Authentication
+    async loginUser(_, { input }) {
+      let isValid = false
+      let user
+      try {
+        user = await User.findByUsername(input.username)
+        isValid = await user.validPassword(input.password)
+      } catch (err) {
+        logger.debug(err)
+      }
+      if (!isValid) {
+        throw new Error('Wrong username or password.')
+      }
+      return {
+        user,
+        token: authentication.token.create(user),
+      }
+    },
   },
   User: {
     teams(user, vars, ctx) {
@@ -57,6 +84,29 @@ const typeDefs = `
     password: String
     rev: String
   }
+
+  # Authentication
+
+  extend type Query {
+    # Get the currently authenticated user based on the JWT in the HTTP headers
+    currentUser: User
+  }
+
+  extend type Mutation {
+    # Authenticate a user using username and password
+    loginUser(input: LoginUserInput): LoginResult
+  }
+
+  # User details and bearer token
+  type LoginResult {
+    user: User!
+    token: String!
+  }
+
+  input LoginUserInput {
+    username: String!
+    password: String!
+  }
 `
 
 module.exports = { resolvers, typeDefs }
diff --git a/packages/components/model-user/src/index.js b/packages/components/model-user/src/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..1125a9f9e5523c4db43be446bedc47eb357db7a7
--- /dev/null
+++ b/packages/components/model-user/src/index.js
@@ -0,0 +1,6 @@
+module.exports = {
+  ...require('./graphql'),
+  modelName: 'User',
+  model: require('./user'),
+  server: () => app => require('./api_users')(app),
+}
diff --git a/packages/components/model-user/src/migrations/1542276313-initial-user-migration.sql b/packages/components/model-user/src/migrations/1542276313-initial-user-migration.sql
new file mode 100644
index 0000000000000000000000000000000000000000..7415b09166e43bb60b99cc8d654be47d4f10a43a
--- /dev/null
+++ b/packages/components/model-user/src/migrations/1542276313-initial-user-migration.sql
@@ -0,0 +1,15 @@
+CREATE TABLE users (
+  id UUID PRIMARY KEY,
+  created TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT current_timestamp,
+  updated TIMESTAMP WITH TIME ZONE,
+  admin BOOLEAN,
+  email TEXT,
+  username TEXT,
+  password_hash TEXT,
+  fragments JSONB,
+  collections JSONB,
+  teams JSONB,
+  password_reset_token TEXT,
+  password_reset_timestamp TIMESTAMP WITH TIME ZONE,
+  type TEXT NOT NULL
+);
\ No newline at end of file
diff --git a/packages/server/src/models/User.js b/packages/components/model-user/src/user.js
similarity index 64%
rename from packages/server/src/models/User.js
rename to packages/components/model-user/src/user.js
index 56a65fa5dec7b6bba2e95319a6ead7b2dff04a02..169814229b0c81cc787a6007f9c78ef9ba054c71 100644
--- a/packages/server/src/models/User.js
+++ b/packages/components/model-user/src/user.js
@@ -1,26 +1,56 @@
-const Model = require('./Model')
-const ConflictError = require('../errors/ConflictError')
+const BaseModel = require('@pubsweet/base-model')
 const bcrypt = require('bcrypt')
-const omit = require('lodash/omit')
+// const omit = require('lodash/omit')
 const pick = require('lodash/pick')
 const config = require('config')
 
 const BCRYPT_COST = config.util.getEnv('NODE_ENV') === 'test' ? 1 : 12
 
-class User extends Model {
+class User extends BaseModel {
   constructor(properties) {
     super(properties)
-
     this.type = 'user'
-    this.email = properties.email
-    this.username = properties.username
     this.collections = this.collections || []
     this.fragments = this.fragments || []
     this.teams = this.teams || []
   }
 
-  toJSON() {
-    return omit(this, ['passwordHash'])
+  $formatJson(json) {
+    json = super.$formatJson(json)
+    delete json.passwordHash
+    return json
+  }
+
+  static get tableName() {
+    return 'users'
+  }
+
+  static get schema() {
+    return {
+      properties: {
+        admin: { type: ['boolean', 'null'] },
+        email: { type: 'string', format: 'email' },
+        username: { type: 'string', pattern: '^[a-zA-Z0-9]+' },
+        passwordHash: { type: 'string' },
+        passwordResetToken: { type: ['string', 'null'] },
+        passwordResetTimestamp: {
+          type: ['string', 'object', 'null'],
+          format: 'date-time',
+        },
+        fragments: {
+          type: 'array',
+          items: { type: 'string', format: 'uuid' },
+        },
+        collections: {
+          type: 'array',
+          items: { type: 'string', format: 'uuid' },
+        },
+        teams: {
+          type: 'array',
+          items: { type: 'string', format: 'uuid' },
+        },
+      },
+    }
   }
 
   // eslint-disable-next-line class-methods-use-this
@@ -53,6 +83,8 @@ class User extends Model {
   }
 
   static async isUniq(user) {
+    const { ConflictError } = require('pubsweet-server')
+
     let result
 
     const swallowNotFound = e => {
diff --git a/packages/components/xpub-review-server/CHANGELOG.md b/packages/components/xpub-review-server/CHANGELOG.md
index c2b63ddcb9b6e0a40fbbee165bf1d5d0f2356aed..635956741c0e09e2fd7fbf105d474f8af617b0b5 100644
--- a/packages/components/xpub-review-server/CHANGELOG.md
+++ b/packages/components/xpub-review-server/CHANGELOG.md
@@ -3,6 +3,17 @@
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
 
+## [0.2.6-alpha.0](https://gitlab.coko.foundation/pubsweet/pubsweet/compare/pubsweet-component-xpub-review-backend@0.2.5...pubsweet-component-xpub-review-backend@0.2.6-alpha.0) (2018-11-23)
+
+
+### Bug Fixes
+
+* adjust xpub-review-server to use the new BaseModel models ([7f745f0](https://gitlab.coko.foundation/pubsweet/pubsweet/commit/7f745f0))
+
+
+
+
+
 <a name="0.2.5"></a>
 ## [0.2.5](https://gitlab.coko.foundation/pubsweet/pubsweet/compare/pubsweet-component-xpub-review-backend@0.2.4...pubsweet-component-xpub-review-backend@0.2.5) (2018-09-25)
 
diff --git a/packages/components/xpub-review-server/package.json b/packages/components/xpub-review-server/package.json
index 327f21a13c776edb1cce9e876db499c4fe0d575f..f708017d312eda88523291c4e3f6eb42dc07f43e 100644
--- a/packages/components/xpub-review-server/package.json
+++ b/packages/components/xpub-review-server/package.json
@@ -1,6 +1,6 @@
 {
   "name": "pubsweet-component-xpub-review-backend",
-  "version": "0.2.5",
+  "version": "0.2.6-alpha.0",
   "main": "src",
   "keywords": [],
   "author": "Collaborative Knowledge Foundation",
diff --git a/packages/components/xpub-review-server/src/reviewBackend.js b/packages/components/xpub-review-server/src/reviewBackend.js
index 881246375c22288e2555e4f272a941189748d954..977cc518eef00464081f710770ba822c90dd2ade 100644
--- a/packages/components/xpub-review-server/src/reviewBackend.js
+++ b/packages/components/xpub-review-server/src/reviewBackend.js
@@ -3,10 +3,12 @@ const config = require('config')
 const passport = require('passport')
 const logger = require('@pubsweet/logger')
 const emailer = require('@pubsweet/component-send-email')
-const User = require('pubsweet-server/src/models/User')
-const Fragment = require('pubsweet-server/src/models/Fragment')
-const Collection = require('pubsweet-server/src/models/Collection')
-const Team = require('pubsweet-server/src/models/Team')
+const {
+  User,
+  Team,
+  Fragment,
+  Collection,
+} = require('pubsweet-server/src/models')
 const authsome = require('pubsweet-server/src/helpers/authsome')
 const AuthorizationError = require('pubsweet-server/src/errors/AuthorizationError')
 
diff --git a/packages/components/xpub-review-server/src/reviewBackend.test.js b/packages/components/xpub-review-server/src/reviewBackend.test.js
index 5d0e499012d4d035e19a441575d7a40bd5bfef3f..d5c1b2458da6039947d3cdf1cb568fd9357f08a5 100644
--- a/packages/components/xpub-review-server/src/reviewBackend.test.js
+++ b/packages/components/xpub-review-server/src/reviewBackend.test.js
@@ -10,50 +10,48 @@ jest.mock('@pubsweet/component-send-email', () => ({
   send: jest.fn().mockImplementation(() => Promise.resolve({})),
 }))
 
-jest.mock('pubsweet-server/src/models/Team', () => () => ({
-  find: jest.fn(() => ({
-    id: '9555530a-ca92-4e74-a48c-b21ccc109ca8',
-    teams: ['08888b14-8b64-420d-898f-b2bdd9fbd57c'],
-    email: 'author@example.org',
+jest.mock('pubsweet-server/src/models', () => ({
+  Team: () => ({
+    find: jest.fn(() => ({
+      id: '9555530a-ca92-4e74-a48c-b21ccc109ca8',
+      teams: ['08888b14-8b64-420d-898f-b2bdd9fbd57c'],
+      email: 'author@example.org',
+    })),
     save: () => {},
-  })),
-  save: () => {},
-}))
-
-jest.mock('pubsweet-server/src/models/User', () => ({
-  find: jest.fn(() => ({
-    id: '9555530a-ca92-4e74-a48c-b21ccc109ca8',
-    teams: ['08888b14-8b64-420d-898f-b2bdd9fbd57c'],
-    email: 'author@example.org',
-    save: () => {},
-  })),
-}))
-
-jest.mock('pubsweet-server/src/models/Fragment', () => ({
-  find: jest.fn(() => ({
-    version: 1,
-    owners: [{}],
-    metadata: {
-      title: 'title',
-      abstract: 'abstract',
-    },
-    updateProperties(update) {
-      Object.assign(this, update)
-    },
-    save: () => {},
-  })),
-}))
-
-jest.mock('pubsweet-server/src/models/Collection', () => ({
-  find: jest.fn(() => ({
-    updateProperties: () => ({}),
-    reviewers: [
-      {
-        user: '9555530a-ca92-4e74-a48c-b21ccc109ca8',
+  }),
+  User: {
+    find: jest.fn(() => ({
+      id: '9555530a-ca92-4e74-a48c-b21ccc109ca8',
+      teams: ['08888b14-8b64-420d-898f-b2bdd9fbd57c'],
+      email: 'author@example.org',
+      save: () => {},
+    })),
+  },
+  Fragment: {
+    find: jest.fn(() => ({
+      version: 1,
+      owners: [{}],
+      metadata: {
+        title: 'title',
+        abstract: 'abstract',
       },
-    ],
-    save: () => {},
-  })),
+      updateProperties(update) {
+        Object.assign(this, update)
+      },
+      save: () => {},
+    })),
+  },
+  Collection: {
+    find: jest.fn(() => ({
+      updateProperties: () => ({}),
+      reviewers: [
+        {
+          user: '9555530a-ca92-4e74-a48c-b21ccc109ca8',
+        },
+      ],
+      save: () => {},
+    })),
+  },
 }))
 
 jest.mock('pubsweet-server/src/helpers/authsome', () => ({
diff --git a/packages/db-manager/config/test.js b/packages/db-manager/config/test.js
index 33aac8ca045e73944c7098ffbf1f4f2ca76b8a7e..d7f26fcdb86d21bbecc2bbcd5b4dea223ee2a13b 100644
--- a/packages/db-manager/config/test.js
+++ b/packages/db-manager/config/test.js
@@ -20,4 +20,12 @@ module.exports = {
     email: 'test@example.com',
     password: 'test_password',
   },
+  pubsweet: {
+    components: [
+      '@pubsweet/model-user',
+      '@pubsweet/model-team',
+      '@pubsweet/model-fragment',
+      '@pubsweet/model-collection',
+    ],
+  },
 }
diff --git a/packages/db-manager/src/commands/add-collection.js b/packages/db-manager/src/commands/add-collection.js
index 6aaecce05c87af8ce74f15fd56f4784fd7114a31..47ad2c7a79e94ffd6545a321f9f05f1e6c1f8d11 100644
--- a/packages/db-manager/src/commands/add-collection.js
+++ b/packages/db-manager/src/commands/add-collection.js
@@ -1,7 +1,9 @@
 const logger = require('@pubsweet/logger')
-const { Collection, User } = require('pubsweet-server')
 
 module.exports = async (collectionData, fragment = null) => {
+  const { model: User } = require('@pubsweet/model-user')
+  const { Collection } = require('pubsweet-server/src/models')
+
   logger.info('Creating collection')
 
   const collection = await new Collection(collectionData).save()
diff --git a/packages/db-manager/src/commands/add-fragment.js b/packages/db-manager/src/commands/add-fragment.js
index ee4484477ab5cee1934fffc73405892ef902b25f..235997b1c5c4a122d7b4e21086efc79fb4d4d987 100644
--- a/packages/db-manager/src/commands/add-fragment.js
+++ b/packages/db-manager/src/commands/add-fragment.js
@@ -1,8 +1,9 @@
 const logger = require('@pubsweet/logger')
-const Fragment = require('pubsweet-server/src/models/Fragment')
-const User = require('pubsweet-server/src/models/User')
 
 module.exports = async fragmentData => {
+  const { Fragment } = require('pubsweet-server/src/models')
+  const { model: User } = require('@pubsweet/model-user')
+
   logger.info('Creating fragment')
 
   const fragment = await new Fragment(fragmentData).save()
diff --git a/packages/db-manager/src/commands/add-user.js b/packages/db-manager/src/commands/add-user.js
index 0b9b827a813fb3aa7a414641ba85e586460a945a..1b4f4c8a9f03d6d1d8fd6ac248d3d71adafee712 100644
--- a/packages/db-manager/src/commands/add-user.js
+++ b/packages/db-manager/src/commands/add-user.js
@@ -1,5 +1,5 @@
 const logger = require('@pubsweet/logger')
-const { Collection, User } = require('pubsweet-server')
+const { Collection, User } = require('pubsweet-server/src/models')
 const { validateUser } = require('../validations')
 
 const addAdminOwnerToAllCollections = async user => {
diff --git a/packages/db-manager/src/validations.js b/packages/db-manager/src/validations.js
index 5692a8860465702e53be1ff468230c6459f52b66..fea22e3ae338c6f3ca224b5653a885d5f3ffcaeb 100644
--- a/packages/db-manager/src/validations.js
+++ b/packages/db-manager/src/validations.js
@@ -1,30 +1,14 @@
 const Joi = require('joi')
-const config = require('config')
-
-let appValidations
-try {
-  appValidations = require(config.validations)
-} catch (err) {
-  appValidations = []
-}
-const schemas = require('pubsweet-server/src/models/validations')(
-  appValidations,
-)
-const _ = require('lodash/fp')
 
 const userSchema = Joi.object({
-  username: _.get('user.username', schemas) || Joi.string().required(),
-  email:
-    _.get('user.email', schemas) ||
-    Joi.string()
-      .email()
-      .required(),
-  password:
-    _.get('user.password', schemas) ||
-    Joi.string()
-      .min(8)
-      .max(60)
-      .required(),
+  username: Joi.string().required(),
+  email: Joi.string()
+    .email()
+    .required(),
+  password: Joi.string()
+    .min(8)
+    .max(60)
+    .required(),
   admin: Joi.boolean().optional(),
 })
 
diff --git a/packages/db-manager/test/commands/add-collection.test.js b/packages/db-manager/test/commands/add-collection.test.js
index c11a576df418ebdd503fe88b9554454f4a177ef1..0201c77f528671ad70f895d270deeb4ab7b39508 100644
--- a/packages/db-manager/test/commands/add-collection.test.js
+++ b/packages/db-manager/test/commands/add-collection.test.js
@@ -1,5 +1,6 @@
 const { addCollection, createTables } = require('../../src')
-const { User, Collection, Fragment } = require('pubsweet-server')
+const { Collection, Fragment } = require('pubsweet-server/src/models')
+const { model: User } = require('@pubsweet/model-user')
 
 describe('add-collection', () => {
   beforeEach(() => createTables(true))
diff --git a/packages/db-manager/test/commands/add-fragment.test.js b/packages/db-manager/test/commands/add-fragment.test.js
index 5ff925ec0f7d97fecc47e3ca5e7475b609af4bba..d0babb4b65d05af1dd7b8c0845166fce1544cb58 100644
--- a/packages/db-manager/test/commands/add-fragment.test.js
+++ b/packages/db-manager/test/commands/add-fragment.test.js
@@ -1,6 +1,6 @@
 const { addFragment, createTables } = require('../../src')
-const Fragment = require('pubsweet-server/src/models/Fragment')
-const User = require('pubsweet-server/src/models/User')
+const { model: Fragment } = require('@pubsweet/model-fragment')
+const { model: User } = require('@pubsweet/model-user')
 
 describe('add-fragment', () => {
   beforeEach(() => createTables(true))
diff --git a/packages/db-manager/test/commands/add-user.test.js b/packages/db-manager/test/commands/add-user.test.js
index c48c8fe6d147016be462a347b4e7de2dae611c93..de217ab8e0c6f6087df7c6e98639bdbfde664a0d 100644
--- a/packages/db-manager/test/commands/add-user.test.js
+++ b/packages/db-manager/test/commands/add-user.test.js
@@ -1,4 +1,4 @@
-const User = require('pubsweet-server/src/models/User')
+const { model: User } = require('@pubsweet/model-user')
 const { addUser, createTables } = require('../../src/')
 
 const nonAdminUser = {
diff --git a/packages/db-manager/test/commands/setup-db.test.js b/packages/db-manager/test/commands/setup-db.test.js
index 5b8e01fd7d4fb69984ea2fd1a51fdb543fe89d1f..5bef00bf85ab054d9d749f784170d5c43dd9bd93 100644
--- a/packages/db-manager/test/commands/setup-db.test.js
+++ b/packages/db-manager/test/commands/setup-db.test.js
@@ -1,4 +1,4 @@
-const User = require('pubsweet-server/src/models/User')
+const { model: User } = require('@pubsweet/model-user')
 const { setupDb } = require('../../src')
 
 describe('setup-db', () => {
diff --git a/packages/server/config/test.js b/packages/server/config/test.js
index 8808fee661de47f7d09fa08acd42e58cadbafd80..5ca21cb3c57dda4045ef8bf7a930ac8c60115c12 100644
--- a/packages/server/config/test.js
+++ b/packages/server/config/test.js
@@ -33,4 +33,12 @@ module.exports = {
       },
     },
   },
+  pubsweet: {
+    components: [
+      '@pubsweet/model-user',
+      '@pubsweet/model-team',
+      '@pubsweet/model-blog',
+      '@pubsweet/model-blogpost',
+    ],
+  },
 }
diff --git a/packages/server/config/validations.js b/packages/server/config/validations.js
deleted file mode 100644
index 6fb7227a2bce9b32cf517f8d84a642aa79e627f0..0000000000000000000000000000000000000000
--- a/packages/server/config/validations.js
+++ /dev/null
@@ -1,26 +0,0 @@
-const Joi = require('joi')
-
-module.exports = {
-  fragment: [
-    {
-      fragmentType: Joi.valid('blogpost').required(),
-      source: Joi.string(),
-      kind: Joi.string(),
-      title: Joi.string(),
-      presentation: Joi.string(),
-      published: Joi.boolean(),
-      filtered: Joi.string(),
-    },
-    {
-      fragmentType: Joi.valid('file').required(),
-      path: Joi.string().required(),
-    },
-  ],
-  collection: {
-    published: Joi.boolean(),
-    nonPublicProperty: Joi.string(),
-    filtered: Joi.string(),
-    created: Joi.date().default(Date.now, 'creation time'),
-    title: Joi.string(),
-  },
-}
diff --git a/packages/server/package.json b/packages/server/package.json
index deca23937fcc8bb34ca59f70f186507d9a113c61..cdbdf9527a7245bb6b63cdb614b7aa46d9149997 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -16,7 +16,7 @@
     "apollo-server-express": "^1.3.2",
     "apollo-upload-server": "^4.0.2",
     "authsome": "^0.1.0",
-    "bcrypt": "^2.0.1",
+    "bcrypt": "^3.0.2",
     "bluebird": "^3.5.1",
     "body-parser": "^1.15.2",
     "colors": "^1.1.2",
diff --git a/packages/server/src/app.js b/packages/server/src/app.js
index 246207340450037a9bb3817b27441a63d1096a0d..7b4cb34ee3cd1c0ccab48ab118cd86d249195503 100644
--- a/packages/server/src/app.js
+++ b/packages/server/src/app.js
@@ -17,7 +17,7 @@ const models = require('./models')
 const authsome = require('./helpers/authsome')
 const logger = require('@pubsweet/logger')
 const sse = require('pubsweet-sse')
-const authentication = require('./authentication')
+
 const _ = require('lodash/fp')
 const STATUS = require('http-status-codes')
 const registerComponents = require('./register-components')
@@ -57,9 +57,6 @@ const configureApp = app => {
 
   // Passport strategies
   app.use(passport.initialize())
-  passport.use('bearer', authentication.strategies.bearer)
-  passport.use('anonymous', authentication.strategies.anonymous)
-  passport.use('local', authentication.strategies.local)
 
   app.locals.passport = passport
   app.locals.authsome = authsome
diff --git a/packages/server/src/graphql/definitions/authentication.js b/packages/server/src/graphql/definitions/authentication.js
deleted file mode 100644
index 8b2d70af3fc382bc1168da43a69d888da5a52b52..0000000000000000000000000000000000000000
--- a/packages/server/src/graphql/definitions/authentication.js
+++ /dev/null
@@ -1,56 +0,0 @@
-const logger = require('@pubsweet/logger')
-const User = require('../../models/User')
-const authentication = require('../../authentication')
-
-const resolvers = {
-  Query: {
-    currentUser(_, vars, ctx) {
-      if (!ctx.user) return null
-      return User.find(ctx.user)
-    },
-  },
-  Mutation: {
-    async loginUser(_, { input }) {
-      let isValid = false
-      let user
-      try {
-        user = await User.findByUsername(input.username)
-        isValid = await user.validPassword(input.password)
-      } catch (err) {
-        logger.debug(err)
-      }
-      if (!isValid) {
-        throw new Error('Wrong username or password.')
-      }
-      return {
-        user,
-        token: authentication.token.create(user),
-      }
-    },
-  },
-}
-
-const typeDefs = `
-  extend type Query {
-    # Get the currently authenticated user based on the JWT in the HTTP headers
-    currentUser: User
-  }
-
-  extend type Mutation {
-    # Authenticate a user using username and password
-    loginUser(input: LoginUserInput): LoginResult
-  }
-
-  # User details and bearer token
-  type LoginResult {
-    user: User!
-    token: String!
-  }
-
-  input LoginUserInput {
-    username: String!
-    password: String!
-  }
-`
-
-module.exports = { resolvers, typeDefs }
diff --git a/packages/server/src/graphql/schema.js b/packages/server/src/graphql/schema.js
index 1dfaeb6cc6e33d581e126086d2c4da38e71c9ebf..8fc3de021f2c61e91cdbebfa7182bfe7b5f4c2a7 100644
--- a/packages/server/src/graphql/schema.js
+++ b/packages/server/src/graphql/schema.js
@@ -2,12 +2,7 @@ const config = require('config')
 const { merge } = require('lodash')
 const { makeExecutableSchema } = require('graphql-tools')
 
-const collection = require('./definitions/collection')
-const fragment = require('./definitions/fragment')
-const team = require('./definitions/team')
-const user = require('./definitions/user')
 const upload = require('./definitions/upload')
-const authentication = require('./definitions/authentication')
 
 const requireRelative = m =>
   require(require.resolve(m, { paths: [process.cwd()] }))
@@ -15,22 +10,10 @@ const requireRelative = m =>
 // load base types and resolvers
 const typeDefs = [
   `type Query, type Mutation, type Subscription`,
-  collection.typeDefs,
-  fragment.typeDefs,
-  team.typeDefs,
-  user.typeDefs,
   upload.typeDefs,
-  authentication.typeDefs,
 ]
-const resolvers = merge(
-  {},
-  collection.resolvers,
-  fragment.resolvers,
-  team.resolvers,
-  user.resolvers,
-  upload.resolvers,
-  authentication.resolvers,
-)
+
+const resolvers = merge({}, upload.resolvers)
 
 // recursively merge in component types and resolvers
 function getSchemaRecursively(componentName) {
diff --git a/packages/server/src/graphql/subscriptions.js b/packages/server/src/graphql/subscriptions.js
index 21fb266f965e6ff36e6434bf1b53ade4af0cf9af..a88c996ca4d951513dd8102b2defbe021d06a3bc 100644
--- a/packages/server/src/graphql/subscriptions.js
+++ b/packages/server/src/graphql/subscriptions.js
@@ -7,7 +7,7 @@ const { SubscriptionServer } = require('subscriptions-transport-ws')
 const logger = require('@pubsweet/logger')
 
 const graphqlSchema = require('./schema')
-const { token } = require('../authentication')
+const { token } = require('@pubsweet/model-user/src/authentication')
 
 module.exports = {
   addSubscriptions: server => {
diff --git a/packages/server/src/helpers/authsome.js b/packages/server/src/helpers/authsome.js
index 5807d2fd37098eda1d660a165c3398e07490eff8..5495f74b8312424cd9766d71f75c71999b324bfe 100644
--- a/packages/server/src/helpers/authsome.js
+++ b/packages/server/src/helpers/authsome.js
@@ -11,21 +11,6 @@ const models = require('../models')
 // as well - if you want your authsome modes to be usable on both platforms.
 const context = { models: Object.assign({}, models) }
 
-// more restrictive with core models, restrict methods passed to mode since
-// these have to be shimmed in the client (withAuthsome, AuthorizeWithGraphQL)
-context.models.Collection = {
-  find: models.Collection.find.bind(models.Collection),
-}
-context.models.Fragment = {
-  find: models.Fragment.find.bind(models.Fragment),
-}
-context.models.User = {
-  find: models.User.find.bind(models.User),
-}
-context.models.Team = {
-  find: models.Team.find.bind(models.Team),
-}
-
 const authsome = new Authsome({ ...config.authsome, mode }, context)
 
 module.exports = authsome
diff --git a/packages/server/src/index.js b/packages/server/src/index.js
index bb22e8aced11011da4fb9856723a48b2f763ae10..0fb3d68afd28b8e3ba4a08911c3496395225b3d2 100644
--- a/packages/server/src/index.js
+++ b/packages/server/src/index.js
@@ -1,18 +1,18 @@
-// Core models
-module.exports.Fragment = require('./models/Fragment')
-module.exports.Team = require('./models/Team')
-module.exports.User = require('./models/User')
-module.exports.Collection = require('./models/Collection')
-
-// Authorization helpers
-module.exports.helpers = require('./helpers/authorization')
-
 module.exports.db = require('./db')
+
 module.exports.pubsubManager = require('./graphql/pubsub')
 module.exports.NotFoundError = require('./errors/NotFoundError')
+module.exports.ConflictError = require('./errors/ConflictError')
+module.exports.ValidationError = require('./errors/ValidationError')
+module.exports.AuthorizationError = require('./errors/AuthorizationError')
 
 module.exports.startServer = require('./start-server')
 
+// Authorization helpers
+// All models are loaded here, be careful with circular dependencies
+module.exports.helpers = require('./helpers/authorization')
+module.exports.util = require('./routes/util')
+
 // Jobs queue
 module.exports.jobs = {
   connectToJobQueue: require('./jobs').connectToJobQueue,
diff --git a/packages/server/src/models/Fragment.js b/packages/server/src/models/Fragment.js
deleted file mode 100644
index ceec14741b56e13941473b7c77d028bc8166b890..0000000000000000000000000000000000000000
--- a/packages/server/src/models/Fragment.js
+++ /dev/null
@@ -1,19 +0,0 @@
-const Model = require('./Model')
-const Team = require('./Team')
-
-class Fragment extends Model {
-  constructor(properties) {
-    super(properties)
-    this.type = 'fragment'
-    this.owners = this.owners || []
-  }
-
-  async delete() {
-    await Team.deleteAssociated(this.type, this.id)
-    return super.delete()
-  }
-}
-
-Fragment.type = 'fragment'
-
-module.exports = Fragment
diff --git a/packages/server/src/models/Model.js b/packages/server/src/models/Model.js
deleted file mode 100644
index df69ae3ded73988283893f7a5fc4a1a9b71c7fe3..0000000000000000000000000000000000000000
--- a/packages/server/src/models/Model.js
+++ /dev/null
@@ -1,183 +0,0 @@
-const uuid = require('uuid')
-const Joi = require('joi')
-const config = require('config')
-const { Client } = require('pg')
-
-const logger = require('@pubsweet/logger')
-const db = require('../db')
-const NotFoundError = require('../errors/NotFoundError')
-const ValidationError = require('../errors/ValidationError')
-
-let validations
-if (config.validations) {
-  const appValidations = require(config.validations)
-  validations = require('./validations')(appValidations)
-} else {
-  validations = require('./validations')()
-}
-
-class Model {
-  constructor(properties) {
-    Object.assign(this, properties)
-  }
-
-  static validations() {
-    return validations[this.type]
-  }
-
-  validate() {
-    const validation = Joi.validate(this, this.constructor.validations())
-
-    if (validation.error) {
-      logger.error(validation.error)
-      throw validation.error
-    }
-
-    return true
-  }
-
-  async save() {
-    let isNew = false
-    if (!this.id) {
-      isNew = true
-      this.id = Model.uuid()
-    }
-
-    this.validate()
-
-    const data = { ...this }
-    // remove id and any custom toJSON function
-    delete data.id
-    delete data.toJSON
-
-    if (isNew) {
-      logger.debug('Saving', this.type, this.id)
-      await db.raw('INSERT INTO entities (id, data) VALUES (?, ?)', [
-        this.id,
-        data,
-      ])
-    } else {
-      logger.debug('Updating', this.type, this.id)
-      await db.raw('UPDATE entities SET data = ? WHERE id = ?', [data, this.id])
-    }
-
-    return this
-  }
-
-  async delete() {
-    await db.raw(`DELETE FROM entities WHERE data->>'type' = ? AND id = ?`, [
-      this.type,
-      this.id,
-    ])
-    logger.debug('Deleted', this.type, this.id)
-    return this
-  }
-
-  async updateProperties(properties) {
-    // These properties are modified through setters
-    delete properties.owners
-
-    logger.debug('Updating properties to', properties)
-
-    const validation = Joi.validate(properties, {}, { allowUnknown: true })
-    if (validation.error) throw validation.error
-
-    Object.assign(this, properties)
-    return this
-  }
-
-  setOwners(owners) {
-    if (Array.isArray(owners)) {
-      owners.forEach(owner => this.constructor.validateOwner(owner))
-      this.owners = owners
-    } else {
-      throw new ValidationError('owners should be an array')
-    }
-  }
-
-  static validateOwner(owner) {
-    if (typeof owner !== 'string')
-      throw new ValidationError('owner should be an id')
-  }
-
-  isOwner(userId) {
-    return Array.isArray(this.owners) && this.owners.includes(userId)
-  }
-
-  static uuid() {
-    return uuid.v4()
-  }
-
-  // Find all of a certain type e.g.
-  // User.all()
-  static async all() {
-    const { rows } = await db.raw(
-      `SELECT * FROM entities WHERE data->>'type' = ?`,
-      [this.type],
-    )
-    return rows.map(result => new this({ id: result.id, ...result.data }))
-  }
-
-  // Find by id e.g.
-  // User.find('394')
-  static async find(id) {
-    const { rows } = await db.raw(
-      `SELECT * FROM entities WHERE data->>'type' = ? AND id = ?`,
-      [this.type, id],
-    )
-
-    if (rows.length === 0) {
-      throw new NotFoundError(`Object not found: ${this.type} with id ${id}`)
-    }
-
-    return new this({ id: rows[0].id, ...rows[0].data })
-  }
-
-  // map dotted paths into JSONB accessors: one.two => data->'one'->>'two'
-  static selectorToSql(selector) {
-    const keys = Object.keys(selector).map(key => {
-      const parts = key.split('.').map(Client.prototype.escapeLiteral)
-      parts.unshift('data')
-      const last = parts.pop()
-      return `${parts.join('->')}->>${last}`
-    })
-    return keys.map((accessor, index) => `${accessor} = ?`)
-  }
-
-  // `field` is a string
-  // `value` is a primitive, or a query object
-  // or
-  // `field` is an object of field, value pairs
-  static async findByField(field, value) {
-    logger.debug('Finding', field, value)
-
-    const selector = {
-      type: this.type,
-    }
-
-    if (value !== undefined) {
-      selector[field] = value
-    } else {
-      Object.assign(selector, field)
-    }
-    const where = this.selectorToSql(selector)
-    const { rows } = await db.raw(
-      `SELECT id, data FROM entities WHERE ${where.join(' AND ')}`,
-      Object.values(selector),
-    )
-
-    if (!rows.length) {
-      throw new NotFoundError()
-    }
-
-    return rows.map(result => new this({ id: result.id, ...result.data }))
-  }
-
-  static async findOneByField(field, value) {
-    const results = await this.findByField(field, value)
-
-    return results.length ? results[0] : null
-  }
-}
-
-module.exports = Model
diff --git a/packages/server/src/models/index.js b/packages/server/src/models/index.js
index f732194641da7d26519757a34d1b0b7bb5e96503..284b0687a7cc9b79d2b7b67ed961b7a1fd72583e 100644
--- a/packages/server/src/models/index.js
+++ b/packages/server/src/models/index.js
@@ -1,12 +1,6 @@
 const config = require('config')
 
-// core models
-const models = {
-  Collection: './Collection',
-  Fragment: './Fragment',
-  User: './User',
-  Team: './Team',
-}
+const models = {}
 
 Object.keys(models).forEach((key, _) => {
   module.exports[key] = require(models[key])
diff --git a/packages/server/src/models/validations.js b/packages/server/src/models/validations.js
deleted file mode 100644
index a0e861bb9daed8a02f3e67472c0b6dad4356365f..0000000000000000000000000000000000000000
--- a/packages/server/src/models/validations.js
+++ /dev/null
@@ -1,116 +0,0 @@
-// This module is used for communicating validation requirements to the
-// client and server, it sits in the middle.
-
-const Joi = require('joi')
-const { merge, flatten } = require('lodash')
-
-// These are fixed/required validations, they are combined with configurable
-// validations later
-
-const validations = {
-  fragment: {
-    id: Joi.string()
-      .guid()
-      .required(),
-    type: Joi.string().required(),
-    fragmentType: Joi.string().required(),
-    fragments: Joi.array().items(Joi.string().guid()),
-    owners: Joi.array().items(Joi.string().guid()),
-  },
-  collection: {
-    id: Joi.string().guid(),
-    type: Joi.string().required(),
-    owners: Joi.array().items(Joi.string().guid()),
-    fragments: Joi.array().items(
-      Joi.alternatives().try(
-        // a fragment ID
-        Joi.string().guid(),
-        // or a fragment object
-        Joi.object({ type: Joi.string().valid('fragment') }).unknown(true),
-      ),
-    ),
-    created: Joi.date(),
-  },
-  user: {
-    id: Joi.string()
-      .guid()
-      .required(),
-    type: Joi.string(),
-    username: Joi.string()
-      .alphanum()
-      .required(),
-    email: Joi.string()
-      .email()
-      .required(),
-    passwordHash: Joi.string().required(),
-    admin: Joi.boolean(),
-    fragments: Joi.array().items(Joi.string().guid()),
-    collections: Joi.array().items(Joi.string().guid()),
-    teams: Joi.array().items(Joi.string().guid()),
-    passwordResetToken: Joi.string(),
-    passwordResetTimestamp: Joi.date().timestamp(),
-  },
-  team: {
-    id: Joi.string()
-      .guid()
-      .required(),
-    type: Joi.string().required(),
-    name: Joi.string().required(),
-    object: Joi.object(),
-    teamType: Joi.string().required(),
-    members: Joi.array().items(Joi.string().guid()),
-    owners: Joi.array().items(Joi.string().guid()),
-    global: Joi.boolean(),
-  },
-}
-
-const allValidations = (type, extendedValidations) => {
-  // If there are several source of extended validations,
-  // deep merge them.
-  if (Array.isArray(extendedValidations)) {
-    extendedValidations = merge(...extendedValidations)
-  }
-
-  let extendedValidationsForType = {}
-
-  if (extendedValidations && extendedValidations[type]) {
-    extendedValidationsForType = extendedValidations[type]
-  }
-
-  if (Array.isArray(extendedValidationsForType)) {
-    const alternatives = extendedValidationsForType.map(alternative => ({
-      ...validations[type],
-      ...alternative,
-    }))
-    return Joi.alternatives().try(...alternatives)
-  }
-
-  return Joi.object().keys({
-    ...validations[type],
-    ...extendedValidationsForType,
-  })
-}
-
-module.exports = extendedValidations => {
-  const coreModels = Object.keys(validations)
-
-  if (!extendedValidations) {
-    extendedValidations = []
-  } else if (!Array.isArray(extendedValidations)) {
-    extendedValidations = [extendedValidations]
-  }
-
-  const externalModels = [
-    ...new Set(
-      flatten(extendedValidations.map(validations => Object.keys(validations))),
-    ),
-  ]
-
-  const models = coreModels.concat(externalModels)
-
-  const exported = {}
-  models.forEach(model => {
-    exported[model] = allValidations(model, extendedValidations)
-  })
-  return exported
-}
diff --git a/packages/server/src/register-components.js b/packages/server/src/register-components.js
index 2fc64877c0bc4a92826c075a088e4bb8de4b2f60..6cefd6030c7a9e5aab182c12bbc8a63be9e51c8a 100644
--- a/packages/server/src/register-components.js
+++ b/packages/server/src/register-components.js
@@ -4,16 +4,23 @@ const config = require('config')
 const requireRelative = m =>
   require(require.resolve(m, { paths: [process.cwd()] }))
 
+const registerRecursively = (app, componentName) => {
+  const component = requireRelative(componentName)
+  logger.info('Registered component', componentName)
+  const serverComponent = component.server || component.backend
+  if (serverComponent) {
+    serverComponent()(app)
+    logger.info('Registered server component', componentName)
+  }
+  if (component.extending) {
+    registerRecursively(app, component.extending)
+  }
+}
+
 module.exports = app => {
   if (config.has('pubsweet.components')) {
-    config.get('pubsweet.components').forEach(name => {
-      const component = requireRelative(name)
-      logger.info('Registered component', name)
-      const serverComponent = component.server || component.backend
-      if (serverComponent) {
-        serverComponent()(app)
-        logger.info('Registered server component', name)
-      }
+    config.get('pubsweet.components').forEach(componentName => {
+      registerRecursively(app, componentName)
     })
   }
 }
diff --git a/packages/server/src/routes/api.js b/packages/server/src/routes/api.js
index 0e63d524873fe1cd1667b357694153ff55919816..29c3816258db8bf4c1117cffa31ba03d3e82b383 100644
--- a/packages/server/src/routes/api.js
+++ b/packages/server/src/routes/api.js
@@ -5,29 +5,9 @@ const api = express.Router({ mergeParams: true })
 
 api.use(helmet())
 
-// Collections
-const collections = require('./api_collections')
-
-api.use(collections)
-
-// Fragments
-const fragments = require('./api_fragments')
-
-api.use(fragments)
-
 // File upload API
 const upload = require('./api_upload')
 
 api.use(upload)
 
-// Users API
-const users = require('./api_users')
-
-api.use(users)
-
-// Teams
-const teams = require('./api_teams')
-
-api.use(teams)
-
 module.exports = api
diff --git a/packages/server/src/routes/api_collections.js b/packages/server/src/routes/api_collections.js
deleted file mode 100644
index 23d42419ae7527cc4c4b72835bacecb2550bae02..0000000000000000000000000000000000000000
--- a/packages/server/src/routes/api_collections.js
+++ /dev/null
@@ -1,169 +0,0 @@
-const express = require('express')
-const STATUS = require('http-status-codes')
-const sse = require('pubsweet-sse')
-const passport = require('passport')
-const { Collection, Team, User } = require('../models')
-
-const {
-  createFilterFromQuery,
-  objectId,
-  buildChangeData,
-  fieldSelector,
-  getTeams,
-  applyPermissionFilter,
-} = require('./util')
-
-const api = express.Router()
-
-const authBearer = passport.authenticate('bearer', { session: false })
-const authBearerAndPublic = passport.authenticate(['bearer', 'anonymous'], {
-  session: false,
-})
-
-// List collections
-api.get('/collections', authBearerAndPublic, async (req, res, next) => {
-  try {
-    const collections = await Collection.all()
-    const filteredCollections = await applyPermissionFilter({
-      req,
-      target: req.route,
-      filterable: collections,
-    })
-
-    const collectionsWithSelectedFields = (await Promise.all(
-      filteredCollections.map(async collection => {
-        collection.owners = await User.ownersWithUsername(collection)
-        const properties = await applyPermissionFilter({
-          req,
-          target: collection,
-        })
-        return fieldSelector(req)(properties)
-      }),
-    )).filter(createFilterFromQuery(req.query))
-
-    res.status(STATUS.OK).json(collectionsWithSelectedFields)
-  } catch (err) {
-    next(err)
-  }
-})
-
-// Create a collection
-api.post('/collections', authBearer, async (req, res, next) => {
-  try {
-    const properties = await applyPermissionFilter({
-      req,
-      target: req.route,
-      filterable: req.body,
-    })
-
-    const collection = new Collection(properties)
-    collection.created = Date.now()
-    collection.setOwners([req.user])
-
-    await collection.save()
-
-    // TODO: filter the output?
-
-    res.status(STATUS.CREATED).json(collection)
-    sse.send({ action: 'collection:create', data: { collection } })
-  } catch (err) {
-    next(err)
-  }
-})
-
-// Retrieve a collection
-api.get(
-  '/collections/:collectionId',
-  authBearerAndPublic,
-  async (req, res, next) => {
-    try {
-      const collection = await Collection.find(req.params.collectionId)
-      collection.owners = await User.ownersWithUsername(collection)
-      const properties = await applyPermissionFilter({
-        req,
-        target: collection,
-      })
-
-      res.status(STATUS.OK).json(properties)
-    } catch (err) {
-      next(err)
-    }
-  },
-)
-
-// Update a collection
-api.patch('/collections/:collectionId', authBearer, async (req, res, next) => {
-  try {
-    const collection = await Collection.find(req.params.collectionId)
-    const currentAndUpdate = { current: collection, update: req.body }
-    const properties = await applyPermissionFilter({
-      req,
-      target: currentAndUpdate,
-      filterable: req.body,
-    })
-
-    await collection.updateProperties(properties)
-    await collection.save()
-
-    const update = buildChangeData(properties, collection)
-
-    res.status(STATUS.OK).json(update)
-    sse.send({
-      action: 'collection:patch',
-      data: { collection: objectId(collection), update },
-    })
-  } catch (err) {
-    next(err)
-  }
-})
-
-// Delete a collection
-api.delete('/collections/:collectionId', authBearer, async (req, res, next) => {
-  try {
-    const collection = await Collection.find(req.params.collectionId)
-    const output = await applyPermissionFilter({ req, target: collection })
-
-    // TODO: filter the output, or return nothing?
-
-    await collection.delete()
-
-    res.status(STATUS.OK).json(output)
-    sse.send({
-      action: 'collection:delete',
-      data: { collection: objectId(collection) },
-    })
-  } catch (err) {
-    next(err)
-  }
-})
-
-// Retrieve teams for a collection
-api.get(
-  '/collections/:collectionId/teams',
-  authBearerAndPublic,
-  async (req, res, next) => {
-    const collection = await Collection.find(req.params.collectionId)
-    await applyPermissionFilter({ req, target: collection })
-
-    try {
-      const teams = (await getTeams({
-        req,
-        Team,
-        id: collection.id,
-        type: 'collection',
-      })).filter(createFilterFromQuery(req.query))
-
-      res.status(STATUS.OK).json(teams)
-    } catch (err) {
-      next(err)
-    }
-  },
-)
-
-// Teams
-// TODO: Nested teams API to be deprecated
-const teams = require('./api_teams')
-
-api.use('/collections/:collectionId/', teams)
-
-module.exports = api
diff --git a/packages/server/src/routes/api_fragments.js b/packages/server/src/routes/api_fragments.js
deleted file mode 100644
index cdc9ca8af928d79cd4b6feffdd867419d8d8d65b..0000000000000000000000000000000000000000
--- a/packages/server/src/routes/api_fragments.js
+++ /dev/null
@@ -1,362 +0,0 @@
-const models = require('../models')
-const authsome = require('../helpers/authsome')
-
-const { User, Collection, Team, Fragment } = models
-const AuthorizationError = require('../errors/AuthorizationError')
-const STATUS = require('http-status-codes')
-const express = require('express')
-const sse = require('pubsweet-sse')
-const passport = require('passport')
-
-const api = express.Router()
-
-const {
-  objectId,
-  createFilterFromQuery,
-  buildChangeData,
-  fieldSelector,
-  authorizationError,
-  getTeams,
-  applyPermissionFilter,
-  getFragment,
-} = require('./util')
-
-const authBearer = passport.authenticate('bearer', { session: false })
-const authBearerAndPublic = passport.authenticate(['bearer', 'anonymous'], {
-  session: false,
-})
-
-// Create a fragment and update the collection with the fragment
-api.post(
-  '/collections/:collectionId/fragments',
-  authBearer,
-  async (req, res, next) => {
-    try {
-      const collection = await Collection.find(req.params.collectionId)
-
-      const object = {
-        path: req.route.path,
-        collection,
-        fragment: req.body,
-      }
-
-      const filteredProperties = await applyPermissionFilter({
-        req,
-        target: object,
-        filterable: req.body,
-      })
-
-      const fragment = new Fragment(filteredProperties)
-
-      fragment.setOwners([req.user])
-      await fragment.save()
-
-      collection.addFragment(fragment)
-      await collection.save()
-
-      fragment.owners = await User.ownersWithUsername(fragment)
-
-      res.status(STATUS.CREATED).json(fragment)
-      sse.send({
-        action: 'fragment:create',
-        data: { collection: objectId(collection), fragment },
-      })
-    } catch (err) {
-      next(err)
-    }
-  },
-)
-
-// Get all fragments
-api.get(
-  '/collections/:collectionId/fragments',
-  authBearerAndPublic,
-  async (req, res, next) => {
-    try {
-      const collection = await Collection.find(req.params.collectionId)
-      let fragments = await collection.getFragments()
-
-      // Filter fragments and their properties
-      fragments = await Promise.all(
-        fragments.map(async fragment => {
-          try {
-            return await applyPermissionFilter({ req, target: fragment })
-          } catch (e) {
-            if (e instanceof AuthorizationError) {
-              return undefined
-            }
-
-            throw e
-          }
-        }),
-      )
-
-      fragments = fragments
-        .filter(fragment => fragment !== undefined)
-        .filter(createFilterFromQuery(req.query))
-
-      // Decorate owners with usernames
-      await Promise.all(
-        fragments.map(async fragment => {
-          fragment.owners = await User.ownersWithUsername(fragment)
-        }),
-      )
-
-      fragments = fragments.map(fieldSelector(req))
-
-      res.status(STATUS.OK).json(fragments)
-    } catch (err) {
-      next(err)
-    }
-  },
-)
-
-// Retrieve a fragment
-api.get(
-  '/collections/:collectionId/fragments/:fragmentId',
-  authBearerAndPublic,
-  async (req, res, next) => {
-    try {
-      const fragment = await getFragment({ req, Collection, Fragment })
-      fragment.owners = await User.ownersWithUsername(fragment)
-      const properties = await applyPermissionFilter({ req, target: fragment })
-
-      res.status(STATUS.OK).json(properties)
-    } catch (err) {
-      res.status(STATUS.NOT_FOUND).json(err.message)
-    }
-  },
-)
-
-// Update a fragment
-api.patch(
-  '/collections/:collectionId/fragments/:fragmentId',
-  authBearer,
-  async (req, res, next) => {
-    try {
-      const fragment = await getFragment({ req, Collection, Fragment })
-      const currentAndUpdate = { current: fragment, update: req.body }
-      const properties = await applyPermissionFilter({
-        req,
-        target: currentAndUpdate,
-        filterable: req.body,
-      })
-
-      await fragment.updateProperties(properties)
-      await fragment.save()
-      fragment.owners = await User.ownersWithUsername(fragment)
-
-      const update = buildChangeData(properties, fragment)
-
-      res.status(STATUS.OK).json(update)
-      sse.send({
-        action: 'fragment:patch',
-        data: { fragment: objectId(fragment), update },
-      })
-    } catch (err) {
-      next(err)
-    }
-  },
-)
-
-// Delete a fragment
-api.delete(
-  '/collections/:collectionId/fragments/:fragmentId',
-  authBearer,
-  async (req, res, next) => {
-    try {
-      const collection = await Collection.find(req.params.collectionId)
-      const fragment = await getFragment({ req, Collection, Fragment })
-      await applyPermissionFilter({ req, target: fragment })
-
-      await fragment.delete()
-      collection.removeFragment(fragment)
-      await collection.save()
-
-      res.status(STATUS.OK).json(fragment)
-      sse.send({
-        action: 'fragment:delete',
-        data: { collection: objectId(collection), fragment },
-      })
-    } catch (err) {
-      next(err)
-    }
-  },
-)
-
-// Retrieve teams for a fragment
-api.get(
-  '/collections/:collectionId/fragments/:fragmentId/teams',
-  authBearerAndPublic,
-  async (req, res, next) => {
-    try {
-      const fragment = await getFragment({ req, Collection, Fragment })
-      await applyPermissionFilter({ req, target: fragment })
-
-      const teams = (await getTeams({
-        req,
-        Team,
-        id: fragment.id,
-        type: 'fragment',
-      })).filter(createFilterFromQuery(req.query))
-
-      res.status(STATUS.OK).json(teams)
-    } catch (err) {
-      next(err)
-    }
-  },
-)
-
-// Get all fragments
-api.get('/fragments', authBearerAndPublic, async (req, res, next) => {
-  try {
-    const fragments = (await Fragment.all()).filter(
-      createFilterFromQuery(req.query),
-    )
-
-    // Filter fragments and their properties
-    const propertyFilter = fieldSelector(req)
-    const filteredFragments = await Promise.all(
-      fragments.map(async fragment => {
-        try {
-          return await applyPermissionFilter({
-            req,
-            target: propertyFilter(fragment),
-          })
-        } catch (e) {
-          if (e instanceof AuthorizationError) {
-            return undefined
-          }
-
-          throw e
-        }
-      }),
-    )
-
-    res.status(STATUS.OK).json(filteredFragments)
-  } catch (err) {
-    next(err)
-  }
-})
-
-api.post('/fragments', authBearer, async (req, res, next) => {
-  try {
-    const permission = await authsome.can(req.user, req.method, {
-      path: req.route.path,
-      fragment: req.body,
-    })
-
-    if (!permission) {
-      throw authorizationError(req.user, req.method, req.body)
-    }
-
-    if (permission.filter) {
-      req.body = permission.filter(req.body)
-    }
-
-    let fragment = new Fragment(req.body)
-
-    fragment.setOwners([req.user])
-    fragment = await fragment.save()
-
-    // How to address this?
-    fragment.owners = await User.ownersWithUsername(fragment)
-
-    res.status(STATUS.CREATED).json(fragment)
-    sse.send({ action: 'fragment:create', data: { fragment } })
-  } catch (err) {
-    next(err)
-  }
-})
-
-// Retrieve a fragment
-api.get(
-  '/fragments/:fragmentId',
-  authBearerAndPublic,
-  async (req, res, next) => {
-    try {
-      let fragment = await Fragment.find(req.params.fragmentId)
-      const permission = await authsome.can(req.user, req.method, fragment)
-
-      if (!permission) {
-        throw authorizationError(req.user, req.method, fragment)
-      }
-
-      if (permission.filter) {
-        fragment = permission.filter(fragment)
-      }
-      res.status(STATUS.OK).json(fragment)
-    } catch (err) {
-      next(err)
-    }
-  },
-)
-
-// Update a fragment
-api.patch('/fragments/:fragmentId', authBearer, async (req, res, next) => {
-  try {
-    let fragment = await Fragment.find(req.params.fragmentId)
-
-    const currentAndUpdate = { current: fragment, update: req.body }
-    const properties = await applyPermissionFilter({
-      req,
-      target: currentAndUpdate,
-      filterable: req.body,
-    })
-
-    fragment.updateProperties(properties)
-    fragment = await fragment.save()
-    fragment.owners = await User.ownersWithUsername(fragment)
-
-    const update = buildChangeData(properties, fragment)
-    res.status(STATUS.OK).json(update)
-
-    sse.send({
-      action: 'fragment:patch',
-      data: { fragment: objectId(fragment), update },
-    })
-  } catch (err) {
-    next(err)
-  }
-})
-
-// Delete a fragment
-api.delete('/fragments/:fragmentId', authBearer, async (req, res, next) => {
-  try {
-    let fragment = await Fragment.find(req.params.fragmentId)
-    const permission = await authsome.can(req.user, req.method, fragment)
-
-    if (!permission) {
-      throw authorizationError(req.user, req.method, fragment)
-    }
-
-    fragment = await fragment.delete()
-
-    res.status(STATUS.OK).json(fragment)
-    sse.send({ action: 'fragment:delete', data: { fragment } })
-  } catch (err) {
-    next(err)
-  }
-})
-
-// Retrieve teams for a fragment
-api.get(
-  '/fragments/:fragmentId/teams',
-  authBearerAndPublic,
-  async (req, res, next) => {
-    try {
-      const teams = (await getTeams({
-        req,
-        Team,
-        id: req.params.fragmentId,
-        type: 'fragment',
-      })).filter(createFilterFromQuery(req.query))
-
-      res.status(STATUS.OK).json(teams)
-    } catch (err) {
-      next(err)
-    }
-  },
-)
-
-module.exports = api
diff --git a/packages/server/src/routes/api_teams.js b/packages/server/src/routes/api_teams.js
deleted file mode 100644
index 9256a49f7eec65c2439e76c721e30bde1f80e519..0000000000000000000000000000000000000000
--- a/packages/server/src/routes/api_teams.js
+++ /dev/null
@@ -1,114 +0,0 @@
-const STATUS = require('http-status-codes')
-const express = require('express')
-const passport = require('passport')
-const sse = require('pubsweet-sse')
-
-const {
-  createFilterFromQuery,
-  applyPermissionFilter,
-  buildChangeData,
-  objectId,
-} = require('./util')
-const Team = require('../models/Team')
-
-const authBearer = passport.authenticate('bearer', { session: false })
-const api = express.Router({ mergeParams: true })
-
-api.get('/teams', authBearer, async (req, res, next) => {
-  try {
-    const teams = await Team.all()
-    const filteredTeams = await applyPermissionFilter({
-      req,
-      target: req.route,
-      filterable: teams,
-    })
-
-    filteredTeams.filter(createFilterFromQuery(req.query))
-
-    res.status(STATUS.OK).json(filteredTeams)
-  } catch (err) {
-    next(err)
-  }
-})
-
-api.post('/teams', authBearer, async (req, res, next) => {
-  try {
-    // Teams are either based around objects or not,
-    // we need to know if they are and around which object
-    const target = Object.assign(
-      {},
-      { path: req.route.path },
-      { team: req.body },
-    )
-
-    const properties = await applyPermissionFilter({
-      req,
-      target,
-      filterable: req.body,
-    })
-
-    const team = new Team(properties)
-
-    await team.save()
-
-    res.status(STATUS.CREATED).json(team)
-    sse.send({ action: 'team:create', data: { team } })
-  } catch (err) {
-    next(err)
-  }
-})
-
-api.get('/teams/:teamId', authBearer, async (req, res, next) => {
-  try {
-    const team = await Team.find(req.params.teamId)
-    const properties = await applyPermissionFilter({
-      req,
-      target: team,
-    })
-
-    res.status(STATUS.OK).json(properties)
-  } catch (err) {
-    next(err)
-  }
-})
-
-api.delete('/teams/:teamId', authBearer, async (req, res, next) => {
-  try {
-    const team = await Team.find(req.params.teamId)
-    const output = await applyPermissionFilter({ req, target: team })
-
-    await team.delete()
-
-    res.status(STATUS.OK).json(output)
-    sse.send({ action: 'team:delete', data: { team: objectId(team) } })
-  } catch (err) {
-    next(err)
-  }
-})
-
-api.patch('/teams/:teamId', authBearer, async (req, res, next) => {
-  try {
-    const team = await Team.find(req.params.teamId)
-    const currentAndUpdate = { current: team, update: req.body }
-    const properties = await applyPermissionFilter({
-      req,
-      target: currentAndUpdate,
-      filterable: req.body,
-    })
-
-    await team.updateProperties(properties)
-    await team.save()
-
-    const update = buildChangeData(properties, team)
-
-    res.status(STATUS.OK).json(update)
-    sse.send({
-      action: 'team:patch',
-      data: { team: objectId(team), update },
-    })
-  } catch (err) {
-    next(err)
-  }
-})
-
-module.exports = api
diff --git a/packages/server/src/routes/api_users.js b/packages/server/src/routes/api_users.js
deleted file mode 100644
index 5a9d22bb3bfc08b4a3d55f065a71c92da7f08eea..0000000000000000000000000000000000000000
--- a/packages/server/src/routes/api_users.js
+++ /dev/null
@@ -1,152 +0,0 @@
-const STATUS = require('http-status-codes')
-const passport = require('passport')
-const express = require('express')
-
-const models = require('../models')
-
-const { User } = models
-
-const authsome = require('../helpers/authsome')
-
-const ValidationError = require('../errors/ValidationError')
-
-const authLocal = passport.authenticate('local', {
-  failWithError: true,
-  session: false,
-})
-const authBearer = passport.authenticate('bearer', { session: false })
-const api = express.Router()
-const authentication = require('../authentication')
-
-const {
-  applyPermissionFilter,
-  fieldSelector,
-  createFilterFromQuery,
-  authorizationError,
-} = require('./util')
-
-// Issue a token
-api.post('/users/authenticate', authLocal, async (req, res) => {
-  const user = Object.assign(
-    { token: authentication.token.create(req.user) },
-    req.user,
-  )
-  req.user = req.user.id
-  const properties = await applyPermissionFilter({
-    req,
-    target: user,
-  })
-
-  return res.status(STATUS.CREATED).json(properties)
-})
-
-// Verify a token
-api.get('/users/authenticate', authBearer, async (req, res, next) => {
-  try {
-    const user = await User.find(req.user)
-    user.token = req.authInfo.token
-    res.status(STATUS.OK).json(user)
-  } catch (err) {
-    next(err)
-  }
-})
-
-// Create a user
-api.post('/users', async (req, res, next) => {
-  try {
-    let user = new User(req.body)
-    if (req.body.admin) throw new ValidationError('invalid property: admin')
-
-    user = await user.save()
-    res.status(STATUS.CREATED).json(user)
-  } catch (err) {
-    next(err)
-  }
-})
-
-// List users
-api.get('/users', authBearer, async (req, res, next) => {
-  try {
-    const users = await User.all()
-    const filteredUsers = await applyPermissionFilter({
-      req,
-      target: req.route,
-      filterable: users,
-    })
-
-    const usersWithSelectedFields = (await Promise.all(
-      filteredUsers.map(async user => {
-        const properties = await applyPermissionFilter({
-          req,
-          target: user,
-        })
-        return fieldSelector(req)(properties)
-      }),
-    )).filter(createFilterFromQuery(req.query))
-
-    res.status(STATUS.OK).json({ users: usersWithSelectedFields })
-  } catch (err) {
-    next(err)
-  }
-})
-
-// Get a user
-api.get('/users/:id', authBearer, async (req, res, next) => {
-  try {
-    const user = await User.find(req.params.id)
-    const permission = await authsome.can(req.user, req.method, user)
-
-    const properties = await applyPermissionFilter({
-      req,
-      target: user,
-    })
-
-    if (!permission) {
-      throw authorizationError(req.user, req.method, req.path)
-    }
-
-    res.status(STATUS.OK).json(properties)
-  } catch (err) {
-    next(err)
-  }
-})
-
-// Delete a user
-api.delete('/users/:id', authBearer, async (req, res, next) => {
-  try {
-    let user = await User.find(req.params.id)
-    const permission = await authsome.can(req.user, req.method, user)
-
-    if (!permission) {
-      throw authorizationError(req.user, req.method, req.path)
-    }
-    user = await user.delete()
-    res.status(STATUS.OK).json(user)
-  } catch (err) {
-    next(err)
-  }
-})
-
-// Patch a user
-api.patch('/users/:id', authBearer, async (req, res, next) => {
-  try {
-    let user = await User.find(req.params.id)
-
-    const currentAndUpdate = { current: user, update: req.body }
-    const properties = await applyPermissionFilter({
-      req,
-      target: currentAndUpdate,
-      filterable: req.body,
-    })
-
-    user = await user.updateProperties(properties)
-    user = await user.save()
-    user = await User.find(req.params.id)
-
-    res.status(STATUS.OK).json(user)
-  } catch (err) {
-    next(err)
-  }
-})
-
-module.exports = api
diff --git a/packages/server/src/routes/util.js b/packages/server/src/routes/util.js
index 8edacd31f44818b724f2bacb76214396809beead..36e5facd7168c5e31fa6b59936fdc26e42df992c 100644
--- a/packages/server/src/routes/util.js
+++ b/packages/server/src/routes/util.js
@@ -1,12 +1,16 @@
 const _ = require('lodash')
 const AuthorizationError = require('../errors/AuthorizationError')
 const NotFoundError = require('../errors/NotFoundError')
-const authsome = require('../helpers/authsome')
 
 const Util = {}
 Util.authorizationError = (username, operation, object) => {
+  const errorDetails = object.type
+    ? `${object.type} ${object.id}`
+    : JSON.stringify(object)
+
   username = username || 'public'
-  const msg = `User ${username} is not allowed to ${operation} ${object}`
+  const msg = `User ${username} is not allowed to ${operation} ${errorDetails}`
+
   return new AuthorizationError(msg)
 }
 
@@ -43,12 +47,17 @@ Util.fieldSelector = req => {
 }
 
 Util.getTeams = async opts => {
+  const authsome = require('../helpers/authsome')
+
   let teams
   try {
-    teams = await opts.Team.findByField({
-      'object.id': opts.id,
-      'object.type': opts.type,
-    })
+    teams = await opts.Team.query().where(
+      'object',
+      JSON.stringify({
+        id: opts.id,
+        type: opts.type,
+      }),
+    )
 
     teams = await Promise.all(
       teams.map(async team => {
@@ -112,6 +121,7 @@ Util.getFragment = async opts => {
  * @returns {Promise} The (possibly filtered) target, if permission is granted
  */
 Util.applyPermissionFilter = async opts => {
+  const authsome = require('../helpers/authsome')
   const permission = await authsome.can(
     opts.req.user,
     opts.req.method,
diff --git a/packages/server/src/setup-base.js b/packages/server/src/setup-base.js
index 4f03e235a5a24fffdce8cffb8640893b0a70d3ea..731939d38c5bd5a6a8cba4df3939caabac169128 100644
--- a/packages/server/src/setup-base.js
+++ b/packages/server/src/setup-base.js
@@ -1,8 +1,9 @@
-const { User, Collection } = require('pubsweet-server')
 const logger = require('@pubsweet/logger')
 
 class Setup {
   static async setup(user, collection) {
+    const { Collection, User } = require('pubsweet-server/src/models')
+
     logger.info('Starting setup')
 
     let admin = new User({
@@ -14,7 +15,6 @@ class Setup {
 
     admin = await admin.save()
     logger.info('Created admin user: ', admin)
-
     collection = new Collection(collection)
     collection.setOwners([admin.id])
     collection = await collection.save()
diff --git a/packages/server/src/start-server.js b/packages/server/src/start-server.js
index f5709434118660c8bb307f600af19e01477651f8..138e0ba5ef339b0d317946b40ba18241ff709ae3 100644
--- a/packages/server/src/start-server.js
+++ b/packages/server/src/start-server.js
@@ -39,7 +39,7 @@ const startServer = async (app = express()) => {
     server = undefined
     await stopJobQueue()
     await wait(500)
-    httpServer.originalClose(cb)
+    return httpServer.originalClose(cb)
   }
 
   server = httpServer
diff --git a/packages/server/test/api_admin_test.js b/packages/server/test/api_admin_test.js
index 1c2ab1d285e62e1c249b31e3493aac5adda141d6..4713014247db6d6e31668ddbbd60eb969db230f4 100644
--- a/packages/server/test/api_admin_test.js
+++ b/packages/server/test/api_admin_test.js
@@ -4,8 +4,7 @@ const createBasicCollection = require('./helpers/basic_collection')
 const cleanDB = require('./helpers/db_cleaner')
 const fixtures = require('./fixtures/fixtures')
 
-const User = require('../src/models/User')
-const Fragment = require('../src/models/Fragment')
+const { User, Fragment } = require('../src/models')
 
 const api = require('./helpers/api')
 
diff --git a/packages/server/test/api_authenticated_test.js b/packages/server/test/api_authenticated_test.js
index 56c5ba1bc3eaf1c41e048c222cb79f624f469e62..c23630454e3839f1f0c47e9dea67d24a764ee2c7 100644
--- a/packages/server/test/api_authenticated_test.js
+++ b/packages/server/test/api_authenticated_test.js
@@ -6,8 +6,7 @@ const api = require('./helpers/api')
 const setTeamForCollection = require('./helpers/set_team')
 const fixtures = require('./fixtures/fixtures')
 
-const Fragment = require('../src/models/Fragment')
-const User = require('../src/models/User')
+const { Fragment, User } = require('../src/models')
 
 describe('authenticated api', () => {
   let otherUser
@@ -20,7 +19,7 @@ describe('authenticated api', () => {
     ;({ user, collection } = await createBasicCollection())
     // Create another user without any roles
     otherUser = new User(fixtures.updatedUser)
-    await otherUser.save()
+    otherUser = await otherUser.save()
   })
 
   it(`fails to create a fragment in a protected
@@ -102,7 +101,6 @@ describe('authenticated api', () => {
       let fragment
 
       beforeEach(async () => {
-        const Fragment = require('../src/models/Fragment')
         fragment = new Fragment(fixtures.fragment)
         fragment.setOwners([user.id])
         await fragment.save()
diff --git a/packages/server/test/api_collections_test.js b/packages/server/test/api_collections_test.js
index cfd6f2faa77ca0b156e1a99b1ede38d5d0c7536b..97274f00c3be147323995db450685dbc95a89411 100644
--- a/packages/server/test/api_collections_test.js
+++ b/packages/server/test/api_collections_test.js
@@ -1,11 +1,11 @@
 const STATUS = require('http-status-codes')
 
-const User = require('../src/models/User')
+const { model: User } = require('@pubsweet/model-user')
 const cleanDB = require('./helpers/db_cleaner')
 const fixtures = require('./fixtures/fixtures')
 
 const api = require('./helpers/api')
-const authentication = require('../src/authentication')
+const authentication = require('@pubsweet/model-user/src/authentication')
 
 let adminToken
 let userToken
diff --git a/packages/server/test/api_file_upload_test.js b/packages/server/test/api_file_upload_test.js
index 6f4ead2fb58680fdfa4f05d58d1e42d5e5463e5d..838a37f314728cf2490c1c3c6a597dc4f12fdadc 100644
--- a/packages/server/test/api_file_upload_test.js
+++ b/packages/server/test/api_file_upload_test.js
@@ -3,7 +3,7 @@ const path = require('path')
 const api = require('./helpers/api')
 const fixtures = require('./fixtures/fixtures')
 const cleanDB = require('./helpers/db_cleaner')
-const User = require('../src/models/User')
+const { model: User } = require('@pubsweet/model-user')
 
 function fileName(name) {
   return path.join(__dirname, 'fixtures', name)
diff --git a/packages/server/test/api_fragments_test.js b/packages/server/test/api_fragments_test.js
index d8f8b49ee3c2f2a8b234e8e6fb62e889809ce909..2ae168aaab445d1cf9fe5caa0a2aee64265dd4e1 100644
--- a/packages/server/test/api_fragments_test.js
+++ b/packages/server/test/api_fragments_test.js
@@ -1,6 +1,6 @@
 const STATUS = require('http-status-codes')
 
-const User = require('../src/models/User')
+const { model: User } = require('@pubsweet/model-user')
 
 const cleanDB = require('./helpers/db_cleaner')
 const fixtures = require('./fixtures/fixtures')
diff --git a/packages/server/test/api_locals_test.js b/packages/server/test/api_locals_test.js
index b2fd1efe76e00150ab0904599dcfa59fd66c971b..8201fad1ccf035ece5a25e253958155e6b6c7d3f 100644
--- a/packages/server/test/api_locals_test.js
+++ b/packages/server/test/api_locals_test.js
@@ -1,4 +1,4 @@
-const User = require('../src/models/User')
+const { model: User } = require('@pubsweet/model-user')
 const fixtures = require('./fixtures/fixtures')
 const cleanDB = require('./helpers/db_cleaner')
 const api = require('../src/app')(require('express')())
diff --git a/packages/server/test/api_sse_disabled_test.js b/packages/server/test/api_sse_disabled_test.js
index 0946d7c83cc03fed7f71a6d2acb7833d5184fef2..57af5b7a6adaf520a64132e9cc60f5ef3644285e 100644
--- a/packages/server/test/api_sse_disabled_test.js
+++ b/packages/server/test/api_sse_disabled_test.js
@@ -1,7 +1,7 @@
 const STATUS = require('http-status-codes')
 const EventSource = require('eventsource')
 
-const User = require('../src/models/User')
+const { model: User } = require('@pubsweet/model-user')
 
 const cleanDB = require('./helpers/db_cleaner')
 const fixtures = require('./fixtures/fixtures')
diff --git a/packages/server/test/api_sse_enabled_test.js b/packages/server/test/api_sse_enabled_test.js
index 3173d9223fe67ee70b9da7ea4298c0263bab4b79..4aac00aeb71bb9c8ce3c1b380e06a3394fe6bef6 100644
--- a/packages/server/test/api_sse_enabled_test.js
+++ b/packages/server/test/api_sse_enabled_test.js
@@ -5,7 +5,7 @@ const config = require('config')
 // override config for test
 config['pubsweet-server'].sse = true
 
-const User = require('../src/models/User')
+const { model: User } = require('@pubsweet/model-user')
 
 const cleanDB = require('./helpers/db_cleaner')
 const fixtures = require('./fixtures/fixtures')
diff --git a/packages/server/test/api_teams_test.js b/packages/server/test/api_teams_test.js
index 22a6cfd7c78d07f0d13ae8b8ebd347d24bad0b1e..0b8b6f7a7df2f14eda2e16a62da8933dff79f08a 100644
--- a/packages/server/test/api_teams_test.js
+++ b/packages/server/test/api_teams_test.js
@@ -31,7 +31,6 @@ describe('Teams API - admin', () => {
         expect(team.teamType).toEqual('teamContributors')
         expect(team.members).toEqual([])
       }))
-
   it('should allow retrieval of a team by id', () =>
     new Team(teamFixture)
       .save()
diff --git a/packages/server/test/api_users_test.js b/packages/server/test/api_users_test.js
index 3545ca22c5c0d8cd9f7ba71d265f414fbc2ba31f..33f40b22b1573a0d096f5704864845ff21024e8e 100644
--- a/packages/server/test/api_users_test.js
+++ b/packages/server/test/api_users_test.js
@@ -3,7 +3,7 @@
 const STATUS = require('http-status-codes')
 
 const cleanDB = require('./helpers/db_cleaner')
-const User = require('../src/models/User')
+const { model: User } = require('@pubsweet/model-user')
 const fixtures = require('./fixtures/fixtures')
 const api = require('./helpers/api')
 const setupBase = require('../src/setup-base')
diff --git a/packages/server/test/graphql/authentication_test.js b/packages/server/test/graphql/authentication_test.js
index dd9229b0429c2f05a42841a7b3aae8f3986b2c5b..fa237e4e21df988548429105d9e272873f54cd81 100644
--- a/packages/server/test/graphql/authentication_test.js
+++ b/packages/server/test/graphql/authentication_test.js
@@ -1,10 +1,10 @@
 const { omit } = require('lodash')
 const authsome = require('../../src/helpers/authsome')
-const User = require('../../src/models/User')
+const { model: User } = require('@pubsweet/model-user')
 const cleanDB = require('../helpers/db_cleaner')
 const fixtures = require('../fixtures/fixtures')
 const api = require('../helpers/api')
-const authentication = require('../../src/authentication')
+const authentication = require('@pubsweet/model-user/src/authentication')
 
 describe('GraphQL authentication', () => {
   let token
@@ -99,7 +99,7 @@ describe('GraphQL authentication', () => {
         errors: [
           {
             message:
-              'Object not found: user with id 123e4567-e89b-12d3-a456-426655440000',
+              'Object not found: User with id 123e4567-e89b-12d3-a456-426655440000',
           },
         ],
       })
diff --git a/packages/server/test/graphql/mutations_test.js b/packages/server/test/graphql/mutations_test.js
index 7ab05d2f771961a6ad679ffcdd588b78dba72c74..8ca08ce3b57192043cae5c995f85b9cecf8718d4 100644
--- a/packages/server/test/graphql/mutations_test.js
+++ b/packages/server/test/graphql/mutations_test.js
@@ -1,9 +1,9 @@
-const User = require('../../src/models/User')
-const Team = require('../../src/models/Team')
+const { model: User } = require('@pubsweet/model-user')
+const { model: Team } = require('@pubsweet/model-team')
 const cleanDB = require('../helpers/db_cleaner')
 const fixtures = require('../fixtures/fixtures')
 const api = require('../helpers/api')
-const authentication = require('../../src/authentication')
+const authentication = require('@pubsweet/model-user/src/authentication')
 
 describe('GraphQL core mutations', () => {
   let token
diff --git a/packages/server/test/graphql/queries_test.js b/packages/server/test/graphql/queries_test.js
index 812d73b9ec7a37e5d2691f04fce033288473e98d..21f64cd0ea53bd6100d0cccc175c69536f9d70aa 100644
--- a/packages/server/test/graphql/queries_test.js
+++ b/packages/server/test/graphql/queries_test.js
@@ -1,9 +1,9 @@
-const User = require('../../src/models/User')
-const Team = require('../../src/models/Team')
+const { model: User } = require('@pubsweet/model-user')
+const { model: Team } = require('@pubsweet/model-team')
 const cleanDB = require('../helpers/db_cleaner')
 const fixtures = require('../fixtures/fixtures')
 const api = require('../helpers/api')
-const authentication = require('../../src/authentication')
+const authentication = require('@pubsweet/model-user/src/authentication')
 
 describe('GraphQL core queries', () => {
   let token
diff --git a/packages/server/test/graphql/subscriptions_test.js b/packages/server/test/graphql/subscriptions_test.js
index e177a8250590856bef38017c050b3ab291760352..d670c4d3b7eb3aaccf4a77c5b6578ee4e206a278 100644
--- a/packages/server/test/graphql/subscriptions_test.js
+++ b/packages/server/test/graphql/subscriptions_test.js
@@ -1,7 +1,7 @@
-const User = require('../../src/models/User')
+const { model: User } = require('@pubsweet/model-user')
 const cleanDB = require('../helpers/db_cleaner')
 const fixtures = require('../fixtures/fixtures')
-const authentication = require('../../src/authentication')
+const authentication = require('@pubsweet/model-user/src/authentication')
 const { startServer } = require('../../src')
 
 const WebSocket = require('ws')
diff --git a/packages/server/test/graphql/upload_test.js b/packages/server/test/graphql/upload_test.js
index 3be8823ac670ddd0dd55a01d133f21c33fc1d9b5..827c47e55000e23a62b4c5c81f9c45fa4f7d3e89 100644
--- a/packages/server/test/graphql/upload_test.js
+++ b/packages/server/test/graphql/upload_test.js
@@ -1,8 +1,8 @@
-const User = require('../../src/models/User')
+const { model: User } = require('@pubsweet/model-user')
 const cleanDB = require('../helpers/db_cleaner')
 const fixtures = require('../fixtures/fixtures')
 const api = require('../helpers/api')
-const authentication = require('../../src/authentication')
+const authentication = require('@pubsweet/model-user/src/authentication')
 
 describe('GraphQL uploads', () => {
   let token
diff --git a/packages/server/test/helpers/authsome_mode.js b/packages/server/test/helpers/authsome_mode.js
index 15633ccf965e708a13eba4cd26453a175326621b..eeb97143973db7ebf02ba619f8a6c559b910f687 100644
--- a/packages/server/test/helpers/authsome_mode.js
+++ b/packages/server/test/helpers/authsome_mode.js
@@ -36,7 +36,7 @@ async function teamPermissions(user, operation, object, context) {
 
 function unauthenticatedUser(operation, object) {
   // Public/unauthenticated users can GET /collections, filtered by 'published'
-  if (operation === 'GET' && object && object.path === '/collections') {
+  if (operation === 'GET' && object && object.path === '/api/collections') {
     return {
       filter: collections =>
         collections.filter(collection => collection.published),
@@ -47,7 +47,7 @@ function unauthenticatedUser(operation, object) {
   if (
     operation === 'GET' &&
     object &&
-    object.path === '/collections/:id/fragments'
+    object.path === '/api/collections/:id/fragments'
   ) {
     return {
       filter: fragments => fragments.filter(fragment => fragment.published),
@@ -77,12 +77,16 @@ function unauthenticatedUser(operation, object) {
     }
   }
 
+  if (operation === 'POST' && object && object.path === '/api/users') {
+    return true
+  }
+
   return false
 }
 
 async function authenticatedUser(user, operation, object, context) {
   // Allow the authenticated user to POST a collection (but not with a 'filtered' property)
-  if (operation === 'POST' && object.path === '/collections') {
+  if (operation === 'POST' && object.path === '/api/collections') {
     return {
       filter: collection => omit(collection, 'filtered'),
     }
@@ -107,7 +111,7 @@ async function authenticatedUser(user, operation, object, context) {
 
   // Allow owners of a collection to GET its teams, e.g.
   // GET /api/collections/1/teams
-  if (operation === 'GET' && get(object, 'path') === '/teams') {
+  if (operation === 'GET' && get(object, 'path') === '/api/teams') {
     const collectionId = get(object, 'params.collectionId')
     if (collectionId) {
       const collection = await context.models.Collection.find(collectionId)
@@ -133,7 +137,7 @@ async function authenticatedUser(user, operation, object, context) {
   // Advanced example
   // Allow authenticated users to create a team based around a collection
   // if they are one of the owners of this collection
-  if (get(object, 'path') === '/teams' && operation === 'POST') {
+  if (get(object, 'path') === '/api/teams' && operation === 'POST') {
     if (operation === 'POST') {
       if (get(object, 'team.object.type') === 'collection') {
         const collectionId = get(object, 'team.object.id')
diff --git a/packages/server/test/helpers/fragment.js b/packages/server/test/helpers/fragment.js
index 140554198723beb490238760bc8123e8b17324e1..96f05e7989ddb1bc69f86aa39e67b69a98eed279 100644
--- a/packages/server/test/helpers/fragment.js
+++ b/packages/server/test/helpers/fragment.js
@@ -1,5 +1,5 @@
 const fixtures = require('../fixtures/fixtures')
-const Fragment = require('../../src/models/Fragment')
+const { Fragment } = require('../../src/models')
 const assign = require('lodash/assign')
 
 module.exports = (opts, collection) => {
diff --git a/packages/server/test/model_test.js b/packages/server/test/model_test.js
index ce827e82753135be8a3c8b353842fd8702940ff5..06902854aa502b57c0d0611c794a4048aa0303f6 100644
--- a/packages/server/test/model_test.js
+++ b/packages/server/test/model_test.js
@@ -1,5 +1,6 @@
 const STATUS = require('http-status-codes')
-const { User, Fragment, Collection } = require('../src/models')
+const { Fragment, Collection } = require('../src/models')
+const { model: User } = require('@pubsweet/model-user')
 const dbCleaner = require('./helpers/db_cleaner')
 const fixtures = require('./fixtures/fixtures')
 
@@ -45,9 +46,7 @@ describe('Model', () => {
     expect.hasAssertions()
     return user.save().catch(err => {
       expect(err.name).toEqual('ValidationError')
-      expect(err.message).toEqual(
-        'child "email" fails because ["email" must be a valid email]',
-      )
+      expect(err.message).toEqual('email: should match format "email"')
     })
   })
 
@@ -58,18 +57,10 @@ describe('Model', () => {
     expect.hasAssertions()
     return fragment.save().catch(err => {
       expect(err.name).toEqual('ValidationError')
-      expect(err.message).toEqual(
-        'child "fragmentType" fails because ["fragmentType" must be one of [blogpost]], child "path" fails because ["path" is required]',
-      )
+      expect(err.message).toEqual('fragmentType: should be equal to constant')
     })
   })
 
-  it('accepts a fragment with alternative fragmentType', () => {
-    const fragment = new Fragment({ fragmentType: 'file', path: '/one/two' })
-
-    return fragment.save()
-  })
-
   // TODO re-enable test once we switch to proper uniqueness constraints
   it.skip('saving the same object multiple times in parallel throws conflict error', async () => {
     expect.hasAssertions()
@@ -91,17 +82,4 @@ describe('Model', () => {
       email: 'test@example.com',
     })
   })
-
-  it('turns an object selector into SQL clauses', () => {
-    expect(User.selectorToSql({ foo: 'bar', 'do.re.mi': 'fa so la' })).toEqual([
-      "data->>'foo' = ?",
-      "data->'do'->'re'->>'mi' = ?",
-    ])
-  })
-
-  it('escapes naughty names', () => {
-    expect(
-      User.selectorToSql({ "Robert'); DROP TABLE Students; --": '' }),
-    ).toEqual(["data->>'Robert''); DROP TABLE Students; --' = ?"])
-  })
 })
diff --git a/packages/server/test/register_components_test.js b/packages/server/test/register_components_test.js
index 08b3794557544aba1e577009fa82120d06ef3b9e..6acf1c05531ea4bb92dc8eb34035fcc2f19ffa47 100644
--- a/packages/server/test/register_components_test.js
+++ b/packages/server/test/register_components_test.js
@@ -3,7 +3,7 @@ const path = require('path')
 const config = require('config')
 
 const mockComponentPath = path.resolve(__dirname, 'mocks', 'mock_component.js')
-config.pubsweet = { components: [mockComponentPath] }
+config.pubsweet.components.push(mockComponentPath)
 
 const api = require('./helpers/api')
 
diff --git a/packages/server/test/token_test.js b/packages/server/test/token_test.js
index 5b8209752ad43105c0d3015cd7a7bcfbf9d19cf1..c851a35207e75c63762816cc72dfe625332cce2c 100644
--- a/packages/server/test/token_test.js
+++ b/packages/server/test/token_test.js
@@ -2,7 +2,7 @@ describe('token management', () => {
   it('creates and verifies a token', done => {
     const {
       token: { create: createToken, verify: verifyToken },
-    } = require('../src/authentication')
+    } = require('@pubsweet/model-user/src/authentication')
 
     const token = createToken({ id: 1, username: 'test' })
 
@@ -20,7 +20,7 @@ describe('token management', () => {
   it('does not verify an expired token', done => {
     const {
       token: { create: createToken, verify: verifyToken },
-    } = require('../src/authentication')
+    } = require('@pubsweet/model-user/src/authentication')
 
     const token = createToken({ id: 1, username: 'test' })
 
@@ -48,7 +48,7 @@ describe('token management', () => {
 
     const {
       token: { create: createToken, verify: verifyToken },
-    } = require('../src/authentication')
+    } = require('@pubsweet/model-user/src/authentication')
 
     const token = createToken({ id: 1, username: 'test' })
 
diff --git a/packages/server/test/user_test.js b/packages/server/test/user_test.js
index 813da2ab3b33d15c3756bca1d88e6d51beeb3bc5..46849b968f06581b0cb0065906d6fa3325d362c6 100644
--- a/packages/server/test/user_test.js
+++ b/packages/server/test/user_test.js
@@ -1,5 +1,5 @@
 const dbCleaner = require('./helpers/db_cleaner')
-const User = require('../src/models/User')
+const { model: User } = require('@pubsweet/model-user')
 
 const fixtures = require('./fixtures/fixtures')
 
diff --git a/packages/server/test/validations_test.js b/packages/server/test/validations_test.js
deleted file mode 100644
index 181537c852ec93fb35251ed9ff56d7c47ad1f11f..0000000000000000000000000000000000000000
--- a/packages/server/test/validations_test.js
+++ /dev/null
@@ -1,37 +0,0 @@
-const Fragment = require('../src/models/Fragment')
-const config = require('config')
-
-const appValidations = require(config.validations)
-const validations = require('../src/models/validations')(appValidations)
-
-describe('Validations export', () => {
-  it('has validations for each type', () => {
-    expect(Object.keys(validations).sort()).toEqual([
-      'collection',
-      'fragment',
-      'team',
-      'user',
-    ])
-  })
-
-  it('allows fragment with required fields', () => {
-    const fragment = new Fragment({
-      // ID is generated on save so provide it explicitly here
-      id: '06521f58-9740-4f38-bd25-f00e528cbb2d',
-      title: 'Testing',
-      fragmentType: 'blogpost',
-    })
-
-    expect(fragment.validate()).toBe(true)
-  })
-
-  it('rejects fragment with missing type', () => {
-    const fragment = new Fragment({
-      id: '06521f58-9740-4f38-bd25-f00e528cbb2d',
-      title: 'Testing',
-    })
-    fragment.type = undefined
-
-    expect(() => fragment.validate()).toThrow('"type" is required')
-  })
-})
diff --git a/packages/styleguide/styleguide.config.js b/packages/styleguide/styleguide.config.js
index d68302a630fcf2061d5f7fb5f3b0912369a5fc36..c0a55564bdff55c1c3efc93c40a46c71841c31a7 100644
--- a/packages/styleguide/styleguide.config.js
+++ b/packages/styleguide/styleguide.config.js
@@ -38,7 +38,6 @@ module.exports = {
     '**/components/**/*.config.js',
     '**/components/*-server/**',
     '**/components/MediumDraft/CustomImageSideButton.jsx',
-    '**/components/FormGroup/**',
     '**/node_modules/**',
     '**/*.test.{js,jsx}',
   ],
diff --git a/yarn.lock b/yarn.lock
index 3954aedf46f728bd28910e87066633cbab70125c..c4715881cca894dbd26afebaf92f771c61457b1e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -417,6 +417,565 @@
   dependencies:
     humps "^2.0.1"
 
+"@lerna/add@^3.4.1":
+  version "3.4.1"
+  resolved "https://registry.yarnpkg.com/@lerna/add/-/add-3.4.1.tgz#d41068317e30f530df48220d256b5e79690b1877"
+  integrity sha512-Vf54B42jlD6G52qnv/cAGH70cVQIa+LX//lfsbkxHvzkhIqBl5J4KsnTOPkA9uq3R+zP58ayicCHB9ReiEWGJg==
+  dependencies:
+    "@lerna/bootstrap" "^3.4.1"
+    "@lerna/command" "^3.3.0"
+    "@lerna/filter-options" "^3.3.2"
+    "@lerna/npm-conf" "^3.4.1"
+    "@lerna/validation-error" "^3.0.0"
+    dedent "^0.7.0"
+    npm-package-arg "^6.0.0"
+    p-map "^1.2.0"
+    pacote "^9.1.0"
+    semver "^5.5.0"
+
+"@lerna/batch-packages@^3.1.2":
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/@lerna/batch-packages/-/batch-packages-3.1.2.tgz#74b5312a01a8916204cbc71237ffbe93144b99df"
+  integrity sha512-HAkpptrYeUVlBYbLScXgeCgk6BsNVXxDd53HVWgzzTWpXV4MHpbpeKrByyt7viXlNhW0w73jJbipb/QlFsHIhQ==
+  dependencies:
+    "@lerna/package-graph" "^3.1.2"
+    "@lerna/validation-error" "^3.0.0"
+    npmlog "^4.1.2"
+
+"@lerna/bootstrap@^3.4.1":
+  version "3.4.1"
+  resolved "https://registry.yarnpkg.com/@lerna/bootstrap/-/bootstrap-3.4.1.tgz#10635e9b547fb7d685949ac78e0923f73da2f52a"
+  integrity sha512-yZDJgNm/KDoRH2klzmQGmpWMg/XMzWgeWvauXkrfW/mj1wwmufOuh5pN4fBFxVmUUa/RFZdfMeaaJt3+W3PPBw==
+  dependencies:
+    "@lerna/batch-packages" "^3.1.2"
+    "@lerna/command" "^3.3.0"
+    "@lerna/filter-options" "^3.3.2"
+    "@lerna/has-npm-version" "^3.3.0"
+    "@lerna/npm-conf" "^3.4.1"
+    "@lerna/npm-install" "^3.3.0"
+    "@lerna/rimraf-dir" "^3.3.0"
+    "@lerna/run-lifecycle" "^3.4.1"
+    "@lerna/run-parallel-batches" "^3.0.0"
+    "@lerna/symlink-binary" "^3.3.0"
+    "@lerna/symlink-dependencies" "^3.3.0"
+    "@lerna/validation-error" "^3.0.0"
+    dedent "^0.7.0"
+    get-port "^3.2.0"
+    multimatch "^2.1.0"
+    npm-package-arg "^6.0.0"
+    npmlog "^4.1.2"
+    p-finally "^1.0.0"
+    p-map "^1.2.0"
+    p-map-series "^1.0.0"
+    p-waterfall "^1.0.0"
+    read-package-tree "^5.1.6"
+    semver "^5.5.0"
+
+"@lerna/changed@^3.4.1":
+  version "3.4.1"
+  resolved "https://registry.yarnpkg.com/@lerna/changed/-/changed-3.4.1.tgz#84a049359a53b8812c3a07a664bd41b1768f5938"
+  integrity sha512-gT7fhl4zQWyGETDO4Yy5wsFnqNlBSsezncS1nkMW1uO6jwnolwYqcr1KbrMR8HdmsZBn/00Y0mRnbtbpPPey8w==
+  dependencies:
+    "@lerna/collect-updates" "^3.3.2"
+    "@lerna/command" "^3.3.0"
+    "@lerna/listable" "^3.0.0"
+    "@lerna/output" "^3.0.0"
+    "@lerna/version" "^3.4.1"
+
+"@lerna/check-working-tree@^3.3.0":
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/@lerna/check-working-tree/-/check-working-tree-3.3.0.tgz#2118f301f28ccb530812e5b27a341b1e6b3c84e2"
+  integrity sha512-oeEP1dNhiiKUaO0pmcIi73YXJpaD0n5JczNctvVNZ8fGZmrALZtEnmC28o6Z7JgQaqq5nd2kO7xbnjoitrC51g==
+  dependencies:
+    "@lerna/describe-ref" "^3.3.0"
+    "@lerna/validation-error" "^3.0.0"
+
+"@lerna/child-process@^3.3.0":
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-3.3.0.tgz#71184a763105b6c8ece27f43f166498d90fe680f"
+  integrity sha512-q2d/OPlNX/cBXB6Iz1932RFzOmOHq6ZzPjqebkINNaTojHWuuRpvJJY4Uz3NGpJ3kEtPDvBemkZqUBTSO5wb1g==
+  dependencies:
+    chalk "^2.3.1"
+    execa "^1.0.0"
+    strong-log-transformer "^2.0.0"
+
+"@lerna/clean@^3.3.2":
+  version "3.3.2"
+  resolved "https://registry.yarnpkg.com/@lerna/clean/-/clean-3.3.2.tgz#9a7e8a1e400e580de260fa124945b2939a025069"
+  integrity sha512-mvqusgSp2ou5SGqQgTEoTvGJpGfH4+L6XSeN+Ims+eNFGXuMazmKCf+rz2PZBMFufaHJ/Os+JF0vPCcWI1Fzqg==
+  dependencies:
+    "@lerna/command" "^3.3.0"
+    "@lerna/filter-options" "^3.3.2"
+    "@lerna/prompt" "^3.3.1"
+    "@lerna/rimraf-dir" "^3.3.0"
+    p-map "^1.2.0"
+    p-map-series "^1.0.0"
+    p-waterfall "^1.0.0"
+
+"@lerna/cli@^3.2.0":
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/@lerna/cli/-/cli-3.2.0.tgz#3ed25bcbc0b8f0878bc6a102ee0296f01476cfdf"
+  integrity sha512-JdbLyTxHqxUlrkI+Ke+ltXbtyA+MPu9zR6kg/n8Fl6uaez/2fZWtReXzYi8MgLxfUFa7+1OHWJv4eAMZlByJ+Q==
+  dependencies:
+    "@lerna/global-options" "^3.1.3"
+    dedent "^0.7.0"
+    npmlog "^4.1.2"
+    yargs "^12.0.1"
+
+"@lerna/collect-updates@^3.3.2":
+  version "3.3.2"
+  resolved "https://registry.yarnpkg.com/@lerna/collect-updates/-/collect-updates-3.3.2.tgz#54df5ce59ca05e8aa04ff8a9299f89cc253a9304"
+  integrity sha512-9WyBJI2S5sYgEZEScu525Lbi6nknNrdBKop35sCDIC9y6AIGvH6Dr5tkTd+Kg3n1dE+kHwW/xjERkx3+h7th3w==
+  dependencies:
+    "@lerna/child-process" "^3.3.0"
+    "@lerna/describe-ref" "^3.3.0"
+    minimatch "^3.0.4"
+    npmlog "^4.1.2"
+    slash "^1.0.0"
+
+"@lerna/command@^3.3.0":
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/@lerna/command/-/command-3.3.0.tgz#e81c4716a676b02dbe9d3f548d5f45b4ba32c25a"
+  integrity sha512-NTOkLEKlWcBLHSvUr9tzVpV7RJ4GROLeOuZ6RfztGOW/31JPSwVVBD2kPifEXNZunldOx5GVWukR+7+NpAWhsg==
+  dependencies:
+    "@lerna/child-process" "^3.3.0"
+    "@lerna/package-graph" "^3.1.2"
+    "@lerna/project" "^3.0.0"
+    "@lerna/validation-error" "^3.0.0"
+    "@lerna/write-log-file" "^3.0.0"
+    dedent "^0.7.0"
+    execa "^1.0.0"
+    is-ci "^1.0.10"
+    lodash "^4.17.5"
+    npmlog "^4.1.2"
+
+"@lerna/conventional-commits@^3.4.1":
+  version "3.4.1"
+  resolved "https://registry.yarnpkg.com/@lerna/conventional-commits/-/conventional-commits-3.4.1.tgz#0b47f9fc0c4a10951883e949d939188da1b527bc"
+  integrity sha512-3NETrA58aUkaEW3RdwdJ766Bg9NVpLzb26mtdlsJQcvB5sQBWH5dJSHIVQH1QsGloBeH2pE/mDUEVY8ZJXuR4w==
+  dependencies:
+    "@lerna/validation-error" "^3.0.0"
+    conventional-changelog-angular "^5.0.1"
+    conventional-changelog-core "^3.1.0"
+    conventional-recommended-bump "^4.0.1"
+    fs-extra "^7.0.0"
+    get-stream "^4.0.0"
+    npm-package-arg "^6.0.0"
+    npmlog "^4.1.2"
+    semver "^5.5.0"
+
+"@lerna/create-symlink@^3.3.0":
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/@lerna/create-symlink/-/create-symlink-3.3.0.tgz#91de00fd576018ba4251f0c6a5b4b7f768f22a82"
+  integrity sha512-0lb88Nnq1c/GG+fwybuReOnw3+ah4dB81PuWwWwuqUNPE0n50qUf/M/7FfSb5JEh/93fcdbZI0La8t3iysNW1w==
+  dependencies:
+    cmd-shim "^2.0.2"
+    fs-extra "^7.0.0"
+    npmlog "^4.1.2"
+
+"@lerna/create@^3.4.1":
+  version "3.4.1"
+  resolved "https://registry.yarnpkg.com/@lerna/create/-/create-3.4.1.tgz#7cad78a5701d7666a0f5d0fe0e325acd8d8f5b63"
+  integrity sha512-l+4t2SRO5nvW0MNYY+EWxbaMHsAN8bkWH3nyt7EzhBjs4+TlRAJRIEqd8o9NWznheE3pzwczFz1Qfl3BWbyM5A==
+  dependencies:
+    "@lerna/child-process" "^3.3.0"
+    "@lerna/command" "^3.3.0"
+    "@lerna/npm-conf" "^3.4.1"
+    "@lerna/validation-error" "^3.0.0"
+    camelcase "^4.1.0"
+    dedent "^0.7.0"
+    fs-extra "^7.0.0"
+    globby "^8.0.1"
+    init-package-json "^1.10.3"
+    npm-package-arg "^6.0.0"
+    pify "^3.0.0"
+    semver "^5.5.0"
+    slash "^1.0.0"
+    validate-npm-package-license "^3.0.3"
+    validate-npm-package-name "^3.0.0"
+    whatwg-url "^7.0.0"
+
+"@lerna/describe-ref@^3.3.0":
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/@lerna/describe-ref/-/describe-ref-3.3.0.tgz#d373adb530d5428ab91e303ccbfcf51a98374a3a"
+  integrity sha512-4t7M4OupnYMSPNLrLUau8qkS+dgLEi4w+DkRkV0+A+KNYga1W0jVgNLPIIsxta7OHfodPkCNAqZCzNCw/dmAwA==
+  dependencies:
+    "@lerna/child-process" "^3.3.0"
+    npmlog "^4.1.2"
+
+"@lerna/diff@^3.3.0":
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/@lerna/diff/-/diff-3.3.0.tgz#c8130a5f508b47fad5fec81404498bc3acdf9cb5"
+  integrity sha512-sIoMjsm3NVxvmt6ofx8Uu/2fxgldQqLl0zmC9X1xW00j831o5hBffx1EoKj9CnmaEvoSP6j/KFjxy2RWjebCIg==
+  dependencies:
+    "@lerna/child-process" "^3.3.0"
+    "@lerna/command" "^3.3.0"
+    "@lerna/validation-error" "^3.0.0"
+    npmlog "^4.1.2"
+
+"@lerna/exec@^3.3.2":
+  version "3.3.2"
+  resolved "https://registry.yarnpkg.com/@lerna/exec/-/exec-3.3.2.tgz#95ecaca617fd85abdb91e9a378ed06ec1763d665"
+  integrity sha512-mN6vGxNir7JOGvWLwKr3DW3LNy1ecCo2ziZj5rO9Mw5Rew3carUu1XLmhF/4judtsvXViUY+rvGIcqHe0vvb+w==
+  dependencies:
+    "@lerna/batch-packages" "^3.1.2"
+    "@lerna/child-process" "^3.3.0"
+    "@lerna/command" "^3.3.0"
+    "@lerna/filter-options" "^3.3.2"
+    "@lerna/run-parallel-batches" "^3.0.0"
+    "@lerna/validation-error" "^3.0.0"
+
+"@lerna/filter-options@^3.3.2":
+  version "3.3.2"
+  resolved "https://registry.yarnpkg.com/@lerna/filter-options/-/filter-options-3.3.2.tgz#ac90702b7876ff4980dcdeaeac049c433dd01773"
+  integrity sha512-0WHqdDgAnt5WKoByi1q+lFw8HWt5tEKP2DnLlGqWv3YFwVF5DsPRlO7xbzjY9sJgvyJtZcnkMtccdBPFhGGyIQ==
+  dependencies:
+    "@lerna/collect-updates" "^3.3.2"
+    "@lerna/filter-packages" "^3.0.0"
+    dedent "^0.7.0"
+
+"@lerna/filter-packages@^3.0.0":
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/@lerna/filter-packages/-/filter-packages-3.0.0.tgz#5eb25ad1610f3e2ab845133d1f8d7d40314e838f"
+  integrity sha512-zwbY1J4uRjWRZ/FgYbtVkq7I3Nduwsg2V2HwLKSzwV2vPglfGqgovYOVkND6/xqe2BHwDX4IyA2+e7OJmLaLSA==
+  dependencies:
+    "@lerna/validation-error" "^3.0.0"
+    multimatch "^2.1.0"
+    npmlog "^4.1.2"
+
+"@lerna/get-npm-exec-opts@^3.0.0":
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/@lerna/get-npm-exec-opts/-/get-npm-exec-opts-3.0.0.tgz#8fc7866e8d8e9a2f2dc385287ba32eb44de8bdeb"
+  integrity sha512-arcYUm+4xS8J3Palhl+5rRJXnZnFHsLFKHBxznkPIxjwGQeAEw7df38uHdVjEQ+HNeFmHnBgSqfbxl1VIw5DHg==
+  dependencies:
+    npmlog "^4.1.2"
+
+"@lerna/global-options@^3.1.3":
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/@lerna/global-options/-/global-options-3.1.3.tgz#cf85e24655a91d04d4efc9a80c1f83fc768d08ae"
+  integrity sha512-LVeZU/Zgc0XkHdGMRYn+EmHfDmmYNwYRv3ta59iCVFXLVp7FRFWF7oB1ss/WRa9x/pYU0o6L8as/5DomLUGASA==
+
+"@lerna/has-npm-version@^3.3.0":
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/@lerna/has-npm-version/-/has-npm-version-3.3.0.tgz#8a73c2c437a0e1e68a19ccbd0dd3c014d4d39135"
+  integrity sha512-GX7omRep1eBRZHgjZLRw3MpBJSdA5gPZFz95P7rxhpvsiG384Tdrr/cKFMhm0A09yq27Tk/nuYTaZIj7HsVE6g==
+  dependencies:
+    "@lerna/child-process" "^3.3.0"
+    semver "^5.5.0"
+
+"@lerna/import@^3.3.1":
+  version "3.3.1"
+  resolved "https://registry.yarnpkg.com/@lerna/import/-/import-3.3.1.tgz#deca8c93c9cc03c5844b975c6da9937dd7530440"
+  integrity sha512-2OzTQDkYKbBPpyP2iOI1sWfcvMjNLjjHjmREq/uOWJaSIk5J3Ukt71OPpcOHh4V2CBOlXidCcO+Hyb4FVIy8fw==
+  dependencies:
+    "@lerna/child-process" "^3.3.0"
+    "@lerna/command" "^3.3.0"
+    "@lerna/prompt" "^3.3.1"
+    "@lerna/validation-error" "^3.0.0"
+    dedent "^0.7.0"
+    fs-extra "^7.0.0"
+    p-map-series "^1.0.0"
+
+"@lerna/init@^3.3.0":
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/@lerna/init/-/init-3.3.0.tgz#998f3497da3d891867c593b808b6db4b8fc4ccb9"
+  integrity sha512-HvgRLkIG6nDIeAO6ix5sUVIVV+W9UMk2rSSmFT66CDOefRi7S028amiyYnFUK1QkIAaUbVUyOnYaErtbJwICuw==
+  dependencies:
+    "@lerna/child-process" "^3.3.0"
+    "@lerna/command" "^3.3.0"
+    fs-extra "^7.0.0"
+    p-map "^1.2.0"
+    write-json-file "^2.3.0"
+
+"@lerna/link@^3.3.0":
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/@lerna/link/-/link-3.3.0.tgz#c0c05ff52d0f0c659fcf221627edfcd58e477a5c"
+  integrity sha512-8CeXzGL7okrsVXsy2sHXI2KuBaczw3cblAnA2+FJPUqSKMPNbUTRzeU3bOlCjYtK0LbxC4ngENJTL3jJ8RaYQQ==
+  dependencies:
+    "@lerna/command" "^3.3.0"
+    "@lerna/package-graph" "^3.1.2"
+    "@lerna/symlink-dependencies" "^3.3.0"
+    p-map "^1.2.0"
+    slash "^1.0.0"
+
+"@lerna/list@^3.3.2":
+  version "3.3.2"
+  resolved "https://registry.yarnpkg.com/@lerna/list/-/list-3.3.2.tgz#1412b3cce2a83b1baa4ff6fb962d50b46c28ec98"
+  integrity sha512-XXEVy7w+i/xx8NeJmGirw4upEoEF9OfD6XPLjISNQc24VgQV+frXdVJ02QcP7Y/PkY1rdIVrOjvo3ipKVLUxaQ==
+  dependencies:
+    "@lerna/command" "^3.3.0"
+    "@lerna/filter-options" "^3.3.2"
+    "@lerna/listable" "^3.0.0"
+    "@lerna/output" "^3.0.0"
+
+"@lerna/listable@^3.0.0":
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/@lerna/listable/-/listable-3.0.0.tgz#27209b1382c87abdbc964220e75c247d803d4199"
+  integrity sha512-HX/9hyx1HLg2kpiKXIUc1EimlkK1T58aKQ7ovO7rQdTx9ForpefoMzyLnHE1n4XrUtEszcSWJIICJ/F898M6Ag==
+  dependencies:
+    chalk "^2.3.1"
+    columnify "^1.5.4"
+
+"@lerna/log-packed@^3.0.4":
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/@lerna/log-packed/-/log-packed-3.0.4.tgz#6d1f6ce5ca68b9971f2a27f0ecf3c50684be174a"
+  integrity sha512-vVQHgMagE2wnbxhNY9nFkdu+Cx2TsyWalkJfkxbNzmo6gOCrDsxCBDj9vTEV8Q+4aWx0C0Bsc0sB2Eb8y/+ofA==
+  dependencies:
+    byte-size "^4.0.3"
+    columnify "^1.5.4"
+    has-unicode "^2.0.1"
+    npmlog "^4.1.2"
+
+"@lerna/npm-conf@^3.4.1":
+  version "3.4.1"
+  resolved "https://registry.yarnpkg.com/@lerna/npm-conf/-/npm-conf-3.4.1.tgz#859e931b0bc9a5eed86309cc09508810c1e7d121"
+  integrity sha512-i9G6DnbCqiAqxKx2rSXej/n14qxlV/XOebL6QZonxJKzNTB+Q2wglnhTXmfZXTPJfoqimLaY4NfAEtbOXRWOXQ==
+  dependencies:
+    config-chain "^1.1.11"
+    pify "^3.0.0"
+
+"@lerna/npm-dist-tag@^3.3.0":
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/@lerna/npm-dist-tag/-/npm-dist-tag-3.3.0.tgz#e1c5ab67674216d901266a16846b21cc81ff6afd"
+  integrity sha512-EtZJXzh3w5tqXEev+EBBPrWKWWn0WgJfxm4FihfS9VgyaAW8udIVZHGkIQ3f+tBtupcAzA9Q8cQNUkGF2efwmA==
+  dependencies:
+    "@lerna/child-process" "^3.3.0"
+    "@lerna/get-npm-exec-opts" "^3.0.0"
+    npmlog "^4.1.2"
+
+"@lerna/npm-install@^3.3.0":
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/@lerna/npm-install/-/npm-install-3.3.0.tgz#16d00ffd668d11b2386b3ac68bdac2cf8320e533"
+  integrity sha512-WoVvKdS8ltROTGSNQwo6NDq0YKnjwhvTG4li1okcN/eHKOS3tL9bxbgPx7No0wOq5DKBpdeS9KhAfee6LFAZ5g==
+  dependencies:
+    "@lerna/child-process" "^3.3.0"
+    "@lerna/get-npm-exec-opts" "^3.0.0"
+    fs-extra "^7.0.0"
+    npm-package-arg "^6.0.0"
+    npmlog "^4.1.2"
+    signal-exit "^3.0.2"
+    write-pkg "^3.1.0"
+
+"@lerna/npm-publish@^3.3.1":
+  version "3.3.1"
+  resolved "https://registry.yarnpkg.com/@lerna/npm-publish/-/npm-publish-3.3.1.tgz#30384665d7ee387343332ece62ca231207bbabea"
+  integrity sha512-bVTlWIcBL6Zpyzqvr9C7rxXYcoPw+l7IPz5eqQDNREj1R39Wj18OWB2KTJq8l7LIX7Wf4C2A1uT5hJaEf9BuvA==
+  dependencies:
+    "@lerna/child-process" "^3.3.0"
+    "@lerna/get-npm-exec-opts" "^3.0.0"
+    "@lerna/has-npm-version" "^3.3.0"
+    "@lerna/log-packed" "^3.0.4"
+    fs-extra "^7.0.0"
+    npmlog "^4.1.2"
+    p-map "^1.2.0"
+
+"@lerna/npm-run-script@^3.3.0":
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/@lerna/npm-run-script/-/npm-run-script-3.3.0.tgz#3c79601c27c67121155b20e039be53130217db72"
+  integrity sha512-YqDguWZzp4jIomaE4aWMUP7MIAJAFvRAf6ziQLpqwoQskfWLqK5mW0CcszT1oLjhfb3cY3MMfSTFaqwbdKmICg==
+  dependencies:
+    "@lerna/child-process" "^3.3.0"
+    "@lerna/get-npm-exec-opts" "^3.0.0"
+    npmlog "^4.1.2"
+
+"@lerna/output@^3.0.0":
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/@lerna/output/-/output-3.0.0.tgz#4ed4a30ed2f311046b714b3840a090990ba3ce35"
+  integrity sha512-EFxnSbO0zDEVKkTKpoCUAFcZjc3gn3DwPlyTDxbeqPU7neCfxP4rA4+0a6pcOfTlRS5kLBRMx79F2TRCaMM3DA==
+  dependencies:
+    npmlog "^4.1.2"
+
+"@lerna/package-graph@^3.1.2":
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/@lerna/package-graph/-/package-graph-3.1.2.tgz#b70298a3a8c82e12090da33233bf242223a38f20"
+  integrity sha512-9wIWb49I1IJmyjPdEVZQ13IAi9biGfH/OZHOC04U2zXGA0GLiY+B3CAx6FQvqkZ8xEGfqzmXnv3LvZ0bQfc1aQ==
+  dependencies:
+    "@lerna/validation-error" "^3.0.0"
+    npm-package-arg "^6.0.0"
+    semver "^5.5.0"
+
+"@lerna/package@^3.0.0":
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/@lerna/package/-/package-3.0.0.tgz#14afc9a6cb1f7f7b23c1d7c7aa81bdac7d44c0e5"
+  integrity sha512-djzEJxzn212wS8d9znBnlXkeRlPL7GqeAYBykAmsuq51YGvaQK67Umh5ejdO0uxexF/4r7yRwgrlRHpQs8Rfqg==
+  dependencies:
+    npm-package-arg "^6.0.0"
+    write-pkg "^3.1.0"
+
+"@lerna/project@^3.0.0":
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/@lerna/project/-/project-3.0.0.tgz#4320d2a2b4080cabcf95161d9c48475217d8a545"
+  integrity sha512-XhDFVfqj79jG2Speggd15RpYaE8uiR25UKcQBDmumbmqvTS7xf2cvl2pq2UTvDafaJ0YwFF3xkxQZeZnFMwdkw==
+  dependencies:
+    "@lerna/package" "^3.0.0"
+    "@lerna/validation-error" "^3.0.0"
+    cosmiconfig "^5.0.2"
+    dedent "^0.7.0"
+    dot-prop "^4.2.0"
+    glob-parent "^3.1.0"
+    globby "^8.0.1"
+    load-json-file "^4.0.0"
+    npmlog "^4.1.2"
+    p-map "^1.2.0"
+    resolve-from "^4.0.0"
+    write-json-file "^2.3.0"
+
+"@lerna/prompt@^3.3.1":
+  version "3.3.1"
+  resolved "https://registry.yarnpkg.com/@lerna/prompt/-/prompt-3.3.1.tgz#ec53f9034a7a02a671627241682947f65078ab88"
+  integrity sha512-eJhofrUCUaItMIH6et8kI7YqHfhjWqGZoTsE+40NRCfAraOMWx+pDzfRfeoAl3qeRAH2HhNj1bkYn70FbUOxuQ==
+  dependencies:
+    inquirer "^6.2.0"
+    npmlog "^4.1.2"
+
+"@lerna/publish@^3.4.3":
+  version "3.4.3"
+  resolved "https://registry.yarnpkg.com/@lerna/publish/-/publish-3.4.3.tgz#fb956ca2a871729982022889f90d0e8eb8528340"
+  integrity sha512-baeRL8xmOR25p86cAaS9mL0jdRzdv4dUo04PlK2Wes+YlL705F55cSXeC9npNie+9rGwFyLzCTQe18WdbZyLuw==
+  dependencies:
+    "@lerna/batch-packages" "^3.1.2"
+    "@lerna/check-working-tree" "^3.3.0"
+    "@lerna/child-process" "^3.3.0"
+    "@lerna/collect-updates" "^3.3.2"
+    "@lerna/command" "^3.3.0"
+    "@lerna/describe-ref" "^3.3.0"
+    "@lerna/get-npm-exec-opts" "^3.0.0"
+    "@lerna/npm-conf" "^3.4.1"
+    "@lerna/npm-dist-tag" "^3.3.0"
+    "@lerna/npm-publish" "^3.3.1"
+    "@lerna/output" "^3.0.0"
+    "@lerna/prompt" "^3.3.1"
+    "@lerna/run-lifecycle" "^3.4.1"
+    "@lerna/run-parallel-batches" "^3.0.0"
+    "@lerna/validation-error" "^3.0.0"
+    "@lerna/version" "^3.4.1"
+    fs-extra "^7.0.0"
+    libnpmaccess "^3.0.0"
+    npm-package-arg "^6.0.0"
+    npm-registry-fetch "^3.8.0"
+    npmlog "^4.1.2"
+    p-finally "^1.0.0"
+    p-map "^1.2.0"
+    p-pipe "^1.2.0"
+    p-reduce "^1.0.0"
+    semver "^5.5.0"
+
+"@lerna/resolve-symlink@^3.3.0":
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/@lerna/resolve-symlink/-/resolve-symlink-3.3.0.tgz#c5d99a60cb17e2ea90b3521a0ba445478d194a44"
+  integrity sha512-KmoPDcFJ2aOK2inYHbrsiO9SodedUj0L1JDvDgirVNIjMUaQe2Q6Vi4Gh+VCJcyB27JtfHioV9R2NxU72Pk2hg==
+  dependencies:
+    fs-extra "^7.0.0"
+    npmlog "^4.1.2"
+    read-cmd-shim "^1.0.1"
+
+"@lerna/rimraf-dir@^3.3.0":
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/@lerna/rimraf-dir/-/rimraf-dir-3.3.0.tgz#687e9bb3668a9e540e281302a52d9a573860f5db"
+  integrity sha512-vSqOcZ4kZduiSprbt+y40qziyN3VKYh+ygiCdnbBbsaxpdKB6CfrSMUtrLhVFrqUfBHIZRzHIzgjTdtQex1KLw==
+  dependencies:
+    "@lerna/child-process" "^3.3.0"
+    npmlog "^4.1.2"
+    path-exists "^3.0.0"
+    rimraf "^2.6.2"
+
+"@lerna/run-lifecycle@^3.4.1":
+  version "3.4.1"
+  resolved "https://registry.yarnpkg.com/@lerna/run-lifecycle/-/run-lifecycle-3.4.1.tgz#6d7e44eada31cb4ec78b18ef050da0d86f6c892b"
+  integrity sha512-N/hi2srM9A4BWEkXccP7vCEbf4MmIuALF00DTBMvc0A/ccItwUpl3XNuM7+ADDRK0mkwE3hDw89lJ3A7f8oUQw==
+  dependencies:
+    "@lerna/npm-conf" "^3.4.1"
+    npm-lifecycle "^2.0.0"
+    npmlog "^4.1.2"
+
+"@lerna/run-parallel-batches@^3.0.0":
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/@lerna/run-parallel-batches/-/run-parallel-batches-3.0.0.tgz#468704934084c74991d3124d80607857d4dfa840"
+  integrity sha512-Mj1ravlXF7AkkewKd9YFq9BtVrsStNrvVLedD/b2wIVbNqcxp8lS68vehXVOzoL/VWNEDotvqCQtyDBilCodGw==
+  dependencies:
+    p-map "^1.2.0"
+    p-map-series "^1.0.0"
+
+"@lerna/run@^3.3.2":
+  version "3.3.2"
+  resolved "https://registry.yarnpkg.com/@lerna/run/-/run-3.3.2.tgz#f521f4a22585c90758f34a584cb1871f8bb2a83e"
+  integrity sha512-cruwRGZZWnQ5I0M+AqcoT3Xpq2wj3135iVw4n59/Op6dZu50sMFXZNLiTTTZ15k8rTKjydcccJMdPSpTHbH7/A==
+  dependencies:
+    "@lerna/batch-packages" "^3.1.2"
+    "@lerna/command" "^3.3.0"
+    "@lerna/filter-options" "^3.3.2"
+    "@lerna/npm-run-script" "^3.3.0"
+    "@lerna/output" "^3.0.0"
+    "@lerna/run-parallel-batches" "^3.0.0"
+    "@lerna/validation-error" "^3.0.0"
+    p-map "^1.2.0"
+
+"@lerna/symlink-binary@^3.3.0":
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/@lerna/symlink-binary/-/symlink-binary-3.3.0.tgz#99ea570b21baabd61ecab27582eeb1d7b2c5f9cf"
+  integrity sha512-zRo6CimhvH/VJqCFl9T4IC6syjpWyQIxEfO2sBhrapEcfwjtwbhoGgKwucsvt4rIpFazCw63jQ/AXMT27KUIHg==
+  dependencies:
+    "@lerna/create-symlink" "^3.3.0"
+    "@lerna/package" "^3.0.0"
+    fs-extra "^7.0.0"
+    p-map "^1.2.0"
+    read-pkg "^3.0.0"
+
+"@lerna/symlink-dependencies@^3.3.0":
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/@lerna/symlink-dependencies/-/symlink-dependencies-3.3.0.tgz#13bcaed3e37986ab01b13498a459c7f609397dc3"
+  integrity sha512-IRngSNCmuD5uBKVv23tHMvr7Mplti0lKHilFKcvhbvhAfu6m/Vclxhkfs/uLyHzG+DeRpl/9o86SQET3h4XDhg==
+  dependencies:
+    "@lerna/create-symlink" "^3.3.0"
+    "@lerna/resolve-symlink" "^3.3.0"
+    "@lerna/symlink-binary" "^3.3.0"
+    fs-extra "^7.0.0"
+    p-finally "^1.0.0"
+    p-map "^1.2.0"
+    p-map-series "^1.0.0"
+
+"@lerna/validation-error@^3.0.0":
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/@lerna/validation-error/-/validation-error-3.0.0.tgz#a27e90051c3ba71995e2a800a43d94ad04b3e3f4"
+  integrity sha512-5wjkd2PszV0kWvH+EOKZJWlHEqCTTKrWsvfHnHhcUaKBe/NagPZFWs+0xlsDPZ3DJt5FNfbAPAnEBQ05zLirFA==
+  dependencies:
+    npmlog "^4.1.2"
+
+"@lerna/version@^3.4.1":
+  version "3.4.1"
+  resolved "https://registry.yarnpkg.com/@lerna/version/-/version-3.4.1.tgz#029448cccd3ccefb4d5f666933bd13cfb37edab0"
+  integrity sha512-oefNaQLBJSI2WLZXw5XxDXk4NyF5/ct0V9ys/J308NpgZthPgwRPjk9ZR0o1IOxW1ABi6z3E317W/dxHDjvAkg==
+  dependencies:
+    "@lerna/batch-packages" "^3.1.2"
+    "@lerna/check-working-tree" "^3.3.0"
+    "@lerna/child-process" "^3.3.0"
+    "@lerna/collect-updates" "^3.3.2"
+    "@lerna/command" "^3.3.0"
+    "@lerna/conventional-commits" "^3.4.1"
+    "@lerna/output" "^3.0.0"
+    "@lerna/prompt" "^3.3.1"
+    "@lerna/run-lifecycle" "^3.4.1"
+    "@lerna/validation-error" "^3.0.0"
+    chalk "^2.3.1"
+    dedent "^0.7.0"
+    minimatch "^3.0.4"
+    npmlog "^4.1.2"
+    p-map "^1.2.0"
+    p-pipe "^1.2.0"
+    p-reduce "^1.0.0"
+    p-waterfall "^1.0.0"
+    semver "^5.5.0"
+    slash "^1.0.0"
+    temp-write "^3.4.0"
+
+"@lerna/write-log-file@^3.0.0":
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/@lerna/write-log-file/-/write-log-file-3.0.0.tgz#2f95fee80c6821fe1ee6ccf8173d2b4079debbd2"
+  integrity sha512-SfbPp29lMeEVOb/M16lJwn4nnx5y+TwCdd7Uom9umd7KcZP0NOvpnX0PHehdonl7TyHZ1Xx2maklYuCLbQrd/A==
+  dependencies:
+    npmlog "^4.1.2"
+    write-file-atomic "^2.3.0"
+
 "@marionebl/sander@^0.6.0":
   version "0.6.1"
   resolved "https://registry.yarnpkg.com/@marionebl/sander/-/sander-0.6.1.tgz#1958965874f24bc51be48875feb50d642fc41f7b"
@@ -439,58 +998,7 @@
   resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b"
   integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==
 
-"@pubsweet/coko-theme@^4.1.0":
-  version "4.1.0"
-  resolved "https://registry.yarnpkg.com/@pubsweet/coko-theme/-/coko-theme-4.1.0.tgz#c163c20f50990d0aa9bf9455418fe1f368465f9a"
-  integrity sha512-7tE0ERvkgxyA/ZrXHczqoCy5JGFLcpRCfdp5vwtbQfD7dVvg8kuKP5+SPV3Dw1oF6aym+eoXLdl8IRx7gCcUWA==
-  dependencies:
-    "@pubsweet/ui-toolkit" "^1.2.0"
-    cokourier-prime-sans "git+https://gitlab.coko.foundation/julientaq/cokourier-sans-prime.git"
-    styled-components "^3.2.5"
-    typeface-fira-sans-condensed "^0.0.54"
-    typeface-vollkorn "^0.0.54"
-
-"@pubsweet/default-theme@3.0.0":
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/@pubsweet/default-theme/-/default-theme-3.0.0.tgz#7760c88b63fba45b41ad3f6019f54dd0d06e1dfc"
-  integrity sha512-XEg3nWXs1807ZmxsSv4FJa0VYtWhjXWR0dmxhL2vp3FAPvc9t97Pl1vcseky5I7MN5icwNeF7wLeF2tABmuiPQ==
-  dependencies:
-    styled-components "^3.2.5"
-    typeface-noto-sans "^0.0.54"
-    typeface-noto-serif "^0.0.54"
-    typeface-ubuntu-mono "^0.0.54"
-
-"@pubsweet/starter@git+https://gitlab.coko.foundation/pubsweet/pubsweet-starter.git":
-  version "1.0.0-alpha.1"
-  resolved "git+https://gitlab.coko.foundation/pubsweet/pubsweet-starter.git#aa768ddf88dceb1966e94acca34b3f3870d1b700"
-  dependencies:
-    "@pubsweet/coko-theme" "^4.1.0"
-    "@pubsweet/db-manager" "^2.0.5"
-    "@pubsweet/default-theme" "3.0.0"
-    "@pubsweet/logger" "^0.2.6"
-    "@pubsweet/ui" "^8.7.0"
-    authsome "^0.1.0"
-    pubsweet "^3.0.5"
-    pubsweet-client "^6.0.0"
-    pubsweet-component-blog "^1.0.5"
-    pubsweet-component-form-group "1.1.25"
-    pubsweet-component-html "^0.2.5"
-    pubsweet-component-login "1.1.17"
-    pubsweet-component-manage "^1.0.0"
-    pubsweet-component-medium-draft "^0.2.3"
-    pubsweet-component-navigation "^1.0.1"
-    pubsweet-component-password-reset-backend "^1.0.5"
-    pubsweet-component-password-reset-frontend "^2.0.15"
-    pubsweet-component-pepper-theme "^0.0.5"
-    pubsweet-component-posts-manager "1.0.40"
-    pubsweet-component-signup "^1.0.31"
-    pubsweet-component-teams-manager "^1.1.24"
-    pubsweet-component-users-manager "^2.0.1"
-    pubsweet-server "^10.0.0"
-    pubsweet-theme-plugin "^0.0.3"
-    react-router-redux next
-
-"@pubsweet/ui-toolkit@^1.1.2", "@pubsweet/ui-toolkit@^1.2.0":
+"@pubsweet/ui-toolkit@^1.1.2":
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/@pubsweet/ui-toolkit/-/ui-toolkit-1.2.0.tgz#5531149fa2229acc06e76e964b8a412bf503c4ce"
   integrity sha512-HwnFt4eES5RopLLY7ajb//UvaCLXS29XCWOOB5cs90DkwysVZaH/GM5HB/QtClQ5tuXiR3BqfjLy2mHMA0Z+PQ==
@@ -499,31 +1007,6 @@
     lodash "^4.17.4"
     styled-components "^3.2.5"
 
-"@pubsweet/ui@^8.7.0", "@pubsweet/ui@^8.8.0":
-  version "8.8.0"
-  resolved "https://registry.yarnpkg.com/@pubsweet/ui/-/ui-8.8.0.tgz#d6a845cd6d0d51c1c14956dccc11900fb87d2178"
-  integrity sha512-Ypr86pfeysF90upV7Ybk5vTr3uCnR0WWlfQZyXsmGn8jFyTlkHMJDNMw1XggfKxdmefyk5hkpcV4YiKklZ0+PA==
-  dependencies:
-    "@pubsweet/ui-toolkit" "^1.2.0"
-    babel-jest "^21.2.0"
-    classnames "^2.2.5"
-    enzyme "^3.7.0"
-    enzyme-adapter-react-16 "^1.1.1"
-    invariant "^2.2.3"
-    lodash "^4.17.4"
-    moment "^2.22.1"
-    prop-types "^15.5.10"
-    react "^16.2.0"
-    react-dom "^16.2.0"
-    react-feather "^1.0.8"
-    react-redux "^5.0.2"
-    react-router-dom "^4.2.2"
-    react-tag-autocomplete "^5.5.0"
-    recompose "^0.26.0"
-    redux "^3.6.0"
-    redux-form "^7.0.3"
-    styled-components "^3.2.5"
-
 "@types/async@2.0.50":
   version "2.0.50"
   resolved "https://registry.yarnpkg.com/@types/async/-/async-2.0.50.tgz#117540e026d64e1846093abbd5adc7e27fda7bcb"
@@ -699,7 +1182,7 @@
   resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.1.tgz#5c85d662f76fa1d34575766c5dcd6615abcd30d8"
   integrity sha512-FZdkNBDqBRHKQ2MEbSC17xnPFOhZxeJ2YGSfr2BKf3sujG49Qe3bB+rGCwQfIaA7WHnGeGkSijX4FuBCdrzW/g==
 
-JSONStream@^1.0.4:
+JSONStream@^1.0.4, JSONStream@^1.3.4:
   version "1.3.5"
   resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0"
   integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==
@@ -786,11 +1269,6 @@ acorn@^6.0.1:
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.0.4.tgz#77377e7353b72ec5104550aa2d2097a2fd40b754"
   integrity sha512-VY4i5EKSKkofY2I+6QLTbTTN/UvEQPCo6eiwzzSaSWfpaDhOmStMCMod6wmuPciNq+XS0faCglFu2lHZpdHUtg==
 
-add-stream@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa"
-  integrity sha1-anmQQ3ynNtXhKI25K9MmbV9csqo=
-
 address@1.0.3, address@^1.0.1:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/address/-/address-1.0.3.tgz#b5f50631f8d6cec8bd20c963963afb55e06cbce9"
@@ -804,6 +1282,20 @@ agent-base@2:
     extend "~3.0.0"
     semver "~5.0.1"
 
+agent-base@4, agent-base@^4.1.0, agent-base@~4.2.0:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
+  integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==
+  dependencies:
+    es6-promisify "^5.0.0"
+
+agentkeepalive@^3.4.1:
+  version "3.5.2"
+  resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-3.5.2.tgz#a113924dd3fa24a0bc3b78108c450c2abee00f67"
+  integrity sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==
+  dependencies:
+    humanize-ms "^1.2.1"
+
 ajv-errors@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.0.tgz#ecf021fa108fd17dfb5e6b383f2dd233e31ffc59"
@@ -1090,6 +1582,11 @@ aproba@^1.0.3, aproba@^1.1.1:
   resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
   integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
 
+aproba@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc"
+  integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==
+
 archiver-utils@^1.3.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-1.3.0.tgz#e50b4c09c70bf3d680e32ff1b7994e9f9d895174"
@@ -1161,6 +1658,11 @@ arr-union@^3.1.0:
   resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4"
   integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=
 
+array-differ@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031"
+  integrity sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=
+
 array-each@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/array-each/-/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f"
@@ -1250,12 +1752,12 @@ array.prototype.flat@^1.2.1:
     es-abstract "^1.10.0"
     function-bind "^1.1.1"
 
-arrify@^1.0.1:
+arrify@^1.0.0, arrify@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
   integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=
 
-asap@^2.0.6, asap@~2.0.3:
+asap@^2.0.0, asap@^2.0.6, asap@~2.0.3:
   version "2.0.6"
   resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
   integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
@@ -1358,7 +1860,7 @@ async@0.2.x, async@~0.2.9:
   resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1"
   integrity sha1-trvgsGdLnXGXCMo43owjfLUmw9E=
 
-async@^1.5.0, async@^1.5.2:
+async@^1.5.2:
   version "1.5.2"
   resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
   integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=
@@ -2332,13 +2834,13 @@ bcrypt-pbkdf@^1.0.0:
   dependencies:
     tweetnacl "^0.14.3"
 
-bcrypt@^2.0.1:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-2.0.1.tgz#229c5afe09379789f918efe86e5e5b682e509f85"
-  integrity sha512-DwB7WgJPdskbR+9Y3OTJtwRq09Lmm7Na6b+4ewvXjkD0nfNRi1OozxljHm5ETlDCBq9DTy04lQz+rj+T2ztIJg==
+bcrypt@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-3.0.2.tgz#3c575c49ccbfdf0875eb42aa1453f5654092a33d"
+  integrity sha512-kE1IaaRchCgdrmzQX/eBQKcsuL4jRHZ+O11sMvEUrI/HgFTQYAGvxlj9z7kb3zfFuwljQ5y8/NrbnXtgx5oJLg==
   dependencies:
-    nan "2.10.0"
-    node-pre-gyp "0.9.1"
+    nan "2.11.1"
+    node-pre-gyp "0.11.0"
 
 big-integer@^1.6.17:
   version "1.6.36"
@@ -2703,6 +3205,11 @@ builtin-status-codes@^3.0.0:
   resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
   integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=
 
+builtins@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88"
+  integrity sha1-y5T662HIaWRR2zZTThQi+U8K7og=
+
 busboy@^0.2.11, busboy@^0.2.13, busboy@^0.2.14:
   version "0.2.14"
   resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.2.14.tgz#6c2a622efcf47c57bbbe1e2a9c37ad36c7925453"
@@ -2716,6 +3223,11 @@ byline@^5.0.0:
   resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1"
   integrity sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE=
 
+byte-size@^4.0.3:
+  version "4.0.4"
+  resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-4.0.4.tgz#29d381709f41aae0d89c631f1c81aec88cd40b23"
+  integrity sha512-82RPeneC6nqCdSwCX2hZUz3JPOvN5at/nTEw/CMf05Smu3Hrpo9Psb7LjN+k+XndNArG1EY8L4+BM3aTM4BCvw==
+
 bytes@3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
@@ -2740,7 +3252,7 @@ cacache@^10.0.4:
     unique-filename "^1.1.0"
     y18n "^4.0.0"
 
-cacache@^11.0.2:
+cacache@^11.0.1, cacache@^11.0.2, cacache@^11.2.0:
   version "11.3.1"
   resolved "https://registry.yarnpkg.com/cacache/-/cacache-11.3.1.tgz#d09d25f6c4aca7a6d305d141ae332613aa1d515f"
   integrity sha512-2PEw4cRRDu+iQvBTTuttQifacYjLPhET+SYO/gEFMy8uhi+jlJREDAjSF5FWSdV/Aw5h18caHA7vMTw2c+wDzA==
@@ -2787,6 +3299,13 @@ call-me-maybe@^1.0.1:
   resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b"
   integrity sha1-JtII6onje1y95gJQoV8DHBak1ms=
 
+caller-callsite@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134"
+  integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=
+  dependencies:
+    callsites "^2.0.0"
+
 caller-path@^0.1.0:
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f"
@@ -2794,6 +3313,13 @@ caller-path@^0.1.0:
   dependencies:
     callsites "^0.2.0"
 
+caller-path@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4"
+  integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=
+  dependencies:
+    caller-callsite "^2.0.0"
+
 callsites@^0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca"
@@ -2841,6 +3367,11 @@ camelcase@^4.1.0:
   resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
   integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=
 
+camelcase@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42"
+  integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==
+
 camelize@1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.0.tgz#164a5483e630fa4321e5af07020e531831b2609b"
@@ -2878,11 +3409,6 @@ capture-exit@^1.2.0:
   dependencies:
     rsvp "^3.3.3"
 
-capture-stack-trace@^1.0.0:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz#a6c0bbe1f38f3aa0b92238ecb6ff42c344d4135d"
-  integrity sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==
-
 caseless@~0.12.0:
   version "0.12.0"
   resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
@@ -3387,11 +3913,6 @@ combined-stream@^1.0.5, combined-stream@^1.0.6, combined-stream@~1.0.6:
   dependencies:
     delayed-stream "~1.0.0"
 
-command-join@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/command-join/-/command-join-2.0.0.tgz#52e8b984f4872d952ff1bdc8b98397d27c7144cf"
-  integrity sha1-Uui5hPSHLZUv8b3IuYOX0nxxRM8=
-
 commander@^2.11.0, commander@^2.16.0, commander@^2.19.0, commander@^2.9.0:
   version "2.19.0"
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
@@ -3499,7 +4020,7 @@ concat-map@0.0.1:
   resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
   integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
 
-concat-stream@^1.4.10, concat-stream@^1.4.7, concat-stream@^1.5.0, concat-stream@^1.5.2, concat-stream@^1.6.0:
+concat-stream@^1.4.7, concat-stream@^1.5.0, concat-stream@^1.5.2, concat-stream@^1.6.0:
   version "1.6.2"
   resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
   integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
@@ -3509,6 +4030,14 @@ concat-stream@^1.4.10, concat-stream@^1.4.7, concat-stream@^1.5.0, concat-stream
     readable-stream "^2.2.2"
     typedarray "^0.0.6"
 
+config-chain@^1.1.11:
+  version "1.1.12"
+  resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa"
+  integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==
+  dependencies:
+    ini "^1.3.4"
+    proto-list "~1.2.1"
+
 config@^2.0.1:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/config/-/config-2.0.2.tgz#b81c8f4e05203e1da7752864c19a11604ca923d7"
@@ -3558,7 +4087,7 @@ content-type@~1.0.4:
   resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
   integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
 
-conventional-changelog-angular@^1.3.3, conventional-changelog-angular@^1.6.6:
+conventional-changelog-angular@^1.3.3:
   version "1.6.6"
   resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-1.6.6.tgz#b27f2b315c16d0a1f23eb181309d0e6a4698ea0f"
   integrity sha512-suQnFSqCxRwyBxY68pYTsFkG0taIdinHLNEAX5ivtw8bCRnIgnpvcHmlR/yjUyZIrNPYAoXlY1WiEKWgSE4BNg==
@@ -3566,105 +4095,45 @@ conventional-changelog-angular@^1.3.3, conventional-changelog-angular@^1.6.6:
     compare-func "^1.3.1"
     q "^1.5.1"
 
-conventional-changelog-atom@^0.2.8:
-  version "0.2.8"
-  resolved "https://registry.yarnpkg.com/conventional-changelog-atom/-/conventional-changelog-atom-0.2.8.tgz#8037693455990e3256f297320a45fa47ee553a14"
-  integrity sha512-8pPZqhMbrnltNBizjoDCb/Sz85KyUXNDQxuAEYAU5V/eHn0okMBVjqc8aHWYpHrytyZWvMGbayOlDv7i8kEf6g==
-  dependencies:
-    q "^1.5.1"
-
-conventional-changelog-cli@^1.3.13:
-  version "1.3.22"
-  resolved "https://registry.yarnpkg.com/conventional-changelog-cli/-/conventional-changelog-cli-1.3.22.tgz#13570fe1728f56f013ff7a88878ff49d5162a405"
-  integrity sha512-pnjdIJbxjkZ5VdAX/H1wndr1G10CY8MuZgnXuJhIHglOXfIrXygb7KZC836GW9uo1u8PjEIvIw/bKX0lOmOzZg==
-  dependencies:
-    add-stream "^1.0.0"
-    conventional-changelog "^1.1.24"
-    lodash "^4.2.1"
-    meow "^4.0.0"
-    tempfile "^1.1.1"
-
-conventional-changelog-codemirror@^0.3.8:
-  version "0.3.8"
-  resolved "https://registry.yarnpkg.com/conventional-changelog-codemirror/-/conventional-changelog-codemirror-0.3.8.tgz#a1982c8291f4ee4d6f2f62817c6b2ecd2c4b7b47"
-  integrity sha512-3HFZKtBXTaUCHvz7ai6nk2+psRIkldDoNzCsom0egDtVmPsvvHZkzjynhdQyULfacRSsBTaiQ0ol6nBOL4dDiQ==
+conventional-changelog-angular@^5.0.1:
+  version "5.0.2"
+  resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.2.tgz#39d945635e03b6d0c9d4078b1df74e06163dc66a"
+  integrity sha512-yx7m7lVrXmt4nKWQgWZqxSALEiAKZhOAcbxdUaU9575mB0CzXVbgrgpfSnSP7OqWDUTYGD0YVJ0MSRdyOPgAwA==
   dependencies:
+    compare-func "^1.3.1"
     q "^1.5.1"
 
-conventional-changelog-core@^2.0.11:
-  version "2.0.11"
-  resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-2.0.11.tgz#19b5fbd55a9697773ed6661f4e32030ed7e30287"
-  integrity sha512-HvTE6RlqeEZ/NFPtQeFLsIDOLrGP3bXYr7lFLMhCVsbduF1MXIe8OODkwMFyo1i9ku9NWBwVnVn0jDmIFXjDRg==
+conventional-changelog-core@^3.1.0:
+  version "3.1.5"
+  resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-3.1.5.tgz#c2edf928539308b54fe1b90a2fc731abc021852c"
+  integrity sha512-iwqAotS4zk0wA4S84YY1JCUG7X3LxaRjJxuUo6GI4dZuIy243j5nOg/Ora35ExT4DOiw5dQbMMQvw2SUjh6moQ==
   dependencies:
-    conventional-changelog-writer "^3.0.9"
-    conventional-commits-parser "^2.1.7"
+    conventional-changelog-writer "^4.0.2"
+    conventional-commits-parser "^3.0.1"
     dateformat "^3.0.0"
     get-pkg-repo "^1.0.0"
-    git-raw-commits "^1.3.6"
+    git-raw-commits "2.0.0"
     git-remote-origin-url "^2.0.0"
-    git-semver-tags "^1.3.6"
+    git-semver-tags "^2.0.2"
     lodash "^4.2.1"
     normalize-package-data "^2.3.5"
     q "^1.5.1"
-    read-pkg "^1.1.0"
-    read-pkg-up "^1.0.1"
+    read-pkg "^3.0.0"
+    read-pkg-up "^3.0.0"
     through2 "^2.0.0"
 
-conventional-changelog-ember@^0.3.12:
-  version "0.3.12"
-  resolved "https://registry.yarnpkg.com/conventional-changelog-ember/-/conventional-changelog-ember-0.3.12.tgz#b7d31851756d0fcb49b031dffeb6afa93b202400"
-  integrity sha512-mmJzA7uzbrOqeF89dMMi6z17O07ORTXlTMArnLG9ZTX4oLaKNolUlxFUFlFm9JUoVWajVpaHQWjxH1EOQ+ARoQ==
-  dependencies:
-    q "^1.5.1"
-
-conventional-changelog-eslint@^1.0.9:
-  version "1.0.9"
-  resolved "https://registry.yarnpkg.com/conventional-changelog-eslint/-/conventional-changelog-eslint-1.0.9.tgz#b13cc7e4b472c819450ede031ff1a75c0e3d07d3"
-  integrity sha512-h87nfVh2fdk9fJIvz26wCBsbDC/KxqCc5wSlNMZbXcARtbgNbNDIF7Y7ctokFdnxkzVdaHsbINkh548T9eBA7Q==
-  dependencies:
-    q "^1.5.1"
-
-conventional-changelog-express@^0.3.6:
-  version "0.3.6"
-  resolved "https://registry.yarnpkg.com/conventional-changelog-express/-/conventional-changelog-express-0.3.6.tgz#4a6295cb11785059fb09202180d0e59c358b9c2c"
-  integrity sha512-3iWVtBJZ9RnRnZveNDzOD8QRn6g6vUif0qVTWWyi5nUIAbuN1FfPVyKdAlJJfp5Im+dE8Kiy/d2SpaX/0X678Q==
-  dependencies:
-    q "^1.5.1"
-
-conventional-changelog-jquery@^0.1.0:
-  version "0.1.0"
-  resolved "https://registry.yarnpkg.com/conventional-changelog-jquery/-/conventional-changelog-jquery-0.1.0.tgz#0208397162e3846986e71273b6c79c5b5f80f510"
-  integrity sha1-Agg5cWLjhGmG5xJztsecW1+A9RA=
-  dependencies:
-    q "^1.4.1"
-
-conventional-changelog-jscs@^0.1.0:
-  version "0.1.0"
-  resolved "https://registry.yarnpkg.com/conventional-changelog-jscs/-/conventional-changelog-jscs-0.1.0.tgz#0479eb443cc7d72c58bf0bcf0ef1d444a92f0e5c"
-  integrity sha1-BHnrRDzH1yxYvwvPDvHURKkvDlw=
-  dependencies:
-    q "^1.4.1"
-
-conventional-changelog-jshint@^0.3.8:
-  version "0.3.8"
-  resolved "https://registry.yarnpkg.com/conventional-changelog-jshint/-/conventional-changelog-jshint-0.3.8.tgz#9051c1ac0767abaf62a31f74d2fe8790e8acc6c8"
-  integrity sha512-hn9QU4ZI/5V50wKPJNPGT4gEWgiBFpV6adieILW4MaUFynuDYOvQ71EMSj3EznJyKi/KzuXpc9dGmX8njZMjig==
-  dependencies:
-    compare-func "^1.3.1"
-    q "^1.5.1"
-
-conventional-changelog-preset-loader@^1.1.8:
-  version "1.1.8"
-  resolved "https://registry.yarnpkg.com/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-1.1.8.tgz#40bb0f142cd27d16839ec6c74ee8db418099b373"
-  integrity sha512-MkksM4G4YdrMlT2MbTsV2F6LXu/hZR0Tc/yenRrDIKRwBl/SP7ER4ZDlglqJsCzLJi4UonBc52Bkm5hzrOVCcw==
+conventional-changelog-preset-loader@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.0.2.tgz#81d1a07523913f3d17da3a49f0091f967ad345b0"
+  integrity sha512-pBY+qnUoJPXAXXqVGwQaVmcye05xi6z231QM98wHWamGAmu/ghkBprQAwmF5bdmyobdVxiLhPY3PrCfSeUNzRQ==
 
-conventional-changelog-writer@^3.0.9:
-  version "3.0.9"
-  resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-3.0.9.tgz#4aecdfef33ff2a53bb0cf3b8071ce21f0e994634"
-  integrity sha512-n9KbsxlJxRQsUnK6wIBRnARacvNnN4C/nxnxCkH+B/R1JS2Fa+DiP1dU4I59mEDEjgnFaN2+9wr1P1s7GYB5/Q==
+conventional-changelog-writer@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-4.0.2.tgz#eb493ed84269e7a663da36e49af51c54639c9a67"
+  integrity sha512-d8/FQY/fix2xXEBUhOo8u3DCbyEw3UOQgYHxLsPDw+wHUDma/GQGAGsGtoH876WyNs32fViHmTOUrgRKVLvBug==
   dependencies:
     compare-func "^1.3.1"
-    conventional-commits-filter "^1.1.6"
+    conventional-commits-filter "^2.0.1"
     dateformat "^3.0.0"
     handlebars "^4.0.2"
     json-stringify-safe "^5.0.1"
@@ -3674,37 +4143,20 @@ conventional-changelog-writer@^3.0.9:
     split "^1.0.0"
     through2 "^2.0.0"
 
-conventional-changelog@^1.1.24:
-  version "1.1.24"
-  resolved "https://registry.yarnpkg.com/conventional-changelog/-/conventional-changelog-1.1.24.tgz#3d94c29c960f5261c002678315b756cdd3d7d1f0"
-  integrity sha512-2WcSUst4Y3Z4hHvoMTWXMJr/DmgVdLiMOVY1Kak2LfFz+GIz2KDp5naqbFesYbfXPmaZ5p491dO0FWZIJoJw1Q==
-  dependencies:
-    conventional-changelog-angular "^1.6.6"
-    conventional-changelog-atom "^0.2.8"
-    conventional-changelog-codemirror "^0.3.8"
-    conventional-changelog-core "^2.0.11"
-    conventional-changelog-ember "^0.3.12"
-    conventional-changelog-eslint "^1.0.9"
-    conventional-changelog-express "^0.3.6"
-    conventional-changelog-jquery "^0.1.0"
-    conventional-changelog-jscs "^0.1.0"
-    conventional-changelog-jshint "^0.3.8"
-    conventional-changelog-preset-loader "^1.1.8"
-
 conventional-commit-types@^2.0.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/conventional-commit-types/-/conventional-commit-types-2.2.0.tgz#5db95739d6c212acbe7b6f656a11b940baa68946"
   integrity sha1-XblXOdbCEqy+e29lahG5QLqmiUY=
 
-conventional-commits-filter@^1.1.1, conventional-commits-filter@^1.1.6:
-  version "1.1.6"
-  resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-1.1.6.tgz#4389cd8e58fe89750c0b5fb58f1d7f0cc8ad3831"
-  integrity sha512-KcDgtCRKJCQhyk6VLT7zR+ZOyCnerfemE/CsR3iQpzRRFbLEs0Y6rwk3mpDvtOh04X223z+1xyJ582Stfct/0Q==
+conventional-commits-filter@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-2.0.1.tgz#55a135de1802f6510b6758e0a6aa9e0b28618db3"
+  integrity sha512-92OU8pz/977udhBjgPEbg3sbYzIxMDFTlQT97w7KdhR9igNqdJvy8smmedAAgn4tPiqseFloKkrVfbXCVd+E7A==
   dependencies:
     is-subset "^0.1.1"
     modify-values "^1.0.0"
 
-conventional-commits-parser@^2.1.0, conventional-commits-parser@^2.1.1, conventional-commits-parser@^2.1.7:
+conventional-commits-parser@^2.1.0:
   version "2.1.7"
   resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-2.1.7.tgz#eca45ed6140d72ba9722ee4132674d639e644e8e"
   integrity sha512-BoMaddIEJ6B4QVMSDu9IkVImlGOSGA1I2BQyOZHeLQ6qVOJLcLKn97+fL6dGbzWEiqDzfH4OkcveULmeq2MHFQ==
@@ -3717,18 +4169,32 @@ conventional-commits-parser@^2.1.0, conventional-commits-parser@^2.1.1, conventi
     through2 "^2.0.0"
     trim-off-newlines "^1.0.0"
 
-conventional-recommended-bump@^1.2.1:
-  version "1.2.1"
-  resolved "https://registry.yarnpkg.com/conventional-recommended-bump/-/conventional-recommended-bump-1.2.1.tgz#1b7137efb5091f99fe009e2fe9ddb7cc490e9375"
-  integrity sha512-oJjG6DkRgtnr/t/VrPdzmf4XZv8c4xKVJrVT4zrSHd92KEL+EYxSbYoKq8lQ7U5yLMw7130wrcQTLRjM/T+d4w==
+conventional-commits-parser@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-3.0.1.tgz#fe1c49753df3f98edb2285a5e485e11ffa7f2e4c"
+  integrity sha512-P6U5UOvDeidUJ8ebHVDIoXzI7gMlQ1OF/id6oUvp8cnZvOXMt1n8nYl74Ey9YMn0uVQtxmCtjPQawpsssBWtGg==
   dependencies:
-    concat-stream "^1.4.10"
-    conventional-commits-filter "^1.1.1"
-    conventional-commits-parser "^2.1.1"
-    git-raw-commits "^1.3.0"
-    git-semver-tags "^1.3.0"
-    meow "^3.3.0"
-    object-assign "^4.0.1"
+    JSONStream "^1.0.4"
+    is-text-path "^1.0.0"
+    lodash "^4.2.1"
+    meow "^4.0.0"
+    split2 "^2.0.0"
+    through2 "^2.0.0"
+    trim-off-newlines "^1.0.0"
+
+conventional-recommended-bump@^4.0.1:
+  version "4.0.4"
+  resolved "https://registry.yarnpkg.com/conventional-recommended-bump/-/conventional-recommended-bump-4.0.4.tgz#05540584641d3da758c8863c09788fcaeb586872"
+  integrity sha512-9mY5Yoblq+ZMqJpBzgS+RpSq+SUfP2miOR3H/NR9drGf08WCrY9B6HAGJZEm6+ThsVP917VHAahSOjM6k1vhPg==
+  dependencies:
+    concat-stream "^1.6.0"
+    conventional-changelog-preset-loader "^2.0.2"
+    conventional-commits-filter "^2.0.1"
+    conventional-commits-parser "^3.0.1"
+    git-raw-commits "2.0.0"
+    git-semver-tags "^2.0.2"
+    meow "^4.0.0"
+    q "^1.5.1"
 
 convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.5.1:
   version "1.6.0"
@@ -3826,6 +4292,16 @@ cosmiconfig@^4.0.0:
     parse-json "^4.0.0"
     require-from-string "^2.0.1"
 
+cosmiconfig@^5.0.2:
+  version "5.0.7"
+  resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.0.7.tgz#39826b292ee0d78eda137dfa3173bd1c21a43b04"
+  integrity sha512-PcLqxTKiDmNT6pSpy4N6KtuPwb53W+2tzNvwOZw0WH9N6O0vLIBq0x8aj8Oj75ere4YcGi48bDFCL+3fRJdlNA==
+  dependencies:
+    import-fresh "^2.0.0"
+    is-directory "^0.3.1"
+    js-yaml "^3.9.0"
+    parse-json "^4.0.0"
+
 crc32-stream@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-2.0.0.tgz#e3cdd3b4df3168dd74e3de3fbbcb7b297fe908f4"
@@ -3849,13 +4325,6 @@ create-ecdh@^4.0.0:
     bn.js "^4.1.0"
     elliptic "^6.0.0"
 
-create-error-class@^3.0.0:
-  version "3.0.2"
-  resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6"
-  integrity sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=
-  dependencies:
-    capture-stack-trace "^1.0.0"
-
 create-hash@^1.1.0, create-hash@^1.1.2:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196"
@@ -4217,6 +4686,11 @@ debug@^3.0.0, debug@^3.1.0, debug@^3.2.5:
   dependencies:
     ms "^2.1.1"
 
+debuglog@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
+  integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=
+
 decamelize-keys@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9"
@@ -4225,7 +4699,7 @@ decamelize-keys@^1.0.0:
     decamelize "^1.1.0"
     map-obj "^1.0.0"
 
-decamelize@^1.0.0, decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.1.2:
+decamelize@^1.0.0, decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
   integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
@@ -4449,6 +4923,14 @@ detect-port-alt@1.1.6:
     address "^1.0.1"
     debug "^2.6.0"
 
+dezalgo@^1.0.0:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456"
+  integrity sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=
+  dependencies:
+    asap "^2.0.0"
+    wrappy "1"
+
 dicer@0.2.5:
   version "0.2.5"
   resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.2.5.tgz#5996c086bb33218c812c090bddc09cd12facb70f"
@@ -4631,7 +5113,7 @@ dot-prop@^3.0.0:
   dependencies:
     is-obj "^1.0.0"
 
-dot-prop@^4.1.1:
+dot-prop@^4.1.1, dot-prop@^4.2.0:
   version "4.2.0"
   resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57"
   integrity sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==
@@ -4643,23 +5125,6 @@ dotenv@^4.0.0:
   resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-4.0.0.tgz#864ef1379aced55ce6f95debecdce179f7a0cd1d"
   integrity sha1-hk7xN5rO1Vzm+V3r7NzhefegzR0=
 
-draft-convert@^1.4.8:
-  version "1.4.10"
-  resolved "https://registry.yarnpkg.com/draft-convert/-/draft-convert-1.4.10.tgz#05007f009b18025451a2ce259aa008f4aa6a1add"
-  integrity sha1-BQB/AJsYAlRRos4lmqAI9KpqGt0=
-  dependencies:
-    immutable "~3.7.4"
-    invariant "^2.2.1"
-
-draft-js@^0.10.0:
-  version "0.10.5"
-  resolved "https://registry.yarnpkg.com/draft-js/-/draft-js-0.10.5.tgz#bfa9beb018fe0533dbb08d6675c371a6b08fa742"
-  integrity sha512-LE6jSCV9nkPhfVX2ggcRLA4FKs6zWq9ceuO/88BpXdNCS7mjRTgs0NsV6piUCJX9YxMsB9An33wnkMmU2sD2Zg==
-  dependencies:
-    fbjs "^0.8.15"
-    immutable "~3.7.4"
-    object-assign "^4.1.0"
-
 duplexer2@~0.1.4:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1"
@@ -4667,11 +5132,6 @@ duplexer2@~0.1.4:
   dependencies:
     readable-stream "^2.0.2"
 
-duplexer3@^0.1.4:
-  version "0.1.4"
-  resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
-  integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=
-
 duplexer@^0.1.1:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"
@@ -4841,6 +5301,11 @@ enzyme@^3.7.0:
     rst-selector-parser "^2.2.3"
     string.prototype.trim "^1.1.2"
 
+err-code@^1.0.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/err-code/-/err-code-1.1.2.tgz#06e0116d3028f6aef4806849eb0ea6a748ae6960"
+  integrity sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=
+
 errno@^0.1.3, errno@~0.1.7:
   version "0.1.7"
   resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618"
@@ -4915,11 +5380,18 @@ es6-object-assign@~1.1.0:
   resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c"
   integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=
 
-es6-promise@^4.2.4:
+es6-promise@^4.0.3, es6-promise@^4.2.4:
   version "4.2.5"
   resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.5.tgz#da6d0d5692efb461e082c14817fe2427d8f5d054"
   integrity sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg==
 
+es6-promisify@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
+  integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=
+  dependencies:
+    es6-promise "^4.0.3"
+
 es6-set@~0.1.5:
   version "0.1.5"
   resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1"
@@ -5456,6 +5928,19 @@ execa@^0.8.0:
     signal-exit "^3.0.0"
     strip-eof "^1.0.0"
 
+execa@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
+  integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==
+  dependencies:
+    cross-spawn "^6.0.0"
+    get-stream "^4.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"
+
 execall@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/execall/-/execall-1.0.0.tgz#73d0904e395b3cab0658b08d09ec25307f29bb73"
@@ -5737,7 +6222,7 @@ fbjs-css-vars@^1.0.0:
   resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.1.tgz#836d876e887d702f45610f5ebd2fbeef649527fc"
   integrity sha512-IM+v/C40MNZWqsLErc32e0TyIk/NhkkQZL0QmjBh6zi1eXv0/GeVKmKmueQX7nn9SXQBQbTUcB8zuexIF3/88w==
 
-fbjs@^0.8.0, fbjs@^0.8.1, fbjs@^0.8.15, fbjs@^0.8.16, fbjs@^0.8.5:
+fbjs@^0.8.0, fbjs@^0.8.1, fbjs@^0.8.16, fbjs@^0.8.5:
   version "0.8.17"
   resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
   integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=
@@ -5769,7 +6254,7 @@ feature-policy@0.2.0:
   resolved "https://registry.yarnpkg.com/feature-policy/-/feature-policy-0.2.0.tgz#22096de49ab240176878ffe2bde2f6ff04d48c43"
   integrity sha512-2hGrlv6efG4hscYVZeaYjpzpT6I2OZgYqE2yDUzeAcKj2D1SH0AsEzqJNXzdoglEddcIXQQYop3lD97XpG75Jw==
 
-figgy-pudding@^3.1.0, figgy-pudding@^3.5.1:
+figgy-pudding@^3.1.0, figgy-pudding@^3.4.1, figgy-pudding@^3.5.1:
   version "3.5.1"
   resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790"
   integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==
@@ -6131,7 +6616,7 @@ fs-extra@^1.0.0:
     jsonfile "^2.1.0"
     klaw "^1.0.0"
 
-fs-extra@^4.0.1, fs-extra@^4.0.2:
+fs-extra@^4.0.2:
   version "4.0.3"
   resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94"
   integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==
@@ -6140,6 +6625,15 @@ fs-extra@^4.0.1, fs-extra@^4.0.2:
     jsonfile "^4.0.0"
     universalify "^0.1.0"
 
+fs-extra@^7.0.0:
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9"
+  integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==
+  dependencies:
+    graceful-fs "^4.1.2"
+    jsonfile "^4.0.0"
+    universalify "^0.1.0"
+
 fs-minipass@^1.2.5:
   version "1.2.5"
   resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d"
@@ -6230,6 +6724,11 @@ gaze@^1.0.0:
   dependencies:
     globule "^1.0.0"
 
+genfun@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/genfun/-/genfun-5.0.0.tgz#9dd9710a06900a5c4a5bf57aca5da4e52fe76537"
+  integrity sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==
+
 get-caller-file@^1.0.1:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a"
@@ -6281,6 +6780,13 @@ get-stream@^3.0.0:
   resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
   integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=
 
+get-stream@^4.0.0, get-stream@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
+  integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==
+  dependencies:
+    pump "^3.0.0"
+
 get-value@^2.0.3, get-value@^2.0.6:
   version "2.0.6"
   resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
@@ -6293,7 +6799,18 @@ getpass@^0.1.1:
   dependencies:
     assert-plus "^1.0.0"
 
-git-raw-commits@^1.3.0, git-raw-commits@^1.3.6:
+git-raw-commits@2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-2.0.0.tgz#d92addf74440c14bcc5c83ecce3fb7f8a79118b5"
+  integrity sha512-w4jFEJFgKXMQJ0H0ikBk2S+4KP2VEjhCvLCNqbNRQC8BgGWgLKNCO7a9K9LI+TVT7Gfoloje502sEnctibffgg==
+  dependencies:
+    dargs "^4.0.1"
+    lodash.template "^4.0.2"
+    meow "^4.0.0"
+    split2 "^2.0.0"
+    through2 "^2.0.0"
+
+git-raw-commits@^1.3.0:
   version "1.3.6"
   resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-1.3.6.tgz#27c35a32a67777c1ecd412a239a6c19d71b95aff"
   integrity sha512-svsK26tQ8vEKnMshTDatSIQSMDdz8CxIIqKsvPqbtV23Etmw6VNaFAitu8zwZ0VrOne7FztwPyRLxK7/DIUTQg==
@@ -6312,10 +6829,10 @@ git-remote-origin-url@^2.0.0:
     gitconfiglocal "^1.0.0"
     pify "^2.3.0"
 
-git-semver-tags@^1.3.0, git-semver-tags@^1.3.6:
-  version "1.3.6"
-  resolved "https://registry.yarnpkg.com/git-semver-tags/-/git-semver-tags-1.3.6.tgz#357ea01f7280794fe0927f2806bee6414d2caba5"
-  integrity sha512-2jHlJnln4D/ECk9FxGEBh3k44wgYdWjWDtMmJPaecjoRmxKo3Y1Lh8GMYuOPu04CHw86NTAODchYjC5pnpMQig==
+git-semver-tags@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/git-semver-tags/-/git-semver-tags-2.0.2.tgz#f506ec07caade191ac0c8d5a21bdb8131b4934e3"
+  integrity sha512-34lMF7Yo1xEmsK2EkbArdoU79umpvm0MfzaDkSNYSJqtM5QLAVTPWgpiXSVI5o/O9EvZPSrP4Zvnec/CqhSd5w==
   dependencies:
     meow "^4.0.0"
     semver "^5.5.0"
@@ -6374,7 +6891,7 @@ glob@7.1.1:
     once "^1.3.0"
     path-is-absolute "^1.0.0"
 
-glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@~7.1.1:
+glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@~7.1.1:
   version "7.1.3"
   resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
   integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
@@ -6449,7 +6966,7 @@ globals@^9.18.0:
   resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
   integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==
 
-globby@8.0.1:
+globby@8.0.1, globby@^8.0.1:
   version "8.0.1"
   resolved "https://registry.yarnpkg.com/globby/-/globby-8.0.1.tgz#b5ad48b8aa80b35b814fc1281ecc851f1d2b5b50"
   integrity sha512-oMrYrJERnKBLXNLVTqhm3vPEdJ/b2ZE28xN4YARiix1NOIOBPEpOUnm844K1iu/BkphCaf2WNFwMszv8Soi1pw==
@@ -6513,23 +7030,6 @@ gonzales-pe@^4.0.3:
   dependencies:
     minimist "1.1.x"
 
-got@^6.7.1:
-  version "6.7.1"
-  resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0"
-  integrity sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=
-  dependencies:
-    create-error-class "^3.0.0"
-    duplexer3 "^0.1.4"
-    get-stream "^3.0.0"
-    is-redirect "^1.0.0"
-    is-retry-allowed "^1.0.0"
-    is-stream "^1.0.0"
-    lowercase-keys "^1.0.0"
-    safe-buffer "^5.0.1"
-    timed-out "^4.0.0"
-    unzip-response "^2.0.1"
-    url-parse-lax "^1.0.0"
-
 graceful-fs@^4.1.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.4, graceful-fs@^4.1.6, graceful-fs@^4.1.9:
   version "4.1.15"
   resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
@@ -6662,7 +7162,7 @@ has-symbols@^1.0.0:
   resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44"
   integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=
 
-has-unicode@^2.0.0:
+has-unicode@^2.0.0, has-unicode@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
   integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=
@@ -6828,7 +7328,7 @@ homedir-polyfill@^1.0.0, homedir-polyfill@^1.0.1:
   dependencies:
     parse-passwd "^1.0.0"
 
-hosted-git-info@^2.1.4, hosted-git-info@^2.5.0:
+hosted-git-info@^2.1.4, hosted-git-info@^2.6.0:
   version "2.7.1"
   resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047"
   integrity sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==
@@ -6898,6 +7398,11 @@ htmlparser2@^3.9.0, htmlparser2@^3.9.1, htmlparser2@^3.9.2:
     inherits "^2.0.1"
     readable-stream "^3.0.6"
 
+http-cache-semantics@^3.8.1:
+  version "3.8.1"
+  resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2"
+  integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==
+
 http-deceiver@^1.2.7:
   version "1.2.7"
   resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87"
@@ -6918,6 +7423,14 @@ http-parser-js@>=0.4.0:
   resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.0.tgz#d65edbede84349d0dc30320815a15d39cc3cbbd8"
   integrity sha512-cZdEF7r4gfRIq7ezX9J0T+kQmJNOub71dWbgAXVHDct80TKP4MCETtZQ31xyv38UwgzkWPYF/Xc0ge55dW9Z9w==
 
+http-proxy-agent@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
+  integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==
+  dependencies:
+    agent-base "4"
+    debug "3.1.0"
+
 http-proxy-middleware@~0.18.0:
   version "0.18.0"
   resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz#0987e6bb5a5606e5a69168d8f967a87f15dd8aab"
@@ -6965,6 +7478,21 @@ https-proxy-agent@^1.0.0:
     debug "2"
     extend "3"
 
+https-proxy-agent@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0"
+  integrity sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==
+  dependencies:
+    agent-base "^4.1.0"
+    debug "^3.1.0"
+
+humanize-ms@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed"
+  integrity sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=
+  dependencies:
+    ms "^2.0.0"
+
 humps@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/humps/-/humps-2.0.1.tgz#dd02ea6081bd0568dc5d073184463957ba9ef9aa"
@@ -7064,15 +7592,13 @@ immutable-tuple@^0.4.9:
   resolved "https://registry.yarnpkg.com/immutable-tuple/-/immutable-tuple-0.4.9.tgz#473ebdd6c169c461913a454bf87ef8f601a20ff0"
   integrity sha512-LWbJPZnidF8eczu7XmcnLBsumuyRBkpwIRPCZxlojouhBo5jEBO4toj6n7hMy6IxHU/c+MqDSWkvaTpPlMQcyA==
 
-immutable@^3.7.6:
-  version "3.8.2"
-  resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3"
-  integrity sha1-wkOZUUVbs5kT2vKBN28VMOEErfM=
-
-immutable@~3.7.4:
-  version "3.7.6"
-  resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.7.6.tgz#13b4d3cb12befa15482a26fe1b2ebae640071e4b"
-  integrity sha1-E7TTyxK++hVIKib+Gy665kAHHks=
+import-fresh@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546"
+  integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY=
+  dependencies:
+    caller-path "^2.0.0"
+    resolve-from "^3.0.0"
 
 import-local@^1.0.0:
   version "1.0.0"
@@ -7150,6 +7676,20 @@ ini@1.x.x, ini@^1.3.2, ini@^1.3.4, ini@~1.3.0:
   resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
   integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
 
+init-package-json@^1.10.3:
+  version "1.10.3"
+  resolved "https://registry.yarnpkg.com/init-package-json/-/init-package-json-1.10.3.tgz#45ffe2f610a8ca134f2bd1db5637b235070f6cbe"
+  integrity sha512-zKSiXKhQveNteyhcj1CoOP8tqp1QuxPIPBl8Bid99DGLFqA1p87M6lNgfjJHSBoWJJlidGOv5rWjyYKEB3g2Jw==
+  dependencies:
+    glob "^7.1.1"
+    npm-package-arg "^4.0.0 || ^5.0.0 || ^6.0.0"
+    promzard "^0.3.0"
+    read "~1.0.1"
+    read-package-json "1 || 2"
+    semver "2.x || 3.x || 4 || 5"
+    validate-npm-package-license "^3.0.1"
+    validate-npm-package-name "^3.0.0"
+
 inquirer@1.2.3:
   version "1.2.3"
   resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-1.2.3.tgz#4dec6f32f37ef7bb0b2ed3f1d1a5c3f545074918"
@@ -7189,7 +7729,7 @@ inquirer@3.0.6:
     strip-ansi "^3.0.0"
     through "^2.3.6"
 
-inquirer@6.2.0:
+inquirer@6.2.0, inquirer@^6.2.0:
   version "6.2.0"
   resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.2.0.tgz#51adcd776f661369dc1e894859c2560a224abdd8"
   integrity sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg==
@@ -7208,7 +7748,7 @@ inquirer@6.2.0:
     strip-ansi "^4.0.0"
     through "^2.3.6"
 
-inquirer@^3.0.6, inquirer@^3.2.2:
+inquirer@^3.0.6:
   version "3.3.0"
   resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9"
   integrity sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==
@@ -7587,11 +8127,6 @@ is-promise@^2.1.0:
   resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
   integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=
 
-is-redirect@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24"
-  integrity sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=
-
 is-regex@^1.0.4:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491"
@@ -7616,17 +8151,12 @@ is-resolvable@^1.0.0:
   resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
   integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==
 
-is-retry-allowed@^1.0.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34"
-  integrity sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=
-
 is-root@2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.0.0.tgz#838d1e82318144e5a6f77819d90207645acc7019"
   integrity sha512-F/pJIk8QD6OX5DNhRB7hWamLsUilmkDGho48KbgZ6xg/lmAZXHxzXQ91jzB3yRSw5kdQGGGc4yz8HYhTYIMWPg==
 
-is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0:
+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"
   integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
@@ -8364,7 +8894,7 @@ json-loader@^0.5.4:
   resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.7.tgz#dca14a70235ff82f0ac9a3abeb60d337a365185d"
   integrity sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==
 
-json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2:
+json-parse-better-errors@^1.0.0, json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
   integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
@@ -8635,50 +9165,28 @@ left-pad@^1.3.0:
   resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e"
   integrity sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==
 
-lerna@^2.5.1:
-  version "2.11.0"
-  resolved "https://registry.yarnpkg.com/lerna/-/lerna-2.11.0.tgz#89b5681e286d388dda5bbbdbbf6b84c8094eff65"
-  integrity sha512-kgM6zwe2P2tR30MYvgiLLW+9buFCm6E7o8HnRlhTgm70WVBvXVhydqv+q/MF2HrVZkCawfVtCfetyQmtd4oHhQ==
-  dependencies:
-    async "^1.5.0"
-    chalk "^2.1.0"
-    cmd-shim "^2.0.2"
-    columnify "^1.5.4"
-    command-join "^2.0.0"
-    conventional-changelog-cli "^1.3.13"
-    conventional-recommended-bump "^1.2.1"
-    dedent "^0.7.0"
-    execa "^0.8.0"
-    find-up "^2.1.0"
-    fs-extra "^4.0.1"
-    get-port "^3.2.0"
-    glob "^7.1.2"
-    glob-parent "^3.1.0"
-    globby "^6.1.0"
-    graceful-fs "^4.1.11"
-    hosted-git-info "^2.5.0"
-    inquirer "^3.2.2"
-    is-ci "^1.0.10"
-    load-json-file "^4.0.0"
-    lodash "^4.17.4"
-    minimatch "^3.0.4"
+lerna@^3.4.3:
+  version "3.4.3"
+  resolved "https://registry.yarnpkg.com/lerna/-/lerna-3.4.3.tgz#501454efb453c65c305802d370ee337f7298787e"
+  integrity sha512-tWq1LvpHqkyB+FaJCmkEweivr88yShDMmauofPVdh0M5gU1cVucszYnIgWafulKYu2LMQ3IfUMUU5Pp3+MvADQ==
+  dependencies:
+    "@lerna/add" "^3.4.1"
+    "@lerna/bootstrap" "^3.4.1"
+    "@lerna/changed" "^3.4.1"
+    "@lerna/clean" "^3.3.2"
+    "@lerna/cli" "^3.2.0"
+    "@lerna/create" "^3.4.1"
+    "@lerna/diff" "^3.3.0"
+    "@lerna/exec" "^3.3.2"
+    "@lerna/import" "^3.3.1"
+    "@lerna/init" "^3.3.0"
+    "@lerna/link" "^3.3.0"
+    "@lerna/list" "^3.3.2"
+    "@lerna/publish" "^3.4.3"
+    "@lerna/run" "^3.3.2"
+    "@lerna/version" "^3.4.1"
+    import-local "^1.0.0"
     npmlog "^4.1.2"
-    p-finally "^1.0.0"
-    package-json "^4.0.1"
-    path-exists "^3.0.0"
-    read-cmd-shim "^1.0.1"
-    read-pkg "^3.0.0"
-    rimraf "^2.6.1"
-    safe-buffer "^5.1.1"
-    semver "^5.4.1"
-    signal-exit "^3.0.2"
-    slash "^1.0.0"
-    strong-log-transformer "^1.0.6"
-    temp-write "^3.3.0"
-    write-file-atomic "^2.3.0"
-    write-json-file "^2.2.0"
-    write-pkg "^3.1.0"
-    yargs "^8.0.2"
 
 leven@^2.1.0:
   version "2.1.0"
@@ -8693,6 +9201,16 @@ levn@^0.3.0, levn@~0.3.0:
     prelude-ls "~1.1.2"
     type-check "~0.3.2"
 
+libnpmaccess@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-3.0.1.tgz#5b3a9de621f293d425191aa2e779102f84167fa8"
+  integrity sha512-RlZ7PNarCBt+XbnP7R6PoVgOq9t+kou5rvhaInoNibhPO7eMlRfS0B8yjatgn2yaHIwWNyoJDolC/6Lc5L/IQA==
+  dependencies:
+    aproba "^2.0.0"
+    get-stream "^4.0.0"
+    npm-package-arg "^6.1.0"
+    npm-registry-fetch "^3.8.0"
+
 libtidy-updated@^0.4.0:
   version "0.4.0"
   resolved "https://registry.yarnpkg.com/libtidy-updated/-/libtidy-updated-0.4.0.tgz#7e34dd4087fb8d721b95354f8d4380d07bfe3be9"
@@ -9146,12 +9664,12 @@ loud-rejection@^1.0.0:
     currently-unhandled "^0.4.1"
     signal-exit "^3.0.0"
 
-lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
+lowercase-keys@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
   integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==
 
-lru-cache@^4.0.1, lru-cache@^4.1.1, lru-cache@^4.1.3:
+lru-cache@^4.0.1, lru-cache@^4.1.1, lru-cache@^4.1.2, lru-cache@^4.1.3:
   version "4.1.4"
   resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.4.tgz#51cc46e8e6d9530771c857e24ccc720ecdbcc031"
   integrity sha512-EPstzZ23znHUVLKj+lcXO1KvZkrlw+ZirdwvOmnAnA/1PB4ggyXJ77LRkCqkff+ShQ+cqoxCxLQOh4cKITO5iA==
@@ -9173,6 +9691,23 @@ make-dir@^1.0.0:
   dependencies:
     pify "^3.0.0"
 
+make-fetch-happen@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-4.0.1.tgz#141497cb878f243ba93136c83d8aba12c216c083"
+  integrity sha512-7R5ivfy9ilRJ1EMKIOziwrns9fGeAD4bAha8EB7BIiBBLHm2KeTUGCrICFt2rbHfzheTLynv50GnNTK1zDTrcQ==
+  dependencies:
+    agentkeepalive "^3.4.1"
+    cacache "^11.0.1"
+    http-cache-semantics "^3.8.1"
+    http-proxy-agent "^2.1.0"
+    https-proxy-agent "^2.2.1"
+    lru-cache "^4.1.2"
+    mississippi "^3.0.0"
+    node-fetch-npm "^2.0.2"
+    promise-retry "^1.1.1"
+    socks-proxy-agent "^4.0.0"
+    ssri "^6.0.0"
+
 make-iterator@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/make-iterator/-/make-iterator-1.0.1.tgz#29b33f312aa8f547c4a5e490f56afcec99133ad6"
@@ -9270,15 +9805,6 @@ media-typer@0.3.0:
   resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
   integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
 
-medium-draft@^0.5.2:
-  version "0.5.9"
-  resolved "https://registry.yarnpkg.com/medium-draft/-/medium-draft-0.5.9.tgz#ae6579e893a2cb251eb53bfd7f07f9f68faf085b"
-  integrity sha512-LKxEoKgtSJl84zn0+hd8NrCIV2uBmHkYmKrVCZdwhqNer9RiwucsoN47uC33Pjq1W/c2owGQiDGhZGbeTBL1DQ==
-  dependencies:
-    draft-js "^0.10.0"
-    immutable "^3.7.6"
-    react-transition-group "^2.5.0"
-
 mem@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76"
@@ -9513,17 +10039,12 @@ minimist@1.2.0, minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2
   resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
   integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
 
-minimist@^0.1.0:
-  version "0.1.0"
-  resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.1.0.tgz#99df657a52574c21c9057497df742790b2b4c0de"
-  integrity sha1-md9lelJXTCHJBXSX33QnkLK0wN4=
-
 minimist@~0.0.1:
   version "0.0.10"
   resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
   integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=
 
-minipass@^2.2.1, minipass@^2.3.4:
+minipass@^2.2.1, minipass@^2.3.4, minipass@^2.3.5:
   version "2.3.5"
   resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848"
   integrity sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==
@@ -9598,7 +10119,7 @@ modify-values@^1.0.0:
   resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022"
   integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==
 
-moment@2.x.x, moment@^2.18.1, moment@^2.22.1, moment@^2.6.0:
+moment@2.x.x, moment@^2.18.1, moment@^2.22.1:
   version "2.22.2"
   resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66"
   integrity sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=
@@ -9636,7 +10157,7 @@ ms@2.0.0:
   resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
   integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
 
-ms@^2.1.1:
+ms@^2.0.0, ms@^2.1.1:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
   integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
@@ -9677,6 +10198,16 @@ multicast-dns@^6.0.1:
     dns-packet "^1.3.1"
     thunky "^1.0.2"
 
+multimatch@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-2.1.0.tgz#9c7906a22fb4c02919e2f5f75161b4cdbd4b2a2b"
+  integrity sha1-nHkGoi+0wCkZ4vX3UWG0zb1LKis=
+  dependencies:
+    array-differ "^1.0.0"
+    array-union "^1.0.1"
+    arrify "^1.0.0"
+    minimatch "^3.0.0"
+
 mute-stream@0.0.6:
   version "0.0.6"
   resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.6.tgz#48962b19e169fd1dfc240b3f1e7317627bbc47db"
@@ -9687,12 +10218,7 @@ mute-stream@0.0.7, mute-stream@~0.0.4:
   resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
   integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=
 
-nan@2.10.0:
-  version "2.10.0"
-  resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f"
-  integrity sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==
-
-nan@^2.10.0, nan@^2.11.0, nan@^2.9.2:
+nan@2.11.1, nan@^2.10.0, nan@^2.11.0, nan@^2.9.2:
   version "2.11.1"
   resolved "https://registry.yarnpkg.com/nan/-/nan-2.11.1.tgz#90e22bccb8ca57ea4cd37cc83d3819b52eea6766"
   integrity sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA==
@@ -9749,7 +10275,7 @@ nearley@^2.7.10:
     randexp "0.4.6"
     semver "^5.4.1"
 
-needle@^2.2.0, needle@^2.2.1:
+needle@^2.2.1:
   version "2.2.4"
   resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.4.tgz#51931bff82533b1928b7d1d69e01f1b00ffd2a4e"
   integrity sha512-HyoqEb4wr/rsoaIDfTH2aVL9nWtQqba2/HvMv+++m8u0dz808MaagKILxtfeSN7QU7nvbQ79zk3vYOJp9zsNEA==
@@ -9810,6 +10336,15 @@ node-dir@^0.1.10:
   dependencies:
     minimatch "^3.0.2"
 
+node-fetch-npm@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz#7258c9046182dca345b4208eda918daf33697ff7"
+  integrity sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw==
+  dependencies:
+    encoding "^0.1.11"
+    json-parse-better-errors "^1.0.0"
+    safe-buffer "^5.1.1"
+
 node-fetch@1.6.3:
   version "1.6.3"
   resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.6.3.tgz#dc234edd6489982d58e8f0db4f695029abcd8c04"
@@ -9909,18 +10444,18 @@ node-notifier@^5.2.1:
     shellwords "^0.1.1"
     which "^1.3.0"
 
-node-pre-gyp@0.9.1:
-  version "0.9.1"
-  resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.9.1.tgz#f11c07516dd92f87199dbc7e1838eab7cd56c9e0"
-  integrity sha1-8RwHUW3ZL4cZnbx+GDjqt81WyeA=
+node-pre-gyp@0.11.0, node-pre-gyp@^0.11.0:
+  version "0.11.0"
+  resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz#db1f33215272f692cd38f03238e3e9b47c5dd054"
+  integrity sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==
   dependencies:
     detect-libc "^1.0.2"
     mkdirp "^0.5.1"
-    needle "^2.2.0"
+    needle "^2.2.1"
     nopt "^4.0.1"
     npm-packlist "^1.1.6"
     npmlog "^4.0.2"
-    rc "^1.1.7"
+    rc "^1.2.7"
     rimraf "^2.6.1"
     semver "^5.3.0"
     tar "^4"
@@ -9941,22 +10476,6 @@ node-pre-gyp@^0.10.0:
     semver "^5.3.0"
     tar "^4"
 
-node-pre-gyp@^0.11.0:
-  version "0.11.0"
-  resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz#db1f33215272f692cd38f03238e3e9b47c5dd054"
-  integrity sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==
-  dependencies:
-    detect-libc "^1.0.2"
-    mkdirp "^0.5.1"
-    needle "^2.2.1"
-    nopt "^4.0.1"
-    npm-packlist "^1.1.6"
-    npmlog "^4.0.2"
-    rc "^1.2.7"
-    rimraf "^2.6.1"
-    semver "^5.3.0"
-    tar "^4"
-
 node-releases@^1.0.0-alpha.11:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.0.4.tgz#2d585de8c6c81d00017e063e7810a63889aa6756"
@@ -10022,7 +10541,7 @@ nopt@^4.0.1:
     abbrev "1"
     osenv "^0.1.4"
 
-normalize-package-data@^2.3.0, normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.3.5:
+normalize-package-data@^2.0.0, normalize-package-data@^2.3.0, normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.3.5, normalize-package-data@^2.4.0:
   version "2.4.0"
   resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f"
   integrity sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==
@@ -10069,7 +10588,31 @@ npm-bundled@^1.0.1:
   resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.5.tgz#3c1732b7ba936b3a10325aef616467c0ccbcc979"
   integrity sha512-m/e6jgWu8/v5niCUKQi9qQl8QdeEduFA96xHDDzFGqly0OOjI7c+60KM/2sppfnUU9JJagf+zs+yGhqSOFj71g==
 
-npm-packlist@^1.1.6:
+npm-lifecycle@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/npm-lifecycle/-/npm-lifecycle-2.1.0.tgz#1eda2eedb82db929e3a0c50341ab0aad140ed569"
+  integrity sha512-QbBfLlGBKsktwBZLj6AviHC6Q9Y3R/AY4a2PYSIRhSKSS0/CxRyD/PfxEX6tPeOCXQgMSNdwGeECacstgptc+g==
+  dependencies:
+    byline "^5.0.0"
+    graceful-fs "^4.1.11"
+    node-gyp "^3.8.0"
+    resolve-from "^4.0.0"
+    slide "^1.1.6"
+    uid-number "0.0.6"
+    umask "^1.1.0"
+    which "^1.3.1"
+
+"npm-package-arg@^4.0.0 || ^5.0.0 || ^6.0.0", npm-package-arg@^6.0.0, npm-package-arg@^6.1.0:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-6.1.0.tgz#15ae1e2758a5027efb4c250554b85a737db7fcc1"
+  integrity sha512-zYbhP2k9DbJhA0Z3HKUePUgdB1x7MfIfKssC+WLPFMKTBZKpZh5m13PgexJjCq6KW7j17r0jHWcCpxEqnnncSA==
+  dependencies:
+    hosted-git-info "^2.6.0"
+    osenv "^0.1.5"
+    semver "^5.5.0"
+    validate-npm-package-name "^3.0.0"
+
+npm-packlist@^1.1.12, npm-packlist@^1.1.6:
   version "1.1.12"
   resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.1.12.tgz#22bde2ebc12e72ca482abd67afc51eb49377243a"
   integrity sha512-WJKFOVMeAlsU/pjXuqVdzU0WfgtIBCupkEVwn+1Y0ERAbUfWw8R4GjgVbaKnUjRoD2FoQbHOCbOyT5Mbs9Lw4g==
@@ -10084,6 +10627,27 @@ npm-path@^2.0.2:
   dependencies:
     which "^1.2.10"
 
+npm-pick-manifest@^2.2.3:
+  version "2.2.3"
+  resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-2.2.3.tgz#32111d2a9562638bb2c8f2bf27f7f3092c8fae40"
+  integrity sha512-+IluBC5K201+gRU85vFlUwX3PFShZAbAgDNp2ewJdWMVSppdo/Zih0ul2Ecky/X7b51J7LrrUAP+XOmOCvYZqA==
+  dependencies:
+    figgy-pudding "^3.5.1"
+    npm-package-arg "^6.0.0"
+    semver "^5.4.1"
+
+npm-registry-fetch@^3.8.0:
+  version "3.8.0"
+  resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-3.8.0.tgz#aa7d9a7c92aff94f48dba0984bdef4bd131c88cc"
+  integrity sha512-hrw8UMD+Nob3Kl3h8Z/YjmKamb1gf7D1ZZch2otrIXM3uFLB5vjEY6DhMlq80z/zZet6eETLbOXcuQudCB3Zpw==
+  dependencies:
+    JSONStream "^1.3.4"
+    bluebird "^3.5.1"
+    figgy-pudding "^3.4.1"
+    lru-cache "^4.1.3"
+    make-fetch-happen "^4.0.1"
+    npm-package-arg "^6.1.0"
+
 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"
@@ -10459,7 +11023,7 @@ os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2:
   resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
   integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
 
-osenv@0, osenv@^0.1.4:
+osenv@0, osenv@^0.1.4, osenv@^0.1.5:
   version "0.1.5"
   resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410"
   integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==
@@ -10519,11 +11083,28 @@ p-locate@^3.0.0:
   dependencies:
     p-limit "^2.0.0"
 
-p-map@^1.1.1:
+p-map-series@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-1.0.0.tgz#bf98fe575705658a9e1351befb85ae4c1f07bdca"
+  integrity sha1-v5j+V1cFZYqeE1G++4WuTB8Hvco=
+  dependencies:
+    p-reduce "^1.0.0"
+
+p-map@^1.1.1, p-map@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b"
   integrity sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==
 
+p-pipe@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/p-pipe/-/p-pipe-1.2.0.tgz#4b1a11399a11520a67790ee5a0c1d5881d6befe9"
+  integrity sha1-SxoROZoRUgpneQ7loMHViB1r7+k=
+
+p-reduce@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa"
+  integrity sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=
+
 p-try@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
@@ -10534,21 +11115,51 @@ p-try@^2.0.0:
   resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1"
   integrity sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==
 
-package-json@^4.0.1:
-  version "4.0.1"
-  resolved "https://registry.yarnpkg.com/package-json/-/package-json-4.0.1.tgz#8869a0401253661c4c4ca3da6c2121ed555f5eed"
-  integrity sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=
+p-waterfall@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/p-waterfall/-/p-waterfall-1.0.0.tgz#7ed94b3ceb3332782353af6aae11aa9fc235bb00"
+  integrity sha1-ftlLPOszMngjU69qrhGqn8I1uwA=
   dependencies:
-    got "^6.7.1"
-    registry-auth-token "^3.0.1"
-    registry-url "^3.0.3"
-    semver "^5.1.0"
+    p-reduce "^1.0.0"
 
 packet-reader@0.3.1:
   version "0.3.1"
   resolved "https://registry.yarnpkg.com/packet-reader/-/packet-reader-0.3.1.tgz#cd62e60af8d7fea8a705ec4ff990871c46871f27"
   integrity sha1-zWLmCvjX/qinBexP+ZCHHEaHHyc=
 
+pacote@^9.1.0:
+  version "9.2.3"
+  resolved "https://registry.yarnpkg.com/pacote/-/pacote-9.2.3.tgz#48cfe87beb9177acd6594355a584a538835424b3"
+  integrity sha512-Y3+yY3nBRAxMlZWvr62XLJxOwCmG9UmkGZkFurWHoCjqF0cZL72cTOCRJTvWw8T4OhJS2RTg13x4oYYriauvEw==
+  dependencies:
+    bluebird "^3.5.2"
+    cacache "^11.2.0"
+    figgy-pudding "^3.5.1"
+    get-stream "^4.1.0"
+    glob "^7.1.3"
+    lru-cache "^4.1.3"
+    make-fetch-happen "^4.0.1"
+    minimatch "^3.0.4"
+    minipass "^2.3.5"
+    mississippi "^3.0.0"
+    mkdirp "^0.5.1"
+    normalize-package-data "^2.4.0"
+    npm-package-arg "^6.1.0"
+    npm-packlist "^1.1.12"
+    npm-pick-manifest "^2.2.3"
+    npm-registry-fetch "^3.8.0"
+    osenv "^0.1.5"
+    promise-inflight "^1.0.1"
+    promise-retry "^1.1.1"
+    protoduck "^5.0.1"
+    rimraf "^2.6.2"
+    safe-buffer "^5.1.2"
+    semver "^5.6.0"
+    ssri "^6.0.1"
+    tar "^4.4.6"
+    unique-filename "^1.1.1"
+    which "^1.3.1"
+
 pad-right@^0.2.2:
   version "0.2.2"
   resolved "https://registry.yarnpkg.com/pad-right/-/pad-right-0.2.2.tgz#6fbc924045d244f2a2a244503060d3bfc6009774"
@@ -11356,7 +11967,7 @@ prelude-ls@~1.1.2:
   resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
   integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
 
-prepend-http@^1.0.0, prepend-http@^1.0.1:
+prepend-http@^1.0.0:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
   integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=
@@ -11427,6 +12038,14 @@ promise-queue@^2.2.3:
   resolved "https://registry.yarnpkg.com/promise-queue/-/promise-queue-2.2.5.tgz#2f6f5f7c0f6d08109e967659c79b88a9ed5e93b4"
   integrity sha1-L29ffA9tCBCelnZZx5uIqe1ek7Q=
 
+promise-retry@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-1.1.1.tgz#6739e968e3051da20ce6497fb2b50f6911df3d6d"
+  integrity sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=
+  dependencies:
+    err-code "^1.0.0"
+    retry "^0.10.0"
+
 promise@^7.1.1:
   version "7.3.1"
   resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
@@ -11452,6 +12071,13 @@ prompts@^0.1.9:
     kleur "^2.0.1"
     sisteransi "^0.1.1"
 
+promzard@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/promzard/-/promzard-0.3.0.tgz#26a5d6ee8c7dee4cb12208305acfb93ba382a9ee"
+  integrity sha1-JqXW7ox97kyxIggwWs+5O6OCqe4=
+  dependencies:
+    read "1"
+
 prop-types-extra@^1.0.1:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/prop-types-extra/-/prop-types-extra-1.1.0.tgz#32609910ea2dcf190366bacd3490d5a6412a605f"
@@ -11583,6 +12209,18 @@ prosemirror-view@^1.0.0, prosemirror-view@^1.1.0:
     prosemirror-state "^1.0.0"
     prosemirror-transform "^1.1.0"
 
+proto-list@~1.2.1:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
+  integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=
+
+protoduck@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/protoduck/-/protoduck-5.0.1.tgz#03c3659ca18007b69a50fd82a7ebcc516261151f"
+  integrity sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==
+  dependencies:
+    genfun "^5.0.0"
+
 proxy-addr@~2.0.4:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.4.tgz#ecfc733bf22ff8c6f407fa275327b9ab67e48b93"
@@ -11625,112 +12263,6 @@ public-encrypt@^4.0.0:
     randombytes "^2.0.1"
     safe-buffer "^5.1.2"
 
-pubsweet-client@^6.0.0:
-  version "6.1.0"
-  resolved "https://registry.yarnpkg.com/pubsweet-client/-/pubsweet-client-6.1.0.tgz#79194234f591bc515bf1802689e1291c8ae8e679"
-  integrity sha512-on/uWQuzkuj0XbLh10ts9IbCL3+tAdFFlODqsVSKNnqB/2QxrcmKIkW2+RI3Oy9rBHCff3HVAjzhhfaHYmb8JQ==
-  dependencies:
-    "@pubsweet/ui" "^8.8.0"
-    "@pubsweet/ui-toolkit" "^1.2.0"
-    apollo-cache-inmemory "^1.2.4"
-    apollo-client "^2.3.4"
-    apollo-link "^1.2.1"
-    apollo-link-context "^1.0.5"
-    apollo-link-http "^1.5.4"
-    apollo-link-ws "^1.0.8"
-    apollo-upload-client "^8.0.0"
-    authsome "^0.1.0"
-    config "^2.0.1"
-    event-source-polyfill "^0.0.10"
-    global "^4.3.1"
-    graphql "^14.0.2"
-    graphql-tag "^2.7.3"
-    isomorphic-fetch "^2.1.1"
-    lint-staged "^6.0.0"
-    lodash "^4.0.0"
-    prop-types "^15.5.8"
-    react "^16.2.0"
-    react-apollo "^2.1.0"
-    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"
-    reselect "^3.0.1"
-    styled-components "^3.2.5"
-    styled-normalize "^3.0.1"
-    subscriptions-transport-ws "^0.9.12"
-
-pubsweet-component-form-group@1.1.25:
-  version "1.1.25"
-  resolved "https://registry.yarnpkg.com/pubsweet-component-form-group/-/pubsweet-component-form-group-1.1.25.tgz#ba93e042483ce7097f799966322940d6b67f2b11"
-  integrity sha512-7QmaHh/DbGcOt5jEx7DfQiWwnL7+CHQWm9DLRC9yS7dZ5yFJGsyLpzerAUbFDBlUftcrufymKDbzICbBj0AaaA==
-  dependencies:
-    joi-browser "^13.4.0"
-    prop-types "^15.5.10"
-    pubsweet-server "^10.0.0"
-
-pubsweet-component-login@1.1.17:
-  version "1.1.17"
-  resolved "https://registry.yarnpkg.com/pubsweet-component-login/-/pubsweet-component-login-1.1.17.tgz#8ea00b85e73a1068fbd6e1d7a4173ba3cfc17c42"
-  integrity sha512-1M5xBtUGi+nWoknzSYQ8C7RAo3XCPCJzgdOQaKi2Cl1iIKHZy0wsNXlK3gv/3dldJ47QLSL2E6sKnlwy2YijLQ==
-  dependencies:
-    "@pubsweet/ui" "^8.7.0"
-    prop-types "^15.5.10"
-    react-redux "^5.0.6"
-    react-router-dom "^4.2.2"
-    react-router-redux "^5.0.0-alpha.9"
-    recompose "^0.26.0"
-    redux-form "^7.0.3"
-
-pubsweet-component-medium-draft@^0.2.3:
-  version "0.2.4"
-  resolved "https://registry.yarnpkg.com/pubsweet-component-medium-draft/-/pubsweet-component-medium-draft-0.2.4.tgz#9877c23538fe22915e6b6552608f905edbb70fc9"
-  integrity sha512-WwaCBdCHW7OtaxJ/Et+ke0xH6+XKjkgQ7TZqs2jv7QYXJ/psD+jVpXFVv5+RNY2wxZbhXWd0vC5ddeewORtwhA==
-  dependencies:
-    draft-convert "^1.4.8"
-    font-awesome "^4.7.0"
-    medium-draft "^0.5.2"
-    prop-types "^15.5.10"
-    react-redux "^5.0.6"
-    redux "^3.7.2"
-
-pubsweet-component-password-reset-frontend@^2.0.15:
-  version "2.0.17"
-  resolved "https://registry.yarnpkg.com/pubsweet-component-password-reset-frontend/-/pubsweet-component-password-reset-frontend-2.0.17.tgz#8b1d33a66ae18704e48c29e18651288dcab98e5d"
-  integrity sha512-3fj63ih7yQiB308CrxwQZvPtq3NmahChiAb+86XtxHXYmPIHVOGvLo1RmZuogt6bY7EnRrGBnGVmEGjm/ph2Vg==
-  dependencies:
-    "@pubsweet/ui" "^8.8.0"
-    "@pubsweet/ui-toolkit" "^1.2.0"
-    prop-types "^15.5.10"
-    query-string "^5.0.0"
-    react-router "^4.2.0"
-    styled-components "^3.2.5"
-
-pubsweet-component-pepper-theme@^0.0.5:
-  version "0.0.5"
-  resolved "https://registry.yarnpkg.com/pubsweet-component-pepper-theme/-/pubsweet-component-pepper-theme-0.0.5.tgz#eb39aafbab8e1a737752b6c5ed7ef057ead414cc"
-  integrity sha512-b84iM59NMjAqKWbKs2/HSNrwFgb4/siOaXHqyfzs4Z+b+/rO1WthUGjJ4h8Uq3GeuTSPSG8QmdraDyPYHPDDQg==
-
-pubsweet-component-posts-manager@1.0.40:
-  version "1.0.40"
-  resolved "https://registry.yarnpkg.com/pubsweet-component-posts-manager/-/pubsweet-component-posts-manager-1.0.40.tgz#ce757fde0ffbedafe441229b11ad4c97b0a72110"
-  integrity sha512-hqsltbexB1iw1/l7E/uHq3VlxA5hw+a1gkdpCdzZ5MBawVln/Db6Tme4zxXSkjjBC73NZh/QZ+ABgDI3aUBOiQ==
-  dependencies:
-    "@pubsweet/ui" "^8.7.0"
-    prop-types "^15.5.10"
-    pubsweet-component-form-group "^1.1.25"
-    react-redux "^5.0.6"
-    react-router-dom "^4.2.2"
-    redux "^3.7.2"
-
-pubsweet-theme-plugin@^0.0.3:
-  version "0.0.3"
-  resolved "https://registry.yarnpkg.com/pubsweet-theme-plugin/-/pubsweet-theme-plugin-0.0.3.tgz#1773543a95ed9f56eb8b14f13f24496e034d140b"
-  integrity sha512-lKEbQW/pfhhhbpPyO+naDFmgyNwqE9Cj6lk7eqxJgKnVc6AL+B8YBTSpsSDy7LoVrwKjM2Q7+0TrN30llcx6Ag==
-
 pump@^2.0.0, pump@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909"
@@ -11790,7 +12322,7 @@ q-i@^2.0.1:
     is-plain-object "^2.0.4"
     stringify-object "^3.2.0"
 
-q@^1.1.2, q@^1.4.1, q@^1.5.1:
+q@^1.1.2, q@^1.5.1:
   version "1.5.1"
   resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
   integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
@@ -11901,7 +12433,7 @@ raw-body@2.3.3:
     iconv-lite "0.4.23"
     unpipe "1.0.0"
 
-rc@^1.0.1, rc@^1.1.2, rc@^1.1.6, rc@^1.1.7, rc@^1.2.7:
+rc@^1.1.2, rc@^1.2.7:
   version "1.2.8"
   resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
   integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
@@ -12287,7 +12819,7 @@ react-test-renderer@^16.0.0-0, react-test-renderer@^16.5.2:
     react-is "^16.6.3"
     scheduler "^0.11.2"
 
-react-transition-group@^2.0.0, react-transition-group@^2.2.0, react-transition-group@^2.5.0:
+react-transition-group@^2.0.0, react-transition-group@^2.2.0:
   version "2.5.0"
   resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.5.0.tgz#70bca0e3546102c4dc5cf3f5f57f73447cce6874"
   integrity sha512-qYB3JBF+9Y4sE4/Mg/9O6WFpdoYjeeYqx0AFb64PTazVy8RPMiE3A47CG9QmM4WJ/mzDiZYslV+Uly6O1Erlgw==
@@ -12314,6 +12846,29 @@ read-cmd-shim@^1.0.1:
   dependencies:
     graceful-fs "^4.1.2"
 
+"read-package-json@1 || 2", read-package-json@^2.0.0:
+  version "2.0.13"
+  resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-2.0.13.tgz#2e82ebd9f613baa6d2ebe3aa72cefe3f68e41f4a"
+  integrity sha512-/1dZ7TRZvGrYqE0UAfN6qQb5GYBsNcqS1C0tNK601CFOJmtHI7NIGXwetEPU/OtoFHZL3hDxm4rolFFVE9Bnmg==
+  dependencies:
+    glob "^7.1.1"
+    json-parse-better-errors "^1.0.1"
+    normalize-package-data "^2.0.0"
+    slash "^1.0.0"
+  optionalDependencies:
+    graceful-fs "^4.1.2"
+
+read-package-tree@^5.1.6:
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/read-package-tree/-/read-package-tree-5.2.1.tgz#6218b187d6fac82289ce4387bbbaf8eef536ad63"
+  integrity sha512-2CNoRoh95LxY47LvqrehIAfUVda2JbuFE/HaGYs42bNrGG+ojbw1h3zOcPcQ+1GQ3+rkzNndZn85u1XyZ3UsIA==
+  dependencies:
+    debuglog "^1.0.1"
+    dezalgo "^1.0.0"
+    once "^1.3.0"
+    read-package-json "^2.0.0"
+    readdir-scoped-modules "^1.0.0"
+
 read-pkg-up@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
@@ -12346,7 +12901,7 @@ read-pkg-up@^4.0.0:
     find-up "^3.0.0"
     read-pkg "^3.0.0"
 
-read-pkg@^1.0.0, read-pkg@^1.1.0:
+read-pkg@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28"
   integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=
@@ -12373,7 +12928,7 @@ read-pkg@^3.0.0:
     normalize-package-data "^2.3.2"
     path-type "^3.0.0"
 
-read@1.0.x:
+read@1, read@1.0.x, read@~1.0.1:
   version "1.0.7"
   resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4"
   integrity sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=
@@ -12425,6 +12980,16 @@ readable-stream@~2.1.5:
     string_decoder "~0.10.x"
     util-deprecate "~1.0.1"
 
+readdir-scoped-modules@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.0.2.tgz#9fafa37d286be5d92cbaebdee030dc9b5f406747"
+  integrity sha1-n6+jfShr5dksuuve4DDcm19AZ0c=
+  dependencies:
+    debuglog "^1.0.1"
+    dezalgo "^1.0.0"
+    graceful-fs "^4.1.2"
+    once "^1.3.0"
+
 readdirp@^2.0.0:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525"
@@ -12659,21 +13224,6 @@ regexpu-core@^4.1.3:
     unicode-match-property-ecmascript "^1.0.4"
     unicode-match-property-value-ecmascript "^1.0.2"
 
-registry-auth-token@^3.0.1:
-  version "3.3.2"
-  resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.2.tgz#851fd49038eecb586911115af845260eec983f20"
-  integrity sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==
-  dependencies:
-    rc "^1.1.6"
-    safe-buffer "^5.0.1"
-
-registry-url@^3.0.3:
-  version "3.1.0"
-  resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942"
-  integrity sha1-PU74cPc93h138M+aOBQyRE4XSUI=
-  dependencies:
-    rc "^1.0.1"
-
 regjsgen@^0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7"
@@ -12988,6 +13538,11 @@ ret@~0.1.10:
   resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
   integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
 
+retry@^0.10.0:
+  version "0.10.1"
+  resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.1.tgz#e76388d217992c252750241d3d3956fed98d8ff4"
+  integrity sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=
+
 revalidator@0.1.x:
   version "0.1.8"
   resolved "https://registry.yarnpkg.com/revalidator/-/revalidator-0.1.8.tgz#fece61bfa0c1b52a206bd6b18198184bdd523a3b"
@@ -13157,6 +13712,13 @@ sax@>=0.6.0, sax@^1.2.4, sax@~1.2.1:
   resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
   integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
 
+schedule@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/schedule/-/schedule-0.3.0.tgz#1be2ab2fc2e768536269ce7326efb478d6c045e8"
+  integrity sha512-20+1KVo517sR7Nt+bYBN8a+bEJDKLPEx7Ohtts1kX05E4/HY53YUNuhfkVNItmWAnBYHcpG9vsd2/CJxG+aPCQ==
+  dependencies:
+    object-assign "^4.1.1"
+
 scheduler@^0.11.2:
   version "0.11.2"
   resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.11.2.tgz#a8db5399d06eba5abac51b705b7151d2319d33d3"
@@ -13209,7 +13771,7 @@ selfsigned@^1.9.1:
   dependencies:
     node-forge "0.7.5"
 
-"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0:
+"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.0.3, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0:
   version "5.6.0"
   resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
   integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==
@@ -13403,6 +13965,16 @@ slice-ansi@1.0.0:
   dependencies:
     is-fullwidth-code-point "^2.0.0"
 
+slide@^1.1.6:
+  version "1.1.6"
+  resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707"
+  integrity sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=
+
+smart-buffer@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.0.1.tgz#07ea1ca8d4db24eb4cac86537d7d18995221ace3"
+  integrity sha512-RFqinRVJVcCAL9Uh1oVqE6FZkqsyLiVOYEZ20TqIOjuX7iFVJ+zsbs4RIghnw/pTs7mZvt8ZHhvm1ZUrR4fykg==
+
 snapdragon-node@^2.0.1:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"
@@ -13465,6 +14037,22 @@ sockjs@0.3.19:
     faye-websocket "^0.10.0"
     uuid "^3.0.1"
 
+socks-proxy-agent@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-4.0.1.tgz#5936bf8b707a993079c6f37db2091821bffa6473"
+  integrity sha512-Kezx6/VBguXOsEe5oU3lXYyKMi4+gva72TwJ7pQY5JfqUx2nMk7NXA6z/mpNqIlfQjWYVfeuNvQjexiTaTn6Nw==
+  dependencies:
+    agent-base "~4.2.0"
+    socks "~2.2.0"
+
+socks@~2.2.0:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/socks/-/socks-2.2.2.tgz#f061219fc2d4d332afb4af93e865c84d3fa26e2b"
+  integrity sha512-g6wjBnnMOZpE0ym6e0uHSddz9p3a+WsBaaYQaBaSCJYvrC4IXykQR9MNGjLQf38e9iIIhp3b1/Zk8YZI3KGJ0Q==
+  dependencies:
+    ip "^1.1.5"
+    smart-buffer "^4.0.1"
+
 sort-keys@^1.0.0:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad"
@@ -13649,7 +14237,7 @@ ssri@^5.2.4:
   dependencies:
     safe-buffer "^5.1.1"
 
-ssri@^6.0.0:
+ssri@^6.0.0, ssri@^6.0.1:
   version "6.0.1"
   resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8"
   integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==
@@ -13903,15 +14491,14 @@ striptags@^3.1.0:
   resolved "https://registry.yarnpkg.com/striptags/-/striptags-3.1.1.tgz#c8c3e7fdd6fb4bb3a32a3b752e5b5e3e38093ebd"
   integrity sha1-yMPn/db7S7OjKjt1LltePjgJPr0=
 
-strong-log-transformer@^1.0.6:
-  version "1.0.6"
-  resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-1.0.6.tgz#f7fb93758a69a571140181277eea0c2eb1301fa3"
-  integrity sha1-9/uTdYpppXEUAYEnfuoMLrEwH6M=
+strong-log-transformer@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-2.0.0.tgz#fa6d8e0a9e62b3c168c3cad5ae5d00dc97ba26cc"
+  integrity sha512-FQmNqAXJgOX8ygOcvPLlGWBNT41mvNJ9ALoYf0GTwVt9t30mGTqpmp/oJx5gLcu52DXK10kS7dVWhx8aPXDTlg==
   dependencies:
     byline "^5.0.0"
     duplexer "^0.1.1"
-    minimist "^0.1.0"
-    moment "^2.6.0"
+    minimist "^1.2.0"
     through "^2.3.4"
 
 style-loader@^0.19.0:
@@ -13966,11 +14553,6 @@ styled-components@^4.1.1:
     stylis-rule-sheet "^0.0.10"
     supports-color "^5.5.0"
 
-styled-normalize@^3.0.1:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/styled-normalize/-/styled-normalize-3.0.1.tgz#217efb96598690addd04699ca71af0db3473fea2"
-  integrity sha512-EtTwCJDKMoJ+GU0YxklkHukltPGz7Swfq/fKyeP/WEQGF01DXGVXmuo1Rp2kMTQ9kz7o6dVBvvmT+KQyKe8Okw==
-
 styled-normalize@^8.0.4:
   version "8.0.4"
   resolved "https://registry.yarnpkg.com/styled-normalize/-/styled-normalize-8.0.4.tgz#6a0885dc16c61d88813dab8f5137da928fe0c947"
@@ -14238,7 +14820,7 @@ tar@^2.0.0:
     fstream "^1.0.2"
     inherits "2"
 
-tar@^4:
+tar@^4, tar@^4.4.6:
   version "4.4.8"
   resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d"
   integrity sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==
@@ -14261,7 +14843,7 @@ temp-dir@^1.0.0:
   resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d"
   integrity sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=
 
-temp-write@^3.3.0:
+temp-write@^3.4.0:
   version "3.4.0"
   resolved "https://registry.yarnpkg.com/temp-write/-/temp-write-3.4.0.tgz#8cff630fb7e9da05f047c74ce4ce4d685457d492"
   integrity sha1-jP9jD7fp2gXwR8dM5M5NaFRX1JI=
@@ -14281,14 +14863,6 @@ temp@^0.8.3:
     os-tmpdir "^1.0.0"
     rimraf "~2.2.6"
 
-tempfile@^1.1.1:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/tempfile/-/tempfile-1.1.1.tgz#5bcc4eaecc4ab2c707d8bc11d99ccc9a2cb287f2"
-  integrity sha1-W8xOrsxKsscH2LwR2ZzMmiyyh/I=
-  dependencies:
-    os-tmpdir "^1.0.0"
-    uuid "^2.0.1"
-
 terser-webpack-plugin@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.1.0.tgz#cf7c25a1eee25bf121f4a587bb9e004e3f80e528"
@@ -14367,11 +14941,6 @@ time-stamp@^2.0.0:
   resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-2.2.0.tgz#917e0a66905688790ec7bbbde04046259af83f57"
   integrity sha512-zxke8goJQpBeEgD82CXABeMh0LSJcj7CXEd0OHOg45HgcofF7pxNwZm9+RknpxpDhwN4gFpySkApKfFYfRQnUA==
 
-timed-out@^4.0.0:
-  version "4.0.1"
-  resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f"
-  integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=
-
 timers-browserify@^2.0.4:
   version "2.0.10"
   resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.10.tgz#1d28e3d2aadf1d5a5996c4e9f95601cd053480ae"
@@ -14679,6 +15248,16 @@ uglifyjs-webpack-plugin@^0.4.6:
     uglify-js "^2.8.29"
     webpack-sources "^1.0.1"
 
+uid-number@0.0.6:
+  version "0.0.6"
+  resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81"
+  integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=
+
+umask@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d"
+  integrity sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0=
+
 umzug@^2.1.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/umzug/-/umzug-2.2.0.tgz#6160bdc1817e4a63a625946775063c638623e62e"
@@ -14767,7 +15346,7 @@ uniqs@^2.0.0:
   resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02"
   integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI=
 
-unique-filename@^1.1.0:
+unique-filename@^1.1.0, unique-filename@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230"
   integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==
@@ -14842,11 +15421,6 @@ unset-value@^1.0.0:
     has-value "^0.3.1"
     isobject "^3.0.0"
 
-unzip-response@^2.0.1:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97"
-  integrity sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=
-
 unzipper@^0.8.9:
   version "0.8.14"
   resolved "https://registry.yarnpkg.com/unzipper/-/unzipper-0.8.14.tgz#ade0524cd2fc14d11b8de258be22f9d247d3f79b"
@@ -14879,13 +15453,6 @@ urix@^0.1.0:
   resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
   integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
 
-url-parse-lax@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73"
-  integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=
-  dependencies:
-    prepend-http "^1.0.1"
-
 url-parse@^1.1.8, url-parse@^1.4.3:
   version "1.4.4"
   resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.4.tgz#cac1556e95faa0303691fec5cf9d5a1bc34648f8"
@@ -14981,11 +15548,6 @@ uuid@3.1.0:
   resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04"
   integrity sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==
 
-uuid@^2.0.1:
-  version "2.0.3"
-  resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a"
-  integrity sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=
-
 uuid@^3.0.1, uuid@^3.1.0, uuid@^3.2.1, uuid@^3.3.2:
   version "3.3.2"
   resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
@@ -15005,7 +15567,7 @@ v8flags@^3.1.1:
   dependencies:
     homedir-polyfill "^1.0.1"
 
-validate-npm-package-license@^3.0.1:
+validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.3:
   version "3.0.4"
   resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
   integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==
@@ -15013,6 +15575,13 @@ validate-npm-package-license@^3.0.1:
     spdx-correct "^3.0.0"
     spdx-expression-parse "^3.0.0"
 
+validate-npm-package-name@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e"
+  integrity sha1-X6kS2B630MdK/BQN5zF/DKffQ34=
+  dependencies:
+    builtins "^1.0.3"
+
 value-equal@^0.4.0:
   version "0.4.0"
   resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-0.4.0.tgz#c5bdd2f54ee093c04839d71ce2e4758a6890abc7"
@@ -15397,7 +15966,7 @@ which-module@^2.0.0:
   resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
   integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
 
-which@1, which@^1.2.10, which@^1.2.12, which@^1.2.14, which@^1.2.9, which@^1.3.0:
+which@1, which@^1.2.10, which@^1.2.12, which@^1.2.14, which@^1.2.9, which@^1.3.0, which@^1.3.1:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
   integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
@@ -15517,7 +16086,7 @@ write-file-atomic@^2.0.0, write-file-atomic@^2.1.0, write-file-atomic@^2.3.0:
     imurmurhash "^0.1.4"
     signal-exit "^3.0.2"
 
-write-json-file@^2.2.0:
+write-json-file@^2.2.0, write-json-file@^2.3.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-2.3.0.tgz#2b64c8a33004d54b8698c76d585a77ceb61da32f"
   integrity sha1-K2TIozAE1UuGmMdtWFp3zrYdoy8=
@@ -15626,6 +16195,14 @@ yargs-parser@^10.1.0:
   dependencies:
     camelcase "^4.1.0"
 
+yargs-parser@^11.1.1:
+  version "11.1.1"
+  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4"
+  integrity sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==
+  dependencies:
+    camelcase "^5.0.0"
+    decamelize "^1.2.0"
+
 yargs-parser@^5.0.0:
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a"
@@ -15683,6 +16260,24 @@ yargs@^11.0.0:
     y18n "^3.2.1"
     yargs-parser "^9.0.2"
 
+yargs@^12.0.1:
+  version "12.0.5"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13"
+  integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==
+  dependencies:
+    cliui "^4.0.0"
+    decamelize "^1.2.0"
+    find-up "^3.0.0"
+    get-caller-file "^1.0.1"
+    os-locale "^3.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 || ^4.0.0"
+    yargs-parser "^11.1.1"
+
 yargs@^7.0.0:
   version "7.1.0"
   resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8"