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"